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