Reformat python libraries.
[csit.git] / resources / libraries / python / HTTPRequest.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 HTTP requests GET, PUT, POST and DELETE used in
15 communication with Honeycomb.
16
17 The HTTP requests are implemented in the class HTTPRequest which uses
18 requests.request.
19 """
20
21 from enum import IntEnum, unique
22
23 from robot.api.deco import keyword
24 from robot.api import logger
25
26 from requests import request, RequestException, Timeout, TooManyRedirects, \
27     HTTPError, ConnectionError
28 from requests.auth import HTTPBasicAuth
29
30
31 @unique
32 class HTTPCodes(IntEnum):
33     """HTTP status codes"""
34     OK = 200
35     UNAUTHORIZED = 401
36     FORBIDDEN = 403
37     NOT_FOUND = 404
38     SERVICE_UNAVAILABLE = 503
39
40
41 class HTTPRequestError(Exception):
42     """Exception raised by HTTPRequest objects.
43
44     When raising this exception, put this information to the message in this
45     order:
46     - short description of the encountered problem,
47     - relevant messages if there are any collected, e.g., from caught
48       exception,
49     - relevant data if there are any collected.
50     The logging is performed on two levels: 1. error - short description of the
51     problem; 2. debug - detailed information.
52     """
53
54     def __init__(self, msg, details='', enable_logging=True):
55         """Sets the exception message and enables / disables logging.
56
57         It is not wanted to log errors when using these keywords together
58         with keywords like "Wait until keyword succeeds". So you can disable
59         logging by setting enable_logging to False.
60
61         :param msg: Message to be displayed and logged.
62         :param enable_logging: When True, logging is enabled, otherwise
63         logging is disabled.
64         :type msg: str
65         :type enable_logging: bool
66         """
67         super(HTTPRequestError, self).__init__()
68         self._msg = "{0}: {1}".format(self.__class__.__name__, msg)
69         self._details = details
70         if enable_logging:
71             logger.error(self._msg)
72             logger.debug(self._details)
73
74     def __repr__(self):
75         return repr(self._msg)
76
77     def __str__(self):
78         return str(self._msg)
79
80
81 class HTTPRequest(object):
82     """A class implementing HTTP requests GET, PUT, POST and DELETE used in
83     communication with Honeycomb.
84
85     The communication with Honeycomb and processing of all exceptions is done in
86     the method _http_request which uses requests.request to send requests and
87     receive responses. The received status code and content of response are
88     logged on the debug level.
89     All possible exceptions raised by requests.request are also processed there.
90
91     The other methods (get, put, post and delete) use _http_request to send
92     corresponding request.
93
94     These methods must not be used as keywords in tests. Use keywords
95     implemented in the module HoneycombAPIKeywords instead.
96     """
97
98     def __init__(self):
99         pass
100
101     @staticmethod
102     def create_full_url(ip_addr, port, path):
103         """Creates full url including host, port, and path to data.
104
105         :param ip_addr: Server IP.
106         :param port: Communication port.
107         :param path: Path to data.
108         :type ip_addr: str
109         :type port: str or int
110         :type path: str
111         :return: Full url.
112         :rtype: str
113         """
114         return "http://{ip}:{port}{path}".format(ip=ip_addr, port=port,
115                                                  path=path)
116
117     @staticmethod
118     def _http_request(method, node, path, enable_logging=True, **kwargs):
119         """Sends specified HTTP request and returns status code and response
120         content.
121
122         :param method: The method to be performed on the resource identified by
123         the given request URI.
124         :param node: Honeycomb node.
125         :param path: URL path, e.g. /index.html.
126         :param enable_logging: Used to suppress errors when checking Honeycomb
127         state during suite setup and teardown.
128         :param kwargs: Named parameters accepted by request.request:
129             params -- (optional) Dictionary or bytes to be sent in the query
130             string for the Request.
131             data -- (optional) Dictionary, bytes, or file-like object to
132             send in the body of the Request.
133             json -- (optional) json data to send in the body of the Request.
134             headers -- (optional) Dictionary of HTTP Headers to send with
135             the Request.
136             cookies -- (optional) Dict or CookieJar object to send with the
137             Request.
138             files -- (optional) Dictionary of 'name': file-like-objects
139             (or {'name': ('filename', fileobj)}) for multipart encoding upload.
140             timeout (float or tuple) -- (optional) How long to wait for the
141             server to send data before giving up, as a float, or a (connect
142             timeout, read timeout) tuple.
143             allow_redirects (bool) -- (optional) Boolean. Set to True if POST/
144             PUT/DELETE redirect following is allowed.
145             proxies -- (optional) Dictionary mapping protocol to the URL of
146             the proxy.
147             verify -- (optional) whether the SSL cert will be verified.
148             A CA_BUNDLE path can also be provided. Defaults to True.
149             stream -- (optional) if False, the response content will be
150             immediately downloaded.
151             cert -- (optional) if String, path to ssl client cert file (.pem).
152             If Tuple, ('cert', 'key') pair.
153         :type method: str
154         :type node: dict
155         :type path: str
156         :type enable_logging: bool
157         :type kwargs: dict
158         :return: Status code and content of response.
159         :rtype: tuple
160         :raises HTTPRequestError: If
161         1. it is not possible to connect,
162         2. invalid HTTP response comes from server,
163         3. request exceeded the configured number of maximum re-directions,
164         4. request timed out,
165         5. there is any other unexpected HTTP request exception.
166         """
167         timeout = kwargs["timeout"]
168         url = HTTPRequest.create_full_url(node['host'],
169                                           node['honeycomb']['port'],
170                                           path)
171         try:
172             auth = HTTPBasicAuth(node['honeycomb']['user'],
173                                  node['honeycomb']['passwd'])
174             rsp = request(method, url, auth=auth, **kwargs)
175
176             logger.debug("Status code: {0}".format(rsp.status_code))
177             logger.debug("Response: {0}".format(rsp.content))
178
179             return rsp.status_code, rsp.content
180
181         except ConnectionError as err:
182             # Switching the logging on / off is needed only for
183             # "requests.ConnectionError"
184             raise HTTPRequestError("Not possible to connect to {0}:{1}.".
185                                    format(node['host'],
186                                           node['honeycomb']['port']),
187                                    repr(err), enable_logging=enable_logging)
188         except HTTPError as err:
189             raise HTTPRequestError("Invalid HTTP response from {0}.".
190                                    format(node['host']), repr(err))
191         except TooManyRedirects as err:
192             raise HTTPRequestError("Request exceeded the configured number "
193                                    "of maximum re-directions.", repr(err))
194         except Timeout as err:
195             raise HTTPRequestError("Request timed out. Timeout is set to {0}.".
196                                    format(timeout), repr(err))
197         except RequestException as err:
198             raise HTTPRequestError("Unexpected HTTP request exception.",
199                                    repr(err))
200
201     @staticmethod
202     @keyword(name="HTTP Get")
203     def get(node, path, headers=None, timeout=10, enable_logging=True):
204         """Sends a GET request and returns the response and status code.
205
206         :param node: Honeycomb node.
207         :param path: URL path, e.g. /index.html.
208         :param headers: Dictionary of HTTP Headers to send with the Request.
209         :param timeout: How long to wait for the server to send data before
210         giving up, as a float, or a (connect timeout, read timeout) tuple.
211         :param enable_logging: Used to suppress errors when checking Honeycomb
212         state during suite setup and teardown. When True, logging is enabled,
213         otherwise logging is disabled.
214         :type node: dict
215         :type path: str
216         :type headers: dict
217         :type timeout: float or tuple
218         :type enable_logging: bool
219         :return: Status code and content of response.
220         :rtype: tuple
221         """
222
223         return HTTPRequest._http_request('GET', node, path,
224                                          enable_logging=enable_logging,
225                                          headers=headers, timeout=timeout)
226
227     @staticmethod
228     @keyword(name="HTTP Put")
229     def put(node, path, headers=None, payload=None, json=None, timeout=10):
230         """Sends a PUT request and returns the response and status code.
231
232         :param node: Honeycomb node.
233         :param path: URL path, e.g. /index.html.
234         :param headers: Dictionary of HTTP Headers to send with the Request.
235         :param payload: Dictionary, bytes, or file-like object to send in
236         the body of the Request.
237         :param json: JSON formatted string to send in the body of the Request.
238         :param timeout: How long to wait for the server to send data before
239         giving up, as a float, or a (connect timeout, read timeout) tuple.
240         :type node: dict
241         :type path: str
242         :type headers: dict
243         :type payload: dict, bytes, or file-like object
244         :type json: str
245         :type timeout: float or tuple
246         :return: Status code and content of response.
247         :rtype: tuple
248         """
249         return HTTPRequest._http_request('PUT', node, path, headers=headers,
250                                          data=payload, json=json,
251                                          timeout=timeout)
252
253     @staticmethod
254     @keyword(name="HTTP Post")
255     def post(node, path, headers=None, payload=None, json=None, timeout=10):
256         """Sends a POST request and returns the response and status code.
257
258         :param node: Honeycomb node.
259         :param path: URL path, e.g. /index.html.
260         :param headers: Dictionary of HTTP Headers to send with the Request.
261         :param payload: Dictionary, bytes, or file-like object to send in
262         the body of the Request.
263         :param json: JSON formatted string to send in the body of the Request.
264         :param timeout: How long to wait for the server to send data before
265         giving up, as a float, or a (connect timeout, read timeout) tuple.
266         :type node: dict
267         :type path: str
268         :type headers: dict
269         :type payload: dict, bytes, or file-like object
270         :type json: str
271         :type timeout: float or tuple
272         :return: Status code and content of response.
273         :rtype: tuple
274         """
275         return HTTPRequest._http_request('POST', node, path, headers=headers,
276                                          data=payload, json=json,
277                                          timeout=timeout)
278
279     @staticmethod
280     @keyword(name="HTTP Delete")
281     def delete(node, path, timeout=10):
282         """Sends a DELETE request and returns the response and status code.
283
284         :param node: Honeycomb node.
285         :param path: URL path, e.g. /index.html.
286         :param timeout: How long to wait for the server to send data before
287         giving up, as a float, or a (connect timeout, read timeout) tuple.
288         :type node: dict
289         :type path: str
290         :type timeout: float or tuple
291         :return: Status code and content of response.
292         :rtype: tuple
293         """
294         return HTTPRequest._http_request('DELETE', node, path, timeout=timeout)