Expose RemoteError exception in the public API
If a remote endpoint raises an exception which the client is not allowed to (or cannot) deserialize, then RPCClient.call() raises a RemoteError exception instead. Make this exception type part of the public API. Change-Id: I70be0ab7d40af3224d93d6bd0522c1a82f6303c3
This commit is contained in:
parent
9ac9f615b2
commit
66f597f30d
@ -6,3 +6,5 @@ RPC Client
|
||||
|
||||
.. autoclass:: RPCClient
|
||||
:members:
|
||||
|
||||
.. autoexception:: RemoteError
|
||||
|
@ -23,6 +23,7 @@ import sys
|
||||
import traceback
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo import messaging
|
||||
import six
|
||||
|
||||
from oslo.messaging.openstack.common import importutils
|
||||
@ -341,7 +342,7 @@ def deserialize_remote_exception(conf, data):
|
||||
# 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)
|
||||
return messaging.RemoteError(name, failure.get('message'), trace)
|
||||
|
||||
try:
|
||||
mod = importutils.import_module(module)
|
||||
@ -351,7 +352,7 @@ def deserialize_remote_exception(conf, data):
|
||||
|
||||
failure = klass(*failure.get('args', []), **failure.get('kwargs', {}))
|
||||
except (AttributeError, TypeError, ImportError):
|
||||
return RemoteError(name, failure.get('message'), trace)
|
||||
return messaging.RemoteError(name, failure.get('message'), trace)
|
||||
|
||||
ex_type = type(failure)
|
||||
str_override = lambda self: message
|
||||
|
@ -21,6 +21,7 @@ __all__ = [
|
||||
'RPCDispatcher',
|
||||
'RPCDispatcherError',
|
||||
'RPCVersionCapError',
|
||||
'RemoteError',
|
||||
'UnsupportedVersion',
|
||||
'expected_exceptions',
|
||||
'get_rpc_server',
|
||||
|
@ -20,6 +20,7 @@ __all__ = [
|
||||
'ClientSendError',
|
||||
'RPCClient',
|
||||
'RPCVersionCapError',
|
||||
'RemoteError',
|
||||
]
|
||||
|
||||
import inspect
|
||||
@ -41,6 +42,26 @@ _client_opts = [
|
||||
_LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RemoteError(exceptions.MessagingException):
|
||||
|
||||
"""Signifies that a remote endpoint method has raised an exception.
|
||||
|
||||
Contains a string representation of the type of the original exception,
|
||||
the value of the original exception, and the traceback. These are
|
||||
sent to the parent as a joined string so printing the exception
|
||||
contains all of the relevant info.
|
||||
"""
|
||||
|
||||
def __init__(self, exc_type=None, value=None, traceback=None):
|
||||
self.exc_type = exc_type
|
||||
self.value = value
|
||||
self.traceback = traceback
|
||||
msg = ("Remote error: %(exc_type)s %(value)s\n%(traceback)s." %
|
||||
dict(exc_type=self.exc_type, value=self.value,
|
||||
traceback=self.traceback))
|
||||
super(RemoteError, self).__init__(msg)
|
||||
|
||||
|
||||
class RPCVersionCapError(exceptions.MessagingException):
|
||||
|
||||
def __init__(self, version, version_cap):
|
||||
@ -335,7 +356,7 @@ class RPCClient(object):
|
||||
:type method: str
|
||||
:param kwargs: a dict of method arguments
|
||||
:param kwargs: dict
|
||||
:raises: MessagingTimeout
|
||||
:raises: MessagingTimeout, RemoteError
|
||||
"""
|
||||
return self.prepare().call(ctxt, method, **kwargs)
|
||||
|
||||
|
@ -17,6 +17,7 @@ import sys
|
||||
|
||||
import testscenarios
|
||||
|
||||
from oslo import messaging
|
||||
from oslo.messaging._drivers import common as exceptions
|
||||
from oslo.messaging.openstack.common import jsonutils
|
||||
from tests import utils as test_utils
|
||||
@ -160,7 +161,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
args=['test'],
|
||||
kwargs={},
|
||||
str='test\ntraceback\ntraceback\n',
|
||||
msg='test',
|
||||
message='test',
|
||||
remote_name='Exception',
|
||||
remote_args=('test\ntraceback\ntraceback\n', ),
|
||||
remote_kwargs={})),
|
||||
@ -172,7 +173,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
args=[],
|
||||
kwargs={},
|
||||
str='test\ntraceback\ntraceback\n',
|
||||
msg='I am Nova',
|
||||
message='I am Nova',
|
||||
remote_name='NovaStyleException_Remote',
|
||||
remote_args=('I am Nova', ),
|
||||
remote_kwargs={})),
|
||||
@ -184,7 +185,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
args=['testing'],
|
||||
kwargs={},
|
||||
str='test\ntraceback\ntraceback\n',
|
||||
msg='testing',
|
||||
message='testing',
|
||||
remote_name='NovaStyleException_Remote',
|
||||
remote_args=('testing', ),
|
||||
remote_kwargs={})),
|
||||
@ -196,7 +197,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
args=[],
|
||||
kwargs={'who': 'Oslo'},
|
||||
str='test\ntraceback\ntraceback\n',
|
||||
msg='I am Oslo',
|
||||
message='I am Oslo',
|
||||
remote_name='KwargsStyleException_Remote',
|
||||
remote_args=('I am Oslo', ),
|
||||
remote_kwargs={})),
|
||||
@ -204,7 +205,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
dict(allowed=[],
|
||||
clsname='Exception',
|
||||
modname='exceptions',
|
||||
cls=exceptions.RemoteError,
|
||||
cls=messaging.RemoteError,
|
||||
args=[],
|
||||
kwargs={},
|
||||
str=("Remote error: Exception test\n"
|
||||
@ -212,8 +213,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
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_args=(),
|
||||
remote_kwargs={'exc_type': 'Exception',
|
||||
'value': 'test',
|
||||
'traceback': 'traceback\ntraceback\n'})),
|
||||
@ -221,7 +221,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
dict(allowed=['notexist'],
|
||||
clsname='Exception',
|
||||
modname='notexist',
|
||||
cls=exceptions.RemoteError,
|
||||
cls=messaging.RemoteError,
|
||||
args=[],
|
||||
kwargs={},
|
||||
str=("Remote error: Exception test\n"
|
||||
@ -229,8 +229,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
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_args=(),
|
||||
remote_kwargs={'exc_type': 'Exception',
|
||||
'value': 'test',
|
||||
'traceback': 'traceback\ntraceback\n'})),
|
||||
@ -238,7 +237,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
dict(allowed=['exceptions'],
|
||||
clsname='FarcicalError',
|
||||
modname='exceptions',
|
||||
cls=exceptions.RemoteError,
|
||||
cls=messaging.RemoteError,
|
||||
args=[],
|
||||
kwargs={},
|
||||
str=("Remote error: FarcicalError test\n"
|
||||
@ -246,8 +245,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
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_args=(),
|
||||
remote_kwargs={'exc_type': 'FarcicalError',
|
||||
'value': 'test',
|
||||
'traceback': 'traceback\ntraceback\n'})),
|
||||
@ -255,7 +253,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
dict(allowed=['exceptions'],
|
||||
clsname='Exception',
|
||||
modname='exceptions',
|
||||
cls=exceptions.RemoteError,
|
||||
cls=messaging.RemoteError,
|
||||
args=[],
|
||||
kwargs={'foobar': 'blaa'},
|
||||
str=("Remote error: Exception test\n"
|
||||
@ -263,8 +261,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
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_args=(),
|
||||
remote_kwargs={'exc_type': 'Exception',
|
||||
'value': 'test',
|
||||
'traceback': 'traceback\ntraceback\n'})),
|
||||
@ -272,7 +269,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
dict(allowed=['exceptions'],
|
||||
clsname='SystemExit',
|
||||
modname='exceptions',
|
||||
cls=exceptions.RemoteError,
|
||||
cls=messaging.RemoteError,
|
||||
args=[],
|
||||
kwargs={},
|
||||
str=("Remote error: SystemExit test\n"
|
||||
@ -280,8 +277,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
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_args=(),
|
||||
remote_kwargs={'exc_type': 'SystemExit',
|
||||
'value': 'test',
|
||||
'traceback': 'traceback\ntraceback\n'})),
|
||||
@ -310,5 +306,8 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
||||
self.assertIsInstance(ex, self.cls)
|
||||
self.assertEqual(ex.__class__.__name__, self.remote_name)
|
||||
self.assertEqual(str(ex), self.str)
|
||||
self.assertEqual(ex.message, self.msg)
|
||||
if hasattr(self, 'msg'):
|
||||
self.assertEqual(ex.msg, self.msg)
|
||||
else:
|
||||
self.assertEqual(ex.message, self.message)
|
||||
self.assertEqual(ex.args, self.remote_args)
|
||||
|
Loading…
Reference in New Issue
Block a user