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
|
.. autoclass:: RPCClient
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autoexception:: RemoteError
|
||||||
|
@ -23,6 +23,7 @@ import sys
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
from oslo import messaging
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from oslo.messaging.openstack.common import importutils
|
from oslo.messaging.openstack.common import importutils
|
||||||
@ -341,7 +342,7 @@ def deserialize_remote_exception(conf, data):
|
|||||||
# order to prevent arbitrary code execution.
|
# order to prevent arbitrary code execution.
|
||||||
conf.register_opts(_exception_opts)
|
conf.register_opts(_exception_opts)
|
||||||
if module not in conf.allowed_rpc_exception_modules:
|
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:
|
try:
|
||||||
mod = importutils.import_module(module)
|
mod = importutils.import_module(module)
|
||||||
@ -351,7 +352,7 @@ def deserialize_remote_exception(conf, data):
|
|||||||
|
|
||||||
failure = klass(*failure.get('args', []), **failure.get('kwargs', {}))
|
failure = klass(*failure.get('args', []), **failure.get('kwargs', {}))
|
||||||
except (AttributeError, TypeError, ImportError):
|
except (AttributeError, TypeError, ImportError):
|
||||||
return RemoteError(name, failure.get('message'), trace)
|
return messaging.RemoteError(name, failure.get('message'), trace)
|
||||||
|
|
||||||
ex_type = type(failure)
|
ex_type = type(failure)
|
||||||
str_override = lambda self: message
|
str_override = lambda self: message
|
||||||
|
@ -21,6 +21,7 @@ __all__ = [
|
|||||||
'RPCDispatcher',
|
'RPCDispatcher',
|
||||||
'RPCDispatcherError',
|
'RPCDispatcherError',
|
||||||
'RPCVersionCapError',
|
'RPCVersionCapError',
|
||||||
|
'RemoteError',
|
||||||
'UnsupportedVersion',
|
'UnsupportedVersion',
|
||||||
'expected_exceptions',
|
'expected_exceptions',
|
||||||
'get_rpc_server',
|
'get_rpc_server',
|
||||||
|
@ -20,6 +20,7 @@ __all__ = [
|
|||||||
'ClientSendError',
|
'ClientSendError',
|
||||||
'RPCClient',
|
'RPCClient',
|
||||||
'RPCVersionCapError',
|
'RPCVersionCapError',
|
||||||
|
'RemoteError',
|
||||||
]
|
]
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
@ -41,6 +42,26 @@ _client_opts = [
|
|||||||
_LOG = logging.getLogger(__name__)
|
_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):
|
class RPCVersionCapError(exceptions.MessagingException):
|
||||||
|
|
||||||
def __init__(self, version, version_cap):
|
def __init__(self, version, version_cap):
|
||||||
@ -335,7 +356,7 @@ class RPCClient(object):
|
|||||||
:type method: str
|
:type method: str
|
||||||
:param kwargs: a dict of method arguments
|
:param kwargs: a dict of method arguments
|
||||||
:param kwargs: dict
|
:param kwargs: dict
|
||||||
:raises: MessagingTimeout
|
:raises: MessagingTimeout, RemoteError
|
||||||
"""
|
"""
|
||||||
return self.prepare().call(ctxt, method, **kwargs)
|
return self.prepare().call(ctxt, method, **kwargs)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import sys
|
|||||||
|
|
||||||
import testscenarios
|
import testscenarios
|
||||||
|
|
||||||
|
from oslo import messaging
|
||||||
from oslo.messaging._drivers import common as exceptions
|
from oslo.messaging._drivers import common as exceptions
|
||||||
from oslo.messaging.openstack.common import jsonutils
|
from oslo.messaging.openstack.common import jsonutils
|
||||||
from tests import utils as test_utils
|
from tests import utils as test_utils
|
||||||
@ -160,7 +161,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
args=['test'],
|
args=['test'],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str='test\ntraceback\ntraceback\n',
|
str='test\ntraceback\ntraceback\n',
|
||||||
msg='test',
|
message='test',
|
||||||
remote_name='Exception',
|
remote_name='Exception',
|
||||||
remote_args=('test\ntraceback\ntraceback\n', ),
|
remote_args=('test\ntraceback\ntraceback\n', ),
|
||||||
remote_kwargs={})),
|
remote_kwargs={})),
|
||||||
@ -172,7 +173,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
args=[],
|
args=[],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str='test\ntraceback\ntraceback\n',
|
str='test\ntraceback\ntraceback\n',
|
||||||
msg='I am Nova',
|
message='I am Nova',
|
||||||
remote_name='NovaStyleException_Remote',
|
remote_name='NovaStyleException_Remote',
|
||||||
remote_args=('I am Nova', ),
|
remote_args=('I am Nova', ),
|
||||||
remote_kwargs={})),
|
remote_kwargs={})),
|
||||||
@ -184,7 +185,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
args=['testing'],
|
args=['testing'],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str='test\ntraceback\ntraceback\n',
|
str='test\ntraceback\ntraceback\n',
|
||||||
msg='testing',
|
message='testing',
|
||||||
remote_name='NovaStyleException_Remote',
|
remote_name='NovaStyleException_Remote',
|
||||||
remote_args=('testing', ),
|
remote_args=('testing', ),
|
||||||
remote_kwargs={})),
|
remote_kwargs={})),
|
||||||
@ -196,7 +197,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
args=[],
|
args=[],
|
||||||
kwargs={'who': 'Oslo'},
|
kwargs={'who': 'Oslo'},
|
||||||
str='test\ntraceback\ntraceback\n',
|
str='test\ntraceback\ntraceback\n',
|
||||||
msg='I am Oslo',
|
message='I am Oslo',
|
||||||
remote_name='KwargsStyleException_Remote',
|
remote_name='KwargsStyleException_Remote',
|
||||||
remote_args=('I am Oslo', ),
|
remote_args=('I am Oslo', ),
|
||||||
remote_kwargs={})),
|
remote_kwargs={})),
|
||||||
@ -204,7 +205,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
dict(allowed=[],
|
dict(allowed=[],
|
||||||
clsname='Exception',
|
clsname='Exception',
|
||||||
modname='exceptions',
|
modname='exceptions',
|
||||||
cls=exceptions.RemoteError,
|
cls=messaging.RemoteError,
|
||||||
args=[],
|
args=[],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str=("Remote error: Exception test\n"
|
str=("Remote error: Exception test\n"
|
||||||
@ -212,8 +213,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
msg=("Remote error: Exception test\n"
|
msg=("Remote error: Exception test\n"
|
||||||
"[u'traceback\\ntraceback\\n']."),
|
"[u'traceback\\ntraceback\\n']."),
|
||||||
remote_name='RemoteError',
|
remote_name='RemoteError',
|
||||||
remote_args=("Remote error: Exception test\n"
|
remote_args=(),
|
||||||
"[u'traceback\\ntraceback\\n'].", ),
|
|
||||||
remote_kwargs={'exc_type': 'Exception',
|
remote_kwargs={'exc_type': 'Exception',
|
||||||
'value': 'test',
|
'value': 'test',
|
||||||
'traceback': 'traceback\ntraceback\n'})),
|
'traceback': 'traceback\ntraceback\n'})),
|
||||||
@ -221,7 +221,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
dict(allowed=['notexist'],
|
dict(allowed=['notexist'],
|
||||||
clsname='Exception',
|
clsname='Exception',
|
||||||
modname='notexist',
|
modname='notexist',
|
||||||
cls=exceptions.RemoteError,
|
cls=messaging.RemoteError,
|
||||||
args=[],
|
args=[],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str=("Remote error: Exception test\n"
|
str=("Remote error: Exception test\n"
|
||||||
@ -229,8 +229,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
msg=("Remote error: Exception test\n"
|
msg=("Remote error: Exception test\n"
|
||||||
"[u'traceback\\ntraceback\\n']."),
|
"[u'traceback\\ntraceback\\n']."),
|
||||||
remote_name='RemoteError',
|
remote_name='RemoteError',
|
||||||
remote_args=("Remote error: Exception test\n"
|
remote_args=(),
|
||||||
"[u'traceback\\ntraceback\\n'].", ),
|
|
||||||
remote_kwargs={'exc_type': 'Exception',
|
remote_kwargs={'exc_type': 'Exception',
|
||||||
'value': 'test',
|
'value': 'test',
|
||||||
'traceback': 'traceback\ntraceback\n'})),
|
'traceback': 'traceback\ntraceback\n'})),
|
||||||
@ -238,7 +237,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
dict(allowed=['exceptions'],
|
dict(allowed=['exceptions'],
|
||||||
clsname='FarcicalError',
|
clsname='FarcicalError',
|
||||||
modname='exceptions',
|
modname='exceptions',
|
||||||
cls=exceptions.RemoteError,
|
cls=messaging.RemoteError,
|
||||||
args=[],
|
args=[],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str=("Remote error: FarcicalError test\n"
|
str=("Remote error: FarcicalError test\n"
|
||||||
@ -246,8 +245,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
msg=("Remote error: FarcicalError test\n"
|
msg=("Remote error: FarcicalError test\n"
|
||||||
"[u'traceback\\ntraceback\\n']."),
|
"[u'traceback\\ntraceback\\n']."),
|
||||||
remote_name='RemoteError',
|
remote_name='RemoteError',
|
||||||
remote_args=("Remote error: FarcicalError test\n"
|
remote_args=(),
|
||||||
"[u'traceback\\ntraceback\\n'].", ),
|
|
||||||
remote_kwargs={'exc_type': 'FarcicalError',
|
remote_kwargs={'exc_type': 'FarcicalError',
|
||||||
'value': 'test',
|
'value': 'test',
|
||||||
'traceback': 'traceback\ntraceback\n'})),
|
'traceback': 'traceback\ntraceback\n'})),
|
||||||
@ -255,7 +253,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
dict(allowed=['exceptions'],
|
dict(allowed=['exceptions'],
|
||||||
clsname='Exception',
|
clsname='Exception',
|
||||||
modname='exceptions',
|
modname='exceptions',
|
||||||
cls=exceptions.RemoteError,
|
cls=messaging.RemoteError,
|
||||||
args=[],
|
args=[],
|
||||||
kwargs={'foobar': 'blaa'},
|
kwargs={'foobar': 'blaa'},
|
||||||
str=("Remote error: Exception test\n"
|
str=("Remote error: Exception test\n"
|
||||||
@ -263,8 +261,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
msg=("Remote error: Exception test\n"
|
msg=("Remote error: Exception test\n"
|
||||||
"[u'traceback\\ntraceback\\n']."),
|
"[u'traceback\\ntraceback\\n']."),
|
||||||
remote_name='RemoteError',
|
remote_name='RemoteError',
|
||||||
remote_args=("Remote error: Exception test\n"
|
remote_args=(),
|
||||||
"[u'traceback\\ntraceback\\n'].", ),
|
|
||||||
remote_kwargs={'exc_type': 'Exception',
|
remote_kwargs={'exc_type': 'Exception',
|
||||||
'value': 'test',
|
'value': 'test',
|
||||||
'traceback': 'traceback\ntraceback\n'})),
|
'traceback': 'traceback\ntraceback\n'})),
|
||||||
@ -272,7 +269,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
dict(allowed=['exceptions'],
|
dict(allowed=['exceptions'],
|
||||||
clsname='SystemExit',
|
clsname='SystemExit',
|
||||||
modname='exceptions',
|
modname='exceptions',
|
||||||
cls=exceptions.RemoteError,
|
cls=messaging.RemoteError,
|
||||||
args=[],
|
args=[],
|
||||||
kwargs={},
|
kwargs={},
|
||||||
str=("Remote error: SystemExit test\n"
|
str=("Remote error: SystemExit test\n"
|
||||||
@ -280,8 +277,7 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
msg=("Remote error: SystemExit test\n"
|
msg=("Remote error: SystemExit test\n"
|
||||||
"[u'traceback\\ntraceback\\n']."),
|
"[u'traceback\\ntraceback\\n']."),
|
||||||
remote_name='RemoteError',
|
remote_name='RemoteError',
|
||||||
remote_args=("Remote error: SystemExit test\n"
|
remote_args=(),
|
||||||
"[u'traceback\\ntraceback\\n'].", ),
|
|
||||||
remote_kwargs={'exc_type': 'SystemExit',
|
remote_kwargs={'exc_type': 'SystemExit',
|
||||||
'value': 'test',
|
'value': 'test',
|
||||||
'traceback': 'traceback\ntraceback\n'})),
|
'traceback': 'traceback\ntraceback\n'})),
|
||||||
@ -310,5 +306,8 @@ class DeserializeRemoteExceptionTestCase(test_utils.BaseTestCase):
|
|||||||
self.assertIsInstance(ex, self.cls)
|
self.assertIsInstance(ex, self.cls)
|
||||||
self.assertEqual(ex.__class__.__name__, self.remote_name)
|
self.assertEqual(ex.__class__.__name__, self.remote_name)
|
||||||
self.assertEqual(str(ex), self.str)
|
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)
|
self.assertEqual(ex.args, self.remote_args)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user