1# Copyright (c) 2017-2018 Dell EMC Inc. 2# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) 3 4from __future__ import absolute_import, division, print_function 5__metaclass__ = type 6 7import json 8from ansible.module_utils.urls import open_url 9from ansible.module_utils._text import to_native 10from ansible.module_utils._text import to_text 11from ansible.module_utils.six.moves import http_client 12from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError 13 14GET_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'} 15POST_HEADERS = {'content-type': 'application/json', 'accept': 'application/json', 16 'OData-Version': '4.0'} 17PATCH_HEADERS = {'content-type': 'application/json', 'accept': 'application/json', 18 'OData-Version': '4.0'} 19DELETE_HEADERS = {'accept': 'application/json', 'OData-Version': '4.0'} 20 21 22class RedfishUtils(object): 23 24 def __init__(self, creds, root_uri, timeout, module): 25 self.root_uri = root_uri 26 self.creds = creds 27 self.timeout = timeout 28 self.module = module 29 self.service_root = '/redfish/v1/' 30 self._init_session() 31 32 # The following functions are to send GET/POST/PATCH/DELETE requests 33 def get_request(self, uri): 34 try: 35 resp = open_url(uri, method="GET", headers=GET_HEADERS, 36 url_username=self.creds['user'], 37 url_password=self.creds['pswd'], 38 force_basic_auth=True, validate_certs=False, 39 follow_redirects='all', 40 use_proxy=False, timeout=self.timeout) 41 data = json.loads(to_native(resp.read())) 42 headers = dict((k.lower(), v) for (k, v) in resp.info().items()) 43 except HTTPError as e: 44 msg = self._get_extended_message(e) 45 return {'ret': False, 46 'msg': "HTTP Error %s on GET request to '%s', extended message: '%s'" 47 % (e.code, uri, msg)} 48 except URLError as e: 49 return {'ret': False, 'msg': "URL Error on GET request to '%s': '%s'" 50 % (uri, e.reason)} 51 # Almost all errors should be caught above, but just in case 52 except Exception as e: 53 return {'ret': False, 54 'msg': "Failed GET request to '%s': '%s'" % (uri, to_text(e))} 55 return {'ret': True, 'data': data, 'headers': headers} 56 57 def post_request(self, uri, pyld): 58 try: 59 resp = open_url(uri, data=json.dumps(pyld), 60 headers=POST_HEADERS, method="POST", 61 url_username=self.creds['user'], 62 url_password=self.creds['pswd'], 63 force_basic_auth=True, validate_certs=False, 64 follow_redirects='all', 65 use_proxy=False, timeout=self.timeout) 66 except HTTPError as e: 67 msg = self._get_extended_message(e) 68 return {'ret': False, 69 'msg': "HTTP Error %s on POST request to '%s', extended message: '%s'" 70 % (e.code, uri, msg)} 71 except URLError as e: 72 return {'ret': False, 'msg': "URL Error on POST request to '%s': '%s'" 73 % (uri, e.reason)} 74 # Almost all errors should be caught above, but just in case 75 except Exception as e: 76 return {'ret': False, 77 'msg': "Failed POST request to '%s': '%s'" % (uri, to_text(e))} 78 return {'ret': True, 'resp': resp} 79 80 def patch_request(self, uri, pyld): 81 headers = PATCH_HEADERS 82 r = self.get_request(uri) 83 if r['ret']: 84 # Get etag from etag header or @odata.etag property 85 etag = r['headers'].get('etag') 86 if not etag: 87 etag = r['data'].get('@odata.etag') 88 if etag: 89 # Make copy of headers and add If-Match header 90 headers = dict(headers) 91 headers['If-Match'] = etag 92 try: 93 resp = open_url(uri, data=json.dumps(pyld), 94 headers=headers, method="PATCH", 95 url_username=self.creds['user'], 96 url_password=self.creds['pswd'], 97 force_basic_auth=True, validate_certs=False, 98 follow_redirects='all', 99 use_proxy=False, timeout=self.timeout) 100 except HTTPError as e: 101 msg = self._get_extended_message(e) 102 return {'ret': False, 103 'msg': "HTTP Error %s on PATCH request to '%s', extended message: '%s'" 104 % (e.code, uri, msg)} 105 except URLError as e: 106 return {'ret': False, 'msg': "URL Error on PATCH request to '%s': '%s'" 107 % (uri, e.reason)} 108 # Almost all errors should be caught above, but just in case 109 except Exception as e: 110 return {'ret': False, 111 'msg': "Failed PATCH request to '%s': '%s'" % (uri, to_text(e))} 112 return {'ret': True, 'resp': resp} 113 114 def delete_request(self, uri, pyld): 115 try: 116 resp = open_url(uri, data=json.dumps(pyld), 117 headers=DELETE_HEADERS, method="DELETE", 118 url_username=self.creds['user'], 119 url_password=self.creds['pswd'], 120 force_basic_auth=True, validate_certs=False, 121 follow_redirects='all', 122 use_proxy=False, timeout=self.timeout) 123 except HTTPError as e: 124 msg = self._get_extended_message(e) 125 return {'ret': False, 126 'msg': "HTTP Error %s on DELETE request to '%s', extended message: '%s'" 127 % (e.code, uri, msg)} 128 except URLError as e: 129 return {'ret': False, 'msg': "URL Error on DELETE request to '%s': '%s'" 130 % (uri, e.reason)} 131 # Almost all errors should be caught above, but just in case 132 except Exception as e: 133 return {'ret': False, 134 'msg': "Failed DELETE request to '%s': '%s'" % (uri, to_text(e))} 135 return {'ret': True, 'resp': resp} 136 137 @staticmethod 138 def _get_extended_message(error): 139 """ 140 Get Redfish ExtendedInfo message from response payload if present 141 :param error: an HTTPError exception 142 :type error: HTTPError 143 :return: the ExtendedInfo message if present, else standard HTTP error 144 """ 145 msg = http_client.responses.get(error.code, '') 146 if error.code >= 400: 147 try: 148 body = error.read().decode('utf-8') 149 data = json.loads(body) 150 ext_info = data['error']['@Message.ExtendedInfo'] 151 msg = ext_info[0]['Message'] 152 except Exception: 153 pass 154 return msg 155 156 def _init_session(self): 157 pass 158 159 def _find_accountservice_resource(self): 160 response = self.get_request(self.root_uri + self.service_root) 161 if response['ret'] is False: 162 return response 163 data = response['data'] 164 if 'AccountService' not in data: 165 return {'ret': False, 'msg': "AccountService resource not found"} 166 else: 167 account_service = data["AccountService"]["@odata.id"] 168 response = self.get_request(self.root_uri + account_service) 169 if response['ret'] is False: 170 return response 171 data = response['data'] 172 accounts = data['Accounts']['@odata.id'] 173 if accounts[-1:] == '/': 174 accounts = accounts[:-1] 175 self.accounts_uri = accounts 176 return {'ret': True} 177 178 def _find_sessionservice_resource(self): 179 response = self.get_request(self.root_uri + self.service_root) 180 if response['ret'] is False: 181 return response 182 data = response['data'] 183 if 'SessionService' not in data: 184 return {'ret': False, 'msg': "SessionService resource not found"} 185 else: 186 session_service = data["SessionService"]["@odata.id"] 187 response = self.get_request(self.root_uri + session_service) 188 if response['ret'] is False: 189 return response 190 data = response['data'] 191 sessions = data['Sessions']['@odata.id'] 192 if sessions[-1:] == '/': 193 sessions = sessions[:-1] 194 self.sessions_uri = sessions 195 return {'ret': True} 196 197 def _find_systems_resource(self): 198 response = self.get_request(self.root_uri + self.service_root) 199 if response['ret'] is False: 200 return response 201 data = response['data'] 202 if 'Systems' not in data: 203 return {'ret': False, 'msg': "Systems resource not found"} 204 response = self.get_request(self.root_uri + data['Systems']['@odata.id']) 205 if response['ret'] is False: 206 return response 207 self.systems_uris = [ 208 i['@odata.id'] for i in response['data'].get('Members', [])] 209 if not self.systems_uris: 210 return { 211 'ret': False, 212 'msg': "ComputerSystem's Members array is either empty or missing"} 213 return {'ret': True} 214 215 def _find_updateservice_resource(self): 216 response = self.get_request(self.root_uri + self.service_root) 217 if response['ret'] is False: 218 return response 219 data = response['data'] 220 if 'UpdateService' not in data: 221 return {'ret': False, 'msg': "UpdateService resource not found"} 222 else: 223 update = data["UpdateService"]["@odata.id"] 224 self.update_uri = update 225 response = self.get_request(self.root_uri + update) 226 if response['ret'] is False: 227 return response 228 data = response['data'] 229 firmware_inventory = data['FirmwareInventory'][u'@odata.id'] 230 self.firmware_uri = firmware_inventory 231 return {'ret': True} 232 233 def _find_chassis_resource(self): 234 chassis_service = [] 235 response = self.get_request(self.root_uri + self.service_root) 236 if response['ret'] is False: 237 return response 238 data = response['data'] 239 if 'Chassis' not in data: 240 return {'ret': False, 'msg': "Chassis resource not found"} 241 else: 242 chassis = data["Chassis"]["@odata.id"] 243 response = self.get_request(self.root_uri + chassis) 244 if response['ret'] is False: 245 return response 246 data = response['data'] 247 for member in data[u'Members']: 248 chassis_service.append(member[u'@odata.id']) 249 self.chassis_uri_list = chassis_service 250 return {'ret': True} 251 252 def _find_managers_resource(self): 253 response = self.get_request(self.root_uri + self.service_root) 254 if response['ret'] is False: 255 return response 256 data = response['data'] 257 if 'Managers' not in data: 258 return {'ret': False, 'msg': "Manager resource not found"} 259 else: 260 manager = data["Managers"]["@odata.id"] 261 response = self.get_request(self.root_uri + manager) 262 if response['ret'] is False: 263 return response 264 data = response['data'] 265 for member in data[u'Members']: 266 manager_service = member[u'@odata.id'] 267 self.manager_uri = manager_service 268 return {'ret': True} 269 270 def get_logs(self): 271 log_svcs_uri_list = [] 272 list_of_logs = [] 273 274 # Find LogService 275 response = self.get_request(self.root_uri + self.manager_uri) 276 if response['ret'] is False: 277 return response 278 data = response['data'] 279 if 'LogServices' not in data: 280 return {'ret': False, 'msg': "LogServices resource not found"} 281 282 # Find all entries in LogServices 283 logs_uri = data["LogServices"]["@odata.id"] 284 response = self.get_request(self.root_uri + logs_uri) 285 if response['ret'] is False: 286 return response 287 data = response['data'] 288 for log_svcs_entry in data[u'Members']: 289 response = self.get_request(self.root_uri + log_svcs_entry[u'@odata.id']) 290 if response['ret'] is False: 291 return response 292 _data = response['data'] 293 log_svcs_uri_list.append(_data['Entries'][u'@odata.id']) 294 295 # For each entry in LogServices, get log name and all log entries 296 for log_svcs_uri in log_svcs_uri_list: 297 logs = {} 298 list_of_log_entries = [] 299 response = self.get_request(self.root_uri + log_svcs_uri) 300 if response['ret'] is False: 301 return response 302 data = response['data'] 303 logs['Description'] = data['Description'] 304 # Get all log entries for each type of log found 305 for logEntry in data[u'Members']: 306 # I only extract some fields - Are these entry names standard? 307 list_of_log_entries.append(dict( 308 Name=logEntry[u'Name'], 309 Created=logEntry[u'Created'], 310 Message=logEntry[u'Message'], 311 Severity=logEntry[u'Severity'])) 312 log_name = log_svcs_uri.split('/')[-1] 313 logs[log_name] = list_of_log_entries 314 list_of_logs.append(logs) 315 316 # list_of_logs[logs{list_of_log_entries[entry{}]}] 317 return {'ret': True, 'entries': list_of_logs} 318 319 def clear_logs(self): 320 # Find LogService 321 response = self.get_request(self.root_uri + self.manager_uri) 322 if response['ret'] is False: 323 return response 324 data = response['data'] 325 if 'LogServices' not in data: 326 return {'ret': False, 'msg': "LogServices resource not found"} 327 328 # Find all entries in LogServices 329 logs_uri = data["LogServices"]["@odata.id"] 330 response = self.get_request(self.root_uri + logs_uri) 331 if response['ret'] is False: 332 return response 333 data = response['data'] 334 335 for log_svcs_entry in data[u'Members']: 336 response = self.get_request(self.root_uri + log_svcs_entry["@odata.id"]) 337 if response['ret'] is False: 338 return response 339 _data = response['data'] 340 # Check to make sure option is available, otherwise error is ugly 341 if "Actions" in _data: 342 if "#LogService.ClearLog" in _data[u"Actions"]: 343 self.post_request(self.root_uri + _data[u"Actions"]["#LogService.ClearLog"]["target"], {}) 344 if response['ret'] is False: 345 return response 346 return {'ret': True} 347 348 def aggregate(self, func): 349 ret = True 350 entries = [] 351 for systems_uri in self.systems_uris: 352 inventory = func(systems_uri) 353 ret = inventory.pop('ret') and ret 354 if 'entries' in inventory: 355 entries.append(({'systems_uri': systems_uri}, 356 inventory['entries'])) 357 return dict(ret=ret, entries=entries) 358 359 def get_storage_controller_inventory(self, systems_uri): 360 result = {} 361 controller_list = [] 362 controller_results = [] 363 # Get these entries, but does not fail if not found 364 properties = ['CacheSummary', 'FirmwareVersion', 'Identifiers', 365 'Location', 'Manufacturer', 'Model', 'Name', 366 'PartNumber', 'SerialNumber', 'SpeedGbps', 'Status'] 367 key = "StorageControllers" 368 369 # Find Storage service 370 response = self.get_request(self.root_uri + systems_uri) 371 if response['ret'] is False: 372 return response 373 data = response['data'] 374 375 if 'Storage' not in data: 376 return {'ret': False, 'msg': "Storage resource not found"} 377 378 # Get a list of all storage controllers and build respective URIs 379 storage_uri = data['Storage']["@odata.id"] 380 response = self.get_request(self.root_uri + storage_uri) 381 if response['ret'] is False: 382 return response 383 result['ret'] = True 384 data = response['data'] 385 386 # Loop through Members and their StorageControllers 387 # and gather properties from each StorageController 388 if data[u'Members']: 389 for storage_member in data[u'Members']: 390 storage_member_uri = storage_member[u'@odata.id'] 391 response = self.get_request(self.root_uri + storage_member_uri) 392 data = response['data'] 393 394 if key in data: 395 controller_list = data[key] 396 for controller in controller_list: 397 controller_result = {} 398 for property in properties: 399 if property in controller: 400 controller_result[property] = controller[property] 401 controller_results.append(controller_result) 402 result['entries'] = controller_results 403 return result 404 else: 405 return {'ret': False, 'msg': "Storage resource not found"} 406 407 def get_multi_storage_controller_inventory(self): 408 return self.aggregate(self.get_storage_controller_inventory) 409 410 def get_disk_inventory(self, systems_uri): 411 result = {'entries': []} 412 controller_list = [] 413 disk_results = [] 414 # Get these entries, but does not fail if not found 415 properties = ['BlockSizeBytes', 'CapableSpeedGbs', 'CapacityBytes', 416 'EncryptionAbility', 'EncryptionStatus', 417 'FailurePredicted', 'HotspareType', 'Id', 'Identifiers', 418 'Manufacturer', 'MediaType', 'Model', 'Name', 419 'PartNumber', 'PhysicalLocation', 'Protocol', 'Revision', 420 'RotationSpeedRPM', 'SerialNumber', 'Status'] 421 422 # Find Storage service 423 response = self.get_request(self.root_uri + systems_uri) 424 if response['ret'] is False: 425 return response 426 data = response['data'] 427 428 if 'SimpleStorage' not in data and 'Storage' not in data: 429 return {'ret': False, 'msg': "SimpleStorage and Storage resource \ 430 not found"} 431 432 if 'Storage' in data: 433 # Get a list of all storage controllers and build respective URIs 434 storage_uri = data[u'Storage'][u'@odata.id'] 435 response = self.get_request(self.root_uri + storage_uri) 436 if response['ret'] is False: 437 return response 438 result['ret'] = True 439 data = response['data'] 440 441 if data[u'Members']: 442 for controller in data[u'Members']: 443 controller_list.append(controller[u'@odata.id']) 444 for c in controller_list: 445 uri = self.root_uri + c 446 response = self.get_request(uri) 447 if response['ret'] is False: 448 return response 449 data = response['data'] 450 if 'Drives' in data: 451 for device in data[u'Drives']: 452 disk_uri = self.root_uri + device[u'@odata.id'] 453 response = self.get_request(disk_uri) 454 data = response['data'] 455 456 disk_result = {} 457 for property in properties: 458 if property in data: 459 if data[property] is not None: 460 disk_result[property] = data[property] 461 disk_results.append(disk_result) 462 result["entries"].append(disk_results) 463 464 if 'SimpleStorage' in data: 465 # Get a list of all storage controllers and build respective URIs 466 storage_uri = data["SimpleStorage"]["@odata.id"] 467 response = self.get_request(self.root_uri + storage_uri) 468 if response['ret'] is False: 469 return response 470 result['ret'] = True 471 data = response['data'] 472 473 for controller in data[u'Members']: 474 controller_list.append(controller[u'@odata.id']) 475 476 for c in controller_list: 477 uri = self.root_uri + c 478 response = self.get_request(uri) 479 if response['ret'] is False: 480 return response 481 data = response['data'] 482 483 for device in data[u'Devices']: 484 disk_result = {} 485 for property in properties: 486 if property in device: 487 disk_result[property] = device[property] 488 disk_results.append(disk_result) 489 result["entries"].append(disk_results) 490 491 return result 492 493 def get_multi_disk_inventory(self): 494 return self.aggregate(self.get_disk_inventory) 495 496 def get_volume_inventory(self, systems_uri): 497 result = {'entries': []} 498 controller_list = [] 499 volume_list = [] 500 volume_results = [] 501 # Get these entries, but does not fail if not found 502 properties = ['Id', 'Name', 'RAIDType', 'VolumeType', 'BlockSizeBytes', 503 'Capacity', 'CapacityBytes', 'CapacitySources', 504 'Encrypted', 'EncryptionTypes', 'Identifiers', 505 'Operations', 'OptimumIOSizeBytes', 'AccessCapabilities', 506 'AllocatedPools', 'Status'] 507 508 # Find Storage service 509 response = self.get_request(self.root_uri + systems_uri) 510 if response['ret'] is False: 511 return response 512 data = response['data'] 513 514 if 'SimpleStorage' not in data and 'Storage' not in data: 515 return {'ret': False, 'msg': "SimpleStorage and Storage resource \ 516 not found"} 517 518 if 'Storage' in data: 519 # Get a list of all storage controllers and build respective URIs 520 storage_uri = data[u'Storage'][u'@odata.id'] 521 response = self.get_request(self.root_uri + storage_uri) 522 if response['ret'] is False: 523 return response 524 result['ret'] = True 525 data = response['data'] 526 527 if data.get('Members'): 528 for controller in data[u'Members']: 529 controller_list.append(controller[u'@odata.id']) 530 for c in controller_list: 531 uri = self.root_uri + c 532 response = self.get_request(uri) 533 if response['ret'] is False: 534 return response 535 data = response['data'] 536 537 if 'Volumes' in data: 538 # Get a list of all volumes and build respective URIs 539 volumes_uri = data[u'Volumes'][u'@odata.id'] 540 response = self.get_request(self.root_uri + volumes_uri) 541 data = response['data'] 542 543 if data.get('Members'): 544 for volume in data[u'Members']: 545 volume_list.append(volume[u'@odata.id']) 546 for v in volume_list: 547 uri = self.root_uri + v 548 response = self.get_request(uri) 549 if response['ret'] is False: 550 return response 551 data = response['data'] 552 553 volume_result = {} 554 for property in properties: 555 if property in data: 556 if data[property] is not None: 557 volume_result[property] = data[property] 558 559 # Get related Drives Id 560 drive_id_list = [] 561 if 'Links' in data: 562 if 'Drives' in data[u'Links']: 563 for link in data[u'Links'][u'Drives']: 564 drive_id_link = link[u'@odata.id'] 565 drive_id = drive_id_link.split("/")[-1] 566 drive_id_list.append({'Id': drive_id}) 567 volume_result['Linked_drives'] = drive_id_list 568 569 volume_results.append(volume_result) 570 result["entries"].append(volume_results) 571 else: 572 return {'ret': False, 'msg': "Storage resource not found"} 573 574 return result 575 576 def get_multi_volume_inventory(self): 577 return self.aggregate(self.get_volume_inventory) 578 579 def restart_manager_gracefully(self): 580 result = {} 581 key = "Actions" 582 583 # Search for 'key' entry and extract URI from it 584 response = self.get_request(self.root_uri + self.manager_uri) 585 if response['ret'] is False: 586 return response 587 result['ret'] = True 588 data = response['data'] 589 action_uri = data[key]["#Manager.Reset"]["target"] 590 591 payload = {'ResetType': 'GracefulRestart'} 592 response = self.post_request(self.root_uri + action_uri, payload) 593 if response['ret'] is False: 594 return response 595 return {'ret': True} 596 597 def manage_indicator_led(self, command): 598 result = {} 599 key = 'IndicatorLED' 600 601 payloads = {'IndicatorLedOn': 'Lit', 'IndicatorLedOff': 'Off', "IndicatorLedBlink": 'Blinking'} 602 603 result = {} 604 for chassis_uri in self.chassis_uri_list: 605 response = self.get_request(self.root_uri + chassis_uri) 606 if response['ret'] is False: 607 return response 608 result['ret'] = True 609 data = response['data'] 610 if key not in data: 611 return {'ret': False, 'msg': "Key %s not found" % key} 612 613 if command in payloads.keys(): 614 payload = {'IndicatorLED': payloads[command]} 615 response = self.patch_request(self.root_uri + chassis_uri, payload) 616 if response['ret'] is False: 617 return response 618 else: 619 return {'ret': False, 'msg': 'Invalid command'} 620 621 return result 622 623 def manage_system_power(self, command): 624 key = "Actions" 625 626 # Search for 'key' entry and extract URI from it 627 response = self.get_request(self.root_uri + self.systems_uris[0]) 628 if response['ret'] is False: 629 return response 630 data = response['data'] 631 power_state = data["PowerState"] 632 633 if power_state == "On" and command == 'PowerOn': 634 return {'ret': True, 'changed': False} 635 636 if power_state == "Off" and command in ['PowerGracefulShutdown', 'PowerForceOff']: 637 return {'ret': True, 'changed': False} 638 639 reset_action = data[key]["#ComputerSystem.Reset"] 640 action_uri = reset_action["target"] 641 allowable_vals = reset_action.get("ResetType@Redfish.AllowableValues", []) 642 restart_cmd = "GracefulRestart" 643 if "ForceRestart" in allowable_vals and "GracefulRestart" not in allowable_vals: 644 restart_cmd = "ForceRestart" 645 646 # Define payload accordingly 647 if command == "PowerOn": 648 payload = {'ResetType': 'On'} 649 elif command == "PowerForceOff": 650 payload = {'ResetType': 'ForceOff'} 651 elif command == "PowerForceRestart": 652 payload = {'ResetType': "ForceRestart"} 653 elif command == "PowerGracefulRestart": 654 payload = {'ResetType': 'GracefulRestart'} 655 elif command == "PowerGracefulShutdown": 656 payload = {'ResetType': 'GracefulShutdown'} 657 elif command == "PowerReboot": 658 if power_state == "On": 659 payload = {'ResetType': restart_cmd} 660 else: 661 payload = {'ResetType': "On"} 662 else: 663 return {'ret': False, 'msg': 'Invalid Command'} 664 665 response = self.post_request(self.root_uri + action_uri, payload) 666 if response['ret'] is False: 667 return response 668 return {'ret': True, 'changed': True} 669 670 def list_users(self): 671 result = {} 672 # listing all users has always been slower than other operations, why? 673 user_list = [] 674 users_results = [] 675 # Get these entries, but does not fail if not found 676 properties = ['Id', 'Name', 'UserName', 'RoleId', 'Locked', 'Enabled'] 677 678 response = self.get_request(self.root_uri + self.accounts_uri) 679 if response['ret'] is False: 680 return response 681 result['ret'] = True 682 data = response['data'] 683 684 for users in data[u'Members']: 685 user_list.append(users[u'@odata.id']) # user_list[] are URIs 686 687 # for each user, get details 688 for uri in user_list: 689 user = {} 690 response = self.get_request(self.root_uri + uri) 691 if response['ret'] is False: 692 return response 693 data = response['data'] 694 695 for property in properties: 696 if property in data: 697 user[property] = data[property] 698 699 users_results.append(user) 700 result["entries"] = users_results 701 return result 702 703 def add_user(self, user): 704 uri = self.root_uri + self.accounts_uri + "/" + user['userid'] 705 username = {'UserName': user['username']} 706 pswd = {'Password': user['userpswd']} 707 roleid = {'RoleId': user['userrole']} 708 enabled = {'Enabled': True} 709 for payload in username, pswd, roleid, enabled: 710 response = self.patch_request(uri, payload) 711 if response['ret'] is False: 712 return response 713 return {'ret': True} 714 715 def enable_user(self, user): 716 uri = self.root_uri + self.accounts_uri + "/" + user['userid'] 717 payload = {'Enabled': True} 718 response = self.patch_request(uri, payload) 719 if response['ret'] is False: 720 return response 721 return {'ret': True} 722 723 def delete_user(self, user): 724 uri = self.root_uri + self.accounts_uri + "/" + user['userid'] 725 payload = {'UserName': ""} 726 response = self.patch_request(uri, payload) 727 if response['ret'] is False: 728 return response 729 return {'ret': True} 730 731 def disable_user(self, user): 732 uri = self.root_uri + self.accounts_uri + "/" + user['userid'] 733 payload = {'Enabled': False} 734 response = self.patch_request(uri, payload) 735 if response['ret'] is False: 736 return response 737 return {'ret': True} 738 739 def update_user_role(self, user): 740 uri = self.root_uri + self.accounts_uri + "/" + user['userid'] 741 payload = {'RoleId': user['userrole']} 742 response = self.patch_request(uri, payload) 743 if response['ret'] is False: 744 return response 745 return {'ret': True} 746 747 def update_user_password(self, user): 748 uri = self.root_uri + self.accounts_uri + "/" + user['userid'] 749 payload = {'Password': user['userpswd']} 750 response = self.patch_request(uri, payload) 751 if response['ret'] is False: 752 return response 753 return {'ret': True} 754 755 def get_sessions(self): 756 result = {} 757 # listing all users has always been slower than other operations, why? 758 session_list = [] 759 sessions_results = [] 760 # Get these entries, but does not fail if not found 761 properties = ['Description', 'Id', 'Name', 'UserName'] 762 763 response = self.get_request(self.root_uri + self.sessions_uri) 764 if response['ret'] is False: 765 return response 766 result['ret'] = True 767 data = response['data'] 768 769 for sessions in data[u'Members']: 770 session_list.append(sessions[u'@odata.id']) # session_list[] are URIs 771 772 # for each session, get details 773 for uri in session_list: 774 session = {} 775 response = self.get_request(self.root_uri + uri) 776 if response['ret'] is False: 777 return response 778 data = response['data'] 779 780 for property in properties: 781 if property in data: 782 session[property] = data[property] 783 784 sessions_results.append(session) 785 result["entries"] = sessions_results 786 return result 787 788 def get_firmware_update_capabilities(self): 789 result = {} 790 response = self.get_request(self.root_uri + self.update_uri) 791 if response['ret'] is False: 792 return response 793 794 result['ret'] = True 795 796 result['entries'] = {} 797 798 data = response['data'] 799 800 if "Actions" in data: 801 actions = data['Actions'] 802 if len(actions) > 0: 803 for key in actions.keys(): 804 action = actions.get(key) 805 if 'title' in action: 806 title = action['title'] 807 else: 808 title = key 809 result['entries'][title] = action.get('TransferProtocol@Redfish.AllowableValues', 810 ["Key TransferProtocol@Redfish.AllowableValues not found"]) 811 else: 812 return {'ret': "False", 'msg': "Actions list is empty."} 813 else: 814 return {'ret': "False", 'msg': "Key Actions not found."} 815 return result 816 817 def get_firmware_inventory(self): 818 result = {} 819 response = self.get_request(self.root_uri + self.firmware_uri) 820 if response['ret'] is False: 821 return response 822 result['ret'] = True 823 data = response['data'] 824 825 result['entries'] = [] 826 for device in data[u'Members']: 827 uri = self.root_uri + device[u'@odata.id'] 828 # Get details for each device 829 response = self.get_request(uri) 830 if response['ret'] is False: 831 return response 832 result['ret'] = True 833 data = response['data'] 834 firmware = {} 835 # Get these standard properties if present 836 for key in ['Name', 'Id', 'Status', 'Version', 'Updateable', 837 'SoftwareId', 'LowestSupportedVersion', 'Manufacturer', 838 'ReleaseDate']: 839 if key in data: 840 firmware[key] = data.get(key) 841 result['entries'].append(firmware) 842 return result 843 844 def get_bios_attributes(self, systems_uri): 845 result = {} 846 bios_attributes = {} 847 key = "Bios" 848 849 # Search for 'key' entry and extract URI from it 850 response = self.get_request(self.root_uri + systems_uri) 851 if response['ret'] is False: 852 return response 853 result['ret'] = True 854 data = response['data'] 855 856 if key not in data: 857 return {'ret': False, 'msg': "Key %s not found" % key} 858 859 bios_uri = data[key]["@odata.id"] 860 861 response = self.get_request(self.root_uri + bios_uri) 862 if response['ret'] is False: 863 return response 864 result['ret'] = True 865 data = response['data'] 866 for attribute in data[u'Attributes'].items(): 867 bios_attributes[attribute[0]] = attribute[1] 868 result["entries"] = bios_attributes 869 return result 870 871 def get_multi_bios_attributes(self): 872 return self.aggregate(self.get_bios_attributes) 873 874 def get_boot_order(self, systems_uri): 875 result = {} 876 # Get these entries from BootOption, if present 877 properties = ['DisplayName', 'BootOptionReference'] 878 879 # Retrieve System resource 880 response = self.get_request(self.root_uri + systems_uri) 881 if response['ret'] is False: 882 return response 883 result['ret'] = True 884 data = response['data'] 885 886 # Confirm needed Boot properties are present 887 if 'Boot' not in data or 'BootOrder' not in data['Boot']: 888 return {'ret': False, 'msg': "Key BootOrder not found"} 889 890 boot = data['Boot'] 891 boot_order = boot['BootOrder'] 892 893 # Retrieve BootOptions if present 894 if 'BootOptions' in boot and '@odata.id' in boot['BootOptions']: 895 boot_options_uri = boot['BootOptions']["@odata.id"] 896 # Get BootOptions resource 897 response = self.get_request(self.root_uri + boot_options_uri) 898 if response['ret'] is False: 899 return response 900 data = response['data'] 901 902 # Retrieve Members array 903 if 'Members' not in data: 904 return {'ret': False, 905 'msg': "Members not found in BootOptionsCollection"} 906 members = data['Members'] 907 else: 908 members = [] 909 910 # Build dict of BootOptions keyed by BootOptionReference 911 boot_options_dict = {} 912 for member in members: 913 if '@odata.id' not in member: 914 return {'ret': False, 'msg': "@odata.id not found in BootOptions"} 915 boot_option_uri = member['@odata.id'] 916 response = self.get_request(self.root_uri + boot_option_uri) 917 if response['ret'] is False: 918 return response 919 data = response['data'] 920 if 'BootOptionReference' not in data: 921 return {'ret': False, 'msg': "BootOptionReference not found in BootOption"} 922 boot_option_ref = data['BootOptionReference'] 923 924 # fetch the props to display for this boot device 925 boot_props = {} 926 for prop in properties: 927 if prop in data: 928 boot_props[prop] = data[prop] 929 930 boot_options_dict[boot_option_ref] = boot_props 931 932 # Build boot device list 933 boot_device_list = [] 934 for ref in boot_order: 935 boot_device_list.append( 936 boot_options_dict.get(ref, {'BootOptionReference': ref})) 937 938 result["entries"] = boot_device_list 939 return result 940 941 def get_multi_boot_order(self): 942 return self.aggregate(self.get_boot_order) 943 944 def get_boot_override(self, systems_uri): 945 result = {} 946 947 properties = ["BootSourceOverrideEnabled", "BootSourceOverrideTarget", 948 "BootSourceOverrideMode", "UefiTargetBootSourceOverride", "BootSourceOverrideTarget@Redfish.AllowableValues"] 949 950 response = self.get_request(self.root_uri + systems_uri) 951 if response['ret'] is False: 952 return response 953 result['ret'] = True 954 data = response['data'] 955 956 if 'Boot' not in data: 957 return {'ret': False, 'msg': "Key Boot not found"} 958 959 boot = data['Boot'] 960 961 boot_overrides = {} 962 if "BootSourceOverrideEnabled" in boot: 963 if boot["BootSourceOverrideEnabled"] is not False: 964 for property in properties: 965 if property in boot: 966 if boot[property] is not None: 967 boot_overrides[property] = boot[property] 968 else: 969 return {'ret': False, 'msg': "No boot override is enabled."} 970 971 result['entries'] = boot_overrides 972 return result 973 974 def get_multi_boot_override(self): 975 return self.aggregate(self.get_boot_override) 976 977 def set_bios_default_settings(self): 978 result = {} 979 key = "Bios" 980 981 # Search for 'key' entry and extract URI from it 982 response = self.get_request(self.root_uri + self.systems_uris[0]) 983 if response['ret'] is False: 984 return response 985 result['ret'] = True 986 data = response['data'] 987 988 if key not in data: 989 return {'ret': False, 'msg': "Key %s not found" % key} 990 991 bios_uri = data[key]["@odata.id"] 992 993 # Extract proper URI 994 response = self.get_request(self.root_uri + bios_uri) 995 if response['ret'] is False: 996 return response 997 result['ret'] = True 998 data = response['data'] 999 reset_bios_settings_uri = data["Actions"]["#Bios.ResetBios"]["target"] 1000 1001 response = self.post_request(self.root_uri + reset_bios_settings_uri, {}) 1002 if response['ret'] is False: 1003 return response 1004 return {'ret': True, 'changed': True, 'msg': "Set BIOS to default settings"} 1005 1006 def set_one_time_boot_device(self, bootdevice, uefi_target, boot_next): 1007 result = {} 1008 key = "Boot" 1009 1010 if not bootdevice: 1011 return {'ret': False, 1012 'msg': "bootdevice option required for SetOneTimeBoot"} 1013 1014 # Search for 'key' entry and extract URI from it 1015 response = self.get_request(self.root_uri + self.systems_uris[0]) 1016 if response['ret'] is False: 1017 return response 1018 result['ret'] = True 1019 data = response['data'] 1020 1021 if key not in data: 1022 return {'ret': False, 'msg': "Key %s not found" % key} 1023 1024 boot = data[key] 1025 1026 annotation = 'BootSourceOverrideTarget@Redfish.AllowableValues' 1027 if annotation in boot: 1028 allowable_values = boot[annotation] 1029 if isinstance(allowable_values, list) and bootdevice not in allowable_values: 1030 return {'ret': False, 1031 'msg': "Boot device %s not in list of allowable values (%s)" % 1032 (bootdevice, allowable_values)} 1033 1034 # read existing values 1035 enabled = boot.get('BootSourceOverrideEnabled') 1036 target = boot.get('BootSourceOverrideTarget') 1037 cur_uefi_target = boot.get('UefiTargetBootSourceOverride') 1038 cur_boot_next = boot.get('BootNext') 1039 1040 if bootdevice == 'UefiTarget': 1041 if not uefi_target: 1042 return {'ret': False, 1043 'msg': "uefi_target option required to SetOneTimeBoot for UefiTarget"} 1044 if enabled == 'Once' and target == bootdevice and uefi_target == cur_uefi_target: 1045 # If properties are already set, no changes needed 1046 return {'ret': True, 'changed': False} 1047 payload = { 1048 'Boot': { 1049 'BootSourceOverrideEnabled': 'Once', 1050 'BootSourceOverrideTarget': bootdevice, 1051 'UefiTargetBootSourceOverride': uefi_target 1052 } 1053 } 1054 elif bootdevice == 'UefiBootNext': 1055 if not boot_next: 1056 return {'ret': False, 1057 'msg': "boot_next option required to SetOneTimeBoot for UefiBootNext"} 1058 if enabled == 'Once' and target == bootdevice and boot_next == cur_boot_next: 1059 # If properties are already set, no changes needed 1060 return {'ret': True, 'changed': False} 1061 payload = { 1062 'Boot': { 1063 'BootSourceOverrideEnabled': 'Once', 1064 'BootSourceOverrideTarget': bootdevice, 1065 'BootNext': boot_next 1066 } 1067 } 1068 else: 1069 if enabled == 'Once' and target == bootdevice: 1070 # If properties are already set, no changes needed 1071 return {'ret': True, 'changed': False} 1072 payload = { 1073 'Boot': { 1074 'BootSourceOverrideEnabled': 'Once', 1075 'BootSourceOverrideTarget': bootdevice 1076 } 1077 } 1078 1079 response = self.patch_request(self.root_uri + self.systems_uris[0], payload) 1080 if response['ret'] is False: 1081 return response 1082 return {'ret': True, 'changed': True} 1083 1084 def set_bios_attributes(self, attr): 1085 result = {} 1086 key = "Bios" 1087 1088 # Search for 'key' entry and extract URI from it 1089 response = self.get_request(self.root_uri + self.systems_uris[0]) 1090 if response['ret'] is False: 1091 return response 1092 result['ret'] = True 1093 data = response['data'] 1094 1095 if key not in data: 1096 return {'ret': False, 'msg': "Key %s not found" % key} 1097 1098 bios_uri = data[key]["@odata.id"] 1099 1100 # Extract proper URI 1101 response = self.get_request(self.root_uri + bios_uri) 1102 if response['ret'] is False: 1103 return response 1104 result['ret'] = True 1105 data = response['data'] 1106 1107 # First, check if BIOS attribute exists 1108 if attr['bios_attr_name'] not in data[u'Attributes']: 1109 return {'ret': False, 'msg': "BIOS attribute not found"} 1110 1111 # Find out if value is already set to what we want. If yes, return 1112 if data[u'Attributes'][attr['bios_attr_name']] == attr['bios_attr_value']: 1113 return {'ret': True, 'changed': False, 'msg': "BIOS attribute already set"} 1114 1115 set_bios_attr_uri = data["@Redfish.Settings"]["SettingsObject"]["@odata.id"] 1116 1117 # Example: bios_attr = {\"name\":\"value\"} 1118 bios_attr = "{\"" + attr['bios_attr_name'] + "\":\"" + attr['bios_attr_value'] + "\"}" 1119 payload = {"Attributes": json.loads(bios_attr)} 1120 response = self.patch_request(self.root_uri + set_bios_attr_uri, payload) 1121 if response['ret'] is False: 1122 return response 1123 return {'ret': True, 'changed': True, 'msg': "Modified BIOS attribute"} 1124 1125 def get_chassis_inventory(self): 1126 result = {} 1127 chassis_results = [] 1128 1129 # Get these entries, but does not fail if not found 1130 properties = ['ChassisType', 'PartNumber', 'AssetTag', 1131 'Manufacturer', 'IndicatorLED', 'SerialNumber', 'Model'] 1132 1133 # Go through list 1134 for chassis_uri in self.chassis_uri_list: 1135 response = self.get_request(self.root_uri + chassis_uri) 1136 if response['ret'] is False: 1137 return response 1138 result['ret'] = True 1139 data = response['data'] 1140 chassis_result = {} 1141 for property in properties: 1142 if property in data: 1143 chassis_result[property] = data[property] 1144 chassis_results.append(chassis_result) 1145 1146 result["entries"] = chassis_results 1147 return result 1148 1149 def get_fan_inventory(self): 1150 result = {} 1151 fan_results = [] 1152 key = "Thermal" 1153 # Get these entries, but does not fail if not found 1154 properties = ['FanName', 'Reading', 'ReadingUnits', 'Status'] 1155 1156 # Go through list 1157 for chassis_uri in self.chassis_uri_list: 1158 response = self.get_request(self.root_uri + chassis_uri) 1159 if response['ret'] is False: 1160 return response 1161 result['ret'] = True 1162 data = response['data'] 1163 if key in data: 1164 # match: found an entry for "Thermal" information = fans 1165 thermal_uri = data[key]["@odata.id"] 1166 response = self.get_request(self.root_uri + thermal_uri) 1167 if response['ret'] is False: 1168 return response 1169 result['ret'] = True 1170 data = response['data'] 1171 1172 for device in data[u'Fans']: 1173 fan = {} 1174 for property in properties: 1175 if property in device: 1176 fan[property] = device[property] 1177 fan_results.append(fan) 1178 result["entries"] = fan_results 1179 return result 1180 1181 def get_chassis_power(self): 1182 result = {} 1183 key = "Power" 1184 1185 # Get these entries, but does not fail if not found 1186 properties = ['Name', 'PowerAllocatedWatts', 1187 'PowerAvailableWatts', 'PowerCapacityWatts', 1188 'PowerConsumedWatts', 'PowerMetrics', 1189 'PowerRequestedWatts', 'RelatedItem', 'Status'] 1190 1191 chassis_power_results = [] 1192 # Go through list 1193 for chassis_uri in self.chassis_uri_list: 1194 chassis_power_result = {} 1195 response = self.get_request(self.root_uri + chassis_uri) 1196 if response['ret'] is False: 1197 return response 1198 result['ret'] = True 1199 data = response['data'] 1200 if key in data: 1201 response = self.get_request(self.root_uri + data[key]['@odata.id']) 1202 data = response['data'] 1203 if 'PowerControl' in data: 1204 if len(data['PowerControl']) > 0: 1205 data = data['PowerControl'][0] 1206 for property in properties: 1207 if property in data: 1208 chassis_power_result[property] = data[property] 1209 else: 1210 return {'ret': False, 'msg': 'Key PowerControl not found.'} 1211 chassis_power_results.append(chassis_power_result) 1212 else: 1213 return {'ret': False, 'msg': 'Key Power not found.'} 1214 1215 result['entries'] = chassis_power_results 1216 return result 1217 1218 def get_chassis_thermals(self): 1219 result = {} 1220 sensors = [] 1221 key = "Thermal" 1222 1223 # Get these entries, but does not fail if not found 1224 properties = ['Name', 'PhysicalContext', 'UpperThresholdCritical', 1225 'UpperThresholdFatal', 'UpperThresholdNonCritical', 1226 'LowerThresholdCritical', 'LowerThresholdFatal', 1227 'LowerThresholdNonCritical', 'MaxReadingRangeTemp', 1228 'MinReadingRangeTemp', 'ReadingCelsius', 'RelatedItem', 1229 'SensorNumber'] 1230 1231 # Go through list 1232 for chassis_uri in self.chassis_uri_list: 1233 response = self.get_request(self.root_uri + chassis_uri) 1234 if response['ret'] is False: 1235 return response 1236 result['ret'] = True 1237 data = response['data'] 1238 if key in data: 1239 thermal_uri = data[key]["@odata.id"] 1240 response = self.get_request(self.root_uri + thermal_uri) 1241 if response['ret'] is False: 1242 return response 1243 result['ret'] = True 1244 data = response['data'] 1245 if "Temperatures" in data: 1246 for sensor in data[u'Temperatures']: 1247 sensor_result = {} 1248 for property in properties: 1249 if property in sensor: 1250 if sensor[property] is not None: 1251 sensor_result[property] = sensor[property] 1252 sensors.append(sensor_result) 1253 1254 if sensors is None: 1255 return {'ret': False, 'msg': 'Key Temperatures was not found.'} 1256 1257 result['entries'] = sensors 1258 return result 1259 1260 def get_cpu_inventory(self, systems_uri): 1261 result = {} 1262 cpu_list = [] 1263 cpu_results = [] 1264 key = "Processors" 1265 # Get these entries, but does not fail if not found 1266 properties = ['Id', 'Manufacturer', 'Model', 'MaxSpeedMHz', 'TotalCores', 1267 'TotalThreads', 'Status'] 1268 1269 # Search for 'key' entry and extract URI from it 1270 response = self.get_request(self.root_uri + systems_uri) 1271 if response['ret'] is False: 1272 return response 1273 result['ret'] = True 1274 data = response['data'] 1275 1276 if key not in data: 1277 return {'ret': False, 'msg': "Key %s not found" % key} 1278 1279 processors_uri = data[key]["@odata.id"] 1280 1281 # Get a list of all CPUs and build respective URIs 1282 response = self.get_request(self.root_uri + processors_uri) 1283 if response['ret'] is False: 1284 return response 1285 result['ret'] = True 1286 data = response['data'] 1287 1288 for cpu in data[u'Members']: 1289 cpu_list.append(cpu[u'@odata.id']) 1290 1291 for c in cpu_list: 1292 cpu = {} 1293 uri = self.root_uri + c 1294 response = self.get_request(uri) 1295 if response['ret'] is False: 1296 return response 1297 data = response['data'] 1298 1299 for property in properties: 1300 if property in data: 1301 cpu[property] = data[property] 1302 1303 cpu_results.append(cpu) 1304 result["entries"] = cpu_results 1305 return result 1306 1307 def get_multi_cpu_inventory(self): 1308 return self.aggregate(self.get_cpu_inventory) 1309 1310 def get_memory_inventory(self, systems_uri): 1311 result = {} 1312 memory_list = [] 1313 memory_results = [] 1314 key = "Memory" 1315 # Get these entries, but does not fail if not found 1316 properties = ['SerialNumber', 'MemoryDeviceType', 'PartNuber', 1317 'MemoryLocation', 'RankCount', 'CapacityMiB', 'OperatingMemoryModes', 'Status', 'Manufacturer', 'Name'] 1318 1319 # Search for 'key' entry and extract URI from it 1320 response = self.get_request(self.root_uri + systems_uri) 1321 if response['ret'] is False: 1322 return response 1323 result['ret'] = True 1324 data = response['data'] 1325 1326 if key not in data: 1327 return {'ret': False, 'msg': "Key %s not found" % key} 1328 1329 memory_uri = data[key]["@odata.id"] 1330 1331 # Get a list of all DIMMs and build respective URIs 1332 response = self.get_request(self.root_uri + memory_uri) 1333 if response['ret'] is False: 1334 return response 1335 result['ret'] = True 1336 data = response['data'] 1337 1338 for dimm in data[u'Members']: 1339 memory_list.append(dimm[u'@odata.id']) 1340 1341 for m in memory_list: 1342 dimm = {} 1343 uri = self.root_uri + m 1344 response = self.get_request(uri) 1345 if response['ret'] is False: 1346 return response 1347 data = response['data'] 1348 1349 if "Status" in data: 1350 if "State" in data["Status"]: 1351 if data["Status"]["State"] == "Absent": 1352 continue 1353 else: 1354 continue 1355 1356 for property in properties: 1357 if property in data: 1358 dimm[property] = data[property] 1359 1360 memory_results.append(dimm) 1361 result["entries"] = memory_results 1362 return result 1363 1364 def get_multi_memory_inventory(self): 1365 return self.aggregate(self.get_memory_inventory) 1366 1367 def get_nic_inventory(self, resource_uri): 1368 result = {} 1369 nic_list = [] 1370 nic_results = [] 1371 key = "EthernetInterfaces" 1372 # Get these entries, but does not fail if not found 1373 properties = ['Description', 'FQDN', 'IPv4Addresses', 'IPv6Addresses', 1374 'NameServers', 'MACAddress', 'PermanentMACAddress', 1375 'SpeedMbps', 'MTUSize', 'AutoNeg', 'Status'] 1376 1377 response = self.get_request(self.root_uri + resource_uri) 1378 if response['ret'] is False: 1379 return response 1380 result['ret'] = True 1381 data = response['data'] 1382 1383 if key not in data: 1384 return {'ret': False, 'msg': "Key %s not found" % key} 1385 1386 ethernetinterfaces_uri = data[key]["@odata.id"] 1387 1388 # Get a list of all network controllers and build respective URIs 1389 response = self.get_request(self.root_uri + ethernetinterfaces_uri) 1390 if response['ret'] is False: 1391 return response 1392 result['ret'] = True 1393 data = response['data'] 1394 1395 for nic in data[u'Members']: 1396 nic_list.append(nic[u'@odata.id']) 1397 1398 for n in nic_list: 1399 nic = {} 1400 uri = self.root_uri + n 1401 response = self.get_request(uri) 1402 if response['ret'] is False: 1403 return response 1404 data = response['data'] 1405 1406 for property in properties: 1407 if property in data: 1408 nic[property] = data[property] 1409 1410 nic_results.append(nic) 1411 result["entries"] = nic_results 1412 return result 1413 1414 def get_multi_nic_inventory(self, resource_type): 1415 ret = True 1416 entries = [] 1417 1418 # Given resource_type, use the proper URI 1419 if resource_type == 'Systems': 1420 resource_uris = self.systems_uris 1421 elif resource_type == 'Manager': 1422 # put in a list to match what we're doing with systems_uris 1423 resource_uris = [self.manager_uri] 1424 1425 for resource_uri in resource_uris: 1426 inventory = self.get_nic_inventory(resource_uri) 1427 ret = inventory.pop('ret') and ret 1428 if 'entries' in inventory: 1429 entries.append(({'resource_uri': resource_uri}, 1430 inventory['entries'])) 1431 return dict(ret=ret, entries=entries) 1432 1433 def get_virtualmedia(self, resource_uri): 1434 result = {} 1435 virtualmedia_list = [] 1436 virtualmedia_results = [] 1437 key = "VirtualMedia" 1438 # Get these entries, but does not fail if not found 1439 properties = ['Description', 'ConnectedVia', 'Id', 'MediaTypes', 1440 'Image', 'ImageName', 'Name', 'WriteProtected', 1441 'TransferMethod', 'TransferProtocolType'] 1442 1443 response = self.get_request(self.root_uri + resource_uri) 1444 if response['ret'] is False: 1445 return response 1446 result['ret'] = True 1447 data = response['data'] 1448 1449 if key not in data: 1450 return {'ret': False, 'msg': "Key %s not found" % key} 1451 1452 virtualmedia_uri = data[key]["@odata.id"] 1453 1454 # Get a list of all virtual media and build respective URIs 1455 response = self.get_request(self.root_uri + virtualmedia_uri) 1456 if response['ret'] is False: 1457 return response 1458 result['ret'] = True 1459 data = response['data'] 1460 1461 for virtualmedia in data[u'Members']: 1462 virtualmedia_list.append(virtualmedia[u'@odata.id']) 1463 1464 for n in virtualmedia_list: 1465 virtualmedia = {} 1466 uri = self.root_uri + n 1467 response = self.get_request(uri) 1468 if response['ret'] is False: 1469 return response 1470 data = response['data'] 1471 1472 for property in properties: 1473 if property in data: 1474 virtualmedia[property] = data[property] 1475 1476 virtualmedia_results.append(virtualmedia) 1477 result["entries"] = virtualmedia_results 1478 return result 1479 1480 def get_multi_virtualmedia(self): 1481 ret = True 1482 entries = [] 1483 1484 # Because _find_managers_resource() only find last Manager uri in self.manager_uri, not one list. This should be 1 issue. 1485 # I have to put manager_uri into list to reduce future changes when the issue is fixed. 1486 resource_uris = [self.manager_uri] 1487 1488 for resource_uri in resource_uris: 1489 virtualmedia = self.get_virtualmedia(resource_uri) 1490 ret = virtualmedia.pop('ret') and ret 1491 if 'entries' in virtualmedia: 1492 entries.append(({'resource_uri': resource_uri}, 1493 virtualmedia['entries'])) 1494 return dict(ret=ret, entries=entries) 1495 1496 def get_psu_inventory(self): 1497 result = {} 1498 psu_list = [] 1499 psu_results = [] 1500 key = "PowerSupplies" 1501 # Get these entries, but does not fail if not found 1502 properties = ['Name', 'Model', 'SerialNumber', 'PartNumber', 'Manufacturer', 1503 'FirmwareVersion', 'PowerCapacityWatts', 'PowerSupplyType', 1504 'Status'] 1505 1506 # Get a list of all Chassis and build URIs, then get all PowerSupplies 1507 # from each Power entry in the Chassis 1508 chassis_uri_list = self.chassis_uri_list 1509 for chassis_uri in chassis_uri_list: 1510 response = self.get_request(self.root_uri + chassis_uri) 1511 if response['ret'] is False: 1512 return response 1513 1514 result['ret'] = True 1515 data = response['data'] 1516 1517 if 'Power' in data: 1518 power_uri = data[u'Power'][u'@odata.id'] 1519 else: 1520 continue 1521 1522 response = self.get_request(self.root_uri + power_uri) 1523 data = response['data'] 1524 1525 if key not in data: 1526 return {'ret': False, 'msg': "Key %s not found" % key} 1527 1528 psu_list = data[key] 1529 for psu in psu_list: 1530 psu_not_present = False 1531 psu_data = {} 1532 for property in properties: 1533 if property in psu: 1534 if psu[property] is not None: 1535 if property == 'Status': 1536 if 'State' in psu[property]: 1537 if psu[property]['State'] == 'Absent': 1538 psu_not_present = True 1539 psu_data[property] = psu[property] 1540 if psu_not_present: 1541 continue 1542 psu_results.append(psu_data) 1543 1544 result["entries"] = psu_results 1545 if not result["entries"]: 1546 return {'ret': False, 'msg': "No PowerSupply objects found"} 1547 return result 1548 1549 def get_multi_psu_inventory(self): 1550 return self.aggregate(self.get_psu_inventory) 1551 1552 def get_system_inventory(self, systems_uri): 1553 result = {} 1554 inventory = {} 1555 # Get these entries, but does not fail if not found 1556 properties = ['Status', 'HostName', 'PowerState', 'Model', 'Manufacturer', 1557 'PartNumber', 'SystemType', 'AssetTag', 'ServiceTag', 1558 'SerialNumber', 'SKU', 'BiosVersion', 'MemorySummary', 1559 'ProcessorSummary', 'TrustedModules'] 1560 1561 response = self.get_request(self.root_uri + systems_uri) 1562 if response['ret'] is False: 1563 return response 1564 result['ret'] = True 1565 data = response['data'] 1566 1567 for property in properties: 1568 if property in data: 1569 inventory[property] = data[property] 1570 1571 result["entries"] = inventory 1572 return result 1573 1574 def get_multi_system_inventory(self): 1575 return self.aggregate(self.get_system_inventory) 1576