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