aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/buildbot_slave-0.8.8-py2.7.egg/buildslave/bot.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/buildbot_slave-0.8.8-py2.7.egg/buildslave/bot.py')
-rw-r--r--lib/python2.7/site-packages/buildbot_slave-0.8.8-py2.7.egg/buildslave/bot.py547
1 files changed, 0 insertions, 547 deletions
diff --git a/lib/python2.7/site-packages/buildbot_slave-0.8.8-py2.7.egg/buildslave/bot.py b/lib/python2.7/site-packages/buildbot_slave-0.8.8-py2.7.egg/buildslave/bot.py
deleted file mode 100644
index 368e547c..00000000
--- a/lib/python2.7/site-packages/buildbot_slave-0.8.8-py2.7.egg/buildslave/bot.py
+++ /dev/null
@@ -1,547 +0,0 @@
-# This file is part of Buildbot. Buildbot is free software: you can
-# redistribute it and/or modify it under the terms of the GNU General Public
-# License as published by the Free Software Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright Buildbot Team Members
-
-import os.path
-import socket
-import sys
-import signal
-
-from twisted.spread import pb
-from twisted.python import log
-from twisted.internet import error, reactor, task, defer
-from twisted.application import service, internet
-from twisted.cred import credentials
-
-import buildslave
-from buildslave.pbutil import ReconnectingPBClientFactory
-from buildslave.commands import registry, base
-from buildslave import monkeypatches
-
-class UnknownCommand(pb.Error):
- pass
-
-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
-
- # .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):
- #service.Service.__init__(self) # Service has no __init__ method
- self.setName(name)
-
- 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:
- bslave = bot.parent
- if bslave:
- bf = bslave.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))
-
- 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
- doesn't do much, but masters call it so it's still here."""
- pass
-
- 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 = registry.getFactory(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):
- log.msg("slave shutting down on command from master")
- log.msg("NOTE: master is using deprecated slavebuilder.shutdown method")
- reactor.stop()
-
-
-class Bot(pb.Referenceable, service.MultiService):
- """I represent the slave-side bot."""
- usePTY = None
- name = "bot"
-
- def __init__(self, basedir, usePTY, unicode_encoding=None):
- service.MultiService.__init__(self)
- self.basedir = basedir
- self.usePTY = usePTY
- self.unicode_encoding = unicode_encoding or sys.getfilesystemencoding() or 'ascii'
- self.builders = {}
-
- def startService(self):
- assert os.path.isdir(self.basedir)
- service.MultiService.startService(self)
-
- def remote_getCommands(self):
- commands = dict([
- (n, base.command_version)
- for n in registry.getAllCommandNames()
- ])
- return commands
-
- @defer.deferredGenerator
- def remote_setBuilderList(self, wanted):
- retval = {}
- wanted_names = set([ name for (name, builddir) in wanted ])
- wanted_dirs = set([ builddir for (name, builddir) in wanted ])
- wanted_dirs.add('info')
- for (name, builddir) in wanted:
- 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)
- b.usePTY = self.usePTY
- b.unicode_encoding = self.unicode_encoding
- b.setServiceParent(self)
- b.setBuilddir(builddir)
- self.builders[name] = b
- retval[name] = b
-
- # disown any builders no longer desired
- to_remove = list(set(self.builders.keys()) - wanted_names)
- dl = defer.DeferredList([
- defer.maybeDeferred(self.builders[name].disownServiceParent)
- for name in to_remove ])
- wfd = defer.waitForDeferred(dl)
- yield wfd
- wfd.getResult()
-
- # and *then* remove them from the builder list
- for name in to_remove:
- del self.builders[name]
-
- # finally warn about any leftover dirs
- for dir in os.listdir(self.basedir):
- if os.path.isdir(os.path.join(self.basedir, dir)):
- if dir 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" % dir)
-
- yield retval # return value
-
- 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 os.path.isdir(basedir):
- for f in os.listdir(basedir):
- filename = os.path.join(basedir, f)
- if os.path.isfile(filename):
- files[f] = open(filename, "r").read()
- files['environ'] = os.environ.copy()
- files['system'] = os.name
- files['basedir'] = self.basedir
- return files
-
- def remote_getVersion(self):
- """Send our version back to the Master"""
- return buildslave.version
-
- def remote_shutdown(self):
- log.msg("slave shutting down on command from master")
- # there's no good way to learn that the PB response has been delivered,
- # so we'll just wait a bit, in hopes the master hears back. Masters are
- # resilinet to slaves dropping their connections, so there is no harm
- # if this timeout is too short.
- reactor.callLater(0.2, reactor.stop)
-
-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. This also gives the operating
- # system a chance to notice that the master has gone away, and inform us
- # of such (although this could take several minutes).
- keepaliveInterval = None # None = do not use keepalives
-
- # 'maxDelay' determines the maximum amount of time the slave will wait
- # between connection retries
- maxDelay = 300
-
- keepaliveTimer = None
- unsafeTracebacks = 1
- perspective = None
-
- # for tests
- _reactor = reactor
-
- def __init__(self, buildmaster_host, port, keepaliveInterval, maxDelay):
- ReconnectingPBClientFactory.__init__(self)
- self.maxDelay = maxDelay
- self.keepaliveInterval = keepaliveInterval
- # NOTE: this class does not actually make the TCP connections - this information is
- # only here to print useful error messages
- self.buildmaster_host = buildmaster_host
- self.port = port
-
- def startedConnecting(self, connector):
- log.msg("Connecting to %s:%s" % (self.buildmaster_host, self.port))
- ReconnectingPBClientFactory.startedConnecting(self, connector)
- self.connector = connector
-
- def gotPerspective(self, perspective):
- log.msg("Connected to %s:%s; slave is ready" % (self.buildmaster_host, self.port))
- 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
- why = reason
- if reason.check(error.ConnectionRefusedError):
- why = "Connection Refused"
- log.msg("Connection to %s:%s failed: %s" % (self.buildmaster_host, self.port, why))
- ReconnectingPBClientFactory.clientConnectionFailed(self,
- connector, reason)
-
- def clientConnectionLost(self, connector, reason):
- log.msg("Lost connection to %s:%s" % (self.buildmaster_host, self.port))
- self.connector = None
- self.stopTimers()
- self.perspective = None
- ReconnectingPBClientFactory.clientConnectionLost(self,
- connector, reason)
-
- def startTimers(self):
- assert self.keepaliveInterval
- assert not self.keepaliveTimer
-
- def doKeepalive():
- self.keepaliveTimer = None
- self.startTimers()
-
- # Send the keepalive request. If an error occurs
- # was already dropped, so just log and ignore.
- log.msg("sending app-level keepalive")
- d = self.perspective.callRemote("keepalive")
- d.addErrback(log.err, "eror sending keepalive")
- self.keepaliveTimer = self._reactor.callLater(self.keepaliveInterval,
- doKeepalive)
-
- def stopTimers(self):
- if self.keepaliveTimer:
- self.keepaliveTimer.cancel()
- self.keepaliveTimer = None
-
- def activity(self, res=None):
- """Subclass or monkey-patch this method to be alerted whenever there is
- active communication between the master and slave."""
- pass
-
- def stopFactory(self):
- ReconnectingPBClientFactory.stopFactory(self)
- self.stopTimers()
-
-
-class BuildSlave(service.MultiService):
- def __init__(self, buildmaster_host, port, name, passwd, basedir,
- keepalive, usePTY, keepaliveTimeout=None, umask=None,
- maxdelay=300, unicode_encoding=None, allow_shutdown=None):
-
- # note: keepaliveTimeout is ignored, but preserved here for
- # backward-compatibility
-
- service.MultiService.__init__(self)
- bot = Bot(basedir, usePTY, unicode_encoding=unicode_encoding)
- bot.setServiceParent(self)
- self.bot = bot
- if keepalive == 0:
- keepalive = None
- self.umask = umask
- self.basedir = basedir
-
- self.shutdown_loop = None
-
- if allow_shutdown == 'signal':
- if not hasattr(signal, 'SIGHUP'):
- raise ValueError("Can't install signal handler")
- elif allow_shutdown == 'file':
- self.shutdown_file = os.path.join(basedir, 'shutdown.stamp')
- self.shutdown_mtime = 0
-
- self.allow_shutdown = allow_shutdown
- bf = self.bf = BotFactory(buildmaster_host, port, keepalive, maxdelay)
- bf.startLogin(credentials.UsernamePassword(name, passwd), client=bot)
- self.connection = c = internet.TCPClient(buildmaster_host, port, bf)
- c.setServiceParent(self)
-
- def startService(self):
- # first, apply all monkeypatches
- monkeypatches.patch_all()
-
- log.msg("Starting BuildSlave -- version: %s" % buildslave.version)
-
- self.recordHostname(self.basedir)
- if self.umask is not None:
- os.umask(self.umask)
-
- service.MultiService.startService(self)
-
- if self.allow_shutdown == 'signal':
- log.msg("Setting up SIGHUP handler to initiate shutdown")
- signal.signal(signal.SIGHUP, self._handleSIGHUP)
- elif self.allow_shutdown == 'file':
- log.msg("Watching %s's mtime to initiate shutdown" % self.shutdown_file)
- if os.path.exists(self.shutdown_file):
- self.shutdown_mtime = os.path.getmtime(self.shutdown_file)
- self.shutdown_loop = l = task.LoopingCall(self._checkShutdownFile)
- l.start(interval=10)
-
- def stopService(self):
- self.bf.continueTrying = 0
- self.bf.stopTrying()
- if self.shutdown_loop:
- self.shutdown_loop.stop()
- self.shutdown_loop = None
- return service.MultiService.stopService(self)
-
- def recordHostname(self, basedir):
- "Record my hostname in twistd.hostname, for user convenience"
- log.msg("recording hostname in twistd.hostname")
- filename = os.path.join(basedir, "twistd.hostname")
-
- try:
- hostname = os.uname()[1] # only on unix
- except AttributeError:
- # this tends to fail on non-connected hosts, e.g., laptops
- # on planes
- hostname = socket.getfqdn()
-
- try:
- open(filename, "w").write("%s\n" % hostname)
- except:
- log.msg("failed - ignoring")
-
- def _handleSIGHUP(self, *args):
- log.msg("Initiating shutdown because we got SIGHUP")
- return self.gracefulShutdown()
-
- def _checkShutdownFile(self):
- if os.path.exists(self.shutdown_file) and \
- os.path.getmtime(self.shutdown_file) > self.shutdown_mtime:
- log.msg("Initiating shutdown because %s was touched" % self.shutdown_file)
- self.gracefulShutdown()
-
- # In case the shutdown fails, update our mtime so we don't keep
- # trying to shutdown over and over again.
- # We do want to be able to try again later if the master is
- # restarted, so we'll keep monitoring the mtime.
- self.shutdown_mtime = os.path.getmtime(self.shutdown_file)
-
- def gracefulShutdown(self):
- """Start shutting down"""
- if not self.bf.perspective:
- log.msg("No active connection, shutting down NOW")
- reactor.stop()
- return
-
- log.msg("Telling the master we want to shutdown after any running builds are finished")
- d = self.bf.perspective.callRemote("shutdown")
- def _shutdownfailed(err):
- if err.check(AttributeError):
- log.msg("Master does not support slave initiated shutdown. Upgrade master to 0.8.3 or later to use this feature.")
- else:
- log.msg('callRemote("shutdown") failed')
- log.err(err)
-
- d.addErrback(_shutdownfailed)
- return d