Add support for API message localization
Add support for doing language resolution for a request, based on the Accept-Language HTTP header. For example, an HTTP client can receive API messages in Chinese even if the locale language of the server is English. The underscore (_) method is initialized so that it returns openstack.common.gettextutils.Message objects. The locale of these Message objects is set when exceptions with which they are associated are raised in the context of an HTTP request. docImpact Partially implements bp i18n-messages Signed-off-by: Fei Long Wang <flwang@cn.ibm.com> Signed-off-by: John Warren <jswarren@us.ibm.com> Change-Id: I352cda57fe119022c59c6c813b5c8053765b2d3c
This commit is contained in:
parent
bfddf66ec4
commit
f70b72b78b
|
@ -1066,7 +1066,7 @@ class ImageDeserializer(wsgi.JSONRequestDeserializer):
|
|||
except exception.InvalidParameterValue as e:
|
||||
msg = unicode(e)
|
||||
LOG.warn(msg, exc_info=True)
|
||||
raise HTTPBadRequest(explanation=msg, request=request)
|
||||
raise HTTPBadRequest(explanation=e.msg, request=request)
|
||||
|
||||
image_meta = result['image_meta']
|
||||
image_meta = validate_image_meta(request, image_meta)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# under the License.
|
||||
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
import webob.exc
|
||||
|
||||
from glance.api import policy
|
||||
|
@ -94,13 +95,11 @@ class Controller(controller.BaseController):
|
|||
registry.delete_member(req.context, image_id, id)
|
||||
self._update_store_acls(req, image_id)
|
||||
except exception.NotFound as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
return webob.exc.HTTPNoContent()
|
||||
|
||||
|
@ -152,17 +151,14 @@ class Controller(controller.BaseController):
|
|||
registry.add_member(req.context, image_id, id, can_share)
|
||||
self._update_store_acls(req, image_id)
|
||||
except exception.Invalid as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
return webob.exc.HTTPNoContent()
|
||||
|
||||
|
@ -190,17 +186,14 @@ class Controller(controller.BaseController):
|
|||
registry.replace_members(req.context, image_id, body)
|
||||
self._update_store_acls(req, image_id)
|
||||
except exception.Invalid as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
return webob.exc.HTTPNoContent()
|
||||
|
||||
|
@ -220,13 +213,11 @@ class Controller(controller.BaseController):
|
|||
try:
|
||||
members = registry.get_member_images(req.context, id)
|
||||
except exception.NotFound as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPNotFound(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
msg = "%s" % e
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPForbidden(msg)
|
||||
LOG.debug(six.text_type(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
return dict(shared_images=members)
|
||||
|
||||
def _update_store_acls(self, req, image_id):
|
||||
|
|
|
@ -92,7 +92,7 @@ class ImageDataController(object):
|
|||
except exception.InvalidImageStatusTransition as e:
|
||||
msg = unicode(e)
|
||||
LOG.debug(msg)
|
||||
raise webob.exc.HTTPConflict(explanation=msg, request=req)
|
||||
raise webob.exc.HTTPConflict(explanation=e.msg, request=req)
|
||||
|
||||
except exception.Forbidden as e:
|
||||
msg = (_("Not allowed to upload image data for image %s") %
|
||||
|
@ -101,7 +101,7 @@ class ImageDataController(object):
|
|||
raise webob.exc.HTTPForbidden(explanation=msg, request=req)
|
||||
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
except exception.StorageFull as e:
|
||||
msg = _("Image storage media is full: %s") % e
|
||||
|
@ -149,11 +149,11 @@ class ImageDataController(object):
|
|||
if not image.locations:
|
||||
raise exception.ImageDataNotFound()
|
||||
except exception.ImageDataNotFound as e:
|
||||
raise webob.exc.HTTPNoContent(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNoContent(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
|
||||
return image
|
||||
|
||||
|
@ -162,8 +162,8 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
|||
def upload(self, request):
|
||||
try:
|
||||
request.get_content_type(('application/octet-stream',))
|
||||
except exception.InvalidContentType:
|
||||
raise webob.exc.HTTPUnsupportedMediaType()
|
||||
except exception.InvalidContentType as e:
|
||||
raise webob.exc.HTTPUnsupportedMediaType(explanation=e.msg)
|
||||
|
||||
image_size = request.content_length or None
|
||||
return {'size': image_size, 'data': request.body_file}
|
||||
|
@ -178,7 +178,7 @@ class ResponseSerializer(wsgi.JSONResponseSerializer):
|
|||
# an iterator very strange
|
||||
response.app_iter = iter(image.get_data())
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
#NOTE(saschpe): "response.app_iter = ..." currently resets Content-MD5
|
||||
# (https://github.com/Pylons/webob/issues/86), so it should be set
|
||||
# afterwards for the time being.
|
||||
|
|
|
@ -67,13 +67,13 @@ class ImageMembersController(object):
|
|||
|
||||
return new_member
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.Duplicate as e:
|
||||
raise webob.exc.HTTPConflict(explanation=unicode(e))
|
||||
raise webob.exc.HTTPConflict(explanation=e.msg)
|
||||
except exception.ImageMemberLimitExceeded as e:
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=unicode(e))
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=e.msg)
|
||||
|
||||
@utils.mutating
|
||||
def update(self, req, image_id, member_id, status):
|
||||
|
@ -100,9 +100,9 @@ class ImageMembersController(object):
|
|||
member_repo.save(member)
|
||||
return member
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except ValueError as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
|
||||
|
@ -132,9 +132,9 @@ class ImageMembersController(object):
|
|||
members.append(member)
|
||||
return dict(members=members)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
|
||||
def show(self, req, image_id, member_id):
|
||||
"""
|
||||
|
@ -157,7 +157,7 @@ class ImageMembersController(object):
|
|||
member = member_repo.get(member_id)
|
||||
return member
|
||||
except (exception.NotFound, exception.Forbidden) as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
@utils.mutating
|
||||
def delete(self, req, image_id, member_id):
|
||||
|
@ -173,9 +173,9 @@ class ImageMembersController(object):
|
|||
member_repo.remove(member)
|
||||
return webob.Response(body='', status=204)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
|
||||
|
||||
class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
||||
|
|
|
@ -43,11 +43,11 @@ class Controller(object):
|
|||
image.tags.add(tag_value)
|
||||
image_repo.save(image)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.ImageTagLimitExceeded as e:
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=unicode(e))
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(explanation=e.msg)
|
||||
|
||||
@utils.mutating
|
||||
def delete(self, req, image_id, tag_value):
|
||||
|
@ -59,9 +59,9 @@ class Controller(object):
|
|||
image.tags.remove(tag_value)
|
||||
image_repo.save(image)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
|
||||
|
||||
class ResponseSerializer(wsgi.JSONResponseSerializer):
|
||||
|
|
|
@ -60,15 +60,15 @@ class ImagesController(object):
|
|||
tags=tags, **image)
|
||||
image_repo.add(image)
|
||||
except exception.DuplicateLocation as dup:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(dup))
|
||||
raise webob.exc.HTTPBadRequest(explanation=dup.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.InvalidParameterValue as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
except exception.LimitExceeded as e:
|
||||
LOG.info(unicode(e))
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(
|
||||
explanation=unicode(e), request=req, content_type='text/plain')
|
||||
explanation=e.msg, request=req, content_type='text/plain')
|
||||
|
||||
return image
|
||||
|
||||
|
@ -93,9 +93,9 @@ class ImagesController(object):
|
|||
result['next_marker'] = images[-1].image_id
|
||||
except (exception.NotFound, exception.InvalidSortKey,
|
||||
exception.InvalidFilterRangeValue) as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
result['images'] = images
|
||||
return result
|
||||
|
||||
|
@ -104,9 +104,9 @@ class ImagesController(object):
|
|||
try:
|
||||
return image_repo.get(image_id)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
|
||||
@utils.mutating
|
||||
def update(self, req, image_id, changes):
|
||||
|
@ -123,11 +123,11 @@ class ImagesController(object):
|
|||
if changes:
|
||||
image_repo.save(image)
|
||||
except exception.NotFound as e:
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.InvalidParameterValue as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
except exception.StorageQuotaFull as e:
|
||||
msg = (_("Denying attempt to upload image because it exceeds the ."
|
||||
"quota: %s") % e)
|
||||
|
@ -137,7 +137,7 @@ class ImagesController(object):
|
|||
except exception.LimitExceeded as e:
|
||||
LOG.info(unicode(e))
|
||||
raise webob.exc.HTTPRequestEntityTooLarge(
|
||||
explanation=unicode(e), request=req, content_type='text/plain')
|
||||
explanation=e.msg, request=req, content_type='text/plain')
|
||||
|
||||
return image
|
||||
|
||||
|
@ -192,7 +192,7 @@ class ImagesController(object):
|
|||
image.delete()
|
||||
image_repo.remove(image)
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
except exception.NotFound as e:
|
||||
msg = (_("Failed to find image %(image_id)s to delete") %
|
||||
{'image_id': image_id})
|
||||
|
@ -228,7 +228,7 @@ class ImagesController(object):
|
|||
if image.status == 'queued':
|
||||
image.status = 'active'
|
||||
except (exception.BadStoreUri, exception.DuplicateLocation) as bse:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(bse))
|
||||
raise webob.exc.HTTPBadRequest(explanation=bse.msg)
|
||||
except ValueError as ve: # update image status failed.
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(ve))
|
||||
|
||||
|
@ -243,7 +243,7 @@ class ImagesController(object):
|
|||
if image.status == 'queued':
|
||||
image.status = 'active'
|
||||
except (exception.BadStoreUri, exception.DuplicateLocation) as bse:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(bse))
|
||||
raise webob.exc.HTTPBadRequest(explanation=bse.msg)
|
||||
except ValueError as ve: # update image status failed.
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(ve))
|
||||
|
||||
|
@ -301,7 +301,7 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
|||
try:
|
||||
self.schema.validate(body)
|
||||
except exception.InvalidObject as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
image = {}
|
||||
properties = body
|
||||
tags = properties.pop('tags', None)
|
||||
|
@ -418,7 +418,7 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
|||
try:
|
||||
self.schema.validate(partial_image)
|
||||
except exception.InvalidObject as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
|
||||
def _validate_path(self, op, path):
|
||||
path_root = path[0]
|
||||
|
@ -595,7 +595,7 @@ class ResponseSerializer(wsgi.JSONResponseSerializer):
|
|||
image_view['schema'] = '/v2/schemas/image'
|
||||
image_view = self.schema.filter(image_view) # domain
|
||||
except exception.Forbidden as e:
|
||||
raise webob.exc.HTTPForbidden(unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
return image_view
|
||||
|
||||
def create(self, response, image):
|
||||
|
|
|
@ -66,7 +66,7 @@ class TasksController(object):
|
|||
msg = (_("Forbidden to create task. Reason: %(reason)s")
|
||||
% {'reason': unicode(e)})
|
||||
LOG.info(msg)
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
|
||||
result = {'task': new_task, 'task_details': new_task_details}
|
||||
return result
|
||||
|
@ -94,10 +94,10 @@ class TasksController(object):
|
|||
except (exception.NotFound, exception.InvalidSortKey,
|
||||
exception.InvalidFilterRangeValue) as e:
|
||||
LOG.info(unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
LOG.info(unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
result['tasks'] = tasks
|
||||
return result
|
||||
|
||||
|
@ -109,12 +109,12 @@ class TasksController(object):
|
|||
msg = (_("Failed to find task %(task_id)s. Reason: %(reason)s") %
|
||||
{'task_id': task_id, 'reason': unicode(e)})
|
||||
LOG.info(msg)
|
||||
raise webob.exc.HTTPNotFound(explanation=unicode(e))
|
||||
raise webob.exc.HTTPNotFound(explanation=e.msg)
|
||||
except exception.Forbidden as e:
|
||||
msg = (_("Forbidden to get task %(task_id)s. Reason: %(reason)s") %
|
||||
{'task_id': task_id, 'reason': unicode(e)})
|
||||
LOG.info(msg)
|
||||
raise webob.exc.HTTPForbidden(explanation=unicode(e))
|
||||
raise webob.exc.HTTPForbidden(explanation=e.msg)
|
||||
result = {'task': task, 'task_details': task_details}
|
||||
return result
|
||||
|
||||
|
@ -197,7 +197,7 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer):
|
|||
try:
|
||||
self.schema.validate(body)
|
||||
except exception.InvalidObject as e:
|
||||
raise webob.exc.HTTPBadRequest(explanation=unicode(e))
|
||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||
task = {}
|
||||
properties = body
|
||||
for key in self._required_properties:
|
||||
|
|
|
@ -14,4 +14,4 @@
|
|||
# under the License.
|
||||
|
||||
from glance.openstack.common import gettextutils
|
||||
gettextutils.install('glance')
|
||||
gettextutils.install('glance', lazy=True)
|
||||
|
|
|
@ -25,6 +25,8 @@ import eventlet
|
|||
import os
|
||||
import sys
|
||||
|
||||
import six
|
||||
|
||||
# Monkey patch socket, time, select, threads
|
||||
eventlet.patcher.monkey_patch(all=False, socket=True, time=True,
|
||||
select=True, thread=True)
|
||||
|
@ -45,7 +47,7 @@ import glance.store
|
|||
|
||||
|
||||
def fail(returncode, e):
|
||||
sys.stderr.write("ERROR: %s\n" % e)
|
||||
sys.stderr.write("ERROR: %s\n" % six.text_type(e))
|
||||
sys.exit(returncode)
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
"""Glance exception subclasses"""
|
||||
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
_FATAL_EXCEPTION_FORMAT_ERRORS = False
|
||||
|
@ -40,16 +41,23 @@ class GlanceException(Exception):
|
|||
if not message:
|
||||
message = self.message
|
||||
try:
|
||||
message = message % kwargs
|
||||
if kwargs:
|
||||
message = message % kwargs
|
||||
except Exception:
|
||||
if _FATAL_EXCEPTION_FORMAT_ERRORS:
|
||||
raise
|
||||
else:
|
||||
# at least get the core message out if something happened
|
||||
pass
|
||||
|
||||
self.msg = message
|
||||
super(GlanceException, self).__init__(message)
|
||||
|
||||
def __unicode__(self):
|
||||
# NOTE(flwang): By default, self.msg is an instance of Message, which
|
||||
# can't be converted by str(). Based on the definition of
|
||||
# __unicode__, it should return unicode always.
|
||||
return six.text_type(self.msg)
|
||||
|
||||
|
||||
class MissingCredentialError(GlanceException):
|
||||
message = _("Missing required credential: %(required)s")
|
||||
|
|
|
@ -178,7 +178,7 @@ class Controller(object):
|
|||
if self.raise_exc:
|
||||
raise
|
||||
|
||||
cls, val = e.__class__, str(e)
|
||||
cls, val = e.__class__, six.text_type(e)
|
||||
msg = (_("RPC Call Error: %(val)s\n%(tb)s") %
|
||||
dict(val=val, tb=traceback.format_exc()))
|
||||
LOG.error(msg)
|
||||
|
@ -188,7 +188,7 @@ class Controller(object):
|
|||
module = cls.__module__
|
||||
if module not in CONF.allowed_rpc_exception_modules:
|
||||
cls = exception.RPCError
|
||||
val = str(exception.RPCError(cls=cls, val=val))
|
||||
val = six.text_type(exception.RPCError(cls=cls, val=val))
|
||||
|
||||
cls_path = "%s.%s" % (cls.__module__, cls.__name__)
|
||||
result = {"_error": {"cls": cls_path, "val": val}}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# Copyright 2010 OpenStack Foundation
|
||||
# Copyright 2014 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -41,6 +42,7 @@ import webob.exc
|
|||
|
||||
from glance.common import exception
|
||||
from glance.common import utils
|
||||
from glance.openstack.common import gettextutils
|
||||
from glance.openstack.common import jsonutils
|
||||
import glance.openstack.common.log as logging
|
||||
|
||||
|
@ -512,6 +514,17 @@ class Request(webob.Request):
|
|||
else:
|
||||
return content_type
|
||||
|
||||
def best_match_language(self):
|
||||
"""Determines best available locale from the Accept-Language header.
|
||||
|
||||
:returns: the best language match or None if the 'Accept-Language'
|
||||
header was not available in the request.
|
||||
"""
|
||||
if not self.accept_language:
|
||||
return None
|
||||
langs = gettextutils.get_available_languages('glance')
|
||||
return self.accept_language.best_match(langs)
|
||||
|
||||
|
||||
class JSONRequestDeserializer(object):
|
||||
def has_body(self, request):
|
||||
|
@ -563,6 +576,23 @@ class JSONResponseSerializer(object):
|
|||
response.body = self.to_json(result)
|
||||
|
||||
|
||||
def translate_exception(req, e):
|
||||
"""Translates all translatable elements of the given exception."""
|
||||
|
||||
# The RequestClass attribute in the webob.dec.wsgify decorator
|
||||
# does not guarantee that the request object will be a particular
|
||||
# type; this check is therefore necessary.
|
||||
if not hasattr(req, "best_match_language"):
|
||||
return e
|
||||
|
||||
locale = req.best_match_language()
|
||||
|
||||
if isinstance(e, webob.exc.HTTPError):
|
||||
e.explanation = gettextutils.translate(e.explanation, locale)
|
||||
e.detail = gettextutils.translate(e.detail, locale)
|
||||
return e
|
||||
|
||||
|
||||
class Resource(object):
|
||||
"""
|
||||
WSGI app that handles (de)serialization and controller dispatch.
|
||||
|
@ -599,17 +629,22 @@ class Resource(object):
|
|||
action_args = self.get_action_args(request.environ)
|
||||
action = action_args.pop('action', None)
|
||||
|
||||
deserialized_request = self.dispatch(self.deserializer,
|
||||
action, request)
|
||||
action_args.update(deserialized_request)
|
||||
try:
|
||||
deserialized_request = self.dispatch(self.deserializer,
|
||||
action, request)
|
||||
action_args.update(deserialized_request)
|
||||
action_result = self.dispatch(self.controller, action,
|
||||
request, **action_args)
|
||||
except webob.exc.WSGIHTTPException as e:
|
||||
exc_info = sys.exc_info()
|
||||
raise translate_exception(request, e), None, exc_info[2]
|
||||
|
||||
action_result = self.dispatch(self.controller, action,
|
||||
request, **action_args)
|
||||
try:
|
||||
response = webob.Response(request=request)
|
||||
self.dispatch(self.serializer, action, response, action_result)
|
||||
return response
|
||||
|
||||
except webob.exc.WSGIHTTPException as e:
|
||||
return translate_exception(request, e)
|
||||
except webob.exc.HTTPException as e:
|
||||
return e
|
||||
# return unserializable result (typically a webob exc)
|
||||
|
|
|
@ -59,6 +59,7 @@ import stat
|
|||
import time
|
||||
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
import xattr
|
||||
|
||||
from glance.common import exception
|
||||
|
@ -281,13 +282,14 @@ class Driver(base.Driver):
|
|||
os.unlink(self.get_image_filepath(image_id, 'queue'))
|
||||
|
||||
def rollback(e):
|
||||
set_attr('error', "%s" % e)
|
||||
set_attr('error', six.text_type(e))
|
||||
|
||||
invalid_path = self.get_image_filepath(image_id, 'invalid')
|
||||
LOG.debug(_("Fetch of cache file failed (%(e)s), rolling back by "
|
||||
"moving '%(incomplete_path)s' to "
|
||||
"'%(invalid_path)s'"),
|
||||
{'e': e, 'incomplete_path': incomplete_path,
|
||||
{'e': six.text_type(e),
|
||||
'incomplete_path': incomplete_path,
|
||||
'invalid_path': invalid_path})
|
||||
os.rename(incomplete_path, invalid_path)
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import warnings
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo import messaging
|
||||
import webob
|
||||
|
@ -70,7 +67,7 @@ class Notifier(object):
|
|||
if CONF.notifier_strategy != 'default':
|
||||
msg = _("notifier_strategy was deprecated in "
|
||||
"favor of `notification_driver`")
|
||||
warnings.warn(msg, DeprecationWarning)
|
||||
LOG.warn(msg)
|
||||
|
||||
# NOTE(flaper87): Use this to keep backwards
|
||||
# compatibility. We'll try to get an oslo.messaging
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# under the License.
|
||||
|
||||
import jsonschema
|
||||
import six
|
||||
|
||||
from glance.common import exception
|
||||
|
||||
|
@ -31,7 +32,8 @@ class Schema(object):
|
|||
try:
|
||||
jsonschema.validate(obj, self.raw())
|
||||
except jsonschema.ValidationError as e:
|
||||
raise exception.InvalidObject(schema=self.name, reason=str(e))
|
||||
raise exception.InvalidObject(schema=self.name,
|
||||
reason=six.text_type(e))
|
||||
|
||||
def filter(self, obj):
|
||||
filtered = {}
|
||||
|
|
|
@ -18,6 +18,7 @@ import copy
|
|||
import sys
|
||||
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
||||
from glance.common import exception
|
||||
from glance.common import utils
|
||||
|
@ -305,7 +306,7 @@ def safe_delete_from_backend(context, uri, image_id, **kwargs):
|
|||
msg = _('Failed to delete image %s in store from URI')
|
||||
LOG.warn(msg % image_id)
|
||||
except exception.StoreDeleteNotSupported as e:
|
||||
LOG.warn(str(e))
|
||||
LOG.warn(six.text_type(e))
|
||||
except UnsupportedBackend:
|
||||
exc_type = sys.exc_info()[0].__name__
|
||||
msg = (_('Failed to delete image %(image_id)s from store '
|
||||
|
@ -369,8 +370,8 @@ def store_add_to_backend(image_id, data, size, store):
|
|||
if not isinstance(metadata, dict):
|
||||
msg = (_("The storage driver %(store)s returned invalid metadata "
|
||||
"%(metadata)s. This must be a dictionary type") %
|
||||
{'store': store,
|
||||
'metadata': metadata})
|
||||
{'store': six.text_type(store),
|
||||
'metadata': six.text_type(metadata)})
|
||||
LOG.error(msg)
|
||||
raise BackendException(msg)
|
||||
try:
|
||||
|
@ -378,9 +379,9 @@ def store_add_to_backend(image_id, data, size, store):
|
|||
except BackendException as e:
|
||||
e_msg = (_("A bad metadata structure was returned from the "
|
||||
"%(store)s storage driver: %(metadata)s. %(error)s.") %
|
||||
{'store': store,
|
||||
'metadata': metadata,
|
||||
'error': e})
|
||||
{'store': six.text_type(store),
|
||||
'metadata': six.text_type(metadata),
|
||||
'error': six.text_type(e)})
|
||||
LOG.error(e_msg)
|
||||
raise BackendException(e_msg)
|
||||
return (location, size, checksum, metadata)
|
||||
|
@ -725,7 +726,7 @@ class ImageProxy(glance.domain.proxy.Image):
|
|||
except Exception as e:
|
||||
LOG.warn(_('Get image %(id)s data failed: '
|
||||
'%(err)s.') % {'id': self.image.image_id,
|
||||
'err': e})
|
||||
'err': six.text_type(e)})
|
||||
err = e
|
||||
# tried all locations
|
||||
LOG.error(_('Glance tried all locations to get data for image %s '
|
||||
|
|
|
@ -22,6 +22,7 @@ import hashlib
|
|||
import os
|
||||
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
|
||||
from glance.common import exception
|
||||
|
@ -279,19 +280,19 @@ class Store(glance.store.base.Store):
|
|||
'used: %(error)s An empty dictionary will be '
|
||||
'returned to the client.') %
|
||||
{'file': CONF.filesystem_store_metadata_file,
|
||||
'error': str(bee)})
|
||||
'error': six.text_type(bee)})
|
||||
return {}
|
||||
except IOError as ioe:
|
||||
LOG.error(_('The path for the metadata file %(file)s could not be '
|
||||
'opened: %(error)s An empty dictionary will be '
|
||||
'returned to the client.') %
|
||||
{'file': CONF.filesystem_store_metadata_file,
|
||||
'error': ioe})
|
||||
'error': six.text_type(ioe)})
|
||||
return {}
|
||||
except Exception as ex:
|
||||
LOG.exception(_('An error occurred processing the storage systems '
|
||||
'meta data file: %s. An empty dictionary will be '
|
||||
'returned to the client.') % str(ex))
|
||||
'returned to the client.') % six.text_type(ex))
|
||||
return {}
|
||||
|
||||
def get(self, location):
|
||||
|
|
|
@ -82,7 +82,8 @@ def register_scheme_map(scheme_map):
|
|||
"""
|
||||
for (k, v) in scheme_map.items():
|
||||
if k not in SCHEME_TO_CLS_MAP:
|
||||
LOG.debug("Registering scheme %s with %s", k, v)
|
||||
LOG.debug(_("Registering scheme %(k)s with %(v)s") % {'k': k,
|
||||
'v': v})
|
||||
SCHEME_TO_CLS_MAP[k] = v
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from glance.common import exception
|
||||
from glance.tests import utils as test_utils
|
||||
|
||||
|
@ -40,3 +42,7 @@ class GlanceExceptionTestCase(test_utils.BaseTestCase):
|
|||
self.assertTrue('test: 500' in
|
||||
unicode(exception.GlanceException('test: %(code)s',
|
||||
code=500)))
|
||||
|
||||
def test_non_unicode_error_msg(self):
|
||||
exc = exception.GlanceException(str('test'))
|
||||
self.assertIsInstance(six.text_type(exc), six.text_type)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright 2014 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
|
@ -16,19 +17,42 @@
|
|||
import datetime
|
||||
import socket
|
||||
|
||||
from babel import localedata
|
||||
import eventlet.patcher
|
||||
import fixtures
|
||||
import gettext
|
||||
import mock
|
||||
import webob
|
||||
|
||||
from glance.common import exception
|
||||
from glance.common import utils
|
||||
from glance.common import wsgi
|
||||
from glance.openstack.common import gettextutils
|
||||
from glance.tests import utils as test_utils
|
||||
|
||||
|
||||
class RequestTest(test_utils.BaseTestCase):
|
||||
|
||||
def _set_expected_languages(self, all_locales=[], avail_locales=None):
|
||||
# Override localedata.locale_identifiers to return some locales.
|
||||
def returns_some_locales(*args, **kwargs):
|
||||
return all_locales
|
||||
|
||||
self.stubs.Set(localedata, 'locale_identifiers', returns_some_locales)
|
||||
|
||||
# Override gettext.find to return other than None for some languages.
|
||||
def fake_gettext_find(lang_id, *args, **kwargs):
|
||||
found_ret = '/glance/%s/LC_MESSAGES/glance.mo' % lang_id
|
||||
if avail_locales is None:
|
||||
# All locales are available.
|
||||
return found_ret
|
||||
languages = kwargs['languages']
|
||||
if languages[0] in avail_locales:
|
||||
return found_ret
|
||||
return None
|
||||
|
||||
self.stubs.Set(gettext, 'find', fake_gettext_find)
|
||||
|
||||
def test_content_type_missing(self):
|
||||
request = wsgi.Request.blank('/tests/123')
|
||||
self.assertRaises(exception.InvalidContentType,
|
||||
|
@ -77,6 +101,50 @@ class RequestTest(test_utils.BaseTestCase):
|
|||
result = request.best_match_content_type()
|
||||
self.assertEqual(result, "application/json")
|
||||
|
||||
def test_language_accept_default(self):
|
||||
request = wsgi.Request.blank('/tests/123')
|
||||
request.headers["Accept-Language"] = "zz-ZZ,zz;q=0.8"
|
||||
result = request.best_match_language()
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_language_accept_none(self):
|
||||
request = wsgi.Request.blank('/tests/123')
|
||||
result = request.best_match_language()
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_best_match_language_expected(self):
|
||||
# If Accept-Language is a supported language, best_match_language()
|
||||
# returns it.
|
||||
self._set_expected_languages(all_locales=['it'])
|
||||
|
||||
req = wsgi.Request.blank('/', headers={'Accept-Language': 'it'})
|
||||
self.assertEqual('it', req.best_match_language())
|
||||
|
||||
def test_request_match_language_unexpected(self):
|
||||
# If Accept-Language is a language we do not support,
|
||||
# best_match_language() returns None.
|
||||
self._set_expected_languages(all_locales=['it'])
|
||||
|
||||
req = wsgi.Request.blank('/', headers={'Accept-Language': 'zh'})
|
||||
self.assertIsNone(req.best_match_language())
|
||||
|
||||
@mock.patch.object(webob.acceptparse.AcceptLanguage, 'best_match')
|
||||
def test_best_match_language_unknown(self, mock_best_match):
|
||||
# Test that we are actually invoking language negotiation by webop
|
||||
request = wsgi.Request.blank('/')
|
||||
accepted = 'unknown-lang'
|
||||
request.headers = {'Accept-Language': accepted}
|
||||
|
||||
mock_best_match.return_value = None
|
||||
|
||||
self.assertIsNone(request.best_match_language())
|
||||
|
||||
# If Accept-Language is missing or empty, match should be None
|
||||
request.headers = {'Accept-Language': ''}
|
||||
self.assertIsNone(request.best_match_language())
|
||||
request.headers.pop('Accept-Language')
|
||||
self.assertIsNone(request.best_match_language())
|
||||
|
||||
|
||||
class ResourceTest(test_utils.BaseTestCase):
|
||||
|
||||
|
@ -171,6 +239,42 @@ class ResourceTest(test_utils.BaseTestCase):
|
|||
self.assertIsInstance(response, webob.exc.HTTPForbidden)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
@mock.patch.object(wsgi, 'translate_exception')
|
||||
def test_resource_call_error_handle_localized(self,
|
||||
mock_translate_exception):
|
||||
class Controller(object):
|
||||
def delete(self, req, identity):
|
||||
raise webob.exc.HTTPBadRequest(explanation='Not Found')
|
||||
|
||||
actions = {'action': 'delete', 'identity': 12}
|
||||
env = {'wsgiorg.routing_args': [None, actions]}
|
||||
request = wsgi.Request.blank('/tests/123', environ=env)
|
||||
message_es = 'No Encontrado'
|
||||
|
||||
resource = wsgi.Resource(Controller(),
|
||||
wsgi.JSONRequestDeserializer(),
|
||||
None)
|
||||
translated_exc = webob.exc.HTTPBadRequest(message_es)
|
||||
mock_translate_exception.return_value = translated_exc
|
||||
|
||||
e = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
resource, request)
|
||||
self.assertEqual(message_es, str(e))
|
||||
|
||||
@mock.patch.object(webob.acceptparse.AcceptLanguage, 'best_match')
|
||||
@mock.patch.object(gettextutils, 'translate')
|
||||
def test_translate_exception(self, mock_translate, mock_best_match):
|
||||
|
||||
mock_translate.return_value = 'No Encontrado'
|
||||
mock_best_match.return_value = 'de'
|
||||
|
||||
req = wsgi.Request.blank('/tests/123')
|
||||
req.headers["Accept-Language"] = "de"
|
||||
|
||||
e = webob.exc.HTTPNotFound(explanation='Not Found')
|
||||
e = wsgi.translate_exception(req, e)
|
||||
self.assertEqual('No Encontrado', e.explanation)
|
||||
|
||||
|
||||
class JSONResponseSerializerTest(test_utils.BaseTestCase):
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ class SwiftTests(object):
|
|||
except BackendException as e:
|
||||
exception_caught = True
|
||||
self.assertTrue("container noexist does not exist "
|
||||
"in Swift" in str(e))
|
||||
"in Swift" in six.text_type(e))
|
||||
self.assertTrue(exception_caught)
|
||||
self.assertEqual(SWIFT_PUT_OBJECT_CALLS, 0)
|
||||
|
||||
|
|
Loading…
Reference in New Issue