From a29b55427e964c7f80f9292665e85fb31acd8b12 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 14 Dec 2011 16:15:52 +0900 Subject: [PATCH] Unbreak start instance and fixes bug 905270 This patch fixes the bug 905270 https://bugs.launchpad.net/nova/+bug/905270 According to EC2 documentation, EBS-instances that initiated shutdown result in stopped state. And then it can be started again. (On the other hand non-EBS instance result in terminted when instance initiated shutdown) However, the current nova case, the shutdowned instance always results in terminated status. As related issues are - describe-instance-attribute instance_initiated_shutdown_behavior doesn't work correctly - instance attribute disable_api_termination isn't supported - stop instance was broken by the change set of the following. It needs unbreak. > commit eb03d47fecd3bfc24243da29ee01679b334a08fe > Author: Vishvananda Ishaya > Date: Fri Sep 23 09:22:32 2011 -0700 > > Remove AoE, Clean up volume code > > * Removes Ata Over Ethernet > * Adds drivers to libvirt for volumes > * Adds initialize_connection and terminate_connection to volume api > * Passes connection info back through volume api > > Change-Id: I1b1626f40bebe8466ab410fb174683293c7c474f This patch - unbreak start instance - implement instance_initiated_shutdown_behavior and make it EC2 compatible - implement disable_api_termination --- Changes 5 -> 6: - fixes to catch up 26b7b9457a5899ecca93fd67d3879efcad4e4968 Changes 4 -> 5: - HACKING compilance Changes 3 -> 4: - rebased to 4c5586a28fd7a085369c49f6039876ffdc86b526 sqlalchemy migrate version Changes 2 -> 3: - rename long name to shorter one s/instance_initiated_shutdown_behavior/shutdown_terminate/g s/disable_api_termination/disable_terminate/g as suggested Kevin L. Mitchell - improved nova.api.ec2.cloud._state_description - pep8 - broken out patches are available for easy review at git://github.com/yamahata/nova.git lp905270-2 Changes 1 -> 2: - fixed an unit test failure pointed out by Mark. (I think ebtabls failure strongly suggests installation problem) - introduce vm_states.SHUTOFF and put instance state which is in power_state.{NOSTATE, SHUTOFF} into vm_states.SHUTOFF. - simplified logic a bit by vm_states.SHUTOFF as suggested by Vish. - instance_initiated_shutdown_behavior:String(255) => instance_initiated_shutdown_terminate:Boolean() as suggested by Vish. - Added Johannes Erdfelt to reviews as they written the vm_states state machine checker. I'd have liked to add David Subiros either, but he doesn't seem to be a registered user of the gerrit. Change-Id: Ibeb94f65137feadad2c343913b39195e3f96a35e --- nova/tests/test_compute.py | 76 ++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 4f500473..16c0db96 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -1358,6 +1358,15 @@ class ComputeAPITestCase(BaseTestCase): 'properties': {'kernel_id': 1, 'ramdisk_id': 1}, } + def _run_instance(self): + instance = self._create_fake_instance() + instance_uuid = instance['uuid'] + self.compute.run_instance(self.context, instance_uuid) + + instance = db.instance_get_by_uuid(self.context, instance_uuid) + self.assertEqual(instance['task_state'], None) + return instance, instance_uuid + def test_create_with_too_little_ram(self): """Test an instance type with too little memory""" @@ -1554,13 +1563,45 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, instance['id']) - def test_delete(self): + def test_start_shutdown(self): + def check_state(instance_uuid, power_state_, vm_state_, task_state_): + instance = db.instance_get_by_uuid(self.context, instance_uuid) + self.assertEqual(instance['power_state'], power_state_) + self.assertEqual(instance['vm_state'], vm_state_) + self.assertEqual(instance['task_state'], task_state_) + + def start_check_state(instance_uuid, + power_state_, vm_state_, task_state_): + instance = db.instance_get_by_uuid(self.context, instance_uuid) + self.compute_api.start(self.context, instance) + check_state(instance_uuid, power_state_, vm_state_, task_state_) + instance = self._create_fake_instance() instance_uuid = instance['uuid'] self.compute.run_instance(self.context, instance_uuid) - instance = db.instance_get_by_uuid(self.context, instance_uuid) - self.assertEqual(instance['task_state'], None) + check_state(instance_uuid, power_state.RUNNING, vm_states.ACTIVE, None) + + # NOTE(yamahata): emulate compute.manager._sync_power_state() that + # the instance is shutdown by itself + db.instance_update(self.context, instance_uuid, + {'power_state': power_state.NOSTATE, + 'vm_state': vm_states.SHUTOFF}) + check_state(instance_uuid, power_state.NOSTATE, vm_states.SHUTOFF, + None) + + start_check_state(instance_uuid, + power_state.NOSTATE, vm_states.SHUTOFF, None) + + db.instance_update(self.context, instance_uuid, + {'shutdown_terminate': False}) + start_check_state(instance_uuid, power_state.NOSTATE, + vm_states.STOPPED, task_states.STARTING) + + db.instance_destroy(self.context, instance['id']) + + def test_delete(self): + instance, instance_uuid = self._run_instance() self.compute_api.delete(self.context, instance) @@ -1569,14 +1610,21 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, instance['id']) - def test_delete_soft(self): - instance = self._create_fake_instance() - instance_uuid = instance['uuid'] - self.compute.run_instance(self.context, instance['uuid']) + def test_delete_fail(self): + instance, instance_uuid = self._run_instance() + + instance = db.instance_update(self.context, instance_uuid, + {'disable_terminate': True}) + self.compute_api.delete(self.context, instance) instance = db.instance_get_by_uuid(self.context, instance_uuid) self.assertEqual(instance['task_state'], None) + db.instance_destroy(self.context, instance['id']) + + def test_delete_soft(self): + instance, instance_uuid = self._run_instance() + self.compute_api.soft_delete(self.context, instance) instance = db.instance_get_by_uuid(self.context, instance_uuid) @@ -1584,6 +1632,18 @@ class ComputeAPITestCase(BaseTestCase): db.instance_destroy(self.context, instance['id']) + def test_delete_soft_fail(self): + instance, instance_uuid = self._run_instance() + + instance = db.instance_update(self.context, instance_uuid, + {'disable_terminate': True}) + self.compute_api.soft_delete(self.context, instance) + + instance = db.instance_get_by_uuid(self.context, instance_uuid) + self.assertEqual(instance['task_state'], None) + + db.instance_destroy(self.context, instance['id']) + def test_force_delete(self): """Ensure instance can be deleted after a soft delete""" instance = self._create_fake_instance() @@ -1621,7 +1681,7 @@ class ComputeAPITestCase(BaseTestCase): instance = self._create_fake_instance() instance_uuid = instance['uuid'] instance_id = instance['id'] - self.compute.run_instance(self.context, instance_uuid ) + self.compute.run_instance(self.context, instance_uuid) db.instance_update(self.context, instance_id, {'vm_state': vm_states.SUSPENDED}) instance = db.instance_get(self.context, instance_id)