#!/usr/bin/env python r""" Using python based redfish library. Refer: https://github.com/DMTF/python-redfish-library """ import redfish import json from robot.libraries.BuiltIn import BuiltIn class HTTPSBadRequestError(Exception): r""" BMC redfish generic raised method for error(s). """ pass class bmc_redfish(object): ROBOT_LIBRARY_SCOPE = "GLOBAL" def __init__(self, hostname, username, password, *args, **kwargs): r""" Establish session connection to host. Description of argument(s): hostname The host name or IP address of the server. username The username to be used to connect to the server. password The password to be used to connect to the server. args/kwargs Additional parms which are passed directly to the redfish_client function. """ self._base_url_ = "https://" + hostname self._username_ = username self._password_ = password self._default_prefix_ = "/redfish/v1" def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback): self._robj_.logout() def login(self, *args, **kwargs): r""" Call the corresponding RestClientBase method and return the result. Description of argument(s): args/kwargs These are passed directly to the corresponding RestClientBase method. """ for arg in args: hostname = self._base_url_.strip("https://") # Class object constructor reinitialized. self.__init__(hostname=hostname, username=arg['username'], password=arg['password']) self._robj_ = redfish.redfish_client(base_url=self._base_url_, username=self._username_, password=self._password_, default_prefix=self._default_prefix_) self._robj_.login(auth=redfish.AuthMethod.SESSION) self._session_location_ = self._robj_.get_session_location() def get(self, resource_path, *args, **kwargs): r""" Perform a GET request and return response. Description of argument(s): resource_path URI resource absolute path (e.g. "/redfish/v1/Systems/1"). args/kwargs These are passed directly to the corresponding RestClientBase method. """ self._rest_response_ = self._robj_.get(resource_path, *args, **kwargs) return self._rest_response_ def post(self, resource_path, *args, **kwargs): r""" Perform a POST request. Description of argument(s): resource_path URI resource relative path (e.g. "Systems/1/Actions/ComputerSystem.Reset"). args/kwargs These are passed directly to the corresponding RestClientBase method. """ self._rest_response_ = self._robj_.post('/redfish/v1/' + resource_path, *args, **kwargs) return self._rest_response_ def patch(self, resource_path, *args, **kwargs): r""" Perform a POST request. Description of argument(s): resource_path URI resource relative path args/kwargs These are passed directly to the corresponding RestClientBase method. """ self._rest_response_ = self._robj_.patch('/redfish/v1/' + resource_path, *args, **kwargs) return self._rest_response_ def put(self, resource_path, actions, attr_data): r""" Perform a PUT request. Description of argument(s): resource_path URI resource relative path. args/kwargs These are passed directly to the corresponding RestClientBase method. """ self._rest_response_ = self._robj_.put('/redfish/v1/' + resource_path, *args, **kwargs) return self._rest_response_ def delete(self, resource_path): r""" Perform a DELETE request. Description of argument(s): resource_path URI resource absoulute path (e.g. "/redfish/v1/SessionService/Sessions/8d1a9wiiNL"). """ self._rest_response_ = self._robj_.delete(resource_path) return self._rest_response_ def logout(self): r""" Logout redfish connection session. """ self._robj_.logout() def get_attribute(self, resource_path, attribute): r""" Perform a GET request and return attribute value. Description of argument(s): resource_path URI resource absolute path (e.g. "/redfish/v1/Systems/1"). attribute Property name (e.g. "PowerState"). """ resp = self._robj_.get(resource_path) if attribute in resp.dict: return resp.dict[attribute] return None def list_request(self, resource_path): r""" Perform a GET list request and return available resource paths. Description of argument(s): resource_path URI resource relative path (e.g. "Systems/1"). """ global resource_list resource_list = [] self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path) # Return empty list. if self._rest_response_.status != 200: return resource_list self.walk_nested_dict(self._rest_response_.dict) if not resource_list: return uri_path for resource in resource_list: self._rest_response_ = self._robj_.get(resource) if self._rest_response_.status != 200: continue self.walk_nested_dict(self._rest_response_.dict) resource_list.sort() return resource_list def enumerate_request(self, resource_path): r""" Perform a GET enumerate request and return available resource paths. Description of argument(s): resource_path URI resource relative path (e.g. "Systems/1"). """ url_list = self.list_request(resource_path) resource_dict = {} # Return empty dict. if not url_list: return resource_dict for resource in url_list: self._rest_response_ = self._robj_.get(resource) if self._rest_response_.status != 200: continue resource_dict[resource] = self._rest_response_.dict return json.dumps(resource_dict, sort_keys=True, indent=4, separators=(',', ': ')) def walk_nested_dict(self, data): r""" Parse through the nested dictionary and get the resource id paths. Description of argument(s): data Nested dictionary data from response message. """ for key, value in data.items(): if isinstance(value, dict): self.walk_nested_dict(value) else: if 'Members' == key: if isinstance(value, list): for index in value: if index['@odata.id'] not in resource_list: resource_list.append(index['@odata.id']) if '@odata.id' == key: if value not in resource_list and not value.endswith('/'): resource_list.append(value)