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