1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 from zope.interface import implements
17
18 from buildbot import util
19 from buildbot.interfaces import ITriggerableScheduler
20 from buildbot.process import buildstep, properties
21 from buildbot.schedulers import base
22 from twisted.internet import defer, reactor
23 from twisted.python import log
24 from buildbot import config
25 from buildbot.changes import filter
26
27 -class Timed(base.BaseScheduler):
28 """
29 Parent class for timed schedulers. This takes care of the (surprisingly
30 subtle) mechanics of ensuring that each timed actuation runs to completion
31 before the service stops.
32 """
33
34 compare_attrs = base.BaseScheduler.compare_attrs
35
36 - def __init__(self, name, builderNames, properties={}, **kwargs):
37 base.BaseScheduler.__init__(self, name, builderNames, properties,
38 **kwargs)
39
40
41 self.lastActuated = None
42
43
44
45 self.actuationLock = defer.DeferredLock()
46 self.actuateOk = False
47 self.actuateAt = None
48 self.actuateAtTimer = None
49
50 self._reactor = reactor
51
53 base.BaseScheduler.startService(self)
54
55
56 self.actuateOk = True
57
58
59 d = self.getState('last_build', None)
60 def set_last(lastActuated):
61 self.lastActuated = lastActuated
62 d.addCallback(set_last)
63
64
65 d.addCallback(lambda _ : self.scheduleNextBuild())
66
67
68 d.addCallback(lambda _ : self.startTimedSchedulerService())
69
70
71 d.addErrback(log.err, "while initializing %s '%s'" %
72 (self.__class__.__name__, self.name))
73
75 """Hook for subclasses to participate in the L{startService} process;
76 can return a Deferred"""
77
79
80
81
82 d = self.actuationLock.acquire()
83 def stop_actuating(_):
84 self.actuateOk = False
85 self.actuateAt = None
86 if self.actuateAtTimer:
87 self.actuateAtTimer.cancel()
88 self.actuateAtTimer = None
89 d.addCallback(stop_actuating)
90 d.addCallback(lambda _ : self.actuationLock.release())
91
92
93 d.addCallback(lambda _ : base.BaseScheduler.stopService(self))
94 return d
95
96
97
99
100
101 return [ self.actuateAt ]
102
103
104
106 """The time has come to start a new build. Returns a Deferred.
107 Override in subclasses."""
108 raise NotImplementedError
109
111 """
112 Called by to calculate the next time to actuate a BuildSet. Override
113 in subclasses. To trigger a fresh call to this method, use
114 L{rescheduleNextBuild}.
115
116 @param lastActuation: the time of the last actuation, or None for never
117
118 @returns: a Deferred firing with the next time a build should occur (in
119 the future), or None for never.
120 """
121 raise NotImplementedError
122
124 """
125 Schedule the next build, re-invoking L{getNextBuildTime}. This can be
126 called at any time, and it will avoid contention with builds being
127 started concurrently.
128
129 @returns: Deferred
130 """
131 d = self.actuationLock.acquire()
132 d.addCallback(lambda _ : self._scheduleNextBuild_locked())
133
134 def release(x):
135 self.actuationLock.release()
136 return x
137 d.addBoth(release)
138 return d
139
140
141
143 "Similar to util.now, but patchable by tests"
144 return util.now(self._reactor)
145
147
148 if self.actuateAtTimer:
149 self.actuateAtTimer.cancel()
150 self.actuateAtTimer = None
151
152
153 d = self.getNextBuildTime(self.lastActuated)
154
155
156 def set_timer(actuateAt):
157 now = self.now()
158 self.actuateAt = max(actuateAt, now)
159 if actuateAt is not None:
160 untilNext = self.actuateAt - now
161 if untilNext == 0:
162 log.msg(("%s: missed scheduled build time, so building "
163 "immediately") % self.name)
164 self.actuateAtTimer = self._reactor.callLater(untilNext,
165 self._actuate)
166 d.addCallback(set_timer)
167
168 return d
169
171
172 self.actuateAtTimer = None
173 self.lastActuated = self.actuateAt
174
175 d = self.actuationLock.acquire()
176
177 @defer.inlineCallbacks
178 def set_state_and_start(_):
179
180 if not self.actuateOk:
181 return
182
183
184 self.actuateAt = None
185 yield self.setState('last_build', self.lastActuated)
186
187
188 yield self.startBuild()
189
190
191 yield self._scheduleNextBuild_locked()
192 d.addCallback(set_state_and_start)
193
194 def unlock(x):
195 self.actuationLock.release()
196 return x
197 d.addBoth(unlock)
198
199
200
201 d.addErrback(log.err, 'while actuating')
202
205 compare_attrs = Timed.compare_attrs + ('periodicBuildTimer', 'branch',)
206
207 - def __init__(self, name, builderNames, periodicBuildTimer,
208 branch=None, properties={}, onlyImportant=False):
217
219 if lastActuated is None:
220 return defer.succeed(self.now())
221 else:
222 return defer.succeed(lastActuated + self.periodicBuildTimer)
223
226
228 compare_attrs = (Timed.compare_attrs
229 + ('minute', 'hour', 'dayOfMonth', 'month', 'dayOfWeek'))
230
234 Timed.__init__(self, name=name, builderNames=builderNames,
235 properties=properties, codebases=codebases)
236
237 self.minute = minute
238 self.hour = hour
239 self.dayOfMonth = dayOfMonth
240 self.month = month
241 self.dayOfWeek = dayOfWeek
242
244 if isinstance(time, int):
245 if isDayOfWeek:
246 time = (time + 1) % 7
247 return time
248
249 if isinstance(time, basestring):
250 return time
251
252 if isDayOfWeek:
253 time = [ (t + 1) % 7 for t in time ]
254
255 return ','.join([ str(s) for s in time ])
256
258
259 from buildbot.util import croniter
260
261 dateTime = lastActuated or self.now()
262 sched = '%s %s %s %s %s' % (self._timeToCron(self.minute),
263 self._timeToCron(self.hour),
264 self._timeToCron(self.dayOfMonth),
265 self._timeToCron(self.month),
266 self._timeToCron(self.dayOfWeek, True))
267 cron = croniter.croniter(sched, dateTime)
268 nextdate = cron.get_next(float)
269 return defer.succeed(nextdate)
270
272 compare_attrs = (NightlyBase.compare_attrs
273 + ('branch', 'onlyIfChanged', 'fileIsImportant',
274 'change_filter', 'onlyImportant',))
275
277 - def __init__(self, name, builderNames, minute=0, hour='*',
278 dayOfMonth='*', month='*', dayOfWeek='*',
279 branch=NoBranch, fileIsImportant=None, onlyIfChanged=False,
280 properties={}, change_filter=None, onlyImportant=False,
281 codebases = base.BaseScheduler.DefaultCodebases):
303
311
321
322 @defer.inlineCallbacks
351
353 implements(ITriggerableScheduler)
357 NightlyBase.__init__(self, name=name, builderNames=builderNames, minute=minute, hour=hour,
358 dayOfWeek=dayOfWeek, dayOfMonth=dayOfMonth, properties=properties, codebases=codebases)
359
360 self._lastTrigger = None
361 self.reason = "The NightlyTriggerable scheduler named '%s' triggered this build" % self.name
362
364 NightlyBase.startService(self)
365
366
367 d = self.getState('lastTrigger', None)
368 def setLast(lastTrigger):
369 try:
370 if lastTrigger:
371 assert isinstance(lastTrigger[0], dict)
372 self._lastTrigger = (lastTrigger[0], properties.Properties.fromDict(lastTrigger[1]))
373 except:
374
375 log.msg("NightlyTriggerable Scheduler <%s>: bad lastTrigger: %r" % (self.name, lastTrigger))
376 d.addCallback(setLast)
377
378 - def trigger(self, sourcestamps, set_props=None):
379 """Trigger this scheduler with the given sourcestamp ID. Returns a
380 deferred that will fire when the buildset is finished."""
381 self._lastTrigger = (sourcestamps, set_props)
382
383
384 if set_props:
385 propsDict = set_props.asDict()
386 else:
387 propsDict = {}
388 d = self.setState('lastTrigger',
389 (sourcestamps, propsDict))
390
391
392
393
394
395 return d.addCallback(lambda _: buildstep.SUCCESS)
396
397 @defer.inlineCallbacks
415