xref: /spdk/scripts/rpc.py (revision 8a0a98d35e21f282088edf28b9e8da66ec390e3a)
1#!/usr/bin/env python
2
3from rpc.client import print_dict, JSONRPCException
4
5import argparse
6import rpc
7
8try:
9    from shlex import quote
10except ImportError:
11    from pipes import quote
12
13
14def print_array(a):
15    print(" ".join((quote(v) for v in a)))
16
17
18def call_cmd(func):
19    def rpc_cmd(*args, **kwargs):
20        try:
21            func(*args, **kwargs)
22        except JSONRPCException as ex:
23            print(ex.message)
24            exit(1)
25    return rpc_cmd
26
27
28if __name__ == "__main__":
29    parser = argparse.ArgumentParser(
30        description='SPDK RPC command line interface')
31    parser.add_argument('-s', dest='server_addr',
32                        help='RPC server address', default='/var/tmp/spdk.sock')
33    parser.add_argument('-p', dest='port',
34                        help='RPC port number (if server_addr is IP address)',
35                        default=5260, type=int)
36    parser.add_argument('-t', dest='timeout',
37                        help='Timout as a floating point number expressed in seconds waiting for reponse. Default: 60.0',
38                        default=60.0, type=float)
39    parser.add_argument('-v', dest='verbose',
40                        help='Verbose mode', action='store_true')
41    subparsers = parser.add_subparsers(help='RPC methods')
42
43    @call_cmd
44    def start_subsystem_init(args):
45        rpc.start_subsystem_init(args.client)
46
47    p = subparsers.add_parser('start_subsystem_init', help='Start initialization of subsystems')
48    p.set_defaults(func=start_subsystem_init)
49
50    @call_cmd
51    def get_rpc_methods(args):
52        print_dict(rpc.get_rpc_methods(args.client, args))
53
54    p = subparsers.add_parser('get_rpc_methods', help='Get list of supported RPC methods')
55    p.add_argument('-c', '--current', help='Get list of RPC methods only callable in the current state.', action='store_true')
56    p.set_defaults(func=get_rpc_methods)
57
58    @call_cmd
59    def save_config(args):
60        rpc.save_config(args.client, args)
61
62    p = subparsers.add_parser('save_config', help="""Write current (live) configuration of SPDK subsystems and targets.
63    If no filename is given write configuration to stdout.""")
64    p.add_argument('-f', '--filename', help="""File where to save JSON configuration to.""")
65    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. If filename is not given default
66    indent level is 2. If writing to file of filename is '-' then default is compact mode.""", type=int, default=2)
67    p.set_defaults(func=save_config)
68
69    @call_cmd
70    def load_config(args):
71        rpc.load_config(args.client, args)
72
73    p = subparsers.add_parser('load_config', help="""Configure SPDK subsystems and tagets using JSON RPC. If no file is
74    provided or file is '-' read configuration from stdin.""")
75    p.add_argument('-f', '--filename', help="""JSON Configuration file.""")
76    p.set_defaults(func=load_config)
77
78    @call_cmd
79    def load_subsystem_config(args):
80        rpc.load_subsystem_config(args.client, args)
81
82    p = subparsers.add_parser('load_subsystem_config', help="""Configure SPDK subsystem using JSON RPC. If no file is
83    provided or file is '-' read configuration from stdin.""")
84    p.add_argument('--filename', help="""JSON Configuration file.""")
85    p.set_defaults(func=load_subsystem_config)
86
87    # app
88    @call_cmd
89    def kill_instance(args):
90        rpc.app.kill_instance(args.client,
91                              sig_name=args.sig_name)
92
93    p = subparsers.add_parser('kill_instance', help='Send signal to instance')
94    p.add_argument('sig_name', help='signal will be sent to server.')
95    p.set_defaults(func=kill_instance)
96
97    @call_cmd
98    def context_switch_monitor(args):
99        enabled = None
100        if args.enable:
101            enabled = True
102        if args.disable:
103            enabled = False
104        print_dict(rpc.app.context_switch_monitor(args.client,
105                                                  enabled=enabled))
106
107    p = subparsers.add_parser('context_switch_monitor', help='Control whether the context switch monitor is enabled')
108    p.add_argument('-e', '--enable', action='store_true', help='Enable context switch monitoring')
109    p.add_argument('-d', '--disable', action='store_true', help='Disable context switch monitoring')
110    p.set_defaults(func=context_switch_monitor)
111
112    # bdev
113    @call_cmd
114    def set_bdev_options(args):
115        rpc.bdev.set_bdev_options(args.client,
116                                  bdev_io_pool_size=args.bdev_io_pool_size,
117                                  bdev_io_cache_size=args.bdev_io_cache_size)
118
119    p = subparsers.add_parser('set_bdev_options', help="""Set options of bdev subsystem""")
120    p.add_argument('-p', '--bdev-io-pool-size', help='Number of bdev_io structures in shared buffer pool', type=int)
121    p.add_argument('-c', '--bdev-io-cache-size', help='Maximum number of bdev_io structures cached per thread', type=int)
122    p.set_defaults(func=set_bdev_options)
123
124    @call_cmd
125    def construct_malloc_bdev(args):
126        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
127        print_array(rpc.bdev.construct_malloc_bdev(args.client,
128                                                   num_blocks=num_blocks,
129                                                   block_size=args.block_size,
130                                                   name=args.name,
131                                                   uuid=args.uuid))
132
133    p = subparsers.add_parser('construct_malloc_bdev',
134                              help='Add a bdev with malloc backend')
135    p.add_argument('-b', '--name', help="Name of the bdev")
136    p.add_argument('-u', '--uuid', help="UUID of the bdev")
137    p.add_argument(
138        'total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
139    p.add_argument('block_size', help='Block size for this bdev', type=int)
140    p.set_defaults(func=construct_malloc_bdev)
141
142    @call_cmd
143    def delete_malloc_bdev(args):
144        rpc.bdev.delete_malloc_bdev(args.client,
145                                    name=args.name)
146
147    p = subparsers.add_parser('delete_malloc_bdev', help='Delete a malloc disk')
148    p.add_argument('name', help='malloc bdev name')
149    p.set_defaults(func=delete_malloc_bdev)
150
151    @call_cmd
152    def construct_null_bdev(args):
153        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
154        print_array(rpc.bdev.construct_null_bdev(args.client,
155                                                 num_blocks=num_blocks,
156                                                 block_size=args.block_size,
157                                                 name=args.name,
158                                                 uuid=args.uuid))
159
160    p = subparsers.add_parser('construct_null_bdev',
161                              help='Add a bdev with null backend')
162    p.add_argument('name', help='Block device name')
163    p.add_argument('-u', '--uuid', help='UUID of the bdev')
164    p.add_argument(
165        'total_size', help='Size of null bdev in MB (int > 0)', type=int)
166    p.add_argument('block_size', help='Block size for this bdev', type=int)
167    p.set_defaults(func=construct_null_bdev)
168
169    @call_cmd
170    def construct_aio_bdev(args):
171        print_array(rpc.bdev.construct_aio_bdev(args.client,
172                                                filename=args.filename,
173                                                name=args.name,
174                                                block_size=args.block_size))
175
176    p = subparsers.add_parser('construct_aio_bdev',
177                              help='Add a bdev with aio backend')
178    p.add_argument('filename', help='Path to device or file (ex: /dev/sda)')
179    p.add_argument('name', help='Block device name')
180    p.add_argument('block_size', help='Block size for this bdev', type=int, nargs='?', default=0)
181    p.set_defaults(func=construct_aio_bdev)
182
183    @call_cmd
184    def delete_aio_bdev(args):
185        rpc.bdev.delete_aio_bdev(args.client,
186                                 name=args.name)
187
188    p = subparsers.add_parser('delete_aio_bdev', help='Delete an aio disk')
189    p.add_argument('name', help='aio bdev name')
190    p.set_defaults(func=delete_aio_bdev)
191
192    @call_cmd
193    def construct_nvme_bdev(args):
194        print_array(rpc.bdev.construct_nvme_bdev(args.client,
195                                                 name=args.name,
196                                                 trtype=args.trtype,
197                                                 traddr=args.traddr,
198                                                 adrfam=args.adrfam,
199                                                 trsvcid=args.trsvcid,
200                                                 subnqn=args.subnqn))
201
202    p = subparsers.add_parser('construct_nvme_bdev',
203                              help='Add bdev with nvme backend')
204    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
205    p.add_argument('-t', '--trtype',
206                   help='NVMe-oF target trtype: e.g., rdma, pcie', required=True)
207    p.add_argument('-a', '--traddr',
208                   help='NVMe-oF target address: e.g., an ip address or BDF', required=True)
209    p.add_argument('-f', '--adrfam',
210                   help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
211    p.add_argument('-s', '--trsvcid',
212                   help='NVMe-oF target trsvcid: e.g., a port number')
213    p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
214    p.set_defaults(func=construct_nvme_bdev)
215
216    @call_cmd
217    def construct_rbd_bdev(args):
218        print_array(rpc.bdev.construct_rbd_bdev(args.client,
219                                                name=args.name,
220                                                pool_name=args.pool_name,
221                                                rbd_name=args.rbd_name,
222                                                block_size=args.block_size))
223
224    p = subparsers.add_parser('construct_rbd_bdev',
225                              help='Add a bdev with ceph rbd backend')
226    p.add_argument('-b', '--name', help="Name of the bdev", required=False)
227    p.add_argument('pool_name', help='rbd pool name')
228    p.add_argument('rbd_name', help='rbd image name')
229    p.add_argument('block_size', help='rbd block size', type=int)
230    p.set_defaults(func=construct_rbd_bdev)
231
232    @call_cmd
233    def construct_error_bdev(args):
234        rpc.bdev.construct_error_bdev(args.client,
235                                      base_name=args.base_name)
236
237    p = subparsers.add_parser('construct_error_bdev',
238                              help='Add bdev with error injection backend')
239    p.add_argument('base_name', help='base bdev name')
240    p.set_defaults(func=construct_error_bdev)
241
242    @call_cmd
243    def delete_error_bdev(args):
244        rpc.bdev.delete_error_bdev(args.client,
245                                   name=args.name)
246
247    p = subparsers.add_parser('delete_error_bdev', help='Delete an error bdev')
248    p.add_argument('name', help='error bdev name')
249    p.set_defaults(func=delete_error_bdev)
250
251    @call_cmd
252    def construct_iscsi_bdev(args):
253        rpc.bdev.construct_iscsi_bdev(args.client,
254                                      name=args.name,
255                                      url=args.url,
256                                      initiator_iqn=args.initiator_iqn)
257
258    p = subparsers.add_parser('construct_iscsi_bdev',
259                              help='Add bdev with iSCSI initiator backend')
260    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
261    p.add_argument('-i', '--initiator-iqn', help="Initiator IQN", required=True)
262    p.add_argument('--url', help="iSCSI Lun URL", required=True)
263    p.set_defaults(func=construct_iscsi_bdev)
264
265    @call_cmd
266    def delete_iscsi_bdev(args):
267        rpc.bdev.delete_iscsi_bdev(args.client,
268                                   name=args.name)
269
270    p = subparsers.add_parser('delete_iscsi_bdev', help='Delete an iSCSI bdev')
271    p.add_argument('name', help='iSCSI bdev name')
272    p.set_defaults(func=delete_iscsi_bdev)
273
274    @call_cmd
275    def construct_pmem_bdev(args):
276        print_array(rpc.bdev.construct_pmem_bdev(args.client,
277                                                 pmem_file=args.pmem_file,
278                                                 name=args.name))
279
280    p = subparsers.add_parser('construct_pmem_bdev', help='Add a bdev with pmem backend')
281    p.add_argument('pmem_file', help='Path to pmemblk pool file')
282    p.add_argument('-n', '--name', help='Block device name', required=True)
283    p.set_defaults(func=construct_pmem_bdev)
284
285    @call_cmd
286    def construct_passthru_bdev(args):
287        print_array(rpc.bdev.construct_passthru_bdev(args.client,
288                                                     base_bdev_name=args.base_bdev_name,
289                                                     passthru_bdev_name=args.passthru_bdev_name))
290
291    p = subparsers.add_parser('construct_passthru_bdev',
292                              help='Add a pass through bdev on existing bdev')
293    p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True)
294    p.add_argument('-p', '--passthru-bdev-name', help="Name of the passthru bdev", required=True)
295    p.set_defaults(func=construct_passthru_bdev)
296
297    @call_cmd
298    def get_bdevs(args):
299        print_dict(rpc.bdev.get_bdevs(args.client,
300                                      name=args.name))
301
302    p = subparsers.add_parser(
303        'get_bdevs', help='Display current blockdev list or required blockdev')
304    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
305    p.set_defaults(func=get_bdevs)
306
307    @call_cmd
308    def get_bdevs_config(args):
309        print_dict(rpc.bdev.get_bdevs_config(args.client,
310                                             name=args.name))
311
312    p = subparsers.add_parser(
313        'get_bdevs_config', help='Display current (live) blockdev configuration list or required blockdev')
314    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
315    p.set_defaults(func=get_bdevs_config)
316
317    @call_cmd
318    def get_bdevs_iostat(args):
319        print_dict(rpc.bdev.get_bdevs_iostat(args.client,
320                                             name=args.name))
321
322    p = subparsers.add_parser(
323        'get_bdevs_iostat', help='Display current I/O statistics of all the blockdevs or required blockdev.')
324    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
325    p.set_defaults(func=get_bdevs_iostat)
326
327    @call_cmd
328    def delete_bdev(args):
329        rpc.bdev.delete_bdev(args.client,
330                             bdev_name=args.bdev_name)
331
332    p = subparsers.add_parser('delete_bdev', help='Delete a blockdev')
333    p.add_argument(
334        'bdev_name', help='Blockdev name to be deleted. Example: Malloc0.')
335    p.set_defaults(func=delete_bdev)
336
337    @call_cmd
338    def set_bdev_qos_limit_iops(args):
339        rpc.bdev.set_bdev_qos_limit_iops(args.client,
340                                         name=args.name,
341                                         ios_per_sec=args.ios_per_sec)
342
343    p = subparsers.add_parser('set_bdev_qos_limit_iops', help='Set QoS IOPS limit on a blockdev')
344    p.add_argument('name', help='Blockdev name to set QoS. Example: Malloc0')
345    p.add_argument('ios_per_sec',
346                   help='IOs per second limit (>=10000, example: 20000). 0 means unlimited.', type=int)
347    p.set_defaults(func=set_bdev_qos_limit_iops)
348
349    @call_cmd
350    def bdev_inject_error(args):
351        rpc.bdev.bdev_inject_error(args.client,
352                                   name=args.name,
353                                   io_type=args.io_type,
354                                   error_type=args.error_type,
355                                   num=args.num)
356
357    p = subparsers.add_parser('bdev_inject_error', help='bdev inject error')
358    p.add_argument('name', help="""the name of the error injection bdev""")
359    p.add_argument('io_type', help="""io_type: 'clear' 'read' 'write' 'unmap' 'flush' 'all'""")
360    p.add_argument('error_type', help="""error_type: 'failure' 'pending'""")
361    p.add_argument(
362        '-n', '--num', help='the number of commands you want to fail', type=int, default=1)
363    p.set_defaults(func=bdev_inject_error)
364
365    @call_cmd
366    def apply_firmware(args):
367        print_dict(rpc.bdev.apply_firmware(args.client,
368                                           bdev_name=args.bdev_name,
369                                           filename=args.filename))
370
371    p = subparsers.add_parser('apply_firmware', help='Download and commit firmware to NVMe device')
372    p.add_argument('filename', help='filename of the firmware to download')
373    p.add_argument('bdev_name', help='name of the NVMe device')
374    p.set_defaults(func=apply_firmware)
375
376    # iSCSI
377    def set_iscsi_options(args):
378        rpc.iscsi.set_iscsi_options(args.client, args)
379
380    p = subparsers.add_parser('set_iscsi_options', help="""Set options of iSCSI subsystem""")
381    p.add_argument('-f', '--auth-file', help='Path to CHAP shared secret file for discovery session')
382    p.add_argument('-b', '--node-base', help='Prefix of the name of iSCSI target node')
383    p.add_argument('-o', '--nop-timeout', help='Timeout in seconds to nop-in request to the initiator', type=int)
384    p.add_argument('-n', '--nop-in-interval', help='Time interval in secs between nop-in requests by the target', type=int)
385    p.add_argument('-d', '--no-discovery-auth', help="""CHAP for discovery session should be disabled.
386    *** Mutually exclusive with --req-discovery-auth""", action='store_true')
387    p.add_argument('-r', '--req-discovery-auth', help="""CHAP for discovery session should be required.
388    *** Mutually exclusive with --no-discovery-auth""", action='store_true')
389    p.add_argument('-m', '--req-discovery-auth-mutual', help='CHAP for discovery session should be mutual', action='store_true')
390    p.add_argument('-g', '--discovery-auth-group', help="""Authentication group ID for discovery session.
391    *** Authentication group must be precreated ***""", type=int)
392    p.add_argument('-a', '--max-sessions', help='Maximum number of sessions in the host.', type=int)
393    p.add_argument('-c', '--max-connections-per-session', help='Negotiated parameter, MaxConnections.', type=int)
394    p.add_argument('-w', '--default-time2wait', help='Negotiated parameter, DefaultTime2Wait.', type=int)
395    p.add_argument('-v', '--default-time2retain', help='Negotiated parameter, DefaultTime2Retain.', type=int)
396    p.add_argument('-i', '--immediate-data', help='Negotiated parameter, ImmediateData.', action='store_true')
397    p.add_argument('-l', '--error-recovery-level', help='Negotiated parameter, ErrorRecoveryLevel', type=int)
398    p.add_argument('-p', '--allow-duplicated-isid', help='Allow duplicated initiator session ID.', action='store_true')
399    p.add_argument('-u', '--min-connections-per-session', help='Allocation unit of connections per core', type=int)
400    p.set_defaults(func=set_iscsi_options)
401
402    @call_cmd
403    def get_portal_groups(args):
404        print_dict(rpc.iscsi.get_portal_groups(args.client, args))
405
406    p = subparsers.add_parser(
407        'get_portal_groups', help='Display current portal group configuration')
408    p.set_defaults(func=get_portal_groups)
409
410    @call_cmd
411    def get_initiator_groups(args):
412        print_dict(rpc.iscsi.get_initiator_groups(args.client, args))
413
414    p = subparsers.add_parser('get_initiator_groups',
415                              help='Display current initiator group configuration')
416    p.set_defaults(func=get_initiator_groups)
417
418    @call_cmd
419    def get_target_nodes(args):
420        print_dict(rpc.iscsi.get_target_nodes(args.client, args))
421
422    p = subparsers.add_parser('get_target_nodes', help='Display target nodes')
423    p.set_defaults(func=get_target_nodes)
424
425    @call_cmd
426    def construct_target_node(args):
427        rpc.iscsi.construct_target_node(args.client, args)
428
429    p = subparsers.add_parser('construct_target_node',
430                              help='Add a target node')
431    p.add_argument('name', help='Target node name (ASCII)')
432    p.add_argument('alias_name', help='Target node alias name (ASCII)')
433    p.add_argument('bdev_name_id_pairs', help="""Whitespace-separated list of <bdev name:LUN ID> pairs enclosed
434    in quotes.  Format:  'bdev_name0:id0 bdev_name1:id1' etc
435    Example: 'Malloc0:0 Malloc1:1 Malloc5:2'
436    *** The bdevs must pre-exist ***
437    *** LUN0 (id = 0) is required ***
438    *** bdevs names cannot contain space or colon characters ***""")
439    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
440    Whitespace separated, quoted, mapping defined with colon
441    separated list of "tags" (int > 0)
442    Example: '1:1 2:2 2:1'
443    *** The Portal/Initiator Groups must be precreated ***""")
444    p.add_argument('queue_depth', help='Desired target queue depth', type=int)
445    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
446    *** Authentication group must be precreated ***""", type=int, default=0)
447    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
448    *** Mutually exclusive with --require-chap ***""", action='store_true')
449    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
450    *** Mutually exclusive with --disable-chap ***""", action='store_true')
451    p.add_argument(
452        '-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.', action='store_true')
453    p.add_argument('-H', '--header-digest',
454                   help='Header Digest should be required for this target node.', action='store_true')
455    p.add_argument('-D', '--data-digest',
456                   help='Data Digest should be required for this target node.', action='store_true')
457    p.set_defaults(func=construct_target_node)
458
459    @call_cmd
460    def target_node_add_lun(args):
461        rpc.iscsi.target_node_add_lun(args.client, args)
462
463    p = subparsers.add_parser('target_node_add_lun', help='Add LUN to the target node')
464    p.add_argument('name', help='Target node name (ASCII)')
465    p.add_argument('bdev_name', help="""bdev name enclosed in quotes.
466    *** bdev name cannot contain space or colon characters ***""")
467    p.add_argument('-i', dest='lun_id', help="""LUN ID (integer >= 0)
468    *** If LUN ID is omitted or -1, the lowest free one is assigned ***""", type=int, required=False)
469    p.set_defaults(func=target_node_add_lun)
470
471    @call_cmd
472    def add_pg_ig_maps(args):
473        rpc.iscsi.add_pg_ig_maps(args.client, args)
474
475    p = subparsers.add_parser('add_pg_ig_maps', help='Add PG-IG maps to the target node')
476    p.add_argument('name', help='Target node name (ASCII)')
477    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
478    Whitespace separated, quoted, mapping defined with colon
479    separated list of "tags" (int > 0)
480    Example: '1:1 2:2 2:1'
481    *** The Portal/Initiator Groups must be precreated ***""")
482    p.set_defaults(func=add_pg_ig_maps)
483
484    @call_cmd
485    def delete_pg_ig_maps(args):
486        rpc.iscsi.delete_pg_ig_maps(args.client, args)
487
488    p = subparsers.add_parser('delete_pg_ig_maps', help='Delete PG-IG maps from the target node')
489    p.add_argument('name', help='Target node name (ASCII)')
490    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
491    Whitespace separated, quoted, mapping defined with colon
492    separated list of "tags" (int > 0)
493    Example: '1:1 2:2 2:1'
494    *** The Portal/Initiator Groups must be precreated ***""")
495    p.set_defaults(func=delete_pg_ig_maps)
496
497    @call_cmd
498    def add_portal_group(args):
499        rpc.iscsi.add_portal_group(args.client, args)
500
501    p = subparsers.add_parser('add_portal_group', help='Add a portal group')
502    p.add_argument(
503        'tag', help='Portal group tag (unique, integer > 0)', type=int)
504    p.add_argument('portal_list', nargs=argparse.REMAINDER, help="""List of portals in 'host:port@cpumask' format, separated by whitespace
505    (cpumask is optional and can be skipped)
506    Example: '192.168.100.100:3260' '192.168.100.100:3261' '192.168.100.100:3262@0x1""")
507    p.set_defaults(func=add_portal_group)
508
509    @call_cmd
510    def add_initiator_group(args):
511        rpc.iscsi.add_initiator_group(args.client, args)
512
513    p = subparsers.add_parser('add_initiator_group',
514                              help='Add an initiator group')
515    p.add_argument(
516        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
517    p.add_argument('initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
518    enclosed in quotes.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""")
519    p.add_argument('netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
520    Example: '255.255.0.0 255.248.0.0' etc""")
521    p.set_defaults(func=add_initiator_group)
522
523    @call_cmd
524    def add_initiators_to_initiator_group(args):
525        rpc.iscsi.add_initiators_to_initiator_group(args.client, args)
526
527    p = subparsers.add_parser('add_initiators_to_initiator_group',
528                              help='Add initiators to an existing initiator group')
529    p.add_argument(
530        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
531    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
532    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
533    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
534    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
535    p.set_defaults(func=add_initiators_to_initiator_group)
536
537    @call_cmd
538    def delete_initiators_from_initiator_group(args):
539        rpc.iscsi.delete_initiators_from_initiator_group(args.client, args)
540
541    p = subparsers.add_parser('delete_initiators_from_initiator_group',
542                              help='Delete initiators from an existing initiator group')
543    p.add_argument(
544        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
545    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
546    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
547    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
548    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
549    p.set_defaults(func=delete_initiators_from_initiator_group)
550
551    @call_cmd
552    def delete_target_node(args):
553        rpc.iscsi.delete_target_node(args.client, args)
554
555    p = subparsers.add_parser('delete_target_node',
556                              help='Delete a target node')
557    p.add_argument('target_node_name',
558                   help='Target node name to be deleted. Example: iqn.2016-06.io.spdk:disk1.')
559    p.set_defaults(func=delete_target_node)
560
561    @call_cmd
562    def delete_portal_group(args):
563        rpc.iscsi.delete_portal_group(args.client, args)
564
565    p = subparsers.add_parser('delete_portal_group',
566                              help='Delete a portal group')
567    p.add_argument(
568        'tag', help='Portal group tag (unique, integer > 0)', type=int)
569    p.set_defaults(func=delete_portal_group)
570
571    @call_cmd
572    def delete_initiator_group(args):
573        rpc.iscsi.delete_initiator_group(args.client, args)
574
575    p = subparsers.add_parser('delete_initiator_group',
576                              help='Delete an initiator group')
577    p.add_argument(
578        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
579    p.set_defaults(func=delete_initiator_group)
580
581    @call_cmd
582    def get_iscsi_connections(args):
583        print_dict(rpc.iscsi.get_iscsi_connections(args.client, args))
584
585    p = subparsers.add_parser('get_iscsi_connections',
586                              help='Display iSCSI connections')
587    p.set_defaults(func=get_iscsi_connections)
588
589    @call_cmd
590    def get_iscsi_global_params(args):
591        print_dict(rpc.iscsi.get_iscsi_global_params(args.client, args))
592
593    p = subparsers.add_parser('get_iscsi_global_params', help='Display iSCSI global parameters')
594    p.set_defaults(func=get_iscsi_global_params)
595
596    @call_cmd
597    def get_scsi_devices(args):
598        print_dict(rpc.iscsi.get_scsi_devices(args.client, args))
599
600    p = subparsers.add_parser('get_scsi_devices', help='Display SCSI devices')
601    p.set_defaults(func=get_scsi_devices)
602
603    # log
604    @call_cmd
605    def set_trace_flag(args):
606        rpc.log.set_trace_flag(args.client, args)
607
608    p = subparsers.add_parser('set_trace_flag', help='set trace flag')
609    p.add_argument(
610        'flag', help='trace mask we want to set. (for example "nvme").')
611    p.set_defaults(func=set_trace_flag)
612
613    @call_cmd
614    def clear_trace_flag(args):
615        rpc.log.clear_trace_flag(args.client, args)
616
617    p = subparsers.add_parser('clear_trace_flag', help='clear trace flag')
618    p.add_argument(
619        'flag', help='trace mask we want to clear. (for example "nvme").')
620    p.set_defaults(func=clear_trace_flag)
621
622    @call_cmd
623    def get_trace_flags(args):
624        print_dict(rpc.log.get_trace_flags(args.client, args))
625
626    p = subparsers.add_parser('get_trace_flags', help='get trace flags')
627    p.set_defaults(func=get_trace_flags)
628
629    @call_cmd
630    def set_log_level(args):
631        rpc.log.set_log_level(args.client, args)
632
633    p = subparsers.add_parser('set_log_level', help='set log level')
634    p.add_argument('level', help='log level we want to set. (for example "DEBUG").')
635    p.set_defaults(func=set_log_level)
636
637    @call_cmd
638    def get_log_level(args):
639        print_dict(rpc.log.get_log_level(args.client, args))
640
641    p = subparsers.add_parser('get_log_level', help='get log level')
642    p.set_defaults(func=get_log_level)
643
644    @call_cmd
645    def set_log_print_level(args):
646        rpc.log.set_log_print_level(args.client, args)
647
648    p = subparsers.add_parser('set_log_print_level', help='set log print level')
649    p.add_argument('level', help='log print level we want to set. (for example "DEBUG").')
650    p.set_defaults(func=set_log_print_level)
651
652    @call_cmd
653    def get_log_print_level(args):
654        print_dict(rpc.log.get_log_print_level(args.client, args))
655
656    p = subparsers.add_parser('get_log_print_level', help='get log print level')
657    p.set_defaults(func=get_log_print_level)
658
659    # lvol
660    @call_cmd
661    def construct_lvol_store(args):
662        print_array(rpc.lvol.construct_lvol_store(args.client,
663                                                  bdev_name=args.bdev_name,
664                                                  lvs_name=args.lvs_name,
665                                                  cluster_sz=args.cluster_sz))
666
667    p = subparsers.add_parser('construct_lvol_store', help='Add logical volume store on base bdev')
668    p.add_argument('bdev_name', help='base bdev name')
669    p.add_argument('lvs_name', help='name for lvol store')
670    p.add_argument('-c', '--cluster-sz', help='size of cluster (in bytes)', type=int, required=False)
671    p.set_defaults(func=construct_lvol_store)
672
673    @call_cmd
674    def rename_lvol_store(args):
675        rpc.lvol.rename_lvol_store(args.client,
676                                   old_name=args.old_name,
677                                   new_name=args.new_name)
678
679    p = subparsers.add_parser('rename_lvol_store', help='Change logical volume store name')
680    p.add_argument('old_name', help='old name')
681    p.add_argument('new_name', help='new name')
682    p.set_defaults(func=rename_lvol_store)
683
684    @call_cmd
685    def construct_lvol_bdev(args):
686        print_array(rpc.lvol.construct_lvol_bdev(args.client,
687                                                 lvol_name=args.lvol_name,
688                                                 size=args.size * 1024 * 1024,
689                                                 thin_provision=args.thin_provision,
690                                                 uuid=args.uuid,
691                                                 lvs_name=args.lvs_name))
692
693    p = subparsers.add_parser('construct_lvol_bdev', help='Add a bdev with an logical volume backend')
694    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
695    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
696    p.add_argument('-t', '--thin-provision', action='store_true', help='create lvol bdev as thin provisioned')
697    p.add_argument('lvol_name', help='name for this lvol')
698    p.add_argument('size', help='size in MiB for this bdev', type=int)
699    p.set_defaults(func=construct_lvol_bdev)
700
701    @call_cmd
702    def snapshot_lvol_bdev(args):
703        rpc.lvol.snapshot_lvol_bdev(args.client,
704                                    lvol_name=args.lvol_name,
705                                    snapshot_name=args.snapshot_name)
706
707    p = subparsers.add_parser('snapshot_lvol_bdev', help='Create a snapshot of an lvol bdev')
708    p.add_argument('lvol_name', help='lvol bdev name')
709    p.add_argument('snapshot_name', help='lvol snapshot name')
710    p.set_defaults(func=snapshot_lvol_bdev)
711
712    @call_cmd
713    def clone_lvol_bdev(args):
714        rpc.lvol.clone_lvol_bdev(args.client,
715                                 snapshot_name=args.snapshot_name,
716                                 clone_name=args.clone_name)
717
718    p = subparsers.add_parser('clone_lvol_bdev', help='Create a clone of an lvol snapshot')
719    p.add_argument('snapshot_name', help='lvol snapshot name')
720    p.add_argument('clone_name', help='lvol clone name')
721    p.set_defaults(func=clone_lvol_bdev)
722
723    @call_cmd
724    def rename_lvol_bdev(args):
725        rpc.lvol.rename_lvol_bdev(args.client,
726                                  old_name=args.old_name,
727                                  new_name=args.new_name)
728
729    p = subparsers.add_parser('rename_lvol_bdev', help='Change lvol bdev name')
730    p.add_argument('old_name', help='lvol bdev name')
731    p.add_argument('new_name', help='new lvol name')
732    p.set_defaults(func=rename_lvol_bdev)
733
734    @call_cmd
735    def inflate_lvol_bdev(args):
736        rpc.lvol.inflate_lvol_bdev(args.client,
737                                   name=args.name)
738
739    p = subparsers.add_parser('inflate_lvol_bdev', help='Make thin provisioned lvol a thick provisioned lvol')
740    p.add_argument('name', help='lvol bdev name')
741    p.set_defaults(func=inflate_lvol_bdev)
742
743    @call_cmd
744    def decouple_parent_lvol_bdev(args):
745        rpc.lvol.decouple_parent_lvol_bdev(args.client,
746                                           name=args.name)
747
748    p = subparsers.add_parser('decouple_parent_lvol_bdev', help='Decouple parent of lvol')
749    p.add_argument('name', help='lvol bdev name')
750    p.set_defaults(func=inflate_lvol_bdev)
751
752    @call_cmd
753    def resize_lvol_bdev(args):
754        rpc.lvol.resize_lvol_bdev(args.client,
755                                  name=args.name,
756                                  size=args.size * 1024 * 1024)
757
758    p = subparsers.add_parser('resize_lvol_bdev', help='Resize existing lvol bdev')
759    p.add_argument('name', help='lvol bdev name')
760    p.add_argument('size', help='new size in MiB for this bdev', type=int)
761    p.set_defaults(func=resize_lvol_bdev)
762
763    @call_cmd
764    def destroy_lvol_bdev(args):
765        rpc.lvol.destroy_lvol_bdev(args.client,
766                                   name=args.name)
767
768    p = subparsers.add_parser('destroy_lvol_bdev', help='Destroy a logical volume')
769    p.add_argument('name', help='lvol bdev name')
770    p.set_defaults(func=destroy_lvol_bdev)
771
772    @call_cmd
773    def destroy_lvol_store(args):
774        rpc.lvol.destroy_lvol_store(args.client,
775                                    uuid=args.uuid,
776                                    lvs_name=args.lvs_name)
777
778    p = subparsers.add_parser('destroy_lvol_store', help='Destroy an logical volume store')
779    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
780    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
781    p.set_defaults(func=destroy_lvol_store)
782
783    @call_cmd
784    def get_lvol_stores(args):
785        print_dict(rpc.lvol.get_lvol_stores(args.client,
786                                            uuid=args.uuid,
787                                            lvs_name=args.lvs_name))
788
789    p = subparsers.add_parser('get_lvol_stores', help='Display current logical volume store list')
790    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
791    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
792    p.set_defaults(func=get_lvol_stores)
793
794    # split
795    @call_cmd
796    def construct_split_vbdev(args):
797        print_dict(rpc.bdev.construct_split_vbdev(args.client,
798                                                  base_bdev=args.base_bdev,
799                                                  split_count=args.split_count,
800                                                  split_size_mb=args.split_size_mb))
801
802    p = subparsers.add_parser('construct_split_vbdev', help="""Add given disk name to split config. If bdev with base_name
803    name exist the split bdevs will be created right away, if not split bdevs will be created when base bdev became
804    available (during examination process).""")
805    p.add_argument('base_bdev', help='base bdev name')
806    p.add_argument('-s', '--split-size-mb', help='size in MiB for each bdev', type=int, default=0)
807    p.add_argument('split_count', help="""Optional - number of split bdevs to create. Total size * split_count must not
808    exceed the base bdev size.""", type=int)
809    p.set_defaults(func=construct_split_vbdev)
810
811    @call_cmd
812    def destruct_split_vbdev(args):
813        rpc.bdev.destruct_split_vbdev(args.client,
814                                      base_bdev=args.base_bdev)
815
816    p = subparsers.add_parser('destruct_split_vbdev', help="""Delete split config with all created splits.""")
817    p.add_argument('base_bdev', help='base bdev name')
818    p.set_defaults(func=destruct_split_vbdev)
819
820    # nbd
821    @call_cmd
822    def start_nbd_disk(args):
823        print(rpc.nbd.start_nbd_disk(args.client,
824                                     bdev_name=args.bdev_name,
825                                     nbd_device=args.nbd_device))
826
827    p = subparsers.add_parser('start_nbd_disk', help='Export a bdev as a nbd disk')
828    p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.')
829    p.add_argument('nbd_device', help='Nbd device name to be assigned. Example: /dev/nbd0.')
830    p.set_defaults(func=start_nbd_disk)
831
832    @call_cmd
833    def stop_nbd_disk(args):
834        rpc.nbd.stop_nbd_disk(args.client,
835                              nbd_device=args.nbd_device)
836
837    p = subparsers.add_parser('stop_nbd_disk', help='Stop a nbd disk')
838    p.add_argument('nbd_device', help='Nbd device name to be stopped. Example: /dev/nbd0.')
839    p.set_defaults(func=stop_nbd_disk)
840
841    @call_cmd
842    def get_nbd_disks(args):
843        print_dict(rpc.nbd.get_nbd_disks(args.client,
844                                         nbd_device=args.nbd_device))
845
846    p = subparsers.add_parser('get_nbd_disks', help='Display full or specified nbd device list')
847    p.add_argument('-n', '--nbd-device', help="Path of the nbd device. Example: /dev/nbd0", required=False)
848    p.set_defaults(func=get_nbd_disks)
849
850    # net
851    @call_cmd
852    def add_ip_address(args):
853        rpc.net.add_ip_address(args.client, args)
854
855    p = subparsers.add_parser('add_ip_address', help='Add IP address')
856    p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
857    p.add_argument('ip_addr', help='ip address will be added.')
858    p.set_defaults(func=add_ip_address)
859
860    @call_cmd
861    def delete_ip_address(args):
862        rpc.net.delete_ip_address(args.client, args)
863
864    p = subparsers.add_parser('delete_ip_address', help='Delete IP address')
865    p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
866    p.add_argument('ip_addr', help='ip address will be deleted.')
867    p.set_defaults(func=delete_ip_address)
868
869    @call_cmd
870    def get_interfaces(args):
871        print_dict(rpc.net.get_interfaces(args.client, args))
872
873    p = subparsers.add_parser(
874        'get_interfaces', help='Display current interface list')
875    p.set_defaults(func=get_interfaces)
876
877    # NVMe-oF
878    @call_cmd
879    def set_nvmf_target_options(args):
880        rpc.nvmf.set_nvmf_target_options(args.client,
881                                         max_queue_depth=args.max_queue_depth,
882                                         max_qpairs_per_ctrlr=args.max_qpairs_per_ctrlr,
883                                         in_capsule_data_size=args.in_capsule_data_size,
884                                         max_io_size=args.max_io_size,
885                                         max_subsystems=args.max_subsystems,
886                                         io_unit_size=args.io_unit_size)
887
888    p = subparsers.add_parser('set_nvmf_target_options', help='Set NVMf target options')
889    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/O per queue', type=int)
890    p.add_argument('-p', '--max-qpairs-per-ctrlr', help='Max number of SQ and CQ per controller', type=int)
891    p.add_argument('-c', '--in-capsule-data-size', help='Max number of in-capsule data size', type=int)
892    p.add_argument('-i', '--max-io-size', help='Max I/O size', type=int)
893    p.add_argument('-x', '--max-subsystems', help='Max number of NVMf subsystems', type=int)
894    p.add_argument('-u', '--io-unit-size', help='I/O unit size', type=int)
895    p.set_defaults(func=set_nvmf_target_options)
896
897    @call_cmd
898    def set_nvmf_target_config(args):
899        rpc.nvmf.set_nvmf_target_config(args.client,
900                                        acceptor_poll_rate=args.acceptor_poll_rate)
901
902    p = subparsers.add_parser('set_nvmf_target_config', help='Set NVMf target config')
903    p.add_argument('-r', '--acceptor-poll-rate', help='How often the acceptor polls for incoming connections', type=int)
904    p.set_defaults(func=set_nvmf_target_config)
905
906    @call_cmd
907    def get_nvmf_subsystems(args):
908        print_dict(rpc.nvmf.get_nvmf_subsystems(args.client))
909
910    p = subparsers.add_parser('get_nvmf_subsystems',
911                              help='Display nvmf subsystems')
912    p.set_defaults(func=get_nvmf_subsystems)
913
914    @call_cmd
915    def construct_nvmf_subsystem(args):
916        listen_addresses = None
917        hosts = None
918        namespaces = None
919        if args.listen:
920            listen_addresses = [
921                dict(
922                    u.split(
923                        ":",
924                        1) for u in a.split(" ")) for a in args.listen.split(",")]
925
926        if args.hosts:
927            hosts = []
928            for u in args.hosts.strip().split(" "):
929                hosts.append(u)
930
931        if args.namespaces:
932            namespaces = []
933            for u in args.namespaces.strip().split(" "):
934                bdev_name = u
935                nsid = 0
936                if ':' in u:
937                    (bdev_name, nsid) = u.split(":")
938
939                ns_params = {'bdev_name': bdev_name}
940
941                nsid = int(nsid)
942                if nsid != 0:
943                    ns_params['nsid'] = nsid
944
945                namespaces.append(ns_params)
946
947        rpc.nvmf.construct_nvmf_subsystem(args.client,
948                                          nqn=args.nqn,
949                                          listen_addresses=listen_addresses,
950                                          hosts=hosts,
951                                          allow_any_host=args.allow_any_host,
952                                          serial_number=args.serial_number,
953                                          namespaces=namespaces,
954                                          max_namespaces=args.max_namespaces)
955
956    p = subparsers.add_parser('construct_nvmf_subsystem', help='Add a nvmf subsystem')
957    p.add_argument('nqn', help='Target nqn(ASCII)')
958    p.add_argument('listen', help="""comma-separated list of Listen <trtype:transport_name traddr:address trsvcid:port_id> pairs enclosed
959    in quotes.  Format:  'trtype:transport0 traddr:traddr0 trsvcid:trsvcid0,trtype:transport1 traddr:traddr1 trsvcid:trsvcid1' etc
960    Example: 'trtype:RDMA traddr:192.168.100.8 trsvcid:4420,trtype:RDMA traddr:192.168.100.9 trsvcid:4420'""")
961    p.add_argument('hosts', help="""Whitespace-separated list of host nqn list.
962    Format:  'nqn1 nqn2' etc
963    Example: 'nqn.2016-06.io.spdk:init nqn.2016-07.io.spdk:init'""")
964    p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce host NQN whitelist)")
965    p.add_argument("-s", "--serial-number", help="""
966    Format:  'sn' etc
967    Example: 'SPDK00000000000001'""", default='0000:00:01.0')
968    p.add_argument("-n", "--namespaces", help="""Whitespace-separated list of namespaces
969    Format:  'bdev_name1[:nsid1] bdev_name2[:nsid2] bdev_name3[:nsid3]' etc
970    Example: '1:Malloc0 2:Malloc1 3:Malloc2'
971    *** The devices must pre-exist ***""")
972    p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed to added during active connection",
973                   type=int, default=0)
974    p.set_defaults(func=construct_nvmf_subsystem)
975
976    @call_cmd
977    def delete_nvmf_subsystem(args):
978        rpc.nvmf.delete_nvmf_subsystem(args.client,
979                                       nqn=args.subsystem_nqn)
980
981    p = subparsers.add_parser('delete_nvmf_subsystem',
982                              help='Delete a nvmf subsystem')
983    p.add_argument('subsystem_nqn',
984                   help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.')
985    p.set_defaults(func=delete_nvmf_subsystem)
986
987    @call_cmd
988    def nvmf_subsystem_add_listener(args):
989        rpc.nvmf.nvmf_subsystem_add_listener(args.client,
990                                             nqn=args.nqn,
991                                             trtype=args.trtype,
992                                             traddr=args.traddr,
993                                             adrfam=args.adrfam,
994                                             trsvcid=args.trsvcid)
995
996    p = subparsers.add_parser('nvmf_subsystem_add_listener', help='Add a listener to an NVMe-oF subsystem')
997    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
998    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
999    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1000    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1001    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
1002    p.set_defaults(func=nvmf_subsystem_add_listener)
1003
1004    @call_cmd
1005    def nvmf_subsystem_remove_listener(args):
1006        rpc.nvmf.nvmf_subsystem_remove_listener(args.client,
1007                                                nqn=args.nqn,
1008                                                trtype=args.trtype,
1009                                                traddr=args.traddr,
1010                                                adrfam=args.adrfam,
1011                                                trsvcid=args.trsvcid)
1012
1013    p = subparsers.add_parser('nvmf_subsystem_remove_listener', help='Remove a listener from an NVMe-oF subsystem')
1014    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1015    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
1016    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1017    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1018    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
1019    p.set_defaults(func=nvmf_subsystem_remove_listener)
1020
1021    @call_cmd
1022    def nvmf_subsystem_add_ns(args):
1023        rpc.nvmf.nvmf_subsystem_add_ns(args.client,
1024                                       nqn=args.nqn,
1025                                       bdev_name=args.bdev_name,
1026                                       nsid=args.nsid,
1027                                       nguid=args.nguid,
1028                                       eui64=args.eui64,
1029                                       uuid=args.uuid)
1030
1031    p = subparsers.add_parser('nvmf_subsystem_add_ns', help='Add a namespace to an NVMe-oF subsystem')
1032    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1033    p.add_argument('bdev_name', help='The name of the bdev that will back this namespace')
1034    p.add_argument('-n', '--nsid', help='The requested NSID (optional)', type=int)
1035    p.add_argument('-g', '--nguid', help='Namespace globally unique identifier (optional)')
1036    p.add_argument('-e', '--eui64', help='Namespace EUI-64 identifier (optional)')
1037    p.add_argument('-u', '--uuid', help='Namespace UUID (optional)')
1038    p.set_defaults(func=nvmf_subsystem_add_ns)
1039
1040    @call_cmd
1041    def nvmf_subsystem_remove_ns(args):
1042        rpc.nvmf.nvmf_subsystem_remove_ns(args.client,
1043                                          nqn=args.nqn,
1044                                          nsid=args.nsid)
1045
1046    p = subparsers.add_parser('nvmf_subsystem_remove_ns', help='Remove a namespace to an NVMe-oF subsystem')
1047    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1048    p.add_argument('nsid', help='The requested NSID', type=int)
1049    p.set_defaults(func=nvmf_subsystem_remove_ns)
1050
1051    @call_cmd
1052    def nvmf_subsystem_add_host(args):
1053        rpc.nvmf.nvmf_subsystem_add_host(args.client,
1054                                         nqn=args.nqn,
1055                                         host=args.host)
1056
1057    p = subparsers.add_parser('nvmf_subsystem_add_host', help='Add a host to an NVMe-oF subsystem')
1058    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1059    p.add_argument('host', help='Host NQN to allow')
1060    p.set_defaults(func=nvmf_subsystem_add_host)
1061
1062    @call_cmd
1063    def nvmf_subsystem_remove_host(args):
1064        rpc.nvmf.nvmf_subsystem_remove_host(args.client,
1065                                            nqn=args.nqn,
1066                                            host=args.host)
1067
1068    p = subparsers.add_parser('nvmf_subsystem_remove_host', help='Remove a host from an NVMe-oF subsystem')
1069    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1070    p.add_argument('host', help='Host NQN to remove')
1071    p.set_defaults(func=nvmf_subsystem_remove_host)
1072
1073    @call_cmd
1074    def nvmf_subsystem_allow_any_host(args):
1075        rpc.nvmf.nvmf_subsystem_allow_any_host(args.client,
1076                                               nqn=args.nqn,
1077                                               disable=args.disable)
1078
1079    p = subparsers.add_parser('nvmf_subsystem_allow_any_host', help='Allow any host to connect to the subsystem')
1080    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1081    p.add_argument('-e', '--enable', action='store_true', help='Enable allowing any host')
1082    p.add_argument('-d', '--disable', action='store_true', help='Disable allowing any host')
1083    p.set_defaults(func=nvmf_subsystem_allow_any_host)
1084
1085    # pmem
1086    @call_cmd
1087    def create_pmem_pool(args):
1088        num_blocks = int((args.total_size * 1024 * 1024) / args.block_size)
1089        rpc.pmem.create_pmem_pool(args.client,
1090                                  pmem_file=args.pmem_file,
1091                                  num_blocks=num_blocks,
1092                                  block_size=args.block_size)
1093
1094    p = subparsers.add_parser('create_pmem_pool', help='Create pmem pool')
1095    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1096    p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
1097    p.add_argument('block_size', help='Block size for this pmem pool', type=int)
1098    p.set_defaults(func=create_pmem_pool)
1099
1100    @call_cmd
1101    def pmem_pool_info(args):
1102        print_dict(rpc.pmem.pmem_pool_info(args.client,
1103                                           pmem_file=args.pmem_file))
1104
1105    p = subparsers.add_parser('pmem_pool_info', help='Display pmem pool info and check consistency')
1106    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1107    p.set_defaults(func=pmem_pool_info)
1108
1109    @call_cmd
1110    def delete_pmem_pool(args):
1111        rpc.pmem.delete_pmem_pool(args.client,
1112                                  pmem_file=args.pmem_file)
1113
1114    p = subparsers.add_parser('delete_pmem_pool', help='Delete pmem pool')
1115    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1116    p.set_defaults(func=delete_pmem_pool)
1117
1118    # subsystem
1119    @call_cmd
1120    def get_subsystems(args):
1121        print_dict(rpc.subsystem.get_subsystems(args.client))
1122
1123    p = subparsers.add_parser('get_subsystems', help="""Print subsystems array in initialization order. Each subsystem
1124    entry contain (unsorted) array of subsystems it depends on.""")
1125    p.set_defaults(func=get_subsystems)
1126
1127    @call_cmd
1128    def get_subsystem_config(args):
1129        print_dict(rpc.subsystem.get_subsystem_config(args.client, args.name))
1130
1131    p = subparsers.add_parser('get_subsystem_config', help="""Print subsystem configuration""")
1132    p.add_argument('name', help='Name of subsystem to query')
1133    p.set_defaults(func=get_subsystem_config)
1134
1135    # vhost
1136    @call_cmd
1137    def set_vhost_controller_coalescing(args):
1138        rpc.vhost.set_vhost_controller_coalescing(args.client,
1139                                                  ctrlr=args.ctrlr,
1140                                                  delay_base_us=args.delay_base_us,
1141                                                  iops_threshold=args.iops_threshold)
1142
1143    p = subparsers.add_parser('set_vhost_controller_coalescing', help='Set vhost controller coalescing')
1144    p.add_argument('ctrlr', help='controller name')
1145    p.add_argument('delay_base_us', help='Base delay time', type=int)
1146    p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int)
1147    p.set_defaults(func=set_vhost_controller_coalescing)
1148
1149    @call_cmd
1150    def construct_vhost_scsi_controller(args):
1151        rpc.vhost.construct_vhost_scsi_controller(args.client,
1152                                                  ctrlr=args.ctrlr,
1153                                                  cpumask=args.cpumask)
1154
1155    p = subparsers.add_parser(
1156        'construct_vhost_scsi_controller', help='Add new vhost controller')
1157    p.add_argument('ctrlr', help='controller name')
1158    p.add_argument('--cpumask', help='cpu mask for this controller')
1159    p.set_defaults(func=construct_vhost_scsi_controller)
1160
1161    @call_cmd
1162    def add_vhost_scsi_lun(args):
1163        rpc.vhost.add_vhost_scsi_lun(args.client,
1164                                     ctrlr=args.ctrlr,
1165                                     scsi_target_num=args.scsi_target_num,
1166                                     bdev_name=args.bdev_name)
1167
1168    p = subparsers.add_parser('add_vhost_scsi_lun',
1169                              help='Add lun to vhost controller')
1170    p.add_argument('ctrlr', help='conntroller name where add lun')
1171    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
1172    p.add_argument('bdev_name', help='bdev name')
1173    p.set_defaults(func=add_vhost_scsi_lun)
1174
1175    @call_cmd
1176    def remove_vhost_scsi_target(args):
1177        rpc.vhost.remove_vhost_scsi_target(args.client,
1178                                           ctrlr=args.ctrlr,
1179                                           scsi_target_num=args.scsi_target_num)
1180
1181    p = subparsers.add_parser('remove_vhost_scsi_target', help='Remove target from vhost controller')
1182    p.add_argument('ctrlr', help='controller name to remove target from')
1183    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
1184    p.set_defaults(func=remove_vhost_scsi_target)
1185
1186    @call_cmd
1187    def construct_vhost_blk_controller(args):
1188        rpc.vhost.construct_vhost_blk_controller(args.client,
1189                                                 ctrlr=args.ctrlr,
1190                                                 dev_name=args.dev_name,
1191                                                 cpumask=args.cpumask,
1192                                                 readonly=args.readonly)
1193
1194    p = subparsers.add_parser('construct_vhost_blk_controller', help='Add a new vhost block controller')
1195    p.add_argument('ctrlr', help='controller name')
1196    p.add_argument('dev_name', help='device name')
1197    p.add_argument('--cpumask', help='cpu mask for this controller')
1198    p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only')
1199    p.set_defaults(func=construct_vhost_blk_controller)
1200
1201    @call_cmd
1202    def construct_vhost_nvme_controller(args):
1203        rpc.vhost.construct_vhost_nvme_controller(args.client,
1204                                                  ctrlr=args.ctrlr,
1205                                                  io_queues=args.io_queues,
1206                                                  cpumask=args.cpumask)
1207
1208    p = subparsers.add_parser('construct_vhost_nvme_controller', help='Add new vhost controller')
1209    p.add_argument('ctrlr', help='controller name')
1210    p.add_argument('io_queues', help='number of IO queues for the controller', type=int)
1211    p.add_argument('--cpumask', help='cpu mask for this controller')
1212    p.set_defaults(func=construct_vhost_nvme_controller)
1213
1214    @call_cmd
1215    def add_vhost_nvme_ns(args):
1216        rpc.vhost.add_vhost_nvme_ns(args.client,
1217                                    ctrlr=args.ctrlr,
1218                                    bdev_name=args.bdev_name)
1219
1220    p = subparsers.add_parser('add_vhost_nvme_ns', help='Add a Namespace to vhost controller')
1221    p.add_argument('ctrlr', help='conntroller name where add a Namespace')
1222    p.add_argument('bdev_name', help='block device name for a new Namespace')
1223    p.set_defaults(func=add_vhost_nvme_ns)
1224
1225    @call_cmd
1226    def get_vhost_controllers(args):
1227        print_dict(rpc.vhost.get_vhost_controllers(args.client))
1228
1229    p = subparsers.add_parser('get_vhost_controllers', help='List vhost controllers')
1230    p.set_defaults(func=get_vhost_controllers)
1231
1232    @call_cmd
1233    def remove_vhost_controller(args):
1234        rpc.vhost.remove_vhost_controller(args.client,
1235                                          ctrlr=args.ctrlr)
1236
1237    p = subparsers.add_parser('remove_vhost_controller', help='Remove a vhost controller')
1238    p.add_argument('ctrlr', help='controller name')
1239    p.set_defaults(func=remove_vhost_controller)
1240
1241    @call_cmd
1242    def construct_virtio_dev(args):
1243        print_dict(rpc.vhost.construct_virtio_dev(args.client,
1244                                                  trtype=args.trtype,
1245                                                  traddr=args.traddr,
1246                                                  dev_type=args.dev_type,
1247                                                  vq_count=args.vq_count,
1248                                                  vq_size=args.vq_size))
1249
1250    p = subparsers.add_parser('construct_virtio_dev', help="""Construct new virtio device using provided
1251    transport type and device type. In case of SCSI device type this implies scan and add bdevs offered by
1252    remote side. Result is array of added bdevs.""")
1253    p.add_argument('name', help="Use this name as base for new created bdevs")
1254    p.add_argument('-t', '--trtype',
1255                   help='Virtio target transport type: pci or user', required=True)
1256    p.add_argument('-a', '--traddr',
1257                   help='Transport type specific target address: e.g. UNIX domain socket path or BDF', required=True)
1258    p.add_argument('-d', '--dev-type',
1259                   help='Device type: blk or scsi', required=True)
1260    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1261    p.add_argument('--vq-size', help='Size of each queue', type=int)
1262    p.set_defaults(func=construct_virtio_dev)
1263
1264    @call_cmd
1265    def construct_virtio_user_scsi_bdev(args):
1266        print_dict(rpc.vhost.construct_virtio_user_scsi_bdev(args.client,
1267                                                             path=args.path,
1268                                                             name=args.name,
1269                                                             vq_count=args.vq_count,
1270                                                             vq_size=args.vq_size))
1271
1272    p = subparsers.add_parser('construct_virtio_user_scsi_bdev', help="""Connect to virtio user scsi device.
1273    This imply scan and add bdevs offered by remote side.
1274    Result is array of added bdevs.""")
1275    p.add_argument('path', help='Path to Virtio SCSI socket')
1276    p.add_argument('name', help="""Use this name as base instead of 'VirtioScsiN'
1277    Base will be used to construct new bdev's found on target by adding 't<TARGET_ID>' sufix.""")
1278    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1279    p.add_argument('--vq-size', help='Size of each queue', type=int)
1280    p.set_defaults(func=construct_virtio_user_scsi_bdev)
1281
1282    @call_cmd
1283    def construct_virtio_pci_scsi_bdev(args):
1284        print_dict(rpc.vhost.construct_virtio_pci_scsi_bdev(args.client,
1285                                                            pci_address=args.pci_address,
1286                                                            name=args.name))
1287
1288    p = subparsers.add_parser('construct_virtio_pci_scsi_bdev', help="""Create a Virtio
1289    SCSI device from a virtio-pci device.""")
1290    p.add_argument('pci_address', help="""PCI address in domain:bus:device.function format or
1291    domain.bus.device.function format""")
1292    p.add_argument('name', help="""Name for the virtio device.
1293    It will be inhereted by all created bdevs, which are named n the following format: <name>t<target_id>""")
1294    p.set_defaults(func=construct_virtio_pci_scsi_bdev)
1295
1296    @call_cmd
1297    def get_virtio_scsi_devs(args):
1298        print_dict(rpc.vhost.get_virtio_scsi_devs(args.client))
1299
1300    p = subparsers.add_parser('get_virtio_scsi_devs', help='List all Virtio-SCSI devices.')
1301    p.set_defaults(func=get_virtio_scsi_devs)
1302
1303    @call_cmd
1304    def remove_virtio_scsi_bdev(args):
1305        rpc.vhost.remove_virtio_scsi_bdev(args.client,
1306                                          name=args.name)
1307
1308    p = subparsers.add_parser('remove_virtio_scsi_bdev', help="""Remove a Virtio-SCSI device
1309    This will delete all bdevs exposed by this device""")
1310    p.add_argument('name', help='Virtio device name. E.g. VirtioUser0')
1311    p.set_defaults(func=remove_virtio_scsi_bdev)
1312
1313    @call_cmd
1314    def construct_virtio_user_blk_bdev(args):
1315        print_dict(rpc.vhost.construct_virtio_user_blk_bdev(args.client,
1316                                                            path=args.path,
1317                                                            name=args.name,
1318                                                            vq_count=args.vq_count,
1319                                                            vq_size=args.vq_size))
1320
1321    p = subparsers.add_parser('construct_virtio_user_blk_bdev', help='Connect to a virtio user blk device.')
1322    p.add_argument('path', help='Path to Virtio BLK socket')
1323    p.add_argument('name', help='Name for the bdev')
1324    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1325    p.add_argument('--vq-size', help='Size of each queue', type=int)
1326    p.set_defaults(func=construct_virtio_user_blk_bdev)
1327
1328    @call_cmd
1329    def construct_virtio_pci_blk_bdev(args):
1330        print_dict(rpc.vhost.construct_virtio_pci_blk_bdev(args.client,
1331                                                           pci_address=args.pci_address,
1332                                                           name=args.name))
1333
1334    p = subparsers.add_parser('construct_virtio_pci_blk_bdev', help='Create a Virtio Blk device from a virtio-pci device.')
1335    p.add_argument('pci_address', help="""PCI address in domain:bus:device.function format or
1336    domain.bus.device.function format""")
1337    p.add_argument('name', help='Name for the bdev')
1338    p.set_defaults(func=construct_virtio_pci_blk_bdev)
1339
1340    # ioat
1341    @call_cmd
1342    def scan_ioat_copy_engine(args):
1343        pci_whitelist = []
1344        if args.pci_whitelist:
1345            for w in args.pci_whitelist.strip().split(" "):
1346                pci_whitelist.append(w)
1347        rpc.ioat.scan_ioat_copy_engine(args.client, pci_whitelist)
1348
1349    p = subparsers.add_parser('scan_ioat_copy_engine', help='Set scan and enable IOAT copy engine offload.')
1350    p.add_argument('-w', '--pci-whitelist', help="""Whitespace-separated list of PCI addresses in
1351    domain:bus:device.function format or domain.bus.device.function format""")
1352    p.set_defaults(func=scan_ioat_copy_engine)
1353
1354    args = parser.parse_args()
1355
1356    try:
1357        args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.verbose, args.timeout)
1358    except JSONRPCException as ex:
1359        print(ex.message)
1360        exit(1)
1361    args.func(args)
1362