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 cherrypy |
8 import os | 9 import os |
9 import shutil | 10 import shutil |
| 11 import socket |
10 import time | 12 import time |
11 import web | 13 |
| 14 def _LogMessage(message): |
| 15 cherrypy.log(message, 'UPDATE') |
12 | 16 |
13 | 17 |
14 class Autoupdate(BuildObject): | 18 class Autoupdate(BuildObject): |
15 """Class that contains functionality that handles Chrome OS update pings. | 19 """Class that contains functionality that handles Chrome OS update pings. |
16 | 20 |
17 Members: | 21 Members: |
18 serve_only: Serve images from a pre-built image.zip file. static_dir | 22 serve_only: Serve images from a pre-built image.zip file. static_dir |
19 must be set to the location of the image.zip. | 23 must be set to the location of the image.zip. |
20 factory_config: Path to the factory config file if handling factory | 24 factory_config: Path to the factory config file if handling factory |
21 requests. | 25 requests. |
22 use_test_image: Use chromiumos_test_image.bin rather than the standard. | 26 use_test_image: Use chromiumos_test_image.bin rather than the standard. |
23 static_url_base: base URL, other than devserver, for update images. | 27 static_url_base: base URL, other than devserver, for update images. |
24 client_prefix: The prefix for the update engine client. | 28 client_prefix: The prefix for the update engine client. |
25 forced_image: Path to an image to use for all updates. | 29 forced_image: Path to an image to use for all updates. |
26 """ | 30 """ |
27 | 31 |
28 def __init__(self, serve_only=None, test_image=False, urlbase=None, | 32 def __init__(self, serve_only=None, test_image=False, urlbase=None, |
29 factory_config_path=None, client_prefix=None, forced_image=None, | 33 factory_config_path=None, client_prefix=None, forced_image=None, |
30 use_cached=False, *args, **kwargs): | 34 use_cached=False, port=8080, *args, **kwargs): |
31 super(Autoupdate, self).__init__(*args, **kwargs) | 35 super(Autoupdate, self).__init__(*args, **kwargs) |
32 self.serve_only = serve_only | 36 self.serve_only = serve_only |
33 self.factory_config = factory_config_path | 37 self.factory_config = factory_config_path |
34 self.use_test_image = test_image | 38 self.use_test_image = test_image |
| 39 self.hostname = '%s:%s' % (socket.gethostname(), port) |
35 if urlbase: | 40 if urlbase: |
36 self.static_urlbase = urlbase | 41 self.static_urlbase = urlbase |
37 elif self.serve_only: | 42 elif self.serve_only: |
38 self.static_urlbase = 'http://%(host)s/static/archive' | 43 self.static_urlbase = 'http://%s/static/archive' % self.hostname |
39 else: | 44 else: |
40 self.static_urlbase = 'http://%(host)s/static' | 45 self.static_urlbase = 'http://%s/static' % self.hostname |
41 | 46 |
42 self.client_prefix = client_prefix | 47 self.client_prefix = client_prefix |
43 self.forced_image = forced_image | 48 self.forced_image = forced_image |
44 self.use_cached = use_cached | 49 self.use_cached = use_cached |
45 | 50 |
46 def _GetSecondsSinceMidnight(self): | 51 def _GetSecondsSinceMidnight(self): |
47 """Returns the seconds since midnight as a decimal value.""" | 52 """Returns the seconds since midnight as a decimal value.""" |
48 now = time.localtime() | 53 now = time.localtime() |
49 return now[3] * 3600 + now[4] * 60 + now[5] | 54 return now[3] * 3600 + now[4] * 60 + now[5] |
50 | 55 |
(...skipping 12 matching lines...) Expand all Loading... |
63 | 68 |
64 def _GetVersionFromDir(self, image_dir): | 69 def _GetVersionFromDir(self, image_dir): |
65 """Returns the version of the image based on the name of the directory.""" | 70 """Returns the version of the image based on the name of the directory.""" |
66 latest_version = os.path.basename(image_dir) | 71 latest_version = os.path.basename(image_dir) |
67 return latest_version.split('-')[0] | 72 return latest_version.split('-')[0] |
68 | 73 |
69 def _CanUpdate(self, client_version, latest_version): | 74 def _CanUpdate(self, client_version, latest_version): |
70 """Returns true if the latest_version is greater than the client_version.""" | 75 """Returns true if the latest_version is greater than the client_version.""" |
71 client_tokens = client_version.replace('_', '').split('.') | 76 client_tokens = client_version.replace('_', '').split('.') |
72 latest_tokens = latest_version.replace('_', '').split('.') | 77 latest_tokens = latest_version.replace('_', '').split('.') |
73 web.debug('client version %s latest version %s' | 78 _LogMessage('client version %s latest version %s' |
74 % (client_version, latest_version)) | 79 % (client_version, latest_version)) |
75 for i in range(4): | 80 for i in range(4): |
76 if int(latest_tokens[i]) == int(client_tokens[i]): | 81 if int(latest_tokens[i]) == int(client_tokens[i]): |
77 continue | 82 continue |
78 return int(latest_tokens[i]) > int(client_tokens[i]) | 83 return int(latest_tokens[i]) > int(client_tokens[i]) |
79 return False | 84 return False |
80 | 85 |
81 def _UnpackStatefulPartition(self, image_path, stateful_file): | 86 def _UnpackStatefulPartition(self, image_path, stateful_file): |
82 """Given an image, unpacks its stateful partition to stateful_file.""" | 87 """Given an image, unpacks its stateful partition to stateful_file.""" |
83 image_dir = os.path.dirname(image_path) | 88 image_dir = os.path.dirname(image_path) |
84 image_file = os.path.basename(image_path) | 89 image_file = os.path.basename(image_path) |
85 | 90 |
86 get_offset = '$(cgpt show -b -i 1 %s)' % image_file | 91 get_offset = '$(cgpt show -b -i 1 %s)' % image_file |
87 get_size = '$(cgpt show -s -i 1 %s)' % image_file | 92 get_size = '$(cgpt show -s -i 1 %s)' % image_file |
88 unpack_command = ( | 93 unpack_command = ( |
89 'cd %s && ' | 94 'cd %s && ' |
90 'dd if=%s of=%s bs=512 skip=%s count=%s' % (image_dir, image_file, | 95 'dd if=%s of=%s bs=512 skip=%s count=%s' % (image_dir, image_file, |
91 stateful_file, get_offset, | 96 stateful_file, get_offset, |
92 get_size)) | 97 get_size)) |
93 web.debug(unpack_command) | 98 _LogMessage(unpack_command) |
94 return os.system(unpack_command) == 0 | 99 return os.system(unpack_command) == 0 |
95 | 100 |
96 def _UnpackZip(self, image_dir): | 101 def _UnpackZip(self, image_dir): |
97 """Unpacks an image.zip into a given directory.""" | 102 """Unpacks an image.zip into a given directory.""" |
98 image = os.path.join(image_dir, self._GetImageName()) | 103 image = os.path.join(image_dir, self._GetImageName()) |
99 if os.path.exists(image): | 104 if os.path.exists(image): |
100 return True | 105 return True |
101 else: | 106 else: |
102 # -n, never clobber an existing file, in case we get invoked | 107 # -n, never clobber an existing file, in case we get invoked |
103 # simultaneously by multiple request handlers. This means that | 108 # simultaneously by multiple request handlers. This means that |
104 # we're assuming each image.zip file lives in a versioned | 109 # we're assuming each image.zip file lives in a versioned |
105 # directory (a la Buildbot). | 110 # directory (a la Buildbot). |
106 return os.system('cd %s && unzip -n image.zip' % image_dir) == 0 | 111 return os.system('cd %s && unzip -n image.zip' % image_dir) == 0 |
107 | 112 |
108 def _GetImageName(self): | 113 def _GetImageName(self): |
109 """Returns the name of the image that should be used.""" | 114 """Returns the name of the image that should be used.""" |
110 if self.use_test_image: | 115 if self.use_test_image: |
111 image_name = 'chromiumos_test_image.bin' | 116 image_name = 'chromiumos_test_image.bin' |
112 else: | 117 else: |
113 image_name = 'chromiumos_image.bin' | 118 image_name = 'chromiumos_image.bin' |
114 return image_name | 119 return image_name |
115 | 120 |
116 def _IsImageNewerThanCached(self, image_path, cached_file_path): | 121 def _IsImageNewerThanCached(self, image_path, cached_file_path): |
117 """Returns true if the image is newer than the cached image.""" | 122 """Returns true if the image is newer than the cached image.""" |
118 if os.path.exists(cached_file_path) and os.path.exists(image_path): | 123 if os.path.exists(cached_file_path) and os.path.exists(image_path): |
119 web.debug('Usable cached image found at %s.' % cached_file_path) | 124 _LogMessage('Usable cached image found at %s.' % cached_file_path) |
120 return os.path.getmtime(image_path) > os.path.getmtime(cached_file_path) | 125 return os.path.getmtime(image_path) > os.path.getmtime(cached_file_path) |
121 elif not os.path.exists(cached_file_path) and not os.path.exists(image_path)
: | 126 elif not os.path.exists(cached_file_path) and not os.path.exists(image_path)
: |
122 raise Exception('Image does not exist and cached image missing') | 127 raise Exception('Image does not exist and cached image missing') |
123 else: | 128 else: |
124 # Only one is missing, figure out which one. | 129 # Only one is missing, figure out which one. |
125 if os.path.exists(image_path): | 130 if os.path.exists(image_path): |
126 web.debug('No cached image found - image generation required.') | 131 _LogMessage('No cached image found - image generation required.') |
127 return True | 132 return True |
128 else: | 133 else: |
129 web.debug('Cached image found to serve at %s.' % cached_file_path) | 134 _LogMessage('Cached image found to serve at %s.' % cached_file_path) |
130 return False | 135 return False |
131 | 136 |
132 def _GetSize(self, update_path): | 137 def _GetSize(self, update_path): |
133 """Returns the size of the file given.""" | 138 """Returns the size of the file given.""" |
134 return os.path.getsize(update_path) | 139 return os.path.getsize(update_path) |
135 | 140 |
136 def _GetHash(self, update_path): | 141 def _GetHash(self, update_path): |
137 """Returns the sha1 of the file given.""" | 142 """Returns the sha1 of the file given.""" |
138 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';' | 143 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';' |
139 % update_path) | 144 % update_path) |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
195 def GenerateUpdateFile(self, image_path): | 200 def GenerateUpdateFile(self, image_path): |
196 """Generates an update gz given a full path to an image. | 201 """Generates an update gz given a full path to an image. |
197 | 202 |
198 Args: | 203 Args: |
199 image_path: Full path to image. | 204 image_path: Full path to image. |
200 Returns: | 205 Returns: |
201 Path to created update_payload or None on error. | 206 Path to created update_payload or None on error. |
202 """ | 207 """ |
203 image_dir = os.path.dirname(image_path) | 208 image_dir = os.path.dirname(image_path) |
204 update_path = os.path.join(image_dir, 'update.gz') | 209 update_path = os.path.join(image_dir, 'update.gz') |
205 web.debug('Generating update image %s' % update_path) | 210 _LogMessage('Generating update image %s' % update_path) |
206 | 211 |
207 mkupdate_command = ( | 212 mkupdate_command = ( |
208 '%s/cros_generate_update_payload --image=%s --output=%s ' | 213 '%s/cros_generate_update_payload --image=%s --output=%s ' |
209 '--patch_kernel' % (self.scripts_dir, image_path, update_path)) | 214 '--patch_kernel' % (self.scripts_dir, image_path, update_path)) |
210 if os.system(mkupdate_command) != 0: | 215 if os.system(mkupdate_command) != 0: |
211 web.debug('Failed to create base update file') | 216 _LogMessage('Failed to create base update file') |
212 return None | 217 return None |
213 | 218 |
214 return update_path | 219 return update_path |
215 | 220 |
216 def GenerateStatefulFile(self, image_path): | 221 def GenerateStatefulFile(self, image_path): |
217 """Generates a stateful update gz given a full path to an image. | 222 """Generates a stateful update gz given a full path to an image. |
218 | 223 |
219 Args: | 224 Args: |
220 image_path: Full path to image. | 225 image_path: Full path to image. |
221 Returns: | 226 Returns: |
222 Path to created stateful update_payload or None on error. | 227 Path to created stateful update_payload or None on error. |
223 """ | 228 """ |
224 stateful_partition_path = '%s/stateful.image' % os.path.dirname(image_path) | 229 stateful_partition_path = '%s/stateful.image' % os.path.dirname(image_path) |
225 | 230 |
226 # Unpack to get stateful partition. | 231 # Unpack to get stateful partition. |
227 if self._UnpackStatefulPartition(image_path, stateful_partition_path): | 232 if self._UnpackStatefulPartition(image_path, stateful_partition_path): |
228 mkstatefulupdate_command = 'gzip -f %s' % stateful_partition_path | 233 mkstatefulupdate_command = 'gzip -f %s' % stateful_partition_path |
229 if os.system(mkstatefulupdate_command) == 0: | 234 if os.system(mkstatefulupdate_command) == 0: |
230 web.debug('Successfully generated %s.gz' % stateful_partition_path) | 235 _LogMessage('Successfully generated %s.gz' % stateful_partition_path) |
231 return '%s.gz' % stateful_partition_path | 236 return '%s.gz' % stateful_partition_path |
232 | 237 |
233 web.debug('Failed to create stateful update file') | 238 _LogMessage('Failed to create stateful update file') |
234 return None | 239 return None |
235 | 240 |
236 def MoveImagesToStaticDir(self, update_path, stateful_update_path, | 241 def MoveImagesToStaticDir(self, update_path, stateful_update_path, |
237 static_image_dir): | 242 static_image_dir): |
238 """Moves gz files from their directories to serving directories. | 243 """Moves gz files from their directories to serving directories. |
239 | 244 |
240 Args: | 245 Args: |
241 update_path: full path to main update gz. | 246 update_path: full path to main update gz. |
242 stateful_update_path: full path to stateful partition gz. | 247 stateful_update_path: full path to stateful partition gz. |
243 static_image_dir: where to put files. | 248 static_image_dir: where to put files. |
244 Returns: | 249 Returns: |
245 Returns True if the files were moved over successfully. | 250 Returns True if the files were moved over successfully. |
246 """ | 251 """ |
247 try: | 252 try: |
248 shutil.copy(update_path, static_image_dir) | 253 shutil.copy(update_path, static_image_dir) |
249 shutil.copy(stateful_update_path, static_image_dir) | 254 shutil.copy(stateful_update_path, static_image_dir) |
250 os.remove(update_path) | 255 os.remove(update_path) |
251 os.remove(stateful_update_path) | 256 os.remove(stateful_update_path) |
252 except Exception: | 257 except Exception: |
253 web.debug('Failed to move %s and %s to %s' % (update_path, | 258 _LogMessage('Failed to move %s and %s to %s' % (update_path, |
254 stateful_update_path, | 259 stateful_update_path, |
255 static_image_dir)) | 260 static_image_dir)) |
256 return False | 261 return False |
257 | 262 |
258 return True | 263 return True |
259 | 264 |
260 def GenerateUpdateImage(self, image_path, move_to_static_dir=False, | 265 def GenerateUpdateImage(self, image_path, move_to_static_dir=False, |
261 static_image_dir=None): | 266 static_image_dir=None): |
262 """Force generates an update payload based on the given image_path. | 267 """Force generates an update payload based on the given image_path. |
263 | 268 |
264 Args: | 269 Args: |
265 image_path: full path to the image. | 270 image_path: full path to the image. |
266 move_to_static_dir: Moves the files from their dir to the static dir. | 271 move_to_static_dir: Moves the files from their dir to the static dir. |
267 static_image_dir: the directory to move images to after generating. | 272 static_image_dir: the directory to move images to after generating. |
268 Returns: | 273 Returns: |
269 True if the update payload was created successfully. | 274 True if the update payload was created successfully. |
270 """ | 275 """ |
271 web.debug('Generating update for image %s' % image_path) | 276 _LogMessage('Generating update for image %s' % image_path) |
272 update_path = self.GenerateUpdateFile(image_path) | 277 update_path = self.GenerateUpdateFile(image_path) |
273 stateful_update_path = self.GenerateStatefulFile(image_path) | 278 stateful_update_path = self.GenerateStatefulFile(image_path) |
274 if not update_path or not stateful_update_path: | 279 if not update_path or not stateful_update_path: |
275 web.debug('Failed to generate update') | 280 _LogMessage('Failed to generate update') |
276 return False | 281 return False |
277 | 282 |
278 if move_to_static_dir: | 283 if move_to_static_dir: |
279 return self.MoveImagesToStaticDir(update_path, stateful_update_path, | 284 return self.MoveImagesToStaticDir(update_path, stateful_update_path, |
280 static_image_dir) | 285 static_image_dir) |
281 else: | 286 else: |
282 return True | 287 return True |
283 | 288 |
284 def GenerateLatestUpdateImage(self, board_id, client_version, | 289 def GenerateLatestUpdateImage(self, board_id, client_version, |
285 static_image_dir=None): | 290 static_image_dir=None): |
286 """Generates an update using the latest image that has been built. | 291 """Generates an update using the latest image that has been built. |
287 | 292 |
288 This will only generate an update if the newest update is newer than that | 293 This will only generate an update if the newest update is newer than that |
289 on the client or client_version is 'ForcedUpdate'. | 294 on the client or client_version is 'ForcedUpdate'. |
290 | 295 |
291 Args: | 296 Args: |
292 board_id: Name of the board. | 297 board_id: Name of the board. |
293 client_version: Current version of the client or 'ForcedUpdate' | 298 client_version: Current version of the client or 'ForcedUpdate' |
294 static_image_dir: the directory to move images to after generating. | 299 static_image_dir: the directory to move images to after generating. |
295 Returns: | 300 Returns: |
296 True if the update payload was created successfully. | 301 True if the update payload was created successfully. |
297 """ | 302 """ |
298 latest_image_dir = self._GetLatestImageDir(board_id) | 303 latest_image_dir = self._GetLatestImageDir(board_id) |
299 latest_version = self._GetVersionFromDir(latest_image_dir) | 304 latest_version = self._GetVersionFromDir(latest_image_dir) |
300 latest_image_path = os.path.join(latest_image_dir, self._GetImageName()) | 305 latest_image_path = os.path.join(latest_image_dir, self._GetImageName()) |
301 | 306 |
302 web.debug('Preparing to generate update from latest built image %s.' % | 307 _LogMessage('Preparing to generate update from latest built image %s.' % |
303 latest_image_path) | 308 latest_image_path) |
304 | 309 |
305 # Check to see whether or not we should update. | 310 # Check to see whether or not we should update. |
306 if client_version != 'ForcedUpdate' and not self._CanUpdate( | 311 if client_version != 'ForcedUpdate' and not self._CanUpdate( |
307 client_version, latest_version): | 312 client_version, latest_version): |
308 web.debug('no update') | 313 _LogMessage('no update') |
309 return False | 314 return False |
310 | 315 |
311 cached_file_path = os.path.join(static_image_dir, 'update.gz') | 316 cached_file_path = os.path.join(static_image_dir, 'update.gz') |
312 if (os.path.exists(cached_file_path) and | 317 if (os.path.exists(cached_file_path) and |
313 not self._IsImageNewerThanCached(latest_image_path, cached_file_path)): | 318 not self._IsImageNewerThanCached(latest_image_path, cached_file_path)): |
314 return True | 319 return True |
315 | 320 |
316 return self.GenerateUpdateImage(latest_image_path, move_to_static_dir=True, | 321 return self.GenerateUpdateImage(latest_image_path, move_to_static_dir=True, |
317 static_image_dir=static_image_dir) | 322 static_image_dir=static_image_dir) |
318 | 323 |
319 def GenerateImageFromZip(self, static_image_dir): | 324 def GenerateImageFromZip(self, static_image_dir): |
320 """Generates an update from an image zip file. | 325 """Generates an update from an image zip file. |
321 | 326 |
322 This method assumes you have an image.zip in directory you are serving | 327 This method assumes you have an image.zip in directory you are serving |
323 from. If this file is newer than a previously cached file, it will unzip | 328 from. If this file is newer than a previously cached file, it will unzip |
324 this file, create a payload and serve it. | 329 this file, create a payload and serve it. |
325 | 330 |
326 Args: | 331 Args: |
327 static_image_dir: Directory where the zip file exists. | 332 static_image_dir: Directory where the zip file exists. |
328 Returns: | 333 Returns: |
329 True if the update payload was created successfully. | 334 True if the update payload was created successfully. |
330 """ | 335 """ |
331 web.debug('Preparing to generate update from zip in %s.' % static_image_dir) | 336 _LogMessage('Preparing to generate update from zip in %s.' % static_image_di
r) |
332 image_path = os.path.join(static_image_dir, self._GetImageName()) | 337 image_path = os.path.join(static_image_dir, self._GetImageName()) |
333 cached_file_path = os.path.join(static_image_dir, 'update.gz') | 338 cached_file_path = os.path.join(static_image_dir, 'update.gz') |
334 zip_file_path = os.path.join(static_image_dir, 'image.zip') | 339 zip_file_path = os.path.join(static_image_dir, 'image.zip') |
335 if not self._IsImageNewerThanCached(zip_file_path, cached_file_path): | 340 if not self._IsImageNewerThanCached(zip_file_path, cached_file_path): |
336 return True | 341 return True |
337 | 342 |
338 if not self._UnpackZip(static_image_dir): | 343 if not self._UnpackZip(static_image_dir): |
339 web.debug('unzip image.zip failed.') | 344 _LogMessage('unzip image.zip failed.') |
340 return False | 345 return False |
341 | 346 |
342 return self.GenerateUpdateImage(image_path, move_to_static_dir=False, | 347 return self.GenerateUpdateImage(image_path, move_to_static_dir=False, |
343 static_image_dir=None) | 348 static_image_dir=None) |
344 | 349 |
345 def ImportFactoryConfigFile(self, filename, validate_checksums=False): | 350 def ImportFactoryConfigFile(self, filename, validate_checksums=False): |
346 """Imports a factory-floor server configuration file. The file should | 351 """Imports a factory-floor server configuration file. The file should |
347 be in this format: | 352 be in this format: |
348 config = [ | 353 config = [ |
349 { | 354 { |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 for stanza in self.factory_config: | 421 for stanza in self.factory_config: |
417 if board_id not in stanza['qual_ids']: | 422 if board_id not in stanza['qual_ids']: |
418 continue | 423 continue |
419 if kind + '_image' not in stanza: | 424 if kind + '_image' not in stanza: |
420 break | 425 break |
421 return (stanza[kind + '_image'], | 426 return (stanza[kind + '_image'], |
422 stanza[kind + '_checksum'], | 427 stanza[kind + '_checksum'], |
423 stanza[kind + '_size']) | 428 stanza[kind + '_size']) |
424 return (None, None, None) | 429 return (None, None, None) |
425 | 430 |
426 def HandleFactoryRequest(self, hostname, board_id, channel): | 431 def HandleFactoryRequest(self, board_id, channel): |
427 (filename, checksum, size) = self.GetFactoryImage(board_id, channel) | 432 (filename, checksum, size) = self.GetFactoryImage(board_id, channel) |
428 if filename is None: | 433 if filename is None: |
429 web.debug('unable to find image for board %s' % board_id) | 434 _LogMessage('unable to find image for board %s' % board_id) |
430 return self.GetNoUpdatePayload() | 435 return self.GetNoUpdatePayload() |
431 url = 'http://%s/static/%s' % (hostname, filename) | 436 url = 'http://%s/static/%s' % (self.hostname, filename) |
432 web.debug('returning update payload ' + url) | 437 _LogMessage('returning update payload ' + url) |
433 # Factory install is using memento updater which is using the sha-1 hash so | 438 # Factory install is using memento updater which is using the sha-1 hash so |
434 # setting sha-256 to an empty string. | 439 # setting sha-256 to an empty string. |
435 return self.GetUpdatePayload(checksum, '', size, url) | 440 return self.GetUpdatePayload(checksum, '', size, url) |
436 | 441 |
437 def HandleUpdatePing(self, data, label=None): | 442 def HandleUpdatePing(self, data, label=None): |
438 """Handles an update ping from an update client. | 443 """Handles an update ping from an update client. |
439 | 444 |
440 Args: | 445 Args: |
441 data: xml blob from client. | 446 data: xml blob from client. |
442 label: optional label for the update. | 447 label: optional label for the update. |
443 Returns: | 448 Returns: |
444 Update payload message for client. | 449 Update payload message for client. |
445 """ | 450 """ |
446 web.debug('handling update ping: %s' % data) | 451 _LogMessage('handling update ping: %s' % data) |
447 update_dom = minidom.parseString(data) | 452 update_dom = minidom.parseString(data) |
448 root = update_dom.firstChild | 453 root = update_dom.firstChild |
449 | 454 |
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 | |
454 # Check the client prefix to make sure you can support this type of update. | 455 # Check the client prefix to make sure you can support this type of update. |
455 if (root.hasAttribute('updaterversion') and | 456 if (root.hasAttribute('updaterversion') and |
456 not root.getAttribute('updaterversion').startswith(self.client_prefix)): | 457 not root.getAttribute('updaterversion').startswith(self.client_prefix)): |
457 web.debug('Got update from unsupported updater:' + | 458 _LogMessage('Got update from unsupported updater:' + |
458 root.getAttribute('updaterversion')) | 459 root.getAttribute('updaterversion')) |
459 return self.GetNoUpdatePayload() | 460 return self.GetNoUpdatePayload() |
460 | 461 |
461 # We only generate update payloads for updatecheck requests. | 462 # We only generate update payloads for updatecheck requests. |
462 update_check = root.getElementsByTagName('o:updatecheck') | 463 update_check = root.getElementsByTagName('o:updatecheck') |
463 if not update_check: | 464 if not update_check: |
464 web.debug('Non-update check received. Returning blank payload.') | 465 _LogMessage('Non-update check received. Returning blank payload.') |
465 # TODO(sosa): Generate correct non-updatecheck payload to better test | 466 # TODO(sosa): Generate correct non-updatecheck payload to better test |
466 # update clients. | 467 # update clients. |
467 return self.GetNoUpdatePayload() | 468 return self.GetNoUpdatePayload() |
468 | 469 |
469 # Since this is an updatecheck, get information about the requester. | 470 # Since this is an updatecheck, get information about the requester. |
470 hostname = web.ctx.host | |
471 query = root.getElementsByTagName('o:app')[0] | 471 query = root.getElementsByTagName('o:app')[0] |
472 client_version = query.getAttribute('version') | 472 client_version = query.getAttribute('version') |
473 channel = query.getAttribute('track') | 473 channel = query.getAttribute('track') |
474 board_id = (query.hasAttribute('board') and query.getAttribute('board') | 474 board_id = (query.hasAttribute('board') and query.getAttribute('board') |
475 or self._GetDefaultBoardID()) | 475 or self._GetDefaultBoardID()) |
476 | 476 |
477 # Separate logic as Factory requests have static url's that override | 477 # Separate logic as Factory requests have static url's that override |
478 # other options. | 478 # other options. |
479 if self.factory_config: | 479 if self.factory_config: |
480 return self.HandleFactoryRequest(hostname, board_id, channel) | 480 return self.HandleFactoryRequest(board_id, channel) |
481 else: | 481 else: |
482 static_image_dir = self.static_dir | 482 static_image_dir = self.static_dir |
483 if label: | 483 if label: |
484 static_image_dir = os.path.join(static_image_dir, label) | 484 static_image_dir = os.path.join(static_image_dir, label) |
485 | 485 |
486 # Prefer cached image if it exists. | 486 # Prefer cached image if it exists. |
487 if self.use_cached and os.path.exists(os.path.join(static_image_dir, | 487 if self.use_cached and os.path.exists(os.path.join(static_image_dir, |
488 'update.gz')): | 488 'update.gz')): |
489 web.debug('Using cached image regardless of timestamps.') | 489 _LogMessage('Using cached image regardless of timestamps.') |
490 has_built_image = True | 490 has_built_image = True |
491 else: | 491 else: |
492 if self.forced_image: | 492 if self.forced_image: |
493 has_built_image = self.GenerateUpdateImage( | 493 has_built_image = self.GenerateUpdateImage( |
494 self.forced_image, move_to_static_dir=True, | 494 self.forced_image, move_to_static_dir=True, |
495 static_image_dir=static_image_dir) | 495 static_image_dir=static_image_dir) |
496 # Now that we've generated it, clear out so that other pings of same | 496 # Now that we've generated it, clear out so that other pings of same |
497 # devserver instance do not generate new images. | 497 # devserver instance do not generate new images. |
498 self.forced_image = None | 498 self.forced_image = None |
499 elif self.serve_only: | 499 elif self.serve_only: |
500 has_built_image = self.GenerateImageFromZip(static_image_dir) | 500 has_built_image = self.GenerateImageFromZip(static_image_dir) |
501 else: | 501 else: |
502 has_built_image = self.GenerateLatestUpdateImage(board_id, | 502 has_built_image = self.GenerateLatestUpdateImage(board_id, |
503 client_version, | 503 client_version, |
504 static_image_dir) | 504 static_image_dir) |
505 | 505 |
506 if has_built_image: | 506 if has_built_image: |
507 hash = self._GetHash(os.path.join(static_image_dir, 'update.gz')) | 507 hash = self._GetHash(os.path.join(static_image_dir, 'update.gz')) |
508 sha256 = self._GetSHA256(os.path.join(static_image_dir, 'update.gz')) | 508 sha256 = self._GetSHA256(os.path.join(static_image_dir, 'update.gz')) |
509 size = self._GetSize(os.path.join(static_image_dir, 'update.gz')) | 509 size = self._GetSize(os.path.join(static_image_dir, 'update.gz')) |
510 if label: | 510 if label: |
511 url = '%s/%s/update.gz' % (self.static_urlbase, label) | 511 url = '%s/%s/update.gz' % (self.static_urlbase, label) |
512 else: | 512 else: |
513 url = '%s/update.gz' % self.static_urlbase | 513 url = '%s/update.gz' % self.static_urlbase |
514 | 514 |
515 web.debug('Responding to client to use url %s to get image.' % url) | 515 _LogMessage('Responding to client to use url %s to get image.' % url) |
516 return self.GetUpdatePayload(hash, sha256, size, url) | 516 return self.GetUpdatePayload(hash, sha256, size, url) |
517 else: | 517 else: |
518 return self.GetNoUpdatePayload() | 518 return self.GetNoUpdatePayload() |
OLD | NEW |