xref: /spdk/scripts/rpc.py (revision 49c6afbf128a130cd17d695170723fa4ff7699d0)
1#!/usr/bin/env python3
2
3from rpc.client import print_dict, print_json, JSONRPCException
4from rpc.helpers import deprecated_aliases
5
6import logging
7import argparse
8import importlib
9import rpc
10import sys
11import shlex
12import json
13
14try:
15    from shlex import quote
16except ImportError:
17    from pipes import quote
18
19
20def print_array(a):
21    print(" ".join((quote(v) for v in a)))
22
23
24if __name__ == "__main__":
25    parser = argparse.ArgumentParser(
26        description='SPDK RPC command line interface', usage='%(prog)s [options]')
27    parser.add_argument('-s', dest='server_addr',
28                        help='RPC domain socket path or IP address', default='/var/tmp/spdk.sock')
29    parser.add_argument('-p', dest='port',
30                        help='RPC port number (if server_addr is IP address)',
31                        default=5260, type=int)
32    parser.add_argument('-t', dest='timeout',
33                        help='Timeout as a floating point number expressed in seconds waiting for response. Default: 60.0',
34                        default=60.0, type=float)
35    parser.add_argument('-r', dest='conn_retries',
36                        help='Retry connecting to the RPC server N times with 0.2s interval. Default: 0',
37                        default=0, type=int)
38    parser.add_argument('-v', dest='verbose', action='store_const', const="INFO",
39                        help='Set verbose mode to INFO', default="ERROR")
40    parser.add_argument('--verbose', dest='verbose', choices=['DEBUG', 'INFO', 'ERROR'],
41                        help="""Set verbose level. """)
42    parser.add_argument('--dry_run', dest='dry_run', action='store_true', help="Display request and exit")
43    parser.set_defaults(dry_run=False)
44    parser.add_argument('--server', dest='is_server', action='store_true',
45                        help="Start listening on stdin, parse each line as a regular rpc.py execution and create \
46                                a separate connection for each command. Each command's output ends with either \
47                                **STATUS=0 if the command succeeded or **STATUS=1 if it failed. --server is meant \
48                                to be used in conjunction with bash coproc, where stdin and stdout are connected to \
49                                pipes and can be used as a faster way to send RPC commands. If enabled, rpc.py \
50                                must be executed without any other parameters.")
51    parser.set_defaults(is_server=False)
52    parser.add_argument('--plugin', dest='rpc_plugin', help='Module name of plugin with additional RPC commands')
53    subparsers = parser.add_subparsers(help='RPC methods', dest='called_rpc_name', metavar='')
54
55    def framework_start_init(args):
56        rpc.framework_start_init(args.client)
57
58    p = subparsers.add_parser('framework_start_init', aliases=['start_subsystem_init'],
59                              help='Start initialization of subsystems')
60    p.set_defaults(func=framework_start_init)
61
62    def framework_wait_init(args):
63        rpc.framework_wait_init(args.client)
64
65    p = subparsers.add_parser('framework_wait_init', aliases=['wait_subsystem_init'],
66                              help='Block until subsystems have been initialized')
67    p.set_defaults(func=framework_wait_init)
68
69    def rpc_get_methods(args):
70        print_dict(rpc.rpc_get_methods(args.client,
71                                       current=args.current,
72                                       include_aliases=args.include_aliases))
73
74    p = subparsers.add_parser('rpc_get_methods', aliases=['get_rpc_methods'],
75                              help='Get list of supported RPC methods')
76    p.add_argument('-c', '--current', help='Get list of RPC methods only callable in the current state.', action='store_true')
77    p.add_argument('-i', '--include-aliases', help='include RPC aliases', action='store_true')
78    p.set_defaults(func=rpc_get_methods)
79
80    def spdk_get_version(args):
81        print_json(rpc.spdk_get_version(args.client))
82
83    p = subparsers.add_parser('spdk_get_version', aliases=['get_spdk_version'],
84                              help='Get SPDK version')
85    p.set_defaults(func=spdk_get_version)
86
87    def save_config(args):
88        rpc.save_config(args.client,
89                        sys.stdout,
90                        indent=args.indent)
91
92    p = subparsers.add_parser('save_config', help="""Write current (live) configuration of SPDK subsystems and targets to stdout.
93    """)
94    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. Default indent level is 2.
95    """, type=int, default=2)
96    p.set_defaults(func=save_config)
97
98    def load_config(args):
99        rpc.load_config(args.client, args.json_conf,
100                        include_aliases=args.include_aliases)
101
102    p = subparsers.add_parser('load_config', help="""Configure SPDK subsystems and targets using JSON RPC.""")
103    p.add_argument('-i', '--include-aliases', help='include RPC aliases', action='store_true')
104    p.add_argument('-j', '--json_conf', help='Valid JSON configuration', default=sys.stdin)
105    p.set_defaults(func=load_config)
106
107    def save_subsystem_config(args):
108        rpc.save_subsystem_config(args.client,
109                                  sys.stdout,
110                                  indent=args.indent,
111                                  name=args.name)
112
113    p = subparsers.add_parser('save_subsystem_config', help="""Write current (live) configuration of SPDK subsystem to stdout.
114    """)
115    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. Default indent level is 2.
116    """, type=int, default=2)
117    p.add_argument('-n', '--name', help='Name of subsystem', required=True)
118    p.set_defaults(func=save_subsystem_config)
119
120    def load_subsystem_config(args):
121        rpc.load_subsystem_config(args.client,
122                                  args.json_conf)
123
124    p = subparsers.add_parser('load_subsystem_config', help="""Configure SPDK subsystem using JSON RPC.""")
125    p.add_argument('-j', '--json_conf', help='Valid JSON configuration', default=sys.stdin)
126    p.set_defaults(func=load_subsystem_config)
127
128    # app
129    def spdk_kill_instance(args):
130        rpc.app.spdk_kill_instance(args.client,
131                                   sig_name=args.sig_name)
132
133    p = subparsers.add_parser('spdk_kill_instance', aliases=['kill_instance'],
134                              help='Send signal to instance')
135    p.add_argument('sig_name', help='signal will be sent to server.')
136    p.set_defaults(func=spdk_kill_instance)
137
138    def framework_monitor_context_switch(args):
139        enabled = None
140        if args.enable:
141            enabled = True
142        if args.disable:
143            enabled = False
144        print_dict(rpc.app.framework_monitor_context_switch(args.client,
145                                                            enabled=enabled))
146
147    p = subparsers.add_parser('framework_monitor_context_switch', aliases=['context_switch_monitor'],
148                              help='Control whether the context switch monitor is enabled')
149    p.add_argument('-e', '--enable', action='store_true', help='Enable context switch monitoring')
150    p.add_argument('-d', '--disable', action='store_true', help='Disable context switch monitoring')
151    p.set_defaults(func=framework_monitor_context_switch)
152
153    def framework_get_reactors(args):
154        print_dict(rpc.app.framework_get_reactors(args.client))
155
156    p = subparsers.add_parser(
157        'framework_get_reactors', help='Display list of all reactors')
158    p.set_defaults(func=framework_get_reactors)
159
160    def framework_set_scheduler(args):
161        rpc.app.framework_set_scheduler(args.client,
162                                        name=args.name,
163                                        period=args.period)
164
165    p = subparsers.add_parser(
166        'framework_set_scheduler', help='Select thread scheduler that will be activated and its period (experimental)')
167    p.add_argument('name', help="Name of a scheduler")
168    p.add_argument('-p', '--period', help="Scheduler period in microseconds", type=int)
169    p.set_defaults(func=framework_set_scheduler)
170
171    def framework_get_scheduler(args):
172        print_dict(rpc.app.framework_get_scheduler(args.client))
173
174    p = subparsers.add_parser(
175        'framework_get_scheduler', help='Display currently set scheduler and its properties.')
176    p.set_defaults(func=framework_get_scheduler)
177
178    # bdev
179    def bdev_set_options(args):
180        rpc.bdev.bdev_set_options(args.client,
181                                  bdev_io_pool_size=args.bdev_io_pool_size,
182                                  bdev_io_cache_size=args.bdev_io_cache_size,
183                                  bdev_auto_examine=args.bdev_auto_examine,
184                                  small_buf_pool_size=args.small_buf_pool_size,
185                                  large_buf_pool_size=args.large_buf_pool_size)
186
187    p = subparsers.add_parser('bdev_set_options', aliases=['set_bdev_options'],
188                              help="""Set options of bdev subsystem""")
189    p.add_argument('-p', '--bdev-io-pool-size', help='Number of bdev_io structures in shared buffer pool', type=int)
190    p.add_argument('-c', '--bdev-io-cache-size', help='Maximum number of bdev_io structures cached per thread', type=int)
191    p.add_argument('-s', '--small-buf-pool-size', help='Maximum number of small buf (i.e., 8KB) pool size', type=int)
192    p.add_argument('-l', '--large-buf-pool-size', help='Maximum number of large buf (i.e., 64KB) pool size', type=int)
193    group = p.add_mutually_exclusive_group()
194    group.add_argument('-e', '--enable-auto-examine', dest='bdev_auto_examine', help='Allow to auto examine', action='store_true')
195    group.add_argument('-d', '--disable-auto-examine', dest='bdev_auto_examine', help='Not allow to auto examine', action='store_false')
196    p.set_defaults(bdev_auto_examine=True)
197    p.set_defaults(func=bdev_set_options)
198
199    def bdev_examine(args):
200        rpc.bdev.bdev_examine(args.client,
201                              name=args.name)
202
203    p = subparsers.add_parser('bdev_examine',
204                              help="""examine a bdev if it exists, or will examine it after it is created""")
205    p.add_argument('-b', '--name', help='Name or alias of the bdev')
206    p.set_defaults(func=bdev_examine)
207
208    def bdev_wait_for_examine(args):
209        rpc.bdev.bdev_wait_for_examine(args.client)
210
211    p = subparsers.add_parser('bdev_wait_for_examine',
212                              help="""Report when all bdevs have been examined""")
213    p.set_defaults(func=bdev_wait_for_examine)
214
215    def bdev_compress_create(args):
216        print_json(rpc.bdev.bdev_compress_create(args.client,
217                                                 base_bdev_name=args.base_bdev_name,
218                                                 pm_path=args.pm_path,
219                                                 lb_size=args.lb_size))
220
221    p = subparsers.add_parser('bdev_compress_create', aliases=['construct_compress_bdev'],
222                              help='Add a compress vbdev')
223    p.add_argument('-b', '--base_bdev_name', help="Name of the base bdev")
224    p.add_argument('-p', '--pm_path', help="Path to persistent memory")
225    p.add_argument('-l', '--lb_size', help="Compressed vol logical block size (optional, if used must be 512 or 4096)", type=int, default=0)
226    p.set_defaults(func=bdev_compress_create)
227
228    def bdev_compress_delete(args):
229        rpc.bdev.bdev_compress_delete(args.client,
230                                      name=args.name)
231
232    p = subparsers.add_parser('bdev_compress_delete', aliases=['delete_compress_bdev'],
233                              help='Delete a compress disk')
234    p.add_argument('name', help='compress bdev name')
235    p.set_defaults(func=bdev_compress_delete)
236
237    def bdev_compress_set_pmd(args):
238        rpc.bdev.bdev_compress_set_pmd(args.client,
239                                       pmd=args.pmd)
240    p = subparsers.add_parser('bdev_compress_set_pmd', aliases=['set_compress_pmd', 'compress_set_pmd'],
241                              help='Set pmd option for a compress disk')
242    p.add_argument('-p', '--pmd', type=int, help='0 = auto-select, 1= QAT only, 2 = ISAL only')
243    p.set_defaults(func=bdev_compress_set_pmd)
244
245    def bdev_compress_get_orphans(args):
246        print_dict(rpc.bdev.bdev_compress_get_orphans(args.client,
247                                                      name=args.name))
248    p = subparsers.add_parser(
249        'bdev_compress_get_orphans', help='Display list of orphaned compress bdevs.')
250    p.add_argument('-b', '--name', help="Name of a comp bdev. Example: COMP_Nvme0n1", required=False)
251    p.set_defaults(func=bdev_compress_get_orphans)
252
253    def bdev_crypto_create(args):
254        print_json(rpc.bdev.bdev_crypto_create(args.client,
255                                               base_bdev_name=args.base_bdev_name,
256                                               name=args.name,
257                                               crypto_pmd=args.crypto_pmd,
258                                               key=args.key,
259                                               cipher=args.cipher,
260                                               key2=args.key2))
261    p = subparsers.add_parser('bdev_crypto_create', aliases=['construct_crypto_bdev'],
262                              help='Add a crypto vbdev')
263    p.add_argument('base_bdev_name', help="Name of the base bdev")
264    p.add_argument('name', help="Name of the crypto vbdev")
265    p.add_argument('crypto_pmd', help="Name of the crypto device driver")
266    p.add_argument('key', help="Key")
267    p.add_argument('-c', '--cipher', help="cipher to use, AES_CBC or AES_XTS (QAT only)", default="AES_CBC")
268    p.add_argument('-k2', '--key2', help="2nd key for cipher AET_XTS", default=None)
269    p.set_defaults(func=bdev_crypto_create)
270
271    def bdev_crypto_delete(args):
272        rpc.bdev.bdev_crypto_delete(args.client,
273                                    name=args.name)
274
275    p = subparsers.add_parser('bdev_crypto_delete', aliases=['delete_crypto_bdev'],
276                              help='Delete a crypto disk')
277    p.add_argument('name', help='crypto bdev name')
278    p.set_defaults(func=bdev_crypto_delete)
279
280    def bdev_ocf_create(args):
281        print_json(rpc.bdev.bdev_ocf_create(args.client,
282                                            name=args.name,
283                                            mode=args.mode,
284                                            cache_line_size=args.cache_line_size,
285                                            cache_bdev_name=args.cache_bdev_name,
286                                            core_bdev_name=args.core_bdev_name))
287    p = subparsers.add_parser('bdev_ocf_create', aliases=['construct_ocf_bdev'],
288                              help='Add an OCF block device')
289    p.add_argument('name', help='Name of resulting OCF bdev')
290    p.add_argument('mode', help='OCF cache mode', choices=['wb', 'wt', 'pt', 'wa', 'wi', 'wo'])
291    p.add_argument(
292        '--cache-line-size',
293        help='OCF cache line size. The unit is KiB',
294        type=int,
295        choices=[4, 8, 16, 32, 64],
296        required=False,
297        default=0,
298    )
299    p.add_argument('cache_bdev_name', help='Name of underlying cache bdev')
300    p.add_argument('core_bdev_name', help='Name of unerlying core bdev')
301    p.set_defaults(func=bdev_ocf_create)
302
303    def bdev_ocf_delete(args):
304        rpc.bdev.bdev_ocf_delete(args.client,
305                                 name=args.name)
306
307    p = subparsers.add_parser('bdev_ocf_delete', aliases=['delete_ocf_bdev'],
308                              help='Delete an OCF block device')
309    p.add_argument('name', help='Name of OCF bdev')
310    p.set_defaults(func=bdev_ocf_delete)
311
312    def bdev_ocf_get_stats(args):
313        print_dict(rpc.bdev.bdev_ocf_get_stats(args.client,
314                                               name=args.name))
315    p = subparsers.add_parser('bdev_ocf_get_stats', aliases=['get_ocf_stats'],
316                              help='Get statistics of chosen OCF block device')
317    p.add_argument('name', help='Name of OCF bdev')
318    p.set_defaults(func=bdev_ocf_get_stats)
319
320    def bdev_ocf_get_bdevs(args):
321        print_dict(rpc.bdev.bdev_ocf_get_bdevs(args.client,
322                                               name=args.name))
323    p = subparsers.add_parser('bdev_ocf_get_bdevs', aliases=['get_ocf_bdevs'],
324                              help='Get list of OCF devices including unregistered ones')
325    p.add_argument('name', nargs='?', default=None, help='name of OCF vbdev or name of cache device or name of core device (optional)')
326    p.set_defaults(func=bdev_ocf_get_bdevs)
327
328    def bdev_ocf_set_cache_mode(args):
329        print_json(rpc.bdev.bdev_ocf_set_cache_mode(args.client,
330                                                    name=args.name,
331                                                    mode=args.mode))
332    p = subparsers.add_parser('bdev_ocf_set_cache_mode',
333                              help='Set cache mode of OCF block device')
334    p.add_argument('name', help='Name of OCF bdev')
335    p.add_argument('mode', help='OCF cache mode', choices=['wb', 'wt', 'pt', 'wa', 'wi', 'wo'])
336    p.set_defaults(func=bdev_ocf_set_cache_mode)
337
338    def bdev_malloc_create(args):
339        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
340        print_json(rpc.bdev.bdev_malloc_create(args.client,
341                                               num_blocks=int(num_blocks),
342                                               block_size=args.block_size,
343                                               name=args.name,
344                                               uuid=args.uuid))
345    p = subparsers.add_parser('bdev_malloc_create', aliases=['construct_malloc_bdev'],
346                              help='Create a bdev with malloc backend')
347    p.add_argument('-b', '--name', help="Name of the bdev")
348    p.add_argument('-u', '--uuid', help="UUID of the bdev")
349    p.add_argument(
350        'total_size', help='Size of malloc bdev in MB (float > 0)', type=float)
351    p.add_argument('block_size', help='Block size for this bdev', type=int)
352    p.set_defaults(func=bdev_malloc_create)
353
354    def bdev_malloc_delete(args):
355        rpc.bdev.bdev_malloc_delete(args.client,
356                                    name=args.name)
357
358    p = subparsers.add_parser('bdev_malloc_delete', aliases=['delete_malloc_bdev'],
359                              help='Delete a malloc disk')
360    p.add_argument('name', help='malloc bdev name')
361    p.set_defaults(func=bdev_malloc_delete)
362
363    def bdev_null_create(args):
364        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
365        if args.dif_type and not args.md_size:
366            print("ERROR: --md-size must be > 0 when --dif-type is > 0")
367            exit(1)
368        print_json(rpc.bdev.bdev_null_create(args.client,
369                                             num_blocks=num_blocks,
370                                             block_size=args.block_size,
371                                             name=args.name,
372                                             uuid=args.uuid,
373                                             md_size=args.md_size,
374                                             dif_type=args.dif_type,
375                                             dif_is_head_of_md=args.dif_is_head_of_md))
376
377    p = subparsers.add_parser('bdev_null_create', aliases=['construct_null_bdev'],
378                              help='Add a bdev with null backend')
379    p.add_argument('name', help='Block device name')
380    p.add_argument('-u', '--uuid', help='UUID of the bdev')
381    p.add_argument('total_size', help='Size of null bdev in MB (int > 0). Includes only data blocks.', type=int)
382    p.add_argument('block_size', help='Block size for this bdev.'
383                                      'Should be a sum of block size and metadata size if --md-size is used.', type=int)
384    p.add_argument('-m', '--md-size', type=int,
385                   help='Metadata size for this bdev. Default=0.')
386    p.add_argument('-t', '--dif-type', type=int, default=0, choices=[0, 1, 2, 3],
387                   help='Protection information type. Parameter --md-size needs'
388                        'to be set along --dif-type. Default=0 - no protection.')
389    p.add_argument('-d', '--dif-is-head-of-md', action='store_true',
390                   help='Protection information is in the first 8 bytes of metadata. Default=false.')
391    p.set_defaults(func=bdev_null_create)
392
393    def bdev_null_delete(args):
394        rpc.bdev.bdev_null_delete(args.client,
395                                  name=args.name)
396
397    p = subparsers.add_parser('bdev_null_delete', aliases=['delete_null_bdev'],
398                              help='Delete a null bdev')
399    p.add_argument('name', help='null bdev name')
400    p.set_defaults(func=bdev_null_delete)
401
402    def bdev_null_resize(args):
403        print_json(rpc.bdev.bdev_null_resize(args.client,
404                                             name=args.name,
405                                             new_size=int(args.new_size)))
406
407    p = subparsers.add_parser('bdev_null_resize',
408                              help='Resize a null bdev')
409    p.add_argument('name', help='null bdev name')
410    p.add_argument('new_size', help='new bdev size for resize operation. The unit is MiB')
411    p.set_defaults(func=bdev_null_resize)
412
413    def bdev_aio_create(args):
414        print_json(rpc.bdev.bdev_aio_create(args.client,
415                                            filename=args.filename,
416                                            name=args.name,
417                                            block_size=args.block_size))
418
419    p = subparsers.add_parser('bdev_aio_create', aliases=['construct_aio_bdev'],
420                              help='Add a bdev with aio backend')
421    p.add_argument('filename', help='Path to device or file (ex: /dev/sda)')
422    p.add_argument('name', help='Block device name')
423    p.add_argument('block_size', help='Block size for this bdev', type=int, nargs='?', default=0)
424    p.set_defaults(func=bdev_aio_create)
425
426    def bdev_aio_delete(args):
427        rpc.bdev.bdev_aio_delete(args.client,
428                                 name=args.name)
429
430    p = subparsers.add_parser('bdev_aio_delete', aliases=['delete_aio_bdev'],
431                              help='Delete an aio disk')
432    p.add_argument('name', help='aio bdev name')
433    p.set_defaults(func=bdev_aio_delete)
434
435    def bdev_uring_create(args):
436        print_json(rpc.bdev.bdev_uring_create(args.client,
437                                              filename=args.filename,
438                                              name=args.name,
439                                              block_size=args.block_size))
440
441    p = subparsers.add_parser('bdev_uring_create', help='Create a bdev with io_uring backend')
442    p.add_argument('filename', help='Path to device or file (ex: /dev/nvme0n1)')
443    p.add_argument('name', help='bdev name')
444    p.add_argument('block_size', help='Block size for this bdev', type=int, nargs='?', default=0)
445    p.set_defaults(func=bdev_uring_create)
446
447    def bdev_uring_delete(args):
448        rpc.bdev.bdev_uring_delete(args.client,
449                                   name=args.name)
450
451    p = subparsers.add_parser('bdev_uring_delete', help='Delete a uring bdev')
452    p.add_argument('name', help='uring bdev name')
453    p.set_defaults(func=bdev_uring_delete)
454
455    def bdev_nvme_set_options(args):
456        rpc.bdev.bdev_nvme_set_options(args.client,
457                                       action_on_timeout=args.action_on_timeout,
458                                       timeout_us=args.timeout_us,
459                                       timeout_admin_us=args.timeout_admin_us,
460                                       keep_alive_timeout_ms=args.keep_alive_timeout_ms,
461                                       retry_count=args.retry_count,
462                                       arbitration_burst=args.arbitration_burst,
463                                       low_priority_weight=args.low_priority_weight,
464                                       medium_priority_weight=args.medium_priority_weight,
465                                       high_priority_weight=args.high_priority_weight,
466                                       nvme_adminq_poll_period_us=args.nvme_adminq_poll_period_us,
467                                       nvme_ioq_poll_period_us=args.nvme_ioq_poll_period_us,
468                                       io_queue_requests=args.io_queue_requests,
469                                       delay_cmd_submit=args.delay_cmd_submit)
470
471    p = subparsers.add_parser('bdev_nvme_set_options', aliases=['set_bdev_nvme_options'],
472                              help='Set options for the bdev nvme type. This is startup command.')
473    p.add_argument('-a', '--action-on-timeout',
474                   help="Action to take on command time out. Valid valies are: none, reset, abort")
475    p.add_argument('-t', '--timeout-us',
476                   help="Timeout for each command, in microseconds. If 0, don't track timeouts.", type=int)
477    p.add_argument('--timeout-admin-us',
478                   help="Timeout for each admin command, in microseconds. If 0, treat same as io timeouts.", type=int)
479    p.add_argument('-k', '--keep-alive-timeout-ms',
480                   help="Keep alive timeout period in millisecond. If 0, disable keep-alive.", type=int)
481    p.add_argument('-n', '--retry-count',
482                   help='the number of attempts per I/O when an I/O fails', type=int)
483    p.add_argument('--arbitration-burst',
484                   help='the value is expressed as a power of two', type=int)
485    p.add_argument('--low-priority-weight',
486                   help='the maximum number of commands that the controller may launch at one time from a low priority queue', type=int)
487    p.add_argument('--medium-priority-weight',
488                   help='the maximum number of commands that the controller may launch at one time from a medium priority queue', type=int)
489    p.add_argument('--high-priority-weight',
490                   help='the maximum number of commands that the controller may launch at one time from a high priority queue', type=int)
491    p.add_argument('-p', '--nvme-adminq-poll-period-us',
492                   help='How often the admin queue is polled for asynchronous events', type=int)
493    p.add_argument('-i', '--nvme-ioq-poll-period-us',
494                   help='How often to poll I/O queues for completions', type=int)
495    p.add_argument('-s', '--io-queue-requests',
496                   help='The number of requests allocated for each NVMe I/O queue. Default: 512', type=int)
497    p.add_argument('-d', '--disable-delay-cmd-submit',
498                   help='Disable delaying NVMe command submission, i.e. no batching of multiple commands',
499                   action='store_false', dest='delay_cmd_submit', default=True)
500    p.set_defaults(func=bdev_nvme_set_options)
501
502    def bdev_nvme_set_hotplug(args):
503        rpc.bdev.bdev_nvme_set_hotplug(args.client, enable=args.enable, period_us=args.period_us)
504
505    p = subparsers.add_parser('bdev_nvme_set_hotplug', aliases=['set_bdev_nvme_hotplug'],
506                              help='Set hotplug options for bdev nvme type.')
507    p.add_argument('-d', '--disable', dest='enable', default=False, action='store_false', help="Disable hotplug (default)")
508    p.add_argument('-e', '--enable', dest='enable', action='store_true', help="Enable hotplug")
509    p.add_argument('-r', '--period-us',
510                   help='How often the hotplug is processed for insert and remove events', type=int)
511    p.set_defaults(func=bdev_nvme_set_hotplug)
512
513    def bdev_nvme_attach_controller(args):
514        print_array(rpc.bdev.bdev_nvme_attach_controller(args.client,
515                                                         name=args.name,
516                                                         trtype=args.trtype,
517                                                         traddr=args.traddr,
518                                                         adrfam=args.adrfam,
519                                                         trsvcid=args.trsvcid,
520                                                         priority=args.priority,
521                                                         subnqn=args.subnqn,
522                                                         hostnqn=args.hostnqn,
523                                                         hostaddr=args.hostaddr,
524                                                         hostsvcid=args.hostsvcid,
525                                                         prchk_reftag=args.prchk_reftag,
526                                                         prchk_guard=args.prchk_guard,
527                                                         hdgst=args.hdgst,
528                                                         ddgst=args.ddgst))
529
530    p = subparsers.add_parser('bdev_nvme_attach_controller', aliases=['construct_nvme_bdev'],
531                              help='Add bdevs with nvme backend')
532    p.add_argument('-b', '--name', help="Name of the NVMe controller, prefix for each bdev name", required=True)
533    p.add_argument('-t', '--trtype',
534                   help='NVMe-oF target trtype: e.g., rdma, pcie', required=True)
535    p.add_argument('-a', '--traddr',
536                   help='NVMe-oF target address: e.g., an ip address or BDF', required=True)
537    p.add_argument('-f', '--adrfam',
538                   help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
539    p.add_argument('-s', '--trsvcid',
540                   help='NVMe-oF target trsvcid: e.g., a port number')
541    p.add_argument('-p', '--priority',
542                   help='NVMe-oF connection priority: e.g., a priority number')
543    p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
544    p.add_argument('-q', '--hostnqn', help='NVMe-oF host subnqn')
545    p.add_argument('-i', '--hostaddr',
546                   help='NVMe-oF host address: e.g., an ip address')
547    p.add_argument('-c', '--hostsvcid',
548                   help='NVMe-oF host svcid: e.g., a port number')
549    p.add_argument('-r', '--prchk-reftag',
550                   help='Enable checking of PI reference tag for I/O processing.', action='store_true')
551    p.add_argument('-g', '--prchk-guard',
552                   help='Enable checking of PI guard for I/O processing.', action='store_true')
553    p.add_argument('-e', '--hdgst',
554                   help='Enable TCP header digest.', action='store_true')
555    p.add_argument('-d', '--ddgst',
556                   help='Enable TCP data digest.', action='store_true')
557    p.set_defaults(func=bdev_nvme_attach_controller)
558
559    def bdev_nvme_get_controllers(args):
560        print_dict(rpc.nvme.bdev_nvme_get_controllers(args.client,
561                                                      name=args.name))
562
563    p = subparsers.add_parser(
564        'bdev_nvme_get_controllers', aliases=['get_nvme_controllers'],
565        help='Display current NVMe controllers list or required NVMe controller')
566    p.add_argument('-n', '--name', help="Name of the NVMe controller. Example: Nvme0", required=False)
567    p.set_defaults(func=bdev_nvme_get_controllers)
568
569    def bdev_nvme_detach_controller(args):
570        rpc.bdev.bdev_nvme_detach_controller(args.client,
571                                             name=args.name,
572                                             trtype=args.trtype,
573                                             traddr=args.traddr,
574                                             adrfam=args.adrfam,
575                                             trsvcid=args.trsvcid,
576                                             subnqn=args.subnqn)
577
578    p = subparsers.add_parser('bdev_nvme_detach_controller', aliases=['delete_nvme_controller'],
579                              help='Detach an NVMe controller and delete any associated bdevs')
580    p.add_argument('name', help="Name of the controller")
581    p.add_argument('-t', '--trtype',
582                   help='NVMe-oF target trtype: e.g., rdma, pcie')
583    p.add_argument('-a', '--traddr',
584                   help='NVMe-oF target address: e.g., an ip address or BDF')
585    p.add_argument('-f', '--adrfam',
586                   help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
587    p.add_argument('-s', '--trsvcid',
588                   help='NVMe-oF target trsvcid: e.g., a port number')
589    p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
590    p.set_defaults(func=bdev_nvme_detach_controller)
591
592    def bdev_nvme_cuse_register(args):
593        rpc.bdev.bdev_nvme_cuse_register(args.client,
594                                         name=args.name)
595
596    p = subparsers.add_parser('bdev_nvme_cuse_register',
597                              help='Register CUSE devices on NVMe controller')
598    p.add_argument('-n', '--name',
599                   help='Name of the NVMe controller. Example: Nvme0', required=True)
600    p.set_defaults(func=bdev_nvme_cuse_register)
601
602    def bdev_nvme_cuse_unregister(args):
603        rpc.bdev.bdev_nvme_cuse_unregister(args.client,
604                                           name=args.name)
605
606    p = subparsers.add_parser('bdev_nvme_cuse_unregister',
607                              help='Unregister CUSE devices on NVMe controller')
608    p.add_argument('-n', '--name',
609                   help='Name of the NVMe controller. Example: Nvme0', required=True)
610    p.set_defaults(func=bdev_nvme_cuse_unregister)
611
612    def bdev_zone_block_create(args):
613        print_json(rpc.bdev.bdev_zone_block_create(args.client,
614                                                   name=args.name,
615                                                   base_bdev=args.base_bdev,
616                                                   zone_capacity=args.zone_capacity,
617                                                   optimal_open_zones=args.optimal_open_zones))
618
619    p = subparsers.add_parser('bdev_zone_block_create',
620                              help='Create virtual zone namespace device with block device backend')
621    p.add_argument('-b', '--name', help="Name of the zone device", required=True)
622    p.add_argument('-n', '--base-bdev', help='Name of underlying, non-zoned bdev', required=True)
623    p.add_argument('-z', '--zone-capacity', help='Surfaced zone capacity in blocks', type=int, required=True)
624    p.add_argument('-o', '--optimal-open-zones', help='Number of zones required to reach optimal write speed', type=int, required=True)
625    p.set_defaults(func=bdev_zone_block_create)
626
627    def bdev_zone_block_delete(args):
628        rpc.bdev.bdev_zone_block_delete(args.client,
629                                        name=args.name)
630
631    p = subparsers.add_parser('bdev_zone_block_delete', help='Delete a virtual zone namespace device')
632    p.add_argument('name', help='Virtual zone bdev name')
633    p.set_defaults(func=bdev_zone_block_delete)
634
635    def bdev_rbd_register_cluster(args):
636        config_param = None
637        if args.config_param:
638            config_param = {}
639            for entry in args.config:
640                parts = entry.split('=', 1)
641                if len(parts) != 2:
642                    raise Exception('--config %s not in key=value form' % entry)
643                config_param[parts[0]] = parts[1]
644        print_json(rpc.bdev.bdev_rbd_register_cluster(args.client,
645                                                      name=args.name,
646                                                      user=args.user,
647                                                      config_param=config_param,
648                                                      config_file=args.config_file))
649
650    p = subparsers.add_parser('bdev_rbd_register_cluster',
651                              help='Add a Rados cluster with ceph rbd backend')
652    p.add_argument('name', help="Name of the Rados cluster only known to rbd bdev")
653    p.add_argument('--user', help="Ceph user name (i.e. admin, not client.admin)", required=False)
654    p.add_argument('--config_param', action='append', metavar='key=value',
655                   help="adds a key=value configuration option for rados_conf_set (default: rely on config file)")
656    p.add_argument('--config_file', help="The file path of the Rados configuration file", required=False)
657    p.set_defaults(func=bdev_rbd_register_cluster)
658
659    def bdev_rbd_unregister_cluster(args):
660        rpc.bdev.bdev_rbd_unregister_cluster(args.client, name=args.name)
661
662    p = subparsers.add_parser('bdev_rbd_unregister_cluster',
663                              help='Unregister a Rados cluster object')
664    p.add_argument('name', help='Name of the Rados Cluster only known to rbd bdev')
665    p.set_defaults(func=bdev_rbd_unregister_cluster)
666
667    def bdev_rbd_get_clusters_info(args):
668        print_json(rpc.bdev.bdev_rbd_get_clusters_info(args.client, name=args.name))
669
670    p = subparsers.add_parser('bdev_rbd_get_clusters_info',
671                              help='Display registered Rados Cluster names and related info')
672    p.add_argument('-b', '--name', help="Name of the registered Rados Cluster Name. Example: Cluster1", required=False)
673    p.set_defaults(func=bdev_rbd_get_clusters_info)
674
675    def bdev_rbd_create(args):
676        config = None
677        if args.config:
678            config = {}
679            for entry in args.config:
680                parts = entry.split('=', 1)
681                if len(parts) != 2:
682                    raise Exception('--config %s not in key=value form' % entry)
683                config[parts[0]] = parts[1]
684        print_json(rpc.bdev.bdev_rbd_create(args.client,
685                                            name=args.name,
686                                            user=args.user,
687                                            config=config,
688                                            pool_name=args.pool_name,
689                                            rbd_name=args.rbd_name,
690                                            block_size=args.block_size,
691                                            cluster_name=args.cluster_name))
692
693    p = subparsers.add_parser('bdev_rbd_create', aliases=['construct_rbd_bdev'],
694                              help='Add a bdev with ceph rbd backend')
695    p.add_argument('-b', '--name', help="Name of the bdev", required=False)
696    p.add_argument('--user', help="Ceph user name (i.e. admin, not client.admin)", required=False)
697    p.add_argument('--config', action='append', metavar='key=value',
698                   help="adds a key=value configuration option for rados_conf_set (default: rely on config file)")
699    p.add_argument('pool_name', help='rbd pool name')
700    p.add_argument('rbd_name', help='rbd image name')
701    p.add_argument('block_size', help='rbd block size', type=int)
702    p.add_argument('-c', '--cluster_name', help="cluster name to identify the Rados cluster", required=False)
703    p.set_defaults(func=bdev_rbd_create)
704
705    def bdev_rbd_delete(args):
706        rpc.bdev.bdev_rbd_delete(args.client,
707                                 name=args.name)
708
709    p = subparsers.add_parser('bdev_rbd_delete', aliases=['delete_rbd_bdev'],
710                              help='Delete a rbd bdev')
711    p.add_argument('name', help='rbd bdev name')
712    p.set_defaults(func=bdev_rbd_delete)
713
714    def bdev_rbd_resize(args):
715        print_json(rpc.bdev.bdev_rbd_resize(args.client,
716                                            name=args.name,
717                                            new_size=int(args.new_size)))
718        rpc.bdev.bdev_rbd_resize(args.client,
719                                 name=args.name,
720                                 new_size=int(args.new_size))
721
722    p = subparsers.add_parser('bdev_rbd_resize',
723                              help='Resize a rbd bdev')
724    p.add_argument('name', help='rbd bdev name')
725    p.add_argument('new_size', help='new bdev size for resize operation. The unit is MiB')
726    p.set_defaults(func=bdev_rbd_resize)
727
728    def bdev_delay_create(args):
729        print_json(rpc.bdev.bdev_delay_create(args.client,
730                                              base_bdev_name=args.base_bdev_name,
731                                              name=args.name,
732                                              avg_read_latency=args.avg_read_latency,
733                                              p99_read_latency=args.nine_nine_read_latency,
734                                              avg_write_latency=args.avg_write_latency,
735                                              p99_write_latency=args.nine_nine_write_latency))
736
737    p = subparsers.add_parser('bdev_delay_create',
738                              help='Add a delay bdev on existing bdev')
739    p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True)
740    p.add_argument('-d', '--name', help="Name of the delay bdev", required=True)
741    p.add_argument('-r', '--avg-read-latency',
742                   help="Average latency to apply before completing read ops (in microseconds)", required=True, type=int)
743    p.add_argument('-t', '--nine-nine-read-latency',
744                   help="latency to apply to 1 in 100 read ops (in microseconds)", required=True, type=int)
745    p.add_argument('-w', '--avg-write-latency',
746                   help="Average latency to apply before completing write ops (in microseconds)", required=True, type=int)
747    p.add_argument('-n', '--nine-nine-write-latency',
748                   help="latency to apply to 1 in 100 write ops (in microseconds)", required=True, type=int)
749    p.set_defaults(func=bdev_delay_create)
750
751    def bdev_delay_delete(args):
752        rpc.bdev.bdev_delay_delete(args.client,
753                                   name=args.name)
754
755    p = subparsers.add_parser('bdev_delay_delete', help='Delete a delay bdev')
756    p.add_argument('name', help='delay bdev name')
757    p.set_defaults(func=bdev_delay_delete)
758
759    def bdev_delay_update_latency(args):
760        print_json(rpc.bdev.bdev_delay_update_latency(args.client,
761                                                      delay_bdev_name=args.delay_bdev_name,
762                                                      latency_type=args.latency_type,
763                                                      latency_us=args.latency_us))
764    p = subparsers.add_parser('bdev_delay_update_latency',
765                              help='Update one of the latency values for a given delay bdev')
766    p.add_argument('delay_bdev_name', help='The name of the given delay bdev')
767    p.add_argument('latency_type', help='one of: avg_read, avg_write, p99_read, p99_write. No other values accepted.')
768    p.add_argument('latency_us', help='new latency value in microseconds.', type=int)
769    p.set_defaults(func=bdev_delay_update_latency)
770
771    def bdev_error_create(args):
772        print_json(rpc.bdev.bdev_error_create(args.client,
773                                              base_name=args.base_name))
774
775    p = subparsers.add_parser('bdev_error_create', aliases=['construct_error_bdev'],
776                              help='Add bdev with error injection backend')
777    p.add_argument('base_name', help='base bdev name')
778    p.set_defaults(func=bdev_error_create)
779
780    def bdev_error_delete(args):
781        rpc.bdev.bdev_error_delete(args.client,
782                                   name=args.name)
783
784    p = subparsers.add_parser('bdev_error_delete', aliases=['delete_error_bdev'],
785                              help='Delete an error bdev')
786    p.add_argument('name', help='error bdev name')
787    p.set_defaults(func=bdev_error_delete)
788
789    def bdev_iscsi_create(args):
790        print_json(rpc.bdev.bdev_iscsi_create(args.client,
791                                              name=args.name,
792                                              url=args.url,
793                                              initiator_iqn=args.initiator_iqn))
794
795    p = subparsers.add_parser('bdev_iscsi_create', aliases=['construct_iscsi_bdev'],
796                              help='Add bdev with iSCSI initiator backend')
797    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
798    p.add_argument('-i', '--initiator-iqn', help="Initiator IQN", required=True)
799    p.add_argument('--url', help="iSCSI Lun URL", required=True)
800    p.set_defaults(func=bdev_iscsi_create)
801
802    def bdev_iscsi_delete(args):
803        rpc.bdev.bdev_iscsi_delete(args.client,
804                                   name=args.name)
805
806    p = subparsers.add_parser('bdev_iscsi_delete', aliases=['delete_iscsi_bdev'],
807                              help='Delete an iSCSI bdev')
808    p.add_argument('name', help='iSCSI bdev name')
809    p.set_defaults(func=bdev_iscsi_delete)
810
811    def bdev_pmem_create(args):
812        print_json(rpc.bdev.bdev_pmem_create(args.client,
813                                             pmem_file=args.pmem_file,
814                                             name=args.name))
815
816    p = subparsers.add_parser('bdev_pmem_create', aliases=['construct_pmem_bdev'],
817                              help='Add a bdev with pmem backend')
818    p.add_argument('pmem_file', help='Path to pmemblk pool file')
819    p.add_argument('-n', '--name', help='Block device name', required=True)
820    p.set_defaults(func=bdev_pmem_create)
821
822    def bdev_pmem_delete(args):
823        rpc.bdev.bdev_pmem_delete(args.client,
824                                  name=args.name)
825
826    p = subparsers.add_parser('bdev_pmem_delete', aliases=['delete_pmem_bdev'],
827                              help='Delete a pmem bdev')
828    p.add_argument('name', help='pmem bdev name')
829    p.set_defaults(func=bdev_pmem_delete)
830
831    def bdev_passthru_create(args):
832        print_json(rpc.bdev.bdev_passthru_create(args.client,
833                                                 base_bdev_name=args.base_bdev_name,
834                                                 name=args.name))
835
836    p = subparsers.add_parser('bdev_passthru_create', aliases=['construct_passthru_bdev'],
837                              help='Add a pass through bdev on existing bdev')
838    p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True)
839    p.add_argument('-p', '--name', help="Name of the pass through bdev", required=True)
840    p.set_defaults(func=bdev_passthru_create)
841
842    def bdev_passthru_delete(args):
843        rpc.bdev.bdev_passthru_delete(args.client,
844                                      name=args.name)
845
846    p = subparsers.add_parser('bdev_passthru_delete', aliases=['delete_passthru_bdev'],
847                              help='Delete a pass through bdev')
848    p.add_argument('name', help='pass through bdev name')
849    p.set_defaults(func=bdev_passthru_delete)
850
851    def bdev_get_bdevs(args):
852        print_dict(rpc.bdev.bdev_get_bdevs(args.client,
853                                           name=args.name))
854
855    p = subparsers.add_parser('bdev_get_bdevs', aliases=['get_bdevs'],
856                              help='Display current blockdev list or required blockdev')
857    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
858    p.set_defaults(func=bdev_get_bdevs)
859
860    def bdev_get_iostat(args):
861        print_dict(rpc.bdev.bdev_get_iostat(args.client,
862                                            name=args.name))
863
864    p = subparsers.add_parser('bdev_get_iostat', aliases=['get_bdevs_iostat'],
865                              help='Display current I/O statistics of all the blockdevs or required blockdev.')
866    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
867    p.set_defaults(func=bdev_get_iostat)
868
869    def bdev_enable_histogram(args):
870        rpc.bdev.bdev_enable_histogram(args.client, name=args.name, enable=args.enable)
871
872    p = subparsers.add_parser('bdev_enable_histogram', aliases=['enable_bdev_histogram'],
873                              help='Enable or disable histogram for specified bdev')
874    p.add_argument('-e', '--enable', default=True, dest='enable', action='store_true', help='Enable histograms on specified device')
875    p.add_argument('-d', '--disable', dest='enable', action='store_false', help='Disable histograms on specified device')
876    p.add_argument('name', help='bdev name')
877    p.set_defaults(func=bdev_enable_histogram)
878
879    def bdev_get_histogram(args):
880        print_dict(rpc.bdev.bdev_get_histogram(args.client, name=args.name))
881
882    p = subparsers.add_parser('bdev_get_histogram', aliases=['get_bdev_histogram'],
883                              help='Get histogram for specified bdev')
884    p.add_argument('name', help='bdev name')
885    p.set_defaults(func=bdev_get_histogram)
886
887    def bdev_set_qd_sampling_period(args):
888        rpc.bdev.bdev_set_qd_sampling_period(args.client,
889                                             name=args.name,
890                                             period=args.period)
891
892    p = subparsers.add_parser('bdev_set_qd_sampling_period', aliases=['set_bdev_qd_sampling_period'],
893                              help="Enable or disable tracking of a bdev's queue depth.")
894    p.add_argument('name', help='Blockdev name. Example: Malloc0')
895    p.add_argument('period', help='Period with which to poll the block device queue depth in microseconds.'
896                   ' If set to 0, polling will be disabled.',
897                   type=int)
898    p.set_defaults(func=bdev_set_qd_sampling_period)
899
900    def bdev_set_qos_limit(args):
901        rpc.bdev.bdev_set_qos_limit(args.client,
902                                    name=args.name,
903                                    rw_ios_per_sec=args.rw_ios_per_sec,
904                                    rw_mbytes_per_sec=args.rw_mbytes_per_sec,
905                                    r_mbytes_per_sec=args.r_mbytes_per_sec,
906                                    w_mbytes_per_sec=args.w_mbytes_per_sec)
907
908    p = subparsers.add_parser('bdev_set_qos_limit', aliases=['set_bdev_qos_limit'],
909                              help='Set QoS rate limit on a blockdev')
910    p.add_argument('name', help='Blockdev name to set QoS. Example: Malloc0')
911    p.add_argument('--rw_ios_per_sec',
912                   help='R/W IOs per second limit (>=1000, example: 20000). 0 means unlimited.',
913                   type=int, required=False)
914    p.add_argument('--rw_mbytes_per_sec',
915                   help="R/W megabytes per second limit (>=10, example: 100). 0 means unlimited.",
916                   type=int, required=False)
917    p.add_argument('--r_mbytes_per_sec',
918                   help="Read megabytes per second limit (>=10, example: 100). 0 means unlimited.",
919                   type=int, required=False)
920    p.add_argument('--w_mbytes_per_sec',
921                   help="Write megabytes per second limit (>=10, example: 100). 0 means unlimited.",
922                   type=int, required=False)
923    p.set_defaults(func=bdev_set_qos_limit)
924
925    def bdev_error_inject_error(args):
926        rpc.bdev.bdev_error_inject_error(args.client,
927                                         name=args.name,
928                                         io_type=args.io_type,
929                                         error_type=args.error_type,
930                                         num=args.num)
931
932    p = subparsers.add_parser('bdev_error_inject_error', aliases=['bdev_inject_error'],
933                              help='bdev inject error')
934    p.add_argument('name', help="""the name of the error injection bdev""")
935    p.add_argument('io_type', help="""io_type: 'clear' 'read' 'write' 'unmap' 'flush' 'all'""")
936    p.add_argument('error_type', help="""error_type: 'failure' 'pending'""")
937    p.add_argument(
938        '-n', '--num', help='the number of commands you want to fail', type=int, default=1)
939    p.set_defaults(func=bdev_error_inject_error)
940
941    def bdev_nvme_apply_firmware(args):
942        print_dict(rpc.bdev.bdev_nvme_apply_firmware(args.client,
943                                                     bdev_name=args.bdev_name,
944                                                     filename=args.filename))
945
946    p = subparsers.add_parser('bdev_nvme_apply_firmware', aliases=['apply_firmware'],
947                              help='Download and commit firmware to NVMe device')
948    p.add_argument('filename', help='filename of the firmware to download')
949    p.add_argument('bdev_name', help='name of the NVMe device')
950    p.set_defaults(func=bdev_nvme_apply_firmware)
951
952    def bdev_nvme_get_transport_statistics(args):
953        print_dict(rpc.bdev.bdev_nvme_get_transport_statistics(args.client))
954
955    p = subparsers.add_parser('bdev_nvme_get_transport_statistics',
956                              help='Get bdev_nvme poll group transport statistics')
957    p.set_defaults(func=bdev_nvme_get_transport_statistics)
958
959    # iSCSI
960    def iscsi_set_options(args):
961        rpc.iscsi.iscsi_set_options(
962            args.client,
963            auth_file=args.auth_file,
964            node_base=args.node_base,
965            nop_timeout=args.nop_timeout,
966            nop_in_interval=args.nop_in_interval,
967            disable_chap=args.disable_chap,
968            require_chap=args.require_chap,
969            mutual_chap=args.mutual_chap,
970            chap_group=args.chap_group,
971            max_sessions=args.max_sessions,
972            max_queue_depth=args.max_queue_depth,
973            max_connections_per_session=args.max_connections_per_session,
974            default_time2wait=args.default_time2wait,
975            default_time2retain=args.default_time2retain,
976            first_burst_length=args.first_burst_length,
977            immediate_data=args.immediate_data,
978            error_recovery_level=args.error_recovery_level,
979            allow_duplicated_isid=args.allow_duplicated_isid,
980            max_large_datain_per_connection=args.max_large_datain_per_connection,
981            max_r2t_per_connection=args.max_r2t_per_connection,
982            pdu_pool_size=args.pdu_pool_size,
983            immediate_data_pool_size=args.immediate_data_pool_size,
984            data_out_pool_size=args.data_out_pool_size)
985
986    p = subparsers.add_parser('iscsi_set_options', aliases=['set_iscsi_options'],
987                              help="""Set options of iSCSI subsystem""")
988    p.add_argument('-f', '--auth-file', help='Path to CHAP shared secret file')
989    p.add_argument('-b', '--node-base', help='Prefix of the name of iSCSI target node')
990    p.add_argument('-o', '--nop-timeout', help='Timeout in seconds to nop-in request to the initiator', type=int)
991    p.add_argument('-n', '--nop-in-interval', help='Time interval in secs between nop-in requests by the target', type=int)
992    p.add_argument('-d', '--disable-chap', help="""CHAP for discovery session should be disabled.
993    *** Mutually exclusive with --require-chap""", action='store_true')
994    p.add_argument('-r', '--require-chap', help="""CHAP for discovery session should be required.
995    *** Mutually exclusive with --disable-chap""", action='store_true')
996    p.add_argument('-m', '--mutual-chap', help='CHAP for discovery session should be mutual', action='store_true')
997    p.add_argument('-g', '--chap-group', help="""Authentication group ID for discovery session.
998    *** Authentication group must be precreated ***""", type=int)
999    p.add_argument('-a', '--max-sessions', help='Maximum number of sessions in the host.', type=int)
1000    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/Os per queue.', type=int)
1001    p.add_argument('-c', '--max-connections-per-session', help='Negotiated parameter, MaxConnections.', type=int)
1002    p.add_argument('-w', '--default-time2wait', help='Negotiated parameter, DefaultTime2Wait.', type=int)
1003    p.add_argument('-v', '--default-time2retain', help='Negotiated parameter, DefaultTime2Retain.', type=int)
1004    p.add_argument('-s', '--first-burst-length', help='Negotiated parameter, FirstBurstLength.', type=int)
1005    p.add_argument('-i', '--immediate-data', help='Negotiated parameter, ImmediateData.', action='store_true')
1006    p.add_argument('-l', '--error-recovery-level', help='Negotiated parameter, ErrorRecoveryLevel', type=int)
1007    p.add_argument('-p', '--allow-duplicated-isid', help='Allow duplicated initiator session ID.', action='store_true')
1008    p.add_argument('-x', '--max-large-datain-per-connection', help='Max number of outstanding split read I/Os per connection', type=int)
1009    p.add_argument('-k', '--max-r2t-per-connection', help='Max number of outstanding R2Ts per connection', type=int)
1010    p.add_argument('-u', '--pdu-pool-size', help='Number of PDUs in the pool', type=int)
1011    p.add_argument('-j', '--immediate_data-pool-size', help='Number of immediate data buffers in the pool', type=int)
1012    p.add_argument('-z', '--data-out-pool-size', help='Number of data out buffers in the pool', type=int)
1013    p.set_defaults(func=iscsi_set_options)
1014
1015    def iscsi_set_discovery_auth(args):
1016        rpc.iscsi.iscsi_set_discovery_auth(
1017            args.client,
1018            disable_chap=args.disable_chap,
1019            require_chap=args.require_chap,
1020            mutual_chap=args.mutual_chap,
1021            chap_group=args.chap_group)
1022
1023    p = subparsers.add_parser('iscsi_set_discovery_auth', aliases=['set_iscsi_discovery_auth'],
1024                              help="""Set CHAP authentication for discovery session.""")
1025    p.add_argument('-d', '--disable-chap', help="""CHAP for discovery session should be disabled.
1026    *** Mutually exclusive with --require-chap""", action='store_true')
1027    p.add_argument('-r', '--require-chap', help="""CHAP for discovery session should be required.
1028    *** Mutually exclusive with --disable-chap""", action='store_true')
1029    p.add_argument('-m', '--mutual-chap', help='CHAP for discovery session should be mutual', action='store_true')
1030    p.add_argument('-g', '--chap-group', help="""Authentication group ID for discovery session.
1031    *** Authentication group must be precreated ***""", type=int)
1032    p.set_defaults(func=iscsi_set_discovery_auth)
1033
1034    def iscsi_create_auth_group(args):
1035        secrets = None
1036        if args.secrets:
1037            secrets = [dict(u.split(":") for u in a.split(" ")) for a in args.secrets.split(",")]
1038
1039        rpc.iscsi.iscsi_create_auth_group(args.client, tag=args.tag, secrets=secrets)
1040
1041    p = subparsers.add_parser('iscsi_create_auth_group', aliases=['add_iscsi_auth_group'],
1042                              help='Create authentication group for CHAP authentication.')
1043    p.add_argument('tag', help='Authentication group tag (unique, integer > 0).', type=int)
1044    p.add_argument('-c', '--secrets', help="""Comma-separated list of CHAP secrets
1045<user:user_name secret:chap_secret muser:mutual_user_name msecret:mutual_chap_secret> enclosed in quotes.
1046Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 msecret:ms2'""", required=False)
1047    p.set_defaults(func=iscsi_create_auth_group)
1048
1049    def iscsi_delete_auth_group(args):
1050        rpc.iscsi.iscsi_delete_auth_group(args.client, tag=args.tag)
1051
1052    p = subparsers.add_parser('iscsi_delete_auth_group', aliases=['delete_iscsi_auth_group'],
1053                              help='Delete an authentication group.')
1054    p.add_argument('tag', help='Authentication group tag', type=int)
1055    p.set_defaults(func=iscsi_delete_auth_group)
1056
1057    def iscsi_auth_group_add_secret(args):
1058        rpc.iscsi.iscsi_auth_group_add_secret(
1059            args.client,
1060            tag=args.tag,
1061            user=args.user,
1062            secret=args.secret,
1063            muser=args.muser,
1064            msecret=args.msecret)
1065
1066    p = subparsers.add_parser('iscsi_auth_group_add_secret', aliases=['add_secret_to_iscsi_auth_group'],
1067                              help='Add a secret to an authentication group.')
1068    p.add_argument('tag', help='Authentication group tag', type=int)
1069    p.add_argument('-u', '--user', help='User name for one-way CHAP authentication', required=True)
1070    p.add_argument('-s', '--secret', help='Secret for one-way CHAP authentication', required=True)
1071    p.add_argument('-m', '--muser', help='User name for mutual CHAP authentication')
1072    p.add_argument('-r', '--msecret', help='Secret for mutual CHAP authentication')
1073    p.set_defaults(func=iscsi_auth_group_add_secret)
1074
1075    def iscsi_auth_group_remove_secret(args):
1076        rpc.iscsi.iscsi_auth_group_remove_secret(args.client, tag=args.tag, user=args.user)
1077
1078    p = subparsers.add_parser('iscsi_auth_group_remove_secret', aliases=['delete_secret_from_iscsi_auth_group'],
1079                              help='Remove a secret from an authentication group.')
1080    p.add_argument('tag', help='Authentication group tag', type=int)
1081    p.add_argument('-u', '--user', help='User name for one-way CHAP authentication', required=True)
1082    p.set_defaults(func=iscsi_auth_group_remove_secret)
1083
1084    def iscsi_get_auth_groups(args):
1085        print_dict(rpc.iscsi.iscsi_get_auth_groups(args.client))
1086
1087    p = subparsers.add_parser('iscsi_get_auth_groups', aliases=['get_iscsi_auth_groups'],
1088                              help='Display current authentication group configuration')
1089    p.set_defaults(func=iscsi_get_auth_groups)
1090
1091    def iscsi_get_portal_groups(args):
1092        print_dict(rpc.iscsi.iscsi_get_portal_groups(args.client))
1093
1094    p = subparsers.add_parser(
1095        'iscsi_get_portal_groups', aliases=['get_portal_groups'],
1096        help='Display current portal group configuration')
1097    p.set_defaults(func=iscsi_get_portal_groups)
1098
1099    def iscsi_get_initiator_groups(args):
1100        print_dict(rpc.iscsi.iscsi_get_initiator_groups(args.client))
1101
1102    p = subparsers.add_parser('iscsi_get_initiator_groups',
1103                              aliases=['get_initiator_groups'],
1104                              help='Display current initiator group configuration')
1105    p.set_defaults(func=iscsi_get_initiator_groups)
1106
1107    def iscsi_get_target_nodes(args):
1108        print_dict(rpc.iscsi.iscsi_get_target_nodes(args.client))
1109
1110    p = subparsers.add_parser('iscsi_get_target_nodes', aliases=['get_target_nodes'],
1111                              help='Display target nodes')
1112    p.set_defaults(func=iscsi_get_target_nodes)
1113
1114    def iscsi_create_target_node(args):
1115        luns = []
1116        for u in args.bdev_name_id_pairs.strip().split(" "):
1117            bdev_name, lun_id = u.split(":")
1118            luns.append({"bdev_name": bdev_name, "lun_id": int(lun_id)})
1119
1120        pg_ig_maps = []
1121        for u in args.pg_ig_mappings.strip().split(" "):
1122            pg, ig = u.split(":")
1123            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
1124
1125        rpc.iscsi.iscsi_create_target_node(
1126            args.client,
1127            luns=luns,
1128            pg_ig_maps=pg_ig_maps,
1129            name=args.name,
1130            alias_name=args.alias_name,
1131            queue_depth=args.queue_depth,
1132            chap_group=args.chap_group,
1133            disable_chap=args.disable_chap,
1134            require_chap=args.require_chap,
1135            mutual_chap=args.mutual_chap,
1136            header_digest=args.header_digest,
1137            data_digest=args.data_digest)
1138
1139    p = subparsers.add_parser('iscsi_create_target_node', aliases=['construct_target_node'],
1140                              help='Add a target node')
1141    p.add_argument('name', help='Target node name (ASCII)')
1142    p.add_argument('alias_name', help='Target node alias name (ASCII)')
1143    p.add_argument('bdev_name_id_pairs', help="""Whitespace-separated list of <bdev name:LUN ID> pairs enclosed
1144    in quotes.  Format:  'bdev_name0:id0 bdev_name1:id1' etc
1145    Example: 'Malloc0:0 Malloc1:1 Malloc5:2'
1146    *** The bdevs must pre-exist ***
1147    *** LUN0 (id = 0) is required ***
1148    *** bdevs names cannot contain space or colon characters ***""")
1149    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
1150    Whitespace separated, quoted, mapping defined with colon
1151    separated list of "tags" (int > 0)
1152    Example: '1:1 2:2 2:1'
1153    *** The Portal/Initiator Groups must be precreated ***""")
1154    p.add_argument('queue_depth', help='Desired target queue depth', type=int)
1155    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
1156    *** Authentication group must be precreated ***""", type=int, default=0)
1157    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
1158    *** Mutually exclusive with --require-chap ***""", action='store_true')
1159    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
1160    *** Mutually exclusive with --disable-chap ***""", action='store_true')
1161    p.add_argument(
1162        '-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.', action='store_true')
1163    p.add_argument('-H', '--header-digest',
1164                   help='Header Digest should be required for this target node.', action='store_true')
1165    p.add_argument('-D', '--data-digest',
1166                   help='Data Digest should be required for this target node.', action='store_true')
1167    p.set_defaults(func=iscsi_create_target_node)
1168
1169    def iscsi_target_node_add_lun(args):
1170        rpc.iscsi.iscsi_target_node_add_lun(
1171            args.client,
1172            name=args.name,
1173            bdev_name=args.bdev_name,
1174            lun_id=args.lun_id)
1175
1176    p = subparsers.add_parser('iscsi_target_node_add_lun', aliases=['target_node_add_lun'],
1177                              help='Add LUN to the target node')
1178    p.add_argument('name', help='Target node name (ASCII)')
1179    p.add_argument('bdev_name', help="""bdev name enclosed in quotes.
1180    *** bdev name cannot contain space or colon characters ***""")
1181    p.add_argument('-i', dest='lun_id', help="""LUN ID (integer >= 0)
1182    *** If LUN ID is omitted or -1, the lowest free one is assigned ***""", type=int, required=False)
1183    p.set_defaults(func=iscsi_target_node_add_lun)
1184
1185    def iscsi_target_node_set_auth(args):
1186        rpc.iscsi.iscsi_target_node_set_auth(
1187            args.client,
1188            name=args.name,
1189            chap_group=args.chap_group,
1190            disable_chap=args.disable_chap,
1191            require_chap=args.require_chap,
1192            mutual_chap=args.mutual_chap)
1193
1194    p = subparsers.add_parser('iscsi_target_node_set_auth', aliases=['set_iscsi_target_node_auth'],
1195                              help='Set CHAP authentication for the target node')
1196    p.add_argument('name', help='Target node name (ASCII)')
1197    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
1198    *** Authentication group must be precreated ***""", type=int, default=0)
1199    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
1200    *** Mutually exclusive with --require-chap ***""", action='store_true')
1201    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
1202    *** Mutually exclusive with --disable-chap ***""", action='store_true')
1203    p.add_argument('-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.',
1204                   action='store_true')
1205    p.set_defaults(func=iscsi_target_node_set_auth)
1206
1207    def iscsi_target_node_add_pg_ig_maps(args):
1208        pg_ig_maps = []
1209        for u in args.pg_ig_mappings.strip().split(" "):
1210            pg, ig = u.split(":")
1211            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
1212        rpc.iscsi.iscsi_target_node_add_pg_ig_maps(
1213            args.client,
1214            pg_ig_maps=pg_ig_maps,
1215            name=args.name)
1216
1217    p = subparsers.add_parser('iscsi_target_node_add_pg_ig_maps',
1218                              aliases=['add_pg_ig_maps'],
1219                              help='Add PG-IG maps to the target node')
1220    p.add_argument('name', help='Target node name (ASCII)')
1221    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
1222    Whitespace separated, quoted, mapping defined with colon
1223    separated list of "tags" (int > 0)
1224    Example: '1:1 2:2 2:1'
1225    *** The Portal/Initiator Groups must be precreated ***""")
1226    p.set_defaults(func=iscsi_target_node_add_pg_ig_maps)
1227
1228    def iscsi_target_node_remove_pg_ig_maps(args):
1229        pg_ig_maps = []
1230        for u in args.pg_ig_mappings.strip().split(" "):
1231            pg, ig = u.split(":")
1232            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
1233        rpc.iscsi.iscsi_target_node_remove_pg_ig_maps(
1234            args.client, pg_ig_maps=pg_ig_maps, name=args.name)
1235
1236    p = subparsers.add_parser('iscsi_target_node_remove_pg_ig_maps',
1237                              aliases=['delete_pg_ig_maps'],
1238                              help='Delete PG-IG maps from the target node')
1239    p.add_argument('name', help='Target node name (ASCII)')
1240    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
1241    Whitespace separated, quoted, mapping defined with colon
1242    separated list of "tags" (int > 0)
1243    Example: '1:1 2:2 2:1'
1244    *** The Portal/Initiator Groups must be precreated ***""")
1245    p.set_defaults(func=iscsi_target_node_remove_pg_ig_maps)
1246
1247    def iscsi_target_node_set_redirect(args):
1248        rpc.iscsi.iscsi_target_node_set_redirect(
1249            args.client,
1250            name=args.name,
1251            pg_tag=args.pg_tag,
1252            redirect_host=args.redirect_host,
1253            redirect_port=args.redirect_port)
1254
1255    p = subparsers.add_parser('iscsi_target_node_set_redirect',
1256                              help="""Update redirect portal of the public portal group for the target node.
1257    Omit redirect host and port to clear previously set redirect settings.""")
1258    p.add_argument('name', help='Target node name (ASCII)')
1259    p.add_argument('pg_tag', help='Portal group tag (unique, integer > 0)', type=int)
1260    p.add_argument('-a', '--redirect_host', help='Numeric IP address for redirect portal', required=False)
1261    p.add_argument('-p', '--redirect_port', help='Numeric TCP port for redirect portal', required=False)
1262    p.set_defaults(func=iscsi_target_node_set_redirect)
1263
1264    def iscsi_target_node_request_logout(args):
1265        rpc.iscsi.iscsi_target_node_request_logout(
1266            args.client,
1267            name=args.name,
1268            pg_tag=args.pg_tag)
1269
1270    p = subparsers.add_parser('iscsi_target_node_request_logout',
1271                              help="""For the target node, request connections whose portal group tag
1272    match to logout, or request all connections if portal group tag is omitted.""")
1273    p.add_argument('name', help='Target node name (ASCII)')
1274    p.add_argument('-t', '--pg-tag', help='Portal group tag (unique, integer > 0)', type=int, required=False)
1275    p.set_defaults(func=iscsi_target_node_request_logout)
1276
1277    def iscsi_create_portal_group(args):
1278        portals = []
1279        for p in args.portal_list.strip().split(' '):
1280            ip, separator, port_cpumask = p.rpartition(':')
1281            split_port_cpumask = port_cpumask.split('@')
1282            if len(split_port_cpumask) == 1:
1283                port = port_cpumask
1284                portals.append({'host': ip, 'port': port})
1285            else:
1286                port = split_port_cpumask[0]
1287                cpumask = split_port_cpumask[1]
1288                portals.append({'host': ip, 'port': port})
1289                print("WARNING: Specifying a portal group with a CPU mask is no longer supported. Ignoring it.")
1290        rpc.iscsi.iscsi_create_portal_group(
1291            args.client,
1292            portals=portals,
1293            tag=args.tag,
1294            private=args.private,
1295            wait=args.wait)
1296
1297    p = subparsers.add_parser('iscsi_create_portal_group', aliases=['add_portal_group'],
1298                              help='Add a portal group')
1299    p.add_argument(
1300        'tag', help='Portal group tag (unique, integer > 0)', type=int)
1301    p.add_argument('portal_list', help="""List of portals in host:port format, separated by whitespace
1302    Example: '192.168.100.100:3260 192.168.100.100:3261 192.168.100.100:3262""")
1303    p.add_argument('-p', '--private', help="""Public (false) or private (true) portal group.
1304    Private portal groups do not have their portals returned by a discovery session. A public
1305    portal group may optionally specify a redirect portal for non-discovery logins. This redirect
1306    portal must be from a private portal group.""", action='store_true')
1307    p.add_argument('-w', '--wait', help="""Do not listening on portals until it is started explicitly.
1308    One major iSCSI initiator may not retry login once it failed. Hence for such initiator, listening
1309    on portals should be allowed after all associated target nodes are created.""", action='store_true')
1310    p.set_defaults(func=iscsi_create_portal_group)
1311
1312    def iscsi_start_portal_group(args):
1313        rpc.iscsi.iscsi_start_portal_group(args.client, tag=args.tag)
1314
1315    p = subparsers.add_parser('iscsi_start_portal_group',
1316                              help='Start listening on portals if it is not started yet.')
1317    p.add_argument(
1318        'tag', help='Portal group tag (unique, integer > 0)', type=int)
1319    p.set_defaults(func=iscsi_start_portal_group)
1320
1321    def iscsi_create_initiator_group(args):
1322        initiators = []
1323        netmasks = []
1324        for i in args.initiator_list.strip().split(' '):
1325            initiators.append(i)
1326        for n in args.netmask_list.strip().split(' '):
1327            netmasks.append(n)
1328        rpc.iscsi.iscsi_create_initiator_group(
1329            args.client,
1330            tag=args.tag,
1331            initiators=initiators,
1332            netmasks=netmasks)
1333
1334    p = subparsers.add_parser('iscsi_create_initiator_group', aliases=['add_initiator_group'],
1335                              help='Add an initiator group')
1336    p.add_argument(
1337        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
1338    p.add_argument('initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
1339    enclosed in quotes.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""")
1340    p.add_argument('netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
1341    Example: '255.255.0.0 255.248.0.0' etc""")
1342    p.set_defaults(func=iscsi_create_initiator_group)
1343
1344    def iscsi_initiator_group_add_initiators(args):
1345        initiators = None
1346        netmasks = None
1347        if args.initiator_list:
1348            initiators = []
1349            for i in args.initiator_list.strip().split(' '):
1350                initiators.append(i)
1351        if args.netmask_list:
1352            netmasks = []
1353            for n in args.netmask_list.strip().split(' '):
1354                netmasks.append(n)
1355        rpc.iscsi.iscsi_initiator_group_add_initiators(
1356            args.client,
1357            tag=args.tag,
1358            initiators=initiators,
1359            netmasks=netmasks)
1360
1361    p = subparsers.add_parser('iscsi_initiator_group_add_initiators',
1362                              aliases=['add_initiators_to_initiator_group'],
1363                              help='Add initiators to an existing initiator group')
1364    p.add_argument(
1365        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
1366    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
1367    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
1368    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
1369    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
1370    p.set_defaults(func=iscsi_initiator_group_add_initiators)
1371
1372    def iscsi_initiator_group_remove_initiators(args):
1373        initiators = None
1374        netmasks = None
1375        if args.initiator_list:
1376            initiators = []
1377            for i in args.initiator_list.strip().split(' '):
1378                initiators.append(i)
1379        if args.netmask_list:
1380            netmasks = []
1381            for n in args.netmask_list.strip().split(' '):
1382                netmasks.append(n)
1383        rpc.iscsi.iscsi_initiator_group_remove_initiators(
1384            args.client,
1385            tag=args.tag,
1386            initiators=initiators,
1387            netmasks=netmasks)
1388
1389    p = subparsers.add_parser('iscsi_initiator_group_remove_initiators',
1390                              aliases=['delete_initiators_from_initiator_group'],
1391                              help='Delete initiators from an existing initiator group')
1392    p.add_argument(
1393        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
1394    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
1395    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
1396    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
1397    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
1398    p.set_defaults(func=iscsi_initiator_group_remove_initiators)
1399
1400    def iscsi_delete_target_node(args):
1401        rpc.iscsi.iscsi_delete_target_node(
1402            args.client, target_node_name=args.target_node_name)
1403
1404    p = subparsers.add_parser('iscsi_delete_target_node', aliases=['delete_target_node'],
1405                              help='Delete a target node')
1406    p.add_argument('target_node_name',
1407                   help='Target node name to be deleted. Example: iqn.2016-06.io.spdk:disk1.')
1408    p.set_defaults(func=iscsi_delete_target_node)
1409
1410    def iscsi_delete_portal_group(args):
1411        rpc.iscsi.iscsi_delete_portal_group(args.client, tag=args.tag)
1412
1413    p = subparsers.add_parser('iscsi_delete_portal_group',
1414                              aliases=['delete_portal_group'],
1415                              help='Delete a portal group')
1416    p.add_argument(
1417        'tag', help='Portal group tag (unique, integer > 0)', type=int)
1418    p.set_defaults(func=iscsi_delete_portal_group)
1419
1420    def iscsi_delete_initiator_group(args):
1421        rpc.iscsi.iscsi_delete_initiator_group(args.client, tag=args.tag)
1422
1423    p = subparsers.add_parser('iscsi_delete_initiator_group',
1424                              aliases=['delete_initiator_group'],
1425                              help='Delete an initiator group')
1426    p.add_argument(
1427        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
1428    p.set_defaults(func=iscsi_delete_initiator_group)
1429
1430    def iscsi_portal_group_set_auth(args):
1431        rpc.iscsi.iscsi_portal_group_set_auth(
1432            args.client,
1433            tag=args.tag,
1434            chap_group=args.chap_group,
1435            disable_chap=args.disable_chap,
1436            require_chap=args.require_chap,
1437            mutual_chap=args.mutual_chap)
1438
1439    p = subparsers.add_parser('iscsi_portal_group_set_auth',
1440                              help='Set CHAP authentication for discovery sessions specific for the portal group')
1441    p.add_argument('tag', help='Portal group tag (unique, integer > 0)', type=int)
1442    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this portal group.
1443    *** Authentication group must be precreated ***""", type=int, default=0)
1444    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this portal group.
1445    *** Mutually exclusive with --require-chap ***""", action='store_true')
1446    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this portal group.
1447    *** Mutually exclusive with --disable-chap ***""", action='store_true')
1448    p.add_argument('-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.',
1449                   action='store_true')
1450    p.set_defaults(func=iscsi_portal_group_set_auth)
1451
1452    def iscsi_get_connections(args):
1453        print_dict(rpc.iscsi.iscsi_get_connections(args.client))
1454
1455    p = subparsers.add_parser('iscsi_get_connections', aliases=['get_iscsi_connections'],
1456                              help='Display iSCSI connections')
1457    p.set_defaults(func=iscsi_get_connections)
1458
1459    def iscsi_get_options(args):
1460        print_dict(rpc.iscsi.iscsi_get_options(args.client))
1461
1462    p = subparsers.add_parser('iscsi_get_options', aliases=['get_iscsi_global_params'],
1463                              help='Display iSCSI global parameters')
1464    p.set_defaults(func=iscsi_get_options)
1465
1466    def scsi_get_devices(args):
1467        print_dict(rpc.iscsi.scsi_get_devices(args.client))
1468
1469    p = subparsers.add_parser('scsi_get_devices', aliases=['get_scsi_devices'],
1470                              help='Display SCSI devices')
1471    p.set_defaults(func=scsi_get_devices)
1472
1473    # trace
1474    def trace_enable_tpoint_group(args):
1475        rpc.trace.trace_enable_tpoint_group(args.client, name=args.name)
1476
1477    p = subparsers.add_parser('trace_enable_tpoint_group', aliases=['enable_tpoint_group'],
1478                              help='enable trace on a specific tpoint group')
1479    p.add_argument(
1480        'name', help="""trace group name we want to enable in tpoint_group_mask.
1481        (for example "bdev" for bdev trace group, "all" for all trace groups).""")
1482    p.set_defaults(func=trace_enable_tpoint_group)
1483
1484    def trace_disable_tpoint_group(args):
1485        rpc.trace.trace_disable_tpoint_group(args.client, name=args.name)
1486
1487    p = subparsers.add_parser('trace_disable_tpoint_group', aliases=['disable_tpoint_group'],
1488                              help='disable trace on a specific tpoint group')
1489    p.add_argument(
1490        'name', help="""trace group name we want to disable in tpoint_group_mask.
1491        (for example "bdev" for bdev trace group, "all" for all trace groups).""")
1492    p.set_defaults(func=trace_disable_tpoint_group)
1493
1494    def trace_get_tpoint_group_mask(args):
1495        print_dict(rpc.trace.trace_get_tpoint_group_mask(args.client))
1496
1497    p = subparsers.add_parser('trace_get_tpoint_group_mask', aliases=['get_tpoint_group_mask'],
1498                              help='get trace point group mask')
1499    p.set_defaults(func=trace_get_tpoint_group_mask)
1500
1501    # log
1502    def log_set_flag(args):
1503        rpc.log.log_set_flag(args.client, flag=args.flag)
1504
1505    p = subparsers.add_parser('log_set_flag', help='set log flag', aliases=['set_log_flag'])
1506    p.add_argument(
1507        'flag', help='log flag we want to set. (for example "nvme").')
1508    p.set_defaults(func=log_set_flag)
1509
1510    def log_clear_flag(args):
1511        rpc.log.log_clear_flag(args.client, flag=args.flag)
1512
1513    p = subparsers.add_parser('log_clear_flag', help='clear log flag', aliases=['clear_log_flag'])
1514    p.add_argument(
1515        'flag', help='log flag we want to clear. (for example "nvme").')
1516    p.set_defaults(func=log_clear_flag)
1517
1518    def log_get_flags(args):
1519        print_dict(rpc.log.log_get_flags(args.client))
1520
1521    p = subparsers.add_parser('log_get_flags', help='get log flags', aliases=['get_log_flags'])
1522    p.set_defaults(func=log_get_flags)
1523
1524    def log_set_level(args):
1525        rpc.log.log_set_level(args.client, level=args.level)
1526
1527    p = subparsers.add_parser('log_set_level', aliases=['set_log_level'],
1528                              help='set log level')
1529    p.add_argument('level', help='log level we want to set. (for example "DEBUG").')
1530    p.set_defaults(func=log_set_level)
1531
1532    def log_get_level(args):
1533        print_dict(rpc.log.log_get_level(args.client))
1534
1535    p = subparsers.add_parser('log_get_level', aliases=['get_log_level'],
1536                              help='get log level')
1537    p.set_defaults(func=log_get_level)
1538
1539    def log_set_print_level(args):
1540        rpc.log.log_set_print_level(args.client, level=args.level)
1541
1542    p = subparsers.add_parser('log_set_print_level', aliases=['set_log_print_level'],
1543                              help='set log print level')
1544    p.add_argument('level', help='log print level we want to set. (for example "DEBUG").')
1545    p.set_defaults(func=log_set_print_level)
1546
1547    def log_get_print_level(args):
1548        print_dict(rpc.log.log_get_print_level(args.client))
1549
1550    p = subparsers.add_parser('log_get_print_level', aliases=['get_log_print_level'],
1551                              help='get log print level')
1552    p.set_defaults(func=log_get_print_level)
1553
1554    # lvol
1555    def bdev_lvol_create_lvstore(args):
1556        print_json(rpc.lvol.bdev_lvol_create_lvstore(args.client,
1557                                                     bdev_name=args.bdev_name,
1558                                                     lvs_name=args.lvs_name,
1559                                                     cluster_sz=args.cluster_sz,
1560                                                     clear_method=args.clear_method))
1561
1562    p = subparsers.add_parser('bdev_lvol_create_lvstore', aliases=['construct_lvol_store'],
1563                              help='Add logical volume store on base bdev')
1564    p.add_argument('bdev_name', help='base bdev name')
1565    p.add_argument('lvs_name', help='name for lvol store')
1566    p.add_argument('-c', '--cluster-sz', help='size of cluster (in bytes)', type=int, required=False)
1567    p.add_argument('--clear-method', help="""Change clear method for data region.
1568        Available: none, unmap, write_zeroes""", required=False)
1569    p.set_defaults(func=bdev_lvol_create_lvstore)
1570
1571    def bdev_lvol_rename_lvstore(args):
1572        rpc.lvol.bdev_lvol_rename_lvstore(args.client,
1573                                          old_name=args.old_name,
1574                                          new_name=args.new_name)
1575
1576    p = subparsers.add_parser('bdev_lvol_rename_lvstore', aliases=['rename_lvol_store'],
1577                              help='Change logical volume store name')
1578    p.add_argument('old_name', help='old name')
1579    p.add_argument('new_name', help='new name')
1580    p.set_defaults(func=bdev_lvol_rename_lvstore)
1581
1582    def bdev_lvol_create(args):
1583        print_json(rpc.lvol.bdev_lvol_create(args.client,
1584                                             lvol_name=args.lvol_name,
1585                                             size=args.size * 1024 * 1024,
1586                                             thin_provision=args.thin_provision,
1587                                             clear_method=args.clear_method,
1588                                             uuid=args.uuid,
1589                                             lvs_name=args.lvs_name))
1590
1591    p = subparsers.add_parser('bdev_lvol_create', aliases=['construct_lvol_bdev'],
1592                              help='Add a bdev with an logical volume backend')
1593    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1594    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1595    p.add_argument('-t', '--thin-provision', action='store_true', help='create lvol bdev as thin provisioned')
1596    p.add_argument('-c', '--clear-method', help="""Change default data clusters clear method.
1597        Available: none, unmap, write_zeroes""", required=False)
1598    p.add_argument('lvol_name', help='name for this lvol')
1599    p.add_argument('size', help='size in MiB for this bdev', type=int)
1600    p.set_defaults(func=bdev_lvol_create)
1601
1602    def bdev_lvol_snapshot(args):
1603        print_json(rpc.lvol.bdev_lvol_snapshot(args.client,
1604                                               lvol_name=args.lvol_name,
1605                                               snapshot_name=args.snapshot_name))
1606
1607    p = subparsers.add_parser('bdev_lvol_snapshot', aliases=['snapshot_lvol_bdev'],
1608                              help='Create a snapshot of an lvol bdev')
1609    p.add_argument('lvol_name', help='lvol bdev name')
1610    p.add_argument('snapshot_name', help='lvol snapshot name')
1611    p.set_defaults(func=bdev_lvol_snapshot)
1612
1613    def bdev_lvol_clone(args):
1614        print_json(rpc.lvol.bdev_lvol_clone(args.client,
1615                                            snapshot_name=args.snapshot_name,
1616                                            clone_name=args.clone_name))
1617
1618    p = subparsers.add_parser('bdev_lvol_clone', aliases=['clone_lvol_bdev'],
1619                              help='Create a clone of an lvol snapshot')
1620    p.add_argument('snapshot_name', help='lvol snapshot name')
1621    p.add_argument('clone_name', help='lvol clone name')
1622    p.set_defaults(func=bdev_lvol_clone)
1623
1624    def bdev_lvol_rename(args):
1625        rpc.lvol.bdev_lvol_rename(args.client,
1626                                  old_name=args.old_name,
1627                                  new_name=args.new_name)
1628
1629    p = subparsers.add_parser('bdev_lvol_rename', aliases=['rename_lvol_bdev'],
1630                              help='Change lvol bdev name')
1631    p.add_argument('old_name', help='lvol bdev name')
1632    p.add_argument('new_name', help='new lvol name')
1633    p.set_defaults(func=bdev_lvol_rename)
1634
1635    def bdev_lvol_inflate(args):
1636        rpc.lvol.bdev_lvol_inflate(args.client,
1637                                   name=args.name)
1638
1639    p = subparsers.add_parser('bdev_lvol_inflate', aliases=['inflate_lvol_bdev'],
1640                              help='Make thin provisioned lvol a thick provisioned lvol')
1641    p.add_argument('name', help='lvol bdev name')
1642    p.set_defaults(func=bdev_lvol_inflate)
1643
1644    def bdev_lvol_decouple_parent(args):
1645        rpc.lvol.bdev_lvol_decouple_parent(args.client,
1646                                           name=args.name)
1647
1648    p = subparsers.add_parser('bdev_lvol_decouple_parent', aliases=['decouple_parent_lvol_bdev'],
1649                              help='Decouple parent of lvol')
1650    p.add_argument('name', help='lvol bdev name')
1651    p.set_defaults(func=bdev_lvol_decouple_parent)
1652
1653    def bdev_lvol_resize(args):
1654        rpc.lvol.bdev_lvol_resize(args.client,
1655                                  name=args.name,
1656                                  size=args.size * 1024 * 1024)
1657
1658    p = subparsers.add_parser('bdev_lvol_resize', aliases=['resize_lvol_bdev'],
1659                              help='Resize existing lvol bdev')
1660    p.add_argument('name', help='lvol bdev name')
1661    p.add_argument('size', help='new size in MiB for this bdev', type=int)
1662    p.set_defaults(func=bdev_lvol_resize)
1663
1664    def bdev_lvol_set_read_only(args):
1665        rpc.lvol.bdev_lvol_set_read_only(args.client,
1666                                         name=args.name)
1667
1668    p = subparsers.add_parser('bdev_lvol_set_read_only', aliases=['set_read_only_lvol_bdev'],
1669                              help='Mark lvol bdev as read only')
1670    p.add_argument('name', help='lvol bdev name')
1671    p.set_defaults(func=bdev_lvol_set_read_only)
1672
1673    def bdev_lvol_delete(args):
1674        rpc.lvol.bdev_lvol_delete(args.client,
1675                                  name=args.name)
1676
1677    p = subparsers.add_parser('bdev_lvol_delete', aliases=['destroy_lvol_bdev'],
1678                              help='Destroy a logical volume')
1679    p.add_argument('name', help='lvol bdev name')
1680    p.set_defaults(func=bdev_lvol_delete)
1681
1682    def bdev_lvol_delete_lvstore(args):
1683        rpc.lvol.bdev_lvol_delete_lvstore(args.client,
1684                                          uuid=args.uuid,
1685                                          lvs_name=args.lvs_name)
1686
1687    p = subparsers.add_parser('bdev_lvol_delete_lvstore', aliases=['destroy_lvol_store'],
1688                              help='Destroy an logical volume store')
1689    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1690    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1691    p.set_defaults(func=bdev_lvol_delete_lvstore)
1692
1693    def bdev_lvol_get_lvstores(args):
1694        print_dict(rpc.lvol.bdev_lvol_get_lvstores(args.client,
1695                                                   uuid=args.uuid,
1696                                                   lvs_name=args.lvs_name))
1697
1698    p = subparsers.add_parser('bdev_lvol_get_lvstores', aliases=['get_lvol_stores'],
1699                              help='Display current logical volume store list')
1700    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1701    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1702    p.set_defaults(func=bdev_lvol_get_lvstores)
1703
1704    def bdev_raid_get_bdevs(args):
1705        print_array(rpc.bdev.bdev_raid_get_bdevs(args.client,
1706                                                 category=args.category))
1707
1708    p = subparsers.add_parser('bdev_raid_get_bdevs', aliases=['get_raid_bdevs'],
1709                              help="""This is used to list all the raid bdev names based on the input category
1710    requested. Category should be one of 'all', 'online', 'configuring' or 'offline'. 'all' means all the raid bdevs whether
1711    they are online or configuring or offline. 'online' is the raid bdev which is registered with bdev layer. 'configuring'
1712    is the raid bdev which does not have full configuration discovered yet. 'offline' is the raid bdev which is not registered
1713    with bdev as of now and it has encountered any error or user has requested to offline the raid bdev""")
1714    p.add_argument('category', help='all or online or configuring or offline')
1715    p.set_defaults(func=bdev_raid_get_bdevs)
1716
1717    def bdev_raid_create(args):
1718        base_bdevs = []
1719        for u in args.base_bdevs.strip().split(" "):
1720            base_bdevs.append(u)
1721
1722        rpc.bdev.bdev_raid_create(args.client,
1723                                  name=args.name,
1724                                  strip_size_kb=args.strip_size_kb,
1725                                  raid_level=args.raid_level,
1726                                  base_bdevs=base_bdevs)
1727    p = subparsers.add_parser('bdev_raid_create', aliases=['construct_raid_bdev'],
1728                              help='Create new raid bdev')
1729    p.add_argument('-n', '--name', help='raid bdev name', required=True)
1730    p.add_argument('-z', '--strip-size-kb', help='strip size in KB', type=int)
1731    p.add_argument('-r', '--raid-level', help='raid level, only raid level 0 is supported', required=True)
1732    p.add_argument('-b', '--base-bdevs', help='base bdevs name, whitespace separated list in quotes', required=True)
1733    p.set_defaults(func=bdev_raid_create)
1734
1735    def bdev_raid_delete(args):
1736        rpc.bdev.bdev_raid_delete(args.client,
1737                                  name=args.name)
1738    p = subparsers.add_parser('bdev_raid_delete', aliases=['destroy_raid_bdev'],
1739                              help='Delete existing raid bdev')
1740    p.add_argument('name', help='raid bdev name')
1741    p.set_defaults(func=bdev_raid_delete)
1742
1743    # split
1744    def bdev_split_create(args):
1745        print_array(rpc.bdev.bdev_split_create(args.client,
1746                                               base_bdev=args.base_bdev,
1747                                               split_count=args.split_count,
1748                                               split_size_mb=args.split_size_mb))
1749
1750    p = subparsers.add_parser('bdev_split_create', aliases=['construct_split_vbdev'],
1751                              help="""Add given disk name to split config. If bdev with base_name
1752    name exist the split bdevs will be created right away, if not split bdevs will be created when base bdev became
1753    available (during examination process).""")
1754    p.add_argument('base_bdev', help='base bdev name')
1755    p.add_argument('-s', '--split-size-mb', help='size in MiB for each bdev', type=int, default=0)
1756    p.add_argument('split_count', help="""Optional - number of split bdevs to create. Total size * split_count must not
1757    exceed the base bdev size.""", type=int)
1758    p.set_defaults(func=bdev_split_create)
1759
1760    def bdev_split_delete(args):
1761        rpc.bdev.bdev_split_delete(args.client,
1762                                   base_bdev=args.base_bdev)
1763
1764    p = subparsers.add_parser('bdev_split_delete', aliases=['destruct_split_vbdev'],
1765                              help="""Delete split config with all created splits.""")
1766    p.add_argument('base_bdev', help='base bdev name')
1767    p.set_defaults(func=bdev_split_delete)
1768
1769    # ftl
1770    ftl_valid_limits = ('crit', 'high', 'low', 'start')
1771
1772    def bdev_ftl_create(args):
1773        def parse_limits(limits, arg_dict, key_suffix=''):
1774            for limit in limits.split(','):
1775                key, value = limit.split(':', 1)
1776                if key in ftl_valid_limits:
1777                    arg_dict['limit_' + key + key_suffix] = int(value)
1778                else:
1779                    raise ValueError('Limit {} is not supported'.format(key))
1780
1781        arg_limits = {}
1782        if args.limit_threshold:
1783            parse_limits(args.limit_threshold, arg_limits, '_threshold')
1784
1785        if args.limit:
1786            parse_limits(args.limit, arg_limits)
1787
1788        print_dict(rpc.bdev.bdev_ftl_create(args.client,
1789                                            name=args.name,
1790                                            base_bdev=args.base_bdev,
1791                                            uuid=args.uuid,
1792                                            cache=args.cache,
1793                                            allow_open_bands=args.allow_open_bands,
1794                                            overprovisioning=args.overprovisioning,
1795                                            l2p_path=args.l2p_path,
1796                                            use_append=args.use_append,
1797                                            **arg_limits))
1798
1799    p = subparsers.add_parser('bdev_ftl_create', aliases=['construct_ftl_bdev'], help='Add FTL bdev')
1800    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
1801    p.add_argument('-d', '--base_bdev', help='Name of zoned bdev used as underlying device',
1802                   required=True)
1803    p.add_argument('-u', '--uuid', help='UUID of restored bdev (not applicable when creating new '
1804                   'instance): e.g. b286d19a-0059-4709-abcd-9f7732b1567d (optional)')
1805    p.add_argument('-c', '--cache', help='Name of the bdev to be used as a write buffer cache (optional)')
1806    p.add_argument('-o', '--allow_open_bands', help='Restoring after dirty shutdown without cache will'
1807                   ' result in partial data recovery, instead of error', action='store_true')
1808    p.add_argument('--overprovisioning', help='Percentage of device used for relocation, not exposed'
1809                   ' to user (optional)', type=int)
1810    p.add_argument('--l2p_path', help='Path to persistent memory file or device to store l2p onto, '
1811                                      'by default l2p is kept in DRAM and is volatile (optional)')
1812    p.add_argument('--use_append', help='Use appends instead of writes', action='store_true')
1813
1814    limits = p.add_argument_group('Defrag limits', 'Configures defrag limits and thresholds for'
1815                                  ' levels ' + str(ftl_valid_limits)[1:-1])
1816    limits.add_argument('--limit', help='Percentage of allowed user versus internal writes at given'
1817                        ' levels, e.g. crit:0,high:20,low:80')
1818    limits.add_argument('--limit-threshold', help='Number of free bands triggering a given level of'
1819                        ' write limiting e.g. crit:1,high:2,low:3,start:4')
1820    p.set_defaults(func=bdev_ftl_create)
1821
1822    def bdev_ftl_delete(args):
1823        print_dict(rpc.bdev.bdev_ftl_delete(args.client, name=args.name))
1824
1825    p = subparsers.add_parser('bdev_ftl_delete', aliases=['delete_ftl_bdev'],
1826                              help='Delete FTL bdev')
1827    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
1828    p.set_defaults(func=bdev_ftl_delete)
1829
1830    # vmd
1831    def enable_vmd(args):
1832        print_dict(rpc.vmd.enable_vmd(args.client))
1833
1834    p = subparsers.add_parser('enable_vmd', help='Enable VMD enumeration')
1835    p.set_defaults(func=enable_vmd)
1836
1837    # nbd
1838    def nbd_start_disk(args):
1839        print(rpc.nbd.nbd_start_disk(args.client,
1840                                     bdev_name=args.bdev_name,
1841                                     nbd_device=args.nbd_device))
1842
1843    p = subparsers.add_parser('nbd_start_disk', aliases=['start_nbd_disk'],
1844                              help='Export a bdev as an nbd disk')
1845    p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.')
1846    p.add_argument('nbd_device', help='Nbd device name to be assigned. Example: /dev/nbd0.', nargs='?')
1847    p.set_defaults(func=nbd_start_disk)
1848
1849    def nbd_stop_disk(args):
1850        rpc.nbd.nbd_stop_disk(args.client,
1851                              nbd_device=args.nbd_device)
1852
1853    p = subparsers.add_parser('nbd_stop_disk', aliases=['stop_nbd_disk'],
1854                              help='Stop an nbd disk')
1855    p.add_argument('nbd_device', help='Nbd device name to be stopped. Example: /dev/nbd0.')
1856    p.set_defaults(func=nbd_stop_disk)
1857
1858    def nbd_get_disks(args):
1859        print_dict(rpc.nbd.nbd_get_disks(args.client,
1860                                         nbd_device=args.nbd_device))
1861
1862    p = subparsers.add_parser('nbd_get_disks', aliases=['get_nbd_disks'],
1863                              help='Display full or specified nbd device list')
1864    p.add_argument('-n', '--nbd-device', help="Path of the nbd device. Example: /dev/nbd0", required=False)
1865    p.set_defaults(func=nbd_get_disks)
1866
1867    # NVMe-oF
1868    def nvmf_set_max_subsystems(args):
1869        rpc.nvmf.nvmf_set_max_subsystems(args.client,
1870                                         max_subsystems=args.max_subsystems)
1871
1872    p = subparsers.add_parser('nvmf_set_max_subsystems', aliases=['set_nvmf_target_max_subsystems'],
1873                              help='Set the maximum number of NVMf target subsystems')
1874    p.add_argument('-x', '--max-subsystems', help='Max number of NVMf subsystems', type=int, required=True)
1875    p.set_defaults(func=nvmf_set_max_subsystems)
1876
1877    def nvmf_set_config(args):
1878        rpc.nvmf.nvmf_set_config(args.client,
1879                                 acceptor_poll_rate=args.acceptor_poll_rate,
1880                                 conn_sched=args.conn_sched,
1881                                 passthru_identify_ctrlr=args.passthru_identify_ctrlr)
1882
1883    p = subparsers.add_parser('nvmf_set_config', aliases=['set_nvmf_target_config'],
1884                              help='Set NVMf target config')
1885    p.add_argument('-r', '--acceptor-poll-rate', help='Polling interval of the acceptor for incoming connections (usec)', type=int)
1886    p.add_argument('-s', '--conn-sched', help='(Deprecated). Ignored.')
1887    p.add_argument('-i', '--passthru-identify-ctrlr', help="""Passthrough fields like serial number and model number
1888    when the controller has a single namespace that is an NVMe bdev""", action='store_true')
1889    p.set_defaults(func=nvmf_set_config)
1890
1891    def nvmf_create_transport(args):
1892        rpc.nvmf.nvmf_create_transport(**vars(args))
1893
1894    p = subparsers.add_parser('nvmf_create_transport', help='Create NVMf transport')
1895    p.add_argument('-t', '--trtype', help='Transport type (ex. RDMA)', type=str, required=True)
1896    p.add_argument('-g', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
1897    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/O per queue', type=int)
1898    p.add_argument('-p', '--max-qpairs-per-ctrlr', help="""Max number of SQ and CQ per controller.
1899    Deprecated, use max-io-qpairs-per-ctrlr""", type=int)
1900    p.add_argument('-m', '--max-io-qpairs-per-ctrlr', help='Max number of IO qpairs per controller', type=int)
1901    p.add_argument('-c', '--in-capsule-data-size', help='Max number of in-capsule data size', type=int)
1902    p.add_argument('-i', '--max-io-size', help='Max I/O size (bytes)', type=int)
1903    p.add_argument('-u', '--io-unit-size', help='I/O unit size (bytes)', type=int)
1904    p.add_argument('-a', '--max-aq-depth', help='Max number of admin cmds per AQ', type=int)
1905    p.add_argument('-n', '--num-shared-buffers', help='The number of pooled data buffers available to the transport', type=int)
1906    p.add_argument('-b', '--buf-cache-size', help='The number of shared buffers to reserve for each poll group', type=int)
1907    p.add_argument('-d', '--num_cqe', help="""The number of CQ entires. Only used when no_srq=true.
1908    Relevant only for RDMA transport""", type=int)
1909    p.add_argument('-s', '--max-srq-depth', help='Max number of outstanding I/O per SRQ. Relevant only for RDMA transport', type=int)
1910    p.add_argument('-r', '--no-srq', action='store_true', help='Disable per-thread shared receive queue. Relevant only for RDMA transport')
1911    p.add_argument('-o', '--c2h-success', action='store_false', help='Disable C2H success optimization. Relevant only for TCP transport')
1912    p.add_argument('-f', '--dif-insert-or-strip', action='store_true', help='Enable DIF insert/strip. Relevant only for TCP transport')
1913    p.add_argument('-y', '--sock-priority', help='The sock priority of the tcp connection. Relevant only for TCP transport', type=int)
1914    p.add_argument('-l', '--acceptor_backlog', help='Pending connections allowed at one time. Relevant only for RDMA transport', type=int)
1915    p.add_argument('-x', '--abort-timeout-sec', help='Abort execution timeout value, in seconds', type=int)
1916    p.add_argument('-w', '--no-wr-batching', action='store_true', help='Disable work requests batching. Relevant only for RDMA transport')
1917    p.add_argument('-e', '--control_msg_num', help="""The number of control messages per poll group.
1918    Relevant only for TCP transport""", type=int)
1919    p.set_defaults(func=nvmf_create_transport)
1920
1921    def nvmf_get_transports(args):
1922        print_dict(rpc.nvmf.nvmf_get_transports(args.client, tgt_name=args.tgt_name))
1923
1924    p = subparsers.add_parser('nvmf_get_transports', aliases=['get_nvmf_transports'],
1925                              help='Display nvmf transports')
1926    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
1927    p.set_defaults(func=nvmf_get_transports)
1928
1929    def nvmf_get_subsystems(args):
1930        print_dict(rpc.nvmf.nvmf_get_subsystems(args.client, tgt_name=args.tgt_name))
1931
1932    p = subparsers.add_parser('nvmf_get_subsystems', aliases=['get_nvmf_subsystems'],
1933                              help='Display nvmf subsystems')
1934    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
1935    p.set_defaults(func=nvmf_get_subsystems)
1936
1937    def nvmf_create_subsystem(args):
1938        rpc.nvmf.nvmf_create_subsystem(args.client,
1939                                       nqn=args.nqn,
1940                                       tgt_name=args.tgt_name,
1941                                       serial_number=args.serial_number,
1942                                       model_number=args.model_number,
1943                                       allow_any_host=args.allow_any_host,
1944                                       max_namespaces=args.max_namespaces,
1945                                       ana_reporting=args.ana_reporting,
1946                                       min_cntlid=args.min_cntlid,
1947                                       max_cntlid=args.max_cntlid)
1948
1949    p = subparsers.add_parser('nvmf_create_subsystem', aliases=['nvmf_subsystem_create'],
1950                              help='Create an NVMe-oF subsystem')
1951    p.add_argument('nqn', help='Subsystem NQN (ASCII)')
1952    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
1953    p.add_argument("-s", "--serial-number", help="""
1954    Format:  'sn' etc
1955    Example: 'SPDK00000000000001'""", default='00000000000000000000')
1956    p.add_argument("-d", "--model-number", help="""
1957    Format:  'mn' etc
1958    Example: 'SPDK Controller'""", default='SPDK bdev Controller')
1959    p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce allowed host NQN list)")
1960    p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed",
1961                   type=int, default=0)
1962    p.add_argument("-r", "--ana-reporting", action='store_true', help="Enable ANA reporting feature")
1963    p.add_argument("-i", "--min_cntlid", help="Minimum controller ID", type=int, default=1)
1964    p.add_argument("-I", "--max_cntlid", help="Maximum controller ID", type=int, default=0xffef)
1965    p.set_defaults(func=nvmf_create_subsystem)
1966
1967    def nvmf_delete_subsystem(args):
1968        rpc.nvmf.nvmf_delete_subsystem(args.client,
1969                                       nqn=args.subsystem_nqn,
1970                                       tgt_name=args.tgt_name)
1971
1972    p = subparsers.add_parser('nvmf_delete_subsystem', aliases=['delete_nvmf_subsystem'],
1973                              help='Delete a nvmf subsystem')
1974    p.add_argument('subsystem_nqn',
1975                   help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.')
1976    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
1977    p.set_defaults(func=nvmf_delete_subsystem)
1978
1979    def nvmf_subsystem_add_listener(args):
1980        rpc.nvmf.nvmf_subsystem_add_listener(**vars(args))
1981
1982    p = subparsers.add_parser('nvmf_subsystem_add_listener', help='Add a listener to an NVMe-oF subsystem')
1983    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1984    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
1985    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1986    p.add_argument('-p', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
1987    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1988    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number (required for RDMA or TCP)')
1989    p.set_defaults(func=nvmf_subsystem_add_listener)
1990
1991    def nvmf_subsystem_remove_listener(args):
1992        rpc.nvmf.nvmf_subsystem_remove_listener(args.client,
1993                                                nqn=args.nqn,
1994                                                trtype=args.trtype,
1995                                                traddr=args.traddr,
1996                                                tgt_name=args.tgt_name,
1997                                                adrfam=args.adrfam,
1998                                                trsvcid=args.trsvcid)
1999
2000    p = subparsers.add_parser('nvmf_subsystem_remove_listener', help='Remove a listener from an NVMe-oF subsystem')
2001    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2002    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
2003    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
2004    p.add_argument('-p', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2005    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
2006    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
2007    p.set_defaults(func=nvmf_subsystem_remove_listener)
2008
2009    def nvmf_subsystem_listener_set_ana_state(args):
2010        rpc.nvmf.nvmf_subsystem_listener_set_ana_state(args.client,
2011                                                       nqn=args.nqn,
2012                                                       ana_state=args.ana_state,
2013                                                       trtype=args.trtype,
2014                                                       traddr=args.traddr,
2015                                                       tgt_name=args.tgt_name,
2016                                                       adrfam=args.adrfam,
2017                                                       trsvcid=args.trsvcid)
2018
2019    p = subparsers.add_parser('nvmf_subsystem_listener_set_ana_state', help='Set ANA state of a listener for an NVMe-oF subsystem')
2020    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2021    p.add_argument('-n', '--ana-state', help='ANA state to set: optimized, non-optimized, or inaccessible', required=True)
2022    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
2023    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
2024    p.add_argument('-p', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2025    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
2026    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
2027    p.set_defaults(func=nvmf_subsystem_listener_set_ana_state)
2028
2029    def nvmf_subsystem_add_ns(args):
2030        rpc.nvmf.nvmf_subsystem_add_ns(args.client,
2031                                       nqn=args.nqn,
2032                                       bdev_name=args.bdev_name,
2033                                       tgt_name=args.tgt_name,
2034                                       ptpl_file=args.ptpl_file,
2035                                       nsid=args.nsid,
2036                                       nguid=args.nguid,
2037                                       eui64=args.eui64,
2038                                       uuid=args.uuid)
2039
2040    p = subparsers.add_parser('nvmf_subsystem_add_ns', help='Add a namespace to an NVMe-oF subsystem')
2041    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2042    p.add_argument('bdev_name', help='The name of the bdev that will back this namespace')
2043    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2044    p.add_argument('-p', '--ptpl-file', help='The persistent reservation storage location (optional)', type=str)
2045    p.add_argument('-n', '--nsid', help='The requested NSID (optional)', type=int)
2046    p.add_argument('-g', '--nguid', help='Namespace globally unique identifier (optional)')
2047    p.add_argument('-e', '--eui64', help='Namespace EUI-64 identifier (optional)')
2048    p.add_argument('-u', '--uuid', help='Namespace UUID (optional)')
2049    p.set_defaults(func=nvmf_subsystem_add_ns)
2050
2051    def nvmf_subsystem_remove_ns(args):
2052        rpc.nvmf.nvmf_subsystem_remove_ns(args.client,
2053                                          nqn=args.nqn,
2054                                          nsid=args.nsid,
2055                                          tgt_name=args.tgt_name)
2056
2057    p = subparsers.add_parser('nvmf_subsystem_remove_ns', help='Remove a namespace to an NVMe-oF subsystem')
2058    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2059    p.add_argument('nsid', help='The requested NSID', type=int)
2060    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2061    p.set_defaults(func=nvmf_subsystem_remove_ns)
2062
2063    def nvmf_subsystem_add_host(args):
2064        rpc.nvmf.nvmf_subsystem_add_host(args.client,
2065                                         nqn=args.nqn,
2066                                         host=args.host,
2067                                         tgt_name=args.tgt_name)
2068
2069    p = subparsers.add_parser('nvmf_subsystem_add_host', help='Add a host to an NVMe-oF subsystem')
2070    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2071    p.add_argument('host', help='Host NQN to allow')
2072    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2073    p.set_defaults(func=nvmf_subsystem_add_host)
2074
2075    def nvmf_subsystem_remove_host(args):
2076        rpc.nvmf.nvmf_subsystem_remove_host(args.client,
2077                                            nqn=args.nqn,
2078                                            host=args.host,
2079                                            tgt_name=args.tgt_name)
2080
2081    p = subparsers.add_parser('nvmf_subsystem_remove_host', help='Remove a host from an NVMe-oF subsystem')
2082    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2083    p.add_argument('host', help='Host NQN to remove')
2084    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2085    p.set_defaults(func=nvmf_subsystem_remove_host)
2086
2087    def nvmf_subsystem_allow_any_host(args):
2088        rpc.nvmf.nvmf_subsystem_allow_any_host(args.client,
2089                                               nqn=args.nqn,
2090                                               disable=args.disable,
2091                                               tgt_name=args.tgt_name)
2092
2093    p = subparsers.add_parser('nvmf_subsystem_allow_any_host', help='Allow any host to connect to the subsystem')
2094    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2095    p.add_argument('-e', '--enable', action='store_true', help='Enable allowing any host')
2096    p.add_argument('-d', '--disable', action='store_true', help='Disable allowing any host')
2097    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2098    p.set_defaults(func=nvmf_subsystem_allow_any_host)
2099
2100    def nvmf_subsystem_get_controllers(args):
2101        print_dict(rpc.nvmf.nvmf_subsystem_get_controllers(args.client,
2102                                                           nqn=args.nqn,
2103                                                           tgt_name=args.tgt_name))
2104
2105    p = subparsers.add_parser('nvmf_subsystem_get_controllers',
2106                              help='Display controllers of an NVMe-oF subsystem.')
2107    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2108    p.add_argument('-t', '--tgt-name', help='The name of the parent NVMe-oF target (optional)', type=str)
2109    p.set_defaults(func=nvmf_subsystem_get_controllers)
2110
2111    def nvmf_subsystem_get_qpairs(args):
2112        print_dict(rpc.nvmf.nvmf_subsystem_get_qpairs(args.client,
2113                                                      nqn=args.nqn,
2114                                                      tgt_name=args.tgt_name))
2115
2116    p = subparsers.add_parser('nvmf_subsystem_get_qpairs',
2117                              help='Display queue pairs of an NVMe-oF subsystem.')
2118    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2119    p.add_argument('-t', '--tgt-name', help='The name of the parent NVMe-oF target (optional)', type=str)
2120    p.set_defaults(func=nvmf_subsystem_get_qpairs)
2121
2122    def nvmf_subsystem_get_listeners(args):
2123        print_dict(rpc.nvmf.nvmf_subsystem_get_listeners(args.client,
2124                                                         nqn=args.nqn,
2125                                                         tgt_name=args.tgt_name))
2126
2127    p = subparsers.add_parser('nvmf_subsystem_get_listeners',
2128                              help='Display listeners of an NVMe-oF subsystem.')
2129    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
2130    p.add_argument('-t', '--tgt-name', help='The name of the parent NVMe-oF target (optional)', type=str)
2131    p.set_defaults(func=nvmf_subsystem_get_listeners)
2132
2133    def nvmf_get_stats(args):
2134        print_dict(rpc.nvmf.nvmf_get_stats(args.client, tgt_name=args.tgt_name))
2135
2136    p = subparsers.add_parser(
2137        'nvmf_get_stats', help='Display current statistics for NVMf subsystem')
2138    p.add_argument('-t', '--tgt_name', help='The name of the parent NVMe-oF target (optional)', type=str)
2139    p.set_defaults(func=nvmf_get_stats)
2140
2141    def nvmf_set_crdt(args):
2142        print_dict(rpc.nvmf.nvmf_set_crdt(args.client, args.crdt1, args.crdt2, args.crdt3))
2143
2144    p = subparsers.add_parser(
2145        'nvmf_set_crdt',
2146        help="""Set the 3 crdt (Command Retry Delay Time) values for NVMf subsystem. All
2147        values are in units of 100 milliseconds (same as the NVM Express specification).""")
2148    p.add_argument('-t1', '--crdt1', help='Command Retry Delay Time 1, in units of 100 milliseconds', type=int)
2149    p.add_argument('-t2', '--crdt2', help='Command Retry Delay Time 2, in units of 100 milliseconds', type=int)
2150    p.add_argument('-t3', '--crdt3', help='Command Retry Delay Time 3, in units of 100 milliseconds', type=int)
2151    p.set_defaults(func=nvmf_set_crdt)
2152
2153    # pmem
2154    def bdev_pmem_create_pool(args):
2155        num_blocks = int((args.total_size * 1024 * 1024) / args.block_size)
2156        rpc.pmem.bdev_pmem_create_pool(args.client,
2157                                       pmem_file=args.pmem_file,
2158                                       num_blocks=num_blocks,
2159                                       block_size=args.block_size)
2160
2161    p = subparsers.add_parser('bdev_pmem_create_pool', aliases=['create_pmem_pool'],
2162                              help='Create pmem pool')
2163    p.add_argument('pmem_file', help='Path to pmemblk pool file')
2164    p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
2165    p.add_argument('block_size', help='Block size for this pmem pool', type=int)
2166    p.set_defaults(func=bdev_pmem_create_pool)
2167
2168    def bdev_pmem_get_pool_info(args):
2169        print_dict(rpc.pmem.bdev_pmem_get_pool_info(args.client,
2170                                                    pmem_file=args.pmem_file))
2171
2172    p = subparsers.add_parser('bdev_pmem_get_pool_info', aliases=['pmem_pool_info'],
2173                              help='Display pmem pool info and check consistency')
2174    p.add_argument('pmem_file', help='Path to pmemblk pool file')
2175    p.set_defaults(func=bdev_pmem_get_pool_info)
2176
2177    def bdev_pmem_delete_pool(args):
2178        rpc.pmem.bdev_pmem_delete_pool(args.client,
2179                                       pmem_file=args.pmem_file)
2180
2181    p = subparsers.add_parser('bdev_pmem_delete_pool', aliases=['delete_pmem_pool'],
2182                              help='Delete pmem pool')
2183    p.add_argument('pmem_file', help='Path to pmemblk pool file')
2184    p.set_defaults(func=bdev_pmem_delete_pool)
2185
2186    # subsystem
2187    def framework_get_subsystems(args):
2188        print_dict(rpc.subsystem.framework_get_subsystems(args.client))
2189
2190    p = subparsers.add_parser('framework_get_subsystems', aliases=['get_subsystems'],
2191                              help="""Print subsystems array in initialization order. Each subsystem
2192    entry contain (unsorted) array of subsystems it depends on.""")
2193    p.set_defaults(func=framework_get_subsystems)
2194
2195    def framework_get_config(args):
2196        print_dict(rpc.subsystem.framework_get_config(args.client, args.name))
2197
2198    p = subparsers.add_parser('framework_get_config', aliases=['get_subsystem_config'],
2199                              help="""Print subsystem configuration""")
2200    p.add_argument('name', help='Name of subsystem to query')
2201    p.set_defaults(func=framework_get_config)
2202
2203    # vhost
2204    def vhost_controller_set_coalescing(args):
2205        rpc.vhost.vhost_controller_set_coalescing(args.client,
2206                                                  ctrlr=args.ctrlr,
2207                                                  delay_base_us=args.delay_base_us,
2208                                                  iops_threshold=args.iops_threshold)
2209
2210    p = subparsers.add_parser('vhost_controller_set_coalescing', aliases=['set_vhost_controller_coalescing'],
2211                              help='Set vhost controller coalescing')
2212    p.add_argument('ctrlr', help='controller name')
2213    p.add_argument('delay_base_us', help='Base delay time', type=int)
2214    p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int)
2215    p.set_defaults(func=vhost_controller_set_coalescing)
2216
2217    def vhost_create_scsi_controller(args):
2218        rpc.vhost.vhost_create_scsi_controller(args.client,
2219                                               ctrlr=args.ctrlr,
2220                                               cpumask=args.cpumask)
2221
2222    p = subparsers.add_parser(
2223        'vhost_create_scsi_controller', aliases=['construct_vhost_scsi_controller'],
2224        help='Add new vhost controller')
2225    p.add_argument('ctrlr', help='controller name')
2226    p.add_argument('--cpumask', help='cpu mask for this controller')
2227    p.set_defaults(func=vhost_create_scsi_controller)
2228
2229    def vhost_scsi_controller_add_target(args):
2230        print_json(rpc.vhost.vhost_scsi_controller_add_target(args.client,
2231                                                              ctrlr=args.ctrlr,
2232                                                              scsi_target_num=args.scsi_target_num,
2233                                                              bdev_name=args.bdev_name))
2234
2235    p = subparsers.add_parser('vhost_scsi_controller_add_target',
2236                              aliases=['add_vhost_scsi_lun'],
2237                              help='Add lun to vhost controller')
2238    p.add_argument('ctrlr', help='conntroller name where add lun')
2239    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
2240    p.add_argument('bdev_name', help='bdev name')
2241    p.set_defaults(func=vhost_scsi_controller_add_target)
2242
2243    def vhost_scsi_controller_remove_target(args):
2244        rpc.vhost.vhost_scsi_controller_remove_target(args.client,
2245                                                      ctrlr=args.ctrlr,
2246                                                      scsi_target_num=args.scsi_target_num)
2247
2248    p = subparsers.add_parser('vhost_scsi_controller_remove_target',
2249                              aliases=['remove_vhost_scsi_target'],
2250                              help='Remove target from vhost controller')
2251    p.add_argument('ctrlr', help='controller name to remove target from')
2252    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
2253    p.set_defaults(func=vhost_scsi_controller_remove_target)
2254
2255    def vhost_create_blk_controller(args):
2256        rpc.vhost.vhost_create_blk_controller(args.client,
2257                                              ctrlr=args.ctrlr,
2258                                              dev_name=args.dev_name,
2259                                              cpumask=args.cpumask,
2260                                              readonly=args.readonly,
2261                                              packed_ring=args.packed_ring,
2262                                              packed_ring_recovery=args.packed_ring_recovery)
2263
2264    p = subparsers.add_parser('vhost_create_blk_controller',
2265                              aliases=['construct_vhost_blk_controller'],
2266                              help='Add a new vhost block controller')
2267    p.add_argument('ctrlr', help='controller name')
2268    p.add_argument('dev_name', help='device name')
2269    p.add_argument('--cpumask', help='cpu mask for this controller')
2270    p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only')
2271    p.add_argument("-p", "--packed_ring", action='store_true', help='Set controller as packed ring supported')
2272    p.add_argument("-l", "--packed_ring_recovery", action='store_true', help='Enable packed ring live reocvery')
2273    p.set_defaults(func=vhost_create_blk_controller)
2274
2275    def vhost_get_controllers(args):
2276        print_dict(rpc.vhost.vhost_get_controllers(args.client, args.name))
2277
2278    p = subparsers.add_parser('vhost_get_controllers', aliases=['get_vhost_controllers'],
2279                              help='List all or specific vhost controller(s)')
2280    p.add_argument('-n', '--name', help="Name of vhost controller", required=False)
2281    p.set_defaults(func=vhost_get_controllers)
2282
2283    def vhost_delete_controller(args):
2284        rpc.vhost.vhost_delete_controller(args.client,
2285                                          ctrlr=args.ctrlr)
2286
2287    p = subparsers.add_parser('vhost_delete_controller', aliases=['remove_vhost_controller'],
2288                              help='Delete a vhost controller')
2289    p.add_argument('ctrlr', help='controller name')
2290    p.set_defaults(func=vhost_delete_controller)
2291
2292    def bdev_virtio_attach_controller(args):
2293        print_array(rpc.vhost.bdev_virtio_attach_controller(args.client,
2294                                                            name=args.name,
2295                                                            trtype=args.trtype,
2296                                                            traddr=args.traddr,
2297                                                            dev_type=args.dev_type,
2298                                                            vq_count=args.vq_count,
2299                                                            vq_size=args.vq_size))
2300
2301    p = subparsers.add_parser('bdev_virtio_attach_controller', aliases=['construct_virtio_dev'],
2302                              help="""Attach virtio controller using provided
2303    transport type and device type. This will also create bdevs for any block devices connected to the
2304    controller (for example, SCSI devices for a virtio-scsi controller).
2305    Result is array of added bdevs.""")
2306    p.add_argument('name', help="Use this name as base for new created bdevs")
2307    p.add_argument('-t', '--trtype',
2308                   help='Virtio target transport type: pci or user', required=True)
2309    p.add_argument('-a', '--traddr',
2310                   help='Transport type specific target address: e.g. UNIX domain socket path or BDF', required=True)
2311    p.add_argument('-d', '--dev-type',
2312                   help='Device type: blk or scsi', required=True)
2313    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
2314    p.add_argument('--vq-size', help='Size of each queue', type=int)
2315    p.set_defaults(func=bdev_virtio_attach_controller)
2316
2317    def bdev_virtio_scsi_get_devices(args):
2318        print_dict(rpc.vhost.bdev_virtio_scsi_get_devices(args.client))
2319
2320    p = subparsers.add_parser('bdev_virtio_scsi_get_devices', aliases=['get_virtio_scsi_devs'],
2321                              help='List all Virtio-SCSI devices.')
2322    p.set_defaults(func=bdev_virtio_scsi_get_devices)
2323
2324    def bdev_virtio_detach_controller(args):
2325        rpc.vhost.bdev_virtio_detach_controller(args.client,
2326                                                name=args.name)
2327
2328    p = subparsers.add_parser('bdev_virtio_detach_controller', aliases=['remove_virtio_bdev'],
2329                              help="""Remove a Virtio device
2330    This will delete all bdevs exposed by this device""")
2331    p.add_argument('name', help='Virtio device name. E.g. VirtioUser0')
2332    p.set_defaults(func=bdev_virtio_detach_controller)
2333
2334    def bdev_virtio_blk_set_hotplug(args):
2335        rpc.vhost.bdev_virtio_blk_set_hotplug(args.client, enable=args.enable, period_us=args.period_us)
2336
2337    p = subparsers.add_parser('bdev_virtio_blk_set_hotplug', help='Set hotplug options for bdev virtio_blk type.')
2338    p.add_argument('-d', '--disable', dest='enable', default=False, action='store_false', help="Disable hotplug (default)")
2339    p.add_argument('-e', '--enable', dest='enable', action='store_true', help="Enable hotplug")
2340    p.add_argument('-r', '--period-us',
2341                   help='How often the hotplug is processed for insert and remove events', type=int)
2342    p.set_defaults(func=bdev_virtio_blk_set_hotplug)
2343
2344    # OCSSD
2345    def bdev_ocssd_create(args):
2346        nsid = int(args.nsid) if args.nsid is not None else None
2347        print_json(rpc.bdev.bdev_ocssd_create(args.client,
2348                                              ctrlr_name=args.ctrlr_name,
2349                                              bdev_name=args.name,
2350                                              nsid=nsid))
2351
2352    p = subparsers.add_parser('bdev_ocssd_create',
2353                              help='Creates zoned bdev on specified Open Channel controller')
2354    p.add_argument('-c', '--ctrlr_name', help='Name of the OC NVMe controller', required=True)
2355    p.add_argument('-b', '--name', help='Name of the bdev to create', required=True)
2356    p.add_argument('-n', '--nsid', help='Namespace ID', required=False)
2357    p.set_defaults(func=bdev_ocssd_create)
2358
2359    def bdev_ocssd_delete(args):
2360        print_json(rpc.bdev.bdev_ocssd_delete(args.client,
2361                                              name=args.name))
2362
2363    p = subparsers.add_parser('bdev_ocssd_delete',
2364                              help='Deletes Open Channel bdev')
2365    p.add_argument('name', help='Name of the Open Channel bdev')
2366    p.set_defaults(func=bdev_ocssd_delete)
2367
2368    # ioat
2369    def ioat_scan_accel_engine(args):
2370        rpc.ioat.ioat_scan_accel_engine(args.client)
2371
2372    p = subparsers.add_parser('ioat_scan_accel_engine',
2373                              aliases=['ioat_scan_copy_engine', 'scan_ioat_copy_engine'],
2374                              help='Enable IOAT accel engine offload.')
2375    p.set_defaults(func=ioat_scan_accel_engine)
2376
2377    # idxd
2378    def idxd_scan_accel_engine(args):
2379        rpc.idxd.idxd_scan_accel_engine(args.client, config_number=args.config_number,
2380                                        config_kernel_mode=args.config_kernel_mode)
2381
2382    p = subparsers.add_parser('idxd_scan_accel_engine',
2383                              help='Set config and enable idxd accel engine offload.')
2384    p.add_argument('-c', '--config-number', help="""Pre-defined configuration number to use. See docs.""", type=int)
2385    p.add_argument('-k', '--config-kernel-mode', help='Use Kernel mode idxd',
2386                   action='store_true', dest='config_kernel_mode')
2387    p.set_defaults(func=idxd_scan_accel_engine, config_kernel_mode=None)
2388
2389    # opal
2390    def bdev_nvme_opal_init(args):
2391        rpc.nvme.bdev_nvme_opal_init(args.client,
2392                                     nvme_ctrlr_name=args.nvme_ctrlr_name,
2393                                     password=args.password)
2394
2395    p = subparsers.add_parser('bdev_nvme_opal_init', help='take ownership and activate')
2396    p.add_argument('-b', '--nvme-ctrlr-name', help='nvme ctrlr name')
2397    p.add_argument('-p', '--password', help='password for admin')
2398    p.set_defaults(func=bdev_nvme_opal_init)
2399
2400    def bdev_nvme_opal_revert(args):
2401        rpc.nvme.bdev_nvme_opal_revert(args.client,
2402                                       nvme_ctrlr_name=args.nvme_ctrlr_name,
2403                                       password=args.password)
2404    p = subparsers.add_parser('bdev_nvme_opal_revert', help='Revert to default factory settings')
2405    p.add_argument('-b', '--nvme-ctrlr-name', help='nvme ctrlr name')
2406    p.add_argument('-p', '--password', help='password')
2407    p.set_defaults(func=bdev_nvme_opal_revert)
2408
2409    def bdev_opal_create(args):
2410        print_json(rpc.bdev.bdev_opal_create(args.client,
2411                                             nvme_ctrlr_name=args.nvme_ctrlr_name,
2412                                             nsid=args.nsid,
2413                                             locking_range_id=args.locking_range_id,
2414                                             range_start=args.range_start,
2415                                             range_length=args.range_length,
2416                                             password=args.password))
2417
2418    p = subparsers.add_parser('bdev_opal_create', help="""Create opal bdev on specified NVMe controller""")
2419    p.add_argument('-b', '--nvme-ctrlr-name', help='nvme ctrlr name', required=True)
2420    p.add_argument('-n', '--nsid', help='namespace ID (only support nsid=1 for now)', type=int, required=True)
2421    p.add_argument('-i', '--locking-range-id', help='locking range id', type=int, required=True)
2422    p.add_argument('-s', '--range-start', help='locking range start LBA', type=int, required=True)
2423    p.add_argument('-l', '--range-length', help='locking range length (in blocks)', type=int, required=True)
2424    p.add_argument('-p', '--password', help='admin password', required=True)
2425    p.set_defaults(func=bdev_opal_create)
2426
2427    def bdev_opal_get_info(args):
2428        print_dict(rpc.bdev.bdev_opal_get_info(args.client,
2429                                               bdev_name=args.bdev_name,
2430                                               password=args.password))
2431
2432    p = subparsers.add_parser('bdev_opal_get_info', help='get opal locking range info for this bdev')
2433    p.add_argument('-b', '--bdev-name', help='opal bdev')
2434    p.add_argument('-p', '--password', help='password')
2435    p.set_defaults(func=bdev_opal_get_info)
2436
2437    def bdev_opal_delete(args):
2438        rpc.bdev.bdev_opal_delete(args.client,
2439                                  bdev_name=args.bdev_name,
2440                                  password=args.password)
2441
2442    p = subparsers.add_parser('bdev_opal_delete', help="""delete a virtual opal bdev""")
2443    p.add_argument('-b', '--bdev-name', help='opal virtual bdev', required=True)
2444    p.add_argument('-p', '--password', help='admin password', required=True)
2445    p.set_defaults(func=bdev_opal_delete)
2446
2447    def bdev_opal_new_user(args):
2448        rpc.bdev.bdev_opal_new_user(args.client,
2449                                    bdev_name=args.bdev_name,
2450                                    admin_password=args.admin_password,
2451                                    user_id=args.user_id,
2452                                    user_password=args.user_password)
2453
2454    p = subparsers.add_parser('bdev_opal_new_user', help="""Add a user to opal bdev who can set lock state for this bdev""")
2455    p.add_argument('-b', '--bdev-name', help='opal bdev', required=True)
2456    p.add_argument('-p', '--admin-password', help='admin password', required=True)
2457    p.add_argument('-i', '--user-id', help='ID for new user', type=int, required=True)
2458    p.add_argument('-u', '--user-password', help='password set for this user', required=True)
2459    p.set_defaults(func=bdev_opal_new_user)
2460
2461    def bdev_opal_set_lock_state(args):
2462        rpc.bdev.bdev_opal_set_lock_state(args.client,
2463                                          bdev_name=args.bdev_name,
2464                                          user_id=args.user_id,
2465                                          password=args.password,
2466                                          lock_state=args.lock_state)
2467
2468    p = subparsers.add_parser('bdev_opal_set_lock_state', help="""set lock state for an opal bdev""")
2469    p.add_argument('-b', '--bdev-name', help='opal bdev', required=True)
2470    p.add_argument('-i', '--user-id', help='ID of the user who want to set lock state, either admin or a user assigned to this bdev',
2471                   type=int, required=True)
2472    p.add_argument('-p', '--password', help='password of this user', required=True)
2473    p.add_argument('-l', '--lock-state', help='lock state to set, choose from {readwrite, readonly, rwlock}', required=True)
2474    p.set_defaults(func=bdev_opal_set_lock_state)
2475
2476    # bdev_nvme_send_cmd
2477    def bdev_nvme_send_cmd(args):
2478        print_dict(rpc.nvme.bdev_nvme_send_cmd(args.client,
2479                                               name=args.nvme_name,
2480                                               cmd_type=args.cmd_type,
2481                                               data_direction=args.data_direction,
2482                                               cmdbuf=args.cmdbuf,
2483                                               data=args.data,
2484                                               metadata=args.metadata,
2485                                               data_len=args.data_length,
2486                                               metadata_len=args.metadata_length,
2487                                               timeout_ms=args.timeout_ms))
2488
2489    p = subparsers.add_parser('bdev_nvme_send_cmd', aliases=['send_nvme_cmd'],
2490                              help='NVMe passthrough cmd.')
2491    p.add_argument('-n', '--nvme-name', help="""Name of the operating NVMe controller""")
2492    p.add_argument('-t', '--cmd-type', help="""Type of nvme cmd. Valid values are: admin, io""")
2493    p.add_argument('-r', '--data-direction', help="""Direction of data transfer. Valid values are: c2h, h2c""")
2494    p.add_argument('-c', '--cmdbuf', help="""NVMe command encoded by base64 urlsafe""")
2495    p.add_argument('-d', '--data', help="""Data transferring to controller from host, encoded by base64 urlsafe""")
2496    p.add_argument('-m', '--metadata', help="""Metadata transferring to controller from host, encoded by base64 urlsafe""")
2497    p.add_argument('-D', '--data-length', help="""Data length required to transfer from controller to host""", type=int)
2498    p.add_argument('-M', '--metadata-length', help="""Metadata length required to transfer from controller to host""", type=int)
2499    p.add_argument('-T', '--timeout-ms',
2500                   help="""Command execution timeout value, in milliseconds,  if 0, don't track timeout""", type=int, default=0)
2501    p.set_defaults(func=bdev_nvme_send_cmd)
2502
2503    # Notifications
2504    def notify_get_types(args):
2505        print_dict(rpc.notify.notify_get_types(args.client))
2506
2507    p = subparsers.add_parser('notify_get_types', aliases=['get_notification_types'],
2508                              help='List available notifications that user can subscribe to.')
2509    p.set_defaults(func=notify_get_types)
2510
2511    def notify_get_notifications(args):
2512        ret = rpc.notify.notify_get_notifications(args.client,
2513                                                  id=args.id,
2514                                                  max=args.max)
2515        print_dict(ret)
2516
2517    p = subparsers.add_parser('notify_get_notifications', aliases=['get_notifications'],
2518                              help='Get notifications')
2519    p.add_argument('-i', '--id', help="""First ID to start fetching from""", type=int)
2520    p.add_argument('-n', '--max', help="""Maximum number of notifications to return in response""", type=int)
2521    p.set_defaults(func=notify_get_notifications)
2522
2523    def thread_get_stats(args):
2524        print_dict(rpc.app.thread_get_stats(args.client))
2525
2526    p = subparsers.add_parser(
2527        'thread_get_stats', help='Display current statistics of all the threads')
2528    p.set_defaults(func=thread_get_stats)
2529
2530    def thread_set_cpumask(args):
2531        ret = rpc.app.thread_set_cpumask(args.client,
2532                                         id=args.id,
2533                                         cpumask=args.cpumask)
2534    p = subparsers.add_parser('thread_set_cpumask',
2535                              help="""set the cpumask of the thread whose ID matches to the
2536    specified value. The thread may be migrated to one of the specified CPUs.""")
2537    p.add_argument('-i', '--id', type=int, help='thread ID')
2538    p.add_argument('-m', '--cpumask', help='cpumask for this thread')
2539    p.set_defaults(func=thread_set_cpumask)
2540
2541    def log_enable_timestamps(args):
2542        ret = rpc.app.log_enable_timestamps(args.client,
2543                                            enabled=args.enabled)
2544    p = subparsers.add_parser('log_enable_timestamps',
2545                              help='Enable or disable timestamps.')
2546    p.add_argument('-d', '--disable', dest='enabled', default=False, action='store_false', help="Disable timestamps")
2547    p.add_argument('-e', '--enable', dest='enabled', action='store_true', help="Enable timestamps")
2548    p.set_defaults(func=log_enable_timestamps)
2549
2550    def thread_get_pollers(args):
2551        print_dict(rpc.app.thread_get_pollers(args.client))
2552
2553    p = subparsers.add_parser(
2554        'thread_get_pollers', help='Display current pollers of all the threads')
2555    p.set_defaults(func=thread_get_pollers)
2556
2557    def thread_get_io_channels(args):
2558        print_dict(rpc.app.thread_get_io_channels(args.client))
2559
2560    p = subparsers.add_parser(
2561        'thread_get_io_channels', help='Display current IO channels of all the threads')
2562    p.set_defaults(func=thread_get_io_channels)
2563
2564    def env_dpdk_get_mem_stats(args):
2565        print_dict(rpc.env_dpdk.env_dpdk_get_mem_stats(args.client))
2566
2567    p = subparsers.add_parser(
2568        'env_dpdk_get_mem_stats', help='write the dpdk memory stats to a file.')
2569    p.set_defaults(func=env_dpdk_get_mem_stats)
2570
2571    # blobfs
2572    def blobfs_detect(args):
2573        print(rpc.blobfs.blobfs_detect(args.client,
2574                                       bdev_name=args.bdev_name))
2575
2576    p = subparsers.add_parser('blobfs_detect', help='Detect whether a blobfs exists on bdev')
2577    p.add_argument('bdev_name', help='Blockdev name to detect blobfs. Example: Malloc0.')
2578    p.set_defaults(func=blobfs_detect)
2579
2580    def blobfs_create(args):
2581        print(rpc.blobfs.blobfs_create(args.client,
2582                                       bdev_name=args.bdev_name,
2583                                       cluster_sz=args.cluster_sz))
2584
2585    p = subparsers.add_parser('blobfs_create', help='Build a blobfs on bdev')
2586    p.add_argument('bdev_name', help='Blockdev name to build blobfs. Example: Malloc0.')
2587    p.add_argument('-c', '--cluster_sz',
2588                   help="""Size of cluster in bytes (Optional). Must be multiple of 4KB page size. Default and minimal value is 1M.""")
2589    p.set_defaults(func=blobfs_create)
2590
2591    def blobfs_mount(args):
2592        print(rpc.blobfs.blobfs_mount(args.client,
2593                                      bdev_name=args.bdev_name,
2594                                      mountpoint=args.mountpoint))
2595
2596    p = subparsers.add_parser('blobfs_mount', help='Mount a blobfs on bdev to host path by FUSE')
2597    p.add_argument('bdev_name', help='Blockdev name where the blobfs is. Example: Malloc0.')
2598    p.add_argument('mountpoint', help='Mountpoint path in host to mount blobfs. Example: /mnt/.')
2599    p.set_defaults(func=blobfs_mount)
2600
2601    def blobfs_set_cache_size(args):
2602        print(rpc.blobfs.blobfs_set_cache_size(args.client,
2603                                               size_in_mb=args.size_in_mb))
2604
2605    p = subparsers.add_parser('blobfs_set_cache_size', help='Set cache size for blobfs')
2606    p.add_argument('size_in_mb', help='Cache size for blobfs in megabytes.', type=int)
2607    p.set_defaults(func=blobfs_set_cache_size)
2608
2609    # sock
2610    def sock_impl_get_options(args):
2611        print_json(rpc.sock.sock_impl_get_options(args.client,
2612                                                  impl_name=args.impl))
2613
2614    p = subparsers.add_parser('sock_impl_get_options', help="""Get options of socket layer implementation""")
2615    p.add_argument('-i', '--impl', help='Socket implementation name, e.g. posix', required=True)
2616    p.set_defaults(func=sock_impl_get_options)
2617
2618    def sock_impl_set_options(args):
2619        rpc.sock.sock_impl_set_options(args.client,
2620                                       impl_name=args.impl,
2621                                       recv_buf_size=args.recv_buf_size,
2622                                       send_buf_size=args.send_buf_size,
2623                                       enable_recv_pipe=args.enable_recv_pipe,
2624                                       enable_zerocopy_send=args.enable_zerocopy_send,
2625                                       enable_quickack=args.enable_quickack,
2626                                       enable_placement_id=args.enable_placement_id,
2627                                       enable_zerocopy_send_server=args.enable_zerocopy_send_server,
2628                                       enable_zerocopy_send_client=args.enable_zerocopy_send_client)
2629
2630    p = subparsers.add_parser('sock_impl_set_options', help="""Set options of socket layer implementation""")
2631    p.add_argument('-i', '--impl', help='Socket implementation name, e.g. posix', required=True)
2632    p.add_argument('-r', '--recv-buf-size', help='Size of receive buffer on socket in bytes', type=int)
2633    p.add_argument('-s', '--send-buf-size', help='Size of send buffer on socket in bytes', type=int)
2634    p.add_argument('-p', '--enable-placement-id', help='Option for placement-id. 0:disable,1:incoming_napi,2:incoming_cpu', type=int)
2635    p.add_argument('--enable-recv-pipe', help='Enable receive pipe',
2636                   action='store_true', dest='enable_recv_pipe')
2637    p.add_argument('--disable-recv-pipe', help='Disable receive pipe',
2638                   action='store_false', dest='enable_recv_pipe')
2639    p.add_argument('--enable-zerocopy-send', help="""Enable zerocopy on send
2640    (Deprecated, use enable-zerocopy-send-server or enable-zerocopy-send-client)""",
2641                   action='store_true', dest='enable_zerocopy_send')
2642    p.add_argument('--disable-zerocopy-send', help="""Enable zerocopy on send
2643    (Deprecated, use disable-zerocopy-send-server or disable-zerocopy-send-client)""",
2644                   action='store_false', dest='enable_zerocopy_send')
2645    p.add_argument('--enable-quickack', help='Enable quick ACK',
2646                   action='store_true', dest='enable_quickack')
2647    p.add_argument('--disable-quickack', help='Disable quick ACK',
2648                   action='store_false', dest='enable_quickack')
2649    p.add_argument('--enable-zerocopy-send-server', help='Enable zerocopy on send for server sockets',
2650                   action='store_true', dest='enable_zerocopy_send_server')
2651    p.add_argument('--disable-zerocopy-send-server', help='Disable zerocopy on send for server sockets',
2652                   action='store_false', dest='enable_zerocopy_server_client')
2653    p.add_argument('--enable-zerocopy-send-client', help='Enable zerocopy on send for client sockets',
2654                   action='store_true', dest='enable_zerocopy_send_client')
2655    p.add_argument('--disable-zerocopy-send-client', help='Disable zerocopy on send for client sockets',
2656                   action='store_false', dest='enable_zerocopy_send_client')
2657    p.set_defaults(func=sock_impl_set_options, enable_recv_pipe=None, enable_zerocopy_send=None,
2658                   enable_quickack=None, enable_placement_id=None, enable_zerocopy_send_server=None,
2659                   enable_zerocopy_send_client=None)
2660
2661    def sock_set_default_impl(args):
2662        print_json(rpc.sock.sock_set_default_impl(args.client,
2663                                                  impl_name=args.impl))
2664
2665    p = subparsers.add_parser('sock_set_default_impl', help="""Set the default sock implementation""")
2666    p.add_argument('-i', '--impl', help='Socket implementation name, e.g. posix', required=True)
2667    p.set_defaults(func=sock_set_default_impl)
2668
2669    def check_called_name(name):
2670        if name in deprecated_aliases:
2671            print("{} is deprecated, use {} instead.".format(name, deprecated_aliases[name]), file=sys.stderr)
2672
2673    class dry_run_client:
2674        def call(self, method, params=None):
2675            print("Request:\n" + json.dumps({"method": method, "params": params}, indent=2))
2676
2677    def null_print(arg):
2678        pass
2679
2680    def call_rpc_func(args):
2681        args.func(args)
2682        check_called_name(args.called_rpc_name)
2683
2684    def execute_script(parser, client, fd):
2685        executed_rpc = ""
2686        for rpc_call in map(str.rstrip, fd):
2687            if not rpc_call.strip():
2688                continue
2689            executed_rpc = "\n".join([executed_rpc, rpc_call])
2690            args = parser.parse_args(shlex.split(rpc_call))
2691            args.client = client
2692            try:
2693                call_rpc_func(args)
2694            except JSONRPCException as ex:
2695                print("Exception:")
2696                print(executed_rpc.strip() + " <<<")
2697                print(ex.message)
2698                exit(1)
2699
2700    def load_plugin(args):
2701        # Create temporary parser, pull out the plugin parameter, load the module, and then run the real argument parser
2702        plugin_parser = argparse.ArgumentParser(add_help=False)
2703        plugin_parser.add_argument('--plugin', dest='rpc_plugin', help='Module name of plugin with additional RPC commands')
2704
2705        rpc_module = plugin_parser.parse_known_args()[0].rpc_plugin
2706        if args is not None:
2707            rpc_module = plugin_parser.parse_known_args(args)[0].rpc_plugin
2708
2709        if rpc_module is not None:
2710            try:
2711                rpc_plugin = importlib.import_module(rpc_module)
2712                try:
2713                    rpc_plugin.spdk_rpc_plugin_initialize(subparsers)
2714                except AttributeError:
2715                    print("Module %s does not contain 'spdk_rpc_plugin_initialize' function" % rpc_module)
2716            except ModuleNotFoundError:
2717                print("Module %s not found" % rpc_module)
2718
2719    load_plugin(None)
2720
2721    args = parser.parse_args()
2722
2723    if sys.stdin.isatty() and not hasattr(args, 'func'):
2724        # No arguments and no data piped through stdin
2725        parser.print_help()
2726        exit(1)
2727    if args.is_server:
2728        for input in sys.stdin:
2729            cmd = shlex.split(input)
2730            try:
2731                load_plugin(cmd)
2732                tmp_args = parser.parse_args(cmd)
2733            except SystemExit as ex:
2734                print("**STATUS=1", flush=True)
2735                continue
2736
2737            try:
2738                tmp_args.client = rpc.client.JSONRPCClient(
2739                    tmp_args.server_addr, tmp_args.port, tmp_args.timeout,
2740                    log_level=getattr(logging, tmp_args.verbose.upper()), conn_retries=tmp_args.conn_retries)
2741                call_rpc_func(tmp_args)
2742                print("**STATUS=0", flush=True)
2743            except JSONRPCException as ex:
2744                print(ex.message)
2745                print("**STATUS=1", flush=True)
2746        exit(0)
2747    elif args.dry_run:
2748        args.client = dry_run_client()
2749        print_dict = null_print
2750        print_json = null_print
2751        print_array = null_print
2752    else:
2753        try:
2754            args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.timeout,
2755                                                   log_level=getattr(logging, args.verbose.upper()),
2756                                                   conn_retries=args.conn_retries)
2757        except JSONRPCException as ex:
2758            print(ex.message)
2759            exit(1)
2760
2761    if hasattr(args, 'func'):
2762        try:
2763            call_rpc_func(args)
2764        except JSONRPCException as ex:
2765            print(ex.message)
2766            exit(1)
2767    else:
2768        execute_script(parser, args.client, sys.stdin)
2769