xref: /openbmc/openbmc-test-automation/lib/bmc_redfish.py (revision b89977a44ae38492550ac82a191db5bbf7f3ad29)
1#!/usr/bin/env python
2
3r"""
4Using python based redfish library.
5Refer: https://github.com/DMTF/python-redfish-library
6"""
7
8import redfish
9import json
10from robot.libraries.BuiltIn import BuiltIn
11
12
13class HTTPSBadRequestError(Exception):
14    r"""
15    BMC redfish generic raised method for error(s).
16    """
17    pass
18
19
20class bmc_redfish(object):
21
22    ROBOT_LIBRARY_SCOPE = "GLOBAL"
23
24    def __init__(self, hostname, username, password, *args, **kwargs):
25        r"""
26        Establish session connection to host.
27
28        Description of argument(s):
29        hostname       The host name or IP address of the server.
30        username       The username to be used to connect to the server.
31        password       The password to be used to connect to the server.
32        args/kwargs    Additional parms which are passed directly
33                       to the redfish_client function.
34        """
35
36        self._base_url_ = "https://" + hostname
37        self._username_ = username
38        self._password_ = password
39        self._default_prefix_ = "/redfish/v1"
40        self._robj_ = \
41            redfish.redfish_client(base_url=self._base_url_,
42                                   username=self._username_,
43                                   password=self._password_,
44                                   default_prefix=self._default_prefix_,
45                                   *args, **kwargs)
46        self._robj_.login(auth=redfish.AuthMethod.SESSION)
47        self._session_location_ = self._robj_.get_session_location()
48
49    def __enter__(self):
50        return self
51
52    def __exit__(self, exception_type, exception_value, traceback):
53        self._robj_.logout()
54
55    def login(self, *args, **kwargs):
56        r"""
57        Call the corresponding RestClientBase method and return the result.
58
59        Description of argument(s):
60        args/kwargs     These are passed directly to the corresponding
61                        RestClientBase method.
62        """
63        self._robj_.__init__(self._base_url_, self._username_, self._password_)
64        self._robj_.login(auth=redfish.AuthMethod.SESSION)
65
66    def get(self, resource_path, *args, **kwargs):
67        r"""
68        Perform a GET request and return response.
69
70        Description of argument(s):
71        resource_path    URI resource absolute path (e.g. "/redfish/v1/Systems/1").
72        args/kwargs      These are passed directly to the corresponding
73                         RestClientBase method.
74        """
75        self._rest_response_ = self._robj_.get(resource_path, *args, **kwargs)
76        return self._rest_response_
77
78    def post(self, resource_path, *args, **kwargs):
79        r"""
80        Perform a POST request.
81
82        Description of argument(s):
83        resource_path    URI resource relative path
84                         (e.g. "Systems/1/Actions/ComputerSystem.Reset").
85        args/kwargs      These are passed directly to the corresponding
86                         RestClientBase method.
87        """
88        self._rest_response_ = self._robj_.post('/redfish/v1/' + resource_path,
89                                                *args, **kwargs)
90        return self._rest_response_
91
92    def patch(self, resource_path, *args, **kwargs):
93        r"""
94        Perform a POST request.
95
96        Description of argument(s):
97        resource_path    URI resource relative path
98        args/kwargs      These are passed directly to the corresponding
99                         RestClientBase method.
100        """
101        self._rest_response_ = self._robj_.patch('/redfish/v1/' + resource_path,
102                                                 *args, **kwargs)
103        return self._rest_response_
104
105    def put(self, resource_path, actions, attr_data):
106        r"""
107        Perform a PUT request.
108
109        Description of argument(s):
110        resource_path    URI resource relative path.
111        args/kwargs      These are passed directly to the corresponding
112                         RestClientBase method.
113        """
114        self._rest_response_ = self._robj_.put('/redfish/v1/' + resource_path,
115                                               *args, **kwargs)
116        return self._rest_response_
117
118    def delete(self, resource_path):
119        r"""
120        Perform a DELETE request.
121
122        Description of argument(s):
123        resource_path  URI resource absoulute path
124                       (e.g. "/redfish/v1/SessionService/Sessions/8d1a9wiiNL").
125        """
126        self._rest_response_ = self._robj_.delete(resource_path)
127        return self._rest_response_
128
129    def logout(self):
130        r"""
131        Logout redfish connection session.
132        """
133        self._robj_.logout()
134
135    def get_attribute(self, resource_path, attribute):
136        r"""
137        Perform a GET request and return attribute value.
138
139        Description of argument(s):
140        resource_path    URI resource absolute path (e.g. "/redfish/v1/Systems/1").
141        attribute        Property name (e.g. "PowerState").
142        """
143
144        resp = self._robj_.get(resource_path)
145
146        if attribute in resp.dict:
147            return resp.dict[attribute]
148
149        return None
150
151    def list_request(self, resource_path):
152        r"""
153        Perform a GET list request and return available resource paths.
154
155        Description of argument(s):
156        resource_path    URI resource relative path (e.g. "Systems/1").
157        """
158
159        global resource_list
160        resource_list = []
161
162        self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path)
163
164        # Return empty list.
165        if self._rest_response_.status != 200:
166            return resource_list
167
168        self.walk_nested_dict(self._rest_response_.dict)
169
170        if not resource_list:
171            return uri_path
172
173        for resource in resource_list:
174            self._rest_response_ = self._robj_.get(resource)
175            if self._rest_response_.status != 200:
176                continue
177            self.walk_nested_dict(self._rest_response_.dict)
178
179        resource_list.sort()
180        return resource_list
181
182    def enumerate_request(self, resource_path):
183        r"""
184        Perform a GET enumerate request and return available resource paths.
185
186        Description of argument(s):
187        resource_path    URI resource relative path (e.g. "Systems/1").
188        """
189
190        url_list = self.list_request(resource_path)
191
192        resource_dict = {}
193
194        # Return empty dict.
195        if not url_list:
196            return resource_dict
197
198        for resource in url_list:
199            self._rest_response_ = self._robj_.get(resource)
200            if self._rest_response_.status != 200:
201                continue
202            resource_dict[resource] = self._rest_response_.dict
203
204        return json.dumps(resource_dict, sort_keys=True,
205                          indent=4, separators=(',', ': '))
206
207    def walk_nested_dict(self, data):
208        r"""
209        Parse through the nested dictionary and get the resource id paths.
210
211        Description of argument(s):
212        data    Nested dictionary data from response message.
213        """
214
215        for key, value in data.items():
216            if isinstance(value, dict):
217                self.walk_nested_dict(value)
218            else:
219                if 'Members' == key:
220                    if isinstance(value, list):
221                        for index in value:
222                            if index['@odata.id'] not in resource_list:
223                                resource_list.append(index['@odata.id'])
224                if '@odata.id' == key:
225                    if value not in resource_list and not value.endswith('/'):
226                        resource_list.append(value)
227