Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(103)

Side by Side Diff: autoupdate.py

Issue 5057005: Revert "Reworked devserver so that update images generated are cached in directories named after" (Closed) Base URL: http://git.chromium.org/git/dev-util.git@master
Patch Set: Created 10 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | autoupdate_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 subprocess 10 import subprocess
11 import tempfile
12 import time 12 import time
13 13
14 14
15 def _LogMessage(message): 15 def _LogMessage(message):
16 cherrypy.log(message, 'UPDATE') 16 cherrypy.log(message, 'UPDATE')
17 17
18 18
19 class Autoupdate(BuildObject): 19 class Autoupdate(BuildObject):
20 """Class that contains functionality that handles Chrome OS update pings. 20 """Class that contains functionality that handles Chrome OS update pings.
21 21
22 Members: 22 Members:
23 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
24 must be set to the location of the image.zip. 24 must be set to the location of the image.zip.
25 factory_config: Path to the factory config file if handling factory 25 factory_config: Path to the factory config file if handling factory
26 requests. 26 requests.
27 use_test_image: Use chromiumos_test_image.bin rather than the standard. 27 use_test_image: Use chromiumos_test_image.bin rather than the standard.
28 static_url_base: base URL, other than devserver, for update images. 28 static_url_base: base URL, other than devserver, for update images.
29 client_prefix: The prefix for the update engine client. 29 client_prefix: The prefix for the update engine client.
30 forced_image: Path to an image to use for all updates. 30 forced_image: Path to an image to use for all updates.
31 """ 31 """
32 32
33 def __init__(self, serve_only=None, test_image=False, urlbase=None, 33 def __init__(self, serve_only=None, test_image=False, urlbase=None,
34 factory_config_path=None, client_prefix=None, forced_image=None, 34 factory_config_path=None, client_prefix=None, forced_image=None,
35 port=8080, src_image='', vm=False, board=None, 35 use_cached=False, port=8080, src_image='', vm=False, board=None,
36 *args, **kwargs): 36 *args, **kwargs):
37 super(Autoupdate, self).__init__(*args, **kwargs) 37 super(Autoupdate, self).__init__(*args, **kwargs)
38 self.serve_only = serve_only 38 self.serve_only = serve_only
39 self.factory_config = factory_config_path 39 self.factory_config = factory_config_path
40 self.use_test_image = test_image 40 self.use_test_image = test_image
41 if urlbase: 41 if urlbase:
42 self.urlbase = urlbase 42 self.urlbase = urlbase
43 else: 43 else:
44 self.urlbase = None 44 self.urlbase = None
45 45
46 self.client_prefix = client_prefix 46 self.client_prefix = client_prefix
47 self.forced_image = forced_image 47 self.forced_image = forced_image
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
51 self.crosutils = os.path.join(os.path.dirname(__file__), '../../scripts') 52 self.crosutils = os.path.join(os.path.dirname(__file__), '../../scripts')
52 53
53 def _GetSecondsSinceMidnight(self): 54 def _GetSecondsSinceMidnight(self):
54 """Returns the seconds since midnight as a decimal value.""" 55 """Returns the seconds since midnight as a decimal value."""
55 now = time.localtime() 56 now = time.localtime()
56 return now[3] * 3600 + now[4] * 60 + now[5] 57 return now[3] * 3600 + now[4] * 60 + now[5]
57 58
58 def _GetDefaultBoardID(self): 59 def _GetDefaultBoardID(self):
59 """Returns the default board id stored in .default_board.""" 60 """Returns the default board id stored in .default_board."""
60 board_file = '%s/.default_board' % (self.scripts_dir) 61 board_file = '%s/.default_board' % (self.scripts_dir)
61 try: 62 try:
62 return open(board_file).read() 63 return open(board_file).read()
63 except IOError: 64 except IOError:
64 return 'x86-generic' 65 return 'x86-generic'
65 66
66 def _GetLatestImageDir(self, board_id): 67 def _GetLatestImageDir(self, board_id):
67 """Returns the latest image dir based on shell script.""" 68 """Returns the latest image dir based on shell script."""
68 cmd = '%s/get_latest_image.sh --board %s' % (self.scripts_dir, board_id) 69 cmd = '%s/get_latest_image.sh --board %s' % (self.scripts_dir, board_id)
69 return os.popen(cmd).read().strip() 70 return os.popen(cmd).read().strip()
70 71
71 def _GetVersionFromDir(self, image_dir): 72 def _GetVersionFromDir(self, image_dir):
72 """Returns the version of the image based on the name of the directory.""" 73 """Returns the version of the image based on the name of the directory."""
73 latest_version = os.path.basename(image_dir) 74 latest_version = os.path.basename(image_dir)
74 return latest_version.split('-')[0] 75 return latest_version.split('-')[0]
75 76
76 def _CanUpdate(self, client_version, latest_version): 77 def _CanUpdate(self, client_version, latest_version):
77 """Returns true if the latest_version is greater than the client_version. 78 """Returns true if the latest_version is greater than the client_version."""
78 """
79 client_tokens = client_version.replace('_', '').split('.') 79 client_tokens = client_version.replace('_', '').split('.')
80 latest_tokens = latest_version.replace('_', '').split('.') 80 latest_tokens = latest_version.replace('_', '').split('.')
81 _LogMessage('client version %s latest version %s' 81 _LogMessage('client version %s latest version %s'
82 % (client_version, latest_version)) 82 % (client_version, latest_version))
83 for i in range(4): 83 for i in range(4):
84 if int(latest_tokens[i]) == int(client_tokens[i]): 84 if int(latest_tokens[i]) == int(client_tokens[i]):
85 continue 85 continue
86 return int(latest_tokens[i]) > int(client_tokens[i]) 86 return int(latest_tokens[i]) > int(client_tokens[i])
87 return False 87 return False
88 88
89 def _UnpackZip(self, image_dir): 89 def _UnpackZip(self, image_dir):
90 """Unpacks an image.zip into a given directory.""" 90 """Unpacks an image.zip into a given directory."""
91 image = os.path.join(image_dir, self._GetImageName()) 91 image = os.path.join(image_dir, self._GetImageName())
92 if os.path.exists(image): 92 if os.path.exists(image):
93 return True 93 return True
94 else: 94 else:
95 # -n, never clobber an existing file, in case we get invoked 95 # -n, never clobber an existing file, in case we get invoked
96 # simultaneously by multiple request handlers. This means that 96 # simultaneously by multiple request handlers. This means that
97 # we're assuming each image.zip file lives in a versioned 97 # we're assuming each image.zip file lives in a versioned
98 # directory (a la Buildbot). 98 # directory (a la Buildbot).
99 return os.system('cd %s && unzip -n image.zip' % image_dir) == 0 99 return os.system('cd %s && unzip -n image.zip' % image_dir) == 0
100 100
101 def _GetImageName(self): 101 def _GetImageName(self):
102 """Returns the name of the image that should be used.""" 102 """Returns the name of the image that should be used."""
103 if self.use_test_image: 103 if self.use_test_image:
104 image_name = 'chromiumos_test_image.bin' 104 image_name = 'chromiumos_test_image.bin'
105 else: 105 else:
106 image_name = 'chromiumos_image.bin' 106 image_name = 'chromiumos_image.bin'
107 return image_name 107 return image_name
108 108
109 def _IsImageNewerThanCached(self, image_path, cached_file_path):
110 """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):
112 _LogMessage('Usable cached image found at %s.' % cached_file_path)
113 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) :
115 raise Exception('Image does not exist and cached image missing')
116 else:
117 # Only one is missing, figure out which one.
118 if os.path.exists(image_path):
119 _LogMessage('No cached image found - image generation required.')
120 return True
121 else:
122 _LogMessage('Cached image found to serve at %s.' % cached_file_path)
123 return False
124
109 def _GetSize(self, update_path): 125 def _GetSize(self, update_path):
110 """Returns the size of the file given.""" 126 """Returns the size of the file given."""
111 return os.path.getsize(update_path) 127 return os.path.getsize(update_path)
112 128
113 def _GetHash(self, update_path): 129 def _GetHash(self, update_path):
114 """Returns the sha1 of the file given.""" 130 """Returns the sha1 of the file given."""
115 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';' 131 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';'
116 % update_path) 132 % update_path)
117 return os.popen(cmd).read().rstrip() 133 return os.popen(cmd).read().rstrip()
118 134
(...skipping 10 matching lines...) Expand all
129 # it takes advantage of reduced I/O and multiple processors. Something like: 145 # it takes advantage of reduced I/O and multiple processors. Something like:
130 # % tee < FILE > /dev/null \ 146 # % tee < FILE > /dev/null \
131 # >( openssl dgst -sha256 -binary | openssl base64 ) \ 147 # >( openssl dgst -sha256 -binary | openssl base64 ) \
132 # >( openssl sha1 -binary | openssl base64 ) 148 # >( openssl sha1 -binary | openssl base64 )
133 def _GetSHA256(self, update_path): 149 def _GetSHA256(self, update_path):
134 """Returns the sha256 of the file given.""" 150 """Returns the sha256 of the file given."""
135 cmd = ('cat %s | openssl dgst -sha256 -binary | openssl base64' % 151 cmd = ('cat %s | openssl dgst -sha256 -binary | openssl base64' %
136 update_path) 152 update_path)
137 return os.popen(cmd).read().rstrip() 153 return os.popen(cmd).read().rstrip()
138 154
139 def _GetMd5(self, update_path):
140 """Returns the md5 checksum of the file given."""
141 cmd = ("md5sum %s | awk '{print $1}'" % update_path)
142 return os.popen(cmd).read().rstrip()
143
144 def GetUpdatePayload(self, hash, sha256, size, url, is_delta_format): 155 def GetUpdatePayload(self, hash, sha256, size, url, is_delta_format):
145 """Returns a payload to the client corresponding to a new update. 156 """Returns a payload to the client corresponding to a new update.
146 157
147 Args: 158 Args:
148 hash: hash of update blob 159 hash: hash of update blob
149 sha256: SHA-256 hash of update blob 160 sha256: SHA-256 hash of update blob
150 size: size of update blob 161 size: size of update blob
151 url: where to find update blob 162 url: where to find update blob
152 Returns: 163 Returns:
153 Xml string to be passed back to client. 164 Xml string to be passed back to client.
(...skipping 26 matching lines...) Expand all
180 < gupdate xmlns = "http://www.google.com/update2/response" protocol = "2.0 " > 191 < gupdate xmlns = "http://www.google.com/update2/response" protocol = "2.0 " >
181 < daystart elapsed_seconds = "%s" /> 192 < daystart elapsed_seconds = "%s" />
182 < app appid = "{%s}" status = "ok" > 193 < app appid = "{%s}" status = "ok" >
183 < ping status = "ok" /> 194 < ping status = "ok" />
184 < updatecheck status = "noupdate" /> 195 < updatecheck status = "noupdate" />
185 </ app > 196 </ app >
186 </ gupdate > 197 </ gupdate >
187 """ 198 """
188 return payload % (self._GetSecondsSinceMidnight(), self.app_id) 199 return payload % (self._GetSecondsSinceMidnight(), self.app_id)
189 200
190 def GenerateUpdateFile(self, src_image, image_path, static_image_dir): 201 def GenerateUpdateFile(self, image_path):
191 """Generates an update gz given a full path to an image. 202 """Generates an update gz given a full path to an image.
192 203
193 Args: 204 Args:
194 image_path: Full path to image. 205 image_path: Full path to image.
195 Returns: 206 Returns:
196 Path to created update_payload or None on error. 207 Path to created update_payload or None on error.
197 """ 208 """
198 update_path = os.path.join(static_image_dir, 'update.gz') 209 image_dir = os.path.dirname(image_path)
210 update_path = os.path.join(image_dir, 'update.gz')
199 patch_kernel_flag = '--patch_kernel' 211 patch_kernel_flag = '--patch_kernel'
200 _LogMessage('Generating update image %s' % update_path) 212 _LogMessage('Generating update image %s' % update_path)
201 213
202 # Don't patch the kernel for vm images as they don't need the patch. 214 # Don't patch the kernel for vm images as they don't need the patch.
203 if self.vm: 215 if self.vm:
204 patch_kernel_flag = '' 216 patch_kernel_flag = ''
205 217
206 mkupdate_command = ( 218 mkupdate_command = (
207 '%s/cros_generate_update_payload --image="%s" --output="%s" ' 219 '%s/cros_generate_update_payload --image="%s" --output="%s" '
208 '%s --noold_style --src_image="%s"' % ( 220 '%s --noold_style --src_image="%s"' % (
209 self.scripts_dir, image_path, update_path, patch_kernel_flag, 221 self.scripts_dir, image_path, update_path, patch_kernel_flag,
210 src_image)) 222 self.src_image))
211 _LogMessage(mkupdate_command) 223 _LogMessage(mkupdate_command)
212 if os.system(mkupdate_command) != 0: 224 if os.system(mkupdate_command) != 0:
213 _LogMessage('Failed to create base update file') 225 _LogMessage('Failed to create base update file')
214 return None 226 return None
215 227
216 return update_path 228 return update_path
217 229
218 def GenerateStatefulFile(self, image_path, static_image_dir): 230 def GenerateStatefulFile(self, image_path):
219 """Generates a stateful update payload given a full path to an image. 231 """Generates a stateful update given a full path to an image.
220 232
221 Args: 233 Args:
222 image_path: Full path to image. 234 image_path: Full path to image.
223 Returns: 235 Returns:
224 Path to created stateful update_payload or None on error. 236 Path to created stateful update_payload.
225 Raises: 237 Raises:
226 A subprocess exception if the update generator fails to generate a 238 A subprocess exception if the update generator fails to generate a
227 stateful payload. 239 stateful payload.
228 """ 240 """
229 output_gz = os.path.join(static_image_dir, 'stateful.tgz') 241 work_dir = os.path.dirname(image_path)
242 output_gz = os.path.join(work_dir, 'stateful.tgz')
230 subprocess.check_call( 243 subprocess.check_call(
231 ['%s/cros_generate_stateful_update_payload' % self.crosutils, 244 ['%s/cros_generate_stateful_update_payload' % self.crosutils,
232 '--image=%s' % image_path, 245 '--image=%s' % image_path,
233 '--output_dir=%s' % static_image_dir, 246 '--output_dir=%s' % work_dir,
234 ]) 247 ])
235 return output_gz 248 return output_gz
236 249
237 def FindCachedUpdateImageSubDir(self, src_image, dest_image): 250 def MoveImagesToStaticDir(self, update_path, stateful_update_path,
238 """Find directory to store a cached update. 251 static_image_dir):
252 """Moves gz files from their directories to serving directories.
239 253
240 Given one, or two images for an update, this finds which 254 Args:
241 cache directory should hold the update files, even if they don't exist 255 update_path: full path to main update gz.
242 yet. The directory will be inside static_image_dir, and of the form: 256 stateful_update_path: full path to stateful partition gz.
257 static_image_dir: where to put files.
258 Returns:
259 Returns True if the files were moved over successfully.
260 """
261 try:
262 shutil.copy(update_path, static_image_dir)
263 shutil.copy(stateful_update_path, static_image_dir)
264 os.remove(update_path)
265 os.remove(stateful_update_path)
266 except Exception:
267 _LogMessage('Failed to move %s and %s to %s' % (update_path,
268 stateful_update_path,
269 static_image_dir))
270 return False
243 271
244 Non-delta updates: 272 return True
245 cache/12345678
246 273
247 Delta updates: 274 def GenerateUpdateImage(self, image_path, move_to_static_dir=False,
248 cache/12345678_12345678 275 static_image_dir=None):
249 """ 276 """Generates an update payload based on the given image_path.
250 # If there is no src, we only have an image file, check image for changes
251 if not src_image:
252 return os.path.join('cache', self._GetMd5(dest_image))
253
254 # If we have src and dest, we are a delta, and check both for changes
255 return os.path.join('cache',
256 "%s_%s" % (self._GetMd5(src_image),
257 self._GetMd5(dest_image)))
258
259 def GenerateUpdateImage(self, src_image, image_path, static_image_dir):
260 """Force generates an update payload based on the given image_path.
261 277
262 Args: 278 Args:
263 image_path: full path to the image. 279 image_path: full path to the image.
264 move_to_static_dir: Moves the files from their dir to the static dir. 280 move_to_static_dir: Moves the files from their dir to the static dir.
265 static_image_dir: the directory to move images to after generating. 281 static_image_dir: the directory to move images to after generating.
266 Returns: 282 Returns:
267 update filename (not directory) on success, or None 283 True if the update payload was created successfully.
268 """ 284 """
269 update_file = None
270 stateful_update_file = None
271
272 # Actually do the generation
273 _LogMessage('Generating update for image %s' % image_path) 285 _LogMessage('Generating update for image %s' % image_path)
274 update_file = self.GenerateUpdateFile(src_image, 286 update_path = self.GenerateUpdateFile(image_path)
275 image_path, 287 if update_path:
288 stateful_update_path = self.GenerateStatefulFile(image_path)
289 if move_to_static_dir:
290 return self.MoveImagesToStaticDir(update_path, stateful_update_path,
276 static_image_dir) 291 static_image_dir)
277 292 return True
278 if update_file:
279 stateful_update_file = self.GenerateStatefulFile(image_path,
280 static_image_dir)
281
282 if update_file and stateful_update_file:
283 return os.path.basename(update_file)
284 293
285 _LogMessage('Failed to generate update') 294 _LogMessage('Failed to generate update')
286 295 return False
287 # Cleanup incomplete files, if they exist
288 if update_file and os.path.exists(update_file):
289 os.remove(update_file)
290
291 return None
292
293 def GenerateUpdateImageWithCache(self, image_path, static_image_dir):
294 """Force generates an update payload based on the given image_path.
295
296 Args:
297 image_path: full path to the image.
298 move_to_static_dir: Moves the files from their dir to the static dir.
299 static_image_dir: the directory to move images to after generating.
300 Returns:
301 update filename (not directory) relative to static_image_dir on success,
302 or None
303 """
304 _LogMessage('Generating update for src %s image %s' % (self.src_image,
305 image_path))
306
307 # Which sub_dir of static_image_dir should hold our cached update image
308 cache_sub_dir = self.FindCachedUpdateImageSubDir(self.src_image, image_path)
309 _LogMessage('Caching in sub_dir "%s"' % cache_sub_dir)
310
311 # Check for a cached image to use
312 if os.path.exists(os.path.join(static_image_dir,
313 cache_sub_dir,
314 'update.gz')):
315 return os.path.join(cache_sub_dir, 'update.gz')
316
317 full_cache_dir = os.path.join(static_image_dir, cache_sub_dir)
318
319 # Create the directory for the cache values
320 if not os.path.exists(full_cache_dir):
321 os.makedirs(full_cache_dir)
322
323 gen_image = self.GenerateUpdateImage(self.src_image,
324 image_path,
325 full_cache_dir)
326
327 # If the generation worked
328 if gen_image:
329 return os.path.join(cache_sub_dir, gen_image)
330 else:
331 return None
332
333 296
334 def GenerateLatestUpdateImage(self, board_id, client_version, 297 def GenerateLatestUpdateImage(self, board_id, client_version,
335 static_image_dir): 298 static_image_dir=None):
336 """Generates an update using the latest image that has been built. 299 """Generates an update using the latest image that has been built.
337 300
338 This will only generate an update if the newest update is newer than that 301 This will only generate an update if the newest update is newer than that
339 on the client or client_version is 'ForcedUpdate'. 302 on the client or client_version is 'ForcedUpdate'.
340 303
341 Args: 304 Args:
342 board_id: Name of the board. 305 board_id: Name of the board.
343 client_version: Current version of the client or 'ForcedUpdate' 306 client_version: Current version of the client or 'ForcedUpdate'
344 static_image_dir: the directory to move images to after generating. 307 static_image_dir: the directory to move images to after generating.
345 Returns: 308 Returns:
346 Name of the update image relative to static_image_dir or None 309 True if the update payload was created successfully.
347 """ 310 """
348 latest_image_dir = self._GetLatestImageDir(board_id) 311 latest_image_dir = self._GetLatestImageDir(board_id)
349 latest_version = self._GetVersionFromDir(latest_image_dir) 312 latest_version = self._GetVersionFromDir(latest_image_dir)
350 latest_image_path = os.path.join(latest_image_dir, self._GetImageName()) 313 latest_image_path = os.path.join(latest_image_dir, self._GetImageName())
351 314
352 _LogMessage('Preparing to generate update from latest built image %s.' % 315 _LogMessage('Preparing to generate update from latest built image %s.' %
353 latest_image_path) 316 latest_image_path)
354 317
355 # Check to see whether or not we should update. 318 # Check to see whether or not we should update.
356 if client_version != 'ForcedUpdate' and not self._CanUpdate( 319 if client_version != 'ForcedUpdate' and not self._CanUpdate(
357 client_version, latest_version): 320 client_version, latest_version):
358 _LogMessage('no update') 321 _LogMessage('no update')
359 return None 322 return False
360 323
361 return self.GenerateUpdateImageWithCache(latest_image_path, 324 cached_file_path = os.path.join(static_image_dir, 'update.gz')
362 static_image_dir=static_image_dir) 325 if (os.path.exists(cached_file_path) and
326 not self._IsImageNewerThanCached(latest_image_path, cached_file_path)):
327 return True
328
329 return self.GenerateUpdateImage(latest_image_path, move_to_static_dir=True,
330 static_image_dir=static_image_dir)
363 331
364 def GenerateImageFromZip(self, static_image_dir): 332 def GenerateImageFromZip(self, static_image_dir):
365 """Generates an update from an image zip file. 333 """Generates an update from an image zip file.
366 334
367 This method assumes you have an image.zip in directory you are serving 335 This method assumes you have an image.zip in directory you are serving
368 from. If this file is newer than a previously cached file, it will unzip 336 from. If this file is newer than a previously cached file, it will unzip
369 this file, create a payload and serve it. 337 this file, create a payload and serve it.
370 338
371 Args: 339 Args:
372 static_image_dir: Directory where the zip file exists. 340 static_image_dir: Directory where the zip file exists.
373 Returns: 341 Returns:
374 Name of the update payload relative to static_image_dir if successful. 342 True if the update payload was created successfully.
375 """ 343 """
376 _LogMessage('Preparing to generate update from zip in %s.' % 344 _LogMessage('Preparing to generate update from zip in %s.' % static_image_di r)
377 static_image_dir)
378 image_path = os.path.join(static_image_dir, self._GetImageName()) 345 image_path = os.path.join(static_image_dir, self._GetImageName())
346 cached_file_path = os.path.join(static_image_dir, 'update.gz')
379 zip_file_path = os.path.join(static_image_dir, 'image.zip') 347 zip_file_path = os.path.join(static_image_dir, 'image.zip')
380 348 if not self._IsImageNewerThanCached(zip_file_path, cached_file_path):
381 # TODO(dgarrett): Either work caching into this path before 349 return True
382 # we unpack, or remove zip support (sosa is considering).
383 # It does currently cache, but after the unpack.
384 350
385 if not self._UnpackZip(static_image_dir): 351 if not self._UnpackZip(static_image_dir):
386 _LogMessage('unzip image.zip failed.') 352 _LogMessage('unzip image.zip failed.')
387 return None 353 return False
388 354
389 return self.GenerateUpdateImageWithCache(image_path, 355 return self.GenerateUpdateImage(image_path, move_to_static_dir=False,
390 static_image_dir=static_image_dir) 356 static_image_dir=None)
391 357
392 def ImportFactoryConfigFile(self, filename, validate_checksums=False): 358 def ImportFactoryConfigFile(self, filename, validate_checksums=False):
393 """Imports a factory-floor server configuration file. The file should 359 """Imports a factory-floor server configuration file. The file should
394 be in this format: 360 be in this format:
395 config = [ 361 config = [
396 { 362 {
397 'qual_ids': set([1, 2, 3, "x86-generic"]), 363 'qual_ids': set([1, 2, 3, "x86-generic"]),
398 'factory_image': 'generic-factory.gz', 364 'factory_image': 'generic-factory.gz',
399 'factory_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=', 365 'factory_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
400 'release_image': 'generic-release.gz', 366 'release_image': 'generic-release.gz',
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 return self.GetNoUpdatePayload() 443 return self.GetNoUpdatePayload()
478 url = '%s/static/%s' % (self.hostname, filename) 444 url = '%s/static/%s' % (self.hostname, filename)
479 is_delta_format = self._IsDeltaFormatFile(filename) 445 is_delta_format = self._IsDeltaFormatFile(filename)
480 _LogMessage('returning update payload ' + url) 446 _LogMessage('returning update payload ' + url)
481 # Factory install is using memento updater which is using the sha-1 hash so 447 # Factory install is using memento updater which is using the sha-1 hash so
482 # setting sha-256 to an empty string. 448 # setting sha-256 to an empty string.
483 return self.GetUpdatePayload(checksum, '', size, url, is_delta_format) 449 return self.GetUpdatePayload(checksum, '', size, url, is_delta_format)
484 450
485 def GenerateUpdatePayloadForNonFactory(self, board_id, client_version, 451 def GenerateUpdatePayloadForNonFactory(self, board_id, client_version,
486 static_image_dir): 452 static_image_dir):
487 """Generates an update for non-factory image. 453 """Generates an update for non-factory and returns True on success."""
454 if self.use_cached and os.path.exists(os.path.join(static_image_dir,
455 'update.gz')):
456 _LogMessage('Using cached image regardless of timestamps.')
457 return True
458 else:
459 if self.forced_image:
460 has_built_image = self.GenerateUpdateImage(
461 self.forced_image, move_to_static_dir=True,
462 static_image_dir=static_image_dir)
463 return has_built_image
464 elif self.serve_only:
465 return self.GenerateImageFromZip(static_image_dir)
466 else:
467 if board_id:
468 return self.GenerateLatestUpdateImage(board_id,
469 client_version,
470 static_image_dir)
488 471
489 Returns: 472 _LogMessage('You must set --board for pre-generating latest update.')
490 file name relative to static_image_dir on success. 473 return False
491 """
492 if self.forced_image:
493 return self.GenerateUpdateImageWithCache(
494 self.forced_image,
495 static_image_dir=static_image_dir)
496 elif self.serve_only:
497 return self.GenerateImageFromZip(static_image_dir)
498 else:
499 if board_id:
500 return self.GenerateLatestUpdateImage(board_id,
501 client_version,
502 static_image_dir)
503
504 _LogMessage('You must set --board for pre-generating latest update.')
505 return None
506 474
507 def PreGenerateUpdate(self): 475 def PreGenerateUpdate(self):
508 """Pre-generates an update. Returns True on success. 476 """Pre-generates an update. Returns True on success."""
509 """
510 # Does not work with factory config. 477 # Does not work with factory config.
511 assert(not self.factory_config) 478 assert(not self.factory_config)
512 _LogMessage('Pre-generating the update payload.') 479 _LogMessage('Pre-generating the update payload.')
513 # Does not work with labels so just use static dir. 480 # Does not work with labels so just use static dir.
514 if self.GenerateUpdatePayloadForNonFactory(self.board, '0.0.0.0', 481 if self.GenerateUpdatePayloadForNonFactory(self.board, '0.0.0.0',
515 self.static_dir): 482 self.static_dir):
483 # Force the devserver to use the pre-generated payload.
484 self.use_cached = True
516 _LogMessage('Pre-generated update successfully.') 485 _LogMessage('Pre-generated update successfully.')
517 return True 486 return True
518 else: 487 else:
519 _LogMessage('Failed to pre-generate update.') 488 _LogMessage('Failed to pre-generate update.')
520 return False 489 return False
521 490
522 def HandleUpdatePing(self, data, label=None): 491 def HandleUpdatePing(self, data, label=None):
523 """Handles an update ping from an update client. 492 """Handles an update ping from an update client.
524 493
525 Args: 494 Args:
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 536
568 # Separate logic as Factory requests have static url's that override 537 # Separate logic as Factory requests have static url's that override
569 # other options. 538 # other options.
570 if self.factory_config: 539 if self.factory_config:
571 return self.HandleFactoryRequest(board_id, channel) 540 return self.HandleFactoryRequest(board_id, channel)
572 else: 541 else:
573 static_image_dir = self.static_dir 542 static_image_dir = self.static_dir
574 if label: 543 if label:
575 static_image_dir = os.path.join(static_image_dir, label) 544 static_image_dir = os.path.join(static_image_dir, label)
576 545
577 payload_path = self.GenerateUpdatePayloadForNonFactory(board_id, 546 if self.GenerateUpdatePayloadForNonFactory(board_id, client_version,
578 client_version, 547 static_image_dir):
579 static_image_dir) 548 filename = os.path.join(static_image_dir, 'update.gz')
580 if payload_path:
581 filename = os.path.join(static_image_dir, payload_path)
582 hash = self._GetHash(filename) 549 hash = self._GetHash(filename)
583 sha256 = self._GetSHA256(filename) 550 sha256 = self._GetSHA256(filename)
584 size = self._GetSize(filename) 551 size = self._GetSize(filename)
585 is_delta_format = self._IsDeltaFormatFile(filename) 552 is_delta_format = self._IsDeltaFormatFile(filename)
586 if label: 553 if label:
587 url = '%s/%s/%s' % (static_urlbase, label, payload_path) 554 url = '%s/%s/update.gz' % (static_urlbase, label)
588 else: 555 else:
589 url = '%s/%s' % (static_urlbase, payload_path) 556 url = '%s/update.gz' % static_urlbase
590 557
591 _LogMessage('Responding to client to use url %s to get image.' % url) 558 _LogMessage('Responding to client to use url %s to get image.' % url)
592 return self.GetUpdatePayload(hash, sha256, size, url, is_delta_format) 559 return self.GetUpdatePayload(hash, sha256, size, url, is_delta_format)
593 else: 560 else:
594 return self.GetNoUpdatePayload() 561 return self.GetNoUpdatePayload()
OLDNEW
« no previous file with comments | « no previous file | autoupdate_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698