xref: /spdk/scripts/rpc.py (revision 65e5bbdc059f740cbbe8271ddec0929ea9b9624e)
1#!/usr/bin/env python3
2
3from rpc.client import print_dict, JSONRPCException
4
5import argparse
6import rpc
7import sys
8
9try:
10    from shlex import quote
11except ImportError:
12    from pipes import quote
13
14
15def print_array(a):
16    print(" ".join((quote(v) for v in a)))
17
18
19def call_cmd(func):
20    def rpc_cmd(*args, **kwargs):
21        try:
22            func(*args, **kwargs)
23        except JSONRPCException as ex:
24            print(ex.message)
25            exit(1)
26    return rpc_cmd
27
28
29if __name__ == "__main__":
30    parser = argparse.ArgumentParser(
31        description='SPDK RPC command line interface')
32    parser.add_argument('-s', dest='server_addr',
33                        help='RPC server address', default='/var/tmp/spdk.sock')
34    parser.add_argument('-p', dest='port',
35                        help='RPC port number (if server_addr is IP address)',
36                        default=5260, type=int)
37    parser.add_argument('-t', dest='timeout',
38                        help='Timout as a floating point number expressed in seconds waiting for reponse. Default: 60.0',
39                        default=60.0, type=float)
40    parser.add_argument('-v', dest='verbose',
41                        help='Verbose mode', action='store_true')
42    subparsers = parser.add_subparsers(help='RPC methods')
43
44    @call_cmd
45    def start_subsystem_init(args):
46        rpc.start_subsystem_init(args.client)
47
48    p = subparsers.add_parser('start_subsystem_init', help='Start initialization of subsystems')
49    p.set_defaults(func=start_subsystem_init)
50
51    @call_cmd
52    def get_rpc_methods(args):
53        print_dict(rpc.get_rpc_methods(args.client,
54                                       current=args.current))
55
56    p = subparsers.add_parser('get_rpc_methods', help='Get list of supported RPC methods')
57    p.add_argument('-c', '--current', help='Get list of RPC methods only callable in the current state.', action='store_true')
58    p.set_defaults(func=get_rpc_methods)
59
60    @call_cmd
61    def save_config(args):
62        rpc.save_config(args.client,
63                        sys.stdout,
64                        indent=args.indent)
65
66    p = subparsers.add_parser('save_config', help="""Write current (live) configuration of SPDK subsystems and targets to stdout.
67    """)
68    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. Default indent level is 2.
69    """, type=int, default=2)
70    p.set_defaults(func=save_config)
71
72    @call_cmd
73    def load_config(args):
74        rpc.load_config(args.client, sys.stdin)
75
76    p = subparsers.add_parser('load_config', help="""Configure SPDK subsystems and targets using JSON RPC read from stdin.""")
77    p.set_defaults(func=load_config)
78
79    @call_cmd
80    def save_subsystem_config(args):
81        rpc.save_subsystem_config(args.client,
82                                  sys.stdout,
83                                  indent=args.indent,
84                                  name=args.name)
85
86    p = subparsers.add_parser('save_subsystem_config', help="""Write current (live) configuration of SPDK subsystem to stdout.
87    """)
88    p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. Default indent level is 2.
89    """, type=int, default=2)
90    p.add_argument('-n', '--name', help='Name of subsystem', required=True)
91    p.set_defaults(func=save_subsystem_config)
92
93    @call_cmd
94    def load_subsystem_config(args):
95        rpc.load_subsystem_config(args.client,
96                                  sys.stdin)
97
98    p = subparsers.add_parser('load_subsystem_config', help="""Configure SPDK subsystem using JSON RPC read from stdin.""")
99    p.set_defaults(func=load_subsystem_config)
100
101    # app
102    @call_cmd
103    def kill_instance(args):
104        rpc.app.kill_instance(args.client,
105                              sig_name=args.sig_name)
106
107    p = subparsers.add_parser('kill_instance', help='Send signal to instance')
108    p.add_argument('sig_name', help='signal will be sent to server.')
109    p.set_defaults(func=kill_instance)
110
111    @call_cmd
112    def context_switch_monitor(args):
113        enabled = None
114        if args.enable:
115            enabled = True
116        if args.disable:
117            enabled = False
118        print_dict(rpc.app.context_switch_monitor(args.client,
119                                                  enabled=enabled))
120
121    p = subparsers.add_parser('context_switch_monitor', help='Control whether the context switch monitor is enabled')
122    p.add_argument('-e', '--enable', action='store_true', help='Enable context switch monitoring')
123    p.add_argument('-d', '--disable', action='store_true', help='Disable context switch monitoring')
124    p.set_defaults(func=context_switch_monitor)
125
126    # bdev
127    @call_cmd
128    def set_bdev_options(args):
129        rpc.bdev.set_bdev_options(args.client,
130                                  bdev_io_pool_size=args.bdev_io_pool_size,
131                                  bdev_io_cache_size=args.bdev_io_cache_size)
132
133    p = subparsers.add_parser('set_bdev_options', help="""Set options of bdev subsystem""")
134    p.add_argument('-p', '--bdev-io-pool-size', help='Number of bdev_io structures in shared buffer pool', type=int)
135    p.add_argument('-c', '--bdev-io-cache-size', help='Maximum number of bdev_io structures cached per thread', type=int)
136    p.set_defaults(func=set_bdev_options)
137
138    @call_cmd
139    def construct_crypto_bdev(args):
140        print(rpc.bdev.construct_crypto_bdev(args.client,
141                                             base_bdev_name=args.base_bdev_name,
142                                             name=args.name,
143                                             crypto_pmd=args.crypto_pmd,
144                                             key=args.key))
145    p = subparsers.add_parser('construct_crypto_bdev',
146                              help='Add a crypto vbdev')
147    p.add_argument('-b', '--base_bdev_name', help="Name of the base bdev")
148    p.add_argument('-c', '--name', help="Name of the crypto vbdev")
149    p.add_argument('-d', '--crypto_pmd', help="Name of the crypto device driver")
150    p.add_argument('-k', '--key', help="Key")
151    p.set_defaults(func=construct_crypto_bdev)
152
153    @call_cmd
154    def delete_crypto_bdev(args):
155        rpc.bdev.delete_crypto_bdev(args.client,
156                                    name=args.name)
157
158    p = subparsers.add_parser('delete_crypto_bdev', help='Delete a crypto disk')
159    p.add_argument('name', help='crypto bdev name')
160    p.set_defaults(func=delete_crypto_bdev)
161
162    @call_cmd
163    def construct_malloc_bdev(args):
164        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
165        print(rpc.bdev.construct_malloc_bdev(args.client,
166                                             num_blocks=int(num_blocks),
167                                             block_size=args.block_size,
168                                             name=args.name,
169                                             uuid=args.uuid))
170    p = subparsers.add_parser('construct_malloc_bdev',
171                              help='Add a bdev with malloc backend')
172    p.add_argument('-b', '--name', help="Name of the bdev")
173    p.add_argument('-u', '--uuid', help="UUID of the bdev")
174    p.add_argument(
175        'total_size', help='Size of malloc bdev in MB (float > 0)', type=float)
176    p.add_argument('block_size', help='Block size for this bdev', type=int)
177    p.set_defaults(func=construct_malloc_bdev)
178
179    @call_cmd
180    def delete_malloc_bdev(args):
181        rpc.bdev.delete_malloc_bdev(args.client,
182                                    name=args.name)
183
184    p = subparsers.add_parser('delete_malloc_bdev', help='Delete a malloc disk')
185    p.add_argument('name', help='malloc bdev name')
186    p.set_defaults(func=delete_malloc_bdev)
187
188    @call_cmd
189    def construct_null_bdev(args):
190        num_blocks = (args.total_size * 1024 * 1024) // args.block_size
191        print(rpc.bdev.construct_null_bdev(args.client,
192                                           num_blocks=num_blocks,
193                                           block_size=args.block_size,
194                                           name=args.name,
195                                           uuid=args.uuid))
196
197    p = subparsers.add_parser('construct_null_bdev',
198                              help='Add a bdev with null backend')
199    p.add_argument('name', help='Block device name')
200    p.add_argument('-u', '--uuid', help='UUID of the bdev')
201    p.add_argument(
202        'total_size', help='Size of null bdev in MB (int > 0)', type=int)
203    p.add_argument('block_size', help='Block size for this bdev', type=int)
204    p.set_defaults(func=construct_null_bdev)
205
206    @call_cmd
207    def delete_null_bdev(args):
208        rpc.bdev.delete_null_bdev(args.client,
209                                  name=args.name)
210
211    p = subparsers.add_parser('delete_null_bdev', help='Delete a null bdev')
212    p.add_argument('name', help='null bdev name')
213    p.set_defaults(func=delete_null_bdev)
214
215    @call_cmd
216    def construct_aio_bdev(args):
217        print(rpc.bdev.construct_aio_bdev(args.client,
218                                          filename=args.filename,
219                                          name=args.name,
220                                          block_size=args.block_size))
221
222    p = subparsers.add_parser('construct_aio_bdev',
223                              help='Add a bdev with aio backend')
224    p.add_argument('filename', help='Path to device or file (ex: /dev/sda)')
225    p.add_argument('name', help='Block device name')
226    p.add_argument('block_size', help='Block size for this bdev', type=int, nargs='?', default=0)
227    p.set_defaults(func=construct_aio_bdev)
228
229    @call_cmd
230    def delete_aio_bdev(args):
231        rpc.bdev.delete_aio_bdev(args.client,
232                                 name=args.name)
233
234    p = subparsers.add_parser('delete_aio_bdev', help='Delete an aio disk')
235    p.add_argument('name', help='aio bdev name')
236    p.set_defaults(func=delete_aio_bdev)
237
238    @call_cmd
239    def set_bdev_nvme_options(args):
240        rpc.bdev.set_bdev_nvme_options(args.client,
241                                       action_on_timeout=args.action_on_timeout,
242                                       timeout_us=args.timeout_us,
243                                       retry_count=args.retry_count,
244                                       nvme_adminq_poll_period_us=args.nvme_adminq_poll_period_us)
245
246    p = subparsers.add_parser('set_bdev_nvme_options',
247                              help='Set options for the bdev nvme type. This is startup command.')
248    p.add_argument('-a', '--action-on-timeout',
249                   help="Action to take on command time out. Valid valies are: none, reset, abort")
250    p.add_argument('-t', '--timeout-us',
251                   help="Timeout for each command, in microseconds. If 0, don't track timeouts.", type=int)
252    p.add_argument('-n', '--retry-count',
253                   help='the number of attempts per I/O when an I/O fails', type=int)
254    p.add_argument('-p', '--nvme-adminq-poll-period-us',
255                   help='How often the admin queue is polled for asynchronous events', type=int)
256    p.set_defaults(func=set_bdev_nvme_options)
257
258    @call_cmd
259    def set_bdev_nvme_hotplug(args):
260        rpc.bdev.set_bdev_nvme_hotplug(args.client, enable=args.enable, period_us=args.period_us)
261
262    p = subparsers.add_parser('set_bdev_nvme_hotplug',
263                              help='Set hotplug options for bdev nvme type.')
264    p.add_argument('-d', '--disable', dest='enable', default=False, action='store_false', help="Disable hotplug (default)")
265    p.add_argument('-e', '--enable', dest='enable', action='store_true', help="Enable hotplug")
266    p.add_argument('-r', '--period-us',
267                   help='How often the hotplug is processed for insert and remove events', type=int)
268    p.set_defaults(func=set_bdev_nvme_hotplug)
269
270    @call_cmd
271    def construct_nvme_bdev(args):
272        print_array(rpc.bdev.construct_nvme_bdev(args.client,
273                                                 name=args.name,
274                                                 trtype=args.trtype,
275                                                 traddr=args.traddr,
276                                                 adrfam=args.adrfam,
277                                                 trsvcid=args.trsvcid,
278                                                 subnqn=args.subnqn))
279
280    p = subparsers.add_parser('construct_nvme_bdev',
281                              help='Add bdev with nvme backend')
282    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
283    p.add_argument('-t', '--trtype',
284                   help='NVMe-oF target trtype: e.g., rdma, pcie', required=True)
285    p.add_argument('-a', '--traddr',
286                   help='NVMe-oF target address: e.g., an ip address or BDF', required=True)
287    p.add_argument('-f', '--adrfam',
288                   help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
289    p.add_argument('-s', '--trsvcid',
290                   help='NVMe-oF target trsvcid: e.g., a port number')
291    p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
292    p.set_defaults(func=construct_nvme_bdev)
293
294    @call_cmd
295    def get_nvme_controllers(args):
296        print_dict(rpc.nvme.get_nvme_controllers(args.client,
297                                                 name=args.name))
298
299    p = subparsers.add_parser(
300        'get_nvme_controllers', help='Display current NVMe controllers list or required NVMe controller')
301    p.add_argument('-n', '--name', help="Name of the NVMe controller. Example: Nvme0", required=False)
302    p.set_defaults(func=get_nvme_controllers)
303
304    @call_cmd
305    def delete_nvme_controller(args):
306        rpc.bdev.delete_nvme_controller(args.client,
307                                        name=args.name)
308
309    p = subparsers.add_parser('delete_nvme_controller',
310                              help='Delete a NVMe controller using controller name')
311    p.add_argument('name', help="Name of the controller")
312    p.set_defaults(func=delete_nvme_controller)
313
314    @call_cmd
315    def construct_rbd_bdev(args):
316        print(rpc.bdev.construct_rbd_bdev(args.client,
317                                          name=args.name,
318                                          pool_name=args.pool_name,
319                                          rbd_name=args.rbd_name,
320                                          block_size=args.block_size))
321
322    p = subparsers.add_parser('construct_rbd_bdev',
323                              help='Add a bdev with ceph rbd backend')
324    p.add_argument('-b', '--name', help="Name of the bdev", required=False)
325    p.add_argument('pool_name', help='rbd pool name')
326    p.add_argument('rbd_name', help='rbd image name')
327    p.add_argument('block_size', help='rbd block size', type=int)
328    p.set_defaults(func=construct_rbd_bdev)
329
330    @call_cmd
331    def delete_rbd_bdev(args):
332        rpc.bdev.delete_rbd_bdev(args.client,
333                                 name=args.name)
334
335    p = subparsers.add_parser('delete_rbd_bdev', help='Delete a rbd bdev')
336    p.add_argument('name', help='rbd bdev name')
337    p.set_defaults(func=delete_rbd_bdev)
338
339    @call_cmd
340    def construct_error_bdev(args):
341        print(rpc.bdev.construct_error_bdev(args.client,
342                                            base_name=args.base_name))
343
344    p = subparsers.add_parser('construct_error_bdev',
345                              help='Add bdev with error injection backend')
346    p.add_argument('base_name', help='base bdev name')
347    p.set_defaults(func=construct_error_bdev)
348
349    @call_cmd
350    def delete_error_bdev(args):
351        rpc.bdev.delete_error_bdev(args.client,
352                                   name=args.name)
353
354    p = subparsers.add_parser('delete_error_bdev', help='Delete an error bdev')
355    p.add_argument('name', help='error bdev name')
356    p.set_defaults(func=delete_error_bdev)
357
358    @call_cmd
359    def construct_iscsi_bdev(args):
360        print(rpc.bdev.construct_iscsi_bdev(args.client,
361                                            name=args.name,
362                                            url=args.url,
363                                            initiator_iqn=args.initiator_iqn))
364
365    p = subparsers.add_parser('construct_iscsi_bdev',
366                              help='Add bdev with iSCSI initiator backend')
367    p.add_argument('-b', '--name', help="Name of the bdev", required=True)
368    p.add_argument('-i', '--initiator-iqn', help="Initiator IQN", required=True)
369    p.add_argument('--url', help="iSCSI Lun URL", required=True)
370    p.set_defaults(func=construct_iscsi_bdev)
371
372    @call_cmd
373    def delete_iscsi_bdev(args):
374        rpc.bdev.delete_iscsi_bdev(args.client,
375                                   name=args.name)
376
377    p = subparsers.add_parser('delete_iscsi_bdev', help='Delete an iSCSI bdev')
378    p.add_argument('name', help='iSCSI bdev name')
379    p.set_defaults(func=delete_iscsi_bdev)
380
381    @call_cmd
382    def construct_pmem_bdev(args):
383        print(rpc.bdev.construct_pmem_bdev(args.client,
384                                           pmem_file=args.pmem_file,
385                                           name=args.name))
386
387    p = subparsers.add_parser('construct_pmem_bdev', help='Add a bdev with pmem backend')
388    p.add_argument('pmem_file', help='Path to pmemblk pool file')
389    p.add_argument('-n', '--name', help='Block device name', required=True)
390    p.set_defaults(func=construct_pmem_bdev)
391
392    @call_cmd
393    def delete_pmem_bdev(args):
394        rpc.bdev.delete_pmem_bdev(args.client,
395                                  name=args.name)
396
397    p = subparsers.add_parser('delete_pmem_bdev', help='Delete a pmem bdev')
398    p.add_argument('name', help='pmem bdev name')
399    p.set_defaults(func=delete_pmem_bdev)
400
401    @call_cmd
402    def construct_passthru_bdev(args):
403        print(rpc.bdev.construct_passthru_bdev(args.client,
404                                               base_bdev_name=args.base_bdev_name,
405                                               passthru_bdev_name=args.passthru_bdev_name))
406
407    p = subparsers.add_parser('construct_passthru_bdev',
408                              help='Add a pass through bdev on existing bdev')
409    p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True)
410    p.add_argument('-p', '--passthru-bdev-name', help="Name of the pass through bdev", required=True)
411    p.set_defaults(func=construct_passthru_bdev)
412
413    @call_cmd
414    def delete_passthru_bdev(args):
415        rpc.bdev.delete_passthru_bdev(args.client,
416                                      name=args.name)
417
418    p = subparsers.add_parser('delete_passthru_bdev', help='Delete a pass through bdev')
419    p.add_argument('name', help='pass through bdev name')
420    p.set_defaults(func=delete_passthru_bdev)
421
422    @call_cmd
423    def get_bdevs(args):
424        print_dict(rpc.bdev.get_bdevs(args.client,
425                                      name=args.name))
426
427    p = subparsers.add_parser(
428        'get_bdevs', help='Display current blockdev list or required blockdev')
429    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
430    p.set_defaults(func=get_bdevs)
431
432    @call_cmd
433    def get_bdevs_iostat(args):
434        print_dict(rpc.bdev.get_bdevs_iostat(args.client,
435                                             name=args.name))
436
437    p = subparsers.add_parser(
438        'get_bdevs_iostat', help='Display current I/O statistics of all the blockdevs or required blockdev.')
439    p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
440    p.set_defaults(func=get_bdevs_iostat)
441
442    @call_cmd
443    def delete_bdev(args):
444        rpc.bdev.delete_bdev(args.client,
445                             bdev_name=args.bdev_name)
446
447    p = subparsers.add_parser('delete_bdev', help='Delete a blockdev')
448    p.add_argument(
449        'bdev_name', help='Blockdev name to be deleted. Example: Malloc0.')
450    p.set_defaults(func=delete_bdev)
451
452    @call_cmd
453    def set_bdev_qd_sampling_period(args):
454        rpc.bdev.set_bdev_qd_sampling_period(args.client,
455                                             name=args.name,
456                                             period=args.period)
457
458    p = subparsers.add_parser('set_bdev_qd_sampling_period', help="Enable or disable tracking of a bdev's queue depth.")
459    p.add_argument('name', help='Blockdev name. Example: Malloc0')
460    p.add_argument('period', help='Period with which to poll the block device queue depth in microseconds.'
461                   ' If set to 0, polling will be disabled.',
462                   type=int)
463    p.set_defaults(func=set_bdev_qd_sampling_period)
464
465    @call_cmd
466    def set_bdev_qos_limit(args):
467        rpc.bdev.set_bdev_qos_limit(args.client,
468                                    name=args.name,
469                                    rw_ios_per_sec=args.rw_ios_per_sec,
470                                    rw_mbytes_per_sec=args.rw_mbytes_per_sec)
471
472    p = subparsers.add_parser('set_bdev_qos_limit', help='Set QoS rate limit on a blockdev')
473    p.add_argument('name', help='Blockdev name to set QoS. Example: Malloc0')
474    p.add_argument('--rw_ios_per_sec',
475                   help='R/W IOs per second limit (>=10000, example: 20000). 0 means unlimited.',
476                   type=int, required=False)
477    p.add_argument('--rw_mbytes_per_sec',
478                   help="R/W megabytes per second limit (>=10, example: 100). 0 means unlimited.",
479                   type=int, required=False)
480    p.set_defaults(func=set_bdev_qos_limit)
481
482    @call_cmd
483    def bdev_inject_error(args):
484        rpc.bdev.bdev_inject_error(args.client,
485                                   name=args.name,
486                                   io_type=args.io_type,
487                                   error_type=args.error_type,
488                                   num=args.num)
489
490    p = subparsers.add_parser('bdev_inject_error', help='bdev inject error')
491    p.add_argument('name', help="""the name of the error injection bdev""")
492    p.add_argument('io_type', help="""io_type: 'clear' 'read' 'write' 'unmap' 'flush' 'all'""")
493    p.add_argument('error_type', help="""error_type: 'failure' 'pending'""")
494    p.add_argument(
495        '-n', '--num', help='the number of commands you want to fail', type=int, default=1)
496    p.set_defaults(func=bdev_inject_error)
497
498    @call_cmd
499    def apply_firmware(args):
500        print_dict(rpc.bdev.apply_firmware(args.client,
501                                           bdev_name=args.bdev_name,
502                                           filename=args.filename))
503
504    p = subparsers.add_parser('apply_firmware', help='Download and commit firmware to NVMe device')
505    p.add_argument('filename', help='filename of the firmware to download')
506    p.add_argument('bdev_name', help='name of the NVMe device')
507    p.set_defaults(func=apply_firmware)
508
509    # iSCSI
510    @call_cmd
511    def set_iscsi_options(args):
512        rpc.iscsi.set_iscsi_options(
513            args.client,
514            auth_file=args.auth_file,
515            node_base=args.node_base,
516            nop_timeout=args.nop_timeout,
517            nop_in_interval=args.nop_in_interval,
518            disable_chap=args.disable_chap,
519            require_chap=args.require_chap,
520            mutual_chap=args.mutual_chap,
521            chap_group=args.chap_group,
522            max_sessions=args.max_sessions,
523            max_queue_depth=args.max_queue_depth,
524            max_connections_per_session=args.max_connections_per_session,
525            default_time2wait=args.default_time2wait,
526            default_time2retain=args.default_time2retain,
527            first_burst_length=args.first_burst_length,
528            immediate_data=args.immediate_data,
529            error_recovery_level=args.error_recovery_level,
530            allow_duplicated_isid=args.allow_duplicated_isid,
531            min_connections_per_core=args.min_connections_per_core)
532
533    p = subparsers.add_parser('set_iscsi_options', help="""Set options of iSCSI subsystem""")
534    p.add_argument('-f', '--auth-file', help='Path to CHAP shared secret file')
535    p.add_argument('-b', '--node-base', help='Prefix of the name of iSCSI target node')
536    p.add_argument('-o', '--nop-timeout', help='Timeout in seconds to nop-in request to the initiator', type=int)
537    p.add_argument('-n', '--nop-in-interval', help='Time interval in secs between nop-in requests by the target', type=int)
538    p.add_argument('-d', '--disable-chap', help="""CHAP for discovery session should be disabled.
539    *** Mutually exclusive with --require-chap""", action='store_true')
540    p.add_argument('-r', '--require-chap', help="""CHAP for discovery session should be required.
541    *** Mutually exclusive with --disable-chap""", action='store_true')
542    p.add_argument('-m', '--mutual-chap', help='CHAP for discovery session should be mutual', action='store_true')
543    p.add_argument('-g', '--chap-group', help="""Authentication group ID for discovery session.
544    *** Authentication group must be precreated ***""", type=int)
545    p.add_argument('-a', '--max-sessions', help='Maximum number of sessions in the host.', type=int)
546    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/Os per queue.', type=int)
547    p.add_argument('-c', '--max-connections-per-session', help='Negotiated parameter, MaxConnections.', type=int)
548    p.add_argument('-w', '--default-time2wait', help='Negotiated parameter, DefaultTime2Wait.', type=int)
549    p.add_argument('-v', '--default-time2retain', help='Negotiated parameter, DefaultTime2Retain.', type=int)
550    p.add_argument('-s', '--first-burst-length', help='Negotiated parameter, FirstBurstLength.', type=int)
551    p.add_argument('-i', '--immediate-data', help='Negotiated parameter, ImmediateData.', action='store_true')
552    p.add_argument('-l', '--error-recovery-level', help='Negotiated parameter, ErrorRecoveryLevel', type=int)
553    p.add_argument('-p', '--allow-duplicated-isid', help='Allow duplicated initiator session ID.', action='store_true')
554    p.add_argument('-u', '--min-connections-per-core', help='Allocation unit of connections per core', type=int)
555    p.set_defaults(func=set_iscsi_options)
556
557    @call_cmd
558    def set_iscsi_discovery_auth(args):
559        rpc.iscsi.set_iscsi_discovery_auth(
560            args.client,
561            disable_chap=args.disable_chap,
562            require_chap=args.require_chap,
563            mutual_chap=args.mutual_chap,
564            chap_group=args.chap_group)
565
566    p = subparsers.add_parser('set_iscsi_discovery_auth', help="""Set CHAP authentication for discovery session.""")
567    p.add_argument('-d', '--disable-chap', help="""CHAP for discovery session should be disabled.
568    *** Mutually exclusive with --require-chap""", action='store_true')
569    p.add_argument('-r', '--require-chap', help="""CHAP for discovery session should be required.
570    *** Mutually exclusive with --disable-chap""", action='store_true')
571    p.add_argument('-m', '--mutual-chap', help='CHAP for discovery session should be mutual', action='store_true')
572    p.add_argument('-g', '--chap-group', help="""Authentication group ID for discovery session.
573    *** Authentication group must be precreated ***""", type=int)
574    p.set_defaults(func=set_iscsi_discovery_auth)
575
576    def add_iscsi_auth_group(args):
577        secrets = None
578        if args.secrets:
579            secrets = [dict(u.split(":") for u in a.split(" ")) for a in args.secrets.split(",")]
580
581        rpc.iscsi.add_iscsi_auth_group(args.client, tag=args.tag, secrets=secrets)
582
583    p = subparsers.add_parser('add_iscsi_auth_group', help='Add authentication group for CHAP authentication.')
584    p.add_argument('tag', help='Authentication group tag (unique, integer > 0).', type=int)
585    p.add_argument('-c', '--secrets', help="""Comma-separated list of CHAP secrets
586<user:user_name secret:chap_secret muser:mutual_user_name msecret:mutual_chap_secret> enclosed in quotes.
587Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 msecret:ms2'""", required=False)
588    p.set_defaults(func=add_iscsi_auth_group)
589
590    @call_cmd
591    def delete_iscsi_auth_group(args):
592        rpc.iscsi.delete_iscsi_auth_group(args.client, tag=args.tag)
593
594    p = subparsers.add_parser('delete_iscsi_auth_group', help='Delete an authentication group.')
595    p.add_argument('tag', help='Authentication group tag', type=int)
596    p.set_defaults(func=delete_iscsi_auth_group)
597
598    @call_cmd
599    def add_secret_to_iscsi_auth_group(args):
600        rpc.iscsi.add_secret_to_iscsi_auth_group(
601            args.client,
602            tag=args.tag,
603            user=args.user,
604            secret=args.secret,
605            muser=args.muser,
606            msecret=args.msecret)
607
608    p = subparsers.add_parser('add_secret_to_iscsi_auth_group', help='Add a secret to an authentication group.')
609    p.add_argument('tag', help='Authentication group tag', type=int)
610    p.add_argument('-u', '--user', help='User name for one-way CHAP authentication', required=True)
611    p.add_argument('-s', '--secret', help='Secret for one-way CHAP authentication', required=True)
612    p.add_argument('-m', '--muser', help='User name for mutual CHAP authentication')
613    p.add_argument('-r', '--msecret', help='Secret for mutual CHAP authentication')
614    p.set_defaults(func=add_secret_to_iscsi_auth_group)
615
616    @call_cmd
617    def delete_secret_from_iscsi_auth_group(args):
618        rpc.iscsi.delete_secret_from_iscsi_auth_group(args.client, tag=args.tag, user=args.user)
619
620    p = subparsers.add_parser('delete_secret_from_iscsi_auth_group', help='Delete a secret from an authentication group.')
621    p.add_argument('tag', help='Authentication group tag', type=int)
622    p.add_argument('-u', '--user', help='User name for one-way CHAP authentication', required=True)
623    p.set_defaults(func=delete_secret_from_iscsi_auth_group)
624
625    @call_cmd
626    def get_iscsi_auth_groups(args):
627        print_dict(rpc.iscsi.get_iscsi_auth_groups(args.client))
628
629    p = subparsers.add_parser('get_iscsi_auth_groups',
630                              help='Display current authentication group configuration')
631    p.set_defaults(func=get_iscsi_auth_groups)
632
633    @call_cmd
634    def get_portal_groups(args):
635        print_dict(rpc.iscsi.get_portal_groups(args.client))
636
637    p = subparsers.add_parser(
638        'get_portal_groups', help='Display current portal group configuration')
639    p.set_defaults(func=get_portal_groups)
640
641    @call_cmd
642    def get_initiator_groups(args):
643        print_dict(rpc.iscsi.get_initiator_groups(args.client))
644
645    p = subparsers.add_parser('get_initiator_groups',
646                              help='Display current initiator group configuration')
647    p.set_defaults(func=get_initiator_groups)
648
649    @call_cmd
650    def get_target_nodes(args):
651        print_dict(rpc.iscsi.get_target_nodes(args.client))
652
653    p = subparsers.add_parser('get_target_nodes', help='Display target nodes')
654    p.set_defaults(func=get_target_nodes)
655
656    @call_cmd
657    def construct_target_node(args):
658        luns = []
659        for u in args.bdev_name_id_pairs.strip().split(" "):
660            bdev_name, lun_id = u.split(":")
661            luns.append({"bdev_name": bdev_name, "lun_id": int(lun_id)})
662
663        pg_ig_maps = []
664        for u in args.pg_ig_mappings.strip().split(" "):
665            pg, ig = u.split(":")
666            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
667
668        rpc.iscsi.construct_target_node(
669            args.client,
670            luns=luns,
671            pg_ig_maps=pg_ig_maps,
672            name=args.name,
673            alias_name=args.alias_name,
674            queue_depth=args.queue_depth,
675            chap_group=args.chap_group,
676            disable_chap=args.disable_chap,
677            require_chap=args.require_chap,
678            mutual_chap=args.mutual_chap,
679            header_digest=args.header_digest,
680            data_digest=args.data_digest)
681
682    p = subparsers.add_parser('construct_target_node',
683                              help='Add a target node')
684    p.add_argument('name', help='Target node name (ASCII)')
685    p.add_argument('alias_name', help='Target node alias name (ASCII)')
686    p.add_argument('bdev_name_id_pairs', help="""Whitespace-separated list of <bdev name:LUN ID> pairs enclosed
687    in quotes.  Format:  'bdev_name0:id0 bdev_name1:id1' etc
688    Example: 'Malloc0:0 Malloc1:1 Malloc5:2'
689    *** The bdevs must pre-exist ***
690    *** LUN0 (id = 0) is required ***
691    *** bdevs names cannot contain space or colon characters ***""")
692    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
693    Whitespace separated, quoted, mapping defined with colon
694    separated list of "tags" (int > 0)
695    Example: '1:1 2:2 2:1'
696    *** The Portal/Initiator Groups must be precreated ***""")
697    p.add_argument('queue_depth', help='Desired target queue depth', type=int)
698    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
699    *** Authentication group must be precreated ***""", type=int, default=0)
700    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
701    *** Mutually exclusive with --require-chap ***""", action='store_true')
702    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
703    *** Mutually exclusive with --disable-chap ***""", action='store_true')
704    p.add_argument(
705        '-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.', action='store_true')
706    p.add_argument('-H', '--header-digest',
707                   help='Header Digest should be required for this target node.', action='store_true')
708    p.add_argument('-D', '--data-digest',
709                   help='Data Digest should be required for this target node.', action='store_true')
710    p.set_defaults(func=construct_target_node)
711
712    @call_cmd
713    def target_node_add_lun(args):
714        rpc.iscsi.target_node_add_lun(
715            args.client,
716            name=args.name,
717            bdev_name=args.bdev_name,
718            lun_id=args.lun_id)
719
720    p = subparsers.add_parser('target_node_add_lun', help='Add LUN to the target node')
721    p.add_argument('name', help='Target node name (ASCII)')
722    p.add_argument('bdev_name', help="""bdev name enclosed in quotes.
723    *** bdev name cannot contain space or colon characters ***""")
724    p.add_argument('-i', dest='lun_id', help="""LUN ID (integer >= 0)
725    *** If LUN ID is omitted or -1, the lowest free one is assigned ***""", type=int, required=False)
726    p.set_defaults(func=target_node_add_lun)
727
728    @call_cmd
729    def set_iscsi_target_node_auth(args):
730        rpc.iscsi.set_iscsi_target_node_auth(
731            args.client,
732            name=args.name,
733            chap_group=args.chap_group,
734            disable_chap=args.disable_chap,
735            require_chap=args.require_chap,
736            mutual_chap=args.mutual_chap)
737
738    p = subparsers.add_parser('set_iscsi_target_node_auth', help='Set CHAP authentication for the target node')
739    p.add_argument('name', help='Target node name (ASCII)')
740    p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node.
741    *** Authentication group must be precreated ***""", type=int, default=0)
742    p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node.
743    *** Mutually exclusive with --require-chap ***""", action='store_true')
744    p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node.
745    *** Mutually exclusive with --disable-chap ***""", action='store_true')
746    p.add_argument('-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.',
747                   action='store_true')
748    p.set_defaults(func=set_iscsi_target_node_auth)
749
750    @call_cmd
751    def add_pg_ig_maps(args):
752        pg_ig_maps = []
753        for u in args.pg_ig_mappings.strip().split(" "):
754            pg, ig = u.split(":")
755            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
756        rpc.iscsi.add_pg_ig_maps(
757            args.client,
758            pg_ig_maps=pg_ig_maps,
759            name=args.name)
760
761    p = subparsers.add_parser('add_pg_ig_maps', help='Add PG-IG maps to the target node')
762    p.add_argument('name', help='Target node name (ASCII)')
763    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
764    Whitespace separated, quoted, mapping defined with colon
765    separated list of "tags" (int > 0)
766    Example: '1:1 2:2 2:1'
767    *** The Portal/Initiator Groups must be precreated ***""")
768    p.set_defaults(func=add_pg_ig_maps)
769
770    @call_cmd
771    def delete_pg_ig_maps(args):
772        pg_ig_maps = []
773        for u in args.pg_ig_mappings.strip().split(" "):
774            pg, ig = u.split(":")
775            pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)})
776        rpc.iscsi.delete_pg_ig_maps(
777            args.client, pg_ig_maps=pg_ig_maps, name=args.name)
778
779    p = subparsers.add_parser('delete_pg_ig_maps', help='Delete PG-IG maps from the target node')
780    p.add_argument('name', help='Target node name (ASCII)')
781    p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
782    Whitespace separated, quoted, mapping defined with colon
783    separated list of "tags" (int > 0)
784    Example: '1:1 2:2 2:1'
785    *** The Portal/Initiator Groups must be precreated ***""")
786    p.set_defaults(func=delete_pg_ig_maps)
787
788    @call_cmd
789    def add_portal_group(args):
790        portals = []
791        for p in args.portal_list:
792            ip, separator, port_cpumask = p.rpartition(':')
793            split_port_cpumask = port_cpumask.split('@')
794            if len(split_port_cpumask) == 1:
795                port = port_cpumask
796                portals.append({'host': ip, 'port': port})
797            else:
798                port = split_port_cpumask[0]
799                cpumask = split_port_cpumask[1]
800                portals.append({'host': ip, 'port': port, 'cpumask': cpumask})
801        rpc.iscsi.add_portal_group(
802            args.client,
803            portals=portals,
804            tag=args.tag)
805
806    p = subparsers.add_parser('add_portal_group', help='Add a portal group')
807    p.add_argument(
808        'tag', help='Portal group tag (unique, integer > 0)', type=int)
809    p.add_argument('portal_list', nargs=argparse.REMAINDER, help="""List of portals in 'host:port@cpumask' format, separated by whitespace
810    (cpumask is optional and can be skipped)
811    Example: '192.168.100.100:3260' '192.168.100.100:3261' '192.168.100.100:3262@0x1""")
812    p.set_defaults(func=add_portal_group)
813
814    @call_cmd
815    def add_initiator_group(args):
816        initiators = []
817        netmasks = []
818        for i in args.initiator_list.strip().split(' '):
819            initiators.append(i)
820        for n in args.netmask_list.strip().split(' '):
821            netmasks.append(n)
822        rpc.iscsi.add_initiator_group(
823            args.client,
824            tag=args.tag,
825            initiators=initiators,
826            netmasks=netmasks)
827
828    p = subparsers.add_parser('add_initiator_group',
829                              help='Add an initiator group')
830    p.add_argument(
831        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
832    p.add_argument('initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
833    enclosed in quotes.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""")
834    p.add_argument('netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
835    Example: '255.255.0.0 255.248.0.0' etc""")
836    p.set_defaults(func=add_initiator_group)
837
838    @call_cmd
839    def add_initiators_to_initiator_group(args):
840        initiators = None
841        netmasks = None
842        if args.initiator_list:
843            initiators = []
844            for i in args.initiator_list.strip().split(' '):
845                initiators.append(i)
846        if args.netmask_list:
847            netmasks = []
848            for n in args.netmask_list.strip().split(' '):
849                netmasks.append(n)
850        rpc.iscsi.add_initiators_to_initiator_group(
851            args.client,
852            tag=args.tag,
853            initiators=initiators,
854            netmasks=netmasks)
855
856    p = subparsers.add_parser('add_initiators_to_initiator_group',
857                              help='Add initiators to an existing initiator group')
858    p.add_argument(
859        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
860    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
861    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
862    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
863    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
864    p.set_defaults(func=add_initiators_to_initiator_group)
865
866    @call_cmd
867    def delete_initiators_from_initiator_group(args):
868        initiators = None
869        netmasks = None
870        if args.initiator_list:
871            initiators = []
872            for i in args.initiator_list.strip().split(' '):
873                initiators.append(i)
874        if args.netmask_list:
875            netmasks = []
876            for n in args.netmask_list.strip().split(' '):
877                netmasks.append(n)
878        rpc.iscsi.delete_initiators_from_initiator_group(
879            args.client,
880            tag=args.tag,
881            initiators=initiators,
882            netmasks=netmasks)
883
884    p = subparsers.add_parser('delete_initiators_from_initiator_group',
885                              help='Delete initiators from an existing initiator group')
886    p.add_argument(
887        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
888    p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
889    enclosed in quotes.  This parameter can be omitted.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False)
890    p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
891    This parameter can be omitted.  Example: '255.255.0.0 255.248.0.0' etc""", required=False)
892    p.set_defaults(func=delete_initiators_from_initiator_group)
893
894    @call_cmd
895    def delete_target_node(args):
896        rpc.iscsi.delete_target_node(
897            args.client, target_node_name=args.target_node_name)
898
899    p = subparsers.add_parser('delete_target_node',
900                              help='Delete a target node')
901    p.add_argument('target_node_name',
902                   help='Target node name to be deleted. Example: iqn.2016-06.io.spdk:disk1.')
903    p.set_defaults(func=delete_target_node)
904
905    @call_cmd
906    def delete_portal_group(args):
907        rpc.iscsi.delete_portal_group(args.client, tag=args.tag)
908
909    p = subparsers.add_parser('delete_portal_group',
910                              help='Delete a portal group')
911    p.add_argument(
912        'tag', help='Portal group tag (unique, integer > 0)', type=int)
913    p.set_defaults(func=delete_portal_group)
914
915    @call_cmd
916    def delete_initiator_group(args):
917        rpc.iscsi.delete_initiator_group(args.client, tag=args.tag)
918
919    p = subparsers.add_parser('delete_initiator_group',
920                              help='Delete an initiator group')
921    p.add_argument(
922        'tag', help='Initiator group tag (unique, integer > 0)', type=int)
923    p.set_defaults(func=delete_initiator_group)
924
925    @call_cmd
926    def get_iscsi_connections(args):
927        print_dict(rpc.iscsi.get_iscsi_connections(args.client))
928
929    p = subparsers.add_parser('get_iscsi_connections',
930                              help='Display iSCSI connections')
931    p.set_defaults(func=get_iscsi_connections)
932
933    @call_cmd
934    def get_iscsi_global_params(args):
935        print_dict(rpc.iscsi.get_iscsi_global_params(args.client))
936
937    p = subparsers.add_parser('get_iscsi_global_params', help='Display iSCSI global parameters')
938    p.set_defaults(func=get_iscsi_global_params)
939
940    @call_cmd
941    def get_scsi_devices(args):
942        print_dict(rpc.iscsi.get_scsi_devices(args.client))
943
944    p = subparsers.add_parser('get_scsi_devices', help='Display SCSI devices')
945    p.set_defaults(func=get_scsi_devices)
946
947    # log
948    @call_cmd
949    def set_trace_flag(args):
950        rpc.log.set_trace_flag(args.client, flag=args.flag)
951
952    p = subparsers.add_parser('set_trace_flag', help='set trace flag')
953    p.add_argument(
954        'flag', help='trace mask we want to set. (for example "nvme").')
955    p.set_defaults(func=set_trace_flag)
956
957    @call_cmd
958    def clear_trace_flag(args):
959        rpc.log.clear_trace_flag(args.client, flag=args.flag)
960
961    p = subparsers.add_parser('clear_trace_flag', help='clear trace flag')
962    p.add_argument(
963        'flag', help='trace mask we want to clear. (for example "nvme").')
964    p.set_defaults(func=clear_trace_flag)
965
966    @call_cmd
967    def get_trace_flags(args):
968        print_dict(rpc.log.get_trace_flags(args.client))
969
970    p = subparsers.add_parser('get_trace_flags', help='get trace flags')
971    p.set_defaults(func=get_trace_flags)
972
973    @call_cmd
974    def set_log_level(args):
975        rpc.log.set_log_level(args.client, level=args.level)
976
977    p = subparsers.add_parser('set_log_level', help='set log level')
978    p.add_argument('level', help='log level we want to set. (for example "DEBUG").')
979    p.set_defaults(func=set_log_level)
980
981    @call_cmd
982    def get_log_level(args):
983        print_dict(rpc.log.get_log_level(args.client))
984
985    p = subparsers.add_parser('get_log_level', help='get log level')
986    p.set_defaults(func=get_log_level)
987
988    @call_cmd
989    def set_log_print_level(args):
990        rpc.log.set_log_print_level(args.client, level=args.level)
991
992    p = subparsers.add_parser('set_log_print_level', help='set log print level')
993    p.add_argument('level', help='log print level we want to set. (for example "DEBUG").')
994    p.set_defaults(func=set_log_print_level)
995
996    @call_cmd
997    def get_log_print_level(args):
998        print_dict(rpc.log.get_log_print_level(args.client))
999
1000    p = subparsers.add_parser('get_log_print_level', help='get log print level')
1001    p.set_defaults(func=get_log_print_level)
1002
1003    # lvol
1004    @call_cmd
1005    def construct_lvol_store(args):
1006        print(rpc.lvol.construct_lvol_store(args.client,
1007                                            bdev_name=args.bdev_name,
1008                                            lvs_name=args.lvs_name,
1009                                            cluster_sz=args.cluster_sz))
1010
1011    p = subparsers.add_parser('construct_lvol_store', help='Add logical volume store on base bdev')
1012    p.add_argument('bdev_name', help='base bdev name')
1013    p.add_argument('lvs_name', help='name for lvol store')
1014    p.add_argument('-c', '--cluster-sz', help='size of cluster (in bytes)', type=int, required=False)
1015    p.set_defaults(func=construct_lvol_store)
1016
1017    @call_cmd
1018    def rename_lvol_store(args):
1019        rpc.lvol.rename_lvol_store(args.client,
1020                                   old_name=args.old_name,
1021                                   new_name=args.new_name)
1022
1023    p = subparsers.add_parser('rename_lvol_store', help='Change logical volume store name')
1024    p.add_argument('old_name', help='old name')
1025    p.add_argument('new_name', help='new name')
1026    p.set_defaults(func=rename_lvol_store)
1027
1028    @call_cmd
1029    def construct_lvol_bdev(args):
1030        print(rpc.lvol.construct_lvol_bdev(args.client,
1031                                           lvol_name=args.lvol_name,
1032                                           size=args.size * 1024 * 1024,
1033                                           thin_provision=args.thin_provision,
1034                                           uuid=args.uuid,
1035                                           lvs_name=args.lvs_name))
1036
1037    p = subparsers.add_parser('construct_lvol_bdev', help='Add a bdev with an logical volume backend')
1038    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1039    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1040    p.add_argument('-t', '--thin-provision', action='store_true', help='create lvol bdev as thin provisioned')
1041    p.add_argument('lvol_name', help='name for this lvol')
1042    p.add_argument('size', help='size in MiB for this bdev', type=int)
1043    p.set_defaults(func=construct_lvol_bdev)
1044
1045    @call_cmd
1046    def snapshot_lvol_bdev(args):
1047        print(rpc.lvol.snapshot_lvol_bdev(args.client,
1048                                          lvol_name=args.lvol_name,
1049                                          snapshot_name=args.snapshot_name))
1050
1051    p = subparsers.add_parser('snapshot_lvol_bdev', help='Create a snapshot of an lvol bdev')
1052    p.add_argument('lvol_name', help='lvol bdev name')
1053    p.add_argument('snapshot_name', help='lvol snapshot name')
1054    p.set_defaults(func=snapshot_lvol_bdev)
1055
1056    @call_cmd
1057    def clone_lvol_bdev(args):
1058        print(rpc.lvol.clone_lvol_bdev(args.client,
1059                                       snapshot_name=args.snapshot_name,
1060                                       clone_name=args.clone_name))
1061
1062    p = subparsers.add_parser('clone_lvol_bdev', help='Create a clone of an lvol snapshot')
1063    p.add_argument('snapshot_name', help='lvol snapshot name')
1064    p.add_argument('clone_name', help='lvol clone name')
1065    p.set_defaults(func=clone_lvol_bdev)
1066
1067    @call_cmd
1068    def rename_lvol_bdev(args):
1069        rpc.lvol.rename_lvol_bdev(args.client,
1070                                  old_name=args.old_name,
1071                                  new_name=args.new_name)
1072
1073    p = subparsers.add_parser('rename_lvol_bdev', help='Change lvol bdev name')
1074    p.add_argument('old_name', help='lvol bdev name')
1075    p.add_argument('new_name', help='new lvol name')
1076    p.set_defaults(func=rename_lvol_bdev)
1077
1078    @call_cmd
1079    def inflate_lvol_bdev(args):
1080        rpc.lvol.inflate_lvol_bdev(args.client,
1081                                   name=args.name)
1082
1083    p = subparsers.add_parser('inflate_lvol_bdev', help='Make thin provisioned lvol a thick provisioned lvol')
1084    p.add_argument('name', help='lvol bdev name')
1085    p.set_defaults(func=inflate_lvol_bdev)
1086
1087    @call_cmd
1088    def decouple_parent_lvol_bdev(args):
1089        rpc.lvol.decouple_parent_lvol_bdev(args.client,
1090                                           name=args.name)
1091
1092    p = subparsers.add_parser('decouple_parent_lvol_bdev', help='Decouple parent of lvol')
1093    p.add_argument('name', help='lvol bdev name')
1094    p.set_defaults(func=decouple_parent_lvol_bdev)
1095
1096    @call_cmd
1097    def resize_lvol_bdev(args):
1098        rpc.lvol.resize_lvol_bdev(args.client,
1099                                  name=args.name,
1100                                  size=args.size * 1024 * 1024)
1101
1102    p = subparsers.add_parser('resize_lvol_bdev', help='Resize existing lvol bdev')
1103    p.add_argument('name', help='lvol bdev name')
1104    p.add_argument('size', help='new size in MiB for this bdev', type=int)
1105    p.set_defaults(func=resize_lvol_bdev)
1106
1107    @call_cmd
1108    def destroy_lvol_bdev(args):
1109        rpc.lvol.destroy_lvol_bdev(args.client,
1110                                   name=args.name)
1111
1112    p = subparsers.add_parser('destroy_lvol_bdev', help='Destroy a logical volume')
1113    p.add_argument('name', help='lvol bdev name')
1114    p.set_defaults(func=destroy_lvol_bdev)
1115
1116    @call_cmd
1117    def destroy_lvol_store(args):
1118        rpc.lvol.destroy_lvol_store(args.client,
1119                                    uuid=args.uuid,
1120                                    lvs_name=args.lvs_name)
1121
1122    p = subparsers.add_parser('destroy_lvol_store', help='Destroy an logical volume store')
1123    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1124    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1125    p.set_defaults(func=destroy_lvol_store)
1126
1127    @call_cmd
1128    def get_lvol_stores(args):
1129        print_dict(rpc.lvol.get_lvol_stores(args.client,
1130                                            uuid=args.uuid,
1131                                            lvs_name=args.lvs_name))
1132
1133    p = subparsers.add_parser('get_lvol_stores', help='Display current logical volume store list')
1134    p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
1135    p.add_argument('-l', '--lvs-name', help='lvol store name', required=False)
1136    p.set_defaults(func=get_lvol_stores)
1137
1138    @call_cmd
1139    def get_raid_bdevs(args):
1140        print_array(rpc.bdev.get_raid_bdevs(args.client,
1141                                            category=args.category))
1142
1143    p = subparsers.add_parser('get_raid_bdevs', help="""This is used to list all the raid bdev names based on the input category
1144    requested. Category should be one of 'all', 'online', 'configuring' or 'offline'. 'all' means all the raid bdevs whether
1145    they are online or configuring or offline. 'online' is the raid bdev which is registered with bdev layer. 'configuring'
1146    is the raid bdev which does not have full configuration discovered yet. 'offline' is the raid bdev which is not registered
1147    with bdev as of now and it has encountered any error or user has requested to offline the raid bdev""")
1148    p.add_argument('category', help='all or online or configuring or offline')
1149    p.set_defaults(func=get_raid_bdevs)
1150
1151    @call_cmd
1152    def construct_raid_bdev(args):
1153        base_bdevs = []
1154        for u in args.base_bdevs.strip().split(" "):
1155            base_bdevs.append(u)
1156
1157        rpc.bdev.construct_raid_bdev(args.client,
1158                                     name=args.name,
1159                                     strip_size=args.strip_size,
1160                                     raid_level=args.raid_level,
1161                                     base_bdevs=base_bdevs)
1162    p = subparsers.add_parser('construct_raid_bdev', help='Construct new raid bdev')
1163    p.add_argument('-n', '--name', help='raid bdev name', required=True)
1164    p.add_argument('-s', '--strip-size', help='strip size in KB', type=int, required=True)
1165    p.add_argument('-r', '--raid-level', help='raid level, only raid level 0 is supported', type=int, required=True)
1166    p.add_argument('-b', '--base-bdevs', help='base bdevs name, whitespace separated list in quotes', required=True)
1167    p.set_defaults(func=construct_raid_bdev)
1168
1169    @call_cmd
1170    def destroy_raid_bdev(args):
1171        rpc.bdev.destroy_raid_bdev(args.client,
1172                                   name=args.name)
1173    p = subparsers.add_parser('destroy_raid_bdev', help='Destroy existing raid bdev')
1174    p.add_argument('name', help='raid bdev name')
1175    p.set_defaults(func=destroy_raid_bdev)
1176
1177    # split
1178    @call_cmd
1179    def construct_split_vbdev(args):
1180        print_array(rpc.bdev.construct_split_vbdev(args.client,
1181                                                   base_bdev=args.base_bdev,
1182                                                   split_count=args.split_count,
1183                                                   split_size_mb=args.split_size_mb))
1184
1185    p = subparsers.add_parser('construct_split_vbdev', help="""Add given disk name to split config. If bdev with base_name
1186    name exist the split bdevs will be created right away, if not split bdevs will be created when base bdev became
1187    available (during examination process).""")
1188    p.add_argument('base_bdev', help='base bdev name')
1189    p.add_argument('-s', '--split-size-mb', help='size in MiB for each bdev', type=int, default=0)
1190    p.add_argument('split_count', help="""Optional - number of split bdevs to create. Total size * split_count must not
1191    exceed the base bdev size.""", type=int)
1192    p.set_defaults(func=construct_split_vbdev)
1193
1194    @call_cmd
1195    def destruct_split_vbdev(args):
1196        rpc.bdev.destruct_split_vbdev(args.client,
1197                                      base_bdev=args.base_bdev)
1198
1199    p = subparsers.add_parser('destruct_split_vbdev', help="""Delete split config with all created splits.""")
1200    p.add_argument('base_bdev', help='base bdev name')
1201    p.set_defaults(func=destruct_split_vbdev)
1202
1203    # nbd
1204    @call_cmd
1205    def start_nbd_disk(args):
1206        print(rpc.nbd.start_nbd_disk(args.client,
1207                                     bdev_name=args.bdev_name,
1208                                     nbd_device=args.nbd_device))
1209
1210    p = subparsers.add_parser('start_nbd_disk', help='Export a bdev as a nbd disk')
1211    p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.')
1212    p.add_argument('nbd_device', help='Nbd device name to be assigned. Example: /dev/nbd0.')
1213    p.set_defaults(func=start_nbd_disk)
1214
1215    @call_cmd
1216    def stop_nbd_disk(args):
1217        rpc.nbd.stop_nbd_disk(args.client,
1218                              nbd_device=args.nbd_device)
1219
1220    p = subparsers.add_parser('stop_nbd_disk', help='Stop a nbd disk')
1221    p.add_argument('nbd_device', help='Nbd device name to be stopped. Example: /dev/nbd0.')
1222    p.set_defaults(func=stop_nbd_disk)
1223
1224    @call_cmd
1225    def get_nbd_disks(args):
1226        print_dict(rpc.nbd.get_nbd_disks(args.client,
1227                                         nbd_device=args.nbd_device))
1228
1229    p = subparsers.add_parser('get_nbd_disks', help='Display full or specified nbd device list')
1230    p.add_argument('-n', '--nbd-device', help="Path of the nbd device. Example: /dev/nbd0", required=False)
1231    p.set_defaults(func=get_nbd_disks)
1232
1233    # net
1234    @call_cmd
1235    def add_ip_address(args):
1236        rpc.net.add_ip_address(args.client, ifc_index=args.ifc_index, ip_addr=args.ip_addr)
1237
1238    p = subparsers.add_parser('add_ip_address', help='Add IP address')
1239    p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
1240    p.add_argument('ip_addr', help='ip address will be added.')
1241    p.set_defaults(func=add_ip_address)
1242
1243    @call_cmd
1244    def delete_ip_address(args):
1245        rpc.net.delete_ip_address(args.client, ifc_index=args.ifc_index, ip_addr=args.ip_addr)
1246
1247    p = subparsers.add_parser('delete_ip_address', help='Delete IP address')
1248    p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
1249    p.add_argument('ip_addr', help='ip address will be deleted.')
1250    p.set_defaults(func=delete_ip_address)
1251
1252    @call_cmd
1253    def get_interfaces(args):
1254        print_dict(rpc.net.get_interfaces(args.client))
1255
1256    p = subparsers.add_parser(
1257        'get_interfaces', help='Display current interface list')
1258    p.set_defaults(func=get_interfaces)
1259
1260    # NVMe-oF
1261    @call_cmd
1262    def set_nvmf_target_options(args):
1263        rpc.nvmf.set_nvmf_target_options(args.client,
1264                                         max_queue_depth=args.max_queue_depth,
1265                                         max_qpairs_per_ctrlr=args.max_qpairs_per_ctrlr,
1266                                         in_capsule_data_size=args.in_capsule_data_size,
1267                                         max_io_size=args.max_io_size,
1268                                         max_subsystems=args.max_subsystems,
1269                                         io_unit_size=args.io_unit_size)
1270
1271    p = subparsers.add_parser('set_nvmf_target_options', help='Set NVMf target options')
1272    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/O per queue', type=int)
1273    p.add_argument('-p', '--max-qpairs-per-ctrlr', help='Max number of SQ and CQ per controller', type=int)
1274    p.add_argument('-c', '--in-capsule-data-size', help='Max number of in-capsule data size', type=int)
1275    p.add_argument('-i', '--max-io-size', help='Max I/O size (bytes)', type=int)
1276    p.add_argument('-x', '--max-subsystems', help='Max number of NVMf subsystems', type=int)
1277    p.add_argument('-u', '--io-unit-size', help='I/O unit size (bytes)', type=int)
1278    p.set_defaults(func=set_nvmf_target_options)
1279
1280    @call_cmd
1281    def set_nvmf_target_config(args):
1282        rpc.nvmf.set_nvmf_target_config(args.client,
1283                                        acceptor_poll_rate=args.acceptor_poll_rate,
1284                                        conn_sched=args.conn_sched)
1285
1286    p = subparsers.add_parser('set_nvmf_target_config', help='Set NVMf target config')
1287    p.add_argument('-r', '--acceptor-poll-rate', help='Polling interval of the acceptor for incoming connections (usec)', type=int)
1288    p.add_argument('-s', '--conn-sched', help="""'roundrobin' - Schedule the incoming connections from any host
1289    on the cores in a round robin manner (Default). 'hostip' - Schedule all the incoming connections from a
1290    specific host IP on to the same core. Connections from different IP will be assigned to cores in a round
1291    robin manner""")
1292    p.set_defaults(func=set_nvmf_target_config)
1293
1294    @call_cmd
1295    def nvmf_create_transport(args):
1296        rpc.nvmf.nvmf_create_transport(args.client,
1297                                       trtype=args.trtype,
1298                                       max_queue_depth=args.max_queue_depth,
1299                                       max_qpairs_per_ctrlr=args.max_qpairs_per_ctrlr,
1300                                       in_capsule_data_size=args.in_capsule_data_size,
1301                                       max_io_size=args.max_io_size,
1302                                       io_unit_size=args.io_unit_size,
1303                                       max_aq_depth=args.max_aq_depth)
1304
1305    p = subparsers.add_parser('nvmf_create_transport', help='Create NVMf transport')
1306    p.add_argument('-t', '--trtype', help='Transport type (ex. RDMA)', type=str, required=True)
1307    p.add_argument('-q', '--max-queue-depth', help='Max number of outstanding I/O per queue', type=int)
1308    p.add_argument('-p', '--max-qpairs-per-ctrlr', help='Max number of SQ and CQ per controller', type=int)
1309    p.add_argument('-c', '--in-capsule-data-size', help='Max number of in-capsule data size', type=int)
1310    p.add_argument('-i', '--max-io-size', help='Max I/O size (bytes)', type=int)
1311    p.add_argument('-u', '--io-unit-size', help='I/O unit size (bytes)', type=int)
1312    p.add_argument('-a', '--max-aq-depth', help='Max number of admin cmds per AQ', type=int)
1313    p.set_defaults(func=nvmf_create_transport)
1314
1315    @call_cmd
1316    def get_nvmf_subsystems(args):
1317        print_dict(rpc.nvmf.get_nvmf_subsystems(args.client))
1318
1319    p = subparsers.add_parser('get_nvmf_subsystems',
1320                              help='Display nvmf subsystems')
1321    p.set_defaults(func=get_nvmf_subsystems)
1322
1323    @call_cmd
1324    def construct_nvmf_subsystem(args):
1325        listen_addresses = None
1326        hosts = None
1327        namespaces = None
1328        if args.listen:
1329            listen_addresses = [
1330                dict(
1331                    u.split(
1332                        ":",
1333                        1) for u in a.split(" ")) for a in args.listen.split(",")]
1334
1335        if args.hosts:
1336            hosts = []
1337            for u in args.hosts.strip().split(" "):
1338                hosts.append(u)
1339
1340        if args.namespaces:
1341            namespaces = []
1342            for u in args.namespaces.strip().split(" "):
1343                bdev_name = u
1344                nsid = 0
1345                if ':' in u:
1346                    (bdev_name, nsid) = u.split(":")
1347
1348                ns_params = {'bdev_name': bdev_name}
1349
1350                nsid = int(nsid)
1351                if nsid != 0:
1352                    ns_params['nsid'] = nsid
1353
1354                namespaces.append(ns_params)
1355
1356        rpc.nvmf.construct_nvmf_subsystem(args.client,
1357                                          nqn=args.nqn,
1358                                          listen_addresses=listen_addresses,
1359                                          hosts=hosts,
1360                                          allow_any_host=args.allow_any_host,
1361                                          serial_number=args.serial_number,
1362                                          namespaces=namespaces,
1363                                          max_namespaces=args.max_namespaces)
1364
1365    p = subparsers.add_parser('construct_nvmf_subsystem', help='Add a nvmf subsystem')
1366    p.add_argument('nqn', help='Target nqn(ASCII)')
1367    p.add_argument('listen', help="""comma-separated list of Listen <trtype:transport_name traddr:address trsvcid:port_id> pairs enclosed
1368    in quotes.  Format:  'trtype:transport0 traddr:traddr0 trsvcid:trsvcid0,trtype:transport1 traddr:traddr1 trsvcid:trsvcid1' etc
1369    Example: 'trtype:RDMA traddr:192.168.100.8 trsvcid:4420,trtype:RDMA traddr:192.168.100.9 trsvcid:4420'""")
1370    p.add_argument('hosts', help="""Whitespace-separated list of host nqn list.
1371    Format:  'nqn1 nqn2' etc
1372    Example: 'nqn.2016-06.io.spdk:init nqn.2016-07.io.spdk:init'""")
1373    p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce host NQN whitelist)")
1374    p.add_argument("-s", "--serial-number", help="""
1375    Format:  'sn' etc
1376    Example: 'SPDK00000000000001'""", default='00000000000000000000')
1377    p.add_argument("-n", "--namespaces", help="""Whitespace-separated list of namespaces
1378    Format:  'bdev_name1[:nsid1] bdev_name2[:nsid2] bdev_name3[:nsid3]' etc
1379    Example: '1:Malloc0 2:Malloc1 3:Malloc2'
1380    *** The devices must pre-exist ***""")
1381    p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed to added during active connection",
1382                   type=int, default=0)
1383    p.set_defaults(func=construct_nvmf_subsystem)
1384
1385    @call_cmd
1386    def nvmf_subsystem_create(args):
1387        rpc.nvmf.nvmf_subsystem_create(args.client,
1388                                       nqn=args.nqn,
1389                                       serial_number=args.serial_number,
1390                                       allow_any_host=args.allow_any_host,
1391                                       max_namespaces=args.max_namespaces)
1392
1393    p = subparsers.add_parser('nvmf_subsystem_create', help='Create an NVMe-oF subsystem')
1394    p.add_argument('nqn', help='Subsystem NQN (ASCII)')
1395    p.add_argument("-s", "--serial-number", help="""
1396    Format:  'sn' etc
1397    Example: 'SPDK00000000000001'""", default='00000000000000000000')
1398    p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce host NQN whitelist)")
1399    p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed",
1400                   type=int, default=0)
1401    p.set_defaults(func=nvmf_subsystem_create)
1402
1403    @call_cmd
1404    def delete_nvmf_subsystem(args):
1405        rpc.nvmf.delete_nvmf_subsystem(args.client,
1406                                       nqn=args.subsystem_nqn)
1407
1408    p = subparsers.add_parser('delete_nvmf_subsystem',
1409                              help='Delete a nvmf subsystem')
1410    p.add_argument('subsystem_nqn',
1411                   help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.')
1412    p.set_defaults(func=delete_nvmf_subsystem)
1413
1414    @call_cmd
1415    def nvmf_subsystem_add_listener(args):
1416        rpc.nvmf.nvmf_subsystem_add_listener(args.client,
1417                                             nqn=args.nqn,
1418                                             trtype=args.trtype,
1419                                             traddr=args.traddr,
1420                                             adrfam=args.adrfam,
1421                                             trsvcid=args.trsvcid)
1422
1423    p = subparsers.add_parser('nvmf_subsystem_add_listener', help='Add a listener to an NVMe-oF subsystem')
1424    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1425    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
1426    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1427    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1428    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
1429    p.set_defaults(func=nvmf_subsystem_add_listener)
1430
1431    @call_cmd
1432    def nvmf_subsystem_remove_listener(args):
1433        rpc.nvmf.nvmf_subsystem_remove_listener(args.client,
1434                                                nqn=args.nqn,
1435                                                trtype=args.trtype,
1436                                                traddr=args.traddr,
1437                                                adrfam=args.adrfam,
1438                                                trsvcid=args.trsvcid)
1439
1440    p = subparsers.add_parser('nvmf_subsystem_remove_listener', help='Remove a listener from an NVMe-oF subsystem')
1441    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1442    p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True)
1443    p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True)
1444    p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
1445    p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number')
1446    p.set_defaults(func=nvmf_subsystem_remove_listener)
1447
1448    @call_cmd
1449    def nvmf_subsystem_add_ns(args):
1450        rpc.nvmf.nvmf_subsystem_add_ns(args.client,
1451                                       nqn=args.nqn,
1452                                       bdev_name=args.bdev_name,
1453                                       nsid=args.nsid,
1454                                       nguid=args.nguid,
1455                                       eui64=args.eui64,
1456                                       uuid=args.uuid)
1457
1458    p = subparsers.add_parser('nvmf_subsystem_add_ns', help='Add a namespace to an NVMe-oF subsystem')
1459    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1460    p.add_argument('bdev_name', help='The name of the bdev that will back this namespace')
1461    p.add_argument('-n', '--nsid', help='The requested NSID (optional)', type=int)
1462    p.add_argument('-g', '--nguid', help='Namespace globally unique identifier (optional)')
1463    p.add_argument('-e', '--eui64', help='Namespace EUI-64 identifier (optional)')
1464    p.add_argument('-u', '--uuid', help='Namespace UUID (optional)')
1465    p.set_defaults(func=nvmf_subsystem_add_ns)
1466
1467    @call_cmd
1468    def nvmf_subsystem_remove_ns(args):
1469        rpc.nvmf.nvmf_subsystem_remove_ns(args.client,
1470                                          nqn=args.nqn,
1471                                          nsid=args.nsid)
1472
1473    p = subparsers.add_parser('nvmf_subsystem_remove_ns', help='Remove a namespace to an NVMe-oF subsystem')
1474    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1475    p.add_argument('nsid', help='The requested NSID', type=int)
1476    p.set_defaults(func=nvmf_subsystem_remove_ns)
1477
1478    @call_cmd
1479    def nvmf_subsystem_add_host(args):
1480        rpc.nvmf.nvmf_subsystem_add_host(args.client,
1481                                         nqn=args.nqn,
1482                                         host=args.host)
1483
1484    p = subparsers.add_parser('nvmf_subsystem_add_host', help='Add a host to an NVMe-oF subsystem')
1485    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1486    p.add_argument('host', help='Host NQN to allow')
1487    p.set_defaults(func=nvmf_subsystem_add_host)
1488
1489    @call_cmd
1490    def nvmf_subsystem_remove_host(args):
1491        rpc.nvmf.nvmf_subsystem_remove_host(args.client,
1492                                            nqn=args.nqn,
1493                                            host=args.host)
1494
1495    p = subparsers.add_parser('nvmf_subsystem_remove_host', help='Remove a host from an NVMe-oF subsystem')
1496    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1497    p.add_argument('host', help='Host NQN to remove')
1498    p.set_defaults(func=nvmf_subsystem_remove_host)
1499
1500    @call_cmd
1501    def nvmf_subsystem_allow_any_host(args):
1502        rpc.nvmf.nvmf_subsystem_allow_any_host(args.client,
1503                                               nqn=args.nqn,
1504                                               disable=args.disable)
1505
1506    p = subparsers.add_parser('nvmf_subsystem_allow_any_host', help='Allow any host to connect to the subsystem')
1507    p.add_argument('nqn', help='NVMe-oF subsystem NQN')
1508    p.add_argument('-e', '--enable', action='store_true', help='Enable allowing any host')
1509    p.add_argument('-d', '--disable', action='store_true', help='Disable allowing any host')
1510    p.set_defaults(func=nvmf_subsystem_allow_any_host)
1511
1512    # pmem
1513    @call_cmd
1514    def create_pmem_pool(args):
1515        num_blocks = int((args.total_size * 1024 * 1024) / args.block_size)
1516        rpc.pmem.create_pmem_pool(args.client,
1517                                  pmem_file=args.pmem_file,
1518                                  num_blocks=num_blocks,
1519                                  block_size=args.block_size)
1520
1521    p = subparsers.add_parser('create_pmem_pool', help='Create pmem pool')
1522    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1523    p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
1524    p.add_argument('block_size', help='Block size for this pmem pool', type=int)
1525    p.set_defaults(func=create_pmem_pool)
1526
1527    @call_cmd
1528    def pmem_pool_info(args):
1529        print_dict(rpc.pmem.pmem_pool_info(args.client,
1530                                           pmem_file=args.pmem_file))
1531
1532    p = subparsers.add_parser('pmem_pool_info', help='Display pmem pool info and check consistency')
1533    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1534    p.set_defaults(func=pmem_pool_info)
1535
1536    @call_cmd
1537    def delete_pmem_pool(args):
1538        rpc.pmem.delete_pmem_pool(args.client,
1539                                  pmem_file=args.pmem_file)
1540
1541    p = subparsers.add_parser('delete_pmem_pool', help='Delete pmem pool')
1542    p.add_argument('pmem_file', help='Path to pmemblk pool file')
1543    p.set_defaults(func=delete_pmem_pool)
1544
1545    # subsystem
1546    @call_cmd
1547    def get_subsystems(args):
1548        print_dict(rpc.subsystem.get_subsystems(args.client))
1549
1550    p = subparsers.add_parser('get_subsystems', help="""Print subsystems array in initialization order. Each subsystem
1551    entry contain (unsorted) array of subsystems it depends on.""")
1552    p.set_defaults(func=get_subsystems)
1553
1554    @call_cmd
1555    def get_subsystem_config(args):
1556        print_dict(rpc.subsystem.get_subsystem_config(args.client, args.name))
1557
1558    p = subparsers.add_parser('get_subsystem_config', help="""Print subsystem configuration""")
1559    p.add_argument('name', help='Name of subsystem to query')
1560    p.set_defaults(func=get_subsystem_config)
1561
1562    # vhost
1563    @call_cmd
1564    def set_vhost_controller_coalescing(args):
1565        rpc.vhost.set_vhost_controller_coalescing(args.client,
1566                                                  ctrlr=args.ctrlr,
1567                                                  delay_base_us=args.delay_base_us,
1568                                                  iops_threshold=args.iops_threshold)
1569
1570    p = subparsers.add_parser('set_vhost_controller_coalescing', help='Set vhost controller coalescing')
1571    p.add_argument('ctrlr', help='controller name')
1572    p.add_argument('delay_base_us', help='Base delay time', type=int)
1573    p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int)
1574    p.set_defaults(func=set_vhost_controller_coalescing)
1575
1576    @call_cmd
1577    def construct_vhost_scsi_controller(args):
1578        rpc.vhost.construct_vhost_scsi_controller(args.client,
1579                                                  ctrlr=args.ctrlr,
1580                                                  cpumask=args.cpumask)
1581
1582    p = subparsers.add_parser(
1583        'construct_vhost_scsi_controller', help='Add new vhost controller')
1584    p.add_argument('ctrlr', help='controller name')
1585    p.add_argument('--cpumask', help='cpu mask for this controller')
1586    p.set_defaults(func=construct_vhost_scsi_controller)
1587
1588    @call_cmd
1589    def add_vhost_scsi_lun(args):
1590        rpc.vhost.add_vhost_scsi_lun(args.client,
1591                                     ctrlr=args.ctrlr,
1592                                     scsi_target_num=args.scsi_target_num,
1593                                     bdev_name=args.bdev_name)
1594
1595    p = subparsers.add_parser('add_vhost_scsi_lun',
1596                              help='Add lun to vhost controller')
1597    p.add_argument('ctrlr', help='conntroller name where add lun')
1598    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
1599    p.add_argument('bdev_name', help='bdev name')
1600    p.set_defaults(func=add_vhost_scsi_lun)
1601
1602    @call_cmd
1603    def remove_vhost_scsi_target(args):
1604        rpc.vhost.remove_vhost_scsi_target(args.client,
1605                                           ctrlr=args.ctrlr,
1606                                           scsi_target_num=args.scsi_target_num)
1607
1608    p = subparsers.add_parser('remove_vhost_scsi_target', help='Remove target from vhost controller')
1609    p.add_argument('ctrlr', help='controller name to remove target from')
1610    p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
1611    p.set_defaults(func=remove_vhost_scsi_target)
1612
1613    @call_cmd
1614    def construct_vhost_blk_controller(args):
1615        rpc.vhost.construct_vhost_blk_controller(args.client,
1616                                                 ctrlr=args.ctrlr,
1617                                                 dev_name=args.dev_name,
1618                                                 cpumask=args.cpumask,
1619                                                 readonly=args.readonly)
1620
1621    p = subparsers.add_parser('construct_vhost_blk_controller', help='Add a new vhost block controller')
1622    p.add_argument('ctrlr', help='controller name')
1623    p.add_argument('dev_name', help='device name')
1624    p.add_argument('--cpumask', help='cpu mask for this controller')
1625    p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only')
1626    p.set_defaults(func=construct_vhost_blk_controller)
1627
1628    @call_cmd
1629    def construct_vhost_nvme_controller(args):
1630        rpc.vhost.construct_vhost_nvme_controller(args.client,
1631                                                  ctrlr=args.ctrlr,
1632                                                  io_queues=args.io_queues,
1633                                                  cpumask=args.cpumask)
1634
1635    p = subparsers.add_parser('construct_vhost_nvme_controller', help='Add new vhost controller')
1636    p.add_argument('ctrlr', help='controller name')
1637    p.add_argument('io_queues', help='number of IO queues for the controller', type=int)
1638    p.add_argument('--cpumask', help='cpu mask for this controller')
1639    p.set_defaults(func=construct_vhost_nvme_controller)
1640
1641    @call_cmd
1642    def add_vhost_nvme_ns(args):
1643        rpc.vhost.add_vhost_nvme_ns(args.client,
1644                                    ctrlr=args.ctrlr,
1645                                    bdev_name=args.bdev_name)
1646
1647    p = subparsers.add_parser('add_vhost_nvme_ns', help='Add a Namespace to vhost controller')
1648    p.add_argument('ctrlr', help='conntroller name where add a Namespace')
1649    p.add_argument('bdev_name', help='block device name for a new Namespace')
1650    p.set_defaults(func=add_vhost_nvme_ns)
1651
1652    @call_cmd
1653    def get_vhost_controllers(args):
1654        print_dict(rpc.vhost.get_vhost_controllers(args.client, args.name))
1655
1656    p = subparsers.add_parser('get_vhost_controllers', help='List all or specific vhost controller(s)')
1657    p.add_argument('-n', '--name', help="Name of vhost controller", required=False)
1658    p.set_defaults(func=get_vhost_controllers)
1659
1660    @call_cmd
1661    def remove_vhost_controller(args):
1662        rpc.vhost.remove_vhost_controller(args.client,
1663                                          ctrlr=args.ctrlr)
1664
1665    p = subparsers.add_parser('remove_vhost_controller', help='Remove a vhost controller')
1666    p.add_argument('ctrlr', help='controller name')
1667    p.set_defaults(func=remove_vhost_controller)
1668
1669    @call_cmd
1670    def construct_virtio_dev(args):
1671        print_array(rpc.vhost.construct_virtio_dev(args.client,
1672                                                   name=args.name,
1673                                                   trtype=args.trtype,
1674                                                   traddr=args.traddr,
1675                                                   dev_type=args.dev_type,
1676                                                   vq_count=args.vq_count,
1677                                                   vq_size=args.vq_size))
1678
1679    p = subparsers.add_parser('construct_virtio_dev', help="""Construct new virtio device using provided
1680    transport type and device type. In case of SCSI device type this implies scan and add bdevs offered by
1681    remote side. Result is array of added bdevs.""")
1682    p.add_argument('name', help="Use this name as base for new created bdevs")
1683    p.add_argument('-t', '--trtype',
1684                   help='Virtio target transport type: pci or user', required=True)
1685    p.add_argument('-a', '--traddr',
1686                   help='Transport type specific target address: e.g. UNIX domain socket path or BDF', required=True)
1687    p.add_argument('-d', '--dev-type',
1688                   help='Device type: blk or scsi', required=True)
1689    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1690    p.add_argument('--vq-size', help='Size of each queue', type=int)
1691    p.set_defaults(func=construct_virtio_dev)
1692
1693    @call_cmd
1694    def construct_virtio_user_scsi_bdev(args):
1695        print_array(rpc.vhost.construct_virtio_user_scsi_bdev(args.client,
1696                                                              path=args.path,
1697                                                              name=args.name,
1698                                                              vq_count=args.vq_count,
1699                                                              vq_size=args.vq_size))
1700
1701    p = subparsers.add_parser('construct_virtio_user_scsi_bdev', help="""Connect to virtio user scsi device.
1702    This imply scan and add bdevs offered by remote side.
1703    Result is array of added bdevs.""")
1704    p.add_argument('path', help='Path to Virtio SCSI socket')
1705    p.add_argument('name', help="""Use this name as base instead of 'VirtioScsiN'
1706    Base will be used to construct new bdev's found on target by adding 't<TARGET_ID>' sufix.""")
1707    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1708    p.add_argument('--vq-size', help='Size of each queue', type=int)
1709    p.set_defaults(func=construct_virtio_user_scsi_bdev)
1710
1711    @call_cmd
1712    def construct_virtio_pci_scsi_bdev(args):
1713        print_array(rpc.vhost.construct_virtio_pci_scsi_bdev(args.client,
1714                                                             pci_address=args.pci_address,
1715                                                             name=args.name))
1716
1717    p = subparsers.add_parser('construct_virtio_pci_scsi_bdev', help="""Create a Virtio
1718    SCSI device from a virtio-pci device.""")
1719    p.add_argument('pci_address', help="""PCI address in domain:bus:device.function format or
1720    domain.bus.device.function format""")
1721    p.add_argument('name', help="""Name for the virtio device.
1722    It will be inhereted by all created bdevs, which are named n the following format: <name>t<target_id>""")
1723    p.set_defaults(func=construct_virtio_pci_scsi_bdev)
1724
1725    @call_cmd
1726    def get_virtio_scsi_devs(args):
1727        print_dict(rpc.vhost.get_virtio_scsi_devs(args.client))
1728
1729    p = subparsers.add_parser('get_virtio_scsi_devs', help='List all Virtio-SCSI devices.')
1730    p.set_defaults(func=get_virtio_scsi_devs)
1731
1732    @call_cmd
1733    def remove_virtio_scsi_bdev(args):
1734        rpc.vhost.remove_virtio_scsi_bdev(args.client,
1735                                          name=args.name)
1736
1737    p = subparsers.add_parser('remove_virtio_scsi_bdev', help="""Remove a Virtio-SCSI device
1738    This will delete all bdevs exposed by this device (this call is deprecated - please use remove_virtio_bdev call instead).""")
1739    p.add_argument('name', help='Virtio device name. E.g. VirtioUser0')
1740    p.set_defaults(func=remove_virtio_scsi_bdev)
1741
1742    @call_cmd
1743    def remove_virtio_bdev(args):
1744        rpc.vhost.remove_virtio_bdev(args.client,
1745                                     name=args.name)
1746
1747    p = subparsers.add_parser('remove_virtio_bdev', help="""Remove a Virtio device
1748    This will delete all bdevs exposed by this device""")
1749    p.add_argument('name', help='Virtio device name. E.g. VirtioUser0')
1750    p.set_defaults(func=remove_virtio_bdev)
1751
1752    @call_cmd
1753    def construct_virtio_user_blk_bdev(args):
1754        print(rpc.vhost.construct_virtio_user_blk_bdev(args.client,
1755                                                       path=args.path,
1756                                                       name=args.name,
1757                                                       vq_count=args.vq_count,
1758                                                       vq_size=args.vq_size))
1759
1760    p = subparsers.add_parser('construct_virtio_user_blk_bdev', help='Connect to a virtio user blk device.')
1761    p.add_argument('path', help='Path to Virtio BLK socket')
1762    p.add_argument('name', help='Name for the bdev')
1763    p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
1764    p.add_argument('--vq-size', help='Size of each queue', type=int)
1765    p.set_defaults(func=construct_virtio_user_blk_bdev)
1766
1767    @call_cmd
1768    def construct_virtio_pci_blk_bdev(args):
1769        print(rpc.vhost.construct_virtio_pci_blk_bdev(args.client,
1770                                                      pci_address=args.pci_address,
1771                                                      name=args.name))
1772
1773    p = subparsers.add_parser('construct_virtio_pci_blk_bdev', help='Create a Virtio Blk device from a virtio-pci device.')
1774    p.add_argument('pci_address', help="""PCI address in domain:bus:device.function format or
1775    domain.bus.device.function format""")
1776    p.add_argument('name', help='Name for the bdev')
1777    p.set_defaults(func=construct_virtio_pci_blk_bdev)
1778
1779    # ioat
1780    @call_cmd
1781    def scan_ioat_copy_engine(args):
1782        pci_whitelist = []
1783        if args.pci_whitelist:
1784            for w in args.pci_whitelist.strip().split(" "):
1785                pci_whitelist.append(w)
1786        rpc.ioat.scan_ioat_copy_engine(args.client, pci_whitelist)
1787
1788    p = subparsers.add_parser('scan_ioat_copy_engine', help='Set scan and enable IOAT copy engine offload.')
1789    p.add_argument('-w', '--pci-whitelist', help="""Whitespace-separated list of PCI addresses in
1790    domain:bus:device.function format or domain.bus.device.function format""")
1791    p.set_defaults(func=scan_ioat_copy_engine)
1792
1793    # send_nvme_cmd
1794    @call_cmd
1795    def send_nvme_cmd(args):
1796        print_dict(rpc.nvme.send_nvme_cmd(args.client,
1797                                          name=args.nvme_name,
1798                                          cmd_type=args.cmd_type,
1799                                          data_direction=args.data_direction,
1800                                          cmdbuf=args.cmdbuf,
1801                                          data=args.data,
1802                                          metadata=args.metadata,
1803                                          data_len=args.data_length,
1804                                          metadata_len=args.metadata_length,
1805                                          timeout_ms=args.timeout_ms))
1806
1807    p = subparsers.add_parser('send_nvme_cmd', help='NVMe passthrough cmd.')
1808    p.add_argument('-n', '--nvme-name', help="""Name of the operating NVMe controller""")
1809    p.add_argument('-t', '--cmd-type', help="""Type of nvme cmd. Valid values are: admin, io""")
1810    p.add_argument('-r', '--data-direction', help="""Direction of data transfer. Valid values are: c2h, h2c""")
1811    p.add_argument('-c', '--cmdbuf', help="""NVMe command encoded by base64 urlsafe""")
1812    p.add_argument('-d', '--data', help="""Data transferring to controller from host, encoded by base64 urlsafe""")
1813    p.add_argument('-m', '--metadata', help="""Metadata transferring to controller from host, encoded by base64 urlsafe""")
1814    p.add_argument('-D', '--data-length', help="""Data length required to transfer from controller to host""", type=int)
1815    p.add_argument('-M', '--metadata-length', help="""Metadata length required to transfer from controller to host""", type=int)
1816    p.add_argument('-T', '--timeout-ms',
1817                   help="""Command execution timeout value, in milliseconds,  if 0, don't track timeout""", type=int, default=0)
1818    p.set_defaults(func=send_nvme_cmd)
1819
1820    args = parser.parse_args()
1821
1822    try:
1823        args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.verbose, args.timeout)
1824    except JSONRPCException as ex:
1825        print(ex.message)
1826        exit(1)
1827    args.func(args)
1828