Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
executable file 482 lines (401 sloc) 13 KB
#!/bin/bash
# Filename: kantan
# Purpose: main interface to the kantan test suite
# Authors: (c) Michael Prokop <mika@grml.org>
# Bug-Reports: see http://grml.org/bugs/
# License: This file is licensed under the GPL v2 or any later version.
################################################################################
set -u
# global settings {{{
PN=$(basename $0)
VERSION="$(dpkg --list ${PN} 2>/dev/null| awk '/^i/ {print $3}')"
[ -n "${VERSION:-}" ] || VERSION="unknown"
NO_CONSOLE=false
DEBUG=0
# }}}
# helper functions {{{
info() {
printf "Info: $*\n"
}
warn() {
printf "Warn: $*\n"
}
error() {
printf "Error: $*\n">&2
}
debug() {
if [ $DEBUG -gt 0 ] ; then
printf "Debug: $*\n"
fi
}
cleanup() {
set +e
[ -n "${socat_pid:-}" ] && kill -9 "$socat_pid" &>/dev/null
[ -n "${http_pid_scripts:-}" ] && kill -9 "$http_pid_scripts" &>/dev/null
[ -n "${http_pid_debs:-}" ] && kill -9 "$http_pid_debs" &>/dev/null
[ -n "${kvm_pid:-}" ] && kill -9 "$kvm_pid" &>/dev/null
[ -n "${vde_pid:-}" ] && kill -9 "$vde_pid" &>/dev/null
[ -n "${terminate_pid:-}" ] && kill -9 "$terminate_pid" &>/dev/null
[ -f "${tmpfile:-}" ] && rm -f "$tmpfile"
[ -e "${monitorfile:-}" ] && rm -f "$monitorfile"
if [ "${TYPE:-}" == "server" ] ; then
rm -rf "${PROFILE_DIRECTORY}/vde_switch"
fi
}
bailout() {
cleanup
[ -n "${1:-}" ] && exit "$1" || exit 1
}
usage() {
printf "${PN} ${VERSION} - test suite for autotesting software using Grml + KVM
Usage: --profile <profile_directory> --type [server|client] \\
--disk <disk.img> [ --iso <grml.iso>] [--bootdir <directory>] \\
[--name <name>] [--kvm <kvm_arguments>] [--no-console] \\
[--kernelappend <arg>] [--kernelcmdline <arg>]
Mandatory options:
--disk <disk.img> use disk.img as virtual hard disk
--profile <directory> use configuration from specified directory
--type [server|client] run in server or client mode
Mandatory options for server mode:
--iso <grml.iso> use specified grml.iso for booting
--bootdir <directory> use kernel (linux26) + initrd (initrd.gz) from
specified directory
Further options:
--help usage instructions (this dialog)
--kernelappend <arg> append <arg> to default kernelcmdline (server only)
--kernelcmdline <arg> use <arg> as default kernelcmdline (server only)
--kvm <option> provide <option> to kvm command line
--name <vmname> read vmname.cfg from profile directory
--no-console do not use interactive console
--verbose be more verbose about execution
--version display program version and exit
Usage example for server:
${PN} --profile /usr/share/kantan/example_profile --type server \\
--disk /srv/images/fai.img --iso /srv/grml_2011.10.27.iso \\
--bootdir /mnt/kantan
Usage example for client:
${PN} --profile /usr/share/kantan/example_profile --type client \\
--disk /srv/images/fai-client.img --kvm '-boot n'
Copyright (c) 2011, Michael Prokop <mika@grml.org>
"
}
# }}}
trap bailout 1 2 3 3 6 9 14 15
# server handling {{{
server_execution() {
VDE_SWITCH="${PROFILE_DIRECTORY}/vde_switch"
mkdir -m 700 -p "$VDE_SWITCH"
monitorfile="${PROFILE_DIRECTORY}/kvm_server.monitor"
# checks
if [ -z "${BOOT_DIR:-}" ] ; then
error "bootdir option is unset, --bootdir is mandatory."
bailout
fi
if ! [ -d "${BOOT_DIR}" ] ; then
error "boot directory $BOOT_DIR does not exist"
bailout
fi
if ! [ -d "${PROFILE_DIRECTORY}/share/" ] ; then
error "${PROFILE_DIRECTORY}/share/ not found."
bailout
fi
if ! [ -r "${ISO_FILE:-}" ] ; then
error "iso file $ISO_FILE could not be read."
bailout
fi
if [ -z "${VM_NAME:-}" ] ; then
if ! [ -r "${PROFILE_DIRECTORY}/server.cfg" ] ; then
warn "Configuration file ${PROFILE_DIRECTORY}/server.cfg could be not read, using defaults."
else
info "Reading configuration file ${PROFILE_DIRECTORY}/server.cfg"
. "${PROFILE_DIRECTORY}/server.cfg"
fi
else
if [ -r "${PROFILE_DIRECTORY}/${VM_NAME}.cfg" ] ; then
info "Reading configuration file ${PROFILE_DIRECTORY}/${VM_NAME}.cfg"
. "${PROFILE_DIRECTORY}/${VM_NAME}.cfg"
else
error "Configuration file ${PROFILE_DIRECTORY}/${VM_NAME}.cfg could be not read."
bailout
fi
fi
current_working_dir=$(pwd)
vde_switch -s ${VDE_SWITCH} &
vde_pid="$!"
KERNEL=$(find ${BOOT_DIR} -name linux26 -print0)
INITRD=$(find ${BOOT_DIR} -name initrd.gz -print0)
if [ -z "${KERNEL:-}" ] ; then
error "no kernel file linux26 found inside $BOOT_DIR" >&2
bailout
fi
if [ -z "${INITRD:-}" ] ; then
error "no initrd file initrd.gz found inside $BOOT_DIR" >&2
bailout
fi
info "Starting up kvm server instance"
info "Connect via serial console: 'sudo screen -m /dev/pts/\$ID 115200 || cu -l /dev/pts/\$ID'"
info "Connect to monitor console: 'socat - UNIX-CONNECT:${monitorfile}'"
info "Enable VNC via: 'echo change vnc :0 | socat - UNIX-CONNECT:${monitorfile}'"
info "Stop execution via entering 'quit'"
tmpfile=$(mktemp)
socat -u TCP-LISTEN:8888,reuseaddr,fork - > "${tmpfile}" &
socat_pid="$!"
info "Serving ${PROFILE_DIRECTORY}/share/ to port 8000 for netscript bootoption."
cd "${PROFILE_DIRECTORY}/share/"
python -m SimpleHTTPServer 8000 &>/dev/null &
http_pid_scripts="$!"
cd "$current_working_dir"
if ! [ -d "${PROFILE_DIRECTORY}/debs" ] ; then
warn "No directory ${PROFILE_DIRECTORY}/debs/ found, will not serve local Debian packages."
else
cd "${PROFILE_DIRECTORY}/debs"
if ! which dpkg-scanpackages &>/dev/null ; then
error "dpkg-scanpackages not available, please install package dpkg-dev."
bailout
fi
dpkg-scanpackages . | gzip > Packages.gz
python -m SimpleHTTPServer 8080 &>/dev/null &
http_pid_debs="$!"
fi
cd "$current_working_dir"
# default to vde:
VLAN1="-net vde,sock=${VDE_SWITCH},vlan=1"
# but if VLAN1_DEVICE is configured then use tap devices instead:
if [ -z "${VLAN1_DEVICE:-}" ] ; then
info "Using vde_switch setup (configure VLAN1_DEVICE to use tap instead)."
else
info "Using tap setup as VLAN1_DEVICE is configured."
VLAN1="-net tap,vlan=1,ifname=$VLAN1_DEVICE"
fi
if [ -z "${KERNEL_CMDLINE:-}" ] ; then
KERNEL_CMDLINE="boot=live ignore_bootid noquickconfig netscript=http://10.0.2.2:8000/netscript.sh console=tty1 console=ttyS0"
fi
KVM_CMDLINE="kvm -k en-us -m ${MEMORY:-1024} -serial pty \
-net nic,macaddr=$(python /usr/share/kantan/scripts/random_mac.py),vlan=0,model=virtio \
-net user,vlan=0 \
-net nic,macaddr=$(python /usr/share/kantan/scripts/random_mac.py),vlan=1,model=virtio \
-monitor unix:${monitorfile},server,nowait \
$VLAN1 \
-hda ${DISK} -cdrom ${ISO_FILE} -kernel ${KERNEL} -initrd ${INITRD} -vnc none"
debug "Invoking KVM using the following cmdline:\n
$KVM_CMDLINE -append ${KERNEL_CMDLINE} ${KERNEL_APPEND:-} ${KVM_ARGS:-}\n"
info "Starting KVM instance"
$KVM_CMDLINE -append "${KERNEL_CMDLINE} ${KERNEL_APPEND:-}" ${KVM_ARGS:-} &
kvm_pid="$!"
terminate_check $kvm_pid &
terminate_pid="$!"
if $NO_CONSOLE ; then
exec $$ >/dev/null 2>&1
else
console
fi
}
# }}}
# client handling {{{
client_execution() {
VDE_SWITCH="${PROFILE_DIRECTORY}/vde_switch"
monitorfile="${PROFILE_DIRECTORY}/kvm_client_${VM_NAME}.monitor"
if [ -z "${VM_NAME:-}" ] ; then
if ! [ -r "${PROFILE_DIRECTORY}/client.cfg" ] ; then
warn "Configuration file ${PROFILE_DIRECTORY}/client.cfg could be not read, using defaults."
else
info "Reading configuration file ${PROFILE_DIRECTORY}/client.cfg"
. "${PROFILE_DIRECTORY}/client.cfg"
fi
else
if [ -r "${PROFILE_DIRECTORY}/${VM_NAME}.cfg" ] ; then
info "Reading configuration file ${PROFILE_DIRECTORY}/${VM_NAME}.cfg"
. "${PROFILE_DIRECTORY}/${VM_NAME}.cfg"
else
error "Configuration file ${PROFILE_DIRECTORY}/${VM_NAME}.cfg could be not read."
bailout
fi
fi
if ! [ -r ${VDE_SWITCH} ] ; then
error "could not retrive vde_switch socket ${VDE_SWITCH}"
error "Server process not running yet?"
bailout
fi
info "Enable VNC via: 'echo change vnc :1 | socat - UNIX-CONNECT:${monitorfile}'"
if [ -n "${ISO_FILE:-}" -a -r "${ISO_FILE:-}" ] ; then
info "Using $ISO_FILE as cdrom file."
KVM_CDROM="-cdrom ${ISO_FILE}"
fi
KVM_CMDLINE="kvm -k en-us -m ${MEMORY:-512} -serial pty \
-net nic,macaddr=$(python /usr/share/kantan/scripts/random_mac.py),vlan=1,model=virtio \
-net vde,sock=${VDE_SWITCH},vlan=1 \
-monitor unix:${monitorfile},server,nowait \
${KVM_CDROM:-} -hda ${DISK} ${KVM_ARGS:-} -vnc none"
debug "Invoking KVM using the following cmdline:\n\n $KVM_CMDLINE\n"
info "Starting KVM instance"
$KVM_CMDLINE &
kvm_pid="$!"
terminate_check $kvm_pid &
terminate_pid="$!"
if $NO_CONSOLE ; then
exec $$ >/dev/null 2>&1
else
console
fi
}
# }}}
# termination secret {{{
terminate_check() {
rm -f ${PROFILE_DIRECTORY}/termination_status.txt
if [ -n "${KANTAN_TERMINATE_SECRECT:-}" ] ; then
info "Termination secret $KANTAN_TERMINATE_SECRECT enabled, awaiting string."
else
return 0
fi
if ! [ -r "${tmpfile:-}" ] ; then
error "Could not read ${tmpfile:-}"
return 1
fi
terminate=false
while ! $terminate ; do
if grep -q "${KANTAN_TERMINATE_SECRECT}" ${tmpfile:-} ; then
info "Termination string ${KANTAN_TERMINATE_SECRECT} found, terminating KVM..."
terminate=true
echo "terminated using ${KANTAN_TERMINATE_SECRECT} on $(date)" > $PROFILE_DIRECTORY/termination_status.txt
cleanup
kill ${1:-} $$ 2>/dev/null
fi
sleep 5
done
}
# }}}
# simple console prompt {{{
console() {
sleep 2
printf "\nEnter control command: [help|data|quit|pause|cont]\nprompt\$ "
while read s ; do
printf 'prompt$ '
case "$s" in
help)
printf "
Usage instructions:
quit Cancel execution of KVM and immediately exit .
pause Interrupt KVM process for later resuming using 'cont'.
cont Continue execution of paused KVM process.
data Display data that has been received from KVM server instance.\n"
printf 'prompt$ '
;;
data)
if [ -r "${tmpfile:-}" ] ; then
printf "Data from KVM instance:\n"
cat "$tmpfile"
printf 'prompt$ '
else
printf "No data from KVM instance received yet.\n"
printf 'prompt$ '
fi
;;
pause)
printf "Pausing KVM process (use 'cont' to continue execution)\n"
kill -s STOP $kvm_pid
printf 'prompt$ '
;;
cont)
printf "Continuing KVM process (use 'pause' to pause execution again)\n"
kill -s CONT $kvm_pid
printf 'prompt$ '
;;
exit|quit)
bailout 0
;;
esac
done
}
# }}}
# cmdline parsing {{{
_options=$(getopt --name kantan -o +b:d:i:k:n:p:t:hv --long \
bootdir:,disk:,iso:,kernelappend:,kernelcmdline:,kvm:,name:,profile:,type:,help,no-console,verbose,version -- "$@")
[ "$?" -ne 0 ] && { usage ; bailout ; }
[ -z "${1:-}" ] && { usage ; bailout ; }
eval set -- "$_options"
while :; do
case "${1:-}" in
--bootdir|-b)
shift ; BOOT_DIR="${1}"
;;
--disk|-d)
shift ; DISK="${1}"
;;
--iso|-i)
shift ; ISO_FILE="${1}"
;;
--kernelappend)
shift ; KERNEL_APPEND="${1}"
;;
--kernelcmdline)
shift ; KERNEL_CMDLINE="${1}"
;;
--kvm|-k)
shift ; KVM_ARGS="${1}"
;;
--name|-n)
shift ; VM_NAME="${1}"
;;
--no-console)
NO_CONSOLE=true
;;
--profile|-p)
shift ; PROFILE_DIRECTORY="${1}"
;;
--type|-t)
shift ; TYPE="${1}"
;;
--verbose)
if [ "${DEBUG:-}" ]; then
DEBUG=$(expr $DEBUG + 1)
else
DEBUG=1;
fi
;;
--help|-h)
usage ; exit 0
;;
--version|-v)
echo "${PN}, version ${VERSION}" ; bailout 0
;;
--)
shift; break
;;
*)
error "internal getopt error."
bailout
;;
esac
shift
done
# }}}
# execution checks {{{
if [ -z "${PROFILE_DIRECTORY:-}" ] ; then
error "profile option is unset, --profile is mandatory." ; bailout
fi
if [ -z "${TYPE:-}" ] ; then
error "executionttype option is unset, --type is mandatory." ; bailout
fi
if [ -z "${DISK:-}" ] ; then
error "harddisk option is unset, --disk is mandatory." ; bailout
fi
if ! [ -r "${DISK:-}" ] ; then
error "harddisk $DISK could not be read."
echo "NOTE: to create it execute e.g.:
qemu-img create $DISK 3G"
bailout
fi
if ! [ -d "${PROFILE_DIRECTORY:-}" ] ; then
error "profile directory $PROFILE_DIRECTORY does not exist." ; bailout
fi
# }}}
# main execution {{{
case "$TYPE" in
server) server_execution ;;
client) client_execution;;
*) error "unsupported --type argument, exiting." ; bailout ;;
esac
bailout 0
# }}}
## END OF FILE #################################################################
# vim: ai tw=100 expandtab foldmethod=marker shiftwidth=2