Tuesday, February 15, 2011

VirtualBox init.d script for CentOS

Here is an init.d script for VirtualBox.

This has been designed for and only tested on CentOS 5.5 and VirtualBox 4.0.2. It'll probably work with RHEL and maybe even Debian/Ubuntu but that is untested.

Define which user will run the VMs
echo "VBOX_USER=vbox" > /etc/sysconfig/vboxcontrol

Enable some virtual machines.
mkdir -p /etc/vbox/machines_enabled.d
touch /etc/vbox/machines_enabled.d/vboxtest
touch /etc/vbox/machines_enabled.d/ftp.example.com

Create the vboxcontrol script.
touch /etc/rc.d/init.d/vboxcontrol
chmod 750 /etc/rc.d/init.d/vboxcontrol

Paste the following script into /etc/rc.d/init.d/vboxcontrol
#!/bin/bash

# $Id: vboxcontrol-init.sh 6347 2011-02-13 23:50:24Z$

# chkconfig: 235 98 02
# description: VirtualBox VM service
#
### BEGIN INIT INFO
# Provides:       vboxcontrol
# Required-Start: vboxdrv
# Required-Stop:  vboxdrv
# Default-Start:  2 3 5
# Default-Stop:   0 6
# Description:    VirtualBox VM service
### END INIT INFO

PATH=$PATH:/bin:/sbin:/usr/sbin

[ -f /etc/sysconfig/vboxcontrol ] && . /etc/sysconfig/vboxcontrol
lockfile=${LOCKFILE:-/var/lock/subsys/vboxcontrol}
pidfile=${PIDFILE:-/var/run/vboxcontrol.pid}
RETVAL=0

# --- VBOX_USER must be defined
[ -z "$VBOX_USER" ] && { echo "ERROR: VBOX_USER must be set in /etc/sysconfig/vboxcontrol"; exit 1; }

SU="su $VBOX_USER -c"
VBOXMANAGE="VBoxManage --nologo"
VBOX_LIST_D=${VBOX_LIST_D:-"/etc/vbox/machines_enabled.d"}
VBOX_BASE=${VBOX_BASE:-"/VirtualMachines"}

# --- maximum loops to wait for all VMs to shutdown, 180 * 5 sec = 15mins
MAX_WAIT=180

# Source function library.
. /etc/rc.d/init.d/functions

vboxdrvrunning() {
    lsmod | grep -q "vboxdrv[^_-]"
}

wait_for_closing_machines() {
  count=0
  while [ ${count} -lt ${MAX_WAIT} ]; do
    RUNNING_MACHINES=`$SU "$VBOXMANAGE list runningvms" | wc -l`
    [ $RUNNING_MACHINES = 0 ] && break
    sleep 5
    count=$((count + 1))
  done
}

