blob: ddd0178d07f3ee3259b2b799660ad5d7aa99cc5d [file] [log] [blame]
Chris Sosa0356d3b2010-09-16 15:46:22 -07001# Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved.
rtc@google.comded22402009-10-26 22:36:21 +00002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
rtc@google.com64244662009-11-12 00:52:08 +00005from buildutil import BuildObject
rtc@google.comded22402009-10-26 22:36:21 +00006from xml.dom import minidom
7
8import os
Darin Petkov798fe7d2010-03-22 15:18:13 -07009import shutil
Darin Petkov2b2ff4b2010-07-27 15:02:09 -070010import time
rtc@google.comded22402009-10-26 22:36:21 +000011import web
12
Chris Sosa0356d3b2010-09-16 15:46:22 -070013
rtc@google.com64244662009-11-12 00:52:08 +000014class Autoupdate(BuildObject):
Chris Sosa0356d3b2010-09-16 15:46:22 -070015 """Class that contains functionality that handles Chrome OS update pings.
16
17 Members:
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.
20 factory_config: Path to the factory config file if handling factory
21 requests.
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.
24 client_prefix: The prefix for the update engine client.
25 forced_image: Path to an image to use for all updates.
26 """
rtc@google.comded22402009-10-26 22:36:21 +000027
Sean O'Connor1f7fd362010-04-07 16:34:52 -070028 def __init__(self, serve_only=None, test_image=False, urlbase=None,
Chris Sosa0356d3b2010-09-16 15:46:22 -070029 factory_config_path=None, client_prefix=None, forced_image=None,
Sean O'Connor1f7fd362010-04-07 16:34:52 -070030 *args, **kwargs):
Sean O'Connor14b6a0a2010-03-20 23:23:48 -070031 super(Autoupdate, self).__init__(*args, **kwargs)
Sean O'Connor1f7fd362010-04-07 16:34:52 -070032 self.serve_only = serve_only
Sean O'Connor1b4b0762010-06-02 17:37:32 -070033 self.factory_config = factory_config_path
Chris Sosa0356d3b2010-09-16 15:46:22 -070034 self.use_test_image = test_image
Sean O'Connor1f7fd362010-04-07 16:34:52 -070035 self.static_urlbase = urlbase
Chris Sosab63a9282010-09-02 10:43:23 -070036 self.client_prefix = client_prefix
Chris Sosa0356d3b2010-09-16 15:46:22 -070037 self.forced_image = forced_image
Sean O'Connor14b6a0a2010-03-20 23:23:48 -070038
Chris Sosa0356d3b2010-09-16 15:46:22 -070039 def _GetSecondsSinceMidnight(self):
40 """Returns the seconds since midnight as a decimal value."""
Darin Petkov2b2ff4b2010-07-27 15:02:09 -070041 now = time.localtime()
42 return now[3] * 3600 + now[4] * 60 + now[5]
43
Chris Sosa0356d3b2010-09-16 15:46:22 -070044 def _GetDefaultBoardID(self):
45 """Returns the default board id stored in .default_board."""
46 board_file = '%s/.default_board' % (self.scripts_dir)
47 try:
48 return open(board_file).read()
49 except IOError:
50 return 'x86-generic'
51
52 def _GetLatestImageDir(self, board_id):
53 """Returns the latest image dir based on shell script."""
54 cmd = '%s/get_latest_image.sh --board %s' % (self.scripts_dir, board_id)
55 return os.popen(cmd).read().strip()
56
57 def _GetVersionFromDir(self, image_dir):
58 """Returns the version of the image based on the name of the directory."""
59 latest_version = os.path.basename(image_dir)
60 return latest_version.split('-')[0]
61
62 def _CanUpdate(self, client_version, latest_version):
63 """Returns true if the latest_version is greater than the client_version."""
64 client_tokens = client_version.replace('_', '').split('.')
65 latest_tokens = latest_version.replace('_', '').split('.')
66 web.debug('client version %s latest version %s'
67 % (client_version, latest_version))
68 for i in range(4):
69 if int(latest_tokens[i]) == int(client_tokens[i]):
70 continue
71 return int(latest_tokens[i]) > int(client_tokens[i])
72 return False
73
74 def _UnpackStatefulPartition(self, image_path, stateful_file):
75 """Given an image, unpacks its stateful partition to stateful_file."""
76 image_dir = os.path.dirname(image_path)
77 image_file = os.path.basename(image_path)
78
79 get_offset = '$(cgpt show -b -i 1 %s)' % image_file
80 get_size = '$(cgpt show -s -i 1 %s)' % image_file
81 unpack_command = (
82 'cd %s && '
83 'dd if=%s of=%s bs=512 skip=%s count=%s' % (image_dir, image_file,
84 stateful_file, get_offset,
85 get_size))
86 web.debug(unpack_command)
87 return os.system(unpack_command) == 0
88
89 def _UnpackZip(self, image_dir):
90 """Unpacks an image.zip into a given directory."""
91 image = os.path.join(image_dir, self._GetImageName())
92 if os.path.exists(image):
93 return True
94 else:
95 # -n, never clobber an existing file, in case we get invoked
96 # simultaneously by multiple request handlers. This means that
97 # we're assuming each image.zip file lives in a versioned
98 # directory (a la Buildbot).
99 return os.system('cd %s && unzip -n image.zip' % image_dir) == 0
100
101 def _GetImageName(self):
102 """Returns the name of the image that should be used."""
103 if self.use_test_image:
104 image_name = 'chromiumos_test_image.bin'
105 else:
106 image_name = 'chromiumos_image.bin'
107 return image_name
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 web.debug('Usable cached image found.')
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 web.debug('No cached image found - image generation required.')
120 return True
121 else:
122 web.debug('Only cached image found to serve.')
123 return False
124
125 def _GetSize(self, update_path):
126 """Returns the size of the file given."""
127 return os.path.getsize(update_path)
128
129 def _GetHash(self, update_path):
130 """Returns the sha1 of the file given."""
131 cmd = ('cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';'
132 % update_path)
133 return os.popen(cmd).read().rstrip()
134
Darin Petkov91436cb2010-09-28 08:52:17 -0700135 # TODO(petkov): Consider optimizing getting both SHA-1 and SHA-256 so that
136 # it takes advantage of reduced I/O and multiple processors. Something like:
137 # % tee < FILE > /dev/null \
138 # >( openssl dgst -sha256 -binary | openssl base64 ) \
139 # >( openssl sha1 -binary | openssl base64 )
140 def _GetSHA256(self, update_path):
141 """Returns the sha256 of the file given."""
142 cmd = ('cat %s | openssl dgst -sha256 -binary | openssl base64' %
143 update_path)
144 return os.popen(cmd).read().rstrip()
145
146 def GetUpdatePayload(self, hash, sha256, size, url):
Chris Sosa0356d3b2010-09-16 15:46:22 -0700147 """Returns a payload to the client corresponding to a new update.
148
149 Args:
150 hash: hash of update blob
Darin Petkov91436cb2010-09-28 08:52:17 -0700151 sha256: SHA-256 hash of update blob
Chris Sosa0356d3b2010-09-16 15:46:22 -0700152 size: size of update blob
153 url: where to find update blob
154 Returns:
155 Xml string to be passed back to client.
156 """
rtc@google.com21a5ca32009-11-04 18:23:23 +0000157 payload = """<?xml version="1.0" encoding="UTF-8"?>
158 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
Darin Petkov2b2ff4b2010-07-27 15:02:09 -0700159 <daystart elapsed_seconds="%s"/>
rtc@google.com21a5ca32009-11-04 18:23:23 +0000160 <app appid="{%s}" status="ok">
161 <ping status="ok"/>
Sean O'Connor14b6a0a2010-03-20 23:23:48 -0700162 <updatecheck
163 codebase="%s"
164 hash="%s"
Darin Petkov91436cb2010-09-28 08:52:17 -0700165 sha256="%s"
Sean O'Connor14b6a0a2010-03-20 23:23:48 -0700166 needsadmin="false"
167 size="%s"
rtc@google.com21a5ca32009-11-04 18:23:23 +0000168 status="ok"/>
169 </app>
170 </gupdate>
171 """
Chris Sosa0356d3b2010-09-16 15:46:22 -0700172 return payload % (self._GetSecondsSinceMidnight(),
Darin Petkov91436cb2010-09-28 08:52:17 -0700173 self.app_id, url, hash, sha256, size)
rtc@google.comded22402009-10-26 22:36:21 +0000174
rtc@google.com21a5ca32009-11-04 18:23:23 +0000175 def GetNoUpdatePayload(self):
Chris Sosa0356d3b2010-09-16 15:46:22 -0700176 """Returns a payload to the client corresponding to no update."""
177 payload = """ < ?xml version = "1.0" encoding = "UTF-8"? >
178 < gupdate xmlns = "http://www.google.com/update2/response" protocol = "2.0" >
179 < daystart elapsed_seconds = "%s" />
180 < app appid = "{%s}" status = "ok" >
181 < ping status = "ok" />
182 < updatecheck status = "noupdate" />
183 </ app >
184 </ gupdate >
rtc@google.com21a5ca32009-11-04 18:23:23 +0000185 """
Chris Sosa0356d3b2010-09-16 15:46:22 -0700186 return payload % (self._GetSecondsSinceMidnight(), self.app_id)
rtc@google.comded22402009-10-26 22:36:21 +0000187
Chris Sosa0356d3b2010-09-16 15:46:22 -0700188 def GenerateUpdateFile(self, image_path):
189 """Generates an update gz given a full path to an image.
190
191 Args:
192 image_path: Full path to image.
193 Returns:
194 Path to created update_payload or None on error.
195 """
196 image_dir = os.path.dirname(image_path)
197 update_path = os.path.join(image_dir, 'update.gz')
198 web.debug('Generating update image %s' % update_path)
199
200 mkupdate_command = (
201 '%s/cros_generate_update_payload --image=%s --output=%s '
202 '--patch_kernel' % (self.scripts_dir, image_path, update_path))
203 if os.system(mkupdate_command) != 0:
204 web.debug('Failed to create base update file')
205 return None
206
207 return update_path
208
209 def GenerateStatefulFile(self, image_path):
210 """Generates a stateful update gz given a full path to an image.
211
212 Args:
213 image_path: Full path to image.
214 Returns:
215 Path to created stateful update_payload or None on error.
216 """
217 stateful_partition_path = '%s/stateful.image' % os.path.dirname(image_path)
218
219 # Unpack to get stateful partition.
220 if self._UnpackStatefulPartition(image_path, stateful_partition_path):
221 mkstatefulupdate_command = 'gzip -f %s' % stateful_partition_path
222 if os.system(mkstatefulupdate_command) == 0:
223 web.debug('Successfully generated %s.gz' % stateful_partition_path)
224 return '%s.gz' % stateful_partition_path
225
226 web.debug('Failed to create stateful update file')
227 return None
228
229 def MoveImagesToStaticDir(self, update_path, stateful_update_path,
230 static_image_dir):
231 """Moves gz files from their directories to serving directories.
232
233 Args:
234 update_path: full path to main update gz.
235 stateful_update_path: full path to stateful partition gz.
236 static_image_dir: where to put files.
237 Returns:
238 Returns True if the files were moved over successfully.
239 """
Andrew de los Reyes9a528712010-06-30 10:29:43 -0700240 try:
Chris Sosa0356d3b2010-09-16 15:46:22 -0700241 shutil.copy(update_path, static_image_dir)
242 shutil.copy(stateful_update_path, static_image_dir)
243 os.remove(update_path)
244 os.remove(stateful_update_path)
245 except Exception:
246 web.debug('Failed to move %s and %s to %s' % (update_path,
247 stateful_update_path,
248 static_image_dir))
249 return False
Andrew de los Reyes9a528712010-06-30 10:29:43 -0700250
rtc@google.com21a5ca32009-11-04 18:23:23 +0000251 return True
rtc@google.comded22402009-10-26 22:36:21 +0000252
Chris Sosa0356d3b2010-09-16 15:46:22 -0700253 def GenerateUpdateImage(self, image_path, move_to_static_dir=False,
254 static_image_dir=None):
255 """Force generates an update payload based on the given image_path.
rtc@google.comded22402009-10-26 22:36:21 +0000256
Chris Sosa0356d3b2010-09-16 15:46:22 -0700257 Args:
258 image_path: full path to the image.
259 move_to_static_dir: Moves the files from their dir to the static dir.
260 static_image_dir: the directory to move images to after generating.
261 Returns:
262 True if the update payload was created successfully.
263 """
264 web.debug('Generating update for image %s' % image_path)
265 update_path = self.GenerateUpdateFile(image_path)
266 stateful_update_path = self.GenerateStatefulFile(image_path)
267 if not update_path or not stateful_update_path:
268 web.debug('Failed to generate update')
269 return False
270
271 if move_to_static_dir:
272 return self.MoveImagesToStaticDir(update_path, stateful_update_path,
273 static_image_dir)
274 else:
275 return True
276
277 def GenerateLatestUpdateImage(self, board_id, client_version,
278 static_image_dir=None):
279 """Generates an update using the latest image that has been built.
280
281 This will only generate an update if the newest update is newer than that
282 on the client or client_version is 'ForcedUpdate'.
283
284 Args:
285 board_id: Name of the board.
286 client_version: Current version of the client or 'ForcedUpdate'
287 static_image_dir: the directory to move images to after generating.
288 Returns:
289 True if the update payload was created successfully.
290 """
291 latest_image_dir = self._GetLatestImageDir(board_id)
292 latest_version = self._GetVersionFromDir(latest_image_dir)
293 latest_image_path = os.path.join(latest_image_dir, self._GetImageName())
294
295 web.debug('Preparing to generate update from latest built image %s.' %
296 latest_image_path)
297
298 # Check to see whether or not we should update.
299 if client_version != 'ForcedUpdate' and not self._CanUpdate(
300 client_version, latest_version):
301 web.debug('no update')
302 return False
303
304 cached_file_path = os.path.join(static_image_dir, 'update.gz')
305 if (os.path.exists(cached_file_path) and
306 not self._IsImageNewerThanCached(latest_image_path, cached_file_path)):
307 return True
308
309 return self.GenerateUpdateImage(latest_image_path, move_to_static_dir=True,
310 static_image_dir=static_image_dir)
311
312 def GenerateImageFromZip(self, static_image_dir):
313 """Generates an update from an image zip file.
314
315 This method assumes you have an image.zip in directory you are serving
316 from. If this file is newer than a previously cached file, it will unzip
317 this file, create a payload and serve it.
318
319 Args:
320 static_image_dir: Directory where the zip file exists.
321 Returns:
322 True if the update payload was created successfully.
323 """
324 web.debug('Preparing to generate update from zip in %s.' % static_image_dir)
325 image_path = os.path.join(static_image_dir, self._GetImageName())
326 cached_file_path = os.path.join(static_image_dir, 'update.gz')
327 zip_file_path = os.path.join(static_image_dir, 'image.zip')
328 if not self._IsImageNewerThanCached(zip_file_path, cached_file_path):
329 return True
330
331 if not self._UnpackZip(static_image_dir):
332 web.debug('unzip image.zip failed.')
333 return False
334
335 return self.GenerateUpdateImage(image_path, move_to_static_dir=False,
336 static_image_dir=None)
Darin Petkov8ef83452010-03-23 16:52:29 -0700337
Andrew de los Reyes52620802010-04-12 13:40:07 -0700338 def ImportFactoryConfigFile(self, filename, validate_checksums=False):
339 """Imports a factory-floor server configuration file. The file should
340 be in this format:
341 config = [
342 {
343 'qual_ids': set([1, 2, 3, "x86-generic"]),
344 'factory_image': 'generic-factory.gz',
345 'factory_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
346 'release_image': 'generic-release.gz',
347 'release_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
348 'oempartitionimg_image': 'generic-oem.gz',
349 'oempartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Nick Sanderse1eea922010-05-19 22:17:08 -0700350 'efipartitionimg_image': 'generic-efi.gz',
351 'efipartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Andrew de los Reyes52620802010-04-12 13:40:07 -0700352 'stateimg_image': 'generic-state.gz',
Tom Wai-Hong Tam65fc6072010-05-20 11:44:26 +0800353 'stateimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Tom Wai-Hong Tamdac3df12010-06-14 09:56:15 +0800354 'firmware_image': 'generic-firmware.gz',
355 'firmware_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Andrew de los Reyes52620802010-04-12 13:40:07 -0700356 },
357 {
358 'qual_ids': set([6]),
359 'factory_image': '6-factory.gz',
360 'factory_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
361 'release_image': '6-release.gz',
362 'release_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
363 'oempartitionimg_image': '6-oem.gz',
364 'oempartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Nick Sanderse1eea922010-05-19 22:17:08 -0700365 'efipartitionimg_image': '6-efi.gz',
366 'efipartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Andrew de los Reyes52620802010-04-12 13:40:07 -0700367 'stateimg_image': '6-state.gz',
Tom Wai-Hong Tam65fc6072010-05-20 11:44:26 +0800368 'stateimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Tom Wai-Hong Tamdac3df12010-06-14 09:56:15 +0800369 'firmware_image': '6-firmware.gz',
370 'firmware_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
Andrew de los Reyes52620802010-04-12 13:40:07 -0700371 },
372 ]
373 The server will look for the files by name in the static files
374 directory.
Chris Sosaa73ec162010-05-03 20:18:02 -0700375
Andrew de los Reyes52620802010-04-12 13:40:07 -0700376 If validate_checksums is True, validates checksums and exits. If
377 a checksum mismatch is found, it's printed to the screen.
378 """
379 f = open(filename, 'r')
380 output = {}
381 exec(f.read(), output)
382 self.factory_config = output['config']
383 success = True
384 for stanza in self.factory_config:
Tom Wai-Hong Tam65fc6072010-05-20 11:44:26 +0800385 for key in stanza.copy().iterkeys():
386 suffix = '_image'
387 if key.endswith(suffix):
388 kind = key[:-len(suffix)]
Chris Sosa0356d3b2010-09-16 15:46:22 -0700389 stanza[kind + '_size'] = self._GetSize(os.path.join(
390 self.static_dir, stanza[kind + '_image']))
Tom Wai-Hong Tam65fc6072010-05-20 11:44:26 +0800391 if validate_checksums:
Chris Sosa0356d3b2010-09-16 15:46:22 -0700392 factory_checksum = self._GetHash(os.path.join(self.static_dir,
393 stanza[kind + '_image']))
Tom Wai-Hong Tam65fc6072010-05-20 11:44:26 +0800394 if factory_checksum != stanza[kind + '_checksum']:
Chris Sosa0356d3b2010-09-16 15:46:22 -0700395 print ('Error: checksum mismatch for %s. Expected "%s" but file '
396 'has checksum "%s".' % (stanza[kind + '_image'],
397 stanza[kind + '_checksum'],
398 factory_checksum))
Tom Wai-Hong Tam65fc6072010-05-20 11:44:26 +0800399 success = False
Chris Sosa0356d3b2010-09-16 15:46:22 -0700400
Andrew de los Reyes52620802010-04-12 13:40:07 -0700401 if validate_checksums:
402 if success is False:
403 raise Exception('Checksum mismatch in conf file.')
Chris Sosa0356d3b2010-09-16 15:46:22 -0700404
Andrew de los Reyes52620802010-04-12 13:40:07 -0700405 print 'Config file looks good.'
406
407 def GetFactoryImage(self, board_id, channel):
Nick Sanders723f3262010-09-16 05:18:41 -0700408 kind = channel.rsplit('-', 1)[0]
Andrew de los Reyes52620802010-04-12 13:40:07 -0700409 for stanza in self.factory_config:
410 if board_id not in stanza['qual_ids']:
411 continue
Nick Sanders15cd6ae2010-06-30 12:30:56 -0700412 if kind + '_image' not in stanza:
413 break
Andrew de los Reyes52620802010-04-12 13:40:07 -0700414 return (stanza[kind + '_image'],
415 stanza[kind + '_checksum'],
416 stanza[kind + '_size'])
Nick Sanders15cd6ae2010-06-30 12:30:56 -0700417 return (None, None, None)
rtc@google.comded22402009-10-26 22:36:21 +0000418
Chris Sosa0356d3b2010-09-16 15:46:22 -0700419 def HandleFactoryRequest(self, hostname, board_id, channel):
420 (filename, checksum, size) = self.GetFactoryImage(board_id, channel)
421 if filename is None:
422 web.debug('unable to find image for board %s' % board_id)
423 return self.GetNoUpdatePayload()
424 url = 'http://%s/static/%s' % (hostname, filename)
425 web.debug('returning update payload ' + url)
Darin Petkov91436cb2010-09-28 08:52:17 -0700426 # Factory install is using memento updater which is using the sha-1 hash so
427 # setting sha-256 to an empty string.
428 return self.GetUpdatePayload(checksum, '', size, url)
Chris Sosa0356d3b2010-09-16 15:46:22 -0700429
Sean O'Connor14b6a0a2010-03-20 23:23:48 -0700430 def HandleUpdatePing(self, data, label=None):
Chris Sosa0356d3b2010-09-16 15:46:22 -0700431 """Handles an update ping from an update client.
432
433 Args:
434 data: xml blob from client.
435 label: optional label for the update.
436 Returns:
437 Update payload message for client.
438 """
439 web.debug('handling update ping: %s' % data)
rtc@google.com21a5ca32009-11-04 18:23:23 +0000440 update_dom = minidom.parseString(data)
441 root = update_dom.firstChild
Chris Sosa0356d3b2010-09-16 15:46:22 -0700442
443 # Check the client prefix to make sure you can support this type of update.
444 if (root.hasAttribute('updaterversion') and
445 not root.getAttribute('updaterversion').startswith(self.client_prefix)):
446 web.debug('Got update from unsupported updater:' +
447 root.getAttribute('updaterversion'))
Andrew de los Reyes9223f132010-05-07 17:08:17 -0700448 return self.GetNoUpdatePayload()
Chris Sosa0356d3b2010-09-16 15:46:22 -0700449
450 # We only generate update payloads for updatecheck requests.
451 update_check = root.getElementsByTagName('o:updatecheck')
452 if not update_check:
453 web.debug('Non-update check received. Returning blank payload.')
454 # TODO(sosa): Generate correct non-updatecheck payload to better test
455 # update clients.
456 return self.GetNoUpdatePayload()
457
458 # Since this is an updatecheck, get information about the requester.
459 hostname = web.ctx.host
Sean O'Connor14b6a0a2010-03-20 23:23:48 -0700460 query = root.getElementsByTagName('o:app')[0]
Charlie Lee8c993082010-02-24 13:27:37 -0800461 client_version = query.getAttribute('version')
Andrew de los Reyes52620802010-04-12 13:40:07 -0700462 channel = query.getAttribute('track')
Chris Sosa0356d3b2010-09-16 15:46:22 -0700463 board_id = (query.hasAttribute('board') and query.getAttribute('board')
464 or self._GetDefaultBoardID())
Andrew de los Reyes52620802010-04-12 13:40:07 -0700465
Chris Sosa0356d3b2010-09-16 15:46:22 -0700466 # Separate logic as Factory requests have static url's that override
467 # other options.
Andrew de los Reyes52620802010-04-12 13:40:07 -0700468 if self.factory_config:
Chris Sosa0356d3b2010-09-16 15:46:22 -0700469 return self.HandleFactoryRequest(hostname, board_id, channel)
Nick Sanders723f3262010-09-16 05:18:41 -0700470 else:
Chris Sosa0356d3b2010-09-16 15:46:22 -0700471 static_image_dir = self.static_dir
472 if label:
473 static_image_dir = os.path.join(static_image_dir, label)
474
475 # Not for factory, find and serve the correct image given the options.
476 if self.forced_image:
477 has_built_image = self.GenerateUpdateImage(
478 self.forced_image, move_to_static_dir=True,
479 static_image_dir=static_image_dir)
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:
486 has_built_image = self.GenerateLatestUpdateImage(board_id,
487 client_version,
488 static_image_dir)
489
490 if has_built_image:
491 hash = self._GetHash(os.path.join(static_image_dir, 'update.gz'))
Darin Petkov91436cb2010-09-28 08:52:17 -0700492 sha256 = self._GetSHA256(os.path.join(static_image_dir, 'update.gz'))
Chris Sosa0356d3b2010-09-16 15:46:22 -0700493 size = self._GetSize(os.path.join(static_image_dir, 'update.gz'))
494 if self.static_urlbase and label:
495 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:
499 url = 'http://%s/static/update.gz' % hostname
Darin Petkov91436cb2010-09-28 08:52:17 -0700500 return self.GetUpdatePayload(hash, sha256, size, url)
Chris Sosa0356d3b2010-09-16 15:46:22 -0700501 else:
Nick Sanders723f3262010-09-16 05:18:41 -0700502 return self.GetNoUpdatePayload()