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