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 relative path (e.g. "Systems/1").
72        args/kwargs      These are passed directly to the corresponding
73                         RestClientBase method.
74        """
75        self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path,
76                                               *args, **kwargs)
77        return self._rest_response_
78
79    def post(self, resource_path, *args, **kwargs):
80        r"""
81        Perform a POST request.
82
83        Description of argument(s):
84        resource_path    URI resource relative path
85                         (e.g. "Systems/1/Actions/ComputerSystem.Reset").
86        args/kwargs      These are passed directly to the corresponding
87                         RestClientBase method.
88        """
89        self._rest_response_ = self._robj_.post('/redfish/v1/' + resource_path,
90                                                *args, **kwargs)
91        return self._rest_response_
92
93    def patch(self, resource_path, *args, **kwargs):
94        r"""
95        Perform a POST request.
96
97        Description of argument(s):
98        resource_path    URI resource relative path
99        args/kwargs      These are passed directly to the corresponding
100                         RestClientBase method.
101        """
102        self._rest_response_ = self._robj_.patch('/redfish/v1/' + resource_path,
103                                                 *args, **kwargs)
104        return self._rest_response_
105
106    def put(self, resource_path, actions, attr_data):
107        r"""
108        Perform a PUT request.
109
110        Description of argument(s):
111        resource_path    URI resource relative path.
112        args/kwargs      These are passed directly to the corresponding
113                         RestClientBase method.
114        """
115        self._rest_response_ = self._robj_.put('/redfish/v1/' + resource_path,
116                                               *args, **kwargs)
117        return self._rest_response_
118
119    def delete(self, resource_path):
120        r"""
121        Perform a DELETE request.
122
123        Description of argument(s):
124        resource_path  URI resource absoulute path
125                       (e.g. "/redfish/v1/SessionService/Sessions/8d1a9wiiNL").
126        """
127        self._rest_response_ = self._robj_.delete(resource_path)
128        return self._rest_response_
129
130    def logout(self):
131        r"""
132        Logout redfish connection session.
133        """
134        self._robj_.logout()
135
136    def list_request(self, resource_path):
137        r"""
138        Perform a GET list request and return available resource paths.
139
140        Description of argument(s):
141        resource_path    URI resource relative path (e.g. "Systems/1").
142        """
143
144        global resource_list
145        resource_list = []
146
147        self._rest_response_ = self._robj_.get('/redfish/v1/' + resource_path)
148
149        # Return empty list.
150        if self._rest_response_.status != 200:
151            return resource_list
152
153        self.walk_nested_dict(self._rest_response_.dict)
154
155        if not resource_list:
156            return uri_path
157
158        for resource in resource_list:
159            self._rest_response_ = self._robj_.get(resource)
160            if self._rest_response_.status != 200:
161                continue
162            self.walk_nested_dict(self._rest_response_.dict)
163
164        resource_list.sort()
165        return resource_list
166
167    def enumerate_request(self, resource_path):
168        r"""
169        Perform a GET enumerate request and return available resource paths.
170
171        Description of argument(s):
172        resource_path    URI resource relative path (e.g. "Systems/1").
173        """
174
175        url_list = self.list_request(resource_path)
176
177        resource_dict = {}
178
179        # Return empty dict.
180        if not url_list:
181            return resource_dict
182
183        for resource in url_list:
184            self._rest_response_ = self._robj_.get(resource)
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