1#!/usr/bin/env python 2 3from rpc.client import print_dict, JSONRPCException 4 5import argparse 6import rpc 7 8try: 9 from shlex import quote 10except ImportError: 11 from pipes import quote 12 13 14def print_array(a): 15 print(" ".join((quote(v) for v in a))) 16 17 18def call_cmd(func): 19 def rpc_cmd(*args, **kwargs): 20 try: 21 func(*args, **kwargs) 22 except JSONRPCException as ex: 23 print(ex.message) 24 exit(1) 25 return rpc_cmd 26 27 28if __name__ == "__main__": 29 parser = argparse.ArgumentParser( 30 description='SPDK RPC command line interface') 31 parser.add_argument('-s', dest='server_addr', 32 help='RPC server address', default='/var/tmp/spdk.sock') 33 parser.add_argument('-p', dest='port', 34 help='RPC port number (if server_addr is IP address)', 35 default=5260, type=int) 36 parser.add_argument('-t', dest='timeout', 37 help='Timout as a floating point number expressed in seconds waiting for reponse. Default: 60.0', 38 default=60.0, type=float) 39 parser.add_argument('-v', dest='verbose', 40 help='Verbose mode', action='store_true') 41 subparsers = parser.add_subparsers(help='RPC methods') 42 43 @call_cmd 44 def get_rpc_methods(args): 45 print_dict(rpc.get_rpc_methods(args.client)) 46 47 p = subparsers.add_parser('get_rpc_methods', help='Get list of supported RPC methods') 48 p.set_defaults(func=get_rpc_methods) 49 50 @call_cmd 51 def save_config(args): 52 rpc.save_config(args.client, args) 53 54 p = subparsers.add_parser('save_config', help="""Write current (live) configuration of SPDK subsystems and targets. 55 If no filename is given write configuration to stdout.""") 56 p.add_argument('-f', '--filename', help="""File where to save JSON configuration to.""") 57 p.add_argument('-i', '--indent', help="""Indent level. Value less than 0 mean compact mode. If filename is not given default 58 indent level is 2. If writing to file of filename is '-' then default is compact mode.""", type=int, default=2) 59 p.set_defaults(func=save_config) 60 61 @call_cmd 62 def load_config(args): 63 rpc.load_config(args.client, args) 64 65 p = subparsers.add_parser('load_config', help="""Configure SPDK subsystems and tagets using JSON RPC. If no file is 66 provided or file is '-' read configuration from stdin.""") 67 p.add_argument('--filename', help="""JSON Configuration file.""") 68 p.set_defaults(func=load_config) 69 70 # app 71 @call_cmd 72 def kill_instance(args): 73 rpc.app.kill_instance(args.client, args) 74 75 p = subparsers.add_parser('kill_instance', help='Send signal to instance') 76 p.add_argument('sig_name', help='signal will be sent to server.') 77 p.set_defaults(func=kill_instance) 78 79 @call_cmd 80 def context_switch_monitor(args): 81 print_dict(rpc.app.context_switch_monitor(args.client, args)) 82 83 p = subparsers.add_parser('context_switch_monitor', help='Control whether the context switch monitor is enabled') 84 p.add_argument('-e', '--enable', action='store_true', help='Enable context switch monitoring') 85 p.add_argument('-d', '--disable', action='store_true', help='Disable context switch monitoring') 86 p.set_defaults(func=context_switch_monitor) 87 88 # bdev 89 @call_cmd 90 def construct_malloc_bdev(args): 91 print_array(rpc.bdev.construct_malloc_bdev(args.client, args)) 92 93 p = subparsers.add_parser('construct_malloc_bdev', 94 help='Add a bdev with malloc backend') 95 p.add_argument('-b', '--name', help="Name of the bdev") 96 p.add_argument('-u', '--uuid', help="UUID of the bdev") 97 p.add_argument( 98 'total_size', help='Size of malloc bdev in MB (int > 0)', type=int) 99 p.add_argument('block_size', help='Block size for this bdev', type=int) 100 p.set_defaults(func=construct_malloc_bdev) 101 102 @call_cmd 103 def construct_null_bdev(args): 104 print_array(rpc.bdev.construct_null_bdev(args.client, args)) 105 106 p = subparsers.add_parser('construct_null_bdev', 107 help='Add a bdev with null backend') 108 p.add_argument('name', help='Block device name') 109 p.add_argument('-u', '--uuid', help='UUID of the bdev') 110 p.add_argument( 111 'total_size', help='Size of null bdev in MB (int > 0)', type=int) 112 p.add_argument('block_size', help='Block size for this bdev', type=int) 113 p.set_defaults(func=construct_null_bdev) 114 115 @call_cmd 116 def construct_aio_bdev(args): 117 print_array(rpc.bdev.construct_aio_bdev(args.client, args)) 118 119 p = subparsers.add_parser('construct_aio_bdev', 120 help='Add a bdev with aio backend') 121 p.add_argument('filename', help='Path to device or file (ex: /dev/sda)') 122 p.add_argument('name', help='Block device name') 123 p.add_argument('block_size', help='Block size for this bdev', type=int, default=argparse.SUPPRESS) 124 p.set_defaults(func=construct_aio_bdev) 125 126 @call_cmd 127 def construct_nvme_bdev(args): 128 print_array(rpc.bdev.construct_nvme_bdev(args.client, args)) 129 130 p = subparsers.add_parser('construct_nvme_bdev', 131 help='Add bdev with nvme backend') 132 p.add_argument('-b', '--name', help="Name of the bdev", required=True) 133 p.add_argument('-t', '--trtype', 134 help='NVMe-oF target trtype: e.g., rdma, pcie', required=True) 135 p.add_argument('-a', '--traddr', 136 help='NVMe-oF target address: e.g., an ip address or BDF', required=True) 137 p.add_argument('-f', '--adrfam', 138 help='NVMe-oF target adrfam: e.g., ipv4, ipv6, ib, fc, intra_host') 139 p.add_argument('-s', '--trsvcid', 140 help='NVMe-oF target trsvcid: e.g., a port number') 141 p.add_argument('-n', '--subnqn', help='NVMe-oF target subnqn') 142 p.set_defaults(func=construct_nvme_bdev) 143 144 @call_cmd 145 def construct_rbd_bdev(args): 146 print_array(rpc.bdev.construct_rbd_bdev(args.client, args)) 147 148 p = subparsers.add_parser('construct_rbd_bdev', 149 help='Add a bdev with ceph rbd backend') 150 p.add_argument('-b', '--name', help="Name of the bdev", required=False) 151 p.add_argument('pool_name', help='rbd pool name') 152 p.add_argument('rbd_name', help='rbd image name') 153 p.add_argument('block_size', help='rbd block size', type=int) 154 p.set_defaults(func=construct_rbd_bdev) 155 156 @call_cmd 157 def construct_error_bdev(args): 158 rpc.bdev.construct_error_bdev(args.client, args) 159 160 p = subparsers.add_parser('construct_error_bdev', 161 help='Add bdev with error injection backend') 162 p.add_argument('base_name', help='base bdev name') 163 p.set_defaults(func=construct_error_bdev) 164 165 @call_cmd 166 def construct_pmem_bdev(args): 167 print_array(rpc.bdev.construct_pmem_bdev(args.client, args)) 168 169 p = subparsers.add_parser('construct_pmem_bdev', help='Add a bdev with pmem backend') 170 p.add_argument('pmem_file', help='Path to pmemblk pool file') 171 p.add_argument('-n', '--name', help='Block device name', required=True) 172 p.set_defaults(func=construct_pmem_bdev) 173 174 @call_cmd 175 def construct_passthru_bdev(args): 176 print_array(rpc.bdev.construct_passthru_bdev(args.client, args)) 177 178 p = subparsers.add_parser('construct_passthru_bdev', 179 help='Add a pass through bdev on existing bdev') 180 p.add_argument('-b', '--base-bdev-name', help="Name of the existing bdev", required=True) 181 p.add_argument('-p', '--passthru-bdev-name', help="Name of the passthru bdev", required=True) 182 p.set_defaults(func=construct_passthru_bdev) 183 184 @call_cmd 185 def get_bdevs(args): 186 print_dict(rpc.bdev.get_bdevs(args.client, args)) 187 188 p = subparsers.add_parser( 189 'get_bdevs', help='Display current blockdev list or required blockdev') 190 p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False) 191 p.set_defaults(func=get_bdevs) 192 193 @call_cmd 194 def get_bdevs_config(args): 195 print_dict(rpc.bdev.get_bdevs_config(args.client, args)) 196 197 p = subparsers.add_parser( 198 'get_bdevs_config', help='Display current (live) blockdev configuration list or required blockdev') 199 p.add_argument('-b', '--name', help="Name of the Blockdev. Example: Nvme0n1", required=False) 200 p.set_defaults(func=get_bdevs_config) 201 202 @call_cmd 203 def delete_bdev(args): 204 rpc.bdev.delete_bdev(args.client, args) 205 206 p = subparsers.add_parser('delete_bdev', help='Delete a blockdev') 207 p.add_argument( 208 'bdev_name', help='Blockdev name to be deleted. Example: Malloc0.') 209 p.set_defaults(func=delete_bdev) 210 211 @call_cmd 212 def set_bdev_qos_limit_iops(args): 213 rpc.bdev.set_bdev_qos_limit_iops(args.client, args) 214 215 p = subparsers.add_parser('set_bdev_qos_limit_iops', help='Set QoS IOPS limit on a blockdev') 216 p.add_argument('name', help='Blockdev name to set QoS. Example: Malloc0') 217 p.add_argument('ios_per_sec', 218 help='IOs per second limit (>=10000, example: 20000). 0 means unlimited.', type=int) 219 p.set_defaults(func=set_bdev_qos_limit_iops) 220 221 @call_cmd 222 def bdev_inject_error(args): 223 rpc.bdev.bdev_inject_error(args.client, args) 224 225 p = subparsers.add_parser('bdev_inject_error', help='bdev inject error') 226 p.add_argument('name', help="""the name of the error injection bdev""") 227 p.add_argument('io_type', help="""io_type: 'clear' 'read' 'write' 'unmap' 'flush' 'all'""") 228 p.add_argument('error_type', help="""error_type: 'failure' 'pending'""") 229 p.add_argument( 230 '-n', '--num', help='the number of commands you want to fail', type=int, default=1) 231 p.set_defaults(func=bdev_inject_error) 232 233 @call_cmd 234 def apply_firmware(args): 235 print_dict(rpc.bdev.apply_firmware(args.client, args)) 236 237 p = subparsers.add_parser('apply_firmware', help='Download and commit firmware to NVMe device') 238 p.add_argument('filename', help='filename of the firmware to download') 239 p.add_argument('bdev_name', help='name of the NVMe device') 240 p.set_defaults(func=apply_firmware) 241 242 # iSCSI 243 @call_cmd 244 def get_portal_groups(args): 245 print_dict(rpc.iscsi.get_portal_groups(args.client, args)) 246 247 p = subparsers.add_parser( 248 'get_portal_groups', help='Display current portal group configuration') 249 p.set_defaults(func=get_portal_groups) 250 251 @call_cmd 252 def get_initiator_groups(args): 253 print_dict(rpc.iscsi.get_initiator_groups(args.client, args)) 254 255 p = subparsers.add_parser('get_initiator_groups', 256 help='Display current initiator group configuration') 257 p.set_defaults(func=get_initiator_groups) 258 259 @call_cmd 260 def get_target_nodes(args): 261 print_dict(rpc.iscsi.get_target_nodes(args.client, args)) 262 263 p = subparsers.add_parser('get_target_nodes', help='Display target nodes') 264 p.set_defaults(func=get_target_nodes) 265 266 @call_cmd 267 def construct_target_node(args): 268 rpc.iscsi.construct_target_node(args.client, args) 269 270 p = subparsers.add_parser('construct_target_node', 271 help='Add a target node') 272 p.add_argument('name', help='Target node name (ASCII)') 273 p.add_argument('alias_name', help='Target node alias name (ASCII)') 274 p.add_argument('bdev_name_id_pairs', help="""Whitespace-separated list of <bdev name:LUN ID> pairs enclosed 275 in quotes. Format: 'bdev_name0:id0 bdev_name1:id1' etc 276 Example: 'Malloc0:0 Malloc1:1 Malloc5:2' 277 *** The bdevs must pre-exist *** 278 *** LUN0 (id = 0) is required *** 279 *** bdevs names cannot contain space or colon characters ***""") 280 p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings 281 Whitespace separated, quoted, mapping defined with colon 282 separated list of "tags" (int > 0) 283 Example: '1:1 2:2 2:1' 284 *** The Portal/Initiator Groups must be precreated ***""") 285 p.add_argument('queue_depth', help='Desired target queue depth', type=int) 286 p.add_argument('-g', '--chap-group', help="""Authentication group ID for this target node. 287 *** Authentication group must be precreated ***""", type=int, default=0) 288 p.add_argument('-d', '--disable-chap', help="""CHAP authentication should be disabled for this target node. 289 *** Mutually exclusive with --require-chap ***""", action='store_true') 290 p.add_argument('-r', '--require-chap', help="""CHAP authentication should be required for this target node. 291 *** Mutually exclusive with --disable-chap ***""", action='store_true') 292 p.add_argument( 293 '-m', '--mutual-chap', help='CHAP authentication should be mutual/bidirectional.', action='store_true') 294 p.add_argument('-H', '--header-digest', 295 help='Header Digest should be required for this target node.', action='store_true') 296 p.add_argument('-D', '--data-digest', 297 help='Data Digest should be required for this target node.', action='store_true') 298 p.set_defaults(func=construct_target_node) 299 300 @call_cmd 301 def target_node_add_lun(args): 302 rpc.iscsi.target_node_add_lun(args.client, args) 303 304 p = subparsers.add_parser('target_node_add_lun', help='Add LUN to the target node') 305 p.add_argument('name', help='Target node name (ASCII)') 306 p.add_argument('bdev_name', help="""bdev name enclosed in quotes. 307 *** bdev name cannot contain space or colon characters ***""") 308 p.add_argument('-i', dest='lun_id', help="""LUN ID (integer >= 0) 309 *** If LUN ID is omitted or -1, the lowest free one is assigned ***""", type=int, required=False) 310 p.set_defaults(func=target_node_add_lun) 311 312 @call_cmd 313 def add_pg_ig_maps(args): 314 rpc.iscsi.add_pg_ig_maps(args.client, args) 315 316 p = subparsers.add_parser('add_pg_ig_maps', help='Add PG-IG maps to the target node') 317 p.add_argument('name', help='Target node name (ASCII)') 318 p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings 319 Whitespace separated, quoted, mapping defined with colon 320 separated list of "tags" (int > 0) 321 Example: '1:1 2:2 2:1' 322 *** The Portal/Initiator Groups must be precreated ***""") 323 p.set_defaults(func=add_pg_ig_maps) 324 325 @call_cmd 326 def delete_pg_ig_maps(args): 327 rpc.iscsi.delete_pg_ig_maps(args.client, args) 328 329 p = subparsers.add_parser('delete_pg_ig_maps', help='Delete PG-IG maps from the target node') 330 p.add_argument('name', help='Target node name (ASCII)') 331 p.add_argument('pg_ig_mappings', help="""List of (Portal_Group_Tag:Initiator_Group_Tag) mappings 332 Whitespace separated, quoted, mapping defined with colon 333 separated list of "tags" (int > 0) 334 Example: '1:1 2:2 2:1' 335 *** The Portal/Initiator Groups must be precreated ***""") 336 p.set_defaults(func=delete_pg_ig_maps) 337 338 @call_cmd 339 def add_portal_group(args): 340 rpc.iscsi.add_portal_group(args.client, args) 341 342 p = subparsers.add_parser('add_portal_group', help='Add a portal group') 343 p.add_argument( 344 'tag', help='Portal group tag (unique, integer > 0)', type=int) 345 p.add_argument('portal_list', nargs=argparse.REMAINDER, help="""List of portals in 'host:port@cpumask' format, separated by whitespace 346 (cpumask is optional and can be skipped) 347 Example: '192.168.100.100:3260' '192.168.100.100:3261' '192.168.100.100:3262@0x1""") 348 p.set_defaults(func=add_portal_group) 349 350 @call_cmd 351 def add_initiator_group(args): 352 rpc.iscsi.add_initiator_group(args.client, args) 353 354 p = subparsers.add_parser('add_initiator_group', 355 help='Add an initiator group') 356 p.add_argument( 357 'tag', help='Initiator group tag (unique, integer > 0)', type=int) 358 p.add_argument('initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses, 359 enclosed in quotes. Example: 'ANY' or '127.0.0.1 192.168.200.100'""") 360 p.add_argument('netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes. 361 Example: '255.255.0.0 255.248.0.0' etc""") 362 p.set_defaults(func=add_initiator_group) 363 364 @call_cmd 365 def add_initiators_to_initiator_group(args): 366 rpc.iscsi.add_initiators_to_initiator_group(args.client, args) 367 368 p = subparsers.add_parser('add_initiators_to_initiator_group', 369 help='Add initiators to an existing initiator group') 370 p.add_argument( 371 'tag', help='Initiator group tag (unique, integer > 0)', type=int) 372 p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses, 373 enclosed in quotes. This parameter can be omitted. Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False) 374 p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes. 375 This parameter can be omitted. Example: '255.255.0.0 255.248.0.0' etc""", required=False) 376 p.set_defaults(func=add_initiators_to_initiator_group) 377 378 @call_cmd 379 def delete_initiators_from_initiator_group(args): 380 rpc.iscsi.delete_initiators_from_initiator_group(args.client, args) 381 382 p = subparsers.add_parser('delete_initiators_from_initiator_group', 383 help='Delete initiators from an existing initiator group') 384 p.add_argument( 385 'tag', help='Initiator group tag (unique, integer > 0)', type=int) 386 p.add_argument('-n', dest='initiator_list', help="""Whitespace-separated list of initiator hostnames or IP addresses, 387 enclosed in quotes. This parameter can be omitted. Example: 'ANY' or '127.0.0.1 192.168.200.100'""", required=False) 388 p.add_argument('-m', dest='netmask_list', help="""Whitespace-separated list of initiator netmasks enclosed in quotes. 389 This parameter can be omitted. Example: '255.255.0.0 255.248.0.0' etc""", required=False) 390 p.set_defaults(func=delete_initiators_from_initiator_group) 391 392 @call_cmd 393 def delete_target_node(args): 394 rpc.iscsi.delete_target_node(args.client, args) 395 396 p = subparsers.add_parser('delete_target_node', 397 help='Delete a target node') 398 p.add_argument('target_node_name', 399 help='Target node name to be deleted. Example: iqn.2016-06.io.spdk:disk1.') 400 p.set_defaults(func=delete_target_node) 401 402 @call_cmd 403 def delete_portal_group(args): 404 rpc.iscsi.delete_portal_group(args.client, args) 405 406 p = subparsers.add_parser('delete_portal_group', 407 help='Delete a portal group') 408 p.add_argument( 409 'tag', help='Portal group tag (unique, integer > 0)', type=int) 410 p.set_defaults(func=delete_portal_group) 411 412 @call_cmd 413 def delete_initiator_group(args): 414 rpc.iscsi.delete_initiator_group(args.client, args) 415 416 p = subparsers.add_parser('delete_initiator_group', 417 help='Delete an initiator group') 418 p.add_argument( 419 'tag', help='Initiator group tag (unique, integer > 0)', type=int) 420 p.set_defaults(func=delete_initiator_group) 421 422 @call_cmd 423 def get_iscsi_connections(args): 424 print_dict(rpc.iscsi.get_iscsi_connections(args.client, args)) 425 426 p = subparsers.add_parser('get_iscsi_connections', 427 help='Display iSCSI connections') 428 p.set_defaults(func=get_iscsi_connections) 429 430 @call_cmd 431 def get_iscsi_global_params(args): 432 print_dict(rpc.iscsi.get_iscsi_global_params(args.client, args)) 433 434 p = subparsers.add_parser('get_iscsi_global_params', help='Display iSCSI global parameters') 435 p.set_defaults(func=get_iscsi_global_params) 436 437 @call_cmd 438 def get_scsi_devices(args): 439 print_dict(rpc.iscsi.get_scsi_devices(args.client, args)) 440 441 p = subparsers.add_parser('get_scsi_devices', help='Display SCSI devices') 442 p.set_defaults(func=get_scsi_devices) 443 444 # log 445 @call_cmd 446 def set_trace_flag(args): 447 rpc.log.set_trace_flag(args.client, args) 448 449 p = subparsers.add_parser('set_trace_flag', help='set trace flag') 450 p.add_argument( 451 'flag', help='trace mask we want to set. (for example "nvme").') 452 p.set_defaults(func=set_trace_flag) 453 454 @call_cmd 455 def clear_trace_flag(args): 456 rpc.log.clear_trace_flag(args.client, args) 457 458 p = subparsers.add_parser('clear_trace_flag', help='clear trace flag') 459 p.add_argument( 460 'flag', help='trace mask we want to clear. (for example "nvme").') 461 p.set_defaults(func=clear_trace_flag) 462 463 @call_cmd 464 def get_trace_flags(args): 465 print_dict(rpc.log.get_trace_flags(args.client, args)) 466 467 p = subparsers.add_parser('get_trace_flags', help='get trace flags') 468 p.set_defaults(func=get_trace_flags) 469 470 @call_cmd 471 def set_log_level(args): 472 rpc.log.set_log_level(args.client, args) 473 474 p = subparsers.add_parser('set_log_level', help='set log level') 475 p.add_argument('level', help='log level we want to set. (for example "DEBUG").') 476 p.set_defaults(func=set_log_level) 477 478 @call_cmd 479 def get_log_level(args): 480 print_dict(rpc.log.get_log_level(args.client, args)) 481 482 p = subparsers.add_parser('get_log_level', help='get log level') 483 p.set_defaults(func=get_log_level) 484 485 @call_cmd 486 def set_log_print_level(args): 487 rpc.log.set_log_print_level(args.client, args) 488 489 p = subparsers.add_parser('set_log_print_level', help='set log print level') 490 p.add_argument('level', help='log print level we want to set. (for example "DEBUG").') 491 p.set_defaults(func=set_log_print_level) 492 493 @call_cmd 494 def get_log_print_level(args): 495 print_dict(rpc.log.get_log_print_level(args.client, args)) 496 497 p = subparsers.add_parser('get_log_print_level', help='get log print level') 498 p.set_defaults(func=get_log_print_level) 499 500 # lvol 501 @call_cmd 502 def construct_lvol_store(args): 503 print_array(rpc.lvol.construct_lvol_store(args.client, 504 bdev_name=args.bdev_name, 505 lvs_name=args.lvs_name, 506 cluster_sz=args.cluster_sz)) 507 508 p = subparsers.add_parser('construct_lvol_store', help='Add logical volume store on base bdev') 509 p.add_argument('bdev_name', help='base bdev name') 510 p.add_argument('lvs_name', help='name for lvol store') 511 p.add_argument('-c', '--cluster-sz', help='size of cluster (in bytes)', type=int, required=False) 512 p.set_defaults(func=construct_lvol_store) 513 514 @call_cmd 515 def rename_lvol_store(args): 516 rpc.lvol.rename_lvol_store(args.client, 517 old_name=args.old_name, 518 new_name=args.new_name) 519 520 p = subparsers.add_parser('rename_lvol_store', help='Change logical volume store name') 521 p.add_argument('old_name', help='old name') 522 p.add_argument('new_name', help='new name') 523 p.set_defaults(func=rename_lvol_store) 524 525 @call_cmd 526 def construct_lvol_bdev(args): 527 print_array(rpc.lvol.construct_lvol_bdev(args.client, 528 lvol_name=args.lvol_name, 529 size=args.size * 1024 * 1024, 530 thin_provision=args.thin_provision, 531 uuid=args.uuid, 532 lvs_name=args.lvs_name)) 533 534 p = subparsers.add_parser('construct_lvol_bdev', help='Add a bdev with an logical volume backend') 535 p.add_argument('-u', '--uuid', help='lvol store UUID', required=False) 536 p.add_argument('-l', '--lvs-name', help='lvol store name', required=False) 537 p.add_argument('-t', '--thin-provision', action='store_true', help='create lvol bdev as thin provisioned') 538 p.add_argument('lvol_name', help='name for this lvol') 539 p.add_argument('size', help='size in MiB for this bdev', type=int) 540 p.set_defaults(func=construct_lvol_bdev) 541 542 @call_cmd 543 def snapshot_lvol_bdev(args): 544 rpc.lvol.snapshot_lvol_bdev(args.client, 545 lvol_name=args.lvol_name, 546 snapshot_name=args.snapshot_name) 547 548 p = subparsers.add_parser('snapshot_lvol_bdev', help='Create a snapshot of an lvol bdev') 549 p.add_argument('lvol_name', help='lvol bdev name') 550 p.add_argument('snapshot_name', help='lvol snapshot name') 551 p.set_defaults(func=snapshot_lvol_bdev) 552 553 @call_cmd 554 def clone_lvol_bdev(args): 555 rpc.lvol.clone_lvol_bdev(args.client, 556 snapshot_name=args.snapshot_name, 557 clone_name=args.clone_name) 558 559 p = subparsers.add_parser('clone_lvol_bdev', help='Create a clone of an lvol snapshot') 560 p.add_argument('snapshot_name', help='lvol snapshot name') 561 p.add_argument('clone_name', help='lvol clone name') 562 p.set_defaults(func=clone_lvol_bdev) 563 564 @call_cmd 565 def rename_lvol_bdev(args): 566 rpc.lvol.rename_lvol_bdev(args.client, 567 old_name=args.old_name, 568 new_name=args.new_name) 569 570 p = subparsers.add_parser('rename_lvol_bdev', help='Change lvol bdev name') 571 p.add_argument('old_name', help='lvol bdev name') 572 p.add_argument('new_name', help='new lvol name') 573 p.set_defaults(func=rename_lvol_bdev) 574 575 @call_cmd 576 def resize_lvol_bdev(args): 577 rpc.lvol.resize_lvol_bdev(args.client, 578 name=args.name, 579 size=args.size * 1024 * 1024) 580 581 p = subparsers.add_parser('resize_lvol_bdev', help='Resize existing lvol bdev') 582 p.add_argument('name', help='lvol bdev name') 583 p.add_argument('size', help='new size in MiB for this bdev', type=int) 584 p.set_defaults(func=resize_lvol_bdev) 585 586 @call_cmd 587 def destroy_lvol_bdev(args): 588 rpc.lvol.destroy_lvol_bdev(args.client, 589 name=args.name) 590 591 p = subparsers.add_parser('destroy_lvol_bdev', help='Destroy a logical volume') 592 p.add_argument('name', help='lvol bdev name') 593 p.set_defaults(func=destroy_lvol_bdev) 594 595 @call_cmd 596 def destroy_lvol_store(args): 597 rpc.lvol.destroy_lvol_store(args.client, 598 uuid=args.uuid, 599 lvs_name=args.lvs_name) 600 601 p = subparsers.add_parser('destroy_lvol_store', help='Destroy an logical volume store') 602 p.add_argument('-u', '--uuid', help='lvol store UUID', required=False) 603 p.add_argument('-l', '--lvs-name', help='lvol store name', required=False) 604 p.set_defaults(func=destroy_lvol_store) 605 606 @call_cmd 607 def get_lvol_stores(args): 608 print_dict(rpc.lvol.get_lvol_stores(args.client, 609 uuid=args.uuid, 610 lvs_name=args.lvs_name)) 611 612 p = subparsers.add_parser('get_lvol_stores', help='Display current logical volume store list') 613 p.add_argument('-u', '--uuid', help='lvol store UUID', required=False) 614 p.add_argument('-l', '--lvs-name', help='lvol store name', required=False) 615 p.set_defaults(func=get_lvol_stores) 616 617 # split 618 def construct_split_vbdev(args): 619 print_dict(rpc.bdev.construct_split_vbdev(args.client, args)) 620 621 p = subparsers.add_parser('construct_split_vbdev', help="""Add given disk name to split config. If bdev with base_name 622 name exist the split bdevs will be created right away, if not split bdevs will be created when base bdev became 623 available (during examination process).""") 624 p.add_argument('base_bdev', help='base bdev name') 625 p.add_argument('-s', '--split-size-mb', help='size in MiB for each bdev', type=int, default=0) 626 p.add_argument('split_count', help="""Optional - number of split bdevs to create. Total size * split_count must not 627 exceed the base bdev size.""", type=int) 628 p.set_defaults(func=construct_split_vbdev) 629 630 def destruct_split_vbdev(args): 631 rpc.destruct_split_vbdev(args.client, args) 632 633 p = subparsers.add_parser('destruct_split_vbdev', help="""Delete split config with all created splits.""") 634 p.add_argument('base_bdev', help='base bdev name') 635 p.set_defaults(func=destruct_split_vbdev) 636 637 # nbd 638 @call_cmd 639 def start_nbd_disk(args): 640 rpc.nbd.start_nbd_disk(args.client, 641 bdev_name=args.bdev_name, 642 nbd_device=args.nbd_device) 643 644 p = subparsers.add_parser('start_nbd_disk', help='Export a bdev as a nbd disk') 645 p.add_argument('bdev_name', help='Blockdev name to be exported. Example: Malloc0.') 646 p.add_argument('nbd_device', help='Nbd device name to be assigned. Example: /dev/nbd0.') 647 p.set_defaults(func=start_nbd_disk) 648 649 @call_cmd 650 def stop_nbd_disk(args): 651 rpc.nbd.stop_nbd_disk(args.client, 652 nbd_device=args.nbd_device) 653 654 p = subparsers.add_parser('stop_nbd_disk', help='Stop a nbd disk') 655 p.add_argument('nbd_device', help='Nbd device name to be stopped. Example: /dev/nbd0.') 656 p.set_defaults(func=stop_nbd_disk) 657 658 @call_cmd 659 def get_nbd_disks(args): 660 print_dict(rpc.nbd.get_nbd_disks(args.client, 661 nbd_device=args.nbd_device)) 662 663 p = subparsers.add_parser('get_nbd_disks', help='Display full or specified nbd device list') 664 p.add_argument('-n', '--nbd-device', help="Path of the nbd device. Example: /dev/nbd0", required=False) 665 p.set_defaults(func=get_nbd_disks) 666 667 # net 668 @call_cmd 669 def add_ip_address(args): 670 rpc.net.add_ip_address(args.client, args) 671 672 p = subparsers.add_parser('add_ip_address', help='Add IP address') 673 p.add_argument('ifc_index', help='ifc index of the nic device.', type=int) 674 p.add_argument('ip_addr', help='ip address will be added.') 675 p.set_defaults(func=add_ip_address) 676 677 @call_cmd 678 def delete_ip_address(args): 679 rpc.net.delete_ip_address(args.client, args) 680 681 p = subparsers.add_parser('delete_ip_address', help='Delete IP address') 682 p.add_argument('ifc_index', help='ifc index of the nic device.', type=int) 683 p.add_argument('ip_addr', help='ip address will be deleted.') 684 p.set_defaults(func=delete_ip_address) 685 686 @call_cmd 687 def get_interfaces(args): 688 print_dict(rpc.net.get_interfaces(args.client, args)) 689 690 p = subparsers.add_parser( 691 'get_interfaces', help='Display current interface list') 692 p.set_defaults(func=get_interfaces) 693 694 # NVMe-oF 695 @call_cmd 696 def get_nvmf_subsystems(args): 697 print_dict(rpc.nvmf.get_nvmf_subsystems(args.client, args)) 698 699 p = subparsers.add_parser('get_nvmf_subsystems', 700 help='Display nvmf subsystems') 701 p.set_defaults(func=get_nvmf_subsystems) 702 703 @call_cmd 704 def construct_nvmf_subsystem(args): 705 rpc.nvmf.construct_nvmf_subsystem(args.client, args) 706 707 p = subparsers.add_parser('construct_nvmf_subsystem', help='Add a nvmf subsystem') 708 p.add_argument('nqn', help='Target nqn(ASCII)') 709 p.add_argument('listen', help="""comma-separated list of Listen <trtype:transport_name traddr:address trsvcid:port_id> pairs enclosed 710 in quotes. Format: 'trtype:transport0 traddr:traddr0 trsvcid:trsvcid0,trtype:transport1 traddr:traddr1 trsvcid:trsvcid1' etc 711 Example: 'trtype:RDMA traddr:192.168.100.8 trsvcid:4420,trtype:RDMA traddr:192.168.100.9 trsvcid:4420'""") 712 p.add_argument('hosts', help="""Whitespace-separated list of host nqn list. 713 Format: 'nqn1 nqn2' etc 714 Example: 'nqn.2016-06.io.spdk:init nqn.2016-07.io.spdk:init'""") 715 p.add_argument("-a", "--allow-any-host", action='store_true', help="Allow any host to connect (don't enforce host NQN whitelist)") 716 p.add_argument("-s", "--serial-number", help=""" 717 Format: 'sn' etc 718 Example: 'SPDK00000000000001'""", default='0000:00:01.0') 719 p.add_argument("-n", "--namespaces", help="""Whitespace-separated list of namespaces 720 Format: 'bdev_name1[:nsid1] bdev_name2[:nsid2] bdev_name3[:nsid3]' etc 721 Example: '1:Malloc0 2:Malloc1 3:Malloc2' 722 *** The devices must pre-exist ***""") 723 p.add_argument("-m", "--max-namespaces", help="Maximum number of namespaces allowed to added during active connection", 724 type=int, default=0) 725 p.set_defaults(func=construct_nvmf_subsystem) 726 727 @call_cmd 728 def delete_nvmf_subsystem(args): 729 rpc.nvmf.delete_nvmf_subsystem(args.client, args) 730 731 p = subparsers.add_parser('delete_nvmf_subsystem', 732 help='Delete a nvmf subsystem') 733 p.add_argument('subsystem_nqn', 734 help='subsystem nqn to be deleted. Example: nqn.2016-06.io.spdk:cnode1.') 735 p.set_defaults(func=delete_nvmf_subsystem) 736 737 @call_cmd 738 def nvmf_subsystem_add_listener(args): 739 rpc.nvmf.nvmf_subsystem_add_listener(args.client, args) 740 741 p = subparsers.add_parser('nvmf_subsystem_add_listener', help='Add a listener to an NVMe-oF subsystem') 742 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 743 p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True) 744 p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True) 745 p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host') 746 p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number') 747 p.set_defaults(func=nvmf_subsystem_add_listener) 748 749 @call_cmd 750 def nvmf_subsystem_remove_listener(args): 751 rpc.nvmf.nvmf_subsystem_remove_listener(args.client, args) 752 753 p = subparsers.add_parser('nvmf_subsystem_remove_listener', help='Remove a listener from an NVMe-oF subsystem') 754 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 755 p.add_argument('-t', '--trtype', help='NVMe-oF transport type: e.g., rdma', required=True) 756 p.add_argument('-a', '--traddr', help='NVMe-oF transport address: e.g., an ip address', required=True) 757 p.add_argument('-f', '--adrfam', help='NVMe-oF transport adrfam: e.g., ipv4, ipv6, ib, fc, intra_host') 758 p.add_argument('-s', '--trsvcid', help='NVMe-oF transport service id: e.g., a port number') 759 p.set_defaults(func=nvmf_subsystem_remove_listener) 760 761 @call_cmd 762 def nvmf_subsystem_add_ns(args): 763 rpc.nvmf.nvmf_subsystem_add_ns(args.client, args) 764 765 p = subparsers.add_parser('nvmf_subsystem_add_ns', help='Add a namespace to an NVMe-oF subsystem') 766 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 767 p.add_argument('bdev_name', help='The name of the bdev that will back this namespace') 768 p.add_argument('-n', '--nsid', help='The requested NSID (optional)', type=int) 769 p.add_argument('-g', '--nguid', help='Namespace globally unique identifier (optional)') 770 p.add_argument('-e', '--eui64', help='Namespace EUI-64 identifier (optional)') 771 p.set_defaults(func=nvmf_subsystem_add_ns) 772 773 @call_cmd 774 def nvmf_subsystem_remove_ns(args): 775 rpc.nvmf.nvmf_subsystem_remove_ns(args.client, args) 776 777 p = subparsers.add_parser('nvmf_subsystem_remove_ns', help='Remove a namespace to an NVMe-oF subsystem') 778 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 779 p.add_argument('nsid', help='The requested NSID', type=int) 780 p.set_defaults(func=nvmf_subsystem_remove_ns) 781 782 @call_cmd 783 def nvmf_subsystem_add_host(args): 784 rpc.nvmf.nvmf_subsystem_add_host(args.client, args) 785 786 p = subparsers.add_parser('nvmf_subsystem_add_host', help='Add a host to an NVMe-oF subsystem') 787 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 788 p.add_argument('host', help='Host NQN to allow') 789 p.set_defaults(func=nvmf_subsystem_add_host) 790 791 @call_cmd 792 def nvmf_subsystem_remove_host(args): 793 rpc.nvmf.nvmf_subsystem_remove_host(args.client, args) 794 795 p = subparsers.add_parser('nvmf_subsystem_remove_host', help='Remove a host from an NVMe-oF subsystem') 796 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 797 p.add_argument('host', help='Host NQN to remove') 798 p.set_defaults(func=nvmf_subsystem_remove_host) 799 800 @call_cmd 801 def nvmf_subsystem_allow_any_host(args): 802 rpc.nvmf.nvmf_subsystem_allow_any_host(args.client, args) 803 804 p = subparsers.add_parser('nvmf_subsystem_allow_any_host', help='Allow any host to connect to the subsystem') 805 p.add_argument('nqn', help='NVMe-oF subsystem NQN') 806 p.add_argument('-e', '--enable', action='store_true', help='Enable allowing any host') 807 p.add_argument('-d', '--disable', action='store_true', help='Disable allowing any host') 808 p.set_defaults(func=nvmf_subsystem_allow_any_host) 809 810 # pmem 811 @call_cmd 812 def create_pmem_pool(args): 813 rpc.pmem.create_pmem_pool(args.client, args) 814 815 p = subparsers.add_parser('create_pmem_pool', help='Create pmem pool') 816 p.add_argument('pmem_file', help='Path to pmemblk pool file') 817 p.add_argument('total_size', help='Size of malloc bdev in MB (int > 0)', type=int) 818 p.add_argument('block_size', help='Block size for this pmem pool', type=int) 819 p.set_defaults(func=create_pmem_pool) 820 821 @call_cmd 822 def pmem_pool_info(args): 823 print_dict(rpc.pmem.pmem_pool_info(args.client, args)) 824 825 p = subparsers.add_parser('pmem_pool_info', help='Display pmem pool info and check consistency') 826 p.add_argument('pmem_file', help='Path to pmemblk pool file') 827 p.set_defaults(func=pmem_pool_info) 828 829 @call_cmd 830 def delete_pmem_pool(args): 831 rpc.pmem.delete_pmem_pool(args.client, args) 832 833 p = subparsers.add_parser('delete_pmem_pool', help='Delete pmem pool') 834 p.add_argument('pmem_file', help='Path to pmemblk pool file') 835 p.set_defaults(func=delete_pmem_pool) 836 837 # subsystem 838 @call_cmd 839 def get_subsystems(args): 840 print_dict(rpc.subsystem.get_subsystems(args.client)) 841 842 p = subparsers.add_parser('get_subsystems', help="""Print subsystems array in initialization order. Each subsystem 843 entry contain (unsorted) array of subsystems it depends on.""") 844 p.set_defaults(func=get_subsystems) 845 846 @call_cmd 847 def get_subsystem_config(args): 848 print_dict(rpc.subsystem.get_subsystem_config(args.client, args.name)) 849 850 p = subparsers.add_parser('get_subsystem_config', help="""Print subsystem configuration""") 851 p.add_argument('name', help='Name of subsystem to query') 852 p.set_defaults(func=get_subsystem_config) 853 854 # vhost 855 @call_cmd 856 def set_vhost_controller_coalescing(args): 857 rpc.vhost.set_vhost_controller_coalescing(args.client, args) 858 859 p = subparsers.add_parser('set_vhost_controller_coalescing', help='Set vhost controller coalescing') 860 p.add_argument('ctrlr', help='controller name') 861 p.add_argument('delay_base_us', help='Base delay time', type=int) 862 p.add_argument('iops_threshold', help='IOPS threshold when coalescing is enabled', type=int) 863 p.set_defaults(func=set_vhost_controller_coalescing) 864 865 @call_cmd 866 def construct_vhost_scsi_controller(args): 867 rpc.vhost.construct_vhost_scsi_controller(args.client, args) 868 869 p = subparsers.add_parser( 870 'construct_vhost_scsi_controller', help='Add new vhost controller') 871 p.add_argument('ctrlr', help='controller name') 872 p.add_argument('--cpumask', help='cpu mask for this controller') 873 p.set_defaults(func=construct_vhost_scsi_controller) 874 875 @call_cmd 876 def add_vhost_scsi_lun(args): 877 rpc.vhost.add_vhost_scsi_lun(args.client, args) 878 879 p = subparsers.add_parser('add_vhost_scsi_lun', 880 help='Add lun to vhost controller') 881 p.add_argument('ctrlr', help='conntroller name where add lun') 882 p.add_argument('scsi_target_num', help='scsi_target_num', type=int) 883 p.add_argument('bdev_name', help='bdev name') 884 p.set_defaults(func=add_vhost_scsi_lun) 885 886 @call_cmd 887 def remove_vhost_scsi_target(args): 888 rpc.vhost.remove_vhost_scsi_target(args.client, args) 889 890 p = subparsers.add_parser('remove_vhost_scsi_target', help='Remove target from vhost controller') 891 p.add_argument('ctrlr', help='controller name to remove target from') 892 p.add_argument('scsi_target_num', help='scsi_target_num', type=int) 893 p.set_defaults(func=remove_vhost_scsi_target) 894 895 @call_cmd 896 def construct_vhost_blk_controller(args): 897 rpc.vhost.construct_vhost_blk_controller(args.client, args) 898 899 p = subparsers.add_parser('construct_vhost_blk_controller', help='Add a new vhost block controller') 900 p.add_argument('ctrlr', help='controller name') 901 p.add_argument('dev_name', help='device name') 902 p.add_argument('--cpumask', help='cpu mask for this controller') 903 p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only') 904 p.set_defaults(func=construct_vhost_blk_controller) 905 906 @call_cmd 907 def construct_vhost_nvme_controller(args): 908 rpc.vhost.construct_vhost_nvme_controller(args.client, args) 909 910 p = subparsers.add_parser('construct_vhost_nvme_controller', help='Add new vhost controller') 911 p.add_argument('ctrlr', help='controller name') 912 p.add_argument('io_queues', help='number of IO queues for the controller', type=int) 913 p.add_argument('--cpumask', help='cpu mask for this controller') 914 p.set_defaults(func=construct_vhost_nvme_controller) 915 916 @call_cmd 917 def add_vhost_nvme_ns(args): 918 rpc.vhost.add_vhost_nvme_ns(args.client, args) 919 920 p = subparsers.add_parser('add_vhost_nvme_ns', help='Add a Namespace to vhost controller') 921 p.add_argument('ctrlr', help='conntroller name where add a Namespace') 922 p.add_argument('bdev_name', help='block device name for a new Namespace') 923 p.set_defaults(func=add_vhost_nvme_ns) 924 925 @call_cmd 926 def get_vhost_controllers(args): 927 print_dict(rpc.vhost.get_vhost_controllers(args.client, args)) 928 929 p = subparsers.add_parser('get_vhost_controllers', help='List vhost controllers') 930 p.set_defaults(func=get_vhost_controllers) 931 932 @call_cmd 933 def remove_vhost_controller(args): 934 rpc.vhost.remove_vhost_controller(args.client, args) 935 936 p = subparsers.add_parser('remove_vhost_controller', help='Remove a vhost controller') 937 p.add_argument('ctrlr', help='controller name') 938 p.set_defaults(func=remove_vhost_controller) 939 940 @call_cmd 941 def construct_virtio_dev(args): 942 print_dict(rpc.vhost.construct_virtio_dev(args.client, args)) 943 944 p = subparsers.add_parser('construct_virtio_dev', help="""Construct new virtio device using provided 945 transport type and device type. In case of SCSI device type this implies scan and add bdevs offered by 946 remote side. Result is array of added bdevs.""") 947 p.add_argument('name', help="Use this name as base for new created bdevs") 948 p.add_argument('-t', '--trtype', 949 help='Virtio target transport type: pci or user', required=True) 950 p.add_argument('-a', '--traddr', 951 help='Transport type specific target address: e.g. UNIX domain socket path or BDF', required=True) 952 p.add_argument('-d', '--dev-type', 953 help='Device type: blk or scsi', required=True) 954 p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int) 955 p.add_argument('--vq-size', help='Size of each queue', type=int) 956 p.set_defaults(func=construct_virtio_dev) 957 958 @call_cmd 959 def construct_virtio_user_scsi_bdev(args): 960 print_dict(rpc.vhost.construct_virtio_user_scsi_bdev(args.client, args)) 961 962 p = subparsers.add_parser('construct_virtio_user_scsi_bdev', help="""Connect to virtio user scsi device. 963 This imply scan and add bdevs offered by remote side. 964 Result is array of added bdevs.""") 965 p.add_argument('path', help='Path to Virtio SCSI socket') 966 p.add_argument('name', help="""Use this name as base instead of 'VirtioScsiN' 967 Base will be used to construct new bdev's found on target by adding 't<TARGET_ID>' sufix.""") 968 p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int) 969 p.add_argument('--vq-size', help='Size of each queue', type=int) 970 p.set_defaults(func=construct_virtio_user_scsi_bdev) 971 972 @call_cmd 973 def construct_virtio_pci_scsi_bdev(args): 974 print_dict(rpc.vhost.construct_virtio_pci_scsi_bdev(args.client, args)) 975 976 p = subparsers.add_parser('construct_virtio_pci_scsi_bdev', help="""Create a Virtio 977 SCSI device from a virtio-pci device.""") 978 p.add_argument('pci_address', help="""PCI address in domain:bus:device.function format or 979 domain.bus.device.function format""") 980 p.add_argument('name', help="""Name for the virtio device. 981 It will be inhereted by all created bdevs, which are named n the following format: <name>t<target_id>""") 982 p.set_defaults(func=construct_virtio_pci_scsi_bdev) 983 984 @call_cmd 985 def get_virtio_scsi_devs(args): 986 print_dict(rpc.vhost.get_virtio_scsi_devs(args.client, args)) 987 988 p = subparsers.add_parser('get_virtio_scsi_devs', help='List all Virtio-SCSI devices.') 989 p.set_defaults(func=get_virtio_scsi_devs) 990 991 @call_cmd 992 def remove_virtio_scsi_bdev(args): 993 rpc.vhost.remove_virtio_scsi_bdev(args.client, args) 994 995 p = subparsers.add_parser('remove_virtio_scsi_bdev', help="""Remove a Virtio-SCSI device 996 This will delete all bdevs exposed by this device""") 997 p.add_argument('name', help='Virtio device name. E.g. VirtioUser0') 998 p.set_defaults(func=remove_virtio_scsi_bdev) 999 1000 @call_cmd 1001 def construct_virtio_user_blk_bdev(args): 1002 print_dict(rpc.vhost.construct_virtio_user_blk_bdev(args.client, args)) 1003 1004 p = subparsers.add_parser('construct_virtio_user_blk_bdev', help='Connect to a virtio user blk device.') 1005 p.add_argument('path', help='Path to Virtio BLK socket') 1006 p.add_argument('name', help='Name for the bdev') 1007 p.add_argument('--vq-count', help='Number of virtual queues to be used.', type=int) 1008 p.add_argument('--vq-size', help='Size of each queue', type=int) 1009 p.set_defaults(func=construct_virtio_user_blk_bdev) 1010 1011 @call_cmd 1012 def construct_virtio_pci_blk_bdev(args): 1013 print_dict(rpc.vhost.construct_virtio_pci_blk_bdev(args.client, args)) 1014 1015 p = subparsers.add_parser('construct_virtio_pci_blk_bdev', help='Create a Virtio Blk device from a virtio-pci device.') 1016 p.add_argument('pci_address', help="""PCI address in domain:bus:device.function format or 1017 domain.bus.device.function format""") 1018 p.add_argument('name', help='Name for the bdev') 1019 p.set_defaults(func=construct_virtio_pci_blk_bdev) 1020 1021 args = parser.parse_args() 1022 1023 try: 1024 args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.verbose, args.timeout) 1025 except JSONRPCException as ex: 1026 print(ex.message) 1027 exit(1) 1028 args.func(args) 1029