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