class InstanceExternalEvent(obj_base.NovaObject):
    # Version 1.0: Initial version
    #              Supports network-changed and vif-plugged
    # Version 1.1: adds network-vif-deleted event
    # Version 1.2: adds volume-extended event
    # Version 1.3: adds power-update event
    VERSION = '1.3'

    fields = {
        'instance_uuid': fields.UUIDField(),
        'name': fields.EnumField(valid_values=EVENT_NAMES),
        'status': fields.EnumField(valid_values=EVENT_STATUSES),
        'tag': fields.StringField(nullable=True),
        'data': fields.DictOfStringsField(),
    }

    @staticmethod
    def make_key(name, tag=None):
        if tag is not None:
            return '%s-%s' % (name, tag)
        else:
            return name

    @property
    def key(self):
        return self.make_key(self.name, self.tag)
Example #2
0
 def setUp(self):
     super(TestEnum, self).setUp()
     self.field = fields.EnumField(valid_values=['foo', 'bar', 1, 1, True])
     self.coerce_good_values = [('foo', 'foo'), (1, '1'), (True, 'True')]
     self.coerce_bad_values = ['boo', 2, False]
     self.to_primitive_values = self.coerce_good_values[0:1]
     self.from_primitive_values = self.coerce_good_values[0:1]
Example #3
0
class VirtCPUModel(base.NovaObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'arch':
        fields.EnumField(nullable=True, valid_values=arch.ALL),
        'vendor':
        fields.StringField(nullable=True),
        'topology':
        fields.ObjectField('VirtCPUTopology', nullable=True),
        'features':
        fields.ListOfObjectsField("VirtCPUFeature", default=[]),
        'mode':
        fields.EnumField(nullable=True, valid_values=cpumodel.ALL_CPUMODES),
        'model':
        fields.StringField(nullable=True),
        'match':
        fields.EnumField(nullable=True, valid_values=cpumodel.ALL_MATCHES),
    }

    obj_relationships = {
        'topology': [('1.0', '1.0')],
        'features': [('1.0', '1.0')],
    }

    def obj_load_attr(self, attrname):
        setattr(self, attrname, None)

    def to_json(self):
        return jsonutils.dumps(self.obj_to_primitive())

    @classmethod
    def from_json(cls, jsonstr):
        return cls.obj_from_primitive(jsonutils.loads(jsonstr))

    @base.remotable_classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        db_extra = db.instance_extra_get_by_instance_uuid(
            context, instance_uuid, columns=['vcpu_model'])
        if not db_extra or not db_extra['vcpu_model']:
            return None
        return cls.obj_from_primitive(jsonutils.loads(db_extra['vcpu_model']))
Example #4
0
class VirtCPUFeature(base.NovaObject):
    VERSION = VirtCPUModel.VERSION

    fields = {
        'policy':
        fields.EnumField(nullable=True, valid_values=cpumodel.ALL_POLICIES),
        'name':
        fields.StringField(nullable=False),
    }

    def obj_load_attr(self, attrname):
        setattr(self, attrname, None)
