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