Skip to content

Commit 125df26

Browse files
committed
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 <[email protected]> Closes-Bug: #1881455
1 parent 03b00ae commit 125df26

File tree

13 files changed

+161
-187
lines changed

13 files changed

+161
-187
lines changed

nova/compute/manager.py

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2384,42 +2384,38 @@ def _build_and_run_instance(self, context, instance, image, injected_files,
23842384
with excutils.save_and_reraise_exception():
23852385
self._notify_about_instance_usage(context, instance,
23862386
'create.error', fault=e)
2387-
tb = traceback.format_exc()
23882387
compute_utils.notify_about_instance_create(
23892388
context, instance, self.host,
23902389
phase=fields.NotificationPhase.ERROR, exception=e,
2391-
bdms=block_device_mapping, tb=tb)
2390+
bdms=block_device_mapping)
23922391
except exception.ComputeResourcesUnavailable as e:
23932392
LOG.debug(e.format_message(), instance=instance)
23942393
self._notify_about_instance_usage(context, instance,
23952394
'create.error', fault=e)
2396-
tb = traceback.format_exc()
23972395
compute_utils.notify_about_instance_create(
23982396
context, instance, self.host,
23992397
phase=fields.NotificationPhase.ERROR, exception=e,
2400-
bdms=block_device_mapping, tb=tb)
2398+
bdms=block_device_mapping)
24012399
raise exception.RescheduledException(
24022400
instance_uuid=instance.uuid, reason=e.format_message())
24032401
except exception.BuildAbortException as e:
24042402
with excutils.save_and_reraise_exception():
24052403
LOG.debug(e.format_message(), instance=instance)
24062404
self._notify_about_instance_usage(context, instance,
24072405
'create.error', fault=e)
2408-
tb = traceback.format_exc()
24092406
compute_utils.notify_about_instance_create(
24102407
context, instance, self.host,
24112408
phase=fields.NotificationPhase.ERROR, exception=e,
2412-
bdms=block_device_mapping, tb=tb)
2409+
bdms=block_device_mapping)
24132410
except exception.NoMoreFixedIps as e:
24142411
LOG.warning('No more fixed IP to be allocated',
24152412
instance=instance)
24162413
self._notify_about_instance_usage(context, instance,
24172414
'create.error', fault=e)
2418-
tb = traceback.format_exc()
24192415
compute_utils.notify_about_instance_create(
24202416
context, instance, self.host,
24212417
phase=fields.NotificationPhase.ERROR, exception=e,
2422-
bdms=block_device_mapping, tb=tb)
2418+
bdms=block_device_mapping)
24232419
msg = _('Failed to allocate the network(s) with error %s, '
24242420
'not rescheduling.') % e.format_message()
24252421
raise exception.BuildAbortException(instance_uuid=instance.uuid,
@@ -2434,11 +2430,10 @@ def _build_and_run_instance(self, context, instance, image, injected_files,
24342430
instance=instance)
24352431
self._notify_about_instance_usage(context, instance,
24362432
'create.error', fault=e)
2437-
tb = traceback.format_exc()
24382433
compute_utils.notify_about_instance_create(
24392434
context, instance, self.host,
24402435
phase=fields.NotificationPhase.ERROR, exception=e,
2441-
bdms=block_device_mapping, tb=tb)
2436+
bdms=block_device_mapping)
24422437
msg = _('Failed to allocate the network(s), not rescheduling.')
24432438
raise exception.BuildAbortException(instance_uuid=instance.uuid,
24442439
reason=msg)
@@ -2457,23 +2452,21 @@ def _build_and_run_instance(self, context, instance, image, injected_files,
24572452
exception.RequestedVRamTooHigh) as e:
24582453
self._notify_about_instance_usage(context, instance,
24592454
'create.error', fault=e)
2460-
tb = traceback.format_exc()
24612455
compute_utils.notify_about_instance_create(
24622456
context, instance, self.host,
24632457
phase=fields.NotificationPhase.ERROR, exception=e,
2464-
bdms=block_device_mapping, tb=tb)
2458+
bdms=block_device_mapping)
24652459
raise exception.BuildAbortException(instance_uuid=instance.uuid,
24662460
reason=e.format_message())
24672461
except Exception as e:
24682462
LOG.exception('Failed to build and run instance',
24692463
instance=instance)
24702464
self._notify_about_instance_usage(context, instance,
24712465
'create.error', fault=e)
2472-
tb = traceback.format_exc()
24732466
compute_utils.notify_about_instance_create(
24742467
context, instance, self.host,
24752468
phase=fields.NotificationPhase.ERROR, exception=e,
2476-
bdms=block_device_mapping, tb=tb)
2469+
bdms=block_device_mapping)
24772470
raise exception.RescheduledException(
24782471
instance_uuid=instance.uuid, reason=six.text_type(e))
24792472

