blob: a6366448a4c894f3c1fcb28061474ec8e038854c [file] [log] [blame]
cmtice46093e52014-12-09 14:59:16 -08001# Copyright 2014 Google Inc. 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.
Luis Lozano439f2b72016-01-08 11:56:02 -08004"""Utilities for launching and accessing ChromeOS buildbots."""
Yunlian Jiang56f13762016-01-06 12:56:24 -08005
Rahul Chaudhrycbc5a262015-12-30 17:05:14 -08006from __future__ import print_function
7
cmtice46093e52014-12-09 14:59:16 -08008import os
9import time
10import urllib2
11
12from utils import command_executer
13from utils import logger
14from utils import buildbot_json
15
16SLEEP_TIME = 600 # 10 minutes; time between polling of buildbot.
Luis Lozanof2a3ef42015-12-15 13:49:30 -080017TIME_OUT = 18000 # Decide the build is dead or will never finish
18# after this time (5 hours).
19OK_STATUS = [ # List of result status values that are 'ok'.
Rahul Chaudhryb2d3dc72015-12-30 18:11:05 -080020 # This was obtained from:
21 # https://chromium.googlesource.com/chromium/tools/build/+/
22 # master/third_party/buildbot_8_4p1/buildbot/status/results.py
Luis Lozanof2a3ef42015-12-15 13:49:30 -080023 0, # "success"
24 1, # "warnings"
25 6, # "retry"
26]
Luis Lozano439f2b72016-01-08 11:56:02 -080027
28
29class BuildbotTimeout(Exception):
30 """Exception to throw when a buildbot operation timesout."""
31 pass
cmtice46093e52014-12-09 14:59:16 -080032
Luis Lozanof2a3ef42015-12-15 13:49:30 -080033
34def ParseReportLog(url, build):
Rahul Chaudhry647950a2015-12-30 18:29:13 -080035 """Scrape the trybot image name off the Reports log page.
cmtice46093e52014-12-09 14:59:16 -080036
37 This takes the URL for a trybot Reports Stage web page,
38 and a trybot build type, such as 'daisy-release'. It
39 opens the web page and parses it looking for the trybot
40 artifact name (e.g. something like
41 'trybot-daisy-release/R40-6394.0.0-b1389'). It returns the
42 artifact name, if found.
43 """
Luis Lozanof2a3ef42015-12-15 13:49:30 -080044 trybot_image = ''
45 url += '/text'
46 newurl = url.replace('uberchromegw', 'chromegw')
47 webpage = urllib2.urlopen(newurl)
48 data = webpage.read()
49 lines = data.split('\n')
50 for l in lines:
51 if l.find('Artifacts') > 0 and l.find('trybot') > 0:
52 trybot_name = 'trybot-%s' % build
53 start_pos = l.find(trybot_name)
54 end_pos = l.find('@https://storage')
55 trybot_image = l[start_pos:end_pos]
cmtice46093e52014-12-09 14:59:16 -080056
Luis Lozanof2a3ef42015-12-15 13:49:30 -080057 return trybot_image
cmtice46093e52014-12-09 14:59:16 -080058
Luis Lozanof2a3ef42015-12-15 13:49:30 -080059
60def GetBuildData(buildbot_queue, build_id):
Rahul Chaudhry647950a2015-12-30 18:29:13 -080061 """Find the Reports stage web page for a trybot build.
cmtice46093e52014-12-09 14:59:16 -080062
63 This takes the name of a buildbot_queue, such as 'daisy-release'
64 and a build id (the build number), and uses the json buildbot api to
65 find the Reports stage web page for that build, if it exists.
66 """
Luis Lozanof2a3ef42015-12-15 13:49:30 -080067 builder = buildbot_json.Buildbot(
68 'http://chromegw/p/tryserver.chromiumos/').builders[buildbot_queue]
69 build_data = builder.builds[build_id].data
70 logs = build_data['logs']
71 for l in logs:
72 fname = l[1]
73 if 'steps/Report/' in fname:
74 return fname
cmtice46093e52014-12-09 14:59:16 -080075
Luis Lozanof2a3ef42015-12-15 13:49:30 -080076 return ''
cmtice46093e52014-12-09 14:59:16 -080077
78
79def FindBuildRecordFromLog(description, log_info):
Rahul Chaudhry647950a2015-12-30 18:29:13 -080080 """Find the right build record in the build logs.
cmtice46093e52014-12-09 14:59:16 -080081
82 Get the first build record from build log with a reason field
83 that matches 'description'. ('description' is a special tag we
84 created when we launched the buildbot, so we could find it at this
85 point.)
86 """
87
Luis Lozanof2a3ef42015-12-15 13:49:30 -080088 current_line = 1
89 while current_line < len(log_info):
90 my_dict = {}
91 # Read all the lines from one "Build" to the next into my_dict
92 while True:
93 key = log_info[current_line].split(':')[0].strip()
94 value = log_info[current_line].split(':', 1)[1].strip()
95 my_dict[key] = value
96 current_line += 1
97 if 'Build' in key or current_line == len(log_info):
98 break
99 try:
100 # Check to see of the build record is the right one.
101 if str(description) in my_dict['reason']:
102 # We found a match; we're done.
103 return my_dict
Luis Lozano439f2b72016-01-08 11:56:02 -0800104 except KeyError:
Rahul Chaudhrycbc5a262015-12-30 17:05:14 -0800105 print("reason is not in dictionary: '%s'" % repr(my_dict))
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800106 else:
107 # Keep going.
108 continue
cmtice46093e52014-12-09 14:59:16 -0800109
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800110 # We hit the bottom of the log without a match.
111 return {}
cmtice46093e52014-12-09 14:59:16 -0800112
113
cmtice717bd4a2015-06-25 09:11:52 -0700114def GetBuildInfo(file_dir, builder):
Rahul Chaudhry647950a2015-12-30 18:29:13 -0800115 """Get all the build records for the trybot builds.
cmtice46093e52014-12-09 14:59:16 -0800116
117 file_dir is the toolchain_utils directory.
118 """
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800119 ce = command_executer.GetCommandExecuter()
120 commands = ('{0}/utils/buildbot_json.py builds '
121 'http://chromegw/i/tryserver.chromiumos/'.format(file_dir))
cmtice46093e52014-12-09 14:59:16 -0800122
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800123 if builder:
124 # For release builds, get logs from the 'release' builder.
125 if builder.endswith('-release'):
126 commands += ' -b release'
127 else:
128 commands += ' -b %s' % builder
129 _, buildinfo, _ = ce.RunCommandWOutput(commands, print_to_console=False)
130 build_log = buildinfo.splitlines()
131 return build_log
cmtice46093e52014-12-09 14:59:16 -0800132
133
cmtice1047b072015-02-10 09:33:21 -0800134def FindArchiveImage(chromeos_root, build, build_id):
Rahul Chaudhry647950a2015-12-30 18:29:13 -0800135 """Returns name of the trybot artifact for board/build_id."""
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800136 ce = command_executer.GetCommandExecuter()
137 command = ('gsutil ls gs://chromeos-image-archive/trybot-%s/*b%s'
138 '/chromiumos_test_image.tar.xz' % (build, build_id))
Rahul Chaudhry9c598822015-12-22 17:27:56 -0800139 _, out, _ = ce.ChrootRunCommandWOutput(chromeos_root,
140 command,
141 print_to_console=False)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800142 #
143 # If build_id is not unique, there may be multiple archive images
144 # to choose from; sort them & pick the first (newest).
145 #
146 # If there are multiple archive images found, out will look something
147 # like this:
148 #
Rahul Chaudhryb2d3dc72015-12-30 18:11:05 -0800149 # 'gs://.../R35-5692.0.0-b105/chromiumos_test_image.tar.xz
150 # gs://.../R46-7339.0.0-b105/chromiumos_test_image.tar.xz'
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800151 #
152 out = out.rstrip('\n')
153 tmp_list = out.split('\n')
154 # After stripping the final '\n' and splitting on any other '\n', we get
155 # something like this:
Rahul Chaudhryb2d3dc72015-12-30 18:11:05 -0800156 # tmp_list = [ 'gs://.../R35-5692.0.0-b105/chromiumos_test_image.tar.xz' ,
157 # 'gs://.../R46-7339.0.0-b105/chromiumos_test_image.tar.xz' ]
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800158 #
159 # If we sort this in descending order, we should end up with the most
160 # recent test image first, so that's what we do here.
161 #
162 if len(tmp_list) > 1:
163 tmp_list = sorted(tmp_list, reverse=True)
164 out = tmp_list[0]
cmtice1047b072015-02-10 09:33:21 -0800165
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800166 trybot_image = ''
167 trybot_name = 'trybot-%s' % build
168 if out and out.find(trybot_name) > 0:
169 start_pos = out.find(trybot_name)
170 end_pos = out.find('/chromiumos_test_image')
171 trybot_image = out[start_pos:end_pos]
cmtice1047b072015-02-10 09:33:21 -0800172
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800173 return trybot_image
cmtice1047b072015-02-10 09:33:21 -0800174
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800175
176def GetTrybotImage(chromeos_root,
177 buildbot_name,
178 patch_list,
179 build_tag,
Luis Lozano8a68b2d2015-04-23 14:37:09 -0700180 build_toolchain=False):
Rahul Chaudhry647950a2015-12-30 18:29:13 -0800181 """Launch buildbot and get resulting trybot artifact name.
cmtice46093e52014-12-09 14:59:16 -0800182
183 This function launches a buildbot with the appropriate flags to
184 build the test ChromeOS image, with the current ToT mobile compiler. It
185 checks every 10 minutes to see if the trybot has finished. When the trybot
186 has finished, it parses the resulting report logs to find the trybot
187 artifact (if one was created), and returns that artifact name.
188
189 chromeos_root is the path to the ChromeOS root, needed for finding chromite
190 and launching the buildbot.
191
192 buildbot_name is the name of the buildbot queue, such as lumpy-release or
193 daisy-paladin.
194
195 patch_list a python list of the patches, if any, for the buildbot to use.
196
197 build_tag is a (unique) string to be used to look up the buildbot results
198 from among all the build records.
199 """
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800200 ce = command_executer.GetCommandExecuter()
201 cbuildbot_path = os.path.join(chromeos_root, 'chromite/cbuildbot')
202 base_dir = os.getcwd()
203 patch_arg = ''
204 if patch_list:
205 for p in patch_list:
206 patch_arg = patch_arg + ' -g ' + repr(p)
207 toolchain_flags = ''
208 if build_toolchain:
209 toolchain_flags += '--latest-toolchain'
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800210 os.chdir(cbuildbot_path)
cmtice46093e52014-12-09 14:59:16 -0800211
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800212 # Launch buildbot with appropriate flags.
213 build = buildbot_name
214 description = build_tag
215 command = ('./cbuildbot --remote --nochromesdk --notests'
216 ' --remote-description=%s %s %s %s' %
217 (description, toolchain_flags, patch_arg, build))
218 _, out, _ = ce.RunCommandWOutput(command)
219 if 'Tryjob submitted!' not in out:
220 logger.GetLogger().LogFatal('Error occurred while launching trybot job: '
221 '%s' % command)
222 os.chdir(base_dir)
cmtice46093e52014-12-09 14:59:16 -0800223
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800224 build_id = 0
225 build_status = None
226 # Wait for buildbot to finish running (check every 10 minutes). Wait
227 # 10 minutes before the first check to give the buildbot time to launch
228 # (so we don't start looking for build data before it's out there).
229 time.sleep(SLEEP_TIME)
230 done = False
231 pending = True
232 # pending_time is the time between when we submit the job and when the
233 # buildbot actually launches the build. running_time is the time between
234 # when the buildbot job launches and when it finishes. The job is
235 # considered 'pending' until we can find an entry for it in the buildbot
236 # logs.
237 pending_time = SLEEP_TIME
238 running_time = 0
239 while not done:
240 done = True
241 build_info = GetBuildInfo(base_dir, build)
242 if not build_info:
243 if pending_time > TIME_OUT:
244 logger.GetLogger().LogFatal('Unable to get build logs for target %s.' %
245 build)
246 else:
247 pending_message = 'Unable to find build log; job may be pending.'
248 done = False
249
250 if done:
251 data_dict = FindBuildRecordFromLog(description, build_info)
252 if not data_dict:
253 # Trybot job may be pending (not actually launched yet).
cmticece5ffa42015-02-12 15:18:43 -0800254 if pending_time > TIME_OUT:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800255 logger.GetLogger().LogFatal('Unable to find build record for trybot'
256 ' %s.' % description)
cmticece5ffa42015-02-12 15:18:43 -0800257 else:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800258 pending_message = 'Unable to find build record; job may be pending.'
cmticece5ffa42015-02-12 15:18:43 -0800259 done = False
cmtice46093e52014-12-09 14:59:16 -0800260
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800261 else:
262 # Now that we have actually found the entry for the build
263 # job in the build log, we know the job is actually
264 # runnning, not pending, so we flip the 'pending' flag. We
265 # still have to wait for the buildbot job to finish running
266 # however.
267 pending = False
268 if 'True' in data_dict['completed']:
269 build_id = data_dict['number']
270 build_status = int(data_dict['result'])
cmticece5ffa42015-02-12 15:18:43 -0800271 else:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800272 done = False
cmtice46093e52014-12-09 14:59:16 -0800273
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800274 if not done:
275 if pending:
276 logger.GetLogger().LogOutput(pending_message)
277 logger.GetLogger().LogOutput('Current pending time: %d minutes.' %
278 (pending_time / 60))
279 pending_time += SLEEP_TIME
280 else:
281 logger.GetLogger().LogOutput('{0} minutes passed.'.format(running_time /
282 60))
283 logger.GetLogger().LogOutput('Sleeping {0} seconds.'.format(SLEEP_TIME))
284 running_time += SLEEP_TIME
cmticeaea53412015-06-19 09:44:43 -0700285
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800286 time.sleep(SLEEP_TIME)
287 if running_time > TIME_OUT:
288 done = True
cmtice46093e52014-12-09 14:59:16 -0800289
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800290 trybot_image = ''
Caroline Tice2e5bdd82015-08-07 13:28:16 -0700291
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800292 if build_status in OK_STATUS:
293 trybot_image = FindArchiveImage(chromeos_root, build, build_id)
294 if not trybot_image:
295 logger.GetLogger().LogError('Trybot job %s failed with status %d;'
296 ' no trybot image generated.' %
297 (description, build_status))
cmtice46093e52014-12-09 14:59:16 -0800298
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800299 logger.GetLogger().LogOutput("trybot_image is '%s'" % trybot_image)
300 logger.GetLogger().LogOutput('build_status is %d' % build_status)
301 return trybot_image
Yunlian Jiang56f13762016-01-06 12:56:24 -0800302
Yunlian Jiang56f13762016-01-06 12:56:24 -0800303
Luis Lozano439f2b72016-01-08 11:56:02 -0800304def DoesImageExist(chromeos_root, build):
305 """Check if the image for the given build exists."""
306
Yunlian Jiang56f13762016-01-06 12:56:24 -0800307 ce = command_executer.GetCommandExecuter()
308 command = ('gsutil ls gs://chromeos-image-archive/%s'
309 '/chromiumos_test_image.tar.xz' % (build))
Luis Lozano439f2b72016-01-08 11:56:02 -0800310 ret = ce.ChrootRunCommand(chromeos_root, command, print_to_console=False)
311 return not ret
312
313
314def WaitForImage(chromeos_root, build):
315 """Wait for an image to be ready."""
316
317 elapsed_time = 0
318 while elapsed_time < TIME_OUT:
319 if DoesImageExist(chromeos_root, build):
320 return
321 logger.GetLogger().LogOutput('Image %s not ready, waiting for 10 minutes' %
322 build)
Yunlian Jiang56f13762016-01-06 12:56:24 -0800323 time.sleep(SLEEP_TIME)
324 elapsed_time += SLEEP_TIME
325
Luis Lozano439f2b72016-01-08 11:56:02 -0800326 logger.GetLogger().LogOutput('Image %s not found, waited for %d hours' %
327 (build, (TIME_OUT / 3600)))
328 raise BuildbotTimeout('Timeout while waiting for image %s' % build)