1#!/usr/bin/env python 2 3r""" 4BMC redfish utility functions. 5""" 6 7import json 8from robot.libraries.BuiltIn import BuiltIn 9import gen_print as gp 10 11 12class bmc_redfish_utils(object): 13 14 def __init__(self): 15 r""" 16 Initialize the bmc_redfish_utils object. 17 """ 18 # Obtain a reference to the global redfish object. 19 self._redfish_ = BuiltIn().get_library_instance('redfish') 20 21 def get_redfish_session_info(self): 22 r""" 23 Returns redfish sessions info dictionary. 24 25 { 26 'key': 'yLXotJnrh5nDhXj5lLiH' , 27 'location': '/redfish/v1/SessionService/Sessions/nblYY4wlz0' 28 } 29 """ 30 session_dict = { 31 "key": self._redfish_.get_session_key(), 32 "location": self._redfish_.get_session_location() 33 } 34 return session_dict 35 36 def get_attribute(self, resource_path, attribute): 37 r""" 38 Get resource attribute. 39 40 Description of argument(s): 41 resource_path URI resource absolute path (e.g. 42 "/redfish/v1/Systems/1"). 43 attribute Name of the attribute (e.g. 'PowerState'). 44 """ 45 46 resp = self._redfish_.get(resource_path) 47 if attribute in resp.dict: 48 return resp.dict[attribute] 49 50 return None 51 52 def get_properties(self, resource_path): 53 r""" 54 Returns dictionary of attributes for the resource. 55 56 Description of argument(s): 57 resource_path URI resource absolute path (e.g. 58 "/redfish/v1/Systems/1"). 59 """ 60 61 resp = self._redfish_.get(resource_path) 62 return resp.dict 63 64 def get_target_actions(self, resource_path, target_attribute): 65 r""" 66 Returns resource target entry of the searched target attribute. 67 68 Description of argument(s): 69 resource_path URI resource absolute path 70 (e.g. "/redfish/v1/Systems/system"). 71 target_attribute Name of the attribute (e.g. 'ComputerSystem.Reset'). 72 73 Example: 74 "Actions": { 75 "#ComputerSystem.Reset": { 76 "ResetType@Redfish.AllowableValues": [ 77 "On", 78 "ForceOff", 79 "GracefulRestart", 80 "GracefulShutdown" 81 ], 82 "target": "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset" 83 } 84 } 85 """ 86 87 global target_list 88 target_list = [] 89 90 resp_dict = self.get_attribute(resource_path, "Actions") 91 if resp_dict is None: 92 return None 93 94 # Recursively search the "target" key in the nested dictionary. 95 # Populate the target_list of target entries. 96 self.get_key_value_nested_dict(resp_dict, "target") 97 98 # Return the matching target URL entry. 99 for target in target_list: 100 # target "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset" 101 if target_attribute in target: 102 return target 103 104 return None 105 106 def get_member_list(self, resource_path): 107 r""" 108 Perform a GET list request and return available members entries. 109 110 Description of argument(s): 111 resource_path URI resource absolute path 112 (e.g. "/redfish/v1/SessionService/Sessions"). 113 114 "Members": [ 115 { 116 "@odata.id": "/redfish/v1/SessionService/Sessions/Z5HummWPZ7" 117 } 118 { 119 "@odata.id": "/redfish/v1/SessionService/Sessions/46CmQmEL7H" 120 } 121 ], 122 """ 123 124 member_list = [] 125 resp_list_dict = self.get_attribute(resource_path, "Members") 126 if resp_list_dict is None: 127 return member_list 128 129 for member_id in range(0, len(resp_list_dict)): 130 member_list.append(resp_list_dict[member_id]["@odata.id"]) 131 132 return member_list 133 134 def list_request(self, resource_path): 135 r""" 136 Perform a GET list request and return available resource paths. 137 Description of argument(s): 138 resource_path URI resource absolute path 139 (e.g. "/redfish/v1/SessionService/Sessions"). 140 """ 141 gp.qprint_executing(style=gp.func_line_style_short) 142 # Set quiet variable to keep subordinate get() calls quiet. 143 quiet = 1 144 self.__pending_enumeration = set() 145 self._rest_response_ = \ 146 self._redfish_.get(resource_path, 147 valid_status_codes=[200, 404, 500]) 148 149 # Return empty list. 150 if self._rest_response_.status != 200: 151 return self.__pending_enumeration 152 self.walk_nested_dict(self._rest_response_.dict) 153 if not self.__pending_enumeration: 154 return resource_path 155 for resource in self.__pending_enumeration.copy(): 156 self._rest_response_ = \ 157 self._redfish_.get(resource, 158 valid_status_codes=[200, 404, 500]) 159 160 if self._rest_response_.status != 200: 161 continue 162 self.walk_nested_dict(self._rest_response_.dict) 163 return list(sorted(self.__pending_enumeration)) 164 165 def enumerate_request(self, resource_path, return_json=1): 166 r""" 167 Perform a GET enumerate request and return available resource paths. 168 169 Description of argument(s): 170 resource_path URI resource absolute path (e.g. 171 "/redfish/v1/SessionService/Sessions"). 172 return_json Indicates whether the result should be 173 returned as a json string or as a 174 dictionary. 175 """ 176 177 gp.qprint_executing(style=gp.func_line_style_short) 178 179 return_json = int(return_json) 180 181 # Set quiet variable to keep subordinate get() calls quiet. 182 quiet = 1 183 184 # Variable to hold enumerated data. 185 self.__result = {} 186 187 # Variable to hold the pending list of resources for which enumeration. 188 # is yet to be obtained. 189 self.__pending_enumeration = set() 190 191 self.__pending_enumeration.add(resource_path) 192 193 # Variable having resources for which enumeration is completed. 194 enumerated_resources = set() 195 196 resources_to_be_enumerated = (resource_path,) 197 198 while resources_to_be_enumerated: 199 for resource in resources_to_be_enumerated: 200 # JsonSchemas data are not required in enumeration. 201 # Example: '/redfish/v1/JsonSchemas/' and sub resources. 202 if 'JsonSchemas' in resource: 203 continue 204 205 self._rest_response_ = \ 206 self._redfish_.get(resource, valid_status_codes=[200, 404, 500]) 207 # Enumeration is done for available resources ignoring the 208 # ones for which response is not obtained. 209 if self._rest_response_.status != 200: 210 continue 211 212 self.walk_nested_dict(self._rest_response_.dict, url=resource) 213 214 enumerated_resources.update(set(resources_to_be_enumerated)) 215 resources_to_be_enumerated = \ 216 tuple(self.__pending_enumeration - enumerated_resources) 217 218 if return_json: 219 return json.dumps(self.__result, sort_keys=True, 220 indent=4, separators=(',', ': ')) 221 else: 222 return self.__result 223 224 def walk_nested_dict(self, data, url=''): 225 r""" 226 Parse through the nested dictionary and get the resource id paths. 227 Description of argument(s): 228 data Nested dictionary data from response message. 229 url Resource for which the response is obtained in data. 230 """ 231 url = url.rstrip('/') 232 233 for key, value in data.items(): 234 235 # Recursion if nested dictionary found. 236 if isinstance(value, dict): 237 self.walk_nested_dict(value) 238 else: 239 # Value contains a list of dictionaries having member data. 240 if 'Members' == key: 241 if isinstance(value, list): 242 for memberDict in value: 243 self.__pending_enumeration.add(memberDict['@odata.id']) 244 if '@odata.id' == key: 245 value = value.rstrip('/') 246 # Data for the given url. 247 if value == url: 248 self.__result[url] = data 249 # Data still needs to be looked up, 250 else: 251 self.__pending_enumeration.add(value) 252 253 def get_key_value_nested_dict(self, data, key): 254 r""" 255 Parse through the nested dictionary and get the searched key value. 256 257 Description of argument(s): 258 data Nested dictionary data from response message. 259 key Search dictionary key element. 260 """ 261 262 for k, v in data.items(): 263 if isinstance(v, dict): 264 self.get_key_value_nested_dict(v, key) 265 266 if k == key: 267 target_list.append(v) 268