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