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