kvmctl
changeset 5 b4e073b1250e
parent 4 1ab9431a52bc
child 6 ed2b863f938f
equal deleted inserted replaced
4:1ab9431a52bc 5:b4e073b1250e
     1 #! /bin/bash
       
     2 # $Id$
       
     3 # $URL$
       
     4 # status: in development
       
     5 # 2008 Heiko Schlittermann
       
     6 
       
     7 # Should be written in Perl!
       
     8 
       
     9 # Structure of virtual machine directories
       
    10 # $BASE     (see below, hard coded into this script)
       
    11 # |
       
    12 # +--<vm1>
       
    13 # |   +--conf
       
    14 # |   |     +--options
       
    15 # |   |     +--id
       
    16 # |   +--var
       
    17 # |   |     +--pid
       
    18 # |   |     +--running_options
       
    19 # |   +--<image1>
       
    20 # |   +-- ....
       
    21 # |   +--<imageN>
       
    22 # |
       
    23 # +--<vm2>             
       
    24 # | ....
       
    25 # +--<vmN>
       
    26 
       
    27 
       
    28 action="${1?}"; shift           # mandatory
       
    29 vm="$1"       ; shift           # optional
       
    30 
       
    31 # no user service able parts below
       
    32 
       
    33 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
       
    34 unset ${!LC_*} LANG CDPATH
       
    35 
       
    36 ME=$(basename $0)
       
    37 BASE=/kvm
       
    38 KVM=$(command -v kvm)
       
    39 NC=$(command -v nc)
       
    40 FUSER=$(command -v fuser)
       
    41 VNCVIEWER=$(command -v vncviewer)
       
    42 TIMEOUT=30
       
    43 
       
    44 
       
    45 for f in "/etc/$ME.conf" ~/.$ME; do test -r "$f" && source "$f"; done
       
    46 
       
    47 
       
    48 die()       { echo $ME: "$@" >&2; exit 1; }
       
    49 warn()      { echo $ME: "$@" >&2; }
       
    50 silently()  { "$@" &>/dev/null; }
       
    51 
       
    52 debug()     { echo "DEBUG: $@" >&2; }
       
    53 
       
    54 unique_ids() {
       
    55     local dups=$(cat $BASE/*/conf/id 2>/dev/null | sort -n | uniq -c \
       
    56         | egrep -v '^[[:space:]]+1 ' | tr -s ' ' | cut -f2 -d:)
       
    57     test "$dups" || return 0
       
    58     ERR="$dups"  && return 1
       
    59 }
       
    60 
       
    61 convert_dirs() {
       
    62     for dir in $BASE/*; do
       
    63         test -d $dir/kvm || test -d $dir/conf || continue
       
    64         local old=$dir/kvm
       
    65         local new=$dir/conf
       
    66         test -d $old && mv $old $new
       
    67         test -f $new/cmdline && mv $new/cmdline $new/options
       
    68     done
       
    69 }
       
    70 
       
    71 monitor() {
       
    72     local port=$1;      shift
       
    73     local command="$1"; shift
       
    74 
       
    75     # check, if the monitor is available, kill if somebody
       
    76     # else is using it
       
    77     local client=$(netstat -tn | egrep ":$port[[:space:]]+EST" | tr -s ' ' | cut -f4 -d' ')
       
    78     test "${client%:*}" = 127.0.0.1 && fuser -k ${client#*:}/tcp
       
    79 
       
    80     local pid0 pid1
       
    81     (
       
    82         local a=$tmp/a.fifo; mkfifo $a
       
    83         trap "rm -v $a" EXIT
       
    84         exec 10<>$a
       
    85         $NC localhost $port <&10 | {
       
    86             read || exit       # banner
       
    87             while read -d' '; do test "$REPLY" = "(qemu)" && break; done
       
    88             echo "$command" >&10
       
    89             while read -d' '; do test "$REPLY" = "(qemu)" && break; done
       
    90         }
       
    91     ) &  pid0=$!
       
    92     ( sleep $TIMEOUT; kill $pid0 ) & pid1=$!
       
    93     wait $pid0
       
    94     silently kill $pid0 $pid1
       
    95 }
       
    96 
       
    97 ### MAIN
       
    98 
       
    99 # sanity
       
   100 test -x "$NC"       || die "need nc (netcat)"
       
   101 test -x "$KVM"      || die "need kvm/qemu/kqemu"
       
   102 test -x "$FUSER"    || die "need fuser"
       
   103 test -d "$BASE"     || die "base dir $BASE does not exist"
       
   104 test -d "$BASE/$vm" || die "vm $vm does not exist"
       
   105 
       
   106 unique_ids          || die "duplicate ids (id: $ERR)"
       
   107 convert_dirs
       
   108 
       
   109 trap 'test "$TMP" && rm -fr $TMP' EXIT
       
   110 
       
   111 for vm in ${vm:-$(echo $BASE/*)}; do
       
   112     vm=$(basename $vm)
       
   113     conf=$BASE/$vm/conf
       
   114     var=$BASE/$vm/var
       
   115 
       
   116     tmp=$(mktemp -d)
       
   117     TMP="$TMP $tmp"
       
   118 
       
   119     test -d $conf           || continue
       
   120     test -f $conf/id        || { warn "need id file (vm: $vm)" && continue; } 
       
   121     test -f $conf/options   || { warn "need options file (vm: $vm)" && continue; }
       
   122     test -d $var            || mkdir $var || exit 2
       
   123 
       
   124     id=$(cat $conf/id)
       
   125     port=$((4000 + id))
       
   126     pid=$(cat $var/pid 2>/dev/null)
       
   127 
       
   128     case "$action" in
       
   129 
       
   130     start)
       
   131         echo "starting $vm"
       
   132         options="$(grep -v '^#' $conf/options) \
       
   133         -pidfile var/pid \
       
   134         -name "$vm" \
       
   135         -vnc :$id \
       
   136         -monitor tcp:localhost:$port,server,nowait \
       
   137         $@"
       
   138         options=$(echo "" $options)        # shorten it
       
   139         echo "$options" > $var/running_options
       
   140         test -f $var/frozen && options="$options $(cat $var/frozen)"
       
   141 
       
   142         if silently fuser $var/pid; then
       
   143             echo "running, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" 
       
   144         else
       
   145             ( cd $BASE/$vm && $KVM $options -daemonize )
       
   146             pid=$(cat $var/pid 2>/dev/null)
       
   147             test "$pid" || { warn "serious problem ($vm), no pid" && continue; }
       
   148             echo "started, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" 
       
   149         fi
       
   150 
       
   151         test "$DISPLAY" && test "$VNCVIEWER" && test -t \
       
   152         && ( silently exec $VNCVIEWER :$id </dev/null & )
       
   153 
       
   154         ;;
       
   155     stop)   
       
   156         {
       
   157         echo "stopping (powerdown) $vm"
       
   158         monitor $port "system_powerdown"
       
   159         silently fuser $var/pid && $0 quit $vm
       
   160         } &
       
   161         ;;
       
   162     quit) 
       
   163         {
       
   164         echo "quit $vm"
       
   165         monitor $port "quit"
       
   166         silently fuser $var/pid && $0 kill $vm
       
   167         } &
       
   168         ;;
       
   169     kill)   
       
   170         echo "killing $vm"
       
   171         kill $pid 
       
   172         ;;
       
   173     freeze) 
       
   174         {
       
   175         echo "freezing $vm"
       
   176         monitor $port "savevm HIBERNATED"
       
   177         monitor $port "quit" 
       
   178         echo "-loadvm HIBERNATED" >$var/frozen
       
   179         } &
       
   180         ;;
       
   181     qemu)
       
   182         echo "DO *NOT* USE \"quit\", use ^C for exiting!"
       
   183         $NC localhost $port
       
   184         ;;
       
   185 
       
   186     list)
       
   187         echo "$vm:" $(silently fuser $var/pid \
       
   188             && echo "running (pid: $pid)" \
       
   189             || echo "not running")
       
   190         ;;
       
   191 
       
   192     *)  die "unknown command \"$action\""
       
   193         ;;
       
   194 
       
   195     esac
       
   196 
       
   197 done
       
   198 
       
   199 wait
       
   200 
       
   201 # vim:ts=4 sw=4 et: