blob: e2125ca2412c09796aadce78489086f0a44cf934 [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
7from redfish.rest.v1 import HttpClient
8import gen_print as gp
Michael Walsh2477e092019-05-17 15:48:56 -05009import func_args as fa
Tony Lee05aa70b2021-01-28 19:18:27 +080010import requests
11import json
12from robot.libraries.BuiltIn import BuiltIn
13
14
15host = 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
58 ROBOT_LIBRARY_SCOPE = 'TEST SUITE'
59
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)
George Keishingc7c771e2021-03-30 13:27:27 -0500118 timeout = kwargs.pop('timeout', 30)
119 self._timeout = timeout
120 max_retry = kwargs.pop('max_retry', 10)
121 self._max_retry = max_retry
Michael Walsh35139f92019-03-01 15:58:05 -0600122 valid_status_codes = kwargs.pop('valid_status_codes', [200])
123 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):
Tony Lee05aa70b2021-01-28 19:18:27 +0800129
130 if MTLS_ENABLED == 'True':
131 return self.rest_request(self.get_with_mtls, *args, **kwargs)
132 else:
133 return self.rest_request(super(redfish_plus, self).get, *args,
134 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600135
136 def head(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800137
138 if MTLS_ENABLED == 'True':
139 return self.rest_request(self.head_with_mtls, *args, **kwargs)
140 else:
141 return self.rest_request(super(redfish_plus, self).head, *args,
142 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600143
144 def post(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800145
146 if MTLS_ENABLED == 'True':
147 return self.rest_request(self.post_with_mtls, *args, **kwargs)
148 else:
149 return self.rest_request(super(redfish_plus, self).post, *args,
150 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600151
152 def put(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800153
154 if MTLS_ENABLED == 'True':
155 return self.rest_request(self.put_with_mtls, *args, **kwargs)
156 else:
157 return self.rest_request(super(redfish_plus, self).put, *args,
158 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600159
160 def patch(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800161
162 if MTLS_ENABLED == 'True':
163 return self.rest_request(self.patch_with_mtls, *args, **kwargs)
164 else:
165 return self.rest_request(super(redfish_plus, self).patch, *args,
166 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600167
168 def delete(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800169
170 if MTLS_ENABLED == 'True':
171 return self.rest_request(self.delete_with_mtls, *args, **kwargs)
172 else:
173 return self.rest_request(super(redfish_plus, self).delete, *args,
174 **kwargs)
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):
180
181 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
182 response = requests.get(url='https://' + host + args[0],
183 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
184 verify=False,
185 headers={"Cache-Control": "no-cache"})
186
187 response.status = response.status_code
188 if response.status == 200:
189 response.dict = json.loads(response.text)
190
191 return response
192
193 def post_with_mtls(self, *args, **kwargs):
194
195 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
196 body = kwargs.pop('body', {})
197 response = requests.post(url='https://' + host + args[0],
198 json=body,
199 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
200 verify=False,
201 headers={"Content-Type": "application/json"})
202
203 response.status = response.status_code
204
205 return response
206
207 def patch_with_mtls(self, *args, **kwargs):
208
209 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
210 body = kwargs.pop('body', {})
211 response = requests.patch(url='https://' + host + args[0],
212 json=body,
213 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
214 verify=False,
215 headers={"Content-Type": "application/json"})
216
217 response.status = response.status_code
218
219 return response
220
221 def delete_with_mtls(self, *args, **kwargs):
222
223 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
224 response = requests.delete(url='https://' + host + args[0],
225 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
226 verify=False,
227 headers={"Content-Type": "application/json"})
228
229 response.status = response.status_code
230
231 return response
232
233 def put_with_mtls(self, *args, **kwargs):
234
235 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
236 body = kwargs.pop('body', {})
237 response = requests.put(url='https://' + host + args[0],
238 json=body,
239 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
240 verify=False,
241 headers={"Content-Type": "application/json"})
242
243 response.status = response.status_code
244
245 return response
246
247 def head_with_mtls(self, *args, **kwargs):
248
249 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
250 body = kwargs.pop('body', {})
251 response = requests.head(url='https://' + host + args[0],
252 json=body,
253 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
254 verify=False,
255 headers={"Content-Type": "application/json"})
256
257 response.status = response.status_code
258
259 return response