OLD | NEW |
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import httplib | 5 import httplib |
6 import logging | 6 import logging |
7 import re | 7 import re |
8 import socket | 8 import socket |
9 import urlparse | 9 import urlparse |
10 | 10 |
| 11 from autotest_lib.client.bin import chromeos_constants |
11 from autotest_lib.client.common_lib import error | 12 from autotest_lib.client.common_lib import error |
12 | 13 |
13 STATEFULDEV_UPDATER = '/usr/local/bin/stateful_update' | 14 STATEFULDEV_UPDATER = '/usr/local/bin/stateful_update' |
14 UPDATER_BIN = '/usr/bin/update_engine_client' | 15 UPDATER_BIN = '/usr/bin/update_engine_client' |
15 UPDATER_IDLE = 'UPDATE_STATUS_IDLE' | 16 UPDATER_IDLE = 'UPDATE_STATUS_IDLE' |
16 UPDATER_NEED_REBOOT = 'UPDATE_STATUS_UPDATED_NEED_REBOOT' | 17 UPDATER_NEED_REBOOT = 'UPDATE_STATUS_UPDATED_NEED_REBOOT' |
17 | 18 |
18 | 19 |
19 class ChromiumOSError(error.InstallError): | 20 class ChromiumOSError(error.InstallError): |
20 """Generic error for ChromiumOS-specific exceptions.""" | 21 """Generic error for ChromiumOS-specific exceptions.""" |
(...skipping 14 matching lines...) Expand all Loading... |
35 | 36 |
36 | 37 |
37 def check_update_status(self): | 38 def check_update_status(self): |
38 update_status_cmd = ' '.join([UPDATER_BIN, '-status', '2>&1', | 39 update_status_cmd = ' '.join([UPDATER_BIN, '-status', '2>&1', |
39 '| grep CURRENT_OP']) | 40 '| grep CURRENT_OP']) |
40 update_status = self._run(update_status_cmd) | 41 update_status = self._run(update_status_cmd) |
41 return update_status.stdout.strip().split('=')[-1] | 42 return update_status.stdout.strip().split('=')[-1] |
42 | 43 |
43 | 44 |
44 def reset_update_engine(self): | 45 def reset_update_engine(self): |
45 self._run('initctl stop update-engine') | 46 logging.info('Resetting update-engine.') |
46 self._run('rm -f /tmp/update_engine_autoupdate_completed') | 47 self._run('rm -f /tmp/update_engine_autoupdate_completed') |
| 48 try: |
| 49 self._run('initctl stop update-engine') |
| 50 except error.AutoservRunError, e: |
| 51 logging.warn('Stopping update-engine service failed. Already dead?') |
47 self._run('initctl start update-engine') | 52 self._run('initctl start update-engine') |
48 # May need to wait if service becomes slow to restart. | 53 # May need to wait if service becomes slow to restart. |
49 if self.check_update_status() != UPDATER_IDLE: | 54 if self.check_update_status() != UPDATER_IDLE: |
50 raise ChromiumOSError('%s is not in an installable state' % | 55 raise ChromiumOSError('%s is not in an installable state' % |
51 self.host.hostname) | 56 self.host.hostname) |
52 | 57 |
53 | 58 |
54 def _run(self, cmd, *args, **kwargs): | 59 def _run(self, cmd, *args, **kwargs): |
55 return self.host.run(cmd, *args, **kwargs) | 60 return self.host.run(cmd, *args, **kwargs) |
56 | 61 |
57 | 62 |
| 63 def rootdev(self): |
| 64 return self._run('rootdev').stdout.strip() |
| 65 |
| 66 |
| 67 def revert_boot_partition(self): |
| 68 part = self.rootdev() |
| 69 logging.warn('Reverting update; Boot partition will be %s', part) |
| 70 return self._run('/postinst %s 2>&1' % part) |
| 71 |
| 72 |
58 def run_update(self): | 73 def run_update(self): |
59 # TODO(seano): Retrieve update_engine.log from target host. | |
60 if not self.update_url: | 74 if not self.update_url: |
61 return False | 75 return False |
62 | 76 |
63 # Check that devserver is accepting connections (from autoserv's host) | 77 # Check that devserver is accepting connections (from autoserv's host) |
64 # If we can't talk to it, the machine host probably can't either. | 78 # If we can't talk to it, the machine host probably can't either. |
65 auserver_host = urlparse.urlparse(self.update_url)[1] | 79 auserver_host = urlparse.urlparse(self.update_url)[1] |
66 try: | 80 try: |
67 httplib.HTTPConnection(auserver_host).connect() | 81 httplib.HTTPConnection(auserver_host).connect() |
68 except socket.error: | 82 except socket.error: |
69 raise ChromiumOSError('Update server at %s not available' % | 83 raise ChromiumOSError('Update server at %s not available' % |
70 auserver_host) | 84 auserver_host) |
71 | 85 |
72 logging.info('Installing from %s to: %s' % (self.update_url, | 86 logging.info('Installing from %s to: %s' % (self.update_url, |
73 self.host.hostname)) | 87 self.host.hostname)) |
74 # If we find the system an updated-but-not-rebooted state, | 88 # Reset update_engine's state & check that update_engine is idle. |
75 # that's probably bad and we shouldn't trust that the previous | 89 self.reset_update_engine() |
76 # update left the machine in a good state. Reset update_engine's | |
77 # state & ensure that update_engine is idle. | |
78 if self.check_update_status() != UPDATER_IDLE: | |
79 self.reset_update_engine() | |
80 | 90 |
81 # Run autoupdate command. This tells the autoupdate process on | 91 # Run autoupdate command. This tells the autoupdate process on |
82 # the host to look for an update at a specific URL and version | 92 # the host to look for an update at a specific URL and version |
83 # string. | 93 # string. |
84 autoupdate_cmd = ' '.join([UPDATER_BIN, | 94 autoupdate_cmd = ' '.join([UPDATER_BIN, |
85 '--update', | 95 '--update', |
86 '--omaha_url=%s' % self.update_url, | 96 '--omaha_url=%s' % self.update_url, |
87 '--app_version ForcedUpdate', | |
88 ' 2>&1']) | 97 ' 2>&1']) |
89 logging.info(autoupdate_cmd) | 98 logging.info(autoupdate_cmd) |
90 try: | 99 try: |
91 self._run(autoupdate_cmd, timeout=900) | 100 self._run(autoupdate_cmd, timeout=900) |
92 except error.AutoservRunError, e: | 101 except error.AutoservRunError, e: |
93 # Either a runtime error occurred on the host, or | 102 # Either a runtime error occurred on the host, or |
94 # update_engine_client exited with > 0. | 103 # update_engine_client exited with > 0. |
95 raise ChromiumOSError('update_engine failed on %s' % | 104 raise ChromiumOSError('update_engine failed on %s' % |
96 self.host.hostname) | 105 self.host.hostname) |
97 | 106 |
98 # Check that the installer completed as expected. | 107 # Check that the installer completed as expected. |
99 status = self.check_update_status() | 108 status = self.check_update_status() |
100 if status != UPDATER_NEED_REBOOT: | 109 if status != UPDATER_NEED_REBOOT: |
101 # TODO(seano): should we aggressively reset update-engine here? | |
102 raise ChromiumOSError('update-engine error on %s: ' | 110 raise ChromiumOSError('update-engine error on %s: ' |
103 '"%s" from update-engine' % | 111 '"%s" from update-engine' % |
104 (self.host.hostname, status)) | 112 (self.host.hostname, status)) |
105 | 113 |
106 # Attempt dev & test tools update (which don't live on the | 114 # Attempt dev & test tools update (which don't live on the |
107 # rootfs). This must succeed so that the newly installed host | 115 # rootfs). This must succeed so that the newly installed host |
108 # is testable after we run the autoupdater. | 116 # is testable after we run the autoupdater. |
109 statefuldev_url = self.update_url.replace('update', 'static/archive') | 117 statefuldev_url = self.update_url.replace('update', 'static/archive') |
110 | 118 |
111 statefuldev_cmd = ' '.join([STATEFULDEV_UPDATER, statefuldev_url, | 119 statefuldev_cmd = ' '.join([STATEFULDEV_UPDATER, statefuldev_url, |
112 '2>&1']) | 120 '2>&1']) |
113 logging.info(statefuldev_cmd) | 121 logging.info(statefuldev_cmd) |
114 try: | 122 try: |
115 self._run(statefuldev_cmd, timeout=600) | 123 self._run(statefuldev_cmd, timeout=600) |
116 except error.AutoservRunError, e: | 124 except error.AutoservRunError, e: |
117 # TODO(seano): If statefuldev update failed, we must mark | 125 # TODO(seano): If statefuldev update failed, we must mark |
118 # the update as failed, and keep the same rootfs after | 126 # the update as failed, and keep the same rootfs after |
119 # reboot. | 127 # reboot. |
120 raise ChromiumOSError('stateful_update failed on %s' % | 128 self.revert_boot_partition() |
| 129 raise ChromiumOSError('stateful_update failed on %s.' % |
121 self.host.hostname) | 130 self.host.hostname) |
122 return True | 131 return True |
123 | 132 |
124 | 133 |
125 def check_version(self): | 134 def check_version(self): |
126 booted_version = self.get_build_id() | 135 booted_version = self.get_build_id() |
127 if booted_version != self.update_version: | 136 if not booted_version in self.update_version: |
128 logging.error('Expected Chromium OS version: %s.' | 137 logging.error('Expected Chromium OS version: %s.' |
129 'Found Chromium OS %s', | 138 'Found Chromium OS %s', |
130 (self.update_version, booted_version)) | 139 self.update_version, booted_version) |
131 raise ChromiumOSError('Updater failed on host %s' % | 140 raise ChromiumOSError('Updater failed on host %s' % |
132 self.host.hostname) | 141 self.host.hostname) |
133 else: | 142 else: |
134 return True | 143 return True |
135 | 144 |
136 | 145 |
137 def get_build_id(self): | 146 def get_build_id(self): |
138 """Turns the CHROMEOS_RELEASE_DESCRIPTION into a string that | 147 """Turns the CHROMEOS_RELEASE_DESCRIPTION into a string that |
139 matches the build ID.""" | 148 matches the build ID.""" |
140 # TODO(seano): handle dev build naming schemes. | 149 # TODO(seano): handle dev build naming schemes. |
141 version = self._run('grep CHROMEOS_RELEASE_DESCRIPTION' | 150 version = self._run('grep CHROMEOS_RELEASE_DESCRIPTION' |
142 ' /etc/lsb-release').stdout | 151 ' /etc/lsb-release').stdout |
143 build_re = (r'CHROMEOS_RELEASE_DESCRIPTION=' | 152 build_re = (r'CHROMEOS_RELEASE_DESCRIPTION=' |
144 '(\d+\.\d+\.\d+\.\d+) \(\w+ \w+ (\w+)(.*)\)') | 153 '(\d+\.\d+\.\d+\.\d+) \(\w+ \w+ (\w+)(.*)\)') |
145 version_match = re.match(build_re, version) | 154 version_match = re.match(build_re, version) |
146 if not version_match: | 155 if not version_match: |
147 raise ChromiumOSError('Unable to get build ID from %s. Found "%s"', | 156 raise ChromiumOSError('Unable to get build ID from %s. Found "%s"', |
148 self.host.hostname, version) | 157 self.host.hostname, version) |
149 version, build_id, builder = version_match.groups() | 158 version, build_id, builder = version_match.groups() |
150 # Continuous builds have an extra "builder number" on the end. | |
151 # Report it if this looks like one. | |
152 build_match = re.match(r'.*: (\d+)', builder) | 159 build_match = re.match(r'.*: (\d+)', builder) |
153 if build_match: | 160 if build_match: |
154 builder_num = '-b%s' % build_match.group(1) | 161 builder_num = '-b%s' % build_match.group(1) |
155 else: | 162 else: |
156 builder_num = '' | 163 builder_num = '' |
157 return '%s-r%s%s' % (version, build_id, builder_num) | 164 return '%s-r%s%s' % (version, build_id, builder_num) |
OLD | NEW |