#!/bin/bash # # mit-kvm - mgmt script for kvm # # # 08-06-27 tmu Version 0.1 # 08-06-30 tmu new command "config" # version="0.2 (08-06-30)" debug=0 cfg=/etc/mit-kvm.cfg rootdir="/srv/virt" rundir="/var/run/mit-kvm" vmsdir="${rootdir}/vm" cmd_kvm="/usr/bin/kvm" cmd_hostname="/bin/hostname" cmd_find="/usr/bin/find" cmd_list_sort="/usr/bin/sort -k1 -t, -g" cmd_socat="/usr/bin/socat" cmd_ssh="/usr/bin/ssh" cmd_mit_kvm="$0" cmd_editor="/usr/bin/editor" # KVM defaults mem=1024 keyboard="de-ch" cpus="2" nic_model="e1000" boot=c halt_timeout=60 migration_startport="20000" if [ -f "${cfg}" ] then source ${cfg} fi function error { echo 'mit-kvm[id='${id}']: error: '${1} exit 1 } function warn { echo 'mit-kvm[id='${id}']: warn: '${1} } function debug { if [ "$debug" = "1" ] then echo "mit-kvm[id=$id]: debug: $1" fi return } function rundir_cleanup { id=$1 rm "${rundir}/${id}.pid" rm "${rundir}/${id}.args" rm "${rundir}/${id}.host" } function get_monitor_path { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg socket="${rundir}/${id}.socket" echo ${socket} } function get_cfg_by_id_or_name { #debug "function get_cfg_by_id_or_name" for i in $(${cmd_find} ${vmsdir} -name config -type f) do source $i if [ "$id" = "$1" ] then echo $i return elif [ "$name" = "$1" ] then echo $i return fi done error "not found any vmcfg for $1" } function calc_migration_port { id="$1" echo "${migration_startport}+${id}" | bc } function do_start { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg incoming_options="$2" if [ -z "$id" ]; then error "id not defined in $vmcfg"; fi if [ -z "$nic_macaddr" ]; then error "nic_macaddr not defined in $vmcfg."; fi vmdir="${vmsdir}/${id}" cmdline="" # Harddisks / Cdrom for i in $(seq 0 4); do if [ -n "${drive[$i]}" ] then cmdline="${cmdline} -drive ${drive[$i]}" fi done # Harddisks / Cdrom for i in $(seq 0 4); do if [ -n "${usbdevice[$i]}" ] then cmdline="${cmdline} -usbdevice ${usbdevice[$i]}" fi done # Netzwerk cmdline="${cmdline} -net tap,ifname=vnet${id} -net nic,model=$nic_model,macaddr=$nic_macaddr" # Monitor cmdline="${cmdline} -monitor unix:${rundir}/${id}.socket,server,nowait " # Diverses cmdline="${cmdline} -usb -boot ${boot} -k $keyboard -vnc :$id -m $mem -smp $cpus -daemonize -pidfile ${rundir}/${id}.pid -name $name" cd ${vmdir} || error "can't change to dir $vmdir" if ! do_status ${id} then ${cmd_kvm} ${cmdline} ${incoming_options} || error "failure starting vm" ${cmd_hostname} > ${rundir}/${id}.host echo $cmdline > ${rundir}/${id}.args else warn "already running" fi } function do_list { for i in $(${cmd_find} ${vmsdir} -name config -type f) do id= name= nic_macaddr= source $i status="nothere" if do_status ${id}; then status="running"; fi echo "$id,$name,$nic_macaddr,$status" done } function do_vnc { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg || error "Can't read file $vmcfg" echo $(cat ${rundir}/${id}.host):$id } function do_monitor { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg || error "Can't read file $vmcfg" socket=$(get_monitor_path ${id}) if [ -e "${rundir}/${id}.socket" ] then ${cmd_socat} stdio $socket else warn "No monitor socket available" return 1 fi } function do_halt { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg || error "Can't read file $vmcfg" if do_status ${id} then echo "system_powerdown" | do_monitor ${id} >/dev/null for i in $(seq 1 ${halt_timeout}) do debug "still running..." if ! do_status ${id} then echo halted rundir_cleanup ${id} exit 0 fi sleep 1 done warn "VM with id ${id} halt timeout reached. Kill it" do_kill ${id} else warn "VM not running anymore" exit 1 fi } function do_status { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg || error "Can't read file $vmcfg" if [ -e "${rundir}/${id}.pid" ] then pid=$(cat ${rundir}/${id}.pid) args=$(cat ${rundir}/${id}.args) else return 1 fi if ps -p ${pid} -f --no-heading >/dev/null then return 0 else return 1 fi } function do_migrate { vmcfg=$(get_cfg_by_id_or_name $1) target=$2 source $vmcfg || error "Can't read file $vmcfg" if do_status ${id} then ${cmd_ssh} ${target} ${cmd_mit_kvm} incoming ${id} || error "error starting incoming on ${target}" & sleep 2 && echo "migrate tcp://${target}:$(calc_migration_port ${id})" | do_monitor ${id} sleep 2 && echo "info migration" | do_monitor ${id} else error "not running here" fi } function do_incoming { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg || error "Can't read file $vmcfg" do_start ${id} "-incoming tcp://0:$(calc_migration_port ${id})" } function do_kill { vmcfg=$(get_cfg_by_id_or_name $1) source $vmcfg || error "Can't read file $vmcfg" pid=$(cat ${rundir}/${id}.pid) args=$(cat ${rundir}/${id}.args) if [ -n "${pid}" ] then if ps -f -p ${pid} | grep -- "${args}" >/dev/null then kill ${pid} sleep 5 if ! ps -p ${pid} >/dev/null then rundir_cleanup ${id} else error "PID ${pid} not gone away" fi else error "PID ${pid} is not started with the same arguments. Args should be: ${args}" fi else error "No PID with number ${pid}" fi } function do_config { vmcfg=$(get_cfg_by_id_or_name $1) ${cmd_editor} ${vmcfg} } function do_help() { cat < Where list - list available images config - edit configfile start - start a machine halt - try to gracefully halt a machine vnc - show vnc port monitor - connect to qemu monitor kill - kill machine immediatly status - running or not Version: $version EOF } case "$1" in start) debug "Start ..." do_start $2;; list) debug "List ..." do_list | ${cmd_list_sort} ;; vnc) debug "Vnc ..." do_vnc $2 ;; monitor) debug "Monitor ..." do_monitor $2 ;; halt) debug "Halt ..." do_halt $2 ;; kill) debug "Kill ..." do_kill $2 ;; status) debug "Status ..." if do_status $2 then echo running exit 0 else echo stopped exit 1 fi ;; config) debug "Config ..." do_config $2 ;; incoming) debug "Incoming..." do_incoming $2 ;; migrate) debug "Migrate..." do_migrate $2 $3 ;; *) do_help esac