Example #1
0
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,
    )
Example #2
0
    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)
Example #3
0
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,
    )
Example #4
0
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,
    )
Example #5
0
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)
Example #6
0
    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)
Example #7
0
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
Example #9
0
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
Example #11
0
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)
Example #12
0
    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
        )