class NovaTimestampObject(object): """Mixin class for db backed objects with timestamp fields. Sqlalchemy models that inherit from the oslo_db TimestampMixin will include these fields and the corresponding objects will benefit from this mixin. """ fields = { 'created_at': obj_fields.DateTimeField(nullable=True), 'updated_at': obj_fields.DateTimeField(nullable=True), }
class NovaPersistentObject(object): """Mixin class for Persistent objects. This adds the fields that we use in common for most persistent objects. """ fields = { 'created_at': obj_fields.DateTimeField(nullable=True), 'updated_at': obj_fields.DateTimeField(nullable=True), 'deleted_at': obj_fields.DateTimeField(nullable=True), 'deleted': obj_fields.BooleanField(default=False), }
class ServiceStatusPayload(notification.NotificationPayloadBase): SCHEMA = { 'host': ('service', 'host'), 'binary': ('service', 'binary'), 'topic': ('service', 'topic'), 'report_count': ('service', 'report_count'), 'disabled': ('service', 'disabled'), 'disabled_reason': ('service', 'disabled_reason'), 'availability_zone': ('service', 'availability_zone'), 'last_seen_up': ('service', 'last_seen_up'), 'forced_down': ('service', 'forced_down'), 'version': ('service', 'version') } # Version 1.0: Initial version VERSION = '1.0' fields = { 'host': fields.StringField(nullable=True), 'binary': fields.StringField(nullable=True), 'topic': fields.StringField(nullable=True), 'report_count': fields.IntegerField(), 'disabled': fields.BooleanField(), 'disabled_reason': fields.StringField(nullable=True), 'availability_zone': fields.StringField(nullable=True), 'last_seen_up': fields.DateTimeField(nullable=True), 'forced_down': fields.BooleanField(), 'version': fields.IntegerField(), } def __init__(self, service): super(ServiceStatusPayload, self).__init__() self.populate_schema(service=service)
class VolumeUsage(base.NovaPersistentObject, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': fields.IntegerField(read_only=True), 'volume_id': fields.UUIDField(), 'instance_uuid': fields.UUIDField(nullable=True), 'project_id': fields.StringField(nullable=True), 'user_id': fields.StringField(nullable=True), 'availability_zone': fields.StringField(nullable=True), 'tot_last_refreshed': fields.DateTimeField(nullable=True, read_only=True), 'tot_reads': fields.IntegerField(read_only=True), 'tot_read_bytes': fields.IntegerField(read_only=True), 'tot_writes': fields.IntegerField(read_only=True), 'tot_write_bytes': fields.IntegerField(read_only=True), 'curr_last_refreshed': fields.DateTimeField(nullable=True, read_only=True), 'curr_reads': fields.IntegerField(), 'curr_read_bytes': fields.IntegerField(), 'curr_writes': fields.IntegerField(), 'curr_write_bytes': fields.IntegerField() } @staticmethod def _from_db_object(context, vol_usage, db_vol_usage): for field in vol_usage.fields: setattr(vol_usage, field, db_vol_usage[field]) vol_usage._context = context vol_usage.obj_reset_changes() return vol_usage @base.remotable def save(self, update_totals=False): db_vol_usage = db.vol_usage_update( self._context, self.volume_id, self.curr_reads, self.curr_read_bytes, self.curr_writes, self.curr_write_bytes, self.instance_uuid, self.project_id, self.user_id, self.availability_zone, update_totals=update_totals) self._from_db_object(self._context, self, db_vol_usage)
class MonitorMetric(base.NovaObject): # Version 1.0: Initial version # Version 1.1: Added NUMA support VERSION = '1.1' fields = { 'name': fields.MonitorMetricTypeField(nullable=False), 'value': fields.IntegerField(nullable=False), 'numa_membw_values': fields.DictOfIntegersField(nullable=True), 'timestamp': fields.DateTimeField(nullable=False), # This will be the stevedore extension full class name # for the plugin from which the metric originates. 'source': fields.StringField(nullable=False), } def obj_make_compatible(self, primitive, target_version): super(MonitorMetric, self).obj_make_compatible(primitive, target_version) target_version = versionutils.convert_version_to_tuple(target_version) if target_version < (1, 1) and 'numa_nodes_values' in primitive: del primitive['numa_membw_values'] # NOTE(jaypipes): This method exists to convert the object to the # format expected by the RPC notifier for metrics events. def to_dict(self): dict_to_return = { 'name': self.name, # NOTE(jaypipes): This is what jsonutils.dumps() does to # datetime.datetime objects, which is what timestamp is in # this object as well as the original simple dict metrics 'timestamp': utils.strtime(self.timestamp), 'source': self.source, } if self.obj_attr_is_set('value'): if self.name in FIELDS_REQUIRING_CONVERSION: dict_to_return['value'] = self.value / 100.0 else: dict_to_return['value'] = self.value elif self.obj_attr_is_set('numa_membw_values'): dict_to_return['numa_membw_values'] = self.numa_membw_values return dict_to_return
class ImageMeta(base.NovaObject): # Version 1.0: Initial version # Version 1.1: updated ImageMetaProps # Version 1.2: ImageMetaProps version 1.2 # Version 1.3: ImageMetaProps version 1.3 # Version 1.4: ImageMetaProps version 1.4 # Version 1.5: ImageMetaProps version 1.5 # Version 1.6: ImageMetaProps version 1.6 # Version 1.7: ImageMetaProps version 1.7 # Version 1.8: ImageMetaProps version 1.8 VERSION = '1.8' # These are driven by what the image client API returns # to Nova from Glance. This is defined in the glance # code glance/api/v2/images.py get_base_properties() # method. A few things are currently left out: # self, file, schema - Nova does not appear to ever use # these field; locations - modelling the arbitrary # data in the 'metadata' subfield is non-trivial as # there's no clear spec. # # TODO(ft): In version 2.0, these fields should be nullable: # name, checksum, owner, size, virtual_size, container_format, disk_format # fields = { 'id': fields.UUIDField(), 'name': fields.StringField(), 'status': fields.StringField(), 'visibility': fields.StringField(), 'protected': fields.FlexibleBooleanField(), 'checksum': fields.StringField(), 'owner': fields.StringField(), 'size': fields.IntegerField(), 'virtual_size': fields.IntegerField(), 'container_format': fields.StringField(), 'disk_format': fields.StringField(), 'created_at': fields.DateTimeField(nullable=True), 'updated_at': fields.DateTimeField(nullable=True), 'tags': fields.ListOfStringsField(), 'direct_url': fields.StringField(), 'min_ram': fields.IntegerField(), 'min_disk': fields.IntegerField(), 'properties': fields.ObjectField('ImageMetaProps'), } @classmethod def from_dict(cls, image_meta): """Create instance from image metadata dict :param image_meta: image metadata dictionary Creates a new object instance, initializing from the properties associated with the image metadata instance :returns: an ImageMeta instance """ if image_meta is None: image_meta = {} # We must turn 'properties' key dict into an object # so copy image_meta to avoid changing original image_meta = copy.deepcopy(image_meta) image_meta["properties"] = \ objects.ImageMetaProps.from_dict( image_meta.get("properties", {})) # Some fields are nullable in Glance DB schema, but was not marked that # in ImageMeta initially by mistake. To keep compatibility with compute # nodes which are run with previous versions these fields are still # not nullable in ImageMeta, but the code below converts None to # approppriate empty values. for fld in NULLABLE_STRING_FIELDS: if fld in image_meta and image_meta[fld] is None: image_meta[fld] = '' for fld in NULLABLE_INTEGER_FIELDS: if fld in image_meta and image_meta[fld] is None: image_meta[fld] = 0 return cls(**image_meta) @classmethod def from_instance(cls, instance): """Create instance from instance system metadata :param instance: Instance object Creates a new object instance, initializing from the system metadata "image_*" properties associated with instance :returns: an ImageMeta instance """ sysmeta = utils.instance_sys_meta(instance) image_meta = utils.get_image_from_system_metadata(sysmeta) return cls.from_dict(image_meta) @classmethod def from_image_ref(cls, context, image_api, image_ref): """Create instance from glance image :param context: the request context :param image_api: the glance client API :param image_ref: the glance image identifier Creates a new object instance, initializing from the properties associated with a glance image :returns: an ImageMeta instance """ image_meta = image_api.get(context, image_ref) image = cls.from_dict(image_meta) setattr(image, "id", image_ref) return image
class Service(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Added compute_node nested object # Version 1.2: String attributes updated to support unicode # Version 1.3: ComputeNode version 1.5 # Version 1.4: Added use_slave to get_by_compute_host # Version 1.5: ComputeNode version 1.6 # Version 1.6: ComputeNode version 1.7 # Version 1.7: ComputeNode version 1.8 # Version 1.8: ComputeNode version 1.9 # Version 1.9: ComputeNode version 1.10 # Version 1.10: Changes behaviour of loading compute_node # Version 1.11: Added get_by_host_and_binary # Version 1.12: ComputeNode version 1.11 # Version 1.13: Added last_seen_up # Version 1.14: Added forced_down # Version 1.15: ComputeNode version 1.12 # Version 1.16: Added version # Version 1.17: ComputeNode version 1.13 # Version 1.18: ComputeNode version 1.14 # Version 1.19: Added get_minimum_version() VERSION = '1.19' fields = { 'id': fields.IntegerField(read_only=True), 'host': fields.StringField(nullable=True), 'binary': fields.StringField(nullable=True), 'topic': fields.StringField(nullable=True), 'report_count': fields.IntegerField(), 'disabled': fields.BooleanField(), 'disabled_reason': fields.StringField(nullable=True), 'availability_zone': fields.StringField(nullable=True), 'compute_node': fields.ObjectField('ComputeNode'), 'last_seen_up': fields.DateTimeField(nullable=True), 'forced_down': fields.BooleanField(), 'version': fields.IntegerField(), 'rpc_current_version': fields.StringField(nullable=True), 'object_current_version': fields.StringField(nullable=True), } _MIN_VERSION_CACHE = {} _SERVICE_VERSION_CACHING = False def __init__(self, *args, **kwargs): # NOTE(danms): We're going against the rules here and overriding # init. The reason is that we want to *ensure* that we're always # setting the current service version on our objects, overriding # whatever else might be set in the database, or otherwise (which # is the normal reason not to override init). # # We also need to do this here so that it's set on the client side # all the time, such that create() and save() operations will # include the current service version. if 'version' in kwargs: raise exception.ObjectActionError( action='init', reason='Version field is immutable') super(Service, self).__init__(*args, **kwargs) self.version = SERVICE_VERSION def obj_make_compatible_from_manifest(self, primitive, target_version, version_manifest): super(Service, self).obj_make_compatible_from_manifest( primitive, target_version, version_manifest) _target_version = versionutils.convert_version_to_tuple(target_version) if _target_version < (1, 16) and 'version' in primitive: del primitive['version'] if _target_version < (1, 14) and 'forced_down' in primitive: del primitive['forced_down'] if _target_version < (1, 13) and 'last_seen_up' in primitive: del primitive['last_seen_up'] if _target_version < (1, 10): # service.compute_node was not lazy-loaded, we need to provide it # when called self._do_compute_node(self._context, primitive, version_manifest) def _do_compute_node(self, context, primitive, version_manifest): try: target_version = version_manifest['ComputeNode'] # NOTE(sbauza): Some drivers (VMware, Ironic) can have multiple # nodes for the same service, but for keeping same behaviour, # returning only the first elem of the list compute = objects.ComputeNodeList.get_all_by_host( context, primitive['host'])[0] except Exception: return primitive['compute_node'] = compute.obj_to_primitive( target_version=target_version, version_manifest=version_manifest) @staticmethod def _from_db_object(context, service, db_service): allow_missing = ('availability_zone',) for key in service.fields: if key in allow_missing and key not in db_service: continue if key == 'compute_node': # NOTE(sbauza); We want to only lazy-load compute_node continue elif key == 'version': # NOTE(danms): Special handling of the version field, since # it is read_only and set in our init. setattr(service, base.get_attrname(key), db_service[key]) else: service[key] = db_service[key] service._context = context service.obj_reset_changes() return service def obj_load_attr(self, attrname): if not self._context: raise exception.OrphanedObjectError(method='obj_load_attr', objtype=self.obj_name()) LOG.debug("Lazy-loading '%(attr)s' on %(name)s id %(id)s", {'attr': attrname, 'name': self.obj_name(), 'id': self.id, }) if attrname != 'compute_node': raise exception.ObjectActionError( action='obj_load_attr', reason='attribute %s not lazy-loadable' % attrname) if self.binary == 'nova-compute': # Only n-cpu services have attached compute_node(s) compute_nodes = objects.ComputeNodeList.get_all_by_host( self._context, self.host) else: # NOTE(sbauza); Previous behaviour was raising a ServiceNotFound, # we keep it for backwards compatibility raise exception.ServiceNotFound(service_id=self.id) # NOTE(sbauza): Some drivers (VMware, Ironic) can have multiple nodes # for the same service, but for keeping same behaviour, returning only # the first elem of the list self.compute_node = compute_nodes[0] @base.remotable_classmethod def get_by_id(cls, context, service_id): db_service = db.service_get(context, service_id) return cls._from_db_object(context, cls(), db_service) @base.remotable_classmethod def get_by_host_and_topic(cls, context, host, topic): db_service = db.service_get_by_host_and_topic(context, host, topic) return cls._from_db_object(context, cls(), db_service) @base.remotable_classmethod def get_by_host_and_binary(cls, context, host, binary): try: db_service = db.service_get_by_host_and_binary(context, host, binary) except exception.HostBinaryNotFound: return return cls._from_db_object(context, cls(), db_service) @staticmethod @db.select_db_reader_mode def _db_service_get_by_compute_host(context, host, use_slave=False): return db.service_get_by_compute_host(context, host) @base.remotable_classmethod def get_by_compute_host(cls, context, host, use_slave=False): db_service = cls._db_service_get_by_compute_host(context, host, use_slave=use_slave) return cls._from_db_object(context, cls(), db_service) # NOTE(ndipanov): This is deprecated and should be removed on the next # major version bump @base.remotable_classmethod def get_by_args(cls, context, host, binary): db_service = db.service_get_by_host_and_binary(context, host, binary) return cls._from_db_object(context, cls(), db_service) def _check_minimum_version(self): """Enforce that we are not older that the minimum version. This is a loose check to avoid creating or updating our service record if we would do so with a version that is older that the current minimum of all services. This could happen if we were started with older code by accident, either due to a rollback or an old and un-updated node suddenly coming back onto the network. There is technically a race here between the check and the update, but since the minimum version should always roll forward and never backwards, we don't need to worry about doing it atomically. Further, the consequence for getting this wrong is minor, in that we'll just fail to send messages that other services understand. """ if not self.obj_attr_is_set('version'): return if not self.obj_attr_is_set('binary'): return minver = self.get_minimum_version(self._context, self.binary) if minver > self.version: raise exception.ServiceTooOld(thisver=self.version, minver=minver) @base.remotable def create(self): if self.obj_attr_is_set('id'): raise exception.ObjectActionError(action='create', reason='already created') self._check_minimum_version() updates = self.obj_get_changes() db_service = db.service_create(self._context, updates) self._from_db_object(self._context, self, db_service) @base.remotable def save(self): updates = self.obj_get_changes() updates.pop('id', None) if list(updates.keys()) == ['version']: # NOTE(danms): Since we set/dirty version in init, don't # do a save if that's all that has changed. This keeps the # "save is a no-op if nothing has changed" behavior. return self._check_minimum_version() db_service = db.service_update(self._context, self.id, updates) self._from_db_object(self._context, self, db_service) self._send_status_update_notification(updates) def _send_status_update_notification(self, updates): # Note(gibi): We do not trigger notification on version as that field # is always dirty, which would cause that compute sends notification on # every other field change. See the comment in save() too. if set(updates.keys()).intersection( {'disabled', 'disabled_reason', 'forced_down'}): payload = ServiceStatusPayload(self) ServiceStatusNotification( publisher=notification.NotificationPublisher.from_service_obj( self), event_type=notification.EventType( object='service', action=fields.NotificationAction.UPDATE), priority=fields.NotificationPriority.INFO, payload=payload).emit(self._context) @base.remotable def destroy(self): db.service_destroy(self._context, self.id) @classmethod def enable_min_version_cache(cls): cls.clear_min_version_cache() cls._SERVICE_VERSION_CACHING = True @classmethod def clear_min_version_cache(cls): cls._MIN_VERSION_CACHE = {} @staticmethod @db.select_db_reader_mode def _db_service_get_minimum_version(context, binary, use_slave=False): return db.service_get_minimum_version(context, binary) @base.remotable_classmethod def get_minimum_version(cls, context, binary, use_slave=False): if not binary.startswith('nova-') and not binary.startswith('jacket-'): LOG.warning(_LW('get_minimum_version called with likely-incorrect ' 'binary `%s\''), binary) raise exception.ObjectActionError(action='get_minimum_version', reason='Invalid binary prefix') if cls._SERVICE_VERSION_CACHING: cached_version = cls._MIN_VERSION_CACHE.get(binary) if cached_version: return cached_version version = cls._db_service_get_minimum_version(context, binary, use_slave=use_slave) if version is None: return 0 # NOTE(danms): Since our return value is not controlled by object # schema, be explicit here. version = int(version) cls._MIN_VERSION_CACHE[binary] = version return version @classmethod def _get_minimum_version(cls, attribute, context, binary): services = ServiceList.get_by_binary(context, binary) min_ver = None min_ver_str = None for s in services: ver_str = getattr(s, attribute) if ver_str is None: # FIXME(dulek) None in *_current_version means that this # service is in Liberty version, so we must assume this is the # lowest one. We use handy and easy to remember token to # indicate that. This may go away as soon as we drop # compatibility with Liberty, possibly in early N. return 'liberty' ver = versionutils.convert_version_to_int(ver_str) if min_ver is None or ver < min_ver: min_ver = ver min_ver_str = ver_str return min_ver_str @base.remotable_classmethod def get_minimum_rpc_version(cls, context, binary): return cls._get_minimum_version('rpc_current_version', context, binary) @base.remotable_classmethod def get_minimum_obj_version(cls, context, binary): return cls._get_minimum_version('object_current_version', context, binary)
class InstanceAction(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: String attributes updated to support unicode VERSION = '1.1' fields = { 'id': fields.IntegerField(), 'action': fields.StringField(nullable=True), 'instance_uuid': fields.UUIDField(nullable=True), 'request_id': fields.StringField(nullable=True), 'user_id': fields.StringField(nullable=True), 'project_id': fields.StringField(nullable=True), 'start_time': fields.DateTimeField(nullable=True), 'finish_time': fields.DateTimeField(nullable=True), 'message': fields.StringField(nullable=True), } @staticmethod def _from_db_object(context, action, db_action): for field in action.fields: action[field] = db_action[field] action._context = context action.obj_reset_changes() return action @staticmethod def pack_action_start(context, instance_uuid, action_name): values = {'request_id': context.request_id, 'instance_uuid': instance_uuid, 'user_id': context.user_id, 'project_id': context.project_id, 'action': action_name, 'start_time': context.timestamp} return values @staticmethod def pack_action_finish(context, instance_uuid): values = {'request_id': context.request_id, 'instance_uuid': instance_uuid, 'finish_time': timeutils.utcnow()} return values @base.remotable_classmethod def get_by_request_id(cls, context, instance_uuid, request_id): db_action = db.action_get_by_request_id(context, instance_uuid, request_id) if db_action: return cls._from_db_object(context, cls(), db_action) @base.remotable_classmethod def action_start(cls, context, instance_uuid, action_name, want_result=True): values = cls.pack_action_start(context, instance_uuid, action_name) db_action = db.action_start(context, values) if want_result: return cls._from_db_object(context, cls(), db_action) @base.remotable_classmethod def action_finish(cls, context, instance_uuid, want_result=True): values = cls.pack_action_finish(context, instance_uuid) db_action = db.action_finish(context, values) if want_result: return cls._from_db_object(context, cls(), db_action) @base.remotable def finish(self): values = self.pack_action_finish(self._context, self.instance_uuid) db_action = db.action_finish(self._context, values) self._from_db_object(self._context, self, db_action)
class InstanceActionEvent(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: event_finish_with_failure decorated with serialize_args VERSION = '1.1' fields = { 'id': fields.IntegerField(), 'event': fields.StringField(nullable=True), 'action_id': fields.IntegerField(nullable=True), 'start_time': fields.DateTimeField(nullable=True), 'finish_time': fields.DateTimeField(nullable=True), 'result': fields.StringField(nullable=True), 'traceback': fields.StringField(nullable=True), } @staticmethod def _from_db_object(context, event, db_event): for field in event.fields: event[field] = db_event[field] event._context = context event.obj_reset_changes() return event @staticmethod def pack_action_event_start(context, instance_uuid, event_name): values = {'event': event_name, 'instance_uuid': instance_uuid, 'request_id': context.request_id, 'start_time': timeutils.utcnow()} return values @staticmethod def pack_action_event_finish(context, instance_uuid, event_name, exc_val=None, exc_tb=None): values = {'event': event_name, 'instance_uuid': instance_uuid, 'request_id': context.request_id, 'finish_time': timeutils.utcnow()} if exc_tb is None: values['result'] = 'Success' else: values['result'] = 'Error' values['message'] = exc_val values['traceback'] = exc_tb return values @base.remotable_classmethod def get_by_id(cls, context, action_id, event_id): db_event = db.action_event_get_by_id(context, action_id, event_id) return cls._from_db_object(context, cls(), db_event) @base.remotable_classmethod def event_start(cls, context, instance_uuid, event_name, want_result=True): values = cls.pack_action_event_start(context, instance_uuid, event_name) db_event = db.action_event_start(context, values) if want_result: return cls._from_db_object(context, cls(), db_event) @base.serialize_args @base.remotable_classmethod def event_finish_with_failure(cls, context, instance_uuid, event_name, exc_val=None, exc_tb=None, want_result=None): values = cls.pack_action_event_finish(context, instance_uuid, event_name, exc_val=exc_val, exc_tb=exc_tb) db_event = db.action_event_finish(context, values) if want_result: return cls._from_db_object(context, cls(), db_event) @base.remotable_classmethod def event_finish(cls, context, instance_uuid, event_name, want_result=True): return cls.event_finish_with_failure(context, instance_uuid, event_name, exc_val=None, exc_tb=None, want_result=want_result) @base.remotable def finish_with_failure(self, exc_val, exc_tb): values = self.pack_action_event_finish(self._context, self.instance_uuid, self.event, exc_val=exc_val, exc_tb=exc_tb) db_event = db.action_event_finish(self._context, values) self._from_db_object(self._context, self, db_event) @base.remotable def finish(self): self.finish_with_failure(self._context, exc_val=None, exc_tb=None)
class BandwidthUsage(base.NovaPersistentObject, base.NovaObject, base.NovaObjectDictCompat): # Version 1.0: Initial version # Version 1.1: Add use_slave to get_by_instance_uuid_and_mac # Version 1.2: Add update_cells to create VERSION = '1.2' fields = { 'instance_uuid': fields.UUIDField(), 'mac': fields.StringField(), 'start_period': fields.DateTimeField(), 'last_refreshed': fields.DateTimeField(), 'bw_in': fields.IntegerField(), 'bw_out': fields.IntegerField(), 'last_ctr_in': fields.IntegerField(), 'last_ctr_out': fields.IntegerField() } @staticmethod def _from_db_object(context, bw_usage, db_bw_usage): for field in bw_usage.fields: if field == 'instance_uuid': bw_usage[field] = db_bw_usage['uuid'] else: bw_usage[field] = db_bw_usage[field] bw_usage._context = context bw_usage.obj_reset_changes() return bw_usage @staticmethod @db.select_db_reader_mode def _db_bw_usage_get(context, uuid, start_period, mac, use_slave=False): return db.bw_usage_get(context, uuid=uuid, start_period=start_period, mac=mac) @base.serialize_args @base.remotable_classmethod def get_by_instance_uuid_and_mac(cls, context, instance_uuid, mac, start_period=None, use_slave=False): db_bw_usage = cls._db_bw_usage_get(context, uuid=instance_uuid, start_period=start_period, mac=mac, use_slave=use_slave) if db_bw_usage: return cls._from_db_object(context, cls(), db_bw_usage) @base.serialize_args @base.remotable def create(self, uuid, mac, bw_in, bw_out, last_ctr_in, last_ctr_out, start_period=None, last_refreshed=None, update_cells=True): db_bw_usage = db.bw_usage_update(self._context, uuid, mac, start_period, bw_in, bw_out, last_ctr_in, last_ctr_out, last_refreshed=last_refreshed, update_cells=update_cells) self._from_db_object(self._context, self, db_bw_usage)
class TaskLog(base.NovaPersistentObject, base.NovaObject): # Version 1.0: Initial version VERSION = '1.0' fields = { 'id': fields.IntegerField(read_only=True), 'task_name': fields.StringField(), 'state': fields.StringField(read_only=True), 'host': fields.StringField(), 'period_beginning': fields.DateTimeField(), 'period_ending': fields.DateTimeField(), 'message': fields.StringField(), 'task_items': fields.IntegerField(), 'errors': fields.IntegerField(), } @staticmethod def _from_db_object(context, task_log, db_task_log): for field in task_log.fields: setattr(task_log, field, db_task_log[field]) task_log._context = context task_log.obj_reset_changes() return task_log @base.serialize_args @base.remotable_classmethod def get(cls, context, task_name, period_beginning, period_ending, host, state=None): db_task_log = db.task_log_get(context, task_name, period_beginning, period_ending, host, state=state) if db_task_log: return cls._from_db_object(context, cls(context), db_task_log) @base.remotable def begin_task(self): db.task_log_begin_task(self._context, self.task_name, self.period_beginning, self.period_ending, self.host, task_items=self.task_items, message=self.message) @base.remotable def end_task(self): db.task_log_end_task(self._context, self.task_name, self.period_beginning, self.period_ending, self.host, errors=self.errors, message=self.message)
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)