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