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