| 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 |