start() {
  [ -d "$VBOX_LIST_D" ] || exit 0
  echo -n $"Starting VirtualBox VMs: "
  vboxdrvrunning || {
   failure "VirtualBox kernel module not loaded!"
   exit 0
  }
  success
  [ $RETVAL = 0 ] && touch ${lockfile}
  echo

  for VMFILE in ${VBOX_LIST_D}/* ;do
    [ -e ${VMFILE} ] && VM=`basename ${VMFILE}` || continue
    action $"  Starting VM: ${VM} ..." $SU "$VBOXMANAGE startvm "$VM" --type headless >/dev/null 2>/dev/null"
    RETVAL=$?
  done

  return $RETVAL
}

stop() {
  # NOTE: this stops all running VM's. Not just the ones listed in the $VBOX_LIST_D
  echo -n $"Stopping VirtualBox VMs: "
  vboxdrvrunning || {
   failure "VirtualBox kernel module not loaded!"
   exit 0
  }
  success
  [ $RETVAL = 0 ] && touch ${lockfile}
  echo

  $SU "$VBOXMANAGE list runningvms" | while read VM VMUUID; do
    action $"  Stopping VM: ${VM} ..." $SU "$VBOXMANAGE controlvm "$VM" acpipowerbutton >/dev/null 2>/dev/null"
    RETVAL=$?
  done

  action $"  Waiting for VMs to stop ..." wait_for_closing_machines

  return $RETVAL
}

restart() {
    stop && start
}

status() {
    echo $"Checking for running VirtualBox VMs: "
    enabled_vms=( ${VBOX_LIST_D}/* )
    running_vms=( `$SU "$VBOXMANAGE list runningvms" | awk '{print $1}'` )
    for VM in ${running_vms[@]}; do
      VM=`echo $VM | sed 's/\"//g'`
      ip=`$SU "$VBOXMANAGE guestproperty get "${VM}" /VirtualBox/GuestInfo/Net/0/V4/IP" | sed "s/^Value: //"`
      echo $"  ${VM} is running [ip: ${ip}]"
      for (( i = 0 ; i < ${#enabled_vms[@]} ; i++ )); do
        [ -e ${enabled_vms[$i]} ] || continue
        vmname=`basename ${enabled_vms[$i]}`
        if [ $vmname == $VM ]; then
          unset enabled_vms[$i]
        fi
        unset vmname
      done
    done
    for (( i = 0 ; i < ${#enabled_vms[@]} ; i++ )); do
      [ -e ${enabled_vms[$i]} ] || continue
      vmname=`basename ${enabled_vms[$i]}`
      echo $"  ${vmname} is NOT running"
      RETVAL=3
    done
}

startvm(){
  vmname=$1
  if [ -z "$vmname" ]; then
    echo "ERROR: You need to provide a vm name."
    exit 1
  fi
  
  action $"Sending start command: ${vmname} ..." $SU "$VBOXMANAGE startvm "$vmname" --type headless >/dev/null 2>/dev/null"
  RETVAL=$?
  
  return $RETVAL
}

deletevm(){
  vmname=$1
  if [ -z "$vmname" ]; then
    echo "ERROR: You need to provide a vm name."
    exit 1
  fi

  if [ ! -e "${VBOX_BASE}/${vmname}/${vmname}.vbox" ]; then
    echo "ERROR: The VM configuration file could not be found."
    exit 1
  fi
  
  action $"Sending unregistervm command: ${vmname} ..." $SU "$VBOXMANAGE unregistervm "$vmname" --delete >/dev/null 2>/dev/null"
  RETVAL=$?
  
  if [ "${RETVAL}" == 0 ]; then
    [ -e "${VBOX_BASE}/${vmname}" ] && action $"Removing VM files from ${VBOX_BASE}/${vmname} ..." rm -rf "${VBOX_BASE}/${vmname}"
  fi
  
  return $RETVAL
}

controlvm(){
  command=$1
  vmname=$2
  if [ -z "$vmname" ]; then
    echo "ERROR: You need to provide a vm name."
    exit 1
  fi
  
  action $"Sending ${command} command: ${vmname} ..." $SU "$VBOXMANAGE controlvm "$vmname" ${command} >/dev/null 2>/dev/null"
  RETVAL=$?
  
  return $RETVAL
}

case "$1" in
start)
    start
    ;;
stop)
    stop
    ;;
restart)
    restart
    ;;
force-reload)
    restart
    ;;
status)
    status
    ;;
shutdownvm)
    shift
    controlvm acpipowerbutton $@
    ;;
startvm)
    shift
    startvm $@
    ;;
poweroffvm)
    shift
    controlvm poweroff $@
    ;;
resetvm)
    shift
    controlvm reset $@
    ;;
deletevm)
    shift
    deletevm $@
    ;;
*)
    echo "Usage: $0 {start|stop|restart|status|shutdownvm|startvm|poweroffvm|resetvm} [vmname]"
    exit 1
esac

exit $RETVAL

UPDATE: Up-to-date version of this script can always be found https://github.com/abnormaliti/puppet/blob/master/virtualbox/files/vboxcontrol-init.sh

Add the service and enable it.
/sbin/chkconfig --add vboxcontrol && /sbin/chkconfig vboxcontrol on

The service is now installed and enabled. It will start all enabled virtual machines at boot and attempt to shutdown all VMs during the host shutdown.

The script can also be used to control your VMs.

# --- start all enabled VMs
/etc/init.d/vboxcontrol start

# --- stop all VMs, not just enabled ones.
/etc/init.d/vboxcontrol stop

# --- get the status of all VMs
/etc/init.d/vboxcontrol status

# --- start a particular VM
/etc/init.d/vboxcontrol startvm VMname

# --- shutdown a particular VM
/etc/init.d/vboxcontrol shutdownvm VMname

# --- poweroff a particular VM
/etc/init.d/vboxcontrol poweroffvm VMname

# --- reset a particular VM
/etc/init.d/vboxcontrol resetvm VMname

1 comment:

  1. thanks a lot for putting this together. Worked like a charm. Only problem I had is that acpid was not installed on the guest system, so vboxcontrol stop would hang forever.

    ReplyDelete