blob: a15d621e43f0e41dd20196f5e3fbab0c0a16b025 [file] [log] [blame]
Chris Sosa5e4246b2012-05-22 18:05:22 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Sean O'Connor5346e4e2010-08-12 18:49:24 +02002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import httplib
6import logging
Chris Sosa77556d82012-04-05 15:23:14 -07007import multiprocessing
Dale Curtis5c32c722011-05-04 19:24:23 -07008import os
Sean O'Connor5346e4e2010-08-12 18:49:24 +02009import re
Sean O'Connor5346e4e2010-08-12 18:49:24 +020010import urlparse
11
Dale Curtis5c32c722011-05-04 19:24:23 -070012from autotest_lib.client.common_lib import error, global_config
Sean O'Connor5346e4e2010-08-12 18:49:24 +020013
Dale Curtis5c32c722011-05-04 19:24:23 -070014# Local stateful update path is relative to the CrOS source directory.
15LOCAL_STATEFUL_UPDATE_PATH = 'src/platform/dev/stateful_update'
Chris Sosaa3ac2152012-05-23 22:23:13 -070016LOCAL_CHROOT_STATEFUL_UPDATE_PATH = '/usr/bin/stateful_update'
Dale Curtis5c32c722011-05-04 19:24:23 -070017REMOTE_STATEUL_UPDATE_PATH = '/usr/local/bin/stateful_update'
18STATEFUL_UPDATE = '/tmp/stateful_update'
Sean O'Connor5346e4e2010-08-12 18:49:24 +020019UPDATER_BIN = '/usr/bin/update_engine_client'
20UPDATER_IDLE = 'UPDATE_STATUS_IDLE'
Sean Oc053dfe2010-08-23 18:22:26 +020021UPDATER_NEED_REBOOT = 'UPDATE_STATUS_UPDATED_NEED_REBOOT'
Darin Petkov7d572992010-09-23 10:11:05 -070022UPDATED_MARKER = '/var/run/update_engine_autoupdate_completed'
Dale Curtis1e973182011-07-12 18:21:36 -070023UPDATER_LOGS = '/var/log/messages /var/log/update_engine'
Sean O'Connor5346e4e2010-08-12 18:49:24 +020024
25
26class ChromiumOSError(error.InstallError):
27 """Generic error for ChromiumOS-specific exceptions."""
28 pass
29
30
Chris Sosa77556d82012-04-05 15:23:14 -070031class RootFSUpdateError(ChromiumOSError):
32 """Raised when the RootFS fails to update."""
33 pass
34
35
36class StatefulUpdateError(ChromiumOSError):
37 """Raised when the stateful partition fails to update."""
38 pass
39
40
Sean O'Connor5346e4e2010-08-12 18:49:24 +020041def url_to_version(update_url):
Dan Shi0f466e82013-02-22 15:44:58 -080042 """Return the version based on update_url.
43
44 @param update_url: url to the image to update to.
45
46 """
Dale Curtisddfdb942011-07-14 13:59:24 -070047 # The Chrome OS version is generally the last element in the URL. The only
48 # exception is delta update URLs, which are rooted under the version; e.g.,
49 # http://.../update/.../0.14.755.0/au/0.14.754.0. In this case we want to
50 # strip off the au section of the path before reading the version.
Dan Shi5002cfc2013-04-29 10:45:05 -070051 return re.sub('/au/.*', '',
52 urlparse.urlparse(update_url).path).split('/')[-1].strip()
Sean O'Connor5346e4e2010-08-12 18:49:24 +020053
54
Scott Zawalskieadbf702013-03-14 09:23:06 -040055def url_to_image_name(update_url):
56 """Return the image name based on update_url.
57
58 From a URL like:
59 http://172.22.50.205:8082/update/lumpy-release/R27-3837.0.0
60 return lumpy-release/R27-3837.0.0
61
62 @param update_url: url to the image to update to.
63 @returns a string representing the image name in the update_url.
64
65 """
66 return '/'.join(urlparse.urlparse(update_url).path.split('/')[-2:])
67
68
Sean O'Connor5346e4e2010-08-12 18:49:24 +020069class ChromiumOSUpdater():
Dan Shi0f466e82013-02-22 15:44:58 -080070 """Helper class used to update DUT with image of desired version."""
Dale Curtisa94c19c2011-05-02 15:05:17 -070071 KERNEL_A = {'name': 'KERN-A', 'kernel': 2, 'root': 3}
72 KERNEL_B = {'name': 'KERN-B', 'kernel': 4, 'root': 5}
73
74
Chris Sosaa3ac2152012-05-23 22:23:13 -070075 def __init__(self, update_url, host=None, local_devserver=False):
Sean O'Connor5346e4e2010-08-12 18:49:24 +020076 self.host = host
77 self.update_url = update_url
Chris Sosa77556d82012-04-05 15:23:14 -070078 self._update_error_queue = multiprocessing.Queue(2)
Chris Sosaa3ac2152012-05-23 22:23:13 -070079 self.local_devserver = local_devserver
80 if not local_devserver:
81 self.update_version = url_to_version(update_url)
82 else:
83 self.update_version = None
Sean Oc053dfe2010-08-23 18:22:26 +020084
Sean O'Connor5346e4e2010-08-12 18:49:24 +020085 def check_update_status(self):
Dale Curtis5c32c722011-05-04 19:24:23 -070086 """Return current status from update-engine."""
87 update_status = self._run(
88 '%s -status 2>&1 | grep CURRENT_OP' % UPDATER_BIN)
Sean O'Connor5346e4e2010-08-12 18:49:24 +020089 return update_status.stdout.strip().split('=')[-1]
90
Sean Oc053dfe2010-08-23 18:22:26 +020091
92 def reset_update_engine(self):
Dale Curtis5c32c722011-05-04 19:24:23 -070093 """Restarts the update-engine service."""
Darin Petkov7d572992010-09-23 10:11:05 -070094 self._run('rm -f %s' % UPDATED_MARKER)
Sean O267c00b2010-08-31 15:54:55 +020095 try:
96 self._run('initctl stop update-engine')
Dale Curtis5c32c722011-05-04 19:24:23 -070097 except error.AutoservRunError:
Sean O267c00b2010-08-31 15:54:55 +020098 logging.warn('Stopping update-engine service failed. Already dead?')
Sean Oc053dfe2010-08-23 18:22:26 +020099 self._run('initctl start update-engine')
Dale Curtis5c32c722011-05-04 19:24:23 -0700100
Sean Oc053dfe2010-08-23 18:22:26 +0200101 if self.check_update_status() != UPDATER_IDLE:
102 raise ChromiumOSError('%s is not in an installable state' %
103 self.host.hostname)
104
105
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200106 def _run(self, cmd, *args, **kwargs):
Dale Curtis5c32c722011-05-04 19:24:23 -0700107 """Abbreviated form of self.host.run(...)"""
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200108 return self.host.run(cmd, *args, **kwargs)
109
Sean Oc053dfe2010-08-23 18:22:26 +0200110
Dale Curtisa94c19c2011-05-02 15:05:17 -0700111 def rootdev(self, options=''):
Dan Shi0f466e82013-02-22 15:44:58 -0800112 """Returns the stripped output of rootdev <options>.
113
114 @param options: options to run rootdev.
115
116 """
Dale Curtisa94c19c2011-05-02 15:05:17 -0700117 return self._run('rootdev %s' % options).stdout.strip()
118
119
120 def get_kernel_state(self):
121 """Returns the (<active>, <inactive>) kernel state as a pair."""
122 active_root = int(re.findall('\d+\Z', self.rootdev('-s'))[0])
123 if active_root == self.KERNEL_A['root']:
124 return self.KERNEL_A, self.KERNEL_B
125 elif active_root == self.KERNEL_B['root']:
126 return self.KERNEL_B, self.KERNEL_A
127 else:
Dale Curtis5c32c722011-05-04 19:24:23 -0700128 raise ChromiumOSError('Encountered unknown root partition: %s' %
Dale Curtisa94c19c2011-05-02 15:05:17 -0700129 active_root)
130
131
132 def _cgpt(self, flag, kernel, dev='$(rootdev -s -d)'):
133 """Return numeric cgpt value for the specified flag, kernel, device. """
134 return int(self._run('cgpt show -n -i %d %s %s' % (
135 kernel['kernel'], flag, dev)).stdout.strip())
136
137
138 def get_kernel_priority(self, kernel):
Dan Shi0f466e82013-02-22 15:44:58 -0800139 """Return numeric priority for the specified kernel.
140
141 @param kernel: information of the given kernel, KERNEL_A or KERNEL_B.
142
143 """
Dale Curtisa94c19c2011-05-02 15:05:17 -0700144 return self._cgpt('-P', kernel)
145
146
147 def get_kernel_success(self, kernel):
Dan Shi0f466e82013-02-22 15:44:58 -0800148 """Return boolean success flag for the specified kernel.
149
150 @param kernel: information of the given kernel, KERNEL_A or KERNEL_B.
151
152 """
Dale Curtisa94c19c2011-05-02 15:05:17 -0700153 return self._cgpt('-S', kernel) != 0
154
155
156 def get_kernel_tries(self, kernel):
Dan Shi0f466e82013-02-22 15:44:58 -0800157 """Return tries count for the specified kernel.
158
159 @param kernel: information of the given kernel, KERNEL_A or KERNEL_B.
160
161 """
Dale Curtisa94c19c2011-05-02 15:05:17 -0700162 return self._cgpt('-T', kernel)
Sean O267c00b2010-08-31 15:54:55 +0200163
164
Chris Sosa5e4246b2012-05-22 18:05:22 -0700165 def get_stateful_update_script(self):
166 """Returns the path to the stateful update script on the target."""
Chris Sosaa3ac2152012-05-23 22:23:13 -0700167 # We attempt to load the local stateful update path in 3 different
168 # ways. First we use the location specified in the autotest global
169 # config. If this doesn't exist, we attempt to use the Chromium OS
170 # Chroot path to the installed script. If all else fails, we use the
171 # stateful update script on the host.
Chris Sosa5e4246b2012-05-22 18:05:22 -0700172 stateful_update_path = os.path.join(
173 global_config.global_config.get_config_value(
174 'CROS', 'source_tree', default=''),
175 LOCAL_STATEFUL_UPDATE_PATH)
176
Chris Sosaa3ac2152012-05-23 22:23:13 -0700177 if not os.path.exists(stateful_update_path):
178 logging.warn('Could not find Chrome OS source location for '
179 'stateful_update script at %s, falling back to chroot '
180 'copy.', stateful_update_path)
181 stateful_update_path = LOCAL_CHROOT_STATEFUL_UPDATE_PATH
182
183 if not os.path.exists(stateful_update_path):
184 logging.warn('Could not chroot stateful_update script, falling '
185 'back on client copy.')
186 statefuldev_script = REMOTE_STATEUL_UPDATE_PATH
187 else:
Chris Sosa5e4246b2012-05-22 18:05:22 -0700188 self.host.send_file(
189 stateful_update_path, STATEFUL_UPDATE, delete_dest=True)
190 statefuldev_script = STATEFUL_UPDATE
Chris Sosa5e4246b2012-05-22 18:05:22 -0700191
192 return statefuldev_script
193
194
195 def reset_stateful_partition(self):
Dan Shi0f466e82013-02-22 15:44:58 -0800196 """Clear any pending stateful update request."""
Chris Sosa5e4246b2012-05-22 18:05:22 -0700197 statefuldev_cmd = [self.get_stateful_update_script()]
198 statefuldev_cmd += ['--stateful_change=reset', '2>&1']
Chris Sosa66d74072013-09-19 11:21:29 -0700199 self._run(' '.join(statefuldev_cmd))
Chris Sosa5e4246b2012-05-22 18:05:22 -0700200
201
Sean O267c00b2010-08-31 15:54:55 +0200202 def revert_boot_partition(self):
Dan Shi0f466e82013-02-22 15:44:58 -0800203 """Revert the boot partition."""
Dale Curtisd9b26b92011-10-24 13:34:46 -0700204 part = self.rootdev('-s')
Sean O267c00b2010-08-31 15:54:55 +0200205 logging.warn('Reverting update; Boot partition will be %s', part)
206 return self._run('/postinst %s 2>&1' % part)
207
208
Gilad Arnold0ed760c2012-11-05 23:42:53 -0800209 def trigger_update(self):
210 """Triggers a background update on a test image.
211
212 @raise RootFSUpdateError if anything went wrong.
213
214 """
215 autoupdate_cmd = '%s --check_for_update --omaha_url=%s' % (
216 UPDATER_BIN, self.update_url)
217 logging.info('triggering update via: %s', autoupdate_cmd)
218 try:
Chris Sosa66d74072013-09-19 11:21:29 -0700219 self._run(autoupdate_cmd)
Gilad Arnold0ed760c2012-11-05 23:42:53 -0800220 except error.AutoservRunError, e:
221 raise RootFSUpdateError('update triggering failed on %s: %s' %
222 (self.host.hostname, str(e)))
223
224
Chris Sosa2f1ae9f2013-08-13 10:00:15 -0700225 def update_rootfs(self):
226 """Updates the rootfs partition only."""
Chris Sosa77556d82012-04-05 15:23:14 -0700227 logging.info('Updating root partition...')
Dale Curtis5c32c722011-05-04 19:24:23 -0700228
229 # Run update_engine using the specified URL.
230 try:
231 autoupdate_cmd = '%s --update --omaha_url=%s 2>&1' % (
232 UPDATER_BIN, self.update_url)
233 self._run(autoupdate_cmd, timeout=900)
234 except error.AutoservRunError:
Chris Sosa77556d82012-04-05 15:23:14 -0700235 update_error = RootFSUpdateError('update-engine failed on %s' %
236 self.host.hostname)
237 self._update_error_queue.put(update_error)
238 raise update_error
Dale Curtis5c32c722011-05-04 19:24:23 -0700239
240 # Check that the installer completed as expected.
241 status = self.check_update_status()
242 if status != UPDATER_NEED_REBOOT:
Chris Sosa77556d82012-04-05 15:23:14 -0700243 update_error = RootFSUpdateError('update-engine error on %s: %s' %
244 (self.host.hostname, status))
245 self._update_error_queue.put(update_error)
246 raise update_error
Dale Curtis5c32c722011-05-04 19:24:23 -0700247
248
Chris Sosa72312602013-04-16 15:01:56 -0700249 def update_stateful(self, clobber=True):
250 """Updates the stateful partition.
251
252 @param clobber: If True, a clean stateful installation.
253 """
Chris Sosa77556d82012-04-05 15:23:14 -0700254 logging.info('Updating stateful partition...')
joychen03eaad92013-06-26 09:55:21 -0700255 statefuldev_url = self.update_url.replace('update',
256 'static')
Chris Sosaa3ac2152012-05-23 22:23:13 -0700257
Dale Curtis5c32c722011-05-04 19:24:23 -0700258 # Attempt stateful partition update; this must succeed so that the newly
259 # installed host is testable after update.
Chris Sosa72312602013-04-16 15:01:56 -0700260 statefuldev_cmd = [self.get_stateful_update_script(), statefuldev_url]
261 if clobber:
262 statefuldev_cmd.append('--stateful_change=clean')
263
264 statefuldev_cmd.append('2>&1')
Dale Curtis5c32c722011-05-04 19:24:23 -0700265 try:
266 self._run(' '.join(statefuldev_cmd), timeout=600)
267 except error.AutoservRunError:
Chris Sosa77556d82012-04-05 15:23:14 -0700268 update_error = StatefulUpdateError('stateful_update failed on %s' %
269 self.host.hostname)
270 self._update_error_queue.put(update_error)
271 raise update_error
Dale Curtis5c32c722011-05-04 19:24:23 -0700272
273
Dan Shi0f466e82013-02-22 15:44:58 -0800274 def run_update(self, force_update, update_root=True):
275 """Update the DUT with image of specific version.
Chris Sosaa3ac2152012-05-23 22:23:13 -0700276
Dan Shi0f466e82013-02-22 15:44:58 -0800277 @param force_update: True to update DUT even if it's running the same
278 version already.
279 @param update_root: True to force a kernel update. If it's False and
280 force_update is True, stateful update will be used to clean up
281 the DUT.
282
283 """
284 booted_version = self.get_build_id()
285 if (self.check_version() and not force_update):
Dale Curtisa94c19c2011-05-02 15:05:17 -0700286 logging.info('System is already up to date. Skipping update.')
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200287 return False
288
Chris Sosaa3ac2152012-05-23 22:23:13 -0700289 if self.update_version:
290 logging.info('Updating from version %s to %s.',
291 booted_version, self.update_version)
Dale Curtis53d55862011-05-16 12:17:59 -0700292
Dale Curtis5c32c722011-05-04 19:24:23 -0700293 # Check that Dev Server is accepting connections (from autoserv's host).
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200294 # If we can't talk to it, the machine host probably can't either.
295 auserver_host = urlparse.urlparse(self.update_url)[1]
296 try:
297 httplib.HTTPConnection(auserver_host).connect()
Dale Curtis5c32c722011-05-04 19:24:23 -0700298 except IOError:
299 raise ChromiumOSError(
300 'Update server at %s not available' % auserver_host)
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200301
Chris Sosaa3ac2152012-05-23 22:23:13 -0700302 logging.info('Installing from %s to %s', self.update_url,
Chris Sosa77556d82012-04-05 15:23:14 -0700303 self.host.hostname)
304
Chris Sosa5e4246b2012-05-22 18:05:22 -0700305 # Reset update state.
Chris Sosa77556d82012-04-05 15:23:14 -0700306 self.reset_update_engine()
Chris Sosa5e4246b2012-05-22 18:05:22 -0700307 self.reset_stateful_partition()
Sean Oc053dfe2010-08-23 18:22:26 +0200308
Dale Curtis1e973182011-07-12 18:21:36 -0700309 try:
Chris Sosa77556d82012-04-05 15:23:14 -0700310 updaters = [
Chris Sosa2f1ae9f2013-08-13 10:00:15 -0700311 multiprocessing.process.Process(target=self.update_rootfs),
Chris Sosa72312602013-04-16 15:01:56 -0700312 multiprocessing.process.Process(target=self.update_stateful)
Chris Sosa77556d82012-04-05 15:23:14 -0700313 ]
Dan Shi0f466e82013-02-22 15:44:58 -0800314 if not update_root:
315 logging.info('Root update is skipped.')
316 updaters = updaters[1:]
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200317
Chris Sosa77556d82012-04-05 15:23:14 -0700318 # Run the updaters in parallel.
319 for updater in updaters: updater.start()
320 for updater in updaters: updater.join()
321
322 # Re-raise the first error that occurred.
323 if not self._update_error_queue.empty():
324 update_error = self._update_error_queue.get()
325 self.revert_boot_partition()
Chris Sosa5e4246b2012-05-22 18:05:22 -0700326 self.reset_stateful_partition()
Chris Sosa77556d82012-04-05 15:23:14 -0700327 raise update_error
Sean Oc053dfe2010-08-23 18:22:26 +0200328
Dale Curtis1e973182011-07-12 18:21:36 -0700329 logging.info('Update complete.')
330 return True
331 except:
332 # Collect update engine logs in the event of failure.
333 if self.host.job:
334 logging.info('Collecting update engine logs...')
335 self.host.get_file(
336 UPDATER_LOGS, self.host.job.sysinfo.sysinfodir,
337 preserve_perm=False)
338 raise
Dan Shi10e992b2013-08-30 11:02:59 -0700339 finally:
340 self.host.show_update_engine_log()
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200341
342
Dale Curtisa94c19c2011-05-02 15:05:17 -0700343 def check_version(self):
Dan Shi0f466e82013-02-22 15:44:58 -0800344 """Check the image running in DUT has the desired version.
345
346 @returns: True if the DUT's image version matches the version that
347 the autoupdater tries to update to.
348
349 """
Dale Curtisf57a25f2011-05-24 14:40:55 -0700350 booted_version = self.get_build_id()
Dan Shib95bb862013-03-22 16:29:28 -0700351 return (self.update_version and
352 self.update_version.endswith(booted_version))
353
354
355 def check_version_to_confirm_install(self):
356 """Check image running in DUT has the desired version to be installed.
357
358 The method should not be used to check if DUT needs to have a full
359 reimage. Only use it to confirm a image is installed.
360
Dan Shi190c7802013-04-04 13:05:30 -0700361 The method is designed to verify version for following 4 scenarios with
362 samples of version to update to and expected booted version:
363 1. trybot paladin build.
364 update version: trybot-lumpy-paladin/R27-3837.0.0-b123
365 booted version: 3837.0.2013_03_21_1340
366
367 2. trybot release build.
368 update version: trybot-lumpy-release/R27-3837.0.0-b456
369 booted version: 3837.0.0
370
371 3. buildbot official release build.
372 update version: lumpy-release/R27-3837.0.0
373 booted version: 3837.0.0
374
375 4. non-official paladin rc build.
376 update version: lumpy-paladin/R27-3878.0.0-rc7
377 booted version: 3837.0.0-rc7
Dan Shib95bb862013-03-22 16:29:28 -0700378
Dan Shi7f795512013-04-12 10:08:17 -0700379 5. chrome-perf build.
380 update version: lumpy-chrome-perf/R28-3837.0.0-b2996
381 booted version: 3837.0.0
382
Dan Shi73aa2902013-05-03 11:22:11 -0700383 6. pgo-generate build.
384 update version: lumpy-release-pgo-generate/R28-3837.0.0-b2996
385 booted version: 3837.0.0-pgo-generate
386
Dan Shib95bb862013-03-22 16:29:28 -0700387 When we are checking if a DUT needs to do a full install, we should NOT
388 use this method to check if the DUT is running the same version, since
Dan Shi190c7802013-04-04 13:05:30 -0700389 it may return false positive for a DUT running trybot paladin build to
390 be updated to another trybot paladin build.
Dan Shib95bb862013-03-22 16:29:28 -0700391
Dan Shi190c7802013-04-04 13:05:30 -0700392 TODO: This logic has a bug if a trybot paladin build failed to be
393 installed in a DUT running an older trybot paladin build with same
394 platform number, but different build number (-b###). So to conclusively
395 determine if a tryjob paladin build is imaged successfully, we may need
396 to find out the date string from update url.
Dan Shib95bb862013-03-22 16:29:28 -0700397
398 @returns: True if the DUT's image version (without the date string if
399 the image is a trybot build), matches the version that the
400 autoupdater is trying to update to.
401
402 """
J. Richard Barnetteec1de422013-06-26 15:44:07 -0700403 # In the local_devserver case, we can't know the expected
404 # build, so just pass.
405 if not self.update_version:
406 return True
407
Dan Shib95bb862013-03-22 16:29:28 -0700408 # Always try the default check_version method first, this prevents
409 # any backward compatibility issue.
410 if self.check_version():
411 return True
412
Dan Shi190c7802013-04-04 13:05:30 -0700413 # Remove R#- and -b# at the end of build version
414 stripped_version = re.sub(r'(R\d+-|-b\d+)', '', self.update_version)
415
Dan Shib95bb862013-03-22 16:29:28 -0700416 booted_version = self.get_build_id()
Dan Shi190c7802013-04-04 13:05:30 -0700417
Dan Shi7f795512013-04-12 10:08:17 -0700418 is_trybot_paladin_build = re.match(r'.+trybot-.+-paladin',
419 self.update_url)
Dan Shi190c7802013-04-04 13:05:30 -0700420
Dan Shi7f795512013-04-12 10:08:17 -0700421 # Replace date string with 0 in booted_version
422 booted_version_no_date = re.sub(r'\d{4}_\d{2}_\d{2}_\d+', '0',
423 booted_version)
424 has_date_string = booted_version != booted_version_no_date
425
Dan Shi73aa2902013-05-03 11:22:11 -0700426 is_pgo_generate_build = re.match(r'.+-pgo-generate',
427 self.update_url)
428
429 # Remove |-pgo-generate| in booted_version
430 booted_version_no_pgo = booted_version.replace('-pgo-generate', '')
431 has_pgo_generate = booted_version != booted_version_no_pgo
432
Dan Shi7f795512013-04-12 10:08:17 -0700433 if is_trybot_paladin_build:
434 if not has_date_string:
435 logging.error('A trybot paladin build is expected. Version ' +
436 '"%s" is not a paladin build.', booted_version)
Dan Shi190c7802013-04-04 13:05:30 -0700437 return False
438 return stripped_version == booted_version_no_date
Dan Shi73aa2902013-05-03 11:22:11 -0700439 elif is_pgo_generate_build:
440 if not has_pgo_generate:
441 logging.error('A pgo-generate build is expected. Version ' +
442 '"%s" is not a pgo-generate build.',
443 booted_version)
444 return False
445 return stripped_version == booted_version_no_pgo
Dan Shi7f795512013-04-12 10:08:17 -0700446 else:
447 if has_date_string:
448 logging.error('Unexpected date found in a non trybot paladin' +
449 ' build.')
450 return False
451 # Versioned build, i.e., rc or release build.
452 return stripped_version == booted_version
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200453
Sean Oc053dfe2010-08-23 18:22:26 +0200454
Sean O'Connor5346e4e2010-08-12 18:49:24 +0200455 def get_build_id(self):
Dale Curtis793f9122011-02-04 15:00:52 -0800456 """Pulls the CHROMEOS_RELEASE_VERSION string from /etc/lsb-release."""
457 return self._run('grep CHROMEOS_RELEASE_VERSION'
458 ' /etc/lsb-release').stdout.split('=')[1].strip()