blob: 064b967eb6f59cbfce62f1d6af2d14f473f4ba40 [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
Michael Walsh37e028f2019-05-22 16:16:32 -0500170 def enumerate_request(self, resource_path, return_json=1):
George Keishingf2613b72019-02-13 12:45:59 -0600171 r"""
172 Perform a GET enumerate request and return available resource paths.
173
174 Description of argument(s):
Michael Walsh37e028f2019-05-22 16:16:32 -0500175 resource_path URI resource absolute path (e.g.
176 "/redfish/v1/SessionService/Sessions").
177 return_json Indicates whether the result should be
178 returned as a json string or as a
179 dictionary.
George Keishingf2613b72019-02-13 12:45:59 -0600180 """
181
Michael Walshc86a2f72019-03-19 13:24:37 -0500182 gp.qprint_executing(style=gp.func_line_style_short)
183
Michael Walsh37e028f2019-05-22 16:16:32 -0500184 return_json = int(return_json)
185
Michael Walshc86a2f72019-03-19 13:24:37 -0500186 # Set quiet variable to keep subordinate get() calls quiet.
187 quiet = 1
188
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500189 # Variable to hold enumerated data.
190 self.__result = {}
George Keishingf2613b72019-02-13 12:45:59 -0600191
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500192 # Variable to hold the pending list of resources for which enumeration.
193 # is yet to be obtained.
194 self.__pending_enumeration = set()
George Keishingf2613b72019-02-13 12:45:59 -0600195
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500196 self.__pending_enumeration.add(resource_path)
George Keishingf2613b72019-02-13 12:45:59 -0600197
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500198 # Variable having resources for which enumeration is completed.
199 enumerated_resources = set()
George Keishingf2613b72019-02-13 12:45:59 -0600200
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500201 resources_to_be_enumerated = (resource_path,)
202
203 while resources_to_be_enumerated:
204 for resource in resources_to_be_enumerated:
205 # JsonSchemas data are not required in enumeration.
206 # Example: '/redfish/v1/JsonSchemas/' and sub resources.
207 if 'JsonSchemas' in resource:
208 continue
209
210 self._rest_response_ = \
211 self._redfish_.get(resource, valid_status_codes=[200, 404, 500])
212 # Enumeration is done for available resources ignoring the
213 # ones for which response is not obtained.
214 if self._rest_response_.status != 200:
215 continue
216
217 self.walk_nested_dict(self._rest_response_.dict, url=resource)
218
219 enumerated_resources.update(set(resources_to_be_enumerated))
220 resources_to_be_enumerated = \
221 tuple(self.__pending_enumeration - enumerated_resources)
222
Michael Walsh37e028f2019-05-22 16:16:32 -0500223 if return_json:
224 return json.dumps(self.__result, sort_keys=True,
225 indent=4, separators=(',', ': '))
226 else:
227 return self.__result
George Keishingf2613b72019-02-13 12:45:59 -0600228
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500229 def walk_nested_dict(self, data, url=''):
George Keishingf2613b72019-02-13 12:45:59 -0600230 r"""
231 Parse through the nested dictionary and get the resource id paths.
George Keishingf2613b72019-02-13 12:45:59 -0600232 Description of argument(s):
233 data Nested dictionary data from response message.
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500234 url Resource for which the response is obtained in data.
George Keishingf2613b72019-02-13 12:45:59 -0600235 """
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500236 url = url.rstrip('/')
George Keishingf2613b72019-02-13 12:45:59 -0600237
238 for key, value in data.items():
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500239
240 # Recursion if nested dictionary found.
George Keishingf2613b72019-02-13 12:45:59 -0600241 if isinstance(value, dict):
242 self.walk_nested_dict(value)
243 else:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500244 # Value contains a list of dictionaries having member data.
George Keishingf2613b72019-02-13 12:45:59 -0600245 if 'Members' == key:
246 if isinstance(value, list):
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500247 for memberDict in value:
248 self.__pending_enumeration.add(memberDict['@odata.id'])
George Keishingf2613b72019-02-13 12:45:59 -0600249 if '@odata.id' == key:
Anusha Dathatri62dfb862019-04-23 06:52:16 -0500250 value = value.rstrip('/')
251 # Data for the given url.
252 if value == url:
253 self.__result[url] = data
254 # Data still needs to be looked up,
255 else:
256 self.__pending_enumeration.add(value)
George Keishing7ec45932019-02-27 14:02:16 -0600257
258 def get_key_value_nested_dict(self, data, key):
259 r"""
260 Parse through the nested dictionary and get the searched key value.
261
262 Description of argument(s):
263 data Nested dictionary data from response message.
264 key Search dictionary key element.
265 """
266
267 for k, v in data.items():
268 if isinstance(v, dict):
269 self.get_key_value_nested_dict(v, key)
270
271 if k == key:
272 target_list.append(v)