blob: 8764791fe4fa072466034f66b0576adc2da4e488 [file] [log] [blame]
cmtice46093e52014-12-09 14:59:16 -08001#!/usr/bin/python
2
3# Copyright 2014 Google Inc. All Rights Reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import os
8import time
9import urllib2
10
11from utils import command_executer
12from utils import logger
13from utils import buildbot_json
14
15SLEEP_TIME = 600 # 10 minutes; time between polling of buildbot.
16TIME_OUT = 18000 # Decide the build is dead or will never finish
17 # after this time (5 hours).
Caroline Tice2e5bdd82015-08-07 13:28:16 -070018OK_STATUS = [ # List of result status values that are 'ok'.
19 # This was obtained from: https://chromium.googlesource.com/chromium/tools/build/+/master/third_party/buildbot_8_4p1/buildbot/status/results.py
20 0, # "success"
21 1, # "warnings"
22 6, # "retry"
23 ]
cmtice46093e52014-12-09 14:59:16 -080024
25"""Utilities for launching and accessing ChromeOS buildbots."""
26
27def ParseReportLog (url, build):
28 """
29 Scrape the trybot image name off the Reports log page.
30
31 This takes the URL for a trybot Reports Stage web page,
32 and a trybot build type, such as 'daisy-release'. It
33 opens the web page and parses it looking for the trybot
34 artifact name (e.g. something like
35 'trybot-daisy-release/R40-6394.0.0-b1389'). It returns the
36 artifact name, if found.
37 """
38 trybot_image = ""
39 url += "/text"
40 newurl = url.replace ("uberchromegw", "chromegw")
41 webpage = urllib2.urlopen(newurl)
42 data = webpage.read()
43 lines = data.split('\n')
44 for l in lines:
cmtice1047b072015-02-10 09:33:21 -080045 if l.find("Artifacts") > 0 and l.find("trybot") > 0:
cmtice46093e52014-12-09 14:59:16 -080046 trybot_name = "trybot-%s" % build
47 start_pos = l.find(trybot_name)
48 end_pos = l.find("@https://storage")
49 trybot_image = l[start_pos:end_pos]
50
51 return trybot_image
52
cmtice46093e52014-12-09 14:59:16 -080053def GetBuildData (buildbot_queue, build_id):
54 """
55 Find the Reports stage web page for a trybot build.
56
57 This takes the name of a buildbot_queue, such as 'daisy-release'
58 and a build id (the build number), and uses the json buildbot api to
59 find the Reports stage web page for that build, if it exists.
60 """
61 builder = buildbot_json.Buildbot(
62 "http://chromegw/p/tryserver.chromiumos/").builders[buildbot_queue]
63 build_data = builder.builds[build_id].data
64 logs = build_data["logs"]
65 for l in logs:
66 fname = l[1]
67 if "steps/Report/" in fname:
68 return fname
69
70 return ""
71
72
73def FindBuildRecordFromLog(description, log_info):
74 """
75 Find the right build record in the build logs.
76
77 Get the first build record from build log with a reason field
78 that matches 'description'. ('description' is a special tag we
79 created when we launched the buildbot, so we could find it at this
80 point.)
81 """
82
83 current_line = 1
84 while current_line < len(log_info):
85 my_dict = {}
86 # Read all the lines from one "Build" to the next into my_dict
87 while True:
88 key = log_info[current_line].split(":")[0].strip()
89 value = log_info[current_line].split(":", 1)[1].strip()
90 my_dict[key] = value
91 current_line += 1
92 if "Build" in key or current_line == len(log_info):
93 break
94 try:
95 # Check to see of the build record is the right one.
96 if str(description) in my_dict["reason"]:
97 # We found a match; we're done.
98 return my_dict
99 except:
100 print "reason is not in dictionary: '%s'" % repr(my_dict)
101 else:
102 # Keep going.
103 continue
104
105 # We hit the bottom of the log without a match.
106 return {}
107
108
cmtice717bd4a2015-06-25 09:11:52 -0700109def GetBuildInfo(file_dir, builder):
cmtice46093e52014-12-09 14:59:16 -0800110 """
111 Get all the build records for the trybot builds.
112
113 file_dir is the toolchain_utils directory.
114 """
115 ce = command_executer.GetCommandExecuter()
116 commands = ("{0}/utils/buildbot_json.py builds "
Han Shen3931b8b2015-04-28 13:17:26 -0700117 "http://chromegw/i/tryserver.chromiumos/"
cmtice46093e52014-12-09 14:59:16 -0800118 .format(file_dir))
119
cmtice717bd4a2015-06-25 09:11:52 -0700120 if builder:
Rahul Chaudhrydac5f562015-08-05 11:22:20 -0700121 # For release builds, get logs from the 'release' builder.
122 if builder.endswith('-release'):
123 commands += " -b release"
124 else:
125 commands += " -b %s" % builder
cmtice46093e52014-12-09 14:59:16 -0800126 _, buildinfo, _ = ce.RunCommand(commands, return_output=True,
127 print_to_console=False)
128 build_log = buildinfo.splitlines()
129 return build_log
130
131
cmtice1047b072015-02-10 09:33:21 -0800132def FindArchiveImage(chromeos_root, build, build_id):
133 """
134 Given a build_id, search Google Storage for a trybot artifact
135 for the correct board with the correct build_id. Return the
136 name of the artifact, if found.
137 """
138 ce = command_executer.GetCommandExecuter()
139 command = ("gsutil ls gs://chromeos-image-archive/trybot-%s/*b%s"
140 "/chromiumos_test_image.tar.xz" % (build, build_id))
cmtice717bd4a2015-06-25 09:11:52 -0700141 retval, out, err = ce.ChrootRunCommand(chromeos_root, command,
142 return_output=True,
cmtice1047b072015-02-10 09:33:21 -0800143 print_to_console=False)
Caroline Tice2e5bdd82015-08-07 13:28:16 -0700144 #
145 # If build_id is not unique, there may be multiple archive images
146 # to choose from; sort them & pick the first (newest).
147 #
148 # If there are multiple archive images found, out will look something
149 # like this:
150 #
151 # 'gs://chromeos-image-archive/trybot-peppy-release/R35-5692.0.0-b105/chromiumos_test_image.tar.xz\ngs://chromeos-image-archive/trybot-peppy-release/R46-7339.0.0-b105/chromiumos_test_image.tar.xz\n'
152 #
153 out = out.rstrip('\n')
154 tmp_list = out.split('\n')
155 # After stripping the final '\n' and splitting on any other '\n', we get
156 # something like this:
157 # tmp_list = [ 'gs://chromeos-image-archive/trybot-peppy-release/R35-5692.0.0-b105/chromiumos_test_image.tar.xz' ,
158 # 'gs://chromeos-image-archive/trybot-peppy-release/R46-7339.0.0-b105/chromiumos_test_image.tar.xz' ]
159 #
160 # If we sort this in descending order, we should end up with the most
161 # recent test image first, so that's what we do here.
162 #
163 if len(tmp_list) > 1:
164 tmp_list = sorted(tmp_list, reverse=True)
165 out = tmp_list[0]
cmtice1047b072015-02-10 09:33:21 -0800166
167 trybot_image = ""
168 trybot_name = "trybot-%s" % build
169 if out and out.find(trybot_name) > 0:
170 start_pos = out.find(trybot_name)
171 end_pos = out.find("/chromiumos_test_image")
172 trybot_image = out[start_pos:end_pos]
173
174 return trybot_image
175
Luis Lozano8a68b2d2015-04-23 14:37:09 -0700176def GetTrybotImage(chromeos_root, buildbot_name, patch_list, build_tag,
177 build_toolchain=False):
cmtice46093e52014-12-09 14:59:16 -0800178 """
179 Launch buildbot and get resulting trybot artifact name.
180
181 This function launches a buildbot with the appropriate flags to
182 build the test ChromeOS image, with the current ToT mobile compiler. It
183 checks every 10 minutes to see if the trybot has finished. When the trybot
184 has finished, it parses the resulting report logs to find the trybot
185 artifact (if one was created), and returns that artifact name.
186
187 chromeos_root is the path to the ChromeOS root, needed for finding chromite
188 and launching the buildbot.
189
190 buildbot_name is the name of the buildbot queue, such as lumpy-release or
191 daisy-paladin.
192
193 patch_list a python list of the patches, if any, for the buildbot to use.
194
195 build_tag is a (unique) string to be used to look up the buildbot results
196 from among all the build records.
197 """
198 ce = command_executer.GetCommandExecuter()
199 cbuildbot_path = os.path.join(chromeos_root, "chromite/cbuildbot")
200 base_dir = os.getcwd()
201 patch_arg = ""
202 if patch_list:
203 patch_arg = "-g "
204 for p in patch_list:
205 patch_arg = patch_arg + " " + repr(p)
Luis Lozano8a68b2d2015-04-23 14:37:09 -0700206 toolchain_flags=""
207 if build_toolchain:
Luis Lozanod7d50842015-04-24 10:03:26 -0700208 toolchain_flags += "--latest-toolchain"
cmtice46093e52014-12-09 14:59:16 -0800209 branch = "master"
210 os.chdir(cbuildbot_path)
211
212 # Launch buildbot with appropriate flags.
213 build = buildbot_name
214 description = build_tag
Luis Lozano8a68b2d2015-04-23 14:37:09 -0700215 command = ("./cbuildbot --remote --nochromesdk --notests"
216 " --remote-description=%s %s %s %s"
217 % (description, toolchain_flags, patch_arg, build))
cmticeaea53412015-06-19 09:44:43 -0700218 _, out, _ = ce.RunCommand(command, return_output=True)
219 if "Tryjob submitted!" not in out:
220 logger.GetLogger().LogFatal("Error occurred while launching trybot job: "
221 "%s" % command)
cmtice46093e52014-12-09 14:59:16 -0800222 os.chdir(base_dir)
223
224 build_id = 0
cmticed54f9802015-02-05 11:04:11 -0800225 build_status = None
cmticea63ffc02015-04-21 11:38:55 -0700226 # 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)
cmtice46093e52014-12-09 14:59:16 -0800230 done = False
cmticeaea53412015-06-19 09:44:43 -0700231 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.
cmticea63ffc02015-04-21 11:38:55 -0700237 pending_time = SLEEP_TIME
cmticeaea53412015-06-19 09:44:43 -0700238 running_time = 0
cmtice46093e52014-12-09 14:59:16 -0800239 while not done:
240 done = True
cmtice717bd4a2015-06-25 09:11:52 -0700241 build_info = GetBuildInfo(base_dir, build)
cmtice46093e52014-12-09 14:59:16 -0800242 if not build_info:
cmticece5ffa42015-02-12 15:18:43 -0800243 if pending_time > TIME_OUT:
cmticeaea53412015-06-19 09:44:43 -0700244 logger.GetLogger().LogFatal("Unable to get build logs for target %s."
245 % build)
cmticece5ffa42015-02-12 15:18:43 -0800246 else:
cmticeaea53412015-06-19 09:44:43 -0700247 pending_message = "Unable to find build log; job may be pending."
cmticece5ffa42015-02-12 15:18:43 -0800248 done = False
cmtice46093e52014-12-09 14:59:16 -0800249
cmticeaea53412015-06-19 09:44:43 -0700250 if done:
251 data_dict = FindBuildRecordFromLog(description, build_info)
252 if not data_dict:
253 # Trybot job may be pending (not actually launched yet).
254 if pending_time > TIME_OUT:
255 logger.GetLogger().LogFatal("Unable to find build record for trybot"
256 " %s." % description)
257 else:
258 pending_message = "Unable to find build record; job may be pending."
259 done = False
260
cmticece5ffa42015-02-12 15:18:43 -0800261 else:
cmticeaea53412015-06-19 09:44:43 -0700262 # 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"])
271 else:
272 done = False
cmtice46093e52014-12-09 14:59:16 -0800273
Rahul Chaudhryc7a9ac52015-08-05 11:13:35 -0700274 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(
282 running_time / 60))
283 logger.GetLogger().LogOutput("Sleeping {0} seconds.".format(
284 SLEEP_TIME))
285 running_time += SLEEP_TIME
cmticeaea53412015-06-19 09:44:43 -0700286
Rahul Chaudhryc7a9ac52015-08-05 11:13:35 -0700287 time.sleep(SLEEP_TIME)
288 if running_time > TIME_OUT:
289 done = True
cmtice46093e52014-12-09 14:59:16 -0800290
Caroline Tice2e5bdd82015-08-07 13:28:16 -0700291 trybot_image = ""
292
293 if build_status in OK_STATUS:
294 trybot_image = FindArchiveImage(chromeos_root, build, build_id)
cmtice1047b072015-02-10 09:33:21 -0800295 if not trybot_image:
296 logger.GetLogger().LogError("Trybot job %s failed with status %d;"
297 " no trybot image generated."
298 % (description, build_status))
cmtice46093e52014-12-09 14:59:16 -0800299
cmtice1047b072015-02-10 09:33:21 -0800300 logger.GetLogger().LogOutput("trybot_image is '%s'" % trybot_image)
cmticeaea53412015-06-19 09:44:43 -0700301 logger.GetLogger().LogOutput("build_status is %d" % build_status)
cmtice1047b072015-02-10 09:33:21 -0800302 return trybot_image