OLD | NEW |
1 # Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2009 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 from buildutil import BuildObject | 5 from buildutil import BuildObject |
6 from xml.dom import minidom | 6 from xml.dom import minidom |
7 | 7 |
8 import os | 8 import os |
9 import shutil | 9 import shutil |
10 import sys | 10 import sys |
11 import web | 11 import web |
12 | 12 |
13 class Autoupdate(BuildObject): | 13 class Autoupdate(BuildObject): |
14 # Basic functionality of handling ChromeOS autoupdate pings | 14 # Basic functionality of handling ChromeOS autoupdate pings |
15 # and building/serving update images. | 15 # and building/serving update images. |
16 # TODO(rtc): Clean this code up and write some tests. | 16 # TODO(rtc): Clean this code up and write some tests. |
17 | 17 |
18 def __init__(self, serve_only=None, test_image=False, urlbase=None, | 18 def __init__(self, serve_only=None, test_image=False, urlbase=None, |
19 factory_config_path=None, validate_factory_config=None, | 19 factory_config_path=None, validate_factory_config=None, |
20 *args, **kwargs): | 20 *args, **kwargs): |
21 super(Autoupdate, self).__init__(*args, **kwargs) | 21 super(Autoupdate, self).__init__(*args, **kwargs) |
22 self.serve_only = serve_only | 22 self.serve_only = serve_only |
23 self.test_image=test_image | 23 self.test_image = test_image |
24 self.static_urlbase = urlbase | 24 self.static_urlbase = urlbase |
25 if serve_only: | 25 if serve_only: |
26 # If we're serving out of an archived build dir (e.g. a | 26 # If we're serving out of an archived build dir (e.g. a |
27 # buildbot), prepare this webserver's magic 'static/' dir with a | 27 # buildbot), prepare this webserver's magic 'static/' dir with a |
28 # link to the build archive. | 28 # link to the build archive. |
29 web.debug('Autoupdate in "serve update images only" mode.') | 29 web.debug('Autoupdate in "serve update images only" mode.') |
30 if os.path.exists('static/archive'): | 30 if os.path.exists('static/archive'): |
31 archive_symlink = os.readlink('static/archive') | 31 archive_symlink = os.readlink('static/archive') |
32 if archive_symlink != self.static_dir: | 32 if archive_symlink != self.static_dir: |
33 web.debug('removing stale symlink to %s' % self.static_dir) | 33 web.debug('removing stale symlink to %s' % self.static_dir) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 return latest_version.split('-')[0] | 77 return latest_version.split('-')[0] |
78 | 78 |
79 def CanUpdate(self, client_version, latest_version): | 79 def CanUpdate(self, client_version, latest_version): |
80 """ | 80 """ |
81 Returns true iff the latest_version is greater than the client_version. | 81 Returns true iff the latest_version is greater than the client_version. |
82 """ | 82 """ |
83 client_tokens = client_version.split('.') | 83 client_tokens = client_version.split('.') |
84 latest_tokens = latest_version.split('.') | 84 latest_tokens = latest_version.split('.') |
85 web.debug('client version %s latest version %s' \ | 85 web.debug('client version %s latest version %s' \ |
86 % (client_version, latest_version)) | 86 % (client_version, latest_version)) |
87 for i in range(0,4): | 87 for i in range(4): |
88 if int(latest_tokens[i]) == int(client_tokens[i]): | 88 if int(latest_tokens[i]) == int(client_tokens[i]): |
89 continue | 89 continue |
90 return int(latest_tokens[i]) > int(client_tokens[i]) | 90 return int(latest_tokens[i]) > int(client_tokens[i]) |
91 return False | 91 return False |
92 | 92 |
93 def UnpackImage(self, image_path, kernel_file, rootfs_file): | 93 def UnpackImage(self, image_path, image_file, stateful_file, kernel_file, root
fs_file): |
94 if os.path.exists(rootfs_file) and os.path.exists(kernel_file): | 94 unpack_command = 'cd %s && ./unpack_partitions.sh %s' % \ |
| 95 (image_path, image_file) |
| 96 if os.system(unpack_command) == 0: |
| 97 shutil.move(os.path.join(image_path, 'part_1'), stateful_file) |
| 98 shutil.move(os.path.join(image_path, 'part_2'), kernel_file) |
| 99 shutil.move(os.path.join(image_path, 'part_3'), rootfs_file) |
| 100 os.system('cd %s && rm part_*' % image_path) |
95 return True | 101 return True |
| 102 return False |
| 103 |
| 104 def UnpackZip(self, image_path, image_file): |
| 105 return os.system('cd %s && unzip -o image.zip %s unpack_partitions.sh' % \ |
| 106 (image_path, image_file)) == 0 |
| 107 |
| 108 def GetImageBinPath(self, image_path): |
96 if self.test_image: | 109 if self.test_image: |
97 image_file = 'chromiumos_test_image.bin' | 110 image_file = 'chromiumos_test_image.bin' |
98 else: | 111 else: |
99 image_file = 'chromiumos_image.bin' | 112 image_file = 'chromiumos_image.bin' |
| 113 return image_file |
| 114 |
| 115 def BuildUpdateImage(self, image_path): |
| 116 stateful_file = '%s/stateful.image' % image_path |
| 117 kernel_file = '%s/kernel.image' % image_path |
| 118 rootfs_file = '%s/rootfs.image' % image_path |
| 119 |
| 120 image_file = self.GetImageBinPath(image_path) |
| 121 bin_path = os.path.join(image_path, image_file) |
| 122 |
| 123 # Get appropriate update.gz to compare timestamps. |
100 if self.serve_only: | 124 if self.serve_only: |
101 err = os.system('cd %s && unzip -o image.zip %s unpack_partitions.sh' % | 125 cached_update_file = os.path.join(image_path, 'update.gz') |
102 (image_path, image_file)) | 126 else: |
103 if err: | 127 cached_update_file = os.path.join(self.static_dir, 'update.gz') |
| 128 |
| 129 # Check whether we need to re-create if the original image is newer. |
| 130 if (os.path.exists(cached_update_file) and |
| 131 os.path.getmtime(cached_update_file) >= os.path.getmtime(bin_path)): |
| 132 web.debug('Using cached update image at %s instead of %s' % |
| 133 (cached_update_file, bin_path)) |
| 134 else: |
| 135 # Unpack zip file if we are serving from a directory. |
| 136 if self.serve_only and not self.UnpackZip(image_path, image_file): |
104 web.debug('unzip image.zip failed.') | 137 web.debug('unzip image.zip failed.') |
105 return False | 138 return False |
106 | 139 |
107 os.system('rm -f %s/part_*' % image_path) | 140 if not self.UnpackImage(image_path, image_file, stateful_file, |
108 os.system('cd %s && ./unpack_partitions.sh %s' % (image_path, image_file)) | 141 kernel_file, rootfs_file): |
109 shutil.move(os.path.join(image_path, 'part_2'), kernel_file) | 142 web.debug('Failed to unpack image.') |
110 shutil.move(os.path.join(image_path, 'part_3'), rootfs_file) | 143 return False |
111 os.system('rm -f %s/part_*' % image_path) | |
112 return True | |
113 | 144 |
114 def BuildUpdateImage(self, image_path): | 145 update_file = os.path.join(image_path, 'update.gz') |
115 kernel_file = '%s/kernel.image' % image_path | 146 web.debug('Generating update image %s' % update_file) |
116 rootfs_file = '%s/rootfs.image' % image_path | 147 mkupdate_command = '%s/mk_memento_images.sh %s %s' % \ |
| 148 (self.scripts_dir, kernel_file, rootfs_file) |
| 149 if os.system(mkupdate_command) != 0: |
| 150 web.debug('Failed to create update image') |
| 151 return False |
117 | 152 |
118 if not self.UnpackImage(image_path, kernel_file, rootfs_file): | 153 mkstatefulupdate_command = 'gzip %s' % stateful_file |
119 web.debug('failed to unpack image.') | 154 if os.system(mkstatefulupdate_command) != 0: |
120 return False | 155 web.debug('Failed to create stateful update image') |
| 156 return False |
121 | 157 |
122 update_file = '%s/update.gz' % image_path | 158 # Add gz suffix |
123 if (os.path.exists(update_file) and | 159 stateful_file = '%s.gz' % stateful_file |
124 os.path.getmtime(update_file) >= os.path.getmtime(rootfs_file)): | 160 |
125 web.debug('Found cached update image %s/update.gz' % image_path) | 161 # Cleanup of image files |
126 else: | 162 os.remove(kernel_file) |
127 web.debug('generating update image %s' % update_file) | 163 os.remove(rootfs_file) |
128 mkupdate = ('%s/mk_memento_images.sh %s %s' % | 164 if not self.serve_only: |
129 (self.scripts_dir, kernel_file, rootfs_file)) | 165 try: |
130 web.debug(mkupdate) | 166 web.debug('Found a new image to serve, copying it to static') |
131 err = os.system(mkupdate) | 167 shutil.copy(update_file, self.static_dir) |
132 if err != 0: | 168 shutil.copy(stateful_file, self.static_dir) |
133 web.debug('failed to create update image') | 169 os.remove(update_file) |
134 return False | 170 os.remove(stateful_file) |
135 if not self.serve_only: | 171 except Exception, e: |
136 web.debug('Found an image, copying it to static') | 172 web.debug('%s' % e) |
137 try: | 173 return False |
138 shutil.copy(update_file, self.static_dir) | |
139 except Exception, e: | |
140 web.debug('Unable to copy %s to %s' % (update_file, self.static_dir)) | |
141 return False | |
142 return True | 174 return True |
143 | 175 |
144 def GetSize(self, update_path): | 176 def GetSize(self, update_path): |
145 return os.path.getsize(update_path) | 177 return os.path.getsize(update_path) |
146 | 178 |
147 def GetHash(self, update_path): | 179 def GetHash(self, update_path): |
148 cmd = "cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';" \ | 180 cmd = "cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';" \ |
149 % update_path | 181 % update_path |
150 return os.popen(cmd).read().rstrip() | 182 return os.popen(cmd).read().rstrip() |
151 | 183 |
(...skipping 19 matching lines...) Expand all Loading... |
171 'release_image': '6-release.gz', | 203 'release_image': '6-release.gz', |
172 'release_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=', | 204 'release_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=', |
173 'oempartitionimg_image': '6-oem.gz', | 205 'oempartitionimg_image': '6-oem.gz', |
174 'oempartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=', | 206 'oempartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=', |
175 'stateimg_image': '6-state.gz', | 207 'stateimg_image': '6-state.gz', |
176 'stateimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=' | 208 'stateimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=' |
177 }, | 209 }, |
178 ] | 210 ] |
179 The server will look for the files by name in the static files | 211 The server will look for the files by name in the static files |
180 directory. | 212 directory. |
181 | 213 |
182 If validate_checksums is True, validates checksums and exits. If | 214 If validate_checksums is True, validates checksums and exits. If |
183 a checksum mismatch is found, it's printed to the screen. | 215 a checksum mismatch is found, it's printed to the screen. |
184 """ | 216 """ |
185 f = open(filename, 'r') | 217 f = open(filename, 'r') |
186 output = {} | 218 output = {} |
187 exec(f.read(), output) | 219 exec(f.read(), output) |
188 self.factory_config = output['config'] | 220 self.factory_config = output['config'] |
189 success = True | 221 success = True |
190 for stanza in self.factory_config: | 222 for stanza in self.factory_config: |
191 for kind in ('factory', 'oempartitionimg', 'release', 'stateimg'): | 223 for kind in ('factory', 'oempartitionimg', 'release', 'stateimg'): |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
260 # In case we configured images to be hosted elsewhere | 292 # In case we configured images to be hosted elsewhere |
261 # (e.g. buildbot's httpd), use that. Otherwise, serve it | 293 # (e.g. buildbot's httpd), use that. Otherwise, serve it |
262 # ourselves using web.py's static resource handler. | 294 # ourselves using web.py's static resource handler. |
263 if self.static_urlbase: | 295 if self.static_urlbase: |
264 urlbase = self.static_urlbase | 296 urlbase = self.static_urlbase |
265 else: | 297 else: |
266 urlbase = 'http://%s/static/archive/' % hostname | 298 urlbase = 'http://%s/static/archive/' % hostname |
267 | 299 |
268 url = '%s/%s/update.gz' % (urlbase, label) | 300 url = '%s/%s/update.gz' % (urlbase, label) |
269 return self.GetUpdatePayload(hash, size, url) | 301 return self.GetUpdatePayload(hash, size, url) |
270 web.debug( 'DONE') | 302 web.debug('DONE') |
271 else: | 303 else: |
272 web.debug('update found %s ' % latest_version) | 304 web.debug('update found %s ' % latest_version) |
273 ok = self.BuildUpdateImage(latest_image_path) | 305 ok = self.BuildUpdateImage(latest_image_path) |
274 if ok != True: | 306 if ok != True: |
275 web.debug('Failed to build an update image') | 307 web.debug('Failed to build an update image') |
276 return self.GetNoUpdatePayload() | 308 return self.GetNoUpdatePayload() |
277 | 309 |
278 hash = self.GetHash('%s/update.gz' % self.static_dir) | 310 hash = self.GetHash('%s/update.gz' % self.static_dir) |
279 size = self.GetSize('%s/update.gz' % self.static_dir) | 311 size = self.GetSize('%s/update.gz' % self.static_dir) |
280 | 312 |
281 url = 'http://%s/static/update.gz' % hostname | 313 url = 'http://%s/static/update.gz' % hostname |
282 return self.GetUpdatePayload(hash, size, url) | 314 return self.GetUpdatePayload(hash, size, url) |
OLD | NEW |