| Index: third_party/buildbot_7_12/buildbot/slave/bot.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/slave/bot.py b/third_party/buildbot_7_12/buildbot/slave/bot.py
|
| deleted file mode 100644
|
| index e8c2e8f6911e559104ab224c97fbcfd5dfac46bc..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/slave/bot.py
|
| +++ /dev/null
|
| @@ -1,516 +0,0 @@
|
| -
|
| -import os.path
|
| -
|
| -import buildbot
|
| -
|
| -from twisted.spread import pb
|
| -from twisted.python import log
|
| -from twisted.internet import reactor, defer
|
| -from twisted.application import service, internet
|
| -from twisted.cred import credentials
|
| -
|
| -from buildbot.util import now
|
| -from buildbot.pbutil import ReconnectingPBClientFactory
|
| -from buildbot.slave import registry
|
| -# make sure the standard commands get registered. This import is performed
|
| -# for its side-effects.
|
| -from buildbot.slave import commands
|
| -# and make pyflakes think we aren't being stupid
|
| -commands = commands
|
| -
|
| -class NoCommandRunning(pb.Error):
|
| - pass
|
| -class WrongCommandRunning(pb.Error):
|
| - pass
|
| -class UnknownCommand(pb.Error):
|
| - pass
|
| -
|
| -class Master:
|
| - def __init__(self, host, port, username, password):
|
| - self.host = host
|
| - self.port = port
|
| - self.username = username
|
| - self.password = password
|
| -
|
| -class SlaveBuild:
|
| -
|
| - """This is an object that can hold state from one step to another in the
|
| - same build. All SlaveCommands have access to it.
|
| - """
|
| - def __init__(self, builder):
|
| - self.builder = builder
|
| -
|
| -class SlaveBuilder(pb.Referenceable, service.Service):
|
| -
|
| - """This is the local representation of a single Builder: it handles a
|
| - single kind of build (like an all-warnings build). It has a name and a
|
| - home directory. The rest of its behavior is determined by the master.
|
| - """
|
| -
|
| - stopCommandOnShutdown = True
|
| -
|
| - # remote is a ref to the Builder object on the master side, and is set
|
| - # when they attach. We use it to detect when the connection to the master
|
| - # is severed.
|
| - remote = None
|
| -
|
| - # .build points to a SlaveBuild object, a new one for each build
|
| - build = None
|
| -
|
| - # .command points to a SlaveCommand instance, and is set while the step
|
| - # is running. We use it to implement the stopBuild method.
|
| - command = None
|
| -
|
| - # .remoteStep is a ref to the master-side BuildStep object, and is set
|
| - # when the step is started
|
| - remoteStep = None
|
| -
|
| - def __init__(self, name, not_really):
|
| - #service.Service.__init__(self) # Service has no __init__ method
|
| - self.setName(name)
|
| - self.not_really = not_really
|
| -
|
| - def __repr__(self):
|
| - return "<SlaveBuilder '%s' at %d>" % (self.name, id(self))
|
| -
|
| - def setServiceParent(self, parent):
|
| - service.Service.setServiceParent(self, parent)
|
| - self.bot = self.parent
|
| - # note that self.parent will go away when the buildmaster's config
|
| - # file changes and this Builder is removed (possibly because it has
|
| - # been changed, so the Builder will be re-added again in a moment).
|
| - # This may occur during a build, while a step is running.
|
| -
|
| - def setBuilddir(self, builddir):
|
| - assert self.parent
|
| - self.builddir = builddir
|
| - self.basedir = os.path.join(self.bot.basedir, self.builddir)
|
| - if not os.path.isdir(self.basedir):
|
| - os.makedirs(self.basedir)
|
| -
|
| - def stopService(self):
|
| - service.Service.stopService(self)
|
| - if self.stopCommandOnShutdown:
|
| - self.stopCommand()
|
| -
|
| - def activity(self):
|
| - bot = self.parent
|
| - if bot:
|
| - buildslave = bot.parent
|
| - if buildslave:
|
| - bf = buildslave.bf
|
| - bf.activity()
|
| -
|
| - def remote_setMaster(self, remote):
|
| - self.remote = remote
|
| - self.remote.notifyOnDisconnect(self.lostRemote)
|
| - def remote_print(self, message):
|
| - log.msg("SlaveBuilder.remote_print(%s): message from master: %s" %
|
| - (self.name, message))
|
| - if message == "ping":
|
| - return self.remote_ping()
|
| -
|
| - def remote_ping(self):
|
| - log.msg("SlaveBuilder.remote_ping(%s)" % self)
|
| - if self.bot and self.bot.parent:
|
| - debugOpts = self.bot.parent.debugOpts
|
| - if debugOpts.get("stallPings"):
|
| - log.msg(" debug_stallPings")
|
| - timeout, timers = debugOpts["stallPings"]
|
| - d = defer.Deferred()
|
| - t = reactor.callLater(timeout, d.callback, None)
|
| - timers.append(t)
|
| - return d
|
| - if debugOpts.get("failPingOnce"):
|
| - log.msg(" debug_failPingOnce")
|
| - class FailPingError(pb.Error): pass
|
| - del debugOpts['failPingOnce']
|
| - raise FailPingError("debug_failPingOnce means we should fail")
|
| -
|
| - def lostRemote(self, remote):
|
| - log.msg("lost remote")
|
| - self.remote = None
|
| -
|
| - def lostRemoteStep(self, remotestep):
|
| - log.msg("lost remote step")
|
| - self.remoteStep = None
|
| - if self.stopCommandOnShutdown:
|
| - self.stopCommand()
|
| -
|
| - # the following are Commands that can be invoked by the master-side
|
| - # Builder
|
| - def remote_startBuild(self):
|
| - """This is invoked before the first step of any new build is run. It
|
| - creates a new SlaveBuild object, which holds slave-side state from
|
| - one step to the next."""
|
| - self.build = SlaveBuild(self)
|
| - log.msg("%s.startBuild" % self)
|
| -
|
| - def remote_startCommand(self, stepref, stepId, command, args):
|
| - """
|
| - This gets invoked by L{buildbot.process.step.RemoteCommand.start}, as
|
| - part of various master-side BuildSteps, to start various commands
|
| - that actually do the build. I return nothing. Eventually I will call
|
| - .commandComplete() to notify the master-side RemoteCommand that I'm
|
| - done.
|
| - """
|
| -
|
| - self.activity()
|
| -
|
| - if self.command:
|
| - log.msg("leftover command, dropping it")
|
| - self.stopCommand()
|
| -
|
| - try:
|
| - factory, version = registry.commandRegistry[command]
|
| - except KeyError:
|
| - raise UnknownCommand, "unrecognized SlaveCommand '%s'" % command
|
| - self.command = factory(self, stepId, args)
|
| -
|
| - log.msg(" startCommand:%s [id %s]" % (command,stepId))
|
| - self.remoteStep = stepref
|
| - self.remoteStep.notifyOnDisconnect(self.lostRemoteStep)
|
| - d = self.command.doStart()
|
| - d.addCallback(lambda res: None)
|
| - d.addBoth(self.commandComplete)
|
| - return None
|
| -
|
| - def remote_interruptCommand(self, stepId, why):
|
| - """Halt the current step."""
|
| - log.msg("asked to interrupt current command: %s" % why)
|
| - self.activity()
|
| - if not self.command:
|
| - # TODO: just log it, a race could result in their interrupting a
|
| - # command that wasn't actually running
|
| - log.msg(" .. but none was running")
|
| - return
|
| - self.command.doInterrupt()
|
| -
|
| -
|
| - def stopCommand(self):
|
| - """Make any currently-running command die, with no further status
|
| - output. This is used when the buildslave is shutting down or the
|
| - connection to the master has been lost. Interrupt the command,
|
| - silence it, and then forget about it."""
|
| - if not self.command:
|
| - return
|
| - log.msg("stopCommand: halting current command %s" % self.command)
|
| - self.command.doInterrupt() # shut up! and die!
|
| - self.command = None # forget you!
|
| -
|
| - # sendUpdate is invoked by the Commands we spawn
|
| - def sendUpdate(self, data):
|
| - """This sends the status update to the master-side
|
| - L{buildbot.process.step.RemoteCommand} object, giving it a sequence
|
| - number in the process. It adds the update to a queue, and asks the
|
| - master to acknowledge the update so it can be removed from that
|
| - queue."""
|
| -
|
| - if not self.running:
|
| - # .running comes from service.Service, and says whether the
|
| - # service is running or not. If we aren't running, don't send any
|
| - # status messages.
|
| - return
|
| - # the update[1]=0 comes from the leftover 'updateNum', which the
|
| - # master still expects to receive. Provide it to avoid significant
|
| - # interoperability issues between new slaves and old masters.
|
| - if self.remoteStep:
|
| - update = [data, 0]
|
| - updates = [update]
|
| - d = self.remoteStep.callRemote("update", updates)
|
| - d.addCallback(self.ackUpdate)
|
| - d.addErrback(self._ackFailed, "SlaveBuilder.sendUpdate")
|
| -
|
| - def ackUpdate(self, acknum):
|
| - self.activity() # update the "last activity" timer
|
| -
|
| - def ackComplete(self, dummy):
|
| - self.activity() # update the "last activity" timer
|
| -
|
| - def _ackFailed(self, why, where):
|
| - log.msg("SlaveBuilder._ackFailed:", where)
|
| - #log.err(why) # we don't really care
|
| -
|
| -
|
| - # this is fired by the Deferred attached to each Command
|
| - def commandComplete(self, failure):
|
| - if failure:
|
| - log.msg("SlaveBuilder.commandFailed", self.command)
|
| - log.err(failure)
|
| - # failure, if present, is a failure.Failure. To send it across
|
| - # the wire, we must turn it into a pb.CopyableFailure.
|
| - failure = pb.CopyableFailure(failure)
|
| - failure.unsafeTracebacks = True
|
| - else:
|
| - # failure is None
|
| - log.msg("SlaveBuilder.commandComplete", self.command)
|
| - self.command = None
|
| - if not self.running:
|
| - log.msg(" but we weren't running, quitting silently")
|
| - return
|
| - if self.remoteStep:
|
| - self.remoteStep.dontNotifyOnDisconnect(self.lostRemoteStep)
|
| - d = self.remoteStep.callRemote("complete", failure)
|
| - d.addCallback(self.ackComplete)
|
| - d.addErrback(self._ackFailed, "sendComplete")
|
| - self.remoteStep = None
|
| -
|
| -
|
| - def remote_shutdown(self):
|
| - print "slave shutting down on command from master"
|
| - reactor.stop()
|
| -
|
| -
|
| -class Bot(pb.Referenceable, service.MultiService):
|
| - """I represent the slave-side bot."""
|
| - usePTY = None
|
| - name = "bot"
|
| -
|
| - def __init__(self, basedir, usePTY, not_really=0):
|
| - service.MultiService.__init__(self)
|
| - self.basedir = basedir
|
| - self.usePTY = usePTY
|
| - self.not_really = not_really
|
| - self.builders = {}
|
| -
|
| - def startService(self):
|
| - assert os.path.isdir(self.basedir)
|
| - service.MultiService.startService(self)
|
| -
|
| - def remote_getDirs(self):
|
| - return filter(lambda d: os.path.isdir(d), os.listdir(self.basedir))
|
| -
|
| - def remote_getCommands(self):
|
| - commands = {}
|
| - for name, (factory, version) in registry.commandRegistry.items():
|
| - commands[name] = version
|
| - return commands
|
| -
|
| - def remote_setBuilderList(self, wanted):
|
| - retval = {}
|
| - wanted_dirs = ["info"]
|
| - for (name, builddir) in wanted:
|
| - wanted_dirs.append(builddir)
|
| - b = self.builders.get(name, None)
|
| - if b:
|
| - if b.builddir != builddir:
|
| - log.msg("changing builddir for builder %s from %s to %s" \
|
| - % (name, b.builddir, builddir))
|
| - b.setBuilddir(builddir)
|
| - else:
|
| - b = SlaveBuilder(name, self.not_really)
|
| - b.usePTY = self.usePTY
|
| - b.setServiceParent(self)
|
| - b.setBuilddir(builddir)
|
| - self.builders[name] = b
|
| - retval[name] = b
|
| - for name in self.builders.keys():
|
| - if not name in map(lambda a: a[0], wanted):
|
| - log.msg("removing old builder %s" % name)
|
| - self.builders[name].disownServiceParent()
|
| - del(self.builders[name])
|
| -
|
| - for d in os.listdir(self.basedir):
|
| - if os.path.isdir(d):
|
| - if d not in wanted_dirs:
|
| - log.msg("I have a leftover directory '%s' that is not "
|
| - "being used by the buildmaster: you can delete "
|
| - "it now" % d)
|
| - return retval
|
| -
|
| - def remote_print(self, message):
|
| - log.msg("message from master:", message)
|
| -
|
| - def remote_getSlaveInfo(self):
|
| - """This command retrieves data from the files in SLAVEDIR/info/* and
|
| - sends the contents to the buildmaster. These are used to describe
|
| - the slave and its configuration, and should be created and
|
| - maintained by the slave administrator. They will be retrieved each
|
| - time the master-slave connection is established.
|
| - """
|
| -
|
| - files = {}
|
| - basedir = os.path.join(self.basedir, "info")
|
| - if not os.path.isdir(basedir):
|
| - return files
|
| - for f in os.listdir(basedir):
|
| - filename = os.path.join(basedir, f)
|
| - if os.path.isfile(filename):
|
| - files[f] = open(filename, "r").read()
|
| - return files
|
| -
|
| - def remote_getVersion(self):
|
| - """Send our version back to the Master"""
|
| - return buildbot.version
|
| -
|
| -
|
| -
|
| -class BotFactory(ReconnectingPBClientFactory):
|
| - # 'keepaliveInterval' serves two purposes. The first is to keep the
|
| - # connection alive: it guarantees that there will be at least some
|
| - # traffic once every 'keepaliveInterval' seconds, which may help keep an
|
| - # interposed NAT gateway from dropping the address mapping because it
|
| - # thinks the connection has been abandoned. The second is to put an upper
|
| - # limit on how long the buildmaster might have gone away before we notice
|
| - # it. For this second purpose, we insist upon seeing *some* evidence of
|
| - # the buildmaster at least once every 'keepaliveInterval' seconds.
|
| - keepaliveInterval = None # None = do not use keepalives
|
| -
|
| - # 'keepaliveTimeout' seconds before the interval expires, we will send a
|
| - # keepalive request, both to add some traffic to the connection, and to
|
| - # prompt a response from the master in case all our builders are idle. We
|
| - # don't insist upon receiving a timely response from this message: a slow
|
| - # link might put the request at the wrong end of a large build message.
|
| - keepaliveTimeout = 30 # how long we will go without a response
|
| -
|
| - # 'maxDelay' determines the maximum amount of time the slave will wait
|
| - # between connection retries
|
| - maxDelay = 300
|
| -
|
| - keepaliveTimer = None
|
| - activityTimer = None
|
| - lastActivity = 0
|
| - unsafeTracebacks = 1
|
| - perspective = None
|
| -
|
| - def __init__(self, keepaliveInterval, keepaliveTimeout, maxDelay):
|
| - ReconnectingPBClientFactory.__init__(self)
|
| - self.maxDelay = maxDelay
|
| - self.keepaliveInterval = keepaliveInterval
|
| - self.keepaliveTimeout = keepaliveTimeout
|
| -
|
| - def startedConnecting(self, connector):
|
| - ReconnectingPBClientFactory.startedConnecting(self, connector)
|
| - self.connector = connector
|
| -
|
| - def gotPerspective(self, perspective):
|
| - ReconnectingPBClientFactory.gotPerspective(self, perspective)
|
| - self.perspective = perspective
|
| - try:
|
| - perspective.broker.transport.setTcpKeepAlive(1)
|
| - except:
|
| - log.msg("unable to set SO_KEEPALIVE")
|
| - if not self.keepaliveInterval:
|
| - self.keepaliveInterval = 10*60
|
| - self.activity()
|
| - if self.keepaliveInterval:
|
| - log.msg("sending application-level keepalives every %d seconds" \
|
| - % self.keepaliveInterval)
|
| - self.startTimers()
|
| -
|
| - def clientConnectionFailed(self, connector, reason):
|
| - self.connector = None
|
| - ReconnectingPBClientFactory.clientConnectionFailed(self,
|
| - connector, reason)
|
| -
|
| - def clientConnectionLost(self, connector, reason):
|
| - self.connector = None
|
| - self.stopTimers()
|
| - self.perspective = None
|
| - ReconnectingPBClientFactory.clientConnectionLost(self,
|
| - connector, reason)
|
| -
|
| - def startTimers(self):
|
| - assert self.keepaliveInterval
|
| - assert not self.keepaliveTimer
|
| - assert not self.activityTimer
|
| - # Insist that doKeepalive fires before checkActivity. Really, it
|
| - # needs to happen at least one RTT beforehand.
|
| - assert self.keepaliveInterval > self.keepaliveTimeout
|
| -
|
| - # arrange to send a keepalive a little while before our deadline
|
| - when = self.keepaliveInterval - self.keepaliveTimeout
|
| - self.keepaliveTimer = reactor.callLater(when, self.doKeepalive)
|
| - # and check for activity too
|
| - self.activityTimer = reactor.callLater(self.keepaliveInterval,
|
| - self.checkActivity)
|
| -
|
| - def stopTimers(self):
|
| - if self.keepaliveTimer:
|
| - self.keepaliveTimer.cancel()
|
| - self.keepaliveTimer = None
|
| - if self.activityTimer:
|
| - self.activityTimer.cancel()
|
| - self.activityTimer = None
|
| -
|
| - def activity(self, res=None):
|
| - self.lastActivity = now()
|
| -
|
| - def doKeepalive(self):
|
| - # send the keepalive request. If it fails outright, the connection
|
| - # was already dropped, so just log and ignore.
|
| - self.keepaliveTimer = None
|
| - log.msg("sending app-level keepalive")
|
| - d = self.perspective.callRemote("keepalive")
|
| - d.addCallback(self.activity)
|
| - d.addErrback(self.keepaliveLost)
|
| -
|
| - def keepaliveLost(self, f):
|
| - log.msg("BotFactory.keepaliveLost")
|
| -
|
| - def checkActivity(self):
|
| - self.activityTimer = None
|
| - if self.lastActivity + self.keepaliveInterval < now():
|
| - log.msg("BotFactory.checkActivity: nothing from master for "
|
| - "%d secs" % (now() - self.lastActivity))
|
| - self.perspective.broker.transport.loseConnection()
|
| - return
|
| - self.startTimers()
|
| -
|
| - def stopFactory(self):
|
| - ReconnectingPBClientFactory.stopFactory(self)
|
| - self.stopTimers()
|
| -
|
| -
|
| -class BuildSlave(service.MultiService):
|
| - botClass = Bot
|
| -
|
| - # debugOpts is a dictionary used during unit tests.
|
| -
|
| - # debugOpts['stallPings'] can be set to a tuple of (timeout, []). Any
|
| - # calls to remote_print will stall for 'timeout' seconds before
|
| - # returning. The DelayedCalls used to implement this are stashed in the
|
| - # list so they can be cancelled later.
|
| -
|
| - # debugOpts['failPingOnce'] can be set to True to make the slaveping fail
|
| - # exactly once.
|
| -
|
| - def __init__(self, buildmaster_host, port, name, passwd, basedir,
|
| - keepalive, usePTY, keepaliveTimeout=30, umask=None,
|
| - maxdelay=300, debugOpts={}):
|
| - log.msg("Creating BuildSlave -- buildbot.version: %s" % buildbot.version)
|
| - service.MultiService.__init__(self)
|
| - self.debugOpts = debugOpts.copy()
|
| - bot = self.botClass(basedir, usePTY)
|
| - bot.setServiceParent(self)
|
| - self.bot = bot
|
| - if keepalive == 0:
|
| - keepalive = None
|
| - self.umask = umask
|
| - bf = self.bf = BotFactory(keepalive, keepaliveTimeout, maxdelay)
|
| - bf.startLogin(credentials.UsernamePassword(name, passwd), client=bot)
|
| - self.connection = c = internet.TCPClient(buildmaster_host, port, bf)
|
| - c.setServiceParent(self)
|
| -
|
| - def waitUntilDisconnected(self):
|
| - # utility method for testing. Returns a Deferred that will fire when
|
| - # we lose the connection to the master.
|
| - if not self.bf.perspective:
|
| - return defer.succeed(None)
|
| - d = defer.Deferred()
|
| - self.bf.perspective.notifyOnDisconnect(lambda res: d.callback(None))
|
| - return d
|
| -
|
| - def startService(self):
|
| - if self.umask is not None:
|
| - os.umask(self.umask)
|
| - service.MultiService.startService(self)
|
| -
|
| - def stopService(self):
|
| - self.bf.continueTrying = 0
|
| - self.bf.stopTrying()
|
| - service.MultiService.stopService(self)
|
| - # now kill the TCP connection
|
| - # twisted >2.0.1 does this for us, and leaves _connection=None
|
| - if self.connection._connection:
|
| - self.connection._connection.disconnect()
|
|
|