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

Unified Diff: tools/post_perf_builder_job.py

Issue 284493005: Add methods to get build status from tryserver perf bisect builders. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 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 | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/post_perf_builder_job.py
diff --git a/tools/post_perf_builder_job.py b/tools/post_perf_builder_job.py
index 0f926b3735d15ba560edb5209042fb0d44e4843c..d6e625fb71129fa0c5e49682f0a814b67e500e8e 100644
--- a/tools/post_perf_builder_job.py
+++ b/tools/post_perf_builder_job.py
@@ -2,11 +2,10 @@
# 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
@@ -14,7 +13,37 @@ import urllib
import urllib2
qyearsley 2014/05/15 19:40:06 Technically, I think there's no need for two blank
prasadv 2014/05/15 20:09:42 Done.
+# 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, SKIPPED)
+# Indicates build failure.
+FAILED = (FAILURE, EXCEPTION)
+# 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 +97,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: JSON object with build data, loaded from buildbot JSON API.
qyearsley 2014/05/15 19:40:06 It could be slightly confusing to say that the exp
prasadv 2014/05/15 20:09:42 Done.
+
+ Returns:
+ Returns True if build is in progress, otherwise False.
qyearsley 2014/05/15 19:40:06 The word "Returns" is redundant here. Same applies
prasadv 2014/05/15 20:09:42 Done.
+ """
+ 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 _IsBuildSucceed.
+
+ Args:
+ build_data: JSON object with build data, loaded from buildbot JSON API.
+
+ Returns:
+ Returns True if revision is failed build, otherwise False.
+ """
+ if (build_data.get('results') in FAILED and
+ not _IsBuildSucceed(build_data)):
+ return True
+ return False
+
+
+def _IsBuildSucceed(build_data):
qyearsley 2014/05/15 19:40:06 Should rename to _IsBuildSuccessful. (Also update
prasadv 2014/05/15 20:09:42 Done.
+ """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, 1 or 3
+ in its first element.
+
+ Args:
+ build_data: JSON object with build data, loaded from buildbot JSON API.
+
+ Returns:
+ 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):
ghost stip (do not use) 2014/05/15 19:27:55 OK includes SKIPPED, did you want to include that
prasadv 2014/05/15 20:09:42 I'm really not sure why a build could be skipped,
+ 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 JSON object 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
ghost stip (do not use) 2014/05/15 19:27:55 nit: remove space
prasadv 2014/05/15 20:09:42 Done.
+ 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 _IsBuildSucceed(build_data):
+ return (OK, results_url)
+ return (PENDING, results_url)
qyearsley 2014/05/15 19:40:06 The function _IsBuildRunning isn't used anywhere c
prasadv 2014/05/15 20:09:42 Added this to check if build is in pending queue o
+
+
+def GetBuildNumFromBuilder(build_reason, bot_name, builder_host, builder_port):
ghost stip (do not use) 2014/05/15 19:27:55 just checking, these methods aren't used yet right
prasadv 2014/05/15 20:09:42 Yes, these aren't used anywhere yet. On 2014/05/15
+ """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
qyearsley 2014/05/15 19:40:06 Could add a period on this line.
prasadv 2014/05/15 20:09:42 Done.
+ """
+ # 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 +323,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))
+
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698