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