From a1a898c4667b8e99d733f1391e0d325c8ce6e187 Mon Sep 17 00:00:00 2001
From: "Luis A. Garcia" <luis@linux.vnet.ibm.com>
Date: Tue, 24 Sep 2013 22:07:55 +0000
Subject: [PATCH] Fix translation of CinderExceptions in REST API

When creating a Fault from a CinderException wrapped in an
HTTPException, we were converting the inner explanation to unicode
before it was able to reach the Fault's call() method which is where
translation occurs, and unicode objects can't be translated.

This patch preserves the CinderException's Message object and puts it in
another Message object as the explanation to the HTTPException so it can
be translated.

Fixes bug: #1229967

Change-Id: Ida71908b639da32b4b85846a117ef21da2fe685b
---
 cinder/api/middleware/fault.py |  8 ++++--
 cinder/tests/test_wsgi.py      | 48 +++++++++++++++++++++++++++++++++-
 2 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/cinder/api/middleware/fault.py b/cinder/api/middleware/fault.py
index dddd166ac..0a500094c 100644
--- a/cinder/api/middleware/fault.py
+++ b/cinder/api/middleware/fault.py
@@ -20,6 +20,7 @@ import webob.dec
 import webob.exc
 
 from cinder.api.openstack import wsgi
+from cinder import exception
 from cinder.openstack.common import log as logging
 from cinder import utils
 from cinder import wsgi as base_wsgi
@@ -63,8 +64,11 @@ class FaultWrapper(base_wsgi.Middleware):
         # inconsistent with the EC2 API to hide every exception,
         # including those that are safe to expose, see bug 1021373
         if safe:
-            outer.explanation = '%s: %s' % (inner.__class__.__name__,
-                                            unicode(inner))
+            msg = (inner.msg if isinstance(inner, exception.CinderException)
+                   else unicode(inner))
+            params = {'exception': inner.__class__.__name__,
+                      'explanation': msg}
+            outer.explanation = _('%(exception)s: %(explanation)s') % params
         return wsgi.Fault(outer)
 
     @webob.dec.wsgify(RequestClass=wsgi.Request)
diff --git a/cinder/tests/test_wsgi.py b/cinder/tests/test_wsgi.py
index de30e02fa..ba7f15d16 100644
--- a/cinder/tests/test_wsgi.py
+++ b/cinder/tests/test_wsgi.py
@@ -18,6 +18,7 @@
 
 """Unit tests for `cinder.wsgi`."""
 
+import mock
 import os.path
 import ssl
 import tempfile
@@ -28,8 +29,8 @@ import testtools
 import webob
 import webob.dec
 
-from cinder.api.middleware import fault
 from cinder import exception
+from cinder.openstack.common import gettextutils
 from cinder import test
 from cinder import utils
 import cinder.wsgi
@@ -188,6 +189,13 @@ class TestWSGIServer(test.TestCase):
 class ExceptionTest(test.TestCase):
 
     def _wsgi_app(self, inner_app):
+        # NOTE(luisg): In order to test localization, we need to
+        # make sure the lazy _() is installed in the 'fault' module
+        # also we don't want to install the _() system-wide and
+        # potentially break other test cases, so we do it here for this
+        # test suite only.
+        gettextutils.install('', lazy=True)
+        from cinder.api.middleware import fault
         return fault.FaultWrapper(inner_app)
 
     def _do_test_exception_safety_reflected_in_faults(self, expose):
@@ -258,3 +266,41 @@ class ExceptionTest(test.TestCase):
         api = self._wsgi_app(fail)
         resp = webob.Request.blank('/').get_response(api)
         self.assertEqual(500, resp.status_int)
+
+    @mock.patch('cinder.openstack.common.gettextutils.get_localized_message')
+    def test_cinder_exception_with_localized_explanation(self, mock_t9n):
+        msg = 'My Not Found'
+        msg_translation = 'Mi No Encontrado'
+        message = gettextutils.Message(msg, '')
+
+        @webob.dec.wsgify
+        def fail(req):
+            class MyVolumeNotFound(exception.NotFound):
+                def __init__(self):
+                    self.msg = message
+                    self.safe = True
+            raise MyVolumeNotFound()
+
+        # Test response without localization
+        def mock_get_non_localized_message(msgid, locale):
+            return msg
+
+        mock_t9n.side_effect = mock_get_non_localized_message
+
+        api = self._wsgi_app(fail)
+        resp = webob.Request.blank('/').get_response(api)
+        self.assertEqual(404, resp.status_int)
+        self.assertIn(msg, resp.body)
+
+        # Test response with localization
+        def mock_get_localized_message(msgid, locale):
+            if isinstance(msgid, gettextutils.Message):
+                return msg_translation
+            return msgid
+
+        mock_t9n.side_effect = mock_get_localized_message
+
+        api = self._wsgi_app(fail)
+        resp = webob.Request.blank('/').get_response(api)
+        self.assertEqual(404, resp.status_int)
+        self.assertIn(msg_translation, resp.body)
-- 
2.45.2