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