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