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