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

Side by Side Diff: bin/cros_image_to_target.py

Issue 5176002: cros_image_to_target handles test images, less verbose (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.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 | cros_generate_update_payload » ('j') | cros_generate_update_payload » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # 2 #
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Create and copy update image to target host. 7 """Create and copy update image to target host.
8 8
9 auto-update and devserver change out from beneath us often enough 9 auto-update and devserver change out from beneath us often enough
10 that despite having to duplicate a litte code, it seems that the 10 that despite having to duplicate a litte code, it seems that the
11 right thing to do here is to start over and do something that is 11 right thing to do here is to start over and do something that is
12 simple enough and easy enough to understand so that when more 12 simple enough and easy enough to understand so that when more
13 stuff breaks, at least we can solve them faster. 13 stuff breaks, at least we can solve them faster.
14 """ 14 """
15 15
16 import BaseHTTPServer 16 import BaseHTTPServer
17 import cgi 17 import cgi
18 import errno 18 import errno
19 import optparse 19 import optparse
20 import os 20 import os
21 import signal 21 import signal
22 import subprocess 22 import subprocess
23 import sys 23 import sys
24 import tempfile 24 import tempfile
25 import time 25 import time
26 import traceback 26 import traceback
27 27
28 from xml.dom import minidom 28 from xml.dom import minidom
29 29
30 30
31 # This is the default filename within the image directory to load updates from 31 # This is the default filename within the image directory to load updates from
32 DEFAULT_IMAGE_NAME = 'chromiumos_image.bin' 32 DEFAULT_IMAGE_NAME = 'chromiumos_image.bin'
33 DEFAULT_IMAGE_NAME_TEST = 'chromiumos_test_image.bin'
33 34
34 # The filenames we provide to clients to pull updates 35 # The filenames we provide to clients to pull updates
35 UPDATE_FILENAME = 'update.gz' 36 UPDATE_FILENAME = 'update.gz'
36 STATEFUL_FILENAME = 'stateful.image.gz' 37 STATEFUL_FILENAME = 'stateful.gz'
37 38
38 # How long do we wait for the server to start before launching client 39 # How long do we wait for the server to start before launching client
39 SERVER_STARTUP_WAIT = 1 40 SERVER_STARTUP_WAIT = 1
40 41
41 42
42 class Command(object): 43 class Command(object):
43 """Shell command ease-ups for Python.""" 44 """Shell command ease-ups for Python."""
44 45
45 def __init__(self, env): 46 def __init__(self, env):
46 self.env = env 47 self.env = env
47 48
48 def RunPipe(self, pipeline, infile=None, outfile=None, 49 def RunPipe(self, pipeline, infile=None, outfile=None,
49 capture=False, oneline=False): 50 capture=False, oneline=False, hide_stderr=False):
50 """Perform a command pipeline, with optional input/output filenames.""" 51 """
52 Perform a command pipeline, with optional input/output filenames.
53
54 hide_stderr Don't allow output of stderr (default False)
55 """
51 56
52 last_pipe = None 57 last_pipe = None
53 while pipeline: 58 while pipeline:
54 cmd = pipeline.pop(0) 59 cmd = pipeline.pop(0)
55 kwargs = {} 60 kwargs = {}
56 if last_pipe is not None: 61 if last_pipe is not None:
57 kwargs['stdin'] = last_pipe.stdout 62 kwargs['stdin'] = last_pipe.stdout
58 elif infile: 63 elif infile:
59 kwargs['stdin'] = open(infile, 'rb') 64 kwargs['stdin'] = open(infile, 'rb')
60 if pipeline or capture: 65 if pipeline or capture:
61 kwargs['stdout'] = subprocess.PIPE 66 kwargs['stdout'] = subprocess.PIPE
62 elif outfile: 67 elif outfile:
63 kwargs['stdout'] = open(outfile, 'wb') 68 kwargs['stdout'] = open(outfile, 'wb')
69 if hide_stderr:
70 kwargs['stderr'] = open('/dev/null', 'wb')
64 71
65 self.env.Info('Running: %s' % ' '.join(cmd)) 72 self.env.Debug('Running: %s' % ' '.join(cmd))
66 last_pipe = subprocess.Popen(cmd, **kwargs) 73 last_pipe = subprocess.Popen(cmd, **kwargs)
67 74
68 if capture: 75 if capture:
69 ret = last_pipe.communicate()[0] 76 ret = last_pipe.communicate()[0]
70 if not ret: 77 if not ret:
71 return None 78 return None
72 elif oneline: 79 elif oneline:
73 return ret.rstrip('\r\n') 80 return ret.rstrip('\r\n')
74 else: 81 else:
75 return ret 82 return ret
(...skipping 27 matching lines...) Expand all
103 self.known_hosts = os.path.join(self.ssh_dir, 'known-hosts') 110 self.known_hosts = os.path.join(self.ssh_dir, 'known-hosts')
104 111
105 def Cleanup(self): 112 def Cleanup(self):
106 Command.RunPipe(self, [['rm', '-rf', self.ssh_dir]]) 113 Command.RunPipe(self, [['rm', '-rf', self.ssh_dir]])
107 self.ssh_dir = None 114 self.ssh_dir = None
108 115
109 def GetArgs(self): 116 def GetArgs(self):
110 if not self.ssh_dir: 117 if not self.ssh_dir:
111 self.Setup() 118 self.Setup()
112 119
120 # allow up to 3 seconds to connect
113 return ['-o', 'Compression=no', 121 return ['-o', 'Compression=no',
114 '-o', 'ConnectTimeout=%d' % self.CONNECT_TIMEOUT, 122 '-o', 'ConnectTimeout=%d' % self.CONNECT_TIMEOUT,
115 '-o', 'StrictHostKeyChecking=no', 123 '-o', 'StrictHostKeyChecking=no',
124 '-o', 'ConnectTimeout=3',
Paul Stewart 2010/11/17 21:37:36 Look 2 lines above this one. I even used a consta
sjg 2010/11/23 18:49:14 Sorry! It was hanging at some point and I was look
116 '-o', 'UserKnownHostsFile=%s' % self.known_hosts, 125 '-o', 'UserKnownHostsFile=%s' % self.known_hosts,
117 '-i', self.identity] 126 '-i', self.identity]
118 127
119 def RunPipe(self, pipeline, **kwargs): 128 def RunPipe(self, pipeline, **kwargs):
120 args = ['ssh'] + self.GetArgs() 129 args = ['ssh'] + self.GetArgs()
121 if 'remote_tunnel' in kwargs: 130 if 'remote_tunnel' in kwargs:
122 ports = kwargs.pop('remote_tunnel') 131 ports = kwargs.pop('remote_tunnel')
123 args += ['-R %d:localhost:%d' % ports] 132 args += ['-R %d:localhost:%d' % ports]
124 pipeline[0] = args + ['root@%s' % self.remote] + list(pipeline[0]) 133 pipeline[0] = args + ['root@%s' % self.remote] + list(pipeline[0])
125 return Command.RunPipe(self, pipeline, **kwargs) 134 return Command.RunPipe(self, pipeline, **kwargs)
126 135
127 def Reset(self): 136 def Reset(self):
128 os.unlink(self.known_hosts) 137 os.unlink(self.known_hosts)
129 138
130 def Copy(self, src, dest): 139 def Copy(self, src, dest):
131 return Command.RunPipe(self, [['scp'] + self.GetArgs() + 140 return Command.RunPipe(self, [['scp'] + self.GetArgs() +
132 [src, 'root@%s:%s' % 141 [src, 'root@%s:%s' %
133 (self.remote, dest)]]) 142 (self.remote, dest)]])
134 143
135 144
136 class CrosEnv(object): 145 class CrosEnv(object):
137 """Encapsulates the ChromeOS build system environment functionality.""" 146 """Encapsulates the ChromeOS build system environment functionality."""
138 147
139 REBOOT_START_WAIT = 5 148 REBOOT_START_WAIT = 5
140 REBOOT_WAIT_TIME = 60 149 REBOOT_WAIT_TIME = 60
141 150
142 def __init__(self, verbose=False): 151 def __init__(self, verbose=0):
143 self.cros_root = os.path.dirname(os.path.abspath(sys.argv[0])) 152 self.cros_root = os.path.dirname(os.path.abspath(sys.argv[0]))
144 parent = os.path.dirname(self.cros_root) 153 parent = os.path.dirname(self.cros_root)
145 if os.path.exists(os.path.join(parent, 'chromeos-common.sh')): 154 if os.path.exists(os.path.join(parent, 'chromeos-common.sh')):
146 self.cros_root = parent 155 self.cros_root = parent
147 self.cmd = Command(self) 156 self.cmd = Command(self)
148 self.verbose = verbose 157 self.verbose = verbose
149 158
159 # do we have the pv progress tool? (sudo apt-get install pv)
160 self.have_pv = (os.system('pv --help >/dev/null 2>&1') == 0)
Paul Stewart 2010/11/17 21:37:36 I'd much prefer if you used self.cmd.Run() instead
161
150 def Error(self, msg): 162 def Error(self, msg):
151 print >> sys.stderr, 'ERROR: %s' % msg 163 print >> sys.stderr, 'ERROR: %s' % msg
152 164
153 def Fatal(self, msg=None): 165 def Fatal(self, msg=None):
154 if msg: 166 if msg:
155 self.Error(msg) 167 self.Error(msg)
156 sys.exit(1) 168 sys.exit(1)
157 169
158 def Info(self, msg): 170 def Info(self, msg):
159 if self.verbose: 171 if self.verbose > 0:
160 print 'INFO: %s' % msg 172 print 'INFO: %s' % msg
161 173
174 def Debug(self, msg):
175 if self.verbose > 1:
176 print 'DEBUG: %s' % msg
177
162 def CrosUtilsPath(self, filename): 178 def CrosUtilsPath(self, filename):
163 return os.path.join(self.cros_root, filename) 179 return os.path.join(self.cros_root, filename)
164 180
165 def ChrootPath(self, filename): 181 def ChrootPath(self, filename):
166 return self.CrosUtilsPath(os.path.join('..', '..', 'chroot', 182 return self.CrosUtilsPath(os.path.join('..', '..', 'chroot',
167 filename.strip(os.path.sep))) 183 filename.strip(os.path.sep)))
168 184
169 def FileOneLine(self, filename): 185 def FileOneLine(self, filename):
170 return file(filename).read().rstrip('\r\n') 186 return file(filename).read().rstrip('\r\n')
171 187
(...skipping 20 matching lines...) Expand all
192 208
193 return True 209 return True
194 210
195 def BuildStateful(self, src, dst): 211 def BuildStateful(self, src, dst):
196 """Create a stateful partition update image.""" 212 """Create a stateful partition update image."""
197 213
198 if self.GetCached(src, dst): 214 if self.GetCached(src, dst):
199 self.Info('Using cached stateful %s' % dst) 215 self.Info('Using cached stateful %s' % dst)
200 return True 216 return True
201 217
218 self.Info('Building stateful')
202 cgpt = self.ChrootPath('/usr/bin/cgpt') 219 cgpt = self.ChrootPath('/usr/bin/cgpt')
203 offset = self.cmd.OutputOneLine(cgpt, 'show', '-b', '-i', '1', src) 220 offset = self.cmd.OutputOneLine(cgpt, 'show', '-b', '-i', '1', src)
204 size = self.cmd.OutputOneLine(cgpt, 'show', '-s', '-i', '1', src) 221 size = self.cmd.OutputOneLine(cgpt, 'show', '-s', '-i', '1', src)
205 if None in (size, offset): 222 if None in (size, offset):
206 self.Error('Unable to use cgpt to get image geometry') 223 self.Error('Unable to use cgpt to get image geometry')
207 return False 224 return False
208 225
209 return self.cmd.RunPipe([['dd', 'if=%s' % src, 'bs=512', 226 return self.cmd.RunPipe([
210 'skip=%s' % offset, 'count=%s' % size], 227 ['dd', 'if=%s' % src, 'bs=512', 'skip=%s' % offset, 'count=%s' % size],
211 ['gzip', '-c']], outfile=dst) 228 self.have_pv and ['pv', '-s', '%d' % (int (size) * 512)] or ['cat'],
229 ['gzip', '-c']], outfile=dst, hide_stderr=True)
212 230
213 def GetSize(self, filename): 231 def GetSize(self, filename):
214 return os.path.getsize(filename) 232 return os.path.getsize(filename)
215 233
216 def GetHash(self, filename): 234 def GetHash(self, filename):
217 return self.cmd.RunPipe([['openssl', 'sha1', '-binary'], 235 return self.cmd.RunPipe([['openssl', 'sha1', '-binary'],
218 ['openssl', 'base64']], 236 ['openssl', 'base64']],
219 infile=filename, 237 infile=filename,
220 capture=True, oneline=True) 238 capture=True, oneline=True)
221 239
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 def CreateServer(self, port, update_file, stateful_file): 273 def CreateServer(self, port, update_file, stateful_file):
256 """Start the devserver clone.""" 274 """Start the devserver clone."""
257 275
258 PingUpdateResponse.Setup(self.GetHash(update_file), 276 PingUpdateResponse.Setup(self.GetHash(update_file),
259 self.GetSha256(update_file), 277 self.GetSha256(update_file),
260 self.GetSize(update_file)) 278 self.GetSize(update_file))
261 279
262 UpdateHandler.SetupUrl('/update', PingUpdateResponse()) 280 UpdateHandler.SetupUrl('/update', PingUpdateResponse())
263 UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME, 281 UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME,
264 FileUpdateResponse(update_file, 282 FileUpdateResponse(update_file,
265 verbose=self.verbose)) 283 verbose=self.verbose, have_pv=self .have_pv))
adlr 2010/11/17 20:43:25 80 cols here, and a couple lines down as well
266 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME, 284 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME,
267 FileUpdateResponse(stateful_file, 285 FileUpdateResponse(stateful_file,
268 verbose=self.verbose)) 286 verbose=self.verbose, have_pv=self .have_pv))
269 287
270 self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler) 288 self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler)
271 289
272 def StartServer(self): 290 def StartServer(self):
273 self.Info('Starting http server') 291 self.Info('Starting http server')
274 self.http_server.serve_forever() 292 self.http_server.serve_forever()
275 293
276 def GetUpdateStatus(self): 294 def GetUpdateStatus(self):
277 status = self.ssh_cmd.Output('/usr/bin/update_engine_client', '--status') 295 status = self.ssh_cmd.Output('/usr/bin/update_engine_client', '--status')
278 if not status: 296 if not status:
(...skipping 18 matching lines...) Expand all
297 self.Info('Client has not yet restarted (try %d). Waiting...' % attempt) 315 self.Info('Client has not yet restarted (try %d). Waiting...' % attempt)
298 wait_time = SSHCommand.CONNECT_TIMEOUT - (time.time() - start) 316 wait_time = SSHCommand.CONNECT_TIMEOUT - (time.time() - start)
299 if wait_time > 0: 317 if wait_time > 0:
300 time.sleep(wait_time) 318 time.sleep(wait_time)
301 319
302 return False 320 return False
303 321
304 def StartClient(self, port): 322 def StartClient(self, port):
305 """Ask the client machine to update from our server.""" 323 """Ask the client machine to update from our server."""
306 324
325 self.Info ("Starting client...")
adlr 2010/11/17 20:43:25 i think style says no spaces between method name a
307 status = self.GetUpdateStatus() 326 status = self.GetUpdateStatus()
308 if status != 'UPDATE_STATUS_IDLE': 327 if status != 'UPDATE_STATUS_IDLE':
309 self.Error('Client update status is not IDLE: %s' % status) 328 self.Error('Client update status is not IDLE: %s' % status)
310 return False 329 return False
311 330
312 url_base = 'http://localhost:%d' % port 331 url_base = 'http://localhost:%d' % port
313 update_url = '%s/update' % url_base 332 update_url = '%s/update' % url_base
314 fd, update_log = tempfile.mkstemp(prefix='image-to-target-') 333 fd, update_log = tempfile.mkstemp(prefix='image-to-target-')
315 self.Info('Starting update on client. Client output stored to %s' % 334 self.Info('Starting update on client. Client output stored to %s' %
316 update_log) 335 update_log)
317 self.ssh_cmd.Run('/usr/bin/update_engine_client', '--update', 336 self.ssh_cmd.Run('/usr/bin/update_engine_client', '--update',
318 '--omaha_url', update_url, remote_tunnel=(port, port), 337 '--omaha_url', update_url, remote_tunnel=(port, port),
319 outfile=update_log) 338 outfile=update_log)
320 339
321 if self.GetUpdateStatus() != 'UPDATE_STATUS_UPDATED_NEED_REBOOT': 340 if self.GetUpdateStatus() != 'UPDATE_STATUS_UPDATED_NEED_REBOOT':
322 self.Error('Client update failed') 341 self.Error('Client update failed')
323 return False 342 return False
324 343
344 self.Info('Update complete - running update script on client')
325 self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'), 345 self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'),
326 '/tmp') 346 '/tmp')
327 if not self.ssh_cmd.Run('/tmp/stateful_update', url_base, 347 if not self.ssh_cmd.Run('/tmp/stateful_update', url_base,
328 remote_tunnel=(port, port)): 348 remote_tunnel=(port, port)):
329 self.Error('Client stateful update failed') 349 self.Error('Client stateful update failed')
330 return False 350 return False
331 351
332 self.Info('Rebooting client') 352 self.Info('Rebooting client')
333 if not self.ClientReboot(): 353 if not self.ClientReboot():
334 self.Error('Client may not have successfully rebooted...') 354 self.Error('Client may not have successfully rebooted...')
335 return False 355 return False
336 356
337 print 'Client update completed successfully!' 357 self.Info ('Client update completed successfully!')
adlr 2010/11/17 20:43:25 remove space before (
338 return True 358 return True
339 359
340 360
341 class UpdateResponse(object): 361 class UpdateResponse(object):
342 """Default response is the 404 error response.""" 362 """Default response is the 404 error response."""
343 363
344 def Reply(self, handler, send_content=True, post_data=None): 364 def Reply(self, handler, send_content=True, post_data=None):
345 handler.send_Error(404, 'File not found') 365 handler.send_error(404, 'File not found')
346 return None 366 return None
347 367
348 368
349 class FileUpdateResponse(UpdateResponse): 369 class FileUpdateResponse(UpdateResponse):
350 """Respond by sending the contents of a file.""" 370 """Respond by sending the contents of a file."""
351 371
352 def __init__(self, filename, content_type='application/octet-stream', 372 def __init__(self, filename, content_type='application/octet-stream',
353 verbose=False, blocksize=16*1024): 373 verbose=False, blocksize=16*1024, have_pv=False):
354 self.filename = filename 374 self.filename = filename
355 self.content_type = content_type 375 self.content_type = content_type
356 self.verbose = verbose 376 self.verbose = verbose
357 self.blocksize = blocksize 377 self.blocksize = blocksize
378 self.have_pv = have_pv
358 379
359 def Reply(self, handler, send_content=True, post_data=None): 380 def Reply(self, handler, send_content=True, post_data=None):
360 """Return file contents to the client. Optionally display progress.""" 381 """Return file contents to the client. Optionally display progress."""
361 382
362 try: 383 try:
363 f = open(self.filename, 'rb') 384 f = open(self.filename, 'rb')
364 except IOError: 385 except IOError:
365 return UpdateResponse.Reply(self, handler) 386 return UpdateResponse.Reply(self, handler)
366 387
367 handler.send_response(200) 388 handler.send_response(200)
368 handler.send_header('Content-type', self.content_type) 389 handler.send_header('Content-type', self.content_type)
369 filestat = os.fstat(f.fileno()) 390 filestat = os.fstat(f.fileno())
370 filesize = filestat[6] 391 filesize = filestat[6]
371 handler.send_header('Content-Length', str(filesize)) 392 handler.send_header('Content-Length', str(filesize))
372 handler.send_header('Last-Modified', 393 handler.send_header('Last-Modified',
373 handler.date_time_string(filestat.st_mtime)) 394 handler.date_time_string(filestat.st_mtime))
374 handler.end_headers() 395 handler.end_headers()
375 396
376 if not send_content: 397 if send_content:
377 return 398 try:
399 fp = False
400 sent_size = 0
401 sent_percentage = None
378 402
379 if filesize <= self.blocksize: 403 # sadly pv gets caught up in the server, not sure why
380 handler.wfile.write(f.read()) 404 if False: # self.verbose and self.have_pv:
381 else: 405 fp = subprocess.Popen('pv -s %d >/dev/null' % filesize,
382 sent_size = 0 406 shell=True, stdin=subprocess.PIPE, close_fds=True).stdin
383 sent_percentage = None 407 while True:
384 while True: 408 buf = f.read(self.blocksize)
385 buf = f.read(self.blocksize) 409 if not buf:
386 if not buf: 410 break
387 break 411 handler.wfile.write(buf)
388 handler.wfile.write(buf) 412 if fp:
389 if self.verbose: 413 fp.write(buf)
390 sent_size += len(buf) 414 if self.verbose:
391 percentage = int(100 * sent_size / filesize) 415 sent_size += len(buf)
392 if sent_percentage != percentage: 416 percentage = int(100 * sent_size / filesize)
393 sent_percentage = percentage 417 if sent_percentage != percentage:
394 print '\rSent %d%%' % sent_percentage, 418 sent_percentage = percentage
395 sys.stdout.flush() 419 print '\rSent %d%%' % sent_percentage,
420 sys.stdout.flush()
421 finally:
422 if fp:
423 fp.close()
Paul Stewart 2010/11/17 21:37:36 If you can't get it working, please revert this ch
396 if self.verbose: 424 if self.verbose:
397 print '\n' 425 print '\n'
398 f.close() 426 f.close()
399 427
400 428
401 class StringUpdateResponse(UpdateResponse): 429 class StringUpdateResponse(UpdateResponse):
402 """Respond by sending the contents of a string.""" 430 """Respond by sending the contents of a string."""
403 431
404 def __init__(self, string, content_type='text/plain'): 432 def __init__(self, string, content_type='text/plain'):
405 self.string = string 433 self.string = string
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 usage = 'usage: %prog [options]' 577 usage = 'usage: %prog [options]'
550 parser = optparse.OptionParser(usage=usage) 578 parser = optparse.OptionParser(usage=usage)
551 parser.add_option('--board', dest='board', default=None, 579 parser.add_option('--board', dest='board', default=None,
552 help='Board platform type') 580 help='Board platform type')
553 parser.add_option('--force-mismatch', dest='force_mismatch', default=False, 581 parser.add_option('--force-mismatch', dest='force_mismatch', default=False,
554 action='store_true', 582 action='store_true',
555 help='Upgrade even if client arch does not match') 583 help='Upgrade even if client arch does not match')
556 parser.add_option('--from', dest='src', default=None, 584 parser.add_option('--from', dest='src', default=None,
557 help='Source image to install') 585 help='Source image to install')
558 parser.add_option('--image-name', dest='image_name', 586 parser.add_option('--image-name', dest='image_name',
559 default=DEFAULT_IMAGE_NAME,
560 help='Filename within image directory to load') 587 help='Filename within image directory to load')
561 parser.add_option('--port', dest='port', default=8081, type='int', 588 parser.add_option('--port', dest='port', default=8081, type='int',
562 help='TCP port to serve from and tunnel through') 589 help='TCP port to serve from and tunnel through')
563 parser.add_option('--remote', dest='remote', default=None, 590 parser.add_option('--remote', dest='remote', default=None,
564 help='Remote device-under-test IP address') 591 help='Remote device-under-test IP address')
565 parser.add_option('--server-only', dest='server_only', default=False, 592 parser.add_option('--server-only', dest='server_only', default=False,
566 action='store_true', help='Do not start client') 593 action='store_true', help='Do not start client')
567 parser.add_option('--verbose', dest='verbose', default=False, 594 parser.add_option('--verbose', dest='verbose', default=False,
595 action='store_true', help='Display progress')
596 parser.add_option('--debug', dest='debug', default=False,
568 action='store_true', help='Display running commands') 597 action='store_true', help='Display running commands')
598 parser.add_option('--test', dest='test', default=False,
599 action='store_true', help='Select test image')
569 600
570 (options, args) = parser.parse_args(argv) 601 (options, args) = parser.parse_args(argv)
571 602
572 cros_env = CrosEnv(verbose=options.verbose) 603 # we can build the test image if it doesn't exist, so remember if we want to
604 maybe_build_test = False
adlr 2010/11/17 20:43:25 it would be nice to have a variable name that more
605
606 verbosity = 0
607 if options.verbose:
608 verbosity = 1
609 if options.debug:
610 verbosity = 2
611 cros_env = CrosEnv(verbose=verbosity)
573 612
574 if not options.board: 613 if not options.board:
575 options.board = cros_env.GetDefaultBoard() 614 options.board = cros_env.GetDefaultBoard()
576 615
577 if not options.src: 616 if not options.src:
578 options.src = cros_env.GetLatestImage(options.board) 617 options.src = cros_env.GetLatestImage(options.board)
579 if options.src is None: 618 if options.src is None:
580 parser.error('No --from argument given and no default image found') 619 parser.error('No --from argument given and no default image found')
581 620
582 cros_env.Info('Performing update from %s' % options.src) 621 cros_env.Info('Performing update from %s' % options.src)
583 622
584 if not os.path.exists(options.src): 623 if not os.path.exists(options.src):
585 parser.error('Path %s does not exist' % options.src) 624 parser.error('Path %s does not exist' % options.src)
586 625
626 if not options.image_name:
627 # auto-select the correct image
628 if options.test:
629 options.image_name = DEFAULT_IMAGE_NAME_TEST
630
631 # we will build the test image if not found
632 maybe_build_test = True
633 else:
634 options.image_name = DEFAULT_IMAGE_NAME
635
587 if os.path.isdir(options.src): 636 if os.path.isdir(options.src):
588 image_directory = options.src 637 image_directory = options.src
589 image_file = os.path.join(options.src, options.image_name) 638 image_file = os.path.join(options.src, options.image_name)
590 639
591 if not os.path.exists(image_file): 640 if not os.path.exists(image_file):
641 if maybe_build_test:
642 # we want a test image but it doesn't exist
643 # try to build it if we can
644 cros_env.Info('Creating test image')
645 test_output = cros_env.cmd.Output(cros_env.CrosUtilsPath('enter_chroot.s h'),
adlr 2010/11/17 20:43:25 a few lines around here (this, 646, 649) are over
646 '--', './mod_image_for_test.sh', '--board=%s' % options.board, ' -y')
647 if not os.path.exists(image_file):
648 print test_output
649 cros_env.Fatal('Failed to create test image - please run ./mod_image_f or_test.sh manually inside the chroot')
592 parser.error('Image file %s does not exist' % image_file) 650 parser.error('Image file %s does not exist' % image_file)
593 else: 651 else:
594 image_file = options.src 652 image_file = options.src
595 image_directory = os.path.dirname(options.src) 653 image_directory = os.path.dirname(options.src)
596 654
655 update_file = os.path.join(image_directory, UPDATE_FILENAME)
656 stateful_file = os.path.join(image_directory, STATEFUL_FILENAME)
657 cros_env.Debug("Image file %s" % image_file)
658 cros_env.Debug("Update file %s" % update_file)
659 cros_env.Debug("Stateful file %s" % stateful_file)
660
597 if options.remote: 661 if options.remote:
662 cros_env.Info('Contacting client %s' % options.remote)
598 cros_env.SetRemote(options.remote) 663 cros_env.SetRemote(options.remote)
599 rel = cros_env.GetRemoteRelease() 664 rel = cros_env.GetRemoteRelease()
600 if not rel: 665 if not rel:
601 cros_env.Fatal('Could not retrieve remote lsb-release') 666 cros_env.Fatal('Could not retrieve remote lsb-release')
602 board = rel.get('CHROMEOS_RELEASE_BOARD', '(None)') 667 board = rel.get('CHROMEOS_RELEASE_BOARD', '(None)')
603 if board != options.board and not options.force_mismatch: 668 if board != options.board and not options.force_mismatch:
604 cros_env.Error('Board %s does not match expected %s' % 669 cros_env.Error('Board %s does not match expected %s' %
605 (board, options.board)) 670 (board, options.board))
606 cros_env.Error('(Use --force-mismatch option to override this)') 671 cros_env.Error('(Use --force-mismatch option to override this)')
607 cros_env.Fatal() 672 cros_env.Fatal()
608 673
609 elif not options.server_only: 674 elif not options.server_only:
610 parser.error('Either --server-only must be specified or ' 675 parser.error('Either --server-only must be specified or '
611 '--remote=<client> needs to be given') 676 '--remote=<client> needs to be given')
612 677
613 update_file = os.path.join(image_directory, UPDATE_FILENAME)
614 stateful_file = os.path.join(image_directory, STATEFUL_FILENAME)
615
616 if (not cros_env.GenerateUpdatePayload(image_file, update_file) or 678 if (not cros_env.GenerateUpdatePayload(image_file, update_file) or
617 not cros_env.BuildStateful(image_file, stateful_file)): 679 not cros_env.BuildStateful(image_file, stateful_file)):
618 cros_env.Fatal() 680 cros_env.Fatal()
619 681
620 cros_env.CreateServer(options.port, update_file, stateful_file) 682 cros_env.CreateServer(options.port, update_file, stateful_file)
621 683
622 exit_status = 1 684 exit_status = 1
623 if options.server_only: 685 if options.server_only:
624 child = None 686 child = None
625 else: 687 else:
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
657 719
658 if child: 720 if child:
659 os.kill(child, 15) 721 os.kill(child, 15)
660 722
661 cros_env.Info('Server exiting with status %d' % exit_status) 723 cros_env.Info('Server exiting with status %d' % exit_status)
662 sys.exit(exit_status) 724 sys.exit(exit_status)
663 725
664 726
665 if __name__ == '__main__': 727 if __name__ == '__main__':
666 main(sys.argv) 728 main(sys.argv)
OLDNEW
« no previous file with comments | « no previous file | cros_generate_update_payload » ('j') | cros_generate_update_payload » ('J')

Powered by Google App Engine
This is Rietveld 408576698