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

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: Changes as suggested Created 10 years 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') | no next file with comments »
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
(...skipping 12 matching lines...) Expand all
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'
Paul Stewart 2010/11/23 21:21:33 The old stateful.gz is dead, I believe, and remove
38 STATEFUL_FILENAME_TGZ = 'stateful.tgz'
37 39
38 # How long do we wait for the server to start before launching client 40 # How long do we wait for the server to start before launching client
39 SERVER_STARTUP_WAIT = 1 41 SERVER_STARTUP_WAIT = 1
40 42
41 43
42 class Command(object): 44 class Command(object):
43 """Shell command ease-ups for Python.""" 45 """Shell command ease-ups for Python."""
44 46
45 def __init__(self, env): 47 def __init__(self, env):
46 self.env = env 48 self.env = env
47 49
48 def RunPipe(self, pipeline, infile=None, outfile=None, 50 def RunPipe(self, pipeline, infile=None, outfile=None,
49 capture=False, oneline=False): 51 capture=False, oneline=False, hide_stderr=False):
50 """Perform a command pipeline, with optional input/output filenames.""" 52 """
53 Perform a command pipeline, with optional input/output filenames.
54
55 hide_stderr Don't allow output of stderr (default False)
56 """
51 57
52 last_pipe = None 58 last_pipe = None
53 while pipeline: 59 while pipeline:
54 cmd = pipeline.pop(0) 60 cmd = pipeline.pop(0)
55 kwargs = {} 61 kwargs = {}
56 if last_pipe is not None: 62 if last_pipe is not None:
57 kwargs['stdin'] = last_pipe.stdout 63 kwargs['stdin'] = last_pipe.stdout
58 elif infile: 64 elif infile:
59 kwargs['stdin'] = open(infile, 'rb') 65 kwargs['stdin'] = open(infile, 'rb')
60 if pipeline or capture: 66 if pipeline or capture:
61 kwargs['stdout'] = subprocess.PIPE 67 kwargs['stdout'] = subprocess.PIPE
62 elif outfile: 68 elif outfile:
63 kwargs['stdout'] = open(outfile, 'wb') 69 kwargs['stdout'] = open(outfile, 'wb')
70 if hide_stderr:
71 kwargs['stderr'] = open('/dev/null', 'wb')
64 72
65 self.env.Info('Running: %s' % ' '.join(cmd)) 73 self.env.Debug('Running: %s' % ' '.join(cmd))
66 last_pipe = subprocess.Popen(cmd, **kwargs) 74 last_pipe = subprocess.Popen(cmd, **kwargs)
67 75
68 if capture: 76 if capture:
69 ret = last_pipe.communicate()[0] 77 ret = last_pipe.communicate()[0]
70 if not ret: 78 if not ret:
71 return None 79 return None
72 elif oneline: 80 elif oneline:
73 return ret.rstrip('\r\n') 81 return ret.rstrip('\r\n')
74 else: 82 else:
75 return ret 83 return ret
(...skipping 27 matching lines...) Expand all
103 self.known_hosts = os.path.join(self.ssh_dir, 'known-hosts') 111 self.known_hosts = os.path.join(self.ssh_dir, 'known-hosts')
104 112
105 def Cleanup(self): 113 def Cleanup(self):
106 Command.RunPipe(self, [['rm', '-rf', self.ssh_dir]]) 114 Command.RunPipe(self, [['rm', '-rf', self.ssh_dir]])
107 self.ssh_dir = None 115 self.ssh_dir = None
108 116
109 def GetArgs(self): 117 def GetArgs(self):
110 if not self.ssh_dir: 118 if not self.ssh_dir:
111 self.Setup() 119 self.Setup()
112 120
121 # allow up to 3 seconds to connect
113 return ['-o', 'Compression=no', 122 return ['-o', 'Compression=no',
114 '-o', 'ConnectTimeout=%d' % self.CONNECT_TIMEOUT, 123 '-o', 'ConnectTimeout=%d' % self.CONNECT_TIMEOUT,
115 '-o', 'StrictHostKeyChecking=no', 124 '-o', 'StrictHostKeyChecking=no',
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 = True
161 try:
162 self.cmd.Output('pv', '--help')
163 except OSError:
164 self.have_pv = False
165
150 def Error(self, msg): 166 def Error(self, msg):
151 print >> sys.stderr, 'ERROR: %s' % msg 167 print >> sys.stderr, 'ERROR: %s' % msg
152 168
153 def Fatal(self, msg=None): 169 def Fatal(self, msg=None):
154 if msg: 170 if msg:
155 self.Error(msg) 171 self.Error(msg)
156 sys.exit(1) 172 sys.exit(1)
157 173
158 def Info(self, msg): 174 def Info(self, msg):
159 if self.verbose: 175 if self.verbose > 0:
160 print 'INFO: %s' % msg 176 print 'INFO: %s' % msg
161 177
178 def Debug(self, msg):
179 if self.verbose > 1:
180 print 'DEBUG: %s' % msg
181
162 def CrosUtilsPath(self, filename): 182 def CrosUtilsPath(self, filename):
163 return os.path.join(self.cros_root, filename) 183 return os.path.join(self.cros_root, filename)
164 184
165 def ChrootPath(self, filename): 185 def ChrootPath(self, filename):
166 return self.CrosUtilsPath(os.path.join('..', '..', 'chroot', 186 return self.CrosUtilsPath(os.path.join('..', '..', 'chroot',
167 filename.strip(os.path.sep))) 187 filename.strip(os.path.sep)))
168 188
169 def FileOneLine(self, filename): 189 def FileOneLine(self, filename):
170 return file(filename).read().rstrip('\r\n') 190 return file(filename).read().rstrip('\r\n')
171 191
(...skipping 20 matching lines...) Expand all
192 212
193 return True 213 return True
194 214
195 def BuildStateful(self, src, dst): 215 def BuildStateful(self, src, dst):
196 """Create a stateful partition update image.""" 216 """Create a stateful partition update image."""
197 217
198 if self.GetCached(src, dst): 218 if self.GetCached(src, dst):
199 self.Info('Using cached stateful %s' % dst) 219 self.Info('Using cached stateful %s' % dst)
200 return True 220 return True
201 221
222 self.Info('Building stateful')
202 cgpt = self.ChrootPath('/usr/bin/cgpt') 223 cgpt = self.ChrootPath('/usr/bin/cgpt')
203 offset = self.cmd.OutputOneLine(cgpt, 'show', '-b', '-i', '1', src) 224 offset = self.cmd.OutputOneLine(cgpt, 'show', '-b', '-i', '1', src)
204 size = self.cmd.OutputOneLine(cgpt, 'show', '-s', '-i', '1', src) 225 size = self.cmd.OutputOneLine(cgpt, 'show', '-s', '-i', '1', src)
205 if None in (size, offset): 226 if None in (size, offset):
206 self.Error('Unable to use cgpt to get image geometry') 227 self.Error('Unable to use cgpt to get image geometry')
207 return False 228 return False
208 229
209 return self.cmd.RunPipe([['dd', 'if=%s' % src, 'bs=512', 230 return self.cmd.RunPipe([
210 'skip=%s' % offset, 'count=%s' % size], 231 ['dd', 'if=%s' % src, 'bs=512', 'skip=%s' % offset, 'count=%s' % size],
211 ['gzip', '-c']], outfile=dst) 232 self.have_pv and ['pv', '-s', '%d' % (int(size) * 512)] or ['cat'],
233 ['gzip', '-c']], outfile=dst)
212 234
213 def GetSize(self, filename): 235 def GetSize(self, filename):
214 return os.path.getsize(filename) 236 return os.path.getsize(filename)
215 237
216 def GetHash(self, filename): 238 def GetHash(self, filename):
217 return self.cmd.RunPipe([['openssl', 'sha1', '-binary'], 239 return self.cmd.RunPipe([['openssl', 'sha1', '-binary'],
218 ['openssl', 'base64']], 240 ['openssl', 'base64']],
219 infile=filename, 241 infile=filename,
220 capture=True, oneline=True) 242 capture=True, oneline=True)
221 243
(...skipping 23 matching lines...) Expand all
245 if var: 267 if var:
246 ret[var] = val.strip('\t ').rstrip('\t ') 268 ret[var] = val.strip('\t ').rstrip('\t ')
247 return ret 269 return ret
248 270
249 def GetRemoteRelease(self): 271 def GetRemoteRelease(self):
250 lsb_release = self.ssh_cmd.Output('cat', '/etc/lsb-release') 272 lsb_release = self.ssh_cmd.Output('cat', '/etc/lsb-release')
251 if not lsb_release: 273 if not lsb_release:
252 return None 274 return None
253 return self.ParseShVars(lsb_release) 275 return self.ParseShVars(lsb_release)
254 276
255 def CreateServer(self, port, update_file, stateful_file): 277 def CreateServer(self, port, update_file, stateful_file, stateful_file_tgz):
256 """Start the devserver clone.""" 278 """Start the devserver clone."""
257 279
258 PingUpdateResponse.Setup(self.GetHash(update_file), 280 PingUpdateResponse.Setup(self.GetHash(update_file),
259 self.GetSha256(update_file), 281 self.GetSha256(update_file),
260 self.GetSize(update_file)) 282 self.GetSize(update_file))
261 283
262 UpdateHandler.SetupUrl('/update', PingUpdateResponse()) 284 UpdateHandler.SetupUrl('/update', PingUpdateResponse())
263 UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME, 285 UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME,
264 FileUpdateResponse(update_file, 286 FileUpdateResponse(update_file,
265 verbose=self.verbose)) 287 verbose=self.verbose, have_pv=self.have_pv))
266 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME, 288 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME,
267 FileUpdateResponse(stateful_file, 289 FileUpdateResponse(stateful_file,
268 verbose=self.verbose)) 290 verbose=self.verbose, have_pv=self.have_pv))
291 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME_TGZ,
292 FileUpdateResponse(stateful_file_tgz,
293 verbose=self.verbose, have_pv=self.have_pv))
Paul Stewart 2010/11/23 21:21:33 Please rebase these changes on what's currently in
269 294
270 self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler) 295 self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler)
271 296
272 def StartServer(self): 297 def StartServer(self):
273 self.Info('Starting http server') 298 self.Info('Starting http server')
274 self.http_server.serve_forever() 299 self.http_server.serve_forever()
275 300
276 def GetUpdateStatus(self): 301 def GetUpdateStatus(self):
277 status = self.ssh_cmd.Output('/usr/bin/update_engine_client', '--status') 302 status = self.ssh_cmd.Output('/usr/bin/update_engine_client', '--status')
278 if not status: 303 if not status:
(...skipping 18 matching lines...) Expand all
297 self.Info('Client has not yet restarted (try %d). Waiting...' % attempt) 322 self.Info('Client has not yet restarted (try %d). Waiting...' % attempt)
298 wait_time = SSHCommand.CONNECT_TIMEOUT - (time.time() - start) 323 wait_time = SSHCommand.CONNECT_TIMEOUT - (time.time() - start)
299 if wait_time > 0: 324 if wait_time > 0:
300 time.sleep(wait_time) 325 time.sleep(wait_time)
301 326
302 return False 327 return False
303 328
304 def StartClient(self, port): 329 def StartClient(self, port):
305 """Ask the client machine to update from our server.""" 330 """Ask the client machine to update from our server."""
306 331
332 self.Info("Starting client...")
307 status = self.GetUpdateStatus() 333 status = self.GetUpdateStatus()
308 if status != 'UPDATE_STATUS_IDLE': 334 if status != 'UPDATE_STATUS_IDLE':
309 self.Error('Client update status is not IDLE: %s' % status) 335 self.Error('Client update status is not IDLE: %s' % status)
310 return False 336 return False
311 337
312 url_base = 'http://localhost:%d' % port 338 url_base = 'http://localhost:%d' % port
313 update_url = '%s/update' % url_base 339 update_url = '%s/update' % url_base
314 fd, update_log = tempfile.mkstemp(prefix='image-to-target-') 340 fd, update_log = tempfile.mkstemp(prefix='image-to-target-')
315 self.Info('Starting update on client. Client output stored to %s' % 341 self.Info('Starting update on client. Client output stored to %s' %
316 update_log) 342 update_log)
343
344 # this will make the client read the files we have set up
317 self.ssh_cmd.Run('/usr/bin/update_engine_client', '--update', 345 self.ssh_cmd.Run('/usr/bin/update_engine_client', '--update',
318 '--omaha_url', update_url, remote_tunnel=(port, port), 346 '--omaha_url', update_url, remote_tunnel=(port, port),
319 outfile=update_log) 347 outfile=update_log)
320 348
321 if self.GetUpdateStatus() != 'UPDATE_STATUS_UPDATED_NEED_REBOOT': 349 if self.GetUpdateStatus() != 'UPDATE_STATUS_UPDATED_NEED_REBOOT':
322 self.Error('Client update failed') 350 self.Error('Client update failed')
323 return False 351 return False
324 352
353 self.Info('Update complete - running update script on client')
325 self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'), 354 self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'),
326 '/tmp') 355 '/tmp')
327 if not self.ssh_cmd.Run('/tmp/stateful_update', url_base, 356 if not self.ssh_cmd.Run('/tmp/stateful_update', url_base,
328 remote_tunnel=(port, port)): 357 remote_tunnel=(port, port)):
329 self.Error('Client stateful update failed') 358 self.Error('Client stateful update failed')
330 return False 359 return False
331 360
332 self.Info('Rebooting client') 361 self.Info('Rebooting client')
333 if not self.ClientReboot(): 362 if not self.ClientReboot():
334 self.Error('Client may not have successfully rebooted...') 363 self.Error('Client may not have successfully rebooted...')
335 return False 364 return False
336 365
337 print 'Client update completed successfully!' 366 self.Info('Client update completed successfully!')
338 return True 367 return True
339 368
340 369
341 class UpdateResponse(object): 370 class UpdateResponse(object):
342 """Default response is the 404 error response.""" 371 """Default response is the 404 error response."""
343 372
344 def Reply(self, handler, send_content=True, post_data=None): 373 def Reply(self, handler, send_content=True, post_data=None):
345 handler.send_Error(404, 'File not found') 374 handler.send_error(404, 'File not found')
346 return None 375 return None
347 376
348 377
349 class FileUpdateResponse(UpdateResponse): 378 class FileUpdateResponse(UpdateResponse):
350 """Respond by sending the contents of a file.""" 379 """Respond by sending the contents of a file."""
351 380
352 def __init__(self, filename, content_type='application/octet-stream', 381 def __init__(self, filename, content_type='application/octet-stream',
353 verbose=False, blocksize=16*1024): 382 verbose=False, blocksize=16*1024, have_pv=False):
354 self.filename = filename 383 self.filename = filename
355 self.content_type = content_type 384 self.content_type = content_type
356 self.verbose = verbose 385 self.verbose = verbose
357 self.blocksize = blocksize 386 self.blocksize = blocksize
387 self.have_pv = have_pv
358 388
359 def Reply(self, handler, send_content=True, post_data=None): 389 def Reply(self, handler, send_content=True, post_data=None):
360 """Return file contents to the client. Optionally display progress.""" 390 """Return file contents to the client. Optionally display progress."""
361 391
362 try: 392 try:
363 f = open(self.filename, 'rb') 393 f = open(self.filename, 'rb')
364 except IOError: 394 except IOError:
365 return UpdateResponse.Reply(self, handler) 395 return UpdateResponse.Reply(self, handler)
366 396
367 handler.send_response(200) 397 handler.send_response(200)
368 handler.send_header('Content-type', self.content_type) 398 handler.send_header('Content-type', self.content_type)
369 filestat = os.fstat(f.fileno()) 399 filestat = os.fstat(f.fileno())
370 filesize = filestat[6] 400 filesize = filestat[6]
371 handler.send_header('Content-Length', str(filesize)) 401 handler.send_header('Content-Length', str(filesize))
372 handler.send_header('Last-Modified', 402 handler.send_header('Last-Modified',
373 handler.date_time_string(filestat.st_mtime)) 403 handler.date_time_string(filestat.st_mtime))
374 handler.end_headers() 404 handler.end_headers()
375 405
376 if not send_content: 406 if send_content:
377 return
378
379 if filesize <= self.blocksize:
380 handler.wfile.write(f.read())
381 else:
382 sent_size = 0 407 sent_size = 0
383 sent_percentage = None 408 sent_percentage = None
409
410 #FIXME: this should use pv also
Paul Stewart 2010/11/23 21:21:33 Use # TODO(sgj): ...
384 while True: 411 while True:
385 buf = f.read(self.blocksize) 412 buf = f.read(self.blocksize)
386 if not buf: 413 if not buf:
387 break 414 break
388 handler.wfile.write(buf) 415 handler.wfile.write(buf)
389 if self.verbose: 416 if self.verbose:
390 sent_size += len(buf) 417 sent_size += len(buf)
391 percentage = int(100 * sent_size / filesize) 418 percentage = int(100 * sent_size / filesize)
392 if sent_percentage != percentage: 419 if sent_percentage != percentage:
393 sent_percentage = percentage 420 sent_percentage = percentage
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
549 usage = 'usage: %prog [options]' 576 usage = 'usage: %prog [options]'
550 parser = optparse.OptionParser(usage=usage) 577 parser = optparse.OptionParser(usage=usage)
551 parser.add_option('--board', dest='board', default=None, 578 parser.add_option('--board', dest='board', default=None,
552 help='Board platform type') 579 help='Board platform type')
553 parser.add_option('--force-mismatch', dest='force_mismatch', default=False, 580 parser.add_option('--force-mismatch', dest='force_mismatch', default=False,
554 action='store_true', 581 action='store_true',
555 help='Upgrade even if client arch does not match') 582 help='Upgrade even if client arch does not match')
556 parser.add_option('--from', dest='src', default=None, 583 parser.add_option('--from', dest='src', default=None,
557 help='Source image to install') 584 help='Source image to install')
558 parser.add_option('--image-name', dest='image_name', 585 parser.add_option('--image-name', dest='image_name',
559 default=DEFAULT_IMAGE_NAME,
560 help='Filename within image directory to load') 586 help='Filename within image directory to load')
561 parser.add_option('--port', dest='port', default=8081, type='int', 587 parser.add_option('--port', dest='port', default=8081, type='int',
562 help='TCP port to serve from and tunnel through') 588 help='TCP port to serve from and tunnel through')
563 parser.add_option('--remote', dest='remote', default=None, 589 parser.add_option('--remote', dest='remote', default=None,
564 help='Remote device-under-test IP address') 590 help='Remote device-under-test IP address')
565 parser.add_option('--server-only', dest='server_only', default=False, 591 parser.add_option('--server-only', dest='server_only', default=False,
566 action='store_true', help='Do not start client') 592 action='store_true', help='Do not start client')
567 parser.add_option('--verbose', dest='verbose', default=False, 593 parser.add_option('--verbose', dest='verbose', default=False,
594 action='store_true', help='Display progress')
595 parser.add_option('--debug', dest='debug', default=False,
568 action='store_true', help='Display running commands') 596 action='store_true', help='Display running commands')
597 parser.add_option('--test', dest='test', default=False,
598 action='store_true', help='Select test image')
569 599
570 (options, args) = parser.parse_args(argv) 600 (options, args) = parser.parse_args(argv)
571 601
572 cros_env = CrosEnv(verbose=options.verbose) 602 # we can build the test image if it doesn't exist, so remember if we want to
603 build_test_image = False
604
605 verbosity = 0
606 if options.verbose:
607 verbosity = 1
608 if options.debug:
609 verbosity = 2
610 cros_env = CrosEnv(verbose=verbosity)
573 611
574 if not options.board: 612 if not options.board:
575 options.board = cros_env.GetDefaultBoard() 613 options.board = cros_env.GetDefaultBoard()
576 614
577 if not options.src: 615 if not options.src:
578 options.src = cros_env.GetLatestImage(options.board) 616 options.src = cros_env.GetLatestImage(options.board)
579 if options.src is None: 617 if options.src is None:
580 parser.error('No --from argument given and no default image found') 618 parser.error('No --from argument given and no default image found')
581 619
582 cros_env.Info('Performing update from %s' % options.src) 620 cros_env.Info('Performing update from %s' % options.src)
583 621
584 if not os.path.exists(options.src): 622 if not os.path.exists(options.src):
585 parser.error('Path %s does not exist' % options.src) 623 parser.error('Path %s does not exist' % options.src)
586 624
625 if not options.image_name:
626 # auto-select the correct image
627 if options.test:
628 options.image_name = DEFAULT_IMAGE_NAME_TEST
629
630 # we will build the test image if not found
631 build_test_image = True
632 else:
633 options.image_name = DEFAULT_IMAGE_NAME
634
587 if os.path.isdir(options.src): 635 if os.path.isdir(options.src):
588 image_directory = options.src 636 image_directory = options.src
589 image_file = os.path.join(options.src, options.image_name) 637 image_file = os.path.join(options.src, options.image_name)
590 638
591 if not os.path.exists(image_file): 639 if not os.path.exists(image_file):
640 if build_test_image:
641 # we want a test image but it doesn't exist
642 # try to build it if we can
643 cros_env.Info('Creating test image')
644 test_output = cros_env.cmd.Output(
645 cros_env.CrosUtilsPath('enter_chroot.sh'),
646 '--', './mod_image_for_test.sh',
647 '--board=%s' % options.board, '-y')
648 if not os.path.exists(image_file):
649 print test_output
650 cros_env.Fatal('Failed to create test image - please run '
651 './mod_image_for_test.sh manually inside the chroot')
592 parser.error('Image file %s does not exist' % image_file) 652 parser.error('Image file %s does not exist' % image_file)
593 else: 653 else:
594 image_file = options.src 654 image_file = options.src
595 image_directory = os.path.dirname(options.src) 655 image_directory = os.path.dirname(options.src)
596 656
657 update_file = os.path.join(image_directory, UPDATE_FILENAME)
658 stateful_file = os.path.join(image_directory, STATEFUL_FILENAME)
659 stateful_file_tgz = cros_env.CrosUtilsPath(
660 '../platform/dev/static/stateful.tgz')
661 cros_env.Debug("Image file %s" % image_file)
662 cros_env.Debug("Update file %s" % update_file)
663 cros_env.Debug("Stateful file %s" % stateful_file)
664 cros_env.Debug("Stateful file tgz %s" % stateful_file_tgz)
665
597 if options.remote: 666 if options.remote:
667 cros_env.Info('Contacting client %s' % options.remote)
598 cros_env.SetRemote(options.remote) 668 cros_env.SetRemote(options.remote)
599 rel = cros_env.GetRemoteRelease() 669 rel = cros_env.GetRemoteRelease()
600 if not rel: 670 if not rel:
601 cros_env.Fatal('Could not retrieve remote lsb-release') 671 cros_env.Fatal('Could not retrieve remote lsb-release')
602 board = rel.get('CHROMEOS_RELEASE_BOARD', '(None)') 672 board = rel.get('CHROMEOS_RELEASE_BOARD', '(None)')
603 if board != options.board and not options.force_mismatch: 673 if board != options.board and not options.force_mismatch:
604 cros_env.Error('Board %s does not match expected %s' % 674 cros_env.Error('Board %s does not match expected %s' %
605 (board, options.board)) 675 (board, options.board))
606 cros_env.Error('(Use --force-mismatch option to override this)') 676 cros_env.Error('(Use --force-mismatch option to override this)')
607 cros_env.Fatal() 677 cros_env.Fatal()
608 678
609 elif not options.server_only: 679 elif not options.server_only:
610 parser.error('Either --server-only must be specified or ' 680 parser.error('Either --server-only must be specified or '
611 '--remote=<client> needs to be given') 681 '--remote=<client> needs to be given')
612 682
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 683 if (not cros_env.GenerateUpdatePayload(image_file, update_file) or
617 not cros_env.BuildStateful(image_file, stateful_file)): 684 not cros_env.BuildStateful(image_file, stateful_file)):
618 cros_env.Fatal() 685 cros_env.Fatal()
619 686
620 cros_env.CreateServer(options.port, update_file, stateful_file) 687 cros_env.CreateServer(options.port, update_file, stateful_file,
688 stateful_file_tgz)
621 689
622 exit_status = 1 690 exit_status = 1
623 if options.server_only: 691 if options.server_only:
624 child = None 692 child = None
625 else: 693 else:
626 # Start an "image-to-live" instance that will pull bits from the server 694 # Start an "image-to-live" instance that will pull bits from the server
627 child = os.fork() 695 child = os.fork()
628 if child: 696 if child:
629 signal.signal(signal.SIGCHLD, ChildFinished(child).SigHandler) 697 signal.signal(signal.SIGCHLD, ChildFinished(child).SigHandler)
630 else: 698 else:
(...skipping 26 matching lines...) Expand all
657 725
658 if child: 726 if child:
659 os.kill(child, 15) 727 os.kill(child, 15)
660 728
661 cros_env.Info('Server exiting with status %d' % exit_status) 729 cros_env.Info('Server exiting with status %d' % exit_status)
662 sys.exit(exit_status) 730 sys.exit(exit_status)
663 731
664 732
665 if __name__ == '__main__': 733 if __name__ == '__main__':
666 main(sys.argv) 734 main(sys.argv)
OLDNEW
« no previous file with comments | « no previous file | cros_generate_update_payload » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698