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