diff options
Diffstat (limited to 'nova/tests/compute/test_compute_api.py')
-rw-r--r-- | nova/tests/compute/test_compute_api.py | 2635 |
1 files changed, 0 insertions, 2635 deletions
diff --git a/nova/tests/compute/test_compute_api.py b/nova/tests/compute/test_compute_api.py deleted file mode 100644 index 932c6af3ae..0000000000 --- a/nova/tests/compute/test_compute_api.py +++ /dev/null @@ -1,2635 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Unit tests for compute API.""" - -import contextlib -import copy -import datetime - -import iso8601 -import mock -import mox -from oslo.utils import timeutils - -from nova.compute import api as compute_api -from nova.compute import arch -from nova.compute import cells_api as compute_cells_api -from nova.compute import delete_types -from nova.compute import flavors -from nova.compute import instance_actions -from nova.compute import task_states -from nova.compute import utils as compute_utils -from nova.compute import vm_mode -from nova.compute import vm_states -from nova import context -from nova import db -from nova import exception -from nova import objects -from nova.objects import base as obj_base -from nova.objects import quotas as quotas_obj -from nova.openstack.common import uuidutils -from nova import quota -from nova import test -from nova.tests import fake_block_device -from nova.tests import fake_instance -from nova.tests.image import fake as fake_image -from nova.tests import matchers -from nova.tests.objects import test_flavor -from nova.tests.objects import test_migration -from nova.tests.objects import test_service -from nova.volume import cinder - - -FAKE_IMAGE_REF = 'fake-image-ref' -NODENAME = 'fakenode1' -SHELVED_IMAGE = 'fake-shelved-image' -SHELVED_IMAGE_NOT_FOUND = 'fake-shelved-image-notfound' -SHELVED_IMAGE_NOT_AUTHORIZED = 'fake-shelved-image-not-authorized' -SHELVED_IMAGE_EXCEPTION = 'fake-shelved-image-exception' - - -class _ComputeAPIUnitTestMixIn(object): - def setUp(self): - super(_ComputeAPIUnitTestMixIn, self).setUp() - self.user_id = 'fake' - self.project_id = 'fake' - self.context = context.RequestContext(self.user_id, - self.project_id) - - def _get_vm_states(self, exclude_states=None): - vm_state = set([vm_states.ACTIVE, vm_states.BUILDING, vm_states.PAUSED, - vm_states.SUSPENDED, vm_states.RESCUED, vm_states.STOPPED, - vm_states.RESIZED, vm_states.SOFT_DELETED, - vm_states.DELETED, vm_states.ERROR, vm_states.SHELVED, - vm_states.SHELVED_OFFLOADED]) - if not exclude_states: - exclude_states = set() - return vm_state - exclude_states - - def _create_flavor(self, params=None): - flavor = {'id': 1, - 'flavorid': 1, - 'name': 'm1.tiny', - 'memory_mb': 512, - 'vcpus': 1, - 'vcpu_weight': None, - 'root_gb': 1, - 'ephemeral_gb': 0, - 'rxtx_factor': 1, - 'swap': 0, - 'deleted': 0, - 'disabled': False, - 'is_public': True, - } - if params: - flavor.update(params) - return flavor - - def _create_instance_obj(self, params=None, flavor=None): - """Create a test instance.""" - if not params: - params = {} - - if flavor is None: - flavor = self._create_flavor() - - def make_fake_sys_meta(): - sys_meta = params.pop("system_metadata", {}) - for key in flavors.system_metadata_flavor_props: - sys_meta['instance_type_%s' % key] = flavor[key] - return sys_meta - - now = timeutils.utcnow() - - instance = objects.Instance() - instance.metadata = {} - instance.metadata.update(params.pop('metadata', {})) - instance.system_metadata = make_fake_sys_meta() - instance.system_metadata.update(params.pop('system_metadata', {})) - instance._context = self.context - instance.id = 1 - instance.uuid = uuidutils.generate_uuid() - instance.cell_name = 'api!child' - instance.vm_state = vm_states.ACTIVE - instance.task_state = None - instance.image_ref = FAKE_IMAGE_REF - instance.reservation_id = 'r-fakeres' - instance.user_id = self.user_id - instance.project_id = self.project_id - instance.host = 'fake_host' - instance.node = NODENAME - instance.instance_type_id = flavor['id'] - instance.ami_launch_index = 0 - instance.memory_mb = 0 - instance.vcpus = 0 - instance.root_gb = 0 - instance.ephemeral_gb = 0 - instance.architecture = arch.X86_64 - instance.os_type = 'Linux' - instance.locked = False - instance.created_at = now - instance.updated_at = now - instance.launched_at = now - instance.disable_terminate = False - instance.info_cache = objects.InstanceInfoCache() - - if params: - instance.update(params) - instance.obj_reset_changes() - return instance - - def test_create_quota_exceeded_messages(self): - image_href = "image_href" - image_id = 0 - instance_type = self._create_flavor() - - self.mox.StubOutWithMock(self.compute_api, "_get_image") - self.mox.StubOutWithMock(quota.QUOTAS, "limit_check") - self.mox.StubOutWithMock(quota.QUOTAS, "reserve") - - quotas = {'instances': 1, 'cores': 1, 'ram': 1} - usages = dict((r, {'in_use': 1, 'reserved': 1}) for r in - ['instances', 'cores', 'ram']) - headroom = dict((res, quotas[res] - - (usages[res]['in_use'] + usages[res]['reserved'])) - for res in quotas.keys()) - quota_exception = exception.OverQuota(quotas=quotas, - usages=usages, overs=['instances'], headroom=headroom) - - for _unused in range(2): - self.compute_api._get_image(self.context, image_href).AndReturn( - (image_id, {})) - quota.QUOTAS.limit_check(self.context, metadata_items=mox.IsA(int)) - quota.QUOTAS.reserve(self.context, instances=40, - cores=mox.IsA(int), - expire=mox.IgnoreArg(), - project_id=mox.IgnoreArg(), - user_id=mox.IgnoreArg(), - ram=mox.IsA(int)).AndRaise(quota_exception) - - self.mox.ReplayAll() - - for min_count, message in [(20, '20-40'), (40, '40')]: - try: - self.compute_api.create(self.context, instance_type, - "image_href", min_count=min_count, - max_count=40) - except exception.TooManyInstances as e: - self.assertEqual(message, e.kwargs['req']) - else: - self.fail("Exception not raised") - - def test_specified_port_and_multiple_instances_neutronv2(self): - # Tests that if port is specified there is only one instance booting - # (i.e max_count == 1) as we can't share the same port across multiple - # instances. - self.flags(network_api_class='nova.network.neutronv2.api.API') - port = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - address = '10.0.0.1' - min_count = 1 - max_count = 2 - requested_networks = objects.NetworkRequestList( - objects=[objects.NetworkRequest(address=address, - port_id=port)]) - - self.assertRaises(exception.MultiplePortsNotApplicable, - self.compute_api.create, self.context, 'fake_flavor', 'image_id', - min_count=min_count, max_count=max_count, - requested_networks=requested_networks) - - def _test_specified_ip_and_multiple_instances_helper(self, - requested_networks): - # Tests that if ip is specified there is only one instance booting - # (i.e max_count == 1) - min_count = 1 - max_count = 2 - self.assertRaises(exception.InvalidFixedIpAndMaxCountRequest, - self.compute_api.create, self.context, "fake_flavor", 'image_id', - min_count=min_count, max_count=max_count, - requested_networks=requested_networks) - - def test_specified_ip_and_multiple_instances(self): - network = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - address = '10.0.0.1' - requested_networks = objects.NetworkRequestList( - objects=[objects.NetworkRequest(network_id=network, - address=address)]) - self._test_specified_ip_and_multiple_instances_helper( - requested_networks) - - def test_specified_ip_and_multiple_instances_neutronv2(self): - self.flags(network_api_class='nova.network.neutronv2.api.API') - network = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' - address = '10.0.0.1' - requested_networks = objects.NetworkRequestList( - objects=[objects.NetworkRequest(network_id=network, - address=address)]) - self._test_specified_ip_and_multiple_instances_helper( - requested_networks) - - def test_suspend(self): - # Ensure instance can be suspended. - instance = self._create_instance_obj() - self.assertEqual(instance.vm_state, vm_states.ACTIVE) - self.assertIsNone(instance.task_state) - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, - '_record_action_start') - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - self.mox.StubOutWithMock(rpcapi, 'suspend_instance') - - instance.save(expected_task_state=[None]) - self.compute_api._record_action_start(self.context, - instance, instance_actions.SUSPEND) - rpcapi.suspend_instance(self.context, instance) - - self.mox.ReplayAll() - - self.compute_api.suspend(self.context, instance) - self.assertEqual(vm_states.ACTIVE, instance.vm_state) - self.assertEqual(task_states.SUSPENDING, - instance.task_state) - - def _test_suspend_fails(self, vm_state): - params = dict(vm_state=vm_state) - instance = self._create_instance_obj(params=params) - self.assertIsNone(instance.task_state) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.suspend, - self.context, instance) - - def test_suspend_fails_invalid_states(self): - invalid_vm_states = self._get_vm_states(set([vm_states.ACTIVE])) - for state in invalid_vm_states: - self._test_suspend_fails(state) - - def test_resume(self): - # Ensure instance can be resumed (if suspended). - instance = self._create_instance_obj( - params=dict(vm_state=vm_states.SUSPENDED)) - self.assertEqual(instance.vm_state, vm_states.SUSPENDED) - self.assertIsNone(instance.task_state) - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, - '_record_action_start') - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - self.mox.StubOutWithMock(rpcapi, 'resume_instance') - - instance.save(expected_task_state=[None]) - self.compute_api._record_action_start(self.context, - instance, instance_actions.RESUME) - rpcapi.resume_instance(self.context, instance) - - self.mox.ReplayAll() - - self.compute_api.resume(self.context, instance) - self.assertEqual(vm_states.SUSPENDED, instance.vm_state) - self.assertEqual(task_states.RESUMING, - instance.task_state) - - def test_start(self): - params = dict(vm_state=vm_states.STOPPED) - instance = self._create_instance_obj(params=params) - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, - '_record_action_start') - - instance.save(expected_task_state=[None]) - self.compute_api._record_action_start(self.context, - instance, instance_actions.START) - - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - - self.mox.StubOutWithMock(rpcapi, 'start_instance') - rpcapi.start_instance(self.context, instance) - - self.mox.ReplayAll() - - self.compute_api.start(self.context, instance) - self.assertEqual(task_states.POWERING_ON, - instance.task_state) - - def test_start_invalid_state(self): - instance = self._create_instance_obj() - self.assertEqual(instance.vm_state, vm_states.ACTIVE) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.start, - self.context, instance) - - def test_start_no_host(self): - params = dict(vm_state=vm_states.STOPPED, host='') - instance = self._create_instance_obj(params=params) - self.assertRaises(exception.InstanceNotReady, - self.compute_api.start, - self.context, instance) - - def _test_stop(self, vm_state, force=False): - # Make sure 'progress' gets reset - params = dict(task_state=None, progress=99, vm_state=vm_state) - instance = self._create_instance_obj(params=params) - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, - '_record_action_start') - - instance.save(expected_task_state=[None]) - self.compute_api._record_action_start(self.context, - instance, instance_actions.STOP) - - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - - self.mox.StubOutWithMock(rpcapi, 'stop_instance') - rpcapi.stop_instance(self.context, instance, do_cast=True) - - self.mox.ReplayAll() - - if force: - self.compute_api.force_stop(self.context, instance) - else: - self.compute_api.stop(self.context, instance) - self.assertEqual(task_states.POWERING_OFF, - instance.task_state) - self.assertEqual(0, instance.progress) - - def test_stop(self): - self._test_stop(vm_states.ACTIVE) - - def test_stop_stopped_instance_with_bypass(self): - self._test_stop(vm_states.STOPPED, force=True) - - def _test_stop_invalid_state(self, vm_state): - params = dict(vm_state=vm_state) - instance = self._create_instance_obj(params=params) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.stop, - self.context, instance) - - def test_stop_fails_invalid_states(self): - invalid_vm_states = self._get_vm_states(set([vm_states.ACTIVE, - vm_states.ERROR])) - for state in invalid_vm_states: - self._test_stop_invalid_state(state) - - def test_stop_a_stopped_inst(self): - params = {'vm_state': vm_states.STOPPED} - instance = self._create_instance_obj(params=params) - - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.stop, - self.context, instance) - - def test_stop_no_host(self): - params = {'host': ''} - instance = self._create_instance_obj(params=params) - self.assertRaises(exception.InstanceNotReady, - self.compute_api.stop, - self.context, instance) - - def _test_reboot_type(self, vm_state, reboot_type, task_state=None): - # Ensure instance can be soft rebooted. - inst = self._create_instance_obj() - inst.vm_state = vm_state - inst.task_state = task_state - - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api, 'update') - self.mox.StubOutWithMock(inst, 'save') - inst.save(expected_task_state=[None, task_states.REBOOTING, - task_states.REBOOT_PENDING, - task_states.REBOOT_STARTED]) - self.compute_api._record_action_start(self.context, inst, - instance_actions.REBOOT) - - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - - self.mox.StubOutWithMock(rpcapi, 'reboot_instance') - rpcapi.reboot_instance(self.context, instance=inst, - block_device_info=None, - reboot_type=reboot_type) - self.mox.ReplayAll() - - self.compute_api.reboot(self.context, inst, reboot_type) - - def _test_reboot_type_fails(self, reboot_type, **updates): - inst = self._create_instance_obj() - inst.update(updates) - - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.reboot, - self.context, inst, reboot_type) - - def test_reboot_hard_active(self): - self._test_reboot_type(vm_states.ACTIVE, 'HARD') - - def test_reboot_hard_error(self): - self._test_reboot_type(vm_states.ERROR, 'HARD') - - def test_reboot_hard_rebooting(self): - self._test_reboot_type(vm_states.ACTIVE, 'HARD', - task_state=task_states.REBOOTING) - - def test_reboot_hard_reboot_started(self): - self._test_reboot_type(vm_states.ACTIVE, 'HARD', - task_state=task_states.REBOOT_STARTED) - - def test_reboot_hard_reboot_pending(self): - self._test_reboot_type(vm_states.ACTIVE, 'HARD', - task_state=task_states.REBOOT_PENDING) - - def test_reboot_hard_rescued(self): - self._test_reboot_type_fails('HARD', vm_state=vm_states.RESCUED) - - def test_reboot_hard_error_not_launched(self): - self._test_reboot_type_fails('HARD', vm_state=vm_states.ERROR, - launched_at=None) - - def test_reboot_soft(self): - self._test_reboot_type(vm_states.ACTIVE, 'SOFT') - - def test_reboot_soft_error(self): - self._test_reboot_type_fails('SOFT', vm_state=vm_states.ERROR) - - def test_reboot_soft_paused(self): - self._test_reboot_type_fails('SOFT', vm_state=vm_states.PAUSED) - - def test_reboot_soft_stopped(self): - self._test_reboot_type_fails('SOFT', vm_state=vm_states.STOPPED) - - def test_reboot_soft_suspended(self): - self._test_reboot_type_fails('SOFT', vm_state=vm_states.SUSPENDED) - - def test_reboot_soft_rebooting(self): - self._test_reboot_type_fails('SOFT', task_state=task_states.REBOOTING) - - def test_reboot_soft_rebooting_hard(self): - self._test_reboot_type_fails('SOFT', - task_state=task_states.REBOOTING_HARD) - - def test_reboot_soft_reboot_started(self): - self._test_reboot_type_fails('SOFT', - task_state=task_states.REBOOT_STARTED) - - def test_reboot_soft_reboot_pending(self): - self._test_reboot_type_fails('SOFT', - task_state=task_states.REBOOT_PENDING) - - def test_reboot_soft_rescued(self): - self._test_reboot_type_fails('SOFT', vm_state=vm_states.RESCUED) - - def test_reboot_soft_error_not_launched(self): - self._test_reboot_type_fails('SOFT', vm_state=vm_states.ERROR, - launched_at=None) - - def _test_delete_resizing_part(self, inst, deltas): - fake_db_migration = test_migration.fake_db_migration() - migration = objects.Migration._from_db_object( - self.context, objects.Migration(), - fake_db_migration) - inst.instance_type_id = migration.new_instance_type_id - old_flavor = {'vcpus': 1, - 'memory_mb': 512} - deltas['cores'] = -old_flavor['vcpus'] - deltas['ram'] = -old_flavor['memory_mb'] - - self.mox.StubOutWithMock(objects.Migration, - 'get_by_instance_and_status') - self.mox.StubOutWithMock(flavors, 'get_flavor') - - self.context.elevated().AndReturn(self.context) - objects.Migration.get_by_instance_and_status( - self.context, inst.uuid, 'post-migrating').AndReturn(migration) - flavors.get_flavor(migration.old_instance_type_id).AndReturn( - old_flavor) - - def _test_delete_resized_part(self, inst): - migration = objects.Migration._from_db_object( - self.context, objects.Migration(), - test_migration.fake_db_migration()) - - self.mox.StubOutWithMock(objects.Migration, - 'get_by_instance_and_status') - - self.context.elevated().AndReturn(self.context) - objects.Migration.get_by_instance_and_status( - self.context, inst.uuid, 'finished').AndReturn(migration) - self.compute_api._downsize_quota_delta(self.context, inst - ).AndReturn('deltas') - fake_quotas = objects.Quotas.from_reservations(self.context, - ['rsvs']) - self.compute_api._reserve_quota_delta(self.context, 'deltas', inst, - ).AndReturn(fake_quotas) - self.compute_api._record_action_start( - self.context, inst, instance_actions.CONFIRM_RESIZE) - self.compute_api.compute_rpcapi.confirm_resize( - self.context, inst, migration, - migration['source_compute'], fake_quotas.reservations, cast=False) - - def _test_delete_shelved_part(self, inst): - image_api = self.compute_api.image_api - self.mox.StubOutWithMock(image_api, 'delete') - - snapshot_id = inst.system_metadata.get('shelved_image_id') - if snapshot_id == SHELVED_IMAGE: - image_api.delete(self.context, snapshot_id).AndReturn(True) - elif snapshot_id == SHELVED_IMAGE_NOT_FOUND: - image_api.delete(self.context, snapshot_id).AndRaise( - exception.ImageNotFound(image_id=snapshot_id)) - elif snapshot_id == SHELVED_IMAGE_NOT_AUTHORIZED: - image_api.delete(self.context, snapshot_id).AndRaise( - exception.ImageNotAuthorized(image_id=snapshot_id)) - elif snapshot_id == SHELVED_IMAGE_EXCEPTION: - image_api.delete(self.context, snapshot_id).AndRaise( - test.TestingException("Unexpected error")) - - def _test_downed_host_part(self, inst, updates, delete_time, delete_type): - inst.info_cache.delete() - compute_utils.notify_about_instance_usage( - self.compute_api.notifier, self.context, inst, - '%s.start' % delete_type) - self.context.elevated().AndReturn(self.context) - self.compute_api.network_api.deallocate_for_instance( - self.context, inst) - state = (delete_types.SOFT_DELETE in delete_type and - vm_states.SOFT_DELETED or - vm_states.DELETED) - updates.update({'vm_state': state, - 'task_state': None, - 'terminated_at': delete_time}) - inst.save() - - updates.update({'deleted_at': delete_time, - 'deleted': True}) - fake_inst = fake_instance.fake_db_instance(**updates) - db.instance_destroy(self.context, inst.uuid, - constraint=None).AndReturn(fake_inst) - compute_utils.notify_about_instance_usage( - self.compute_api.notifier, - self.context, inst, '%s.end' % delete_type, - system_metadata=inst.system_metadata) - - def _test_delete(self, delete_type, **attrs): - reservations = ['fake-resv'] - inst = self._create_instance_obj() - inst.update(attrs) - inst._context = self.context - deltas = {'instances': -1, - 'cores': -inst.vcpus, - 'ram': -inst.memory_mb} - delete_time = datetime.datetime(1955, 11, 5, 9, 30, - tzinfo=iso8601.iso8601.Utc()) - timeutils.set_time_override(delete_time) - task_state = (delete_type == delete_types.SOFT_DELETE and - task_states.SOFT_DELETING or task_states.DELETING) - updates = {'progress': 0, 'task_state': task_state} - if delete_type == delete_types.SOFT_DELETE: - updates['deleted_at'] = delete_time - self.mox.StubOutWithMock(inst, 'save') - self.mox.StubOutWithMock(objects.BlockDeviceMappingList, - 'get_by_instance_uuid') - self.mox.StubOutWithMock(quota.QUOTAS, 'reserve') - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(db, 'service_get_by_compute_host') - self.mox.StubOutWithMock(self.compute_api.servicegroup_api, - 'service_is_up') - self.mox.StubOutWithMock(self.compute_api, '_downsize_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - self.mox.StubOutWithMock(inst.info_cache, 'delete') - self.mox.StubOutWithMock(self.compute_api.network_api, - 'deallocate_for_instance') - self.mox.StubOutWithMock(db, 'instance_system_metadata_get') - self.mox.StubOutWithMock(db, 'instance_destroy') - self.mox.StubOutWithMock(compute_utils, - 'notify_about_instance_usage') - self.mox.StubOutWithMock(quota.QUOTAS, 'commit') - rpcapi = self.compute_api.compute_rpcapi - self.mox.StubOutWithMock(rpcapi, 'confirm_resize') - - if (inst.vm_state in - (vm_states.SHELVED, vm_states.SHELVED_OFFLOADED)): - self._test_delete_shelved_part(inst) - - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - self.mox.StubOutWithMock(rpcapi, 'terminate_instance') - self.mox.StubOutWithMock(rpcapi, 'soft_delete_instance') - - objects.BlockDeviceMappingList.get_by_instance_uuid( - self.context, inst.uuid).AndReturn([]) - inst.save() - if inst.task_state == task_states.RESIZE_FINISH: - self._test_delete_resizing_part(inst, deltas) - quota.QUOTAS.reserve(self.context, project_id=inst.project_id, - user_id=inst.user_id, - expire=mox.IgnoreArg(), - **deltas).AndReturn(reservations) - - # NOTE(comstud): This is getting messy. But what we are wanting - # to test is: - # If cells is enabled and we're the API cell: - # * Cast to cells_rpcapi.<method> with reservations=None - # * Commit reservations - # Otherwise: - # * Check for downed host - # * If downed host: - # * Clean up instance, destroying it, sending notifications. - # (Tested in _test_downed_host_part()) - # * Commit reservations - # * If not downed host: - # * Record the action start. - # * Cast to compute_rpcapi.<method> with the reservations - - cast = True - commit_quotas = True - if self.cell_type != 'api': - if inst.vm_state == vm_states.RESIZED: - self._test_delete_resized_part(inst) - - self.context.elevated().AndReturn(self.context) - db.service_get_by_compute_host( - self.context, inst.host).AndReturn( - test_service.fake_service) - self.compute_api.servicegroup_api.service_is_up( - mox.IsA(objects.Service)).AndReturn( - inst.host != 'down-host') - - if inst.host == 'down-host': - self._test_downed_host_part(inst, updates, delete_time, - delete_type) - cast = False - else: - # Happens on the manager side - commit_quotas = False - - if cast: - if self.cell_type != 'api': - self.compute_api._record_action_start(self.context, inst, - instance_actions.DELETE) - if commit_quotas: - cast_reservations = None - else: - cast_reservations = reservations - if delete_type == delete_types.SOFT_DELETE: - rpcapi.soft_delete_instance(self.context, inst, - reservations=cast_reservations) - elif delete_type in [delete_types.DELETE, - delete_types.FORCE_DELETE]: - rpcapi.terminate_instance(self.context, inst, [], - reservations=cast_reservations) - - if commit_quotas: - # Local delete or when we're testing API cell. - quota.QUOTAS.commit(self.context, reservations, - project_id=inst.project_id, - user_id=inst.user_id) - - self.mox.ReplayAll() - - getattr(self.compute_api, delete_type)(self.context, inst) - for k, v in updates.items(): - self.assertEqual(inst[k], v) - - self.mox.UnsetStubs() - - def test_delete(self): - self._test_delete(delete_types.DELETE) - - def test_delete_if_not_launched(self): - self._test_delete(delete_types.DELETE, launched_at=None) - - def test_delete_in_resizing(self): - self._test_delete(delete_types.DELETE, - task_state=task_states.RESIZE_FINISH) - - def test_delete_in_resized(self): - self._test_delete(delete_types.DELETE, vm_state=vm_states.RESIZED) - - def test_delete_shelved(self): - fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE} - self._test_delete(delete_types.DELETE, - vm_state=vm_states.SHELVED, - system_metadata=fake_sys_meta) - - def test_delete_shelved_offloaded(self): - fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE} - self._test_delete(delete_types.DELETE, - vm_state=vm_states.SHELVED_OFFLOADED, - system_metadata=fake_sys_meta) - - def test_delete_shelved_image_not_found(self): - fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE_NOT_FOUND} - self._test_delete(delete_types.DELETE, - vm_state=vm_states.SHELVED_OFFLOADED, - system_metadata=fake_sys_meta) - - def test_delete_shelved_image_not_authorized(self): - fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE_NOT_AUTHORIZED} - self._test_delete(delete_types.DELETE, - vm_state=vm_states.SHELVED_OFFLOADED, - system_metadata=fake_sys_meta) - - def test_delete_shelved_exception(self): - fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE_EXCEPTION} - self._test_delete(delete_types.DELETE, - vm_state=vm_states.SHELVED, - system_metadata=fake_sys_meta) - - def test_delete_with_down_host(self): - self._test_delete(delete_types.DELETE, host='down-host') - - def test_delete_soft_with_down_host(self): - self._test_delete(delete_types.SOFT_DELETE, host='down-host') - - def test_delete_soft(self): - self._test_delete(delete_types.SOFT_DELETE) - - def test_delete_forced(self): - for vm_state in self._get_vm_states(): - self._test_delete(delete_types.FORCE_DELETE, vm_state=vm_state) - - def test_delete_forced_when_task_state_deleting(self): - for vm_state in self._get_vm_states(): - self._test_delete(delete_types.FORCE_DELETE, vm_state=vm_state, - task_state=task_states.DELETING) - - def test_no_delete_when_task_state_deleting(self): - if self.cell_type == 'api': - # In 'api' cell, the callback terminate_instance will - # get called, and quota will be committed before returning. - # It doesn't check for below condition, hence skipping the test. - """ - if original_task_state in (task_states.DELETING, - task_states.SOFT_DELETING): - LOG.info(_('Instance is already in deleting state, ' - 'ignoring this request'), instance=instance) - quotas.rollback() - return - """ - self.skipTest("API cell doesn't delete instance directly.") - - attrs = {} - fake_sys_meta = {'shelved_image_id': SHELVED_IMAGE} - - for vm_state in self._get_vm_states(): - if vm_state in (vm_states.SHELVED, vm_states.SHELVED_OFFLOADED): - attrs.update({'system_metadata': fake_sys_meta}) - - attrs.update({'vm_state': vm_state, 'task_state': 'deleting'}) - reservations = ['fake-resv'] - inst = self._create_instance_obj() - inst.update(attrs) - inst._context = self.context - deltas = {'instances': -1, - 'cores': -inst.vcpus, - 'ram': -inst.memory_mb} - delete_time = datetime.datetime(1955, 11, 5, 9, 30, - tzinfo=iso8601.iso8601.Utc()) - timeutils.set_time_override(delete_time) - bdms = [] - migration = objects.Migration._from_db_object( - self.context, objects.Migration(), - test_migration.fake_db_migration()) - - fake_quotas = objects.Quotas.from_reservations(self.context, - ['rsvs']) - - image_api = self.compute_api.image_api - rpcapi = self.compute_api.compute_rpcapi - - with contextlib.nested( - mock.patch.object(image_api, 'delete'), - mock.patch.object(inst, 'save'), - mock.patch.object(objects.BlockDeviceMappingList, - 'get_by_instance_uuid', - return_value=bdms), - mock.patch.object(objects.Migration, - 'get_by_instance_and_status'), - mock.patch.object(quota.QUOTAS, 'reserve', - return_value=reservations), - mock.patch.object(self.context, 'elevated', - return_value=self.context), - mock.patch.object(db, 'service_get_by_compute_host', - return_value=test_service.fake_service), - mock.patch.object(self.compute_api.servicegroup_api, - 'service_is_up', - return_value=inst.host != 'down-host'), - mock.patch.object(self.compute_api, - '_downsize_quota_delta', - return_value=fake_quotas), - mock.patch.object(self.compute_api, - '_reserve_quota_delta'), - mock.patch.object(self.compute_api, - '_record_action_start'), - mock.patch.object(db, 'instance_update_and_get_original'), - mock.patch.object(inst.info_cache, 'delete'), - mock.patch.object(self.compute_api.network_api, - 'deallocate_for_instance'), - mock.patch.object(db, 'instance_system_metadata_get'), - mock.patch.object(db, 'instance_destroy'), - mock.patch.object(compute_utils, - 'notify_about_instance_usage'), - mock.patch.object(quota.QUOTAS, 'commit'), - mock.patch.object(quota.QUOTAS, 'rollback'), - mock.patch.object(rpcapi, 'confirm_resize'), - mock.patch.object(rpcapi, 'terminate_instance') - ) as ( - image_delete, - save, - get_by_instance_uuid, - get_by_instance_and_status, - reserve, - elevated, - service_get_by_compute_host, - service_is_up, - _downsize_quota_delta, - _reserve_quota_delta, - _record_action_start, - instance_update_and_get_original, - delete, - deallocate_for_instance, - instance_system_metadata_get, - instance_destroy, - notify_about_instance_usage, - commit, - rollback, - confirm_resize, - terminate_instance - ): - if (inst.vm_state in (vm_states.SHELVED, - vm_states.SHELVED_OFFLOADED)): - image_delete.return_value = True - - if inst.vm_state == vm_states.RESIZED: - get_by_instance_and_status.return_value = migration - _downsize_quota_delta.return_value = deltas - - self.compute_api.delete(self.context, inst) - self.assertEqual(1, rollback.call_count) - self.assertEqual(0, terminate_instance.call_count) - - def test_delete_fast_if_host_not_set(self): - inst = self._create_instance_obj() - inst.host = '' - quotas = quotas_obj.Quotas(self.context) - updates = {'progress': 0, 'task_state': task_states.DELETING} - - self.mox.StubOutWithMock(inst, 'save') - self.mox.StubOutWithMock(db, - 'block_device_mapping_get_all_by_instance') - - self.mox.StubOutWithMock(db, 'constraint') - self.mox.StubOutWithMock(db, 'instance_destroy') - self.mox.StubOutWithMock(self.compute_api, '_create_reservations') - self.mox.StubOutWithMock(compute_utils, - 'notify_about_instance_usage') - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - self.mox.StubOutWithMock(rpcapi, 'terminate_instance') - - db.block_device_mapping_get_all_by_instance(self.context, - inst.uuid, - use_slave=False).AndReturn([]) - inst.save() - self.compute_api._create_reservations(self.context, - inst, inst.task_state, - inst.project_id, inst.user_id - ).AndReturn(quotas) - - if self.cell_type == 'api': - rpcapi.terminate_instance( - self.context, inst, - mox.IsA(objects.BlockDeviceMappingList), - reservations=None) - else: - compute_utils.notify_about_instance_usage( - self.compute_api.notifier, self.context, - inst, 'delete.start') - db.constraint(host=mox.IgnoreArg()).AndReturn('constraint') - delete_time = datetime.datetime(1955, 11, 5, 9, 30, - tzinfo=iso8601.iso8601.Utc()) - updates['deleted_at'] = delete_time - updates['deleted'] = True - fake_inst = fake_instance.fake_db_instance(**updates) - db.instance_destroy(self.context, inst.uuid, - constraint='constraint').AndReturn(fake_inst) - compute_utils.notify_about_instance_usage( - self.compute_api.notifier, self.context, - inst, 'delete.end', - system_metadata=inst.system_metadata) - - self.mox.ReplayAll() - - self.compute_api.delete(self.context, inst) - for k, v in updates.items(): - self.assertEqual(inst[k], v) - - def test_local_delete_with_deleted_volume(self): - bdms = [objects.BlockDeviceMapping( - **fake_block_device.FakeDbBlockDeviceDict( - {'id': 42, 'volume_id': 'volume_id', - 'source_type': 'volume', 'destination_type': 'volume', - 'delete_on_termination': False}))] - - def _fake_do_delete(context, instance, bdms, - rservations=None, local=False): - pass - - inst = self._create_instance_obj() - inst._context = self.context - - self.mox.StubOutWithMock(inst, 'destroy') - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(inst.info_cache, 'delete') - self.mox.StubOutWithMock(self.compute_api.network_api, - 'deallocate_for_instance') - self.mox.StubOutWithMock(db, 'instance_system_metadata_get') - self.mox.StubOutWithMock(compute_utils, - 'notify_about_instance_usage') - self.mox.StubOutWithMock(self.compute_api.volume_api, - 'terminate_connection') - self.mox.StubOutWithMock(objects.BlockDeviceMapping, 'destroy') - - inst.info_cache.delete() - compute_utils.notify_about_instance_usage( - self.compute_api.notifier, self.context, - inst, 'delete.start') - self.context.elevated().MultipleTimes().AndReturn(self.context) - if self.cell_type != 'api': - self.compute_api.network_api.deallocate_for_instance( - self.context, inst) - - self.compute_api.volume_api.terminate_connection( - mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()).\ - AndRaise(exception. VolumeNotFound('volume_id')) - bdms[0].destroy(self.context) - - inst.destroy() - compute_utils.notify_about_instance_usage( - self.compute_api.notifier, self.context, - inst, 'delete.end', - system_metadata=inst.system_metadata) - - self.mox.ReplayAll() - self.compute_api._local_delete(self.context, inst, bdms, - delete_types.DELETE, - _fake_do_delete) - - def test_delete_disabled(self): - inst = self._create_instance_obj() - inst.disable_terminate = True - self.mox.StubOutWithMock(db, 'instance_update_and_get_original') - self.mox.ReplayAll() - self.compute_api.delete(self.context, inst) - - def test_delete_soft_rollback(self): - inst = self._create_instance_obj() - self.mox.StubOutWithMock(db, - 'block_device_mapping_get_all_by_instance') - self.mox.StubOutWithMock(inst, 'save') - - delete_time = datetime.datetime(1955, 11, 5) - timeutils.set_time_override(delete_time) - - db.block_device_mapping_get_all_by_instance( - self.context, inst.uuid, use_slave=False).AndReturn([]) - inst.save().AndRaise(test.TestingException) - - self.mox.ReplayAll() - - self.assertRaises(test.TestingException, - self.compute_api.soft_delete, self.context, inst) - - def _test_confirm_resize(self, mig_ref_passed=False): - params = dict(vm_state=vm_states.RESIZED) - fake_inst = self._create_instance_obj(params=params) - fake_mig = objects.Migration._from_db_object( - self.context, objects.Migration(), - test_migration.fake_db_migration()) - - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(objects.Migration, - 'get_by_instance_and_status') - self.mox.StubOutWithMock(self.compute_api, '_downsize_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(fake_mig, 'save') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api.compute_rpcapi, - 'confirm_resize') - - self.context.elevated().AndReturn(self.context) - if not mig_ref_passed: - objects.Migration.get_by_instance_and_status( - self.context, fake_inst['uuid'], 'finished').AndReturn( - fake_mig) - self.compute_api._downsize_quota_delta(self.context, - fake_inst).AndReturn('deltas') - - resvs = ['resvs'] - fake_quotas = objects.Quotas.from_reservations(self.context, resvs) - - self.compute_api._reserve_quota_delta(self.context, 'deltas', - fake_inst).AndReturn(fake_quotas) - - def _check_mig(expected_task_state=None): - self.assertEqual('confirming', fake_mig.status) - - fake_mig.save().WithSideEffects(_check_mig) - - if self.cell_type: - fake_quotas.commit(self.context) - - self.compute_api._record_action_start(self.context, fake_inst, - 'confirmResize') - - self.compute_api.compute_rpcapi.confirm_resize( - self.context, fake_inst, fake_mig, 'compute-source', - [] if self.cell_type else fake_quotas.reservations) - - self.mox.ReplayAll() - - if mig_ref_passed: - self.compute_api.confirm_resize(self.context, fake_inst, - migration=fake_mig) - else: - self.compute_api.confirm_resize(self.context, fake_inst) - - def test_confirm_resize(self): - self._test_confirm_resize() - - def test_confirm_resize_with_migration_ref(self): - self._test_confirm_resize(mig_ref_passed=True) - - def _test_revert_resize(self): - params = dict(vm_state=vm_states.RESIZED) - fake_inst = self._create_instance_obj(params=params) - fake_mig = objects.Migration._from_db_object( - self.context, objects.Migration(), - test_migration.fake_db_migration()) - - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(objects.Migration, - 'get_by_instance_and_status') - self.mox.StubOutWithMock(self.compute_api, - '_reverse_upsize_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(fake_inst, 'save') - self.mox.StubOutWithMock(fake_mig, 'save') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api.compute_rpcapi, - 'revert_resize') - - self.context.elevated().AndReturn(self.context) - objects.Migration.get_by_instance_and_status( - self.context, fake_inst['uuid'], 'finished').AndReturn( - fake_mig) - self.compute_api._reverse_upsize_quota_delta( - self.context, fake_mig).AndReturn('deltas') - - resvs = ['resvs'] - fake_quotas = objects.Quotas.from_reservations(self.context, resvs) - - self.compute_api._reserve_quota_delta(self.context, 'deltas', - fake_inst).AndReturn(fake_quotas) - - def _check_state(expected_task_state=None): - self.assertEqual(task_states.RESIZE_REVERTING, - fake_inst.task_state) - - fake_inst.save(expected_task_state=[None]).WithSideEffects( - _check_state) - - def _check_mig(expected_task_state=None): - self.assertEqual('reverting', fake_mig.status) - - fake_mig.save().WithSideEffects(_check_mig) - - if self.cell_type: - fake_quotas.commit(self.context) - - self.compute_api._record_action_start(self.context, fake_inst, - 'revertResize') - - self.compute_api.compute_rpcapi.revert_resize( - self.context, fake_inst, fake_mig, 'compute-dest', - [] if self.cell_type else fake_quotas.reservations) - - self.mox.ReplayAll() - - self.compute_api.revert_resize(self.context, fake_inst) - - def test_revert_resize(self): - self._test_revert_resize() - - def test_revert_resize_concurent_fail(self): - params = dict(vm_state=vm_states.RESIZED) - fake_inst = self._create_instance_obj(params=params) - fake_mig = objects.Migration._from_db_object( - self.context, objects.Migration(), - test_migration.fake_db_migration()) - - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(objects.Migration, - 'get_by_instance_and_status') - self.mox.StubOutWithMock(self.compute_api, - '_reverse_upsize_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(fake_inst, 'save') - - self.context.elevated().AndReturn(self.context) - objects.Migration.get_by_instance_and_status( - self.context, fake_inst['uuid'], 'finished').AndReturn(fake_mig) - - delta = ['delta'] - self.compute_api._reverse_upsize_quota_delta( - self.context, fake_mig).AndReturn(delta) - resvs = ['resvs'] - fake_quotas = objects.Quotas.from_reservations(self.context, resvs) - self.compute_api._reserve_quota_delta( - self.context, delta, fake_inst).AndReturn(fake_quotas) - - exc = exception.UnexpectedTaskStateError( - actual=task_states.RESIZE_REVERTING, expected=None) - fake_inst.save(expected_task_state=[None]).AndRaise(exc) - - fake_quotas.rollback(self.context) - - self.mox.ReplayAll() - self.assertRaises(exception.UnexpectedTaskStateError, - self.compute_api.revert_resize, - self.context, - fake_inst) - - def _test_resize(self, flavor_id_passed=True, - same_host=False, allow_same_host=False, - allow_mig_same_host=False, - project_id=None, - extra_kwargs=None, - same_flavor=False): - if extra_kwargs is None: - extra_kwargs = {} - - self.flags(allow_resize_to_same_host=allow_same_host, - allow_migrate_to_same_host=allow_mig_same_host) - - params = {} - if project_id is not None: - # To test instance w/ different project id than context (admin) - params['project_id'] = project_id - fake_inst = self._create_instance_obj(params=params) - - self.mox.StubOutWithMock(flavors, 'get_flavor_by_flavor_id') - self.mox.StubOutWithMock(self.compute_api, '_upsize_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(fake_inst, 'save') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'resize_instance') - - current_flavor = flavors.extract_flavor(fake_inst) - if flavor_id_passed: - new_flavor = dict(id=200, flavorid='new-flavor-id', - name='new_flavor', disabled=False) - if same_flavor: - cur_flavor = flavors.extract_flavor(fake_inst) - new_flavor['id'] = cur_flavor['id'] - flavors.get_flavor_by_flavor_id( - 'new-flavor-id', - read_deleted='no').AndReturn(new_flavor) - else: - new_flavor = current_flavor - - if (self.cell_type == 'compute' or - not (flavor_id_passed and same_flavor)): - resvs = ['resvs'] - project_id, user_id = quotas_obj.ids_from_instance(self.context, - fake_inst) - fake_quotas = objects.Quotas.from_reservations(self.context, - resvs) - - self.compute_api._upsize_quota_delta( - self.context, new_flavor, - current_flavor).AndReturn('deltas') - self.compute_api._reserve_quota_delta(self.context, 'deltas', - fake_inst).AndReturn(fake_quotas) - - def _check_state(expected_task_state=None): - self.assertEqual(task_states.RESIZE_PREP, - fake_inst.task_state) - self.assertEqual(fake_inst.progress, 0) - for key, value in extra_kwargs.items(): - self.assertEqual(value, getattr(fake_inst, key)) - - fake_inst.save(expected_task_state=[None]).WithSideEffects( - _check_state) - - if allow_same_host: - filter_properties = {'ignore_hosts': []} - else: - filter_properties = {'ignore_hosts': [fake_inst['host']]} - - if not flavor_id_passed and not allow_mig_same_host: - filter_properties['ignore_hosts'].append(fake_inst['host']) - - expected_reservations = fake_quotas.reservations - if self.cell_type == 'api': - fake_quotas.commit(self.context) - expected_reservations = [] - mig = objects.Migration() - - def _get_migration(): - return mig - - def _check_mig(ctxt): - self.assertEqual(fake_inst.uuid, mig.instance_uuid) - self.assertEqual(current_flavor['id'], - mig.old_instance_type_id) - self.assertEqual(new_flavor['id'], - mig.new_instance_type_id) - self.assertEqual('finished', mig.status) - - self.stubs.Set(objects, 'Migration', _get_migration) - self.mox.StubOutWithMock(self.context, 'elevated') - self.mox.StubOutWithMock(mig, 'create') - - self.context.elevated().AndReturn(self.context) - mig.create(self.context).WithSideEffects(_check_mig) - - if flavor_id_passed: - self.compute_api._record_action_start(self.context, fake_inst, - 'resize') - else: - self.compute_api._record_action_start(self.context, fake_inst, - 'migrate') - - scheduler_hint = {'filter_properties': filter_properties} - - self.compute_api.compute_task_api.resize_instance( - self.context, fake_inst, extra_kwargs, - scheduler_hint=scheduler_hint, - flavor=new_flavor, reservations=expected_reservations) - - self.mox.ReplayAll() - - if flavor_id_passed: - self.compute_api.resize(self.context, fake_inst, - flavor_id='new-flavor-id', - **extra_kwargs) - else: - self.compute_api.resize(self.context, fake_inst, **extra_kwargs) - - def _test_migrate(self, *args, **kwargs): - self._test_resize(*args, flavor_id_passed=False, **kwargs) - - def test_resize(self): - self._test_resize() - - def test_resize_with_kwargs(self): - self._test_resize(extra_kwargs=dict(cow='moo')) - - def test_resize_same_host_and_allowed(self): - self._test_resize(same_host=True, allow_same_host=True) - - def test_resize_same_host_and_not_allowed(self): - self._test_resize(same_host=True, allow_same_host=False) - - def test_resize_different_project_id(self): - self._test_resize(project_id='different') - - def test_migrate(self): - self._test_migrate() - - def test_migrate_with_kwargs(self): - self._test_migrate(extra_kwargs=dict(cow='moo')) - - def test_migrate_same_host_and_allowed(self): - self._test_migrate(same_host=True, allow_same_host=True) - - def test_migrate_same_host_and_not_allowed(self): - self._test_migrate(same_host=True, allow_same_host=False) - - def test_migrate_different_project_id(self): - self._test_migrate(project_id='different') - - def test_resize_invalid_flavor_fails(self): - self.mox.StubOutWithMock(flavors, 'get_flavor_by_flavor_id') - # Should never reach these. - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(self.compute_api, 'update') - self.mox.StubOutWithMock(quota.QUOTAS, 'commit') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'resize_instance') - - fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) - exc = exception.FlavorNotFound(flavor_id='flavor-id') - - flavors.get_flavor_by_flavor_id('flavor-id', - read_deleted='no').AndRaise(exc) - - self.mox.ReplayAll() - - self.assertRaises(exception.FlavorNotFound, - self.compute_api.resize, self.context, - fake_inst, flavor_id='flavor-id') - - def test_resize_disabled_flavor_fails(self): - self.mox.StubOutWithMock(flavors, 'get_flavor_by_flavor_id') - # Should never reach these. - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - self.mox.StubOutWithMock(self.compute_api, 'update') - self.mox.StubOutWithMock(quota.QUOTAS, 'commit') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'resize_instance') - - fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) - fake_flavor = dict(id=200, flavorid='flavor-id', name='foo', - disabled=True) - - flavors.get_flavor_by_flavor_id( - 'flavor-id', read_deleted='no').AndReturn(fake_flavor) - - self.mox.ReplayAll() - - self.assertRaises(exception.FlavorNotFound, - self.compute_api.resize, self.context, - fake_inst, flavor_id='flavor-id') - - @mock.patch.object(flavors, 'get_flavor_by_flavor_id') - def test_resize_to_zero_disk_flavor_fails(self, get_flavor_by_flavor_id): - fake_inst = self._create_instance_obj() - fake_flavor = dict(id=200, flavorid='flavor-id', name='foo', - root_gb=0) - - get_flavor_by_flavor_id.return_value = fake_flavor - - self.assertRaises(exception.CannotResizeDisk, - self.compute_api.resize, self.context, - fake_inst, flavor_id='flavor-id') - - def test_resize_quota_exceeds_fails(self): - self.mox.StubOutWithMock(flavors, 'get_flavor_by_flavor_id') - self.mox.StubOutWithMock(self.compute_api, '_upsize_quota_delta') - self.mox.StubOutWithMock(self.compute_api, '_reserve_quota_delta') - # Should never reach these. - self.mox.StubOutWithMock(self.compute_api, 'update') - self.mox.StubOutWithMock(quota.QUOTAS, 'commit') - self.mox.StubOutWithMock(self.compute_api, '_record_action_start') - self.mox.StubOutWithMock(self.compute_api.compute_task_api, - 'resize_instance') - - fake_inst = obj_base.obj_to_primitive(self._create_instance_obj()) - current_flavor = flavors.extract_flavor(fake_inst) - fake_flavor = dict(id=200, flavorid='flavor-id', name='foo', - disabled=False) - flavors.get_flavor_by_flavor_id( - 'flavor-id', read_deleted='no').AndReturn(fake_flavor) - deltas = dict(resource=0) - self.compute_api._upsize_quota_delta( - self.context, fake_flavor, - current_flavor).AndReturn(deltas) - usage = dict(in_use=0, reserved=0) - quotas = {'resource': 0} - usages = {'resource': usage} - overs = ['resource'] - headroom = {'resource': quotas['resource'] - - (usages['resource']['in_use'] + usages['resource']['reserved'])} - over_quota_args = dict(quotas=quotas, - usages=usages, - overs=overs, - headroom=headroom) - - self.compute_api._reserve_quota_delta(self.context, deltas, - fake_inst).AndRaise( - exception.OverQuota(**over_quota_args)) - - self.mox.ReplayAll() - - self.assertRaises(exception.TooManyInstances, - self.compute_api.resize, self.context, - fake_inst, flavor_id='flavor-id') - - def test_pause(self): - # Ensure instance can be paused. - instance = self._create_instance_obj() - self.assertEqual(instance.vm_state, vm_states.ACTIVE) - self.assertIsNone(instance.task_state) - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, - '_record_action_start') - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - self.mox.StubOutWithMock(rpcapi, 'pause_instance') - - instance.save(expected_task_state=[None]) - self.compute_api._record_action_start(self.context, - instance, instance_actions.PAUSE) - rpcapi.pause_instance(self.context, instance) - - self.mox.ReplayAll() - - self.compute_api.pause(self.context, instance) - self.assertEqual(vm_states.ACTIVE, instance.vm_state) - self.assertEqual(task_states.PAUSING, - instance.task_state) - - def _test_pause_fails(self, vm_state): - params = dict(vm_state=vm_state) - instance = self._create_instance_obj(params=params) - self.assertIsNone(instance.task_state) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.pause, - self.context, instance) - - def test_pause_fails_invalid_states(self): - invalid_vm_states = self._get_vm_states(set([vm_states.ACTIVE])) - for state in invalid_vm_states: - self._test_pause_fails(state) - - def test_unpause(self): - # Ensure instance can be unpaused. - params = dict(vm_state=vm_states.PAUSED) - instance = self._create_instance_obj(params=params) - self.assertEqual(instance.vm_state, vm_states.PAUSED) - self.assertIsNone(instance.task_state) - - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api, - '_record_action_start') - if self.cell_type == 'api': - rpcapi = self.compute_api.cells_rpcapi - else: - rpcapi = self.compute_api.compute_rpcapi - self.mox.StubOutWithMock(rpcapi, 'unpause_instance') - - instance.save(expected_task_state=[None]) - self.compute_api._record_action_start(self.context, - instance, instance_actions.UNPAUSE) - rpcapi.unpause_instance(self.context, instance) - - self.mox.ReplayAll() - - self.compute_api.unpause(self.context, instance) - self.assertEqual(vm_states.PAUSED, instance.vm_state) - self.assertEqual(task_states.UNPAUSING, instance.task_state) - - def test_swap_volume_volume_api_usage(self): - # This test ensures that volume_id arguments are passed to volume_api - # and that volumes return to previous states in case of error. - def fake_vol_api_begin_detaching(context, volume_id): - self.assertTrue(uuidutils.is_uuid_like(volume_id)) - volumes[volume_id]['status'] = 'detaching' - - def fake_vol_api_roll_detaching(context, volume_id): - self.assertTrue(uuidutils.is_uuid_like(volume_id)) - if volumes[volume_id]['status'] == 'detaching': - volumes[volume_id]['status'] = 'in-use' - - def fake_vol_api_reserve(context, volume_id): - self.assertTrue(uuidutils.is_uuid_like(volume_id)) - self.assertEqual(volumes[volume_id]['status'], 'available') - volumes[volume_id]['status'] = 'attaching' - - def fake_vol_api_unreserve(context, volume_id): - self.assertTrue(uuidutils.is_uuid_like(volume_id)) - if volumes[volume_id]['status'] == 'attaching': - volumes[volume_id]['status'] = 'available' - - def fake_swap_volume_exc(context, instance, old_volume_id, - new_volume_id): - raise AttributeError # Random exception - - # Should fail if VM state is not valid - instance = {'vm_state': vm_states.BUILDING, - 'launched_at': timeutils.utcnow(), - 'locked': False, - 'availability_zone': 'fake_az', - 'uuid': 'fake'} - volumes = {} - old_volume_id = uuidutils.generate_uuid() - volumes[old_volume_id] = {'id': old_volume_id, - 'display_name': 'old_volume', - 'attach_status': 'attached', - 'instance_uuid': 'fake', - 'size': 5, - 'status': 'in-use'} - new_volume_id = uuidutils.generate_uuid() - volumes[new_volume_id] = {'id': new_volume_id, - 'display_name': 'new_volume', - 'attach_status': 'detached', - 'instance_uuid': None, - 'size': 5, - 'status': 'available'} - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.swap_volume, self.context, instance, - volumes[old_volume_id], volumes[new_volume_id]) - instance['vm_state'] = vm_states.ACTIVE - instance['task_state'] = None - - # Should fail if old volume is not attached - volumes[old_volume_id]['attach_status'] = 'detached' - self.assertRaises(exception.VolumeUnattached, - self.compute_api.swap_volume, self.context, instance, - volumes[old_volume_id], volumes[new_volume_id]) - self.assertEqual(volumes[old_volume_id]['status'], 'in-use') - self.assertEqual(volumes[new_volume_id]['status'], 'available') - volumes[old_volume_id]['attach_status'] = 'attached' - - # Should fail if old volume's instance_uuid is not that of the instance - volumes[old_volume_id]['instance_uuid'] = 'fake2' - self.assertRaises(exception.InvalidVolume, - self.compute_api.swap_volume, self.context, instance, - volumes[old_volume_id], volumes[new_volume_id]) - self.assertEqual(volumes[old_volume_id]['status'], 'in-use') - self.assertEqual(volumes[new_volume_id]['status'], 'available') - volumes[old_volume_id]['instance_uuid'] = 'fake' - - # Should fail if new volume is attached - volumes[new_volume_id]['attach_status'] = 'attached' - self.assertRaises(exception.InvalidVolume, - self.compute_api.swap_volume, self.context, instance, - volumes[old_volume_id], volumes[new_volume_id]) - self.assertEqual(volumes[old_volume_id]['status'], 'in-use') - self.assertEqual(volumes[new_volume_id]['status'], 'available') - volumes[new_volume_id]['attach_status'] = 'detached' - - # Should fail if new volume is smaller than the old volume - volumes[new_volume_id]['size'] = 4 - self.assertRaises(exception.InvalidVolume, - self.compute_api.swap_volume, self.context, instance, - volumes[old_volume_id], volumes[new_volume_id]) - self.assertEqual(volumes[old_volume_id]['status'], 'in-use') - self.assertEqual(volumes[new_volume_id]['status'], 'available') - volumes[new_volume_id]['size'] = 5 - - # Fail call to swap_volume - self.stubs.Set(self.compute_api.volume_api, 'begin_detaching', - fake_vol_api_begin_detaching) - self.stubs.Set(self.compute_api.volume_api, 'roll_detaching', - fake_vol_api_roll_detaching) - self.stubs.Set(self.compute_api.volume_api, 'reserve_volume', - fake_vol_api_reserve) - self.stubs.Set(self.compute_api.volume_api, 'unreserve_volume', - fake_vol_api_unreserve) - self.stubs.Set(self.compute_api.compute_rpcapi, 'swap_volume', - fake_swap_volume_exc) - self.assertRaises(AttributeError, - self.compute_api.swap_volume, self.context, instance, - volumes[old_volume_id], volumes[new_volume_id]) - self.assertEqual(volumes[old_volume_id]['status'], 'in-use') - self.assertEqual(volumes[new_volume_id]['status'], 'available') - - # Should succeed - self.stubs.Set(self.compute_api.compute_rpcapi, 'swap_volume', - lambda c, instance, old_volume_id, new_volume_id: True) - self.compute_api.swap_volume(self.context, instance, - volumes[old_volume_id], - volumes[new_volume_id]) - - def _test_snapshot_and_backup(self, is_snapshot=True, - with_base_ref=False, min_ram=None, - min_disk=None, - create_fails=False, - instance_vm_state=vm_states.ACTIVE): - # 'cache_in_nova' is for testing non-inheritable properties - # 'user_id' should also not be carried from sys_meta into - # image property...since it should be set explicitly by - # _create_image() in compute api. - fake_sys_meta = dict(image_foo='bar', blah='bug?', - image_cache_in_nova='dropped', - cache_in_nova='dropped', - user_id='meow') - if with_base_ref: - fake_sys_meta['image_base_image_ref'] = 'fake-base-ref' - params = dict(system_metadata=fake_sys_meta, locked=True) - instance = self._create_instance_obj(params=params) - instance.vm_state = instance_vm_state - fake_sys_meta.update(instance.system_metadata) - extra_props = dict(cow='moo', cat='meow') - - self.mox.StubOutWithMock(compute_utils, 'get_image_metadata') - self.mox.StubOutWithMock(self.compute_api.image_api, - 'create') - self.mox.StubOutWithMock(instance, 'save') - self.mox.StubOutWithMock(self.compute_api.compute_rpcapi, - 'snapshot_instance') - self.mox.StubOutWithMock(self.compute_api.compute_rpcapi, - 'backup_instance') - - image_type = is_snapshot and 'snapshot' or 'backup' - - expected_sys_meta = dict(fake_sys_meta) - expected_sys_meta.pop('cache_in_nova') - expected_sys_meta.pop('image_cache_in_nova') - expected_sys_meta.pop('user_id') - expected_sys_meta['foo'] = expected_sys_meta.pop('image_foo') - if with_base_ref: - expected_sys_meta['base_image_ref'] = expected_sys_meta.pop( - 'image_base_image_ref') - - expected_props = {'instance_uuid': instance.uuid, - 'user_id': self.context.user_id, - 'image_type': image_type} - expected_props.update(extra_props) - expected_props.update(expected_sys_meta) - expected_meta = {'name': 'fake-name', - 'is_public': False, - 'properties': expected_props} - if is_snapshot: - if min_ram is not None: - expected_meta['min_ram'] = min_ram - if min_disk is not None: - expected_meta['min_disk'] = min_disk - else: - expected_props['backup_type'] = 'fake-backup-type' - - compute_utils.get_image_metadata( - self.context, self.compute_api.image_api, - FAKE_IMAGE_REF, instance).AndReturn(expected_meta) - - fake_image = dict(id='fake-image-id') - mock_method = self.compute_api.image_api.create( - self.context, expected_meta) - if create_fails: - mock_method.AndRaise(test.TestingException()) - else: - mock_method.AndReturn(fake_image) - - def check_state(expected_task_state=None): - expected_state = (is_snapshot and - task_states.IMAGE_SNAPSHOT_PENDING or - task_states.IMAGE_BACKUP) - self.assertEqual(expected_state, instance.task_state) - - if not create_fails: - instance.save(expected_task_state=[None]).WithSideEffects( - check_state) - if is_snapshot: - self.compute_api.compute_rpcapi.snapshot_instance( - self.context, instance, fake_image['id']) - else: - self.compute_api.compute_rpcapi.backup_instance( - self.context, instance, fake_image['id'], - 'fake-backup-type', 'fake-rotation') - - self.mox.ReplayAll() - - got_exc = False - try: - if is_snapshot: - res = self.compute_api.snapshot(self.context, instance, - 'fake-name', - extra_properties=extra_props) - else: - res = self.compute_api.backup(self.context, instance, - 'fake-name', - 'fake-backup-type', - 'fake-rotation', - extra_properties=extra_props) - self.assertEqual(fake_image, res) - except test.TestingException: - got_exc = True - self.assertEqual(create_fails, got_exc) - self.mox.UnsetStubs() - - def test_snapshot(self): - self._test_snapshot_and_backup() - - def test_snapshot_fails(self): - self._test_snapshot_and_backup(create_fails=True) - - def test_snapshot_invalid_state(self): - instance = self._create_instance_obj() - instance.vm_state = vm_states.ACTIVE - instance.task_state = task_states.IMAGE_SNAPSHOT - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.snapshot, - self.context, instance, 'fake-name') - instance.vm_state = vm_states.ACTIVE - instance.task_state = task_states.IMAGE_BACKUP - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.snapshot, - self.context, instance, 'fake-name') - instance.vm_state = vm_states.BUILDING - instance.task_state = None - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.snapshot, - self.context, instance, 'fake-name') - - def test_snapshot_with_base_image_ref(self): - self._test_snapshot_and_backup(with_base_ref=True) - - def test_snapshot_min_ram(self): - self._test_snapshot_and_backup(min_ram=42) - - def test_snapshot_min_disk(self): - self._test_snapshot_and_backup(min_disk=42) - - def test_backup(self): - for state in [vm_states.ACTIVE, vm_states.STOPPED, - vm_states.PAUSED, vm_states.SUSPENDED]: - self._test_snapshot_and_backup(is_snapshot=False, - instance_vm_state=state) - - def test_backup_fails(self): - self._test_snapshot_and_backup(is_snapshot=False, create_fails=True) - - def test_backup_invalid_state(self): - instance = self._create_instance_obj() - instance.vm_state = vm_states.ACTIVE - instance.task_state = task_states.IMAGE_SNAPSHOT - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.backup, - self.context, instance, 'fake-name', - 'fake', 'fake') - instance.vm_state = vm_states.ACTIVE - instance.task_state = task_states.IMAGE_BACKUP - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.backup, - self.context, instance, 'fake-name', - 'fake', 'fake') - instance.vm_state = vm_states.BUILDING - instance.task_state = None - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.backup, - self.context, instance, 'fake-name', - 'fake', 'fake') - - def test_backup_with_base_image_ref(self): - self._test_snapshot_and_backup(is_snapshot=False, - with_base_ref=True) - - def test_snapshot_volume_backed(self): - params = dict(locked=True) - instance = self._create_instance_obj(params=params) - instance['root_device_name'] = 'vda' - - instance_bdms = [] - - image_meta = { - 'id': 'fake-image-id', - 'properties': {'mappings': []}, - 'status': 'fake-status', - 'location': 'far-away', - 'owner': 'fake-tenant', - } - - expect_meta = { - 'name': 'test-snapshot', - 'properties': {'root_device_name': 'vda', - 'mappings': 'DONTCARE'}, - 'size': 0, - 'is_public': False - } - - def fake_get_all_by_instance(context, instance, use_slave=False): - return copy.deepcopy(instance_bdms) - - def fake_image_create(context, image_meta, data=None): - self.assertThat(image_meta, matchers.DictMatches(expect_meta)) - - def fake_volume_get(context, volume_id): - return {'id': volume_id, 'display_description': ''} - - def fake_volume_create_snapshot(context, volume_id, name, description): - return {'id': '%s-snapshot' % volume_id} - - self.stubs.Set(db, 'block_device_mapping_get_all_by_instance', - fake_get_all_by_instance) - self.stubs.Set(self.compute_api.image_api, 'create', - fake_image_create) - self.stubs.Set(self.compute_api.volume_api, 'get', - fake_volume_get) - self.stubs.Set(self.compute_api.volume_api, 'create_snapshot_force', - fake_volume_create_snapshot) - - # No block devices defined - self.compute_api.snapshot_volume_backed( - self.context, instance, copy.deepcopy(image_meta), 'test-snapshot') - - bdm = fake_block_device.FakeDbBlockDeviceDict( - {'no_device': False, 'volume_id': '1', 'boot_index': 0, - 'connection_info': 'inf', 'device_name': '/dev/vda', - 'source_type': 'volume', 'destination_type': 'volume'}) - instance_bdms.append(bdm) - - expect_meta['properties']['bdm_v2'] = True - expect_meta['properties']['block_device_mapping'] = [] - expect_meta['properties']['block_device_mapping'].append( - {'guest_format': None, 'boot_index': 0, 'no_device': None, - 'image_id': None, 'volume_id': None, 'disk_bus': None, - 'volume_size': None, 'source_type': 'snapshot', - 'device_type': None, 'snapshot_id': '1-snapshot', - 'destination_type': 'volume', 'delete_on_termination': None}) - - # All the db_only fields and the volume ones are removed - self.compute_api.snapshot_volume_backed( - self.context, instance, copy.deepcopy(image_meta), 'test-snapshot') - - image_mappings = [{'virtual': 'ami', 'device': 'vda'}, - {'device': 'vda', 'virtual': 'ephemeral0'}, - {'device': 'vdb', 'virtual': 'swap'}, - {'device': 'vdc', 'virtual': 'ephemeral1'}] - - image_meta['properties']['mappings'] = image_mappings - - expect_meta['properties']['mappings'] = [ - {'virtual': 'ami', 'device': 'vda'}] - - # Check that the mappgins from the image properties are included - self.compute_api.snapshot_volume_backed( - self.context, instance, copy.deepcopy(image_meta), 'test-snapshot') - - def test_volume_snapshot_create(self): - volume_id = '1' - create_info = {'id': 'eyedee'} - fake_bdm = fake_block_device.FakeDbBlockDeviceDict({ - 'id': 123, - 'device_name': '/dev/sda2', - 'source_type': 'volume', - 'destination_type': 'volume', - 'connection_info': "{'fake': 'connection_info'}", - 'volume_id': 1, - 'boot_index': -1}) - fake_bdm['instance'] = fake_instance.fake_db_instance() - fake_bdm['instance_uuid'] = fake_bdm['instance']['uuid'] - fake_bdm = objects.BlockDeviceMapping._from_db_object( - self.context, objects.BlockDeviceMapping(), - fake_bdm, expected_attrs=['instance']) - - self.mox.StubOutWithMock(objects.BlockDeviceMapping, - 'get_by_volume_id') - self.mox.StubOutWithMock(self.compute_api.compute_rpcapi, - 'volume_snapshot_create') - - objects.BlockDeviceMapping.get_by_volume_id( - self.context, volume_id, - expected_attrs=['instance']).AndReturn(fake_bdm) - self.compute_api.compute_rpcapi.volume_snapshot_create(self.context, - fake_bdm['instance'], volume_id, create_info) - - self.mox.ReplayAll() - - snapshot = self.compute_api.volume_snapshot_create(self.context, - volume_id, create_info) - - expected_snapshot = { - 'snapshot': { - 'id': create_info['id'], - 'volumeId': volume_id, - }, - } - self.assertEqual(snapshot, expected_snapshot) - - def test_volume_snapshot_delete(self): - volume_id = '1' - snapshot_id = '2' - fake_bdm = fake_block_device.FakeDbBlockDeviceDict({ - 'id': 123, - 'device_name': '/dev/sda2', - 'source_type': 'volume', - 'destination_type': 'volume', - 'connection_info': "{'fake': 'connection_info'}", - 'volume_id': 1, - 'boot_index': -1}) - fake_bdm['instance'] = fake_instance.fake_db_instance() - fake_bdm['instance_uuid'] = fake_bdm['instance']['uuid'] - fake_bdm = objects.BlockDeviceMapping._from_db_object( - self.context, objects.BlockDeviceMapping(), - fake_bdm, expected_attrs=['instance']) - - self.mox.StubOutWithMock(objects.BlockDeviceMapping, - 'get_by_volume_id') - self.mox.StubOutWithMock(self.compute_api.compute_rpcapi, - 'volume_snapshot_delete') - - objects.BlockDeviceMapping.get_by_volume_id( - self.context, volume_id, - expected_attrs=['instance']).AndReturn(fake_bdm) - self.compute_api.compute_rpcapi.volume_snapshot_delete(self.context, - fake_bdm['instance'], volume_id, snapshot_id, {}) - - self.mox.ReplayAll() - - self.compute_api.volume_snapshot_delete(self.context, volume_id, - snapshot_id, {}) - - def _test_boot_volume_bootable(self, is_bootable=False): - def get_vol_data(*args, **kwargs): - return {'bootable': is_bootable} - block_device_mapping = [{ - 'id': 1, - 'device_name': 'vda', - 'no_device': None, - 'virtual_name': None, - 'snapshot_id': None, - 'volume_id': '1', - 'delete_on_termination': False, - }] - - expected_meta = {'min_disk': 0, 'min_ram': 0, 'properties': {}, - 'size': 0, 'status': 'active'} - - with mock.patch.object(self.compute_api.volume_api, 'get', - side_effect=get_vol_data): - if not is_bootable: - self.assertRaises(exception.InvalidBDMVolumeNotBootable, - self.compute_api._get_bdm_image_metadata, - self.context, block_device_mapping) - else: - meta = self.compute_api._get_bdm_image_metadata(self.context, - block_device_mapping) - self.assertEqual(expected_meta, meta) - - def test_boot_volume_non_bootable(self): - self._test_boot_volume_bootable(False) - - def test_boot_volume_bootable(self): - self._test_boot_volume_bootable(True) - - def test_boot_volume_basic_property(self): - block_device_mapping = [{ - 'id': 1, - 'device_name': 'vda', - 'no_device': None, - 'virtual_name': None, - 'snapshot_id': None, - 'volume_id': '1', - 'delete_on_termination': False, - }] - fake_volume = {"volume_image_metadata": - {"min_ram": 256, "min_disk": 128, "foo": "bar"}} - with mock.patch.object(self.compute_api.volume_api, 'get', - return_value=fake_volume): - meta = self.compute_api._get_bdm_image_metadata( - self.context, block_device_mapping) - self.assertEqual(256, meta['min_ram']) - self.assertEqual(128, meta['min_disk']) - self.assertEqual('active', meta['status']) - self.assertEqual('bar', meta['properties']['foo']) - - def test_boot_volume_snapshot_basic_property(self): - block_device_mapping = [{ - 'id': 1, - 'device_name': 'vda', - 'no_device': None, - 'virtual_name': None, - 'snapshot_id': '2', - 'volume_id': None, - 'delete_on_termination': False, - }] - fake_volume = {"volume_image_metadata": - {"min_ram": 256, "min_disk": 128, "foo": "bar"}} - fake_snapshot = {"volume_id": "1"} - with contextlib.nested( - mock.patch.object(self.compute_api.volume_api, 'get', - return_value=fake_volume), - mock.patch.object(self.compute_api.volume_api, 'get_snapshot', - return_value=fake_snapshot)) as ( - volume_get, volume_get_snapshot): - meta = self.compute_api._get_bdm_image_metadata( - self.context, block_device_mapping) - self.assertEqual(256, meta['min_ram']) - self.assertEqual(128, meta['min_disk']) - self.assertEqual('active', meta['status']) - self.assertEqual('bar', meta['properties']['foo']) - volume_get_snapshot.assert_called_once_with(self.context, - block_device_mapping[0]['snapshot_id']) - volume_get.assert_called_once_with(self.context, - fake_snapshot['volume_id']) - - def _create_instance_with_disabled_disk_config(self, object=False): - sys_meta = {"image_auto_disk_config": "Disabled"} - params = {"system_metadata": sys_meta} - instance = self._create_instance_obj(params=params) - if object: - return instance - return obj_base.obj_to_primitive(instance) - - def _setup_fake_image_with_disabled_disk_config(self): - self.fake_image = { - 'id': 1, - 'name': 'fake_name', - 'status': 'active', - 'properties': {"auto_disk_config": "Disabled"}, - } - - def fake_show(obj, context, image_id, **kwargs): - return self.fake_image - fake_image.stub_out_image_service(self.stubs) - self.stubs.Set(fake_image._FakeImageService, 'show', fake_show) - return self.fake_image['id'] - - def test_resize_with_disabled_auto_disk_config_fails(self): - fake_inst = self._create_instance_with_disabled_disk_config() - - self.assertRaises(exception.AutoDiskConfigDisabledByImage, - self.compute_api.resize, - self.context, fake_inst, - auto_disk_config=True) - - def test_create_with_disabled_auto_disk_config_fails(self): - image_id = self._setup_fake_image_with_disabled_disk_config() - - self.assertRaises(exception.AutoDiskConfigDisabledByImage, - self.compute_api.create, self.context, - "fake_flavor", image_id, auto_disk_config=True) - - def test_rebuild_with_disabled_auto_disk_config_fails(self): - fake_inst = self._create_instance_with_disabled_disk_config( - object=True) - image_id = self._setup_fake_image_with_disabled_disk_config() - self.assertRaises(exception.AutoDiskConfigDisabledByImage, - self.compute_api.rebuild, - self.context, - fake_inst, - image_id, - "new password", - auto_disk_config=True) - - @mock.patch.object(objects.Instance, 'save') - @mock.patch.object(objects.Instance, 'get_flavor') - @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid') - @mock.patch.object(compute_api.API, '_get_image') - @mock.patch.object(compute_api.API, '_check_auto_disk_config') - @mock.patch.object(compute_api.API, '_checks_for_create_and_rebuild') - @mock.patch.object(compute_api.API, '_record_action_start') - def test_rebuild(self, _record_action_start, - _checks_for_create_and_rebuild, _check_auto_disk_config, - _get_image, bdm_get_by_instance_uuid, get_flavor, instance_save): - orig_system_metadata = {} - instance = fake_instance.fake_instance_obj(self.context, - vm_state=vm_states.ACTIVE, cell_name='fake-cell', - launched_at=timeutils.utcnow(), - system_metadata=orig_system_metadata, - expected_attrs=['system_metadata']) - get_flavor.return_value = test_flavor.fake_flavor - flavor = instance.get_flavor() - image_href = '' - image = {"min_ram": 10, "min_disk": 1, - "properties": {'architecture': arch.X86_64}} - admin_pass = '' - files_to_inject = [] - bdms = [] - - _get_image.return_value = (None, image) - bdm_get_by_instance_uuid.return_value = bdms - - with mock.patch.object(self.compute_api.compute_task_api, - 'rebuild_instance') as rebuild_instance: - self.compute_api.rebuild(self.context, instance, image_href, - admin_pass, files_to_inject) - - rebuild_instance.assert_called_once_with(self.context, - instance=instance, new_pass=admin_pass, - injected_files=files_to_inject, image_ref=image_href, - orig_image_ref=image_href, - orig_sys_metadata=orig_system_metadata, bdms=bdms, - preserve_ephemeral=False, host=instance.host, kwargs={}) - - _check_auto_disk_config.assert_called_once_with(image=image) - _checks_for_create_and_rebuild.assert_called_once_with(self.context, - None, image, flavor, {}, []) - self.assertNotEqual(orig_system_metadata, instance.system_metadata) - - @mock.patch.object(objects.Instance, 'save') - @mock.patch.object(objects.Instance, 'get_flavor') - @mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid') - @mock.patch.object(compute_api.API, '_get_image') - @mock.patch.object(compute_api.API, '_check_auto_disk_config') - @mock.patch.object(compute_api.API, '_checks_for_create_and_rebuild') - @mock.patch.object(compute_api.API, '_record_action_start') - def test_rebuild_change_image(self, _record_action_start, - _checks_for_create_and_rebuild, _check_auto_disk_config, - _get_image, bdm_get_by_instance_uuid, get_flavor, instance_save): - orig_system_metadata = {} - get_flavor.return_value = test_flavor.fake_flavor - orig_image_href = 'orig_image' - orig_image = {"min_ram": 10, "min_disk": 1, - "properties": {'architecture': arch.X86_64, - 'vm_mode': 'hvm'}} - new_image_href = 'new_image' - new_image = {"min_ram": 10, "min_disk": 1, - "properties": {'architecture': arch.X86_64, - 'vm_mode': 'xen'}} - admin_pass = '' - files_to_inject = [] - bdms = [] - - instance = fake_instance.fake_instance_obj(self.context, - vm_state=vm_states.ACTIVE, cell_name='fake-cell', - launched_at=timeutils.utcnow(), - system_metadata=orig_system_metadata, - expected_attrs=['system_metadata'], - image_ref=orig_image_href, - vm_mode=vm_mode.HVM) - flavor = instance.get_flavor() - - def get_image(context, image_href): - if image_href == new_image_href: - return (None, new_image) - if image_href == orig_image_href: - return (None, orig_image) - _get_image.side_effect = get_image - bdm_get_by_instance_uuid.return_value = bdms - - with mock.patch.object(self.compute_api.compute_task_api, - 'rebuild_instance') as rebuild_instance: - self.compute_api.rebuild(self.context, instance, new_image_href, - admin_pass, files_to_inject) - - rebuild_instance.assert_called_once_with(self.context, - instance=instance, new_pass=admin_pass, - injected_files=files_to_inject, image_ref=new_image_href, - orig_image_ref=orig_image_href, - orig_sys_metadata=orig_system_metadata, bdms=bdms, - preserve_ephemeral=False, host=instance.host, kwargs={}) - - _check_auto_disk_config.assert_called_once_with(image=new_image) - _checks_for_create_and_rebuild.assert_called_once_with(self.context, - None, new_image, flavor, {}, []) - self.assertEqual(vm_mode.XEN, instance.vm_mode) - - def _test_check_injected_file_quota_onset_file_limit_exceeded(self, - side_effect): - injected_files = [ - { - "path": "/etc/banner.txt", - "contents": "foo" - } - ] - with mock.patch.object(quota.QUOTAS, 'limit_check', - side_effect=side_effect): - self.compute_api._check_injected_file_quota( - self.context, injected_files) - - def test_check_injected_file_quota_onset_file_limit_exceeded(self): - # This is the first call to limit_check. - side_effect = exception.OverQuota(overs='injected_files') - self.assertRaises(exception.OnsetFileLimitExceeded, - self._test_check_injected_file_quota_onset_file_limit_exceeded, - side_effect) - - def test_check_injected_file_quota_onset_file_path_limit(self): - # This is the second call to limit_check. - side_effect = (mock.DEFAULT, - exception.OverQuota(overs='injected_file_path_bytes')) - self.assertRaises(exception.OnsetFilePathLimitExceeded, - self._test_check_injected_file_quota_onset_file_limit_exceeded, - side_effect) - - def test_check_injected_file_quota_onset_file_content_limit(self): - # This is the second call to limit_check but with different overs. - side_effect = (mock.DEFAULT, - exception.OverQuota(overs='injected_file_content_bytes')) - self.assertRaises(exception.OnsetFileContentLimitExceeded, - self._test_check_injected_file_quota_onset_file_limit_exceeded, - side_effect) - - @mock.patch('nova.objects.Quotas.commit') - @mock.patch('nova.objects.Quotas.reserve') - @mock.patch('nova.objects.Instance.save') - @mock.patch('nova.objects.InstanceAction.action_start') - def test_restore(self, action_start, instance_save, quota_reserve, - quota_commit): - instance = self._create_instance_obj() - instance.vm_state = vm_states.SOFT_DELETED - instance.task_state = None - instance.save() - with mock.patch.object(self.compute_api, 'compute_rpcapi') as rpc: - self.compute_api.restore(self.context, instance) - rpc.restore_instance.assert_called_once_with(self.context, - instance) - self.assertEqual(instance.task_state, task_states.RESTORING) - self.assertEqual(1, quota_commit.call_count) - - def test_external_instance_event(self): - instances = [ - objects.Instance(uuid='uuid1', host='host1'), - objects.Instance(uuid='uuid2', host='host1'), - objects.Instance(uuid='uuid3', host='host2'), - ] - events = [ - objects.InstanceExternalEvent(instance_uuid='uuid1'), - objects.InstanceExternalEvent(instance_uuid='uuid2'), - objects.InstanceExternalEvent(instance_uuid='uuid3'), - ] - self.compute_api.compute_rpcapi = mock.MagicMock() - self.compute_api.external_instance_event(self.context, - instances, events) - method = self.compute_api.compute_rpcapi.external_instance_event - method.assert_any_call(self.context, instances[0:2], events[0:2]) - method.assert_any_call(self.context, instances[2:], events[2:]) - self.assertEqual(2, method.call_count) - - def test_volume_ops_invalid_task_state(self): - instance = self._create_instance_obj() - self.assertEqual(instance.vm_state, vm_states.ACTIVE) - instance.task_state = 'Any' - volume_id = uuidutils.generate_uuid() - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.attach_volume, - self.context, instance, volume_id) - - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.detach_volume, - self.context, instance, volume_id) - - new_volume_id = uuidutils.generate_uuid() - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.swap_volume, - self.context, instance, - volume_id, new_volume_id) - - @mock.patch.object(cinder.API, 'get', - side_effect=exception.CinderConnectionFailed(reason='error')) - def test_get_bdm_image_metadata_with_cinder_down(self, mock_get): - bdms = [objects.BlockDeviceMapping( - **fake_block_device.FakeDbBlockDeviceDict( - { - 'id': 1, - 'volume_id': 1, - 'source_type': 'volume', - 'destination_type': 'volume', - 'device_name': 'vda', - }))] - self.assertRaises(exception.CinderConnectionFailed, - self.compute_api._get_bdm_image_metadata, - self.context, - bdms, legacy_bdm=True) - - @mock.patch.object(cinder.API, 'get') - @mock.patch.object(cinder.API, 'check_attach', - side_effect=exception.InvalidVolume(reason='error')) - def test_validate_bdm_with_error_volume(self, mock_check_attach, mock_get): - # Tests that an InvalidVolume exception raised from - # volume_api.check_attach due to the volume status not being - # 'available' results in _validate_bdm re-raising InvalidVolume. - instance = self._create_instance_obj() - instance_type = self._create_flavor() - volume_id = 'e856840e-9f5b-4894-8bde-58c6e29ac1e8' - volume_info = {'status': 'error', - 'attach_status': 'detached', - 'id': volume_id} - mock_get.return_value = volume_info - bdms = [objects.BlockDeviceMapping( - **fake_block_device.FakeDbBlockDeviceDict( - { - 'boot_index': 0, - 'volume_id': volume_id, - 'source_type': 'volume', - 'destination_type': 'volume', - 'device_name': 'vda', - }))] - - self.assertRaises(exception.InvalidVolume, - self.compute_api._validate_bdm, - self.context, - instance, instance_type, bdms) - - mock_get.assert_called_once_with(self.context, volume_id) - mock_check_attach.assert_called_once_with( - self.context, volume_info, instance=instance) - - @mock.patch.object(cinder.API, 'get_snapshot', - side_effect=exception.CinderConnectionFailed(reason='error')) - @mock.patch.object(cinder.API, 'get', - side_effect=exception.CinderConnectionFailed(reason='error')) - def test_validate_bdm_with_cinder_down(self, mock_get, mock_get_snapshot): - instance = self._create_instance_obj() - instance_type = self._create_flavor() - bdm = [objects.BlockDeviceMapping( - **fake_block_device.FakeDbBlockDeviceDict( - { - 'id': 1, - 'volume_id': 1, - 'source_type': 'volume', - 'destination_type': 'volume', - 'device_name': 'vda', - 'boot_index': 0, - }))] - bdms = [objects.BlockDeviceMapping( - **fake_block_device.FakeDbBlockDeviceDict( - { - 'id': 1, - 'snapshot_id': 1, - 'source_type': 'volume', - 'destination_type': 'volume', - 'device_name': 'vda', - 'boot_index': 0, - }))] - self.assertRaises(exception.CinderConnectionFailed, - self.compute_api._validate_bdm, - self.context, - instance, instance_type, bdm) - self.assertRaises(exception.CinderConnectionFailed, - self.compute_api._validate_bdm, - self.context, - instance, instance_type, bdms) - - def _test_create_db_entry_for_new_instance_with_cinder_error(self, - expected_exception): - - @mock.patch.object(objects.Instance, 'create') - @mock.patch.object(compute_api.SecurityGroupAPI, 'ensure_default') - @mock.patch.object(compute_api.API, '_populate_instance_names') - @mock.patch.object(compute_api.API, '_populate_instance_for_create') - def do_test(self, mock_create, mock_names, mock_ensure, - mock_inst_create): - instance = self._create_instance_obj() - instance['display_name'] = 'FAKE_DISPLAY_NAME' - instance['shutdown_terminate'] = False - instance_type = self._create_flavor() - fake_image = { - 'id': 'fake-image-id', - 'properties': {'mappings': []}, - 'status': 'fake-status', - 'location': 'far-away'} - fake_security_group = None - fake_num_instances = 1 - fake_index = 1 - bdm = [objects.BlockDeviceMapping( - **fake_block_device.FakeDbBlockDeviceDict( - { - 'id': 1, - 'volume_id': 1, - 'source_type': 'volume', - 'destination_type': 'volume', - 'device_name': 'vda', - 'boot_index': 0, - }))] - with mock.patch.object(instance, "destroy") as destroy: - self.assertRaises(expected_exception, - self.compute_api. - create_db_entry_for_new_instance, - self.context, - instance_type, - fake_image, - instance, - fake_security_group, - bdm, - fake_num_instances, - fake_index) - destroy.assert_called_once_with(self.context) - - # We use a nested method so we can decorate with the mocks. - do_test(self) - - @mock.patch.object(cinder.API, 'get', - side_effect=exception.CinderConnectionFailed(reason='error')) - def test_create_db_entry_for_new_instancewith_cinder_down(self, mock_get): - self._test_create_db_entry_for_new_instance_with_cinder_error( - expected_exception=exception.CinderConnectionFailed) - - @mock.patch.object(cinder.API, 'get', - return_value={'id': 1, 'status': 'error', - 'attach_status': 'detached'}) - def test_create_db_entry_for_new_instancewith_error_volume(self, mock_get): - self._test_create_db_entry_for_new_instance_with_cinder_error( - expected_exception=exception.InvalidVolume) - - def _test_rescue(self, vm_state): - instance = self._create_instance_obj(params={'vm_state': vm_state}) - bdms = [] - with contextlib.nested( - mock.patch.object(objects.BlockDeviceMappingList, - 'get_by_instance_uuid', return_value=bdms), - mock.patch.object(self.compute_api, 'is_volume_backed_instance', - return_value=False), - mock.patch.object(instance, 'save'), - mock.patch.object(self.compute_api, '_record_action_start'), - mock.patch.object(self.compute_api.compute_rpcapi, - 'rescue_instance') - ) as ( - bdm_get_by_instance_uuid, volume_backed_inst, instance_save, - record_action_start, rpcapi_rescue_instance - ): - self.compute_api.rescue(self.context, instance) - # assert field values set on the instance object - self.assertEqual(task_states.RESCUING, instance.task_state) - # assert our mock calls - bdm_get_by_instance_uuid.assert_called_once_with( - self.context, instance.uuid) - volume_backed_inst.assert_called_once_with( - self.context, instance, bdms) - instance_save.assert_called_once_with(expected_task_state=[None]) - record_action_start.assert_called_once_with( - self.context, instance, instance_actions.RESCUE) - rpcapi_rescue_instance.assert_called_once_with( - self.context, instance=instance, rescue_password=None, - rescue_image_ref=None) - - def test_rescue_active(self): - self._test_rescue(vm_state=vm_states.ACTIVE) - - def test_rescue_stopped(self): - self._test_rescue(vm_state=vm_states.STOPPED) - - def test_rescue_error(self): - self._test_rescue(vm_state=vm_states.ERROR) - - def test_unrescue(self): - instance = self._create_instance_obj( - params={'vm_state': vm_states.RESCUED}) - with contextlib.nested( - mock.patch.object(instance, 'save'), - mock.patch.object(self.compute_api, '_record_action_start'), - mock.patch.object(self.compute_api.compute_rpcapi, - 'unrescue_instance') - ) as ( - instance_save, record_action_start, rpcapi_unrescue_instance - ): - self.compute_api.unrescue(self.context, instance) - # assert field values set on the instance object - self.assertEqual(task_states.UNRESCUING, instance.task_state) - # assert our mock calls - instance_save.assert_called_once_with(expected_task_state=[None]) - record_action_start.assert_called_once_with( - self.context, instance, instance_actions.UNRESCUE) - rpcapi_unrescue_instance.assert_called_once_with( - self.context, instance=instance) - - def test_set_admin_password_invalid_state(self): - # Tests that InstanceInvalidState is raised when not ACTIVE. - instance = self._create_instance_obj({'vm_state': vm_states.STOPPED}) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.set_admin_password, - self.context, instance) - - def test_set_admin_password(self): - # Ensure instance can have its admin password set. - instance = self._create_instance_obj() - - @mock.patch.object(objects.Instance, 'save') - @mock.patch.object(self.compute_api, '_record_action_start') - @mock.patch.object(self.compute_api.compute_rpcapi, - 'set_admin_password') - def do_test(compute_rpcapi_mock, record_mock, instance_save_mock): - # call the API - self.compute_api.set_admin_password(self.context, instance) - # make our assertions - instance_save_mock.assert_called_once_with( - expected_task_state=[None]) - record_mock.assert_called_once_with( - self.context, instance, instance_actions.CHANGE_PASSWORD) - compute_rpcapi_mock.assert_called_once_with( - self.context, instance=instance, new_pass=None) - - do_test() - - def _test_attach_interface_invalid_state(self, state): - instance = self._create_instance_obj( - params={'vm_state': state}) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.attach_interface, - self.context, instance, '', '', '', []) - - def test_attach_interface_invalid_state(self): - for state in [vm_states.BUILDING, vm_states.DELETED, - vm_states.ERROR, vm_states.RESCUED, - vm_states.RESIZED, vm_states.SOFT_DELETED, - vm_states.SUSPENDED, vm_states.SHELVED, - vm_states.SHELVED_OFFLOADED]: - self._test_attach_interface_invalid_state(state) - - def _test_detach_interface_invalid_state(self, state): - instance = self._create_instance_obj( - params={'vm_state': state}) - self.assertRaises(exception.InstanceInvalidState, - self.compute_api.detach_interface, - self.context, instance, '', '', '', []) - - def test_detach_interface_invalid_state(self): - for state in [vm_states.BUILDING, vm_states.DELETED, - vm_states.ERROR, vm_states.RESCUED, - vm_states.RESIZED, vm_states.SOFT_DELETED, - vm_states.SUSPENDED, vm_states.SHELVED, - vm_states.SHELVED_OFFLOADED]: - self._test_detach_interface_invalid_state(state) - - -class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase): - def setUp(self): - super(ComputeAPIUnitTestCase, self).setUp() - self.compute_api = compute_api.API() - self.cell_type = None - - def test_resize_same_flavor_fails(self): - self.assertRaises(exception.CannotResizeToSameFlavor, - self._test_resize, same_flavor=True) - - -class ComputeAPIAPICellUnitTestCase(_ComputeAPIUnitTestMixIn, - test.NoDBTestCase): - def setUp(self): - super(ComputeAPIAPICellUnitTestCase, self).setUp() - self.flags(cell_type='api', enable=True, group='cells') - self.compute_api = compute_cells_api.ComputeCellsAPI() - self.cell_type = 'api' - - def test_resize_same_flavor_fails(self): - self.assertRaises(exception.CannotResizeToSameFlavor, - self._test_resize, same_flavor=True) - - -class ComputeAPIComputeCellUnitTestCase(_ComputeAPIUnitTestMixIn, - test.NoDBTestCase): - def setUp(self): - super(ComputeAPIComputeCellUnitTestCase, self).setUp() - self.flags(cell_type='compute', enable=True, group='cells') - self.compute_api = compute_api.API() - self.cell_type = 'compute' - - def test_resize_same_flavor_passes(self): - self._test_resize(same_flavor=True) - - -class DiffDictTestCase(test.NoDBTestCase): - """Unit tests for _diff_dict().""" - - def test_no_change(self): - old = dict(a=1, b=2, c=3) - new = dict(a=1, b=2, c=3) - diff = compute_api._diff_dict(old, new) - - self.assertEqual(diff, {}) - - def test_new_key(self): - old = dict(a=1, b=2, c=3) - new = dict(a=1, b=2, c=3, d=4) - diff = compute_api._diff_dict(old, new) - - self.assertEqual(diff, dict(d=['+', 4])) - - def test_changed_key(self): - old = dict(a=1, b=2, c=3) - new = dict(a=1, b=4, c=3) - diff = compute_api._diff_dict(old, new) - - self.assertEqual(diff, dict(b=['+', 4])) - - def test_removed_key(self): - old = dict(a=1, b=2, c=3) - new = dict(a=1, c=3) - diff = compute_api._diff_dict(old, new) - - self.assertEqual(diff, dict(b=['-'])) - - -class SecurityGroupAPITest(test.NoDBTestCase): - def setUp(self): - super(SecurityGroupAPITest, self).setUp() - self.secgroup_api = compute_api.SecurityGroupAPI() - self.user_id = 'fake' - self.project_id = 'fake' - self.context = context.RequestContext(self.user_id, - self.project_id) - - @mock.patch('nova.objects.security_group.SecurityGroupList.' - 'get_by_instance') - def test_get_instance_security_groups(self, mock_get): - groups = objects.SecurityGroupList() - groups.objects = [objects.SecurityGroup(name='foo'), - objects.SecurityGroup(name='bar')] - mock_get.return_value = groups - names = self.secgroup_api.get_instance_security_groups(self.context, - 'fake-uuid') - self.assertEqual([{'name': 'bar'}, {'name': 'foo'}], sorted(names)) - self.assertEqual(1, mock_get.call_count) - self.assertEqual('fake-uuid', mock_get.call_args_list[0][0][1].uuid) |