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 relative path (e.g. "Systems/1"). 72 args/kwargs These are passed directly to the corresponding 73 RestClientBase method. 74 """ 75 self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path, 76 *args, **kwargs) 77 return self._rest_response_ 78 79 def post(self, resource_path, *args, **kwargs): 80 r""" 81 Perform a POST request. 82 83 Description of argument(s): 84 resource_path URI resource relative path 85 (e.g. "Systems/1/Actions/ComputerSystem.Reset"). 86 args/kwargs These are passed directly to the corresponding 87 RestClientBase method. 88 """ 89 self._rest_response_ = self._robj_.post('/redfish/v1/' + resource_path, 90 *args, **kwargs) 91 return self._rest_response_ 92 93 def patch(self, resource_path, *args, **kwargs): 94 r""" 95 Perform a POST request. 96 97 Description of argument(s): 98 resource_path URI resource relative path 99 args/kwargs These are passed directly to the corresponding 100 RestClientBase method. 101 """ 102 self._rest_response_ = self._robj_.patch('/redfish/v1/' + resource_path, 103 *args, **kwargs) 104 return self._rest_response_ 105 106 def put(self, resource_path, actions, attr_data): 107 r""" 108 Perform a PUT request. 109 110 Description of argument(s): 111 resource_path URI resource relative path. 112 args/kwargs These are passed directly to the corresponding 113 RestClientBase method. 114 """ 115 self._rest_response_ = self._robj_.put('/redfish/v1/' + resource_path, 116 *args, **kwargs) 117 return self._rest_response_ 118 119 def delete(self, resource_path): 120 r""" 121 Perform a DELETE request. 122 123 Description of argument(s): 124 resource_path URI resource absoulute path 125 (e.g. "/redfish/v1/SessionService/Sessions/8d1a9wiiNL"). 126 """ 127 self._rest_response_ = self._robj_.delete(resource_path) 128 return self._rest_response_ 129 130 def logout(self): 131 r""" 132 Logout redfish connection session. 133 """ 134 self._robj_.logout() 135 136 def list_request(self, resource_path): 137 r""" 138 Perform a GET list request and return available resource paths. 139 140 Description of argument(s): 141 resource_path URI resource relative path (e.g. "Systems/1"). 142 """ 143 144 global resource_list 145 resource_list = [] 146 147 self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path) 148 149 # Return empty list. 150 if self._rest_response_.status != 200: 151 return resource_list 152 153 self.walk_nested_dict(self._rest_response_.dict) 154 155 if not resource_list: 156 return uri_path 157 158 for resource in resource_list: 159 self._rest_response_ = self._robj_.get(resource) 160 if self._rest_response_.status != 200: 161 continue 162 self.walk_nested_dict(self._rest_response_.dict) 163 164 resource_list.sort() 165 return resource_list 166 167 def enumerate_request(self, resource_path): 168 r""" 169 Perform a GET enumerate request and return available resource paths. 170 171 Description of argument(s): 172 resource_path URI resource relative path (e.g. "Systems/1"). 173 """ 174 175 url_list = self.list_request(resource_path) 176 177 resource_dict = {} 178 179 # Return empty dict. 180 if not url_list: 181 return resource_dict 182 183 for resource in url_list: 184 self._rest_response_ = self._robj_.get(resource) 185 if self._rest_response_.status != 200: 186 continue 187 resource_dict[resource] = self._rest_response_.dict 188 189 return json.dumps(resource_dict, sort_keys=True, 190 indent=4, separators=(',', ': ')) 191 192 def walk_nested_dict(self, data): 193 r""" 194 Parse through the nested dictionary and get the resource id paths. 195 196 Description of argument(s): 197 data Nested dictionary data from response message. 198 """ 199 200 for key, value in data.items(): 201 if isinstance(value, dict): 202 self.walk_nested_dict(value) 203 else: 204 if 'Members' == key: 205 if isinstance(value, list): 206 for index in value: 207 if index['@odata.id'] not in resource_list: 208 resource_list.append(index['@odata.id']) 209 if '@odata.id' == key: 210 if value not in resource_list and not value.endswith('/'): 211 resource_list.append(value) 212