[Buildbot-commits] buildbot/buildbot/status/web baseweb.py, NONE, 1.1
Brian Warner
warner at users.sourceforge.net
Wed Aug 1 22:07:56 UTC 2007
Update of /cvsroot/buildbot/buildbot/buildbot/status/web
In directory sc8-pr-cvs3.sourceforge.net:/tmp/cvs-serv28727/buildbot/status/web
Added Files:
baseweb.py
Log Message:
[project @ web-parts: implement and document WebStatus, and some (but not nearly all) of the pages it provides]
Original author: warner at lothar.com
Date: 2007-03-01 09:01:45+00:00
--- NEW FILE: baseweb.py ---
from itertools import count
from zope.interface import implements
from twisted.python import log
from twisted.application import service, strports
from twisted.web.resource import Resource
from twisted.web import server, distrib, static
from twisted.spread import pb
from buildbot.interfaces import IStatusReceiver, IControl
from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
from buildbot.status.web.waterfall import WaterfallStatusResource
class ImprovedWaterfall(WaterfallStatusResource):
def __init__(self):
HtmlResource.__init__(self)
def render(self, request):
status = request.site.status
HEADER = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
lang="en"
xml:lang="en">
'''
FOOTER = '''
</html>
'''
class WebStatus(service.MultiService):
implements(IStatusReceiver)
def __init__(self, http_port=None, distrib_port=None, allowForce=False):
service.MultiService.__init__(self)
if type(http_port) is int:
http_port = "tcp:%d" % http_port
self.http_port = http_port
if distrib_port is not None:
if type(distrib_port) is int:
distrib_port = "tcp:%d" % distrib_port
if distrib_port[0] in "/~.": # pathnames
distrib_port = "unix:%s" % distrib_port
self.distrib_port = distrib_port
self.allowForce = allowForce
self.root = static.File("public_html")
log.msg("WebStatus using (%s)" % self.root.path)
self.setupUsualPages()
# once we get enabled, we'll stash a reference to the main IStatus
# instance in site.status, so all of our childrens' render() methods
# can access it as request.site.status
self.site = server.Site(self.root)
self.site.header = HEADER
self.site.footer = FOOTER
if self.http_port is not None:
s = strports.service(self.http_port, self.site)
s.setServiceParent(self)
if self.distrib_port is not None:
f = pb.PBServerFactory(distrib.ResourcePublisher(self.site))
s = strports.service(self.distrib_port, f)
s.setServiceParent(self)
def setupUsualPages(self):
r = static.Data("This tree contains the built-in status pages\n",
"text/plain")
self.root.putChild("_buildbot", r)
#r.putChild("waterfall", WaterfallStatusResource
r.putChild("one_line_per_build", OneLinePerBuild())
def getStatus(self):
return self.site.status
def setServiceParent(self, parent):
"""
@type parent: L{buildbot.master.BuildMaster}
"""
service.MultiService.setServiceParent(self, parent)
self.setup()
def setup(self):
status = self.parent.getStatus()
if self.allowForce:
control = IControl(self.parent)
else:
control = None
self.site.webstatus = self # TODO: why?
self.site.status = status
self.site.control = control
self.site.basedir = self.parent.basedir # TODO: also why?
# resources can get access to the site with request.site
class HtmlResource(Resource):
# this is a cheap sort of template thingy
css = None
contentType = "text/html; charset=UTF-8"
title = "Dummy"
def render(self, request):
data = self.content(request)
if isinstance(data, unicode):
data = data.encode("utf-8")
request.setHeader("content-type", self.contentType)
if request.method == "HEAD":
request.setHeader("content-length", len(data))
return ''
return data
def make_head(self, request):
data = ""
data += ' <title>%s</title>\n' % self.title
# TODO: use some sort of relative link up to the root page, so
# this css can be used from child pages too
data += ' <link href="/buildbot.css" rel="stylesheet" type="text/css"/>\n'
return data
def content(self, request):
data = ""
data += request.site.header
data += "<head>\n"
data += self.make_head(request)
data += "</head>\n"
data += '<body vlink="#800080">\n'
data += self.body(request)
data += "</body>\n"
data += request.site.footer
return data
def body(self, request):
return "Dummy\n"
class TimelineOfEverything(WaterfallStatusResource):
def __init__(self):
HtmlResource.__init__(self)
def render(self, request):
webstatus = request.site.webstatus
self.css = webstatus.css
self.status = request.site.status
self.changemaster = webstatus.parent.change_svc
self.categories = None
self.title = self.status.getProjectName()
if self.title is None:
self.title = "BuildBot"
return WaterfallStatusResource.render(self, request)
class LastBuild(HtmlResource):
def body(self, request):
return "missing\n"
def getLastNBuilds(status, numbuilds, desired_builder_names=None):
"""Return a list with the last few Builds, sorted by start time.
builder_names=None means all builders
"""
# TODO: this unsorts the list of builder names, ick
builder_names = set(status.getBuilderNames())
if desired_builder_names is not None:
desired_builder_names = set(desired_builder_names)
builder_names = builder_names.intersection(desired_builder_names)
# to make sure that we get everything, we must get 'numbuilds' builds
# from *each* source, then sort by ending time, then trim to the last
# 20. We could be more efficient, but it would require the same
# gnarly code that the Waterfall uses to generate one event at a
# time.
events = []
for builder_name in builder_names:
builder = status.getBuilder(builder_name)
for build_number in count(1):
if build_number > numbuilds:
break # enough from this builder, move on to another
build = builder.getBuild(-build_number)
if not build:
break # no more builds here, move on to the next builder
#if not build.isFinished():
# continue
(build_start, build_end) = build.getTimes()
event = (build_start, builder_name, build)
events.append(event)
def _sorter(a, b):
return cmp( a[:2], b[:2] )
events.sort(_sorter)
# now only return the actual build, and only return some of them
return [e[2] for e in events[-numbuilds:]]
def oneLineForABuild(status, build):
css_classes = {SUCCESS: "success",
WARNINGS: "warnings",
FAILURE: "failure",
EXCEPTION: "exception",
}
builder_name = build.getBuilder().getName()
results = build.getResults()
rev = build.getProperty("got_revision")
if len(rev) > 20:
rev = "?"
values = {'class': css_classes[results],
'builder_name': builder_name,
'buildnum': build.getNumber(),
'results': css_classes[results],
'buildurl': status.getURLForThing(build),
'rev': rev,
}
fmt = ('<div class="%(class)s">Build '
'<a href="%(buildurl)s">#%(buildnum)d</a> of '
'%(builder_name)s [%(rev)s]: '
'<span class="%(class)s">%(results)s</span></div>\n')
data = fmt % values
return data
# /_buildbot/one_line_per_build
class OneLinePerBuild(HtmlResource):
"""This shows one line per build, combining all builders together. Useful
query arguments:
numbuilds=: how many lines to display
builder=: show only builds for this builder. Multiple builder= arguments
can be used to see builds from any builder in the set.
"""
def __init__(self, numbuilds=20):
HtmlResource.__init__(self)
self.numbuilds = numbuilds
def getChild(self, path, request):
status = request.site.status
builder = status.getBuilder(path)
return OneLinePerBuildOneBuilder(builder)
def body(self, request):
status = request.site.status
numbuilds = self.numbuilds
if "numbuilds" in request.args:
numbuilds = int(request.args["numbuilds"][0])
desired_builder_names = None
if "builder" in request.args:
desired_builder_names = request.args["builder"]
builds = getLastNBuilds(status, numbuilds, desired_builder_names)
data = ""
for build in reversed(builds):
data += oneLineForABuild(status, build)
else:
data += "<div>No matching builds found</div>"
return data
# /_buildbot/one_line_per_build/$BUILDERNAME
class OneLinePerBuildOneBuilder(HtmlResource):
def __init__(self, builder, numbuilds=20):
HtmlResource.__init__(self)
self.builder = builder
self.numbuilds = numbuilds
def body(self, request):
status = request.site.status
numbuilds = self.numbuilds
if "numbuilds" in request.args:
numbuilds = int(request.args["numbuilds"][0])
# walk backwards through all builds of a single builder
# islice is cool but not exactly what we need here
#events = itertools.islice(b.eventGenerator(), self.numbuilds)
css_classes = {SUCCESS: "success",
WARNINGS: "warnings",
FAILURE: "failure",
EXCEPTION: "exception",
}
data = ""
i = 1
while i < numbuilds:
build = self.builder.getBuild(-i)
if not build:
break
i += 1
data += oneLineForABuild(status, build)
return data
More information about the Commits
mailing list