blob: f492312a82dc3a73f58a598739b19ea343535402 [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 Dathatricdb77db2019-09-10 08:10:29 -0500211 # JsonSchemas and SessionService data are not required in enumeration.
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500212 # Example: '/redfish/v1/JsonSchemas/' and sub resources.
Anusha Dathatricdb77db2019-09-10 08:10:29 -0500213 # '/redfish/v1/SessionService'
214 if ('JsonSchemas' in resource) or ('SessionService' in resource):
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500215 continue
216
217 self._rest_response_ = \
218 self._redfish_.get(resource, valid_status_codes=[200, 404, 500])
219 # Enumeration is done for available resources ignoring the
220 # ones for which response is not obtained.
221 if self._rest_response_.status != 200:
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600222 if include_dead_resources:
223 try:
224 dead_resources[self._rest_response_.status].append(
225 resource)
226 except KeyError:
227 dead_resources[self._rest_response_.status] = \
228 [resource]
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500229 continue
230
231 self.walk_nested_dict(self._rest_response_.dict, url=resource)
232
233 enumerated_resources.update(set(resources_to_be_enumerated))
234 resources_to_be_enumerated = \
235 tuple(self.__pending_enumeration - enumerated_resources)
236
Michael Walsh37e028f2019-05-22 16:16:32 -0500237 if return_json:
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600238 if include_dead_resources:
239 return json.dumps(self.__result, sort_keys=True,
240 indent=4, separators=(',', ': ')), dead_resources
241 else:
242 return json.dumps(self.__result, sort_keys=True,
243 indent=4, separators=(',', ': '))
Michael Walsh37e028f2019-05-22 16:16:32 -0500244 else:
Anusha Dathatri3e7930d2019-11-06 03:55:35 -0600245 if include_dead_resources:
246 return self.__result, dead_resources
247 else:
248 return self.__result
George Keishingf2613b72019-02-13 12:45:59 -0600249
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500250 def walk_nested_dict(self, data, url=''):
George Keishingf2613b72019-02-13 12:45:59 -0600251 r"""
252 Parse through the nested dictionary and get the resource id paths.
George Keishingf2613b72019-02-13 12:45:59 -0600253 Description of argument(s):
254 data Nested dictionary data from response message.
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500255 url Resource for which the response is obtained in data.
George Keishingf2613b72019-02-13 12:45:59 -0600256 """
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500257 url = url.rstrip('/')
George Keishingf2613b72019-02-13 12:45:59 -0600258
259 for key, value in data.items():
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500260
261 # Recursion if nested dictionary found.
George Keishingf2613b72019-02-13 12:45:59 -0600262 if isinstance(value, dict):
263 self.walk_nested_dict(value)
264 else:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500265 # Value contains a list of dictionaries having member data.
George Keishingf2613b72019-02-13 12:45:59 -0600266 if 'Members' == key:
267 if isinstance(value, list):
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500268 for memberDict in value:
269 self.__pending_enumeration.add(memberDict['@odata.id'])
George Keishingf2613b72019-02-13 12:45:59 -0600270 if '@odata.id' == key:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500271 value = value.rstrip('/')
272 # Data for the given url.
273 if value == url:
274 self.__result[url] = data
275 # Data still needs to be looked up,
276 else:
277 self.__pending_enumeration.add(value)
George Keishing7ec45932019-02-27 14:02:16 -0600278
279 def get_key_value_nested_dict(self, data, key):
280 r"""
281 Parse through the nested dictionary and get the searched key value.
282
283 Description of argument(s):
284 data Nested dictionary data from response message.
285 key Search dictionary key element.
286 """
287
288 for k, v in data.items():
289 if isinstance(v, dict):
290 self.get_key_value_nested_dict(v, key)
291
292 if k == key:
293 target_list.append(v)