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        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 list_request(self, resource_path):
136        r"""
137        Perform a GET list request and return available resource paths.
138
139        Description of argument(s):
140        resource_path    URI resource relative path (e.g. "Systems/1").
141        """
142
143        self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path)
144        if self._rest_response_.status != 200:
145            return self._rest_response_
146
147        global resource_list
148        resource_list = []
149        self.walk_nested_dict(self._rest_response_.dict)
150
151        if not resource_list:
152            return uri_path
153
154        for resource in resource_list:
155            self._rest_response_ = self._robj_.get(resource)
156            if self._rest_response_.status != 200:
157                continue
158            self.walk_nested_dict(self._rest_response_.dict)
159
160        resource_list.sort()
161        return resource_list
162
163    def enumerate_request(self, resource_path):
164        r"""
165        Perform a GET enumerate request and return available resource paths.
166
167        Description of argument(s):
168        resource_path    URI resource relative path (e.g. "Systems/1").
169        """
170
171        self._rest_response_ = self.list_request(resource_path)
172        if self._rest_response_.status != 200:
173            return self._rest_response_
174
175        resource_dict = {}
176        for resource in json.loads(self._rest_response_):
177            self._rest_response_ = self._robj_.get(resource)
178            if self._rest_response_.status != 200:
179                continue
180            resource_dict[resource] = self._rest_response_.dict
181
182        return json.dumps(resource_dict, sort_keys=True,
183                          indent=4, separators=(',', ': '))
184
185    def walk_nested_dict(self, data):
186        r"""
187        Parse through the nested dictionary and get the resource id paths.
188
189        Description of argument(s):
190        data    Nested dictionary data from response message.
191        """
192
193        for key, value in data.items():
194            if isinstance(value, dict):
195                self.walk_nested_dict(value)
196            else:
197                if 'Members' == key:
198                    if isinstance(value, list):
199                        for index in value:
200                            if index['@odata.id'] not in resource_list:
201                                resource_list.append(index['@odata.id'])
202                if '@odata.id' == key:
203                    if value not in resource_list and not value.endswith('/'):
204                        resource_list.append(value)
205