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
10
11
12class HTTPSBadRequestError(Exception):
13    r"""
14    BMC redfish generic raised method for error(s).
15    """
16    pass
17
18
19class bmc_redfish(object):
20
21    ROBOT_LIBRARY_SCOPE = "GLOBAL"
22
23    def __init__(self, hostname, username, password, *args, **kwargs):
24        r"""
25        Establish session connection to host.
26
27        Description of argument(s):
28        hostname       The host name or IP address of the server.
29        username       The username to be used to connect to the server.
30        password       The password to be used to connect to the server.
31        args/kwargs    Additional parms which are passed directly
32                       to the redfish_client function.
33        """
34
35        self._base_url_ = "https://" + hostname
36        self._username_ = username
37        self._password_ = password
38        self._default_prefix_ = "/redfish/v1"
39        self._robj_ = \
40            redfish.redfish_client(base_url=self._base_url_,
41                                   username=self._username_,
42                                   password=self._password_,
43                                   default_prefix=self._default_prefix_,
44                                   *args, **kwargs)
45        self._robj_.login(auth=redfish.AuthMethod.SESSION)
46        self._session_location_ = self._robj_.get_session_location()
47
48    def __enter__(self):
49        return self
50
51    def __exit__(self, exception_type, exception_value, traceback):
52        self._robj_.logout()
53
54    def login(self, *args, **kwargs):
55        r"""
56        Call the corresponding RestClientBase method and return the result.
57
58        Description of argument(s):
59        args/kwargs     These are passed directly to the corresponding
60                        RestClientBase method.
61        """
62        self._robj_.__init__(self._base_url_, self._username_, self._password_)
63        self._robj_.login(auth=redfish.AuthMethod.SESSION)
64
65    def get(self, resource_path, *args, **kwargs):
66        r"""
67        Perform a GET request and return response.
68
69        Description of argument(s):
70        resource_path    URI resource relative path (e.g. "Systems/1").
71        args/kwargs      These are passed directly to the corresponding
72                         RestClientBase method.
73        """
74        self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path,
75                                               *args, **kwargs)
76        if self._rest_response_.status != 200:
77            raise HTTPSBadRequestError("GET Session location: %s, "
78                                       "return code: %d"
79                                       % (self._session_location_,
80                                          self._rest_response_.status))
81        return self._rest_response_
82
83    def post(self, resource_path, *args, **kwargs):
84        r"""
85        Perform a POST request.
86
87        Description of argument(s):
88        resource_path    URI resource relative path
89                         (e.g. "Systems/1/Actions/ComputerSystem.Reset").
90        args/kwargs      These are passed directly to the corresponding
91                         RestClientBase method.
92        """
93        self._rest_response_ = self._robj_.post('/redfish/v1/' + resource_path,
94                                                *args, **kwargs)
95        if self._rest_response_.status != 200:
96            raise HTTPSBadRequestError("POST Session location: %s, "
97                                       "return code: %d"
98                                       % (self._session_location_,
99                                          self._rest_response_.status))
100        return self._rest_response_
101
102    def patch(self, resource_path, *args, **kwargs):
103        r"""
104        Perform a POST request.
105
106        Description of argument(s):
107        resource_path    URI resource relative path
108        args/kwargs      These are passed directly to the corresponding
109                         RestClientBase method.
110        """
111        self._rest_response_ = self._robj_.patch('/redfish/v1/' + resource_path,
112                                                 *args, **kwargs)
113        if self._rest_response_.status != 200:
114            raise HTTPSBadRequestError("PATCH Session location: %s, "
115                                       "return code: %d"
116                                       % (self._session_location_,
117                                          self._rest_response_.status))
118        return self._rest_response_
119
120    def put(self, resource_path, actions, attr_data):
121        r"""
122        Perform a PUT request.
123
124        Description of argument(s):
125        resource_path    URI resource relative path.
126        args/kwargs      These are passed directly to the corresponding
127                         RestClientBase method.
128        """
129        self._rest_response_ = self._robj_.put('/redfish/v1/' + resource_path,
130                                               *args, **kwargs)
131        if self._rest_response_.status != 200:
132            raise HTTPSBadRequestError("PUT Session location: %s, "
133                                       "return code: %d"
134                                       % (self._session_location_,
135                                          self._rest_response_.status))
136        return self._rest_response_
137
138    def delete(self, resource_path):
139        r"""
140        Perform a DELETE request.
141
142        Description of argument(s):
143        resource_path  URI resource absoulute path
144                       (e.g. "/redfish/v1/SessionService/Sessions/8d1a9wiiNL").
145        """
146        self._rest_response_ = self._robj_.delete(resource_path)
147        if self._rest_response_.status != 200:
148            raise HTTPSBadRequestError("Session location: %s, "
149                                       "return code: %d"
150                                       % (self._session_location_,
151                                          self._rest_response_.status))
152        return self._rest_response_
153
154    def logout(self):
155        r"""
156        Logout redfish connection session.
157        """
158        self._robj_.logout()
159
160    def list_request(self, resource_path):
161        r"""
162        Perform a GET list request and return available resource paths.
163
164        Description of argument(s):
165        resource_path    URI resource relative path (e.g. "Systems/1").
166        """
167
168        self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path)
169
170        global resource_list
171        resource_list = []
172        self.walk_nested_dict(self._rest_response_.dict)
173
174        if not resource_list:
175            return uri_path
176
177        for resource in resource_list:
178            self._rest_response_ = self._robj_.get(resource)
179            if self._rest_response_.status != 200:
180                continue
181            self.walk_nested_dict(self._rest_response_.dict)
182
183        resource_list.sort()
184        return json.dumps(resource_list, sort_keys=True,
185                          indent=4, separators=(',', ': '))
186
187    def enumerate_request(self, resource_path):
188        r"""
189        Perform a GET enumerate request and return available resource paths.
190
191        Description of argument(s):
192        resource_path    URI resource relative path (e.g. "Systems/1").
193        """
194
195        self._rest_response_ = self.list_request(resource_path)
196
197        resource_dict = {}
198        for resource in json.loads(self._rest_response_):
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