Exemple #1
0
    def _defaultSorter(self, master, builders):
        timer = metrics.Timer("BuildRequestDistributor._defaultSorter()")
        timer.start()

        # perform an asynchronous schwarzian transform, transforming None
        # into sys.maxint so that it sorts to the end
        def xform(bldr):
            d = defer.maybeDeferred(lambda: bldr.getOldestRequestTime())
            d.addCallback(lambda time:
                          (((time is None) and None or time), bldr))
            return d

        xformed = yield defer.gatherResults([xform(bldr) for bldr in builders])

        # sort the transformed list synchronously, comparing None to the end of
        # the list
        def nonecmp(a, b):
            if a[0] is None: return 1
            if b[0] is None: return -1
            return cmp(a, b)

        xformed.sort(cmp=nonecmp)

        # and reverse the transform
        rv = [xf[1] for xf in xformed]
        timer.stop()
        defer.returnValue(rv)
Exemple #2
0
    def _activityLoop(self):
        self.active = True

        timer = metrics.Timer('BuildRequestDistributor._activityLoop()')
        timer.start()

        while 1:
            yield self.activity_lock.acquire()

            # lock pending_builders, pop an element from it, and release
            yield self.pending_builders_lock.acquire()

            # bail out if we shouldn't keep looping
            if not self.running or not self._pending_builders:
                self.pending_builders_lock.release()
                self.activity_lock.release()
                break

            bldr_name = self._pending_builders.pop(0)
            self.pending_builders_lock.release()

            try:
                yield self._callABuilder(bldr_name)
            except Exception:
                log.err(
                    Failure(),
                    "from maybeStartBuild for builder '%s'" % (bldr_name, ))

            self.activity_lock.release()

        timer.stop()

        self.active = False
        self._quiet()
Exemple #3
0
    def reconfig(self):
        # this method wraps doConfig, ensuring it is only ever called once at
        # a time, and alerting the user if the reconfig takes too long
        if self.reconfig_active:
            log.msg("reconfig already active; will reconfig again after")
            self.reconfig_requested = True
            return

        self.reconfig_active = self.reactor.seconds()
        metrics.MetricCountEvent.log("loaded_config", 1)

        # notify every 10 seconds that the reconfig is still going on, the duration of reconfigs is
        # longer on larger installations and may take a while.
        self.reconfig_notifier = task.LoopingCall(
            lambda: log.msg("reconfig is ongoing for "
                            f"{self.reactor.seconds() - self.reconfig_active:.3f} s"))
        self.reconfig_notifier.start(10, now=False)

        timer = metrics.Timer("BuildMaster.reconfig")
        timer.start()

        try:
            yield self.doReconfig()
        except Exception as e:
            log.err(e, 'while reconfiguring')
        finally:
            timer.stop()
            self.reconfig_notifier.stop()
            self.reconfig_notifier = None
            self.reconfig_active = False
            if self.reconfig_requested:
                self.reconfig_requested = False
                self.reconfig()
Exemple #4
0
    def reconfigServiceWithBuildbotConfig(self, new_config):
        timer = metrics.Timer(
            "ChangeManager.reconfigServiceWithBuildbotConfig")
        timer.start()

        removed, added = util.diffSets(set(self), new_config.change_sources)

        if removed or added:
            log.msg("adding %d new changesources, removing %d" %
                    (len(added), len(removed)))

            for src in removed:
                yield defer.maybeDeferred(src.disownServiceParent)

            for src in added:
                yield src.setServiceParent(self)

        num_sources = len(list(self))
        assert num_sources == len(new_config.change_sources)
        metrics.MetricCountEvent.log("num_sources", num_sources, absolute=True)

        # reconfig any newly-added change sources, as well as existing
        yield service.ReconfigurableServiceMixin.reconfigServiceWithBuildbotConfig(
            self, new_config)

        timer.stop()
