OLD | NEW |
1 # Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2009-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 from buildutil import BuildObject | 5 from buildutil import BuildObject |
6 from xml.dom import minidom | 6 from xml.dom import minidom |
7 | |
8 import cherrypy | 7 import cherrypy |
9 import os | 8 import os |
10 import shutil | 9 import shutil |
11 import socket | 10 import subprocess |
| 11 import tempfile |
12 import time | 12 import time |
13 | 13 |
| 14 |
14 def _LogMessage(message): | 15 def _LogMessage(message): |
15 cherrypy.log(message, 'UPDATE') | 16 cherrypy.log(message, 'UPDATE') |
16 | 17 |
17 | 18 |
18 class Autoupdate(BuildObject): | 19 class Autoupdate(BuildObject): |
19 """Class that contains functionality that handles Chrome OS update pings. | 20 """Class that contains functionality that handles Chrome OS update pings. |
20 | 21 |
21 Members: | 22 Members: |
22 serve_only: Serve images from a pre-built image.zip file. static_dir | 23 serve_only: Serve images from a pre-built image.zip file. static_dir |
23 must be set to the location of the image.zip. | 24 must be set to the location of the image.zip. |
(...skipping 17 matching lines...) Expand all Loading... |
41 self.urlbase = urlbase | 42 self.urlbase = urlbase |
42 else: | 43 else: |
43 self.urlbase = None | 44 self.urlbase = None |
44 | 45 |
45 self.client_prefix = client_prefix | 46 self.client_prefix = client_prefix |
46 self.forced_image = forced_image | 47 self.forced_image = forced_image |
47 self.use_cached = use_cached | 48 self.use_cached = use_cached |
48 self.src_image = src_image | 49 self.src_image = src_image |
49 self.vm = vm | 50 self.vm = vm |
50 self.board = board | 51 self.board = board |
| 52 self.crosutils = os.path.join(os.path.dirname(__file__), '../../scripts') |
51 | 53 |
52 def _GetSecondsSinceMidnight(self): | 54 def _GetSecondsSinceMidnight(self): |
53 """Returns the seconds since midnight as a decimal value.""" | 55 """Returns the seconds since midnight as a decimal value.""" |
54 now = time.localtime() | 56 now = time.localtime() |
55 return now[3] * 3600 + now[4] * 60 + now[5] | 57 return now[3] * 3600 + now[4] * 60 + now[5] |
56 | 58 |
57 def _GetDefaultBoardID(self): | 59 def _GetDefaultBoardID(self): |
58 """Returns the default board id stored in .default_board.""" | 60 """Returns the default board id stored in .default_board.""" |
59 board_file = '%s/.default_board' % (self.scripts_dir) | 61 board_file = '%s/.default_board' % (self.scripts_dir) |
60 try: | 62 try: |
(...skipping 16 matching lines...) Expand all Loading... |
77 client_tokens = client_version.replace('_', '').split('.') | 79 client_tokens = client_version.replace('_', '').split('.') |
78 latest_tokens = latest_version.replace('_', '').split('.') | 80 latest_tokens = latest_version.replace('_', '').split('.') |
79 _LogMessage('client version %s latest version %s' | 81 _LogMessage('client version %s latest version %s' |
80 % (client_version, latest_version)) | 82 % (client_version, latest_version)) |
81 for i in range(4): | 83 for i in range(4): |
82 if int(latest_tokens[i]) == int(client_tokens[i]): | 84 if int(latest_tokens[i]) == int(client_tokens[i]): |
83 continue | 85 continue |
84 return int(latest_tokens[i]) > int(client_tokens[i]) | 86 return int(latest_tokens[i]) > int(client_tokens[i]) |
85 return False | 87 return False |
86 | 88 |
87 def _UnpackStatefulPartition(self, image_path, stateful_file): | |
88 """Given an image, unpacks its stateful partition to stateful_file.""" | |
89 image_dir = os.path.dirname(image_path) | |
90 image_file = os.path.basename(image_path) | |
91 | |
92 get_offset = '$(cgpt show -b -i 1 %s)' % image_file | |
93 get_size = '$(cgpt show -s -i 1 %s)' % image_file | |
94 unpack_command = ( | |
95 'cd %s && ' | |
96 'dd if=%s of=%s bs=512 skip=%s count=%s' % (image_dir, image_file, | |
97 stateful_file, get_offset, | |
98 get_size)) | |
99 _LogMessage(unpack_command) | |
100 return os.system(unpack_command) == 0 | |
101 | |
102 def _UnpackZip(self, image_dir): | 89 def _UnpackZip(self, image_dir): |
103 """Unpacks an image.zip into a given directory.""" | 90 """Unpacks an image.zip into a given directory.""" |
104 image = os.path.join(image_dir, self._GetImageName()) | 91 image = os.path.join(image_dir, self._GetImageName()) |
105 if os.path.exists(image): | 92 if os.path.exists(image): |
106 return True | 93 return True |
107 else: | 94 else: |
108 # -n, never clobber an existing file, in case we get invoked | 95 # -n, never clobber an existing file, in case we get invoked |
109 # simultaneously by multiple request handlers. This means that | 96 # simultaneously by multiple request handlers. This means that |
110 # we're assuming each image.zip file lives in a versioned | 97 # we're assuming each image.zip file lives in a versioned |
111 # directory (a la Buildbot). | 98 # directory (a la Buildbot). |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 return update_path | 228 return update_path |
242 | 229 |
243 def GenerateStatefulFile(self, image_path): | 230 def GenerateStatefulFile(self, image_path): |
244 """Generates a stateful update gz given a full path to an image. | 231 """Generates a stateful update gz given a full path to an image. |
245 | 232 |
246 Args: | 233 Args: |
247 image_path: Full path to image. | 234 image_path: Full path to image. |
248 Returns: | 235 Returns: |
249 Path to created stateful update_payload or None on error. | 236 Path to created stateful update_payload or None on error. |
250 """ | 237 """ |
251 stateful_partition_path = '%s/stateful.image' % os.path.dirname(image_path) | 238 _LogMessage('Generating stateful update file.') |
| 239 from_dir = os.path.dirname(image_path) |
| 240 image = os.path.basename(image_path) |
| 241 output_gz = os.path.join(from_dir, 'stateful.tgz') |
252 | 242 |
253 # Unpack to get stateful partition. | 243 # Temporary directories for this function. |
254 if self._UnpackStatefulPartition(image_path, stateful_partition_path): | 244 rootfs_dir = tempfile.mkdtemp(suffix='rootfs', prefix='tmp') |
255 mkstatefulupdate_command = 'gzip -f %s' % stateful_partition_path | 245 stateful_dir = tempfile.mkdtemp(suffix='stateful', prefix='tmp') |
256 if os.system(mkstatefulupdate_command) == 0: | |
257 _LogMessage('Successfully generated %s.gz' % stateful_partition_path) | |
258 return '%s.gz' % stateful_partition_path | |
259 | 246 |
260 _LogMessage('Failed to create stateful update file') | 247 # Mount the image to pull out the important directories. |
261 return None | 248 try: |
| 249 # Only need stateful partition, but this saves us having to manage our |
| 250 # own loopback device. |
| 251 subprocess.check_call(['%s/mount_gpt_image.sh' % self.crosutils, |
| 252 '--from=%s' % from_dir, |
| 253 '--image=%s' % image, |
| 254 '--read_only', |
| 255 '--rootfs_mountpt=%s' % rootfs_dir, |
| 256 '--stateful_mountpt=%s' % stateful_dir, |
| 257 ]) |
| 258 _LogMessage('Tarring up /usr/local and /var!') |
| 259 subprocess.check_call(['sudo', |
| 260 'tar', |
| 261 '-czf', |
| 262 output_gz, |
| 263 '--directory=%s' % stateful_dir, |
| 264 'dev_image', |
| 265 'var', |
| 266 ]) |
| 267 except: |
| 268 _LogMessage('Failed to create stateful update file') |
| 269 raise |
| 270 finally: |
| 271 # Unmount best effort regardless. |
| 272 subprocess.call(['%s/mount_gpt_image.sh' % self.crosutils, |
| 273 '--unmount', |
| 274 '--rootfs_mountpt=%s' % rootfs_dir, |
| 275 '--stateful_mountpt=%s' % stateful_dir, |
| 276 ]) |
| 277 # Clean up our directories. |
| 278 os.rmdir(rootfs_dir) |
| 279 os.rmdir(stateful_dir) |
| 280 |
| 281 _LogMessage('Successfully generated %s' % output_gz) |
| 282 return output_gz |
262 | 283 |
263 def MoveImagesToStaticDir(self, update_path, stateful_update_path, | 284 def MoveImagesToStaticDir(self, update_path, stateful_update_path, |
264 static_image_dir): | 285 static_image_dir): |
265 """Moves gz files from their directories to serving directories. | 286 """Moves gz files from their directories to serving directories. |
266 | 287 |
267 Args: | 288 Args: |
268 update_path: full path to main update gz. | 289 update_path: full path to main update gz. |
269 stateful_update_path: full path to stateful partition gz. | 290 stateful_update_path: full path to stateful partition gz. |
270 static_image_dir: where to put files. | 291 static_image_dir: where to put files. |
271 Returns: | 292 Returns: |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
567 is_delta_format = self._IsDeltaFormatFile(filename) | 588 is_delta_format = self._IsDeltaFormatFile(filename) |
568 if label: | 589 if label: |
569 url = '%s/%s/update.gz' % (static_urlbase, label) | 590 url = '%s/%s/update.gz' % (static_urlbase, label) |
570 else: | 591 else: |
571 url = '%s/update.gz' % static_urlbase | 592 url = '%s/update.gz' % static_urlbase |
572 | 593 |
573 _LogMessage('Responding to client to use url %s to get image.' % url) | 594 _LogMessage('Responding to client to use url %s to get image.' % url) |
574 return self.GetUpdatePayload(hash, sha256, size, url, is_delta_format) | 595 return self.GetUpdatePayload(hash, sha256, size, url, is_delta_format) |
575 else: | 596 else: |
576 return self.GetNoUpdatePayload() | 597 return self.GetNoUpdatePayload() |
OLD | NEW |