From 6b56b30e8b56568b88638384cd215692db9403ff Mon Sep 17 00:00:00 2001 From: Alex Kavanagh Date: Tue, 27 Feb 2018 11:02:14 +0000 Subject: [PATCH] Add a test for destroying an instance when in rescue mode A previous commit added some code to delete the rescued comtainer when the instance was in rescue mode. This means that there are two containers for the instance (and thus using the profile). However, no test was added at that stage. This patchset adds a test to verify that the containers do get deleted and modifies the destroy code to also ensure that the rescue container is also stopped before deletion. Change-Id: I586261f25e8c8b9b8acdba6cafe67491bd55b46a --- nova/tests/unit/virt/lxd/test_driver.py | 35 +++++++++++++++++++++++++ nova/virt/lxd/driver.py | 18 ++++++++----- 2 files changed, 46 insertions(+), 7 deletions(-) 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.