def create_resource(self, resource_type, id, created_by_user_id, created_by_project_id, user_id=None, project_id=None, started_at=None, ended_at=None, metrics=None, **kwargs): if (started_at is not None and ended_at is not None and started_at > ended_at): raise ValueError("Start timestamp cannot be after end timestamp") with self.facade.writer() as session: resource_cls = self._resource_type_to_classes( session, resource_type)['resource'] r = resource_cls(id=id, type=resource_type, created_by_user_id=created_by_user_id, created_by_project_id=created_by_project_id, user_id=user_id, project_id=project_id, started_at=started_at, ended_at=ended_at, **kwargs) session.add(r) try: session.flush() except exception.DBDuplicateEntry: raise indexer.ResourceAlreadyExists(id) except exception.DBReferenceError as ex: raise indexer.ResourceValueError(r.type, ex.key, getattr(r, ex.key)) if metrics is not None: self._set_metrics_for_resource(session, r, metrics) # NOTE(jd) Force load of metrics :) r.metrics return r
def update_resource(self, resource_type, resource_id, ended_at=_marker, metrics=_marker, append_metrics=False, create_revision=True, **kwargs): resource_cls = self._resource_type_to_class(resource_type) resource_history_cls = self._resource_type_to_class( resource_type, "history") with self.facade.writer() as session: try: # NOTE(sileht): We use FOR UPDATE that is not galera friendly, # but they are no other way to cleanly patch a resource and # store the history that safe when two concurrent calls are # done. q = session.query(resource_cls).filter( resource_cls.id == resource_id).with_for_update() r = q.first() if r is None: raise indexer.NoSuchResource(resource_id) if create_revision: # Build history rh = resource_history_cls() for col in sqlalchemy.inspect(resource_cls).columns: setattr(rh, col.name, getattr(r, col.name)) now = utils.utcnow() rh.revision_end = now session.add(rh) r.revision_start = now # Update the resource if ended_at is not _marker: # NOTE(jd) MySQL does not honor checks. I hate it. engine = session.connection() if engine.dialect.name == "mysql": if r.started_at is not None and ended_at is not None: if r.started_at > ended_at: raise indexer.ResourceValueError( resource_type, "ended_at", ended_at) r.ended_at = ended_at if kwargs: for attribute, value in six.iteritems(kwargs): if hasattr(r, attribute): setattr(r, attribute, value) else: raise indexer.ResourceAttributeError( r.type, attribute) if metrics is not _marker: if not append_metrics: session.query(Metric).filter( Metric.resource_id == resource_id, Metric.status == 'active').update( {"resource_id": None}) self._set_metrics_for_resource(session, r, metrics) session.flush() except exception.DBConstraintError as e: if e.check_name == "ck_started_before_ended": raise indexer.ResourceValueError(resource_type, "ended_at", ended_at) raise # NOTE(jd) Force load of metrics – do it outside the session! r.metrics return r