Evaluate lazy translation in exception __str__

The OpenStackException.__str__ method returns
self._error_string which is actually a gettextutil.Message
object not a str.

str(o) will check the return type of o.__str__() and raise
TypeError if __str__ returns non-string object.

The fix will not go into oslo as OpenStackException is
about to be deprecated. And this patch is also the first
step to remove all the references to oslo exception.py.

Fixes bug #1208454

Change-Id: Ib6abe3517bcb1b34fc6a71e5c46ee3774e067f9a
This commit is contained in:
Liang Chen 2013-08-08 18:11:48 +08:00
parent 1c20d351b1
commit a67f85267a
5 changed files with 113 additions and 51 deletions

View File

@ -89,13 +89,7 @@ class FaultWrapper(wsgi.Middleware):
if ex_type.endswith(rpc_common._REMOTE_POSTFIX):
ex_type = ex_type[:-len(rpc_common._REMOTE_POSTFIX)]
if isinstance(ex, exception.OpenstackException):
# If the exception is an OpenstackException it is going to have a
# translated Message object as the message, let's recreate it here
message = (ex.message % ex.kwargs
if hasattr(ex, 'kwargs') else ex.message)
else:
message = ex.message
message = str(ex.message)
if not trace:
trace = str(ex)

View File

@ -20,14 +20,20 @@
import functools
import urlparse
import sys
from heat.openstack.common import exception
from heat.openstack.common import log as logging
OpenstackException = exception.OpenstackException
NotFound = exception.NotFound
Error = exception.Error
InvalidContentType = exception.InvalidContentType
_FATAL_EXCEPTION_FORMAT_ERRORS = False
logger = logging.getLogger(__name__)
class RedirectException(Exception):
def __init__(self, url):
@ -90,32 +96,62 @@ def wrap_exception(notifier=None, publisher_id=None, event_type=None,
return inner
class MissingCredentialError(OpenstackException):
class HeatException(Exception):
"""Base Heat Exception
To correctly use this class, inherit from it and define
a 'msg_fmt' property. That msg_fmt will get printf'd
with the keyword arguments provided to the constructor.
"""
message = _("An unknown exception occurred.")
def __init__(self, **kwargs):
self.kwargs = kwargs
try:
self.message = self.message % kwargs
except KeyError:
exc_info = sys.exc_info()
#kwargs doesn't match a variable in the message
#log the issue and the kwargs
logger.exception(_('Exception in string format operation'))
for name, value in kwargs.iteritems():
logger.error("%s: %s" % (name, value))
if _FATAL_EXCEPTION_FORMAT_ERRORS:
raise exc_info[0], exc_info[1], exc_info[2]
def __str__(self):
return str(self.message)
class MissingCredentialError(HeatException):
message = _("Missing required credential: %(required)s")
class BadAuthStrategy(OpenstackException):
class BadAuthStrategy(HeatException):
message = _("Incorrect auth strategy, expected \"%(expected)s\" but "
"received \"%(received)s\"")
class AuthBadRequest(OpenstackException):
class AuthBadRequest(HeatException):
message = _("Connect error/bad request to Auth service at URL %(url)s.")
class AuthUrlNotFound(OpenstackException):
class AuthUrlNotFound(HeatException):
message = _("Auth service at URL %(url)s not found.")
class AuthorizationFailure(OpenstackException):
class AuthorizationFailure(HeatException):
message = _("Authorization failed.")
class NotAuthenticated(OpenstackException):
class NotAuthenticated(HeatException):
message = _("You are not authenticated.")
class Forbidden(OpenstackException):
class Forbidden(HeatException):
message = _("You are not authorized to complete this action.")
@ -124,25 +160,25 @@ class NotAuthorized(Forbidden):
message = _("You are not authorized to complete this action.")
class Invalid(OpenstackException):
class Invalid(HeatException):
message = _("Data supplied was not valid: %(reason)s")
class AuthorizationRedirect(OpenstackException):
class AuthorizationRedirect(HeatException):
message = _("Redirecting to %(uri)s for authorization.")
class ClientConfigurationError(OpenstackException):
class ClientConfigurationError(HeatException):
message = _("There was an error configuring the client.")
class MultipleChoices(OpenstackException):
class MultipleChoices(HeatException):
message = _("The request returned a 302 Multiple Choices. This generally "
"means that you have not included a version indicator in a "
"request URI.\n\nThe body of response returned:\n%(body)s")
class LimitExceeded(OpenstackException):
class LimitExceeded(HeatException):
message = _("The request returned a 413 Request Entity Too Large. This "
"generally means that rate limiting or a quota threshold was "
"breached.\n\nThe response body:\n%(body)s")
@ -153,7 +189,7 @@ class LimitExceeded(OpenstackException):
super(LimitExceeded, self).__init__(*args, **kwargs)
class ServiceUnavailable(OpenstackException):
class ServiceUnavailable(HeatException):
message = _("The request returned a 503 ServiceUnavilable. This "
"generally occurs on service overload or other transient "
"outage.")
@ -164,106 +200,106 @@ class ServiceUnavailable(OpenstackException):
super(ServiceUnavailable, self).__init__(*args, **kwargs)
class RequestUriTooLong(OpenstackException):
class RequestUriTooLong(HeatException):
message = _("The URI was too long.")
class ServerError(OpenstackException):
class ServerError(HeatException):
message = _("The request returned 500 Internal Server Error"
"\n\nThe response body:\n%(body)s")
class MaxRedirectsExceeded(OpenstackException):
class MaxRedirectsExceeded(HeatException):
message = _("Maximum redirects (%(redirects)s) was exceeded.")
class InvalidRedirect(OpenstackException):
class InvalidRedirect(HeatException):
message = _("Received invalid HTTP redirect.")
class NoServiceEndpoint(OpenstackException):
class NoServiceEndpoint(HeatException):
message = _("Response from Keystone does not contain a Heat endpoint.")
class RegionAmbiguity(OpenstackException):
class RegionAmbiguity(HeatException):
message = _("Multiple 'image' service matches for region %(region)s. This "
"generally means that a region is required and you have not "
"supplied one.")
class UserParameterMissing(OpenstackException):
class UserParameterMissing(HeatException):
message = _("The Parameter (%(key)s) was not provided.")
class UnknownUserParameter(OpenstackException):
class UnknownUserParameter(HeatException):
message = _("The Parameter (%(key)s) was not defined in template.")
class InvalidTemplateAttribute(OpenstackException):
class InvalidTemplateAttribute(HeatException):
message = _("The Referenced Attribute (%(resource)s %(key)s)"
" is incorrect.")
class InvalidTemplateReference(OpenstackException):
class InvalidTemplateReference(HeatException):
message = _("The specified reference (%(resource)s %(key)s)"
" is incorrect.")
class UserKeyPairMissing(OpenstackException):
class UserKeyPairMissing(HeatException):
message = _("The Key (%(key_name)s) could not be found.")
class FlavorMissing(OpenstackException):
class FlavorMissing(HeatException):
message = _("The Flavor ID (%(flavor_id)s) could not be found.")
class ImageNotFound(OpenstackException):
class ImageNotFound(HeatException):
message = _("The Image (%(image_name)s) could not be found.")
class NoUniqueImageFound(OpenstackException):
class NoUniqueImageFound(HeatException):
message = _("Multiple images were found with name (%(image_name)s).")
class InvalidTenant(OpenstackException):
class InvalidTenant(HeatException):
message = _("Searching Tenant %(target)s "
"from Tenant %(actual)s forbidden.")
class StackNotFound(OpenstackException):
class StackNotFound(HeatException):
message = _("The Stack (%(stack_name)s) could not be found.")
class StackExists(OpenstackException):
class StackExists(HeatException):
message = _("The Stack (%(stack_name)s) already exists.")
class StackValidationFailed(OpenstackException):
class StackValidationFailed(HeatException):
message = _("%(message)s")
class ResourceNotFound(OpenstackException):
class ResourceNotFound(HeatException):
message = _("The Resource (%(resource_name)s) could not be found "
"in Stack %(stack_name)s.")
class ResourceTypeNotFound(OpenstackException):
class ResourceTypeNotFound(HeatException):
message = _("The Resource Type (%(type_name)s) could not be found.")
class ResourceNotAvailable(OpenstackException):
class ResourceNotAvailable(HeatException):
message = _("The Resource (%(resource_name)s) is not available.")
class PhysicalResourceNotFound(OpenstackException):
class PhysicalResourceNotFound(HeatException):
message = _("The Resource (%(resource_id)s) could not be found.")
class WatchRuleNotFound(OpenstackException):
class WatchRuleNotFound(HeatException):
message = _("The Watch Rule (%(watch_name)s) could not be found.")
class ResourceFailure(OpenstackException):
class ResourceFailure(HeatException):
message = _("%(exc_type)s: %(message)s")
def __init__(self, exception, resource, action=None):
@ -277,11 +313,11 @@ class ResourceFailure(OpenstackException):
message=str(exception))
class NotSupported(OpenstackException):
class NotSupported(HeatException):
message = _("%(feature)s is not supported.")
class ResourcePropertyConflict(OpenstackException):
class ResourcePropertyConflict(HeatException):
message = _('Cannot define the following properties at the same time: %s.')
def __init__(self, *args):
@ -299,5 +335,5 @@ class HTTPExceptionDisguise(Exception):
self.tb = sys.exc_info()[2]
class TemplateTooBig(OpenstackException):
class TemplateTooBig(HeatException):
message = _('Template exceeds maximum allowed size.')

