Add details to HttpException string
Without this change:
400 Client Error: Bad Request
With this change, the much more useful:
HttpException: 400 Client Error: Bad Request, Validation error: instance 'flavorRef' is a required property
Change-Id: Iaa63969e327532c02d72999efcbee42aa0d381d3
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
Exception definitions.
|
||||
"""
|
||||
|
||||
import six
|
||||
|
||||
|
||||
class SDKException(Exception):
|
||||
"""The base exception class for all exceptions this library raises."""
|
||||
@@ -62,6 +64,15 @@ class HttpException(SDKException):
|
||||
super(HttpException, self).__init__(message)
|
||||
self.details = details
|
||||
|
||||
def __unicode__(self):
|
||||
msg = self.__class__.__name__ + ": " + self.message
|
||||
if self.details:
|
||||
msg += ", " + six.text_type(self.details)
|
||||
return msg
|
||||
|
||||
def __str__(self):
|
||||
return self.__unicode__()
|
||||
|
||||
|
||||
class MethodNotSupported(SDKException):
|
||||
"""The resource does not support this operation type."""
|
||||
|
||||
@@ -15,6 +15,7 @@ import logging
|
||||
|
||||
import fixtures
|
||||
import httpretty
|
||||
import mock
|
||||
import requests
|
||||
import six
|
||||
|
||||
@@ -634,3 +635,18 @@ class TestTransportRedirects(base.TestTransportBase):
|
||||
for r, s in zip(req_resp.history, resp.history):
|
||||
self.assertEqual(s.url, r.url)
|
||||
self.assertEqual(s.status_code, r.status_code)
|
||||
|
||||
def test_parse_error_response(self):
|
||||
xport = transport.Transport(redirect=True)
|
||||
resp = mock.Mock()
|
||||
resp.json = mock.Mock()
|
||||
resp.json.return_value = {"badRequest": {"message": "Not defined"}}
|
||||
self.assertEqual("Not defined", xport._parse_error_response(resp))
|
||||
resp.json.return_value = {"message": {"response": "Not Allowed"}}
|
||||
self.assertEqual("Not Allowed", xport._parse_error_response(resp))
|
||||
resp.json.return_value = {"itemNotFound": {"message": "Not found"}}
|
||||
self.assertEqual("Not found", xport._parse_error_response(resp))
|
||||
resp.json.return_value = {"instanceFault": {"message": "Wot?"}}
|
||||
self.assertEqual("Wot?", xport._parse_error_response(resp))
|
||||
resp.json.return_value = {"QuantumError": "Network error"}
|
||||
self.assertEqual("Network error", xport._parse_error_response(resp))
|
||||
|
||||
@@ -151,7 +151,8 @@ class Transport(requests.Session):
|
||||
try:
|
||||
resp.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
raise exceptions.HttpException(six.text_type(e), details=resp.text)
|
||||
raise exceptions.HttpException(six.text_type(e),
|
||||
self._parse_error_response(resp))
|
||||
if accept == JSON:
|
||||
try:
|
||||
resp.body = resp.json()
|
||||
@@ -202,6 +203,28 @@ class Transport(requests.Session):
|
||||
|
||||
return resp
|
||||
|
||||
def _parse_error_response(self, resp):
|
||||
try:
|
||||
jresp = resp.json()
|
||||
# compute
|
||||
if "badRequest" in jresp and "message" in jresp["badRequest"]:
|
||||
return jresp["badRequest"]["message"]
|
||||
# identity
|
||||
if "message" in jresp and "response" in jresp["message"]:
|
||||
return jresp["message"]["response"]
|
||||
# network
|
||||
if "QuantumError" in jresp:
|
||||
return jresp["QuantumError"]
|
||||
# database
|
||||
if "itemNotFound" in jresp and "message" in jresp["itemNotFound"]:
|
||||
return jresp["itemNotFound"]["message"]
|
||||
if "instanceFault" in jresp:
|
||||
if "message" in jresp["instanceFault"]:
|
||||
return jresp["instanceFault"]["message"]
|
||||
except ValueError:
|
||||
pass
|
||||
return resp.text
|
||||
|
||||
def _log_request(self, method, url, **kwargs):
|
||||
if not _logger.isEnabledFor(logging.DEBUG):
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user