Trees | Indices | Help |
|
---|
|
1 # This file is part of Buildbot. Buildbot is free software: you can 2 # redistribute it and/or modify it under the terms of the GNU General Public 3 # License as published by the Free Software Foundation, version 2. 4 # 5 # This program is distributed in the hope that it will be useful, but WITHOUT 6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 8 # details. 9 # 10 # You should have received a copy of the GNU General Public License along with 11 # this program; if not, write to the Free Software Foundation, Inc., 51 12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 13 # 14 # Copyright Buildbot Team Members 15 16 from twisted.internet import defer, reactor 17 from twisted.python import log 18 from buildbot import util, config 19 from buildbot.util import NotABranch 20 from collections import defaultdict 21 from buildbot.changes import filter, changes 22 from buildbot.schedulers import base, dependent25 """ 26 @param onlyImportant: If True, only important changes will be added to the 27 buildset. 28 @type onlyImportant: boolean 29 30 """ 31 32 compare_attrs = (base.BaseScheduler.compare_attrs + 33 ('treeStableTimer', 'change_filter', 'fileIsImportant', 34 'onlyImportant') ) 35 36 _reactor = reactor # for tests 37107 108 @util.deferredLocked('_stable_timers_lock')39 - def __init__(self, name, shouldntBeSet=NotSet, treeStableTimer=None, 40 builderNames=None, branch=NotABranch, branches=NotABranch, 41 fileIsImportant=None, properties={}, categories=None, 42 change_filter=None, onlyImportant=False, **kwargs):43 if shouldntBeSet is not self.NotSet: 44 config.error( 45 "pass arguments to schedulers using keyword arguments") 46 if fileIsImportant and not callable(fileIsImportant): 47 config.error( 48 "fileIsImportant must be a callable") 49 50 # initialize parent classes 51 base.BaseScheduler.__init__(self, name, builderNames, properties, **kwargs) 52 53 self.treeStableTimer = treeStableTimer 54 self.fileIsImportant = fileIsImportant 55 self.onlyImportant = onlyImportant 56 self.change_filter = self.getChangeFilter(branch=branch, 57 branches=branches, change_filter=change_filter, 58 categories=categories) 59 60 # the IDelayedCall used to wake up when this scheduler's 61 # treeStableTimer expires. 62 self._stable_timers = defaultdict(lambda : None) 63 self._stable_timers_lock = defer.DeferredLock()64 6769 base.BaseScheduler.startService(self) 70 71 d = self.startConsumingChanges(fileIsImportant=self.fileIsImportant, 72 change_filter=self.change_filter, 73 onlyImportant=self.onlyImportant) 74 75 # if treeStableTimer is False, then we don't care about classified 76 # changes, so get rid of any hanging around from previous 77 # configurations 78 if not self.treeStableTimer: 79 d.addCallback(lambda _ : 80 self.master.db.schedulers.flushChangeClassifications( 81 self.objectid)) 82 83 # otherwise, if there are classified changes out there, start their 84 # treeStableTimers again 85 else: 86 d.addCallback(lambda _ : 87 self.scanExistingClassifiedChanges()) 88 89 # handle Deferred errors, since startService does not return a Deferred 90 d.addErrback(log.err, "while starting SingleBranchScheduler '%s'" 91 % self.name) 92 93 if _returnDeferred: 94 return d # only used in tests9597 # the base stopService will unsubscribe from new changes 98 d = base.BaseScheduler.stopService(self) 99 @util.deferredLocked(self._stable_timers_lock) 100 def cancel_timers(_): 101 for timer in self._stable_timers.values(): 102 if timer: 103 timer.cancel() 104 self._stable_timers.clear()105 d.addCallback(cancel_timers) 106 return d110 if not self.treeStableTimer: 111 # if there's no treeStableTimer, we can completely ignore 112 # unimportant changes 113 if not important: 114 return defer.succeed(None) 115 # otherwise, we'll build it right away 116 return self.addBuildsetForChanges(reason='scheduler', 117 changeids=[ change.number ]) 118 119 timer_name = self.getTimerNameForChange(change) 120 121 # if we have a treeStableTimer, then record the change's importance 122 # and: 123 # - for an important change, start the timer 124 # - for an unimportant change, reset the timer if it is running 125 d = self.master.db.schedulers.classifyChanges( 126 self.objectid, { change.number : important }) 127 def fix_timer(_): 128 if not important and not self._stable_timers[timer_name]: 129 return 130 if self._stable_timers[timer_name]: 131 self._stable_timers[timer_name].cancel() 132 def fire_timer(): 133 d = self.stableTimerFired(timer_name) 134 d.addErrback(log.err, "while firing stable timer")135 self._stable_timers[timer_name] = self._reactor.callLater( 136 self.treeStableTimer, fire_timer) 137 d.addCallback(fix_timer) 138 return d 139 140 @defer.inlineCallbacks142 # call gotChange for each classified change. This is called at startup 143 # and is intended to re-start the treeStableTimer for any changes that 144 # had not yet been built when the scheduler was stopped. 145 146 # NOTE: this may double-call gotChange for changes that arrive just as 147 # the scheduler starts up. In practice, this doesn't hurt anything. 148 classifications = \ 149 yield self.master.db.schedulers.getChangeClassifications( 150 self.objectid) 151 152 # call gotChange for each change, after first fetching it from the db 153 for changeid, important in classifications.iteritems(): 154 chdict = yield self.master.db.changes.getChange(changeid) 155 156 if not chdict: 157 continue 158 159 change = yield changes.Change.fromChdict(self.master, chdict) 160 yield self.gotChange(change, important)161 164166 """similar to db.schedulers.getChangeClassifications, but given timer 167 name""" 168 raise NotImplementedError # see subclasses169 170 @util.deferredLocked('_stable_timers_lock') 171 @defer.inlineCallbacks173 # if the service has already been stoppd then just bail out 174 if not self._stable_timers[timer_name]: 175 return 176 177 # delete this now-fired timer 178 del self._stable_timers[timer_name] 179 180 classifications = \ 181 yield self.getChangeClassificationsForTimer(self.objectid, 182 timer_name) 183 184 # just in case: databases do weird things sometimes! 185 if not classifications: # pragma: no cover 186 return 187 188 changeids = sorted(classifications.keys()) 189 yield self.addBuildsetForChanges(reason='scheduler', 190 changeids=changeids) 191 192 max_changeid = changeids[-1] # (changeids are sorted) 193 yield self.master.db.schedulers.flushChangeClassifications( 194 self.objectid, less_than=max_changeid+1)195197 # This isn't locked, since the caller expects and immediate value, 198 # and in any case, this is only an estimate. 199 return [timer.getTime() for timer in self._stable_timers.values() if timer and timer.active()]200223203 if branch is NotABranch and not change_filter: 204 config.error( 205 "the 'branch' argument to SingleBranchScheduler is " + 206 "mandatory unless change_filter is provided") 207 elif branches is not NotABranch: 208 config.error( 209 "the 'branches' argument is not allowed for " + 210 "SingleBranchScheduler") 211 212 213 return filter.ChangeFilter.fromSchedulerConstructorArgs( 214 change_filter=change_filter, branch=branch, 215 categories=categories)216 219226 "alias for SingleBranchScheduler"233228 log.msg("WARNING: the name 'Scheduler' is deprecated; use " + 229 "buildbot.schedulers.basic.SingleBranchScheduler instead " + 230 "(note that this may require you to change your import " + 231 "statement)") 232 SingleBranchScheduler.__init__(self, *args, **kwargs)249 250 # now at buildbot.schedulers.dependent, but keep the old name alive 251 Dependent = dependent.Dependent 252237 assert branch is NotABranch 238 return filter.ChangeFilter.fromSchedulerConstructorArgs( 239 change_filter=change_filter, branch=branches, 240 categories=categories)241243 return change.branch244246 branch = timer_name # set in getTimerNameForChange 247 return self.master.db.schedulers.getChangeClassifications( 248 self.objectid, branch=branch)
Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Wed Nov 21 16:23:02 2012 | http://epydoc.sourceforge.net |