Transform instance.create.error notification

Along with the transformation changes, I modified notify_about_instance_action
method so it can handle errors now. (It wraps the exception object into an
ExceptionPayload.)

Implements: bp versioned-notification-transformation-ocata

Change-Id: I171990290c8de529c9521a5ee556f97b18b741b0
This commit is contained in:
Gábor Antal 2016-11-15 13:22:31 +01:00 committed by Matt Riedemann
parent 80bf21d889
commit 94563ade46
5 changed files with 147 additions and 15 deletions

View File

@ -0,0 +1,59 @@
{
"event_type":"instance.create.error",
"payload":{
"nova_object.data":{
"architecture":"x86_64",
"availability_zone":null,
"created_at":"2012-10-29T13:42:11Z",
"deleted_at":null,
"display_name":"some-server",
"fault": {
"nova_object.data": {
"exception": "FlavorDiskTooSmall",
"exception_message": "The created instance's disk would be too small.",
"function_name": "_build_resources",
"module_name": "nova.tests.functional.notification_sample_tests.test_instance"
},
"nova_object.name": "ExceptionPayload",
"nova_object.namespace": "nova",
"nova_object.version": "1.0"
},
"host":"compute",
"host_name":"some-server",
"ip_addresses": [],
"kernel_id":"",
"launched_at":null,
"image_uuid": "155d900f-4e14-4e4c-a73d-069cbf4541e6",
"metadata":{},
"node":"fake-mini",
"os_type":null,
"progress":0,
"ramdisk_id":"",
"reservation_id":"r-npxv0e40",
"state":"building",
"task_state":null,
"power_state":"pending",
"tenant_id":"6f70656e737461636b20342065766572",
"terminated_at":null,
"flavor": {
"nova_object.name": "FlavorPayload",
"nova_object.data": {
"flavorid": "a22d5517-147c-4147-a0d1-e698df5cd4e3",
"root_gb": 1,
"vcpus": 1,
"ephemeral_gb": 0,
"memory_mb": 512
},
"nova_object.version": "1.0",
"nova_object.namespace": "nova"
},
"user_id":"fake",
"uuid":"178b0921-8f85-4257-88b6-2e743b5a975c"
},
"nova_object.name":"InstanceActionPayload",
"nova_object.namespace":"nova",
"nova_object.version":"1.0"
},
"priority":"ERROR",
"publisher_id":"nova-compute:compute"
}

View File

@ -1930,10 +1930,18 @@ 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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
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:
@ -1941,12 +1949,20 @@ 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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
except (exception.FixedIpLimitExceeded, except (exception.FixedIpLimitExceeded,
exception.NoMoreNetworks, exception.NoMoreFixedIps) as e: exception.NoMoreNetworks, exception.NoMoreFixedIps) as e:
LOG.warning(_LW('No more network or fixed IP to be allocated'), LOG.warning(_LW('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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
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,
@ -1959,6 +1975,10 @@ 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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
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)
@ -1971,11 +1991,19 @@ class ComputeManager(manager.Manager):
exception.SignatureVerificationError) as e: exception.SignatureVerificationError) as e:
self._notify_about_instance_usage(context, instance, self._notify_about_instance_usage(context, instance,
'create.error', fault=e) 'create.error', fault=e)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
raise exception.RescheduledException( raise exception.RescheduledException(
instance_uuid=instance.uuid, reason=six.text_type(e)) instance_uuid=instance.uuid, reason=six.text_type(e))
@ -2007,6 +2035,10 @@ 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)
compute_utils.notify_about_instance_action(
context, instance, self.host,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.ERROR, exception=e)
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',

View File

