From b5643e8bf62755ec47476fadf3320fdcfca5609d Mon Sep 17 00:00:00 2001 From: Rahul Chaturvedi Date: Fri, 9 Jul 2010 10:46:05 +0530 Subject: [PATCH] Final set of VM scripts - integrated with changes from Zelidrag to create a test image if specified. Integrated comments from http://codereview.chromium.org/2604001/show [the git repository that commit was on was deleted and replaced, hence this new commit] Review URL: http://codereview.chromium.org/2803015 --- fixup_image_for_qemu.py | 148 ++++++++++++++++++++++++++ image_to_vm.sh | 229 ++++++++++++++++++++++++++++++++++++++++ image_to_vmware.sh | 153 --------------------------- 3 files changed, 377 insertions(+), 153 deletions(-) create mode 100755 fixup_image_for_qemu.py create mode 100755 image_to_vm.sh delete mode 100755 image_to_vmware.sh diff --git a/fixup_image_for_qemu.py b/fixup_image_for_qemu.py new file mode 100755 index 0000000000..b0e5ee0dcb --- /dev/null +++ b/fixup_image_for_qemu.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +# 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. + +"""Makes changes to mounted Chromium OS image to allow it to run with VMs + +This script changes two files within the Chromium OS image to let the image +work with VMs, particularly QEMU + +Currently this script does the following, +1.) Modify the post install script to remove EFI fixup section; the VM's we +support don't have EFI support anyway and this section of the script needs +access to the actual device drives + +2.) For QEMU/KVM, we change the xorg.conf to remove mouse support and instead +change it to complete tablet support. This is done to provide better mouse +response in the VM since tablets work of absolute coordinates while the mouse +works of relative. In a screen that doesn't support a mouse grab (e.g., VNC), +relative coordinates can cause the mouse to be flaky + +""" + +from optparse import OptionParser +import os +import stat +import sys + +USAGE = "usage: %prog --mounted_dir=directory --for_qemu=[true]" + +POST_INST_IN_FILENAME = 'usr/sbin/chromeos-postinst' +POST_INST_OUT_FILENAME = 'postinst_vm' +XORG_CONF_FILENAME = os.path.join('etc', 'X11', 'xorg.conf') + +EFI_CODE_MARKER_START = r'echo "Updating grub target for EFI BIOS"' +EFI_CODE_MARKER_END = \ + r"""gpt -S boot -i $NEW_PART_NUM -b /tmp/oldpmbr.bin ${ROOT_DEV} 2>&1 + fi +else""" + +INPUT_SECTION_MARKER = r'Section "InputDevice"' +SECTION_END_MARKER = r'EndSection' + +MOUSE_SECTION_IDENTIFIERS = [] +MOUSE_SECTION_IDENTIFIERS += ['Identifier "Mouse'] +MOUSE_SECTION_IDENTIFIERS += ['Identifier "USBMouse'] + +REPLACE_USB_MOUSE_PAIR = ('InputDevice "USBMouse" "AlwaysCore"', + '') + + +TABLET_DEVICE_CONFIG = """ +Section "InputDevice" + Identifier "Mouse1" + Driver "evdev" + Option "Device" "/dev/input/event2" + Option "CorePointer" "true" +EndSection +""" + + +# Modify the xorg.conf file to remove all mouse sections and replace it +# with ours containing the tablet - note: when running under QEMU, you +# *need* to specify the -usbdevice tablet option to get the mouse to work +def FixXorgConf(mount_point): + xorg_conf_filename = os.path.join(mount_point, XORG_CONF_FILENAME) + f = open(xorg_conf_filename, 'r') + xorg_conf = f.read() + f.close() + + more_sections = 1 + last_found = 0 + while (more_sections): + # Find the input section. + m1 = xorg_conf.find(INPUT_SECTION_MARKER, last_found) + if m1 > -1: + m2 = xorg_conf.find(SECTION_END_MARKER, m1) + m2 += len(SECTION_END_MARKER) + # Make sure the next iteration doesn't rinse/repeat. + last_found = m2 + # Check if this is a mouse section. + for ident in MOUSE_SECTION_IDENTIFIERS: + if xorg_conf.find(ident, m1, m2) != -1: + xorg_conf = xorg_conf[0:m1] + xorg_conf[m2:] + last_found -= (m2-m1) + break + else: + more_sections = 0 + + xorg_conf = xorg_conf[0:last_found] + TABLET_DEVICE_CONFIG + \ + xorg_conf[last_found:] + + # Replace UsbMouse with Tablet. + xorg_conf = xorg_conf.replace(REPLACE_USB_MOUSE_PAIR[0], + REPLACE_USB_MOUSE_PAIR[1]) + + # Write the file back out. + f = open(xorg_conf_filename, 'w') + f.write(xorg_conf) + f.close() + + +# Remove the code that does EFI processing from the postinst script +def FixPostInst(mount_point): + postinst_in = os.path.join(mount_point, POST_INST_IN_FILENAME) + f = open(postinst_in, 'r') + postinst = f.read() + f.close() + m1 = postinst.find(EFI_CODE_MARKER_START) + m2 = postinst.find(EFI_CODE_MARKER_END) + if (m1 == -1) or (m2 == -1) or (m1 > m2): + # basic sanity check + return + m2 += len(EFI_CODE_MARKER_END) + postinst = postinst[0:m1] + postinst[m2:] + + # Write the file back out. + postinst_out = os.path.join(mount_point, POST_INST_OUT_FILENAME) + f = open(postinst_out, 'w') + f.write(postinst) + f.close() + + # Mark the file read/execute. + os.chmod(postinst_out, stat.S_IEXEC | stat.S_IREAD) + + +def main(): + parser = OptionParser(USAGE) + parser.add_option('--mounted_dir', dest='mounted_dir', + help='directory where the Chromium OS image is mounted') + parser.add_option('--for_qemu', dest='for_qemu', + default="true", + help='fixup image for qemu') + (options, args) = parser.parse_args() + + if not options.mounted_dir: + parser.error("Please specify the mount point for the Chromium OS image"); + if options.for_qemu not in ('true', 'false'): + parser.error("Please specify either true or false for --for_qemu") + + FixPostInst(options.mounted_dir) + if (options.for_qemu == 'true'): + FixXorgConf(options.mounted_dir) + + +if __name__ == '__main__': + main() diff --git a/image_to_vm.sh b/image_to_vm.sh new file mode 100755 index 0000000000..8450639276 --- /dev/null +++ b/image_to_vm.sh @@ -0,0 +1,229 @@ +#!/bin/bash + +# 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. + +# Script to convert the output of build_image.sh to a VMware image and write a +# corresponding VMware config file. + +# Load common constants. This should be the first executable line. +# The path to common.sh should be relative to your script's location. +. "$(dirname "$0")/common.sh" +. "$(dirname "$0")/chromeos-common.sh" + +get_default_board + +DEFAULT_VMDK="ide.vmdk" +DEFAULT_VMX="chromiumos.vmx" +DEFAULT_VBOX_DISK="os.vdi" +DEFAULT_QEMU_IMAGE="chromiumos_qemu.image" + +MOD_SCRIPTS_ROOT="${GCLIENT_ROOT}/src/scripts/mod_for_test_scripts" + +# Flags +DEFINE_string board "${DEFAULT_BOARD}" \ + "Board for which the image was built" +DEFINE_boolean factory $FLAGS_FALSE \ + "Modify the image for manufacturing testing" +DEFINE_boolean factory_install $FLAGS_FALSE \ + "Modify the image for factory install shim" +DEFINE_boolean force_copy ${FLAGS_FALSE} "Always rebuild test image" +DEFINE_string format "qemu" \ + "Output format, either qemu, vmware or virtualbox" +DEFINE_string from "" \ + "Directory containing rootfs.image and mbr.image" +DEFINE_boolean make_vmx ${FLAGS_TRUE} \ + "Create a vmx file for use with vmplayer (vmware only)." +DEFINE_integer mem "${DEFAULT_MEM}" \ + "Memory size for the vm config in MBs (vmware only)." +DEFINE_integer rootfs_partition_size 1024 \ + "rootfs parition size in MBs." +DEFINE_string state_image "" \ + "Stateful partition image (defaults to creating new statful partition)" +DEFINE_boolean test_image "${FLAGS_FALSE}" \ + "Copies normal image to chromiumos_test_image.bin, modifies it for test." +DEFINE_string to "" \ + "Destination folder for VM output file(s)" +DEFINE_string vbox_disk "${DEFAULT_VBOX_DISK}" \ + "Filename for the output disk (virtualbox only)." +DEFINE_integer vdisk_size 3072 \ + "virtual disk size in MBs." +DEFINE_string vmdk "${DEFAULT_VMDK}" \ + "Filename for the vmware disk image (vmware only)." +DEFINE_string vmx "${DEFAULT_VMX}" \ + "Filename for the vmware config (vmware only)." + +# Parse command line +FLAGS "$@" || exit 1 +eval set -- "${FLAGS_ARGV}" + +# Die on any errors. +set -e + +if [ -z "${FLAGS_board}" ] ; then + die "--board is required." +fi + +IMAGES_DIR="${DEFAULT_BUILD_ROOT}/images/${FLAGS_board}" +# Default to the most recent image +if [ -z "${FLAGS_from}" ] ; then + FLAGS_from="${IMAGES_DIR}/$(ls -t $IMAGES_DIR | head -1)" +fi +if [ -z "${FLAGS_to}" ] ; then + FLAGS_to="${FLAGS_from}" +fi + +# Use this image as the source image to copy +SRC_IMAGE="${FLAGS_from}/chromiumos_image.bin" + +# If we're asked to modify the image for test, then let's make a copy and +# modify that instead. +if [ ${FLAGS_test_image} -eq ${FLAGS_TRUE} ] ; then + if [ ! -f "${FLAGS_from}/chromiumos_test_image.bin" ] || \ + [ ${FLAGS_force_copy} -eq ${FLAGS_TRUE} ] ; then + # Copy it. + echo "Creating test image from original..." + cp -f "${SRC_IMAGE}" "${FLAGS_from}/chromiumos_test_image.bin" + + # Check for manufacturing image. + if [ ${FLAGS_factory} -eq ${FLAGS_TRUE} ] ; then + EXTRA_ARGS="--factory" + fi + + # Check for install shim. + if [ ${FLAGS_factory_install} -eq ${FLAGS_TRUE} ] ; then + EXTRA_ARGS="--factory_install" + fi + + # Modify it. Pass --yes so that mod_image_for_test.sh won't ask us if we + # really want to modify the image; the user gave their assent already with + # --test-image and the original image is going to be preserved. + "${SCRIPTS_DIR}/mod_image_for_test.sh" --image \ + "${FLAGS_from}/chromiumos_test_image.bin" ${EXTRA_ARGS} --yes + echo "Done with mod_image_for_test." + else + echo "Using cached test image." + fi + SRC_IMAGE="${FLAGS_from}/chromiumos_test_image.bin" + echo "Source test image is: ${SRC_IMAGE}" +fi + +# Memory units are in MBs +DEFAULT_MEM="1024" +TEMP_IMAGE="${IMAGES_DIR}/temp_image.img" + + +# If we're not building for VMWare, don't build the vmx +if [ "${FLAGS_format}" != "vmware" ]; then + FLAGS_make_vmx="${FLAGS_FALSE}" +fi + +# Convert args to paths. Need eval to un-quote the string so that shell +# chars like ~ are processed; just doing FOO=`readlink -f $FOO` won't work. +FLAGS_from=`eval readlink -f $FLAGS_from` +FLAGS_to=`eval readlink -f $FLAGS_to` + +# Split apart the partitions and make some new ones +TEMP_DIR=$(mktemp -d) +(cd "${TEMP_DIR}" && + "${FLAGS_from}/unpack_partitions.sh" "${SRC_IMAGE}") + +# Fix the kernel command line +TEMP_ESP="${TEMP_DIR}"/part_12 +TEMP_ROOTFS="${TEMP_DIR}"/part_3 +TEMP_STATE="${TEMP_DIR}"/part_1 +if [ -n "${FLAGS_state_image}" ]; then + TEMP_STATE="${FLAGS_state_image}" +fi +TEMP_KERN="${TEMP_DIR}"/part_2 +TEMP_PMBR="${TEMP_DIR}"/pmbr +dd if="${SRC_IMAGE}" of="${TEMP_PMBR}" bs=512 count=1 + +TEMP_MNT=$(mktemp -d) +cleanup() { + sudo umount -d "${TEMP_MNT}" + rmdir "${TEMP_MNT}" +} +trap cleanup INT TERM EXIT +mkdir -p "${TEMP_MNT}" +sudo mount -o loop "${TEMP_ROOTFS}" "${TEMP_MNT}" +if [ "${FLAGS_format}" = "qemu" ]; then + sudo python ./fixup_image_for_qemu.py --mounted_dir="${TEMP_MNT}" \ + --for_qemu=true +else + sudo python ./fixup_image_for_qemu.py --mounted_dir="${TEMP_MNT}" \ + --for_qemu=false +fi + +# Change this value if the rootfs partition changes +ROOTFS_PARTITION=/dev/sda3 +sudo "${TEMP_MNT}"/postinst_vm "${ROOTFS_PARTITION}" +trap - INT TERM EXIT +cleanup + +# Make 3 GiB output image +TEMP_IMG=$(mktemp) +# TOOD(adlr): pick a size that will for sure accomodate the partitions +sudo dd if=/dev/zero of="${TEMP_IMG}" bs=1 count=1 \ + seek=$((${FLAGS_vdisk_size} * 1024 * 1024 - 1)) + +# Set up the partition table +install_gpt "${TEMP_IMG}" "${TEMP_ROOTFS}" "${TEMP_KERN}" "${TEMP_STATE}" \ + "${TEMP_PMBR}" "${TEMP_ESP}" true false ${FLAGS_rootfs_partition_size} +# Copy into the partition parts of the file +dd if="${TEMP_ROOTFS}" of="${TEMP_IMG}" conv=notrunc bs=512 \ + seek="${START_ROOTFS_A}" +dd if="${TEMP_STATE}" of="${TEMP_IMG}" conv=notrunc bs=512 \ + seek="${START_STATEFUL}" +dd if="${TEMP_KERN}" of="${TEMP_IMG}" conv=notrunc bs=512 \ + seek="${START_KERN_A}" +dd if="${TEMP_ESP}" of="${TEMP_IMG}" conv=notrunc bs=512 \ + seek="${START_ESP}" + +echo Creating final image +# Convert image to output format +if [ "${FLAGS_format}" = "virtualbox" -o "${FLAGS_format}" = "qemu" ]; then + if [ "${FLAGS_format}" = "virtualbox" ]; then + VBoxManage convertdd "${TEMP_IMG}" "${FLAGS_to}/${FLAGS_vbox_disk}" + else + mv ${TEMP_IMG} ${FLAGS_to}/${DEFAULT_QEMU_IMAGE} + fi +elif [ "${FLAGS_format}" = "vmware" ]; then + qemu-img convert -f raw "${TEMP_IMG}" \ + -O vmdk "${FLAGS_to}/${FLAGS_vmdk}" +else + die "Invalid format: ${FLAGS_format}" +fi + +rm -rf "${TEMP_DIR}" "${TEMP_IMG}" +if [ -z "${FLAGS_state_image}" ]; then + rm -f "${STATE_IMAGE}" +fi + +echo "Created image at ${FLAGS_to}" + +# Generate the vmware config file +# A good reference doc: http://www.sanbarrow.com/vmx.html +VMX_CONFIG="#!/usr/bin/vmware +.encoding = \"UTF-8\" +config.version = \"8\" +virtualHW.version = \"4\" +memsize = \"${FLAGS_mem}\" +ide0:0.present = \"TRUE\" +ide0:0.fileName = \"${FLAGS_vmdk}\" +ethernet0.present = \"TRUE\" +usb.present = \"TRUE\" +sound.present = \"TRUE\" +sound.virtualDev = \"es1371\" +displayName = \"Chromium OS\" +guestOS = \"otherlinux\" +ethernet0.addressType = \"generated\" +floppy0.present = \"FALSE\"" + +if [[ "${FLAGS_make_vmx}" = "${FLAGS_TRUE}" ]]; then + echo "${VMX_CONFIG}" > "${FLAGS_to}/${FLAGS_vmx}" + echo "Wrote the following config to: ${FLAGS_to}/${FLAGS_vmx}" + echo "${VMX_CONFIG}" +fi + diff --git a/image_to_vmware.sh b/image_to_vmware.sh deleted file mode 100755 index e9810150dc..0000000000 --- a/image_to_vmware.sh +++ /dev/null @@ -1,153 +0,0 @@ -#!/bin/bash - -# Copyright (c) 2009 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. - -# Script to convert the output of build_image.sh to a VMware image and write a -# corresponding VMware config file. - -# Load common constants. This should be the first executable line. -# The path to common.sh should be relative to your script's location. -. "$(dirname "$0")/common.sh" -. "$(dirname "$0")/chromeos-common.sh" - -IMAGES_DIR="${DEFAULT_BUILD_ROOT}/images" -# Default to the most recent image -DEFAULT_FROM="${IMAGES_DIR}/`ls -t $IMAGES_DIR | head -1`" -DEFAULT_TO="${DEFAULT_FROM}" -DEFAULT_VMDK="ide.vmdk" -DEFAULT_VMX="chromeos.vmx" -DEFAULT_VBOX_DISK="os.vdi" -# Memory units are in MBs -DEFAULT_MEM="1024" -VBOX_TEMP_IMAGE="${IMAGES_DIR}/vbox_temp.img" - - -# Flags -DEFINE_string from "$DEFAULT_FROM" \ - "Directory containing rootfs.image and mbr.image" -DEFINE_string to "$DEFAULT_TO" \ - "Destination folder for VM output file(s)" -DEFINE_string state_image "" \ - "Stateful partition image (defaults to creating new statful partition)" -DEFINE_string format "vmware" \ - "Output format, either vmware or virtualbox" - -DEFINE_boolean make_vmx ${FLAGS_TRUE} \ - "Create a vmx file for use with vmplayer (vmware only)." -DEFINE_string vmdk "$DEFAULT_VMDK" \ - "Filename for the vmware disk image (vmware only)." -DEFINE_string vmx "$DEFAULT_VMX" \ - "Filename for the vmware config (vmware only)." -DEFINE_integer mem "$DEFAULT_MEM" \ - "Memory size for the vm config in MBs (vmware only)." - -DEFINE_string vbox_disk "$DEFAULT_VBOX_DISK" \ - "Filename for the output disk (virtualbox only)." - -# Parse command line -FLAGS "$@" || exit 1 -eval set -- "${FLAGS_ARGV}" - -# Die on any errors. -set -e - -if [ "$FLAGS_format" != "vmware" ]; then - FLAGS_make_vmx=${FLAGS_FALSE} -fi - -# Convert args to paths. Need eval to un-quote the string so that shell -# chars like ~ are processed; just doing FOO=`readlink -f $FOO` won't work. -FLAGS_from=`eval readlink -f $FLAGS_from` -FLAGS_to=`eval readlink -f $FLAGS_to` - -# Split apart the partitions and make some new ones -TEMP_DIR=$(mktemp -d) -(cd "$TEMP_DIR" && - "${FLAGS_from}/unpack_partitions.sh" "${FLAGS_from}/chromiumos_image.bin") - -# Fix the kernel command line -# FIXME: TEMP_ESP is only partition 4 at the moment. It may change! -TEMP_ESP="$TEMP_DIR"/part_4 -TEMP_ROOTFS="$TEMP_DIR"/part_3 -TEMP_STATE="$TEMP_DIR"/part_1 -if [ -n "${FLAGS_state_image}" ]; then - TEMP_STATE="${FLAGS_state_image}" -fi -TEMP_KERN="$TEMP_DIR"/part_2 -TEMP_PMBR="$TEMP_DIR"/pmbr -dd if="${FLAGS_from}/chromiumos_image.bin" of="$TEMP_PMBR" bs=512 count=1 - -TEMP_MNT=$(mktemp -d) -cleanup() { - sudo umount -d "$TEMP_MNT" - rmdir "$TEMP_MNT" -} -trap cleanup INT TERM EXIT -mkdir -p "$TEMP_MNT" -sudo mount -o loop "$TEMP_ROOTFS" "$TEMP_MNT" -sudo "$TEMP_MNT"/postinst /dev/sda3 -trap - INT TERM EXIT -cleanup - -# Make 3 GiB output image -TEMP_IMG=$(mktemp) -# TOOD(adlr): pick a size that will for sure accomodate the partitions -sudo dd if=/dev/zero of="${TEMP_IMG}" bs=1 count=1 \ - seek=$((3 * 1024 * 1024 * 1024 - 1)) - -# Set up the partition table -install_gpt "$TEMP_IMG" "$TEMP_ROOTFS" "$TEMP_KERN" "$TEMP_STATE" \ - "$TEMP_PMBR" "$TEMP_ESP" true -# Copy into the partition parts of the file -dd if="$TEMP_ROOTFS" of="$TEMP_IMG" conv=notrunc bs=512 seek="$START_ROOTFS_A" -dd if="$TEMP_STATE" of="$TEMP_IMG" conv=notrunc bs=512 seek="$START_STATEFUL" -dd if="$TEMP_KERN" of="$TEMP_IMG" conv=notrunc bs=512 seek="$START_KERN_A" -dd if="$TEMP_ESP" of="$TEMP_IMG" conv=notrunc bs=512 seek="$START_ESP" - -echo Creating final image -# Convert image to output format -if [ "$FLAGS_format" = "virtualbox" ]; then - qemu-img convert -f raw $TEMP_IMG \ - -O raw "${VBOX_TEMP_IMAGE}" - VBoxManage convertdd "${VBOX_TEMP_IMAGE}" "${FLAGS_to}/${FLAGS_vbox_disk}" -elif [ "$FLAGS_format" = "vmware" ]; then - qemu-img convert -f raw $TEMP_IMG \ - -O vmdk "${FLAGS_to}/${FLAGS_vmdk}" -else - echo invalid format: "$FLAGS_format" - exit 1 -fi - -rm -rf "$TEMP_DIR" "${VBOX_TEMP_IMAGE}" "$TEMP_IMG" -if [ -z "$FLAGS_state_image" ]; then - rm -f "$STATE_IMAGE" -fi - -echo "Created image ${FLAGS_to}" - -# Generate the vmware config file -# A good reference doc: http://www.sanbarrow.com/vmx.html -VMX_CONFIG="#!/usr/bin/vmware -.encoding = \"UTF-8\" -config.version = \"8\" -virtualHW.version = \"4\" -memsize = \"${FLAGS_mem}\" -ide0:0.present = \"TRUE\" -ide0:0.fileName = \"${FLAGS_vmdk}\" -ethernet0.present = \"TRUE\" -usb.present = \"TRUE\" -sound.present = \"TRUE\" -sound.virtualDev = \"es1371\" -displayName = \"Chromium OS\" -guestOS = \"otherlinux\" -ethernet0.addressType = \"generated\" -floppy0.present = \"FALSE\"" - -if [[ ${FLAGS_make_vmx} = ${FLAGS_TRUE} ]]; then - echo "${VMX_CONFIG}" > "${FLAGS_to}/${FLAGS_vmx}" - echo "Wrote the following config to: ${FLAGS_to}/${FLAGS_vmx}" - echo "${VMX_CONFIG}" -fi -