aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py')
-rw-r--r--lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py744
1 files changed, 0 insertions, 744 deletions
diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py
deleted file mode 100644
index 60eb67c0..00000000
--- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/status/web/console.py
+++ /dev/null
@@ -1,744 +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 time
-import operator
-import re
-import urllib
-from twisted.internet import defer
-from buildbot import util
-from buildbot.status import builder
-from buildbot.status.web.base import HtmlResource
-from buildbot.changes import changes
-
-class DoesNotPassFilter(Exception): pass # Used for filtering revs
-
-def getResultsClass(results, prevResults, inProgress):
- """Given the current and past results, return the class that will be used
- by the css to display the right color for a box."""
-
- if inProgress:
- return "running"
-
- if results is None:
- return "notstarted"
-
- if results == builder.SUCCESS:
- return "success"
-
- if results == builder.WARNINGS:
- return "warnings"
-
- if results == builder.FAILURE:
- if not prevResults:
- # This is the bottom box. We don't know if the previous one failed
- # or not. We assume it did not.
- return "failure"
-
- if prevResults != builder.FAILURE:
- # This is a new failure.
- return "failure"
- else:
- # The previous build also failed.
- return "failure-again"
-
- # Any other results? Like EXCEPTION?
- return "exception"
-
-class ANYBRANCH: pass # a flag value, used below
-
-class DevRevision:
- """Helper class that contains all the information we need for a revision."""
-
- def __init__(self, change):
- self.revision = change.revision
- self.comments = change.comments
- self.who = change.who
- self.date = change.getTime()
- self.revlink = getattr(change, 'revlink', None)
- self.when = change.when
- self.repository = change.repository
- self.project = change.project
-
-
-class DevBuild:
- """Helper class that contains all the information we need for a build."""
-
- def __init__(self, revision, build, details):
- self.revision = revision
- self.results = build.getResults()
- self.number = build.getNumber()
- self.isFinished = build.isFinished()
- self.text = build.getText()
- self.eta = build.getETA()
- self.details = details
- self.when = build.getTimes()[0]
- #TODO: support multiple sourcestamps
- self.source = build.getSourceStamps()[0]
-
-
-class ConsoleStatusResource(HtmlResource):
- """Main console class. It displays a user-oriented status page.
- Every change is a line in the page, and it shows the result of the first
- build with this change for each slave."""
-
- def __init__(self, orderByTime=False):
- HtmlResource.__init__(self)
-
- self.status = None
-
- if orderByTime:
- self.comparator = TimeRevisionComparator()
- else:
- self.comparator = IntegerRevisionComparator()
-
- def getPageTitle(self, request):
- status = self.getStatus(request)
- title = status.getTitle()
- if title:
- return "BuildBot: %s" % title
- else:
- return "BuildBot"
-
- def getChangeManager(self, request):
- return request.site.buildbot_service.parent.change_svc
-
- ##
- ## Data gathering functions
- ##
-
- def getHeadBuild(self, builder):
- """Get the most recent build for the given builder.
- """
- build = builder.getBuild(-1)
-
- # HACK: Work around #601, the head build may be None if it is
- # locked.
- if build is None:
- build = builder.getBuild(-2)
-
- return build
-
- def fetchChangesFromHistory(self, status, max_depth, max_builds, debugInfo):
- """Look at the history of the builders and try to fetch as many changes
- as possible. We need this when the main source does not contain enough
- sourcestamps.
-
- max_depth defines how many builds we will parse for a given builder.
- max_builds defines how many builds total we want to parse. This is to
- limit the amount of time we spend in this function.
-
- This function is sub-optimal, but the information returned by this
- function is cached, so this function won't be called more than once.
- """
-
- allChanges = list()
- build_count = 0
- for builderName in status.getBuilderNames()[:]:
- if build_count > max_builds:
- break
-
- builder = status.getBuilder(builderName)
- build = self.getHeadBuild(builder)
- depth = 0
- while build and depth < max_depth and build_count < max_builds:
- depth += 1
- build_count += 1
- sourcestamp = build.getSourceStamps()[0]
- allChanges.extend(sourcestamp.changes[:])
- build = build.getPreviousBuild()
-
- debugInfo["source_fetch_len"] = len(allChanges)
- return allChanges
-
- @defer.inlineCallbacks
- def getAllChanges(self, request, status, debugInfo):
- master = request.site.buildbot_service.master
-
- chdicts = yield master.db.changes.getRecentChanges(25)
-
- # convert those to Change instances
- allChanges = yield defer.gatherResults([
- changes.Change.fromChdict(master, chdict)
- for chdict in chdicts ])
-
- allChanges.sort(key=self.comparator.getSortingKey())
-
- # Remove the dups
- prevChange = None
- newChanges = []
- for change in allChanges:
- rev = change.revision
- if not prevChange or rev != prevChange.revision:
- newChanges.append(change)
- prevChange = change
- allChanges = newChanges
-
- defer.returnValue(allChanges)
-
- def getBuildDetails(self, request, builderName, build):
- """Returns an HTML list of failures for a given build."""
- details = {}
- if not build.getLogs():
- return details
-
- for step in build.getSteps():
- (result, reason) = step.getResults()
- if result == builder.FAILURE:
- name = step.getName()
-
- # Remove html tags from the error text.
- stripHtml = re.compile(r'<.*?>')
- strippedDetails = stripHtml.sub('', ' '.join(step.getText()))
-
- details['buildername'] = builderName
- details['status'] = strippedDetails
- details['reason'] = reason
- logs = details['logs'] = []
-
- if step.getLogs():
- for log in step.getLogs():
- logname = log.getName()
- logurl = request.childLink(
- "../builders/%s/builds/%s/steps/%s/logs/%s" %
- (urllib.quote(builderName),
- build.getNumber(),
- urllib.quote(name),
- urllib.quote(logname)))
- logs.append(dict(url=logurl, name=logname))
- return details
-
- def getBuildsForRevision(self, request, builder, builderName, codebase,
- lastRevision, numBuilds, debugInfo):
- """Return the list of all the builds for a given builder that we will
- need to be able to display the console page. We start by the most recent
- build, and we go down until we find a build that was built prior to the
- last change we are interested in."""
-
- revision = lastRevision
-
- builds = []
- build = self.getHeadBuild(builder)
- number = 0
- while build and number < numBuilds:
- debugInfo["builds_scanned"] += 1
-
- got_rev = None
- sourceStamps = build.getSourceStamps(absolute=True)
-
- # The console page cannot handle builds that have more than 1 revision
- if codebase is not None:
- # Get the last revision in this build for this codebase.
- for ss in sourceStamps:
- if ss.codebase == codebase:
- got_rev = ss.revision
- break
- elif len(sourceStamps) == 1:
- ss = sourceStamps[0]
- # Get the last revision in this build.
- got_rev = ss.revision
-
- # We ignore all builds that don't have last revisions.
- # TODO(nsylvain): If the build is over, maybe it was a problem
- # with the update source step. We need to find a way to tell the
- # user that his change might have broken the source update.
- if got_rev is not None:
- number += 1
- details = self.getBuildDetails(request, builderName, build)
- devBuild = DevBuild(got_rev, build, details)
- builds.append(devBuild)
-
- # Now break if we have enough builds.
- current_revision = self.getChangeForBuild(
- build, revision)
- if self.comparator.isRevisionEarlier(
- devBuild, current_revision):
- break
-
- build = build.getPreviousBuild()
-
- return builds
-
- def getChangeForBuild(self, build, revision):
- if not build or not build.getChanges(): # Forced build
- return DevBuild(revision, build, None)
-
- for change in build.getChanges():
- if change.revision == revision:
- return change
-
- # No matching change, return the last change in build.
- changes = list(build.getChanges())
- changes.sort(key=self.comparator.getSortingKey())
- return changes[-1]
-
- def getAllBuildsForRevision(self, status, request, codebase, lastRevision,
- numBuilds, categories, builders, debugInfo):
- """Returns a dictionary of builds we need to inspect to be able to
- display the console page. The key is the builder name, and the value is
- an array of build we care about. We also returns a dictionary of
- builders we care about. The key is it's category.
-
- codebase is the codebase to get revisions from
- lastRevision is the last revision we want to display in the page.
- categories is a list of categories to display. It is coming from the
- HTTP GET parameters.
- builders is a list of builders to display. It is coming from the HTTP
- GET parameters.
- """
-
- allBuilds = dict()
-
- # List of all builders in the dictionary.
- builderList = dict()
-
- debugInfo["builds_scanned"] = 0
- # Get all the builders.
- builderNames = status.getBuilderNames()[:]
- for builderName in builderNames:
- builder = status.getBuilder(builderName)
-
- # Make sure we are interested in this builder.
- if categories and builder.category not in categories:
- continue
- if builders and builderName not in builders:
- continue
-
- # We want to display this builder.
- category = builder.category or "default"
- # Strip the category to keep only the text before the first |.
- # This is a hack to support the chromium usecase where they have
- # multiple categories for each slave. We use only the first one.
- # TODO(nsylvain): Create another way to specify "display category"
- # in master.cfg.
- category = category.split('|')[0]
- if not builderList.get(category):
- builderList[category] = []
-
- # Append this builder to the dictionary of builders.
- builderList[category].append(builderName)
- # Set the list of builds for this builder.
- allBuilds[builderName] = self.getBuildsForRevision(request,
- builder,
- builderName,
- codebase,
- lastRevision,
- numBuilds,
- debugInfo)
-
- return (builderList, allBuilds)
-
-
- ##
- ## Display functions
- ##
-
- def displayCategories(self, builderList, debugInfo):
- """Display the top category line."""
-
- count = 0
- for category in builderList:
- count += len(builderList[category])
-
- categories = builderList.keys()
- categories.sort()
-
- cs = []
-
- for category in categories:
- c = {}
-
- c["name"] = category
-
- # To be able to align the table correctly, we need to know
- # what percentage of space this category will be taking. This is
- # (#Builders in Category) / (#Builders Total) * 100.
- c["size"] = (len(builderList[category]) * 100) / count
- cs.append(c)
-
- return cs
-
- def displaySlaveLine(self, status, builderList, debugInfo):
- """Display a line the shows the current status for all the builders we
- care about."""
-
- nbSlaves = 0
-
- # Get the number of builders.
- for category in builderList:
- nbSlaves += len(builderList[category])
-
- # Get the categories, and order them alphabetically.
- categories = builderList.keys()
- categories.sort()
-
- slaves = {}
-
- # For each category, we display each builder.
- for category in categories:
- slaves[category] = []
- # For each builder in this category, we set the build info and we
- # display the box.
- for builder in builderList[category]:
- s = {}
- s["color"] = "notstarted"
- s["pageTitle"] = builder
- s["url"] = "./builders/%s" % urllib.quote(builder)
- state, builds = status.getBuilder(builder).getState()
- # Check if it's offline, if so, the box is purple.
- if state == "offline":
- s["color"] = "offline"
- else:
- # If not offline, then display the result of the last
- # finished build.
- build = self.getHeadBuild(status.getBuilder(builder))
- while build and not build.isFinished():
- build = build.getPreviousBuild()
-
- if build:
- s["color"] = getResultsClass(build.getResults(), None,
- False)
-
- slaves[category].append(s)
-
- return slaves
-
- def displayStatusLine(self, builderList, allBuilds, revision, debugInfo):
- """Display the boxes that represent the status of each builder in the
- first build "revision" was in. Returns an HTML list of errors that
- happened during these builds."""
-
- details = []
- nbSlaves = 0
- for category in builderList:
- nbSlaves += len(builderList[category])
-
- # Sort the categories.
- categories = builderList.keys()
- categories.sort()
-
- builds = {}
-
- # Display the boxes by category group.
- for category in categories:
-
- builds[category] = []
-
- # Display the boxes for each builder in this category.
- for builder in builderList[category]:
- introducedIn = None
- firstNotIn = None
-
- # Find the first build that does not include the revision.
- for build in allBuilds[builder]:
- if self.comparator.isRevisionEarlier(build, revision):
- firstNotIn = build
- break
- else:
- introducedIn = build
-
- # Get the results of the first build with the revision, and the
- # first build that does not include the revision.
- results = None
- previousResults = None
- if introducedIn:
- results = introducedIn.results
- if firstNotIn:
- previousResults = firstNotIn.results
-
- isRunning = False
- if introducedIn and not introducedIn.isFinished:
- isRunning = True
-
- url = "./waterfall"
- pageTitle = builder
- tag = ""
- current_details = {}
- if introducedIn:
- current_details = introducedIn.details or ""
- url = "./buildstatus?builder=%s&number=%s" % (urllib.quote(builder),
- introducedIn.number)
- pageTitle += " "
- pageTitle += urllib.quote(' '.join(introducedIn.text), ' \n\\/:')
-
- builderStrip = builder.replace(' ', '')
- builderStrip = builderStrip.replace('(', '')
- builderStrip = builderStrip.replace(')', '')
- builderStrip = builderStrip.replace('.', '')
- tag = "Tag%s%s" % (builderStrip, introducedIn.number)
-
- if isRunning:
- pageTitle += ' ETA: %ds' % (introducedIn.eta or 0)
-
- resultsClass = getResultsClass(results, previousResults, isRunning)
-
- b = {}
- b["url"] = url
- b["pageTitle"] = pageTitle
- b["color"] = resultsClass
- b["tag"] = tag
-
- builds[category].append(b)
-
- # If the box is red, we add the explaination in the details
- # section.
- if current_details and resultsClass == "failure":
- details.append(current_details)
-
- return (builds, details)
-
- def filterRevisions(self, revisions, filter=None, max_revs=None):
- """Filter a set of revisions based on any number of filter criteria.
- If specified, filter should be a dict with keys corresponding to
- revision attributes, and values of 1+ strings"""
- if not filter:
- if max_revs is None:
- for rev in reversed(revisions):
- yield DevRevision(rev)
- else:
- for index,rev in enumerate(reversed(revisions)):
- if index >= max_revs:
- break
- yield DevRevision(rev)
- else:
- for index, rev in enumerate(reversed(revisions)):
- if max_revs and index >= max_revs:
- break
- try:
- for field,acceptable in filter.iteritems():
- if not hasattr(rev, field):
- raise DoesNotPassFilter
- if type(acceptable) in (str, unicode):
- if getattr(rev, field) != acceptable:
- raise DoesNotPassFilter
- elif type(acceptable) in (list, tuple, set):
- if getattr(rev, field) not in acceptable:
- raise DoesNotPassFilter
- yield DevRevision(rev)
- except DoesNotPassFilter:
- pass
-
- def displayPage(self, request, status, builderList, allBuilds, codebase,
- revisions, categories, repository, project, branch,
- debugInfo):
- """Display the console page."""
- # Build the main template directory with all the informations we have.
- subs = dict()
- subs["branch"] = branch or 'trunk'
- subs["repository"] = repository
- subs["project"] = project
- subs["codebase"] = codebase
- if categories:
- subs["categories"] = ' '.join(categories)
- subs["time"] = time.strftime("%a %d %b %Y %H:%M:%S",
- time.localtime(util.now()))
- subs["debugInfo"] = debugInfo
- subs["ANYBRANCH"] = ANYBRANCH
-
- if builderList:
- subs["categories"] = self.displayCategories(builderList, debugInfo)
- subs['slaves'] = self.displaySlaveLine(status, builderList, debugInfo)
- else:
- subs["categories"] = []
-
- subs['revisions'] = []
-
- # For each revision we show one line
- for revision in revisions:
- r = {}
-
- # Fill the dictionary with this new information
- r['id'] = revision.revision
- r['link'] = revision.revlink
- r['who'] = revision.who
- r['date'] = revision.date
- r['comments'] = revision.comments
- r['repository'] = revision.repository
- r['project'] = revision.project
-
- # Display the status for all builders.
- (builds, details) = self.displayStatusLine(builderList,
- allBuilds,
- revision,
- debugInfo)
- r['builds'] = builds
- r['details'] = details
-
- # Calculate the td span for the comment and the details.
- r["span"] = len(builderList) + 2
-
- subs['revisions'].append(r)
-
- #
- # Display the footer of the page.
- #
- debugInfo["load_time"] = time.time() - debugInfo["load_time"]
- return subs
-
-
- def content(self, request, cxt):
- "This method builds the main console view display."
-
- reload_time = None
- # Check if there was an arg. Don't let people reload faster than
- # every 15 seconds. 0 means no reload.
- if "reload" in request.args:
- try:
- reload_time = int(request.args["reload"][0])
- if reload_time != 0:
- reload_time = max(reload_time, 15)
- except ValueError:
- pass
-
- request.setHeader('Cache-Control', 'no-cache')
-
- # Sets the default reload time to 60 seconds.
- if not reload_time:
- reload_time = 60
-
- # Append the tag to refresh the page.
- if reload_time is not None and reload_time != 0:
- cxt['refresh'] = reload_time
-
- # Debug information to display at the end of the page.
- debugInfo = cxt['debuginfo'] = dict()
- debugInfo["load_time"] = time.time()
-
- # get url parameters
- # Categories to show information for.
- categories = request.args.get("category", [])
- # List of all builders to show on the page.
- builders = request.args.get("builder", [])
- # Repo used to filter the changes shown.
- repository = request.args.get("repository", [None])[0]
- # Project used to filter the changes shown.
- project = request.args.get("project", [None])[0]
- # Branch used to filter the changes shown.
- branch = request.args.get("branch", [ANYBRANCH])[0]
- # Codebase used to filter the changes shown.
- codebase = request.args.get("codebase", [None])[0]
- # List of all the committers name to display on the page.
- devName = request.args.get("name", [])
-
- # and the data we want to render
- status = self.getStatus(request)
-
- # Keep only the revisions we care about.
- # By default we process the last 40 revisions.
- # If a dev name is passed, we look for the changes by this person in the
- # last 80 revisions.
- numRevs = int(request.args.get("revs", [40])[0])
- if devName:
- numRevs *= 2
- numBuilds = numRevs
-
- # Get all changes we can find. This is a DB operation, so it must use
- # a deferred.
- d = self.getAllChanges(request, status, debugInfo)
- def got_changes(allChanges):
- debugInfo["source_all"] = len(allChanges)
-
- revFilter = {}
- if branch != ANYBRANCH:
- revFilter['branch'] = branch
- if devName:
- revFilter['who'] = devName
- if repository:
- revFilter['repository'] = repository
- if project:
- revFilter['project'] = project
- if codebase is not None:
- revFilter['codebase'] = codebase
- revisions = list(self.filterRevisions(allChanges, max_revs=numRevs,
- filter=revFilter))
- debugInfo["revision_final"] = len(revisions)
-
- # Fetch all the builds for all builders until we get the next build
- # after lastRevision.
- builderList = None
- allBuilds = None
- if revisions:
- lastRevision = revisions[len(revisions) - 1].revision
- debugInfo["last_revision"] = lastRevision
-
- (builderList, allBuilds) = self.getAllBuildsForRevision(status,
- request,
- codebase,
- lastRevision,
- numBuilds,
- categories,
- builders,
- debugInfo)
-
- debugInfo["added_blocks"] = 0
-
- cxt.update(self.displayPage(request, status, builderList,
- allBuilds, codebase, revisions,
- categories, repository, project,
- branch, debugInfo))
-
- templates = request.site.buildbot_service.templates
- template = templates.get_template("console.html")
- data = template.render(cxt)
- return data
- d.addCallback(got_changes)
- return d
-
-class RevisionComparator(object):
- """Used for comparing between revisions, as some
- VCS use a plain counter for revisions (like SVN)
- while others use different concepts (see Git).
- """
-
- # TODO (avivby): Should this be a zope interface?
-
- def isRevisionEarlier(self, first_change, second_change):
- """Used for comparing 2 changes"""
- raise NotImplementedError
-
- def isValidRevision(self, revision):
- """Checks whether the revision seems like a VCS revision"""
- raise NotImplementedError
-
- def getSortingKey(self):
- raise NotImplementedError
-
-class TimeRevisionComparator(RevisionComparator):
- def isRevisionEarlier(self, first, second):
- return first.when < second.when
-
- def isValidRevision(self, revision):
- return True # No general way of determining
-
- def getSortingKey(self):
- return operator.attrgetter('when')
-
-class IntegerRevisionComparator(RevisionComparator):
- def isRevisionEarlier(self, first, second):
- try:
- return int(first.revision) < int(second.revision)
- except (TypeError, ValueError):
- return False
-
- def isValidRevision(self, revision):
- try:
- int(revision)
- return True
- except:
- return False
-
- def getSortingKey(self):
- return operator.attrgetter('revision')
-