diff options
| author | Dan Smith <danms@us.ibm.com> | 2012-12-05 12:22:40 -0800 |
|---|---|---|
| committer | Dan Smith <danms@us.ibm.com> | 2012-12-06 08:31:13 -0800 |
| commit | 15ae704d927ba2ecd97d29195d30d5587987e2ad (patch) | |
| tree | bcca356f634c5f7a67d7f3e7b0672a8ac3e7ef72 /openstack/common/rpc/common.py | |
| parent | 2f7a7edc41d0e7b663877592aeda14e766a64241 (diff) | |
Allow exceptions to pass over RPC silently
When one service performs an operation on behalf of another, the
act of passing back an exception (even a known one) causes a lot
of scary log messages about the (presumed to be) error case. This
patch adds a client_exceptions decorator common/rpc/common.py,
which allows RPC services to declare the list of expected exceptions
for each method. If such an exception is raised during the RPC
dispatch, it is wrapped in a ClientException so that the RPC layer
can gracefully pass it back without overly-verbose logging.
This will allow us to fix nova bug 1084707
Change-Id: I4e7b19dc730342091fd70a717065741d56da4555
Diffstat (limited to 'openstack/common/rpc/common.py')
| -rw-r--r-- | openstack/common/rpc/common.py | 41 |
1 files changed, 38 insertions, 3 deletions
diff --git a/openstack/common/rpc/common.py b/openstack/common/rpc/common.py index c56c9a6..efdf26f 100644 --- a/openstack/common/rpc/common.py +++ b/openstack/common/rpc/common.py @@ -18,6 +18,7 @@ # under the License. import copy +import sys import traceback from openstack.common.gettextutils import _ @@ -195,7 +196,7 @@ def _safe_log(log_func, msg, msg_data): return log_func(msg, msg_data) -def serialize_remote_exception(failure_info): +def serialize_remote_exception(failure_info, log_failure=True): """Prepares exception data to be sent over rpc. Failure_info should be a sys.exc_info() tuple. @@ -203,8 +204,9 @@ def serialize_remote_exception(failure_info): """ tb = traceback.format_exception(*failure_info) failure = failure_info[1] - LOG.error(_("Returning exception %s to caller"), unicode(failure)) - LOG.error(tb) + if log_failure: + LOG.error(_("Returning exception %s to caller"), unicode(failure)) + LOG.error(tb) kwargs = {} if hasattr(failure, 'kwargs'): @@ -309,3 +311,36 @@ class CommonRpcContext(object): context.values['read_deleted'] = read_deleted return context + + +class ClientException(Exception): + """This encapsulates some actual exception that is expected to be + hit by an RPC proxy object. Merely instantiating it records the + current exception information, which will be passed back to the + RPC client without exceptional logging.""" + def __init__(self): + self._exc_info = sys.exc_info() + + +def catch_client_exception(exceptions, func, *args, **kwargs): + try: + return func(*args, **kwargs) + except Exception, e: + if type(e) in exceptions: + raise ClientException() + else: + raise + + +def client_exceptions(*exceptions): + """Decorator for manager methods that raise expected exceptions. + Marking a Manager method with this decorator allows the declaration + of expected exceptions that the RPC layer should not consider fatal, + and not log as if they were generated in a real error scenario. Note + that this will cause listed exceptions to be wrapped in a + ClientException, which is used internally by the RPC layer.""" + def outer(func): + def inner(*args, **kwargs): + return catch_client_exception(exceptions, func, *args, **kwargs) + return inner + return outer |
