Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1086)

Unified Diff: master/webstatus/console.py

Issue 648353002: Remove Skia's forked buildbot code (Closed) Base URL: https://skia.googlesource.com/buildbot.git@master
Patch Set: Address comment Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « master/webstatus/buildstatus.py ('k') | master/webstatus/waterfall.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: master/webstatus/console.py
diff --git a/master/webstatus/console.py b/master/webstatus/console.py
deleted file mode 100644
index 3b920a937832760ab04746c994d35bdb74d3a9a1..0000000000000000000000000000000000000000
--- a/master/webstatus/console.py
+++ /dev/null
@@ -1,658 +0,0 @@
-# Copyright (c) 2013 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-""" Skia's override of buildbot.status.web.console """
-
-
-from buildbot import util
-from buildbot.changes import changes as changes_module
-from buildbot.status import builder as builder_status
-from buildbot.status.web.base import HtmlResource
-from buildbot.status.web.console import ANYBRANCH, \
- CacheStatus, \
- DevBuild, \
- DevRevision, \
- DoesNotPassFilter, \
- getInProgressResults, \
- getResultsClass, \
- TimeRevisionComparator, \
- IntegerRevisionComparator
-from buildbot.status.web.status_json import JsonResource
-from skia_master_scripts import utils
-from twisted.internet import defer
-
-import builder_name_schema
-import re
-import skia_vars
-import time
-import urllib
-
-
-class ConsoleJsonStatusResource(JsonResource):
- """JSON interface for the console page."""
-
- def __init__(self, status, order_by_time=False):
- JsonResource.__init__(self, status)
-
- self.cache = CacheStatus()
-
- if order_by_time:
- self.comparator = TimeRevisionComparator()
- else:
- self.comparator = IntegerRevisionComparator()
-
- def asDict(self, request):
- cxt = {}
- status = request.site.buildbot_service.getStatus()
-
- # 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]
- # Branch used to filter the changes shown.
- branch = request.args.get("branch", [ANYBRANCH])[0]
- # List of all the committers name to display on the page.
- dev_name = request.args.get("name", [])
-
- # Debug information to display at the end of the page.
- debug_info = cxt['debuginfo'] = dict()
- debug_info["load_time"] = time.time()
-
- # 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 160 revisions.
- num_revs = int(request.args.get("revs", [40])[0])
- if dev_name:
- num_revs *= 4
- num_builds = num_revs
-
- # Get all changes we can find. This is a DB operation, so it must use
- # a deferred.
- d = self.getAllChanges(request, status, debug_info)
- def got_changes(all_changes):
- debug_info["source_all"] = len(all_changes)
-
- rev_filter = {}
- if branch != ANYBRANCH:
- rev_filter['branch'] = branch
- if dev_name:
- rev_filter['who'] = dev_name
- rev_filter['repository'] = skia_vars.GetGlobalVariable('skia_git_url')
- revisions = list(self.filterRevisions(all_changes, max_revs=num_revs,
- rev_filter=rev_filter))
- debug_info["revision_final"] = len(revisions)
-
- # Fetch all the builds for all builders until we get the next build
- # after last_revision.
- builder_list = None
- all_builds = None
- if revisions:
- last_revision = revisions[len(revisions) - 1].revision
- debug_info["last_revision"] = last_revision
-
- (builder_list, all_builds) = self.getAllBuildsForRevision(status,
- request,
- last_revision,
- num_builds,
- categories,
- builders,
- debug_info)
-
- debug_info["added_blocks"] = 0
- debug_info["from_cache"] = 0
-
- if request.args.get("display_cache", None):
- data = ""
- data += "\nGlobal Cache\n"
- data += self.cache.display()
- return data
-
- cxt.update(self.displayPage(request, status, builder_list,
- all_builds, revisions, categories,
- repository, branch, debug_info))
- # Clean up the cache.
- if debug_info["added_blocks"]:
- self.cache.trim()
- return {'builders': cxt['builders'],
- 'revisions': cxt['revisions']}
- d.addCallback(got_changes)
- return d
-
- ##
- ## 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, debug_info):
- """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.
- """
-
- all_changes = list()
- build_count = 0
- for builder_name in status.getBuilderNames()[:]:
- if build_count > max_builds:
- break
-
- builder = status.getBuilder(builder_name)
- build = self.getHeadBuild(builder)
- depth = 0
- while build and depth < max_depth and build_count < max_builds:
- depth += 1
- build_count += 1
- sourcestamp = build.getSourceStamp()
- all_changes.extend(sourcestamp.changes[:])
- build = build.getPreviousBuild()
-
- debug_info["source_fetch_len"] = len(all_changes)
- return all_changes
-
- @defer.deferredGenerator
- def getAllChanges(self, request, status, debug_info):
- master = request.site.buildbot_service.master
- max_rev_limit = skia_vars.GetGlobalVariable('console_max_rev_limit')
- default_rev_limit = skia_vars.GetGlobalVariable('console_default_rev_limit')
- limit = min(max_rev_limit,
- max(1, int(request.args.get('limit', [default_rev_limit])[0])))
- wfd = defer.waitForDeferred(master.db.changes.getRecentChanges(limit))
- yield wfd
- chdicts = wfd.getResult()
-
- # convert those to Change instances
- wfd = defer.waitForDeferred(
- defer.gatherResults([
- changes_module.Change.fromChdict(master, chdict)
- for chdict in chdicts ]))
- yield wfd
- all_changes = wfd.getResult()
-
- all_changes.sort(key=self.comparator.getSortingKey())
-
- # Remove the dups
- prev_change = None
- new_changes = []
- for change in all_changes:
- rev = change.revision
- if not prev_change or rev != prev_change.revision:
- new_changes.append(change)
- prev_change = change
- all_changes = new_changes
-
- debug_info["source_len"] = len(all_changes)
- yield all_changes
-
- def getBuildDetails(self, request, builder_name, 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_status.FAILURE:
- name = step.getName()
-
- # Remove html tags from the error text.
- strip_html = re.compile(r'<.*?>')
- stripped_details = strip_html.sub('', ' '.join(step.getText()))
-
- details['buildername'] = builder_name
- details['status'] = stripped_details
- 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(builder_name),
- build.getNumber(),
- urllib.quote(name),
- urllib.quote(logname)))
- logs.append(dict(url=logurl, name=logname))
- return details
-
- def getBuildsForRevision(self, request, builder, builder_name, last_revision,
- num_builds, debug_info):
- """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 = last_revision
-
- builds = []
- build = self.getHeadBuild(builder)
- number = 0
- while build and number < num_builds:
- debug_info["builds_scanned"] += 1
- number += 1
-
- # Get the last revision in this build.
- # We first try "got_revision", but if it does not work, then
- # we try "revision".
- got_rev = -1
- try:
- got_rev = build.getProperty("got_revision")
- if not self.comparator.isValidRevision(got_rev):
- got_rev = -1
- except KeyError:
- pass
-
- try:
- if got_rev == -1:
- got_rev = build.getProperty("revision")
- if not self.comparator.isValidRevision(got_rev):
- got_rev = -1
- except Exception:
- pass
-
- # 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 and got_rev != -1:
- dev_revision = self.getChangeForBuild(build, got_rev)
- details = self.getBuildDetails(request, builder_name, build)
- dev_build = DevBuild(dev_revision, build, details,
- getInProgressResults(build))
- builds.append(dev_build)
-
- # Now break if we have enough builds.
- current_revision = self.getChangeForBuild(build, revision)
- if self.comparator.isRevisionEarlier(dev_build, 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, last_revision, num_builds,
- categories, builders, debug_info):
- """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.
-
- last_revision 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.
- """
-
- all_builds = dict()
-
- # List of all builders in the dictionary.
- builder_list = dict()
-
- debug_info["builds_scanned"] = 0
- # Get all the builders.
- builder_names = status.getBuilderNames()[:]
- for builder_name in builder_names:
- builder = status.getBuilder(builder_name)
-
- # Make sure we are interested in this builder.
- if categories and builder.category not in categories:
- continue
- if builders and builder_name not in builders:
- continue
- if builder_name_schema.IsTrybot(builder_name):
- continue
-
- # We want to display this builder.
- category_full = builder.category or 'default'
-
- category_parts = category_full.split('|')
- category = category_parts[0]
- if len(category_parts) > 1:
- subcategory = category_parts[1]
- else:
- subcategory = 'default'
- if not builder_list.get(category):
- builder_list[category] = {}
- if not builder_list[category].get(subcategory):
- builder_list[category][subcategory] = {}
- if not builder_list[category][subcategory].get(category_full):
- builder_list[category][subcategory][category_full] = []
-
- b = {}
- b["color"] = "notstarted"
- b["pageTitle"] = builder_name
- b["url"] = "./builders/%s" % urllib.quote(builder_name, safe='() ')
- b["builderName"] = builder_name
- state, _ = status.getBuilder(builder_name).getState()
- # Check if it's offline, if so, the box is purple.
- if state == "offline":
- b["color"] = "offline"
- else:
- # If not offline, then display the result of the last
- # finished build.
- build = self.getHeadBuild(status.getBuilder(builder_name))
- while build and not build.isFinished():
- build = build.getPreviousBuild()
-
- if build:
- b["color"] = getResultsClass(build.getResults(), None, False)
-
- # Append this builder to the dictionary of builders.
- builder_list[category][subcategory][category_full].append(b)
- # Set the list of builds for this builder.
- all_builds[builder_name] = self.getBuildsForRevision(request,
- builder,
- builder_name,
- last_revision,
- num_builds,
- debug_info)
-
- return (builder_list, all_builds)
-
-
- ##
- ## Display functions
- ##
-
- def displayStatusLine(self, builder_list, all_builds, revision, debug_info):
- """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 = []
- builds = {}
-
- # Display the boxes by category group.
- for category in builder_list:
- for subcategory in builder_list[category]:
- for category_full in builder_list[category][subcategory]:
- for builder in builder_list[category][subcategory][category_full]:
- builder_name = builder['builderName']
- builds[builder_name] = []
- introduced_in = None
- first_not_in = None
-
- cached_value = self.cache.get(builder_name, revision.revision)
- if cached_value:
- debug_info["from_cache"] += 1
-
- b = {}
- b["url"] = cached_value.url
- b["pageTitle"] = cached_value.pageTitle
- b["color"] = cached_value.color
- b["tag"] = cached_value.tag
- b["builderName"] = cached_value.builderName
-
- builds[builder_name].append(b)
-
- if cached_value.details and cached_value.color == "failure":
- details.append(cached_value.details)
-
- continue
-
- # Find the first build that does not include the revision.
- for build in all_builds[builder_name]:
- if self.comparator.isRevisionEarlier(build.revision, revision):
- first_not_in = build
- break
- else:
- introduced_in = build
-
- # Get the results of the first build with the revision, and the
- # first build that does not include the revision.
- results = None
- in_progress_results = None
- previous_results = None
- if introduced_in:
- results = introduced_in.results
- in_progress_results = introduced_in.inProgressResults
- if first_not_in:
- previous_results = first_not_in.results
-
- is_running = False
- if introduced_in and not introduced_in.isFinished:
- is_running = True
-
- url = "./waterfall"
- page_title = builder_name
- tag = ""
- current_details = {}
- if introduced_in:
- current_details = introduced_in.details or ""
- url = "./buildstatus?builder=%s&number=%s" % (
- urllib.quote(builder_name), introduced_in.number)
- page_title += " "
- page_title += urllib.quote(' '.join(introduced_in.text),
- ' \n\\/:')
-
- builder_strip = builder_name.replace(' ', '')
- builder_strip = builder_strip.replace('(', '')
- builder_strip = builder_strip.replace(')', '')
- builder_strip = builder_strip.replace('.', '')
- tag = "Tag%s%s" % (builder_strip, introduced_in.number)
-
- if is_running:
- page_title += ' ETA: %ds' % (introduced_in.eta or 0)
-
- results_class = getResultsClass(results, previous_results,
- is_running, in_progress_results)
-
- b = {}
- b["url"] = url
- b["pageTitle"] = page_title
- b["color"] = results_class
- b["tag"] = tag
- b["builderName"] = builder_name
-
- builds[builder_name].append(b)
-
- # If the box is red, we add the explaination in the details
- # section.
- if current_details and results_class == "failure":
- details.append(current_details)
-
- # Add this box to the cache if it's completed so we don't have
- # to compute it again.
- if results_class not in ("running", "running_failure",
- "notstarted"):
- debug_info["added_blocks"] += 1
- self.cache.insert(builder_name, revision.revision, results_class,
- page_title, current_details, url, tag)
-
- return (builds, details)
-
- def filterRevisions(self, revisions, rev_filter=None, max_revs=None):
- """Filter a set of revisions based on any number of filter criteria.
- If specified, rev_filter should be a dict with keys corresponding to
- revision attributes, and values of 1+ strings"""
- if not rev_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:
- num_revs = 0
- for rev in reversed(revisions):
- if max_revs and num_revs >= max_revs:
- break
- try:
- for field, acceptable in rev_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
- num_revs += 1
- yield DevRevision(rev)
- except DoesNotPassFilter:
- pass
-
- def displayPage(self, request, status, builder_list, all_builds, revisions,
- categories, repository, branch, debug_info):
- """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
- if categories:
- subs["categories"] = ' '.join(categories)
- subs["time"] = time.strftime("%a %d %b %Y %H:%M:%S",
- time.localtime(util.now()))
- subs["debugInfo"] = debug_info
- subs["ANYBRANCH"] = ANYBRANCH
-
- if builder_list:
- builders = builder_list
- else:
- builders = {}
- subs['builders'] = builders
- 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
- if (skia_vars.GetGlobalVariable('commit_bot_username') in revision.who
- and 'Author: ' in revision.comments):
- who = revision.comments.split('Author: ')[1].split('\n')[0]
- who += ' (commit-bot)'
- else:
- who = revision.who
- r['who'] = utils.FixGitSvnEmail(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(builder_list,
- all_builds,
- revision,
- debug_info)
- r['builds'] = builds
- r['details'] = details
-
- # Calculate the td span for the comment and the details.
- r["span"] = sum ([len(builder_list[category]) \
- for category in builder_list]) + 2
-
- subs['revisions'].append(r)
-
- #
- # Display the footer of the page.
- #
- debug_info["load_time"] = time.time() - debug_info["load_time"]
- return subs
-
-
-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 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
-
- 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 = skia_vars.GetGlobalVariable('default_webstatus_refresh')
-
- # Append the tag to refresh the page.
- if reload_time is not None and reload_time != 0:
- cxt['refresh'] = reload_time
-
- # List of categories for which we load information but hide initially.
- hidden_categories_sets = request.args.get("hideCategories", [])
- hide_categories = []
- for category_set in hidden_categories_sets:
- hide_categories.extend(category_set.split(','))
- cxt['hide_categories'] = hide_categories
-
- # List of subcategories for which we load information but hide initially.
- hidden_subcategories_sets = request.args.get("hideSubcategories", [])
- hide_subcategories = []
- for subcategory_set in hidden_subcategories_sets:
- hide_subcategories.extend(subcategory_set.split(','))
- cxt['hide_subcategories'] = hide_subcategories
-
- # Console event-loading limits.
- cxt['default_console_limit'] = \
- skia_vars.GetGlobalVariable('console_default_rev_limit')
- cxt['max_console_limit'] = \
- skia_vars.GetGlobalVariable('console_max_rev_limit')
-
- templates = request.site.buildbot_service.templates
- template = templates.get_template("console.html")
- data = template.render(cxt)
-
- return data
« no previous file with comments | « master/webstatus/buildstatus.py ('k') | master/webstatus/waterfall.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698