diff --git a/oslo/messaging/_drivers/common.py b/oslo/messaging/_drivers/common.py
index 93316c9d7..4e25d1f0f 100644
--- a/oslo/messaging/_drivers/common.py
+++ b/oslo/messaging/_drivers/common.py
@@ -72,6 +72,18 @@ _MESSAGE_KEY = 'oslo.message'
 
 _REMOTE_POSTFIX = '_Remote'
 
+# FIXME(markmc): add an API to replace this option
+_exception_opts = [
+    cfg.ListOpt('allowed_rpc_exception_modules',
+                default=['oslo.messaging.exceptions',
+                         'nova.exception',
+                         'cinder.exception',
+                         'exceptions',
+                         ],
+                help='Modules of exceptions that are permitted to be recreated'
+                     'upon receiving exception data from an rpc call.'),
+]
+
 
 class RPCException(Exception):
     msg_fmt = _("An unknown RPC related exception occurred.")
@@ -327,6 +339,7 @@ def deserialize_remote_exception(conf, data):
 
     # NOTE(ameade): We DO NOT want to allow just any module to be imported, in
     # order to prevent arbitrary code execution.
+    conf.register_opts(_exception_opts)
     if module not in conf.allowed_rpc_exception_modules:
         return RemoteError(name, failure.get('message'), trace)
 
