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