| Index: third_party/buildbot_7_12/buildbot/status/web/baseweb.py
|
| diff --git a/third_party/buildbot_7_12/buildbot/status/web/baseweb.py b/third_party/buildbot_7_12/buildbot/status/web/baseweb.py
|
| deleted file mode 100644
|
| index d7e18663f3284353290d406a70c643a13b61e77f..0000000000000000000000000000000000000000
|
| --- a/third_party/buildbot_7_12/buildbot/status/web/baseweb.py
|
| +++ /dev/null
|
| @@ -1,716 +0,0 @@
|
| -
|
| -import os, sys, urllib, weakref
|
| -from itertools import count
|
| -
|
| -from zope.interface import implements
|
| -from twisted.python import log
|
| -from twisted.application import strports, service
|
| -from twisted.web import server, distrib, static, html
|
| -from twisted.spread import pb
|
| -
|
| -from buildbot.interfaces import IControl, IStatusReceiver
|
| -
|
| -from buildbot.status.web.base import HtmlResource, Box, \
|
| - build_get_class, ICurrentBox, OneLineMixin, map_branches, \
|
| - make_stop_form, make_force_build_form
|
| -from buildbot.status.web.feeds import Rss20StatusResource, \
|
| - Atom10StatusResource
|
| -from buildbot.status.web.waterfall import WaterfallStatusResource
|
| -from buildbot.status.web.console import ConsoleStatusResource
|
| -from buildbot.status.web.grid import GridStatusResource, TransposedGridStatusResource
|
| -from buildbot.status.web.changes import ChangesResource
|
| -from buildbot.status.web.builder import BuildersResource
|
| -from buildbot.status.web.buildstatus import BuildStatusStatusResource
|
| -from buildbot.status.web.slaves import BuildSlavesResource
|
| -from buildbot.status.web.status_json import JsonStatusResource
|
| -from buildbot.status.web.xmlrpc import XMLRPCServer
|
| -from buildbot.status.web.about import AboutBuildbot
|
| -from buildbot.status.web.auth import IAuth, AuthFailResource
|
| -
|
| -# this class contains the status services (WebStatus and the older Waterfall)
|
| -# which can be put in c['status']. It also contains some of the resources
|
| -# that are attached to the WebStatus at various well-known URLs, which the
|
| -# admin might wish to attach (using WebStatus.putChild) at other URLs.
|
| -
|
| -
|
| -class LastBuild(HtmlResource):
|
| - def body(self, request):
|
| - return "missing\n"
|
| -
|
| -def getLastNBuilds(status, numbuilds, builders=[], branches=[]):
|
| - """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 builders:
|
| - builder_names = builder_names.intersection(set(builders))
|
| -
|
| - # 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. TODO: factor that code out into some useful class.
|
| - 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:]]
|
| -
|
| -
|
| -# /one_line_per_build
|
| -# accepts builder=, branch=, numbuilds=, reload=
|
| -class OneLinePerBuild(HtmlResource, OneLineMixin):
|
| - """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.
|
| - reload=: reload the page after this many seconds
|
| - """
|
| -
|
| - title = "Recent Builds"
|
| -
|
| - def __init__(self, numbuilds=20):
|
| - HtmlResource.__init__(self)
|
| - self.numbuilds = numbuilds
|
| -
|
| - def getChild(self, path, req):
|
| - status = self.getStatus(req)
|
| - builder = status.getBuilder(path)
|
| - return OneLinePerBuildOneBuilder(builder, numbuilds=self.numbuilds)
|
| -
|
| - def get_reload_time(self, request):
|
| - if "reload" in request.args:
|
| - try:
|
| - reload_time = int(request.args["reload"][0])
|
| - return max(reload_time, 15)
|
| - except ValueError:
|
| - pass
|
| - return None
|
| -
|
| - def head(self, request):
|
| - head = ''
|
| - reload_time = self.get_reload_time(request)
|
| - if reload_time is not None:
|
| - head += '<meta http-equiv="refresh" content="%d">\n' % reload_time
|
| - return head
|
| -
|
| - def body(self, req):
|
| - status = self.getStatus(req)
|
| - control = self.getControl(req)
|
| - numbuilds = int(req.args.get("numbuilds", [self.numbuilds])[0])
|
| - builders = req.args.get("builder", [])
|
| - branches = [b for b in req.args.get("branch", []) if b]
|
| -
|
| - g = status.generateFinishedBuilds(builders, map_branches(branches),
|
| - numbuilds, max_search=numbuilds)
|
| -
|
| - data = ""
|
| -
|
| - # really this is "up to %d builds"
|
| - html_branches = map(html.escape, branches)
|
| - data += "<h1>Last %d finished builds: %s</h1>\n" % \
|
| - (numbuilds, ", ".join(html_branches))
|
| - if builders:
|
| - html_builders = map(html.escape, builders)
|
| - data += ("<p>of builders: %s</p>\n" % (", ".join(html_builders)))
|
| - data += "<ul>\n"
|
| - got = 0
|
| - building = False
|
| - online = 0
|
| - for build in g:
|
| - got += 1
|
| - data += " <li>" + self.make_line(req, build) + "</li>\n"
|
| - builder_status = build.getBuilder().getState()[0]
|
| - if builder_status == "building":
|
| - building = True
|
| - online += 1
|
| - elif builder_status != "offline":
|
| - online += 1
|
| - if not got:
|
| - data += " <li>No matching builds found</li>\n"
|
| - data += "</ul>\n"
|
| -
|
| - if control is not None:
|
| - if building:
|
| - stopURL = "builders/_all/stop"
|
| - data += make_stop_form(stopURL, self.isUsingUserPasswd(req),
|
| - True, "Builds")
|
| - if online:
|
| - forceURL = "builders/_all/force"
|
| - data += make_force_build_form(forceURL,
|
| - self.isUsingUserPasswd(req), True)
|
| -
|
| - return data
|
| -
|
| -
|
| -
|
| -# /one_line_per_build/$BUILDERNAME
|
| -# accepts branch=, numbuilds=
|
| -
|
| -class OneLinePerBuildOneBuilder(HtmlResource, OneLineMixin):
|
| - def __init__(self, builder, numbuilds=20):
|
| - HtmlResource.__init__(self)
|
| - self.builder = builder
|
| - self.builder_name = builder.getName()
|
| - self.numbuilds = numbuilds
|
| - self.title = "Recent Builds of %s" % self.builder_name
|
| -
|
| - def body(self, req):
|
| - status = self.getStatus(req)
|
| - numbuilds = int(req.args.get("numbuilds", [self.numbuilds])[0])
|
| - branches = [b for b in req.args.get("branch", []) if b]
|
| -
|
| - # walk backwards through all builds of a single builder
|
| - g = self.builder.generateFinishedBuilds(map_branches(branches),
|
| - numbuilds)
|
| -
|
| - data = ""
|
| - html_branches = map(html.escape, branches)
|
| - data += ("<h1>Last %d builds of builder %s: %s</h1>\n" %
|
| - (numbuilds, self.builder_name, ", ".join(html_branches)))
|
| - data += "<ul>\n"
|
| - got = 0
|
| - for build in g:
|
| - got += 1
|
| - data += " <li>" + self.make_line(req, build) + "</li>\n"
|
| - if not got:
|
| - data += " <li>No matching builds found</li>\n"
|
| - data += "</ul>\n"
|
| -
|
| - return data
|
| -
|
| -# /one_box_per_builder
|
| -# accepts builder=, branch=
|
| -class OneBoxPerBuilder(HtmlResource):
|
| - """This shows a narrow table with one row per builder. The leftmost column
|
| - contains the builder name. The next column contains the results of the
|
| - most recent build. The right-hand column shows the builder's current
|
| - activity.
|
| -
|
| - builder=: show only builds for this builder. Multiple builder= arguments
|
| - can be used to see builds from any builder in the set.
|
| - """
|
| -
|
| - title = "Latest Build"
|
| -
|
| - def body(self, req):
|
| - status = self.getStatus(req)
|
| - control = self.getControl(req)
|
| -
|
| - builders = req.args.get("builder", status.getBuilderNames())
|
| - branches = [b for b in req.args.get("branch", []) if b]
|
| -
|
| - data = ""
|
| -
|
| - html_branches = map(html.escape, branches)
|
| - data += "<h2>Latest builds: %s</h2>\n" % ", ".join(html_branches)
|
| - data += "<table>\n"
|
| -
|
| - building = False
|
| - online = 0
|
| - base_builders_url = self.path_to_root(req) + "builders/"
|
| - for bn in builders:
|
| - base_builder_url = base_builders_url + urllib.quote(bn, safe='')
|
| - builder = status.getBuilder(bn)
|
| - data += "<tr>\n"
|
| - data += '<td class="box"><a href="%s">%s</a></td>\n' \
|
| - % (base_builder_url, html.escape(bn))
|
| - builds = list(builder.generateFinishedBuilds(map_branches(branches),
|
| - num_builds=1))
|
| - if builds:
|
| - b = builds[0]
|
| - url = (base_builder_url + "/builds/%d" % b.getNumber())
|
| - try:
|
| - label = b.getProperty("got_revision")
|
| - except KeyError:
|
| - label = None
|
| - if not label or len(str(label)) > 20:
|
| - label = "#%d" % b.getNumber()
|
| - text = ['<a href="%s">%s</a>' % (url, label)]
|
| - text.extend(b.getText())
|
| - box = Box(text,
|
| - class_="LastBuild box %s" % build_get_class(b))
|
| - data += box.td(align="center")
|
| - else:
|
| - data += '<td class="LastBuild box" >no build</td>\n'
|
| - current_box = ICurrentBox(builder).getBox(status)
|
| - data += current_box.td(align="center")
|
| -
|
| - builder_status = builder.getState()[0]
|
| - if builder_status == "building":
|
| - building = True
|
| - online += 1
|
| - elif builder_status != "offline":
|
| - online += 1
|
| -
|
| - data += "</table>\n"
|
| -
|
| - if control is not None:
|
| - if building:
|
| - stopURL = "builders/_all/stop"
|
| - data += make_stop_form(stopURL, self.isUsingUserPasswd(req),
|
| - True, "Builds")
|
| - if online:
|
| - forceURL = "builders/_all/force"
|
| - data += make_force_build_form(forceURL,
|
| - self.isUsingUserPasswd(req), True)
|
| -
|
| - return data
|
| -
|
| -
|
| -
|
| -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">
|
| -'''
|
| -
|
| -HEAD_ELEMENTS = [
|
| - '<title>%(title)s</title>',
|
| - '<link href="%(root)sbuildbot.css" rel="stylesheet" type="text/css" />',
|
| - ]
|
| -BODY_ATTRS = {
|
| - 'vlink': "#800080",
|
| - }
|
| -
|
| -FOOTER = '''
|
| -</html>
|
| -'''
|
| -
|
| -
|
| -class WebStatus(service.MultiService):
|
| - implements(IStatusReceiver)
|
| - # TODO: IStatusReceiver is really about things which subscribe to hear
|
| - # about buildbot events. We need a different interface (perhaps a parent
|
| - # of IStatusReceiver) for status targets that don't subscribe, like the
|
| - # WebStatus class. buildbot.master.BuildMaster.loadConfig:737 asserts
|
| - # that everything in c['status'] provides IStatusReceiver, but really it
|
| - # should check that they provide IStatusTarget instead.
|
| -
|
| - """
|
| - The webserver provided by this class has the following resources:
|
| -
|
| - /waterfall : the big time-oriented 'waterfall' display, with links
|
| - to individual changes, builders, builds, steps, and logs.
|
| - A number of query-arguments can be added to influence
|
| - the display.
|
| - /rss : a rss feed summarizing all failed builds. The same
|
| - query-arguments used by 'waterfall' can be added to
|
| - influence the feed output.
|
| - /atom : an atom feed summarizing all failed builds. The same
|
| - query-arguments used by 'waterfall' can be added to
|
| - influence the feed output.
|
| - /grid : another summary display that shows a grid of builds, with
|
| - sourcestamps on the x axis, and builders on the y. Query
|
| - arguments similar to those for the waterfall can be added.
|
| - /tgrid : similar to the grid display, but the commits are down the
|
| - left side, and the build hosts are across the top.
|
| - /builders/BUILDERNAME: a page summarizing the builder. This includes
|
| - references to the Schedulers that feed it,
|
| - any builds currently in the queue, which
|
| - buildslaves are designated or attached, and a
|
| - summary of the build process it uses.
|
| - /builders/BUILDERNAME/builds/NUM: a page describing a single Build
|
| - /builders/BUILDERNAME/builds/NUM/steps/STEPNAME: describes a single step
|
| - /builders/BUILDERNAME/builds/NUM/steps/STEPNAME/logs/LOGNAME: a StatusLog
|
| - /builders/BUILDERNAME/builds/NUM/tests : summarize test results
|
| - /builders/BUILDERNAME/builds/NUM/tests/TEST.NAME: results of one test
|
| - /builders/_all/{force,stop}: force a build/stop building on all builders.
|
| - /changes : summarize all ChangeSources
|
| - /changes/CHANGENUM: a page describing a single Change
|
| - /schedulers/SCHEDULERNAME: a page describing a Scheduler, including
|
| - a description of its behavior, a list of the
|
| - Builders it triggers, and list of the Changes
|
| - that are queued awaiting the tree-stable
|
| - timer, and controls to accelerate the timer.
|
| - /buildslaves : list all BuildSlaves
|
| - /buildslaves/SLAVENAME : describe a single BuildSlave
|
| - /one_line_per_build : summarize the last few builds, one line each
|
| - /one_line_per_build/BUILDERNAME : same, but only for a single builder
|
| - /one_box_per_builder : show the latest build and current activity
|
| - /about : describe this buildmaster (Buildbot and support library versions)
|
| - /xmlrpc : (not yet implemented) an XMLRPC server with build status
|
| -
|
| -
|
| - All URLs for pages which are not defined here are used to look
|
| - for files in PUBLIC_HTML, which defaults to BASEDIR/public_html.
|
| - This means that /robots.txt or /buildbot.css or /favicon.ico can
|
| - be placed in that directory.
|
| -
|
| - If an index file (index.html, index.htm, or index, in that order) is
|
| - present in PUBLIC_HTML, it will be used for the root resource. If not,
|
| - the default behavior is to put a redirection to the /waterfall page.
|
| -
|
| - All of the resources provided by this service use relative URLs to reach
|
| - each other. The only absolute links are the c['projectURL'] links at the
|
| - top and bottom of the page, and the buildbot home-page link at the
|
| - bottom.
|
| -
|
| - This webserver defines class attributes on elements so they can be styled
|
| - with CSS stylesheets. All pages pull in PUBLIC_HTML/buildbot.css, and you
|
| - can cause additional stylesheets to be loaded by adding a suitable <link>
|
| - to the WebStatus instance's .head_elements attribute.
|
| -
|
| - Buildbot uses some generic classes to identify the type of object, and
|
| - some more specific classes for the various kinds of those types. It does
|
| - this by specifying both in the class attributes where applicable,
|
| - separated by a space. It is important that in your CSS you declare the
|
| - more generic class styles above the more specific ones. For example,
|
| - first define a style for .Event, and below that for .SUCCESS
|
| -
|
| - The following CSS class names are used:
|
| - - Activity, Event, BuildStep, LastBuild: general classes
|
| - - waiting, interlocked, building, offline, idle: Activity states
|
| - - start, running, success, failure, warnings, skipped, exception:
|
| - LastBuild and BuildStep states
|
| - - Change: box with change
|
| - - Builder: box for builder name (at top)
|
| - - Project
|
| - - Time
|
| -
|
| - """
|
| -
|
| - # we are not a ComparableMixin, and therefore the webserver will be
|
| - # rebuilt every time we reconfig. This is because WebStatus.putChild()
|
| - # makes it too difficult to tell whether two instances are the same or
|
| - # not (we'd have to do a recursive traversal of all children to discover
|
| - # all the changes).
|
| -
|
| - def __init__(self, http_port=None, distrib_port=None, allowForce=False,
|
| - public_html="public_html", site=None, numbuilds=20,
|
| - num_events=200, num_events_max=None, auth=None,
|
| - order_console_by_time=False):
|
| - """Run a web server that provides Buildbot status.
|
| -
|
| - @type http_port: int or L{twisted.application.strports} string
|
| - @param http_port: a strports specification describing which port the
|
| - buildbot should use for its web server, with the
|
| - Waterfall display as the root page. For backwards
|
| - compatibility this can also be an int. Use
|
| - 'tcp:8000' to listen on that port, or
|
| - 'tcp:12345:interface=127.0.0.1' if you only want
|
| - local processes to connect to it (perhaps because
|
| - you are using an HTTP reverse proxy to make the
|
| - buildbot available to the outside world, and do not
|
| - want to make the raw port visible).
|
| -
|
| - @type distrib_port: int or L{twisted.application.strports} string
|
| - @param distrib_port: Use this if you want to publish the Waterfall
|
| - page using web.distrib instead. The most common
|
| - case is to provide a string that is an absolute
|
| - pathname to the unix socket on which the
|
| - publisher should listen
|
| - (C{os.path.expanduser(~/.twistd-web-pb)} will
|
| - match the default settings of a standard
|
| - twisted.web 'personal web server'). Another
|
| - possibility is to pass an integer, which means
|
| - the publisher should listen on a TCP socket,
|
| - allowing the web server to be on a different
|
| - machine entirely. Both forms are provided for
|
| - backwards compatibility; the preferred form is a
|
| - strports specification like
|
| - 'unix:/home/buildbot/.twistd-web-pb'. Providing
|
| - a non-absolute pathname will probably confuse
|
| - the strports parser.
|
| -
|
| - @param allowForce: boolean, if True then the webserver will allow
|
| - visitors to trigger and cancel builds
|
| -
|
| - @param public_html: the path to the public_html directory for this display,
|
| - either absolute or relative to the basedir. The default
|
| - is 'public_html', which selects BASEDIR/public_html.
|
| -
|
| - @type site: None or L{twisted.web.server.Site}
|
| - @param site: Use this if you want to define your own object instead of
|
| - using the default.`
|
| -
|
| - @type numbuilds: int
|
| - @param numbuilds: Default number of entries in lists at the /one_line_per_build
|
| - and /builders/FOO URLs. This default can be overriden both programatically ---
|
| - by passing the equally named argument to constructors of OneLinePerBuildOneBuilder
|
| - and OneLinePerBuild --- and via the UI, by tacking ?numbuilds=xy onto the URL.
|
| -
|
| - @type num_events: int
|
| - @param num_events: Defaualt number of events to show in the waterfall.
|
| -
|
| - @type num_events_max: int
|
| - @param num_events_max: The maximum number of events that are allowed to be
|
| - shown in the waterfall. The default value of C{None} will disable this
|
| - check
|
| -
|
| - @type auth: a L{status.web.auth.IAuth} or C{None}
|
| - @param auth: an object that performs authentication to restrict access
|
| - to the C{allowForce} features. Ignored if C{allowForce}
|
| - is not C{True}. If C{auth} is C{None}, people can force or
|
| - stop builds without auth.
|
| -
|
| - @type order_console_by_time: bool
|
| - @param order_console_by_time: Whether to order changes (commits) in the console
|
| - view according to the time they were created (for VCS like Git) or
|
| - according to their integer revision numbers (for VCS like SVN).
|
| - """
|
| -
|
| - 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.num_events = num_events
|
| - if num_events_max:
|
| - assert num_events_max >= num_events
|
| - self.num_events_max = num_events_max
|
| - self.public_html = public_html
|
| -
|
| - if self.allowForce and auth:
|
| - assert IAuth.providedBy(auth)
|
| - self.auth = auth
|
| - else:
|
| - if auth:
|
| - log.msg("Warning: Ignoring authentication. allowForce must be"
|
| - " set to True use this")
|
| - self.auth = None
|
| -
|
| - self.orderConsoleByTime = order_console_by_time
|
| -
|
| - # If we were given a site object, go ahead and use it.
|
| - if site:
|
| - self.site = site
|
| - else:
|
| - # this will be replaced once we've been attached to a parent (and
|
| - # thus have a basedir and can reference BASEDIR)
|
| - root = static.Data("placeholder", "text/plain")
|
| - self.site = server.Site(root)
|
| - self.childrenToBeAdded = {}
|
| -
|
| - self.setupUsualPages(numbuilds=numbuilds, num_events=num_events,
|
| - num_events_max=num_events_max)
|
| -
|
| - # the following items are accessed by HtmlResource when it renders
|
| - # each page.
|
| - self.site.buildbot_service = self
|
| - self.header = HEADER
|
| - self.head_elements = HEAD_ELEMENTS[:]
|
| - self.body_attrs = BODY_ATTRS.copy()
|
| - self.footer = FOOTER
|
| - self.template_values = {}
|
| -
|
| - # keep track of cached connections so we can break them when we shut
|
| - # down. See ticket #102 for more details.
|
| - self.channels = weakref.WeakKeyDictionary()
|
| -
|
| - 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, numbuilds, num_events, num_events_max):
|
| - #self.putChild("", IndexOrWaterfallRedirection())
|
| - self.putChild("waterfall", WaterfallStatusResource(num_events=num_events,
|
| - num_events_max=num_events_max))
|
| - self.putChild("grid", GridStatusResource())
|
| - self.putChild("console", ConsoleStatusResource(
|
| - orderByTime=self.orderConsoleByTime))
|
| - self.putChild("tgrid", TransposedGridStatusResource())
|
| - self.putChild("builders", BuildersResource()) # has builds/steps/logs
|
| - self.putChild("changes", ChangesResource())
|
| - self.putChild("buildslaves", BuildSlavesResource())
|
| - self.putChild("buildstatus", BuildStatusStatusResource())
|
| - #self.putChild("schedulers", SchedulersResource())
|
| - self.putChild("one_line_per_build",
|
| - OneLinePerBuild(numbuilds=numbuilds))
|
| - self.putChild("one_box_per_builder", OneBoxPerBuilder())
|
| - self.putChild("xmlrpc", XMLRPCServer())
|
| - self.putChild("about", AboutBuildbot())
|
| - self.putChild("authfail", AuthFailResource())
|
| -
|
| - def __repr__(self):
|
| - if self.http_port is None:
|
| - return "<WebStatus on path %s at %s>" % (self.distrib_port,
|
| - hex(id(self)))
|
| - if self.distrib_port is None:
|
| - return "<WebStatus on port %s at %s>" % (self.http_port,
|
| - hex(id(self)))
|
| - return ("<WebStatus on port %s and path %s at %s>" %
|
| - (self.http_port, self.distrib_port, hex(id(self))))
|
| -
|
| - def setServiceParent(self, parent):
|
| - service.MultiService.setServiceParent(self, parent)
|
| -
|
| - # this class keeps a *separate* link to the buildmaster, rather than
|
| - # just using self.parent, so that when we are "disowned" (and thus
|
| - # parent=None), any remaining HTTP clients of this WebStatus will still
|
| - # be able to get reasonable results.
|
| - self.master = parent
|
| -
|
| - self.setupSite()
|
| -
|
| - def setupSite(self):
|
| - # this is responsible for creating the root resource. It isn't done
|
| - # at __init__ time because we need to reference the parent's basedir.
|
| - htmldir = os.path.abspath(os.path.join(self.master.basedir, self.public_html))
|
| - if os.path.isdir(htmldir):
|
| - log.msg("WebStatus using (%s)" % htmldir)
|
| - else:
|
| - log.msg("WebStatus: warning: %s is missing. Do you need to run"
|
| - " 'buildbot upgrade-master' on this buildmaster?" % htmldir)
|
| - # all static pages will get a 404 until upgrade-master is used to
|
| - # populate this directory. Create the directory, though, since
|
| - # otherwise we get internal server errors instead of 404s.
|
| - os.mkdir(htmldir)
|
| - root = static.File(htmldir)
|
| -
|
| - for name, child_resource in self.childrenToBeAdded.iteritems():
|
| - root.putChild(name, child_resource)
|
| -
|
| - status = self.getStatus()
|
| - # Disabled from Chromium.
|
| - # root.putChild("rss", Rss20StatusResource(status))
|
| - # root.putChild("atom", Atom10StatusResource(status))
|
| - root.putChild("json", JsonStatusResource(status))
|
| -
|
| - self.site.resource = root
|
| -
|
| - def putChild(self, name, child_resource):
|
| - """This behaves a lot like root.putChild() . """
|
| - self.childrenToBeAdded[name] = child_resource
|
| -
|
| - def registerChannel(self, channel):
|
| - self.channels[channel] = 1 # weakrefs
|
| -
|
| - def stopService(self):
|
| - for channel in self.channels:
|
| - try:
|
| - channel.transport.loseConnection()
|
| - except:
|
| - log.msg("WebStatus.stopService: error while disconnecting"
|
| - " leftover clients")
|
| - log.err()
|
| - return service.MultiService.stopService(self)
|
| -
|
| - def getStatus(self):
|
| - return self.master.getStatus()
|
| -
|
| - def getControl(self):
|
| - if self.allowForce:
|
| - return IControl(self.master)
|
| - return None
|
| -
|
| - def getChangeSvc(self):
|
| - return self.master.change_svc
|
| -
|
| - def getPortnum(self):
|
| - # this is for the benefit of unit tests
|
| - s = list(self)[0]
|
| - return s._port.getHost().port
|
| -
|
| - def isUsingUserPasswd(self):
|
| - """Returns boolean to indicate if this WebStatus uses authentication"""
|
| - if self.auth:
|
| - return True
|
| - return False
|
| -
|
| - def authUser(self, user, passwd):
|
| - """Check that user/passwd is a valid user/pass tuple and can should be
|
| - allowed to perform the action. If this WebStatus is not password
|
| - protected, this function returns False."""
|
| - if not self.isUsingUserPasswd():
|
| - return False
|
| - if self.auth.authenticate(user, passwd):
|
| - return True
|
| - log.msg("Authentication failed for '%s': %s" % (user,
|
| - self.auth.errmsg()))
|
| - return False
|
| -
|
| -# resources can get access to the IStatus by calling
|
| -# request.site.buildbot_service.getStatus()
|
| -
|
| -# this is the compatibility class for the old waterfall. It is exactly like a
|
| -# regular WebStatus except that the root resource (e.g. http://buildbot.net/)
|
| -# always redirects to a WaterfallStatusResource, and the old arguments are
|
| -# mapped into the new resource-tree approach. In the normal WebStatus, the
|
| -# root resource either redirects the browser to /waterfall or serves
|
| -# PUBLIC_HTML/index.html, and favicon/robots.txt are provided by
|
| -# having the admin write actual files into PUBLIC_HTML/ .
|
| -
|
| -# note: we don't use a util.Redirect here because HTTP requires that the
|
| -# Location: header provide an absolute URI, and it's non-trivial to figure
|
| -# out our absolute URI from here.
|
| -
|
| -class Waterfall(WebStatus):
|
| -
|
| - if hasattr(sys, "frozen"):
|
| - # all 'data' files are in the directory of our executable
|
| - here = os.path.dirname(sys.executable)
|
| - buildbot_icon = os.path.abspath(os.path.join(here, "buildbot.png"))
|
| - buildbot_css = os.path.abspath(os.path.join(here, "classic.css"))
|
| - else:
|
| - # running from source
|
| - # the icon is sibpath(__file__, "../buildbot.png") . This is for
|
| - # portability.
|
| - up = os.path.dirname
|
| - buildbot_icon = os.path.abspath(os.path.join(up(up(up(__file__))),
|
| - "buildbot.png"))
|
| - buildbot_css = os.path.abspath(os.path.join(up(__file__),
|
| - "classic.css"))
|
| -
|
| - compare_attrs = ["http_port", "distrib_port", "allowForce",
|
| - "categories", "css", "favicon", "robots_txt"]
|
| -
|
| - def __init__(self, http_port=None, distrib_port=None, allowForce=True,
|
| - categories=None, css=buildbot_css, favicon=buildbot_icon,
|
| - robots_txt=None):
|
| - import warnings
|
| - m = ("buildbot.status.html.Waterfall is deprecated as of 0.7.6 "
|
| - "and will be removed from a future release. "
|
| - "Please use html.WebStatus instead.")
|
| - warnings.warn(m, DeprecationWarning)
|
| -
|
| - WebStatus.__init__(self, http_port, distrib_port, allowForce)
|
| - self.css = css
|
| - if css:
|
| - if os.path.exists(os.path.join("public_html", "buildbot.css")):
|
| - # they've upgraded, so defer to that copy instead
|
| - pass
|
| - else:
|
| - data = open(css, "rb").read()
|
| - self.putChild("buildbot.css", static.Data(data, "text/css"))
|
| - self.favicon = favicon
|
| - self.robots_txt = robots_txt
|
| - if favicon:
|
| - data = open(favicon, "rb").read()
|
| - self.putChild("favicon.ico", static.Data(data, "image/x-icon"))
|
| - if robots_txt:
|
| - data = open(robots_txt, "rb").read()
|
| - self.putChild("robots.txt", static.Data(data, "text/plain"))
|
| - self.putChild("", WaterfallStatusResource(categories))
|
|
|