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 enum import IntEnum, unique
23 from robot.api.deco import keyword
24 from robot.api import logger
25 from robot.libraries.BuiltIn import BuiltIn
27 from requests import request, RequestException, Timeout, TooManyRedirects, \
28 HTTPError, ConnectionError
29 from requests.auth import HTTPBasicAuth
33 class HTTPCodes(IntEnum):
34 """HTTP status codes"""
41 INTERNAL_SERVER_ERROR = 500
42 SERVICE_UNAVAILABLE = 503
45 class HTTPRequestError(Exception):
46 """Exception raised by HTTPRequest objects.
48 When raising this exception, put this information to the message in this
50 - short description of the encountered problem,
51 - relevant messages if there are any collected, e.g., from caught
53 - relevant data if there are any collected.
54 The logging is performed on two levels: 1. error - short description of the
55 problem; 2. debug - detailed information.
58 def __init__(self, msg, details='', enable_logging=True):
59 """Sets the exception message and enables / disables logging.
61 It is not wanted to log errors when using these keywords together
62 with keywords like "Wait until keyword succeeds". So you can disable
63 logging by setting enable_logging to False.
65 :param msg: Message to be displayed and logged.
66 :param enable_logging: When True, logging is enabled, otherwise
69 :type enable_logging: bool
71 super(HTTPRequestError, self).__init__()
72 self._msg = "{0}: {1}".format(self.__class__.__name__, msg)
73 self._details = details
75 logger.error(self._msg)
76 logger.debug(self._details)
79 return repr(self._msg)
85 class HTTPRequest(object):
86 """A class implementing HTTP requests GET, PUT, POST and DELETE used in
87 communication with Honeycomb.
89 The communication with Honeycomb and processing of all exceptions is done in
90 the method _http_request which uses requests.request to send requests and
91 receive responses. The received status code and content of response are
92 logged on the debug level.
93 All possible exceptions raised by requests.request are also processed there.
95 The other methods (get, put, post and delete) use _http_request to send
96 corresponding request.
98 These methods must not be used as keywords in tests. Use keywords
99 implemented in the module HoneycombAPIKeywords instead.
106 def create_full_url(ip_addr, port, path):
107 """Creates full url including host, port, and path to data.
109 :param ip_addr: Server IP.
110 :param port: Communication port.
111 :param path: Path to data.
113 :type port: str or int
118 return "http://{ip}:{port}{path}".format(ip=ip_addr, port=port,
122 def _http_request(method, node, path, enable_logging=True, **kwargs):
123 """Sends specified HTTP request and returns status code and response
126 :param method: The method to be performed on the resource identified by
127 the given request URI.
128 :param node: Honeycomb node.
129 :param path: URL path, e.g. /index.html.
130 :param enable_logging: Used to suppress errors when checking Honeycomb
131 state during suite setup and teardown.
132 :param kwargs: Named parameters accepted by request.request:
133 params -- (optional) Dictionary or bytes to be sent in the query
134 string for the Request.
135 data -- (optional) Dictionary, bytes, or file-like object to
136 send in the body of the Request.
137 json -- (optional) json data to send in the body of the Request.
138 headers -- (optional) Dictionary of HTTP Headers to send with
140 cookies -- (optional) Dict or CookieJar object to send with the
142 files -- (optional) Dictionary of 'name': file-like-objects
143 (or {'name': ('filename', fileobj)}) for multipart encoding upload.
144 timeout (float or tuple) -- (optional) How long to wait for the
145 server to send data before giving up, as a float, or a (connect
146 timeout, read timeout) tuple.
147 allow_redirects (bool) -- (optional) Boolean. Set to True if POST/
148 PUT/DELETE redirect following is allowed.
149 proxies -- (optional) Dictionary mapping protocol to the URL of
151 verify -- (optional) whether the SSL cert will be verified.
152 A CA_BUNDLE path can also be provided. Defaults to True.
153 stream -- (optional) if False, the response content will be
154 immediately downloaded.
155 cert -- (optional) if String, path to ssl client cert file (.pem).
156 If Tuple, ('cert', 'key') pair.
160 :type enable_logging: bool
162 :return: Status code and content of response.
164 :raises HTTPRequestError: If
165 1. it is not possible to connect,
166 2. invalid HTTP response comes from server,
167 3. request exceeded the configured number of maximum re-directions,
168 4. request timed out,
169 5. there is any other unexpected HTTP request exception.
171 timeout = kwargs["timeout"]
173 if BuiltIn().get_variable_value("${use_odl_client}"):
174 # TODO: node["honeycomb"]["odl_port"]
176 odl_url_part = "/network-topology:network-topology/topology/" \
177 "topology-netconf/node/vpp/yang-ext:mount"
179 port = node["honeycomb"]["port"]
183 path = path.format(odl_url_part=odl_url_part)
187 url = HTTPRequest.create_full_url(node['host'],
191 auth = HTTPBasicAuth(node['honeycomb']['user'],
192 node['honeycomb']['passwd'])
193 rsp = request(method, url, auth=auth, **kwargs)
195 logger.debug("Status code: {0}".format(rsp.status_code))
196 logger.debug("Response: {0}".format(rsp.content))
198 return rsp.status_code, rsp.content
200 except ConnectionError as err:
201 # Switching the logging on / off is needed only for
202 # "requests.ConnectionError"
203 raise HTTPRequestError("Not possible to connect to {0}:{1}.".
205 node['honeycomb']['port']),
206 repr(err), enable_logging=enable_logging)
207 except HTTPError as err:
208 raise HTTPRequestError("Invalid HTTP response from {0}.".
209 format(node['host']), repr(err))
210 except TooManyRedirects as err:
211 raise HTTPRequestError("Request exceeded the configured number "
212 "of maximum re-directions.", repr(err))
213 except Timeout as err:
214 raise HTTPRequestError("Request timed out. Timeout is set to {0}.".
215 format(timeout), repr(err))
216 except RequestException as err:
217 raise HTTPRequestError("Unexpected HTTP request exception.",
221 @keyword(name="HTTP Get")
222 def get(node, path, headers=None, timeout=10, enable_logging=True):
223 """Sends a GET request and returns the response and status code.
225 :param node: Honeycomb node.
226 :param path: URL path, e.g. /index.html.
227 :param headers: Dictionary of HTTP Headers to send with the Request.
228 :param timeout: How long to wait for the server to send data before
229 giving up, as a float, or a (connect timeout, read timeout) tuple.
230 :param enable_logging: Used to suppress errors when checking Honeycomb
231 state during suite setup and teardown. When True, logging is enabled,
232 otherwise logging is disabled.
236 :type timeout: float or tuple
237 :type enable_logging: bool
238 :return: Status code and content of response.
242 return HTTPRequest._http_request('GET', node, path,
243 enable_logging=enable_logging,
244 headers=headers, timeout=timeout)
247 @keyword(name="HTTP Put")
248 def put(node, path, headers=None, payload=None, json=None, timeout=10):
249 """Sends a PUT request and returns the response and status code.
251 :param node: Honeycomb node.
252 :param path: URL path, e.g. /index.html.
253 :param headers: Dictionary of HTTP Headers to send with the Request.
254 :param payload: Dictionary, bytes, or file-like object to send in
255 the body of the Request.
256 :param json: JSON formatted string to send in the body of the Request.
257 :param timeout: How long to wait for the server to send data before
258 giving up, as a float, or a (connect timeout, read timeout) tuple.
262 :type payload: dict, bytes, or file-like object
264 :type timeout: float or tuple
265 :return: Status code and content of response.
268 return HTTPRequest._http_request('PUT', node, path, headers=headers,
269 data=payload, json=json,
273 @keyword(name="HTTP Post")
274 def post(node, path, headers=None, payload=None, json=None, timeout=10,
275 enable_logging=True):
276 """Sends a POST request and returns the response and status code.
278 :param node: Honeycomb node.
279 :param path: URL path, e.g. /index.html.
280 :param headers: Dictionary of HTTP Headers to send with the Request.
281 :param payload: Dictionary, bytes, or file-like object to send in
282 the body of the Request.
283 :param json: JSON formatted string to send in the body of the Request.
284 :param timeout: How long to wait for the server to send data before
285 giving up, as a float, or a (connect timeout, read timeout) tuple.
286 :param enable_logging: Used to suppress errors when checking ODL
287 state during suite setup and teardown. When True, logging is enabled,
288 otherwise logging is disabled.
292 :type payload: dict, bytes, or file-like object
294 :type timeout: float or tuple
295 :type enable_logging: bool
296 :return: Status code and content of response.
299 return HTTPRequest._http_request('POST', node, path,
300 enable_logging=enable_logging,
301 headers=headers, data=payload,
302 json=json, timeout=timeout)
305 @keyword(name="HTTP Delete")
306 def delete(node, path, timeout=10):
307 """Sends a DELETE request and returns the response and status code.
309 :param node: Honeycomb node.
310 :param path: URL path, e.g. /index.html.
311 :param timeout: How long to wait for the server to send data before
312 giving up, as a float, or a (connect timeout, read timeout) tuple.
315 :type timeout: float or tuple
316 :return: Status code and content of response.
319 return HTTPRequest._http_request('DELETE', node, path, timeout=timeout)