@@ -2505,11 +2498,10 @@ def _build_and_run_instance(self, context, instance, image, injected_files,
25052498
with excutils.save_and_reraise_exception():
25062499
self._notify_about_instance_usage(context, instance,
25072500
'create.error', fault=e)
2508-
tb = traceback.format_exc()
25092501
compute_utils.notify_about_instance_create(
25102502
context, instance, self.host,
25112503
phase=fields.NotificationPhase.ERROR, exception=e,
2512-
bdms=block_device_mapping, tb=tb)
2504+
bdms=block_device_mapping)
25132505

25142506
self._update_scheduler_instance_info(context, instance)
25152507
self._notify_about_instance_usage(context, instance, 'create.end',
@@ -3280,13 +3272,11 @@ def _rebuild_default_impl(self, context, instance, image_meta,
32803272
block_device_info=new_block_device_info)
32813273

32823274
def _notify_instance_rebuild_error(self, context, instance, error, bdms):
3283-
tb = traceback.format_exc()
32843275
self._notify_about_instance_usage(context, instance,
32853276
'rebuild.error', fault=error)
32863277
compute_utils.notify_about_instance_rebuild(
32873278
context, instance, self.host,
3288-
phase=fields.NotificationPhase.ERROR, exception=error, bdms=bdms,
3289-
tb=tb)
3279+
phase=fields.NotificationPhase.ERROR, exception=error, bdms=bdms)
32903280

32913281
@messaging.expected_exceptions(exception.PreserveEphemeralNotSupported)
32923282
@wrap_exception()
@@ -3793,12 +3783,11 @@ def bad_volumes_callback(bad_devices):
37933783
instance, error, exc_info)
37943784
self._notify_about_instance_usage(context, instance,
37953785
'reboot.error', fault=error)
3796-
tb = traceback.format_exc()
37973786
compute_utils.notify_about_instance_action(
37983787
context, instance, self.host,
37993788
action=fields.NotificationAction.REBOOT,
38003789
phase=fields.NotificationPhase.ERROR,
3801-
exception=error, bdms=bdms, tb=tb
3790+
exception=error, bdms=bdms
38023791
)
38033792
ctxt.reraise = False
38043793
else:
@@ -5329,7 +5318,7 @@ def _reschedule_resize_or_reraise(self, context, instance, exc_info,
53295318
action=fields.NotificationAction.RESIZE,
53305319
phase=fields.NotificationPhase.ERROR,
53315320
exception=error,
5332-
tb=','.join(traceback.format_exception(*exc_info)))
5321+
)
53335322

53345323
if rescheduled:
53355324
self._log_original_error(exc_info, instance_uuid)
@@ -5342,7 +5331,7 @@ def _reschedule_resize_or_reraise(self, context, instance, exc_info,
53425331
action=fields.NotificationAction.RESIZE,
53435332
phase=fields.NotificationPhase.ERROR,
53445333
exception=exc_info[1],
5345-
tb=','.join(traceback.format_exception(*exc_info)))
5334+
)
53465335
else:
53475336
# not re-scheduling
53485337
six.reraise(*exc_info)
@@ -7043,13 +7032,12 @@ def _attach_volume(self, context, instance, bdm):
70437032
exc, instance=instance)
70447033
else:
70457034
self.volume_api.unreserve_volume(context, bdm.volume_id)
7046-
tb = traceback.format_exc()
70477035
compute_utils.notify_about_volume_attach_detach(
70487036
context, instance, self.host,
70497037
action=fields.NotificationAction.VOLUME_ATTACH,
70507038
phase=fields.NotificationPhase.ERROR,
70517039
exception=e,
7052-
volume_id=bdm.volume_id, tb=tb)
7040+
volume_id=bdm.volume_id)
70537041

70547042
info = {'volume_id': bdm.volume_id}
70557043
self._notify_about_instance_usage(
@@ -7250,11 +7238,10 @@ def _swap_volume(self, context, instance, bdm, connector,
72507238
except Exception as ex:
72517239
failed = True
72527240
with excutils.save_and_reraise_exception():
7253-
tb = traceback.format_exc()
72547241
compute_utils.notify_about_volume_swap(
72557242
context, instance, self.host,
72567243
fields.NotificationPhase.ERROR,
7257-
old_volume_id, new_volume_id, ex, tb)
7244+
old_volume_id, new_volume_id, ex)
72587245
if new_cinfo:
72597246
msg = ("Failed to swap volume %(old_volume_id)s "
72607247
"for %(new_volume_id)s")
@@ -7563,12 +7550,11 @@ def attach_interface(self, context, instance, network_id, port_id,
75637550
instance=instance)
75647551
self._deallocate_port_for_instance(context, instance, port_id)
75657552

7566-
tb = traceback.format_exc()
75677553
compute_utils.notify_about_instance_action(
75687554
context, instance, self.host,
75697555
action=fields.NotificationAction.INTERFACE_ATTACH,
75707556
phase=fields.NotificationPhase.ERROR,
7571-
exception=ex, tb=tb)
7557+
exception=ex)
75727558

