diff --git a/ceilometerclient/common/http.py b/ceilometerclient/common/http.py index a02c8812..23353e03 100644 --- a/ceilometerclient/common/http.py +++ b/ceilometerclient/common/http.py @@ -170,7 +170,7 @@ class HTTPClient(object): if 400 <= resp.status < 600: LOG.warn("Request returned failure status.") - raise exc.from_response(resp) + raise exc.from_response(resp, ''.join(body_iter)) elif resp.status in (301, 302, 305): # Redirected. Reissue the request to the new location. return self._http_request(resp['location'], method, **kwargs) diff --git a/ceilometerclient/exc.py b/ceilometerclient/exc.py index 83e182d8..57c60c01 100644 --- a/ceilometerclient/exc.py +++ b/ceilometerclient/exc.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import json import sys @@ -65,7 +66,17 @@ class BadRequest(HTTPException): class HTTPBadRequest(BadRequest): - pass + + def __str__(self): + try: + data = json.loads(self.details) + message = data.get("error_message", {}).get("faultstring") + if message: + return "%s (HTTP %s) ERROR %s" % ( + self.__class__.__name__, self.code, message) + except (ValueError, TypeError, AttributeError): + pass + return super(HTTPBadRequest, self).__str__() class Unauthorized(HTTPException): @@ -147,10 +158,10 @@ for obj_name in dir(sys.modules[__name__]): _code_map[obj.code] = obj -def from_response(response): +def from_response(response, details=None): """Return an instance of an HTTPException based on httplib response.""" cls = _code_map.get(response.status, HTTPException) - return cls() + return cls(details) class NoTokenLookupException(Exception): diff --git a/ceilometerclient/tests/test_exc.py b/ceilometerclient/tests/test_exc.py new file mode 100644 index 00000000..53ae5124 --- /dev/null +++ b/ceilometerclient/tests/test_exc.py @@ -0,0 +1,51 @@ +# Copyright 2013 eNovance +# 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 json + +from ceilometerclient import exc + +from ceilometerclient.tests import utils + + +class HTTPBadRequestTest(utils.BaseTestCase): + + def test_str_no_details(self): + exception = exc.HTTPBadRequest() + self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception)) + + def test_str_no_json(self): + exception = exc.HTTPBadRequest(details="foo") + self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception)) + + def test_str_no_error_message(self): + exception = exc.HTTPBadRequest(details=json.dumps({})) + self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception)) + + def test_str_no_faultstring(self): + exception = exc.HTTPBadRequest( + details=json.dumps({"error_message": {"foo": "bar"}})) + self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception)) + + def test_str_error_message_unknown_format(self): + exception = exc.HTTPBadRequest( + details=json.dumps({"error_message": "oops"})) + self.assertEqual("HTTPBadRequest (HTTP 400)", str(exception)) + + def test_str_faultstring(self): + exception = exc.HTTPBadRequest( + details=json.dumps({"error_message": {"faultstring": "oops"}})) + self.assertEqual("HTTPBadRequest (HTTP 400) ERROR oops", + str(exception))