image_chromeos.py revision fd356fb31c4340851fecc750230d05c9518b125c
1#!/usr/bin/python2.6 2# 3# Copyright 2011 Google Inc. All Rights Reserved. 4 5"""Script to image a ChromeOS device. 6 7This script images a remote ChromeOS device with a specific image." 8""" 9 10__author__ = "asharif@google.com (Ahmad Sharif)" 11 12import filecmp 13import glob 14import optparse 15import os 16import shutil 17import sys 18import tempfile 19from utils import command_executer 20from utils import logger 21from utils import misc 22from utils.file_utils import FileUtils 23 24checksum_file = "/usr/local/osimage_checksum_file" 25 26 27def Usage(parser, message): 28 print "ERROR: " + message 29 parser.print_help() 30 sys.exit(0) 31 32def Main(argv): 33 """Build ChromeOS.""" 34 # Common initializations 35 cmd_executer = command_executer.GetCommandExecuter() 36 l = logger.GetLogger() 37 38 parser = optparse.OptionParser() 39 parser.add_option("-c", "--chromeos_root", dest="chromeos_root", 40 help="Target directory for ChromeOS installation.") 41 parser.add_option("-r", "--remote", dest="remote", 42 help="Target device.") 43 parser.add_option("-i", "--image", dest="image", 44 help="Image binary file.") 45 parser.add_option("-b", "--board", dest="board", 46 help="Target board override.") 47 parser.add_option("-f", "--force", dest="force", 48 action="store_true", 49 default=False, 50 help="Force an image even if it is non-test.") 51 parser.add_option("-a", 52 "--image_to_live_args", 53 dest="image_to_live_args") 54 55 56 options = parser.parse_args(argv[1:])[0] 57 58 if options.chromeos_root is None: 59 Usage(parser, "--chromeos_root must be set") 60 61 if options.remote is None: 62 Usage(parser, "--remote must be set") 63 64 options.chromeos_root = os.path.expanduser(options.chromeos_root) 65 66 if options.board is None: 67 board = cmd_executer.CrosLearnBoard(options.chromeos_root, options.remote) 68 else: 69 board = options.board 70 71 if options.image is None: 72 images_dir = misc.GetImageDir(options.chromeos_root, board) 73 image = os.path.join(images_dir, 74 "latest", 75 "chromiumos_test_image.bin") 76 if not os.path.exists(image): 77 image = os.path.join(images_dir, 78 "latest", 79 "chromiumos_image.bin") 80 else: 81 image = options.image 82 image = os.path.expanduser(image) 83 84 image = os.path.realpath(image) 85 86 if not os.path.exists(image): 87 Usage(parser, "Image file: " + image + " does not exist!") 88 89 image_checksum = FileUtils().Md5File(image) 90 91 command = "cat " + checksum_file 92 retval, device_checksum, err = cmd_executer.CrosRunCommand(command, 93 return_output=True, 94 chromeos_root=options.chromeos_root, 95 machine=options.remote) 96 97 device_checksum = device_checksum.strip() 98 image_checksum = str(image_checksum) 99 100 l.LogOutput("Image checksum: " + image_checksum) 101 l.LogOutput("Device checksum: " + device_checksum) 102 103 if image_checksum != device_checksum: 104 [found, located_image] = LocateOrCopyImage(options.chromeos_root, 105 image, 106 board=board) 107 108 l.LogOutput("Checksums do not match. Re-imaging...") 109 110 is_test_image = IsImageModdedForTest(options.chromeos_root, 111 located_image) 112 113 if not is_test_image and not options.force: 114 logger.GetLogger().LogFatal("Have to pass --force to image a non-test " 115 "image!") 116 117 # If the device has /tmp mounted as noexec, image_to_live.sh can fail. 118 command = "mount -o remount,rw,exec /tmp" 119 cmd_executer.CrosRunCommand(command, 120 chromeos_root=options.chromeos_root, 121 machine=options.remote) 122 123 command = (options.chromeos_root + 124 "/src/scripts/image_to_live.sh --remote=" + 125 options.remote + 126 " --image=" + located_image) 127 if options.image_to_live_args: 128 command += " %s" % options.image_to_live_args 129 130 retval = cmd_executer.RunCommand(command) 131 132 if found == False: 133 temp_dir = os.path.dirname(located_image) 134 l.LogOutput("Deleting temp image dir: %s" % temp_dir) 135 shutil.rmtree(temp_dir) 136 137 logger.GetLogger().LogFatalIf(retval, "Image command failed") 138 command = "echo %s > %s && chmod -w %s" % (image_checksum, checksum_file, 139 checksum_file) 140 retval = cmd_executer.CrosRunCommand(command, 141 chromeos_root=options.chromeos_root, 142 machine=options.remote) 143 logger.GetLogger().LogFatalIf(retval, "Writing checksum failed.") 144 145 successfully_imaged = VerifyChromeChecksum(options.chromeos_root, 146 image, 147 options.remote) 148 logger.GetLogger().LogFatalIf(not successfully_imaged, 149 "Image verification failed!") 150 else: 151 l.LogOutput("Checksums match. Skipping reimage") 152 153 return retval 154 155 156def LocateOrCopyImage(chromeos_root, image, board=None): 157 l = logger.GetLogger() 158 if board is None: 159 board_glob = "*" 160 else: 161 board_glob = board 162 163 chromeos_root_realpath = os.path.realpath(chromeos_root) 164 image = os.path.realpath(image) 165 166 if image.startswith("%s/" % chromeos_root_realpath): 167 return [True, image] 168 169 # First search within the existing build dirs for any matching files. 170 images_glob = ("%s/src/build/images/%s/*/*.bin" % 171 (chromeos_root_realpath, 172 board_glob)) 173 images_list = glob.glob(images_glob) 174 for potential_image in images_list: 175 if filecmp.cmp(potential_image, image): 176 l.LogOutput("Found matching image %s in chromeos_root." % potential_image) 177 return [True, potential_image] 178 # We did not find an image. Copy it in the src dir and return the copied file. 179 if board is None: 180 board = "" 181 base_dir = ("%s/src/build/images/%s" % 182 (chromeos_root_realpath, 183 board)) 184 if not os.path.isdir(base_dir): 185 os.makedirs(base_dir) 186 temp_dir = tempfile.mkdtemp(prefix="%s/tmp" % base_dir) 187 new_image = "%s/%s" % (temp_dir, os.path.basename(image)) 188 l.LogOutput("No matching image found. Copying %s to %s" % 189 (image, new_image)) 190 shutil.copyfile(image, new_image) 191 return [False, new_image] 192 193 194def GetImageMountCommand(chromeos_root, image, rootfs_mp, stateful_mp): 195 image_dir = os.path.dirname(image) 196 image_file = os.path.basename(image) 197 mount_command = ("cd %s/src/scripts &&" 198 "./mount_gpt_image.sh --from=%s --image=%s" 199 " --safe --read_only" 200 " --rootfs_mountpt=%s" 201 " --stateful_mountpt=%s" % 202 (chromeos_root, image_dir, image_file, rootfs_mp, 203 stateful_mp)) 204 return mount_command 205 206 207def MountImage(chromeos_root, image, rootfs_mp, stateful_mp, unmount=False): 208 cmd_executer = command_executer.GetCommandExecuter() 209 command = GetImageMountCommand(chromeos_root, image, rootfs_mp, stateful_mp) 210 if unmount: 211 command = "%s --unmount" % command 212 retval = cmd_executer.RunCommand(command) 213 logger.GetLogger().LogFatalIf(retval, "Mount/unmount command failed!") 214 return retval 215 216 217def IsImageModdedForTest(chromeos_root, image): 218 rootfs_mp = tempfile.mkdtemp() 219 stateful_mp = tempfile.mkdtemp() 220 MountImage(chromeos_root, image, rootfs_mp, stateful_mp) 221 lsb_release_file = os.path.join(rootfs_mp, "etc/lsb-release") 222 is_test_image = "Test Build" in open(lsb_release_file).read() 223 MountImage(chromeos_root, image, rootfs_mp, stateful_mp, unmount=True) 224 return is_test_image 225 226 227def VerifyChromeChecksum(chromeos_root, image, remote): 228 cmd_executer = command_executer.GetCommandExecuter() 229 rootfs_mp = tempfile.mkdtemp() 230 stateful_mp = tempfile.mkdtemp() 231 MountImage(chromeos_root, image, rootfs_mp, stateful_mp) 232 image_chrome_checksum = FileUtils().Md5File("%s/opt/google/chrome/chrome" % 233 rootfs_mp) 234 MountImage(chromeos_root, image, rootfs_mp, stateful_mp, unmount=True) 235 236 command = "md5sum /opt/google/chrome/chrome" 237 [r, o, e] = cmd_executer.CrosRunCommand(command, 238 return_output=True, 239 chromeos_root=chromeos_root, 240 machine=remote) 241 device_chrome_checksum = o.split()[0] 242 if image_chrome_checksum.strip() == device_chrome_checksum.strip(): 243 return True 244 else: 245 return False 246 247 248if __name__ == "__main__": 249 retval = Main(sys.argv) 250 sys.exit(retval) 251