[Buildbot-commits] buildbot/buildbot/status client.py,1.23,1.24 html.py,1.68,1.69 words.py,1.40,1.41 builder.py,1.67,1.68
Brian Warner
warner at users.sourceforge.net
Fri Oct 14 19:42:42 UTC 2005
Update of /cvsroot/buildbot/buildbot/buildbot/status
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv32254/buildbot/status
Modified Files:
client.py html.py words.py builder.py
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-326
Creator: Brian Warner <warner at lothar.com>
implement multiple slaves per Builder, allowing concurrent Builds
* lots: implement multiple slaves per Builder, which means multiple
current builds per Builder. Some highlights:
* buildbot/interfaces.py (IBuilderStatus.getState): return a tuple
of (state,currentBuilds) instead of (state,currentBuild)
(IBuilderStatus.getCurrentBuilds): replace getCurrentBuild()
(IBuildStatus.getSlavename): new method, so you can tell which
slave got used. This only gets set when the build completes.
(IBuildRequestStatus.getBuilds): new method
* buildbot/process/builder.py (SlaveBuilder): add a .state
attribute to track things like ATTACHING and IDLE and BUILDING,
instead of..
(Builder): .. the .slaves attribute here, which has been turned
into a simple list of available slaves. Added a separate
attaching_slaves list to track ones that are not yet ready for
builds.
(Builder.fireTestEvent): put off the test-event callback for a
reactor turn, to make tests a bit more consistent.
(Ping): cleaned up the slaveping a bit, now it disconnects if the
ping fails due to an exception. This needs work, I'm worried that
a code error could lead to a constantly re-connecting slave.
Especially since I'm trying to move to a distinct remote_ping
method, separate from the remote_print that we currently use.
(BuilderControl.requestBuild): return a convenience Deferred that
provides an IBuildStatus when the build finishes.
(BuilderControl.ping): ping all connected slaves, only return True
if they all respond.
* buildbot/slave/bot.py (BuildSlave.stopService): stop trying to
reconnect when we shut down.
* buildbot/status/builder.py: implement new methods, convert
one-build-at-a-time methods to handle multiple builds
* buildbot/status/*.py: do the same in all default status targets
* buildbot/status/html.py: report the build's slavename in the
per-Build page, report all buildslaves on the per-Builder page
* buildbot/test/test_run.py: update/create tests
* buildbot/test/test_slaves.py: same
* buildbot/test/test_scheduler.py: remove stale test
* docs/buildbot.texinfo: document the new builder-specification
'slavenames' parameter
Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/builder.py,v
retrieving revision 1.67
retrieving revision 1.68
diff -u -d -r1.67 -r1.68
--- builder.py 14 Oct 2005 19:32:55 -0000 1.67
+++ builder.py 14 Oct 2005 19:42:40 -0000 1.68
@@ -645,6 +645,8 @@
return self.source
def getBuilderName(self):
return self.builderName
+ def getBuilds(self):
+ return self.builds
def subscribe(self, observer):
self.observers.append(observer)
@@ -909,6 +911,7 @@
text = []
color = None
results = None
+ slavename = None
# these lists/dicts are defined here so that unserialized instances have
# (empty) values. They are set in __init__ to new objects to make sure
@@ -1018,6 +1021,9 @@
def getResults(self):
return self.results
+ def getSlavename(self):
+ return self.slavename
+
def getTestResults(self):
return self.testResults
@@ -1094,6 +1100,9 @@
# the world about us
self.builder.buildStarted(self)
+ def setSlavename(self, slavename):
+ self.slavename = slavename
+
def setText(self, text):
assert type(text) in (list, tuple)
self.text = text
@@ -1280,9 +1289,7 @@
buildHorizon = 100 # forget builds beyond this
stepHorizon = 50 # forget steps in builds beyond this
- slavename = None
category = None
- currentBuild = None
currentBigState = "offline" # or idle/waiting/interlocked/building
nextBuildNumber = 0
basedir = None # filled in by our parent
@@ -1291,27 +1298,30 @@
self.name = buildername
self.category = category
+ self.slavenames = []
self.events = []
# these three hold Events, and are used to retrieve the current
# state of the boxes.
self.lastBuildStatus = None
#self.currentBig = None
#self.currentSmall = None
+ self.currentBuilds = []
self.pendingBuilds = []
self.nextBuild = None
self.watchers = []
self.buildCache = [] # TODO: age builds out of the cache
# persistence
+ # TODO: why am I not using styles.Versioned for this?
def __getstate__(self):
d = self.__dict__.copy()
d['watchers'] = []
del d['buildCache']
- if self.currentBuild:
- self.currentBuild.saveYourself()
+ for b in self.currentBuilds:
+ b.saveYourself()
# TODO: push a 'hey, build was interrupted' event
- del d['currentBuild']
+ del d['currentBuilds']
del d['pendingBuilds']
del d['currentBigState']
del d['basedir']
@@ -1321,8 +1331,12 @@
def __setstate__(self, d):
self.__dict__ = d
self.buildCache = []
+ self.currentBuilds = []
self.pendingBuilds = []
self.watchers = []
+ if d.has_key('slavename'):
+ self.slavenames = [self.slavename]
+ del self.slavename
# self.basedir must be filled in by our parent
# self.status must be filled in by our parent
@@ -1356,8 +1370,9 @@
self.buildCache.pop(0)
def getBuildByNumber(self, number):
- if self.currentBuild and self.currentBuild.number == number:
- return self.currentBuild
+ for b in self.currentBuilds:
+ if b.number == number:
+ return b
for build in self.buildCache:
if build.number == number:
return build
@@ -1387,16 +1402,16 @@
return self.name
def getState(self):
- return (self.currentBigState, self.currentBuild)
+ return (self.currentBigState, self.currentBuilds)
- def getSlave(self):
- return self.status.getSlave(self.slavename)
+ def getSlaves(self):
+ return [self.status.getSlave(name) for name in self.slavenames]
def getPendingBuilds(self):
return self.pendingBuilds
- def getCurrentBuild(self):
- return self.currentBuild
+ def getCurrentBuilds(self):
+ return self.currentBuilds
def getLastFinishedBuild(self):
b = self.getBuild(-1)
@@ -1461,8 +1476,8 @@
## Builder interface (methods called by the Builder which feeds us)
- def setSlavename(self, name):
- self.slavename = name
+ def setSlavenames(self, names):
+ self.slavenames = names
def addEvent(self, text=[], color=None):
# this adds a duration event. When it is done, the user should call
@@ -1486,7 +1501,7 @@
return e # for consistency, but they really shouldn't touch it
def setBigState(self, state):
- needToUpdate = state != self.currentBuild
+ needToUpdate = state != self.currentBigState
self.currentBigState = state
if needToUpdate:
self.publishState()
@@ -1525,8 +1540,9 @@
assert s.builder is self # paranoia
assert s.number == self.nextBuildNumber - 1
- self.currentBuild = s
- self.addBuildToCache(self.currentBuild)
+ assert s not in self.currentBuilds
+ self.currentBuilds.append(s)
+ self.addBuildToCache(s)
# now that the BuildStatus is prepared to answer queries, we can
# announce the new build to all our watchers
@@ -1540,9 +1556,9 @@
s.subscribe(receiver)
def _buildFinished(self, s):
- assert s is self.currentBuild
- self.currentBuild.saveYourself()
- self.currentBuild = None
+ assert s in self.currentBuilds
+ s.saveYourself()
+ self.currentBuilds.remove(s)
name = self.getName()
results = s.getResults()
Index: client.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/client.py,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -d -r1.23 -r1.24
--- client.py 31 Aug 2005 01:12:06 -0000 1.23
+++ client.py 14 Oct 2005 19:42:40 -0000 1.24
@@ -72,17 +72,19 @@
return self.b.getName()
def remote_getState(self):
- state, build = self.b.getState()
- return (state, None, makeRemote(build)) # TODO: remove leftover ETA
+ state, builds = self.b.getState()
+ return (state,
+ None, # TODO: remove leftover ETA
+ [makeRemote(b) for b in builds])
- def remote_getSlave(self):
- return IRemote(self.b.getSlave())
+ def remote_getSlaves(self):
+ return [IRemote(s) for s in self.b.getSlaves()]
def remote_getLastFinishedBuild(self):
return makeRemote(self.b.getLastFinishedBuild())
- def remote_getCurrentBuild(self):
- return makeRemote(self.b.getCurrentBuild())
+ def remote_getCurrentBuilds(self):
+ return makeRemote(self.b.getCurrentBuilds())
def remote_getBuild(self, number):
return makeRemote(self.b.getBuild(number))
Index: html.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/html.py,v
retrieving revision 1.68
retrieving revision 1.69
diff -u -d -r1.68 -r1.69
--- html.py 2 Sep 2005 15:40:41 -0000 1.68
+++ html.py 14 Oct 2005 19:42:40 -0000 1.69
@@ -303,7 +303,8 @@
% (b.getBuilder().getName(), b.getNumber(),
html.escape(b.getReason())))
if b.isFinished():
- data += "<h2>Results:</h2>"
+ data += "<h4>Buildslave: %s</h4>\n" % html.escape(b.getSlavename())
+ data += "<h2>Results:</h2>\n"
data += " ".join(b.getText()) + "\n"
if b.getTestResults():
url = request.childLink("tests")
@@ -382,22 +383,30 @@
def body(self, request):
b = self.builder
- slave = b.getSlave()
+ slaves = b.getSlaves()
+ connected_slaves = [s for s in slaves if s.isConnected()]
+
data = make_row("Builder:", html.escape(b.getName()))
b1 = b.getBuild(-1)
if b1 is not None:
data += make_row("Current/last build:", str(b1.getNumber()))
- if slave.isConnected():
- data += "\nCONNECTED (slave '%s')<br />\n" % slave.getName()
- if slave.getAdmin():
- data += make_row("Admin:", html.escape(slave.getAdmin()))
- if slave.getHost():
- data += "<span class='label'>Host info:</span>\n"
- data += html.PRE(slave.getHost())
- else:
- data += "\nNOT CONNECTED (slave '%s')<br />\n" % slave.getName()
+ data += "\n<br />BUILDSLAVES<br />\n"
+ data += "<ol>\n"
+ for slave in slaves:
+ data += "<li><b>%s</b>: " % html.escape(slave.getName())
+ if slave.isConnected():
+ data += "CONNECTED\n"
+ if slave.getAdmin():
+ data += make_row("Admin:", html.escape(slave.getAdmin()))
+ if slave.getHost():
+ data += "<span class='label'>Host info:</span>\n"
+ data += html.PRE(slave.getHost())
+ else:
+ data += ("NOT CONNECTED\n")
+ data += "</li>\n"
+ data += "</ol>\n"
- if self.control is not None and slave.isConnected():
+ if self.control is not None and connected_slaves:
forceURL = urllib.quote(request.childLink("force"))
data += (
"""
@@ -414,7 +423,7 @@
""") % {"forceURL": forceURL}
elif self.control is not None:
data += """
- <p>This slave appears to be offline, so it's not possible
+ <p>All buildslaves appear to be offline, so it's not possible
to force this build to execute at this time.</p>
"""
@@ -422,7 +431,7 @@
pingURL = urllib.quote(request.childLink("ping"))
data += """
<form action="%s" class='command pingbuilder'>
- <p>To ping a builder, push the 'Ping' button</p>
+ <p>To ping the buildslave(s), push the 'Ping' button</p>
<input type="submit" value="Ping Builder" />
</form>
@@ -684,15 +693,16 @@
def getBox(self, status):
# getState() returns offline, idle, or building
- state, build = self.original.getState()
+ state, builds = self.original.getState()
color = "white"
if state == "building":
color = "yellow"
text = ["building"]
- if build:
- eta = build.getETA()
- if eta:
- text.extend(self.formatETA(eta))
+ if builds:
+ for b in builds:
+ eta = b.getETA()
+ if eta:
+ text.extend(self.formatETA(eta))
elif state == "offline":
color = "red"
text = ["offline"]
@@ -1013,7 +1023,7 @@
for b in builders:
text = ""
color = "#ca88f7"
- state, build = b.getState()
+ state, builds = b.getState()
if state != "offline":
text += "%s<br />\n" % state #b.getCurrentBig().text[0]
else:
Index: words.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/words.py,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -d -r1.40 -r1.41
--- words.py 1 Sep 2005 20:53:21 -0000 1.40
+++ words.py 14 Oct 2005 19:42:40 -0000 1.41
@@ -242,20 +242,21 @@
raise UsageError("try 'watch <builder>'")
which = args[0]
b = self.getBuilder(which)
- build = b.getCurrentBuild()
- if not build:
- self.reply(reply, "there is no build currently running")
+ builds = b.getCurrentBuilds()
+ if not builds:
+ self.reply(reply, "there are no builds currently running")
return
- assert not build.isFinished()
- d = build.waitUntilFinished()
- d.addCallback(self.buildFinished, reply)
- r = "watching build %s #%d until it finishes" \
- % (which, build.getNumber())
- eta = build.getETA()
- if eta is not None:
- r += " [%s]" % self.convertTime(eta)
- r += ".."
- self.reply(reply, r)
+ for build in builds:
+ assert not build.isFinished()
+ d = build.waitUntilFinished()
+ d.addCallback(self.buildFinished, reply)
+ r = "watching build %s #%d until it finishes" \
+ % (which, build.getNumber())
+ eta = build.getETA()
+ if eta is not None:
+ r += " [%s]" % self.convertTime(eta)
+ r += ".."
+ self.reply(reply, r)
command_WATCH.usage = "watch <which> - announce the completion of an active build"
def buildFinished(self, b, reply):
@@ -331,25 +332,27 @@
# find an in-progress build
builderstatus = self.getBuilder(which)
- buildstatus = builderstatus.getCurrentBuild()
- if not buildstatus:
+ builds = builderstatus.getCurrentBuilds()
+ if not builds:
self.reply(reply, "sorry, no build is currently running")
return
- num = buildstatus.getNumber()
+ for build in builds:
+ num = build.getNumber()
- # obtain the BuildControl object
- buildcontrol = buildercontrol.getBuild(num)
+ # obtain the BuildControl object
+ buildcontrol = buildercontrol.getBuild(num)
- # make it stop
- buildcontrol.stopBuild(r)
+ # make it stop
+ buildcontrol.stopBuild(r)
+
+ self.reply(reply, "build %d interrupted" % num)
- self.reply(reply, "build %d interrupted" % num)
command_STOP.usage = "stop build <which> <reason> - Stop a running build"
def emit_status(self, reply, which):
b = self.getBuilder(which)
str = "%s: " % which
- state, build = b.getState()
+ state, builds = b.getState()
str += state
if state == "idle":
last = b.getLastFinishedBuild()
@@ -358,13 +361,15 @@
str += ", last build %s secs ago: %s" % \
(int(util.now() - finished), " ".join(last.getText()))
if state == "building":
- build = b.getCurrentBuild()
- if build:
+ t = []
+ for build in builds:
step = build.getCurrentStep()
- str += " (%s)" % " ".join(step.getText())
+ s = "(%s)" % " ".join(step.getText())
ETA = build.getETA()
if ETA is not None:
- str += " [ETA %s]" % self.convertTime(ETA)
+ s += " [ETA %s]" % self.convertTime(ETA)
+ t.append(s)
+ str += ", ".join(t)
self.reply(reply, str)
def emit_last(self, reply, which):
More information about the Commits
mailing list