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