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