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)
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]
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']))
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)
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)
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))
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
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)
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)