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