Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 505 lines (405 sloc) 11.4 KB
#!/bin/bash
# Build a guest filesystem image
# Script name
THIS="build-guest"
# Where to store disk image
IMGDIR="$HOME/var/guests"
# Where guest-builder caches downloaded tarballs etc.
CACHE="/var/cache/guest-builder"
# Location of GRUB files
GRUB_ORIG="/usr/lib/grub/x86_64-pc"
# Default distro to install
DISTRO="debian"
# Default release to install (blank for per-distro default)
RELEASE=""
# Target architecture
TARGET_ARCH=amd64
# Location of site specific configs, kernels etc.
GB_SITE_DIR="/var/lib/guest-builder/"
# Root of location of stock kernels and kernel modules
# Kernel image is looked for in $KERNEL_DIR/boot and
# modules in $KERNEL_DIR/lib/modules
# Use the running system kernel
#KERNEL_DIR=""
# Specify a location of working kernel
KERNEL_DIR="$GB_SITE_DIR/$TARGET_ARCH"
# Use the running kernel version
#KERNEL_VERSION=`uname -r`
# Specify a kernel
KERNEL_VERSION="2.6.18-6-amd64"
############################################################
## No configurable variables past this point ##
############################################################
SAVED_PWD=`pwd`
VERBOSE=""
DRYRUN=""
XM_ARGS=""
version () {
echo >&2 "$THIS version "1
exit 1
}
usage () {
echo >&2 "$THIS, build a guest filesystem"
echo >&2
echo >&2 "Usage: $THIS [options] command hostname"
echo >&2
echo >&2 "Commands"
echo >&2 " create Create a guest filesystem"
echo >&2 " start Start a virtual machine"
echo >&2 " help Display this help and exit"
echo >&2
echo >&2 "General options"
echo >&2 " -n, --dry-run Don't actually run any commands; just print them."
echo >&2
echo >&2 "Installation options"
echo >&2 " -a ARCH, --arch ARCH Specify target architecture"
echo >&2 " -k KERNEL, --kernel KERNEL Specify kernel (\`uname -r\`)"
echo >&2 " -d DISTRO, --distro DISTRO Install DISTRO (default: debian)"
echo >&2 " -l RELEASE_NAME, --release RELEASE_NAME"
echo >&2 " Specify name of release to install"
echo >&2 " -r root, --root root Specify root device or filename"
echo >&2 " -R size, --root-size size Set the root filesystem size (GB)"
echo >&2
echo >&2 "Virtualization options"
echo >&2 " -c, --console Create a running instance of the domain, and"
echo >&2 " attach to its console"
echo >&2 " --create Create a running instance of the domain"
echo >&2
echo >&2 "Removal options"
echo >&2 " -p, --purge Purge the filesystems and config for hostname"
echo >&2
echo >&2 "Miscellaneous options"
echo >&2 " -h, --help Display this help and exit"
echo >&2 " -v, --verbose Print informative messages"
echo >&2 " --version Output version information and exit"
echo >&2
exit 1
}
############################################################
## General functions
############################################################
verbose_echo () {
if test "x$VERBOSE" != "x"; then
echo $*
fi
}
#
# try_run desc cmd
#
try_run () {
desc=$1
shift
verbose_echo ==========================================================================
verbose_echo $desc ...
if test "x$DRYRUN" != "x" || test "x$VERBOSE" != "x"; then
echo $*
fi
if test "x$DRYRUN" = "x" ; then
eval $*
if test "$?" != "0"; then
echo "$THIS: Failed command:"
echo $*
cd $SAVED_PWD
exit 1
fi
fi
}
############################################################
## BEGIN: Parse options
############################################################
do_create=0
do_auto=0
distro_installed=0
ROOT=""
ROOT_XTYPE="file"
ROOT_SIZE="2" # GiB
GETOPTEST=`getopt --version`
SHORTOPTS="nhva:k:d:l:r:R:s:S:ca"
case $GETOPTEST in
getopt*) # GNU getopt
TEMP=`getopt -l dry-run -l verbose -l version -l help -l arch:: -l kernel:: -l distro:: -l release:: -l root:: -l root-size:: -l console -l create -l auto -- +$SHORTOPTS $@`
;;
*) # POSIX getopt ?
TEMP=`getopt $SHORTOPTS $@`
;;
esac
if test "$?" != "0"; then
usage
fi
eval set -- "$TEMP"
while test "X$1" != "X--"; do
case "$1" in
-n|--dry-run)
DRYRUN="y"
;;
-v|verbose)
VERBOSE="y"
;;
--version)
version
;;
-h|--help)
usage
;;
-a|--arch)
shift
TARGET_ARCH=$1
;;
-k|--kernel)
shift
KERNEL_VERSION=$1
;;
-d|--distro)
shift
DISTRO=$1
;;
-l|--release)
shift
RELEASE=$1
;;
-r|--root)
shift
ROOT=$1
;;
-R|--root-size)
shift
ROOT_SIZE=$1
;;
-c|--console)
do_create=1
XM_ARGS="$XM_ARGS -c"
;;
--create)
do_create=1
;;
-a|--auto)
do_auto=1
;;
esac
shift
done
# Check that all options parsed ok
if test "x$1" != "x--"; then
usage
fi
shift #get rid of the '--'
command=$1
if test "x$command" = "x"; then
usage
else
shift
fi
############################################################
## Setup
############################################################
GUEST_NAME=$1
GUEST_PATH="$IMGDIR/$GUEST_NAME"
if test "x$ROOT" == "x" ; then
ROOT="$GUEST_PATH/pristine"
fi
ROOT_LABEL="$GUEST_NAME-root"
QCOW2="$GUEST_PATH/live-0"
MACADDR_FILE="${QCOW2}.macaddr"
MOUNTPOINT="/mnt/$GUEST_NAME"
MOUNT_OPTS=""
#CONF="$CONFDIR/$GUEST_NAME.conf"
#
#VMLINUZ="/boot/vmlinuz-$KERNEL"
############################################################
## Cleanup functions
############################################################
# exit status
stat=1
create_cleanup () {
if test "$do_cleanup" -eq 1 ; then
try_run "Removing filesystems" rm -rf $GUEST_PATH
(mount|grep -q $MOUNTPOINT && umount $MOUNTPOINT) || \
losetup -s $LOOP && losetup -d $LOOP
#try_run "Removing $CONF" rm -f $CONF
echo wheee
fi
[ -d $MOUNTPOINT ] && try_run "Removing mountpoint" rmdir $MOUNTPOINT
exit $stat
}
create_init () {
# clean up built files on error
do_cleanup=1
trap cleanup_create 0
trap 'echo $THIS: Interrupted, cleaning up ...' INT QUIT KILL
## Distros
DISTROS_AVAILABLE=""
for s in ./distros/*; do
source $s
done
}
############################################################
## Actions
############################################################
create_init_filesystems () {
if [ -e $GUEST_PATH ] ; then
echo "$GUEST_PATH already exists, exiting ..."
do_cleanup=0
exit 1
fi
if [ -e $MOUNTPOINT ] ; then
echo "Mount point $MOUNTPOINT already exists, exiting ..."
do_cleanup=0
exit 1
fi
if test -b $ROOT ; then
try_run "Initializing root filesystem" \
mkfs.ext3 -L $ROOT_LABEL $ROOT
ROOT_XTYPE="phy"
else
ROOT_DIR=`dirname $ROOT`
try_run "Finding loop device" \
"(LOOP=$(losetup -f) && verbose_echo $LOOP)"
try_run "Copying bootloaders for root filesystem" \
"(mkdir -p $ROOT_DIR && \
dd if=$GRUB_ORIG/stage1 of=$ROOT bs=512 count=1 && \
dd if=$GRUB_ORIG/stage2 of=$ROOT bs=512 seek=1)"
try_run "Creating sparse disk file for root filesystem" \
"dd if=/dev/zero of=$ROOT bs=1K count=1 seek=${ROOT_SIZE}M"
try_run "Attaching disk file to loopback device" \
"losetup $LOOP $ROOT"
# Ensure OFFSET is larger than the sum of the sizes of
# GRUB stage1 and stage2
# OFFSET=128000
OFFSET=$(S=0 ; \
for i in $(ls -s1 --block-size=1 $GRUB_ORIG/stage[12] | \
awk '{print $1}'); \
do S=$(($S + $i)); \
done; \
Q=32000 ; \
echo $(( (($S + $Q)/$Q) * $Q )) \
)
# parted returns 1 on success for the last two steps, so we
# fake the final exit value
try_run "Partitioning disk image" \
"parted -s $LOOP mklabel msdos; \
parted -s $LOOP -- mkpartfs primary ext2 ${OFFSET}B -1s; \
parted -s $LOOP set 1 boot on; true"
try_run "Detaching disk file from loopback device" \
"losetup -d $LOOP"
#try_run "Making ext3 filesystem" \
# "yes | mkfs.ext3 -L $ROOT_LABEL $ROOT"
MOUNT_OPTS="-o loop,offset=$OFFSET"
fi
try_run "Mounting $ROOT at $MOUNTPOINT" \
"(mkdir $MOUNTPOINT && mount $MOUNT_OPTS $ROOT $MOUNTPOINT)"
}
create_install_os () {
if test "x$DISTRO" != "xnone" ; then
for d in $DISTROS_AVAILABLE ; do
if test "x$DISTRO" = "x$d" ; then
echo >&2 Installing $DISTRO ...
eval init_$d $RELEASE
distro_installed=1
fi
done
if test "$distro_installed" -eq 0 ; then
echo >&2 Installation of $DISTRO failed
echo >&2 Available distributions: $DISTROS_AVAILABLE
exit 1
fi
fi
}
create_install_kernel () {
if [ ! -e $KERNEL_DIR ] ; then
echo "No kernel to install ..."
return
fi
try_run "Copying the kernel's image files to $MOUNTPOINT/boot" \
"(mkdir -p $MOUNTPOINT/boot && \
cp $KERNEL_DIR/boot/*${KERNEL_VERSION}* $MOUNTPOINT/boot)"
try_run "Copying kernel modules to $MOUNTPOINT/lib/modules" \
"(mkdir -p $MOUNTPOINT/lib/modules && \
cp -a $KERNEL_DIR/lib/modules/$KERNEL_VERSION $MOUNTPOINT/lib/modules)"
# Configure menu.lst
GRUB_TARGET="$MOUNTPOINT/boot/grub"
try_run "Copying GRUB bootfiles" \
"(mkdir -p $GRUB_TARGET && cp $GRUB_ORIG/* $GRUB_TARGET)"
if test "x$DRYRUN" = "x" ; then
cat > $MOUNTPOINT/boot/grub/menu.lst << EOF
default 0
timeout 3
hiddenmenu
title Linux kernel $KERNEL_VERSION
root (hd0,0)
kernel /boot/vmlinuz-$KERNEL_VERSION root=/dev/hda1 ro
initrd /boot/initrd.img-$KERNEL_VERSION
quiet
title Linux kernel $KERNEL_VERSION (recovery mode)
root (hd0,0)
kernel /boot/vmlinuz-$KERNEL_VERSION root=/dev/hda1 ro single
initrd /boot/initrd.img-$KERNEL_VERSION
quiet
EOF
fi
try_run "Unmounting $MOUNTPOINT" \
umount $MOUNTPOINT
}
create_install_grub() {
try_run "Installing grub to disk bootsector" \
grub --device-map=/dev/null << EOF
device (hd0) $ROOT
root (hd0,0)
setup (hd0)
EOF
}
create_qcow2() {
try_run "Creating copy-on-write live disk" \
qemu-img create -b $ROOT -f qcow2 $QCOW2
}
generate_macaddr() {
# generate a random mac address for the qemu nic
# By pheldens on qemu forum
MACADDR=$(echo -n DE:AD:BE:EF ; \
for i in `seq 1 2` ; do \
echo -n $(echo ":$RANDOM$RANDOM" | cut -n -c -3) ; \
done)
try_run "Generated MAC address $MACADDR" \
"(echo $MACADDR > $MACADDR_FILE)"
}
############################################################
## start
############################################################
start_kvm () {
try_run "Starting kvm" \
kvm $QCOW2 -net nic,macaddr=$(cat $MACADDR_FILE) -net tap $*
}
############################################################
## create
############################################################
create () {
create_init
create_init_filesystems
create_install_os
create_install_kernel
create_install_grub
create_qcow2
generate_macaddr
# From this point, the filesystem and config are all set up
do_cleanup=0
if test "$distro_installed" -eq 1 ; then
if test "$do_create" -eq 1 ; then
start_kvm $*
fi
fi
# Set exit status to 0
stat=0
}
############################################################
## Main
############################################################
case $command in
help )
usage
;;
create)
create $*
;;
start|run)
start_kvm $*
;;
esac