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