Example #5
0
class InstanceExternalEvent(obj_base.NovaObject,
                            obj_base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    #              Supports network-changed and vif-plugged
    VERSION = '1.0'

    fields = {
        'instance_uuid': fields.UUIDField(),
        'name': fields.EnumField(valid_values=EVENT_NAMES),
        'status': fields.StringField(),
        'tag': fields.StringField(nullable=True),
        'data': fields.DictOfStringsField(),
    }

    @staticmethod
    def make_key(name, tag=None):
        if tag is not None:
            return '%s-%s' % (name, tag)
        else:
            return name

    @property
    def key(self):
        return self.make_key(self.name, self.tag)
Example #6
0
 def test_fingerprint(self):
     # Notes(yjiang5): make sure changing valid_value will be detected
     # in test_objects.test_versions
     field1 = fields.EnumField(valid_values=['foo', 'bar'])
     field2 = fields.EnumField(valid_values=['foo', 'bar1'])
     self.assertNotEqual(str(field1), str(field2))
Example #7
0
class Migration(base.NovaPersistentObject, base.NovaObject,
                base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    # Version 1.2: Added migration_type and hidden
    # Version 1.3: Added get_by_id_and_instance()
    # Version 1.4: Added migration progress detail
    # Version 1.5: Added uuid
    VERSION = '1.5'

    fields = {
        'id': fields.IntegerField(),
        'uuid': fields.UUIDField(),
        'source_compute': fields.StringField(nullable=True),  # source hostname
        'dest_compute': fields.StringField(nullable=True),    # dest hostname
        'source_node': fields.StringField(nullable=True),     # source nodename
        'dest_node': fields.StringField(nullable=True),       # dest nodename
        'dest_host': fields.StringField(nullable=True),       # dest host IP
        'old_instance_type_id': fields.IntegerField(nullable=True),
        'new_instance_type_id': fields.IntegerField(nullable=True),
        'instance_uuid': fields.StringField(nullable=True),
        'status': fields.StringField(nullable=True),
        'migration_type': fields.EnumField(['migration', 'resize',
                                            'live-migration', 'evacuation'],
                                           nullable=False),
        'hidden': fields.BooleanField(nullable=False, default=False),
        'memory_total': fields.IntegerField(nullable=True),
        'memory_processed': fields.IntegerField(nullable=True),
        'memory_remaining': fields.IntegerField(nullable=True),
        'disk_total': fields.IntegerField(nullable=True),
        'disk_processed': fields.IntegerField(nullable=True),
        'disk_remaining': fields.IntegerField(nullable=True),
        }

    @staticmethod
    def _from_db_object(context, migration, db_migration):
        for key in migration.fields:
            value = db_migration[key]
            if key == 'migration_type' and value is None:
                value = determine_migration_type(db_migration)
            elif key == 'uuid' and value is None:
                continue
            migration[key] = value

        migration._context = context
        migration.obj_reset_changes()
        migration._ensure_uuid()
        return migration

    def obj_make_compatible(self, primitive, target_version):
        super(Migration, self).obj_make_compatible(primitive, target_version)
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version < (1, 2):
            if 'migration_type' in primitive:
                del primitive['migration_type']
                del primitive['hidden']
        if target_version < (1, 4):
            if 'memory_total' in primitive:
                del primitive['memory_total']
                del primitive['memory_processed']
                del primitive['memory_remaining']
                del primitive['disk_total']
                del primitive['disk_processed']
                del primitive['disk_remaining']
        if target_version < (1, 5):
            if 'uuid' in primitive:
                del primitive['uuid']

    def obj_load_attr(self, attrname):
        if attrname == 'migration_type':
            # NOTE(danms): The only reason we'd need to load this is if
            # some older node sent us one. So, guess the type.
            self.migration_type = determine_migration_type(self)
        elif attrname == 'hidden':
            self.hidden = False
        else:
            super(Migration, self).obj_load_attr(attrname)

    def _ensure_uuid(self):
        if 'uuid' in self:
            return

        self.uuid = uuidutils.generate_uuid()
        try:
            self.save()
        except db_exc.DBDuplicateEntry:
            # NOTE(danms) We raced to generate a uuid for this,
            # so fetch the winner and use that uuid
            fresh = self.__class__.get_by_id(self.context, self.id)
            self.uuid = fresh.uuid

    @base.remotable_classmethod
    def get_by_id(cls, context, migration_id):
        db_migration = db.migration_get(context, migration_id)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable_classmethod
    def get_by_id_and_instance(cls, context, migration_id, instance_uuid):
        db_migration = db.migration_get_by_id_and_instance(
            context, migration_id, instance_uuid)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable_classmethod
    def get_by_instance_and_status(cls, context, instance_uuid, status):
        db_migration = db.migration_get_by_instance_and_status(
            context, instance_uuid, status)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        if 'uuid' not in self:
            self.uuid = uuidutils.generate_uuid()
        updates = self.obj_get_changes()
        if 'migration_type' not in updates:
            raise exception.ObjectActionError(
                action="create",
                reason=_("cannot create a Migration object without a "
                         "migration_type set"))
        db_migration = db.migration_create(self._context, updates)
        self._from_db_object(self._context, self, db_migration)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        updates.pop('id', None)
        db_migration = db.migration_update(self._context, self.id, updates)
        self._from_db_object(self._context, self, db_migration)
        self.obj_reset_changes()

    @property
    def instance(self):
        if not hasattr(self, '_cached_instance'):
            self._cached_instance = objects.Instance.get_by_uuid(
                self._context, self.instance_uuid)
        return self._cached_instance

    @instance.setter
    def instance(self, instance):
        self._cached_instance = instance
Example #8
0
class Migration(base.NovaPersistentObject, base.NovaObject,
                base.NovaObjectDictCompat):
    # Version 1.0: Initial version
    # Version 1.1: String attributes updated to support unicode
    # Version 1.2: Added migration_type and hidden
    VERSION = '1.2'

    fields = {
        'id':
        fields.IntegerField(),
        'source_compute':
        fields.StringField(nullable=True),
        'dest_compute':
        fields.StringField(nullable=True),
        'source_node':
        fields.StringField(nullable=True),
        'dest_node':
        fields.StringField(nullable=True),
        'dest_host':
        fields.StringField(nullable=True),
        'old_instance_type_id':
        fields.IntegerField(nullable=True),
        'new_instance_type_id':
        fields.IntegerField(nullable=True),
        'instance_uuid':
        fields.StringField(nullable=True),
        'status':
        fields.StringField(nullable=True),
        'migration_type':
        fields.EnumField(
            ['migration', 'resize', 'live-migration', 'evacuation'],
            nullable=False),
        'hidden':
        fields.BooleanField(nullable=False, default=False),
    }

    @staticmethod
    def _from_db_object(context, migration, db_migration):
        for key in migration.fields:
            value = db_migration[key]
            if key == 'migration_type' and value is None:
                value = determine_migration_type(db_migration)
            migration[key] = value

        migration._context = context
        migration.obj_reset_changes()
        return migration

    def obj_make_compatible(self, primitive, target_version):
        super(Migration, self).obj_make_compatible(primitive, target_version)
        target_version = versionutils.convert_version_to_tuple(target_version)
        if target_version < (1, 2):
            if 'migration_type' in primitive:
                del primitive['migration_type']
                del primitive['hidden']

    def obj_load_attr(self, attrname):
        if attrname == 'migration_type':
            # NOTE(danms): The only reason we'd need to load this is if
            # some older node sent us one. So, guess the type.
            self.migration_type = determine_migration_type(self)
        elif attrname == 'hidden':
            self.hidden = False
        else:
            super(Migration, self).obj_load_attr(attrname)

    @base.remotable_classmethod
    def get_by_id(cls, context, migration_id):
        db_migration = db.migration_get(context, migration_id)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable_classmethod
    def get_by_instance_and_status(cls, context, instance_uuid, status):
        db_migration = db.migration_get_by_instance_and_status(
            context, instance_uuid, status)
        return cls._from_db_object(context, cls(), db_migration)

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        updates = self.obj_get_changes()
        if 'migration_type' not in updates:
            raise exception.ObjectActionError(
                action="create",
                reason="cannot create a Migration object without a "
                "migration_type set")
        db_migration = db.migration_create(self._context, updates)
        self._from_db_object(self._context, self, db_migration)

    @base.remotable
    def save(self):
        updates = self.obj_get_changes()
        updates.pop('id', None)
        db_migration = db.migration_update(self._context, self.id, updates)
        self._from_db_object(self._context, self, db_migration)
        self.obj_reset_changes()

    @property
    def instance(self):
        return objects.Instance.get_by_uuid(self._context, self.instance_uuid)
Example #9
0
class BuildRequest(base.NovaObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'id': fields.IntegerField(),
        'instance_uuid': fields.UUIDField(),
        'project_id': fields.StringField(),
        'user_id': fields.StringField(),
        'display_name': fields.StringField(nullable=True),
        'instance_metadata': fields.DictOfStringsField(nullable=True),
        'progress': fields.IntegerField(nullable=True),
        'vm_state': fields.StringField(nullable=True),
        'task_state': fields.StringField(nullable=True),
        'image_ref': fields.StringField(nullable=True),
        'access_ip_v4': fields.IPV4AddressField(nullable=True),
        'access_ip_v6': fields.IPV6AddressField(nullable=True),
        'info_cache': fields.ObjectField('InstanceInfoCache', nullable=True),
        'security_groups': fields.ObjectField('SecurityGroupList'),
        'config_drive': fields.BooleanField(default=False),
        'key_name': fields.StringField(nullable=True),
        'locked_by': fields.EnumField(['owner', 'admin'], nullable=True),
        'request_spec': fields.ObjectField('RequestSpec'),
        'instance': fields.ObjectField('Instance'),
        # NOTE(alaski): Normally these would come from the NovaPersistentObject
        # mixin but they're being set explicitly because we only need
        # created_at/updated_at. There is no soft delete for this object.
        # These fields should be carried over to the instance when it is
        # scheduled and created in a cell database.
        'created_at': fields.DateTimeField(nullable=True),
        'updated_at': fields.DateTimeField(nullable=True),
    }

    def _load_request_spec(self, db_spec):
        self.request_spec = objects.RequestSpec._from_db_object(
            self._context, objects.RequestSpec(), db_spec)

    def _load_info_cache(self, db_info_cache):
        self.info_cache = objects.InstanceInfoCache.obj_from_primitive(
            jsonutils.loads(db_info_cache))

    def _load_security_groups(self, db_sec_group):
        self.security_groups = objects.SecurityGroupList.obj_from_primitive(
            jsonutils.loads(db_sec_group))

    def _load_instance(self, db_instance):
        # NOTE(alaski): Be very careful with instance loading because it
        # changes more than most objects.
        try:
            self.instance = objects.Instance.obj_from_primitive(
                jsonutils.loads(db_instance))
        except TypeError:
            LOG.debug('Failed to load instance from BuildRequest with uuid '
                      '%s because it is None' % (self.instance_uuid))
            raise exception.BuildRequestNotFound(uuid=self.instance_uuid)
        except ovoo_exc.IncompatibleObjectVersion as exc:
            # This should only happen if proper service upgrade strategies are
            # not followed. Log the exception and raise BuildRequestNotFound.
            # If the instance can't be loaded this object is useless and may
            # as well not exist.
            LOG.debug(
                'Could not deserialize instance store in BuildRequest '
                'with uuid %(instance_uuid)s. Found version %(version)s '
                'which is not supported here.',
                dict(instance_uuid=self.instance_uuid, version=exc.objver))
            LOG.exception(
                _LE('Could not deserialize instance in '
                    'BuildRequest'))
            raise exception.BuildRequestNotFound(uuid=self.instance_uuid)

    @staticmethod
    def _from_db_object(context, req, db_req):
        # Set this up front so that it can be pulled for error messages or
        # logging at any point.
        req.instance_uuid = db_req['instance_uuid']

        for key in req.fields:
            if isinstance(req.fields[key], fields.ObjectField):
                try:
                    getattr(req, '_load_%s' % key)(db_req[key])
                except AttributeError:
                    LOG.exception(_LE('No load handler for %s'), key)
            elif key in JSON_FIELDS and db_req[key] is not None:
                setattr(req, key, jsonutils.loads(db_req[key]))
            else:
                setattr(req, key, db_req[key])
        req.obj_reset_changes()
        req._context = context
        return req

    @staticmethod
    @db.api_context_manager.reader
    def _get_by_instance_uuid_from_db(context, instance_uuid):
        db_req = (context.session.query(api_models.BuildRequest).options(
            joinedload('request_spec')).filter_by(
                instance_uuid=instance_uuid)).first()
        if not db_req:
            raise exception.BuildRequestNotFound(uuid=instance_uuid)
        return db_req

    @base.remotable_classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        db_req = cls._get_by_instance_uuid_from_db(context, instance_uuid)
        return cls._from_db_object(context, cls(), db_req)

    @staticmethod
    @db.api_context_manager.writer
    def _create_in_db(context, updates):
        db_req = api_models.BuildRequest()
        db_req.update(updates)
        db_req.save(context.session)
        # NOTE: This is done because a later access will trigger a lazy load
        # outside of the db session so it will fail. We don't lazy load
        # request_spec on the object later because we never need a BuildRequest
        # without the RequestSpec.
        db_req.request_spec
        return db_req

    def _get_update_primitives(self):
        updates = self.obj_get_changes()
        for key, value in six.iteritems(updates):
            if key in OBJECT_FIELDS and value is not None:
                updates[key] = jsonutils.dumps(value.obj_to_primitive())
            elif key in JSON_FIELDS and value is not None:
                updates[key] = jsonutils.dumps(value)
            elif key in IP_FIELDS and value is not None:
                # These are stored as a string in the db and must be converted
                updates[key] = str(value)
        req_spec_obj = updates.pop('request_spec', None)
        if req_spec_obj:
            updates['request_spec_id'] = req_spec_obj.id
        return updates

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')
        if not self.obj_attr_is_set('instance_uuid'):
            # We can't guarantee this is not null in the db so check here
            raise exception.ObjectActionError(
                action='create', reason='instance_uuid must be set')

        updates = self._get_update_primitives()
        db_req = self._create_in_db(self._context, updates)
        self._from_db_object(self._context, self, db_req)

    @staticmethod
    @db.api_context_manager.writer
    def _destroy_in_db(context, id):
        context.session.query(
            api_models.BuildRequest).filter_by(id=id).delete()

    @base.remotable
    def destroy(self):
        self._destroy_in_db(self._context, self.id)
class BuildRequest(base.NovaObject):
    # Version 1.0: Initial version
    VERSION = '1.0'

    fields = {
        'id': fields.IntegerField(),
        'project_id': fields.StringField(),
        'user_id': fields.StringField(),
        'display_name': fields.StringField(nullable=True),
        'instance_metadata': fields.DictOfStringsField(nullable=True),
        'progress': fields.IntegerField(nullable=True),
        'vm_state': fields.StringField(nullable=True),
        'task_state': fields.StringField(nullable=True),
        'image_ref': fields.StringField(nullable=True),
        'access_ip_v4': fields.IPV4AddressField(nullable=True),
        'access_ip_v6': fields.IPV6AddressField(nullable=True),
        'info_cache': fields.ObjectField('InstanceInfoCache', nullable=True),
        'security_groups': fields.ObjectField('SecurityGroupList'),
        'config_drive': fields.BooleanField(default=False),
        'key_name': fields.StringField(nullable=True),
        'locked_by': fields.EnumField(['owner', 'admin'], nullable=True),
        'request_spec': fields.ObjectField('RequestSpec'),
        # NOTE(alaski): Normally these would come from the NovaPersistentObject
        # mixin but they're being set explicitly because we only need
        # created_at/updated_at. There is no soft delete for this object.
        # These fields should be carried over to the instance when it is
        # scheduled and created in a cell database.
        'created_at': fields.DateTimeField(nullable=True),
        'updated_at': fields.DateTimeField(nullable=True),
    }

    def _load_request_spec(self, db_spec):
        self.request_spec = objects.RequestSpec._from_db_object(
            self._context, objects.RequestSpec(), db_spec)

    def _load_info_cache(self, db_info_cache):
        self.info_cache = objects.InstanceInfoCache.obj_from_primitive(
            jsonutils.loads(db_info_cache))

    def _load_security_groups(self, db_sec_group):
        self.security_groups = objects.SecurityGroupList.obj_from_primitive(
            jsonutils.loads(db_sec_group))

    @staticmethod
    def _from_db_object(context, req, db_req):
        for key in req.fields:
            if isinstance(req.fields[key], fields.ObjectField):
                try:
                    getattr(req, '_load_%s' % key)(db_req[key])
                except AttributeError:
                    LOG.exception(_LE('No load handler for %s'), key)
            elif key in JSON_FIELDS and db_req[key] is not None:
                setattr(req, key, jsonutils.loads(db_req[key]))
            else:
                setattr(req, key, db_req[key])
        req.obj_reset_changes()
        req._context = context
        return req

    @staticmethod
    @db.api_context_manager.reader
    def _get_by_instance_uuid_from_db(context, instance_uuid):
        db_req = (context.session.query(api_models.BuildRequest).join(
            api_models.RequestSpec).with_entities(
                api_models.BuildRequest, api_models.RequestSpec).filter(
                    api_models.RequestSpec.instance_uuid == instance_uuid)
                  ).first()
        if not db_req:
            raise exception.BuildRequestNotFound(uuid=instance_uuid)
        # db_req is a tuple (api_models.BuildRequest, api_models.RequestSpect)
        build_req = db_req[0]
        build_req['request_spec'] = db_req[1]
        return build_req

    @base.remotable_classmethod
    def get_by_instance_uuid(cls, context, instance_uuid):
        db_req = cls._get_by_instance_uuid_from_db(context, instance_uuid)
        return cls._from_db_object(context, cls(), db_req)

    @staticmethod
    @db.api_context_manager.writer
    def _create_in_db(context, updates):
        db_req = api_models.BuildRequest()
        db_req.update(updates)
        db_req.save(context.session)
        # NOTE: This is done because a later access will trigger a lazy load
        # outside of the db session so it will fail. We don't lazy load
        # request_spec on the object later because we never need a BuildRequest
        # without the RequestSpec.
        db_req.request_spec
        return db_req

    def _get_update_primitives(self):
        updates = self.obj_get_changes()
        for key, value in six.iteritems(updates):
            if key in OBJECT_FIELDS and value is not None:
                updates[key] = jsonutils.dumps(value.obj_to_primitive())
            elif key in JSON_FIELDS and value is not None:
                updates[key] = jsonutils.dumps(value)
            elif key in IP_FIELDS and value is not None:
                # These are stored as a string in the db and must be converted
                updates[key] = str(value)
        req_spec_obj = updates.pop('request_spec', None)
        if req_spec_obj:
            updates['request_spec_id'] = req_spec_obj.id
        return updates

    @base.remotable
    def create(self):
        if self.obj_attr_is_set('id'):
            raise exception.ObjectActionError(action='create',
                                              reason='already created')

        updates = self._get_update_primitives()
        db_req = self._create_in_db(self._context, updates)
        self._from_db_object(self._context, self, db_req)

    @staticmethod
    @db.api_context_manager.writer
    def _destroy_in_db(context, id):
        context.session.query(
            api_models.BuildRequest).filter_by(id=id).delete()

    @base.remotable
    def destroy(self):
        self._destroy_in_db(self._context, self.id)