blob: b9a39a240fbf13c94dd998e6d0488250af2b8d49 [file] [log] [blame]
George Keishingf2613b72019-02-13 12:45:59 -06001#!/usr/bin/env python
2
3r"""
4BMC redfish utility functions.
5"""
6
7import json
8from robot.libraries.BuiltIn import BuiltIn
Michael Walshc86a2f72019-03-19 13:24:37 -05009import gen_print as gp
George Keishingf2613b72019-02-13 12:45:59 -060010
11
12class bmc_redfish_utils(object):
13
14 def __init__(self):
15 r"""
16 Initialize the bmc_redfish_utils object.
17 """
18 # Obtain a reference to the global redfish object.
19 self._redfish_ = BuiltIn().get_library_instance('redfish')
20
George Keishing374e6842019-02-20 08:57:18 -060021 def get_redfish_session_info(self):
22 r"""
23 Returns redfish sessions info dictionary.
24
25 {
26 'key': 'yLXotJnrh5nDhXj5lLiH' ,
27 'location': '/redfish/v1/SessionService/Sessions/nblYY4wlz0'
28 }
29 """
30 session_dict = {
George Keishing97c93942019-03-04 12:45:07 -060031 "key": self._redfish_.get_session_key(),
32 "location": self._redfish_.get_session_location()
George Keishing374e6842019-02-20 08:57:18 -060033 }
34 return session_dict
35
Sandhya Somashekar37122b62019-06-18 06:02:02 -050036 def get_attribute(self, resource_path, attribute, verify=None):
George Keishingf2613b72019-02-13 12:45:59 -060037 r"""
38 Get resource attribute.
39
40 Description of argument(s):
Michael Walshc86a2f72019-03-19 13:24:37 -050041 resource_path URI resource absolute path (e.g.
42 "/redfish/v1/Systems/1").
43 attribute Name of the attribute (e.g. 'PowerState').
George Keishingf2613b72019-02-13 12:45:59 -060044 """
45
46 resp = self._redfish_.get(resource_path)
Sandhya Somashekar37122b62019-06-18 06:02:02 -050047
48 if verify:
49 if resp.dict[attribute] == verify:
50 return resp.dict[attribute]
51 else:
52 raise ValueError("Attribute value is not equal")
53 elif attribute in resp.dict:
George Keishingf2613b72019-02-13 12:45:59 -060054 return resp.dict[attribute]
55
56 return None
57
George Keishingc3c05c22019-02-19 01:04:54 -060058 def get_properties(self, resource_path):
59 r"""
60 Returns dictionary of attributes for the resource.
61
62 Description of argument(s):
Michael Walshc86a2f72019-03-19 13:24:37 -050063 resource_path URI resource absolute path (e.g.
Sandhya Somashekar37122b62019-06-18 06:02:02 -050064 /redfish/v1/Systems/1").
George Keishingc3c05c22019-02-19 01:04:54 -060065 """
66
67 resp = self._redfish_.get(resource_path)
68 return resp.dict
69
George Keishing7ec45932019-02-27 14:02:16 -060070 def get_target_actions(self, resource_path, target_attribute):
71 r"""
72 Returns resource target entry of the searched target attribute.
73
74 Description of argument(s):
75 resource_path URI resource absolute path
George Keishingf8acde92019-04-19 19:46:48 +000076 (e.g. "/redfish/v1/Systems/system").
George Keishing7ec45932019-02-27 14:02:16 -060077 target_attribute Name of the attribute (e.g. 'ComputerSystem.Reset').
78
79 Example:
Anusha Dathatri62dfb862019-04-23 06:52:16 -050080 "Actions": {
81 "#ComputerSystem.Reset": {
82 "ResetType@Redfish.AllowableValues": [
George Keishing7ec45932019-02-27 14:02:16 -060083 "On",
84 "ForceOff",
85 "GracefulRestart",
86 "GracefulShutdown"
Anusha Dathatri62dfb862019-04-23 06:52:16 -050087 ],
88 "target": "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"
89 }
90 }
George Keishing7ec45932019-02-27 14:02:16 -060091 """
92
93 global target_list
94 target_list = []
95
96 resp_dict = self.get_attribute(resource_path, "Actions")
97 if resp_dict is None:
98 return None
99
100 # Recursively search the "target" key in the nested dictionary.
101 # Populate the target_list of target entries.
102 self.get_key_value_nested_dict(resp_dict, "target")
George Keishing7ec45932019-02-27 14:02:16 -0600103 # Return the matching target URL entry.
104 for target in target_list:
105 # target "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"
106 if target_attribute in target:
107 return target
108
109 return None
110
George Keishingdabf38f2019-03-10 09:52:40 -0500111 def get_member_list(self, resource_path):
112 r"""
113 Perform a GET list request and return available members entries.
114
115 Description of argument(s):
116 resource_path URI resource absolute path
George Keishingf8acde92019-04-19 19:46:48 +0000117 (e.g. "/redfish/v1/SessionService/Sessions").
George Keishingdabf38f2019-03-10 09:52:40 -0500118
119 "Members": [
120 {
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500121 "@odata.id": "/redfish/v1/SessionService/Sessions/Z5HummWPZ7"
George Keishingdabf38f2019-03-10 09:52:40 -0500122 }
123 {
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500124 "@odata.id": "/redfish/v1/SessionService/Sessions/46CmQmEL7H"
George Keishingdabf38f2019-03-10 09:52:40 -0500125 }
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500126 ],
George Keishingdabf38f2019-03-10 09:52:40 -0500127 """
128
129 member_list = []
130 resp_list_dict = self.get_attribute(resource_path, "Members")
131 if resp_list_dict is None:
132 return member_list
133
134 for member_id in range(0, len(resp_list_dict)):
135 member_list.append(resp_list_dict[member_id]["@odata.id"])
136
137 return member_list
138
George Keishingf2613b72019-02-13 12:45:59 -0600139 def list_request(self, resource_path):
140 r"""
141 Perform a GET list request and return available resource paths.
George Keishingf2613b72019-02-13 12:45:59 -0600142 Description of argument(s):
143 resource_path URI resource absolute path
George Keishingf8acde92019-04-19 19:46:48 +0000144 (e.g. "/redfish/v1/SessionService/Sessions").
George Keishingf2613b72019-02-13 12:45:59 -0600145 """
Michael Walshc86a2f72019-03-19 13:24:37 -0500146 gp.qprint_executing(style=gp.func_line_style_short)
Michael Walshc86a2f72019-03-19 13:24:37 -0500147 # Set quiet variable to keep subordinate get() calls quiet.
148 quiet = 1
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500149 self.__pending_enumeration = set()
Michael Walshc86a2f72019-03-19 13:24:37 -0500150 self._rest_response_ = \
George Keishingf8acde92019-04-19 19:46:48 +0000151 self._redfish_.get(resource_path,
152 valid_status_codes=[200, 404, 500])
George Keishingf2613b72019-02-13 12:45:59 -0600153
154 # Return empty list.
155 if self._rest_response_.status != 200:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500156 return self.__pending_enumeration
George Keishingf2613b72019-02-13 12:45:59 -0600157 self.walk_nested_dict(self._rest_response_.dict)
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500158 if not self.__pending_enumeration:
159 return resource_path
160 for resource in self.__pending_enumeration.copy():
Michael Walshc86a2f72019-03-19 13:24:37 -0500161 self._rest_response_ = \
George Keishingf8acde92019-04-19 19:46:48 +0000162 self._redfish_.get(resource,
163 valid_status_codes=[200, 404, 500])
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500164
George Keishingf2613b72019-02-13 12:45:59 -0600165 if self._rest_response_.status != 200:
166 continue
167 self.walk_nested_dict(self._rest_response_.dict)
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500168 return list(sorted(self.__pending_enumeration))
George Keishingf2613b72019-02-13 12:45:59 -0600169
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600170 def enumerate_request(self, resource_path, return_json=1,
171 include_dead_resources=False):
George Keishingf2613b72019-02-13 12:45:59 -0600172 r"""
173 Perform a GET enumerate request and return available resource paths.
174
175 Description of argument(s):
Michael Walsh37e028f2019-05-22 16:16:32 -0500176 resource_path URI resource absolute path (e.g.
177 "/redfish/v1/SessionService/Sessions").
178 return_json Indicates whether the result should be
179 returned as a json string or as a
180 dictionary.
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600181 include_dead_resources Check and return a list of dead/broken URI
182 resources.
George Keishingf2613b72019-02-13 12:45:59 -0600183 """
184
Michael Walshc86a2f72019-03-19 13:24:37 -0500185 gp.qprint_executing(style=gp.func_line_style_short)
186
Michael Walsh37e028f2019-05-22 16:16:32 -0500187 return_json = int(return_json)
188
Michael Walshc86a2f72019-03-19 13:24:37 -0500189 # Set quiet variable to keep subordinate get() calls quiet.
190 quiet = 1
191
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500192 # Variable to hold enumerated data.
193 self.__result = {}
George Keishingf2613b72019-02-13 12:45:59 -0600194
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500195 # Variable to hold the pending list of resources for which enumeration.
196 # is yet to be obtained.
197 self.__pending_enumeration = set()
George Keishingf2613b72019-02-13 12:45:59 -0600198
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500199 self.__pending_enumeration.add(resource_path)
George Keishingf2613b72019-02-13 12:45:59 -0600200
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500201 # Variable having resources for which enumeration is completed.
202 enumerated_resources = set()
George Keishingf2613b72019-02-13 12:45:59 -0600203
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600204 if include_dead_resources:
205 dead_resources = {}
206
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500207 resources_to_be_enumerated = (resource_path,)
208
209 while resources_to_be_enumerated:
210 for resource in resources_to_be_enumerated:
Anusha Dathatri6d2d42f2019-11-20 06:17:51 -0600211 # JsonSchemas, SessionService or URLs containing # are not
212 # required in enumeration.
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500213 # Example: '/redfish/v1/JsonSchemas/' and sub resources.
Anusha Dathatricdb77db2019-09-10 08:10:29 -0500214 # '/redfish/v1/SessionService'
Anusha Dathatri6d2d42f2019-11-20 06:17:51 -0600215 # '/redfish/v1/Managers/bmc#/Oem'
216 if ('JsonSchemas' in resource) or ('SessionService' in resource)\
217 or ('#' in resource):
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500218 continue
219
220 self._rest_response_ = \
221 self._redfish_.get(resource, valid_status_codes=[200, 404, 500])
222 # Enumeration is done for available resources ignoring the
223 # ones for which response is not obtained.
224 if self._rest_response_.status != 200:
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600225 if include_dead_resources:
226 try:
227 dead_resources[self._rest_response_.status].append(
228 resource)
229 except KeyError:
230 dead_resources[self._rest_response_.status] = \
231 [resource]
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500232 continue
233
234 self.walk_nested_dict(self._rest_response_.dict, url=resource)
235
236 enumerated_resources.update(set(resources_to_be_enumerated))
237 resources_to_be_enumerated = \
238 tuple(self.__pending_enumeration - enumerated_resources)
239
Michael Walsh37e028f2019-05-22 16:16:32 -0500240 if return_json:
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600241 if include_dead_resources:
242 return json.dumps(self.__result, sort_keys=True,
243 indent=4, separators=(',', ': ')), dead_resources
244 else:
245 return json.dumps(self.__result, sort_keys=True,
246 indent=4, separators=(',', ': '))
Michael Walsh37e028f2019-05-22 16:16:32 -0500247 else:
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600248 if include_dead_resources:
249 return self.__result, dead_resources
250 else:
251 return self.__result
George Keishingf2613b72019-02-13 12:45:59 -0600252
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500253 def walk_nested_dict(self, data, url=''):
George Keishingf2613b72019-02-13 12:45:59 -0600254 r"""
255 Parse through the nested dictionary and get the resource id paths.
George Keishingf2613b72019-02-13 12:45:59 -0600256 Description of argument(s):
257 data Nested dictionary data from response message.
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500258 url Resource for which the response is obtained in data.
George Keishingf2613b72019-02-13 12:45:59 -0600259 """
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500260 url = url.rstrip('/')
George Keishingf2613b72019-02-13 12:45:59 -0600261
262 for key, value in data.items():
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500263
264 # Recursion if nested dictionary found.
George Keishingf2613b72019-02-13 12:45:59 -0600265 if isinstance(value, dict):
266 self.walk_nested_dict(value)
267 else:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500268 # Value contains a list of dictionaries having member data.
George Keishingf2613b72019-02-13 12:45:59 -0600269 if 'Members' == key:
270 if isinstance(value, list):
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500271 for memberDict in value:
272 self.__pending_enumeration.add(memberDict['@odata.id'])
George Keishingf2613b72019-02-13 12:45:59 -0600273 if '@odata.id' == key:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500274 value = value.rstrip('/')
275 # Data for the given url.
276 if value == url:
277 self.__result[url] = data
278 # Data still needs to be looked up,
279 else:
280 self.__pending_enumeration.add(value)
George Keishing7ec45932019-02-27 14:02:16 -0600281
282 def get_key_value_nested_dict(self, data, key):
283 r"""
284 Parse through the nested dictionary and get the searched key value.
285
286 Description of argument(s):
287 data Nested dictionary data from response message.
288 key Search dictionary key element.
289 """
290
291 for k, v in data.items():
292 if isinstance(v, dict):
293 self.get_key_value_nested_dict(v, key)
294
295 if k == key:
296 target_list.append(v)