diff --git a/nova/tests/unit/virt/lxd/test_driver.py b/nova/tests/unit/virt/lxd/test_driver.py index 12826455..0722f47d 100644 --- a/nova/tests/unit/virt/lxd/test_driver.py +++ b/nova/tests/unit/virt/lxd/test_driver.py @@ -26,6 +26,7 @@ from nova import utils from nova import test from nova.compute import manager from nova.compute import power_state +from nova.compute import vm_states from nova.network import model as network_model from nova.tests.unit import fake_instance from pylxd import exceptions as lxdcore_exceptions @@ -544,6 +545,40 @@ class LXDDriverTest(test.NoDBTestCase): mock_container.stop.assert_called_once_with(wait=True) mock_container.delete.assert_called_once_with(wait=True) + def test_destroy_when_in_rescue(self): + mock_stopped_container = mock.Mock() + mock_stopped_container.status = 'Stopped' + mock_rescued_container = mock.Mock() + mock_rescued_container.status = 'Running' + ctx = context.get_admin_context() + instance = fake_instance.fake_instance_obj( + ctx, name='test', memory_mb=0) + network_info = [_VIF] + + lxd_driver = driver.LXDDriver(None) + lxd_driver.init_host(None) + lxd_driver.cleanup = mock.Mock() + + # set the vm_state on the fake instance to RESCUED + instance.vm_state = vm_states.RESCUED + + # set up the containers.get to return the stopped container and then + # the rescued container + self.client.containers.get.side_effect = [ + mock_stopped_container, mock_rescued_container] + + lxd_driver.destroy(ctx, instance, network_info) + + lxd_driver.cleanup.assert_called_once_with( + ctx, instance, network_info, None) + lxd_driver.client.containers.get.assert_has_calls([ + mock.call(instance.name), + mock.call('{}-rescue'.format(instance.name))]) + mock_stopped_container.stop.assert_not_called() + mock_stopped_container.delete.assert_called_once_with(wait=True) + mock_rescued_container.stop.assert_called_once_with(wait=True) + mock_rescued_container.delete.assert_called_once_with(wait=True) + def test_destroy_without_instance(self): def side_effect(*args, **kwargs): raise lxdcore_exceptions.LXDAPIException(MockResponse(404)) diff --git a/nova/virt/lxd/driver.py b/nova/virt/lxd/driver.py index 8681d063..adefe385 100644 --- a/nova/virt/lxd/driver.py +++ b/nova/virt/lxd/driver.py @@ -613,6 +613,8 @@ class LXDDriver(driver.ComputeDriver): if (instance.vm_state == vm_states.RESCUED): rescued_container = self.client.containers.get( '%s-rescue' % instance.name) + if rescued_container.status != 'Stopped': + rescued_container.stop(wait=True) rescued_container.delete(wait=True) except lxd_exceptions.LXDAPIException as e: if e.response.status_code == 404: @@ -890,13 +892,15 @@ class LXDDriver(driver.ComputeDriver): rescue_password): """Rescue a LXD container. - Rescuing a instance requires a number of steps. First, - the failed container is stopped. Next, '-rescue', is - appended to the failed container's name, this is done - so the container can be unrescued. The container's - profile is updated with the rootfs of the - failed container. Finally, a new container - is created and started. + From the perspective of nova, rescuing a instance requires a number of + steps. First, the failed container is stopped, and then this method is + called. + + So the original container is already stopped, and thus, next, + '-rescue', is appended to the failed container's name, this is done so + the container can be unrescued. The container's profile is updated with + the rootfs of the failed container. Finally, a new container is created + and started. See 'nova.virt.driver.ComputeDriver.rescue` for more information.