| #!/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 = "TEST SUITE" |
| ROBOT_EXIT_ON_FAILURE = True |
| |
| 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 __del__(self): |
| del self |
| |
| 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) |