Correctly initialize PepperPluginInstanceImpl::layer_is_hardware_
[chromium-blink-merge.git] / tools / post_perf_builder_job.py
blobb1e4e857e1985d6a3b99d6630a7604e5f2bf9ef7
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 """Post a try job request via HTTP to the Tryserver to produce build."""
7 import getpass
8 import json
9 import optparse
10 import os
11 import sys
12 import urllib
13 import urllib2
15 # Link to get JSON data of builds
16 BUILDER_JSON_URL = ('%(server_url)s/json/builders/%(bot_name)s/builds/'
17 '%(build_num)s?as_text=1&filter=0')
19 # Link to display build steps
20 BUILDER_HTML_URL = ('%(server_url)s/builders/%(bot_name)s/builds/%(build_num)s')
22 # Tryserver buildbots status page
23 TRY_SERVER_URL = 'http://build.chromium.org/p/tryserver.chromium'
25 # Hostname of the tryserver where perf bisect builders are hosted. This is used
26 # for posting build request to tryserver.
27 BISECT_BUILDER_HOST = 'master4.golo.chromium.org'
28 # 'try_job_port' on tryserver to post build request.
29 BISECT_BUILDER_PORT = '8328'
32 # From buildbot.status.builder.
33 # See: http://docs.buildbot.net/current/developer/results.html
34 SUCCESS, WARNINGS, FAILURE, SKIPPED, EXCEPTION, RETRY, TRYPENDING = range(7)
36 # Status codes that can be returned by the GetBuildStatus method.
37 OK = (SUCCESS, WARNINGS)
38 # Indicates build failure.
39 FAILED = (FAILURE, EXCEPTION, SKIPPED)
40 # Inidcates build in progress or in pending queue.
41 PENDING = (RETRY, TRYPENDING)
44 class ServerAccessError(Exception):
46 def __str__(self):
47 return '%s\nSorry, cannot connect to server.' % self.args[0]
50 def PostTryJob(url_params):
51 """Sends a build request to the server using the HTTP protocol.
53 Args:
54 url_params: A dictionary of query parameters to be sent in the request.
55 In order to post build request to try server, this dictionary
56 should contain information for following keys:
57 'host': Hostname of the try server.
58 'port': Port of the try server.
59 'revision': SVN Revision to build.
60 'bot': Name of builder bot which would be used.
61 Returns:
62 True if the request is posted successfully. Otherwise throws an exception.
63 """
64 # Parse url parameters to be sent to Try server.
65 if not url_params.get('host'):
66 raise ValueError('Hostname of server to connect is missing.')
67 if not url_params.get('port'):
68 raise ValueError('Port of server to connect is missing.')
69 if not url_params.get('revision'):
70 raise ValueError('Missing revision details. Please specify revision'
71 ' information.')
72 if not url_params.get('bot'):
73 raise ValueError('Missing bot details. Please specify bot information.')
75 # Pop 'host' and 'port' to avoid passing them as query params.
76 url = 'http://%s:%s/send_try_patch' % (url_params.pop('host'),
77 url_params.pop('port'))
79 print 'Sending by HTTP'
80 query_params = '&'.join('%s=%s' % (k, v) for k, v in url_params.iteritems())
81 print 'url: %s?%s' % (url, query_params)
83 connection = None
84 try:
85 print 'Opening connection...'
86 connection = urllib2.urlopen(url, urllib.urlencode(url_params))
87 print 'Done, request sent to server to produce build.'
88 except IOError, e:
89 raise ServerAccessError('%s is unaccessible. Reason: %s' % (url, e))
90 if not connection:
91 raise ServerAccessError('%s is unaccessible.' % url)
92 response = connection.read()
93 print 'Received %s from server' % response
94 if response != 'OK':
95 raise ServerAccessError('%s is unaccessible. Got:\n%s' % (url, response))
96 return True
99 def _IsBuildRunning(build_data):
100 """Checks whether the build is in progress on buildbot.
102 Presence of currentStep element in build JSON indicates build is in progress.
104 Args:
105 build_data: A dictionary with build data, loaded from buildbot JSON API.
107 Returns:
108 True if build is in progress, otherwise False.
110 current_step = build_data.get('currentStep')
111 if (current_step and current_step.get('isStarted') and
112 current_step.get('results') is None):
113 return True
114 return False
117 def _IsBuildFailed(build_data):
118 """Checks whether the build failed on buildbot.
120 Sometime build status is marked as failed even though compile and packaging
121 steps are successful. This may happen due to some intermediate steps of less
122 importance such as gclient revert, generate_telemetry_profile are failed.
123 Therefore we do an addition check to confirm if build was successful by
124 calling _IsBuildSuccessful.
126 Args:
127 build_data: A dictionary with build data, loaded from buildbot JSON API.
129 Returns:
130 True if revision is failed build, otherwise False.
132 if (build_data.get('results') in FAILED and
133 not _IsBuildSuccessful(build_data)):
134 return True
135 return False
138 def _IsBuildSuccessful(build_data):
139 """Checks whether the build succeeded on buildbot.
141 We treat build as successful if the package_build step is completed without
142 any error i.e., when results attribute of the this step has value 0 or 1
143 in its first element.
145 Args:
146 build_data: A dictionary with build data, loaded from buildbot JSON API.
148 Returns:
149 True if revision is successfully build, otherwise False.
151 if build_data.get('steps'):
152 for item in build_data.get('steps'):
153 # The 'results' attribute of each step consists of two elements,
154 # results[0]: This represents the status of build step.
155 # See: http://docs.buildbot.net/current/developer/results.html
156 # results[1]: List of items, contains text if step fails, otherwise empty.
157 if (item.get('name') == 'package_build' and
158 item.get('isFinished') and
159 item.get('results')[0] in OK):
160 return True
161 return False
164 def _FetchBuilderData(builder_url):
165 """Fetches JSON data for the all the builds from the tryserver.
167 Args:
168 builder_url: A tryserver URL to fetch builds information.
170 Returns:
171 A dictionary with information of all build on the tryserver.
173 data = None
174 try:
175 url = urllib2.urlopen(builder_url)
176 except urllib2.URLError, e:
177 print ('urllib2.urlopen error %s, waterfall status page down.[%s]' % (
178 builder_url, str(e)))
179 return None
180 if url is not None:
181 try:
182 data = url.read()
183 except IOError, e:
184 print 'urllib2 file object read error %s, [%s].' % (builder_url, str(e))
185 return data
188 def _GetBuildData(buildbot_url):
189 """Gets build information for the given build id from the tryserver.
191 Args:
192 buildbot_url: A tryserver URL to fetch build information.
194 Returns:
195 A dictionary with build information if build exists, otherwise None.
197 builds_json = _FetchBuilderData(buildbot_url)
198 if builds_json:
199 return json.loads(builds_json)
200 return None
203 def _GetBuildBotUrl(builder_host, builder_port):
204 """Gets build bot URL based on the host and port of the builders.
206 Note: All bisect builder bots are hosted on tryserver.chromium i.e.,
207 on master4:8328, since we cannot access tryserver using host and port
208 number directly, we use tryserver URL.
210 Args:
211 builder_host: Hostname of the server where the builder is hosted.
212 builder_port: Port number of ther server where the builder is hosted.
214 Returns:
215 URL of the buildbot as a string.
217 if (builder_host == BISECT_BUILDER_HOST and
218 builder_port == BISECT_BUILDER_PORT):
219 return TRY_SERVER_URL
220 else:
221 return 'http://%s:%s' % (builder_host, builder_port)
224 def GetBuildStatus(build_num, bot_name, builder_host, builder_port):
225 """Gets build status from the buildbot status page for a given build number.
227 Args:
228 build_num: A build number on tryserver to determine its status.
229 bot_name: Name of the bot where the build information is scanned.
230 builder_host: Hostname of the server where the builder is hosted.
231 builder_port: Port number of ther server where the builder is hosted.
233 Returns:
234 A tuple consists of build status (SUCCESS, FAILED or PENDING) and a link
235 to build status page on the waterfall.
237 # Gets the buildbot url for the given host and port.
238 server_url = _GetBuildBotUrl(builder_host, builder_port)
239 buildbot_url = BUILDER_JSON_URL % {'server_url': server_url,
240 'bot_name': bot_name,
241 'build_num': build_num
243 build_data = _GetBuildData(buildbot_url)
244 results_url = None
245 if build_data:
246 # Link to build on the buildbot showing status of build steps.
247 results_url = BUILDER_HTML_URL % {'server_url': server_url,
248 'bot_name': bot_name,
249 'build_num': build_num
251 if _IsBuildFailed(build_data):
252 return (FAILED, results_url)
254 elif _IsBuildSuccessful(build_data):
255 return (OK, results_url)
256 return (PENDING, results_url)
259 def GetBuildNumFromBuilder(build_reason, bot_name, builder_host, builder_port):
260 """Gets build number on build status page for a given build reason.
262 It parses the JSON data from buildbot page and collect basic information
263 about the all the builds and then this uniquely identifies the build based
264 on the 'reason' attribute in builds's JSON data.
265 The 'reason' attribute set while a build request is posted, and same is used
266 to identify the build on status page.
268 Args:
269 build_reason: A unique build name set to build on tryserver.
270 bot_name: Name of the bot where the build information is scanned.
271 builder_host: Hostname of the server where the builder is hosted.
272 builder_port: Port number of ther server where the builder is hosted.
274 Returns:
275 A build number as a string if found, otherwise None.
277 # Gets the buildbot url for the given host and port.
278 server_url = _GetBuildBotUrl(builder_host, builder_port)
279 buildbot_url = BUILDER_JSON_URL % {'server_url': server_url,
280 'bot_name': bot_name,
281 'build_num': '_all'
283 builds_json = _FetchBuilderData(buildbot_url)
284 if builds_json:
285 builds_data = json.loads(builds_json)
286 for current_build in builds_data:
287 if builds_data[current_build].get('reason') == build_reason:
288 return builds_data[current_build].get('number')
289 return None
292 def _GetQueryParams(options):
293 """Parses common query parameters which will be passed to PostTryJob.
295 Args:
296 options: The options object parsed from the command line.
298 Returns:
299 A dictionary consists of query parameters.
301 values = {'host': options.host,
302 'port': options.port,
303 'user': options.user,
304 'name': options.name
306 if options.email:
307 values['email'] = options.email
308 if options.revision:
309 values['revision'] = options.revision
310 if options.root:
311 values['root'] = options.root
312 if options.bot:
313 values['bot'] = options.bot
314 if options.patch:
315 values['patch'] = options.patch
316 return values
319 def _GenParser():
320 """Parses the command line for posting build request."""
321 usage = ('%prog [options]\n'
322 'Post a build request to the try server for the given revision.\n')
323 parser = optparse.OptionParser(usage=usage)
324 parser.add_option('-H', '--host',
325 help='Host address of the try server.')
326 parser.add_option('-P', '--port', type='int',
327 help='HTTP port of the try server.')
328 parser.add_option('-u', '--user', default=getpass.getuser(),
329 dest='user',
330 help='Owner user name [default: %default]')
331 parser.add_option('-e', '--email',
332 default=os.environ.get('TRYBOT_RESULTS_EMAIL_ADDRESS',
333 os.environ.get('EMAIL_ADDRESS')),
334 help=('Email address where to send the results. Use either '
335 'the TRYBOT_RESULTS_EMAIL_ADDRESS environment '
336 'variable or EMAIL_ADDRESS to set the email address '
337 'the try bots report results to [default: %default]'))
338 parser.add_option('-n', '--name',
339 default='try_job_http',
340 help='Descriptive name of the try job')
341 parser.add_option('-b', '--bot',
342 help=('IMPORTANT: specify ONE builder per run is supported.'
343 'Run script for each builders separately.'))
344 parser.add_option('-r', '--revision',
345 help=('Revision to use for the try job; default: the '
346 'revision will be determined by the try server; see '
347 'its waterfall for more info'))
348 parser.add_option('--root',
349 help=('Root to use for the patch; base subdirectory for '
350 'patch created in a subdirectory'))
351 parser.add_option('--patch',
352 help='Patch information.')
353 return parser
356 def Main(argv):
357 parser = _GenParser()
358 options, _ = parser.parse_args()
359 if not options.host:
360 raise ServerAccessError('Please use the --host option to specify the try '
361 'server host to connect to.')
362 if not options.port:
363 raise ServerAccessError('Please use the --port option to specify the try '
364 'server port to connect to.')
365 params = _GetQueryParams(options)
366 PostTryJob(params)
369 if __name__ == '__main__':
370 sys.exit(Main(sys.argv))