Sync network_info if instance not found before _build_resources yield
If instance is deleted before _build_resources yield, there won't waiting for network_info sync finish. So there is race case, when the exception catching for 'InstanceNotFound' in do_build_and_run_instance invoke '_cleanup_allocated_networks', the '_allocate_network_async' didn't create port yet. After '_cleanup_allocated_networks' finished, '_allocate_network_async' create new port for the instance, but there isn't any cleanup for the new port anymore. Change-Id: I5027a1aaeb9a2106f7982fd0422f88c4caa20ffb Related-Bug: #1357055
This commit is contained in:
parent
51de439a4d
commit
4adc8376f7
|
@ -2172,7 +2172,7 @@ class ComputeManager(manager.Manager):
|
|||
def _build_resources(self, context, instance, requested_networks,
|
||||
security_groups, image, block_device_mapping):
|
||||
resources = {}
|
||||
|
||||
network_info = None
|
||||
try:
|
||||
network_info = self._build_networks_for_instance(context, instance,
|
||||
requested_networks, security_groups)
|
||||
|
@ -2207,13 +2207,22 @@ class ComputeManager(manager.Manager):
|
|||
resources['block_device_info'] = block_device_info
|
||||
except (exception.InstanceNotFound,
|
||||
exception.UnexpectedDeletingTaskStateError):
|
||||
raise
|
||||
with excutils.save_and_reraise_exception() as ctxt:
|
||||
# Make sure the async call finishes
|
||||
if network_info is not None:
|
||||
network_info.wait(do_raise=False)
|
||||
except exception.UnexpectedTaskStateError as e:
|
||||
# Make sure the async call finishes
|
||||
if network_info is not None:
|
||||
network_info.wait(do_raise=False)
|
||||
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
||||
reason=e.format_message())
|
||||
except Exception:
|
||||
LOG.exception(_LE('Failure prepping block device'),
|
||||
instance=instance)
|
||||
# Make sure the async call finishes
|
||||
if network_info is not None:
|
||||
network_info.wait(do_raise=False)
|
||||
msg = _('Failure prepping block device.')
|
||||
raise exception.BuildAbortException(instance_uuid=instance.uuid,
|
||||
reason=msg)
|
||||
|
|
|
@ -2667,6 +2667,66 @@ class ComputeManagerBuildInstanceTestCase(test.NoDBTestCase):
|
|||
except Exception as e:
|
||||
self.assertEqual(test_exception, e)
|
||||
|
||||
@mock.patch('nova.network.model.NetworkInfoAsyncWrapper.wait')
|
||||
@mock.patch(
|
||||
'nova.compute.manager.ComputeManager._build_networks_for_instance')
|
||||
@mock.patch('nova.objects.Instance.save')
|
||||
def test_build_resources_instance_not_found_before_yield(
|
||||
self, mock_save, mock_build_network, mock_info_wait):
|
||||
mock_build_network.return_value = self.network_info
|
||||
expected_exc = exception.InstanceNotFound(
|
||||
instance_id=self.instance.uuid)
|
||||
mock_save.side_effect = expected_exc
|
||||
try:
|
||||
with self.compute._build_resources(self.context, self.instance,
|
||||
self.requested_networks, self.security_groups,
|
||||
self.image, self.block_device_mapping):
|
||||
raise
|
||||
except Exception as e:
|
||||
self.assertEqual(expected_exc, e)
|
||||
mock_build_network.assert_called_once_with(self.context, self.instance,
|
||||
self.requested_networks, self.security_groups)
|
||||
mock_info_wait.assert_called_once_with(do_raise=False)
|
||||
|
||||
@mock.patch('nova.network.model.NetworkInfoAsyncWrapper.wait')
|
||||
@mock.patch(
|
||||
'nova.compute.manager.ComputeManager._build_networks_for_instance')
|
||||
@mock.patch('nova.objects.Instance.save')
|
||||
def test_build_resources_unexpected_task_error_before_yield(
|
||||
self, mock_save, mock_build_network, mock_info_wait):
|
||||
mock_build_network.return_value = self.network_info
|
||||
mock_save.side_effect = exception.UnexpectedTaskStateError(
|
||||
expected='', actual='')
|
||||
try:
|
||||
with self.compute._build_resources(self.context, self.instance,
|
||||
self.requested_networks, self.security_groups,
|
||||
self.image, self.block_device_mapping):
|
||||
raise
|
||||
except exception.BuildAbortException:
|
||||
pass
|
||||
mock_build_network.assert_called_once_with(self.context, self.instance,
|
||||
self.requested_networks, self.security_groups)
|
||||
mock_info_wait.assert_called_once_with(do_raise=False)
|
||||
|
||||
@mock.patch('nova.network.model.NetworkInfoAsyncWrapper.wait')
|
||||
@mock.patch(
|
||||
'nova.compute.manager.ComputeManager._build_networks_for_instance')
|
||||
@mock.patch('nova.objects.Instance.save')
|
||||
def test_build_resources_exception_before_yield(
|
||||
self, mock_save, mock_build_network, mock_info_wait):
|
||||
mock_build_network.return_value = self.network_info
|
||||
mock_save.side_effect = Exception()
|
||||
try:
|
||||
with self.compute._build_resources(self.context, self.instance,
|
||||
self.requested_networks, self.security_groups,
|
||||
self.image, self.block_device_mapping):
|
||||
raise
|
||||
except exception.BuildAbortException:
|
||||
pass
|
||||
mock_build_network.assert_called_once_with(self.context, self.instance,
|
||||
self.requested_networks, self.security_groups)
|
||||
mock_info_wait.assert_called_once_with(do_raise=False)
|
||||
|
||||
def test_build_resources_aborts_on_cleanup_failure(self):
|
||||
self.mox.StubOutWithMock(self.compute, '_build_networks_for_instance')
|
||||
self.mox.StubOutWithMock(self.compute, '_shutdown_instance')
|
||||
|
|
Loading…
Reference in New Issue