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