Exemple #5
0
    def _sortBuilders(self, buildernames):
        timer = metrics.Timer("BuildRequestDistributor._sortBuilders()")
        timer.start()
        # note that this takes and returns a list of builder names

        # convert builder names to builders
        builders_dict = self.botmaster.builders
        builders = [
            builders_dict.get(n) for n in buildernames if n in builders_dict
        ]

        # find a sorting function
        sorter = self.botmaster.prioritizeBuilders
        if not sorter:
            sorter = self._defaultSorter

        # run it
        try:
            wfd = defer.waitForDeferred(
                defer.maybeDeferred(lambda: sorter(self.master, builders)))
            yield wfd
            builders = wfd.getResult()
        except:
            log.msg("Exception prioritizing builders; order unspecified")
            log.err(Failure())

        # and return the names
        yield [b.name for b in builders]
        timer.stop()
Exemple #6
0
    def reconfigService(self, new_config):
        timer = metrics.Timer("BotMaster.reconfigService")
        timer.start()

        # reconfigure slaves
        wfd = defer.waitForDeferred(self.reconfigServiceSlaves(new_config))
        yield wfd
        wfd.getResult()

        # reconfigure builders
        wfd = defer.waitForDeferred(self.reconfigServiceBuilders(new_config))
        yield wfd
        wfd.getResult()

        wfd = defer.waitForDeferred(
            config.ReconfigurableServiceMixin.reconfigService(
                self, new_config))
        yield wfd
        wfd.getResult()

        # try to start a build for every builder; this is necessary at master
        # startup, and a good idea in any other case
        self.maybeStartBuildsForAllBuilders()

        timer.stop()
Exemple #7
0
 def reconfigServiceWithBuildbotConfig(self, new_config):
     timer = metrics.Timer("f{self.name}.reconfigServiceWithBuildbotConfig")
     timer.start()
     yield super().reconfigServiceWithBuildbotConfig(new_config)
     metrics.MetricCountEvent.log(f"num_{self.managed_services_name}",
                                  len(list(self)), absolute=True)
     timer.stop()
    def _sortBuilders(self, buildernames):
        timer = metrics.Timer("BuildRequestDistributor._sortBuilders()")
        timer.start()
        # note that this takes and returns a list of builder names

        # convert builder names to builders
        builders_dict = self.botmaster.builders
        builders = [
            builders_dict.get(n) for n in buildernames if n in builders_dict
        ]

        # find a sorting function
        sorter = self.master.config.prioritizeBuilders
        if not sorter:
            sorter = self._defaultSorter

        # run it
        try:
            builders = yield defer.maybeDeferred(sorter, self.master, builders)
        except Exception:
            log.err(Failure(), "prioritizing builders; order unspecified")

        # and return the names
        rv = [b.name for b in builders]
        timer.stop()
        defer.returnValue(rv)
Exemple #9
0
 def reconfigServiceWithBuildbotConfig(self, new_config):
     timer = metrics.Timer(
         "{0}.reconfigServiceWithBuildbotConfig".format(self.name))
     timer.start()
     yield super(MeasuredBuildbotServiceManager, self).reconfigServiceWithBuildbotConfig(new_config)
     metrics.MetricCountEvent.log("num_{0}".format(self.managed_services_name),
                                  len(list(self)), absolute=True)
     timer.stop()
Exemple #10
0
    def pollDatabaseChanges(self):
        # Older versions of Buildbot had each scheduler polling the database
        # independently, and storing a "last_processed" state indicating the
        # last change it had processed.  This had the advantage of allowing
        # schedulers to pick up changes that arrived in the database while
        # the scheduler was not running, but was horribly inefficient.

        # This version polls the database on behalf of the schedulers, using a
        # similar state at the master level.

        timer = metrics.Timer("BuildMaster.pollDatabaseChanges()")
        timer.start()

        need_setState = False

        # get the last processed change id
        if self._last_processed_change is None:
            self._last_processed_change = \
                yield self._getState('last_processed_change')

        # if it's still None, assume we've processed up to the latest changeid
        if self._last_processed_change is None:
            lpc = yield self.db.changes.getLatestChangeid()
            # if there *are* no changes, count the last as '0' so that we don't
            # skip the first change
            if lpc is None:
                lpc = 0
            self._last_processed_change = lpc

            need_setState = True

        if self._last_processed_change is None:
            timer.stop()
            return

        while True:
            changeid = self._last_processed_change + 1
            chdict = yield self.db.changes.getChange(changeid)

            # if there's no such change, we've reached the end and can
            # stop polling
            if not chdict:
                break

            change = yield changes.Change.fromChdict(self, chdict)

            self._change_subs.deliver(change)

            self._last_processed_change = changeid
            need_setState = True

        # write back the updated state, if it's changed
        if need_setState:
            yield self._setState('last_processed_change',
                                 self._last_processed_change)
        timer.stop()
