Skip to content

Commit 06d2592

Browse files
committed
Allow best effort sending of notifications
In the previous patch we changed the ordering of operations during post_live_migration() to minimize guest networking downtime by activating destination host port bindings as soon as possible. Review of that patch led to the realization that exceptions during notification sending can prevent the port binding activation from happening. Instead of handling that in a localized try/catch, this patch implements a general best_effort kwarg to our two notification sending helpers to allow callers to indicate that any exceptions during notification sending should not be fatal. Change-Id: I01a15d6fffe98816ae019e67dc72784299fedfd3
1 parent 26fbc9e commit 06d2592

File tree

5 files changed

+72
-10
lines changed

5 files changed

+72
-10
lines changed

nova/compute/manager.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2261,11 +2261,12 @@ def _sync_scheduler_instance_info(self, context):
22612261

22622262
def _notify_about_instance_usage(self, context, instance, event_suffix,
22632263
network_info=None, extra_usage_info=None,
2264-
fault=None):
2264+
fault=None, best_effort=False):
22652265
compute_utils.notify_about_instance_usage(
22662266
self.notifier, context, instance, event_suffix,
22672267
network_info=network_info,
2268-
extra_usage_info=extra_usage_info, fault=fault)
2268+
extra_usage_info=extra_usage_info, fault=fault,
2269+
best_effort=best_effort)
22692270

22702271
def _deallocate_network(self, context, instance,
22712272
requested_networks=None):
@@ -9358,11 +9359,12 @@ def _post_live_migration(self, ctxt, instance, dest,
93589359

93599360
self._notify_about_instance_usage(ctxt, instance,
93609361
"live_migration._post.start",
9361-
network_info=network_info)
9362+
network_info=network_info,
9363+
best_effort=True)
93629364
compute_utils.notify_about_instance_action(
93639365
ctxt, instance, self.host,
93649366
action=fields.NotificationAction.LIVE_MIGRATION_POST,
9365-
phase=fields.NotificationPhase.START)
9367+
phase=fields.NotificationPhase.START, best_effort=True)
93669368

93679369
migration = objects.Migration(
93689370
source_compute=self.host, dest_compute=dest,

nova/compute/utils.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ def notify_usage_exists(notifier, context, instance_ref, host,
406406

407407
def notify_about_instance_usage(notifier, context, instance, event_suffix,
408408
network_info=None, extra_usage_info=None,
409-
fault=None):
409+
fault=None, best_effort=False):
410410
"""Send an unversioned legacy notification about an instance.
411411
412412
All new notifications should use notify_about_instance_action which sends
@@ -435,7 +435,14 @@ def notify_about_instance_usage(notifier, context, instance, event_suffix,
435435
else:
436436
method = notifier.info
437437

438-
method(context, 'compute.instance.%s' % event_suffix, usage_info)
438+
try:
439+
method(context, 'compute.instance.%s' % event_suffix, usage_info)
440+
except Exception as e:
441+
if best_effort:
442+
LOG.error('Exception during notification sending: %s. '
443+
'Attempting to proceed with normal operation.', e)
444+
else:
445+
raise e
439446

440447

441448
def _get_fault_and_priority_from_exception(exception: Exception):
@@ -454,7 +461,7 @@ def _get_fault_and_priority_from_exception(exception: Exception):
454461
@rpc.if_notifications_enabled
455462
def notify_about_instance_action(context, instance, host, action, phase=None,
456463
source=fields.NotificationSource.COMPUTE,
457-
exception=None, bdms=None):
464+
exception=None, bdms=None, best_effort=False):
458465
"""Send versioned notification about the action made on the instance
459466
:param instance: the instance which the action performed on
460467
:param host: the host emitting the notification
@@ -481,7 +488,14 @@ def notify_about_instance_action(context, instance, host, action, phase=None,
481488
action=action,
482489
phase=phase),
483490
payload=payload)
484-
notification.emit(context)
491+
try:
492+
notification.emit(context)
493+
except Exception as e:
494+
if best_effort:
495+
LOG.error('Exception during notification sending: %s. '
496+
'Attempting to proceed with normal operation.', e)
497+
else:
498+
raise e
485499

