diff --git a/bin/cinder-all b/bin/cinder-all index 8e19ab5f489..33ae0ea849b 100755 --- a/bin/cinder-all +++ b/bin/cinder-all @@ -44,7 +44,7 @@ if os.path.exists(os.path.join(possible_topdir, "cinder", "__init__.py")): sys.path.insert(0, possible_topdir) from cinder.openstack.common import gettextutils -gettextutils.install('cinder') +gettextutils.install('cinder', lazy=True) from cinder.common import config # Need to register global_opts from cinder.openstack.common import log as logging diff --git a/bin/cinder-api b/bin/cinder-api index 7005d24cecf..7c296c6bfd6 100755 --- a/bin/cinder-api +++ b/bin/cinder-api @@ -24,7 +24,6 @@ # eventlet is updated/released to fix the root issue import eventlet - eventlet.monkey_patch() import os @@ -39,7 +38,7 @@ if os.path.exists(os.path.join(possible_topdir, "cinder", "__init__.py")): sys.path.insert(0, possible_topdir) from cinder.openstack.common import gettextutils -gettextutils.install('cinder') +gettextutils.install('cinder', lazy=True) from cinder.common import config # Need to register global_opts from cinder.openstack.common import log as logging diff --git a/cinder/api/openstack/wsgi.py b/cinder/api/openstack/wsgi.py index a2101c1961b..89a4356ae41 100644 --- a/cinder/api/openstack/wsgi.py +++ b/cinder/api/openstack/wsgi.py @@ -1,6 +1,7 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2011 OpenStack LLC. +# Copyright 2013 IBM Corp. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -21,6 +22,7 @@ import time import webob from cinder import exception +from cinder.openstack.common import gettextutils from cinder.openstack.common import jsonutils from cinder.openstack.common import log as logging from cinder import utils @@ -101,6 +103,12 @@ class Request(webob.Request): return content_type + def best_match_language(self): + """Determines best available locale from the Accept-Language header.""" + all_languages = gettextutils.get_available_languages('cinder') + return self.accept_language.best_match(all_languages, + default_match='en_US') + class ActionDispatcher(object): """Maps method name to local methods through action name.""" @@ -1069,12 +1077,15 @@ class Fault(webob.exc.HTTPException): def __call__(self, req): """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. + locale = req.best_match_language() code = self.wrapped_exc.status_int fault_name = self._fault_names.get(code, "computeFault") + explanation = self.wrapped_exc.explanation fault_data = { fault_name: { 'code': code, - 'message': self.wrapped_exc.explanation}} + 'message': gettextutils.get_localized_message(explanation, + locale)}} if code == 413: retry = self.wrapped_exc.headers['Retry-After'] fault_data[fault_name]['retryAfter'] = retry @@ -1134,13 +1145,19 @@ class OverLimitFault(webob.exc.HTTPException): @webob.dec.wsgify(RequestClass=Request) def __call__(self, request): - """ - Return the wrapped exception with a serialized body conforming to our - error format. - """ + """Serializes the wrapped exception conforming to our error format.""" content_type = request.best_match_content_type() metadata = {"attributes": {"overLimitFault": "code"}} + def translate(msg): + locale = request.best_match_language() + return gettextutils.get_localized_message(msg, locale) + + self.content['overLimitFault']['message'] = \ + translate(self.content['overLimitFault']['message']) + self.content['overLimitFault']['details'] = \ + translate(self.content['overLimitFault']['details']) + xml_serializer = XMLDictSerializer(metadata, XMLNS_V1) serializer = { 'application/xml': xml_serializer, diff --git a/cinder/tests/api/middleware/test_faults.py b/cinder/tests/api/middleware/test_faults.py index aff1dfbcc96..7be3b05fc4e 100644 --- a/cinder/tests/api/middleware/test_faults.py +++ b/cinder/tests/api/middleware/test_faults.py @@ -17,12 +17,12 @@ from xml.dom import minidom -import webob import webob.dec import webob.exc from cinder.api import common from cinder.api.openstack import wsgi +from cinder.openstack.common import gettextutils from cinder.openstack.common import jsonutils from cinder import test @@ -109,6 +109,27 @@ class TestFaults(test.TestCase): self.assertTrue('resizeNotAllowed' not in resp.body) self.assertTrue('forbidden' in resp.body) + def test_raise_localized_explanation(self): + params = ('blah', ) + expl = gettextutils.Message("String with params: %s" % params, 'test') + + def _mock_translation(msg, locale): + return "Mensaje traducido" + + self.stubs.Set(gettextutils, + "get_localized_message", _mock_translation) + + @webob.dec.wsgify + def raiser(req): + raise wsgi.Fault(webob.exc.HTTPNotFound(explanation=expl)) + + req = webob.Request.blank('/.xml') + resp = req.get_response(raiser) + self.assertEqual(resp.content_type, "application/xml") + self.assertEqual(resp.status_int, 404) + self.assertTrue(("Mensaje traducido") in resp.body) + self.stubs.UnsetAll() + def test_fault_has_status_int(self): """Ensure the status_int is set correctly on faults""" fault = wsgi.Fault(webob.exc.HTTPBadRequest(explanation='what?')) diff --git a/cinder/tests/api/openstack/test_wsgi.py b/cinder/tests/api/openstack/test_wsgi.py index 75e0fa21553..0413a842eaa 100644 --- a/cinder/tests/api/openstack/test_wsgi.py +++ b/cinder/tests/api/openstack/test_wsgi.py @@ -87,6 +87,21 @@ class RequestTest(test.TestCase): result = request.best_match_content_type() self.assertEqual(result, "application/json") + def test_best_match_language(self): + # Here we test that we are actually invoking language negotiation + # by webob and also that the default locale always available is en-US + request = wsgi.Request.blank('/') + accepted = 'unknown-lang' + request.headers = {'Accept-Language': accepted} + + def fake_best_match(self, offers, default_match=None): + return default_match + + self.stubs.SmartSet(request.accept_language, + 'best_match', fake_best_match) + + self.assertEqual(request.best_match_language(), 'en_US') + class ActionDispatcherTest(test.TestCase): def test_dispatch(self): diff --git a/cinder/utils.py b/cinder/utils.py index fa05e73e690..8942c273d6d 100644 --- a/cinder/utils.py +++ b/cinder/utils.py @@ -48,6 +48,7 @@ from oslo.config import cfg from cinder import exception from cinder.openstack.common import excutils +from cinder.openstack.common import gettextutils from cinder.openstack.common import importutils from cinder.openstack.common import lockutils from cinder.openstack.common import log as logging @@ -659,6 +660,8 @@ def utf8(value): """ if isinstance(value, unicode): return value.encode('utf-8') + elif isinstance(value, gettextutils.Message): + return unicode(value).encode('utf-8') elif isinstance(value, str): return value else: diff --git a/cinder/wsgi.py b/cinder/wsgi.py index 098c1d3a675..50e71038a1f 100644 --- a/cinder/wsgi.py +++ b/cinder/wsgi.py @@ -204,7 +204,8 @@ class Server(object): backlog=backlog) self._server = eventlet.spawn(self._start) (self._host, self._port) = self._socket.getsockname()[0:2] - LOG.info(_("Started %(name)s on %(_host)s:%(_port)s") % self.__dict__) + LOG.info(_("Started %(name)s on %(host)s:%(port)s") % + {'name': self.name, 'host': self.host, 'port': self.port}) @property def host(self):