Exemple #11
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = dict([(b.name, b) for b in list(self)
                            if isinstance(b, Builder)])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([(bc.name, bc) for bc in new_config.builders])
        new_set = set(new_by_name.iterkeys())

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                yield defer.maybeDeferred(
                    lambda: builder.disownServiceParent())

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                builder.setServiceParent(self)

        self.builderNames = self.builders.keys()

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders),
                                     absolute=True)

        # remove unclaimed builds if the builder has been removed from configuration
        if len(self.master.config.projects) > 1:
            queued_builds = yield self.master.db.buildrequests.getBuildRequestInQueue(
                sorted=True)
            # TODO: if we are running in multimaster mode with multiple instance of katana we need
            # to check for the project key as well
            removed_builders = [
                b for b in queued_builds if b["buildername"] not in new_set
            ]
            self.removeQueuedBuilds(removed_builders)

        timer.stop()
Exemple #12
0
    def testTimer(self):
        clock = task.Clock()
        t = metrics.Timer('foo_time')
        t._reactor = clock
        t.start()

        clock.advance(5)
        t.stop()

        report = self.observer.asDict()
        self.assertEquals(report['timers']['foo_time'], 5)
Exemple #13
0
    def reconfigServiceSlaves(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceSlaves")
        timer.start()

        # arrange slaves by name
        old_by_name = dict([(s.slavename, s) for s in list(self)
                            if interfaces.IBuildSlave.providedBy(s)])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([(s.slavename, s) for s in new_config.slaves])
        new_set = set(new_by_name.iterkeys())

        # calculate new slaves, by name, and removed slaves
        removed_names, added_names = util.diffSets(old_set, new_set)

        # find any slaves for which the fully qualified class name has
        # changed, and treat those as an add and remove
        for n in old_set & new_set:
            old = old_by_name[n]
            new = new_by_name[n]
            # detect changed class name
            if reflect.qual(old.__class__) != reflect.qual(new.__class__):
                removed_names.add(n)
                added_names.add(n)

        if removed_names or added_names:
            log.msg("adding %d new slaves, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                slave = old_by_name[n]

                del self.slaves[n]
                slave.master = None
                slave.botmaster = None

                wfd = defer.waitForDeferred(
                    defer.maybeDeferred(lambda: slave.disownServiceParent()))
                yield wfd
                wfd.getResult()

            for n in added_names:
                slave = new_by_name[n]
                slave.setServiceParent(self)

                slave.botmaster = self
                slave.master = self.master
                self.slaves[n] = slave

        metrics.MetricCountEvent.log("num_slaves",
                                     len(self.slaves),
                                     absolute=True)

        timer.stop()
Exemple #14
0
    def reconfigServiceWithBuildbotConfig(self, new_config):

        timer = metrics.Timer("BuildSlaveManager.reconfigServiceSlaves")
        timer.start()

        yield service.BuildbotServiceManager.reconfigServiceWithBuildbotConfig(self,
                                                                               new_config)

        metrics.MetricCountEvent.log("num_slaves",
                                     len(self.slaves), absolute=True)

        timer.stop()
Exemple #15
0
    def reconfigServiceSlaves(self, new_config):

        timer = metrics.Timer("BuildSlaveManager.reconfigServiceSlaves")
        timer.start()

        # first we deconfigure everything to let the slaves register again
        yield self.master.data.updates.deconfigureAllBuidslavesForMaster(
            self.master.masterid)

        # arrange slaves by name
        old_by_name = dict([(s.slavename, s) for s in list(self)
                            if interfaces.IBuildSlave.providedBy(s)])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([(s.slavename, s) for s in new_config.slaves])
        new_set = set(new_by_name.iterkeys())

        # calculate new slaves, by name, and removed slaves
        removed_names, added_names = util.diffSets(old_set, new_set)

        # find any slaves for which the fully qualified class name has
        # changed, and treat those as an add and remove
        for n in old_set & new_set:
            old = old_by_name[n]
            new = new_by_name[n]
            # detect changed class name
            if reflect.qual(old.__class__) != reflect.qual(new.__class__):
                removed_names.add(n)
                added_names.add(n)

        if removed_names or added_names:
            log.msg("adding %d new slaves, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                slave = old_by_name[n]

                del self.slaves[n]
                slave.master = None

                yield slave.disownServiceParent()

            for n in added_names:
                slave = new_by_name[n]
                yield slave.setServiceParent(self)
                self.slaves[n] = slave

        metrics.MetricCountEvent.log("num_slaves",
                                     len(self.slaves),
                                     absolute=True)

        timer.stop()
Exemple #16
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = {b.name: b
                       for b in list(self)
                       if isinstance(b, Builder)}
        old_set = set(old_by_name)
        new_by_name = {bc.name: bc
                       for bc in new_config.builders}
        new_set = set(new_by_name)

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                # pylint: disable=cell-var-from-loop
                yield defer.maybeDeferred(lambda:
                                          builder.disownServiceParent())

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                yield builder.setServiceParent(self)

        self.builderNames = list(self.builders)

        yield self.master.data.updates.updateBuilderList(
            self.master.masterid,
            [util.bytes2unicode(n) for n in self.builderNames])

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders), absolute=True)

        timer.stop()
Exemple #17
0
    def pollDatabaseBuildRequests(self):
        # deal with cleaning up unclaimed requests, and (if necessary)
        # requests from a previous instance of this master
        timer = metrics.Timer("BuildMaster.pollDatabaseBuildRequests()")
        timer.start()

        # cleanup unclaimed builds
        since_last_cleanup = reactor.seconds() - self._last_claim_cleanup 
        if since_last_cleanup < self.RECLAIM_BUILD_INTERVAL:
            unclaimed_age = (self.RECLAIM_BUILD_INTERVAL
                           * self.UNCLAIMED_BUILD_FACTOR)
            wfd = defer.waitForDeferred(
                self.db.buildrequests.unclaimExpiredRequests(unclaimed_age))
            yield wfd
            wfd.getResult()

            self._last_claim_cleanup = reactor.seconds()

        # _last_unclaimed_brids_set tracks the state of unclaimed build
        # requests; whenever it sees a build request which was not claimed on
        # the last poll, it notifies the subscribers.  It only tracks that
        # state within the master instance, though; on startup, it notifies for
        # all unclaimed requests in the database.

        last_unclaimed = self._last_unclaimed_brids_set or set()
        if len(last_unclaimed) > self.WARNING_UNCLAIMED_COUNT:
            log.msg("WARNING: %d unclaimed buildrequests - is a scheduler "
                    "producing builds for which no builder is running?"
                    % len(last_unclaimed))

        # get the current set of unclaimed buildrequests
        wfd = defer.waitForDeferred(
            self.db.buildrequests.getBuildRequests(claimed=False))
        yield wfd
        now_unclaimed_brdicts = wfd.getResult()
        now_unclaimed = set([ brd['brid'] for brd in now_unclaimed_brdicts ])

        # and store that for next time
        self._last_unclaimed_brids_set = now_unclaimed

        # see what's new, and notify if anything is
        new_unclaimed = now_unclaimed - last_unclaimed
        if new_unclaimed:
            brdicts = dict((brd['brid'], brd) for brd in now_unclaimed_brdicts)
            for brid in new_unclaimed:
                brd = brdicts[brid]
                self.buildRequestAdded(brd['buildsetid'], brd['brid'],
                                       brd['buildername'])
        timer.stop()
Exemple #18
0
    def reconfigServiceWithBuildbotConfig(self, new_config):
        timer = metrics.Timer("BotMaster.reconfigServiceWithBuildbotConfig")
        timer.start()

        # reconfigure builders
        yield self.reconfigServiceBuilders(new_config)

        # call up
        yield super().reconfigServiceWithBuildbotConfig(new_config)

        # try to start a build for every builder; this is necessary at master
        # startup, and a good idea in any other case
        self.maybeStartBuildsForAllBuilders()

        timer.stop()
Exemple #19
0
    def reconfigService(self, new_config):
        timer = metrics.Timer("BotMaster.reconfigService")
        timer.start()

        # reconfigure slaves
        yield self.reconfigServiceSlaves(new_config)

        # reconfigure builders
        yield self.reconfigServiceBuilders(new_config)

        # call up
        yield config.ReconfigurableServiceMixin.reconfigService(
            self, new_config)

        timer.stop()
Exemple #20
0
    def reconfigServiceBuilders(self, new_config):

        timer = metrics.Timer("BotMaster.reconfigServiceBuilders")
        timer.start()

        # arrange builders by name
        old_by_name = dict([(b.name, b) for b in list(self)
                            if isinstance(b, Builder)])
        old_set = set(old_by_name.iterkeys())
        new_by_name = dict([(bc.name, bc) for bc in new_config.builders])
        new_set = set(new_by_name.iterkeys())

        # calculate new builders, by name, and removed builders
        removed_names, added_names = util.diffSets(old_set, new_set)

        if removed_names or added_names:
            log.msg("adding %d new builders, removing %d" %
                    (len(added_names), len(removed_names)))

            for n in removed_names:
                builder = old_by_name[n]

                del self.builders[n]
                builder.master = None
                builder.botmaster = None

                wfd = defer.waitForDeferred(
                    defer.maybeDeferred(lambda: builder.disownServiceParent()))
                yield wfd
                wfd.getResult()

            for n in added_names:
                builder = Builder(n)
                self.builders[n] = builder

                builder.botmaster = self
                builder.master = self.master
                builder.setServiceParent(self)

        self.builderNames = self.builders.keys()

        metrics.MetricCountEvent.log("num_builders",
                                     len(self.builders),
                                     absolute=True)

        timer.stop()
Exemple #21
0
    def _updateAllSlaves(self):
        """Notify all buildslaves about changes in their Builders."""
        timer = metrics.Timer("BotMaster._updateAllSlaves()")
        timer.start()
        dl = []
        for s in self.slaves.values():
            d = s.updateSlave()
            d.addErrback(log.err)
            dl.append(d)
        d = defer.DeferredList(dl)

        def stop(_):
            timer.stop()
            return _

        d.addBoth(stop)
        return d
    def _activityLoop(self):
        self.active = True

        timer = metrics.Timer('BuildRequestDistributor._activityLoop()')
        timer.start()
        pending_builders = []
        while True:
            yield self.activity_lock.acquire()
            if not self.running:
                self.activity_lock.release()
                break

            if not pending_builders:
                # lock pending_builders, pop an element from it, and release
                yield self.pending_builders_lock.acquire()

                # bail out if we shouldn't keep looping
                if not self._pending_builders:
                    self.pending_builders_lock.release()
                    self.activity_lock.release()
                    break
                # take that builder list, and run it until the end
                # we make a copy of it, as it could be modified meanwhile
                pending_builders = copy.copy(self._pending_builders)
                self._pending_builders = []
                self.pending_builders_lock.release()

            bldr_name = pending_builders.pop(0)

            # get the actual builder object
            bldr = self.botmaster.builders.get(bldr_name)
            try:
                if bldr:
                    yield self._maybeStartBuildsOnBuilder(bldr)
            except Exception:
                log.err(
                    Failure(),
                    "from maybeStartBuild for builder '%s'" % (bldr_name, ))

            self.activity_lock.release()

        timer.stop()

        self.active = False
        self._quiet()
    def _defaultSorter(self, master, builders):
        timer = metrics.Timer("BuildRequestDistributor._defaultSorter()")
        timer.start()

        # perform an asynchronous schwarzian transform, transforming None
        # into sys.maxint so that it sorts to the end

        def xform(bldr):
            d = defer.maybeDeferred(bldr.getOldestRequestTime)
            d.addCallback(lambda time:
                          (((time is None) and None or time), bldr))
            return d

        xformed = yield defer.gatherResults([xform(bldr) for bldr in builders])

        # sort the transformed list synchronously, comparing None to the end of
        # the list
        def xformedKey(a):
            """
            Key function can be used to sort a list
            where each list element is a tuple:
                (datetime.datetime, Builder)

            @return: a tuple of (date, builder name)
            """
            (date, builder) = a
            if date is None:
                # Choose a really big date, so that any
                # date set to 'None' will appear at the
                # end of the list during comparisons.
                date = datetime.max
                # Need to set the timezone on the date, in order
                # to perform comparisons with other dates which
                # have the time zone set.
                date = date.replace(tzinfo=tzutc())
            return (date, builder.name)

        xformed.sort(key=xformedKey)

        # and reverse the transform
        rv = [xf[1] for xf in xformed]
        timer.stop()
        defer.returnValue(rv)
Exemple #24
0
    def testStartStopDecorators(self):
        clock = task.Clock()
        t = metrics.Timer('foo_time')
        t._reactor = clock

        @t.startTimer
        def foo():
            clock.advance(5)
            return "foo!"

        @t.stopTimer
        def bar():
            clock.advance(5)
            return "bar!"

        foo()
        bar()
        report = self.observer.asDict()
        self.assertEquals(report['timers']['foo_time'], 10)
Exemple #25
0
    def reconfigService(self, new_config):
        timer = metrics.Timer("BotMaster.reconfigService")
        timer.start()

        # reconfigure slaves
        yield self.reconfigServiceSlaves(new_config)

        # reconfigure builders
        yield self.reconfigServiceBuilders(new_config)

        # call up
        yield config.ReconfigurableServiceMixin.reconfigService(
            self, new_config)

        # try to start a build for every builder; this is necessary at master
        # startup, and a good idea in any other case
        self.maybeStartBuildsForAllBuilders()

        timer.stop()
Exemple #26
0
    def reconfig(self):
        # this method wraps doConfig, ensuring it is only ever called once at
        # a time, and alerting the user if the reconfig takes too long
        if self.reconfig_active:
            log.msg("reconfig already active; will reconfig again after")
            self.reconfig_requested = True
            return

        self.reconfig_active = self.reactor.seconds()
        metrics.MetricCountEvent.log("loaded_config", 1)

        # notify every 10 seconds that the reconfig is still going on, although
        # reconfigs should not take that long!
        self.reconfig_notifier = task.LoopingCall(
            lambda: log.msg("reconfig is ongoing for %d s" %
                            (self.reactor.seconds() - self.reconfig_active)))
        self.reconfig_notifier.start(10, now=False)

        timer = metrics.Timer("BuildMaster.reconfig")
        timer.start()

        d = self.doReconfig()

        @d.addBoth
        def cleanup(res):
            timer.stop()
            self.reconfig_notifier.stop()
            self.reconfig_notifier = None
            self.reconfig_active = False
            if self.reconfig_requested:
                self.reconfig_requested = False
                self.reconfig()
            return res

        @d.addCallback
        def advertiseItself(res):
            if self.masterHeartbeatService.parent is None:
                self.masterHeartbeatService.setServiceParent(self)

        d.addErrback(log.err, 'while reconfiguring')

        return d  # for tests
Exemple #27
0
    def _defaultSorter(self, master, builders):
        timer = metrics.Timer("BuildRequestDistributor._defaultSorter()")
        timer.start()

        @defer.inlineCallbacks
        def key(bldr):
            # Sort by time of oldest build request
            time = yield bldr.getOldestRequestTime()
            if time is None:
                # for builders that do not have pending buildrequest, we just use large number
                time = math.inf
            else:
                if isinstance(time, datetime):
                    time = time.timestamp()
            return (time, bldr.name)

        yield async_sort(builders, key)

        timer.stop()
        return builders
Exemple #28
0
    def _defaultSorter(self, master, builders):
        timer = metrics.Timer("BuildRequestDistributor._defaultSorter()")
        timer.start()

        # perform an asynchronous schwarzian transform, transforming None
        # into sys.maxint so that it sorts to the end

        def xform(bldr):
            d = defer.maybeDeferred(lambda: bldr.getOldestRequestTime())
            d.addCallback(lambda time:
                          (((time is None) and None or time), bldr))
            return d

        xformed = yield defer.gatherResults([xform(bldr) for bldr in builders])

        # sort the transformed list synchronously, comparing None to the end of
        # the list
        def nonekey(a):
            if a[0] is None:
                b = list(a)
                # Choose a really big date, so that any
                # date set to 'None' will appear at the
                # end of the list during comparisons.
                b[0] = datetime.max
                # Need to set the timezone on the date, in order
                # to perform comparisons with other dates which
                # have the time zone set.
                b[0] = b[0].replace(tzinfo=tzutc())
                return tuple(b)
            return a

        xformed.sort(key=nonekey)

        # and reverse the transform
        rv = [xf[1] for xf in xformed]
        timer.stop()
        defer.returnValue(rv)
Exemple #29
0
    def reconfigService(self, new_config):
        timer = metrics.Timer("ChangeManager.reconfigService")
        timer.start()

        removed, added = util.diffSets(
                set(self),
                new_config.change_sources)

        if removed or added:
            log.msg("adding %d new changesources, removing %d" %
                    (len(added), len(removed)))

            for src in removed:
                wfd = defer.waitForDeferred(
                    defer.maybeDeferred(
                        src.disownServiceParent))
                yield wfd
                wfd.getResult()
                src.master = None

            for src in added:
                src.master = self.master
                src.setServiceParent(self)

        num_sources = len(list(self))
        assert num_sources == len(new_config.change_sources)
        metrics.MetricCountEvent.log("num_sources", num_sources, absolute=True)

        # reconfig any newly-added change sources, as well as existing
        wfd = defer.waitForDeferred(
            config.ReconfigurableServiceMixin.reconfigService(self,
                                                        new_config))
        yield wfd
        wfd.getResult()

        timer.stop()
Exemple #30
0
    def reconfigService(self, new_config):
        timer = metrics.Timer("SchedulerManager.reconfigService")
        timer.start()

        old_by_name = dict((sch.name, sch) for sch in self)
        old_set = set(old_by_name.iterkeys())
        new_by_name = new_config.schedulers
        new_set = set(new_by_name.iterkeys())

        removed_names, added_names = util.diffSets(old_set, new_set)

        # find any schedulers that don't know how to reconfig, and, if they
        # have changed, add them to both removed and added, so that we
        # run the new version.  While we're at it, find any schedulers whose
        # fully qualified class name has changed, and consider those a removal
        # and re-add as well.
        for n in old_set & new_set:
            old = old_by_name[n]
            new = new_by_name[n]
            # detect changed class name
            if reflect.qual(old.__class__) != reflect.qual(new.__class__):
                removed_names.add(n)
                added_names.add(n)

            # compare using ComparableMixin if they don't support reconfig
            elif not hasattr(old, 'reconfigService'):
                if old != new:
                    removed_names.add(n)
                    added_names.add(n)

        # removals first

        for sch_name in removed_names:
            log.msg("removing scheduler '%s'" % (sch_name, ))
            sch = old_by_name[sch_name]
            yield defer.maybeDeferred(lambda: sch.disownServiceParent())
            sch.master = None

        # .. then additions

        for sch_name in added_names:
            log.msg("adding scheduler '%s'" % (sch_name, ))
            sch = new_by_name[sch_name]

            # get the scheduler's objectid
            class_name = '%s.%s' % (sch.__class__.__module__,
                                    sch.__class__.__name__)
            objectid = yield self.master.db.state.getObjectId(
                sch.name, class_name)

            # set up the scheduler
            sch.objectid = objectid
            sch.master = self.master

            # *then* attacah and start it
            yield sch.setServiceParent(self)

        metrics.MetricCountEvent.log("num_schedulers",
                                     len(list(self)),
                                     absolute=True)

        # reconfig any newly-added schedulers, as well as existing
        yield config.ReconfigurableServiceMixin.reconfigService(
            self, new_config)

        timer.stop()