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