Honeycomb API keywords
[csit.git] / resources / libraries / python / HoneycombUtil.py
1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Implementation of low level functionality used in communication with
15 Honeycomb.
16
17 Exception HoneycombError is used in all methods and in all modules with
18 Honeycomb keywords.
19
20 Class HoneycombUtil implements methods used by Honeycomb keywords. They must not
21 be used directly in tests. Use keywords implemented in the module
22 HoneycombAPIKeywords instead.
23 """
24
25 from json import loads
26 from enum import Enum, unique
27
28 from robot.api import logger
29
30 from resources.libraries.python.HTTPRequest import HTTPRequest
31 from resources.libraries.python.constants import Constants as Const
32
33
34 @unique
35 class DataRepresentation(Enum):
36     """Representation of data sent by PUT and POST requests."""
37     NO_DATA = 0
38     JSON = 1
39     XML = 2
40     TXT = 3
41
42
43 # Headers used in requests. Key - content representation, value - header.
44 HEADERS = {DataRepresentation.NO_DATA:
45                {},  # Must be empty.
46            DataRepresentation.JSON:
47                {"Content-Type": "application/json",
48                 "Accept": "text/plain"},
49            DataRepresentation.XML:
50                {"Content-Type": "application/xml",
51                 "Accept": "text/plain"},
52            DataRepresentation.TXT:
53                {"Content-Type": "text/plain",
54                 "Accept": "text/plain"}
55           }
56
57
58 class HoneycombError(Exception):
59
60     """Exception(s) raised by methods working with Honeycomb.
61
62     When raising this exception, put this information to the message in this
63     order:
64      - short description of the encountered problem (parameter msg),
65      - relevant messages if there are any collected, e.g., from caught
66        exception (optional parameter details),
67      - relevant data if there are any collected (optional parameter details).
68      The logging is performed on two levels: 1. error - short description of the
69      problem; 2. debug - detailed information.
70     """
71
72     def __init__(self, msg, details='', enable_logging=True):
73         """Sets the exception message and enables / disables logging.
74
75         It is not wanted to log errors when using these keywords together
76         with keywords like "Wait until keyword succeeds". So you can disable
77         logging by setting enable_logging to False.
78
79         :param msg: Message to be displayed and logged.
80         :param enable_logging: When True, logging is enabled, otherwise
81         logging is disabled.
82         :type msg: str
83         :type enable_logging: bool
84         """
85         super(HoneycombError, self).__init__()
86         self._msg = "{0}: {1}".format(self.__class__.__name__, msg)
87         self._details = details
88         if enable_logging:
89             logger.error(self._msg)
90             logger.debug(self._details)
91
92     def __repr__(self):
93         return repr(self._msg)
94
95     def __str__(self):
96         return str(self._msg)
97
98
99 class HoneycombUtil(object):
100     """Implements low level functionality used in communication with Honeycomb.
101
102     There are implemented methods to get, put and delete data to/from Honeycomb.
103     They are based on functionality implemented in the module HTTPRequests which
104     uses HTTP requests GET, PUT, POST and DELETE to communicate with Honeycomb.
105
106     It is possible to PUT the data represented as XML or JSON structures or as
107     plain text.
108     Data received in the response of GET are always represented as a JSON
109     structure.
110
111     There are also two supportive methods implemented:
112     - read_path_from_url_file which reads URL file and returns a path (see
113       docs/honeycomb_url_files.rst).
114     - parse_json_response which parses data from response in JSON representation
115       according to given path.
116     """
117
118     def __init__(self):
119         pass
120
121     @staticmethod
122     def read_path_from_url_file(url_file):
123         """Read path from *.url file.
124
125         For more information about *.url file see docs/honeycomb_url_files.rst
126         :param url_file: URL file. The argument contains only the name of file
127         without extension, not the full path.
128         :type url_file: str
129         :return: Requested path.
130         :rtype: str
131         """
132
133         url_file = "{0}/{1}.url".format(Const.RESOURCES_TPL_HC, url_file)
134         with open(url_file) as template:
135             path = template.readline()
136         return path
137
138     @staticmethod
139     def parse_json_response(response, path=None):
140         """Parse data from response string in JSON format according to given
141         path.
142
143         :param response: JSON formatted string.
144         :param path: Path to navigate down the data structure.
145         :type response: str
146         :type path: tuple
147         :return: JSON dictionary/list tree.
148         :rtype: list
149         """
150         data = loads(response)
151
152         if path:
153             data = HoneycombUtil._parse_json_tree(data, path)
154             if not isinstance(data, list):
155                 data = [data, ]
156
157         return data
158
159     @staticmethod
160     def _parse_json_tree(data, path):
161         """Retrieve data addressed by path from python representation of JSON
162         object.
163
164         :param data: Parsed JSON dictionary tree.
165         :param path: Path to navigate down the dictionary tree.
166         :type data: dict
167         :type path: tuple
168         :return: Data from specified path.
169         :rtype: list, dict or str
170         """
171
172         count = 0
173         for key in path:
174             if isinstance(data, dict):
175                 data = data[key]
176                 count += 1
177             elif isinstance(data, list):
178                 result = []
179                 for item in data:
180                     result.append(HoneycombUtil._parse_json_tree(item,
181                                                                  path[count:]))
182                     return result
183         return data
184
185     @staticmethod
186     def get_honeycomb_data(node, url_file):
187         """Retrieve data from Honeycomb according to given URL.
188
189         :param node: Honeycomb node.
190         :param url_file: URL file. The argument contains only the name of file
191         without extension, not the full path.
192         :type node: dict
193         :type url_file: str
194         :return: Status code and content of response.
195         :rtype tuple
196         """
197
198         path = HoneycombUtil.read_path_from_url_file(url_file)
199         return HTTPRequest.get(node, path)
200
201     @staticmethod
202     def put_honeycomb_data(node, url_file, data,
203                            data_representation=DataRepresentation.JSON):
204         """Send configuration data using PUT request and return the status code
205         and response content.
206
207         :param node: Honeycomb node.
208         :param url_file: URL file. The argument contains only the name of file
209         without extension, not the full path.
210         :param data: Configuration data to be sent to Honeycomb.
211         :param data_representation: How the data is represented.
212         :type node: dict
213         :type url_file: str
214         :type data: str
215         :type data_representation: DataRepresentation
216         :return: Status code and content of response.
217         :rtype: tuple
218         :raises HoneycombError: If the given data representation is not defined
219         in HEADERS.
220         """
221
222         try:
223             header = HEADERS[data_representation]
224         except AttributeError as err:
225             raise HoneycombError("Wrong data representation: {0}.".
226                                  format(data_representation), repr(err))
227
228         path = HoneycombUtil.read_path_from_url_file(url_file)
229         return HTTPRequest.put(node=node, path=path, headers=header,
230                                payload=data)
231
232     @staticmethod
233     def post_honeycomb_data(node, url_file, data=None,
234                             data_representation=DataRepresentation.JSON,
235                             timeout=10):
236         """Send a POST request and return the status code and response content.
237
238         :param node: Honeycomb node.
239         :param url_file: URL file. The argument contains only the name of file
240         without extension, not the full path.
241         :param data: Configuration data to be sent to Honeycomb.
242         :param data_representation: How the data is represented.
243         :param timeout: How long to wait for the server to send data before
244         giving up.
245         :type node: dict
246         :type url_file: str
247         :type data: str
248         :type data_representation: DataRepresentation
249         :type timeout: int
250         :return: Status code and content of response.
251         :rtype: tuple
252         :raises HoneycombError: If the given data representation is not defined
253         in HEADERS.
254         """
255
256         try:
257             header = HEADERS[data_representation]
258         except AttributeError as err:
259             raise HoneycombError("Wrong data representation: {0}.".
260                                  format(data_representation), repr(err))
261
262         path = HoneycombUtil.read_path_from_url_file(url_file)
263         return HTTPRequest.post(node=node, path=path, headers=header,
264                                 payload=data, timeout=timeout)
265
266     @staticmethod
267     def delete_honeycomb_data(node, url_file):
268         """Delete data from Honeycomb according to given URL.
269
270         :param node: Honeycomb node.
271         :param url_file: URL file. The argument contains only the name of file
272         without extension, not the full path.
273         :type node: dict
274         :type url_file: str
275         :return: Status code and content of response.
276         :rtype tuple
277         """
278
279         path = HoneycombUtil.read_path_from_url_file(url_file)
280         return HTTPRequest.delete(node, path)