Exemplo n.º 1
0
 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)
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
    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))
Exemplo n.º 4
0
 def _assert_no_hosts(self, action):
     if 'hosts' in self.obj_what_changed():
         raise exception.ObjectActionError(action=action,
                                           reason='hosts updated inline')
Exemplo n.º 5
0
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
Exemplo n.º 6
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'))
Exemplo n.º 8
0
 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)
Exemplo n.º 9
0
 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()
Exemplo n.º 10
0
 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)