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