Files
Anusha Ramineni f5b935a7e1 Fix exceptions to be displayed properly
Valence exception messages are not displayed properly. This commit
is to fix the same.

Change-Id: If4da51f736d28e60a3cfa78094b2e60bedecdd83
Closes-Bug:#1715580
2017-09-18 10:36:05 +05:30

177 lines
6.1 KiB
Python

# Copyright 2017 99cloud, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import functools
import logging
import requests
import time
import six
from six.moves import http_client
import six.moves.urllib.parse as urlparse
from oslo_serialization import jsonutils
from oslo_utils import strutils
from valenceclient.common.i18n import _
from valenceclient.common.i18n import _LE
from valenceclient import exc
LOG = logging.getLogger(__name__)
USER_AGENT = 'python-valenceclient'
API_VERSION = '/v1'
DEFAULT_VERSION = 1
DEFAULT_MAX_RETRIES = 5
DEFAULT_RETRY_INTERVAL = 2
def _trim_endpoint_api_version(url):
"""Trim API version and trailing slash from endpoint."""
return url.rstrip('/').rstip(API_VERSION)
def _extract_error_json(body):
return jsonutils.loads(body)
def with_retries(func):
"""Wrapper for _http_request adding support for retries."""
@functools.wraps(func)
def wrapper(self, url, method, **kwargs):
if self.conflict_max_retries is None:
self.conflict_max_retries = DEFAULT_MAX_RETRIES
if self.conflict_retry_interval is None:
self.conflict_retry_interval = DEFAULT_RETRY_INTERVAL
num_attempts = self.conflict_max_retries + 1
for attempt in range(1, num_attempts + 1):
try:
return func(url, method, **kwargs)
except Exception as error:
msg = (_LE("Error contacting Valence server: %(error)s."
"Attempt %(attempt)d of %(total)d") %
{'attempt': attempt,
'total': num_attempts,
'error': error})
if attempt == num_attempts:
LOG.error(msg)
else:
LOG.debug(msg)
time.sleep(self.conflict_retry_interval)
return wrapper
class HTTPClient(object):
def __init__(self, **kwargs):
self.valence_api_version = kwargs.get('valence_api_version',
DEFAULT_VERSION)
self.valence_url = kwargs.get('valence_url')
self.session = requests.Session()
def log_curl_request(self, method, url, kwargs):
curl = ['curl -i -X %s' % method]
for (key, value) in kwargs['headers'].items():
header = "-H '%s: %s'" % (key, value)
curl.append(header)
if 'body' in kwargs:
body = strutils.mask_password(kwargs['body'])
curl.append("-d '%s'" % body)
curl.append(urlparse.urljoin('v1', self.valence_url))
LOG.debug(" ".join(curl))
@staticmethod
def log_http_response(resp, body=None):
status = (resp.raw.version / 10.0, resp.status_code, resp.reason)
dump = ['\nHTTP/%.1f %s %s' % status]
dump.extend(['%s: %s' % (k, v) for k, v in resp.headers.items()])
dump.append('')
if body:
body = strutils.mask_password(body)
dump.extend([body, ''])
LOG.debug('\n'.join(dump))
def _make_connection_url(self, url):
return urlparse.urljoin(self.valence_url, url)
def _http_request(self, conn_url, method, **kwargs):
"""Send an http request with the specified characteristics
Wrapper around request.Session.request to handle tasks such as
setting headers and error handling.
"""
kwargs['headers'] = kwargs.get('headers', {})
kwargs['headers'].setdefault('User-agent', USER_AGENT)
self.log_curl_request(method, conn_url, kwargs)
body = kwargs.pop('body', None)
headers = kwargs.pop('headers')
conn_url = self._make_connection_url(conn_url)
try:
resp = self.session.request(method, conn_url, headers=headers,
data=body, json=kwargs)
except requests.exceptions.RequestException as e:
msg = (_("Error has occured while handling request for "
"%(url)s: %(e)s") % dict(url=conn_url, e=e))
if isinstance(e, ValueError):
raise exc.ValidationError(msg)
raise exc.ConnectionRefuse(msg)
self.log_http_response(resp, resp.text)
body_iter = six.StringIO(resp.text)
if resp.status_code >= http_client.BAD_REQUEST:
error_json = _extract_error_json(resp.text)
raise exc.from_response(resp, error_json, method, conn_url)
elif resp.status_code in (http_client.FOUND,
http_client.USE_PROXY):
return self._http_request(resp['location'], method, **kwargs)
return resp, body_iter
def json_request(self, method, conn_url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
kwargs['headers'].setdefault('Accept', 'application/json')
if 'body' in kwargs:
kwargs['body'] = jsonutils.dump_as_bytes(kwargs['body'])
resp, body_iter = self._http_request(conn_url, method, **kwargs)
content_type = resp.headers.get('Content-Type')
if(resp.status_code in (http_client.NO_CONTENT,
http_client.RESET_CONTENT)
or content_type is None):
return resp, list()
if 'application/json' in content_type:
body = ''.join([chunk for chunk in body_iter])
try:
body = jsonutils.loads(body)
except ValueError:
LOG.error(_LE('Could not decode response body as JSON'))
else:
body = None
return resp, body