OLD | NEW |
---|---|
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 Loading... | |
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.tgz' | 37 STATEFUL_FILENAME = 'stateful.tgz' |
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 Loading... | |
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 | |
Paul Stewart
2010/11/23 22:49:07
Drop this comment. The associated code is gone.
| |
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', |
116 '-o', 'UserKnownHostsFile=%s' % self.known_hosts, | 124 '-o', 'UserKnownHostsFile=%s' % self.known_hosts, |
117 '-i', self.identity] | 125 '-i', self.identity] |
118 | 126 |
119 def RunPipe(self, pipeline, **kwargs): | 127 def RunPipe(self, pipeline, **kwargs): |
120 args = ['ssh'] + self.GetArgs() | 128 args = ['ssh'] + self.GetArgs() |
121 if 'remote_tunnel' in kwargs: | 129 if 'remote_tunnel' in kwargs: |
122 ports = kwargs.pop('remote_tunnel') | 130 ports = kwargs.pop('remote_tunnel') |
123 args += ['-R %d:localhost:%d' % ports] | 131 args += ['-R %d:localhost:%d' % ports] |
124 pipeline[0] = args + ['root@%s' % self.remote] + list(pipeline[0]) | 132 pipeline[0] = args + ['root@%s' % self.remote] + list(pipeline[0]) |
125 return Command.RunPipe(self, pipeline, **kwargs) | 133 return Command.RunPipe(self, pipeline, **kwargs) |
126 | 134 |
127 def Reset(self): | 135 def Reset(self): |
128 os.unlink(self.known_hosts) | 136 os.unlink(self.known_hosts) |
129 | 137 |
130 def Copy(self, src, dest): | 138 def Copy(self, src, dest): |
131 return Command.RunPipe(self, [['scp'] + self.GetArgs() + | 139 return Command.RunPipe(self, [['scp'] + self.GetArgs() + |
132 [src, 'root@%s:%s' % | 140 [src, 'root@%s:%s' % |
133 (self.remote, dest)]]) | 141 (self.remote, dest)]]) |
134 | 142 |
135 | 143 |
136 class CrosEnv(object): | 144 class CrosEnv(object): |
137 """Encapsulates the ChromeOS build system environment functionality.""" | 145 """Encapsulates the ChromeOS build system environment functionality.""" |
138 | 146 |
139 REBOOT_START_WAIT = 5 | 147 REBOOT_START_WAIT = 5 |
140 REBOOT_WAIT_TIME = 60 | 148 REBOOT_WAIT_TIME = 60 |
141 | 149 |
142 def __init__(self, verbose=False): | 150 def __init__(self, verbose=0): |
Paul Stewart
2010/11/23 22:49:07
Please make CrossEnv constants as above for SILENT
| |
143 self.cros_root = os.path.dirname(os.path.abspath(sys.argv[0])) | 151 self.cros_root = os.path.dirname(os.path.abspath(sys.argv[0])) |
144 parent = os.path.dirname(self.cros_root) | 152 parent = os.path.dirname(self.cros_root) |
145 if os.path.exists(os.path.join(parent, 'chromeos-common.sh')): | 153 if os.path.exists(os.path.join(parent, 'chromeos-common.sh')): |
146 self.cros_root = parent | 154 self.cros_root = parent |
147 self.cmd = Command(self) | 155 self.cmd = Command(self) |
148 self.verbose = verbose | 156 self.verbose = verbose |
149 | 157 |
158 # do we have the pv progress tool? (sudo apt-get install pv) | |
159 self.have_pv = True | |
160 try: | |
161 self.cmd.Output('pv', '--help') | |
162 except OSError: | |
163 self.have_pv = False | |
164 | |
150 def Error(self, msg): | 165 def Error(self, msg): |
151 print >> sys.stderr, 'ERROR: %s' % msg | 166 print >> sys.stderr, 'ERROR: %s' % msg |
152 | 167 |
153 def Fatal(self, msg=None): | 168 def Fatal(self, msg=None): |
154 if msg: | 169 if msg: |
155 self.Error(msg) | 170 self.Error(msg) |
156 sys.exit(1) | 171 sys.exit(1) |
157 | 172 |
158 def Info(self, msg): | 173 def Info(self, msg): |
159 if self.verbose: | 174 if self.verbose > 0: |
160 print 'INFO: %s' % msg | 175 print 'INFO: %s' % msg |
161 | 176 |
177 def Debug(self, msg): | |
178 if self.verbose > 1: | |
179 print 'DEBUG: %s' % msg | |
180 | |
162 def CrosUtilsPath(self, filename): | 181 def CrosUtilsPath(self, filename): |
163 return os.path.join(self.cros_root, filename) | 182 return os.path.join(self.cros_root, filename) |
164 | 183 |
165 def ChrootPath(self, filename): | 184 def ChrootPath(self, filename): |
166 return self.CrosUtilsPath(os.path.join('..', '..', 'chroot', | 185 return self.CrosUtilsPath(os.path.join('..', '..', 'chroot', |
167 filename.strip(os.path.sep))) | 186 filename.strip(os.path.sep))) |
168 | 187 |
169 def FileOneLine(self, filename): | 188 def FileOneLine(self, filename): |
170 return file(filename).read().rstrip('\r\n') | 189 return file(filename).read().rstrip('\r\n') |
171 | 190 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
248 def CreateServer(self, port, update_file, stateful_file): | 267 def CreateServer(self, port, update_file, stateful_file): |
249 """Start the devserver clone.""" | 268 """Start the devserver clone.""" |
250 | 269 |
251 PingUpdateResponse.Setup(self.GetHash(update_file), | 270 PingUpdateResponse.Setup(self.GetHash(update_file), |
252 self.GetSha256(update_file), | 271 self.GetSha256(update_file), |
253 self.GetSize(update_file)) | 272 self.GetSize(update_file)) |
254 | 273 |
255 UpdateHandler.SetupUrl('/update', PingUpdateResponse()) | 274 UpdateHandler.SetupUrl('/update', PingUpdateResponse()) |
256 UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME, | 275 UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME, |
257 FileUpdateResponse(update_file, | 276 FileUpdateResponse(update_file, |
258 verbose=self.verbose)) | 277 verbose=self.verbose, |
278 have_pv=self.have_pv)) | |
259 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME, | 279 UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME, |
260 FileUpdateResponse(stateful_file, | 280 FileUpdateResponse(stateful_file, |
261 verbose=self.verbose)) | 281 verbose=self.verbose, |
282 have_pv=self.have_pv)) | |
262 | 283 |
263 self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler) | 284 self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler) |
264 | 285 |
265 def StartServer(self): | 286 def StartServer(self): |
266 self.Info('Starting http server') | 287 self.Info('Starting http server') |
267 self.http_server.serve_forever() | 288 self.http_server.serve_forever() |
268 | 289 |
269 def GetUpdateStatus(self): | 290 def GetUpdateStatus(self): |
270 status = self.ssh_cmd.Output('/usr/bin/update_engine_client', '--status') | 291 status = self.ssh_cmd.Output('/usr/bin/update_engine_client', '--status') |
271 if not status: | 292 if not status: |
(...skipping 18 matching lines...) Expand all Loading... | |
290 self.Info('Client has not yet restarted (try %d). Waiting...' % attempt) | 311 self.Info('Client has not yet restarted (try %d). Waiting...' % attempt) |
291 wait_time = SSHCommand.CONNECT_TIMEOUT - (time.time() - start) | 312 wait_time = SSHCommand.CONNECT_TIMEOUT - (time.time() - start) |
292 if wait_time > 0: | 313 if wait_time > 0: |
293 time.sleep(wait_time) | 314 time.sleep(wait_time) |
294 | 315 |
295 return False | 316 return False |
296 | 317 |
297 def StartClient(self, port): | 318 def StartClient(self, port): |
298 """Ask the client machine to update from our server.""" | 319 """Ask the client machine to update from our server.""" |
299 | 320 |
321 self.Info("Starting client...") | |
300 status = self.GetUpdateStatus() | 322 status = self.GetUpdateStatus() |
301 if status != 'UPDATE_STATUS_IDLE': | 323 if status != 'UPDATE_STATUS_IDLE': |
302 self.Error('Client update status is not IDLE: %s' % status) | 324 self.Error('Client update status is not IDLE: %s' % status) |
303 return False | 325 return False |
304 | 326 |
305 url_base = 'http://localhost:%d' % port | 327 url_base = 'http://localhost:%d' % port |
306 update_url = '%s/update' % url_base | 328 update_url = '%s/update' % url_base |
307 fd, update_log = tempfile.mkstemp(prefix='image-to-target-') | 329 fd, update_log = tempfile.mkstemp(prefix='image-to-target-') |
308 self.Info('Starting update on client. Client output stored to %s' % | 330 self.Info('Starting update on client. Client output stored to %s' % |
309 update_log) | 331 update_log) |
332 | |
333 # this will make the client read the files we have set up | |
310 self.ssh_cmd.Run('/usr/bin/update_engine_client', '--update', | 334 self.ssh_cmd.Run('/usr/bin/update_engine_client', '--update', |
311 '--omaha_url', update_url, remote_tunnel=(port, port), | 335 '--omaha_url', update_url, remote_tunnel=(port, port), |
312 outfile=update_log) | 336 outfile=update_log) |
313 | 337 |
314 if self.GetUpdateStatus() != 'UPDATE_STATUS_UPDATED_NEED_REBOOT': | 338 if self.GetUpdateStatus() != 'UPDATE_STATUS_UPDATED_NEED_REBOOT': |
315 self.Error('Client update failed') | 339 self.Error('Client update failed') |
316 return False | 340 return False |
317 | 341 |
342 self.Info('Update complete - running update script on client') | |
318 self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'), | 343 self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'), |
319 '/tmp') | 344 '/tmp') |
320 if not self.ssh_cmd.Run('/tmp/stateful_update', url_base, | 345 if not self.ssh_cmd.Run('/tmp/stateful_update', url_base, |
321 remote_tunnel=(port, port)): | 346 remote_tunnel=(port, port)): |
322 self.Error('Client stateful update failed') | 347 self.Error('Client stateful update failed') |
323 return False | 348 return False |
324 | 349 |
325 self.Info('Rebooting client') | 350 self.Info('Rebooting client') |
326 if not self.ClientReboot(): | 351 if not self.ClientReboot(): |
327 self.Error('Client may not have successfully rebooted...') | 352 self.Error('Client may not have successfully rebooted...') |
328 return False | 353 return False |
329 | 354 |
330 print 'Client update completed successfully!' | 355 self.Info('Client update completed successfully!') |
331 return True | 356 return True |
332 | 357 |
333 | 358 |
334 class UpdateResponse(object): | 359 class UpdateResponse(object): |
335 """Default response is the 404 error response.""" | 360 """Default response is the 404 error response.""" |
336 | 361 |
337 def Reply(self, handler, send_content=True, post_data=None): | 362 def Reply(self, handler, send_content=True, post_data=None): |
338 handler.send_Error(404, 'File not found') | 363 handler.send_error(404, 'File not found') |
339 return None | 364 return None |
340 | 365 |
341 | 366 |
342 class FileUpdateResponse(UpdateResponse): | 367 class FileUpdateResponse(UpdateResponse): |
343 """Respond by sending the contents of a file.""" | 368 """Respond by sending the contents of a file.""" |
344 | 369 |
345 def __init__(self, filename, content_type='application/octet-stream', | 370 def __init__(self, filename, content_type='application/octet-stream', |
346 verbose=False, blocksize=16*1024): | 371 verbose=False, blocksize=16*1024, have_pv=False): |
347 self.filename = filename | 372 self.filename = filename |
348 self.content_type = content_type | 373 self.content_type = content_type |
349 self.verbose = verbose | 374 self.verbose = verbose |
350 self.blocksize = blocksize | 375 self.blocksize = blocksize |
376 self.have_pv = have_pv | |
351 | 377 |
352 def Reply(self, handler, send_content=True, post_data=None): | 378 def Reply(self, handler, send_content=True, post_data=None): |
353 """Return file contents to the client. Optionally display progress.""" | 379 """Return file contents to the client. Optionally display progress.""" |
354 | 380 |
355 try: | 381 try: |
356 f = open(self.filename, 'rb') | 382 f = open(self.filename, 'rb') |
357 except IOError: | 383 except IOError: |
358 return UpdateResponse.Reply(self, handler) | 384 return UpdateResponse.Reply(self, handler) |
359 | 385 |
360 handler.send_response(200) | 386 handler.send_response(200) |
361 handler.send_header('Content-type', self.content_type) | 387 handler.send_header('Content-type', self.content_type) |
362 filestat = os.fstat(f.fileno()) | 388 filestat = os.fstat(f.fileno()) |
363 filesize = filestat[6] | 389 filesize = filestat[6] |
364 handler.send_header('Content-Length', str(filesize)) | 390 handler.send_header('Content-Length', str(filesize)) |
365 handler.send_header('Last-Modified', | 391 handler.send_header('Last-Modified', |
366 handler.date_time_string(filestat.st_mtime)) | 392 handler.date_time_string(filestat.st_mtime)) |
367 handler.end_headers() | 393 handler.end_headers() |
368 | 394 |
369 if not send_content: | 395 if send_content: |
370 return | |
371 | |
372 if filesize <= self.blocksize: | |
373 handler.wfile.write(f.read()) | |
374 else: | |
375 sent_size = 0 | 396 sent_size = 0 |
376 sent_percentage = None | 397 sent_percentage = None |
398 | |
399 #TODO(sjg): this should use pv also | |
377 while True: | 400 while True: |
378 buf = f.read(self.blocksize) | 401 buf = f.read(self.blocksize) |
379 if not buf: | 402 if not buf: |
380 break | 403 break |
381 handler.wfile.write(buf) | 404 handler.wfile.write(buf) |
382 if self.verbose: | 405 if self.verbose: |
383 sent_size += len(buf) | 406 sent_size += len(buf) |
384 percentage = int(100 * sent_size / filesize) | 407 percentage = int(100 * sent_size / filesize) |
385 if sent_percentage != percentage: | 408 if sent_percentage != percentage: |
386 sent_percentage = percentage | 409 sent_percentage = percentage |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
542 usage = 'usage: %prog [options]' | 565 usage = 'usage: %prog [options]' |
543 parser = optparse.OptionParser(usage=usage) | 566 parser = optparse.OptionParser(usage=usage) |
544 parser.add_option('--board', dest='board', default=None, | 567 parser.add_option('--board', dest='board', default=None, |
545 help='Board platform type') | 568 help='Board platform type') |
546 parser.add_option('--force-mismatch', dest='force_mismatch', default=False, | 569 parser.add_option('--force-mismatch', dest='force_mismatch', default=False, |
547 action='store_true', | 570 action='store_true', |
548 help='Upgrade even if client arch does not match') | 571 help='Upgrade even if client arch does not match') |
549 parser.add_option('--from', dest='src', default=None, | 572 parser.add_option('--from', dest='src', default=None, |
550 help='Source image to install') | 573 help='Source image to install') |
551 parser.add_option('--image-name', dest='image_name', | 574 parser.add_option('--image-name', dest='image_name', |
552 default=DEFAULT_IMAGE_NAME, | |
553 help='Filename within image directory to load') | 575 help='Filename within image directory to load') |
554 parser.add_option('--port', dest='port', default=8081, type='int', | 576 parser.add_option('--port', dest='port', default=8081, type='int', |
555 help='TCP port to serve from and tunnel through') | 577 help='TCP port to serve from and tunnel through') |
556 parser.add_option('--remote', dest='remote', default=None, | 578 parser.add_option('--remote', dest='remote', default=None, |
557 help='Remote device-under-test IP address') | 579 help='Remote device-under-test IP address') |
558 parser.add_option('--server-only', dest='server_only', default=False, | 580 parser.add_option('--server-only', dest='server_only', default=False, |
559 action='store_true', help='Do not start client') | 581 action='store_true', help='Do not start client') |
560 parser.add_option('--verbose', dest='verbose', default=False, | 582 parser.add_option('--verbose', dest='verbose', default=False, |
583 action='store_true', help='Display progress') | |
584 parser.add_option('--debug', dest='debug', default=False, | |
561 action='store_true', help='Display running commands') | 585 action='store_true', help='Display running commands') |
586 parser.add_option('--test', dest='test', default=False, | |
587 action='store_true', help='Select test image') | |
562 | 588 |
563 (options, args) = parser.parse_args(argv) | 589 (options, args) = parser.parse_args(argv) |
564 | 590 |
565 cros_env = CrosEnv(verbose=options.verbose) | 591 # we can build the test image if it doesn't exist, so remember if we want to |
592 build_test_image = False | |
593 | |
594 verbosity = 0 | |
595 if options.verbose: | |
596 verbosity = 1 | |
597 if options.debug: | |
598 verbosity = 2 | |
599 cros_env = CrosEnv(verbose=verbosity) | |
566 | 600 |
567 if not options.board: | 601 if not options.board: |
568 options.board = cros_env.GetDefaultBoard() | 602 options.board = cros_env.GetDefaultBoard() |
569 | 603 |
570 if not options.src: | 604 if not options.src: |
571 options.src = cros_env.GetLatestImage(options.board) | 605 options.src = cros_env.GetLatestImage(options.board) |
572 if options.src is None: | 606 if options.src is None: |
573 parser.error('No --from argument given and no default image found') | 607 parser.error('No --from argument given and no default image found') |
574 | 608 |
575 cros_env.Info('Performing update from %s' % options.src) | 609 cros_env.Info('Performing update from %s' % options.src) |
576 | 610 |
577 if not os.path.exists(options.src): | 611 if not os.path.exists(options.src): |
578 parser.error('Path %s does not exist' % options.src) | 612 parser.error('Path %s does not exist' % options.src) |
579 | 613 |
614 if not options.image_name: | |
615 # auto-select the correct image | |
616 if options.test: | |
617 options.image_name = DEFAULT_IMAGE_NAME_TEST | |
618 | |
619 # we will build the test image if not found | |
620 build_test_image = True | |
621 else: | |
622 options.image_name = DEFAULT_IMAGE_NAME | |
623 | |
580 if os.path.isdir(options.src): | 624 if os.path.isdir(options.src): |
581 image_directory = options.src | 625 image_directory = options.src |
582 image_file = os.path.join(options.src, options.image_name) | 626 image_file = os.path.join(options.src, options.image_name) |
583 | 627 |
584 if not os.path.exists(image_file): | 628 if not os.path.exists(image_file): |
629 if build_test_image: | |
630 # we want a test image but it doesn't exist | |
631 # try to build it if we can | |
632 cros_env.Info('Creating test image') | |
633 test_output = cros_env.cmd.Output( | |
634 cros_env.CrosUtilsPath('enter_chroot.sh'), | |
635 '--', './mod_image_for_test.sh', | |
636 '--board=%s' % options.board, '-y') | |
637 if not os.path.exists(image_file): | |
638 print test_output | |
639 cros_env.Fatal('Failed to create test image - please run ' | |
640 './mod_image_for_test.sh manually inside the chroot') | |
585 parser.error('Image file %s does not exist' % image_file) | 641 parser.error('Image file %s does not exist' % image_file) |
586 else: | 642 else: |
587 image_file = options.src | 643 image_file = options.src |
588 image_directory = os.path.dirname(options.src) | 644 image_directory = os.path.dirname(options.src) |
589 | 645 |
646 update_file = os.path.join(image_directory, UPDATE_FILENAME) | |
647 stateful_file = os.path.join(image_directory, STATEFUL_FILENAME) | |
648 | |
649 cros_env.Debug("Image file %s" % image_file) | |
650 cros_env.Debug("Update file %s" % update_file) | |
651 cros_env.Debug("Stateful file %s" % stateful_file) | |
652 | |
590 if options.remote: | 653 if options.remote: |
654 cros_env.Info('Contacting client %s' % options.remote) | |
591 cros_env.SetRemote(options.remote) | 655 cros_env.SetRemote(options.remote) |
592 rel = cros_env.GetRemoteRelease() | 656 rel = cros_env.GetRemoteRelease() |
593 if not rel: | 657 if not rel: |
594 cros_env.Fatal('Could not retrieve remote lsb-release') | 658 cros_env.Fatal('Could not retrieve remote lsb-release') |
595 board = rel.get('CHROMEOS_RELEASE_BOARD', '(None)') | 659 board = rel.get('CHROMEOS_RELEASE_BOARD', '(None)') |
596 if board != options.board and not options.force_mismatch: | 660 if board != options.board and not options.force_mismatch: |
597 cros_env.Error('Board %s does not match expected %s' % | 661 cros_env.Error('Board %s does not match expected %s' % |
598 (board, options.board)) | 662 (board, options.board)) |
599 cros_env.Error('(Use --force-mismatch option to override this)') | 663 cros_env.Error('(Use --force-mismatch option to override this)') |
600 cros_env.Fatal() | 664 cros_env.Fatal() |
601 | 665 |
602 elif not options.server_only: | 666 elif not options.server_only: |
603 parser.error('Either --server-only must be specified or ' | 667 parser.error('Either --server-only must be specified or ' |
604 '--remote=<client> needs to be given') | 668 '--remote=<client> needs to be given') |
605 | 669 |
606 update_file = os.path.join(image_directory, UPDATE_FILENAME) | |
607 stateful_file = os.path.join(image_directory, STATEFUL_FILENAME) | |
608 | |
609 if (not cros_env.GenerateUpdatePayload(image_file, update_file) or | 670 if (not cros_env.GenerateUpdatePayload(image_file, update_file) or |
610 not cros_env.BuildStateful(image_file, image_directory, stateful_file)): | 671 not cros_env.BuildStateful(image_file, image_directory, stateful_file)): |
611 cros_env.Fatal() | 672 cros_env.Fatal() |
612 | 673 |
613 cros_env.CreateServer(options.port, update_file, stateful_file) | 674 cros_env.CreateServer(options.port, update_file, stateful_file) |
614 | 675 |
615 exit_status = 1 | 676 exit_status = 1 |
616 if options.server_only: | 677 if options.server_only: |
617 child = None | 678 child = None |
618 else: | 679 else: |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
650 | 711 |
651 if child: | 712 if child: |
652 os.kill(child, 15) | 713 os.kill(child, 15) |
653 | 714 |
654 cros_env.Info('Server exiting with status %d' % exit_status) | 715 cros_env.Info('Server exiting with status %d' % exit_status) |
655 sys.exit(exit_status) | 716 sys.exit(exit_status) |
656 | 717 |
657 | 718 |
658 if __name__ == '__main__': | 719 if __name__ == '__main__': |
659 main(sys.argv) | 720 main(sys.argv) |
OLD | NEW |