blob: 3630cc191ea99c88125b36fab8751d956c5172f2 [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
ganesanb4d430282023-04-27 14:33:23 +000098 response = bmc_redfish.get("/redfish/v1/Managers/${MANAGER_ID}/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
ganesanb4d430282023-04-27 14:33:23 +0000106 ${response}= BMC_Redfish.Get /redfish/v1/Managers/${MANAGER_ID}/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])
George Keishing7049fba2025-01-16 10:44:46 +0530123
124 try:
125 response = func(*args, **kwargs)
126 except Exception as e:
127 error_response = type(e).__name__ + " from redfish_plus class"
128 BuiltIn().log_to_console(error_response)
129 return
130
Michael Walsh35139f92019-03-01 15:58:05 -0600131 valid_http_status_code(response.status, valid_status_codes)
132 return response
133
134 # Define rest function wrappers.
135 def get(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600136 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800137 return self.rest_request(self.get_with_mtls, *args, **kwargs)
138 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600139 return self.rest_request(
140 super(redfish_plus, self).get, *args, **kwargs
141 )
Michael Walsh35139f92019-03-01 15:58:05 -0600142
143 def head(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600144 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800145 return self.rest_request(self.head_with_mtls, *args, **kwargs)
146 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600147 return self.rest_request(
148 super(redfish_plus, self).head, *args, **kwargs
149 )
Michael Walsh35139f92019-03-01 15:58:05 -0600150
151 def post(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600152 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800153 return self.rest_request(self.post_with_mtls, *args, **kwargs)
154 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600155 return self.rest_request(
156 super(redfish_plus, self).post, *args, **kwargs
157 )
Michael Walsh35139f92019-03-01 15:58:05 -0600158
159 def put(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600160 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800161 return self.rest_request(self.put_with_mtls, *args, **kwargs)
162 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600163 return self.rest_request(
164 super(redfish_plus, self).put, *args, **kwargs
165 )
Michael Walsh35139f92019-03-01 15:58:05 -0600166
167 def patch(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600168 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800169 return self.rest_request(self.patch_with_mtls, *args, **kwargs)
170 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600171 return self.rest_request(
172 super(redfish_plus, self).patch, *args, **kwargs
173 )
Michael Walsh35139f92019-03-01 15:58:05 -0600174
175 def delete(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600176 if MTLS_ENABLED == "True":
Tony Lee05aa70b2021-01-28 19:18:27 +0800177 return self.rest_request(self.delete_with_mtls, *args, **kwargs)
178 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600179 return self.rest_request(
180 super(redfish_plus, self).delete, *args, **kwargs
181 )
Michael Walsh35139f92019-03-01 15:58:05 -0600182
183 def __del__(self):
184 del self
Tony Lee05aa70b2021-01-28 19:18:27 +0800185
186 def get_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600187 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
188 response = requests.get(
189 url="https://" + host + args[0],
190 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
191 verify=False,
192 headers={"Cache-Control": "no-cache"},
193 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800194
195 response.status = response.status_code
196 if response.status == 200:
197 response.dict = json.loads(response.text)
198
199 return response
200
201 def post_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600202 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
203 body = kwargs.pop("body", {})
204 response = requests.post(
205 url="https://" + host + args[0],
206 json=body,
207 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
208 verify=False,
209 headers={"Content-Type": "application/json"},
210 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800211
212 response.status = response.status_code
213
214 return response
215
216 def patch_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600217 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
218 body = kwargs.pop("body", {})
219 response = requests.patch(
220 url="https://" + host + args[0],
221 json=body,
222 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
223 verify=False,
224 headers={"Content-Type": "application/json"},
225 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800226
227 response.status = response.status_code
228
229 return response
230
231 def delete_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600232 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
233 response = requests.delete(
234 url="https://" + host + args[0],
235 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
236 verify=False,
237 headers={"Content-Type": "application/json"},
238 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800239
240 response.status = response.status_code
241
242 return response
243
244 def put_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600245 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
246 body = kwargs.pop("body", {})
247 response = requests.put(
248 url="https://" + host + args[0],
249 json=body,
250 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
251 verify=False,
252 headers={"Content-Type": "application/json"},
253 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800254
255 response.status = response.status_code
256
257 return response
258
259 def head_with_mtls(self, *args, **kwargs):
Patrick Williams20f38712022-12-08 06:18:26 -0600260 cert_dict = kwargs.pop("certificate", {"certificate_name": VALID_CERT})
261 body = kwargs.pop("body", {})
262 response = requests.head(
263 url="https://" + host + args[0],
264 json=body,
265 cert=CERT_DIR_PATH + "/" + cert_dict["certificate_name"],
266 verify=False,
267 headers={"Content-Type": "application/json"},
268 )
Tony Lee05aa70b2021-01-28 19:18:27 +0800269
270 response.status = response.status_code
271
272 return response