[Buildbot-commits] buildbot/buildbot scheduler.py,1.9,1.10

Brian Warner warner at users.sourceforge.net
Sat Nov 26 02:09:26 UTC 2005


Update of /cvsroot/buildbot/buildbot/buildbot
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv9411/buildbot

Modified Files:
	scheduler.py 
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-438
Creator:  Brian Warner <warner at lothar.com>

add cron-style 'scheduler.Nightly', thanks to Dobes Vandermeer

	* docs/buildbot.texinfo (Scheduler Types): give a few hints about
	what Schedulers are available

	* buildbot/scheduler.py (Nightly): add new Scheduler based upon
	work by Dobes Vandermeer and hacked mercilessly by me. This offers
	'cron'-style build scheduling at certain times of day, week,
	month, or year.
	* buildbot/test/test_scheduler.py (Scheduling.testNightly): test it


Index: scheduler.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/scheduler.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- scheduler.py	25 Nov 2005 23:26:35 -0000	1.9
+++ scheduler.py	26 Nov 2005 02:09:24 -0000	1.10
@@ -339,6 +339,164 @@
                                SourceStamp(branch=self.branch))
         self.submit(bs)
 
+
+
+class Nightly(BaseUpstreamScheduler):
+    """Imitate 'cron' scheduling. This can be used to schedule a nightly
+    build, or one which runs are certain times of the day, week, or month.
+
+    Pass some subset of minute, hour, dayOfMonth, month, and dayOfWeek; each
+    may be a single number or a list of valid values. The builds will be
+    triggered whenever the current time matches these values. Wildcards are
+    represented by a '*' string. All fields default to a wildcard except
+    'minute', so with no fields this defaults to a build every hour, on the
+    hour.
+
+    For example, the following master.cfg clause will cause a build to be
+    started every night at 3:00am:
+
+     s = Nightly('nightly', ['builder1', 'builder2'], hour=3, minute=0)
+     c['schedules'].append(s)
+
+    This scheduler will perform a build each monday morning at 6:23am and
+    again at 8:23am:
+
+     s = Nightly('BeforeWork', ['builder1'],
+                 dayOfWeek=0, hour=[6,8], minute=23)
+
+    The following runs a build every two hours:
+
+     s = Nightly('every2hours', ['builder1'], hour=range(0, 24, 2))
+
+    And this one will run only on December 24th:
+
+     s = Nightly('SleighPreflightCheck', ['flying_circuits', 'radar'],
+                 month=12, dayOfMonth=24, hour=12, minute=0)
+
+    For dayOfWeek and dayOfMonth, builds are triggered if the date matches
+    either of them.  Month and day numbers start at 1, not zero.
+    """
+
+    compare_attrs = ('name', 'builderNames',
+                     'minute', 'hour', 'dayOfMonth', 'month',
+                     'dayOfWeek', 'branch')
+
+    def __init__(self, name, builderNames, minute=0, hour='*',
+                 dayOfMonth='*', month='*', dayOfWeek='*',
+                 branch=None):
+        # Setting minute=0 really makes this an 'Hourly' scheduler. This
+        # seemed like a better default than minute='*', which would result in
+        # a build every 60 seconds.
+        BaseUpstreamScheduler.__init__(self, name)
+        self.builderNames = builderNames
+        self.minute = minute
+        self.hour = hour
+        self.dayOfMonth = dayOfMonth
+        self.month = month
+        self.dayOfWeek = dayOfWeek
+        self.branch = branch
+        self.delayedRun = None
+        self.nextRunTime = None
+
+    def addTime(self, timetuple, secs):
+        return time.localtime(time.mktime(timetuple)+secs)
+    def findFirstValueAtLeast(self, values, value, default=None):
+        for v in values:
+            if v >= value: return v
+        return default
+
+    def setTimer(self):
+        self.nextRunTime = self.calculateNextRunTime()
+        self.delayedRun = reactor.callLater(self.nextRunTime - time.time(),
+                                            self.doPeriodicBuild)
+
+    def startService(self):
+        BaseUpstreamScheduler.startService(self)
+        self.setTimer()
+
+    def stopService(self):
+        BaseUpstreamScheduler.stopService(self)
+        self.delayedRun.cancel()
+
+    def isRunTime(self, timetuple):
+        def check(ourvalue, value):
+            if ourvalue == '*': return True
+            if isinstance(ourvalue, int): return value == ourvalue
+            return (value in ourvalue)
+
+        if not check(self.minute, timetuple[4]):
+            #print 'bad minute', timetuple[4], self.minute
+            return False
+
+        if not check(self.hour, timetuple[3]):
+            #print 'bad hour', timetuple[3], self.hour
+            return False
+
+        if not check(self.month, timetuple[1]):
+            #print 'bad month', timetuple[1], self.month
+            return False
+
+        if self.dayOfMonth != '*' and self.dayOfWeek != '*':
+            # They specified both day(s) of month AND day(s) of week.
+            # This means that we only have to match one of the two. If
+            # neither one matches, this time is not the right time.
+            if not (check(self.dayOfMonth, timetuple[2]) or
+                    check(self.dayOfWeek, timetuple[6])):
+                #print 'bad day'
+                return False
+        else:
+            if not check(self.dayOfMonth, timetuple[2]):
+                #print 'bad day of month'
+                return False
+
+            if not check(self.dayOfWeek, timetuple[6]):
+                #print 'bad day of week'
+                return False
+
+        return True
+
+    def calculateNextRunTime(self):
+        return self.calculateNextRunTimeFrom(time.time())
+
+    def calculateNextRunTimeFrom(self, now):
+        dateTime = time.localtime(now)
+
+        # Remove seconds by advancing to at least the next minue
+        dateTime = self.addTime(dateTime, 60-dateTime[5])
+
+        # Now we just keep adding minutes until we find something that matches
+
+        # It not an efficient algorithm, but it'll *work* for now
+        yearLimit = dateTime[0]+2
+        while not self.isRunTime(dateTime):
+            dateTime = self.addTime(dateTime, 60)
+            #print 'Trying', time.asctime(dateTime)
+            assert dateTime[0] < yearLimit, 'Something is wrong with this code'
+        return time.mktime(dateTime)
+
+    def listBuilderNames(self):
+        return self.builderNames
+
+    def getPendingBuildTimes(self):
+        # TODO: figure out when self.timer is going to fire next and report
+        # that
+        if self.nextRunTime is None: return []
+        return [self.nextRunTime]
+
+    def doPeriodicBuild(self):
+        # Schedule the next run
+        self.setTimer()
+
+        # And trigger a build
+        bs = buildset.BuildSet(self.builderNames,
+                               SourceStamp(branch=self.branch))
+        self.submit(bs)
+
+    def addChange(self, change):
+        pass
+
+
+
 class TryBase(service.MultiService, util.ComparableMixin):
     if implements:
         implements(interfaces.IScheduler)





More information about the Commits mailing list