486500

487501
@rpc.if_notifications_enabled

nova/tests/functional/compute/test_live_migration.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ def test_live_migrate_vifs_from_info_cache(self):
255255
the network_info from the instance info cache, and not Neutron.
256256
"""
257257
def stub_notify(context, instance, event_suffix,
258-
network_info=None, extra_usage_info=None, fault=None):
258+
network_info=None, extra_usage_info=None, fault=None,
259+
best_effort=False):
259260
vif = network_info[0]
260261
# Make sure we have the correct VIF (the NeutronFixture
261262
# deterministically uses port_2 for networks=auto) and that the

nova/tests/unit/compute/test_compute.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6675,7 +6675,8 @@ def test_post_live_migration_working_correctly(self, mock_notify):
66756675
source_bdms=bdms)
66766676
mock_notify.assert_has_calls([
66776677
mock.call(c, instance, 'fake-mini',
6678-
action='live_migration_post', phase='start'),
6678+
action='live_migration_post', phase='start',
6679+
best_effort=True),
66796680
mock.call(c, instance, 'fake-mini',
66806681
action='live_migration_post', phase='end')])
66816682
self.assertEqual(2, mock_notify.call_count)

nova/tests/unit/compute/test_utils.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from nova import exception
3737
from nova.image import glance
3838
from nova.network import model
39+
from nova.notifications.objects import base as notifications_objects_base
3940
from nova import objects
4041
from nova.objects import base
4142
from nova.objects import block_device as block_device_obj
@@ -472,6 +473,31 @@ def test_notify_usage_exists_deleted_instance(self):
472473
glance.generate_glance_url(self.context), uuids.fake_image_ref)
473474
self.assertEqual(payload['image_ref_url'], image_ref_url)
474475

476+
def test_notify_about_instance_action_best_effort(self):
477+
instance = create_instance(self.context)
478+
bdms = block_device_obj.block_device_make_list(
479+
self.context,
480+
[fake_block_device.FakeDbBlockDeviceDict(
481+
{'source_type': 'volume',
482+
'device_name': '/dev/vda',
483+
'instance_uuid': 'f8000000-0000-0000-0000-000000000000',
484+
'destination_type': 'volume',
485+
'boot_index': 0,
486+
'volume_id': 'de8836ac-d75e-11e2-8271-5254009297d6'})])
487+
with mock.patch.object(
488+
notifications_objects_base.NotificationBase, 'emit',
489+
side_effect=Exception()
490+
) as mock_emit:
491+
compute_utils.notify_about_instance_action(
492+
self.context,
493+
instance,
494+
host='fake-compute',
495+
action='delete',
496+
phase='start',
497+
bdms=bdms,
498+
best_effort=True)
499+
mock_emit.assert_called_once()
500+
475501
def test_notify_about_instance_action(self):
476502
instance = create_instance(self.context)
477503
bdms = block_device_obj.block_device_make_list(
@@ -873,6 +899,24 @@ def test_notify_about_volume_usage(self):
873899
self.assertEqual(200, payload['write_bytes'])
874900
self.assertEqual(200, payload['writes'])
875901

902+
def test_notify_about_instance_usage_best_effort(self):
903+
instance = create_instance(self.context)
904+
# Set some system metadata
905+
sys_metadata = {'image_md_key1': 'val1',
906+
'image_md_key2': 'val2',
907+
'other_data': 'meow'}
908+
instance.system_metadata.update(sys_metadata)
909+
instance.save()
910+
extra_usage_info = {'image_name': 'fake_name'}
911+
notifier = rpc.get_notifier('compute')
912+
with mock.patch.object(
913+
notifier, 'info', side_effect=Exception()
914+
) as mock_info:
915+
compute_utils.notify_about_instance_usage(
916+
notifier, self.context, instance, 'create.start',
917+
extra_usage_info=extra_usage_info, best_effort=True)
918+
mock_info.assert_called_once()
919+
876920
def test_notify_about_instance_usage(self):
877921
instance = create_instance(self.context)
878922
# Set some system metadata

0 commit comments

Comments
 (0)