@ -339,28 +339,40 @@ def _get_instance_ips(instance):
return ips return ips
def _get_fault_and_priority_from_exc(exception):
fault = None
priority = fields.NotificationPriority.INFO
if exception:
priority = fields.NotificationPriority.ERROR
fault = notification_exception.ExceptionPayload.from_exception(
exception)
return fault, priority
def notify_about_instance_action(context, instance, host, action, phase=None, def notify_about_instance_action(context, instance, host, action, phase=None,
binary='nova-compute'): binary='nova-compute', exception=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
:param action: the name of the action :param action: the name of the action
:param phase: the phase of the action :param phase: the phase of the action
:param binary: the binary emitting the notification :param binary: the binary emitting the notification
:param exception: the thrown exception (used in error notifications)
""" """
ips = _get_instance_ips(instance) ips = _get_instance_ips(instance)
flavor = instance_notification.FlavorPayload(instance=instance) flavor = instance_notification.FlavorPayload(instance=instance)
# TODO(gibi): handle fault during the transformation of the first error fault, priority = _get_fault_and_priority_from_exc(exception)
# notifications
payload = instance_notification.InstanceActionPayload( payload = instance_notification.InstanceActionPayload(
instance=instance, instance=instance,
fault=None, fault=fault,
ip_addresses=ips, ip_addresses=ips,
flavor=flavor) flavor=flavor)
notification = instance_notification.InstanceActionNotification( notification = instance_notification.InstanceActionNotification(
context=context, context=context,
priority=fields.NotificationPriority.INFO, priority=priority,
publisher=notification_base.NotificationPublisher( publisher=notification_base.NotificationPublisher(
context=context, host=host, binary=binary), context=context, host=host, binary=binary),
event_type=notification_base.EventType( event_type=notification_base.EventType(
@ -389,14 +401,7 @@ def notify_about_volume_swap(context, instance, host, action, phase,
flavor = instance_notification.FlavorPayload(instance=instance) flavor = instance_notification.FlavorPayload(instance=instance)
if exception: fault, priority = _get_fault_and_priority_from_exc(exception)
priority = fields.NotificationPriority.ERROR
fault = notification_exception.ExceptionPayload.from_exception(
exception)
else:
priority = fields.NotificationPriority.INFO
fault = None
payload = instance_notification.InstanceActionVolumeSwapPayload( payload = instance_notification.InstanceActionVolumeSwapPayload(
instance=instance, instance=instance,
fault=fault, fault=fault,

View File

@ -9,9 +9,11 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import mock
import time import time
from nova import context from nova import context
from nova import exception
from nova.tests import fixtures from nova.tests import fixtures
from nova.tests.functional.notification_sample_tests \ from nova.tests.functional.notification_sample_tests \
import notification_sample_base import notification_sample_base
@ -100,6 +102,32 @@ class TestInstanceNotificationSample(
'uuid': server['id']}, 'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[idx]) actual=fake_notifier.VERSIONED_NOTIFICATIONS[idx])
@mock.patch('nova.compute.manager.ComputeManager._build_resources')
def test_create_server_error(self, mock_build):
def _build_resources(*args, **kwargs):
raise exception.FlavorDiskTooSmall()
mock_build.side_effect = _build_resources
server = self._boot_a_server(
expected_status='ERROR',
extra_params={'networks': [{'port': self.neutron.port_1['id']}]})
self.assertEqual(2, len(fake_notifier.VERSIONED_NOTIFICATIONS))
self._verify_notification(
'instance-create-start',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[0])
self._verify_notification(
'instance-create-error',
replacements={
'reservation_id': server['reservation_id'],
'uuid': server['id']},
actual=fake_notifier.VERSIONED_NOTIFICATIONS[1])
def _verify_instance_update_steps(self, steps, notifications, def _verify_instance_update_steps(self, steps, notifications,
initial=None): initial=None):
replacements = {} replacements = {}

View File

@ -4045,10 +4045,12 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
mock.patch.object(self.compute, mock.patch.object(self.compute,
'_shutdown_instance'), '_shutdown_instance'),
mock.patch.object(self.compute, mock.patch.object(self.compute,
'_validate_instance_group_policy') '_validate_instance_group_policy'),
mock.patch('nova.compute.utils.notify_about_instance_action')
) as (spawn, save, ) as (spawn, save,
_build_networks_for_instance, _notify_about_instance_usage, _build_networks_for_instance, _notify_about_instance_usage,
_shutdown_instance, _validate_instance_group_policy): _shutdown_instance, _validate_instance_group_policy,
mock_notify):
self.assertRaises(exception.BuildAbortException, self.assertRaises(exception.BuildAbortException,
self.compute._build_and_run_instance, self.context, self.compute._build_and_run_instance, self.context,
@ -4069,6 +4071,12 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
mock.call(self.context, self.instance, 'create.error', mock.call(self.context, self.instance, 'create.error',
fault=exc)]) fault=exc)])
mock_notify.assert_has_calls([
mock.call(self.context, self.instance, 'fake-mini',
action='create', phase='start'),
mock.call(self.context, self.instance, 'fake-mini',
action='create', phase='error', exception=exc)])
save.assert_has_calls([ save.assert_has_calls([
mock.call(), mock.call(),
mock.call(), mock.call(),