1#!/usr/bin/env python3 2 3r""" 4This module contains functions having to do with redfish path walking. 5""" 6 7import json 8import subprocess 9 10ERROR_RESPONSE = { 11 "404": "Response Error: status_code: 404 -- Not Found", 12 "500": "Response Error: status_code: 500 -- Internal Server Error", 13} 14 15# Variable to hold enumerated data. 16result = {} 17 18# Variable to hold the pending list of resources for which enumeration. 19# is yet to be obtained. 20pending_enumeration = set() 21 22 23def execute_redfish_cmd(parms, json_type="json"): 24 r""" 25 Run CLI standard redfish tool. 26 27 Description of variable: 28 parms_string Command to execute from the current SHELL. 29 quiet do not print tool error message if True 30 """ 31 resp = subprocess.run( 32 [parms], 33 stdout=subprocess.PIPE, 34 stderr=subprocess.PIPE, 35 shell=True, 36 universal_newlines=True, 37 ) 38 39 if resp.stderr: 40 print("\n\t\tERROR with %s " % parms) 41 print("\t\t" + resp.stderr) 42 return resp.stderr 43 elif json_type == "json": 44 json_data = json.loads(resp.stdout) 45 return json_data 46 else: 47 return resp.stdout 48 49 50def enumerate_request(hostname, username, password, url, return_json="json"): 51 r""" 52 Perform a GET enumerate request and return available resource paths. 53 54 Description of argument(s): 55 url URI resource absolute path (e.g. 56 "/redfish/v1/SessionService/Sessions"). 57 return_json Indicates whether the result should be 58 returned as a json string or as a 59 dictionary. 60 """ 61 parms = ( 62 "redfishtool -u " 63 + username 64 + " -p " 65 + password 66 + " -r " 67 + hostname 68 + " -S Always raw GET " 69 ) 70 71 pending_enumeration.add(url) 72 73 # Variable having resources for which enumeration is completed. 74 enumerated_resources = set() 75 76 resources_to_be_enumerated = (url,) 77 78 while resources_to_be_enumerated: 79 for resource in resources_to_be_enumerated: 80 # JsonSchemas, SessionService or URLs containing # are not 81 # required in enumeration. 82 # Example: '/redfish/v1/JsonSchemas/' and sub resources. 83 # '/redfish/v1/SessionService' 84 # '/redfish/v1/Managers/${MANAGER_ID}#/Oem' 85 if ( 86 ("JsonSchemas" in resource) 87 or ("SessionService" in resource) 88 or ("PostCodes" in resource) 89 or ("Registries" in resource) 90 or ("#" in resource) 91 ): 92 continue 93 94 response = execute_redfish_cmd(parms + resource) 95 # Enumeration is done for available resources ignoring the 96 # ones for which response is not obtained. 97 if "Error getting response" in response: 98 continue 99 100 walk_nested_dict(response, url=resource) 101 102 enumerated_resources.update(set(resources_to_be_enumerated)) 103 resources_to_be_enumerated = tuple( 104 pending_enumeration - enumerated_resources 105 ) 106 107 if return_json == "json": 108 return json.dumps( 109 result, sort_keys=True, indent=4, separators=(",", ": ") 110 ) 111 else: 112 return result 113 114 115def walk_nested_dict(data, url=""): 116 r""" 117 Parse through the nested dictionary and get the resource id paths. 118 119 Description of argument(s): 120 data Nested dictionary data from response message. 121 url Resource for which the response is obtained in data. 122 """ 123 url = url.rstrip("/") 124 125 for key, value in data.items(): 126 # Recursion if nested dictionary found. 127 if isinstance(value, dict): 128 walk_nested_dict(value) 129 else: 130 # Value contains a list of dictionaries having member data. 131 if "Members" == key: 132 if isinstance(value, list): 133 for memberDict in value: 134 if isinstance(memberDict, str): 135 pending_enumeration.add(memberDict) 136 else: 137 pending_enumeration.add(memberDict["@odata.id"]) 138 139 if "@odata.id" == key: 140 value = value.rstrip("/") 141 # Data for the given url. 142 if value == url: 143 result[url] = data 144 # Data still needs to be looked up, 145 else: 146 pending_enumeration.add(value) 147 148 149def get_key_value_nested_dict(data, key): 150 r""" 151 Parse through the nested dictionary and get the searched key value. 152 153 Description of argument(s): 154 data Nested dictionary data from response message. 155 key Search dictionary key element. 156 """ 157 158 for k, v in data.items(): 159 if isinstance(v, dict): 160 get_key_value_nested_dict(v, key) 161