1# SPDX-License-Identifier: BSD-3-Clause 2# Copyright (C) 2018 Intel Corporation. 3# All rights reserved. 4 5from configshell_fb import ExecutionError 6from ..rpc.client import JSONRPCException 7from .ui_node import UINode 8 9 10class UIISCSI(UINode): 11 def __init__(self, parent): 12 UINode.__init__(self, "iscsi", parent) 13 self.refresh() 14 15 def refresh(self): 16 self._children = set([]) 17 UIISCSIDevices(self) 18 UIPortalGroups(self) 19 UIInitiatorGroups(self) 20 UIISCSIConnections(self) 21 UIISCSIAuthGroups(self) 22 UIISCSIGlobalParams(self) 23 24 25class UIISCSIGlobalParams(UINode): 26 def __init__(self, parent): 27 UINode.__init__(self, "global_params", parent) 28 self.refresh() 29 30 def refresh(self): 31 self._children = set([]) 32 iscsi_global_params = self.get_root().iscsi_get_options() 33 if not iscsi_global_params: 34 return 35 for param, val in iscsi_global_params.items(): 36 UIISCSIGlobalParam("%s: %s" % (param, val), self) 37 38 def ui_command_set_auth(self, g=None, d=None, r=None, m=None): 39 """Set CHAP authentication for discovery service. 40 41 Optional arguments: 42 g = chap_group: Authentication group ID for discovery session 43 d = disable_chap: CHAP for discovery session should be disabled 44 r = require_chap: CHAP for discovery session should be required 45 m = mutual_chap: CHAP for discovery session should be mutual 46 """ 47 chap_group = self.ui_eval_param(g, "number", None) 48 disable_chap = self.ui_eval_param(d, "bool", None) 49 require_chap = self.ui_eval_param(r, "bool", None) 50 mutual_chap = self.ui_eval_param(m, "bool", None) 51 self.get_root().iscsi_set_discovery_auth( 52 chap_group=chap_group, disable_chap=disable_chap, 53 require_chap=require_chap, mutual_chap=mutual_chap) 54 55 56class UIISCSIGlobalParam(UINode): 57 def __init__(self, param, parent): 58 UINode.__init__(self, param, parent) 59 60 61class UIISCSIDevices(UINode): 62 def __init__(self, parent): 63 UINode.__init__(self, "target_nodes", parent) 64 self.scsi_devices = list() 65 self.refresh() 66 67 def refresh(self): 68 self._children = set([]) 69 self.target_nodes = list(self.get_root().iscsi_get_target_nodes()) 70 self.scsi_devices = list(self.get_root().scsi_get_devices()) 71 for device in self.scsi_devices: 72 for node in self.target_nodes: 73 if hasattr(device, "device_name") and node['name'] \ 74 == device.device_name: 75 UIISCSIDevice(device, node, self) 76 77 def delete(self, name): 78 self.get_root().iscsi_delete_target_node(target_node_name=name) 79 80 def ui_command_create(self, name, alias_name, bdev_name_id_pairs, 81 pg_ig_mappings, queue_depth, g=None, d=None, r=None, 82 m=None, h=None, t=None): 83 """Create target node 84 85 Positional args: 86 name: Target node name (ASCII) 87 alias_name: Target node alias name (ASCII) 88 bdev_name_id_pairs: List of bdev_name_id_pairs 89 pg_ig_mappings: List of pg_ig_mappings 90 queue_depth: Desired target queue depth 91 Optional args: 92 g = chap_group: Authentication group ID for this target node 93 d = disable_chap: CHAP authentication should be disabled for this target node 94 r = require_chap: CHAP authentication should be required for this target node 95 m = mutual_chap: CHAP authentication should be mutual/bidirectional 96 h = header_digest: Header Digest should be required for this target node 97 t = data_digest: Data Digest should be required for this target node 98 """ 99 luns = [] 100 print("bdev_name_id_pairs: %s" % bdev_name_id_pairs) 101 print("pg_ig_mappings: %s" % pg_ig_mappings) 102 for u in bdev_name_id_pairs.strip().split(" "): 103 bdev_name, lun_id = u.split(":") 104 luns.append({"bdev_name": bdev_name, "lun_id": int(lun_id)}) 105 pg_ig_maps = [] 106 for u in pg_ig_mappings.strip().split(" "): 107 pg, ig = u.split(":") 108 pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)}) 109 queue_depth = self.ui_eval_param(queue_depth, "number", None) 110 chap_group = self.ui_eval_param(g, "number", None) 111 disable_chap = self.ui_eval_param(d, "bool", None) 112 require_chap = self.ui_eval_param(r, "bool", None) 113 mutual_chap = self.ui_eval_param(m, "bool", None) 114 header_digest = self.ui_eval_param(h, "bool", None) 115 data_digest = self.ui_eval_param(t, "bool", None) 116 self.get_root().iscsi_create_target_node( 117 name=name, alias_name=alias_name, luns=luns, 118 pg_ig_maps=pg_ig_maps, queue_depth=queue_depth, 119 chap_group=chap_group, disable_chap=disable_chap, 120 require_chap=require_chap, mutual_chap=mutual_chap, 121 header_digest=header_digest, data_digest=data_digest) 122 123 def ui_command_delete(self, name=None): 124 """Delete a target node. If name is not specified delete all target nodes. 125 126 Arguments: 127 name - Target node name. 128 """ 129 self.delete(name) 130 131 def ui_command_delete_all(self): 132 """Delete all target nodes""" 133 rpc_messages = "" 134 for device in self.scsi_devices: 135 try: 136 self.delete(device.device_name) 137 except JSONRPCException as e: 138 rpc_messages += e.message 139 if rpc_messages: 140 raise JSONRPCException(rpc_messages) 141 142 def ui_command_add_lun(self, name, bdev_name, lun_id=None): 143 """Add lun to the target node. 144 145 Required args: 146 name: Target node name (ASCII) 147 bdev_name: bdev name 148 Positional args: 149 lun_id: LUN ID (integer >= 0) 150 """ 151 if lun_id: 152 lun_id = self.ui_eval_param(lun_id, "number", None) 153 self.get_root().iscsi_target_node_add_lun( 154 name=name, bdev_name=bdev_name, lun_id=lun_id) 155 156 def summary(self): 157 count = 0 158 for device in self.scsi_devices: 159 for node in self.target_nodes: 160 if hasattr(device, "device_name") and node['name'] \ 161 == device.device_name: 162 count = count + 1 163 return "Target nodes: %d" % count, None 164 165 166class UIISCSIDevice(UINode): 167 def __init__(self, device, target, parent): 168 UINode.__init__(self, device.device_name, parent) 169 self.device = device 170 self.target = target 171 self.refresh() 172 173 def ui_command_set_auth(self, g=None, d=None, r=None, m=None): 174 """Set CHAP authentication for the target node. 175 176 Optionals args: 177 g = chap_group: Authentication group ID for this target node 178 d = disable_chap: CHAP authentication should be disabled for this target node 179 r = require_chap: CHAP authentication should be required for this target node 180 m = mutual_chap: CHAP authentication should be mutual/bidirectional 181 """ 182 chap_group = self.ui_eval_param(g, "number", None) 183 disable_chap = self.ui_eval_param(d, "bool", None) 184 require_chap = self.ui_eval_param(r, "bool", None) 185 mutual_chap = self.ui_eval_param(m, "bool", None) 186 self.get_root().iscsi_target_node_set_auth( 187 name=self.device.device_name, chap_group=chap_group, 188 disable_chap=disable_chap, 189 require_chap=require_chap, mutual_chap=mutual_chap) 190 191 def ui_command_iscsi_target_node_add_pg_ig_maps(self, pg_ig_mappings): 192 """Add PG-IG maps to the target node. 193 194 Args: 195 pg_ig_maps: List of pg_ig_mappings, e.g. pg_tag:ig_tag pg_tag2:ig_tag2 196 """ 197 pg_ig_maps = [] 198 for u in pg_ig_mappings.strip().split(" "): 199 pg, ig = u.split(":") 200 pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)}) 201 self.get_root().iscsi_target_node_add_pg_ig_maps( 202 pg_ig_maps=pg_ig_maps, name=self.device.device_name) 203 204 def ui_command_iscsi_target_node_remove_pg_ig_maps(self, pg_ig_mappings): 205 """Remove PG-IG maps from the target node. 206 207 Args: 208 pg_ig_maps: List of pg_ig_mappings, e.g. pg_tag:ig_tag pg_tag2:ig_tag2 209 """ 210 pg_ig_maps = [] 211 for u in pg_ig_mappings.strip().split(" "): 212 pg, ig = u.split(":") 213 pg_ig_maps.append({"pg_tag": int(pg), "ig_tag": int(ig)}) 214 self.get_root().iscsi_target_node_remove_pg_ig_maps( 215 pg_ig_maps=pg_ig_maps, name=self.device.device_name) 216 217 def refresh(self): 218 self._children = set([]) 219 UIISCSILuns(self.target['luns'], self) 220 UIISCSIPgIgMaps(self.target['pg_ig_maps'], self) 221 auths = {"disable_chap": self.target["disable_chap"], 222 "require_chap": self.target["require_chap"], 223 "mutual_chap": self.target["mutual_chap"], 224 "chap_group": self.target["chap_group"], 225 "data_digest": self.target["data_digest"]} 226 UIISCSIAuth(auths, self) 227 228 def summary(self): 229 return "Id: %s, QueueDepth: %s" % (self.device.id, 230 self.target['queue_depth']), None 231 232 233class UIISCSIAuth(UINode): 234 def __init__(self, auths, parent): 235 UINode.__init__(self, "auths", parent) 236 self.auths = auths 237 self.refresh() 238 239 def summary(self): 240 return "disable_chap: %s, require_chap: %s, mutual_chap: %s, chap_group: %s" % ( 241 self.auths['disable_chap'], self.auths['require_chap'], 242 self.auths['mutual_chap'], self.auths['chap_group']), None 243 244 245class UIISCSILuns(UINode): 246 def __init__(self, luns, parent): 247 UINode.__init__(self, "luns", parent) 248 self.luns = luns 249 self.refresh() 250 251 def refresh(self): 252 self._children = set([]) 253 for lun in self.luns: 254 UIISCSILun(lun, self) 255 256 def summary(self): 257 return "Luns: %d" % len(self.luns), None 258 259 260class UIISCSILun(UINode): 261 def __init__(self, lun, parent): 262 UINode.__init__(self, "lun %s" % lun['lun_id'], parent) 263 self.lun = lun 264 self.refresh() 265 266 def summary(self): 267 return "%s" % self.lun['bdev_name'], None 268 269 270class UIISCSIPgIgMaps(UINode): 271 def __init__(self, pg_ig_maps, parent): 272 UINode.__init__(self, "pg_ig_maps", parent) 273 self.pg_ig_maps = pg_ig_maps 274 self.refresh() 275 276 def refresh(self): 277 self._children = set([]) 278 for pg_ig in self.pg_ig_maps: 279 UIISCSIPgIg(pg_ig, self) 280 281 def summary(self): 282 return "Pg_ig_maps: %d" % len(self.pg_ig_maps), None 283 284 285class UIISCSIPgIg(UINode): 286 def __init__(self, pg_ig, parent): 287 UINode.__init__(self, "portal_group%s - initiator_group%s" % 288 (pg_ig['pg_tag'], pg_ig['ig_tag']), parent) 289 self.pg_ig = pg_ig 290 self.refresh() 291 292 293class UIPortalGroups(UINode): 294 def __init__(self, parent): 295 UINode.__init__(self, "portal_groups", parent) 296 self.refresh() 297 298 def delete(self, tag): 299 self.get_root().iscsi_delete_portal_group(tag=tag) 300 301 def ui_command_create(self, tag, portal_list): 302 """Add a portal group. 303 304 Args: 305 portals: List of portals e.g. ip:port ip2:port2 306 tag: Portal group tag (unique, integer > 0) 307 """ 308 portals = [] 309 for portal in portal_list.strip().split(" "): 310 host = portal 311 cpumask = None 312 if "@" in portal: 313 host, cpumask = portal.split("@") 314 if ":" not in host: 315 raise ExecutionError("Incorrect format of portal group. Port is missing." 316 "Use 'help create' to see the command syntax.") 317 host, port = host.rsplit(":", -1) 318 portals.append({'host': host, 'port': port}) 319 if cpumask: 320 print("WARNING: Specifying a CPU mask for portal groups is no longer supported. Ignoring.") 321 tag = self.ui_eval_param(tag, "number", None) 322 self.get_root().construct_portal_group(tag=tag, portals=portals, private=None, wait=None) 323 324 def ui_command_delete(self, tag): 325 """Delete a portal group with given tag (unique, integer > 0))""" 326 tag = self.ui_eval_param(tag, "number", None) 327 self.delete(tag) 328 329 def ui_command_delete_all(self): 330 """Delete all portal groups""" 331 rpc_messages = "" 332 for pg in self.pgs: 333 try: 334 self.delete(pg.tag) 335 except JSONRPCException as e: 336 rpc_messages += e.message 337 if rpc_messages: 338 raise JSONRPCException(rpc_messages) 339 340 def refresh(self): 341 self._children = set([]) 342 self.pgs = list(self.get_root().iscsi_get_portal_groups()) 343 for pg in self.pgs: 344 try: 345 UIPortalGroup(pg, self) 346 except JSONRPCException as e: 347 self.shell.log.error(e.message) 348 349 def summary(self): 350 return "Portal groups: %d" % len(self.pgs), None 351 352 353class UIPortalGroup(UINode): 354 def __init__(self, pg, parent): 355 UINode.__init__(self, "portal_group%s" % pg.tag, parent) 356 self.pg = pg 357 self.refresh() 358 359 def refresh(self): 360 self._children = set([]) 361 for portal in self.pg.portals: 362 UIPortal(portal['host'], portal['port'], self) 363 364 def summary(self): 365 return "Portals: %d" % len(self.pg.portals), None 366 367 368class UIPortal(UINode): 369 def __init__(self, host, port, parent): 370 UINode.__init__(self, "host=%s, port=%s" % ( 371 host, port), parent) 372 self.refresh() 373 374 375class UIInitiatorGroups(UINode): 376 def __init__(self, parent): 377 UINode.__init__(self, "initiator_groups", parent) 378 self.refresh() 379 380 def delete(self, tag): 381 self.get_root().iscsi_delete_initiator_group(tag=tag) 382 383 def ui_command_create(self, tag, initiator_list, netmask_list): 384 """Add an initiator group. 385 386 Args: 387 tag: Initiator group tag (unique, integer > 0) 388 initiators: List of initiator hostnames or IP addresses 389 separated with whitespaces, e.g. 127.0.0.1 192.168.200.100 390 netmasks: List of initiator netmasks separated with whitespaces, 391 e.g. 255.255.0.0 255.248.0.0 392 """ 393 tag = self.ui_eval_param(tag, "number", None) 394 self.get_root().construct_initiator_group( 395 tag=tag, initiators=initiator_list.split(" "), 396 netmasks=netmask_list.split(" ")) 397 398 def ui_command_delete(self, tag): 399 """Delete an initiator group. 400 401 Args: 402 tag: Initiator group tag (unique, integer > 0) 403 """ 404 tag = self.ui_eval_param(tag, "number", None) 405 self.delete(tag) 406 407 def ui_command_delete_all(self): 408 """Delete all initiator groups""" 409 rpc_messages = "" 410 for ig in self.igs: 411 try: 412 self.delete(ig.tag) 413 except JSONRPCException as e: 414 rpc_messages += e.message 415 if rpc_messages: 416 raise JSONRPCException(rpc_messages) 417 418 def ui_command_add_initiator(self, tag, initiators, netmasks): 419 """Add initiators to an existing initiator group. 420 421 Args: 422 tag: Initiator group tag (unique, integer > 0) 423 initiators: List of initiator hostnames or IP addresses, 424 e.g. 127.0.0.1 192.168.200.100 425 netmasks: List of initiator netmasks, 426 e.g. 255.255.0.0 255.248.0.0 427 """ 428 tag = self.ui_eval_param(tag, "number", None) 429 self.get_root().iscsi_initiator_group_add_initiators( 430 tag=tag, initiators=initiators.split(" "), 431 netmasks=netmasks.split(" ")) 432 433 def ui_command_delete_initiator(self, tag, initiators=None, netmasks=None): 434 """Delete initiators from an existing initiator group. 435 436 Args: 437 tag: Initiator group tag (unique, integer > 0) 438 initiators: List of initiator hostnames or IP addresses, e.g. 127.0.0.1 192.168.200.100 439 netmasks: List of initiator netmasks, e.g. 255.255.0.0 255.248.0.0 440 """ 441 tag = self.ui_eval_param(tag, "number", None) 442 if initiators: 443 initiators = initiators.split(" ") 444 if netmasks: 445 netmasks = netmasks.split(" ") 446 self.get_root().iscsi_initiator_group_remove_initiators( 447 tag=tag, initiators=initiators, 448 netmasks=netmasks) 449 450 def refresh(self): 451 self._children = set([]) 452 self.igs = list(self.get_root().iscsi_get_initiator_groups()) 453 for ig in self.igs: 454 UIInitiatorGroup(ig, self) 455 456 def summary(self): 457 return "Initiator groups: %d" % len(self.igs), None 458 459 460class UIInitiatorGroup(UINode): 461 def __init__(self, ig, parent): 462 UINode.__init__(self, "initiator_group%s" % ig.tag, parent) 463 self.ig = ig 464 self.refresh() 465 466 def refresh(self): 467 self._children = set([]) 468 for initiator, netmask in zip(self.ig.initiators, self.ig.netmasks): 469 UIInitiator(initiator, netmask, self) 470 471 def summary(self): 472 return "Initiators: %d" % len(self.ig.initiators), None 473 474 475class UIInitiator(UINode): 476 def __init__(self, initiator, netmask, parent): 477 UINode.__init__(self, "hostname=%s, netmask=%s" % (initiator, netmask), parent) 478 self.refresh() 479 480 481class UIISCSIConnections(UINode): 482 def __init__(self, parent): 483 UINode.__init__(self, "iscsi_connections", parent) 484 self.refresh() 485 486 def refresh(self): 487 self._children = set([]) 488 self.iscsicons = list(self.get_root().iscsi_get_connections()) 489 for ic in self.iscsicons: 490 UIISCSIConnection(ic, self) 491 492 def summary(self): 493 return "Connections: %d" % len(self.iscsicons), None 494 495 496class UIISCSIConnection(UINode): 497 def __init__(self, ic, parent): 498 UINode.__init__(self, "%s" % ic['id'], parent) 499 self.ic = ic 500 self.refresh() 501 502 def refresh(self): 503 self._children = set([]) 504 for key, val in self.ic.items(): 505 if key == "id": 506 continue 507 UIISCSIConnectionDetails("%s: %s" % (key, val), self) 508 509 510class UIISCSIConnectionDetails(UINode): 511 def __init__(self, info, parent): 512 UINode.__init__(self, "%s" % info, parent) 513 self.refresh() 514 515 516class UIISCSIAuthGroups(UINode): 517 def __init__(self, parent): 518 UINode.__init__(self, "auth_groups", parent) 519 self.refresh() 520 521 def refresh(self): 522 self._children = set([]) 523 self.iscsi_auth_groups = list(self.get_root().iscsi_get_auth_groups()) 524 if self.iscsi_auth_groups is None: 525 self.iscsi_auth_groups = [] 526 for ag in self.iscsi_auth_groups: 527 UIISCSIAuthGroup(ag, self) 528 529 def delete(self, tag): 530 self.get_root().iscsi_delete_auth_group(tag=tag) 531 532 def delete_secret(self, tag, user): 533 self.get_root().iscsi_auth_group_remove_secret( 534 tag=tag, user=user) 535 536 def ui_command_create(self, tag, secrets=None): 537 """Add authentication group for CHAP authentication. 538 539 Args: 540 tag: Authentication group tag (unique, integer > 0). 541 Optional args: 542 secrets: Array of secrets objects separated by comma sign, 543 e.g. user:test secret:test muser:mutual_test msecret:mutual_test 544 """ 545 tag = self.ui_eval_param(tag, "number", None) 546 if secrets: 547 secrets = [dict(u.split(":") for u in a.split(" ")) 548 for a in secrets.split(",")] 549 self.get_root().iscsi_create_auth_group(tag=tag, secrets=secrets) 550 551 def ui_command_delete(self, tag): 552 """Delete an authentication group. 553 554 Args: 555 tag: Authentication group tag (unique, integer > 0) 556 """ 557 tag = self.ui_eval_param(tag, "number", None) 558 self.delete(tag) 559 560 def ui_command_delete_all(self): 561 """Delete all authentication groups.""" 562 rpc_messages = "" 563 for iscsi_auth_group in self.iscsi_auth_groups: 564 try: 565 self.delete(iscsi_auth_group['tag']) 566 except JSONRPCException as e: 567 rpc_messages += e.message 568 if rpc_messages: 569 raise JSONRPCException(rpc_messages) 570 571 def ui_command_add_secret(self, tag, user, secret, 572 muser=None, msecret=None): 573 """Add a secret to an authentication group. 574 575 Args: 576 tag: Authentication group tag (unique, integer > 0) 577 user: User name for one-way CHAP authentication 578 secret: Secret for one-way CHAP authentication 579 Optional args: 580 muser: User name for mutual CHAP authentication 581 msecret: Secret for mutual CHAP authentication 582 """ 583 tag = self.ui_eval_param(tag, "number", None) 584 self.get_root().iscsi_auth_group_add_secret( 585 tag=tag, user=user, secret=secret, 586 muser=muser, msecret=msecret) 587 588 def ui_command_delete_secret(self, tag, user): 589 """Delete a secret from an authentication group. 590 591 Args: 592 tag: Authentication group tag (unique, integer > 0) 593 user: User name for one-way CHAP authentication 594 """ 595 tag = self.ui_eval_param(tag, "number", None) 596 self.delete_secret(tag, user) 597 598 def ui_command_delete_secret_all(self, tag): 599 """Delete all secrets from an authentication group. 600 601 Args: 602 tag: Authentication group tag (unique, integer > 0) 603 """ 604 rpc_messages = "" 605 tag = self.ui_eval_param(tag, "number", None) 606 for ag in self.iscsi_auth_groups: 607 if ag['tag'] == tag: 608 for secret in ag['secrets']: 609 try: 610 self.delete_secret(tag, secret['user']) 611 except JSONRPCException as e: 612 rpc_messages += e.message 613 if rpc_messages: 614 raise JSONRPCException(rpc_messages) 615 616 def summary(self): 617 return "Groups: %s" % len(self.iscsi_auth_groups), None 618 619 620class UIISCSIAuthGroup(UINode): 621 def __init__(self, ag, parent): 622 UINode.__init__(self, "group" + str(ag['tag']), parent) 623 self.ag = ag 624 self.refresh() 625 626 def refresh(self): 627 self._children = set([]) 628 for secret in self.ag['secrets']: 629 UISCSIAuthSecret(secret, self) 630 631 def summary(self): 632 return "Secrets: %s" % len(self.ag['secrets']), None 633 634 635class UISCSIAuthSecret(UINode): 636 def __init__(self, secret, parent): 637 info_list = ["%s=%s" % (key, val) 638 for key, val in secret.items()] 639 info_list.sort(reverse=True) 640 info = ", ".join(info_list) 641 UINode.__init__(self, info, parent) 642 self.secret = secret 643 self.refresh() 644