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