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