Target context for build notification in conductor

When sending notifications about an instance, it's possible the
notification payload will have attributes not already loaded on
the instance. In this case, a lazy-load will happen and if the
load method needs to lookup the instance (example: in the
_check_instance_exists_in_project method in the DB layer), the
InstanceNotFound exception will be raised and the notification
will fail to be sent.

This targets the instance context to the instance's cell when
calling the send notification method.

Closes-Bug: #1721670

 Conflicts:
	nova/conductor/manager.py
	nova/tests/unit/conductor/test_conductor.py

NOTE(melwitt): Conflict was due to the older version of target_cell
not yielding a new RequestContext, the absence of set_target_cell,
and an older version of the unit test in Ocata.

Change-Id: I6b9eb120b6e7fcbf919a3791afe2d1f6a7bd3b60
(cherry picked from commit 54cf9f5a90)
This commit is contained in:
melanie witt 2017-10-05 23:55:58 +00:00
parent 014effb11e
commit d7a60f8a1c
2 changed files with 21 additions and 6 deletions

View File

@ -916,12 +916,12 @@ class ComputeTaskManager(base.Base):
with obj_target_cell(instance, cell):
instance.create()
# send a state update notification for the initial create to
# show it going from non-existent to BUILDING
notifications.send_update_with_states(context, instance, None,
vm_states.BUILDING, None, None, service="conductor")
with obj_target_cell(instance, cell):
# send a state update notification for the initial create to
# show it going from non-existent to BUILDING
# This can lazy-load attributes on instance.
notifications.send_update_with_states(context, instance, None,
vm_states.BUILDING, None, None, service="conductor")
objects.InstanceAction.action_start(
context, instance.uuid, instance_actions.CREATE,
want_result=False)

View File

@ -1429,10 +1429,11 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
objects=[bdm])
self.params = params
@mock.patch('nova.notifications.send_update_with_states')
@mock.patch('nova.compute.rpcapi.ComputeAPI.build_and_run_instance')
@mock.patch('nova.scheduler.rpcapi.SchedulerAPI.select_destinations')
def test_schedule_and_build_instances(self, select_destinations,
build_and_run_instance):
build_and_run_instance, mock_notify):
select_destinations.return_value = [{'host': 'fake-host',
'nodename': 'fake-nodename',
'limits': None}]
@ -1448,6 +1449,20 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
self.start_service('compute', host='fake-host')
build_and_run_instance.side_effect = _build_and_run_instance
# NOTE(melwitt): This won't work with call_args because the call
# arguments are recorded as references and not as copies of objects.
# So even though the notify method was called with Instance._context
# targeted, by the time we assert with call_args, the target_cell
# context manager has already exited and the referenced Instance
# object's _context.db_connection has been restored to None.
def fake_notify(ctxt, instance, *args, **kwargs):
# Assert the instance object is targeted when going through the
# notification code.
self.assertIsNotNone(instance._context.db_connection)
mock_notify.side_effect = fake_notify
self.conductor.schedule_and_build_instances(**params)
self.assertTrue(build_and_run_instance.called)