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