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