From b53f83eaf106bcf37551dac3393c1a3e4a7ac938 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 30 May 2013 13:10:09 -0700 Subject: Make sync_power_state routines use InstanceList This makes the periodic sync_power_states task use InstanceList objects instead of querying conductor directly. This also affects handle_lifecycle_event() as well, which now uses Instance to fetch the instance for the event. Note that none of the power state sync code was covered by unit tests, so this adds that as well to verify the behavior and instance object usage. Also note that compute_stop is already instance-object- ready, so passing one to it is okay. Related to blueprint unified-object-model Change-Id: I4e34e0a2b9db77468305aaf1782feb74bcfdccbd --- nova/compute/api.py | 8 ---- nova/compute/manager.py | 25 ++++++------- nova/tests/compute/test_compute.py | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 22 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 91f3a60ef..001342822 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -49,7 +49,6 @@ from nova import network from nova.network.security_group import openstack_driver from nova.network.security_group import security_group_base from nova import notifications -from nova.objects import instance as instance_obj from nova.openstack.common import excutils from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -1385,13 +1384,6 @@ class API(base.Base): """Stop an instance.""" LOG.debug(_("Going to try to stop instance"), instance=instance) - # NOTE(danms): Temporary transition to objects. Remove after - # compute/manager.py _sync_instance_power_state() is migrated. - if isinstance(instance, dict): - instance = instance_obj.Instance._from_db_object( - instance_obj.Instance(), instance) - instance._context = context - instance.task_state = task_states.POWERING_OFF instance.progress = 0 instance.save(expected_task_state=None) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 2ae435f9a..7098c92a9 100755 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -620,7 +620,7 @@ class ComputeManager(manager.SchedulerDependentManager): {'state': event.get_transition(), 'uuid': event.get_instance_uuid()}) context = nova.context.get_admin_context() - instance = self.conductor_api.instance_get_by_uuid( + instance = instance_obj.Instance.get_by_uuid( context, event.get_instance_uuid()) vm_power_state = None if event.get_transition() == virtevent.EVENT_LIFECYCLE_STOPPED: @@ -3910,8 +3910,8 @@ class ComputeManager(manager.SchedulerDependentManager): loop, one database record at a time, checking if the hypervisor has the same power state as is in the database. """ - db_instances = self.conductor_api.instance_get_all_by_host( - context, self.host, columns_to_join=[]) + db_instances = instance_obj.InstanceList.get_by_host(context, + self.host) num_vm_instances = self.driver.get_num_instances() num_db_instances = len(db_instances) @@ -3948,13 +3948,11 @@ class ComputeManager(manager.SchedulerDependentManager): # We re-query the DB to get the latest instance info to minimize # (not eliminate) race condition. - u = self.conductor_api.instance_get_by_uuid(context, - db_instance['uuid'], - columns_to_join=[]) - db_power_state = u["power_state"] - vm_state = u['vm_state'] + db_instance.refresh() + db_power_state = db_instance.power_state + vm_state = db_instance.vm_state - if self.host != u['host']: + if self.host != db_instance.host: # on the sending end of nova-compute _sync_power_state # may have yielded to the greenthread performing a live # migration; this in turn has changed the resident-host @@ -3966,10 +3964,10 @@ class ComputeManager(manager.SchedulerDependentManager): "instance has moved from " "host %(src)s to host %(dst)s") % {'src': self.host, - 'dst': u['host']}, + 'dst': db_instance.host}, instance=db_instance) return - elif u['task_state'] is not None: + elif db_instance.task_state is not None: # on the receiving end of nova-compute, it could happen # that the DB instance already report the new resident # but the actual VM has not showed up on the hypervisor @@ -3981,9 +3979,8 @@ class ComputeManager(manager.SchedulerDependentManager): if vm_power_state != db_power_state: # power_state is always updated from hypervisor to db - self._instance_update(context, - db_instance['uuid'], - power_state=vm_power_state) + db_instance.power_state = vm_power_state + db_instance.save() db_power_state = vm_power_state # Note(maoy): Now resolve the discrepancy between vm_state and diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index bcf48ebb6..cb8047cec 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -5158,6 +5158,82 @@ class ComputeTestCase(BaseTestCase): self.compute._reclaim_queued_deletes(ctxt) + def test_sync_power_states(self): + ctxt = self.context.elevated() + self._create_fake_instance({'host': self.compute.host}) + self._create_fake_instance({'host': self.compute.host}) + self.mox.StubOutWithMock(self.compute.driver, 'get_info') + self.mox.StubOutWithMock(self.compute, '_sync_instance_power_state') + self.compute.driver.get_info(mox.IgnoreArg()).AndReturn( + {'state': power_state.RUNNING}) + self.compute._sync_instance_power_state(ctxt, mox.IgnoreArg(), + power_state.RUNNING) + self.compute.driver.get_info(mox.IgnoreArg()).AndReturn( + {'state': power_state.SHUTDOWN}) + self.compute._sync_instance_power_state(ctxt, mox.IgnoreArg(), + power_state.SHUTDOWN) + self.mox.ReplayAll() + self.compute._sync_power_states(ctxt) + + def _get_sync_instance(self, power_state, vm_state, task_state=None): + instance = instance_obj.Instance() + instance.uuid = 'fake-uuid' + instance.power_state = power_state + instance.vm_state = vm_state + instance.host = self.compute.host + instance.task_state = task_state + self.mox.StubOutWithMock(instance, 'refresh') + self.mox.StubOutWithMock(instance, 'save') + return instance + + def test_sync_instance_power_state_match(self): + instance = self._get_sync_instance(power_state.RUNNING, + vm_states.ACTIVE) + instance.refresh() + self.mox.ReplayAll() + self.compute._sync_instance_power_state(self.context, instance, + power_state.RUNNING) + + def test_sync_instance_power_state_running_stopped(self): + instance = self._get_sync_instance(power_state.RUNNING, + vm_states.ACTIVE) + instance.refresh() + instance.save() + self.mox.ReplayAll() + self.compute._sync_instance_power_state(self.context, instance, + power_state.SHUTDOWN) + self.assertEqual(instance.power_state, power_state.SHUTDOWN) + + def _test_sync_to_stop(self, power_state, vm_state, driver_power_state, + stop=True): + instance = self._get_sync_instance(power_state, vm_state) + instance.refresh() + instance.save() + self.mox.StubOutWithMock(self.compute.conductor_api, 'compute_stop') + if stop: + self.compute.conductor_api.compute_stop(self.context, instance) + self.mox.ReplayAll() + self.compute._sync_instance_power_state(self.context, instance, + driver_power_state) + self.mox.VerifyAll() + self.mox.UnsetStubs() + + def test_sync_instance_power_state_to_stop(self): + for ps in (power_state.SHUTDOWN, power_state.CRASHED, + power_state.SUSPENDED): + self._test_sync_to_stop(power_state.RUNNING, vm_states.ACTIVE, ps) + self._test_sync_to_stop(power_state.SHUTDOWN, vm_states.STOPPED, + power_state.RUNNING) + + def test_sync_instance_power_state_to_no_stop(self): + for ps in (power_state.PAUSED, power_state.NOSTATE): + self._test_sync_to_stop(power_state.RUNNING, vm_states.ACTIVE, ps, + stop=False) + for vs in (vm_states.SOFT_DELETED, vm_states.DELETED): + for ps in (power_state.NOSTATE, power_state.SHUTDOWN): + self._test_sync_to_stop(power_state.RUNNING, vs, ps, + stop=False) + class ComputeAPITestCase(BaseTestCase): -- cgit