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