blob: 8364b018d44309bf8a84da7ba9d080791c6b792a [file] [log] [blame]
Michael Walsh35139f92019-03-01 15:58:05 -06001#!/usr/bin/env python
2
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").
Michael Walsh35139f92019-03-01 15:58:05 -0600110 """
111 gp.qprint_executing(stack_frame_ix=3, style=gp.func_line_style_short)
Michael Walsh410b1782019-10-22 15:56:18 -0500112 # Convert python string object definitions to objects (mostly useful for robot callers).
Michael Walsh2477e092019-05-17 15:48:56 -0500113 args = fa.args_to_objects(args)
114 kwargs = fa.args_to_objects(kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600115 valid_status_codes = kwargs.pop('valid_status_codes', [200])
116 response = func(*args, **kwargs)
117 valid_http_status_code(response.status, valid_status_codes)
118 return response
119
120 # Define rest function wrappers.
121 def get(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800122
123 if MTLS_ENABLED == 'True':
124 return self.rest_request(self.get_with_mtls, *args, **kwargs)
125 else:
126 return self.rest_request(super(redfish_plus, self).get, *args,
127 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600128
129 def head(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800130
131 if MTLS_ENABLED == 'True':
132 return self.rest_request(self.head_with_mtls, *args, **kwargs)
133 else:
134 return self.rest_request(super(redfish_plus, self).head, *args,
135 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600136
137 def post(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800138
139 if MTLS_ENABLED == 'True':
140 return self.rest_request(self.post_with_mtls, *args, **kwargs)
141 else:
142 return self.rest_request(super(redfish_plus, self).post, *args,
143 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600144
145 def put(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800146
147 if MTLS_ENABLED == 'True':
148 return self.rest_request(self.put_with_mtls, *args, **kwargs)
149 else:
150 return self.rest_request(super(redfish_plus, self).put, *args,
151 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600152
153 def patch(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800154
155 if MTLS_ENABLED == 'True':
156 return self.rest_request(self.patch_with_mtls, *args, **kwargs)
157 else:
158 return self.rest_request(super(redfish_plus, self).patch, *args,
159 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600160
161 def delete(self, *args, **kwargs):
Tony Lee05aa70b2021-01-28 19:18:27 +0800162
163 if MTLS_ENABLED == 'True':
164 return self.rest_request(self.delete_with_mtls, *args, **kwargs)
165 else:
166 return self.rest_request(super(redfish_plus, self).delete, *args,
167 **kwargs)
Michael Walsh35139f92019-03-01 15:58:05 -0600168
169 def __del__(self):
170 del self
Tony Lee05aa70b2021-01-28 19:18:27 +0800171
172 def get_with_mtls(self, *args, **kwargs):
173
174 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
175 response = requests.get(url='https://' + host + args[0],
176 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
177 verify=False,
178 headers={"Cache-Control": "no-cache"})
179
180 response.status = response.status_code
181 if response.status == 200:
182 response.dict = json.loads(response.text)
183
184 return response
185
186 def post_with_mtls(self, *args, **kwargs):
187
188 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
189 body = kwargs.pop('body', {})
190 response = requests.post(url='https://' + host + args[0],
191 json=body,
192 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
193 verify=False,
194 headers={"Content-Type": "application/json"})
195
196 response.status = response.status_code
197
198 return response
199
200 def patch_with_mtls(self, *args, **kwargs):
201
202 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
203 body = kwargs.pop('body', {})
204 response = requests.patch(url='https://' + host + args[0],
205 json=body,
206 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
207 verify=False,
208 headers={"Content-Type": "application/json"})
209
210 response.status = response.status_code
211
212 return response
213
214 def delete_with_mtls(self, *args, **kwargs):
215
216 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
217 response = requests.delete(url='https://' + host + args[0],
218 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
219 verify=False,
220 headers={"Content-Type": "application/json"})
221
222 response.status = response.status_code
223
224 return response
225
226 def put_with_mtls(self, *args, **kwargs):
227
228 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
229 body = kwargs.pop('body', {})
230 response = requests.put(url='https://' + host + args[0],
231 json=body,
232 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
233 verify=False,
234 headers={"Content-Type": "application/json"})
235
236 response.status = response.status_code
237
238 return response
239
240 def head_with_mtls(self, *args, **kwargs):
241
242 cert_dict = kwargs.pop('certificate', {"certificate_name": VALID_CERT})
243 body = kwargs.pop('body', {})
244 response = requests.head(url='https://' + host + args[0],
245 json=body,
246 cert=CERT_DIR_PATH + '/' + cert_dict['certificate_name'],
247 verify=False,
248 headers={"Content-Type": "application/json"})
249
250 response.status = response.status_code
251
252 return response