blob: 2cc7531c4d8fbf26eb67dd6caafec14b5a97f9ac [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Michael Walsh35139f92019-03-01 15:58:05 -06002
3r"""
4See redfish_plus class prolog below for details.
5"""
6
George Keishinge635ddc2022-12-08 07:38:02 -06007import json
Tony Lee05aa70b2021-01-28 19:18:27 +08008
Patrick Williams20f38712022-12-08 06:18:26 -06009import func_args as fa
10import gen_print as gp
11import requests
12from redfish.rest.v1 import HttpClient
13from robot.libraries.BuiltIn import BuiltIn
George Keishinge635ddc2022-12-08 07:38:02 -060014
Tony Lee05aa70b2021-01-28 19:18:27 +080015host = BuiltIn().get_variable_value("${OPENBMC_HOST}")
16MTLS_ENABLED = BuiltIn().get_variable_value("${MTLS_ENABLED}")
17CERT_DIR_PATH = BuiltIn().get_variable_value("${CERT_DIR_PATH}")
18VALID_CERT = BuiltIn().get_variable_value("${VALID_CERT}")
Michael Walsh35139f92019-03-01 15:58:05 -060019
20
21def valid_http_status_code(status, valid_status_codes):
22 r"""
23 Raise exception if status is not found in the valid_status_codes list.
24
25 Description of argument(s):
26 status An HTTP status code (e.g. 200, 400, etc.).
Michael Walsh410b1782019-10-22 15:56:18 -050027 valid_status_codes A list of status codes that the caller considers acceptable. If this is
28 a null list, then any status code is considered acceptable. Note that
29 for the convenience of the caller, valid_status_codes may be either a
30 python list or a string which can be evaluated to become a python list
31 (e.g. "[200]").
Michael Walsh35139f92019-03-01 15:58:05 -060032 """
33
34 if type(valid_status_codes) is not list:
35 valid_status_codes = eval(valid_status_codes)
36 if len(valid_status_codes) == 0:
37 return
38 if status in valid_status_codes:
39 return
40
41 message = "The HTTP status code was not valid:\n"
42 message += gp.sprint_vars(status, valid_status_codes)
43 raise ValueError(message)
44
45
46class redfish_plus(HttpClient):
47 r"""
Michael Walsh410b1782019-10-22 15:56:18 -050048 redfish_plus is a wrapper for redfish rest that provides the following benefits vs. using redfish
49 directly:
Michael Walsh35139f92019-03-01 15:58:05 -060050
51 For rest_request functions (e.g. get, put, post, etc.):
52 - Function-call logging to stdout.
Michael Walsh410b1782019-10-22 15:56:18 -050053 - Automatic valid_status_codes processing (i.e. an exception will be raised if the rest response
54 status code is not as expected.
Michael Walsh35139f92019-03-01 15:58:05 -060055 - Easily used from robot programs.
56 """
57
Patrick Williams20f38712022-12-08 06:18:26 -060058 ROBOT_LIBRARY_SCOPE = "TEST SUITE"
Michael Walsh35139f92019-03-01 15:58:05 -060059
60 def rest_request(self, func, *args, **kwargs):
61 r"""
62 Perform redfish rest request and return response.
63
64 This function provides the following additional functionality.
Michael Walsh410b1782019-10-22 15:56:18 -050065 - The calling function's call line is logged to standard out (provided that global variable "quiet"
66 is not set).
Michael Walsh35139f92019-03-01 15:58:05 -060067 - The caller may include a valid_status_codes argument.
Michael Walsh410b1782019-10-22 15:56:18 -050068 - Callers may include inline python code strings to define arguments. This predominantly benefits
69 robot callers.
Michael Walsh2477e092019-05-17 15:48:56 -050070
71 For example, instead of calling like this:
72 ${data}= Create Dictionary HostName=${hostname}
73 Redfish.patch ${REDFISH_NW_PROTOCOL_URI} body=&{data}
74
75 Callers may do this:
76
77 Redfish.patch ${REDFISH_NW_PROTOCOL_URI}
78 ... body=[('HostName', '${hostname}')]
Michael Walsh35139f92019-03-01 15:58:05 -060079
80 Description of argument(s):
Michael Walsh410b1782019-10-22 15:56:18 -050081 func A reference to the parent class function which is to be called (e.g. get,
82 put, etc.).
83 args This is passed directly to the function referenced by the func argument
84 (see the documentation for the corresponding redfish HttpClient method
85 for details).
86 kwargs This is passed directly to the function referenced by the func argument
87 (see the documentation for the corresponding redfish HttpClient method
88 for details) with the following exception: If kwargs contains a
89 valid_status_codes key, it will be removed from kwargs and processed by
90 this function. This allows the caller to indicate what rest status codes
91 are acceptable. The default value is [200]. See the
92 valid_http_status_code function above for details.
Michael Walsh35139f92019-03-01 15:58:05 -060093
94 Example uses:
95
96 From a python program:
97
Michael Walsh410b1782019-10-22 15:56:18 -050098 response = bmc_redfish.get("/redfish/v1/Managers/bmc/EthernetInterfaces", [200, 201])
Michael Walsh35139f92019-03-01 15:58:05 -060099
Michael Walsh410b1782019-10-22 15:56:18 -0500100 If this call to the get method generates a response.status equal to anything other than 200 or 201,
101 an exception will be raised.
Michael Walsh35139f92019-03-01 15:58:05 -0600102
103 From a robot program:
104
105 BMC_Redfish.logout
Michael Walsh410b1782019-10-22 15:56:18 -0500106 ${response}= BMC_Redfish.Get /redfish/v1/Managers/bmc/EthernetInterfaces valid_status_codes=[401]
Michael Walsh35139f92019-03-01 15:58:05 -0600107
Michael Walsh410b1782019-10-22 15:56:18 -0500108 As part of a robot test, the programmer has logged out to verify that the get request will generate a
109 status code of 401 (i.e. "Unauthorized").
George Keishingc7c771e2021-03-30 13:27:27 -0500110
111 Timeout for GET/POST/PATCH/DELETE operations. By default 30 seconds, else user defined value.
112 Similarly, Max retry by default 10 attempt for the operation, else user defined value.
Michael Walsh35139f92019-03-01 15:58:05 -0600113 """
114 gp.qprint_executing(stack_frame_ix=3, style=gp.func_line_style_short)
Michael Walsh410b1782019-10-22 15:56:18 -0500115 # Convert python string object definitions to objects (mostly useful for robot callers).
Michael Walsh2477e092019-05-17 15:48:56 -0500116 args = fa.args_to_objects(args)
117 kwargs = fa.args_to_objects(kwargs)
Patrick Williams20f38712022-12-08 06:18:26 -0600118 timeout = kwargs.pop("timeout", 30)
George Keishingc7c771e2021-03-30 13:27:27 -0500119 self._timeout = timeout
Patrick Williams20f38712022-12-08 06:18:26 -0600120 max_retry = kwargs.pop("max_retry", 10)
George Keishingc7c771e2021-03-30 13:27:27 -0500121 self._max_retry = max_retry
Patrick Williams20f38712022-12-08 06:18:26 -0600122 valid_status_codes = kwargs.pop("valid_status_codes", [200])
Michael Walsh35139f92019-03-01 15:58:05 -0600123 response = func(*args, **kwargs)
124 valid_http_status_code(response.status, valid_status_codes)
125 return response
126
127 # Define rest function wrappers.
128 def get(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600129 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800130 return self.rest_request(self.get_with_mtls, *args, **kwargs)
131 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600132 return self.rest_request(
133 super(redfish_plus, self).get, *args, **kwargs
134 )
Michael Walsh35139f92019-03-01 15:58:05 -0600135
136 def head(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600137 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800138 return self.rest_request(self.head_with_mtls, *args, **kwargs)
139 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600140 return self.rest_request(
141 super(redfish_plus, self).head, *args, **kwargs
142 )
Michael Walsh35139f92019-03-01 15:58:05 -0600143
144 def post(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600145 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800146 return self.rest_request(self.post_with_mtls, *args, **kwargs)
147 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600148 return self.rest_request(
149 super(redfish_plus, self).post, *args, **kwargs
150 )
Michael Walsh35139f92019-03-01 15:58:05 -0600151
152 def put(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600153 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800154 return self.rest_request(self.put_with_mtls, *args, **kwargs)
155 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600156 return self.rest_request(
157 super(redfish_plus, self).put, *args, **kwargs
158 )
Michael Walsh35139f92019-03-01 15:58:05 -0600159
160 def patch(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600161 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800162 return self.rest_request(self.patch_with_mtls, *args, **kwargs)
163 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600164 return self.rest_request(
165 super(redfish_plus, self).patch, *args, **kwargs
166 )
Michael Walsh35139f92019-03-01 15:58:05 -0600167
168 def delete(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600169 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800170 return self.rest_request(self.delete_with_mtls, *args, **kwargs)
171 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600172 return self.rest_request(
173 super(redfish_plus, self).delete, *args, **kwargs
174 )
Michael Walsh35139f92019-03-01 15:58:05 -0600175
176 def __del__(self):
177 del self
Tony Lee05aa70b2021-01-28 19:18:27 +0800178
179 def get_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600180 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
181 response = requests.get(
182 url="https://" + host + args[0],
183 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
184 verify=False,
185 headers={"Cache-Control": "no-cache"},
186 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800187
188 response.status = response.status_code
189 if response.status == 200:
190 response.dict = json.loads(response.text)
191
192 return response
193
194 def post_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600195 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
196 body = kwargs.pop("body", {})
197 response = requests.post(
198 url="https://" + host + args[0],
199 json=body,
200 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
201 verify=False,
202 headers={"Content-Type": "application/json"},
203 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800204
205 response.status = response.status_code
206
207 return response
208
209 def patch_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600210 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
211 body = kwargs.pop("body", {})
212 response = requests.patch(
213 url="https://" + host + args[0],
214 json=body,
215 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
216 verify=False,
217 headers={"Content-Type": "application/json"},
218 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800219
220 response.status = response.status_code
221
222 return response
223
224 def delete_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600225 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
226 response = requests.delete(
227 url="https://" + host + args[0],
228 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
229 verify=False,
230 headers={"Content-Type": "application/json"},
231 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800232
233 response.status = response.status_code
234
235 return response
236
237 def put_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600238 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
239 body = kwargs.pop("body", {})
240 response = requests.put(
241 url="https://" + host + args[0],
242 json=body,
243 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
244 verify=False,
245 headers={"Content-Type": "application/json"},
246 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800247
248 response.status = response.status_code
249
250 return response
251
252 def head_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600253 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
254 body = kwargs.pop("body", {})
255 response = requests.head(
256 url="https://" + host + args[0],
257 json=body,
258 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
259 verify=False,
260 headers={"Content-Type": "application/json"},
261 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800262
263 response.status = response.status_code
264
265 return response