1 # Copyright (C) 2013 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 from google.appengine.api import memcache
42 # Buildbot steps that have test in the name, but don't run tests.
43 NON_TEST_STEP_NAMES = [
46 'find isolated tests',
48 'Download latest chromedriver',
51 'update test result log',
56 # Buildbot steps that run tests but don't upload results to the flakiness dashboard server.
57 # FIXME: These should be fixed to upload and then removed from this list.
58 TEST_STEPS_THAT_DO_NOT_UPLOAD_YET = [
60 'python_tests(chrome',
64 'test_mini_installer',
65 'telemetry_unittests',
66 'webkit_python_tests',
70 BUILDS_URL_TEMPLATE = 'http://chrome-build-extract.appspot.com/get_builds?builder=%s&master=%s&num_builds=1'
71 MASTER_URL_TEMPLATE = 'http://chrome-build-extract.appspot.com/get_master/%s'
74 class FetchBuildersException(Exception):
79 logging.debug('Fetching %s' % url)
82 resp = urllib2.urlopen(url)
84 exc_info = sys.exc_info()
85 logging.warning('Error while fetching %s: %s', url, exc_info[1])
89 fetched_json = json.load(resp)
91 exc_info = sys.exc_info()
92 logging.warning('Unable to parse JSON response from %s: %s', url, exc_info[1])
98 return json.dumps(data, separators=(',', ':'), sort_keys=True)
101 def fetch_buildbot_data(masters=None):
102 start_time = datetime.datetime.now()
103 all_masters_data = []
105 masters = [master_config.getMaster(m) for m in masters]
107 masters = master_config.getAllMasters()
109 for master_data in masters:
110 all_masters_data.append(master_data)
111 url_name = master_data['url_name']
112 master_url = MASTER_URL_TEMPLATE % url_name
113 builders = fetch_json(master_url)
115 msg = 'Aborting fetch. Could not fetch builders from %s' % master_url
117 raise FetchBuildersException(msg)
119 tests_object = master_data.setdefault('tests', {})
121 for builder in builders['builders'].keys():
122 build = fetch_json(BUILDS_URL_TEMPLATE % (urllib2.quote(builder), url_name))
124 logging.info('Skipping builder %s on master %s due to empty data.', builder, url_name)
127 if not build['builds']:
128 logging.info('Skipping builder %s on master %s due to empty builds list.', builder, url_name)
131 for step in build['builds'][0]['steps']:
132 step_name = step['name']
134 if not 'test' in step_name:
137 if any(name in step_name for name in NON_TEST_STEP_NAMES):
140 if re.search('_only|_ignore|_perf$', step_name):
143 if step_name == 'webkit_tests':
144 step_name = 'layout-tests'
146 tests_object.setdefault(step_name, {'builders': []})
147 tests_object[step_name]['builders'].append(builder)
149 for builders in tests_object.values():
150 builders['builders'].sort()
152 output_data = {'masters': all_masters_data, 'no_upload_test_types': TEST_STEPS_THAT_DO_NOT_UPLOAD_YET}
154 delta = datetime.datetime.now() - start_time
156 logging.info('Fetched buildbot data in %s seconds.', delta.seconds)
158 return dump_json(output_data)
161 class UpdateBuilders(webapp2.RequestHandler):
162 """Fetch and update the cached buildbot data."""
165 buildbot_data = fetch_buildbot_data()
166 memcache.set('buildbot_data', buildbot_data)
167 self.response.set_status(200)
168 self.response.out.write("ok")
169 except FetchBuildersException, ex:
170 logging.error('Not updating builders because fetch failed: %s', str(ex))
171 self.response.set_status(500)
172 self.response.out.write(ex.message)
175 class GetBuilders(webapp2.RequestHandler):
176 """Return a list of masters mapped to their respective builders, possibly using cached data."""
178 buildbot_data = memcache.get('buildbot_data')
180 if not buildbot_data:
181 logging.warning('No buildbot data in memcache. If this message repeats, something is probably wrong with memcache.')
183 buildbot_data = fetch_buildbot_data()
184 memcache.set('buildbot_data', buildbot_data)
185 except FetchBuildersException, ex:
186 logging.error('Builders fetch failed: %s', str(ex))
187 self.response.set_status(500)
188 self.response.out.write(ex.message)
191 callback = self.request.get('callback')
193 buildbot_data = callback + '(' + buildbot_data + ');'
195 self.response.out.write(buildbot_data)