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