1#!/usr/bin/env python 2 3r""" 4Using python based redfish library. 5Refer: https://github.com/DMTF/python-redfish-library 6""" 7 8import redfish 9import json 10from robot.libraries.BuiltIn import BuiltIn 11 12 13class HTTPSBadRequestError(Exception): 14 r""" 15 BMC redfish generic raised method for error(s). 16 """ 17 pass 18 19 20class bmc_redfish(object): 21 22 ROBOT_LIBRARY_SCOPE = "TEST SUITE" 23 ROBOT_EXIT_ON_FAILURE = True 24 25 def __init__(self, hostname, username, password, *args, **kwargs): 26 r""" 27 Establish session connection to host. 28 29 Description of argument(s): 30 hostname The host name or IP address of the server. 31 username The username to be used to connect to the server. 32 password The password to be used to connect to the server. 33 args/kwargs Additional parms which are passed directly 34 to the redfish_client function. 35 """ 36 self._base_url_ = "https://" + hostname 37 self._username_ = username 38 self._password_ = password 39 self._default_prefix_ = "/redfish/v1" 40 41 def __enter__(self): 42 return self 43 44 def __del__(self): 45 del self 46 47 def login(self, *args, **kwargs): 48 r""" 49 Call the corresponding RestClientBase method and return the result. 50 51 Description of argument(s): 52 args/kwargs These are passed directly to the corresponding 53 RestClientBase method. 54 """ 55 56 for arg in args: 57 hostname = self._base_url_.strip("https://") 58 # Class object constructor reinitialized. 59 self.__init__(hostname=hostname, 60 username=arg['username'], 61 password=arg['password']) 62 63 self._robj_ = redfish.redfish_client(base_url=self._base_url_, 64 username=self._username_, 65 password=self._password_, 66 default_prefix=self._default_prefix_) 67 self._robj_.login(auth=redfish.AuthMethod.SESSION) 68 self._session_location_ = self._robj_.get_session_location() 69 70 def get(self, resource_path, *args, **kwargs): 71 r""" 72 Perform a GET request and return response. 73 74 Description of argument(s): 75 resource_path URI resource absolute path (e.g. "/redfish/v1/Systems/1"). 76 args/kwargs These are passed directly to the corresponding 77 RestClientBase method. 78 """ 79 self._rest_response_ = self._robj_.get(resource_path, *args, **kwargs) 80 return self._rest_response_ 81 82 def post(self, resource_path, *args, **kwargs): 83 r""" 84 Perform a POST request. 85 86 Description of argument(s): 87 resource_path URI resource relative path 88 (e.g. "Systems/1/Actions/ComputerSystem.Reset"). 89 args/kwargs These are passed directly to the corresponding 90 RestClientBase method. 91 """ 92 self._rest_response_ = self._robj_.post('/redfish/v1/' + resource_path, 93 *args, **kwargs) 94 return self._rest_response_ 95 96 def patch(self, resource_path, *args, **kwargs): 97 r""" 98 Perform a POST request. 99 100 Description of argument(s): 101 resource_path URI resource relative path 102 args/kwargs These are passed directly to the corresponding 103 RestClientBase method. 104 """ 105 self._rest_response_ = self._robj_.patch('/redfish/v1/' + resource_path, 106 *args, **kwargs) 107 return self._rest_response_ 108 109 def put(self, resource_path, actions, attr_data): 110 r""" 111 Perform a PUT request. 112 113 Description of argument(s): 114 resource_path URI resource relative path. 115 args/kwargs These are passed directly to the corresponding 116 RestClientBase method. 117 """ 118 self._rest_response_ = self._robj_.put('/redfish/v1/' + resource_path, 119 *args, **kwargs) 120 return self._rest_response_ 121 122 def delete(self, resource_path): 123 r""" 124 Perform a DELETE request. 125 126 Description of argument(s): 127 resource_path URI resource absoulute path 128 (e.g. "/redfish/v1/SessionService/Sessions/8d1a9wiiNL"). 129 """ 130 self._rest_response_ = self._robj_.delete(resource_path) 131 return self._rest_response_ 132 133 def logout(self): 134 r""" 135 Logout redfish connection session. 136 """ 137 self._robj_.logout() 138 139 def get_attribute(self, resource_path, attribute): 140 r""" 141 Perform a GET request and return attribute value. 142 143 Description of argument(s): 144 resource_path URI resource absolute path (e.g. "/redfish/v1/Systems/1"). 145 attribute Property name (e.g. "PowerState"). 146 """ 147 148 resp = self._robj_.get(resource_path) 149 150 if attribute in resp.dict: 151 return resp.dict[attribute] 152 153 return None 154 155 def list_request(self, resource_path): 156 r""" 157 Perform a GET list request and return available resource paths. 158 159 Description of argument(s): 160 resource_path URI resource relative path (e.g. "Systems/1"). 161 """ 162 163 global resource_list 164 resource_list = [] 165 166 self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path) 167 168 # Return empty list. 169 if self._rest_response_.status != 200: 170 return resource_list 171 172 self.walk_nested_dict(self._rest_response_.dict) 173 174 if not resource_list: 175 return uri_path 176 177 for resource in resource_list: 178 self._rest_response_ = self._robj_.get(resource) 179 if self._rest_response_.status != 200: 180 continue 181 self.walk_nested_dict(self._rest_response_.dict) 182 183 resource_list.sort() 184 return resource_list 185 186 def enumerate_request(self, resource_path): 187 r""" 188 Perform a GET enumerate request and return available resource paths. 189 190 Description of argument(s): 191 resource_path URI resource relative path (e.g. "Systems/1"). 192 """ 193 194 url_list = self.list_request(resource_path) 195 196 resource_dict = {} 197 198 # Return empty dict. 199 if not url_list: 200 return resource_dict 201 202 for resource in url_list: 203 self._rest_response_ = self._robj_.get(resource) 204 if self._rest_response_.status != 200: 205 continue 206 resource_dict[resource] = self._rest_response_.dict 207 208 return json.dumps(resource_dict, sort_keys=True, 209 indent=4, separators=(',', ': ')) 210 211 def walk_nested_dict(self, data): 212 r""" 213 Parse through the nested dictionary and get the resource id paths. 214 215 Description of argument(s): 216 data Nested dictionary data from response message. 217 """ 218 219 for key, value in data.items(): 220 if isinstance(value, dict): 221 self.walk_nested_dict(value) 222 else: 223 if 'Members' == key: 224 if isinstance(value, list): 225 for index in value: 226 if index['@odata.id'] not in resource_list: 227 resource_list.append(index['@odata.id']) 228 if '@odata.id' == key: 229 if value not in resource_list and not value.endswith('/'): 230 resource_list.append(value) 231