# # Copyright (C) 2007, 2008, 2009 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. AWK="@AWK@" MKDIR_P="@MKDIR_P@" DUMP="@DUMP@" RESTORE="@RESTORE@" LOSETUP="@LOSETUP@" KPARTX="@KPARTX@" SFDISK="@SFDISK@" QEMU_IMG="@QEMU_IMG@" NTFSCLONE="@NTFSCLONE@" NTFSRESIZE="@NTFSRESIZE@" XMLSTARLET="@XMLSTARLET@" INSTALL_MBR="@INSTALL_MBR@" @WINSUPPORT_TRUE@windows_support="yes" CLEANUP=( ) log_error() { echo "$@" >&2 } debug() { [ "$IMAGE_DEBUG" == "1" -o "$IMAGE_DEBUG" == "yes" ] && $@ || : } get_api5_arguments() { GETOPT_RESULT=$* # Note the quotes around `$TEMP': they are essential! eval set -- "$GETOPT_RESULT" while true; do case "$1" in -i|-n) instance=$2; shift 2;; -o) old_name=$2; shift 2;; -b) blockdev=$2; shift 2;; -s) swapdev=$2; shift 2;; --) shift; break;; *) log_error "Internal error!" >&2; exit 1;; esac done if [ -z "$instance" -o -z "$blockdev" ]; then log_error "Missing OS API Argument (-i, -n, or -b)" exit 1 fi if [ "$SCRIPT_NAME" != "export" -a -z "$swapdev" ]; then log_error "Missing OS API Argument -s (swapdev)" exit 1 fi if [ "$SCRIPT_NAME" = "rename" -a -z "$old_name" ]; then log_error "Missing OS API Argument -o (old_name)" exit 1 fi } get_api10_arguments() { if [ -z "$INSTANCE_NAME" -o -z "$HYPERVISOR" -o -z "$DISK_COUNT" ]; then log_error "Missing OS API Variable:" log_error "(INSTANCE_NAME HYPERVISOR or DISK_COUNT)" exit 1 fi instance=$INSTANCE_NAME if [ $DISK_COUNT -lt 1 -o -z "$DISK_0_PATH" ]; then log_error "At least one disk is needed" exit 1 fi if [ "$SCRIPT_NAME" = "export" ]; then if [ -z "$EXPORT_DEVICE" ]; then log_error "Missing OS API Variable EXPORT_DEVICE" fi blockdev=$EXPORT_DEVICE elif [ "$SCRIPT_NAME" = "import" ]; then if [ -z "$IMPORT_DEVICE" ]; then log_error "Missing OS API Variable IMPORT_DEVICE" fi blockdev=$IMPORT_DEVICE else blockdev=$DISK_0_PATH fi if [ "$SCRIPT_NAME" = "rename" -a -z "$OLD_INSTANCE_NAME" ]; then log_error "Missing OS API Variable OLD_INSTANCE_NAME" fi old_name=$OLD_INSTANCE_NAME } get_api20_parameters() { if [ -z "$OSP_IMG_ID" -o -z "$OSP_IMG_FORMAT" -o -z "$OSP_IMG_PASSWD" ]; then log_error "Missing OS API Parameter:" log_error "(OSP_IMG_ID or OSP_IMG_FORMAT or OSP_IMG_PASSWD)" exit 1 fi IMG_ID=$OSP_IMG_ID IMG_FORMAT=$OSP_IMG_FORMAT IMG_PASSWD=$OSP_IMG_PASSWD } mount_disk0() { local target=$1 mount $root_dev $target CLEANUP+=("umount $target") if [ -n "${boot_dev}" ] ; then $MKDIR_P $target/boot mount $boot_dev $target/boot CLEANUP+=("umount $target/boot") fi # sync the file systems before unmounting to ensure everything is flushed # out CLEANUP+=("sync") } map_disk0() { blockdev="$1" filesystem_dev_base=`$KPARTX -l -p- $blockdev | \ grep -m 1 -- "-1.*$blockdev" | \ $AWK '{print $1}'` if [ -z "$filesystem_dev_base" ]; then log_error "Cannot interpret kpartx output and get partition mapping" exit 1 fi $KPARTX -a -p- $blockdev > /dev/null filesystem_dev="/dev/mapper/${filesystem_dev_base/%-1/}" if [ ! -b "/dev/mapper/$filesystem_dev_base" ]; then log_error "Can't find kpartx mapped partition: /dev/mapper/$filesystem_dev_base" exit 1 fi echo "$filesystem_dev" } map_partition() { filesystem_dev="$1" partition="$2" if [ "${SWAP}" = "yes" -a "${BOOT}" = "yes" ] ; then boot_dev="${filesystem_dev}-1" swap_dev="${filesystem_dev}-2" root_dev="${filesystem_dev}-3" elif [ "${SWAP}" = "no" -a "${BOOT}" = "yes" ] ; then boot_dev="${filesystem_dev}-1" root_dev="${filesystem_dev}-2" elif [ "${SWAP}" = "yes" -a "${BOOT}" = "no" ] ; then swap_dev="${filesystem_dev}-1" root_dev="${filesystem_dev}-2" elif [ "${SWAP}" = "no" -a "${BOOT}" = "no" ] ; then root_dev="${filesystem_dev}-1" fi echo "$(eval "echo \${$(echo ${partition}_dev)"})" } unmap_disk0() { $KPARTX -d -p- $1 } cleanup() { # if something fails here, it souldn't call cleanup again... trap - EXIT if [ ${#CLEANUP[*]} -gt 0 ]; then LAST_ELEMENT=$((${#CLEANUP[*]}-1)) REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0) for i in $REVERSE_INDEXES; do # If something fails here, it's better to retry it for a few times # before we give up with an error. This is needed for kpartx when # dealing with ntfs partitions mounted through fuse. umount is not # synchronous and may return while the partition is still busy. A # premature attempt to delete partition mappings through kpartx on a # device that hosts previously mounted ntfs partition may fail with an # `device-mapper: remove ioctl failed: Device or resource busy' # error. A sensible workaround for this is to wait for a while and then # try again. local cmd=${CLEANUP[$i]} $cmd || for interval in 0.25 0.5 1 2 4; do echo "Command $cmd failed!" echo "I'll wait for $interval secs and will retry..." sleep $interval $cmd && break done test $? -eq 1 && { echo "Giving Up..."; exit 1; } done fi echo "Clean UP executed" } trap cleanup EXIT DEFAULT_FILE="@DEFAULT_DIR@/ganeti-instance-image" if [ -f "$DEFAULT_FILE" ]; then . "$DEFAULT_FILE" fi # note: we don't set a default mirror since debian and ubuntu have # different defaults, and it's better to use the default # only if the user want to specify a mirror in the defaults file we # will use it, this declaration is to make sure the variable is set : ${CDINSTALL:="no"} : ${BOOT:="no"} : ${SWAP:="yes"} : ${SWAP_SIZE:="${INSTANCE_BE_memory}"} : ${FILESYSTEM:="ext3"} : ${KERNEL_ARGS=""} : ${OVERLAY=""} : ${IMAGE_NAME:=""} : ${IMAGE_TYPE:="dump"} : ${NOMOUNT:="no"} : ${ARCH:=""} : ${CUSTOMIZE_DIR:="@sysconfdir@/ganeti/instance-image/hooks"} : ${VARIANTS_DIR:="@sysconfdir@/ganeti/instance-image/variants"} : ${NETWORKS_DIR:="@sysconfdir@/ganeti/instance-image/networks"} : ${OVERLAYS_DIR:="@sysconfdir@/ganeti/instance-image/overlays"} : ${IMAGE_DIR:="@localstatedir@/cache/ganeti-instance-image"} : ${IMAGE_DEBUG:="no"} : ${TOOLS_DIR:="@osdir@/@osname@/tools"} SCRIPT_NAME=$(basename $0) KERNEL_PATH="$INSTANCE_HV_kernel_path" if [ -f /sbin/blkid -a -x /sbin/blkid ]; then VOL_ID="/sbin/blkid -c /dev/null -o value -s UUID" VOL_TYPE="/sbin/blkid -c /dev/null -o value -s TYPE" else for dir in /lib/udev /sbin; do if [ -f $dir/vol_id -a -x $dir/vol_id ]; then VOL_ID="$dir/vol_id -u" VOL_TYPE="$dir/vol_id -t" fi done fi if [ -z "$VOL_ID" ]; then log_error "vol_id or blkid not found, please install udev or util-linux" exit 1 fi if [ -z "$OS_API_VERSION" -o "$OS_API_VERSION" = "5" ]; then OS_API_VERSION=5 GETOPT_RESULT=`getopt -o o:n:i:b:s: -n '$0' -- "$@"` if [ $? != 0 ] ; then log_error "Terminating..."; exit 1 ; fi get_api5_arguments $GETOPT_RESULT elif [ "$OS_API_VERSION" = "10" -o "$OS_API_VERSION" = "15" ]; then get_api10_arguments elif [ "$OS_API_VERSION" = "20" ]; then get_api10_arguments get_api20_parameters IMAGE_NAME=$IMG_ID IMAGE_TYPE=$IMG_FORMAT else log_error "Unknown OS API VERSION $OS_API_VERSION" exit 1 fi if [ -n "$OS_VARIANT" ]; then if [ ! -d "$VARIANTS_DIR" ]; then log_error "OS Variants directory $VARIANTS_DIR doesn't exist" exit 1 fi VARIANT_CONFIG="$VARIANTS_DIR/$OS_VARIANT.conf" if [ -f "$VARIANT_CONFIG" ]; then . "$VARIANT_CONFIG" else if grep -qxF "$OS_VARIANT" variants.list; then log_error "ERROR: instance-image configuration error" log_error " Published variant $OS_VARIANT is missing its config file" log_error " Please create $VARIANT_CONFIG or unpublish the variant" log_error " (by removing $OS_VARIANT from variants.list)" else log_error "Unofficial variant $OS_VARIANT is unsupported" log_error "Most probably this is a user error, forcing a wrong name" log_error "To support this variant please create file $VARIANT_CONFIG" fi exit 1 fi fi