xref: /spdk/scripts/rpc.py (revision f27cbce4289b0fb89102da8a571d752efc34a17e)
1#!/usr/bin/env python3
2
3from rpc.client import print_dict, print_string, JSONRPCException
4from rpc.helpers import deprecated_aliases
5
6import logging
7import argparse
8import rpc
9import sys
10import shlex
11import json
12
13try:
14    from shlex import quote
15except ImportError:
16    from pipes import quote
17
18
19def print_array(a):
20    print(" ".join((quote(v) for v in a)))
21
22
23if __name__ == "__main__":
24    parser = argparse.ArgumentParser(
25        description='SPDK RPC command line interface')
26    parser.add_argument('-s', dest='server_addr',
27                        help='RPC domain socket path or IP address', default='/var/tmp/spdk.sock')
28    parser.add_argument('-p', dest='port',
29                        help='RPC port number (if server_addr is IP address)',
30                        default=5260, type=int)
31    parser.add_argument('-t', dest='timeout',
32                        help='Timeout as a floating point number expressed in seconds waiting for response. Default: 60.0',
33                        default=60.0, type=float)
34    parser.add_argument('-v', dest='verbose', action='store_const', const="INFO",
35                        help='Set verbose mode to INFO', default="ERROR")
36    parser.add_argument('--verbose', dest='verbose', choices=['DEBUG', 'INFO', 'ERROR'],
37                        help="""Set verbose level. """)
38    parser.add_argument('--dry_run', dest='dry_run', action='store_true', help="Display request and exit")
39    parser.set_defaults(dry_run=False)
40    subparsers = parser.add_subparsers(help='RPC methods', dest='called_rpc_name')
41
42    def start_subsystem_init(args):
43        rpc.start_subsystem_init(args.client)
44
45    p = subparsers.add_parser('start_subsystem_init', help='Start initialization of subsystems')
46    p.set_defaults(func=start_subsystem_init)
47
48    def wait_subsystem_init(args):
49        rpc.wait_subsystem_init(args.client)
50
51    p = subparsers.add_parser('wait_subsystem_init', help='Block until subsystems have been initialized')
52    p.set_defaults(func=wait_subsystem_init)
53
54    def rpc_get_methods(args):
55        print_dict(rpc.rpc_get_methods(args.client,
56                                       current=args.current))
57
58    p = subparsers.add_parser('rpc_get_methods', help='Get list of supported RPC methods', aliases=['get_rpc_methods'])
59    p.add_argument('-c', '--current', help='Get list of RPC methods only callable in the current state.', action='store_true')
60    p.set_defaults(func=rpc_get_methods)
61
62    def get_spdk_version(args):
63        print_string(rpc.get_spdk_version(args.client))
64
65    p = subparsers.add_parser('get_spdk_version', help='Get SPDK version')
66    p.set_defaults(func=get_spdk_version)
67
68    def save_config(args):
69        rpc.save_config(args.client,
70                        sys.stdout,
71                        indent=args.indent)
72
73    p = subparsers.add_parser('save_config', help="""Write current (live) configuration of SPDK subsystems and targets to stdout.
74    """)
75    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. Default indent level is 2.
76    """, type=int, default=2)
77    p.set_defaults(func=save_config)
78
79    def load_config(args):
80        rpc.load_config(args.client, sys.stdin)
81
82    p = subparsers.add_parser('load_config', help="""Configure SPDK subsystems and targets using JSON RPC read from stdin.""")
83    p.set_defaults(func=load_config)
84
85    def save_subsystem_config(args):
86        rpc.save_subsystem_config(args.client,
87                                  sys.stdout,
88                                  indent=args.indent,
89                                  name=args.name)
90
91    p = subparsers.add_parser('save_subsystem_config', help="""Write current (live) configuration of SPDK subsystem to stdout.
92    """)
93    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. Default indent level is 2.
94    """, type=int, default=2)
95    p.add_argument('-n', '--name', help='Name of subsystem', required=True)
96    p.set_defaults(func=save_subsystem_config)
97
98    def load_subsystem_config(args):
99        rpc.load_subsystem_config(args.client,
100                                  sys.stdin)
101
102    p = subparsers.add_parser('load_subsystem_config', help="""Configure SPDK subsystem using JSON RPC read from stdin.""")
103    p.set_defaults(func=load_subsystem_config)
104
105    # app
106    def kill_instance(args):
107        rpc.app.kill_instance(args.client,
108                              sig_name=args.sig_name)
109
110    p = subparsers.add_parser('kill_instance', help='Send signal to instance')
111    p.add_argument('sig_name', help='signal will be sent to server.')
112    p.set_defaults(func=kill_instance)
113
114    def context_switch_monitor(args):
115        enabled = None
116        if args.enable:
117            enabled = True
118        if args.disable:
119            enabled = False
120        print_dict(rpc.app.context_switch_monitor(args.client,
121                                                  enabled=enabled))
122
123    p = subparsers.add_parser('context_switch_monitor', help='Control whether the context switch monitor is enabled')
124    p.add_argument('-e', '--enable', action='store_true', help='Enable context switch monitoring')
125    p.add_argument('-d', '--disable', action='store_true', help='Disable context switch monitoring')
126    p.set_defaults(func=context_switch_monitor)
127
128    # bdev
129    def set_bdev_options(args):
130        rpc.bdev.set_bdev_options(args.client,
131                                  bdev_io_pool_size=args.bdev_io_pool_size,
132                                  bdev_io_cache_size=args.bdev_io_cache_size)
133
134    p = subparsers.add_parser('set_bdev_options', help="""Set options of bdev subsystem""")
135    p.add_argument('-p', '--bdev-io-pool-size', help='Number of bdev_io structures in shared buffer pool', type=int)
136    p.add_argument('-c', '--bdev-io-cache-size', help='Maximum number of bdev_io structures cached per thread', type=int)
137    p.set_defaults(func=set_bdev_options)
138
139    def construct_compress_bdev(args):
140        print_string(rpc.bdev.construct_compress_bdev(args.client,
141                                                      base_bdev_name=args.base_bdev_name,
142                                                      pm_path=args.pm_path,
143                                                      comp_pmd=args.comp_pmd))
144    p = subparsers.add_parser('construct_compress_bdev',
145                              help='Add a compress vbdev')
146    p.add_argument('-b', '--base_bdev_name', help="Name of the base bdev")
147    p.add_argument('-p', '--pm_path', help="Path to persistent memory")
148    p.add_argument('-d', '--comp_pmd', help="Name of the compression device driver")
149    p.set_defaults(func=construct_compress_bdev)
150
151    def delete_compress_bdev(args):
152        rpc.bdev.delete_compress_bdev(args.client,
153                                      name=args.name)
154
155    p = subparsers.add_parser('delete_compress_bdev', help='Delete a compress disk')
156    p.add_argument('name', help='compress bdev name')
157    p.set_defaults(func=delete_compress_bdev)
158
159    def construct_crypto_bdev(args):
160        print_string(rpc.bdev.construct_crypto_bdev(args.client,
161                                                    base_bdev_name=args.base_bdev_name,
162                                                    name=args.name,
163                                                    crypto_pmd=args.crypto_pmd,
164                                                    key=args.key))
165    p = subparsers.add_parser('construct_crypto_bdev',
166                              help='Add a crypto vbdev')
167    p.add_argument('-b', '--base_bdev_name', help="Name of the base bdev")
168    p.add_argument('-c', '--name', help="Name of the crypto vbdev")
169    p.add_argument('-d', '--crypto_pmd', help="Name of the crypto device driver")
170    p.add_argument('-k', '--key', help="Key")
171    p.set_defaults(func=construct_crypto_bdev)
172
173    def delete_crypto_bdev(args):
174        rpc.bdev.delete_crypto_bdev(args.client,
175                                    name=args.name)
176
177    p = subparsers.add_parser('delete_crypto_bdev', help='Delete a crypto disk')
178    p.add_argument('name', help='crypto bdev name')
179    p.set_defaults(func=delete_crypto_bdev)
180
181    def construct_ocf_bdev(args):
182        print_string(rpc.bdev.construct_ocf_bdev(args.client,
183                                                 name=args.name,
184                                                 mode=args.mode,
185                                                 cache_bdev_name=args.cache_bdev_name,
186                                                 core_bdev_name=args.core_bdev_name))
187    p = subparsers.add_parser('construct_ocf_bdev',
188                              help='Add an OCF block device')
189    p.add_argument('name', help='Name of resulting OCF bdev')
190    p.add_argument('mode', help='OCF cache mode', choices=['wb', 'wt', 'pt'])
191    p.add_argument('cache_bdev_name', help='Name of underlying cache bdev')
192    p.add_argument('core_bdev_name', help='Name of unerlying core bdev')
193    p.set_defaults(func=construct_ocf_bdev)
194
195    def delete_ocf_bdev(args):
196        rpc.bdev.delete_ocf_bdev(args.client,
197                                 name=args.name)
198
199    p = subparsers.add_parser('delete_ocf_bdev',
200                              help='Delete an OCF block device')
201    p.add_argument('name', help='Name of OCF bdev')
202    p.set_defaults(func=delete_ocf_bdev)
203
204    def get_ocf_stats(args):
205        print_dict(rpc.bdev.get_ocf_stats(args.client,
206                                          name=args.name))
207    p = subparsers.add_parser('get_ocf_stats',
208                              help='Get statistics of chosen OCF block device')
209    p.add_argument('name', help='Name of OCF bdev')
210    p.set_defaults(func=get_ocf_stats)
211
212    def get_ocf_bdevs(args):
213        print_dict(rpc.bdev.get_ocf_bdevs(args.client,
214                                          name=args.name))
215    p = subparsers.add_parser('get_ocf_bdevs',
216                              help='Get list of OCF devices including unregistered ones')
217    p.add_argument('name', nargs='?', default=None, help='name of OCF vbdev or name of cache device or name of core device (optional)')
218    p.set_defaults(func=get_ocf_bdevs)
219
220    def construct_malloc_bdev(args):
221        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
222        print_string(rpc.bdev.construct_malloc_bdev(args.client,
223                                                    num_blocks=int(num_blocks),
224                                                    block_size=args.block_size,
225                                                    name=args.name,
226                                                    uuid=args.uuid))
227    p = subparsers.add_parser('construct_malloc_bdev',
228                              help='Add a bdev with malloc backend')
229    p.add_argument('-b', '--name', help="Name of the bdev")
230    p.add_argument('-u', '--uuid', help="UUID of the bdev")
231    p.add_argument(
232        'total_size', help='Size of malloc bdev in MB (float > 0)', type=float)
233    p.add_argument('block_size', help='Block size for this bdev', type=int)
234    p.set_defaults(func=construct_malloc_bdev)
235
236    def delete_malloc_bdev(args):
237        rpc.bdev.delete_malloc_bdev(args.client,
238                                    name=args.name)
239
240    p = subparsers.add_parser('delete_malloc_bdev', help='Delete a malloc disk')
241    p.add_argument('name', help='malloc bdev name')
242    p.set_defaults(func=delete_malloc_bdev)
243
244    def construct_null_bdev(args):
245        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
246        print_string(rpc.bdev.construct_null_bdev(args.client,
247                                                  num_blocks=num_blocks,
248                                                  block_size=args.block_size,
249                                                  name=args.name,
250                                                  uuid=args.uuid))
251
252    p = subparsers.add_parser('construct_null_bdev',
253                              help='Add a bdev with null backend')
254    p.add_argument('name', help='Block device name')
255    p.add_argument('-u', '--uuid', help='UUID of the bdev')
256    p.add_argument(
257        'total_size', help='Size of null bdev in MB (int > 0)', type=int)
258    p.add_argument('block_size', help='Block size for this bdev', type=int)
259    p.set_defaults(func=construct_null_bdev)
260
261    def delete_null_bdev(args):
262        rpc.bdev.delete_null_bdev(args.client,
263                                  name=args.name)
264
265    p = subparsers.add_parser('delete_null_bdev', help='Delete a null bdev')
266    p.add_argument('name', help='null bdev name')
267    p.set_defaults(func=delete_null_bdev)
268
269    def construct_aio_bdev(args):
270        print_string(rpc.bdev.construct_aio_bdev(args.client,
271                                                 filename=args.filename,
272                                                 name=args.name,
273                                                 block_size=args.block_size))
274
275    p = subparsers.add_parser('construct_aio_bdev',
276                              help='Add a bdev with aio backend')
277    p.add_argument('filename', help='Path to device or file (ex: /dev/sda)')
278    p.add_argument('name', help='Block device name')
279    p.add_argument('block_size', help='Block size for this bdev', type=int, nargs='?', default=0)
280    p.set_defaults(func=construct_aio_bdev)
281
282    def delete_aio_bdev(args):
283        rpc.bdev.delete_aio_bdev(args.client,
284                                 name=args.name)
285
286    p = subparsers.add_parser('delete_aio_bdev', help='Delete an aio disk')
287    p.add_argument('name', help='aio bdev name')
288    p.set_defaults(func=delete_aio_bdev)
289
290    def set_bdev_nvme_options(args):
291        rpc.bdev.set_bdev_nvme_options(args.client,
292                                       action_on_timeout=args.action_on_timeout,
293                                       timeout_us=args.timeout_us,
294                                       retry_count=args.retry_count,
295                                       nvme_adminq_poll_period_us=args.nvme_adminq_poll_period_us,
296                                       nvme_ioq_poll_period_us=args.nvme_ioq_poll_period_us)
297
298    p = subparsers.add_parser('set_bdev_nvme_options',
299                              help='Set options for the bdev nvme type. This is startup command.')
300    p.add_argument('-a', '--action-on-timeout',
301                   help="Action to take on command time out. Valid valies are: none, reset, abort")
302    p.add_argument('-t', '--timeout-us',
303                   help="Timeout for each command, in microseconds. If 0, don't track timeouts.", type=int)
304    p.add_argument('-n', '--retry-count',
305                   help='the number of attempts per I/O when an I/O fails', type=int)
306    p.add_argument('-p', '--nvme-adminq-poll-period-us',
307                   help='How often the admin queue is polled for asynchronous events', type=int)
308    p.add_argument('-i', '--nvme-ioq-poll-period-us',
309                   help='How often to poll I/O queues for completions', type=int)
310    p.set_defaults(func=set_bdev_nvme_options)
311
312    def set_bdev_nvme_hotplug(args):
313        rpc.bdev.set_bdev_nvme_hotplug(args.client, enable=args.enable, period_us=args.period_us)
314
315    p = subparsers.add_parser('set_bdev_nvme_hotplug',
316                              help='Set hotplug options for bdev nvme type.')
317    p.add_argument('-d', '--disable', dest='enable', default=False, action='store_false', help="Disable hotplug (default)")
318    p.add_argument('-e', '--enable', dest='enable', action='store_true', help="Enable hotplug")
319    p.add_argument('-r', '--period-us',
320                   help='How often the hotplug is processed for insert and remove events', type=int)
321    p.set_defaults(func=set_bdev_nvme_hotplug)
322
323    def construct_nvme_bdev(args):
324        print_array(rpc.bdev.construct_nvme_bdev(args.client,
325                                                 name=args.name,
326                                                 trtype=args.trtype,
327                                                 traddr=args.traddr,
328                                                 adrfam=args.adrfam,
329                                                 trsvcid=args.trsvcid,
330                                                 subnqn=args.subnqn,
331                                                 hostnqn=args.hostnqn,
332                                                 hostaddr=args.hostaddr,
333                                                 hostsvcid=args.hostsvcid,
334                                                 prchk_reftag=args.prchk_reftag,
335                                                 prchk_guard=args.prchk_guard))
336
337    p = subparsers.add_parser('construct_nvme_bdev',
338                              help='Add bdevs with nvme backend')
339    p.add_argument('-b', '--name', help="Name of the NVMe controller, prefix for each bdev name", required=True)
340    p.add_argument('-t', '--trtype',
341                   help='NVMe-oF target trtype: e.g., rdma, pcie', required=True)
342    p.add_argument('-a', '--traddr',
343                   help='NVMe-oF target address: e.g., an ip address or BDF', required=True)
344    p.add_argument('-f', '--adrfam',
345                   help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
346    p.add_argument('-s', '--trsvcid',
347                   help='NVMe-oF target trsvcid: e.g., a port number')
348    p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
349    p.add_argument('-q', '--hostnqn', help='NVMe-oF host subnqn')
350    p.add_argument('-i', '--hostaddr',
351                   help='NVMe-oF host address: e.g., an ip address')
352    p.add_argument('-c', '--hostsvcid',
353                   help='NVMe-oF host svcid: e.g., a port number')
354    p.add_argument('-r', '--prchk-reftag',
355                   help='Enable checking of PI reference tag for I/O processing.', action='store_true')
356    p.add_argument('-g', '--prchk-guard',
357                   help='Enable checking of PI guard for I/O processing.', action='store_true')
358    p.set_defaults(func=construct_nvme_bdev)
359
360    def get_nvme_controllers(args):
361        print_dict(rpc.nvme.get_nvme_controllers(args.client,
362                                                 name=args.name))
363
364    p = subparsers.add_parser(
365        'get_nvme_controllers', help='Display current NVMe controllers list or required NVMe controller')
366    p.add_argument('-n', '--name', help="Name of the NVMe controller. Example: Nvme0", required=False)
367    p.set_defaults(func=get_nvme_controllers)
368
369    def delete_nvme_controller(args):
370        rpc.bdev.delete_nvme_controller(args.client,
371                                        name=args.name)
372
373    p = subparsers.add_parser('delete_nvme_controller',
374                              help='Delete a NVMe controller using controller name')
375    p.add_argument('name', help="Name of the controller")
376    p.set_defaults(func=delete_nvme_controller)
377
378    def construct_rbd_bdev(args):
379        config = None
380        if args.config:
381            config = {}
382            for entry in args.config:
383                parts = entry.split('=', 1)
384                if len(parts) != 2:
385                    raise Exception('--config %s not in key=value form' % entry)
386                config[parts[0]] = parts[1]
387        print_string(rpc.bdev.construct_rbd_bdev(args.client,
388                                                 name=args.name,
389                                                 user=args.user,
390                                                 config=config,
391                                                 pool_name=args.pool_name,
392                                                 rbd_name=args.rbd_name,
393                                                 block_size=args.block_size))
394
395    p = subparsers.add_parser('construct_rbd_bdev',
396                              help='Add a bdev with ceph rbd backend')
397    p.add_argument('-b', '--name', help="Name of the bdev", required=False)
398    p.add_argument('--user', help="Ceph user name (i.e. admin, not client.admin)", required=False)
399    p.add_argument('--config', action='append', metavar='key=value',
400                   help="adds a key=value configuration option for rados_conf_set (default: rely on config file)")
401    p.add_argument('pool_name', help='rbd pool name')
402    p.add_argument('rbd_name', help='rbd image name')
403    p.add_argument('block_size', help='rbd block size', type=int)
404    p.set_defaults(func=construct_rbd_bdev)
405
406    def delete_rbd_bdev(args):
407        rpc.bdev.delete_rbd_bdev(args.client,
408                                 name=args.name)
409
410    p = subparsers.add_parser('delete_rbd_bdev', help='Delete a rbd bdev')
411    p.add_argument('name', help='rbd bdev name')
412    p.set_defaults(func=delete_rbd_bdev)
413
414    def bdev_delay_create(args):
415        print_string(rpc.bdev.bdev_delay_create(args.client,
416                                                base_bdev_name=args.base_bdev_name,
417                                                name=args.name,
418                                                avg_read_latency=args.avg_read_latency,
419                                                p99_read_latency=args.nine_nine_read_latency,
420                                                avg_write_latency=args.avg_write_latency,
421                                                p99_write_latency=args.nine_nine_write_latency))
422
423    p = subparsers.add_parser('bdev_delay_create',
424                              help='Add a delay bdev on existing bdev')
425    p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True)
426    p.add_argument('-d', '--name', help="Name of the delay bdev", required=True)
427    p.add_argument('-r', '--avg-read-latency', help="Average latency to apply before completing read ops", required=True, type=int)
428    p.add_argument('-t', '--nine-nine-read-latency', help="latency to apply to 1 in 100 read ops", required=True, type=int)
429    p.add_argument('-w', '--avg-write-latency', help="Average latency to apply before completing write ops", required=True, type=int)
430    p.add_argument('-n', '--nine-nine-write-latency', help="latency to apply to 1 in 100 write ops", required=True, type=int)
431    p.set_defaults(func=bdev_delay_create)
432
433    def bdev_delay_delete(args):
434        rpc.bdev.bdev_delay_delete(args.client,
435                                   name=args.name)
436
437    p = subparsers.add_parser('bdev_delay_delete', help='Delete a delay bdev')
438    p.add_argument('name', help='delay bdev name')
439    p.set_defaults(func=bdev_delay_delete)
440
441    def construct_error_bdev(args):
442        print_string(rpc.bdev.construct_error_bdev(args.client,
443                                                   base_name=args.base_name))
444
445    p = subparsers.add_parser('construct_error_bdev',
446                              help='Add bdev with error injection backend')
447    p.add_argument('base_name', help='base bdev name')
448    p.set_defaults(func=construct_error_bdev)
449
450    def delete_error_bdev(args):
451        rpc.bdev.delete_error_bdev(args.client,
452                                   name=args.name)
453
454    p = subparsers.add_parser('delete_error_bdev', help='Delete an error bdev')
455    p.add_argument('name', help='error bdev name')
456    p.set_defaults(func=delete_error_bdev)
457
458    def construct_iscsi_bdev(args):
459        print_string(rpc.bdev.construct_iscsi_bdev(args.client,
460                                                   name=args.name,
461                                                   url=args.url,
462                                                   initiator_iqn=args.initiator_iqn))
463
464    p = subparsers.add_parser('construct_iscsi_bdev',
465                              help='Add bdev with iSCSI initiator backend')
466    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
467    p.add_argument('-i', '--initiator-iqn', help="Initiator IQN", required=True)
468    p.add_argument('--url', help="iSCSI Lun URL", required=True)
469    p.set_defaults(func=construct_iscsi_bdev)
470
471    def delete_iscsi_bdev(args):
472        rpc.bdev.delete_iscsi_bdev(args.client,
473                                   name=args.name)
474
475    p = subparsers.add_parser('delete_iscsi_bdev', help='Delete an iSCSI bdev')
476    p.add_argument('name', help='iSCSI bdev name')
477    p.set_defaults(func=delete_iscsi_bdev)
478
479    def construct_pmem_bdev(args):
480        print_string(rpc.bdev.construct_pmem_bdev(args.client,
481                                                  pmem_file=args.pmem_file,
482                                                  name=args.name))
483
484    p = subparsers.add_parser('construct_pmem_bdev', help='Add a bdev with pmem backend')
485    p.add_argument('pmem_file', help='Path to pmemblk pool file')
486    p.add_argument('-n', '--name', help='Block device name', required=True)
487    p.set_defaults(func=construct_pmem_bdev)
488
489    def delete_pmem_bdev(args):
490        rpc.bdev.delete_pmem_bdev(args.client,
491                                  name=args.name)
492
493    p = subparsers.add_parser('delete_pmem_bdev', help='Delete a pmem bdev')
494    p.add_argument('name', help='pmem bdev name')
495    p.set_defaults(func=delete_pmem_bdev)
496
497    def construct_passthru_bdev(args):
498        print_string(rpc.bdev.construct_passthru_bdev(args.client,
499                                                      base_bdev_name=args.base_bdev_name,
500                                                      name=args.name))
501
502    p = subparsers.add_parser('construct_passthru_bdev',
503                              help='Add a pass through bdev on existing bdev')
504    p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True)
505    p.add_argument('-p', '--name', help="Name of the pass through bdev", required=True)
506    p.set_defaults(func=construct_passthru_bdev)
507
508    def delete_passthru_bdev(args):
509        rpc.bdev.delete_passthru_bdev(args.client,
510                                      name=args.name)
511
512    p = subparsers.add_parser('delete_passthru_bdev', help='Delete a pass through bdev')
513    p.add_argument('name', help='pass through bdev name')
514    p.set_defaults(func=delete_passthru_bdev)
515
516    def get_bdevs(args):
517        print_dict(rpc.bdev.get_bdevs(args.client,
518                                      name=args.name))
519
520    p = subparsers.add_parser(
521        'get_bdevs', help='Display current blockdev list or required blockdev')
522    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
523    p.set_defaults(func=get_bdevs)
524
525    def get_bdevs_iostat(args):
526        print_dict(rpc.bdev.get_bdevs_iostat(args.client,
527                                             name=args.name))
528
529    p = subparsers.add_parser(
530        'get_bdevs_iostat', help='Display current I/O statistics of all the blockdevs or required blockdev.')
531    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
532    p.set_defaults(func=get_bdevs_iostat)
533
534    def enable_bdev_histogram(args):
535        rpc.bdev.enable_bdev_histogram(args.client, name=args.name, enable=args.enable)
536
537    p = subparsers.add_parser('enable_bdev_histogram', help='Enable or disable histogram for specified bdev')
538    p.add_argument('-e', '--enable', default=True, dest='enable', action='store_true', help='Enable histograms on specified device')
539    p.add_argument('-d', '--disable', dest='enable', action='store_false', help='Disable histograms on specified device')
540    p.add_argument('name', help='bdev name')
541    p.set_defaults(func=enable_bdev_histogram)
542
543    def get_bdev_histogram(args):
544        print_dict(rpc.bdev.get_bdev_histogram(args.client, name=args.name))
545
546    p = subparsers.add_parser('get_bdev_histogram', help='Get histogram for specified bdev')
547    p.add_argument('name', help='bdev name')
548    p.set_defaults(func=get_bdev_histogram)
549
550    def set_bdev_qd_sampling_period(args):
551        rpc.bdev.set_bdev_qd_sampling_period(args.client,
552                                             name=args.name,
553                                             period=args.period)
554
555    p = subparsers.add_parser('set_bdev_qd_sampling_period', help="Enable or disable tracking of a bdev's queue depth.")
556    p.add_argument('name', help='Blockdev name. Example: Malloc0')
557    p.add_argument('period', help='Period with which to poll the block device queue depth in microseconds.'
558                   ' If set to 0, polling will be disabled.',
559                   type=int)
560    p.set_defaults(func=set_bdev_qd_sampling_period)
561
562    def set_bdev_qos_limit(args):
563        rpc.bdev.set_bdev_qos_limit(args.client,
564                                    name=args.name,
565                                    rw_ios_per_sec=args.rw_ios_per_sec,
566                                    rw_mbytes_per_sec=args.rw_mbytes_per_sec,
567                                    r_mbytes_per_sec=args.r_mbytes_per_sec,
568                                    w_mbytes_per_sec=args.w_mbytes_per_sec)
569
570    p = subparsers.add_parser('set_bdev_qos_limit', help='Set QoS rate limit on a blockdev')
571    p.add_argument('name', help='Blockdev name to set QoS. Example: Malloc0')
572    p.add_argument('--rw_ios_per_sec',
573                   help='R/W IOs per second limit (>=10000, example: 20000). 0 means unlimited.',
574                   type=int, required=False)
575    p.add_argument('--rw_mbytes_per_sec',
576                   help="R/W megabytes per second limit (>=10, example: 100). 0 means unlimited.",
577                   type=int, required=False)
578    p.add_argument('--r_mbytes_per_sec',
579                   help="Read megabytes per second limit (>=10, example: 100). 0 means unlimited.",
580                   type=int, required=False)
581    p.add_argument('--w_mbytes_per_sec',
582                   help="Write megabytes per second limit (>=10, example: 100). 0 means unlimited.",
583                   type=int, required=False)
584    p.set_defaults(func=set_bdev_qos_limit)
585
586    def bdev_inject_error(args):
587        rpc.bdev.bdev_inject_error(args.client,
588                                   name=args.name,
589                                   io_type=args.io_type,
590                                   error_type=args.error_type,
591                                   num=args.num)
592
593    p = subparsers.add_parser('bdev_inject_error', help='bdev inject error')
594    p.add_argument('name', help="""the name of the error injection bdev""")
595    p.add_argument('io_type', help="""io_type: 'clear' 'read' 'write' 'unmap' 'flush' 'all'""")
596    p.add_argument('error_type', help="""error_type: 'failure' 'pending'""")
597    p.add_argument(
598        '-n', '--num', help='the number of commands you want to fail', type=int, default=1)
599    p.set_defaults(func=bdev_inject_error)
600
601    def apply_firmware(args):
602        print_dict(rpc.bdev.apply_firmware(args.client,
603                                           bdev_name=args.bdev_name,
604                                           filename=args.filename))
605
606    p = subparsers.add_parser('apply_firmware', help='Download and commit firmware to NVMe device')
607    p.add_argument('filename', help='filename of the firmware to download')
608    p.add_argument('bdev_name', help='name of the NVMe device')
609    p.set_defaults(func=apply_firmware)
610
611    # iSCSI
612    def set_iscsi_options(args):
613        rpc.iscsi.set_iscsi_options(
614            args.client,
615            auth_file=args.auth_file,
616            node_base=args.node_base,
617            nop_timeout=args.nop_timeout,
618            nop_in_interval=args.nop_in_interval,
619            disable_chap=args.disable_chap,
620            require_chap=args.require_chap,
621            mutual_chap=args.mutual_chap,
622            chap_group=args.chap_group,
623            max_sessions=args.max_sessions,
624            max_queue_depth=args.max_queue_depth,
625            max_connections_per_session=args.max_connections_per_session,
626            default_time2wait=args.default_time2wait,
627            default_time2retain=args.default_time2retain,
628            first_burst_length=args.first_burst_length,
629            immediate_data=args.immediate_data,
630            error_recovery_level=args.error_recovery_level,
631            allow_duplicated_isid=args.allow_duplicated_isid)
632
633    p = subparsers.add_parser('set_iscsi_options', help="""Set options of iSCSI subsystem""")
634    p.add_argument('-f', '--auth-file', help='Path to CHAP shared secret file')
635    p.add_argument('-b', '--node-base', help='Prefix of the name of iSCSI target node')
636    p.add_argument('-o', '--nop-timeout', help='Timeout in seconds to nop-in request to the initiator', type=int)
637    p.add_argument('-n', '--nop-in-interval', help='Time interval in secs between nop-in requests by the target', type=int)
638    p.add_argument('-d', '--disable-chap', help="""CHAP for discovery session should be disabled.
639    *** Mutually exclusive with --require-chap""", action='store_true')
640    p.add_argument('-r', '--require-chap', help="""CHAP for discovery session should be required.
641    *** Mutually exclusive with --disable-chap""", action='store_true')
642    p.add_argument('-m', '--mutual-chap', help='CHAP for discovery session should be mutual', action='store_true')
643    p.add_argument('-g', '--chap-group', help="""Authentication group ID for discovery session.
644    *** Authentication group must be precreated ***""", type=int)
645    p.add_argument('-a', '--max-sessions', help='Maximum number of sessions in the host.', type=int)
646    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/Os per queue.', type=int)
647    p.add_argument('-c', '--max-connections-per-session', help='Negotiated parameter, MaxConnections.', type=int)
648    p.add_argument('-w', '--default-time2wait', help='Negotiated parameter, DefaultTime2Wait.', type=int)
649    p.add_argument('-v', '--default-time2retain', help='Negotiated parameter, DefaultTime2Retain.', type=int)
650    p.add_argument('-s', '--first-burst-length', help='Negotiated parameter, FirstBurstLength.', type=int)
651    p.add_argument('-i', '--immediate-data', help='Negotiated parameter, ImmediateData.', action='store_true')
652    p.add_argument('-l', '--error-recovery-level', help='Negotiated parameter, ErrorRecoveryLevel', type=int)
653    p.add_argument('-p', '--allow-duplicated-isid', help='Allow duplicated initiator session ID.', action='store_true')
654    p.set_defaults(func=set_iscsi_options)
655
656    def set_iscsi_discovery_auth(args):
657        rpc.iscsi.set_iscsi_discovery_auth(
658            args.client,
659            disable_chap=args.disable_chap,
660            require_chap=args.require_chap,
661            mutual_chap=args.mutual_chap,
662            chap_group=args.chap_group)
663
664    p = subparsers.add_parser('set_iscsi_discovery_auth', help="""Set CHAP authentication for discovery session.""")
665    p.add_argument('-d', '--disable-chap', help="""CHAP for discovery session should be disabled.
666    *** Mutually exclusive with --require-chap""", action='store_true')
667    p.add_argument('-r', '--require-chap', help="""CHAP for discovery session should be required.
668    *** Mutually exclusive with --disable-chap""", action='store_true')
669    p.add_argument('-m', '--mutual-chap', help='CHAP for discovery session should be mutual', action='store_true')
670    p.add_argument('-g', '--chap-group', help="""Authentication group ID for discovery session.
671    *** Authentication group must be precreated ***""", type=int)
672    p.set_defaults(func=set_iscsi_discovery_auth)
673
674    def add_iscsi_auth_group(args):
675        secrets = None
676        if args.secrets:
677            secrets = [dict(u.split(":") for u in a.split(" ")) for a in args.secrets.split(",")]
678
679        rpc.iscsi.add_iscsi_auth_group(args.client, tag=args.tag, secrets=secrets)
680
681    p = subparsers.add_parser('add_iscsi_auth_group', help='Add authentication group for CHAP authentication.')
682    p.add_argument('tag', help='Authentication group tag (unique, integer > 0).', type=int)
683    p.add_argument('-c', '--secrets', help="""Comma-separated list of CHAP secrets
684<user:user_name secret:chap_secret muser:mutual_user_name msecret:mutual_chap_secret> enclosed in quotes.
685Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 msecret:ms2'""", required=False)
686    p.set_defaults(func=add_iscsi_auth_group)
687
688    def delete_iscsi_auth_group(args):
689        rpc.iscsi.delete_iscsi_auth_group(args.client, tag=args.tag)
690
691    p = subparsers.add_parser('delete_iscsi_auth_group', help='Delete an authentication group.')
692    p.add_argument('tag', help='Authentication group tag', type=int)
693    p.set_defaults(func=delete_iscsi_auth_group)
694
695    def add_secret_to_iscsi_auth_group(args):
696        rpc.iscsi.add_secret_to_iscsi_auth_group(
697            args.client,
698            tag=args.tag,
699            user=args.user,
700            secret=args.secret,
701            muser=args.muser,
702            msecret=args.msecret)
703
704    p = subparsers.add_parser('add_secret_to_iscsi_auth_group', help='Add a secret to an authentication group.')
705    p.add_argument('tag', help='Authentication group tag', type=int)
706    p.add_argument('-u', '--user', help='User name for one-way CHAP authentication', required=True)
707    p.add_argument('-s', '--secret', help='Secret for one-way CHAP authentication', required=True)
708    p.add_argument('-m', '--muser', help='User name for mutual CHAP authentication')
709    p.add_argument('-r', '--msecret', help='Secret for mutual CHAP authentication')
710    p.set_defaults(func=add_secret_to_iscsi_auth_group)
711
712    def delete_secret_from_iscsi_auth_group(args):
713        rpc.iscsi.delete_secret_from_iscsi_auth_group(args.client, tag=args.tag, user=args.user)
714
715    p = subparsers.add_parser('delete_secret_from_iscsi_auth_group', help='Delete a secret from an authentication group.')
716    p.add_argument('tag', help='Authentication group tag', type=int)
717    p.add_argument('-u', '--user', help='User name for one-way CHAP authentication', required=True)
718    p.set_defaults(func=delete_secret_from_iscsi_auth_group)
719
720    def get_iscsi_auth_groups(args):
721        print_dict(rpc.iscsi.get_iscsi_auth_groups(args.client))
722
723    p = subparsers.add_parser('get_iscsi_auth_groups',
724                              help='Display current authentication group configuration')
725    p.set_defaults(func=get_iscsi_auth_groups)
726
727    def get_portal_groups(args):
728        print_dict(rpc.iscsi.get_portal_groups(args.client))
729
730    p = subparsers.add_parser(
731        'get_portal_groups', help='Display current portal group configuration')
732    p.set_defaults(func=get_portal_groups)
733
734    def get_initiator_groups(args):
735        print_dict(rpc.iscsi.get_initiator_groups(args.client))
736
737    p = subparsers.add_parser('get_initiator_groups',
738                              help='Display current initiator group configuration')
739    p.set_defaults(func=get_initiator_groups)
740
741    def get_target_nodes(args):
742        print_dict(rpc.iscsi.get_target_nodes(args.client))
743
744    p = subparsers.add_parser('get_target_nodes', help='Display target nodes')
745    p.set_defaults(func=get_target_nodes)
746
747    def construct_target_node(args):
748        luns = []
749        for u in args.bdev_name_id_pairs.strip().split(" "):
750            bdev_name, lun_id = u.split(":")
751            luns.append({"bdev_name": bdev_name, "lun_id": int(lun_id)})
752
753        pg_ig_maps = []
754        for u in args.pg_ig_mappings.strip().split(" "):
755            pg, ig = u.split(":")
756            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
757
758        rpc.iscsi.construct_target_node(
759            args.client,
760            luns=luns,
761            pg_ig_maps=pg_ig_maps,
762            name=args.name,
763            alias_name=args.alias_name,
764            queue_depth=args.queue_depth,
765            chap_group=args.chap_group,
766            disable_chap=args.disable_chap,
767            require_chap=args.require_chap,
768            mutual_chap=args.mutual_chap,
769            header_digest=args.header_digest,
770            data_digest=args.data_digest)
771
772    p = subparsers.add_parser('construct_target_node',
773                              help='Add a target node')
774    p.add_argument('name', help='Target node name (ASCII)')
775    p.add_argument('alias_name', help='Target node alias name (ASCII)')
776    p.add_argument('bdev_name_id_pairs', help="""Whitespace-separated list of <bdev name:LUN ID> pairs enclosed
777    in quotes.  Format:  'bdev_name0:id0 bdev_name1:id1' etc
778    Example: 'Malloc0:0 Malloc1:1 Malloc5:2'
779    *** The bdevs must pre-exist ***
780    *** LUN0 (id = 0) is required ***
781    *** bdevs names cannot contain space or colon characters ***""")
782    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
783    Whitespace separated, quoted, mapping defined with colon
784    separated list of "tags" (int > 0)
785    Example: '1:1 2:2 2:1'
786    *** The Portal/Initiator Groups must be precreated ***""")
787    p.add_argument('queue_depth', help='Desired target queue depth', type=int)
788    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
789    *** Authentication group must be precreated ***""", type=int, default=0)
790    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
791    *** Mutually exclusive with --require-chap ***""", action='store_true')
792    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
793    *** Mutually exclusive with --disable-chap ***""", action='store_true')
794    p.add_argument(
795        '-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.', action='store_true')
796    p.add_argument('-H', '--header-digest',
797                   help='Header Digest should be required for this target node.', action='store_true')
798    p.add_argument('-D', '--data-digest',
799                   help='Data Digest should be required for this target node.', action='store_true')
800    p.set_defaults(func=construct_target_node)
801
802    def target_node_add_lun(args):
803        rpc.iscsi.target_node_add_lun(
804            args.client,
805            name=args.name,
806            bdev_name=args.bdev_name,
807            lun_id=args.lun_id)
808
809    p = subparsers.add_parser('target_node_add_lun', help='Add LUN to the target node')
810    p.add_argument('name', help='Target node name (ASCII)')
811    p.add_argument('bdev_name', help="""bdev name enclosed in quotes.
812    *** bdev name cannot contain space or colon characters ***""")
813    p.add_argument('-i', dest='lun_id', help="""LUN ID (integer >= 0)
814    *** If LUN ID is omitted or -1, the lowest free one is assigned ***""", type=int, required=False)
815    p.set_defaults(func=target_node_add_lun)
816
817    def set_iscsi_target_node_auth(args):
818        rpc.iscsi.set_iscsi_target_node_auth(
819            args.client,
820            name=args.name,
821            chap_group=args.chap_group,
822            disable_chap=args.disable_chap,
823            require_chap=args.require_chap,
824            mutual_chap=args.mutual_chap)
825
826    p = subparsers.add_parser('set_iscsi_target_node_auth', help='Set CHAP authentication for the target node')
827    p.add_argument('name', help='Target node name (ASCII)')
828    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
829    *** Authentication group must be precreated ***""", type=int, default=0)
830    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
831    *** Mutually exclusive with --require-chap ***""", action='store_true')
832    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
833    *** Mutually exclusive with --disable-chap ***""", action='store_true')
834    p.add_argument('-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.',
835                   action='store_true')
836    p.set_defaults(func=set_iscsi_target_node_auth)
837
838    def add_pg_ig_maps(args):
839        pg_ig_maps = []
840        for u in args.pg_ig_mappings.strip().split(" "):
841            pg, ig = u.split(":")
842            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
843        rpc.iscsi.add_pg_ig_maps(
844            args.client,
845            pg_ig_maps=pg_ig_maps,
846            name=args.name)
847
848    p = subparsers.add_parser('add_pg_ig_maps', help='Add PG-IG maps to the target node')
849    p.add_argument('name', help='Target node name (ASCII)')
850    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
851    Whitespace separated, quoted, mapping defined with colon
852    separated list of "tags" (int > 0)
853    Example: '1:1 2:2 2:1'
854    *** The Portal/Initiator Groups must be precreated ***""")
855    p.set_defaults(func=add_pg_ig_maps)
856
857    def delete_pg_ig_maps(args):
858        pg_ig_maps = []
859        for u in args.pg_ig_mappings.strip().split(" "):
860            pg, ig = u.split(":")
861            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
862        rpc.iscsi.delete_pg_ig_maps(
863            args.client, pg_ig_maps=pg_ig_maps, name=args.name)
864
865    p = subparsers.add_parser('delete_pg_ig_maps', help='Delete PG-IG maps from the target node')
866    p.add_argument('name', help='Target node name (ASCII)')
867    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
868    Whitespace separated, quoted, mapping defined with colon
869    separated list of "tags" (int > 0)
870    Example: '1:1 2:2 2:1'
871    *** The Portal/Initiator Groups must be precreated ***""")
872    p.set_defaults(func=delete_pg_ig_maps)
873
874    def add_portal_group(args):
875        portals = []
876        for p in args.portal_list.strip().split(' '):
877            ip, separator, port_cpumask = p.rpartition(':')
878            split_port_cpumask = port_cpumask.split('@')
879            if len(split_port_cpumask) == 1:
880                port = port_cpumask
881                portals.append({'host': ip, 'port': port})
882            else:
883                port = split_port_cpumask[0]
884                cpumask = split_port_cpumask[1]
885                portals.append({'host': ip, 'port': port, 'cpumask': cpumask})
886        rpc.iscsi.add_portal_group(
887            args.client,
888            portals=portals,
889            tag=args.tag)
890
891    p = subparsers.add_parser('add_portal_group', help='Add a portal group')
892    p.add_argument(
893        'tag', help='Portal group tag (unique, integer > 0)', type=int)
894    p.add_argument('portal_list', help="""List of portals in host:port@cpumask format, separated by whitespace
895    (cpumask is optional and can be skipped)
896    Example: '192.168.100.100:3260 192.168.100.100:3261 192.168.100.100:3262@0x1""")
897    p.set_defaults(func=add_portal_group)
898
899    def add_initiator_group(args):
900        initiators = []
901        netmasks = []
902        for i in args.initiator_list.strip().split(' '):
903            initiators.append(i)
904        for n in args.netmask_list.strip().split(' '):
905            netmasks.append(n)
906        rpc.iscsi.add_initiator_group(
907            args.client,
908            tag=args.tag,
909            initiators=initiators,
910            netmasks=netmasks)
911
912    p = subparsers.add_parser('add_initiator_group',
913                              help='Add an initiator group')
914    p.add_argument(
915        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
916    p.add_argument('initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
917    enclosed in quotes.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""")
918    p.add_argument('netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
919    Example: '255.255.0.0 255.248.0.0' etc""")
920    p.set_defaults(func=add_initiator_group)
921
922    def add_initiators_to_initiator_group(args):
923        initiators = None
924        netmasks = None
925        if args.initiator_list:
926            initiators = []
927            for i in args.initiator_list.strip().split(' '):
928                initiators.append(i)
929        if args.netmask_list:
930            netmasks = []
931            for n in args.netmask_list.strip().split(' '):
932                netmasks.append(n)
933        rpc.iscsi.add_initiators_to_initiator_group(
934            args.client,
935            tag=args.tag,
936            initiators=initiators,
937            netmasks=netmasks)
938
939    p = subparsers.add_parser('add_initiators_to_initiator_group',
940                              help='Add initiators to an existing initiator group')
941    p.add_argument(
942        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
943    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
944    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
945    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
946    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
947    p.set_defaults(func=add_initiators_to_initiator_group)
948
949    def delete_initiators_from_initiator_group(args):
950        initiators = None
951        netmasks = None
952        if args.initiator_list:
953            initiators = []
954            for i in args.initiator_list.strip().split(' '):
955                initiators.append(i)
956        if args.netmask_list:
957            netmasks = []
958            for n in args.netmask_list.strip().split(' '):
959                netmasks.append(n)
960        rpc.iscsi.delete_initiators_from_initiator_group(
961            args.client,
962            tag=args.tag,
963            initiators=initiators,
964            netmasks=netmasks)
965
966    p = subparsers.add_parser('delete_initiators_from_initiator_group',
967                              help='Delete initiators from an existing initiator group')
968    p.add_argument(
969        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
970    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
971    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
972    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
973    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
974    p.set_defaults(func=delete_initiators_from_initiator_group)
975
976    def delete_target_node(args):
977        rpc.iscsi.delete_target_node(
978            args.client, target_node_name=args.target_node_name)
979
980    p = subparsers.add_parser('delete_target_node',
981                              help='Delete a target node')
982    p.add_argument('target_node_name',
983                   help='Target node name to be deleted. Example: iqn.2016-06.io.spdk:disk1.')
984    p.set_defaults(func=delete_target_node)
985
986    def delete_portal_group(args):
987        rpc.iscsi.delete_portal_group(args.client, tag=args.tag)
988
989    p = subparsers.add_parser('delete_portal_group',
990                              help='Delete a portal group')
991    p.add_argument(
992        'tag', help='Portal group tag (unique, integer > 0)', type=int)
993    p.set_defaults(func=delete_portal_group)
994
995    def delete_initiator_group(args):
996        rpc.iscsi.delete_initiator_group(args.client, tag=args.tag)
997
998    p = subparsers.add_parser('delete_initiator_group',
999                              help='Delete an initiator group')
1000    p.add_argument(
1001        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
1002    p.set_defaults(func=delete_initiator_group)
1003
1004    def get_iscsi_connections(args):
1005        print_dict(rpc.iscsi.get_iscsi_connections(args.client))
1006
1007    p = subparsers.add_parser('get_iscsi_connections',
1008                              help='Display iSCSI connections')
1009    p.set_defaults(func=get_iscsi_connections)
1010
1011    def get_iscsi_global_params(args):
1012        print_dict(rpc.iscsi.get_iscsi_global_params(args.client))
1013
1014    p = subparsers.add_parser('get_iscsi_global_params', help='Display iSCSI global parameters')
1015    p.set_defaults(func=get_iscsi_global_params)
1016
1017    def get_scsi_devices(args):
1018        print_dict(rpc.iscsi.get_scsi_devices(args.client))
1019
1020    p = subparsers.add_parser('get_scsi_devices', help='Display SCSI devices')
1021    p.set_defaults(func=get_scsi_devices)
1022
1023    # trace
1024    def enable_tpoint_group(args):
1025        rpc.trace.enable_tpoint_group(args.client, name=args.name)
1026
1027    p = subparsers.add_parser('enable_tpoint_group', help='enable trace on a specific tpoint group')
1028    p.add_argument(
1029        'name', help="""trace group name we want to enable in tpoint_group_mask.
1030        (for example "bdev" for bdev trace group, "all" for all trace groups).""")
1031    p.set_defaults(func=enable_tpoint_group)
1032
1033    def disable_tpoint_group(args):
1034        rpc.trace.disable_tpoint_group(args.client, name=args.name)
1035
1036    p = subparsers.add_parser('disable_tpoint_group', help='disable trace on a specific tpoint group')
1037    p.add_argument(
1038        'name', help="""trace group name we want to disable in tpoint_group_mask.
1039        (for example "bdev" for bdev trace group, "all" for all trace groups).""")
1040    p.set_defaults(func=disable_tpoint_group)
1041
1042    def get_tpoint_group_mask(args):
1043        print_dict(rpc.trace.get_tpoint_group_mask(args.client))
1044
1045    p = subparsers.add_parser('get_tpoint_group_mask', help='get trace point group mask')
1046    p.set_defaults(func=get_tpoint_group_mask)
1047
1048    # log
1049    def set_log_flag(args):
1050        rpc.log.set_log_flag(args.client, flag=args.flag)
1051
1052    p = subparsers.add_parser('set_log_flag', help='set log flag', aliases=['set_trace_flag'])
1053    p.add_argument(
1054        'flag', help='log flag we want to set. (for example "nvme").')
1055    p.set_defaults(func=set_log_flag)
1056
1057    def clear_log_flag(args):
1058        rpc.log.clear_log_flag(args.client, flag=args.flag)
1059
1060    p = subparsers.add_parser('clear_log_flag', help='clear log flag', aliases=['clear_trace_flag'])
1061    p.add_argument(
1062        'flag', help='log flag we want to clear. (for example "nvme").')
1063    p.set_defaults(func=clear_log_flag)
1064
1065    def get_log_flags(args):
1066        print_dict(rpc.log.get_log_flags(args.client))
1067
1068    p = subparsers.add_parser('get_log_flags', help='get log flags', aliases=['get_trace_flags'])
1069    p.set_defaults(func=get_log_flags)
1070
1071    def set_log_level(args):
1072        rpc.log.set_log_level(args.client, level=args.level)
1073
1074    p = subparsers.add_parser('set_log_level', help='set log level')
1075    p.add_argument('level', help='log level we want to set. (for example "DEBUG").')
1076    p.set_defaults(func=set_log_level)
1077
1078    def get_log_level(args):
1079        print_dict(rpc.log.get_log_level(args.client))
1080
1081    p = subparsers.add_parser('get_log_level', help='get log level')
1082    p.set_defaults(func=get_log_level)
1083
1084    def set_log_print_level(args):
1085        rpc.log.set_log_print_level(args.client, level=args.level)
1086
1087    p = subparsers.add_parser('set_log_print_level', help='set log print level')
1088    p.add_argument('level', help='log print level we want to set. (for example "DEBUG").')
1089    p.set_defaults(func=set_log_print_level)
1090
1091    def get_log_print_level(args):
1092        print_dict(rpc.log.get_log_print_level(args.client))
1093
1094    p = subparsers.add_parser('get_log_print_level', help='get log print level')
1095    p.set_defaults(func=get_log_print_level)
1096
1097    # lvol
1098    def construct_lvol_store(args):
1099        print_string(rpc.lvol.construct_lvol_store(args.client,
1100                                                   bdev_name=args.bdev_name,
1101                                                   lvs_name=args.lvs_name,
1102                                                   cluster_sz=args.cluster_sz,
1103                                                   clear_method=args.clear_method))
1104
1105    p = subparsers.add_parser('construct_lvol_store', help='Add logical volume store on base bdev')
1106    p.add_argument('bdev_name', help='base bdev name')
1107    p.add_argument('lvs_name', help='name for lvol store')
1108    p.add_argument('-c', '--cluster-sz', help='size of cluster (in bytes)', type=int, required=False)
1109    p.add_argument('--clear-method', help="""Change clear method for data region.
1110        Available: none, unmap, write_zeroes""", required=False)
1111    p.set_defaults(func=construct_lvol_store)
1112
1113    def rename_lvol_store(args):
1114        rpc.lvol.rename_lvol_store(args.client,
1115                                   old_name=args.old_name,
1116                                   new_name=args.new_name)
1117
1118    p = subparsers.add_parser('rename_lvol_store', help='Change logical volume store name')
1119    p.add_argument('old_name', help='old name')
1120    p.add_argument('new_name', help='new name')
1121    p.set_defaults(func=rename_lvol_store)
1122
1123    def construct_lvol_bdev(args):
1124        print_string(rpc.lvol.construct_lvol_bdev(args.client,
1125                                                  lvol_name=args.lvol_name,
1126                                                  size=args.size * 1024 * 1024,
1127                                                  thin_provision=args.thin_provision,
1128                                                  clear_method=args.clear_method,
1129                                                  uuid=args.uuid,
1130                                                  lvs_name=args.lvs_name))
1131
1132    p = subparsers.add_parser('construct_lvol_bdev', help='Add a bdev with an logical volume backend')
1133    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1134    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1135    p.add_argument('-t', '--thin-provision', action='store_true', help='create lvol bdev as thin provisioned')
1136    p.add_argument('-c', '--clear-method', help="""Change default data clusters clear method.
1137        Available: none, unmap, write_zeroes""", required=False)
1138    p.add_argument('lvol_name', help='name for this lvol')
1139    p.add_argument('size', help='size in MiB for this bdev', type=int)
1140    p.set_defaults(func=construct_lvol_bdev)
1141
1142    def snapshot_lvol_bdev(args):
1143        print_string(rpc.lvol.snapshot_lvol_bdev(args.client,
1144                                                 lvol_name=args.lvol_name,
1145                                                 snapshot_name=args.snapshot_name))
1146
1147    p = subparsers.add_parser('snapshot_lvol_bdev', help='Create a snapshot of an lvol bdev')
1148    p.add_argument('lvol_name', help='lvol bdev name')
1149    p.add_argument('snapshot_name', help='lvol snapshot name')
1150    p.set_defaults(func=snapshot_lvol_bdev)
1151
1152    def clone_lvol_bdev(args):
1153        print_string(rpc.lvol.clone_lvol_bdev(args.client,
1154                                              snapshot_name=args.snapshot_name,
1155                                              clone_name=args.clone_name))
1156
1157    p = subparsers.add_parser('clone_lvol_bdev', help='Create a clone of an lvol snapshot')
1158    p.add_argument('snapshot_name', help='lvol snapshot name')
1159    p.add_argument('clone_name', help='lvol clone name')
1160    p.set_defaults(func=clone_lvol_bdev)
1161
1162    def rename_lvol_bdev(args):
1163        rpc.lvol.rename_lvol_bdev(args.client,
1164                                  old_name=args.old_name,
1165                                  new_name=args.new_name)
1166
1167    p = subparsers.add_parser('rename_lvol_bdev', help='Change lvol bdev name')
1168    p.add_argument('old_name', help='lvol bdev name')
1169    p.add_argument('new_name', help='new lvol name')
1170    p.set_defaults(func=rename_lvol_bdev)
1171
1172    def inflate_lvol_bdev(args):
1173        rpc.lvol.inflate_lvol_bdev(args.client,
1174                                   name=args.name)
1175
1176    p = subparsers.add_parser('inflate_lvol_bdev', help='Make thin provisioned lvol a thick provisioned lvol')
1177    p.add_argument('name', help='lvol bdev name')
1178    p.set_defaults(func=inflate_lvol_bdev)
1179
1180    def decouple_parent_lvol_bdev(args):
1181        rpc.lvol.decouple_parent_lvol_bdev(args.client,
1182                                           name=args.name)
1183
1184    p = subparsers.add_parser('decouple_parent_lvol_bdev', help='Decouple parent of lvol')
1185    p.add_argument('name', help='lvol bdev name')
1186    p.set_defaults(func=decouple_parent_lvol_bdev)
1187
1188    def resize_lvol_bdev(args):
1189        rpc.lvol.resize_lvol_bdev(args.client,
1190                                  name=args.name,
1191                                  size=args.size * 1024 * 1024)
1192
1193    p = subparsers.add_parser('resize_lvol_bdev', help='Resize existing lvol bdev')
1194    p.add_argument('name', help='lvol bdev name')
1195    p.add_argument('size', help='new size in MiB for this bdev', type=int)
1196    p.set_defaults(func=resize_lvol_bdev)
1197
1198    def set_read_only_lvol_bdev(args):
1199        rpc.lvol.set_read_only_lvol_bdev(args.client,
1200                                         name=args.name)
1201
1202    p = subparsers.add_parser('set_read_only_lvol_bdev', help='Mark lvol bdev as read only')
1203    p.add_argument('name', help='lvol bdev name')
1204    p.set_defaults(func=set_read_only_lvol_bdev)
1205
1206    def destroy_lvol_bdev(args):
1207        rpc.lvol.destroy_lvol_bdev(args.client,
1208                                   name=args.name)
1209
1210    p = subparsers.add_parser('destroy_lvol_bdev', help='Destroy a logical volume')
1211    p.add_argument('name', help='lvol bdev name')
1212    p.set_defaults(func=destroy_lvol_bdev)
1213
1214    def destroy_lvol_store(args):
1215        rpc.lvol.destroy_lvol_store(args.client,
1216                                    uuid=args.uuid,
1217                                    lvs_name=args.lvs_name)
1218
1219    p = subparsers.add_parser('destroy_lvol_store', help='Destroy an logical volume store')
1220    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1221    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1222    p.set_defaults(func=destroy_lvol_store)
1223
1224    def get_lvol_stores(args):
1225        print_dict(rpc.lvol.get_lvol_stores(args.client,
1226                                            uuid=args.uuid,
1227                                            lvs_name=args.lvs_name))
1228
1229    p = subparsers.add_parser('get_lvol_stores', help='Display current logical volume store list')
1230    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1231    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1232    p.set_defaults(func=get_lvol_stores)
1233
1234    def get_raid_bdevs(args):
1235        print_array(rpc.bdev.get_raid_bdevs(args.client,
1236                                            category=args.category))
1237
1238    p = subparsers.add_parser('get_raid_bdevs', help="""This is used to list all the raid bdev names based on the input category
1239    requested. Category should be one of 'all', 'online', 'configuring' or 'offline'. 'all' means all the raid bdevs whether
1240    they are online or configuring or offline. 'online' is the raid bdev which is registered with bdev layer. 'configuring'
1241    is the raid bdev which does not have full configuration discovered yet. 'offline' is the raid bdev which is not registered
1242    with bdev as of now and it has encountered any error or user has requested to offline the raid bdev""")
1243    p.add_argument('category', help='all or online or configuring or offline')
1244    p.set_defaults(func=get_raid_bdevs)
1245
1246    def construct_raid_bdev(args):
1247        base_bdevs = []
1248        for u in args.base_bdevs.strip().split(" "):
1249            base_bdevs.append(u)
1250
1251        rpc.bdev.construct_raid_bdev(args.client,
1252                                     name=args.name,
1253                                     strip_size=args.strip_size,
1254                                     strip_size_kb=args.strip_size_kb,
1255                                     raid_level=args.raid_level,
1256                                     base_bdevs=base_bdevs)
1257    p = subparsers.add_parser('construct_raid_bdev', help='Construct new raid bdev')
1258    p.add_argument('-n', '--name', help='raid bdev name', required=True)
1259    p.add_argument('-s', '--strip-size', help='strip size in KB (deprecated)', type=int)
1260    p.add_argument('-z', '--strip-size_kb', help='strip size in KB', type=int)
1261    p.add_argument('-r', '--raid-level', help='raid level, only raid level 0 is supported', type=int, required=True)
1262    p.add_argument('-b', '--base-bdevs', help='base bdevs name, whitespace separated list in quotes', required=True)
1263    p.set_defaults(func=construct_raid_bdev)
1264
1265    def destroy_raid_bdev(args):
1266        rpc.bdev.destroy_raid_bdev(args.client,
1267                                   name=args.name)
1268    p = subparsers.add_parser('destroy_raid_bdev', help='Destroy existing raid bdev')
1269    p.add_argument('name', help='raid bdev name')
1270    p.set_defaults(func=destroy_raid_bdev)
1271
1272    # split
1273    def construct_split_vbdev(args):
1274        print_array(rpc.bdev.construct_split_vbdev(args.client,
1275                                                   base_bdev=args.base_bdev,
1276                                                   split_count=args.split_count,
1277                                                   split_size_mb=args.split_size_mb))
1278
1279    p = subparsers.add_parser('construct_split_vbdev', help="""Add given disk name to split config. If bdev with base_name
1280    name exist the split bdevs will be created right away, if not split bdevs will be created when base bdev became
1281    available (during examination process).""")
1282    p.add_argument('base_bdev', help='base bdev name')
1283    p.add_argument('-s', '--split-size-mb', help='size in MiB for each bdev', type=int, default=0)
1284    p.add_argument('split_count', help="""Optional - number of split bdevs to create. Total size * split_count must not
1285    exceed the base bdev size.""", type=int)
1286    p.set_defaults(func=construct_split_vbdev)
1287
1288    def destruct_split_vbdev(args):
1289        rpc.bdev.destruct_split_vbdev(args.client,
1290                                      base_bdev=args.base_bdev)
1291
1292    p = subparsers.add_parser('destruct_split_vbdev', help="""Delete split config with all created splits.""")
1293    p.add_argument('base_bdev', help='base bdev name')
1294    p.set_defaults(func=destruct_split_vbdev)
1295
1296    # ftl
1297    def construct_ftl_bdev(args):
1298        print_dict(rpc.bdev.construct_ftl_bdev(args.client,
1299                                               name=args.name,
1300                                               trtype=args.trtype,
1301                                               traddr=args.traddr,
1302                                               punits=args.punits,
1303                                               uuid=args.uuid,
1304                                               cache=args.cache,
1305                                               allow_open_bands=args.allow_open_bands))
1306
1307    p = subparsers.add_parser('construct_ftl_bdev',
1308                              help='Add FTL bdev')
1309    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
1310    p.add_argument('-t', '--trtype',
1311                   help='NVMe target trtype: e.g., pcie', default='pcie')
1312    p.add_argument('-a', '--traddr',
1313                   help='NVMe target address: e.g., an ip address or BDF', required=True)
1314    p.add_argument('-l', '--punits', help='Parallel unit range in the form of start-end: e.g. 4-8',
1315                   required=True)
1316    p.add_argument('-u', '--uuid', help='UUID of restored bdev (not applicable when creating new '
1317                   'instance): e.g. b286d19a-0059-4709-abcd-9f7732b1567d (optional)')
1318    p.add_argument('-c', '--cache', help='Name of the bdev to be used as a write buffer cache (optional)')
1319    p.add_argument('-o', '--allow_open_bands', help='Restoring after dirty shutdown without cache will'
1320                   ' result in partial data recovery, instead of error', action='store_true')
1321    p.set_defaults(func=construct_ftl_bdev)
1322
1323    def delete_ftl_bdev(args):
1324        print_dict(rpc.bdev.delete_ftl_bdev(args.client, name=args.name))
1325
1326    p = subparsers.add_parser('delete_ftl_bdev', help='Delete FTL bdev')
1327    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
1328    p.set_defaults(func=delete_ftl_bdev)
1329
1330    # nbd
1331    def start_nbd_disk(args):
1332        print(rpc.nbd.start_nbd_disk(args.client,
1333                                     bdev_name=args.bdev_name,
1334                                     nbd_device=args.nbd_device))
1335
1336    p = subparsers.add_parser('start_nbd_disk', help='Export a bdev as a nbd disk')
1337    p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.')
1338    p.add_argument('nbd_device', help='Nbd device name to be assigned. Example: /dev/nbd0.', nargs='?')
1339    p.set_defaults(func=start_nbd_disk)
1340
1341    def stop_nbd_disk(args):
1342        rpc.nbd.stop_nbd_disk(args.client,
1343                              nbd_device=args.nbd_device)
1344
1345    p = subparsers.add_parser('stop_nbd_disk', help='Stop a nbd disk')
1346    p.add_argument('nbd_device', help='Nbd device name to be stopped. Example: /dev/nbd0.')
1347    p.set_defaults(func=stop_nbd_disk)
1348
1349    def get_nbd_disks(args):
1350        print_dict(rpc.nbd.get_nbd_disks(args.client,
1351                                         nbd_device=args.nbd_device))
1352
1353    p = subparsers.add_parser('get_nbd_disks', help='Display full or specified nbd device list')
1354    p.add_argument('-n', '--nbd-device', help="Path of the nbd device. Example: /dev/nbd0", required=False)
1355    p.set_defaults(func=get_nbd_disks)
1356
1357    # net
1358    def add_ip_address(args):
1359        rpc.net.add_ip_address(args.client, ifc_index=args.ifc_index, ip_addr=args.ip_addr)
1360
1361    p = subparsers.add_parser('add_ip_address', help='Add IP address')
1362    p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
1363    p.add_argument('ip_addr', help='ip address will be added.')
1364    p.set_defaults(func=add_ip_address)
1365
1366    def delete_ip_address(args):
1367        rpc.net.delete_ip_address(args.client, ifc_index=args.ifc_index, ip_addr=args.ip_addr)
1368
1369    p = subparsers.add_parser('delete_ip_address', help='Delete IP address')
1370    p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
1371    p.add_argument('ip_addr', help='ip address will be deleted.')
1372    p.set_defaults(func=delete_ip_address)
1373
1374    def get_interfaces(args):
1375        print_dict(rpc.net.get_interfaces(args.client))
1376
1377    p = subparsers.add_parser(
1378        'get_interfaces', help='Display current interface list')
1379    p.set_defaults(func=get_interfaces)
1380
1381    # NVMe-oF
1382    def set_nvmf_target_max_subsystems(args):
1383        rpc.nvmf.set_nvmf_target_max_subsystems(args.client,
1384                                                max_subsystems=args.max_subsystems)
1385
1386    p = subparsers.add_parser('set_nvmf_target_max_subsystems', help='Set the maximum number of NVMf target subsystems')
1387    p.add_argument('-x', '--max-subsystems', help='Max number of NVMf subsystems', type=int, required=True)
1388    p.set_defaults(func=set_nvmf_target_max_subsystems)
1389
1390    def set_nvmf_target_config(args):
1391        rpc.nvmf.set_nvmf_target_config(args.client,
1392                                        acceptor_poll_rate=args.acceptor_poll_rate,
1393                                        conn_sched=args.conn_sched)
1394
1395    p = subparsers.add_parser('set_nvmf_target_config', help='Set NVMf target config')
1396    p.add_argument('-r', '--acceptor-poll-rate', help='Polling interval of the acceptor for incoming connections (usec)', type=int)
1397    p.add_argument('-s', '--conn-sched', help="""'roundrobin' - Schedule the incoming connections from any host
1398    on the cores in a round robin manner (Default). 'hostip' - Schedule all the incoming connections from a
1399    specific host IP on to the same core. Connections from different IP will be assigned to cores in a round
1400    robin manner""")
1401    p.set_defaults(func=set_nvmf_target_config)
1402
1403    def nvmf_create_transport(args):
1404        rpc.nvmf.nvmf_create_transport(args.client,
1405                                       trtype=args.trtype,
1406                                       max_queue_depth=args.max_queue_depth,
1407                                       max_qpairs_per_ctrlr=args.max_qpairs_per_ctrlr,
1408                                       in_capsule_data_size=args.in_capsule_data_size,
1409                                       max_io_size=args.max_io_size,
1410                                       io_unit_size=args.io_unit_size,
1411                                       max_aq_depth=args.max_aq_depth,
1412                                       num_shared_buffers=args.num_shared_buffers,
1413                                       buf_cache_size=args.buf_cache_size,
1414                                       max_srq_depth=args.max_srq_depth,
1415                                       no_srq=args.no_srq)
1416
1417    p = subparsers.add_parser('nvmf_create_transport', help='Create NVMf transport')
1418    p.add_argument('-t', '--trtype', help='Transport type (ex. RDMA)', type=str, required=True)
1419    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/O per queue', type=int)
1420    p.add_argument('-p', '--max-qpairs-per-ctrlr', help='Max number of SQ and CQ per controller', type=int)
1421    p.add_argument('-c', '--in-capsule-data-size', help='Max number of in-capsule data size', type=int)
1422    p.add_argument('-i', '--max-io-size', help='Max I/O size (bytes)', type=int)
1423    p.add_argument('-u', '--io-unit-size', help='I/O unit size (bytes)', type=int)
1424    p.add_argument('-a', '--max-aq-depth', help='Max number of admin cmds per AQ', type=int)
1425    p.add_argument('-n', '--num-shared-buffers', help='The number of pooled data buffers available to the transport', type=int)
1426    p.add_argument('-b', '--buf-cache-size', help='The number of shared buffers to reserve for each poll group', type=int)
1427    p.add_argument('-s', '--max-srq-depth', help='Max number of outstanding I/O per SRQ. Relevant only for RDMA transport', type=int)
1428    p.add_argument('-r', '--no-srq', action='store_true', help='Disable per-thread shared receive queue. Relevant only for RDMA transport')
1429    p.set_defaults(func=nvmf_create_transport)
1430
1431    def get_nvmf_transports(args):
1432        print_dict(rpc.nvmf.get_nvmf_transports(args.client))
1433
1434    p = subparsers.add_parser('get_nvmf_transports',
1435                              help='Display nvmf transports')
1436    p.set_defaults(func=get_nvmf_transports)
1437
1438    def get_nvmf_subsystems(args):
1439        print_dict(rpc.nvmf.get_nvmf_subsystems(args.client))
1440
1441    p = subparsers.add_parser('get_nvmf_subsystems',
1442                              help='Display nvmf subsystems')
1443    p.set_defaults(func=get_nvmf_subsystems)
1444
1445    def nvmf_subsystem_create(args):
1446        rpc.nvmf.nvmf_subsystem_create(args.client,
1447                                       nqn=args.nqn,
1448                                       serial_number=args.serial_number,
1449                                       model_number=args.model_number,
1450                                       allow_any_host=args.allow_any_host,
1451                                       max_namespaces=args.max_namespaces)
1452
1453    p = subparsers.add_parser('nvmf_subsystem_create', help='Create an NVMe-oF subsystem')
1454    p.add_argument('nqn', help='Subsystem NQN (ASCII)')
1455    p.add_argument("-s", "--serial-number", help="""
1456    Format:  'sn' etc
1457    Example: 'SPDK00000000000001'""", default='00000000000000000000')
1458    p.add_argument("-d", "--model-number", help="""
1459    Format:  'mn' etc
1460    Example: 'SPDK Controller'""", default='SPDK bdev Controller')
1461    p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce host NQN whitelist)")
1462    p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed",
1463                   type=int, default=0)
1464    p.set_defaults(func=nvmf_subsystem_create)
1465
1466    def delete_nvmf_subsystem(args):
1467        rpc.nvmf.delete_nvmf_subsystem(args.client,
1468                                       nqn=args.subsystem_nqn)
1469
1470    p = subparsers.add_parser('delete_nvmf_subsystem',
1471                              help='Delete a nvmf subsystem')
1472    p.add_argument('subsystem_nqn',
1473                   help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.')
1474    p.set_defaults(func=delete_nvmf_subsystem)
1475
1476    def nvmf_subsystem_add_listener(args):
1477        rpc.nvmf.nvmf_subsystem_add_listener(args.client,
1478                                             nqn=args.nqn,
1479                                             trtype=args.trtype,
1480                                             traddr=args.traddr,
1481                                             adrfam=args.adrfam,
1482                                             trsvcid=args.trsvcid)
1483
1484    p = subparsers.add_parser('nvmf_subsystem_add_listener', help='Add a listener to an NVMe-oF subsystem')
1485    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1486    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
1487    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1488    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1489    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
1490    p.set_defaults(func=nvmf_subsystem_add_listener)
1491
1492    def nvmf_subsystem_remove_listener(args):
1493        rpc.nvmf.nvmf_subsystem_remove_listener(args.client,
1494                                                nqn=args.nqn,
1495                                                trtype=args.trtype,
1496                                                traddr=args.traddr,
1497                                                adrfam=args.adrfam,
1498                                                trsvcid=args.trsvcid)
1499
1500    p = subparsers.add_parser('nvmf_subsystem_remove_listener', help='Remove a listener from an NVMe-oF subsystem')
1501    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1502    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
1503    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1504    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1505    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
1506    p.set_defaults(func=nvmf_subsystem_remove_listener)
1507
1508    def nvmf_subsystem_add_ns(args):
1509        rpc.nvmf.nvmf_subsystem_add_ns(args.client,
1510                                       nqn=args.nqn,
1511                                       bdev_name=args.bdev_name,
1512                                       nsid=args.nsid,
1513                                       nguid=args.nguid,
1514                                       eui64=args.eui64,
1515                                       uuid=args.uuid)
1516
1517    p = subparsers.add_parser('nvmf_subsystem_add_ns', help='Add a namespace to an NVMe-oF subsystem')
1518    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1519    p.add_argument('bdev_name', help='The name of the bdev that will back this namespace')
1520    p.add_argument('-n', '--nsid', help='The requested NSID (optional)', type=int)
1521    p.add_argument('-g', '--nguid', help='Namespace globally unique identifier (optional)')
1522    p.add_argument('-e', '--eui64', help='Namespace EUI-64 identifier (optional)')
1523    p.add_argument('-u', '--uuid', help='Namespace UUID (optional)')
1524    p.set_defaults(func=nvmf_subsystem_add_ns)
1525
1526    def nvmf_subsystem_remove_ns(args):
1527        rpc.nvmf.nvmf_subsystem_remove_ns(args.client,
1528                                          nqn=args.nqn,
1529                                          nsid=args.nsid)
1530
1531    p = subparsers.add_parser('nvmf_subsystem_remove_ns', help='Remove a namespace to an NVMe-oF subsystem')
1532    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1533    p.add_argument('nsid', help='The requested NSID', type=int)
1534    p.set_defaults(func=nvmf_subsystem_remove_ns)
1535
1536    def nvmf_subsystem_add_host(args):
1537        rpc.nvmf.nvmf_subsystem_add_host(args.client,
1538                                         nqn=args.nqn,
1539                                         host=args.host)
1540
1541    p = subparsers.add_parser('nvmf_subsystem_add_host', help='Add a host to an NVMe-oF subsystem')
1542    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1543    p.add_argument('host', help='Host NQN to allow')
1544    p.set_defaults(func=nvmf_subsystem_add_host)
1545
1546    def nvmf_subsystem_remove_host(args):
1547        rpc.nvmf.nvmf_subsystem_remove_host(args.client,
1548                                            nqn=args.nqn,
1549                                            host=args.host)
1550
1551    p = subparsers.add_parser('nvmf_subsystem_remove_host', help='Remove a host from an NVMe-oF subsystem')
1552    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1553    p.add_argument('host', help='Host NQN to remove')
1554    p.set_defaults(func=nvmf_subsystem_remove_host)
1555
1556    def nvmf_subsystem_allow_any_host(args):
1557        rpc.nvmf.nvmf_subsystem_allow_any_host(args.client,
1558                                               nqn=args.nqn,
1559                                               disable=args.disable)
1560
1561    p = subparsers.add_parser('nvmf_subsystem_allow_any_host', help='Allow any host to connect to the subsystem')
1562    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1563    p.add_argument('-e', '--enable', action='store_true', help='Enable allowing any host')
1564    p.add_argument('-d', '--disable', action='store_true', help='Disable allowing any host')
1565    p.set_defaults(func=nvmf_subsystem_allow_any_host)
1566
1567    # pmem
1568    def create_pmem_pool(args):
1569        num_blocks = int((args.total_size * 1024 * 1024) / args.block_size)
1570        rpc.pmem.create_pmem_pool(args.client,
1571                                  pmem_file=args.pmem_file,
1572                                  num_blocks=num_blocks,
1573                                  block_size=args.block_size)
1574
1575    p = subparsers.add_parser('create_pmem_pool', help='Create pmem pool')
1576    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1577    p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
1578    p.add_argument('block_size', help='Block size for this pmem pool', type=int)
1579    p.set_defaults(func=create_pmem_pool)
1580
1581    def pmem_pool_info(args):
1582        print_dict(rpc.pmem.pmem_pool_info(args.client,
1583                                           pmem_file=args.pmem_file))
1584
1585    p = subparsers.add_parser('pmem_pool_info', help='Display pmem pool info and check consistency')
1586    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1587    p.set_defaults(func=pmem_pool_info)
1588
1589    def delete_pmem_pool(args):
1590        rpc.pmem.delete_pmem_pool(args.client,
1591                                  pmem_file=args.pmem_file)
1592
1593    p = subparsers.add_parser('delete_pmem_pool', help='Delete pmem pool')
1594    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1595    p.set_defaults(func=delete_pmem_pool)
1596
1597    # subsystem
1598    def get_subsystems(args):
1599        print_dict(rpc.subsystem.get_subsystems(args.client))
1600
1601    p = subparsers.add_parser('get_subsystems', help="""Print subsystems array in initialization order. Each subsystem
1602    entry contain (unsorted) array of subsystems it depends on.""")
1603    p.set_defaults(func=get_subsystems)
1604
1605    def get_subsystem_config(args):
1606        print_dict(rpc.subsystem.get_subsystem_config(args.client, args.name))
1607
1608    p = subparsers.add_parser('get_subsystem_config', help="""Print subsystem configuration""")
1609    p.add_argument('name', help='Name of subsystem to query')
1610    p.set_defaults(func=get_subsystem_config)
1611
1612    # vhost
1613    def set_vhost_controller_coalescing(args):
1614        rpc.vhost.set_vhost_controller_coalescing(args.client,
1615                                                  ctrlr=args.ctrlr,
1616                                                  delay_base_us=args.delay_base_us,
1617                                                  iops_threshold=args.iops_threshold)
1618
1619    p = subparsers.add_parser('set_vhost_controller_coalescing', help='Set vhost controller coalescing')
1620    p.add_argument('ctrlr', help='controller name')
1621    p.add_argument('delay_base_us', help='Base delay time', type=int)
1622    p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int)
1623    p.set_defaults(func=set_vhost_controller_coalescing)
1624
1625    def construct_vhost_scsi_controller(args):
1626        rpc.vhost.construct_vhost_scsi_controller(args.client,
1627                                                  ctrlr=args.ctrlr,
1628                                                  cpumask=args.cpumask)
1629
1630    p = subparsers.add_parser(
1631        'construct_vhost_scsi_controller', help='Add new vhost controller')
1632    p.add_argument('ctrlr', help='controller name')
1633    p.add_argument('--cpumask', help='cpu mask for this controller')
1634    p.set_defaults(func=construct_vhost_scsi_controller)
1635
1636    def add_vhost_scsi_lun(args):
1637        print_string(rpc.vhost.add_vhost_scsi_lun(args.client,
1638                                                  ctrlr=args.ctrlr,
1639                                                  scsi_target_num=args.scsi_target_num,
1640                                                  bdev_name=args.bdev_name))
1641
1642    p = subparsers.add_parser('add_vhost_scsi_lun',
1643                              help='Add lun to vhost controller')
1644    p.add_argument('ctrlr', help='conntroller name where add lun')
1645    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
1646    p.add_argument('bdev_name', help='bdev name')
1647    p.set_defaults(func=add_vhost_scsi_lun)
1648
1649    def remove_vhost_scsi_target(args):
1650        rpc.vhost.remove_vhost_scsi_target(args.client,
1651                                           ctrlr=args.ctrlr,
1652                                           scsi_target_num=args.scsi_target_num)
1653
1654    p = subparsers.add_parser('remove_vhost_scsi_target', help='Remove target from vhost controller')
1655    p.add_argument('ctrlr', help='controller name to remove target from')
1656    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
1657    p.set_defaults(func=remove_vhost_scsi_target)
1658
1659    def construct_vhost_blk_controller(args):
1660        rpc.vhost.construct_vhost_blk_controller(args.client,
1661                                                 ctrlr=args.ctrlr,
1662                                                 dev_name=args.dev_name,
1663                                                 cpumask=args.cpumask,
1664                                                 readonly=args.readonly)
1665
1666    p = subparsers.add_parser('construct_vhost_blk_controller', help='Add a new vhost block controller')
1667    p.add_argument('ctrlr', help='controller name')
1668    p.add_argument('dev_name', help='device name')
1669    p.add_argument('--cpumask', help='cpu mask for this controller')
1670    p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only')
1671    p.set_defaults(func=construct_vhost_blk_controller)
1672
1673    def construct_vhost_nvme_controller(args):
1674        rpc.vhost.construct_vhost_nvme_controller(args.client,
1675                                                  ctrlr=args.ctrlr,
1676                                                  io_queues=args.io_queues,
1677                                                  cpumask=args.cpumask)
1678
1679    p = subparsers.add_parser('construct_vhost_nvme_controller', help='Add new vhost controller')
1680    p.add_argument('ctrlr', help='controller name')
1681    p.add_argument('io_queues', help='number of IO queues for the controller', type=int)
1682    p.add_argument('--cpumask', help='cpu mask for this controller')
1683    p.set_defaults(func=construct_vhost_nvme_controller)
1684
1685    def add_vhost_nvme_ns(args):
1686        rpc.vhost.add_vhost_nvme_ns(args.client,
1687                                    ctrlr=args.ctrlr,
1688                                    bdev_name=args.bdev_name)
1689
1690    p = subparsers.add_parser('add_vhost_nvme_ns', help='Add a Namespace to vhost controller')
1691    p.add_argument('ctrlr', help='conntroller name where add a Namespace')
1692    p.add_argument('bdev_name', help='block device name for a new Namespace')
1693    p.set_defaults(func=add_vhost_nvme_ns)
1694
1695    def get_vhost_controllers(args):
1696        print_dict(rpc.vhost.get_vhost_controllers(args.client, args.name))
1697
1698    p = subparsers.add_parser('get_vhost_controllers', help='List all or specific vhost controller(s)')
1699    p.add_argument('-n', '--name', help="Name of vhost controller", required=False)
1700    p.set_defaults(func=get_vhost_controllers)
1701
1702    def remove_vhost_controller(args):
1703        rpc.vhost.remove_vhost_controller(args.client,
1704                                          ctrlr=args.ctrlr)
1705
1706    p = subparsers.add_parser('remove_vhost_controller', help='Remove a vhost controller')
1707    p.add_argument('ctrlr', help='controller name')
1708    p.set_defaults(func=remove_vhost_controller)
1709
1710    def construct_virtio_dev(args):
1711        print_array(rpc.vhost.construct_virtio_dev(args.client,
1712                                                   name=args.name,
1713                                                   trtype=args.trtype,
1714                                                   traddr=args.traddr,
1715                                                   dev_type=args.dev_type,
1716                                                   vq_count=args.vq_count,
1717                                                   vq_size=args.vq_size))
1718
1719    p = subparsers.add_parser('construct_virtio_dev', help="""Construct new virtio device using provided
1720    transport type and device type. In case of SCSI device type this implies scan and add bdevs offered by
1721    remote side. Result is array of added bdevs.""")
1722    p.add_argument('name', help="Use this name as base for new created bdevs")
1723    p.add_argument('-t', '--trtype',
1724                   help='Virtio target transport type: pci or user', required=True)
1725    p.add_argument('-a', '--traddr',
1726                   help='Transport type specific target address: e.g. UNIX domain socket path or BDF', required=True)
1727    p.add_argument('-d', '--dev-type',
1728                   help='Device type: blk or scsi', required=True)
1729    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1730    p.add_argument('--vq-size', help='Size of each queue', type=int)
1731    p.set_defaults(func=construct_virtio_dev)
1732
1733    def get_virtio_scsi_devs(args):
1734        print_dict(rpc.vhost.get_virtio_scsi_devs(args.client))
1735
1736    p = subparsers.add_parser('get_virtio_scsi_devs', help='List all Virtio-SCSI devices.')
1737    p.set_defaults(func=get_virtio_scsi_devs)
1738
1739    def remove_virtio_bdev(args):
1740        rpc.vhost.remove_virtio_bdev(args.client,
1741                                     name=args.name)
1742
1743    p = subparsers.add_parser('remove_virtio_bdev', help="""Remove a Virtio device
1744    This will delete all bdevs exposed by this device""")
1745    p.add_argument('name', help='Virtio device name. E.g. VirtioUser0')
1746    p.set_defaults(func=remove_virtio_bdev)
1747
1748    # ioat
1749    def scan_ioat_copy_engine(args):
1750        pci_whitelist = []
1751        if args.pci_whitelist:
1752            for w in args.pci_whitelist.strip().split(" "):
1753                pci_whitelist.append(w)
1754        rpc.ioat.scan_ioat_copy_engine(args.client, pci_whitelist)
1755
1756    p = subparsers.add_parser('scan_ioat_copy_engine', help='Set scan and enable IOAT copy engine offload.')
1757    p.add_argument('-w', '--pci-whitelist', help="""Whitespace-separated list of PCI addresses in
1758    domain:bus:device.function format or domain.bus.device.function format""")
1759    p.set_defaults(func=scan_ioat_copy_engine)
1760
1761    # send_nvme_cmd
1762    def send_nvme_cmd(args):
1763        print_dict(rpc.nvme.send_nvme_cmd(args.client,
1764                                          name=args.nvme_name,
1765                                          cmd_type=args.cmd_type,
1766                                          data_direction=args.data_direction,
1767                                          cmdbuf=args.cmdbuf,
1768                                          data=args.data,
1769                                          metadata=args.metadata,
1770                                          data_len=args.data_length,
1771                                          metadata_len=args.metadata_length,
1772                                          timeout_ms=args.timeout_ms))
1773
1774    p = subparsers.add_parser('send_nvme_cmd', help='NVMe passthrough cmd.')
1775    p.add_argument('-n', '--nvme-name', help="""Name of the operating NVMe controller""")
1776    p.add_argument('-t', '--cmd-type', help="""Type of nvme cmd. Valid values are: admin, io""")
1777    p.add_argument('-r', '--data-direction', help="""Direction of data transfer. Valid values are: c2h, h2c""")
1778    p.add_argument('-c', '--cmdbuf', help="""NVMe command encoded by base64 urlsafe""")
1779    p.add_argument('-d', '--data', help="""Data transferring to controller from host, encoded by base64 urlsafe""")
1780    p.add_argument('-m', '--metadata', help="""Metadata transferring to controller from host, encoded by base64 urlsafe""")
1781    p.add_argument('-D', '--data-length', help="""Data length required to transfer from controller to host""", type=int)
1782    p.add_argument('-M', '--metadata-length', help="""Metadata length required to transfer from controller to host""", type=int)
1783    p.add_argument('-T', '--timeout-ms',
1784                   help="""Command execution timeout value, in milliseconds,  if 0, don't track timeout""", type=int, default=0)
1785    p.set_defaults(func=send_nvme_cmd)
1786
1787    # Notifications
1788    def get_notification_types(args):
1789        print_dict(rpc.notify.get_notification_types(args.client))
1790
1791    p = subparsers.add_parser('get_notification_types', help='List available notifications that user can subscribe to.')
1792    p.set_defaults(func=get_notification_types)
1793
1794    def get_notifications(args):
1795        ret = rpc.notify.get_notifications(args.client,
1796                                           id=args.id,
1797                                           max=args.max)
1798        print_dict(ret)
1799
1800    p = subparsers.add_parser('get_notifications', help='Get notifications')
1801    p.add_argument('-i', '--id', help="""First ID to start fetching from""", type=int)
1802    p.add_argument('-n', '--max', help="""Maximum number of notifications to return in response""", type=int)
1803    p.set_defaults(func=get_notifications)
1804
1805    def check_called_name(name):
1806        if name in deprecated_aliases:
1807            print("{} is deprecated, use {} instead.".format(name, deprecated_aliases[name]), file=sys.stderr)
1808
1809    class mock_client:
1810        def call(self, method, params=None):
1811            print("Request:\n" + json.dumps({"method": method, "params": params}, indent=2))
1812
1813    def mock_print(arg):
1814        pass
1815
1816    def call_rpc_func(args):
1817        args.func(args)
1818        check_called_name(args.called_rpc_name)
1819
1820    def execute_script(parser, client, fd):
1821        executed_rpc = ""
1822        for rpc_call in map(str.rstrip, fd):
1823            if not rpc_call.strip():
1824                continue
1825            executed_rpc = "\n".join([executed_rpc, rpc_call])
1826            args = parser.parse_args(shlex.split(rpc_call))
1827            args.client = client
1828            try:
1829                call_rpc_func(args)
1830            except JSONRPCException as ex:
1831                print("Exception:")
1832                print(executed_rpc.strip() + " <<<")
1833                print(ex.message)
1834                exit(1)
1835
1836    args = parser.parse_args()
1837    if args.dry_run:
1838        args.client = mock_client()
1839        print_dict = mock_print
1840        print_string = mock_print
1841        print_array = mock_print
1842    else:
1843        args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.timeout, log_level=getattr(logging, args.verbose.upper()))
1844    if hasattr(args, 'func'):
1845        try:
1846            call_rpc_func(args)
1847        except JSONRPCException as ex:
1848            print(ex)
1849            exit(1)
1850    elif sys.stdin.isatty():
1851        # No arguments and no data piped through stdin
1852        parser.print_help()
1853        exit(1)
1854    else:
1855        execute_script(parser, args.client, sys.stdin)
1856