75737559
raise exception.InterfaceAttachFailed(
75747560
instance_uuid=instance.uuid)

nova/compute/utils.py

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -452,22 +452,23 @@ def notify_about_instance_usage(notifier, context, instance, event_suffix,
452452
method(context, 'compute.instance.%s' % event_suffix, usage_info)
453453

454454

455-
def _get_fault_and_priority_from_exc_and_tb(exception, tb):
455+
def _get_fault_and_priority_from_exception(exception: Exception):
456456
fault = None
457457
priority = fields.NotificationPriority.INFO
458458

459-
if exception:
460-
priority = fields.NotificationPriority.ERROR
461-
fault = notification_exception.ExceptionPayload.from_exc_and_traceback(
462-
exception, tb)
459+
if not exception:
460+
return fault, priority
461+
462+
fault = notification_exception.ExceptionPayload.from_exception(exception)
463+
priority = fields.NotificationPriority.ERROR
463464

464465
return fault, priority
465466

466467

467468
@rpc.if_notifications_enabled
468469
def notify_about_instance_action(context, instance, host, action, phase=None,
469470
source=fields.NotificationSource.COMPUTE,
470-
exception=None, bdms=None, tb=None):
471+
exception=None, bdms=None):
471472
"""Send versioned notification about the action made on the instance
472473
:param instance: the instance which the action performed on
473474
:param host: the host emitting the notification
@@ -476,10 +477,9 @@ def notify_about_instance_action(context, instance, host, action, phase=None,
476477
:param source: the source of the notification
477478
:param exception: the thrown exception (used in error notifications)
478479
:param bdms: BlockDeviceMappingList object for the instance. If it is not
479-
provided then we will load it from the db if so configured
480-
:param tb: the traceback (used in error notifications)
480+
provided then we will load it from the db if so configured
481481
"""
482-
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
482+
fault, priority = _get_fault_and_priority_from_exception(exception)
483483
payload = instance_notification.InstanceActionPayload(
484484
context=context,
485485
instance=instance,
@@ -500,7 +500,7 @@ def notify_about_instance_action(context, instance, host, action, phase=None,
500500

501501
@rpc.if_notifications_enabled
502502
def notify_about_instance_create(context, instance, host, phase=None,
503-
exception=None, bdms=None, tb=None):
503+
exception=None, bdms=None):
504504
"""Send versioned notification about instance creation
505505
506506
:param context: the request context
@@ -510,9 +510,8 @@ def notify_about_instance_create(context, instance, host, phase=None,
510510
:param exception: the thrown exception (used in error notifications)
511511
:param bdms: BlockDeviceMappingList object for the instance. If it is not
512512
provided then we will load it from the db if so configured
513-
:param tb: the traceback (used in error notifications)
514513
"""
515-
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
514+
fault, priority = _get_fault_and_priority_from_exception(exception)
516515
payload = instance_notification.InstanceCreatePayload(
517516
context=context,
518517
instance=instance,
@@ -558,17 +557,16 @@ def notify_about_scheduler_action(context, request_spec, action, phase=None,
558557

559558
@rpc.if_notifications_enabled
560559
def notify_about_volume_attach_detach(context, instance, host, action, phase,
561-
volume_id=None, exception=None, tb=None):
560+
volume_id=None, exception=None):
562561
"""Send versioned notification about the action made on the instance
563562
:param instance: the instance which the action performed on
564563
:param host: the host emitting the notification
565564
:param action: the name of the action
566565
:param phase: the phase of the action
567566
:param volume_id: id of the volume will be attached
568567
:param exception: the thrown exception (used in error notifications)
569-
:param tb: the traceback (used in error notifications)
570568
"""
571-
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
569+
fault, priority = _get_fault_and_priority_from_exception(exception)
572570
payload = instance_notification.InstanceActionVolumePayload(
573571
context=context,
574572
instance=instance,
@@ -590,17 +588,16 @@ def notify_about_volume_attach_detach(context, instance, host, action, phase,
590588
@rpc.if_notifications_enabled
591589
def notify_about_instance_rescue_action(context, instance, host,
592590
rescue_image_ref, phase=None,
593-
exception=None, tb=None):
591+
exception=None):
594592
"""Send versioned notification about the action made on the instance
595593
596594
:param instance: the instance which the action performed on
597595
:param host: the host emitting the notification
598596
:param rescue_image_ref: the rescue image ref
599597
:param phase: the phase of the action
600598
:param exception: the thrown exception (used in error notifications)
601-
:param tb: the traceback (used in error notifications)
602599
"""
603-
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
600+
fault, priority = _get_fault_and_priority_from_exception(exception)
604601
payload = instance_notification.InstanceActionRescuePayload(
605602
context=context,
606603
instance=instance,
@@ -644,8 +641,7 @@ def notify_about_keypair_action(context, keypair, action, phase):
644641

645642
@rpc.if_notifications_enabled
646643
def notify_about_volume_swap(context, instance, host, phase,
647-
old_volume_id, new_volume_id, exception=None,
648-
tb=None):
644+
old_volume_id, new_volume_id, exception=None):
649645
"""Send versioned notification about the volume swap action
650646
on the instance
651647
@@ -656,9 +652,8 @@ def notify_about_volume_swap(context, instance, host, phase,
656652
:param old_volume_id: the ID of the volume that is copied from and detached
657653
:param new_volume_id: the ID of the volume that is copied to and attached
658654
:param exception: an exception
659-
:param tb: the traceback (used in error notifications)
660655
"""
661-
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
656+
fault, priority = _get_fault_and_priority_from_exception(exception)
662657
payload = instance_notification.InstanceActionVolumeSwapPayload(
663658
context=context,
664659
instance=instance,
@@ -877,7 +872,7 @@ def notify_about_instance_rebuild(context, instance, host,
877872
action=fields.NotificationAction.REBUILD,
878873
phase=None,
879874
source=fields.NotificationSource.COMPUTE,
880-
exception=None, bdms=None, tb=None):
875+
exception=None, bdms=None):
881876
"""Send versioned notification about instance rebuild
882877
883878
:param instance: the instance which the action performed on
@@ -888,9 +883,8 @@ def notify_about_instance_rebuild(context, instance, host,
888883
:param exception: the thrown exception (used in error notifications)
889884
:param bdms: BlockDeviceMappingList object for the instance. If it is not
890885
provided then we will load it from the db if so configured
891-
:param tb: the traceback (used in error notifications)
892886
"""
893-
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
887+
fault, priority = _get_fault_and_priority_from_exception(exception)
894888
payload = instance_notification.InstanceActionRebuildPayload(
895889
context=context,
896890
instance=instance,
@@ -938,15 +932,14 @@ def notify_about_metrics_update(context, host, host_ip, nodename,
938932

939933

940934
@rpc.if_notifications_enabled
941-
def notify_about_libvirt_connect_error(context, ip, exception, tb):
935+
def notify_about_libvirt_connect_error(context, ip, exception):
942936
"""Send a versioned notification about libvirt connect error.
943937
944938
:param context: the request context
945939
:param ip: the IP address of the host
946940
:param exception: the thrown exception
947-
:param tb: the traceback
948941
"""
949-
fault, _ = _get_fault_and_priority_from_exc_and_tb(exception, tb)
942+
fault, _ = _get_fault_and_priority_from_exception(exception)
950943
payload = libvirt_notification.LibvirtErrorPayload(ip=ip, reason=fault)
951944
notification = libvirt_notification.LibvirtErrorNotification(
952945
priority=fields.NotificationPriority.ERROR,
@@ -984,7 +977,7 @@ def notify_about_volume_usage(context, vol_usage, host):
984977

985978
@rpc.if_notifications_enabled
986979
def notify_about_compute_task_error(context, action, instance_uuid,
987-
request_spec, state, exception, tb):
980+
request_spec, state, exception):
988981
"""Send a versioned notification about compute task error.
989982
990983
:param context: the request context
@@ -1001,7 +994,7 @@ def notify_about_compute_task_error(context, action, instance_uuid,
1001994
request_spec = objects.RequestSpec.from_primitives(
1002995
context, request_spec, {})
1003996

1004-
fault, _ = _get_fault_and_priority_from_exc_and_tb(exception, tb)
997+
fault, _ = _get_fault_and_priority_from_exception(exception)
1005998
payload = task_notification.ComputeTaskPayload(
1006999
instance_uuid=instance_uuid, request_spec=request_spec, state=state,
10071000
reason=fault)

0 commit comments

Comments
 (0)