#!/bin/bash # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. #This is a quick prototype to convert VM images to a barebone/live image. #Several steps/guidelines need to be followed in preparation of the VM image. #The assumption is that the VM image is running Ubuntu would have live-boot and #live-config installed; consists of only a root (/) partition and a swap #partition where the root is the first partition. # #Few other adjustment required to the kernel in order to get this working: #add max_loop=x and loop.max_part=y to the /etc/default/grub and update-grub. #Version $thisver: Written 2011 by Jing-Yuan Luke at MIMOS #export PYTHONPATH=/opt/tashi/tashi/branches/luke-zoni/src export PATH=$PATH:$PYTHONPATH/zoni/extra thisver="1.0" usage() { cat << EOF usage: $0 options This script converts an VM image to a live boot image and registers it to Zoni. Currently it only support Ubuntu/Debian based VM images using the live-boot, live-config and live-tools packages. It consists of 2 stages: - 1st stage to check and if necessary converts the VM image from any formats to raw, insert live-* packages if missing and finally create a squashfs image. - 2nd stage (still work in progress), the script will check the version of kernel and initrd with Zoni's database before registering the new image to Zoni and move it to the appropriate folder. OPTIONS: -h Show this message -i Your input VM image -n Name for the live image to be registered in Zoni -u Update live image -p The / or root partition of the input VM Image (optional) -v Print version and Exit EOF } # Self Check routine, should run just once when convertz is triggered the first time. self_check () { if [ -f $WHEREISGRUB ] then if [ ! "`grep -E "max_loop=.*loop\.max_part=" $WHEREISGRUB`" ] then echo "Missing max_loop and loop.max_part in ${WHEREISGRUB}, please check and correct then reboot." exit 1 else echo "grub and loop OK!" fi else echo "Grub missing! Please check!" exit 1 fi if [ -z "${MYQEMUIMG}" -o ! -f "${MYQEMUIMG}" ] then echo "Missing qemu-img, please install the package." exit 1 else echo "qemu-img OK!" fi if [ -z "${MYMKSQUASHFS}" -o ! -f "${MYMKSQUASHFS}" ] then echo "Missing mksquashfs, please install the package." exit 1 else echo "mksquashfs OK!" fi if [ -z "${MYUUIDGEN}" -o ! -f "${MYUUIDGEN}" ] then echo "Missing uuidgen, please install the package." exit 1 else echo "uuidgen OK!" fi if [ -z "${MYPARTED}" -o ! -f "${MYPARTED}" ] then echo "Missing parted, please install the package." exit 1 else echo "parted OK!" fi if [ `ls /var/www/live-boot-support/live-*.deb 2> /dev/null | wc -l` -ge 5 ] then echo "live boot and live config debs OK!" else echo "Missing necessary debs, please build and copy to /var/www/live-boot-support." exit 1 fi # Create a dummy self-checked file touch $HOME/.convertz-selfchecked } # Add live packages to the mounted image but first check and if necessary remove casper package add_live_package () { echo "Checking for casper..." if [ -f $MYTEMPDIR/var/lib/dpkg/info/casper.list ] then echo "chroot remove casper pacakge..." chroot $MYTEMPDIR dpkg --purge casper fi echo "Checking for live-* packages..." if [ -f $MYTEMPDIR/var/lib/dpkg/info/live-boot.list -a -f $MYTEMPDIR/var/lib/dpkg/info/live-config.list -a -f $MYTEMPDIR/var/lib/dpkg/info/live-config-upstart.list -a -f $MYTEMPDIR/var/lib/dpkg/info/live-tools.list ] then echo "debian live packages installed" else echo "chroot adding live packages..." cp /var/www/live-boot-support/live-* $MYTEMPDIR index=0 for i in `ls /var/www/live-boot-support/live-* | cut -f5 -d"/"` do LIVEDEBS[index]=$i let index=index+1 done chroot $MYTEMPDIR dpkg -i ${LIVEDEBS[@]} rm $MYTEMPDIR/live-* fi } # Clean up temporary file and folder before unmounting clean_tmp () { if [ -e $MYTEMPIMG ] then echo "Cleaning ${MYTEMPIMG}..." rm $MYTEMPIMG fi if [ -d $MYTEMPDIR ] then if mount | grep $MYTEMPDIR | grep $MYFREELOOP &> /dev/null then echo "Unmounting ${MYTEMPDIR}..." umount $MYTEMPDIR fi echo "Removing ${MYTEMPDIR}..." rmdir $MYTEMPDIR fi echo "Cleaning done." } # Check the loop device partitions check_loop_part () { echo "Checking Image Partitions..." if [ `parted $MYFREELOOP unit s p | tail -n +7 | head -n -1 | grep -v extended | wc -l` -gt 2 ] then echo "You got more than 2 partitions, please use the \"-p \" option." umnt_loop exit 1 else echo "Just 2 partitions." MYFREELOOPPART=${MYFREELOOP}p`parted $MYFREELOOP unit s p | tail -n +7 | head -n -1 | grep -v extended | grep -v linux-swap | awk '{ print $1 }'` fi } # Detach the loop device but first clean temp file and folder umnt_loop () { # Got to clean and umount the temp dir first before detach loop clean_tmp echo "Detaching loop..." losetup -d $MYFREELOOP &> /dev/null if [ ! $? == "0" ] then echo "Error detaching ${MYFREELOOP}, you may need to manually do it" exit 1 else echo "losetup detach success." fi } # Convert image to raw format convert_fmt () { echo "Converting from ${MYIMGFMT} to raw..." $MYQEMUIMG convert -O raw $MYIMGFILE $MYTEMPIMG &> /dev/null } # Check image type and if required convert it check_img_info () { echo "Checking Your Image Format..." MYIMGFMT=`qemu-img info $MYIMGFILE | grep "file format" | cut -f3 -d" "` echo "Your image is in ${MYIMGFMT} format." if [ ! $MYIMGFMT == "raw" ] then convert_fmt FORLOOPMNT=$MYTEMPIMG else FORLOOPMNT=$MYIMGFILE fi } # Get info on vmlinuz, initrd.img, architecture, distribution's name and version as well as actual kernel version retrieve_img_internals () { # Find the distro name (e.g. Ubuntu, Debian, etc.) and version here (e.g. Lucid, Maverick, etc.) if [ -f $MYTEMPDIR/etc/lsb-release ] then DIST=`grep DISTRIB_ID $MYTEMPDIR/etc/lsb-release | cut -f2 -d"="` echo $DIST DIST_VER=`grep DISTRIB_CODENAME $MYTEMPDIR/etc/lsb-release | cut -f2 -d"="` echo $DIST_VER else echo "Not Ubuntu/Debian! Only Ubuntu/Debian is currently supported." umnt_loop exit 1 fi # Get the vmlinuz and initrd then get their version cd $MYTEMPDIR/boot if [ `ls vmlinuz* | wc -l` -gt 1 ] then echo "You have more than 1 kernel installed" MYVMLINUZ=`ls -t vmlinuz* | head -n 1` MYINITRD=`ls -t initrd* | head -n 1` echo "Your latest vmlinuz is ${MYVMLINUZ} and initrd is ${MYINITRD}" else MYVMLINUZ=`ls vmlinuz*` MYINITRD=`ls initrd*` echo "Your vmlinuz is ${MYVMLINUZ} and initrd is ${MYINITRD}" fi MYVMLINUZ_VER=`echo $MYVMLINUZ | sed 's/vmlinuz-//'` MYINITRD_VER=`echo $MYINITRD | sed 's/initrd.img-//'` # Get the actual kernel version (e.g. 2.x.y-a.b instead of 2.x.y-a-generic) KERN=`zcat /usr/share/doc/linux-image-${KERN_UNAME}/changelog.Debian.gz | head -n 1 | awk '{ print $2 }'` KERN_VERSION=${KERN:1:(-1)} echo "Your actual kernel version is ${KERN_VERSION}" # Get bitness/arch type (e.g. i386 or x86_64) if grep "elf64-x86-64" config-${MYVMLINUZ_VER} &> /dev/null && grep "CONFIG_X86_64=y" config-${MYVMLINUZ_VER} &> /dev/null then MYARCHNESS="x86_64" elif grep "elf32-i386" config-${MYVMLINUZ_VER} &> /dev/null && grep "CONFIG_X86_32=y" config-${MYVMLINUZ_VER} &> /dev/null then MYARCHNESS="i386" else echo "Can't determine your image's OS Architecture!" umnt_loop exit 1 fi echo "Your have a ${MYARCHNESS} OS" cd $WORKDIR } # Main starts here. First declare a few variables that is required for self check, more declaration after the self check. WHEREISGRUB="/boot/grub/grub.cfg" WORKDIR=$PWD MYQEMUIMG=`which qemu-img` MYMKSQUASHFS=`which mksquashfs` MYUUIDGEN=`which uuidgen` MYPARTED=`which parted` INPUTIMAGE= IMAGENAME= PARTNUM= while getopts "hi:n:p:vu" OPTION do case $OPTION in h) usage exit 0 ;; i) INPUTIMAGE=$OPTARG ;; n) IMAGENAME=$OPTARG ;; p) PARTNUM=$OPTARG ;; v) echo echo "$0: Version ${thisver}" echo "By Jing-Yuan Luke, Written 2011 at MIMOS" echo exit 0 ;; u) UPDATE=1 ;; ?) usage exit 1 ;; esac done if [[ -z $IMAGENAME ]] || [[ -z $INPUTIMAGE ]] then usage exit 1 fi MYIMGFILE=$INPUTIMAGE if [ ! -f $MYIMGFILE ] then echo "Input Image File does not exist!" exit 1 fi if zoni -I | grep ${IMAGENAME} &> /dev/null then if [[ -n $UPDATE ]] then echo "Image will be updated." else echo "Image is already registered in Zoni! Use -u if you intend to update/refresh the same Image." exit 1 fi else echo "Image Name OK!" fi echo "Start of Phase 1: Preparing the VM image to squashfs image..." echo "Working from ${WORKDIR}..." # Check for key tools and files, should only run once so check for dummy convertz-selfchecked file first. if [ ! -f $HOME/.convertz-selfchecked ] then echo "Running Self Check for the first time." self_check else echo "Self Check not needed, bypassing it." fi # More variables for the rest of the work KIIDS= MYVMLINUZ= MYINITRD= MYVMLINUZ_VER= MYINITRD_VER= KERN_VERSION= MYARCHNESS= DIST= DIST_VER= MYFREELOOPPART= MYPID="`echo $$`-`uuidgen -t`" MYTEMPDIR="/tmp/convertz-${MYPID}" MYTEMPIMG="/tmp/convertz-${MYPID}.img" MYDESTFILE="${IMAGENAME}.squashfs" LIVEIMAGEDIR="/var/www/liveboot/" TFTPBOOTDIR="/var/lib/tftpboot/liveboot/" # Check image file info and if necessary convert it to raw check_img_info # Check for free loop device and then attach image to the free loop device MYFREELOOP=`losetup -f` if [ -z $MYFREELOOP ] then echo "No more loop devices available." exit 1 else echo "loop device available: $MYFREELOOP" fi losetup $MYFREELOOP $FORLOOPMNT &> /dev/null if [ ! $? == "0" ] then echo "Error attaching ${FORLOOPMNT} to ${MYFREELOOP}" clean_tmp exit 1 else echo "losetup attached successfully." fi # Mount the loop device partition assuming image is created with only 2 partitions else user need to determine themselves but first create a temp dir if [ ! -d $MYTEMPDIR ] then echo "Create temp folder..." mkdir -p $MYTEMPDIR fi if [ -z $PARTNUM ] then check_loop_part else MYFREELOOPPART=${MYFREELOOP}p${PARTNUM} fi if mount "${MYFREELOOPPART}" $MYTEMPDIR &> /dev/null then echo "mount ${MYFREELOOPPART} to ${MYTEMPDIR} success." else echo "Can't mount ${MYFREELOOPPART} to ${MYTEMPDIR}, please check/recreate your image." umnt_loop exit 1 fi # Get some internal info out from the mounted image first, is it ubuntu/debian or something else, check versions of the vmlinuz and initrd as well as OS architecture retrieve_img_internals # Add live packages if necessary add_live_package # Squash the mounted image now, but first clean up /etc/hosts and /etc/hostname rm $MYTEMPDIR/etc/hosts &> /dev/null rm $MYTEMPDIR/etc/hostname &> /dev/null echo "Creating ${MYDESTFILE}, squashing..." mksquashfs $MYTEMPDIR $MYDESTFILE -e boot &> /dev/null if [ $? == "0" ] then echo "mksquashfs done, your ${MYDESTFILE} is ready." else echo "Error while squashing, please check and redo." umnt_loop exit 1 fi echo "End of Phase 1!" echo "Start of Phase 2: Registering to Zoni..." KIIDS=`zoni --getKernelInitrdID ${MYVMLINUZ}:${MYINITRD}:${MYARCHNESS}` &> /dev/null if [ ! -z $KIIDS ] then echo "The kernel and initrd are already registered in Zoni." # To add update feature - need to check the kernel version else echo "Registering new kernel and initrd into Zoni..." KIIDS=`zoni --registerKernelInitrd "${MYVMLINUZ}:${KERN_VERSION}:${MYARCHNESS}:${MYINITRD}:${MYARCHNESS}:${MYDESTFILE}"` &> /dev/null echo "Copying new kernel and initrd to tftpboot..." cp $MYTEMPDIR/boot/$MYVMLINUZ $TFTPBOOTDIR cp $MYTEMPDIR/boot/$MYINITRD $TFTPBOOTDIR fi if [[ -n $UPDATE ]] then echo "Updating/Refreshing Image, no registration needed." else echo "Registering new live image to Zoni Database..." zoni --addImage "${IMAGENAME}:${DIST}:${DIST_VER}:${KIIDS}" fi echo "Updating/Moving ${MYDESTFILE} to live image folder..." mv $MYDESTFILE $LIVEIMAGEDIR #create template for this new image? #zoni addpxe here? echo "End of Phase 2: unmount, clean temp file/folder and remove loop device..." umnt_loop echo "All Done!" exit 0