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