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