diff --git a/tests/test_exception_serialization.py b/tests/test_exception_serialization.py
new file mode 100644
index 000000000..c3103a22b
--- /dev/null
+++ b/tests/test_exception_serialization.py
@@ -0,0 +1,314 @@
+
+# Copyright 2013 Red Hat, Inc.
+#
+#    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 sys
+
+import testscenarios
+
+from oslo.messaging._drivers import common as exceptions
+from oslo.messaging.openstack.common import jsonutils
+from tests import utils as test_utils
+
+load_tests = testscenarios.load_tests_apply_scenarios
+
+
+class NovaStyleException(Exception):
+
+    format = 'I am Nova'
+
+    def __init__(self, message=None, **kwargs):
+        self.kwargs = kwargs
+        if not message:
+            message = self.format % kwargs
+        super(NovaStyleException, self).__init__(message)
+
+
+class KwargsStyleException(NovaStyleException):
+
+    format = 'I am %(who)s'
+
+
+def add_remote_postfix(ex):
+    ex_type = type(ex)
+    message = str(ex)
+    str_override = lambda self: message
+    new_ex_type = type(ex_type.__name__ + "_Remote", (ex_type,),
+                       {'__str__': str_override,
+                        '__unicode__': str_override})
+    new_ex_type.__module__ = '%s_Remote' % ex.__class__.__module__
+    try:
+        ex.__class__ = new_ex_type
+    except TypeError:
+        ex.args = (message,) + ex.args[1:]
+    return ex
+
+
+class SerializeRemoteExceptionTestCase(test_utils.BaseTestCase):
+
+    _log_failure = [
+        ('log_failure', dict(log_failure=True)),
+        ('do_not_log_failure', dict(log_failure=False)),
+    ]
+
+    _add_remote = [
+        ('add_remote', dict(add_remote=True)),
+        ('do_not_add_remote', dict(add_remote=False)),
+    ]
+
+    _exception_types = [
+        ('bog_standard', dict(cls=Exception,
+                              args=['test'],
+                              kwargs={},
+                              clsname='Exception',
+                              modname='exceptions',
+                              msg='test')),
+        ('nova_style', dict(cls=NovaStyleException,
+                            args=[],
+                            kwargs={},
+                            clsname='NovaStyleException',
+                            modname=__name__,
+                            msg='I am Nova')),
+        ('nova_style_with_msg', dict(cls=NovaStyleException,
+                                     args=['testing'],
+                                     kwargs={},
+                                     clsname='NovaStyleException',
+                                     modname=__name__,
+                                     msg='testing')),
+        ('kwargs_style', dict(cls=KwargsStyleException,
+                              args=[],
+                              kwargs={'who': 'Oslo'},
+                              clsname='KwargsStyleException',
+                              modname=__name__,
+                              msg='I am Oslo')),
+    ]
+
+    @classmethod
+    def generate_scenarios(cls):
+        cls.scenarios = testscenarios.multiply_scenarios(cls._log_failure,
+                                                         cls._add_remote,
+                                                         cls._exception_types)
+
+    def setUp(self):
+        super(SerializeRemoteExceptionTestCase, self).setUp()
+
+    def test_serialize_remote_exception(self):
+        errors = []
+
+        def stub_error(msg, *a, **kw):
+            if (a and len(a) == 1 and isinstance(a[0], dict) and a[0]):
+                a = a[0]
+            errors.append(str(msg) % a)
+
+        self.stubs.Set(exceptions.LOG, 'error', stub_error)
+
+        try:
+            try:
+                raise self.cls(*self.args, **self.kwargs)
+            except Exception as ex:
+                if self.add_remote:
+                    ex = add_remote_postfix(ex)
+                raise ex
+        except Exception:
+            exc_info = sys.exc_info()
+
+        serialized = exceptions.serialize_remote_exception(
+            exc_info, log_failure=self.log_failure)
+
+        failure = jsonutils.loads(serialized)
+
+        self.assertEqual(failure['class'], self.clsname, failure)
+        self.assertEqual(failure['module'], self.modname)
+        self.assertEqual(failure['message'], self.msg)
+        self.assertEqual(failure['args'], [self.msg])
+        self.assertEqual(failure['kwargs'], self.kwargs)
+
+        # Note: _Remote prefix not stripped from tracebacks
+        tb = ex.__class__.__name__ + ': ' + self.msg
+        self.assertTrue(tb in ''.join(failure['tb']))
+
+        if self.log_failure:
+            self.assertTrue(len(errors) > 0, errors)
+        else:
+            self.assertEqual(len(errors), 0, errors)
+
+
+SerializeRemoteExceptionTestCase.generate_scenarios()
+
+
+class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
+
+    _standard_allowed = [__name__, 'exceptions']
+
+    scenarios = [
+        ('bog_standard',
+         dict(allowed=_standard_allowed,
+              clsname='Exception',
+              modname='exceptions',
+              cls=Exception,
+              args=['test'],
+              kwargs={},
+              str='test\ntraceback\ntraceback\n',
+              msg='test',
+              remote_name='Exception',
+              remote_args=('test\ntraceback\ntraceback\n', ),
+              remote_kwargs={})),
+        ('nova_style',
+         dict(allowed=_standard_allowed,
+              clsname='NovaStyleException',
+              modname=__name__,
+              cls=NovaStyleException,
+              args=[],
+              kwargs={},
+              str='test\ntraceback\ntraceback\n',
+              msg='I am Nova',
+              remote_name='NovaStyleException_Remote',
+              remote_args=('I am Nova', ),
+              remote_kwargs={})),
+        ('nova_style_with_msg',
+         dict(allowed=_standard_allowed,
+              clsname='NovaStyleException',
+              modname=__name__,
+              cls=NovaStyleException,
+              args=['testing'],
+              kwargs={},
+              str='test\ntraceback\ntraceback\n',
+              msg='testing',
+              remote_name='NovaStyleException_Remote',
+              remote_args=('testing', ),
+              remote_kwargs={})),
+        ('kwargs_style',
+         dict(allowed=_standard_allowed,
+              clsname='KwargsStyleException',
+              modname=__name__,
+              cls=KwargsStyleException,
+              args=[],
+              kwargs={'who': 'Oslo'},
+              str='test\ntraceback\ntraceback\n',
+              msg='I am Oslo',
+              remote_name='KwargsStyleException_Remote',
+              remote_args=('I am Oslo', ),
+              remote_kwargs={})),
+        ('not_allowed',
+         dict(allowed=[],
+              clsname='Exception',
+              modname='exceptions',
+              cls=exceptions.RemoteError,
+              args=[],
+              kwargs={},
+              str=("Remote error: Exception test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              msg=("Remote error: Exception test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              remote_name='RemoteError',
+              remote_args=("Remote error: Exception test\n"
+                           "[u'traceback\\ntraceback\\n'].", ),
+              remote_kwargs={'exc_type': 'Exception',
+                             'value': 'test',
+                             'traceback': 'traceback\ntraceback\n'})),
+        ('unknown_module',
+         dict(allowed=['notexist'],
+              clsname='Exception',
+              modname='notexist',
+              cls=exceptions.RemoteError,
+              args=[],
+              kwargs={},
+              str=("Remote error: Exception test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              msg=("Remote error: Exception test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              remote_name='RemoteError',
+              remote_args=("Remote error: Exception test\n"
+                           "[u'traceback\\ntraceback\\n'].", ),
+              remote_kwargs={'exc_type': 'Exception',
+                             'value': 'test',
+                             'traceback': 'traceback\ntraceback\n'})),
+        ('unknown_exception',
+         dict(allowed=['exceptions'],
+              clsname='FarcicalError',
+              modname='exceptions',
+              cls=exceptions.RemoteError,
+              args=[],
+              kwargs={},
+              str=("Remote error: FarcicalError test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              msg=("Remote error: FarcicalError test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              remote_name='RemoteError',
+              remote_args=("Remote error: FarcicalError test\n"
+                           "[u'traceback\\ntraceback\\n'].", ),
+              remote_kwargs={'exc_type': 'FarcicalError',
+                             'value': 'test',
+                             'traceback': 'traceback\ntraceback\n'})),
+        ('unknown_kwarg',
+         dict(allowed=['exceptions'],
+              clsname='Exception',
+              modname='exceptions',
+              cls=exceptions.RemoteError,
+              args=[],
+              kwargs={'foobar': 'blaa'},
+              str=("Remote error: Exception test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              msg=("Remote error: Exception test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              remote_name='RemoteError',
+              remote_args=("Remote error: Exception test\n"
+                           "[u'traceback\\ntraceback\\n'].", ),
+              remote_kwargs={'exc_type': 'Exception',
+                             'value': 'test',
+                             'traceback': 'traceback\ntraceback\n'})),
+        ('system_exit',
+         dict(allowed=['exceptions'],
+              clsname='SystemExit',
+              modname='exceptions',
+              cls=exceptions.RemoteError,
+              args=[],
+              kwargs={},
+              str=("Remote error: SystemExit test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              msg=("Remote error: SystemExit test\n"
+                   "[u'traceback\\ntraceback\\n']."),
+              remote_name='RemoteError',
+              remote_args=("Remote error: SystemExit test\n"
+                           "[u'traceback\\ntraceback\\n'].", ),
+              remote_kwargs={'exc_type': 'SystemExit',
+                             'value': 'test',
+                             'traceback': 'traceback\ntraceback\n'})),
+    ]
+
+    def setUp(self):
+        super(DeserializeRemoteExceptionTestCase, self).setUp()
+        self.conf.register_opts(exceptions._exception_opts)
+
+    def test_deserialize_remote_exception(self):
+        self.config(allowed_rpc_exception_modules=self.allowed)
+
+        failure = {
+            'class': self.clsname,
+            'module': self.modname,
+            'message': 'test',
+            'tb': ['traceback\ntraceback\n'],
+            'args': self.args,
+            'kwargs': self.kwargs,
+        }
+
+        serialized = jsonutils.dumps(failure)
+
+        ex = exceptions.deserialize_remote_exception(self.conf, serialized)
+
+        self.assertTrue(isinstance(ex, self.cls))
+        self.assertEqual(ex.__class__.__name__, self.remote_name)
+        self.assertEqual(str(ex), self.str)
+        self.assertEqual(ex.message, self.msg)
+        self.assertEqual(ex.args, self.remote_args)