xref: /spdk/scripts/rpc.py (revision 5e799ff470261620a1d3952cad88c003785108a2)
1#!/usr/bin/env python
2
3import argparse
4import json
5import socket
6
7try:
8    from shlex import quote
9except ImportError:
10    from pipes import quote
11
12def print_dict(d):
13    print json.dumps(d, indent=2)
14
15def print_array(a):
16    print " ".join((quote(v) for v in a))
17
18parser = argparse.ArgumentParser(description='SPDK RPC command line interface')
19parser.add_argument('-s', dest='server_addr', help='RPC server address', default='/var/tmp/spdk.sock')
20parser.add_argument('-p', dest='port', help='RPC port number (if server_addr is IP address)', default=5260, type=int)
21parser.add_argument('-v', dest='verbose', help='Verbose mode', action='store_true')
22subparsers = parser.add_subparsers(help='RPC methods')
23
24
25def int_arg(arg):
26    return int(arg, 0)
27
28
29def jsonrpc_call(method, params={}):
30    if args.server_addr.startswith('/'):
31        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
32        s.connect(args.server_addr)
33    else:
34        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
35        s.connect((args.server_addr, args.port))
36    req = {}
37    req['jsonrpc'] = '2.0'
38    req['method'] = method
39    req['id'] = 1
40    if (params):
41        req['params'] = params
42    reqstr = json.dumps(req)
43
44    if args.verbose:
45        print("request:")
46        print(json.dumps(req, indent=2))
47
48    s.sendall(reqstr)
49    buf = ''
50    closed = False
51    response = {}
52    while not closed:
53        newdata = s.recv(4096)
54        if (newdata == b''):
55            closed = True
56        buf += newdata
57        try:
58            response = json.loads(buf)
59        except ValueError:
60            continue  # incomplete response; keep buffering
61        break
62    s.close()
63
64    if not response:
65        if method == "kill_instance":
66            exit(0)
67        print "Connection closed with partial response:"
68        print buf
69        exit(1)
70
71    if 'error' in response:
72        print "Got JSON-RPC error response"
73        print "request:"
74        print_dict(json.loads(reqstr))
75        print "response:"
76        print_dict(response['error'])
77        exit(1)
78
79    if args.verbose:
80        print("response:")
81        print(json.dumps(response, indent=2))
82
83    return response['result']
84
85
86def get_portal_groups(args):
87    print_dict(jsonrpc_call('get_portal_groups'))
88
89p = subparsers.add_parser('get_portal_groups', help='Display current portal group configuration')
90p.set_defaults(func=get_portal_groups)
91
92
93def get_initiator_groups(args):
94    print_dict(jsonrpc_call('get_initiator_groups'))
95
96p = subparsers.add_parser('get_initiator_groups', help='Display current initiator group configuration')
97p.set_defaults(func=get_initiator_groups)
98
99
100def get_target_nodes(args):
101    print_dict(jsonrpc_call('get_target_nodes'))
102
103p = subparsers.add_parser('get_target_nodes', help='Display target nodes')
104p.set_defaults(func=get_target_nodes)
105
106
107def construct_target_node(args):
108    bdev_name_id_dict = dict(u.split(":")
109                             for u in args.bdev_name_id_pairs.strip().split(" "))
110    bdev_names = bdev_name_id_dict.keys()
111    lun_ids = list(map(int, bdev_name_id_dict.values()))
112
113    pg_tags = []
114    ig_tags = []
115    for u in args.pg_ig_mappings.strip().split(" "):
116        pg, ig = u.split(":")
117        pg_tags.append(int(pg))
118        ig_tags.append(int(ig))
119
120    params = {
121        'name': args.name,
122        'alias_name': args.alias_name,
123        'pg_tags': pg_tags,
124        'ig_tags': ig_tags,
125        'bdev_names': bdev_names,
126        'lun_ids': lun_ids,
127        'queue_depth': args.queue_depth,
128        'chap_disabled': args.chap_disabled,
129        'chap_required': args.chap_required,
130        'chap_mutual': args.chap_mutual,
131        'chap_auth_group': args.chap_auth_group,
132    }
133    if args.header_digest:
134        params['header_digest'] = args.header_digest
135    if args.data_digest:
136        params['data_digest'] = args.data_digest
137    jsonrpc_call('construct_target_node', params)
138
139p = subparsers.add_parser('construct_target_node', help='Add a target node')
140p.add_argument('name', help='Target node name (ASCII)')
141p.add_argument('alias_name', help='Target node alias name (ASCII)')
142p.add_argument('bdev_name_id_pairs', help="""Whitespace-separated list of <bdev name:LUN ID> pairs enclosed
143in quotes.  Format:  'bdev_name0:id0 bdev_name1:id1' etc
144Example: 'Malloc0:0 Malloc1:1 Malloc5:2'
145*** The bdevs must pre-exist ***
146*** LUN0 (id = 0) is required ***
147*** bdev names cannot contain space or colon characters ***""")
148p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
149Whitespace separated, quoted, mapping defined with colon
150separated list of "tags" (int > 0)
151Example: '1:1 2:2 2:1'
152*** The Portal/Initiator Groups must be precreated ***""")
153p.add_argument('queue_depth', help='Desired target queue depth', type=int)
154p.add_argument('chap_disabled', help="""CHAP authentication should be disabled for this target node.
155*** Mutually exclusive with chap_required ***""", type=int)
156p.add_argument('chap_required', help="""CHAP authentication should be required for this target node.
157*** Mutually exclusive with chap_disabled ***""", type=int)
158p.add_argument('chap_mutual', help='CHAP authentication should be mutual/bidirectional.', type=int)
159p.add_argument('chap_auth_group', help="""Authentication group ID for this target node.
160*** Authentication group must be precreated ***""", type=int)
161p.add_argument('-H', dest='header_digest', help='Header Digest should be required for this target node.', type=int, required=False)
162p.add_argument('-D', dest='data_digest', help='Data Digest should be required for this target node.', type=int, required=False)
163p.set_defaults(func=construct_target_node)
164
165
166def target_node_add_lun(args):
167    params = {
168        'name': args.name,
169        'bdev_name': args.bdev_name,
170    }
171    if args.lun_id:
172        params['lun_id'] = args.lun_id
173    jsonrpc_call('target_node_add_lun', params)
174
175p = subparsers.add_parser('target_node_add_lun', help='Add LUN to the target node')
176p.add_argument('name', help='Target node name (ASCII)')
177p.add_argument('bdev_name', help="""bdev name enclosed in quotes.
178*** bdev name cannot contain space or colon characters ***""")
179p.add_argument('-i', dest='lun_id', help="""LUN ID (integer >= 0)
180*** If LUN ID is omitted or -1, the lowest free one is assigned ***""", type=int, required=False)
181p.set_defaults(func=target_node_add_lun)
182
183
184def add_pg_ig_maps(args):
185    pg_tags = []
186    ig_tags = []
187    for u in args.pg_ig_mappings.strip().split(" "):
188        pg, ig = u.split(":")
189        pg_tags.append(int(pg))
190        ig_tags.append(int(ig))
191
192    params = {
193        'name': args.name,
194        'pg_tags': pg_tags,
195        'ig_tags': ig_tags,
196    }
197    jsonrpc_call('add_pg_ig_maps', params)
198
199p = subparsers.add_parser('add_pg_ig_maps', help='Add PG-IG maps to the target node')
200p.add_argument('name', help='Target node name (ASCII)')
201p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
202Whitespace separated, quoted, mapping defined with colon
203separated list of "tags" (int > 0)
204Example: '1:1 2:2 2:1'
205*** The Portal/Initiator Groups must be precreated ***""")
206p.set_defaults(func=add_pg_ig_maps)
207
208
209def delete_pg_ig_maps(args):
210    pg_tags = []
211    ig_tags = []
212    for u in args.pg_ig_mappings.strip().split(" "):
213        pg, ig = u.split(":")
214        pg_tags.append(int(pg))
215        ig_tags.append(int(ig))
216
217    params = {
218        'name': args.name,
219        'pg_tags': pg_tags,
220        'ig_tags': ig_tags,
221    }
222    jsonrpc_call('delete_pg_ig_maps', params)
223
224p = subparsers.add_parser('delete_pg_ig_maps', help='Delete PG-IG maps from the target node')
225p.add_argument('name', help='Target node name (ASCII)')
226p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings
227Whitespace separated, quoted, mapping defined with colon
228separated list of "tags" (int > 0)
229Example: '1:1 2:2 2:1'
230*** The Portal/Initiator Groups must be precreated ***""")
231p.set_defaults(func=delete_pg_ig_maps)
232
233
234def construct_malloc_bdev(args):
235    num_blocks = (args.total_size * 1024 * 1024) / args.block_size
236    params = {'num_blocks': num_blocks, 'block_size': args.block_size}
237    if args.name:
238        params['name'] = args.name
239    print_array(jsonrpc_call('construct_malloc_bdev', params))
240
241p = subparsers.add_parser('construct_malloc_bdev', help='Add a bdev with malloc backend')
242p.add_argument('-b', '--name', help="Name of the bdev")
243p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
244p.add_argument('block_size', help='Block size for this bdev', type=int)
245p.set_defaults(func=construct_malloc_bdev)
246
247
248def create_pmem_pool(args):
249    num_blocks = (args.total_size * 1024 * 1024) / args.block_size
250    params = {'pmem_file': args.pmem_file,
251              'num_blocks': num_blocks,
252              'block_size': args.block_size}
253    jsonrpc_call('create_pmem_pool', params)
254
255p = subparsers.add_parser('create_pmem_pool', help='Create pmem pool')
256p.add_argument('pmem_file', help='Path to pmemblk pool file')
257p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int)
258p.add_argument('block_size', help='Block size for this pmem pool', type=int)
259p.set_defaults(func=create_pmem_pool)
260
261
262def pmem_pool_info(args):
263    params = {'pmem_file': args.pmem_file}
264    print_dict(jsonrpc_call('pmem_pool_info', params))
265
266p = subparsers.add_parser('pmem_pool_info', help='Display pmem pool info and check consistency')
267p.add_argument('pmem_file', help='Path to pmemblk pool file')
268p.set_defaults(func=pmem_pool_info)
269
270
271def delete_pmem_pool(args):
272    params = {'pmem_file': args.pmem_file}
273    jsonrpc_call('delete_pmem_pool', params)
274
275p = subparsers.add_parser('delete_pmem_pool', help='Delete pmem pool')
276p.add_argument('pmem_file', help='Path to pmemblk pool file')
277p.set_defaults(func=delete_pmem_pool)
278
279
280def construct_pmem_bdev(args):
281    params = {
282        'pmem_file': args.pmem_file,
283        'name': args.name
284    }
285    print_array(jsonrpc_call('construct_pmem_bdev', params))
286
287p = subparsers.add_parser('construct_pmem_bdev', help='Add a bdev with pmem backend')
288p.add_argument('pmem_file', help='Path to pmemblk pool file')
289p.add_argument('-n', '--name', help='Block device name', required=True)
290p.set_defaults(func=construct_pmem_bdev)
291
292def construct_null_bdev(args):
293    num_blocks = (args.total_size * 1024 * 1024) / args.block_size
294    params = {'name': args.name, 'num_blocks': num_blocks, 'block_size': args.block_size}
295    print_array(jsonrpc_call('construct_null_bdev', params))
296
297p = subparsers.add_parser('construct_null_bdev', help='Add a bdev with null backend')
298p.add_argument('name', help='Block device name')
299p.add_argument('total_size', help='Size of null bdev in MB (int > 0)', type=int)
300p.add_argument('block_size', help='Block size for this bdev', type=int)
301p.set_defaults(func=construct_null_bdev)
302
303
304def construct_aio_bdev(args):
305    params = {'name': args.name,
306              'filename': args.filename}
307
308    if args.block_size:
309        params['block_size'] = args.block_size
310
311    print_array(jsonrpc_call('construct_aio_bdev', params))
312
313p = subparsers.add_parser('construct_aio_bdev', help='Add a bdev with aio backend')
314p.add_argument('filename', help='Path to device or file (ex: /dev/sda)')
315p.add_argument('name', help='Block device name')
316p.add_argument('block_size', help='Block size for this bdev', type=int, default=argparse.SUPPRESS)
317p.set_defaults(func=construct_aio_bdev)
318
319def construct_nvme_bdev(args):
320    params = {'name': args.name,
321              'trtype': args.trtype,
322              'traddr': args.traddr}
323
324    if args.adrfam:
325        params['adrfam'] = args.adrfam
326
327    if args.trsvcid:
328        params['trsvcid'] = args.trsvcid
329
330    if args.subnqn:
331        params['subnqn'] = args.subnqn
332
333    jsonrpc_call('construct_nvme_bdev', params)
334
335p = subparsers.add_parser('construct_nvme_bdev', help='Add bdev with nvme backend')
336p.add_argument('-b', '--name', help="Name of the bdev", required=True)
337p.add_argument('-t', '--trtype', help='NVMe-oF target trtype: e.g., rdma, pcie', required=True)
338p.add_argument('-a', '--traddr', help='NVMe-oF target address: e.g., an ip address or BDF', required=True)
339p.add_argument('-f', '--adrfam', help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host')
340p.add_argument('-s', '--trsvcid', help='NVMe-oF target trsvcid: e.g., a port number')
341p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn')
342p.set_defaults(func=construct_nvme_bdev)
343
344def construct_rbd_bdev(args):
345    params = {
346        'pool_name': args.pool_name,
347        'rbd_name': args.rbd_name,
348        'block_size': args.block_size,
349    }
350    print_array(jsonrpc_call('construct_rbd_bdev', params))
351
352p = subparsers.add_parser('construct_rbd_bdev', help='Add a bdev with ceph rbd backend')
353p.add_argument('pool_name', help='rbd pool name')
354p.add_argument('rbd_name', help='rbd image name')
355p.add_argument('block_size', help='rbd block size', type=int)
356p.set_defaults(func=construct_rbd_bdev)
357
358def construct_error_bdev(args):
359    params = {'base_name': args.base_name}
360    jsonrpc_call('construct_error_bdev', params)
361p = subparsers.add_parser('construct_error_bdev', help='Add bdev with error injection backend')
362p.add_argument('base_name', help='base bdev name')
363p.set_defaults(func=construct_error_bdev)
364
365
366def construct_lvol_store(args):
367    params = {'bdev_name': args.bdev_name, 'lvs_name': args.lvs_name}
368
369    if args.cluster_sz:
370        params['cluster_sz'] = args.cluster_sz
371
372    print_array(jsonrpc_call('construct_lvol_store', params))
373p = subparsers.add_parser('construct_lvol_store', help='Add logical volume store on base bdev')
374p.add_argument('bdev_name', help='base bdev name')
375p.add_argument('lvs_name', help='name for lvol store')
376p.add_argument('-c', '--cluster-sz', help='size of cluster (in bytes)', type=int, required=False)
377p.set_defaults(func=construct_lvol_store)
378
379
380def construct_lvol_bdev(args):
381    num_bytes = (args.size * 1024 * 1024)
382    params = {'lvol_name': args.lvol_name, 'size': num_bytes}
383    if (args.uuid and args.lvs_name) or (not args.uuid and not args.lvs_name):
384        print("You need to specify either uuid or name of lvolstore")
385    else:
386        if args.uuid:
387            params['uuid'] = args.uuid
388        if args.lvs_name:
389            params['lvs_name'] = args.lvs_name
390        print_array(jsonrpc_call('construct_lvol_bdev', params))
391p = subparsers.add_parser('construct_lvol_bdev', help='Add a bdev with an logical volume backend')
392p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
393p.add_argument('-l', '--lvs_name', help='lvol store name', required=False)
394p.add_argument('lvol_name', help='name for this lvol')
395p.add_argument('size', help='size in MiB for this bdev', type=int)
396p.set_defaults(func=construct_lvol_bdev)
397
398# Logical volume resize feature is disabled, as it is currently work in progress
399#
400# def resize_lvol_bdev(args):
401#     params = {
402#         'name': args.name,
403#         'size': args.size,
404#     }
405#     jsonrpc_call('resize_lvol_bdev', params)
406# p = subparsers.add_parser('resize_lvol_bdev', help='Resize existing lvol bdev')
407# p.add_argument('name', help='lvol bdev name')
408# p.add_argument('size', help='new size in MiB for this bdev', type=int)
409# p.set_defaults(func=resize_lvol_bdev)
410
411
412def destroy_lvol_store(args):
413    params = {}
414    if (args.uuid and args.lvs_name) or (not args.uuid and not args.lvs_name):
415        print("You need to specify either uuid or name of lvolstore")
416    else:
417        if args.uuid:
418            params['uuid'] = args.uuid
419        if args.lvs_name:
420            params['lvs_name'] = args.lvs_name
421        jsonrpc_call('destroy_lvol_store', params)
422p = subparsers.add_parser('destroy_lvol_store', help='Destroy an logical volume store')
423p.add_argument('-u', '--uuid', help='lvol store UUID', required=False)
424p.add_argument('-l', '--lvs_name', help='lvol store name', required=False)
425p.set_defaults(func=destroy_lvol_store)
426
427
428def get_lvol_stores(args):
429    print_dict(jsonrpc_call('get_lvol_stores'))
430
431p = subparsers.add_parser('get_lvol_stores', help='Display current logical volume store list')
432p.set_defaults(func=get_lvol_stores)
433
434
435def set_trace_flag(args):
436    params = {'flag': args.flag}
437    jsonrpc_call('set_trace_flag', params)
438
439p = subparsers.add_parser('set_trace_flag', help='set trace flag')
440p.add_argument('flag', help='trace mask we want to set. (for example "debug").')
441p.set_defaults(func=set_trace_flag)
442
443
444def clear_trace_flag(args):
445    params = {'flag': args.flag}
446    jsonrpc_call('clear_trace_flag', params)
447
448p = subparsers.add_parser('clear_trace_flag', help='clear trace flag')
449p.add_argument('flag', help='trace mask we want to clear. (for example "debug").')
450p.set_defaults(func=clear_trace_flag)
451
452
453def get_trace_flags(args):
454    print_dict(jsonrpc_call('get_trace_flags'))
455
456p = subparsers.add_parser('get_trace_flags', help='get trace flags')
457p.set_defaults(func=get_trace_flags)
458
459def set_log_level(args):
460    params = {'level': args.level}
461    jsonrpc_call('set_log_level', params)
462
463p = subparsers.add_parser('set_log_level', help='set log level')
464p.add_argument('level', help='log level we want to set. (for example "DEBUG").')
465p.set_defaults(func=set_log_level)
466
467def get_log_level(args):
468    print_dict(jsonrpc_call('get_log_level'))
469
470p = subparsers.add_parser('get_log_level', help='get log level')
471p.set_defaults(func=get_log_level)
472
473def set_log_print_level(args):
474    params = {'level': args.level}
475    jsonrpc_call('set_log_print_level', params)
476
477p = subparsers.add_parser('set_log_print_level', help='set log print level')
478p.add_argument('level', help='log print level we want to set. (for example "DEBUG").')
479p.set_defaults(func=set_log_print_level)
480
481def get_log_print_level(args):
482    print_dict(jsonrpc_call('get_log_print_level'))
483
484p = subparsers.add_parser('get_log_print_level', help='get log print level')
485p.set_defaults(func=get_log_print_level)
486
487def add_portal_group(args):
488    # parse out portal list host1:port1 host2:port2
489    portals = []
490    for p in args.portal_list:
491        host_port = p.split(':')
492        portals.append({'host': host_port[0], 'port': host_port[1]})
493
494    params = {'tag': args.tag, 'portals': portals}
495    jsonrpc_call('add_portal_group', params)
496
497p = subparsers.add_parser('add_portal_group', help='Add a portal group')
498p.add_argument('tag', help='Portal group tag (unique, integer > 0)', type=int)
499p.add_argument('portal_list', nargs=argparse.REMAINDER, help="""List of portals in 'host:port' format, separated by whitespace
500Example: '192.168.100.100:3260' '192.168.100.100:3261'""")
501p.set_defaults(func=add_portal_group)
502
503
504def add_initiator_group(args):
505    initiators = []
506    netmasks = []
507    for i in args.initiator_list.strip().split(' '):
508        initiators.append(i)
509    for n in args.netmask_list.strip().split(' '):
510        netmasks.append(n)
511
512    params = {'tag': args.tag, 'initiators': initiators, 'netmasks': netmasks}
513    jsonrpc_call('add_initiator_group', params)
514
515
516p = subparsers.add_parser('add_initiator_group', help='Add an initiator group')
517p.add_argument('tag', help='Initiator group tag (unique, integer > 0)', type=int)
518p.add_argument('initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses,
519enclosed in quotes.  Example: 'ANY' or '127.0.0.1 192.168.200.100'""")
520p.add_argument('netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes.
521Example: '255.255.0.0 255.248.0.0' etc""")
522p.set_defaults(func=add_initiator_group)
523
524
525def delete_target_node(args):
526    params = {'name': args.target_node_name}
527    jsonrpc_call('delete_target_node', params)
528
529p = subparsers.add_parser('delete_target_node', help='Delete a target node')
530p.add_argument('target_node_name', help='Target node name to be deleted. Example: iqn.2016-06.io.spdk:disk1.')
531p.set_defaults(func=delete_target_node)
532
533
534def delete_portal_group(args):
535    params = {'tag': args.tag}
536    jsonrpc_call('delete_portal_group', params)
537
538p = subparsers.add_parser('delete_portal_group', help='Delete a portal group')
539p.add_argument('tag', help='Portal group tag (unique, integer > 0)', type=int)
540p.set_defaults(func=delete_portal_group)
541
542
543def delete_initiator_group(args):
544    params = {'tag': args.tag}
545    jsonrpc_call('delete_initiator_group', params)
546
547p = subparsers.add_parser('delete_initiator_group', help='Delete an initiator group')
548p.add_argument('tag', help='Initiator group tag (unique, integer > 0)', type=int)
549p.set_defaults(func=delete_initiator_group)
550
551
552def get_iscsi_connections(args):
553    print_dict(jsonrpc_call('get_iscsi_connections'))
554
555p = subparsers.add_parser('get_iscsi_connections', help='Display iSCSI connections')
556p.set_defaults(func=get_iscsi_connections)
557
558
559def get_iscsi_global_params(args):
560    print_dict(jsonrpc_call('get_iscsi_global_params'))
561
562p = subparsers.add_parser('get_iscsi_global_params', help='Display iSCSI global parameters')
563p.set_defaults(func=get_iscsi_global_params)
564
565
566def get_scsi_devices(args):
567    print_dict(jsonrpc_call('get_scsi_devices'))
568
569p = subparsers.add_parser('get_scsi_devices', help='Display SCSI devices')
570p.set_defaults(func=get_scsi_devices)
571
572
573def add_ip_address(args):
574    params = {'ifc_index': args.ifc_index, 'ip_address': args.ip_addr}
575    jsonrpc_call('add_ip_address', params)
576
577p = subparsers.add_parser('add_ip_address', help='Add IP address')
578p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
579p.add_argument('ip_addr', help='ip address will be added.')
580p.set_defaults(func=add_ip_address)
581
582
583def delete_ip_address(args):
584    params = {'ifc_index': args.ifc_index, 'ip_address': args.ip_addr}
585    jsonrpc_call('delete_ip_address', params)
586
587p = subparsers.add_parser('delete_ip_address', help='Delete IP address')
588p.add_argument('ifc_index', help='ifc index of the nic device.', type=int)
589p.add_argument('ip_addr', help='ip address will be deleted.')
590p.set_defaults(func=delete_ip_address)
591
592
593def get_interfaces(args):
594    print_dict(jsonrpc_call('get_interfaces'))
595
596p = subparsers.add_parser('get_interfaces', help='Display current interface list')
597p.set_defaults(func=get_interfaces)
598
599def apply_firmware(args):
600
601    params = {
602        'filename': args.filename,
603        'bdev_name': args.bdev_name,
604    }
605
606    print_dict(jsonrpc_call('apply_nvme_firmware', params))
607
608p = subparsers.add_parser('apply_firmware', help='Download and commit firmware to NVMe device')
609p.add_argument('filename', help='filename of the firmware to download')
610p.add_argument('bdev_name', help='name of the NVMe device')
611p.set_defaults(func=apply_firmware)
612
613def get_bdevs(args):
614    params = {}
615    if args.name:
616        params['name'] = args.name
617    print_dict(jsonrpc_call('get_bdevs', params))
618
619p = subparsers.add_parser('get_bdevs', help='Display current blockdev list or required blockdev')
620p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False)
621p.set_defaults(func=get_bdevs)
622
623
624def delete_bdev(args):
625    params = {'name': args.bdev_name}
626    jsonrpc_call('delete_bdev', params)
627
628p = subparsers.add_parser('delete_bdev', help='Delete a blockdev')
629p.add_argument('bdev_name', help='Blockdev name to be deleted. Example: Malloc0.')
630p.set_defaults(func=delete_bdev)
631
632def start_nbd_disk(args):
633    params = {
634        'bdev_name': args.bdev_name,
635        'nbd_device': args.nbd_device
636    }
637    jsonrpc_call('start_nbd_disk', params)
638
639p = subparsers.add_parser('start_nbd_disk', help='Export a bdev as a nbd disk')
640p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.')
641p.add_argument('nbd_device', help='Nbd device name to be assigned. Example: /dev/nbd0.')
642p.set_defaults(func=start_nbd_disk)
643
644def stop_nbd_disk(args):
645    params = {'nbd_device': args.nbd_device}
646    jsonrpc_call('stop_nbd_disk', params)
647
648p = subparsers.add_parser('stop_nbd_disk', help='Stop a nbd disk')
649p.add_argument('nbd_device', help='Nbd device name to be stopped. Example: /dev/nbd0.')
650p.set_defaults(func=stop_nbd_disk)
651
652def get_nbd_disks(args):
653    params = {}
654    if args.nbd_device:
655        params['nbd_device'] = args.nbd_device
656    print_dict(jsonrpc_call('get_nbd_disks', params))
657
658p = subparsers.add_parser('get_nbd_disks', help='Display full or specified nbd device list')
659p.add_argument('-n', '--nbd_device', help="Path of the nbd device. Example: /dev/nbd0", required=False)
660p.set_defaults(func=get_nbd_disks)
661
662def get_nvmf_subsystems(args):
663    print_dict(jsonrpc_call('get_nvmf_subsystems'))
664
665p = subparsers.add_parser('get_nvmf_subsystems', help='Display nvmf subsystems')
666p.set_defaults(func=get_nvmf_subsystems)
667
668def construct_nvmf_subsystem(args):
669    listen_addresses = [dict(u.split(":") for u in a.split(" ")) for a in args.listen.split(",")]
670
671    params = {
672        'nqn': args.nqn,
673        'listen_addresses': listen_addresses,
674        'serial_number': args.serial_number,
675    }
676
677    if args.hosts:
678        hosts = []
679        for u in args.hosts.strip().split(" "):
680            hosts.append(u)
681        params['hosts'] = hosts
682
683    if args.allow_any_host:
684        params['allow_any_host'] = True
685
686    if args.namespaces:
687        namespaces = []
688        for u in args.namespaces.strip().split(" "):
689            bdev_name = u
690            nsid = 0
691            if ':' in u:
692                (bdev_name, nsid) = u.split(":")
693
694            ns_params = {'bdev_name': bdev_name}
695
696            nsid = int(nsid)
697            if nsid != 0:
698                ns_params['nsid'] = nsid
699
700            namespaces.append(ns_params)
701        params['namespaces'] = namespaces
702
703    jsonrpc_call('construct_nvmf_subsystem', params)
704
705p = subparsers.add_parser('construct_nvmf_subsystem', help='Add a nvmf subsystem')
706p.add_argument('nqn', help='Target nqn(ASCII)')
707p.add_argument('listen', help="""comma-separated list of Listen <trtype:transport_name traddr:address trsvcid:port_id> pairs enclosed
708in quotes.  Format:  'trtype:transport0 traddr:traddr0 trsvcid:trsvcid0,trtype:transport1 traddr:traddr1 trsvcid:trsvcid1' etc
709Example: 'trtype:RDMA traddr:192.168.100.8 trsvcid:4420,trtype:RDMA traddr:192.168.100.9 trsvcid:4420'""")
710p.add_argument('hosts', help="""Whitespace-separated list of host nqn list.
711Format:  'nqn1 nqn2' etc
712Example: 'nqn.2016-06.io.spdk:init nqn.2016-07.io.spdk:init'""")
713p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce host NQN whitelist)")
714p.add_argument("-s", "--serial_number", help="""
715Format:  'sn' etc
716Example: 'SPDK00000000000001'""", default='0000:00:01.0')
717p.add_argument("-n", "--namespaces", help="""Whitespace-separated list of namespaces
718Format:  'bdev_name1[:nsid1] bdev_name2[:nsid2] bdev_name3[:nsid3]' etc
719Example: '1:Malloc0 2:Malloc1 3:Malloc2'
720*** The devices must pre-exist ***""")
721p.set_defaults(func=construct_nvmf_subsystem)
722
723def delete_nvmf_subsystem(args):
724    params = {'nqn': args.subsystem_nqn}
725    jsonrpc_call('delete_nvmf_subsystem', params)
726
727p = subparsers.add_parser('delete_nvmf_subsystem', help='Delete a nvmf subsystem')
728p.add_argument('subsystem_nqn', help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.')
729p.set_defaults(func=delete_nvmf_subsystem)
730
731def bdev_inject_error(args):
732    params = {
733        'name': args.name,
734        'io_type': args.io_type,
735        'error_type': args.error_type,
736        'num': args.num,
737    }
738
739    jsonrpc_call('bdev_inject_error', params)
740
741p = subparsers.add_parser('bdev_inject_error', help='bdev inject error')
742p.add_argument('name', help="""the name of the error injection bdev""")
743p.add_argument('io_type', help="""io_type: 'clear' 'read' 'write' 'unmap' 'flush' 'all'""")
744p.add_argument('error_type', help="""error_type: 'failure' 'pending'""")
745p.add_argument('-n', '--num', help='the number of commands you want to fail', type=int, default=1)
746p.set_defaults(func=bdev_inject_error)
747
748def kill_instance(args):
749    params = {'sig_name': args.sig_name}
750    jsonrpc_call('kill_instance', params)
751
752p = subparsers.add_parser('kill_instance', help='Send signal to instance')
753p.add_argument('sig_name', help='signal will be sent to server.')
754p.set_defaults(func=kill_instance)
755
756def set_vhost_controller_coalescing(args):
757    params = {
758        'ctrlr': args.ctrlr,
759        'delay_base_us': args.delay_base_us,
760        'iops_threshold': args.iops_threshold,
761    }
762    jsonrpc_call('set_vhost_controller_coalescing', params)
763
764p = subparsers.add_parser('set_vhost_controller_coalescing', help='Set vhost controller coalescing')
765p.add_argument('ctrlr', help='controller name')
766p.add_argument('delay_base_us', help='Base delay time', type=int)
767p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int)
768p.set_defaults(func=set_vhost_controller_coalescing)
769
770def construct_vhost_scsi_controller(args):
771    params = {'ctrlr': args.ctrlr}
772
773    if args.cpumask:
774        params['cpumask'] = args.cpumask
775
776    jsonrpc_call('construct_vhost_scsi_controller', params)
777
778p = subparsers.add_parser('construct_vhost_scsi_controller', help='Add new vhost controller')
779p.add_argument('ctrlr', help='controller name')
780p.add_argument('--cpumask', help='cpu mask for this controller')
781p.set_defaults(func=construct_vhost_scsi_controller)
782
783def add_vhost_scsi_lun(args):
784    params = {
785        'ctrlr': args.ctrlr,
786        'bdev_name': args.bdev_name,
787        'scsi_target_num': args.scsi_target_num
788    }
789
790    jsonrpc_call('add_vhost_scsi_lun', params)
791
792p = subparsers.add_parser('add_vhost_scsi_lun', help='Add lun to vhost controller')
793p.add_argument('ctrlr', help='conntroller name where add lun')
794p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
795p.add_argument('bdev_name', help='bdev name')
796p.set_defaults(func=add_vhost_scsi_lun)
797
798def remove_vhost_scsi_target(args):
799    params = {
800        'ctrlr': args.ctrlr,
801        'scsi_target_num': args.scsi_target_num
802    }
803    jsonrpc_call('remove_vhost_scsi_target', params)
804
805p = subparsers.add_parser('remove_vhost_scsi_target', help='Remove target from vhost controller')
806p.add_argument('ctrlr', help='controller name to remove target from')
807p.add_argument('scsi_target_num', help='scsi_target_num', type=int)
808p.set_defaults(func=remove_vhost_scsi_target)
809
810def construct_vhost_blk_controller(args):
811    params = {
812        'ctrlr': args.ctrlr,
813        'dev_name': args.dev_name,
814    }
815    if args.cpumask:
816        params['cpumask'] = args.cpumask
817    if args.readonly:
818        params['readonly'] = args.readonly
819    jsonrpc_call('construct_vhost_blk_controller', params)
820
821p = subparsers.add_parser('construct_vhost_blk_controller', help='Add a new vhost block controller')
822p.add_argument('ctrlr', help='controller name')
823p.add_argument('dev_name', help='device name')
824p.add_argument('--cpumask', help='cpu mask for this controller')
825p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only')
826p.set_defaults(func=construct_vhost_blk_controller)
827
828def get_vhost_controllers(args):
829    print_dict(jsonrpc_call('get_vhost_controllers'))
830
831p = subparsers.add_parser('get_vhost_controllers', help='List vhost controllers')
832p.set_defaults(func=get_vhost_controllers)
833
834def remove_vhost_controller(args):
835    params = {'ctrlr': args.ctrlr}
836    jsonrpc_call('remove_vhost_controller', params)
837
838p = subparsers.add_parser('remove_vhost_controller', help='Remove a vhost controller')
839p.add_argument('ctrlr', help='controller name')
840p.set_defaults(func=remove_vhost_controller)
841
842def construct_virtio_user_scsi_bdev(args):
843    params = {
844        'path': args.path,
845        'name': args.name,
846    }
847    if args.vq_count:
848        params['vq_count'] = args.vq_count
849    if args.vq_size:
850        params['vq_size'] = args.vq_size
851    print_dict(jsonrpc_call('construct_virtio_user_scsi_bdev', params))
852
853p = subparsers.add_parser('construct_virtio_user_scsi_bdev', help="""Connect to virtio user scsi device.
854This imply scan and add bdevs offered by remote side.
855Result is array of added bdevs.""")
856p.add_argument('path', help='Path to Virtio SCSI socket')
857p.add_argument('name', help="""Use this name as base instead of 'VirtioScsiN'
858Base will be used to construct new bdev's found on target by adding 't<TARGET_ID>' sufix.""")
859p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int)
860p.add_argument('--vq-size', help='Size of each queue', type=int)
861p.set_defaults(func=construct_virtio_user_scsi_bdev)
862
863def remove_virtio_scsi_bdev(args):
864    params = {'name': args.name}
865    jsonrpc_call('remove_virtio_scsi_bdev', params)
866
867p = subparsers.add_parser('remove_virtio_scsi_bdev', help="""Remove a Virtio-SCSI device
868This will delete all bdevs exposed by this device""")
869p.add_argument('name', help='Virtio device name. E.g. VirtioUser0')
870p.set_defaults(func=remove_virtio_scsi_bdev)
871
872def get_rpc_methods(args):
873    print_dict(jsonrpc_call('get_rpc_methods'))
874
875p = subparsers.add_parser('get_rpc_methods', help='Get list of supported RPC methods')
876p.set_defaults(func=get_rpc_methods)
877
878def context_switch_monitor(args):
879    params = {}
880    if args.enable:
881        params['enabled'] = True
882    if args.disable:
883        params['enabled'] = False
884    print_dict(jsonrpc_call('context_switch_monitor', params))
885
886p = subparsers.add_parser('context_switch_monitor', help='Control whether the context switch monitor is enabled')
887p.add_argument('-e', '--enable', action='store_true', help='Enable context switch monitoring')
888p.add_argument('-d', '--disable', action='store_true', help='Disable context switch monitoring')
889p.set_defaults(func=context_switch_monitor)
890
891args = parser.parse_args()
892args.func(args)
893