1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """This module contains functionality for starting build try jobs via HTTP.
7 This includes both sending a request to start a job, and also related code
8 for querying the status of the job.
10 This module can be either run as a stand-alone script to send a request to a
11 builder, or imported and used by calling the public functions below.
24 # URL template for fetching JSON data about builds.
25 BUILDER_JSON_URL
= ('%(server_url)s/json/builders/%(bot_name)s/builds/'
26 '%(build_num)s?as_text=1&filter=0')
28 # URL template for displaying build steps.
29 BUILDER_HTML_URL
= '%(server_url)s/builders/%(bot_name)s/builds/%(build_num)s'
31 # Try server status page URLs, used to get build status.
32 PERF_TRY_SERVER_URL
= 'http://build.chromium.org/p/tryserver.chromium.perf'
33 LINUX_TRY_SERVER_URL
= 'http://build.chromium.org/p/tryserver.chromium.linux'
35 # Status codes that can be returned by the GetBuildStatus method
36 # From buildbot.status.builder.
37 # See: http://docs.buildbot.net/current/developer/results.html
38 SUCCESS
, WARNINGS
, FAILURE
, SKIPPED
, EXCEPTION
, RETRY
, TRYPENDING
= range(7)
39 OK
= (SUCCESS
, WARNINGS
) # These indicate build is complete.
40 FAILED
= (FAILURE
, EXCEPTION
, SKIPPED
) # These indicate build failure.
41 PENDING
= (RETRY
, TRYPENDING
) # These indicate in progress or in pending queue.
44 class ServerAccessError(Exception):
47 return '%s\nSorry, cannot connect to server.' % self
.args
[0]
50 def _IsBuildRunning(build_data
):
51 """Checks whether the build is in progress on buildbot.
53 Presence of currentStep element in build JSON indicates build is in progress.
56 build_data: A dictionary with build data, loaded from buildbot JSON API.
59 True if build is in progress, otherwise False.
61 current_step
= build_data
.get('currentStep')
62 if (current_step
and current_step
.get('isStarted') and
63 current_step
.get('results') is None):
68 def _IsBuildFailed(build_data
):
69 """Checks whether the build failed on buildbot.
71 Sometime build status is marked as failed even though compile and packaging
72 steps are successful. This may happen due to some intermediate steps of less
73 importance such as gclient revert, generate_telemetry_profile are failed.
74 Therefore we do an addition check to confirm if build was successful by
75 calling _IsBuildSuccessful.
78 build_data: A dictionary with build data, loaded from buildbot JSON API.
81 True if revision is failed build, otherwise False.
83 if (build_data
.get('results') in FAILED
and
84 not _IsBuildSuccessful(build_data
)):
89 def _IsBuildSuccessful(build_data
):
90 """Checks whether the build succeeded on buildbot.
92 We treat build as successful if the package_build step is completed without
93 any error i.e., when results attribute of the this step has value 0 or 1
97 build_data: A dictionary with build data, loaded from buildbot JSON API.
100 True if revision is successfully build, otherwise False.
102 if build_data
.get('steps'):
103 for item
in build_data
.get('steps'):
104 # The 'results' attribute of each step consists of two elements,
105 # results[0]: This represents the status of build step.
106 # See: http://docs.buildbot.net/current/developer/results.html
107 # results[1]: List of items, contains text if step fails, otherwise empty.
108 if (item
.get('name') == 'package_build' and
109 item
.get('isFinished') and
110 item
.get('results')[0] in OK
):
115 def _FetchBuilderData(builder_url
):
116 """Fetches JSON data for the all the builds from the tryserver.
119 builder_url: A tryserver URL to fetch builds information.
122 A dictionary with information of all build on the tryserver.
126 url
= urllib2
.urlopen(builder_url
)
127 except urllib2
.URLError
as e
:
128 print ('urllib2.urlopen error %s, waterfall status page down.[%s]' % (
129 builder_url
, str(e
)))
135 print 'urllib2 file object read error %s, [%s].' % (builder_url
, str(e
))
139 def _GetBuildData(buildbot_url
):
140 """Gets build information for the given build id from the tryserver.
143 buildbot_url: A tryserver URL to fetch build information.
146 A dictionary with build information if build exists, otherwise None.
148 builds_json
= _FetchBuilderData(buildbot_url
)
150 return json
.loads(builds_json
)
154 def _GetBuildBotUrl(builder_type
):
155 """Gets build bot URL for fetching build info.
157 Bisect builder bots are hosted on tryserver.chromium.perf, though we cannot
158 access this tryserver using host and port number directly, so we use another
159 tryserver URL for the perf tryserver.
162 builder_type: Determines what type of builder is used, e.g. "perf".
165 URL of the buildbot as a string.
167 if builder_type
== fetch_build
.PERF_BUILDER
:
168 return PERF_TRY_SERVER_URL
169 if builder_type
== fetch_build
.FULL_BUILDER
:
170 return LINUX_TRY_SERVER_URL
171 raise NotImplementedError('Unsupported builder type "%s".' % builder_type
)
174 def GetBuildStatus(build_num
, bot_name
, builder_type
):
175 """Gets build status from the buildbot status page for a given build number.
178 build_num: A build number on tryserver to determine its status.
179 bot_name: Name of the bot where the build information is scanned.
180 builder_type: Type of builder, e.g. "perf".
183 A pair which consists of build status (SUCCESS, FAILED or PENDING) and a
184 link to build status page on the waterfall.
186 # TODO(prasadv, qyearsley): Make this a method of BuildArchive
187 # (which may be renamed to BuilderTryBot or Builder).
190 # Get the URL for requesting JSON data with status information.
191 server_url
= _GetBuildBotUrl(builder_type
)
192 buildbot_url
= BUILDER_JSON_URL
% {
193 'server_url': server_url
,
194 'bot_name': bot_name
,
195 'build_num': build_num
,
197 build_data
= _GetBuildData(buildbot_url
)
199 # Link to build on the buildbot showing status of build steps.
200 results_url
= BUILDER_HTML_URL
% {
201 'server_url': server_url
,
202 'bot_name': bot_name
,
203 'build_num': build_num
,
205 if _IsBuildFailed(build_data
):
206 return (FAILED
, results_url
)
208 elif _IsBuildSuccessful(build_data
):
209 return (OK
, results_url
)
210 return (PENDING
, results_url
)
213 def GetBuildNumFromBuilder(build_reason
, bot_name
, builder_type
):
214 """Gets build number on build status page for a given 'build reason'.
216 This function parses the JSON data from buildbot page and collects basic
217 information about the all the builds, and then uniquely identifies the build
218 based on the 'reason' attribute in builds's JSON data.
220 The 'reason' attribute set is when a build request is posted, and it is used
221 to identify the build on status page.
224 build_reason: A unique build name set to build on tryserver.
225 bot_name: Name of the bot where the build information is scanned.
226 builder_type: Type of builder, e.g. "perf".
229 A build number as a string if found, otherwise None.
231 # TODO(prasadv, qyearsley): Make this a method of BuildArchive
232 # (which may be renamed to BuilderTryBot or Builder).
233 # Gets the buildbot url for the given host and port.
234 server_url
= _GetBuildBotUrl(builder_type
)
235 buildbot_url
= BUILDER_JSON_URL
% {
236 'server_url': server_url
,
237 'bot_name': bot_name
,
240 builds_json
= _FetchBuilderData(buildbot_url
)
242 builds_data
= json
.loads(builds_json
)
243 for current_build
in builds_data
:
244 if builds_data
[current_build
].get('reason') == build_reason
:
245 return builds_data
[current_build
].get('number')