Esempio n. 1
0
    def _assign_tickets(self):
        with session_scope() as session:
            qticket = QTickets(session)
            tickets = [
                x.id
                for x in qticket.waiting().order_by(models.Ticket.id).all()
            ]

        for ticket_id in tickets:
            notify_ticket = False
            with session_scope() as session:
                ticket = session.query(models.Ticket).get(ticket_id)
                qres = QResources(session)
                resources = qres.ready().all()
                ticket_tags = ticket.tag_set
                for resource in resources:
                    res_tags = resource.tag_set
                    if ticket_tags.issubset(res_tags):
                        # We have found appropriate resource!
                        ticket.resource_id = resource.id
                        if ticket.tid:
                            notify_ticket = ticket.tid
                        session.add_all([ticket])
                        break
            if notify_ticket:
                self._notify_waiting(notify_ticket)
Esempio n. 2
0
    def _assign_tickets(self):
        with session_scope() as session:
            qticket = QTickets(session)
            tickets = [x.id for x in qticket.waiting().order_by(models.Ticket.id).all()]

        for ticket_id in tickets:
            notify_ticket = False
            with session_scope() as session:
                ticket = session.query(models.Ticket).get(ticket_id)
                qres = QResources(session)
                resources = qres.ready().all()
                ticket_tags = ticket.tag_set
                for resource in resources:
                    res_tags = resource.tag_set
                    if resource.sandbox and resource.sandbox != ticket.sandbox:
                        continue
                    if not ticket_tags.issubset(res_tags):
                        continue

                    # We have found appropriate resource!
                    log.debug("Assigning %s to %s", resource.name, ticket.id)
                    assign_ticket(resource, ticket)
                    if ticket.tid:
                        notify_ticket = ticket.tid
                    break

            if notify_ticket:
                self._notify_waiting(notify_ticket)
Esempio n. 3
0
    def resource_delete(self, resources=None):
        if not resources or type(resources) != list:
            log.error("no resources specified")
            return

        for res_id in resources:
            with session_scope() as session:
                resources = QResources(session=session)
                resources.kill(res_id)
Esempio n. 4
0
    def _assign_tickets(self):
        with session_scope() as session:
            qticket = QTickets(session)
            tickets = [x.id for x in qticket.waiting().order_by(models.Ticket.id).all()]

        for ticket_id in tickets:
            notify_ticket = False
            with session_scope() as session:
                ticket = session.query(models.Ticket).get(ticket_id)
                qres = QResources(session)
                resources = qres.ready().all()

                if not resources:
                    app.log.debug("No available resource, skipping %s", ticket)
                    continue

                queue = PriorityQueue()
                ticket_tags = ticket.tag_set
                for resource in resources:
                    res_tags = resource.tag_set
                    if resource.sandbox and resource.sandbox != ticket.sandbox:
                        continue
                    if not ticket_tags.issubset(res_tags):
                        continue

                    priority = 0
                    for tag in resource.tags:
                        if tag.priority is not None and tag.id in ticket_tags:
                            priority += tag.priority

                    if resource.sandbox:
                        # Re-used resources should be preferred to avoid
                        # allocating new and new resources for the same
                        # sandboxes.  TODO, make this configurable once needed.
                        priority += REUSED_RESOURCE_PRIORITY

                    queue.add_task(resource, priority)

                try:
                    resource = queue.pop_task()
                except KeyError:
                    app.log.debug("%d resources UP but unusable for %s",
                                  len(resources), ticket)
                    continue

                # we found an appropriate resource
                app.log.debug("Assigning %s to %s", resource.name, ticket.id)
                assign_ticket(resource, ticket)
                if ticket.tid:
                    notify_ticket = ticket.tid

            # notify ticket when the session is closed (to have short sessions)
            if notify_ticket:
                self._notify_waiting(notify_ticket)
Esempio n. 5
0
    def _detect_closed_tickets(self, event):
        with session_scope() as session:
            qres = QResources(session, pool=self.name)

            for resource in qres.taken():
                ticket = resource.ticket
                assert ticket
                if ticket.state == helpers.TState.CLOSED:
                    release_resource(ticket)
                    if self.cmd_release:
                        # UP → RELEASING → UP, TODO: we might want to optimize
                        # this a bit, and stop calling the releasing script when
                        # the resource is not releasable anymore (max_reuses
                        # reached, etc.).
                        resource.state = helpers.RState.RELEASING
                        ReleaseWorker(event, self, int(resource.id)).start()
