class LogEntry(Base): """ An audit log of a particular effective action (DB modification) done, such as changing package's attributes by user. Append-only table. Currently not displayed by anything. Can be used by administrators manually via SQL. Produced mostly by frontend and koschei-admin. """ __table_args__ = (CheckConstraint( "user_id IS NOT NULL or environment = 'backend'", name='log_entry_user_id_check', ), ) id = Column(Integer, primary_key=True) # ID of a user, may be root for koschei-admin entries, is null for backend entries user_id = Column( ForeignKey('user.id', ondelete='CASCADE'), nullable=True, ) user = relationship('User') environment = Column( Enum('admin', 'backend', 'frontend', name='log_environment'), nullable=False, ) timestamp = Column( DateTime, nullable=False, server_default=func.clock_timestamp(), ) # Readable description of the event that occured message = Column(String, nullable=False) # Package to which the action pertains, if any base_id = Column( ForeignKey('base_package.id', ondelete='CASCADE'), nullable=True, )
def current_priority_expression(cls, collection, last_build): """ Return computed value for packages priority or None if package is not schedulable. :param: collection package's collection. It should either be the Collection class object, or it should be a concrete collection object if all packages have the same collection. This is done as an optimization to avoid adding join on collection table when the collection is known. :param: last_build package's last complete build. As with the previous argument, should be either Build class object or particular last complete build object. :returns: SQLA expression that, when evaluated in the DB, returns the priority """ # if last_build is concrete object, it may be None # packages with no last build should have no priority # (they cannot be scheduled) if not last_build: return null() dynamic_priority = cls.dependency_priority + cls.build_priority # compute time priority seconds = extract('EPOCH', func.clock_timestamp() - last_build.started) a, b = TIME_PRIORITY.inputs # avoid zero/negative values, when time difference too small log_arg = func.greatest(0.000001, seconds / 3600) dynamic_priority += func.greatest(a * func.log(log_arg) + b, -30) # dynamic priority is affected by coefficient dynamic_priority *= collection.priority_coefficient # manual and static priority are not affected by coefficient current_priority = cls.manual_priority + cls.static_priority + dynamic_priority return case( [ # handle unschedulable packages ( # WHEN blocked OR untracked cls.blocked | ~cls.tracked | # OR has running build (cls.last_complete_build_id != cls.last_build_id) | # OR is unresolved (cls.resolved == False) | # OR resolution is not yet done ((cls.resolved == None) & ~cls.skip_resolution) | # OR the collection's buildroot is broken (collection.latest_repo_resolved == False) | # OR the collection's buildroot wasn't resolved yet (collection.latest_repo_resolved == None), # THEN return NULL None, ) ], # ELSE return the computed priority else_=current_priority)
class LogEntry(Base): __table_args__ = (CheckConstraint( "user_id IS NOT NULL or environment = 'backend'", name='log_entry_user_id_check', ), ) id = Column(Integer, primary_key=True) user_id = Column( Integer, ForeignKey('user.id', ondelete='CASCADE'), nullable=True, ) user = relationship('User') environment = Column( Enum('admin', 'backend', 'frontend', name='log_environment'), nullable=False, ) timestamp = Column(DateTime, nullable=False, server_default=func.clock_timestamp()) message = Column(String, nullable=False) base_id = Column( Integer, ForeignKey('base_package.id', ondelete='CASCADE'), nullable=True, )
class ResolutionChange(Base): id = Column(Integer, primary_key=True) resolved = Column(Boolean, nullable=False) timestamp = Column(DateTime, nullable=False, server_default=func.clock_timestamp()) package_id = Column( Integer, ForeignKey(Package.id, ondelete='CASCADE'), nullable=False, index=True, )
class CoprRebuildRequest(Base): """ Used by copr plugin to represent a users request to rebuild packages with additional copr repo added, in order to test whether his changes break anything. """ id = Column(Integer, primary_key=True) # Requestor user_id = Column( ForeignKey('user.id', ondelete='CASCADE'), nullable=False, ) collection_id = Column( ForeignKey('collection.id', ondelete='CASCADE'), nullable=False, ) # String refering to copr used to abtain the repo # format: copr:owner/name or copr:name repo_source = Column(String, nullable=False) # Set by resolver to raw yum repo URL yum_repo = Column(String) # Creation time timestamp = Column( DateTime, nullable=False, server_default=func.clock_timestamp(), ) # Descirption entered by the user. Informative only description = Column(String) # Koji repo ID used to obtain the base repo, taken from colection.latest_repo_id # at resolution time repo_id = Column(Integer) # How many builds should be scheduled. User can bump this value schedule_count = Column(Integer) # Current cursor into the build queue scheduler_queue_index = Column(Integer) state = Column( Enum( 'new', # just submitted by user 'in progress', # resolved, scheduling in progress 'scheduled', # every build was scheduled 'finished', # every build completed 'failed', # error occured, processing stopped name='rebuild_request_state', ), nullable=False, server_default='new', ) error = Column(String) def __str__(self): return 'copr-request-{}'.format(self.id)
def current_priority_expression(cls, collection, last_build): """ Return computed value for packages priority or None if package is not schedulable. """ # if last_build is concrete object, it may be None # packages with no last build should have no priority # (they cannot be scheduled) if not last_build: return null() dynamic_priority = cls.dependency_priority + cls.build_priority # compute time priority seconds = extract('EPOCH', func.clock_timestamp() - last_build.started) a, b = TIME_PRIORITY.inputs # avoid zero/negative values, when time difference too small log_arg = func.greatest(0.000001, seconds / 3600) dynamic_priority += func.greatest(a * func.log(log_arg) + b, -30) # dynamic priority is affected by coefficient dynamic_priority *= collection.priority_coefficient # manual and static priority are not affected by coefficient current_priority = cls.manual_priority + cls.static_priority + dynamic_priority return case( [ # handle unschedulable packages ( # WHEN blocked OR untracked cls.blocked | ~cls.tracked | # OR has running build (cls.last_complete_build_id != cls.last_build_id) | # OR is unresolved (cls.resolved == False) | # OR resolution is not yet done ((cls.resolved == None) & ~cls.skip_resolution) | # OR the collection's buildroot is broken (collection.latest_repo_resolved == False) | # OR the collection's buildroot wasn't resolved yet (collection.latest_repo_resolved == None), # THEN return NULL None, ) ], # ELSE return the computed priority else_=current_priority)
class ResolutionChange(Base): """ An entry representing that a particular package's reslution state (whether its dependencies were installable) has changed at given point of time. Used only the frontend to show past changes. The use-case is that without this feature, people often saw a fedmsg that a package failed to resolve, but by the time they opened Koschei, it had already been resolved again and they had no idea what had been wrong. """ id = Column(Integer, primary_key=True) # Whether package's dependencies were installable or not resolved = Column(Boolean, nullable=False) # Timestamp of the resolution, used to order them and relate them to builds timestamp = Column(DateTime, nullable=False, server_default=func.clock_timestamp()) package_id = Column( ForeignKey(Package.id, ondelete='CASCADE'), nullable=False, index=True, )
def update_amount_cache(self, account, now=None): """Update amount cache """ if now is None: now = func.clock_timestamp() query = (self.session.query(func.sum(Ledger.amount)).filter( Ledger.account == account).filter(Ledger.created_at <= now)) cache = self.session.query(AccountAmount).get(account.guid) if cache is None: cache = AccountAmount( account_guid=account.guid, amount=query.scalar(), updated_at=now, ) else: cache.amount = query.scalar() cache.updated_at = now self.session.add(cache) return cache.amount
class CoprRebuildRequest(Base): id = Column(Integer, primary_key=True) user_id = Column( Integer, ForeignKey('user.id', ondelete='CASCADE'), nullable=False, ) collection_id = Column( Integer, ForeignKey('collection.id', ondelete='CASCADE'), nullable=False, ) repo_source = Column(String, nullable=False) yum_repo = Column(String) # set by resolver to raw yum repo path timestamp = Column(DateTime, nullable=False, server_default=func.clock_timestamp()) description = Column(String) repo_id = Column(Integer) # how many builds should be scheduled. User can bump this value schedule_count = Column(Integer) scheduler_queue_index = Column(Integer) state = Column( Enum( 'new', # just submitted by user 'in progress', # resolved, scheduling in progress 'scheduled', # every build was scheduled 'finished', # every build completed 'failed', # error occured, processing stopped name='rebuild_request_state', ), nullable=False, server_default='new') error = Column(String) def __str__(self): return 'copr-request-{}'.format(self.id)
def update_amount_cache(self, account, now=None): """Update amount cache """ if now is None: now = func.clock_timestamp() query = ( self.session .query(func.sum(Ledger.amount)) .filter(Ledger.account == account) .filter(Ledger.created_at <= now) ) cache = self.session.query(AccountAmount).get(account.guid) if cache is None: cache = AccountAmount( account_guid=account.guid, amount=query.scalar(), updated_at=now, ) else: cache.amount = query.scalar() cache.updated_at = now self.session.add(cache) return cache.amount
class CaseDetermination(Base): __tablename__ = "case_determinations" __table_args__ = (PrimaryKeyConstraint( "case_number", "created_at", "determiner_email", "result", "reason", ), ) case_number = Column( BigInteger, ForeignKey("cases.case_number", ondelete="cascade", onupdate="cascade"), nullable=False, ) created_at = Column( ArrowType(timezone=True), server_default=func.clock_timestamp(), nullable=False, ) determiner_email = Column(Text, nullable=False) result = Column( Text, CheckConstraint( "result in ('approved', 'denied', 'closed')", "case_determination_result_check", ), nullable=False, ) reason = Column(Text, nullable=False)
def current_priority_expression(cls, collection, last_build): """ Return computed value for packages priority or None if package is not schedulable. :param: collection package's collection. It should either be the Collection class object, or it should be a concrete collection object if all packages have the same collection. This is done as an optimization to avoid adding join on collection table when the collection is known. :param: last_build package's last complete build. As with the previous argument, should be either Build class object or particular last complete build object. :returns: SQLA expression that, when evaluated in the DB, returns the priority """ # if last_build is concrete object, it may be None # packages with no last build should have no priority # (they cannot be scheduled) if not last_build: return null() dynamic_priority = cls.dependency_priority + cls.build_priority # compute time priority seconds = extract('EPOCH', func.clock_timestamp() - last_build.started) a, b = TIME_PRIORITY.inputs # avoid zero/negative values, when time difference too small log_arg = func.greatest(0.000001, seconds / 3600) dynamic_priority += func.greatest(a * func.log(log_arg) + b, -30) # dynamic priority is affected by coefficient dynamic_priority *= collection.priority_coefficient # manual and static priority are not affected by coefficient current_priority = cls.manual_priority + cls.static_priority + dynamic_priority return case( [ # handle unschedulable packages ( # WHEN blocked OR untracked cls.blocked | ~cls.tracked | # OR has running build (cls.last_complete_build_id != cls.last_build_id) | # OR is unresolved (cls.resolved == False) | # OR resolution is not yet done ((cls.resolved == None) & ~cls.skip_resolution) | # OR the collection's buildroot is broken (collection.latest_repo_resolved == False) | # OR the collection's buildroot wasn't resolved yet (collection.latest_repo_resolved == None), # THEN return NULL None, ) ], # ELSE return the computed priority else_=current_priority )