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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | cros_generate_update_payload » ('j') | cros_generate_update_payload » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: bin/cros_image_to_target.py
diff --git a/bin/cros_image_to_target.py b/bin/cros_image_to_target.py
index dc6a0f1be5619fbeb7db3e0869c789df269c32e5..cc7d358bbe3fbe95a6a5b95934143fec5f6d9a91 100755
--- a/bin/cros_image_to_target.py
+++ b/bin/cros_image_to_target.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
#
-# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -30,10 +30,11 @@ from xml.dom import minidom
# This is the default filename within the image directory to load updates from
DEFAULT_IMAGE_NAME = 'chromiumos_image.bin'
+DEFAULT_IMAGE_NAME_TEST = 'chromiumos_test_image.bin'
# The filenames we provide to clients to pull updates
UPDATE_FILENAME = 'update.gz'
-STATEFUL_FILENAME = 'stateful.image.gz'
+STATEFUL_FILENAME = 'stateful.gz'
# How long do we wait for the server to start before launching client
SERVER_STARTUP_WAIT = 1
@@ -46,8 +47,12 @@ class Command(object):
self.env = env
def RunPipe(self, pipeline, infile=None, outfile=None,
- capture=False, oneline=False):
- """Perform a command pipeline, with optional input/output filenames."""
+ capture=False, oneline=False, hide_stderr=False):
+ """
+ Perform a command pipeline, with optional input/output filenames.
+
+ hide_stderr Don't allow output of stderr (default False)
+ """
last_pipe = None
while pipeline:
@@ -61,8 +66,10 @@ class Command(object):
kwargs['stdout'] = subprocess.PIPE
elif outfile:
kwargs['stdout'] = open(outfile, 'wb')
+ if hide_stderr:
+ kwargs['stderr'] = open('/dev/null', 'wb')
- self.env.Info('Running: %s' % ' '.join(cmd))
+ self.env.Debug('Running: %s' % ' '.join(cmd))
last_pipe = subprocess.Popen(cmd, **kwargs)
if capture:
@@ -110,9 +117,11 @@ class SSHCommand(Command):
if not self.ssh_dir:
self.Setup()
+ # allow up to 3 seconds to connect
return ['-o', 'Compression=no',
'-o', 'ConnectTimeout=%d' % self.CONNECT_TIMEOUT,
'-o', 'StrictHostKeyChecking=no',
+ '-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
'-o', 'UserKnownHostsFile=%s' % self.known_hosts,
'-i', self.identity]
@@ -139,7 +148,7 @@ class CrosEnv(object):
REBOOT_START_WAIT = 5
REBOOT_WAIT_TIME = 60
- def __init__(self, verbose=False):
+ def __init__(self, verbose=0):
self.cros_root = os.path.dirname(os.path.abspath(sys.argv[0]))
parent = os.path.dirname(self.cros_root)
if os.path.exists(os.path.join(parent, 'chromeos-common.sh')):
@@ -147,6 +156,9 @@ class CrosEnv(object):
self.cmd = Command(self)
self.verbose = verbose
+ # do we have the pv progress tool? (sudo apt-get install pv)
+ 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
+
def Error(self, msg):
print >> sys.stderr, 'ERROR: %s' % msg
@@ -156,9 +168,13 @@ class CrosEnv(object):
sys.exit(1)
def Info(self, msg):
- if self.verbose:
+ if self.verbose > 0:
print 'INFO: %s' % msg
+ def Debug(self, msg):
+ if self.verbose > 1:
+ print 'DEBUG: %s' % msg
+
def CrosUtilsPath(self, filename):
return os.path.join(self.cros_root, filename)
@@ -199,6 +215,7 @@ class CrosEnv(object):
self.Info('Using cached stateful %s' % dst)
return True
+ self.Info('Building stateful')
cgpt = self.ChrootPath('/usr/bin/cgpt')
offset = self.cmd.OutputOneLine(cgpt, 'show', '-b', '-i', '1', src)
size = self.cmd.OutputOneLine(cgpt, 'show', '-s', '-i', '1', src)
@@ -206,9 +223,10 @@ class CrosEnv(object):
self.Error('Unable to use cgpt to get image geometry')
return False
- return self.cmd.RunPipe([['dd', 'if=%s' % src, 'bs=512',
- 'skip=%s' % offset, 'count=%s' % size],
- ['gzip', '-c']], outfile=dst)
+ return self.cmd.RunPipe([
+ ['dd', 'if=%s' % src, 'bs=512', 'skip=%s' % offset, 'count=%s' % size],
+ self.have_pv and ['pv', '-s', '%d' % (int (size) * 512)] or ['cat'],
+ ['gzip', '-c']], outfile=dst, hide_stderr=True)
def GetSize(self, filename):
return os.path.getsize(filename)
@@ -262,10 +280,10 @@ class CrosEnv(object):
UpdateHandler.SetupUrl('/update', PingUpdateResponse())
UpdateHandler.SetupUrl('/%s' % UPDATE_FILENAME,
FileUpdateResponse(update_file,
- verbose=self.verbose))
+ 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
UpdateHandler.SetupUrl('/%s' % STATEFUL_FILENAME,
FileUpdateResponse(stateful_file,
- verbose=self.verbose))
+ verbose=self.verbose, have_pv=self.have_pv))
self.http_server = BaseHTTPServer.HTTPServer(('', port), UpdateHandler)
@@ -304,6 +322,7 @@ class CrosEnv(object):
def StartClient(self, port):
"""Ask the client machine to update from our server."""
+ self.Info ("Starting client...")
adlr 2010/11/17 20:43:25 i think style says no spaces between method name a
status = self.GetUpdateStatus()
if status != 'UPDATE_STATUS_IDLE':
self.Error('Client update status is not IDLE: %s' % status)
@@ -322,6 +341,7 @@ class CrosEnv(object):
self.Error('Client update failed')
return False
+ self.Info('Update complete - running update script on client')
self.ssh_cmd.Copy(self.CrosUtilsPath('../platform/dev/stateful_update'),
'/tmp')
if not self.ssh_cmd.Run('/tmp/stateful_update', url_base,
@@ -334,7 +354,7 @@ class CrosEnv(object):
self.Error('Client may not have successfully rebooted...')
return False
- print 'Client update completed successfully!'
+ self.Info ('Client update completed successfully!')
adlr 2010/11/17 20:43:25 remove space before (
return True
@@ -342,7 +362,7 @@ class UpdateResponse(object):
"""Default response is the 404 error response."""
def Reply(self, handler, send_content=True, post_data=None):
- handler.send_Error(404, 'File not found')
+ handler.send_error(404, 'File not found')
return None
@@ -350,11 +370,12 @@ class FileUpdateResponse(UpdateResponse):
"""Respond by sending the contents of a file."""
def __init__(self, filename, content_type='application/octet-stream',
- verbose=False, blocksize=16*1024):
+ verbose=False, blocksize=16*1024, have_pv=False):
self.filename = filename
self.content_type = content_type
self.verbose = verbose
self.blocksize = blocksize
+ self.have_pv = have_pv
def Reply(self, handler, send_content=True, post_data=None):
"""Return file contents to the client. Optionally display progress."""
@@ -373,26 +394,33 @@ class FileUpdateResponse(UpdateResponse):
handler.date_time_string(filestat.st_mtime))
handler.end_headers()
- if not send_content:
- return
-
- if filesize <= self.blocksize:
- handler.wfile.write(f.read())
- else:
- sent_size = 0
- sent_percentage = None
- while True:
- buf = f.read(self.blocksize)
- if not buf:
- break
- handler.wfile.write(buf)
- if self.verbose:
- sent_size += len(buf)
- percentage = int(100 * sent_size / filesize)
- if sent_percentage != percentage:
- sent_percentage = percentage
- print '\rSent %d%%' % sent_percentage,
- sys.stdout.flush()
+ if send_content:
+ try:
+ fp = False
+ sent_size = 0
+ sent_percentage = None
+
+ # sadly pv gets caught up in the server, not sure why
+ if False: # self.verbose and self.have_pv:
+ fp = subprocess.Popen('pv -s %d >/dev/null' % filesize,
+ shell=True, stdin=subprocess.PIPE, close_fds=True).stdin
+ while True:
+ buf = f.read(self.blocksize)
+ if not buf:
+ break
+ handler.wfile.write(buf)
+ if fp:
+ fp.write(buf)
+ if self.verbose:
+ sent_size += len(buf)
+ percentage = int(100 * sent_size / filesize)
+ if sent_percentage != percentage:
+ sent_percentage = percentage
+ print '\rSent %d%%' % sent_percentage,
+ sys.stdout.flush()
+ finally:
+ if fp:
+ fp.close()
Paul Stewart 2010/11/17 21:37:36 If you can't get it working, please revert this ch
if self.verbose:
print '\n'
f.close()
@@ -556,7 +584,6 @@ def main(argv):
parser.add_option('--from', dest='src', default=None,
help='Source image to install')
parser.add_option('--image-name', dest='image_name',
- default=DEFAULT_IMAGE_NAME,
help='Filename within image directory to load')
parser.add_option('--port', dest='port', default=8081, type='int',
help='TCP port to serve from and tunnel through')
@@ -565,11 +592,23 @@ def main(argv):
parser.add_option('--server-only', dest='server_only', default=False,
action='store_true', help='Do not start client')
parser.add_option('--verbose', dest='verbose', default=False,
+ action='store_true', help='Display progress')
+ parser.add_option('--debug', dest='debug', default=False,
action='store_true', help='Display running commands')
+ parser.add_option('--test', dest='test', default=False,
+ action='store_true', help='Select test image')
(options, args) = parser.parse_args(argv)
- cros_env = CrosEnv(verbose=options.verbose)
+ # we can build the test image if it doesn't exist, so remember if we want to
+ maybe_build_test = False
adlr 2010/11/17 20:43:25 it would be nice to have a variable name that more
+
+ verbosity = 0
+ if options.verbose:
+ verbosity = 1
+ if options.debug:
+ verbosity = 2
+ cros_env = CrosEnv(verbose=verbosity)
if not options.board:
options.board = cros_env.GetDefaultBoard()
@@ -584,17 +623,43 @@ def main(argv):
if not os.path.exists(options.src):
parser.error('Path %s does not exist' % options.src)
+ if not options.image_name:
+ # auto-select the correct image
+ if options.test:
+ options.image_name = DEFAULT_IMAGE_NAME_TEST
+
+ # we will build the test image if not found
+ maybe_build_test = True
+ else:
+ options.image_name = DEFAULT_IMAGE_NAME
+
if os.path.isdir(options.src):
image_directory = options.src
image_file = os.path.join(options.src, options.image_name)
if not os.path.exists(image_file):
+ if maybe_build_test:
+ # we want a test image but it doesn't exist
+ # try to build it if we can
+ cros_env.Info('Creating test image')
+ test_output = cros_env.cmd.Output(cros_env.CrosUtilsPath('enter_chroot.sh'),
adlr 2010/11/17 20:43:25 a few lines around here (this, 646, 649) are over
+ '--', './mod_image_for_test.sh', '--board=%s' % options.board, '-y')
+ if not os.path.exists(image_file):
+ print test_output
+ cros_env.Fatal('Failed to create test image - please run ./mod_image_for_test.sh manually inside the chroot')
parser.error('Image file %s does not exist' % image_file)
else:
image_file = options.src
image_directory = os.path.dirname(options.src)
+ update_file = os.path.join(image_directory, UPDATE_FILENAME)
+ stateful_file = os.path.join(image_directory, STATEFUL_FILENAME)
+ cros_env.Debug("Image file %s" % image_file)
+ cros_env.Debug("Update file %s" % update_file)
+ cros_env.Debug("Stateful file %s" % stateful_file)
+
if options.remote:
+ cros_env.Info('Contacting client %s' % options.remote)
cros_env.SetRemote(options.remote)
rel = cros_env.GetRemoteRelease()
if not rel:
@@ -610,9 +675,6 @@ def main(argv):
parser.error('Either --server-only must be specified or '
'--remote=<client> needs to be given')
- update_file = os.path.join(image_directory, UPDATE_FILENAME)
- stateful_file = os.path.join(image_directory, STATEFUL_FILENAME)
-
if (not cros_env.GenerateUpdatePayload(image_file, update_file) or
not cros_env.BuildStateful(image_file, stateful_file)):
cros_env.Fatal()
« 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