From 26c082f466783faab8492395adf707457293591b Mon Sep 17 00:00:00 2001 From: Terry Howe Date: Sun, 14 Sep 2014 09:28:44 -0600 Subject: [PATCH] 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 --- openstack/exceptions.py | 11 +++++++++++ openstack/tests/test_transport.py | 16 ++++++++++++++++ openstack/transport.py | 25 ++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/openstack/exceptions.py b/openstack/exceptions.py index a560040f..06c3c709 100644 --- a/openstack/exceptions.py +++ b/openstack/exceptions.py @@ -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.""" diff --git a/openstack/tests/test_transport.py b/openstack/tests/test_transport.py index 1bb54d60..a9121ce6 100644 --- a/openstack/tests/test_transport.py +++ b/openstack/tests/test_transport.py @@ -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)) diff --git a/openstack/transport.py b/openstack/transport.py index 2ac884b7..b410844a 100644 --- a/openstack/transport.py +++ b/openstack/transport.py @@ -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