Esempio n. 6
0
    def _allocate_more_resources(self, event):
        while True:
            with session_scope() as session:
                qres = QResources(session, pool=self.name)
                stats = qres.stats()

            msg = "=> POOL('{0}'):".format(self.name)
            for key, val in stats.items():
                msg = msg + ' {0}={1}'.format(key, val)
            log.debug(msg)

            if stats['on'] >= self.max \
                   or stats['ready'] + stats['start'] >= self.max_prealloc \
                   or stats['start'] >= self.max_starting \
                   or self._too_soon():
                # Quota reached, don't allocate more.
                break

            self.allocate(event)
Esempio n. 7
0
    def resource_list(self, up=None):
        with session_scope() as session:
            resources = QResources(session)
            if up:
                resources = resources.up()
            else:
                resources = resources.on()

            for resource in resources.all():
                msg = "{id} - {name} pool={pool} tags={tags} status={status}"
                tags = ','.join(list(resource.tag_set))
                print(
                    msg.format(
                        id=resource.id,
                        name=resource.name,
                        pool=resource.pool,
                        tags=tags,
                        status=resource.state,
                    ))
Esempio n. 8
0
    def foreach_resource(self, args):
        """ Execute shell command for each resource """
        command = args.command
        with session_scope() as session:
            resources = QResources(session)
            for resource in resources.on().all():
                try:
                    utf_data = ""
                    if resource.data:
                        utf_data = resource.data.decode("utf8")
                    command = args.command.format(
                        name=resource.name,
                        state=resource.state,
                        data_utf8=utf_data,
                    )
                except KeyError as err:
                    sys.stderr.write(str(err))

                subprocess.call(command, shell=True)
Esempio n. 9
0
    def _request_resource_removal(self):
        with session_scope() as session:
            now = time.time()
            qres = QResources(session, pool=self.name)

            for res in qres.check_failure_candidates():
                if res.check_failed_count >= 3:
                    app.log.debug("Removing %s, continuous failures", res.name)
                    res.state = RState.DELETE_REQUEST
                    continue

            for res in qres.clean_candidates():
                if not self.reuse_opportunity_time:
                    # reuse turned off by default, remove no matter what
                    app.log.debug("Removing %s, not reusable", res.name)
                    res.state = RState.DELETE_REQUEST
                    continue

                if res.released_at < (now - self.reuse_opportunity_time):
                    app.log.debug("Removing %s, not taken quickly enough", res.name)
                    res.state = RState.DELETE_REQUEST
                    continue

                if self.reuse_max_time:
                    last_allowed = now - self.reuse_max_time
                    if res.sandboxed_since < last_allowed:
                        app.log.debug(
                                  "Removing %s, too long in one sandbox, "
                                  "since %s, last_allowed %s, now %s",
                                  res.name, res.sandboxed_since, last_allowed,
                                  now)
                        res.state = RState.DELETE_REQUEST
                        continue

                if self.reuse_max_count and \
                        res.releases_counter > self.reuse_max_count:
                    app.log.debug("Removing %s, max reuses reached", res.name)
                    res.state = RState.DELETE_REQUEST
                    continue
Esempio n. 10
0
    def loop(self):
        log.debug("Watcher loop")
        pools = reload_config()
        to_check = {}
        with session_scope() as session:
            # Even though we never terminate resources that have assigned
            # ticket, we still check them.  This raises the check limit before
            # user releases the ticket and the resource can be terminated as
            # soon as possible.
            up = QResources(session).up().all()
            for item in up:
                if not item.pool in pools:
                    continue
                to_check[item.id] = {
                    'name': item.name,
                    'pool': item.pool,
                    'last': item.check_last_time,
                    'fail': item.check_failed_count,
                    'id_in_pool': item.id_in_pool,
                    'data': item.data,
                }

        for res_id, data in to_check.items():
            pool = pools[data['pool']]
            if not pool.cmd_livecheck:
                continue
            if data['last'] + pool.livecheck_period > time.time():
                # Not yet needed check.
                continue

            rc = run_command(
                    pool.id,
                    res_id,
                    data['name'],
                    data['id_in_pool'],
                    pool.cmd_livecheck,
                    'watch',
                    data=data["data"],
            )

            with session_scope() as session:
                res = session.query(models.Resource).get(res_id)
                res.check_last_time = time.time()
                if rc['status']:
                    res.check_failed_count = res.check_failed_count + 1
                    log.debug("failed check #{0} for {1}"\
                            .format(res.check_failed_count, res_id))
                else:
                    res.check_failed_count = 0
                session.add(res)
                session.flush()
