diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 759ed15136c1..a9a1ed0de2c3 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -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) diff --git a/nova/tests/compute/test_compute_mgr.py b/nova/tests/compute/test_compute_mgr.py index 4ee8b2ec4e83..4a2e9c3d824f 100644 --- a/nova/tests/compute/test_compute_mgr.py +++ b/nova/tests/compute/test_compute_mgr.py @@ -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')