| #!/usr/bin/env python | 
 |  | 
 | r""" | 
 | See redfish_plus class prolog below for details. | 
 | """ | 
 |  | 
 | from redfish.rest.v1 import HttpClient | 
 | import gen_print as gp | 
 | import func_args as fa | 
 | import requests | 
 | import json | 
 | from robot.libraries.BuiltIn import BuiltIn | 
 |  | 
 |  | 
 | host = BuiltIn().get_variable_value("${OPENBMC_HOST}") | 
 | MTLS_ENABLED = BuiltIn().get_variable_value("${MTLS_ENABLED}") | 
 | CERT_DIR_PATH = BuiltIn().get_variable_value("${CERT_DIR_PATH}") | 
 | VALID_CERT = BuiltIn().get_variable_value("${VALID_CERT}") | 
 |  | 
 |  | 
 | def valid_http_status_code(status, valid_status_codes): | 
 |     r""" | 
 |     Raise exception if status is not found in the valid_status_codes list. | 
 |  | 
 |     Description of argument(s): | 
 |     status                          An HTTP status code (e.g. 200, 400, etc.). | 
 |     valid_status_codes              A list of status codes that the caller considers acceptable.  If this is | 
 |                                     a null list, then any status code is considered acceptable.  Note that | 
 |                                     for the convenience of the caller, valid_status_codes may be either a | 
 |                                     python list or a string which can be evaluated to become a python list | 
 |                                     (e.g. "[200]"). | 
 |     """ | 
 |  | 
 |     if type(valid_status_codes) is not list: | 
 |         valid_status_codes = eval(valid_status_codes) | 
 |     if len(valid_status_codes) == 0: | 
 |         return | 
 |     if status in valid_status_codes: | 
 |         return | 
 |  | 
 |     message = "The HTTP status code was not valid:\n" | 
 |     message += gp.sprint_vars(status, valid_status_codes) | 
 |     raise ValueError(message) | 
 |  | 
 |  | 
 | class redfish_plus(HttpClient): | 
 |     r""" | 
 |     redfish_plus is a wrapper for redfish rest that provides the following benefits vs. using redfish | 
 |     directly: | 
 |  | 
 |     For rest_request functions (e.g. get, put, post, etc.): | 
 |         - Function-call logging to stdout. | 
 |         - Automatic valid_status_codes processing (i.e. an exception will be raised if the rest response | 
 |           status code is not as expected. | 
 |         - Easily used from robot programs. | 
 |     """ | 
 |  | 
 |     ROBOT_LIBRARY_SCOPE = 'TEST SUITE' | 
 |  | 
 |     def rest_request(self, func, *args, **kwargs): | 
 |         r""" | 
 |         Perform redfish rest request and return response. | 
 |  | 
 |         This function provides the following additional functionality. | 
 |         - The calling function's call line is logged to standard out (provided that global variable "quiet" | 
 |           is not set). | 
 |         - The caller may include a valid_status_codes argument. | 
 |         - Callers may include inline python code strings to define arguments.  This predominantly benefits | 
 |           robot callers. | 
 |  | 
 |           For example, instead of calling like this: | 
 |             ${data}=  Create Dictionary  HostName=${hostname} | 
 |             Redfish.patch  ${REDFISH_NW_PROTOCOL_URI}  body=&{data} | 
 |  | 
 |           Callers may do this: | 
 |  | 
 |             Redfish.patch  ${REDFISH_NW_PROTOCOL_URI} | 
 |             ...  body=[('HostName', '${hostname}')] | 
 |  | 
 |         Description of argument(s): | 
 |         func                        A reference to the parent class function which is to be called (e.g. get, | 
 |                                     put, etc.). | 
 |         args                        This is passed directly to the function referenced by the func argument | 
 |                                     (see the documentation for the corresponding redfish HttpClient method | 
 |                                     for details). | 
 |         kwargs                      This is passed directly to the function referenced by the func argument | 
 |                                     (see the documentation for the corresponding redfish HttpClient method | 
 |                                     for details) with the following exception:  If kwargs contains a | 
 |                                     valid_status_codes key, it will be removed from kwargs and processed by | 
 |                                     this function.  This allows the caller to indicate what rest status codes | 
 |                                     are acceptable.  The default value is [200].  See the | 
 |                                     valid_http_status_code function above for details. | 
 |  | 
 |         Example uses: | 
 |  | 
 |         From a python program: | 
 |  | 
 |         response = bmc_redfish.get("/redfish/v1/Managers/bmc/EthernetInterfaces", [200, 201]) | 
 |  | 
 |         If this call to the get method generates a response.status equal to anything other than 200 or 201, | 
 |         an exception will be raised. | 
 |  | 
 |         From a robot program: | 
 |  | 
 |         BMC_Redfish.logout | 
 |         ${response}=  BMC_Redfish.Get  /redfish/v1/Managers/bmc/EthernetInterfaces  valid_status_codes=[401] | 
 |  | 
 |         As part of a robot test, the programmer has logged out to verify that the get request will generate a | 
 |         status code of 401 (i.e. "Unauthorized"). | 
 |  | 
 |         Timeout for GET/POST/PATCH/DELETE operations. By default 30 seconds, else user defined value. | 
 |         Similarly, Max retry by default 10 attempt for the operation, else user defined value. | 
 |         """ | 
 |         gp.qprint_executing(stack_frame_ix=3, style=gp.func_line_style_short) | 
 |         # Convert python string object definitions to objects (mostly useful for robot callers). | 
 |         args = fa.args_to_objects(args) | 
 |         kwargs = fa.args_to_objects(kwargs) | 
 |         timeout = kwargs.pop('timeout', 30) | 
 |         self._timeout = timeout | 
 |         max_retry = kwargs.pop('max_retry', 10) | 
 |         self._max_retry = max_retry | 
 |         valid_status_codes = kwargs.pop('valid_status_codes', [200]) | 
 |         response = func(*args, **kwargs) | 
 |         valid_http_status_code(response.status, valid_status_codes) | 
 |         return response | 
 |  | 
 |     # Define rest function wrappers. | 
 |     def get(self, *args, **kwargs): | 
 |  | 
 |         if MTLS_ENABLED == 'True': | 
 |             return self.rest_request(self.get_with_mtls, *args, **kwargs) | 
 |         else: | 
 |             return self.rest_request(super(redfish_plus, self).get, *args, | 
 |                                      **kwargs) | 
 |  | 
 |     def head(self, *args, **kwargs): | 
 |  | 
 |         if MTLS_ENABLED == 'True': | 
 |             return self.rest_request(self.head_with_mtls, *args, **kwargs) | 
 |         else: | 
 |             return self.rest_request(super(redfish_plus, self).head, *args, | 
 |                                      **kwargs) | 
 |  | 
 |     def post(self, *args, **kwargs): | 
 |  | 
 |         if MTLS_ENABLED == 'True': | 
 |             return self.rest_request(self.post_with_mtls, *args, **kwargs) | 
 |         else: | 
 |             return self.rest_request(super(redfish_plus, self).post, *args, | 
 |                                      **kwargs) | 
 |  | 
 |     def put(self, *args, **kwargs): | 
 |  | 
 |         if MTLS_ENABLED == 'True': | 
 |             return self.rest_request(self.put_with_mtls, *args, **kwargs) | 
 |         else: | 
 |             return self.rest_request(super(redfish_plus, self).put, *args, | 
 |                                      **kwargs) | 
 |  | 
 |     def patch(self, *args, **kwargs): | 
 |  | 
 |         if MTLS_ENABLED == 'True': | 
 |             return self.rest_request(self.patch_with_mtls, *args, **kwargs) | 
 |         else: | 
 |             return self.rest_request(super(redfish_plus, self).patch, *args, | 
 |                                      **kwargs) | 
 |  | 
 |     def delete(self, *args, **kwargs): | 
 |  | 
 |         if MTLS_ENABLED == 'True': | 
 |             return self.rest_request(self.delete_with_mtls, *args, **kwargs) | 
 |         else: | 
 |             return self.rest_request(super(redfish_plus, self).delete, *args, | 
 |                                      **kwargs) | 
 |  | 
 |     def __del__(self): | 
 |         del self | 
 |  | 
 |     def get_with_mtls(self, *args, **kwargs): | 
 |  | 
 |         cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT}) | 
 |         response = requests.get(url='https://' + host + args[0], | 
 |                                 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'], | 
 |                                 verify=False, | 
 |                                 headers={"Cache-Control": "no-cache"}) | 
 |  | 
 |         response.status = response.status_code | 
 |         if response.status == 200: | 
 |             response.dict = json.loads(response.text) | 
 |  | 
 |         return response | 
 |  | 
 |     def post_with_mtls(self, *args, **kwargs): | 
 |  | 
 |         cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT}) | 
 |         body = kwargs.pop('body', {}) | 
 |         response = requests.post(url='https://' + host + args[0], | 
 |                                  json=body, | 
 |                                  cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'], | 
 |                                  verify=False, | 
 |                                  headers={"Content-Type": "application/json"}) | 
 |  | 
 |         response.status = response.status_code | 
 |  | 
 |         return response | 
 |  | 
 |     def patch_with_mtls(self, *args, **kwargs): | 
 |  | 
 |         cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT}) | 
 |         body = kwargs.pop('body', {}) | 
 |         response = requests.patch(url='https://' + host + args[0], | 
 |                                   json=body, | 
 |                                   cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'], | 
 |                                   verify=False, | 
 |                                   headers={"Content-Type": "application/json"}) | 
 |  | 
 |         response.status = response.status_code | 
 |  | 
 |         return response | 
 |  | 
 |     def delete_with_mtls(self, *args, **kwargs): | 
 |  | 
 |         cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT}) | 
 |         response = requests.delete(url='https://' + host + args[0], | 
 |                                    cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'], | 
 |                                    verify=False, | 
 |                                    headers={"Content-Type": "application/json"}) | 
 |  | 
 |         response.status = response.status_code | 
 |  | 
 |         return response | 
 |  | 
 |     def put_with_mtls(self, *args, **kwargs): | 
 |  | 
 |         cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT}) | 
 |         body = kwargs.pop('body', {}) | 
 |         response = requests.put(url='https://' + host + args[0], | 
 |                                 json=body, | 
 |                                 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'], | 
 |                                 verify=False, | 
 |                                 headers={"Content-Type": "application/json"}) | 
 |  | 
 |         response.status = response.status_code | 
 |  | 
 |         return response | 
 |  | 
 |     def head_with_mtls(self, *args, **kwargs): | 
 |  | 
 |         cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT}) | 
 |         body = kwargs.pop('body', {}) | 
 |         response = requests.head(url='https://' + host + args[0], | 
 |                                  json=body, | 
 |                                  cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'], | 
 |                                  verify=False, | 
 |                                  headers={"Content-Type": "application/json"}) | 
 |  | 
 |         response.status = response.status_code | 
 |  | 
 |         return response |