1 # Copyright (c) 2017 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.auth import HTTPBasicAuth
34 class HTTPCodes(IntEnum):
35 """HTTP status codes"""
42 INTERNAL_SERVER_ERROR = 500
43 SERVICE_UNAVAILABLE = 503
46 class HTTPRequestError(Exception):
47 """Exception raised by HTTPRequest objects.
49 When raising this exception, put this information to the message in this
51 - short description of the encountered problem,
52 - relevant messages if there are any collected, e.g., from caught
54 - relevant data if there are any collected.
55 The logging is performed on two levels: 1. error - short description of the
56 problem; 2. debug - detailed information.
59 def __init__(self, msg, details='', enable_logging=True):
60 """Sets the exception message and enables / disables logging.
62 It is not wanted to log errors when using these keywords together
63 with keywords like "Wait until keyword succeeds". So you can disable
64 logging by setting enable_logging to False.
66 :param msg: Message to be displayed and logged.
67 :param enable_logging: When True, logging is enabled, otherwise
70 :type enable_logging: bool
72 super(HTTPRequestError, self).__init__()
73 self._msg = "{0}: {1}".format(self.__class__.__name__, msg)
74 self._details = details
76 logger.info(self._msg)
77 logger.debug(self._details)
80 return repr(self._msg)
86 class HTTPRequest(object):
87 """A class implementing HTTP requests GET, PUT, POST and DELETE used in
88 communication with Honeycomb.
90 The communication with Honeycomb and processing of all exceptions is done in
91 the method _http_request which uses requests.request to send requests and
92 receive responses. The received status code and content of response are
93 logged on the debug level.
94 All possible exceptions raised by requests.request are also processed there.
96 The other methods (get, put, post and delete) use _http_request to send
97 corresponding request.
99 These methods must not be used as keywords in tests. Use keywords
100 implemented in the module HoneycombAPIKeywords instead.
107 def create_full_url(ip_addr, port, path):
108 """Creates full url including host, port, and path to data.
110 :param ip_addr: Server IP.
111 :param port: Communication port.
112 :param path: Path to data.
114 :type port: str or int
121 IPv6Address(unicode(ip_addr))
122 # IPv6 address must be in brackets
123 ip_addr = "[{0}]".format(ip_addr)
124 except (AttributeError, AddressValueError):
127 return "http://{ip}:{port}{path}".format(ip=ip_addr, port=port,
131 def _http_request(method, node, path, enable_logging=True, **kwargs):
132 """Sends specified HTTP request and returns status code and response
135 :param method: The method to be performed on the resource identified by
136 the given request URI.
137 :param node: Honeycomb node.
138 :param path: URL path, e.g. /index.html.
139 :param enable_logging: Used to suppress errors when checking Honeycomb
140 state during suite setup and teardown.
141 :param kwargs: Named parameters accepted by request.request:
142 params -- (optional) Dictionary or bytes to be sent in the query
143 string for the Request.
144 data -- (optional) Dictionary, bytes, or file-like object to
145 send in the body of the Request.
146 json -- (optional) json data to send in the body of the Request.
147 headers -- (optional) Dictionary of HTTP Headers to send with
149 cookies -- (optional) Dict or CookieJar object to send with the
151 files -- (optional) Dictionary of 'name': file-like-objects
152 (or {'name': ('filename', fileobj)}) for multipart encoding upload.
153 timeout (float or tuple) -- (optional) How long to wait for the
154 server to send data before giving up, as a float, or a (connect
155 timeout, read timeout) tuple.
156 allow_redirects (bool) -- (optional) Boolean. Set to True if POST/
157 PUT/DELETE redirect following is allowed.
158 proxies -- (optional) Dictionary mapping protocol to the URL of
160 verify -- (optional) whether the SSL cert will be verified.
161 A CA_BUNDLE path can also be provided. Defaults to True.
162 stream -- (optional) if False, the response content will be
163 immediately downloaded.
164 cert -- (optional) if String, path to ssl client cert file (.pem).
165 If Tuple, ('cert', 'key') pair.
169 :type enable_logging: bool
171 :return: Status code and content of response.
173 :raises HTTPRequestError: If
174 1. it is not possible to connect,
175 2. invalid HTTP response comes from server,
176 3. request exceeded the configured number of maximum re-directions,
177 4. request timed out,
178 5. there is any other unexpected HTTP request exception.
180 timeout = kwargs["timeout"]
182 use_odl = BuiltIn().get_variable_value("${use_odl_client}")
186 # Using default ODL Restconf port
187 # TODO: add node["honeycomb"]["odl_port"] to topology, use it here
188 odl_url_part = "/network-topology:network-topology/topology/" \
189 "topology-netconf/node/vpp/yang-ext:mount"
191 port = node["honeycomb"]["port"]
195 path = path.format(odl_url_part=odl_url_part)
199 url = HTTPRequest.create_full_url(node['host'],
203 auth = HTTPBasicAuth(node['honeycomb']['user'],
204 node['honeycomb']['passwd'])
205 rsp = request(method, url, auth=auth, verify=False, **kwargs)
207 logger.debug("Status code: {0}".format(rsp.status_code))
208 logger.debug("Response: {0}".format(rsp.content))
210 return rsp.status_code, rsp.content
212 except ConnectionError as err:
213 # Switching the logging on / off is needed only for
214 # "requests.ConnectionError"
215 raise HTTPRequestError("Not possible to connect to {0}:{1}.".
217 node['honeycomb']['port']),
218 repr(err), enable_logging=enable_logging)
219 except HTTPError as err:
220 raise HTTPRequestError("Invalid HTTP response from {0}.".
221 format(node['host']), repr(err))
222 except TooManyRedirects as err:
223 raise HTTPRequestError("Request exceeded the configured number "
224 "of maximum re-directions.", repr(err))
225 except Timeout as err:
226 raise HTTPRequestError("Request timed out. Timeout is set to {0}.".
227 format(timeout), repr(err))
228 except RequestException as err:
229 raise HTTPRequestError("Unexpected HTTP request exception.",
233 @keyword(name="HTTP Get")
234 def get(node, path, headers=None, timeout=15, enable_logging=True):
235 """Sends a GET request and returns the response and status code.
237 :param node: Honeycomb node.
238 :param path: URL path, e.g. /index.html.
239 :param headers: Dictionary of HTTP Headers to send with the Request.
240 :param timeout: How long to wait for the server to send data before
241 giving up, as a float, or a (connect timeout, read timeout) tuple.
242 :param enable_logging: Used to suppress errors when checking Honeycomb
243 state during suite setup and teardown. When True, logging is enabled,
244 otherwise logging is disabled.
248 :type timeout: float or tuple
249 :type enable_logging: bool
250 :return: Status code and content of response.
254 return HTTPRequest._http_request('GET', node, path,
255 enable_logging=enable_logging,
256 headers=headers, timeout=timeout)
259 @keyword(name="HTTP Put")
260 def put(node, path, headers=None, payload=None, json=None, timeout=15):
261 """Sends a PUT request and returns the response and status code.
263 :param node: Honeycomb node.
264 :param path: URL path, e.g. /index.html.
265 :param headers: Dictionary of HTTP Headers to send with the Request.
266 :param payload: Dictionary, bytes, or file-like object to send in
267 the body of the Request.
268 :param json: JSON formatted string to send in the body of the Request.
269 :param timeout: How long to wait for the server to send data before
270 giving up, as a float, or a (connect timeout, read timeout) tuple.
274 :type payload: dict, bytes, or file-like object
276 :type timeout: float or tuple
277 :return: Status code and content of response.
280 return HTTPRequest._http_request('PUT', node, path, headers=headers,
281 data=payload, json=json,
285 @keyword(name="HTTP Post")
286 def post(node, path, headers=None, payload=None, json=None, timeout=15,
287 enable_logging=True):
288 """Sends a POST request and returns the response and status code.
290 :param node: Honeycomb node.
291 :param path: URL path, e.g. /index.html.
292 :param headers: Dictionary of HTTP Headers to send with the Request.
293 :param payload: Dictionary, bytes, or file-like object to send in
294 the body of the Request.
295 :param json: JSON formatted string to send in the body of the Request.
296 :param timeout: How long to wait for the server to send data before
297 giving up, as a float, or a (connect timeout, read timeout) tuple.
298 :param enable_logging: Used to suppress errors when checking ODL
299 state during suite setup and teardown. When True, logging is enabled,
300 otherwise logging is disabled.
304 :type payload: dict, bytes, or file-like object
306 :type timeout: float or tuple
307 :type enable_logging: bool
308 :return: Status code and content of response.
311 return HTTPRequest._http_request('POST', node, path,
312 enable_logging=enable_logging,
313 headers=headers, data=payload,
314 json=json, timeout=timeout)
317 @keyword(name="HTTP Delete")
318 def delete(node, path, timeout=15):
319 """Sends a DELETE request and returns the response and status code.
321 :param node: Honeycomb node.
322 :param path: URL path, e.g. /index.html.
323 :param timeout: How long to wait for the server to send data before
324 giving up, as a float, or a (connect timeout, read timeout) tuple.
327 :type timeout: float or tuple
328 :return: Status code and content of response.
331 return HTTPRequest._http_request('DELETE', node, path, timeout=timeout)