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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Post a try job to the Try server to produce build. It communicates 5 """Post a try job request via HTTP to the Tryserver to produce build."""
6 to server by directly connecting via HTTP.
7 """
8 6
9 import getpass 7 import getpass
8 import json
10 import optparse 9 import optparse
11 import os 10 import os
12 import sys 11 import sys
13 import urllib 12 import urllib
14 import urllib2 13 import urllib2
15 14
16 15
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.
16 # Link to get JSON data of builds
17 BUILDER_JSON_URL = ('%(server_url)s/json/builders/%(bot_name)s/builds/'
18 '%(build_num)s?as_text=1&filter=0')
19
20 # Link to display build steps
21 BUILDER_HTML_URL = ('%(server_url)s/builders/%(bot_name)s/builds/%(build_num)s')
22
23 # Tryserver buildbots status page
24 TRY_SERVER_URL = 'http://build.chromium.org/p/tryserver.chromium'
25
26 # Hostname of the tryserver where perf bisect builders are hosted. This is used
27 # for posting build request to tryserver.
28 BISECT_BUILDER_HOST = 'master4.golo.chromium.org'
29 # 'try_job_port' on tryserver to post build request.
30 BISECT_BUILDER_PORT = '8328'
31
32
33 # From buildbot.status.builder.
34 # See: http://docs.buildbot.net/current/developer/results.html
35 SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY, TRYPENDING = range(7)
36
37 # Status codes that can be returned by the GetBuildStatus method.
38 OK = (SUCCESS, WARNINGS, SKIPPED)
39 # Indicates build failure.
40 FAILED = (FAILURE, EXCEPTION)
41 # Inidcates build in progress or in pending queue.
42 PENDING = (RETRY, TRYPENDING)
43
44
17 class ServerAccessError(Exception): 45 class ServerAccessError(Exception):
46
18 def __str__(self): 47 def __str__(self):
19 return '%s\nSorry, cannot connect to server.' % self.args[0] 48 return '%s\nSorry, cannot connect to server.' % self.args[0]
20 49
21 50
22 def PostTryJob(url_params): 51 def PostTryJob(url_params):
23 """Sends a build request to the server using the HTTP protocol. 52 """Sends a build request to the server using the HTTP protocol.
24 53
25 Args: 54 Args:
26 url_params: A dictionary of query parameters to be sent in the request. 55 url_params: A dictionary of query parameters to be sent in the request.
27 In order to post build request to try server, this dictionary 56 In order to post build request to try server, this dictionary
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 raise ServerAccessError('%s is unaccessible. Reason: %s' % (url, e)) 90 raise ServerAccessError('%s is unaccessible. Reason: %s' % (url, e))
62 if not connection: 91 if not connection:
63 raise ServerAccessError('%s is unaccessible.' % url) 92 raise ServerAccessError('%s is unaccessible.' % url)
64 response = connection.read() 93 response = connection.read()
65 print 'Received %s from server' % response 94 print 'Received %s from server' % response
66 if response != 'OK': 95 if response != 'OK':
67 raise ServerAccessError('%s is unaccessible. Got:\n%s' % (url, response)) 96 raise ServerAccessError('%s is unaccessible. Got:\n%s' % (url, response))
68 return True 97 return True
69 98
70 99
100 def _IsBuildRunning(build_data):
101 """Checks whether the build is in progress on buildbot.
102
103 Presence of currentStep element in build JSON indicates build is in progress.
104
105 Args:
106 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.
107
108 Returns:
109 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.
110 """
111 current_step = build_data.get('currentStep')
112 if (current_step and current_step.get('isStarted') and
113 current_step.get('results') is None):
114 return True
115 return False
116
117
118 def _IsBuildFailed(build_data):
119 """Checks whether the build failed on buildbot.
120
121 Sometime build status is marked as failed even though compile and packaging
122 steps are successful. This may happen due to some intermediate steps of less
123 importance such as gclient revert, generate_telemetry_profile are failed.
124 Therefore we do an addition check to confirm if build was successful by
125 calling _IsBuildSucceed.
126
127 Args:
128 build_data: JSON object with build data, loaded from buildbot JSON API.
129
130 Returns:
131 Returns True if revision is failed build, otherwise False.
132 """
133 if (build_data.get('results') in FAILED and
134 not _IsBuildSucceed(build_data)):
135 return True
136 return False
137
138
139 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.
140 """Checks whether the build succeeded on buildbot.
141
142 We treat build as successful if the package_build step is completed without
143 any error i.e., when results attribute of the this step has value 0, 1 or 3
144 in its first element.
145
146 Args:
147 build_data: JSON object with build data, loaded from buildbot JSON API.
148
149 Returns:
150 Returns True if revision is successfully build, otherwise False.
151 """
152 if build_data.get('steps'):
153 for item in build_data.get('steps'):
154 # The 'results' attribute of each step consists of two elements,
155 # results[0]: This represents the status of build step.
156 # See: http://docs.buildbot.net/current/developer/results.html
157 # results[1]: List of items, contains text if step fails, otherwise empty.
158 if (item.get('name') == 'package_build' and
159 item.get('isFinished') and
160 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,
161 return True
162 return False
163
164
165 def _FetchBuilderData(builder_url):
166 """Fetches JSON data for the all the builds from the tryserver.
167
168 Args:
169 builder_url: A tryserver URL to fetch builds information.
170
171 Returns:
172 A dictionary with information of all build on the tryserver.
173 """
174 data = None
175 try:
176 url = urllib2.urlopen(builder_url)
177 except urllib2.URLError, e:
178 print ('urllib2.urlopen error %s, waterfall status page down.[%s]' % (
179 builder_url, str(e)))
180 return None
181 if url is not None:
182 try:
183 data = url.read()
184 except IOError, e:
185 print 'urllib2 file object read error %s, [%s].' % (builder_url, str(e))
186 return data
187
188
189 def _GetBuildData(buildbot_url):
190 """Gets build information for the given build id from the tryserver.
191
192 Args:
193 buildbot_url: A tryserver URL to fetch build information.
194
195 Returns:
196 A JSON object with build information if build exists, otherwise None.
197 """
198 builds_json = _FetchBuilderData(buildbot_url)
199 if builds_json:
200 return json.loads(builds_json)
201 return None
202
203
204 def _GetBuildBotUrl(builder_host, builder_port):
205 """Gets build bot URL based on the host and port of the builders.
206
207 Note: All bisect builder bots are hosted on tryserver.chromium i.e.,
208 on master4:8328, since we cannot access tryserver using host and port
209 number directly, we use tryserver URL.
210
211 Args:
212 builder_host: Hostname of the server where the builder is hosted.
213 builder_port: Port number of ther server where the builder is hosted.
214
215 Returns:
216 URL of the buildbot as a string.
217 """
218 if (builder_host == BISECT_BUILDER_HOST and
219 builder_port == BISECT_BUILDER_PORT):
220 return TRY_SERVER_URL
221 else:
222 return 'http://%s:%s' % (builder_host, builder_port)
223
224
225 def GetBuildStatus(build_num, bot_name, builder_host, builder_port):
226 """Gets build status from the buildbot status page for a given build number.
227
228 Args:
229 build_num: A build number on tryserver to determine its status.
230 bot_name: Name of the bot where the build information is scanned.
231 builder_host: Hostname of the server where the builder is hosted.
232 builder_port: Port number of ther server where the builder is hosted.
233
234 Returns:
235 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.
236 to build status page on the waterfall.
237 """
238 # Gets the buildbot url for the given host and port.
239 server_url = _GetBuildBotUrl(builder_host, builder_port)
240 buildbot_url = BUILDER_JSON_URL % {'server_url': server_url,
241 'bot_name': bot_name,
242 'build_num': build_num
243 }
244 build_data = _GetBuildData(buildbot_url)
245 results_url = None
246 if build_data:
247 # Link to build on the buildbot showing status of build steps.
248 results_url = BUILDER_HTML_URL % {'server_url': server_url,
249 'bot_name': bot_name,
250 'build_num': build_num
251 }
252 if _IsBuildFailed(build_data):
253 return (FAILED, results_url)
254
255 elif _IsBuildSucceed(build_data):
256 return (OK, results_url)
257 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
258
259
260 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
261 """Gets build number on build status page for a given build reason.
262
263 It parses the JSON data from buildbot page and collect basic information
264 about the all the builds and then this uniquely identifies the build based
265 on the 'reason' attribute in builds's JSON data.
266 The 'reason' attribute set while a build request is posted, and same is used
267 to identify the build on status page.
268
269 Args:
270 build_reason: A unique build name set to build on tryserver.
271 bot_name: Name of the bot where the build information is scanned.
272 builder_host: Hostname of the server where the builder is hosted.
273 builder_port: Port number of ther server where the builder is hosted.
274
275 Returns:
276 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.
277 """
278 # Gets the buildbot url for the given host and port.
279 server_url = _GetBuildBotUrl(builder_host, builder_port)
280 buildbot_url = BUILDER_JSON_URL % {'server_url': server_url,
281 'bot_name': bot_name,
282 'build_num': '_all'
283 }
284 builds_json = _FetchBuilderData(buildbot_url)
285 if builds_json:
286 builds_data = json.loads(builds_json)
287 for current_build in builds_data:
288 if builds_data[current_build].get('reason') == build_reason:
289 return builds_data[current_build].get('number')
290 return None
291
292
71 def _GetQueryParams(options): 293 def _GetQueryParams(options):
72 """Parses common query parameters which will be passed to PostTryJob. 294 """Parses common query parameters which will be passed to PostTryJob.
73 295
74 Args: 296 Args:
75 options: The options object parsed from the command line. 297 options: The options object parsed from the command line.
76 298
77 Returns: 299 Returns:
78 A dictionary consists of query parameters. 300 A dictionary consists of query parameters.
79 """ 301 """
80 values = {'host': options.host, 302 values = {'host': options.host,
(...skipping 13 matching lines...) Expand all
94 values['patch'] = options.patch 316 values['patch'] = options.patch
95 return values 317 return values
96 318
97 319
98 def _GenParser(): 320 def _GenParser():
99 """Parses the command line for posting build request.""" 321 """Parses the command line for posting build request."""
100 usage = ('%prog [options]\n' 322 usage = ('%prog [options]\n'
101 'Post a build request to the try server for the given revision.\n') 323 'Post a build request to the try server for the given revision.\n')
102 parser = optparse.OptionParser(usage=usage) 324 parser = optparse.OptionParser(usage=usage)
103 parser.add_option('-H', '--host', 325 parser.add_option('-H', '--host',
104 help='Host address of the try server.') 326 help='Host address of the try server.')
105 parser.add_option('-P', '--port', type='int', 327 parser.add_option('-P', '--port', type='int',
106 help='HTTP port of the try server.') 328 help='HTTP port of the try server.')
107 parser.add_option('-u', '--user', default=getpass.getuser(), 329 parser.add_option('-u', '--user', default=getpass.getuser(),
108 dest='user', 330 dest='user',
109 help='Owner user name [default: %default]') 331 help='Owner user name [default: %default]')
110 parser.add_option('-e', '--email', 332 parser.add_option('-e', '--email',
111 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS', 333 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS',
112 os.environ.get('EMAIL_ADDRESS')), 334 os.environ.get('EMAIL_ADDRESS')),
113 help='Email address where to send the results. Use either ' 335 help=('Email address where to send the results. Use either '
114 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment ' 336 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment '
115 'variable or EMAIL_ADDRESS to set the email address ' 337 'variable or EMAIL_ADDRESS to set the email address '
116 'the try bots report results to [default: %default]') 338 'the try bots report results to [default: %default]'))
117 parser.add_option('-n', '--name', 339 parser.add_option('-n', '--name',
118 default= 'try_job_http', 340 default='try_job_http',
119 help='Descriptive name of the try job') 341 help='Descriptive name of the try job')
120 parser.add_option('-b', '--bot', 342 parser.add_option('-b', '--bot',
121 help=('IMPORTANT: specify ONE builder per run is supported.' 343 help=('IMPORTANT: specify ONE builder per run is supported.'
122 'Run script for each builders separately.')) 344 'Run script for each builders separately.'))
123 parser.add_option('-r', '--revision', 345 parser.add_option('-r', '--revision',
124 help='Revision to use for the try job; default: the ' 346 help=('Revision to use for the try job; default: the '
125 'revision will be determined by the try server; see ' 347 'revision will be determined by the try server; see '
126 'its waterfall for more info') 348 'its waterfall for more info'))
127 parser.add_option('--root', 349 parser.add_option('--root',
128 help='Root to use for the patch; base subdirectory for ' 350 help=('Root to use for the patch; base subdirectory for '
129 'patch created in a subdirectory') 351 'patch created in a subdirectory'))
130 parser.add_option('--patch', 352 parser.add_option('--patch',
131 help='Patch information.') 353 help='Patch information.')
132 return parser 354 return parser
133 355
134 356
135 def Main(argv): 357 def Main(argv):
136 parser = _GenParser() 358 parser = _GenParser()
137 options, args = parser.parse_args() 359 options, _ = parser.parse_args()
138 if not options.host: 360 if not options.host:
139 raise ServerAccessError('Please use the --host option to specify the try ' 361 raise ServerAccessError('Please use the --host option to specify the try '
140 'server host to connect to.') 362 'server host to connect to.')
141 if not options.port: 363 if not options.port:
142 raise ServerAccessError('Please use the --port option to specify the try ' 364 raise ServerAccessError('Please use the --port option to specify the try '
143 'server port to connect to.') 365 'server port to connect to.')
144 params = _GetQueryParams(options) 366 params = _GetQueryParams(options)
145 PostTryJob(params) 367 PostTryJob(params)
146 368
147 369
148 if __name__ == '__main__': 370 if __name__ == '__main__':
149 sys.exit(Main(sys.argv)) 371 sys.exit(Main(sys.argv))
372
OLDNEW
« 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