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