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