Add full traceback to ExceptionPayload in versioned notifications
This patch adds full traceback to ExceptionPayload in versioned notifications. The instance fault field and instance-action REST API has already provide the traceback to the admin users (controlable through policy) and the notifications are also admin only things as they are emitted to the message bus by default. So it is assumed that security is not a bigger concern for the notification than for the REST API. On the ML [1] post there was no objection to add new string field to the ExceptionPayload that will hold the serialized traceback object. [1] http://lists.openstack.org/pipermail/openstack-dev/2018-March/128105.html Implements: blueprint add-full-traceback-to-error-notifications Change-Id: Id587967ea4f9980c292492e2f659bf55fb037b28
This commit is contained in:
@@ -5,11 +5,12 @@
|
|||||||
"exception": "AggregateNameExists",
|
"exception": "AggregateNameExists",
|
||||||
"exception_message": "Aggregate versioned_exc_aggregate already exists.",
|
"exception_message": "Aggregate versioned_exc_aggregate already exists.",
|
||||||
"function_name": "_aggregate_create_in_db",
|
"function_name": "_aggregate_create_in_db",
|
||||||
"module_name": "nova.objects.aggregate"
|
"module_name": "nova.objects.aggregate",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name": "ExceptionPayload",
|
"nova_object.name": "ExceptionPayload",
|
||||||
"nova_object.namespace": "nova",
|
"nova_object.namespace": "nova",
|
||||||
"nova_object.version": "1.0"
|
"nova_object.version": "1.1"
|
||||||
},
|
},
|
||||||
"priority": "ERROR",
|
"priority": "ERROR",
|
||||||
"publisher_id": "nova-api:fake-mini"
|
"publisher_id": "nova-api:fake-mini"
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
"exception": "FlavorDiskTooSmall",
|
"exception": "FlavorDiskTooSmall",
|
||||||
"exception_message": "The created instance's disk would be too small.",
|
"exception_message": "The created instance's disk would be too small.",
|
||||||
"function_name": "_build_resources",
|
"function_name": "_build_resources",
|
||||||
"module_name": "nova.tests.functional.notification_sample_tests.test_instance"
|
"module_name": "nova.tests.functional.notification_sample_tests.test_instance",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name": "ExceptionPayload",
|
"nova_object.name": "ExceptionPayload",
|
||||||
"nova_object.namespace": "nova",
|
"nova_object.namespace": "nova",
|
||||||
"nova_object.version": "1.0"
|
"nova_object.version": "1.1"
|
||||||
},
|
},
|
||||||
"ip_addresses": [],
|
"ip_addresses": [],
|
||||||
"launched_at": null,
|
"launched_at": null,
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
"exception": "InterfaceAttachFailed",
|
"exception": "InterfaceAttachFailed",
|
||||||
"exception_message": "dummy",
|
"exception_message": "dummy",
|
||||||
"function_name": "_unsuccessful_attach_interface",
|
"function_name": "_unsuccessful_attach_interface",
|
||||||
"module_name": "nova.tests.functional.notification_sample_tests.test_instance"
|
"module_name": "nova.tests.functional.notification_sample_tests.test_instance",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name": "ExceptionPayload",
|
"nova_object.name": "ExceptionPayload",
|
||||||
"nova_object.namespace": "nova",
|
"nova_object.namespace": "nova",
|
||||||
"nova_object.version": "1.0"
|
"nova_object.version": "1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
"exception": "UnsupportedVirtType",
|
"exception": "UnsupportedVirtType",
|
||||||
"exception_message": "Virtualization type 'FakeVirt' is not supported by this compute driver",
|
"exception_message": "Virtualization type 'FakeVirt' is not supported by this compute driver",
|
||||||
"function_name": "_hard_reboot",
|
"function_name": "_hard_reboot",
|
||||||
"module_name": "nova.tests.functional.notification_sample_tests.test_instance"
|
"module_name": "nova.tests.functional.notification_sample_tests.test_instance",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name": "ExceptionPayload",
|
"nova_object.name": "ExceptionPayload",
|
||||||
"nova_object.namespace": "nova",
|
"nova_object.namespace": "nova",
|
||||||
"nova_object.version": "1.0"
|
"nova_object.version": "1.1"
|
||||||
},
|
},
|
||||||
"task_state":"reboot_started_hard"
|
"task_state":"reboot_started_hard"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@
|
|||||||
"module_name": "nova.tests.functional.notification_sample_tests.test_instance",
|
"module_name": "nova.tests.functional.notification_sample_tests.test_instance",
|
||||||
"exception_message": "Virtual Interface creation failed",
|
"exception_message": "Virtual Interface creation failed",
|
||||||
"function_name": "_virtual_interface_create_failed",
|
"function_name": "_virtual_interface_create_failed",
|
||||||
"exception": "VirtualInterfaceCreateException"
|
"exception": "VirtualInterfaceCreateException",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.version": "1.0",
|
"nova_object.version": "1.1",
|
||||||
"nova_object.namespace": "nova"
|
"nova_object.namespace": "nova"
|
||||||
},
|
},
|
||||||
"architecture": null,
|
"architecture": null,
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
"exception":"FlavorDiskTooSmall",
|
"exception":"FlavorDiskTooSmall",
|
||||||
"exception_message":"The created instance's disk would be too small.",
|
"exception_message":"The created instance's disk would be too small.",
|
||||||
"function_name":"_build_resources",
|
"function_name":"_build_resources",
|
||||||
"module_name":"nova.tests.functional.notification_sample_tests.test_instance"
|
"module_name":"nova.tests.functional.notification_sample_tests.test_instance",
|
||||||
|
"traceback":"Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name":"ExceptionPayload",
|
"nova_object.name":"ExceptionPayload",
|
||||||
"nova_object.namespace":"nova",
|
"nova_object.namespace":"nova",
|
||||||
"nova_object.version":"1.0"
|
"nova_object.version":"1.1"
|
||||||
},
|
},
|
||||||
"block_devices": [],
|
"block_devices": [],
|
||||||
"task_state": "resize_prep"
|
"task_state": "resize_prep"
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
"exception": "CinderConnectionFailed",
|
"exception": "CinderConnectionFailed",
|
||||||
"exception_message": "Connection to cinder host failed: Connection timed out",
|
"exception_message": "Connection to cinder host failed: Connection timed out",
|
||||||
"function_name": "attach_volume",
|
"function_name": "attach_volume",
|
||||||
"module_name": "nova.tests.functional.notification_sample_tests.test_instance"
|
"module_name": "nova.tests.functional.notification_sample_tests.test_instance",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name": "ExceptionPayload",
|
"nova_object.name": "ExceptionPayload",
|
||||||
"nova_object.namespace": "nova",
|
"nova_object.namespace": "nova",
|
||||||
"nova_object.version": "1.0"
|
"nova_object.version": "1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,11 +10,12 @@
|
|||||||
"exception": "TypeError",
|
"exception": "TypeError",
|
||||||
"exception_message": "'tuple' object does not support item assignment",
|
"exception_message": "'tuple' object does not support item assignment",
|
||||||
"function_name": "_init_volume_connection",
|
"function_name": "_init_volume_connection",
|
||||||
"module_name": "nova.compute.manager"
|
"module_name": "nova.compute.manager",
|
||||||
|
"traceback": "Traceback (most recent call last):\n File \"nova/compute/manager.py\", line ..."
|
||||||
},
|
},
|
||||||
"nova_object.name": "ExceptionPayload",
|
"nova_object.name": "ExceptionPayload",
|
||||||
"nova_object.namespace": "nova",
|
"nova_object.namespace": "nova",
|
||||||
"nova_object.version": "1.0"
|
"nova_object.version": "1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+31
-15
@@ -2017,18 +2017,20 @@ class ComputeManager(manager.Manager):
|
|||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
except exception.ComputeResourcesUnavailable as e:
|
except exception.ComputeResourcesUnavailable as e:
|
||||||
LOG.debug(e.format_message(), instance=instance)
|
LOG.debug(e.format_message(), instance=instance)
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
raise exception.RescheduledException(
|
raise exception.RescheduledException(
|
||||||
instance_uuid=instance.uuid, reason=e.format_message())
|
instance_uuid=instance.uuid, reason=e.format_message())
|
||||||
except exception.BuildAbortException as e:
|
except exception.BuildAbortException as e:
|
||||||
@@ -2036,20 +2038,22 @@ class ComputeManager(manager.Manager):
|
|||||||
LOG.debug(e.format_message(), instance=instance)
|
LOG.debug(e.format_message(), instance=instance)
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
except (exception.FixedIpLimitExceeded,
|
except (exception.FixedIpLimitExceeded,
|
||||||
exception.NoMoreNetworks, exception.NoMoreFixedIps) as e:
|
exception.NoMoreNetworks, exception.NoMoreFixedIps) as e:
|
||||||
LOG.warning('No more network or fixed IP to be allocated',
|
LOG.warning('No more network or fixed IP to be allocated',
|
||||||
instance=instance)
|
instance=instance)
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
msg = _('Failed to allocate the network(s) with error %s, '
|
msg = _('Failed to allocate the network(s) with error %s, '
|
||||||
'not rescheduling.') % e.format_message()
|
'not rescheduling.') % e.format_message()
|
||||||
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
||||||
@@ -2062,10 +2066,11 @@ class ComputeManager(manager.Manager):
|
|||||||
instance=instance)
|
instance=instance)
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
msg = _('Failed to allocate the network(s), not rescheduling.')
|
msg = _('Failed to allocate the network(s), not rescheduling.')
|
||||||
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
||||||
reason=msg)
|
reason=msg)
|
||||||
@@ -2084,19 +2089,21 @@ class ComputeManager(manager.Manager):
|
|||||||
exception.RequestedVRamTooHigh) as e:
|
exception.RequestedVRamTooHigh) as e:
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
||||||
reason=e.format_message())
|
reason=e.format_message())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
raise exception.RescheduledException(
|
raise exception.RescheduledException(
|
||||||
instance_uuid=instance.uuid, reason=six.text_type(e))
|
instance_uuid=instance.uuid, reason=six.text_type(e))
|
||||||
|
|
||||||
@@ -2128,10 +2135,11 @@ class ComputeManager(manager.Manager):
|
|||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'create.error', fault=e)
|
'create.error', fault=e)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_create(
|
compute_utils.notify_about_instance_create(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=e,
|
phase=fields.NotificationPhase.ERROR, exception=e,
|
||||||
bdms=block_device_mapping)
|
bdms=block_device_mapping, tb=tb)
|
||||||
|
|
||||||
self._update_scheduler_instance_info(context, instance)
|
self._update_scheduler_instance_info(context, instance)
|
||||||
self._notify_about_instance_usage(context, instance, 'create.end',
|
self._notify_about_instance_usage(context, instance, 'create.end',
|
||||||
@@ -2775,11 +2783,13 @@ class ComputeManager(manager.Manager):
|
|||||||
block_device_info=new_block_device_info)
|
block_device_info=new_block_device_info)
|
||||||
|
|
||||||
def _notify_instance_rebuild_error(self, context, instance, error, bdms):
|
def _notify_instance_rebuild_error(self, context, instance, error, bdms):
|
||||||
|
tb = traceback.format_exc()
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'rebuild.error', fault=error)
|
'rebuild.error', fault=error)
|
||||||
compute_utils.notify_about_instance_rebuild(
|
compute_utils.notify_about_instance_rebuild(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
phase=fields.NotificationPhase.ERROR, exception=error, bdms=bdms)
|
phase=fields.NotificationPhase.ERROR, exception=error, bdms=bdms,
|
||||||
|
tb=tb)
|
||||||
|
|
||||||
@messaging.expected_exceptions(exception.PreserveEphemeralNotSupported)
|
@messaging.expected_exceptions(exception.PreserveEphemeralNotSupported)
|
||||||
@wrap_exception()
|
@wrap_exception()
|
||||||
@@ -3229,11 +3239,12 @@ class ComputeManager(manager.Manager):
|
|||||||
instance, error, exc_info)
|
instance, error, exc_info)
|
||||||
self._notify_about_instance_usage(context, instance,
|
self._notify_about_instance_usage(context, instance,
|
||||||
'reboot.error', fault=error)
|
'reboot.error', fault=error)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_action(
|
compute_utils.notify_about_instance_action(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
action=fields.NotificationAction.REBOOT,
|
action=fields.NotificationAction.REBOOT,
|
||||||
phase=fields.NotificationPhase.ERROR,
|
phase=fields.NotificationPhase.ERROR,
|
||||||
exception=error, bdms=bdms
|
exception=error, bdms=bdms, tb=tb
|
||||||
)
|
)
|
||||||
ctxt.reraise = False
|
ctxt.reraise = False
|
||||||
else:
|
else:
|
||||||
@@ -4202,7 +4213,8 @@ class ComputeManager(manager.Manager):
|
|||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
action=fields.NotificationAction.RESIZE,
|
action=fields.NotificationAction.RESIZE,
|
||||||
phase=fields.NotificationPhase.ERROR,
|
phase=fields.NotificationPhase.ERROR,
|
||||||
exception=error)
|
exception=error,
|
||||||
|
tb=','.join(traceback.format_exception(*exc_info)))
|
||||||
if rescheduled:
|
if rescheduled:
|
||||||
self._log_original_error(exc_info, instance_uuid)
|
self._log_original_error(exc_info, instance_uuid)
|
||||||
compute_utils.add_instance_fault_from_exc(context,
|
compute_utils.add_instance_fault_from_exc(context,
|
||||||
@@ -4213,7 +4225,8 @@ class ComputeManager(manager.Manager):
|
|||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
action=fields.NotificationAction.RESIZE,
|
action=fields.NotificationAction.RESIZE,
|
||||||
phase=fields.NotificationPhase.ERROR,
|
phase=fields.NotificationPhase.ERROR,
|
||||||
exception=exc_info[1])
|
exception=exc_info[1],
|
||||||
|
tb=','.join(traceback.format_exception(*exc_info)))
|
||||||
else:
|
else:
|
||||||
# not re-scheduling
|
# not re-scheduling
|
||||||
six.reraise(*exc_info)
|
six.reraise(*exc_info)
|
||||||
@@ -5360,12 +5373,13 @@ class ComputeManager(manager.Manager):
|
|||||||
bdm['attachment_id'])
|
bdm['attachment_id'])
|
||||||
else:
|
else:
|
||||||
self.volume_api.unreserve_volume(context, bdm.volume_id)
|
self.volume_api.unreserve_volume(context, bdm.volume_id)
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_volume_attach_detach(
|
compute_utils.notify_about_volume_attach_detach(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
action=fields.NotificationAction.VOLUME_ATTACH,
|
action=fields.NotificationAction.VOLUME_ATTACH,
|
||||||
phase=fields.NotificationPhase.ERROR,
|
phase=fields.NotificationPhase.ERROR,
|
||||||
exception=e,
|
exception=e,
|
||||||
volume_id=bdm.volume_id)
|
volume_id=bdm.volume_id, tb=tb)
|
||||||
|
|
||||||
info = {'volume_id': bdm.volume_id}
|
info = {'volume_id': bdm.volume_id}
|
||||||
self._notify_about_instance_usage(
|
self._notify_about_instance_usage(
|
||||||
@@ -5561,10 +5575,11 @@ class ComputeManager(manager.Manager):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
failed = True
|
failed = True
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_volume_swap(
|
compute_utils.notify_about_volume_swap(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
fields.NotificationPhase.ERROR,
|
fields.NotificationPhase.ERROR,
|
||||||
old_volume_id, new_volume_id, ex)
|
old_volume_id, new_volume_id, ex, tb)
|
||||||
if new_cinfo:
|
if new_cinfo:
|
||||||
msg = ("Failed to swap volume %(old_volume_id)s "
|
msg = ("Failed to swap volume %(old_volume_id)s "
|
||||||
"for %(new_volume_id)s")
|
"for %(new_volume_id)s")
|
||||||
@@ -5799,11 +5814,12 @@ class ComputeManager(manager.Manager):
|
|||||||
LOG.warning("deallocate port %(port_id)s failed",
|
LOG.warning("deallocate port %(port_id)s failed",
|
||||||
{'port_id': port_id}, instance=instance)
|
{'port_id': port_id}, instance=instance)
|
||||||
|
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_instance_action(
|
compute_utils.notify_about_instance_action(
|
||||||
context, instance, self.host,
|
context, instance, self.host,
|
||||||
action=fields.NotificationAction.INTERFACE_ATTACH,
|
action=fields.NotificationAction.INTERFACE_ATTACH,
|
||||||
phase=fields.NotificationPhase.ERROR,
|
phase=fields.NotificationPhase.ERROR,
|
||||||
exception=ex)
|
exception=ex, tb=tb)
|
||||||
|
|
||||||
raise exception.InterfaceAttachFailed(
|
raise exception.InterfaceAttachFailed(
|
||||||
instance_uuid=instance.uuid)
|
instance_uuid=instance.uuid)
|
||||||
|
|||||||
+24
-16
@@ -366,14 +366,14 @@ def notify_about_instance_usage(notifier, context, instance, event_suffix,
|
|||||||
method(context, 'compute.instance.%s' % event_suffix, usage_info)
|
method(context, 'compute.instance.%s' % event_suffix, usage_info)
|
||||||
|
|
||||||
|
|
||||||
def _get_fault_and_priority_from_exc(exception):
|
def _get_fault_and_priority_from_exc_and_tb(exception, tb):
|
||||||
fault = None
|
fault = None
|
||||||
priority = fields.NotificationPriority.INFO
|
priority = fields.NotificationPriority.INFO
|
||||||
|
|
||||||
if exception:
|
if exception:
|
||||||
priority = fields.NotificationPriority.ERROR
|
priority = fields.NotificationPriority.ERROR
|
||||||
fault = notification_exception.ExceptionPayload.from_exception(
|
fault = notification_exception.ExceptionPayload.from_exc_and_traceback(
|
||||||
exception)
|
exception, tb)
|
||||||
|
|
||||||
return fault, priority
|
return fault, priority
|
||||||
|
|
||||||
@@ -381,7 +381,7 @@ def _get_fault_and_priority_from_exc(exception):
|
|||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def notify_about_instance_action(context, instance, host, action, phase=None,
|
def notify_about_instance_action(context, instance, host, action, phase=None,
|
||||||
source=fields.NotificationSource.COMPUTE,
|
source=fields.NotificationSource.COMPUTE,
|
||||||
exception=None, bdms=None):
|
exception=None, bdms=None, tb=None):
|
||||||
"""Send versioned notification about the action made on the instance
|
"""Send versioned notification about the action made on the instance
|
||||||
:param instance: the instance which the action performed on
|
:param instance: the instance which the action performed on
|
||||||
:param host: the host emitting the notification
|
:param host: the host emitting the notification
|
||||||
@@ -391,8 +391,9 @@ def notify_about_instance_action(context, instance, host, action, phase=None,
|
|||||||
:param exception: the thrown exception (used in error notifications)
|
:param exception: the thrown exception (used in error notifications)
|
||||||
:param bdms: BlockDeviceMappingList object for the instance. If it is not
|
:param bdms: BlockDeviceMappingList object for the instance. If it is not
|
||||||
provided then we will load it from the db if so configured
|
provided then we will load it from the db if so configured
|
||||||
|
:param tb: the traceback (used in error notifications)
|
||||||
"""
|
"""
|
||||||
fault, priority = _get_fault_and_priority_from_exc(exception)
|
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||||
payload = instance_notification.InstanceActionPayload(
|
payload = instance_notification.InstanceActionPayload(
|
||||||
context=context,
|
context=context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
@@ -413,7 +414,7 @@ def notify_about_instance_action(context, instance, host, action, phase=None,
|
|||||||
|
|
||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def notify_about_instance_create(context, instance, host, phase=None,
|
def notify_about_instance_create(context, instance, host, phase=None,
|
||||||
exception=None, bdms=None):
|
exception=None, bdms=None, tb=None):
|
||||||
"""Send versioned notification about instance creation
|
"""Send versioned notification about instance creation
|
||||||
|
|
||||||
:param context: the request context
|
:param context: the request context
|
||||||
@@ -423,8 +424,9 @@ def notify_about_instance_create(context, instance, host, phase=None,
|
|||||||
:param exception: the thrown exception (used in error notifications)
|
:param exception: the thrown exception (used in error notifications)
|
||||||
:param bdms: BlockDeviceMappingList object for the instance. If it is not
|
:param bdms: BlockDeviceMappingList object for the instance. If it is not
|
||||||
provided then we will load it from the db if so configured
|
provided then we will load it from the db if so configured
|
||||||
|
:param tb: the traceback (used in error notifications)
|
||||||
"""
|
"""
|
||||||
fault, priority = _get_fault_and_priority_from_exc(exception)
|
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||||
payload = instance_notification.InstanceCreatePayload(
|
payload = instance_notification.InstanceCreatePayload(
|
||||||
context=context,
|
context=context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
@@ -445,7 +447,7 @@ def notify_about_instance_create(context, instance, host, phase=None,
|
|||||||
|
|
||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def notify_about_volume_attach_detach(context, instance, host, action, phase,
|
def notify_about_volume_attach_detach(context, instance, host, action, phase,
|
||||||
volume_id=None, exception=None):
|
volume_id=None, exception=None, tb=None):
|
||||||
"""Send versioned notification about the action made on the instance
|
"""Send versioned notification about the action made on the instance
|
||||||
:param instance: the instance which the action performed on
|
:param instance: the instance which the action performed on
|
||||||
:param host: the host emitting the notification
|
:param host: the host emitting the notification
|
||||||
@@ -453,8 +455,9 @@ def notify_about_volume_attach_detach(context, instance, host, action, phase,
|
|||||||
:param phase: the phase of the action
|
:param phase: the phase of the action
|
||||||
:param volume_id: id of the volume will be attached
|
:param volume_id: id of the volume will be attached
|
||||||
:param exception: the thrown exception (used in error notifications)
|
:param exception: the thrown exception (used in error notifications)
|
||||||
|
:param tb: the traceback (used in error notifications)
|
||||||
"""
|
"""
|
||||||
fault, priority = _get_fault_and_priority_from_exc(exception)
|
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||||
payload = instance_notification.InstanceActionVolumePayload(
|
payload = instance_notification.InstanceActionVolumePayload(
|
||||||
context=context,
|
context=context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
@@ -474,8 +477,9 @@ def notify_about_volume_attach_detach(context, instance, host, action, phase,
|
|||||||
|
|
||||||
|
|
||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def notify_about_instance_rescue_action(
|
def notify_about_instance_rescue_action(context, instance, host,
|
||||||
context, instance, host, rescue_image_ref, phase=None, exception=None):
|
rescue_image_ref, phase=None,
|
||||||
|
exception=None, tb=None):
|
||||||
"""Send versioned notification about the action made on the instance
|
"""Send versioned notification about the action made on the instance
|
||||||
|
|
||||||
:param instance: the instance which the action performed on
|
:param instance: the instance which the action performed on
|
||||||
@@ -483,8 +487,9 @@ def notify_about_instance_rescue_action(
|
|||||||
:param rescue_image_ref: the rescue image ref
|
:param rescue_image_ref: the rescue image ref
|
||||||
:param phase: the phase of the action
|
:param phase: the phase of the action
|
||||||
:param exception: the thrown exception (used in error notifications)
|
:param exception: the thrown exception (used in error notifications)
|
||||||
|
:param tb: the traceback (used in error notifications)
|
||||||
"""
|
"""
|
||||||
fault, priority = _get_fault_and_priority_from_exc(exception)
|
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||||
payload = instance_notification.InstanceActionRescuePayload(
|
payload = instance_notification.InstanceActionRescuePayload(
|
||||||
context=context,
|
context=context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
@@ -528,7 +533,8 @@ def notify_about_keypair_action(context, keypair, action, phase):
|
|||||||
|
|
||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def notify_about_volume_swap(context, instance, host, phase,
|
def notify_about_volume_swap(context, instance, host, phase,
|
||||||
old_volume_id, new_volume_id, exception=None):
|
old_volume_id, new_volume_id, exception=None,
|
||||||
|
tb=None):
|
||||||
"""Send versioned notification about the volume swap action
|
"""Send versioned notification about the volume swap action
|
||||||
on the instance
|
on the instance
|
||||||
|
|
||||||
@@ -539,8 +545,9 @@ def notify_about_volume_swap(context, instance, host, phase,
|
|||||||
:param old_volume_id: the ID of the volume that is copied from and detached
|
:param old_volume_id: the ID of the volume that is copied from and detached
|
||||||
:param new_volume_id: the ID of the volume that is copied to and attached
|
:param new_volume_id: the ID of the volume that is copied to and attached
|
||||||
:param exception: an exception
|
:param exception: an exception
|
||||||
|
:param tb: the traceback (used in error notifications)
|
||||||
"""
|
"""
|
||||||
fault, priority = _get_fault_and_priority_from_exc(exception)
|
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||||
payload = instance_notification.InstanceActionVolumeSwapPayload(
|
payload = instance_notification.InstanceActionVolumeSwapPayload(
|
||||||
context=context,
|
context=context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
@@ -717,7 +724,7 @@ def notify_about_server_group_add_member(context, group_id):
|
|||||||
|
|
||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def notify_about_instance_rebuild(context, instance, host, phase=None,
|
def notify_about_instance_rebuild(context, instance, host, phase=None,
|
||||||
exception=None, bdms=None):
|
exception=None, bdms=None, tb=None):
|
||||||
"""Send versioned notification about instance rebuild
|
"""Send versioned notification about instance rebuild
|
||||||
|
|
||||||
:param instance: the instance which the action performed on
|
:param instance: the instance which the action performed on
|
||||||
@@ -726,8 +733,9 @@ def notify_about_instance_rebuild(context, instance, host, phase=None,
|
|||||||
:param exception: the thrown exception (used in error notifications)
|
:param exception: the thrown exception (used in error notifications)
|
||||||
:param bdms: BlockDeviceMappingList object for the instance. If it is not
|
:param bdms: BlockDeviceMappingList object for the instance. If it is not
|
||||||
provided then we will load it from the db if so configured
|
provided then we will load it from the db if so configured
|
||||||
|
:param tb: the traceback (used in error notifications)
|
||||||
"""
|
"""
|
||||||
fault, priority = _get_fault_and_priority_from_exc(exception)
|
fault, priority = _get_fault_and_priority_from_exc_and_tb(exception, tb)
|
||||||
payload = instance_notification.InstanceActionRebuildPayload(
|
payload = instance_notification.InstanceActionRebuildPayload(
|
||||||
context=context,
|
context=context,
|
||||||
instance=instance,
|
instance=instance,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import traceback
|
||||||
|
|
||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
|
|
||||||
@@ -27,15 +28,16 @@ CONF = nova.conf.CONF
|
|||||||
|
|
||||||
|
|
||||||
def _emit_exception_notification(notifier, context, ex, function_name, args,
|
def _emit_exception_notification(notifier, context, ex, function_name, args,
|
||||||
source):
|
source, trace_back):
|
||||||
_emit_legacy_exception_notification(notifier, context, ex, function_name,
|
_emit_legacy_exception_notification(notifier, context, ex, function_name,
|
||||||
args)
|
args)
|
||||||
_emit_versioned_exception_notification(context, ex, source)
|
_emit_versioned_exception_notification(context, ex, source, trace_back)
|
||||||
|
|
||||||
|
|
||||||
@rpc.if_notifications_enabled
|
@rpc.if_notifications_enabled
|
||||||
def _emit_versioned_exception_notification(context, ex, source):
|
def _emit_versioned_exception_notification(context, ex, source, trace_back):
|
||||||
versioned_exception_payload = exception.ExceptionPayload.from_exception(ex)
|
versioned_exception_payload = \
|
||||||
|
exception.ExceptionPayload.from_exc_and_traceback(ex, trace_back)
|
||||||
publisher = base.NotificationPublisher(host=CONF.host, source=source)
|
publisher = base.NotificationPublisher(host=CONF.host, source=source)
|
||||||
event_type = base.EventType(
|
event_type = base.EventType(
|
||||||
object='compute',
|
object='compute',
|
||||||
@@ -66,6 +68,7 @@ def wrap_exception(notifier=None, get_notifier=None, binary=None):
|
|||||||
try:
|
try:
|
||||||
return f(self, context, *args, **kw)
|
return f(self, context, *args, **kw)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
tb = traceback.format_exc()
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
if notifier or get_notifier:
|
if notifier or get_notifier:
|
||||||
call_dict = _get_call_dict(
|
call_dict = _get_call_dict(
|
||||||
@@ -73,7 +76,7 @@ def wrap_exception(notifier=None, get_notifier=None, binary=None):
|
|||||||
function_name = f.__name__
|
function_name = f.__name__
|
||||||
_emit_exception_notification(
|
_emit_exception_notification(
|
||||||
notifier or get_notifier(), context, e,
|
notifier or get_notifier(), context, e,
|
||||||
function_name, call_dict, binary)
|
function_name, call_dict, binary, tb)
|
||||||
|
|
||||||
return functools.wraps(f)(wrapped)
|
return functools.wraps(f)(wrapped)
|
||||||
return inner
|
return inner
|
||||||
|
|||||||
@@ -21,24 +21,27 @@ from nova.objects import fields
|
|||||||
@nova_base.NovaObjectRegistry.register_notification
|
@nova_base.NovaObjectRegistry.register_notification
|
||||||
class ExceptionPayload(base.NotificationPayloadBase):
|
class ExceptionPayload(base.NotificationPayloadBase):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
VERSION = '1.0'
|
# Version 1.1: Add traceback field to ExceptionPayload
|
||||||
|
VERSION = '1.1'
|
||||||
fields = {
|
fields = {
|
||||||
'module_name': fields.StringField(),
|
'module_name': fields.StringField(),
|
||||||
'function_name': fields.StringField(),
|
'function_name': fields.StringField(),
|
||||||
'exception': fields.StringField(),
|
'exception': fields.StringField(),
|
||||||
'exception_message': fields.StringField()
|
'exception_message': fields.StringField(),
|
||||||
|
'traceback': fields.StringField()
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, module_name, function_name, exception,
|
def __init__(self, module_name, function_name, exception,
|
||||||
exception_message):
|
exception_message, traceback):
|
||||||
super(ExceptionPayload, self).__init__()
|
super(ExceptionPayload, self).__init__()
|
||||||
self.module_name = module_name
|
self.module_name = module_name
|
||||||
self.function_name = function_name
|
self.function_name = function_name
|
||||||
self.exception = exception
|
self.exception = exception
|
||||||
self.exception_message = exception_message
|
self.exception_message = exception_message
|
||||||
|
self.traceback = traceback
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_exception(cls, fault):
|
def from_exc_and_traceback(cls, fault, traceback):
|
||||||
trace = inspect.trace()[-1]
|
trace = inspect.trace()[-1]
|
||||||
# TODO(gibi): apply strutils.mask_password on exception_message and
|
# TODO(gibi): apply strutils.mask_password on exception_message and
|
||||||
# consider emitting the exception_message only if the safe flag is
|
# consider emitting the exception_message only if the safe flag is
|
||||||
@@ -49,7 +52,8 @@ class ExceptionPayload(base.NotificationPayloadBase):
|
|||||||
function_name=trace[3],
|
function_name=trace[3],
|
||||||
module_name=module_name,
|
module_name=module_name,
|
||||||
exception=fault.__class__.__name__,
|
exception=fault.__class__.__name__,
|
||||||
exception_message=six.text_type(fault))
|
exception_message=six.text_type(fault),
|
||||||
|
traceback=traceback)
|
||||||
|
|
||||||
|
|
||||||
@base.notification_sample('compute-exception.json')
|
@base.notification_sample('compute-exception.json')
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ class TestExceptionNotificationSample(
|
|||||||
self.admin_api.api_post, 'os-aggregates', post)
|
self.admin_api.api_post, 'os-aggregates', post)
|
||||||
|
|
||||||
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
self.assertEqual(4, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||||
|
traceback = fake_notifier.VERSIONED_NOTIFICATIONS[3][
|
||||||
|
'payload']['nova_object.data']['traceback']
|
||||||
|
self.assertIn('AggregateNameExists', traceback)
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'compute-exception',
|
'compute-exception',
|
||||||
|
replacements={
|
||||||
|
'traceback': self.ANY},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[3])
|
||||||
|
|||||||
@@ -367,6 +367,10 @@ class TestInstanceNotificationSample(
|
|||||||
|
|
||||||
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[1]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn('raise exception.FlavorDiskTooSmall()', tb)
|
||||||
|
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'instance-create-start',
|
'instance-create-start',
|
||||||
replacements={
|
replacements={
|
||||||
@@ -377,7 +381,8 @@ class TestInstanceNotificationSample(
|
|||||||
'instance-create-error',
|
'instance-create-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']},
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
||||||
|
|
||||||
def test_instance_exists_usage_audit(self):
|
def test_instance_exists_usage_audit(self):
|
||||||
@@ -910,10 +915,16 @@ class TestInstanceNotificationSample(
|
|||||||
fake_notifier.VERSIONED_NOTIFICATIONS)
|
fake_notifier.VERSIONED_NOTIFICATIONS)
|
||||||
# Note(gibi): There is also an instance.exists notification emitted
|
# Note(gibi): There is also an instance.exists notification emitted
|
||||||
# during the rescheduling
|
# during the rescheduling
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[2]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn("raise exception.FlavorDiskTooSmall()", tb)
|
||||||
|
|
||||||
self._verify_notification('instance-resize-error',
|
self._verify_notification('instance-resize-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY
|
||||||
},
|
},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
|
||||||
|
|
||||||
@@ -970,10 +981,16 @@ class TestInstanceNotificationSample(
|
|||||||
self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS),
|
self.assertEqual(5, len(fake_notifier.VERSIONED_NOTIFICATIONS),
|
||||||
'Unexpected number of notifications: %s' %
|
'Unexpected number of notifications: %s' %
|
||||||
fake_notifier.VERSIONED_NOTIFICATIONS)
|
fake_notifier.VERSIONED_NOTIFICATIONS)
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[2]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn("raise exception.FlavorDiskTooSmall()", tb)
|
||||||
|
|
||||||
self._verify_notification('instance-resize-error',
|
self._verify_notification('instance-resize-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY
|
||||||
},
|
},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[2])
|
||||||
|
|
||||||
@@ -1146,12 +1163,18 @@ class TestInstanceNotificationSample(
|
|||||||
self._wait_for_state_change(self.api, server, expected_status='ERROR')
|
self._wait_for_state_change(self.api, server, expected_status='ERROR')
|
||||||
notification = self._get_notifications('instance.rebuild.error')
|
notification = self._get_notifications('instance.rebuild.error')
|
||||||
self.assertEqual(1, len(notification))
|
self.assertEqual(1, len(notification))
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[0]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn('raise exception.VirtualInterfaceCreateException()', tb)
|
||||||
|
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'instance-rebuild-error',
|
'instance-rebuild-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id'],
|
'uuid': server['id'],
|
||||||
'trusted_image_certificates': None},
|
'trusted_image_certificates': None,
|
||||||
|
'fault.traceback': self.ANY},
|
||||||
actual=notification[0])
|
actual=notification[0])
|
||||||
|
|
||||||
def _test_restore_server(self, server):
|
def _test_restore_server(self, server):
|
||||||
@@ -1207,6 +1230,11 @@ class TestInstanceNotificationSample(
|
|||||||
self._wait_for_notification('instance.reboot.start')
|
self._wait_for_notification('instance.reboot.start')
|
||||||
self._wait_for_notification('instance.reboot.error')
|
self._wait_for_notification('instance.reboot.error')
|
||||||
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[1]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn("raise exception.UnsupportedVirtType", tb)
|
||||||
|
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'instance-reboot-start',
|
'instance-reboot-start',
|
||||||
replacements={
|
replacements={
|
||||||
@@ -1217,7 +1245,8 @@ class TestInstanceNotificationSample(
|
|||||||
'instance-reboot-error',
|
'instance-reboot-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']},
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
||||||
|
|
||||||
def _detach_volume_from_server(self, server, volume_id):
|
def _detach_volume_from_server(self, server, volume_id):
|
||||||
@@ -1312,12 +1341,21 @@ class TestInstanceNotificationSample(
|
|||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']},
|
'uuid': server['id']},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[5])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[5])
|
||||||
|
|
||||||
|
tb1 = fake_notifier.VERSIONED_NOTIFICATIONS[6]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn("_swap_volume", tb1)
|
||||||
|
tb2 = fake_notifier.VERSIONED_NOTIFICATIONS[7]['payload'][
|
||||||
|
'nova_object.data']['traceback']
|
||||||
|
self.assertIn("_swap_volume", tb2)
|
||||||
|
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'instance-volume_swap-error',
|
'instance-volume_swap-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'block_devices': block_devices,
|
'block_devices': block_devices,
|
||||||
'uuid': server['id']},
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[6])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[6])
|
||||||
|
|
||||||
def test_resize_confirm_server(self):
|
def test_resize_confirm_server(self):
|
||||||
@@ -1525,13 +1563,19 @@ class TestInstanceNotificationSample(
|
|||||||
'volume_id': self.cinder.SWAP_NEW_VOL,
|
'volume_id': self.cinder.SWAP_NEW_VOL,
|
||||||
'uuid': server['id']},
|
'uuid': server['id']},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[1]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn("CinderConnectionFailed:", tb)
|
||||||
|
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'instance-volume_attach-error',
|
'instance-volume_attach-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'block_devices': block_devices,
|
'block_devices': block_devices,
|
||||||
'volume_id': self.cinder.SWAP_NEW_VOL,
|
'volume_id': self.cinder.SWAP_NEW_VOL,
|
||||||
'uuid': server['id']},
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
||||||
|
|
||||||
@mock.patch('nova.volume.cinder.API.attachment_update')
|
@mock.patch('nova.volume.cinder.API.attachment_update')
|
||||||
@@ -1605,11 +1649,17 @@ class TestInstanceNotificationSample(
|
|||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']},
|
'uuid': server['id']},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
|
||||||
|
|
||||||
|
tb = fake_notifier.VERSIONED_NOTIFICATIONS[1]['payload'][
|
||||||
|
'nova_object.data']['fault']['nova_object.data']['traceback']
|
||||||
|
self.assertIn("raise exception.InterfaceAttachFailed", tb)
|
||||||
|
|
||||||
self._verify_notification(
|
self._verify_notification(
|
||||||
'instance-interface_attach-error',
|
'instance-interface_attach-error',
|
||||||
replacements={
|
replacements={
|
||||||
'reservation_id': server['reservation_id'],
|
'reservation_id': server['reservation_id'],
|
||||||
'uuid': server['id']},
|
'uuid': server['id'],
|
||||||
|
'fault.traceback': self.ANY},
|
||||||
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -428,7 +428,8 @@ class ComputeVolumeTestCase(BaseTestCase):
|
|||||||
mock.call(self.context, instance, 'fake-mini',
|
mock.call(self.context, instance, 'fake-mini',
|
||||||
action='volume_attach', phase='error',
|
action='volume_attach', phase='error',
|
||||||
volume_id=uuids.volume_id,
|
volume_id=uuids.volume_id,
|
||||||
exception=expected_exception),
|
exception=expected_exception,
|
||||||
|
tb=mock.ANY),
|
||||||
])
|
])
|
||||||
mock_event.assert_called_once_with(
|
mock_event.assert_called_once_with(
|
||||||
self.context, 'compute_attach_volume', CONF.host,
|
self.context, 'compute_attach_volume', CONF.host,
|
||||||
@@ -468,7 +469,8 @@ class ComputeVolumeTestCase(BaseTestCase):
|
|||||||
mock.call(self.context, instance, 'fake-mini',
|
mock.call(self.context, instance, 'fake-mini',
|
||||||
action='volume_attach', phase='error',
|
action='volume_attach', phase='error',
|
||||||
volume_id=uuids.volume_id,
|
volume_id=uuids.volume_id,
|
||||||
exception=expected_exception),
|
exception=expected_exception,
|
||||||
|
tb=mock.ANY),
|
||||||
])
|
])
|
||||||
|
|
||||||
@mock.patch.object(compute_utils, 'EventReporter')
|
@mock.patch.object(compute_utils, 'EventReporter')
|
||||||
@@ -3182,7 +3184,7 @@ class ComputeTestCase(BaseTestCase,
|
|||||||
notify_action_call_list.append(
|
notify_action_call_list.append(
|
||||||
mock.call(econtext, instance, 'fake-mini',
|
mock.call(econtext, instance, 'fake-mini',
|
||||||
action='reboot', phase='error', exception=fault,
|
action='reboot', phase='error', exception=fault,
|
||||||
bdms=bdms))
|
bdms=bdms, tb=mock.ANY))
|
||||||
notify_call_list.append(mock.call(econtext, instance,
|
notify_call_list.append(mock.call(econtext, instance,
|
||||||
'reboot.end'))
|
'reboot.end'))
|
||||||
notify_action_call_list.append(
|
notify_action_call_list.append(
|
||||||
@@ -10253,7 +10255,7 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
mock.call(self.context, instance, self.compute.host,
|
mock.call(self.context, instance, self.compute.host,
|
||||||
action='interface_attach',
|
action='interface_attach',
|
||||||
exception=mock_attach.side_effect,
|
exception=mock_attach.side_effect,
|
||||||
phase='error')])
|
phase='error', tb=mock.ANY)])
|
||||||
|
|
||||||
@mock.patch.object(compute_utils, 'notify_about_instance_action')
|
@mock.patch.object(compute_utils, 'notify_about_instance_action')
|
||||||
def test_detach_interface(self, mock_notify):
|
def test_detach_interface(self, mock_notify):
|
||||||
@@ -12529,7 +12531,7 @@ class ComputeRescheduleResizeOrReraiseTestCase(BaseTestCase):
|
|||||||
task_states.RESIZE_PREP, exc_info, host_list=None)
|
task_states.RESIZE_PREP, exc_info, host_list=None)
|
||||||
mock_notify.assert_called_once_with(
|
mock_notify.assert_called_once_with(
|
||||||
self.context, instance, 'fake-mini', action='resize',
|
self.context, instance, 'fake-mini', action='resize',
|
||||||
phase='error', exception=mock_res.side_effect)
|
phase='error', exception=mock_res.side_effect, tb=mock.ANY)
|
||||||
|
|
||||||
@mock.patch.object(compute_manager.ComputeManager, "_reschedule")
|
@mock.patch.object(compute_manager.ComputeManager, "_reschedule")
|
||||||
def test_reschedule_false(self, mock_res):
|
def test_reschedule_false(self, mock_res):
|
||||||
|
|||||||
@@ -2031,7 +2031,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
|
|||||||
self.compute.host,
|
self.compute.host,
|
||||||
fields.NotificationPhase.ERROR,
|
fields.NotificationPhase.ERROR,
|
||||||
uuids.old_volume, uuids.new_volume,
|
uuids.old_volume, uuids.new_volume,
|
||||||
test.MatchType(expected_exception))
|
test.MatchType(expected_exception), mock.ANY)
|
||||||
else:
|
else:
|
||||||
self.compute.swap_volume(self.context, uuids.old_volume,
|
self.compute.swap_volume(self.context, uuids.old_volume,
|
||||||
uuids.new_volume, instance1, None)
|
uuids.new_volume, instance1, None)
|
||||||
@@ -3987,7 +3987,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
|
|||||||
)
|
)
|
||||||
mock_notify.assert_called_once_with(
|
mock_notify.assert_called_once_with(
|
||||||
mock.ANY, instance, 'fake-mini', phase='error', exception=exc,
|
mock.ANY, instance, 'fake-mini', phase='error', exception=exc,
|
||||||
bdms=None)
|
bdms=None, tb=mock.ANY)
|
||||||
|
|
||||||
def test_rebuild_deleting(self):
|
def test_rebuild_deleting(self):
|
||||||
instance = fake_instance.fake_instance_obj(self.context)
|
instance = fake_instance.fake_instance_obj(self.context)
|
||||||
@@ -4115,7 +4115,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
|
|||||||
elevated_context, instance, 'fake-node', node_type='destination')
|
elevated_context, instance, 'fake-node', node_type='destination')
|
||||||
mock_notify.assert_called_once_with(
|
mock_notify.assert_called_once_with(
|
||||||
elevated_context, instance, 'fake-mini', bdms=None, exception=exc,
|
elevated_context, instance, 'fake-mini', bdms=None, exception=exc,
|
||||||
phase='error')
|
phase='error', tb=mock.ANY)
|
||||||
|
|
||||||
def test_rebuild_node_not_updated_if_not_recreate(self):
|
def test_rebuild_node_not_updated_if_not_recreate(self):
|
||||||
node = uuidutils.generate_uuid() # ironic node uuid
|
node = uuidutils.generate_uuid() # ironic node uuid
|
||||||
@@ -5567,7 +5567,7 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
|||||||
mock.call(self.context, self.instance, 'fake-mini',
|
mock.call(self.context, self.instance, 'fake-mini',
|
||||||
phase='start', bdms=[]),
|
phase='start', bdms=[]),
|
||||||
mock.call(self.context, self.instance, 'fake-mini',
|
mock.call(self.context, self.instance, 'fake-mini',
|
||||||
phase='error', exception=exc, bdms=[])])
|
phase='error', exception=exc, bdms=[], tb=mock.ANY)])
|
||||||
|
|
||||||
save.assert_has_calls([
|
save.assert_has_calls([
|
||||||
mock.call(),
|
mock.call(),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import string
|
import string
|
||||||
|
import traceback
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
@@ -733,10 +734,11 @@ class UsageInfoTestCase(test.TestCase):
|
|||||||
# To get exception trace, raise and catch an exception
|
# To get exception trace, raise and catch an exception
|
||||||
raise test.TestingException('Volume swap error.')
|
raise test.TestingException('Volume swap error.')
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
tb = traceback.format_exc()
|
||||||
compute_utils.notify_about_volume_swap(
|
compute_utils.notify_about_volume_swap(
|
||||||
self.context, instance, 'fake-compute',
|
self.context, instance, 'fake-compute',
|
||||||
fields.NotificationPhase.ERROR,
|
fields.NotificationPhase.ERROR,
|
||||||
uuids.old_volume_id, uuids.new_volume_id, ex)
|
uuids.old_volume_id, uuids.new_volume_id, ex, tb)
|
||||||
|
|
||||||
self.assertEqual(len(fake_notifier.VERSIONED_NOTIFICATIONS), 1)
|
self.assertEqual(len(fake_notifier.VERSIONED_NOTIFICATIONS), 1)
|
||||||
notification = fake_notifier.VERSIONED_NOTIFICATIONS[0]
|
notification = fake_notifier.VERSIONED_NOTIFICATIONS[0]
|
||||||
@@ -776,6 +778,8 @@ class UsageInfoTestCase(test.TestCase):
|
|||||||
exception_payload['function_name'])
|
exception_payload['function_name'])
|
||||||
self.assertEqual('nova.tests.unit.compute.test_compute_utils',
|
self.assertEqual('nova.tests.unit.compute.test_compute_utils',
|
||||||
exception_payload['module_name'])
|
exception_payload['module_name'])
|
||||||
|
self.assertIn('test_notify_about_volume_swap_with_error',
|
||||||
|
exception_payload['traceback'])
|
||||||
|
|
||||||
def test_notify_about_instance_rescue_action(self):
|
def test_notify_about_instance_rescue_action(self):
|
||||||
instance = create_instance(self.context)
|
instance = create_instance(self.context)
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ notification_object_data = {
|
|||||||
'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8',
|
'BlockDevicePayload': '1.0-29751e1b6d41b1454e36768a1e764df8',
|
||||||
'EventType': '1.10-2a6ab743d767837366ab1aec387d7c3b',
|
'EventType': '1.10-2a6ab743d767837366ab1aec387d7c3b',
|
||||||
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
'ExceptionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||||
'ExceptionPayload': '1.0-27db46ee34cd97e39f2643ed92ad0cc5',
|
'ExceptionPayload': '1.1-6c43008bd81885a63bc7f7c629f0793b',
|
||||||
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
'FlavorNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||||
'FlavorPayload': '1.4-2e7011b8b4e59167fe8b7a0a81f0d452',
|
'FlavorPayload': '1.4-2e7011b8b4e59167fe8b7a0a81f0d452',
|
||||||
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
'InstanceActionNotification': '1.0-a73147b93b520ff0061865849d3dfa56',
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class WrapExceptionTestCase(test.NoDBTestCase):
|
|||||||
|
|
||||||
payload = notification['payload']
|
payload = notification['payload']
|
||||||
self.assertEqual('ExceptionPayload', payload['nova_object.name'])
|
self.assertEqual('ExceptionPayload', payload['nova_object.name'])
|
||||||
self.assertEqual('1.0', payload['nova_object.version'])
|
self.assertEqual('1.1', payload['nova_object.version'])
|
||||||
|
|
||||||
payload = payload['nova_object.data']
|
payload = payload['nova_object.data']
|
||||||
self.assertEqual('TestingException', payload['exception'])
|
self.assertEqual('TestingException', payload['exception'])
|
||||||
@@ -112,6 +112,7 @@ class WrapExceptionTestCase(test.NoDBTestCase):
|
|||||||
self.assertEqual('bad_function_exception', payload['function_name'])
|
self.assertEqual('bad_function_exception', payload['function_name'])
|
||||||
self.assertEqual('nova.tests.unit.test_exception',
|
self.assertEqual('nova.tests.unit.test_exception',
|
||||||
payload['module_name'])
|
payload['module_name'])
|
||||||
|
self.assertIn('bad_function_exception', payload['traceback'])
|
||||||
|
|
||||||
@mock.patch('nova.rpc.NOTIFIER')
|
@mock.patch('nova.rpc.NOTIFIER')
|
||||||
@mock.patch('nova.notifications.objects.exception.'
|
@mock.patch('nova.notifications.objects.exception.'
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
A new ``traceback`` field has been added to each versioned instance
|
||||||
|
notification. In an error notification this field contains the full
|
||||||
|
traceback string of the exception which caused the error notification.
|
||||||
|
See the `notification dev ref`_ for the sample file of
|
||||||
|
``instance.create.error`` as an example.
|
||||||
|
|
||||||
|
.. _notification dev ref: https://docs.openstack.org/nova/latest/reference/notifications.html#existing-versioned-notifications
|
||||||
Reference in New Issue
Block a user