View File

@ -21,7 +21,7 @@ from heat.common import exception
from heat.openstack.common.gettextutils import _
class CircularDependencyException(exception.OpenstackException):
class CircularDependencyException(exception.HeatException):
message = _("Circular Dependency Found: %(cycle)s")

View File

@ -20,7 +20,6 @@ except ImportError:
pass
from heat.openstack.common import log as logging
from heat.openstack.common.exception import OpenstackException
from heat.openstack.common.gettextutils import _
from heat.engine import scheduler
from heat.engine.properties import Properties
@ -30,7 +29,7 @@ from heat.common import exception
logger = logging.getLogger(__name__)
class LoadbalancerBuildError(OpenstackException):
class LoadbalancerBuildError(exception.HeatException):
message = _("There was an error building the loadbalancer:%(lb_name)s.")

View File

@ -0,0 +1,33 @@
# Copyright 2012 OpenStack, LLC
# 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 fixtures
from heat.common import exception
from heat.tests import common
class TestException(exception.HeatException):
message = _("Testing message %(text)s")
class TestHeatException(common.HeatTestCase):
def test_fatal_exception_error(self):
self.useFixture(fixtures.MonkeyPatch(
'heat.common.exception._FATAL_EXCEPTION_FORMAT_ERRORS',
True))
self.assertRaises(KeyError, TestException)