Use 'Exception.__traceback__' for versioned notifications
The 'inspect.trace()' function is expected to be called within the context of an exception handler. The 'from_exc_and_traceback' class method of the 'nova.notification.objects.exception.ExceptionPayload' class uses this to get information about a provided exception, however, there are cases where this is called from outside of an exception handler. In these cases, we see an 'IndexError' since we can't get the last frame of a non-existent stacktrace. The solution to this is to fallback to using the traceback embedded in the exception. This is a bit lossy when decorators are involved but for all other cases this will give us the same information. This also allows us to avoid passing a traceback argument to the function since we have it to hand already. Change-Id: I404ca316b1bf2a963106cd34e927934befbd9b12 Signed-off-by: Stephen Finucane <stephenfin@redhat.com> Closes-Bug: #1881455
This commit is contained in:
@@ -12,14 +12,12 @@
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import traceback
|
||||
|
||||
from oslo_utils import excutils
|
||||
|
||||
|
||||
import nova.conf
|
||||
from nova.notifications.objects import base
|
||||
from nova.notifications.objects import exception
|
||||
from nova.notifications.objects import exception as exception_obj
|
||||
from nova.objects import fields
|
||||
from nova import rpc
|
||||
from nova import safe_utils
|
||||
@@ -27,26 +25,28 @@ from nova import safe_utils
|
||||
CONF = nova.conf.CONF
|
||||
|
||||
|
||||
def _emit_exception_notification(notifier, context, ex, function_name, args,
|
||||
source, trace_back):
|
||||
_emit_legacy_exception_notification(notifier, context, ex, function_name,
|
||||
args)
|
||||
_emit_versioned_exception_notification(context, ex, source, trace_back)
|
||||
def _emit_exception_notification(
|
||||
notifier, context, exception, function_name, args, source,
|
||||
):
|
||||
_emit_legacy_exception_notification(
|
||||
notifier, context, exception, function_name, args)
|
||||
_emit_versioned_exception_notification(context, exception, source)
|
||||
|
||||
|
||||
@rpc.if_notifications_enabled
|
||||
def _emit_versioned_exception_notification(context, ex, source, trace_back):
|
||||
versioned_exception_payload = \
|
||||
exception.ExceptionPayload.from_exc_and_traceback(ex, trace_back)
|
||||
def _emit_versioned_exception_notification(context, exception, source):
|
||||
payload = exception_obj.ExceptionPayload.from_exception(exception)
|
||||
publisher = base.NotificationPublisher(host=CONF.host, source=source)
|
||||
event_type = base.EventType(
|
||||
object='compute',
|
||||
action=fields.NotificationAction.EXCEPTION)
|
||||
notification = exception.ExceptionNotification(
|
||||
object='compute',
|
||||
action=fields.NotificationAction.EXCEPTION,
|
||||
)
|
||||
notification = exception_obj.ExceptionNotification(
|
||||
publisher=publisher,
|
||||
event_type=event_type,
|
||||
priority=fields.NotificationPriority.ERROR,
|
||||
payload=versioned_exception_payload)
|
||||
payload=payload,
|
||||
)
|
||||
notification.emit(context)
|
||||
|
||||
|
||||
@@ -67,16 +67,15 @@ def wrap_exception(notifier=None, get_notifier=None, binary=None):
|
||||
# contain confidential information.
|
||||
try:
|
||||
return f(self, context, *args, **kw)
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
except Exception as exc:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if notifier or get_notifier:
|
||||
call_dict = _get_call_dict(
|
||||
f, self, context, *args, **kw)
|
||||
function_name = f.__name__
|
||||
_emit_exception_notification(
|
||||
notifier or get_notifier(), context, e,
|
||||
function_name, call_dict, binary, tb)
|
||||
notifier or get_notifier(), context, exc,
|
||||
function_name, call_dict, binary)
|
||||
|
||||
return functools.wraps(f)(wrapped)
|
||||
return inner
|
||||
|
Reference in New Issue
Block a user