summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorprasadv@chromium.org <prasadv@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-15 23:59:39 +0000
committerprasadv@chromium.org <prasadv@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-15 23:59:39 +0000
commitf621fda5bfd1400bacdd63d03ea9df8fd0d2ec3d (patch)
tree7c72c9a7f6478ca75b8ebae98a0cbe3a4d4687c1 /tools
parent3a5d43771fd92c3cec2e31b9de6e920793ade529 (diff)
downloadchromium_src-f621fda5bfd1400bacdd63d03ea9df8fd0d2ec3d.zip
chromium_src-f621fda5bfd1400bacdd63d03ea9df8fd0d2ec3d.tar.gz
chromium_src-f621fda5bfd1400bacdd63d03ea9df8fd0d2ec3d.tar.bz2
Add methods to get build status from tryserver perf bisect builders.
These methods will be used bisect script to track the status of build. BUG= NOTRY=true Review URL: https://codereview.chromium.org/284493005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270865 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
-rw-r--r--tools/post_perf_builder_job.py260
1 files changed, 241 insertions, 19 deletions
diff --git a/tools/post_perf_builder_job.py b/tools/post_perf_builder_job.py
index 0f926b3..b1e4e85 100644
--- a/tools/post_perf_builder_job.py
+++ b/tools/post_perf_builder_job.py
@@ -2,19 +2,47 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""Post a try job to the Try server to produce build. It communicates
-to server by directly connecting via HTTP.
-"""
+"""Post a try job request via HTTP to the Tryserver to produce build."""
import getpass
+import json
import optparse
import os
import sys
import urllib
import urllib2
+# Link to get JSON data of builds
+BUILDER_JSON_URL = ('%(server_url)s/json/builders/%(bot_name)s/builds/'
+ '%(build_num)s?as_text=1&filter=0')
+
+# Link to display build steps
+BUILDER_HTML_URL = ('%(server_url)s/builders/%(bot_name)s/builds/%(build_num)s')
+
+# Tryserver buildbots status page
+TRY_SERVER_URL = 'http://build.chromium.org/p/tryserver.chromium'
+
+# Hostname of the tryserver where perf bisect builders are hosted. This is used
+# for posting build request to tryserver.
+BISECT_BUILDER_HOST = 'master4.golo.chromium.org'
+# 'try_job_port' on tryserver to post build request.
+BISECT_BUILDER_PORT = '8328'
+
+
+# From buildbot.status.builder.
+# See: http://docs.buildbot.net/current/developer/results.html
+SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY, TRYPENDING = range(7)
+
+# Status codes that can be returned by the GetBuildStatus method.
+OK = (SUCCESS, WARNINGS)
+# Indicates build failure.
+FAILED = (FAILURE, EXCEPTION, SKIPPED)
+# Inidcates build in progress or in pending queue.
+PENDING = (RETRY, TRYPENDING)
+
class ServerAccessError(Exception):
+
def __str__(self):
return '%s\nSorry, cannot connect to server.' % self.args[0]
@@ -68,6 +96,199 @@ def PostTryJob(url_params):
return True
+def _IsBuildRunning(build_data):
+ """Checks whether the build is in progress on buildbot.
+
+ Presence of currentStep element in build JSON indicates build is in progress.
+
+ Args:
+ build_data: A dictionary with build data, loaded from buildbot JSON API.
+
+ Returns:
+ True if build is in progress, otherwise False.
+ """
+ current_step = build_data.get('currentStep')
+ if (current_step and current_step.get('isStarted') and
+ current_step.get('results') is None):
+ return True
+ return False
+
+
+def _IsBuildFailed(build_data):
+ """Checks whether the build failed on buildbot.
+
+ Sometime build status is marked as failed even though compile and packaging
+ steps are successful. This may happen due to some intermediate steps of less
+ importance such as gclient revert, generate_telemetry_profile are failed.
+ Therefore we do an addition check to confirm if build was successful by
+ calling _IsBuildSuccessful.
+
+ Args:
+ build_data: A dictionary with build data, loaded from buildbot JSON API.
+
+ Returns:
+ True if revision is failed build, otherwise False.
+ """
+ if (build_data.get('results') in FAILED and
+ not _IsBuildSuccessful(build_data)):
+ return True
+ return False
+
+
+def _IsBuildSuccessful(build_data):
+ """Checks whether the build succeeded on buildbot.
+
+ We treat build as successful if the package_build step is completed without
+ any error i.e., when results attribute of the this step has value 0 or 1
+ in its first element.
+
+ Args:
+ build_data: A dictionary with build data, loaded from buildbot JSON API.
+
+ Returns:
+ True if revision is successfully build, otherwise False.
+ """
+ if build_data.get('steps'):
+ for item in build_data.get('steps'):
+ # The 'results' attribute of each step consists of two elements,
+ # results[0]: This represents the status of build step.
+ # See: http://docs.buildbot.net/current/developer/results.html
+ # results[1]: List of items, contains text if step fails, otherwise empty.
+ if (item.get('name') == 'package_build' and
+ item.get('isFinished') and
+ item.get('results')[0] in OK):
+ return True
+ return False
+
+
+def _FetchBuilderData(builder_url):
+ """Fetches JSON data for the all the builds from the tryserver.
+
+ Args:
+ builder_url: A tryserver URL to fetch builds information.
+
+ Returns:
+ A dictionary with information of all build on the tryserver.
+ """
+ data = None
+ try:
+ url = urllib2.urlopen(builder_url)
+ except urllib2.URLError, e:
+ print ('urllib2.urlopen error %s, waterfall status page down.[%s]' % (
+ builder_url, str(e)))
+ return None
+ if url is not None:
+ try:
+ data = url.read()
+ except IOError, e:
+ print 'urllib2 file object read error %s, [%s].' % (builder_url, str(e))
+ return data
+
+
+def _GetBuildData(buildbot_url):
+ """Gets build information for the given build id from the tryserver.
+
+ Args:
+ buildbot_url: A tryserver URL to fetch build information.
+
+ Returns:
+ A dictionary with build information if build exists, otherwise None.
+ """
+ builds_json = _FetchBuilderData(buildbot_url)
+ if builds_json:
+ return json.loads(builds_json)
+ return None
+
+
+def _GetBuildBotUrl(builder_host, builder_port):
+ """Gets build bot URL based on the host and port of the builders.
+
+ Note: All bisect builder bots are hosted on tryserver.chromium i.e.,
+ on master4:8328, since we cannot access tryserver using host and port
+ number directly, we use tryserver URL.
+
+ Args:
+ builder_host: Hostname of the server where the builder is hosted.
+ builder_port: Port number of ther server where the builder is hosted.
+
+ Returns:
+ URL of the buildbot as a string.
+ """
+ if (builder_host == BISECT_BUILDER_HOST and
+ builder_port == BISECT_BUILDER_PORT):
+ return TRY_SERVER_URL
+ else:
+ return 'http://%s:%s' % (builder_host, builder_port)
+
+
+def GetBuildStatus(build_num, bot_name, builder_host, builder_port):
+ """Gets build status from the buildbot status page for a given build number.
+
+ Args:
+ build_num: A build number on tryserver to determine its status.
+ bot_name: Name of the bot where the build information is scanned.
+ builder_host: Hostname of the server where the builder is hosted.
+ builder_port: Port number of ther server where the builder is hosted.
+
+ Returns:
+ A tuple consists of build status (SUCCESS, FAILED or PENDING) and a link
+ to build status page on the waterfall.
+ """
+ # Gets the buildbot url for the given host and port.
+ server_url = _GetBuildBotUrl(builder_host, builder_port)
+ buildbot_url = BUILDER_JSON_URL % {'server_url': server_url,
+ 'bot_name': bot_name,
+ 'build_num': build_num
+ }
+ build_data = _GetBuildData(buildbot_url)
+ results_url = None
+ if build_data:
+ # Link to build on the buildbot showing status of build steps.
+ results_url = BUILDER_HTML_URL % {'server_url': server_url,
+ 'bot_name': bot_name,
+ 'build_num': build_num
+ }
+ if _IsBuildFailed(build_data):
+ return (FAILED, results_url)
+
+ elif _IsBuildSuccessful(build_data):
+ return (OK, results_url)
+ return (PENDING, results_url)
+
+
+def GetBuildNumFromBuilder(build_reason, bot_name, builder_host, builder_port):
+ """Gets build number on build status page for a given build reason.
+
+ It parses the JSON data from buildbot page and collect basic information
+ about the all the builds and then this uniquely identifies the build based
+ on the 'reason' attribute in builds's JSON data.
+ The 'reason' attribute set while a build request is posted, and same is used
+ to identify the build on status page.
+
+ Args:
+ build_reason: A unique build name set to build on tryserver.
+ bot_name: Name of the bot where the build information is scanned.
+ builder_host: Hostname of the server where the builder is hosted.
+ builder_port: Port number of ther server where the builder is hosted.
+
+ Returns:
+ A build number as a string if found, otherwise None.
+ """
+ # Gets the buildbot url for the given host and port.
+ server_url = _GetBuildBotUrl(builder_host, builder_port)
+ buildbot_url = BUILDER_JSON_URL % {'server_url': server_url,
+ 'bot_name': bot_name,
+ 'build_num': '_all'
+ }
+ builds_json = _FetchBuilderData(buildbot_url)
+ if builds_json:
+ builds_data = json.loads(builds_json)
+ for current_build in builds_data:
+ if builds_data[current_build].get('reason') == build_reason:
+ return builds_data[current_build].get('number')
+ return None
+
+
def _GetQueryParams(options):
"""Parses common query parameters which will be passed to PostTryJob.
@@ -101,49 +322,50 @@ def _GenParser():
'Post a build request to the try server for the given revision.\n')
parser = optparse.OptionParser(usage=usage)
parser.add_option('-H', '--host',
- help='Host address of the try server.')
+ help='Host address of the try server.')
parser.add_option('-P', '--port', type='int',
- help='HTTP port of the try server.')
+ help='HTTP port of the try server.')
parser.add_option('-u', '--user', default=getpass.getuser(),
dest='user',
help='Owner user name [default: %default]')
parser.add_option('-e', '--email',
default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS',
os.environ.get('EMAIL_ADDRESS')),
- help='Email address where to send the results. Use either '
- 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment '
- 'variable or EMAIL_ADDRESS to set the email address '
- 'the try bots report results to [default: %default]')
+ help=('Email address where to send the results. Use either '
+ 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment '
+ 'variable or EMAIL_ADDRESS to set the email address '
+ 'the try bots report results to [default: %default]'))
parser.add_option('-n', '--name',
- default= 'try_job_http',
+ default='try_job_http',
help='Descriptive name of the try job')
parser.add_option('-b', '--bot',
help=('IMPORTANT: specify ONE builder per run is supported.'
'Run script for each builders separately.'))
parser.add_option('-r', '--revision',
- help='Revision to use for the try job; default: the '
- 'revision will be determined by the try server; see '
- 'its waterfall for more info')
+ help=('Revision to use for the try job; default: the '
+ 'revision will be determined by the try server; see '
+ 'its waterfall for more info'))
parser.add_option('--root',
- help='Root to use for the patch; base subdirectory for '
- 'patch created in a subdirectory')
+ help=('Root to use for the patch; base subdirectory for '
+ 'patch created in a subdirectory'))
parser.add_option('--patch',
- help='Patch information.')
+ help='Patch information.')
return parser
def Main(argv):
parser = _GenParser()
- options, args = parser.parse_args()
+ options, _ = parser.parse_args()
if not options.host:
raise ServerAccessError('Please use the --host option to specify the try '
- 'server host to connect to.')
+ 'server host to connect to.')
if not options.port:
raise ServerAccessError('Please use the --port option to specify the try '
- 'server port to connect to.')
+ 'server port to connect to.')
params = _GetQueryParams(options)
PostTryJob(params)
if __name__ == '__main__':
sys.exit(Main(sys.argv))
+