def create(self): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') db_imap = db.ec2_instance_create(self._context, self.uuid) self._from_db_object(self._context, self, db_imap)
class CellsComputeAPITestCase(test_compute.ComputeAPITestCase): def setUp(self): self.flags(use_neutron=False) super(CellsComputeAPITestCase, self).setUp() global ORIG_COMPUTE_API ORIG_COMPUTE_API = self.compute_api self.flags(enable=True, group='cells') def _fake_validate_cell(*args, **kwargs): return self.compute_api = compute_cells_api.ComputeCellsAPI() self.stubs.Set(self.compute_api, '_validate_cell', _fake_validate_cell) deploy_stubs(self.stubs, self.compute_api) def tearDown(self): global ORIG_COMPUTE_API self.compute_api = ORIG_COMPUTE_API super(CellsComputeAPITestCase, self).tearDown() def test_instance_metadata(self): self.skipTest("Test is incompatible with cells.") def _test_evacuate(self, force=None): @mock.patch.object(compute_api.API, 'evacuate') def _test(mock_evacuate): instance = objects.Instance(uuid=uuids.evacuate_instance, cell_name='fake_cell_name') dest_host = 'fake_cell_name@fakenode2' self.compute_api.evacuate(self.context, instance, host=dest_host, force=force) mock_evacuate.assert_called_once_with(self.context, instance, 'fakenode2', force=force) _test() def test_error_evacuate(self): self.skipTest("Test is incompatible with cells.") def test_create_instance_sets_system_metadata(self): self.skipTest("Test is incompatible with cells.") def test_create_saves_flavor(self): self.skipTest("Test is incompatible with cells.") def test_create_instance_associates_security_groups(self): self.skipTest("Test is incompatible with cells.") @mock.patch('nova.objects.quotas.Quotas.check_deltas') def test_create_instance_over_quota_during_recheck(self, check_deltas_mock): self.stub_out('nova.tests.unit.image.fake._FakeImageService.show', self.fake_show) # Simulate a race where the first check passes and the recheck fails. fake_quotas = {'instances': 5, 'cores': 10, 'ram': 4096} fake_headroom = {'instances': 5, 'cores': 10, 'ram': 4096} fake_usages = {'instances': 5, 'cores': 10, 'ram': 4096} exc = exception.OverQuota(overs=['instances'], quotas=fake_quotas, headroom=fake_headroom, usages=fake_usages) check_deltas_mock.side_effect = [None, exc] inst_type = flavors.get_default_flavor() # Try to create 3 instances. self.assertRaises(exception.QuotaError, self.compute_api.create, self.context, inst_type, self.fake_image['id'], min_count=3) project_id = self.context.project_id self.assertEqual(2, check_deltas_mock.call_count) call1 = mock.call(self.context, { 'instances': 3, 'cores': inst_type.vcpus * 3, 'ram': inst_type.memory_mb * 3 }, project_id, user_id=None, check_project_id=project_id, check_user_id=None) call2 = mock.call(self.context, { 'instances': 0, 'cores': 0, 'ram': 0 }, project_id, user_id=None, check_project_id=project_id, check_user_id=None) check_deltas_mock.assert_has_calls([call1, call2]) # Verify we removed the artifacts that were added after the first # quota check passed. instances = objects.InstanceList.get_all(self.context) self.assertEqual(0, len(instances)) build_requests = objects.BuildRequestList.get_all(self.context) self.assertEqual(0, len(build_requests)) @db_api.api_context_manager.reader def request_spec_get_all(context): return context.session.query(api_models.RequestSpec).all() request_specs = request_spec_get_all(self.context) self.assertEqual(0, len(request_specs)) instance_mappings = objects.InstanceMappingList.get_by_project_id( self.context, project_id) self.assertEqual(0, len(instance_mappings)) @mock.patch('nova.objects.quotas.Quotas.check_deltas') def test_create_instance_no_quota_recheck(self, check_deltas_mock): self.stub_out('nova.tests.unit.image.fake._FakeImageService.show', self.fake_show) # Disable recheck_quota. self.flags(recheck_quota=False, group='quota') inst_type = flavors.get_default_flavor() (refs, resv_id) = self.compute_api.create(self.context, inst_type, self.fake_image['id']) self.assertEqual(1, len(refs)) project_id = self.context.project_id # check_deltas should have been called only once. check_deltas_mock.assert_called_once_with(self.context, { 'instances': 1, 'cores': inst_type.vcpus, 'ram': inst_type.memory_mb }, project_id, user_id=None, check_project_id=project_id, check_user_id=None) @mock.patch.object(compute_api.API, '_local_delete') @mock.patch.object(compute_api.API, '_lookup_instance', return_value=(None, None)) def test_delete_instance_no_cell_instance_disappear( self, mock_lookup, mock_local_delete): inst = self._create_fake_instance_obj() @mock.patch.object(self.compute_api.cells_rpcapi, 'instance_delete_everywhere') def test(mock_inst_del): self.compute_api.delete(self.context, inst) mock_lookup.assert_called_once_with(self.context, inst.uuid) mock_inst_del.assert_called_once_with(self.context, inst, 'hard') self.assertFalse(mock_local_delete.called) test() @mock.patch.object(compute_api.API, '_local_delete') def _test_delete_instance_no_cell(self, method_name, mock_local_delete): cells_rpcapi = self.compute_api.cells_rpcapi inst = self._create_fake_instance_obj() delete_type = method_name == 'soft_delete' and 'soft' or 'hard' @mock.patch.object(cells_rpcapi, 'instance_delete_everywhere') @mock.patch.object(compute_api.API, '_lookup_instance', return_value=(None, inst)) def test(mock_lookup, mock_inst_del): self.stub_out('nova.network.api.deallocate_for_instance', lambda *a, **kw: None) getattr(self.compute_api, method_name)(self.context, inst) mock_lookup.assert_called_once_with(self.context, inst.uuid) mock_local_delete.assert_called_once_with(self.context, inst, mock.ANY, method_name, mock.ANY) mock_inst_del.assert_called_once_with(self.context, inst, delete_type) test() def test_delete_instance_no_cell_constraint_failure_does_not_loop(self): inst = self._create_fake_instance_obj() inst.cell_name = None inst.destroy = mock.MagicMock() inst.destroy.side_effect = exception.ObjectActionError(action='', reason='') inst.refresh = mock.MagicMock() @mock.patch.object(self.compute_api.cells_rpcapi, 'instance_delete_everywhere') @mock.patch.object(compute_api.API, '_lookup_instance', return_value=(None, inst)) def _test(_mock_lookup_inst, _mock_delete_everywhere): self.assertRaises(exception.ObjectActionError, self.compute_api.delete, self.context, inst) inst.destroy.assert_called_once_with() _test() def test_delete_instance_no_cell_constraint_failure_corrects_itself(self): def add_cell_name(context, instance, delete_type): instance.cell_name = 'fake_cell_name' inst = self._create_fake_instance_obj() inst.cell_name = None inst.destroy = mock.MagicMock() inst.destroy.side_effect = exception.ObjectActionError(action='', reason='') inst.refresh = mock.MagicMock() @mock.patch.object(compute_api.API, 'delete') @mock.patch.object(self.compute_api.cells_rpcapi, 'instance_delete_everywhere', side_effect=add_cell_name) @mock.patch.object(compute_api.API, '_lookup_instance', return_value=(None, inst)) def _test(_mock_lookup_inst, mock_delete_everywhere, mock_compute_delete): self.compute_api.delete(self.context, inst) inst.destroy.assert_called_once_with() mock_compute_delete.assert_called_once_with(self.context, inst) _test() def test_delete_instance_no_cell_destroy_fails_already_deleted(self): # If the instance.destroy() is reached during _local_delete, # it will raise ObjectActionError if the instance has already # been deleted by a instance_destroy_at_top, and instance.refresh() # will raise InstanceNotFound instance = objects.Instance(context=self.context, uuid=uuids.destroy_instance, cell_name=None, host=None) actionerror = exception.ObjectActionError(action='destroy', reason='') notfound = exception.InstanceNotFound(instance_id=instance.uuid) @mock.patch.object(compute_api.API, 'delete') @mock.patch.object(self.compute_api.cells_rpcapi, 'instance_delete_everywhere') @mock.patch.object(compute_api.API, '_local_delete', side_effect=actionerror) @mock.patch.object(instance, 'refresh', side_effect=notfound) @mock.patch.object(compute_api.API, '_lookup_instance', return_value=(None, instance)) def _test(_mock_lookup_instance, mock_refresh, mock_local_delete, mock_delete_everywhere, mock_compute_delete): self.compute_api.delete(self.context, instance) mock_delete_everywhere.assert_called_once_with( self.context, instance, 'hard') mock_local_delete.assert_called_once_with( self.context, instance, mock.ANY, 'delete', self.compute_api._do_delete) mock_refresh.assert_called_once_with() self.assertFalse(mock_compute_delete.called) _test() def test_delete_instance_no_cell_instance_not_found_already_deleted(self): # If anything in _local_delete accesses the instance causing a db # lookup before instance.destroy() is reached, if the instance has # already been deleted by a instance_destroy_at_top, # InstanceNotFound will be raised instance = objects.Instance(context=self.context, uuid=uuids.delete_instance, cell_name=None, host=None) notfound = exception.InstanceNotFound(instance_id=instance.uuid) @mock.patch.object(compute_api.API, 'delete') @mock.patch.object(self.compute_api.cells_rpcapi, 'instance_delete_everywhere') @mock.patch.object(compute_api.API, '_lookup_instance', return_value=(None, instance)) @mock.patch.object(compute_api.API, '_local_delete', side_effect=notfound) def _test(mock_local_delete, _mock_lookup, mock_delete_everywhere, mock_compute_delete): self.compute_api.delete(self.context, instance) mock_delete_everywhere.assert_called_once_with( self.context, instance, 'hard') mock_local_delete.assert_called_once_with( self.context, instance, mock.ANY, 'delete', self.compute_api._do_delete) self.assertFalse(mock_compute_delete.called) _test() def test_soft_delete_instance_no_cell(self): self._test_delete_instance_no_cell('soft_delete') def test_delete_instance_no_cell(self): self._test_delete_instance_no_cell('delete') def test_force_delete_instance_no_cell(self): self._test_delete_instance_no_cell('force_delete') @mock.patch.object(compute_api.API, '_delete_while_booting', side_effect=exception.ObjectActionError( action='delete', reason='host now set')) @mock.patch.object(compute_api.API, '_local_delete') @mock.patch.object(compute_api.API, '_lookup_instance') @mock.patch.object(compute_api.API, 'delete') def test_delete_instance_no_cell_then_cell(self, mock_delete, mock_lookup_instance, mock_local_delete, mock_delete_while_booting): # This checks the case where initially an instance has no cell_name, # and therefore no host, set but instance.destroy fails because # there is now a host. instance = self._create_fake_instance_obj() instance_with_cell = copy.deepcopy(instance) instance_with_cell.cell_name = 'foo' mock_lookup_instance.return_value = None, instance_with_cell cells_rpcapi = self.compute_api.cells_rpcapi @mock.patch.object(cells_rpcapi, 'instance_delete_everywhere') def test(mock_inst_delete_everywhere): self.compute_api.delete(self.context, instance) mock_local_delete.assert_not_called() mock_delete.assert_called_once_with(self.context, instance_with_cell) test() @mock.patch.object(compute_api.API, '_delete_while_booting', side_effect=exception.ObjectActionError( action='delete', reason='host now set')) @mock.patch.object(compute_api.API, '_local_delete') @mock.patch.object(compute_api.API, '_lookup_instance') @mock.patch.object(compute_api.API, 'delete') def test_delete_instance_no_cell_then_no_instance( self, mock_delete, mock_lookup_instance, mock_local_delete, mock_delete_while_booting): # This checks the case where initially an instance has no cell_name, # and therefore no host, set but instance.destroy fails because # there is now a host. And then the instance can't be looked up. instance = self._create_fake_instance_obj() mock_lookup_instance.return_value = None, None cells_rpcapi = self.compute_api.cells_rpcapi @mock.patch.object(cells_rpcapi, 'instance_delete_everywhere') def test(mock_inst_delete_everywhere): self.compute_api.delete(self.context, instance) mock_local_delete.assert_not_called() mock_delete.assert_not_called() test() def test_get_migrations(self): filters = {'cell_name': 'ChildCell', 'status': 'confirmed'} migrations = {'migrations': [{'id': 1234}]} @mock.patch.object(self.compute_api.cells_rpcapi, 'get_migrations', return_value=migrations) def test(mock_cell_get_migrations): response = self.compute_api.get_migrations(self.context, filters) mock_cell_get_migrations.assert_called_once_with( self.context, filters) self.assertEqual(migrations, response) test() def test_create_block_device_mapping(self): instance_type = {'swap': 1, 'ephemeral_gb': 1} instance = self._create_fake_instance_obj() bdms = [ block_device.BlockDeviceDict({ 'source_type': 'image', 'destination_type': 'local', 'image_id': 'fake-image', 'boot_index': 0 }) ] self.compute_api._create_block_device_mapping(instance_type, instance.uuid, bdms) bdms = db.block_device_mapping_get_all_by_instance( self.context, instance['uuid']) self.assertEqual(0, len(bdms)) def test_create_bdm_from_flavor(self): self.skipTest("Test is incompatible with cells.") @mock.patch('nova.cells.messaging._TargetedMessage') def test_rebuild_sig(self, mock_msg): # TODO(belliott) Cells could benefit from better testing to ensure API # and manager signatures stay up to date def wire(version): # wire the rpc cast directly to the manager method to make sure # the signature matches cells_mgr = manager.CellsManager() def cast(context, method, *args, **kwargs): fn = getattr(cells_mgr, method) fn(context, *args, **kwargs) cells_mgr.cast = cast return cells_mgr cells_rpcapi = self.compute_api.cells_rpcapi client = cells_rpcapi.client with mock.patch.object(client, 'prepare', side_effect=wire): inst = self._create_fake_instance_obj() inst.cell_name = 'mycell' cells_rpcapi.rebuild_instance(self.context, inst, 'pass', None, None, None, None, None, recreate=False, on_shared_storage=False, host='host', preserve_ephemeral=True, kwargs=None) # one targeted message should have been created self.assertEqual(1, mock_msg.call_count) def test_populate_instance_for_create(self): super(CellsComputeAPITestCase, self).test_populate_instance_for_create(num_instances=2) def test_multi_instance_display_name_default(self): self._multi_instance_display_name_default(cells_enabled=True) def test_multi_instance_display_name_template(self): super( CellsComputeAPITestCase, self).test_multi_instance_display_name_template(cells_enabled=True)
def obj_make_compatible(self, primitive, target_version): super(ImageMetaProps, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 27): primitive.pop('hw_tpm_model', None) primitive.pop('hw_tpm_version', None) if target_version < (1, 26): policy = primitive.get('hw_cpu_policy', None) if policy == fields.CPUAllocationPolicy.MIXED: raise exception.ObjectActionError( action='obj_make_compatible', reason='hw_cpu_policy=%s not supported in version %s' % (policy, target_version)) if target_version < (1, 25): primitive.pop('hw_pci_numa_affinity_policy', None) if target_version < (1, 24): primitive.pop('hw_mem_encryption', None) if target_version < (1, 23): primitive.pop('hw_pmu', None) # NOTE(sean-k-mooney): unlike other nova object we version this object # when composed object are updated. if target_version < (1, 22): video = primitive.get('hw_video_model', None) if video in ('gop', 'virtio', 'none'): raise exception.ObjectActionError( action='obj_make_compatible', reason='hw_video_model=%s not supported in version %s' % ( video, target_version)) if target_version < (1, 21): primitive.pop('hw_time_hpet', None) if target_version < (1, 20): primitive.pop('traits_required', None) if target_version < (1, 19): primitive.pop('img_hide_hypervisor_id', None) if target_version < (1, 16) and 'hw_watchdog_action' in primitive: # Check to see if hw_watchdog_action was set to 'disabled' and if # so, remove it since not specifying it is the same behavior. if primitive['hw_watchdog_action'] == \ fields.WatchdogAction.DISABLED: primitive.pop('hw_watchdog_action') if target_version < (1, 15): primitive.pop('hw_rescue_bus', None) primitive.pop('hw_rescue_device', None) if target_version < (1, 14): primitive.pop('hw_pointer_model', None) if target_version < (1, 13): primitive.pop('os_secure_boot', None) if target_version < (1, 11): primitive.pop('hw_firmware_type', None) if target_version < (1, 10): primitive.pop('hw_cpu_realtime_mask', None) if target_version < (1, 9): primitive.pop('hw_cpu_thread_policy', None) if target_version < (1, 7): primitive.pop('img_config_drive', None) if target_version < (1, 5): primitive.pop('os_admin_user', None) if target_version < (1, 4): primitive.pop('hw_vif_multiqueue_enabled', None) if target_version < (1, 2): primitive.pop('img_hv_type', None) primitive.pop('img_hv_requested_version', None) if target_version < (1, 1): primitive.pop('os_require_quiesce', None) if target_version < (1, 6): bus = primitive.get('hw_disk_bus', None) if bus in ('lxc', 'uml'): raise exception.ObjectActionError( action='obj_make_compatible', reason='hw_disk_bus=%s not supported in version %s' % ( bus, target_version))
def _assert_no_hosts(self, action): if 'hosts' in self.obj_what_changed(): raise exception.ObjectActionError(action=action, reason='hosts updated inline')
def get_minimum_version_all_cells(context, binaries, require_all=False): """Get the minimum service version, checking all cells. This attempts to calculate the minimum service version for a set of binaries across all the cells in the system. If require_all is False, then any cells that fail to report a version will be ignored (assuming they won't be candidates for scheduling and thus excluding them from the minimum version calculation is reasonable). If require_all is True, then a failing cell will cause this to raise exception.CellTimeout, as would be appropriate for gating some data migration until everything is new enough. Note that services that do not report a positive version are excluded from this, as it crosses all cells which will naturally not have all services. """ if not all(binary.startswith('nova-') for binary in binaries): LOG.warning( 'get_minimum_version_all_cells called with ' 'likely-incorrect binaries `%s\'', ','.join(binaries)) raise exception.ObjectActionError( action='get_minimum_version_all_cells', reason='Invalid binary prefix') # NOTE(danms): Instead of using Service.get_minimum_version_multi(), we # replicate the call directly to the underlying DB method here because # we want to defeat the caching and we need to filter non-present # services differently from the single-cell method. results = nova_context.scatter_gather_all_cells( context, Service._db_service_get_minimum_version, binaries) min_version = None for cell_uuid, result in results.items(): if result is nova_context.did_not_respond_sentinel: LOG.warning( 'Cell %s did not respond when getting minimum ' 'service version', cell_uuid) if require_all: raise exception.CellTimeout() elif isinstance(result, Exception): LOG.warning('Failed to get minimum service version for cell %s', cell_uuid) if require_all: # NOTE(danms): Okay, this isn't necessarily a timeout, but # it's functionally the same from the caller's perspective # and we logged the fact that it was actually a failure # for the forensic investigator during the scatter/gather # routine. raise exception.CellTimeout() else: # NOTE(danms): Don't consider a zero or None result as the minimum # since we're crossing cells and will likely not have all the # services being probed. relevant_versions = [ version for version in result.values() if version ] if relevant_versions: min_version_cell = min(relevant_versions) min_version = (min(min_version, min_version_cell) if min_version else min_version_cell) # NOTE(danms): If we got no matches at all (such as at first startup) # then report that as zero to be consistent with the other such # methods. return min_version or 0
def save(self): updates = self.obj_get_changes() if updates and updates.keys() != ['name']: raise exception.ObjectActionError( action='save', reason='Immutable fields changed') self._update_in_db(self._context, self.id, updates)
def destroy(self): if not self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='destroy', reason='already destroyed') db.block_device_mapping_destroy(self._context, self.id) delattr(self, base.get_attrname('id'))
def remove_access(self, context, project_id): if 'projects' in self.obj_what_changed(): raise exception.ObjectActionError(action='remove_access', reason='projects modified') db.flavor_access_remove(context, self.flavorid, project_id) self._load_projects(context)
def remove_access(self, project_id): if 'projects' in self.obj_what_changed(): raise exception.ObjectActionError(action='remove_access', reason='projects modified') self._remove_access(project_id) self._load_projects()
def create(self, context): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') db_s3imap = db.s3_image_create(context, self.uuid) self._from_db_object(context, self, db_s3imap)