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

Side by Side Diff: src/platform/dev/autoupdate.py

Issue 1596016: Dev server: Support image hosting for factory install. (Closed)
Patch Set: support state,oem partitions Created 10 years, 8 months 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
« no previous file with comments | « no previous file | src/platform/dev/devserver.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 The Chromium OS Authors. All rights reserved. 1 # Copyright (c) 2009 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 sys
10 import web 11 import web
11 12
12 class Autoupdate(BuildObject): 13 class Autoupdate(BuildObject):
13 # Basic functionality of handling ChromeOS autoupdate pings 14 # Basic functionality of handling ChromeOS autoupdate pings
14 # and building/serving update images. 15 # and building/serving update images.
15 # TODO(rtc): Clean this code up and write some tests. 16 # TODO(rtc): Clean this code up and write some tests.
16 17
17 def __init__(self, serve_only=None, test_image=False, urlbase=None, 18 def __init__(self, serve_only=None, test_image=False, urlbase=None,
19 factory_config_path=None, validate_factory_config=None,
18 *args, **kwargs): 20 *args, **kwargs):
19 super(Autoupdate, self).__init__(*args, **kwargs) 21 super(Autoupdate, self).__init__(*args, **kwargs)
20 self.serve_only = serve_only 22 self.serve_only = serve_only
21 self.test_image=test_image 23 self.test_image=test_image
22 self.static_urlbase = urlbase 24 self.static_urlbase = urlbase
23 if serve_only: 25 if serve_only:
24 # If we're serving out of an archived build dir (e.g. a 26 # If we're serving out of an archived build dir (e.g. a
25 # buildbot), prepare this webserver's magic 'static/' dir with a 27 # buildbot), prepare this webserver's magic 'static/' dir with a
26 # link to the build archive. 28 # link to the build archive.
27 web.debug('Autoupdate in "serve update images only" mode.') 29 web.debug('Autoupdate in "serve update images only" mode.')
28 if os.path.exists('static/archive'): 30 if os.path.exists('static/archive'):
29 archive_symlink = os.readlink('static/archive') 31 archive_symlink = os.readlink('static/archive')
30 if archive_symlink != self.static_dir: 32 if archive_symlink != self.static_dir:
31 web.debug('removing stale symlink to %s' % self.static_dir) 33 web.debug('removing stale symlink to %s' % self.static_dir)
32 os.unlink('static/archive') 34 os.unlink('static/archive')
33 else: 35 else:
34 os.symlink(self.static_dir, 'static/archive') 36 os.symlink(self.static_dir, 'static/archive')
37 if factory_config_path is not None:
38 self.ImportFactoryConfigFile(factory_config_path, validate_factory_config)
35 39
36 def GetUpdatePayload(self, hash, size, url): 40 def GetUpdatePayload(self, hash, size, url):
37 payload = """<?xml version="1.0" encoding="UTF-8"?> 41 payload = """<?xml version="1.0" encoding="UTF-8"?>
38 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0"> 42 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
39 <app appid="{%s}" status="ok"> 43 <app appid="{%s}" status="ok">
40 <ping status="ok"/> 44 <ping status="ok"/>
41 <updatecheck 45 <updatecheck
42 codebase="%s" 46 codebase="%s"
43 hash="%s" 47 hash="%s"
44 needsadmin="false" 48 needsadmin="false"
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 web.debug('Unable to copy %s to %s' % (update_file, self.static_dir)) 135 web.debug('Unable to copy %s to %s' % (update_file, self.static_dir))
132 return False 136 return False
133 return True 137 return True
134 138
135 def GetSize(self, update_path): 139 def GetSize(self, update_path):
136 return os.path.getsize(update_path) 140 return os.path.getsize(update_path)
137 141
138 def GetHash(self, update_path): 142 def GetHash(self, update_path):
139 cmd = "cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';" \ 143 cmd = "cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';" \
140 % update_path 144 % update_path
141 web.debug(cmd) 145 return os.popen(cmd).read().rstrip()
142 return os.popen(cmd).read()
143 146
147 def ImportFactoryConfigFile(self, filename, validate_checksums=False):
148 """Imports a factory-floor server configuration file. The file should
149 be in this format:
150 config = [
151 {
152 'qual_ids': set([1, 2, 3, "x86-generic"]),
153 'factory_image': 'generic-factory.gz',
154 'factory_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
155 'release_image': 'generic-release.gz',
156 'release_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
157 'oempartitionimg_image': 'generic-oem.gz',
158 'oempartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
159 'stateimg_image': 'generic-state.gz',
160 'stateimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM='
161 },
162 {
163 'qual_ids': set([6]),
164 'factory_image': '6-factory.gz',
165 'factory_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
166 'release_image': '6-release.gz',
167 'release_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
168 'oempartitionimg_image': '6-oem.gz',
169 'oempartitionimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM=',
170 'stateimg_image': '6-state.gz',
171 'stateimg_checksum': 'AtiI8B64agHVN+yeBAyiNMX3+HM='
172 },
173 ]
174 The server will look for the files by name in the static files
175 directory.
176
177 If validate_checksums is True, validates checksums and exits. If
178 a checksum mismatch is found, it's printed to the screen.
179 """
180 f = open(filename, 'r')
181 output = {}
182 exec(f.read(), output)
183 self.factory_config = output['config']
184 success = True
185 for stanza in self.factory_config:
186 for kind in ('factory', 'oempartitionimg', 'release', 'stateimg'):
187 stanza[kind + '_size'] = \
188 os.path.getsize(self.static_dir + '/' + stanza[kind + '_image'])
189 if validate_checksums:
190 factory_checksum = self.GetHash(self.static_dir + '/' +
191 stanza[kind + '_image'])
192 if factory_checksum != stanza[kind + '_checksum']:
193 print 'Error: checksum mismatch for %s. Expected "%s" but file ' \
194 'has checksum "%s".' % (stanza[kind + '_image'],
195 stanza[kind + '_checksum'],
196 factory_checksum)
197 success = False
198 if validate_checksums:
199 if success is False:
200 raise Exception('Checksum mismatch in conf file.')
201 print 'Config file looks good.'
202
203 def GetFactoryImage(self, board_id, channel):
204 kind = channel.rsplit('-', 1)[0]
205 for stanza in self.factory_config:
206 if board_id not in stanza['qual_ids']:
207 continue
208 return (stanza[kind + '_image'],
209 stanza[kind + '_checksum'],
210 stanza[kind + '_size'])
144 211
145 def HandleUpdatePing(self, data, label=None): 212 def HandleUpdatePing(self, data, label=None):
213 web.debug('handle update ping')
146 update_dom = minidom.parseString(data) 214 update_dom = minidom.parseString(data)
147 root = update_dom.firstChild 215 root = update_dom.firstChild
148 query = root.getElementsByTagName('o:app')[0] 216 query = root.getElementsByTagName('o:app')[0]
149 client_version = query.getAttribute('version') 217 client_version = query.getAttribute('version')
218 channel = query.getAttribute('track')
150 board_id = query.hasAttribute('board') and query.getAttribute('board') \ 219 board_id = query.hasAttribute('board') and query.getAttribute('board') \
151 or 'x86-generic' 220 or 'x86-generic'
152 latest_image_path = self.GetLatestImagePath(board_id) 221 latest_image_path = self.GetLatestImagePath(board_id)
153 latest_version = self.GetLatestVersion(latest_image_path) 222 latest_version = self.GetLatestVersion(latest_image_path)
223 hostname = web.ctx.host
224
225 # If this is a factory floor server, return the image here:
226 if self.factory_config:
227 (filename, checksum, size) = \
228 self.GetFactoryImage(board_id, channel)
229 if filename is None:
230 web.debug('unable to find image for board %s' % board_id)
231 return self.GetNoUpdatePayload()
232 url = 'http://%s/static/%s' % (hostname, filename)
233 web.debug('returning update payload ' + url)
234 return self.GetUpdatePayload(checksum, size, url)
235
154 if client_version != 'ForcedUpdate' \ 236 if client_version != 'ForcedUpdate' \
155 and not self.CanUpdate(client_version, latest_version): 237 and not self.CanUpdate(client_version, latest_version):
156 web.debug('no update') 238 web.debug('no update')
157 return self.GetNoUpdatePayload() 239 return self.GetNoUpdatePayload()
158 hostname = web.ctx.host
159 if label: 240 if label:
160 web.debug('Client requested version %s' % label) 241 web.debug('Client requested version %s' % label)
161 # Check that matching build exists 242 # Check that matching build exists
162 image_path = '%s/%s' % (self.static_dir, label) 243 image_path = '%s/%s' % (self.static_dir, label)
163 if not os.path.exists(image_path): 244 if not os.path.exists(image_path):
164 web.debug('%s not found.' % image_path) 245 web.debug('%s not found.' % image_path)
165 return self.GetNoUpdatePayload() 246 return self.GetNoUpdatePayload()
166 # Construct a response 247 # Construct a response
167 ok = self.BuildUpdateImage(image_path) 248 ok = self.BuildUpdateImage(image_path)
168 if ok != True: 249 if ok != True:
(...skipping 18 matching lines...) Expand all
187 ok = self.BuildUpdateImage(latest_image_path) 268 ok = self.BuildUpdateImage(latest_image_path)
188 if ok != True: 269 if ok != True:
189 web.debug('Failed to build an update image') 270 web.debug('Failed to build an update image')
190 return self.GetNoUpdatePayload() 271 return self.GetNoUpdatePayload()
191 272
192 hash = self.GetHash('%s/update.gz' % self.static_dir) 273 hash = self.GetHash('%s/update.gz' % self.static_dir)
193 size = self.GetSize('%s/update.gz' % self.static_dir) 274 size = self.GetSize('%s/update.gz' % self.static_dir)
194 275
195 url = 'http://%s/static/update.gz' % hostname 276 url = 'http://%s/static/update.gz' % hostname
196 return self.GetUpdatePayload(hash, size, url) 277 return self.GetUpdatePayload(hash, size, url)
OLDNEW
« no previous file with comments | « no previous file | src/platform/dev/devserver.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698