From 8ab6a0fd9e16f650e202c8cf631fa03004451d9f Mon Sep 17 00:00:00 2001 From: Rick Harris Date: Mon, 28 Jan 2013 23:25:23 +0000 Subject: Code cleanup for rebuild block device mapping * Removes extra call to get block-device-mappings when detaching during recreate. * Untangle network handling from block-device handling in rebuild * DRYs up evacuate hosts tests * Temporary mutation supports dicts as well as objs * Make instance recreate exceptions more specific to aid testing Change-Id: I242f9f6a7d329f27b06b7e81b9a418e01682c82e --- nova/tests/compute/test_compute.py | 426 ++++++++++++++----------------------- 1 file changed, 157 insertions(+), 269 deletions(-) (limited to 'nova/tests') diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 6bd2c3cac..7b05e4b06 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -144,7 +144,22 @@ class BaseTestCase(test.TestCase): self.stubs.Set(self.compute, 'scheduler_rpcapi', fake_rpcapi) fake_network.set_stub_network_methods(self.stubs) + def fake_get_nw_info(cls, ctxt, instance, *args, **kwargs): + self.assertTrue(ctxt.is_admin) + return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, + spectacular=True) + + self.stubs.Set(network_api.API, 'get_instance_nw_info', + fake_get_nw_info) + self.stubs.Set(network_api.API, 'allocate_for_instance', + fake_get_nw_info) + self.compute_api = compute.API() + + # Just to make long lines short + self.rt = self.compute._get_resource_tracker(NODENAME) + def tearDown(self): + timeutils.clear_time_override() ctxt = context.get_admin_context() fake_image.FakeImageService_reset() instances = db.instance_get_all(ctxt) @@ -212,25 +227,6 @@ class BaseTestCase(test.TestCase): class ComputeTestCase(BaseTestCase): - def setUp(self): - def fake_get_nw_info(cls, ctxt, instance, *args, **kwargs): - self.assertTrue(ctxt.is_admin) - return fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, - spectacular=True) - - super(ComputeTestCase, self).setUp() - self.stubs.Set(network_api.API, 'get_instance_nw_info', - fake_get_nw_info) - self.stubs.Set(network_api.API, 'allocate_for_instance', - fake_get_nw_info) - self.compute_api = compute.API() - # Just to make long lines short - self.rt = self.compute._get_resource_tracker(NODENAME) - - def tearDown(self): - super(ComputeTestCase, self).tearDown() - timeutils.clear_time_override() - def test_wrap_instance_fault(self): inst = {"uuid": "fake_uuid"} @@ -1570,7 +1566,7 @@ class ComputeTestCase(BaseTestCase): # Ensure failure when running an instance that already exists. instance = jsonutils.to_primitive(self._create_fake_instance()) self.compute.run_instance(self.context, instance=instance) - self.assertRaises(exception.Invalid, + self.assertRaises(exception.InstanceExists, self.compute.run_instance, self.context, instance=instance) @@ -3397,255 +3393,6 @@ class ComputeTestCase(BaseTestCase): result = self.compute._get_instances_on_driver(fake_context) self.assertEqual(driver_instances, result) - def test_rebuild_on_host_updated_target(self): - """Confirm evacuate scenario updates host.""" - - # creating testdata - c = self.context.elevated() - - inst_ref = self._create_fake_instance({'host': 'someotherhost'}) - db.instance_update(self.context, inst_ref['uuid'], - {"task_state": task_states.REBUILDING}) - inst_id = inst_ref["id"] - inst_uuid = inst_ref["uuid"] - dest = self.compute.host - - def set_shared_storage(instance): - return True - - self.stubs.Set(self.compute.driver, 'instance_on_disk', - set_shared_storage) - - self.compute.rebuild_instance(c, instance=inst_ref, - injected_files=None, image_ref=None, - orig_image_ref=None, new_pass=None, - orig_sys_metadata=None, bdms=[], - recreate=True, on_shared_storage=True) - - # make sure instance is updated with destination hostname. - instance = db.instance_get(c, inst_id) - self.assertTrue(instance['host']) - self.assertEqual(instance['host'], dest) - - # cleanup - db.instance_destroy(c, inst_uuid) - - def test_rebuild_with_wrong_shared_storage(self): - """Confirm evacuate scenario does not update host.""" - - # creating testdata - c = self.context.elevated() - - inst_ref = self._create_fake_instance({'host': 'srchost'}) - db.instance_update(self.context, inst_ref['uuid'], - {"task_state": task_states.REBUILDING}) - inst_id = inst_ref["id"] - inst_uuid = inst_ref["uuid"] - dest = self.compute.host - - def set_shared_storage(instance): - return True - - self.stubs.Set(self.compute.driver, 'instance_on_disk', - set_shared_storage) - - self.assertRaises(exception.Invalid, - self.compute.rebuild_instance, c, instance=inst_ref, - injected_files=None, image_ref=None, - orig_image_ref=None, new_pass=None, - orig_sys_metadata=None, - recreate=True, on_shared_storage=False) - - # make sure instance was not updated with destination hostname. - instance = db.instance_get(c, inst_id) - self.assertTrue(instance['host']) - self.assertEqual(instance['host'], 'srchost') - - # cleanup - db.instance_destroy(c, inst_uuid) - - def test_rebuild_on_host_with_volumes(self): - """Confirm evacuate scenario reconnects volumes.""" - - # creating testdata - inst_ref = jsonutils.to_primitive(self._create_fake_instance - ({'host': 'fake_host_2'})) - db.instance_update(self.context, inst_ref['uuid'], - {"task_state": task_states.REBUILDING}) - - inst_id = inst_ref["id"] - inst_uuid = inst_ref["uuid"] - - volume_id = 'fake' - values = {'instance_uuid': inst_ref['uuid'], - 'device_name': '/dev/vdc', - 'delete_on_termination': False, - 'volume_id': volume_id, - } - - admin = context.get_admin_context() - db.block_device_mapping_create(admin, values) - - def set_shared_storage(instance): - return True - - self.stubs.Set(self.compute.driver, 'instance_on_disk', - set_shared_storage) - - def fake_volume_get(self, context, volume): - return {'id': volume_id} - self.stubs.Set(cinder.API, "get", fake_volume_get) - - # Stub out and record whether it gets detached - result = {"detached": False} - - def fake_detach(self, context, volume): - result["detached"] = volume["id"] == volume_id - self.stubs.Set(cinder.API, "detach", fake_detach) - - def fake_terminate_connection(self, context, volume, connector): - return {} - self.stubs.Set(cinder.API, "terminate_connection", - fake_terminate_connection) - - # make sure volumes attach, detach are called - self.mox.StubOutWithMock(self.compute.volume_api, 'detach') - self.compute.volume_api.detach(mox.IsA(admin), mox.IgnoreArg()) - - self.mox.StubOutWithMock(self.compute, '_setup_block_device_mapping') - self.compute._setup_block_device_mapping(mox.IsA(admin), - mox.IsA(inst_ref), - mox.IgnoreArg()) - - # start test - self.mox.ReplayAll() - - self.compute.rebuild_instance(admin, instance=inst_ref, - injected_files=None, image_ref=None, - orig_image_ref=None, new_pass=None, - orig_sys_metadata=None, bdms=[], - recreate=True, on_shared_storage=True) - - # cleanup - for bdms in db.block_device_mapping_get_all_by_instance( - admin, inst_uuid): - db.block_device_mapping_destroy(admin, bdms['id']) - db.instance_destroy(admin, inst_uuid) - - def test_rebuild_on_host_with_shared_storage(self): - """Confirm evacuate scenario on shared storage.""" - - # creating testdata - c = self.context.elevated() - - inst_ref = jsonutils.to_primitive(self._create_fake_instance - ({'host': 'fake_host_2'})) - - inst_uuid = inst_ref["uuid"] - dest = self.compute.host - - def set_shared_storage(instance): - return True - - self.stubs.Set(self.compute.driver, 'instance_on_disk', - set_shared_storage) - - self.mox.StubOutWithMock(self.compute.driver, - 'spawn') - self.compute.driver.spawn(mox.IsA(c), mox.IsA(inst_ref), {}, - mox.IgnoreArg(), None, - mox.IgnoreArg(), mox.IgnoreArg()) - - # start test - self.mox.ReplayAll() - db.instance_update(self.context, inst_ref['uuid'], - {"task_state": task_states.REBUILDING}) - - self.compute.rebuild_instance(c, instance=inst_ref, - injected_files=None, image_ref=None, - orig_image_ref=None, new_pass=None, - orig_sys_metadata=None, bdms=[], - recreate=True, on_shared_storage=True) - - # cleanup - db.instance_destroy(c, inst_uuid) - - def test_rebuild_on_host_without_shared_storage(self): - """Confirm evacuate scenario without shared storage - (rebuild from image)""" - - # creating testdata - c = self.context.elevated() - - inst_ref = jsonutils.to_primitive(self._create_fake_instance - ({'host': 'fake_host_2'})) - - inst_uuid = inst_ref["uuid"] - dest = self.compute.host - - fake_image = { - 'id': 1, - 'name': 'fake_name', - 'properties': {'kernel_id': 'fake_kernel_id', - 'ramdisk_id': 'fake_ramdisk_id'}, - } - - def set_shared_storage(instance): - return False - - self.stubs.Set(self.compute.driver, 'instance_on_disk', - set_shared_storage) - - self.mox.StubOutWithMock(self.compute.driver, - 'spawn') - self.compute.driver.spawn(mox.IsA(c), mox.IsA(inst_ref), - mox.IsA(fake_image), mox.IgnoreArg(), - mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg()) - - # start test - self.mox.ReplayAll() - - db.instance_update(self.context, inst_ref['uuid'], - {"task_state": task_states.REBUILDING}) - - self.compute.rebuild_instance(c, instance=inst_ref, - injected_files=None, image_ref=None, - orig_image_ref=None, new_pass='newpass', - orig_sys_metadata=None, bdms=[], - recreate=True, on_shared_storage=False) - - # cleanup - db.instance_destroy(c, inst_uuid) - - def test_rebuild_on_host_instance_exists(self): - """Rebuild if instance exists raise an exception.""" - - # creating testdata - c = self.context.elevated() - inst_ref = self._create_fake_instance({'host': 'fake_host_2'}) - dest = self.compute.host - - instance = jsonutils.to_primitive(self._create_fake_instance()) - instance_uuid = instance['uuid'] - dest = self.compute.host - - self.compute.run_instance(self.context, instance=instance) - - db.instance_update(self.context, inst_ref['uuid'], - {"task_state": task_states.REBUILDING}) - - self.assertRaises(exception.Invalid, - self.compute.rebuild_instance, c, instance=inst_ref, - injected_files=None, image_ref=None, - orig_image_ref=None, new_pass=None, - orig_sys_metadata=None, - recreate=True, on_shared_storage=True) - - # cleanup - db.instance_destroy(c, inst_ref['uuid']) - self.compute.terminate_instance(self.context, instance=instance) - class ComputeAPITestCase(BaseTestCase): @@ -6996,3 +6743,144 @@ class ComputeInactiveImageTestCase(BaseTestCase): self.assertRaises(exception.ImageNotActive, self.compute_api.create, self.context, inst_type, 'fake-image-uuid') + + +class EvacuateHostTestCase(BaseTestCase): + def setUp(self): + super(EvacuateHostTestCase, self).setUp() + self.inst_ref = jsonutils.to_primitive(self._create_fake_instance + ({'host': 'fake_host_2'})) + db.instance_update(self.context, self.inst_ref['uuid'], + {"task_state": task_states.REBUILDING}) + + def tearDown(self): + db.instance_destroy(self.context, self.inst_ref['uuid']) + super(EvacuateHostTestCase, self).tearDown() + + def _rebuild(self, on_shared_storage=True): + orig_image_ref = None + image_ref = None + injected_files = None + self.compute.rebuild_instance( + self.context, self.inst_ref, orig_image_ref, image_ref, + injected_files, 'newpass', recreate=True, + on_shared_storage=on_shared_storage) + + def test_rebuild_on_host_updated_target(self): + """Confirm evacuate scenario updates host.""" + self.stubs.Set(self.compute.driver, 'instance_on_disk', lambda x: True) + self.mox.ReplayAll() + + self._rebuild() + + # Should be on destination host + instance = db.instance_get(self.context, self.inst_ref['id']) + self.assertEqual(instance['host'], self.compute.host) + + def test_rebuild_with_wrong_shared_storage(self): + """Confirm evacuate scenario does not update host.""" + self.stubs.Set(self.compute.driver, 'instance_on_disk', lambda x: True) + self.mox.ReplayAll() + + self.assertRaises(exception.InvalidSharedStorage, + lambda: self._rebuild(on_shared_storage=False)) + + # Should remain on original host + instance = db.instance_get(self.context, self.inst_ref['id']) + self.assertEqual(instance['host'], 'fake_host_2') + + def test_rebuild_on_host_with_volumes(self): + """Confirm evacuate scenario reconnects volumes.""" + values = {'instance_uuid': self.inst_ref['uuid'], + 'device_name': '/dev/vdc', + 'delete_on_termination': False, + 'volume_id': 'fake_volume_id'} + + db.block_device_mapping_create(self.context, values) + + def fake_volume_get(self, context, volume): + return {'id': 'fake_volume_id'} + self.stubs.Set(cinder.API, "get", fake_volume_get) + + # Stub out and record whether it gets detached + result = {"detached": False} + + def fake_detach(self, context, volume): + result["detached"] = volume["id"] == 'fake_volume_id' + self.stubs.Set(cinder.API, "detach", fake_detach) + + def fake_terminate_connection(self, context, volume, connector): + return {} + self.stubs.Set(cinder.API, "terminate_connection", + fake_terminate_connection) + + # make sure volumes attach, detach are called + self.mox.StubOutWithMock(self.compute.volume_api, 'detach') + self.compute.volume_api.detach(mox.IsA(self.context), mox.IgnoreArg()) + + self.mox.StubOutWithMock(self.compute, '_setup_block_device_mapping') + self.compute._setup_block_device_mapping(mox.IsA(self.context), + mox.IsA(self.inst_ref), + mox.IgnoreArg()) + + self.stubs.Set(self.compute.driver, 'instance_on_disk', lambda x: True) + self.mox.ReplayAll() + + self._rebuild() + + # cleanup + for bdms in db.block_device_mapping_get_all_by_instance( + self.context, self.inst_ref['uuid']): + db.block_device_mapping_destroy(self.context, bdms['id']) + + def test_rebuild_on_host_with_shared_storage(self): + """Confirm evacuate scenario on shared storage.""" + self.mox.StubOutWithMock(self.compute.driver, 'spawn') + self.compute.driver.spawn(mox.IsA(self.context), + mox.IsA(self.inst_ref), {}, mox.IgnoreArg(), 'newpass', + network_info=mox.IgnoreArg(), + block_device_info=mox.IgnoreArg()) + + self.stubs.Set(self.compute.driver, 'instance_on_disk', lambda x: True) + self.mox.ReplayAll() + + self._rebuild() + + def test_rebuild_on_host_without_shared_storage(self): + """Confirm evacuate scenario without shared storage + (rebuild from image) + """ + fake_image = {'id': 1, + 'name': 'fake_name', + 'properties': {'kernel_id': 'fake_kernel_id', + 'ramdisk_id': 'fake_ramdisk_id'}} + + self.mox.StubOutWithMock(self.compute.driver, 'spawn') + self.compute.driver.spawn(mox.IsA(self.context), + mox.IsA(self.inst_ref), mox.IsA(fake_image), mox.IgnoreArg(), + mox.IsA('newpass'), network_info=mox.IgnoreArg(), + block_device_info=mox.IgnoreArg()) + + self.stubs.Set(self.compute.driver, 'instance_on_disk', + lambda x: False) + self.mox.ReplayAll() + + self._rebuild(on_shared_storage=False) + + def test_rebuild_on_host_instance_exists(self): + """Rebuild if instance exists raises an exception.""" + db.instance_update(self.context, self.inst_ref['uuid'], + {"task_state": task_states.SCHEDULING}) + self.compute.run_instance(self.context, instance=self.inst_ref) + + self.stubs.Set(self.compute.driver, 'instance_on_disk', lambda x: True) + self.assertRaises(exception.InstanceExists, + lambda: self._rebuild(on_shared_storage=True)) + + def test_driver_doesnt_support_recreate(self): + with utils.temporary_mutation(self.compute.driver.capabilities, + supports_recreate=False): + self.stubs.Set(self.compute.driver, 'instance_on_disk', + lambda x: True) + self.assertRaises(exception.InstanceRecreateNotSupported, + lambda: self._rebuild(on_shared_storage=True)) -- cgit