Skip to content

Commit

Permalink
Build slaves now have locks
Browse files Browse the repository at this point in the history
These locks will:
  * be acquired before a build is started.
  * not cause latent slaves to be substantiated
  * not cause a build wait
  * cause a build to go to another slave if this one isnt available
  • Loading branch information
John Carr committed Jul 6, 2010
1 parent beb9780 commit 40dbc48
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 5 deletions.
65 changes: 62 additions & 3 deletions master/buildbot/buildslave.py
Expand Up @@ -14,6 +14,7 @@
from buildbot.status.mail import MailNotifier
from buildbot.interfaces import IBuildSlave, ILatentBuildSlave
from buildbot.process.properties import Properties
from buildbot.locks import LockAccess

import sys
if sys.version_info[:3] < (2,4,0):
Expand All @@ -34,7 +35,7 @@ class AbstractBuildSlave(NewCredPerspective, service.MultiService):

def __init__(self, name, password, max_builds=None,
notify_on_missing=[], missing_timeout=3600,
properties={}):
properties={}, locks=None):
"""
@param name: botname this machine will supply when it connects
@param password: password this machine will supply when
Expand All @@ -45,6 +46,9 @@ def __init__(self, name, password, max_builds=None,
@param properties: properties that will be applied to builds run on
this slave
@type properties: dictionary
@param locks: A list of locks that must be acquired before this slave
can be used
@type locks: dictionary
"""
service.MultiService.__init__(self)
self.slavename = name
Expand All @@ -55,6 +59,7 @@ def __init__(self, name, password, max_builds=None,
self.slave_commands = None
self.slavebuilders = {}
self.max_builds = max_builds
self.access = locks

self.properties = Properties()
self.properties.update(properties, "BuildSlave")
Expand All @@ -80,6 +85,9 @@ def update(self, new):
assert self.password == new.password
assert self.__class__ == new.__class__
self.max_builds = new.max_builds
self.access = new.access
if self.botmaster:
self.updateLocks()

def __repr__(self):
if self.botmaster:
Expand All @@ -91,9 +99,56 @@ def __repr__(self):
return "<%s '%s', (no builders yet)>" % \
(self.__class__.__name__, self.slavename)

def updateLocks(self):
# convert locks into their real form
locks = []
for access in self.access:
if not isinstance(access, LockAccess):
access = access.defaultAccess()
lock = self.botmaster.getLockByID(access.lockid)
locks.append((lock, access))
self.locks = [(l.getLock(self), la) for l, la in locks]

def locksAvailable(self):
"""
I am called to see if all the locks I depend on are available,
in which I return True, otherwise I return False
"""
if not self.locks:
return True
for lock, access in self.locks:
if not lock.isAvailable(access):
return False
return True

def acquireLocks(self):
"""
I am called when a build is preparing to run. I try to claim all
the locks that are needed for a build to happen. If I can't, then
my caller should give up the build and try to get another slave
to look at it.
"""
log.msg("acquireLocks(slave %s, locks %s)" % (self, self.locks))
if not self.locksAvailable():
log.msg("slave %s can't lock, giving up" % (self, ))
return False
# all locks are available, claim them all
for lock, access in self.locks:
lock.claim(self, access)
return True

def releaseLocks(self):
"""
I am called to release any locks after a build has finished
"""
log.msg("releaseLocks(%s): %s" % (self, self.locks))
for lock, access in self.locks:
lock.release(self, access)

def setBotmaster(self, botmaster):
assert not self.botmaster, "BuildSlave already has a botmaster"
self.botmaster = botmaster
self.updateLocks()
self.startMissingTimer()

def stopMissingTimer(self):
Expand Down Expand Up @@ -356,6 +411,10 @@ def canStartBuild(self):
if sb.isBusy()]
if len(active_builders) >= self.max_builds:
return False

if not self.locksAvailable():
return False

return True

def _mail_missing_message(self, subject, text):
Expand Down Expand Up @@ -478,10 +537,10 @@ class AbstractLatentBuildSlave(AbstractBuildSlave):
def __init__(self, name, password, max_builds=None,
notify_on_missing=[], missing_timeout=60*20,
build_wait_timeout=60*10,
properties={}):
properties={}, locks=None):
AbstractBuildSlave.__init__(
self, name, password, max_builds, notify_on_missing,
missing_timeout, properties)
missing_timeout, properties, locks)
self.building = set()
self.build_wait_timeout = build_wait_timeout

Expand Down
4 changes: 2 additions & 2 deletions master/buildbot/ec2buildslave.py
Expand Up @@ -34,10 +34,10 @@ def __init__(self, name, password, instance_type, ami=None,
keypair_name='latent_buildbot_slave',
security_name='latent_buildbot_slave',
max_builds=None, notify_on_missing=[], missing_timeout=60*20,
build_wait_timeout=60*10, properties={}):
build_wait_timeout=60*10, properties={}, locks):
AbstractLatentBuildSlave.__init__(
self, name, password, max_builds, notify_on_missing,
missing_timeout, build_wait_timeout, properties)
missing_timeout, build_wait_timeout, properties, locks)
if not ((ami is not None) ^
(valid_ami_owners is not None or
valid_ami_location_regex is not None)):
Expand Down
12 changes: 12 additions & 0 deletions master/buildbot/process/builder.py
Expand Up @@ -34,6 +34,7 @@ def __init__(self):
self.remote = None
self.slave = None
self.builder_name = None
self.locks = None

def __repr__(self):
r = ["<", self.__class__.__name__]
Expand Down Expand Up @@ -110,6 +111,7 @@ def _attached2(self, res):
def _attached3(self, res):
# now we say they're really attached
self.state = IDLE

return self

def _attachFailure(self, why, where):
Expand All @@ -119,6 +121,8 @@ def _attachFailure(self, why, where):
return why

def prepare(self, builder_status):
if not self.slave.acquireLocks():
return defer.succeed(False)
return defer.succeed(True)

def ping(self, status=None):
Expand Down Expand Up @@ -242,6 +246,10 @@ def __init__(self, slave, builder):
self.builder_name))

def prepare(self, builder_status):
# If we can't lock, then don't bother trying to substantiate
if not self.slave.acquireLocks():
return defer.succeed(False)

log.msg("substantiating slave %s" % (self,))
d = self.substantiate()
def substantiation_failed(f):
Expand Down Expand Up @@ -832,6 +840,7 @@ def _prepared(ready):
self.building.remove(build)
self._resubmit_buildreqs(build).addErrback(log.err)

sb.slave.releaseLocks()
self.triggerNewBuildCheck()

return d
Expand Down Expand Up @@ -918,6 +927,9 @@ def buildFinished(self, build, sb, bids):
else:
brids = [br.id for br in build.requests]
self.db.retire_buildrequests(brids, results)

sb.slave.releaseLocks()

self.triggerNewBuildCheck()

def _resubmit_buildreqs(self, build):
Expand Down

0 comments on commit 40dbc48

Please sign in to comment.