1#!/usr/bin/env python
2
3r"""
4BMC redfish utility functions.
5"""
6
7import json
8from robot.libraries.BuiltIn import BuiltIn
9
10
11class bmc_redfish_utils(object):
12
13    def __init__(self):
14        r"""
15        Initialize the bmc_redfish_utils object.
16        """
17        # Obtain a reference to the global redfish object.
18        self._redfish_ = BuiltIn().get_library_instance('redfish')
19
20    def get_redfish_session_info(self):
21        r"""
22        Returns redfish sessions info dictionary.
23
24        {
25            'key': 'yLXotJnrh5nDhXj5lLiH' ,
26            'location': '/redfish/v1/SessionService/Sessions/nblYY4wlz0'
27        }
28        """
29        session_dict = {
30            "key": self._redfish_.get_session_key(),
31            "location": self._redfish_.get_session_location()
32        }
33        return session_dict
34
35    def get_attribute(self, resource_path, attribute):
36        r"""
37        Get resource attribute.
38
39        Description of argument(s):
40        resource_path    URI resource absolute path (e.g. "/redfish/v1/Systems/1").
41        attribute        Name of the attribute (e.g. 'PowerState').
42        """
43
44        resp = self._redfish_.get(resource_path)
45        if attribute in resp.dict:
46            return resp.dict[attribute]
47
48        return None
49
50    def get_properties(self, resource_path):
51        r"""
52        Returns dictionary of attributes for the resource.
53
54        Description of argument(s):
55        resource_path    URI resource absolute path (e.g. "/redfish/v1/Systems/1").
56        """
57
58        resp = self._redfish_.get(resource_path)
59        return resp.dict
60
61    def get_target_actions(self, resource_path, target_attribute):
62        r"""
63        Returns resource target entry of the searched target attribute.
64
65        Description of argument(s):
66        resource_path      URI resource absolute path
67                           (e.g. "/redfish/v1/Systems/system").
68        target_attribute   Name of the attribute (e.g. 'ComputerSystem.Reset').
69
70        Example:
71         "Actions": {
72         "#ComputerSystem.Reset": {
73          "ResetType@Redfish.AllowableValues": [
74            "On",
75            "ForceOff",
76            "GracefulRestart",
77            "GracefulShutdown"
78          ],
79          "target": "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"
80          }
81         }
82        """
83
84        global target_list
85        target_list = []
86
87        resp_dict = self.get_attribute(resource_path, "Actions")
88        if resp_dict is None:
89            return None
90
91        # Recursively search the "target" key in the nested dictionary.
92        # Populate the target_list of target entries.
93        self.get_key_value_nested_dict(resp_dict, "target")
94
95        # Return the matching target URL entry.
96        for target in target_list:
97            # target "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"
98            if target_attribute in target:
99                return target
100
101        return None
102
103    def get_member_list(self, resource_path):
104        r"""
105        Perform a GET list request and return available members entries.
106
107        Description of argument(s):
108        resource_path  URI resource absolute path
109                       (e.g. "/redfish/v1/SessionService/Sessions").
110
111        "Members": [
112            {
113              "@odata.id": "/redfish/v1/SessionService/Sessions/Z5HummWPZ7"
114            }
115            {
116              "@odata.id": "/redfish/v1/SessionService/Sessions/46CmQmEL7H"
117            }
118          ],
119        """
120
121        member_list = []
122        resp_list_dict = self.get_attribute(resource_path, "Members")
123        if resp_list_dict is None:
124            return member_list
125
126        for member_id in range(0, len(resp_list_dict)):
127            member_list.append(resp_list_dict[member_id]["@odata.id"])
128
129        return member_list
130
131    def list_request(self, resource_path):
132        r"""
133        Perform a GET list request and return available resource paths.
134
135        Description of argument(s):
136        resource_path  URI resource absolute path
137                       (e.g. "/redfish/v1/SessionService/Sessions").
138        """
139
140        global resource_list
141        resource_list = []
142        self._rest_response_ = self._redfish_.get(resource_path, valid_status_codes=[200, 404, 500])
143
144        # Return empty list.
145        if self._rest_response_.status != 200:
146            return resource_list
147
148        self.walk_nested_dict(self._rest_response_.dict)
149
150        if not resource_list:
151            return uri_path
152
153        for resource in resource_list:
154            self._rest_response_ = self._redfish_.get(resource, valid_status_codes=[200, 404, 500])
155            if self._rest_response_.status != 200:
156                continue
157            self.walk_nested_dict(self._rest_response_.dict)
158
159        resource_list.sort()
160        return resource_list
161
162    def enumerate_request(self, resource_path):
163        r"""
164        Perform a GET enumerate request and return available resource paths.
165
166        Description of argument(s):
167        resource_path  URI resource absolute path
168                       (e.g. "/redfish/v1/SessionService/Sessions").
169        """
170
171        url_list = self.list_request(resource_path)
172
173        resource_dict = {}
174
175        # Return empty dict.
176        if not url_list:
177            return resource_dict
178
179        for resource in url_list:
180            # JsonSchemas data are not required in enumeration.
181            # Example: '/redfish/v1/JsonSchemas/' and sub resources.
182            if 'JsonSchemas' in resource:
183                continue
184            self._rest_response_ = self._redfish_.get(resource, valid_status_codes=[200, 404, 500])
185            if self._rest_response_.status != 200:
186                continue
187            resource_dict[resource] = self._rest_response_.dict
188
189        return json.dumps(resource_dict, sort_keys=True,
190                          indent=4, separators=(',', ': '))
191
192    def walk_nested_dict(self, data):
193        r"""
194        Parse through the nested dictionary and get the resource id paths.
195
196        Description of argument(s):
197        data    Nested dictionary data from response message.
198        """
199
200        for key, value in data.items():
201            if isinstance(value, dict):
202                self.walk_nested_dict(value)
203            else:
204                if 'Members' == key:
205                    if isinstance(value, list):
206                        for index in value:
207                            if index['@odata.id'] not in resource_list:
208                                resource_list.append(index['@odata.id'])
209                if '@odata.id' == key:
210                    if value not in resource_list and not value.endswith('/'):
211                        resource_list.append(value)
212
213    def get_key_value_nested_dict(self, data, key):
214        r"""
215        Parse through the nested dictionary and get the searched key value.
216
217        Description of argument(s):
218        data    Nested dictionary data from response message.
219        key     Search dictionary key element.
220        """
221
222        for k, v in data.items():
223            if isinstance(v, dict):
224                self.get_key_value_nested_dict(v, key)
225
226            if k == key:
227                target_list.append(v)
228