1# SPDX-License-Identifier: BSD-3-Clause 2# Copyright (C) 2018 Intel Corporation. 3# All rights reserved. 4 5from .ui_node import UINode, UIBdevs, UILvolStores, UIVhosts 6from .ui_node_nvmf import UINVMf 7from .ui_node_iscsi import UIISCSI 8from .. import rpc 9from functools import wraps 10 11 12class UIRoot(UINode): 13 """ 14 Root node for CLI menu tree structure. Refreshes running config on startup. 15 """ 16 def __init__(self, client, shell): 17 UINode.__init__(self, "/", shell=shell) 18 self.current_bdevs = [] 19 self.current_lvol_stores = [] 20 self.current_vhost_ctrls = [] 21 self.current_nvmf_transports = [] 22 self.current_nvmf_subsystems = [] 23 self.current_nvmf_referrals = [] 24 self.set_rpc_target(client) 25 self.verbose = False 26 self.is_init = self.check_init() 27 self.methods = [] 28 29 def refresh(self): 30 self.methods = self.rpc_get_methods(current=True) 31 if self.is_init is False: 32 methods = "\n".join(self.methods) 33 self.shell.log.warning("SPDK Application is not yet initialized.\n" 34 "Please initialize subsystems with framework_start_init command.\n" 35 "List of available commands in current state:\n" 36 "%s" % methods) 37 else: 38 # Pass because we'd like to build main tree structure for "ls" 39 # even if state is uninitialized 40 pass 41 42 self._children = set([]) 43 UIBdevs(self) 44 UILvolStores(self) 45 if self.has_subsystem("vhost_scsi") or self.has_subsystem("vhost_blk"): 46 UIVhosts(self) 47 if self.has_subsystem("nvmf"): 48 UINVMf(self) 49 if self.has_subsystem("iscsi"): 50 UIISCSI(self) 51 52 def set_rpc_target(self, client): 53 self.client = client 54 55 def print_array(self, a): 56 return " ".join(a) 57 58 def verbose(f): 59 # For any configuration calls (create, delete, construct, etc.) 60 # Check if verbose option is to be used and set appropriately. 61 # Do not use for "get_*" methods so that output is not 62 # flooded. 63 def w(self, **kwargs): 64 self.client.log_set_level("INFO" if self.verbose else "ERROR") 65 r = f(self, **kwargs) 66 self.client.log_set_level("ERROR") 67 return r 68 return w 69 70 def is_method_available(f): 71 # Check if method f is available for given spdk target 72 def w(self, **kwargs): 73 if f.__name__ in self.methods: 74 r = f(self, **kwargs) 75 return r 76 # If given method is not available return empty list 77 # similar to real get_* like rpc 78 return [] 79 return w 80 81 def ui_command_framework_start_init(self): 82 if rpc.framework_start_init(self.client): 83 self.is_init = True 84 self.refresh() 85 86 def ui_command_load_config(self, filename): 87 with open(filename, "r") as fd: 88 rpc.load_config(self.client, fd) 89 90 def ui_command_load_subsystem_config(self, filename): 91 with open(filename, "r") as fd: 92 rpc.load_subsystem_config(self.client, fd) 93 94 def ui_command_save_config(self, filename, indent=2): 95 with open(filename, "w") as fd: 96 rpc.save_config(self.client, fd, indent) 97 98 def ui_command_save_subsystem_config(self, filename, subsystem, indent=2): 99 with open(filename, "w") as fd: 100 rpc.save_subsystem_config(self.client, fd, indent, subsystem) 101 102 def rpc_get_methods(self, current=False): 103 return rpc.rpc_get_methods(self.client, current=current) 104 105 def check_init(self): 106 return "framework_start_init" not in self.rpc_get_methods(current=True) 107 108 def bdev_get_bdevs(self, bdev_type): 109 if self.is_init: 110 self.current_bdevs = rpc.bdev.bdev_get_bdevs(self.client) 111 # Following replace needs to be done in order for some of the bdev 112 # listings to work: logical volumes, split disk. 113 # For example logical volumes: listing in menu is "Logical_Volume" 114 # (cannot have space), but the product name in SPDK is "Logical Volume" 115 bdev_type = bdev_type.replace("_", " ") 116 for bdev in [x for x in self.current_bdevs if bdev_type in x["product_name"].lower()]: 117 test = Bdev(bdev) 118 yield test 119 120 def bdev_get_iostat(self, **kwargs): 121 return rpc.bdev.bdev_get_iostat(self.client, **kwargs) 122 123 @verbose 124 def bdev_split_create(self, **kwargs): 125 response = rpc.bdev.bdev_split_create(self.client, **kwargs) 126 return self.print_array(response) 127 128 @verbose 129 def bdev_split_delete(self, **kwargs): 130 rpc.bdev.bdev_split_delete(self.client, **kwargs) 131 132 @verbose 133 def create_malloc_bdev(self, **kwargs): 134 response = rpc.bdev.bdev_malloc_create(self.client, **kwargs) 135 return response 136 137 @verbose 138 def bdev_malloc_delete(self, **kwargs): 139 rpc.bdev.bdev_malloc_delete(self.client, **kwargs) 140 141 @verbose 142 def create_iscsi_bdev(self, **kwargs): 143 response = rpc.bdev.bdev_iscsi_create(self.client, **kwargs) 144 return response 145 146 @verbose 147 def bdev_iscsi_delete(self, **kwargs): 148 rpc.bdev.bdev_iscsi_delete(self.client, **kwargs) 149 150 @verbose 151 def bdev_aio_create(self, **kwargs): 152 response = rpc.bdev.bdev_aio_create(self.client, **kwargs) 153 return response 154 155 @verbose 156 def bdev_aio_delete(self, **kwargs): 157 rpc.bdev.bdev_aio_delete(self.client, **kwargs) 158 159 @verbose 160 def create_lvol_bdev(self, **kwargs): 161 response = rpc.lvol.bdev_lvol_create(self.client, **kwargs) 162 return response 163 164 @verbose 165 def bdev_lvol_delete(self, **kwargs): 166 response = rpc.lvol.bdev_lvol_delete(self.client, **kwargs) 167 return response 168 169 @verbose 170 def create_nvme_bdev(self, **kwargs): 171 response = rpc.bdev.bdev_nvme_attach_controller(self.client, **kwargs) 172 return response 173 174 @verbose 175 def bdev_nvme_detach_controller(self, **kwargs): 176 rpc.bdev.bdev_nvme_detach_controller(self.client, **kwargs) 177 178 @verbose 179 def bdev_null_create(self, **kwargs): 180 response = rpc.bdev.bdev_null_create(self.client, **kwargs) 181 return response 182 183 @verbose 184 def bdev_null_delete(self, **kwargs): 185 rpc.bdev.bdev_null_delete(self.client, **kwargs) 186 187 @verbose 188 def create_error_bdev(self, **kwargs): 189 response = rpc.bdev.bdev_error_create(self.client, **kwargs) 190 191 @verbose 192 def bdev_error_delete(self, **kwargs): 193 rpc.bdev.bdev_error_delete(self.client, **kwargs) 194 195 @verbose 196 @is_method_available 197 def bdev_lvol_get_lvstores(self): 198 if self.is_init: 199 self.current_lvol_stores = rpc.lvol.bdev_lvol_get_lvstores(self.client) 200 for lvs in self.current_lvol_stores: 201 yield LvolStore(lvs) 202 203 @verbose 204 def bdev_lvol_create_lvstore(self, **kwargs): 205 response = rpc.lvol.bdev_lvol_create_lvstore(self.client, **kwargs) 206 return response 207 208 @verbose 209 def bdev_lvol_delete_lvstore(self, **kwargs): 210 rpc.lvol.bdev_lvol_delete_lvstore(self.client, **kwargs) 211 212 @verbose 213 def create_rbd_bdev(self, **kwargs): 214 response = rpc.bdev.bdev_rbd_create(self.client, **kwargs) 215 return response 216 217 @verbose 218 def bdev_rbd_delete(self, **kwargs): 219 response = rpc.bdev.bdev_rbd_delete(self.client, **kwargs) 220 return response 221 222 @verbose 223 def create_virtio_dev(self, **kwargs): 224 response = rpc.vhost.bdev_virtio_attach_controller(self.client, **kwargs) 225 return self.print_array(response) 226 227 @verbose 228 def bdev_virtio_detach_controller(self, **kwargs): 229 response = rpc.vhost.bdev_virtio_detach_controller(self.client, **kwargs) 230 return response 231 232 @verbose 233 def bdev_raid_create(self, **kwargs): 234 rpc.bdev.bdev_raid_create(self.client, **kwargs) 235 236 @verbose 237 def bdev_raid_delete(self, **kwargs): 238 rpc.bdev.bdev_raid_delete(self.client, **kwargs) 239 240 @verbose 241 def bdev_uring_create(self, **kwargs): 242 response = rpc.bdev.bdev_uring_create(self.client, **kwargs) 243 return response 244 245 @verbose 246 def bdev_uring_delete(self, **kwargs): 247 rpc.bdev.bdev_uring_delete(self.client, **kwargs) 248 249 @verbose 250 @is_method_available 251 def bdev_virtio_scsi_get_devices(self): 252 if self.is_init: 253 for bdev in rpc.vhost.bdev_virtio_scsi_get_devices(self.client): 254 test = Bdev(bdev) 255 yield test 256 257 def list_vhost_ctrls(self): 258 if self.is_init: 259 self.current_vhost_ctrls = rpc.vhost.vhost_get_controllers(self.client) 260 261 @verbose 262 @is_method_available 263 def vhost_get_controllers(self, ctrlr_type): 264 if self.is_init: 265 self.list_vhost_ctrls() 266 for ctrlr in [x for x in self.current_vhost_ctrls if ctrlr_type in list(x["backend_specific"].keys())]: 267 yield VhostCtrlr(ctrlr) 268 269 @verbose 270 def vhost_delete_controller(self, **kwargs): 271 rpc.vhost.vhost_delete_controller(self.client, **kwargs) 272 273 @verbose 274 def vhost_create_scsi_controller(self, **kwargs): 275 rpc.vhost.vhost_create_scsi_controller(self.client, **kwargs) 276 277 @verbose 278 def vhost_start_scsi_controller(self, **kwargs): 279 rpc.vhost.vhost_start_scsi_controller(self.client, **kwargs) 280 281 @verbose 282 def vhost_create_blk_controller(self, **kwargs): 283 rpc.vhost.vhost_create_blk_controller(self.client, **kwargs) 284 285 @verbose 286 def vhost_scsi_controller_remove_target(self, **kwargs): 287 rpc.vhost.vhost_scsi_controller_remove_target(self.client, **kwargs) 288 289 @verbose 290 def vhost_scsi_controller_add_target(self, **kwargs): 291 rpc.vhost.vhost_scsi_controller_add_target(self.client, **kwargs) 292 293 def vhost_controller_set_coalescing(self, **kwargs): 294 rpc.vhost.vhost_controller_set_coalescing(self.client, **kwargs) 295 296 @verbose 297 def create_nvmf_transport(self, **kwargs): 298 rpc.nvmf.nvmf_create_transport(self.client, **kwargs) 299 300 def list_nvmf_transports(self): 301 if self.is_init: 302 self.current_nvmf_transports = rpc.nvmf.nvmf_get_transports(self.client) 303 304 @verbose 305 @is_method_available 306 def nvmf_get_transports(self): 307 if self.is_init: 308 self.list_nvmf_transports() 309 for transport in self.current_nvmf_transports: 310 yield NvmfTransport(transport) 311 312 def list_nvmf_subsystems(self): 313 if self.is_init: 314 self.current_nvmf_subsystems = rpc.nvmf.nvmf_get_subsystems(self.client) 315 316 @verbose 317 @is_method_available 318 def nvmf_get_subsystems(self): 319 if self.is_init: 320 self.list_nvmf_subsystems() 321 for subsystem in self.current_nvmf_subsystems: 322 yield NvmfSubsystem(subsystem) 323 324 def list_nvmf_referrals(self): 325 if self.is_init: 326 self.current_nvmf_referrals = rpc.nvmf.nvmf_discovery_get_referrals(self.client) 327 328 @verbose 329 @is_method_available 330 def nvmf_discovery_get_referrals(self): 331 if self.is_init: 332 self.list_nvmf_referrals() 333 for referral in self.current_nvmf_referrals: 334 yield NvmfReferral(referral) 335 336 @verbose 337 def nvmf_discovery_add_referral(self, **kwargs): 338 rpc.nvmf.nvmf_discovery_add_referral(self.client, **kwargs) 339 340 @verbose 341 def nvmf_discovery_remove_referral(self, **kwargs): 342 rpc.nvmf.nvmf_discovery_remove_referral(self.client, **kwargs) 343 344 @verbose 345 def create_nvmf_subsystem(self, **kwargs): 346 rpc.nvmf.nvmf_create_subsystem(self.client, **kwargs) 347 348 @verbose 349 def nvmf_delete_subsystem(self, **kwargs): 350 rpc.nvmf.nvmf_delete_subsystem(self.client, **kwargs) 351 352 @verbose 353 def nvmf_subsystem_add_listener(self, **kwargs): 354 rpc.nvmf.nvmf_subsystem_add_listener(self.client, **kwargs) 355 356 @verbose 357 def nvmf_subsystem_remove_listener(self, **kwargs): 358 rpc.nvmf.nvmf_subsystem_remove_listener(self.client, **kwargs) 359 360 @verbose 361 def nvmf_subsystem_add_host(self, **kwargs): 362 rpc.nvmf.nvmf_subsystem_add_host(self.client, **kwargs) 363 364 @verbose 365 def nvmf_subsystem_remove_host(self, **kwargs): 366 rpc.nvmf.nvmf_subsystem_remove_host(self.client, **kwargs) 367 368 @verbose 369 def nvmf_subsystem_allow_any_host(self, **kwargs): 370 rpc.nvmf.nvmf_subsystem_allow_any_host(self.client, **kwargs) 371 372 @verbose 373 def nvmf_subsystem_add_ns(self, **kwargs): 374 rpc.nvmf.nvmf_subsystem_add_ns(self.client, **kwargs) 375 376 @verbose 377 def nvmf_subsystem_remove_ns(self, **kwargs): 378 rpc.nvmf.nvmf_subsystem_remove_ns(self.client, **kwargs) 379 380 @verbose 381 def nvmf_subsystem_allow_any_host(self, **kwargs): 382 rpc.nvmf.nvmf_subsystem_allow_any_host(self.client, **kwargs) 383 384 @verbose 385 @is_method_available 386 def scsi_get_devices(self): 387 if self.is_init: 388 for device in rpc.iscsi.scsi_get_devices(self.client): 389 yield ScsiObj(device) 390 391 @verbose 392 @is_method_available 393 def iscsi_get_target_nodes(self): 394 if self.is_init: 395 for tg in rpc.iscsi.iscsi_get_target_nodes(self.client): 396 yield tg 397 398 @verbose 399 def iscsi_create_target_node(self, **kwargs): 400 rpc.iscsi.iscsi_create_target_node(self.client, **kwargs) 401 402 @verbose 403 def iscsi_delete_target_node(self, **kwargs): 404 rpc.iscsi.iscsi_delete_target_node(self.client, **kwargs) 405 406 @verbose 407 @is_method_available 408 def iscsi_get_portal_groups(self): 409 if self.is_init: 410 for pg in rpc.iscsi.iscsi_get_portal_groups(self.client): 411 yield ScsiObj(pg) 412 413 @verbose 414 @is_method_available 415 def iscsi_get_initiator_groups(self): 416 if self.is_init: 417 for ig in rpc.iscsi.iscsi_get_initiator_groups(self.client): 418 yield ScsiObj(ig) 419 420 @verbose 421 def construct_portal_group(self, **kwargs): 422 rpc.iscsi.iscsi_create_portal_group(self.client, **kwargs) 423 424 @verbose 425 def iscsi_delete_portal_group(self, **kwargs): 426 rpc.iscsi.iscsi_delete_portal_group(self.client, **kwargs) 427 428 @verbose 429 def construct_initiator_group(self, **kwargs): 430 rpc.iscsi.iscsi_create_initiator_group(self.client, **kwargs) 431 432 @verbose 433 def iscsi_delete_initiator_group(self, **kwargs): 434 rpc.iscsi.iscsi_delete_initiator_group(self.client, **kwargs) 435 436 @verbose 437 @is_method_available 438 def iscsi_get_connections(self, **kwargs): 439 if self.is_init: 440 for ic in rpc.iscsi.iscsi_get_connections(self.client, **kwargs): 441 yield ic 442 443 @verbose 444 def iscsi_initiator_group_add_initiators(self, **kwargs): 445 rpc.iscsi.iscsi_initiator_group_add_initiators(self.client, **kwargs) 446 447 @verbose 448 def iscsi_initiator_group_remove_initiators(self, **kwargs): 449 rpc.iscsi.iscsi_initiator_group_remove_initiators(self.client, **kwargs) 450 451 @verbose 452 def iscsi_target_node_add_pg_ig_maps(self, **kwargs): 453 rpc.iscsi.iscsi_target_node_add_pg_ig_maps(self.client, **kwargs) 454 455 @verbose 456 def iscsi_target_node_remove_pg_ig_maps(self, **kwargs): 457 rpc.iscsi.iscsi_target_node_remove_pg_ig_maps(self.client, **kwargs) 458 459 @verbose 460 def iscsi_auth_group_add_secret(self, **kwargs): 461 rpc.iscsi.iscsi_auth_group_add_secret(self.client, **kwargs) 462 463 @verbose 464 def iscsi_auth_group_remove_secret(self, **kwargs): 465 rpc.iscsi.iscsi_auth_group_remove_secret(self.client, **kwargs) 466 467 @verbose 468 @is_method_available 469 def iscsi_get_auth_groups(self, **kwargs): 470 return rpc.iscsi.iscsi_get_auth_groups(self.client, **kwargs) 471 472 @verbose 473 def iscsi_create_auth_group(self, **kwargs): 474 rpc.iscsi.iscsi_create_auth_group(self.client, **kwargs) 475 476 @verbose 477 def iscsi_delete_auth_group(self, **kwargs): 478 rpc.iscsi.iscsi_delete_auth_group(self.client, **kwargs) 479 480 @verbose 481 def iscsi_target_node_set_auth(self, **kwargs): 482 rpc.iscsi.iscsi_target_node_set_auth(self.client, **kwargs) 483 484 @verbose 485 def iscsi_target_node_add_lun(self, **kwargs): 486 rpc.iscsi.iscsi_target_node_add_lun(self.client, **kwargs) 487 488 @verbose 489 def iscsi_set_discovery_auth(self, **kwargs): 490 rpc.iscsi.iscsi_set_discovery_auth(self.client, **kwargs) 491 492 @verbose 493 @is_method_available 494 def iscsi_get_options(self, **kwargs): 495 return rpc.iscsi.iscsi_get_options(self.client, **kwargs) 496 497 def has_subsystem(self, subsystem): 498 for system in rpc.subsystem.framework_get_subsystems(self.client): 499 if subsystem.lower() == system["subsystem"].lower(): 500 return True 501 return False 502 503 @verbose 504 def create_compress_bdev(self, **kwargs): 505 response = rpc.bdev.bdev_compress_create(self.client, **kwargs) 506 return response 507 508 @verbose 509 def bdev_compress_delete(self, **kwargs): 510 rpc.bdev.bdev_compress_delete(self.client, **kwargs) 511 512 513class Bdev(object): 514 def __init__(self, bdev_info): 515 """ 516 All class attributes are set based on what information is received 517 from bdev_get_bdevs RPC call. 518 # TODO: Document in docstring parameters which describe bdevs. 519 # TODO: Possible improvement: JSON schema might be used here in future 520 """ 521 for i in list(bdev_info.keys()): 522 setattr(self, i, bdev_info[i]) 523 524 525class LvolStore(object): 526 def __init__(self, lvs_info): 527 """ 528 All class attributes are set based on what information is received 529 from bdev_get_bdevs RPC call. 530 # TODO: Document in docstring parameters which describe bdevs. 531 # TODO: Possible improvement: JSON schema might be used here in future 532 """ 533 for i in list(lvs_info.keys()): 534 setattr(self, i, lvs_info[i]) 535 536 537class VhostCtrlr(object): 538 def __init__(self, ctrlr_info): 539 """ 540 All class attributes are set based on what information is received 541 from vhost_get_controllers RPC call. 542 # TODO: Document in docstring parameters which describe bdevs. 543 # TODO: Possible improvement: JSON schema might be used here in future 544 """ 545 for i in list(ctrlr_info.keys()): 546 setattr(self, i, ctrlr_info[i]) 547 548 549class NvmfTransport(object): 550 def __init__(self, transport_info): 551 """ 552 All class attributes are set based on what information is received 553 from get_nvmf_transport RPC call. 554 # TODO: Document in docstring parameters which describe bdevs. 555 # TODO: Possible improvement: JSON schema might be used here in future 556 """ 557 for i in transport_info.keys(): 558 setattr(self, i, transport_info[i]) 559 560 561class NvmfSubsystem(object): 562 def __init__(self, subsystem_info): 563 """ 564 All class attributes are set based on what information is received 565 from get_nvmf_subsystem RPC call. 566 # TODO: Document in docstring parameters which describe bdevs. 567 # TODO: Possible improvement: JSON schema might be used here in future 568 """ 569 for i in subsystem_info.keys(): 570 setattr(self, i, subsystem_info[i]) 571 572 573class NvmfReferral(object): 574 def __init__(self, referral_info): 575 """ 576 All class attributes are set based on what information is received 577 from get_nvmf_referrals RPC call. 578 """ 579 for i in referral_info.keys(): 580 setattr(self, i, referral_info[i]) 581 582 583class ScsiObj(object): 584 def __init__(self, device_info): 585 """ 586 All class attributes are set based on what information is received 587 from iscsi related RPC calls. 588 # TODO: Document in docstring parameters which describe bdevs. 589 # TODO: Possible improvement: JSON schema might be used here in future 590 """ 591 for i in device_info.keys(): 592 setattr(self, i, device_info[i]) 593