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