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