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