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 | 7 |
8 import os | 8 import os |
9 import shutil | 9 import shutil |
10 import time | 10 import time |
11 import web | 11 import web |
12 | 12 |
13 | 13 |
14 class Autoupdate(BuildObject): | 14 class Autoupdate(BuildObject): |
15 """Class that contains functionality that handles Chrome OS update pings. | 15 """Class that contains functionality that handles Chrome OS update pings. |
16 | 16 |
17 Members: | 17 Members: |
18 serve_only: Serve images from a pre-built image.zip file. static_dir | 18 serve_only: Serve images from a pre-built image.zip file. static_dir |
19 must be set to the location of the image.zip. | 19 must be set to the location of the image.zip. |
20 factory_config: Path to the factory config file if handling factory | 20 factory_config: Path to the factory config file if handling factory |
21 requests. | 21 requests. |
22 use_test_image: Use chromiumos_test_image.bin rather than the standard. | 22 use_test_image: Use chromiumos_test_image.bin rather than the standard. |
23 static_url_base: base URL, other than devserver, for update images. | 23 static_url_base: base URL, other than devserver, for update images. |
24 client_prefix: The prefix for the update engine client. | 24 client_prefix: The prefix for the update engine client. |
25 forced_image: Path to an image to use for all updates. | 25 forced_image: Path to an image to use for all updates. |
26 """ | 26 """ |
27 | 27 |
28 def __init__(self, serve_only=None, test_image=False, urlbase=None, | 28 def __init__(self, serve_only=None, test_image=False, urlbase=None, |
29 factory_config_path=None, client_prefix=None, forced_image=None, | 29 factory_config_path=None, client_prefix=None, forced_image=None, |
30 *args, **kwargs): | 30 use_cached=False, *args, **kwargs): |
31 super(Autoupdate, self).__init__(*args, **kwargs) | 31 super(Autoupdate, self).__init__(*args, **kwargs) |
32 self.serve_only = serve_only | 32 self.serve_only = serve_only |
33 self.factory_config = factory_config_path | 33 self.factory_config = factory_config_path |
34 self.use_test_image = test_image | 34 self.use_test_image = test_image |
35 self.static_urlbase = urlbase | 35 if urlbase: |
| 36 self.static_urlbase = urlbase |
| 37 elif self.serve_only: |
| 38 self.static_urlbase = 'http://%(host)s/static/archive' |
| 39 else: |
| 40 self.static_urlbase = 'http://%(host)s/static' |
| 41 |
36 self.client_prefix = client_prefix | 42 self.client_prefix = client_prefix |
37 self.forced_image = forced_image | 43 self.forced_image = forced_image |
| 44 self.use_cached = use_cached |
38 | 45 |
39 def _GetSecondsSinceMidnight(self): | 46 def _GetSecondsSinceMidnight(self): |
40 """Returns the seconds since midnight as a decimal value.""" | 47 """Returns the seconds since midnight as a decimal value.""" |
41 now = time.localtime() | 48 now = time.localtime() |
42 return now[3] * 3600 + now[4] * 60 + now[5] | 49 return now[3] * 3600 + now[4] * 60 + now[5] |
43 | 50 |
44 def _GetDefaultBoardID(self): | 51 def _GetDefaultBoardID(self): |
45 """Returns the default board id stored in .default_board.""" | 52 """Returns the default board id stored in .default_board.""" |
46 board_file = '%s/.default_board' % (self.scripts_dir) | 53 board_file = '%s/.default_board' % (self.scripts_dir) |
47 try: | 54 try: |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
102 """Returns the name of the image that should be used.""" | 109 """Returns the name of the image that should be used.""" |
103 if self.use_test_image: | 110 if self.use_test_image: |
104 image_name = 'chromiumos_test_image.bin' | 111 image_name = 'chromiumos_test_image.bin' |
105 else: | 112 else: |
106 image_name = 'chromiumos_image.bin' | 113 image_name = 'chromiumos_image.bin' |
107 return image_name | 114 return image_name |
108 | 115 |
109 def _IsImageNewerThanCached(self, image_path, cached_file_path): | 116 def _IsImageNewerThanCached(self, image_path, cached_file_path): |
110 """Returns true if the image is newer than the cached image.""" | 117 """Returns true if the image is newer than the cached image.""" |
111 if os.path.exists(cached_file_path) and os.path.exists(image_path): | 118 if os.path.exists(cached_file_path) and os.path.exists(image_path): |
112 web.debug('Usable cached image found.') | 119 web.debug('Usable cached image found at %s.' % cached_file_path) |
113 return os.path.getmtime(image_path) > os.path.getmtime(cached_file_path) | 120 return os.path.getmtime(image_path) > os.path.getmtime(cached_file_path) |
114 elif not os.path.exists(cached_file_path) and not os.path.exists(image_path)
: | 121 elif not os.path.exists(cached_file_path) and not os.path.exists(image_path)
: |
115 raise Exception('Image does not exist and cached image missing') | 122 raise Exception('Image does not exist and cached image missing') |
116 else: | 123 else: |
117 # Only one is missing, figure out which one. | 124 # Only one is missing, figure out which one. |
118 if os.path.exists(image_path): | 125 if os.path.exists(image_path): |
119 web.debug('No cached image found - image generation required.') | 126 web.debug('No cached image found - image generation required.') |
120 return True | 127 return True |
121 else: | 128 else: |
122 web.debug('Only cached image found to serve.') | 129 web.debug('Cached image found to serve at %s.' % cached_file_path) |
123 return False | 130 return False |
124 | 131 |
125 def _GetSize(self, update_path): | 132 def _GetSize(self, update_path): |
126 """Returns the size of the file given.""" | 133 """Returns the size of the file given.""" |
127 return os.path.getsize(update_path) | 134 return os.path.getsize(update_path) |
128 | 135 |
129 def _GetHash(self, update_path): | 136 def _GetHash(self, update_path): |
130 """Returns the sha1 of the file given.""" | 137 """Returns the sha1 of the file given.""" |
131 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';' | 138 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';' |
132 % update_path) | 139 % update_path) |
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
433 Args: | 440 Args: |
434 data: xml blob from client. | 441 data: xml blob from client. |
435 label: optional label for the update. | 442 label: optional label for the update. |
436 Returns: | 443 Returns: |
437 Update payload message for client. | 444 Update payload message for client. |
438 """ | 445 """ |
439 web.debug('handling update ping: %s' % data) | 446 web.debug('handling update ping: %s' % data) |
440 update_dom = minidom.parseString(data) | 447 update_dom = minidom.parseString(data) |
441 root = update_dom.firstChild | 448 root = update_dom.firstChild |
442 | 449 |
| 450 # Parse host if not done yet. |
| 451 if '%(host)' in self.static_urlbase: |
| 452 self.static_urlbase = self.static_urlbase % {'host' : web.ctx.host} |
| 453 |
443 # Check the client prefix to make sure you can support this type of update. | 454 # Check the client prefix to make sure you can support this type of update. |
444 if (root.hasAttribute('updaterversion') and | 455 if (root.hasAttribute('updaterversion') and |
445 not root.getAttribute('updaterversion').startswith(self.client_prefix)): | 456 not root.getAttribute('updaterversion').startswith(self.client_prefix)): |
446 web.debug('Got update from unsupported updater:' + | 457 web.debug('Got update from unsupported updater:' + |
447 root.getAttribute('updaterversion')) | 458 root.getAttribute('updaterversion')) |
448 return self.GetNoUpdatePayload() | 459 return self.GetNoUpdatePayload() |
449 | 460 |
450 # We only generate update payloads for updatecheck requests. | 461 # We only generate update payloads for updatecheck requests. |
451 update_check = root.getElementsByTagName('o:updatecheck') | 462 update_check = root.getElementsByTagName('o:updatecheck') |
452 if not update_check: | 463 if not update_check: |
(...skipping 12 matching lines...) Expand all Loading... |
465 | 476 |
466 # Separate logic as Factory requests have static url's that override | 477 # Separate logic as Factory requests have static url's that override |
467 # other options. | 478 # other options. |
468 if self.factory_config: | 479 if self.factory_config: |
469 return self.HandleFactoryRequest(hostname, board_id, channel) | 480 return self.HandleFactoryRequest(hostname, board_id, channel) |
470 else: | 481 else: |
471 static_image_dir = self.static_dir | 482 static_image_dir = self.static_dir |
472 if label: | 483 if label: |
473 static_image_dir = os.path.join(static_image_dir, label) | 484 static_image_dir = os.path.join(static_image_dir, label) |
474 | 485 |
475 # Not for factory, find and serve the correct image given the options. | 486 # Prefer cached image if it exists. |
476 if self.forced_image: | 487 if self.use_cached and os.path.exists(os.path.join(static_image_dir, |
477 has_built_image = self.GenerateUpdateImage( | 488 'update.gz')): |
478 self.forced_image, move_to_static_dir=True, | 489 web.debug('Using cached image regardless of timestamps.') |
479 static_image_dir=static_image_dir) | 490 has_built_image = True |
480 # Now that we've generated it, clear out so that other pings of same | |
481 # devserver instance do not generate new images. | |
482 self.forced_image = None | |
483 elif self.serve_only: | |
484 has_built_image = self.GenerateImageFromZip(static_image_dir) | |
485 else: | 491 else: |
486 has_built_image = self.GenerateLatestUpdateImage(board_id, | 492 if self.forced_image: |
487 client_version, | 493 has_built_image = self.GenerateUpdateImage( |
488 static_image_dir) | 494 self.forced_image, move_to_static_dir=True, |
| 495 static_image_dir=static_image_dir) |
| 496 # Now that we've generated it, clear out so that other pings of same |
| 497 # devserver instance do not generate new images. |
| 498 self.forced_image = None |
| 499 elif self.serve_only: |
| 500 has_built_image = self.GenerateImageFromZip(static_image_dir) |
| 501 else: |
| 502 has_built_image = self.GenerateLatestUpdateImage(board_id, |
| 503 client_version, |
| 504 static_image_dir) |
489 | 505 |
490 if has_built_image: | 506 if has_built_image: |
491 hash = self._GetHash(os.path.join(static_image_dir, 'update.gz')) | 507 hash = self._GetHash(os.path.join(static_image_dir, 'update.gz')) |
492 sha256 = self._GetSHA256(os.path.join(static_image_dir, 'update.gz')) | 508 sha256 = self._GetSHA256(os.path.join(static_image_dir, 'update.gz')) |
493 size = self._GetSize(os.path.join(static_image_dir, 'update.gz')) | 509 size = self._GetSize(os.path.join(static_image_dir, 'update.gz')) |
494 if self.static_urlbase and label: | 510 if label: |
495 url = '%s/%s/update.gz' % (self.static_urlbase, label) | 511 url = '%s/%s/update.gz' % (self.static_urlbase, label) |
496 elif self.serve_only: | |
497 url = 'http://%s/static/archive/update.gz' % hostname | |
498 else: | 512 else: |
499 url = 'http://%s/static/update.gz' % hostname | 513 url = '%s/update.gz' % self.static_urlbase |
| 514 |
| 515 web.debug('Responding to client to use url %s to get image.' % url) |
500 return self.GetUpdatePayload(hash, sha256, size, url) | 516 return self.GetUpdatePayload(hash, sha256, size, url) |
501 else: | 517 else: |
502 return self.GetNoUpdatePayload() | 518 return self.GetNoUpdatePayload() |
OLD | NEW |