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