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