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:
6 # http://www.apache.org/licenses/LICENSE-2.0
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.
14 """Implementation of HTTP requests GET, PUT, POST and DELETE used in
15 communication with Honeycomb.
17 The HTTP requests are implemented in the class HTTPRequest which uses
21 from ipaddress import IPv6Address, AddressValueError
22 from enum import IntEnum, unique
24 from robot.api.deco import keyword
25 from robot.api import logger
26 from robot.libraries.BuiltIn import BuiltIn
28 from requests import request, RequestException, Timeout, TooManyRedirects, \
29 HTTPError, ConnectionError
30 from requests.packages.urllib3 import disable_warnings
31 from requests.packages.urllib3.exceptions import InsecureRequestWarning
32 from requests.auth import HTTPBasicAuth
36 class HTTPCodes(IntEnum):
37 """HTTP status codes"""
44 INTERNAL_SERVER_ERROR = 500
45 SERVICE_UNAVAILABLE = 503
48 class HTTPRequestError(Exception):
49 """Exception raised by HTTPRequest objects.
51 When raising this exception, put this information to the message in this
53 - short description of the encountered problem,
54 - relevant messages if there are any collected, e.g., from caught
56 - relevant data if there are any collected.
57 The logging is performed on two levels: 1. error - short description of the
58 problem; 2. debug - detailed information.
61 def __init__(self, msg, details='', enable_logging=True):
62 """Sets the exception message and enables / disables logging.
64 It is not wanted to log errors when using these keywords together
65 with keywords like "Wait until keyword succeeds". So you can disable
66 logging by setting enable_logging to False.
68 :param msg: Message to be displayed and logged.
69 :param enable_logging: When True, logging is enabled, otherwise
72 :type enable_logging: bool
74 super(HTTPRequestError, self).__init__()
75 self._msg = "{0}: {1}".format(self.__class__.__name__, msg)
76 self._details = details
78 logger.error(self._msg)
79 logger.debug(self._details)
81 # suppress warnings about disabled SSL verification
82 disable_warnings(InsecureRequestWarning)
85 return repr(self._msg)
91 class HTTPRequest(object):
92 """A class implementing HTTP requests GET, PUT, POST and DELETE used in
93 communication with Honeycomb.
95 The communication with Honeycomb and processing of all exceptions is done in
96 the method _http_request which uses requests.request to send requests and
97 receive responses. The received status code and content of response are
98 logged on the debug level.
99 All possible exceptions raised by requests.request are also processed there.
101 The other methods (get, put, post and delete) use _http_request to send
102 corresponding request.
104 These methods must not be used as keywords in tests. Use keywords
105 implemented in the module HoneycombAPIKeywords instead.
112 def create_full_url(ip_addr, port, path):
113 """Creates full url including host, port, and path to data.
115 :param ip_addr: Server IP.
116 :param port: Communication port.
117 :param path: Path to data.
119 :type port: str or int
126 IPv6Address(unicode(ip_addr))
127 # IPv6 address must be in brackets
128 ip_addr = "[{0}]".format(ip_addr)
129 except (AttributeError, AddressValueError):
132 return "https://{ip}:{port}{path}".format(ip=ip_addr, port=port,
136 def _http_request(method, node, path, enable_logging=True, **kwargs):
137 """Sends specified HTTP request and returns status code and response
140 :param method: The method to be performed on the resource identified by
141 the given request URI.
142 :param node: Honeycomb node.
143 :param path: URL path, e.g. /index.html.
144 :param enable_logging: Used to suppress errors when checking Honeycomb
145 state during suite setup and teardown.
146 :param kwargs: Named parameters accepted by request.request:
147 params -- (optional) Dictionary or bytes to be sent in the query
148 string for the Request.
149 data -- (optional) Dictionary, bytes, or file-like object to
150 send in the body of the Request.
151 json -- (optional) json data to send in the body of the Request.
152 headers -- (optional) Dictionary of HTTP Headers to send with
154 cookies -- (optional) Dict or CookieJar object to send with the
156 files -- (optional) Dictionary of 'name': file-like-objects
157 (or {'name': ('filename', fileobj)}) for multipart encoding upload.
158 timeout (float or tuple) -- (optional) How long to wait for the
159 server to send data before giving up, as a float, or a (connect
160 timeout, read timeout) tuple.
161 allow_redirects (bool) -- (optional) Boolean. Set to True if POST/
162 PUT/DELETE redirect following is allowed.
163 proxies -- (optional) Dictionary mapping protocol to the URL of
165 verify -- (optional) whether the SSL cert will be verified.
166 A CA_BUNDLE path can also be provided. Defaults to True.
167 stream -- (optional) if False, the response content will be
168 immediately downloaded.
169 cert -- (optional) if String, path to ssl client cert file (.pem).
170 If Tuple, ('cert', 'key') pair.
174 :type enable_logging: bool
176 :return: Status code and content of response.
178 :raises HTTPRequestError: If
179 1. it is not possible to connect,
180 2. invalid HTTP response comes from server,
181 3. request exceeded the configured number of maximum re-directions,
182 4. request timed out,
183 5. there is any other unexpected HTTP request exception.
185 timeout = kwargs["timeout"]
187 if BuiltIn().get_variable_value("${use_odl_client}"):
188 # TODO: node["honeycomb"]["odl_port"]
190 odl_url_part = "/network-topology:network-topology/topology/" \
191 "topology-netconf/node/vpp/yang-ext:mount"
193 port = node["honeycomb"]["port"]
197 path = path.format(odl_url_part=odl_url_part)
201 url = HTTPRequest.create_full_url(node['host'],
205 auth = HTTPBasicAuth(node['honeycomb']['user'],
206 node['honeycomb']['passwd'])
207 rsp = request(method, url, auth=auth, verify=False, **kwargs)
209 logger.debug("Status code: {0}".format(rsp.status_code))
210 logger.debug("Response: {0}".format(rsp.content))
212 return rsp.status_code, rsp.content
214 except ConnectionError as err:
215 # Switching the logging on / off is needed only for
216 # "requests.ConnectionError"
217 raise HTTPRequestError("Not possible to connect to {0}:{1}.".
219 node['honeycomb']['port']),
220 repr(err), enable_logging=enable_logging)
221 except HTTPError as err:
222 raise HTTPRequestError("Invalid HTTP response from {0}.".
223 format(node['host']), repr(err))
224 except TooManyRedirects as err:
225 raise HTTPRequestError("Request exceeded the configured number "
226 "of maximum re-directions.", repr(err))
227 except Timeout as err:
228 raise HTTPRequestError("Request timed out. Timeout is set to {0}.".
229 format(timeout), repr(err))
230 except RequestException as err:
231 raise HTTPRequestError("Unexpected HTTP request exception.",
235 @keyword(name="HTTP Get")
236 def get(node, path, headers=None, timeout=10, enable_logging=True):
237 """Sends a GET request and returns the response and status code.
239 :param node: Honeycomb node.
240 :param path: URL path, e.g. /index.html.
241 :param headers: Dictionary of HTTP Headers to send with the Request.
242 :param timeout: How long to wait for the server to send data before
243 giving up, as a float, or a (connect timeout, read timeout) tuple.
244 :param enable_logging: Used to suppress errors when checking Honeycomb
245 state during suite setup and teardown. When True, logging is enabled,
246 otherwise logging is disabled.
250 :type timeout: float or tuple
251 :type enable_logging: bool
252 :return: Status code and content of response.
256 return HTTPRequest._http_request('GET', node, path,
257 enable_logging=enable_logging,
258 headers=headers, timeout=timeout)
261 @keyword(name="HTTP Put")
262 def put(node, path, headers=None, payload=None, json=None, timeout=10):
263 """Sends a PUT request and returns the response and status code.
265 :param node: Honeycomb node.
266 :param path: URL path, e.g. /index.html.
267 :param headers: Dictionary of HTTP Headers to send with the Request.
268 :param payload: Dictionary, bytes, or file-like object to send in
269 the body of the Request.
270 :param json: JSON formatted string to send in the body of the Request.
271 :param timeout: How long to wait for the server to send data before
272 giving up, as a float, or a (connect timeout, read timeout) tuple.
276 :type payload: dict, bytes, or file-like object
278 :type timeout: float or tuple
279 :return: Status code and content of response.
282 return HTTPRequest._http_request('PUT', node, path, headers=headers,
283 data=payload, json=json,
287 @keyword(name="HTTP Post")
288 def post(node, path, headers=None, payload=None, json=None, timeout=10,
289 enable_logging=True):
290 """Sends a POST request and returns the response and status code.
292 :param node: Honeycomb node.
293 :param path: URL path, e.g. /index.html.
294 :param headers: Dictionary of HTTP Headers to send with the Request.
295 :param payload: Dictionary, bytes, or file-like object to send in
296 the body of the Request.
297 :param json: JSON formatted string to send in the body of the Request.
298 :param timeout: How long to wait for the server to send data before
299 giving up, as a float, or a (connect timeout, read timeout) tuple.
300 :param enable_logging: Used to suppress errors when checking ODL
301 state during suite setup and teardown. When True, logging is enabled,
302 otherwise logging is disabled.
306 :type payload: dict, bytes, or file-like object
308 :type timeout: float or tuple
309 :type enable_logging: bool
310 :return: Status code and content of response.
313 return HTTPRequest._http_request('POST', node, path,
314 enable_logging=enable_logging,
315 headers=headers, data=payload,
316 json=json, timeout=timeout)
319 @keyword(name="HTTP Delete")
320 def delete(node, path, timeout=10):
321 """Sends a DELETE request and returns the response and status code.
323 :param node: Honeycomb node.
324 :param path: URL path, e.g. /index.html.
325 :param timeout: How long to wait for the server to send data before
326 giving up, as a float, or a (connect timeout, read timeout) tuple.
329 :type timeout: float or tuple
330 :return: Status code and content of response.
333 return HTTPRequest._http_request('DELETE', node, path, timeout=timeout)