Esempio n. 11
0
    def resource_list(self, up=None):
        with session_scope() as session:
            resources = QResources(session)
            if up:
                resources = resources.up()
            else:
                resources = resources.on()

            for resource in resources.all():
                msg = ("{id} - {name} pool={pool} tags={tags} status={status} "
                       "releases={releases} ticket={ticket}")
                tags = ','.join(list(resource.tag_set))
                print(
                    msg.format(
                        id=resource.id,
                        name=resource.name,
                        pool=resource.pool,
                        tags=tags,
                        status=resource.state,
                        releases=resource.releases_counter,
                        ticket=resource.ticket.id
                        if resource.ticket else 'NULL',
                    ))
Esempio n. 12
0
    def resource_delete(self, args):
        resources = args.resource

        with session_scope() as session:
            qresources = QResources(session=session)
            if args.all:
                resources = [res.id for res in qresources.up()]
            elif args.unused:
                resources = [res.id for res in qresources.ready()]

        if not resources or type(resources) != list:
            log.error("no resources specified")
            return

        for res_id in resources:
            with session_scope() as session:
                qresources = QResources(session=session)
                qresources.kill(res_id)
Esempio n. 13
0
    def loop(self):
        log.debug("Watcher loop")
        pools = reload_config()
        to_check = {}
        with session_scope() as session:
            up = QResources(session).up().all()
            for item in up:
                if not item.pool in pools:
                    continue
                to_check[item.id] = {
                    'name': item.name,
                    'pool': item.pool,
                    'last': item.check_last_time,
                    'fail': item.check_failed_count,
                    'id_in_pool': item.id_in_pool,
                }

        for res_id, data in to_check.items():
            pool = pools[data['pool']]
            if not pool.cmd_livecheck:
                continue
            if data['last'] + pool.livecheck_period > time.time():
                # Not yet needed check.
                continue

            failed_count = 0
            rc = run_command(pool.id, res_id, data['name'], data['id_in_pool'],
                             pool.cmd_livecheck, 'watch')

            with session_scope() as session:
                res = session.query(models.Resource).get(res_id)
                res.check_last_time = time.time()
                if rc['status']:
                    res.check_failed_count = res.check_failed_count + 1
                    log.debug("failed check #{0} for {1}"\
                            .format(res.check_failed_count, res_id))
                else:
                    res.check_failed_count = 0
                session.add(res)
                session.flush()
                failed_count = res.check_failed_count

            if failed_count >= 3:
                log.debug("Watcher plans to kill {0}".format(res_id))
                TerminateWorker(self.event, pool, res_id).start()
Esempio n. 14
0
def main():
    """ module entrypoint """
    # Create the database, if not exist yet.
    init_by_alembic()

    # Synchronization tool.
    sync = Synchronizer()

    # Delete leftovers from previous session, we need to run everything
    # asynchronously, see https://github.com/praiskup/resalloc/issues/41
    with session_scope() as session:
        QResources(session=session).fix_broken_after_restart(app.log)

    # Start server on background.
    server = Server()
    server.sync = sync
    server.start()

    try:
        Manager(sync).run()
    except KeyboardInterrupt:
        pass
    finally:
        server.shutdown()
Esempio n. 15
0
 def _garbage_collector(self, event):
     to_terminate = []
     with session_scope() as session:
         qres = QResources(session, pool=self.name)
         for res in qres.clean().all():
             TerminateWorker(event, self, int(res.id)).start()
Esempio n. 16
0
 def _detect_closed_tickets(self):
     with session_scope() as session:
         qres = QResources(session, pool=self.name)
         for res in qres.clean_candidates().all():
             res.state = RState.DELETE_REQUEST
             session.add(res)