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