воскресенье, 3 апреля 2011 г.

VirtualBox: авто запуск виртуалок

После установки VirtualBox 4.x встали 2 вопроса:
  • автозапуск - некоторые виртуалки нужно запускать одновременно с host сервером
  • корректное завершение работы, т.е. автоматическое и корректное выключение виртуалок вместе с host сервером
После усиленного гугления выяснилось, что:
а) родными средствами VirtualBox-а эти задачи не решаются
б) есть некоторое кол-во самописных скриптов, но...
в) все они некорректно работают с VB v4.x.
Пришлось брать, и "допиливать"...


За основу взял скрипты некого Jochem Kossen-а. Как оказалось, "несовместимостей всего 2:
  1. Изменился формат вывода списка виртуалок (результаты VBoxManage list runningvms)
  2. Немного изменился формат запуска
По ходу, добавил несколько функций для перезагрузки и "приостановки" виртуалок (результат приведен в конце топика). Инструкцию по установке и настройке можно найти в оригинальном архиве, либо почитать вот здесь. Вкратце:
  1. Скачать и распаковать архив (например в /opt/vboxcontrol)
  2. Поколдовать над содержимым /opt/vboxcontrol/vboxcontrol
  3. Еще раз внимательно прочитать README и создать файлы /etc/virtualbox/config и /etc/virtualbox/machines_enabled
  4. Сделать ссылку в /etc/init.d/ и обновить init rc скрипты. Например:
    sudo ln -s /opt/vboxcontrol/vboxcontrol /etc/init.d/vboxcontrol
    sudo update-rc.d vboxcontrol defaults 99 10
После чего, стали доступны команды (в Ubuntu Server):
sudo service vboxcontrol start|stop|status|savestate|poweroff
А также
sudo service vboxcontrol vm-start|vm-stop|vm-poweroff|vm-savestate|vm-reset VM_NAME

Также, надо не забыть установить пакет acpid на гостевые системы.

Модифицированный скрипт:
#! /bin/sh
### BEGIN INIT INFO
# Provides: virtualbox_vms
# Required-Start:    $local_fs $syslog $remote_fs
# Required-Stop:     $local_fs $syslog $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Control VirtualBox Virtual Machine instances
### END INIT INFO
#
# Version 2008051100 by Jochem Kossen 
# http://farfewertoes.com
#
# Released in the public domain
#
# This file came with a README file containing the instructions on how
# to use this script.
#

. /lib/lsb/init-functions

# Are we running from init?
run_by_init() {
    ([ "$previous" ] && [ "$runlevel" ]) || [ "$runlevel" = S ]
}

################################################################################
# INITIAL CONFIGURATION
VBOXDIR="/etc/virtualbox"
VM_USER="root"
USE_NAT="yes"

export PATH="${PATH:+$PATH:}/bin:/usr/bin:/usr/sbin:/sbin"

if [ -f $VBOXDIR/config ]; then
    . $VBOXDIR/config
else
    echo "ERROR: $VBOXDIR/config does not exist. Exiting."
    exit 1
fi

SU="su $VM_USER -c"
VBOXMANAGE="VBoxManage -nologo"

################################################################################
# FUNCTIONS

# Determine if USE_NAT is set to "yes"
use_nat() {
    if [ "$USE_NAT" = "yes" ]; then
        return `true`
    else
        return `false`
    fi
}

# Bring up the bridge interface
enable_bridge() {
    # If NAT is enabled, don't do anything
    use_nat && return

    # Load the tun module
    if [ ! -e /dev/net/tun ]; then
        modprobe tun
    fi

    brctl addbr br0 || /bin/true

    # Disable $HOST_IF; host will use br0 instead
    ifdown $HOST_IF
    ifconfig $HOST_IF 0.0.0.0 promisc
    brctl addif br0 $HOST_IF

    # Bring up br0
    ifup br0

    # Answer ARP requests for $HOST_IP (which now come on br0) with the MAC
    # address of $HOST_IF
    arp -Ds $HOST_IP $HOST_IF pub
}

# Bring down the bridge interface
disable_bridge() {
    # If NAT is enabled, don't do anything
    use_nat && return

    ifdown br0
    brctl delbr br0
    ifup $HOST_IF
}

# Activate tap interfaces
enable_taps() {
    # If NAT is enabled, don't do anything
    use_nat && return

    for TAP in $TAPS; do
        # Check if $TAP is configured already
        ifconfig $TAP > /dev/null 2>&1
        if [ $? != 0 ]; then
            tunctl -t $TAP -u $VM_USER
            brctl addif br0 $TAP

            # Disable tap interfaces for host; guest will activate them for themselves
            ifconfig $TAP 0.0.0.0 promisc

            # Enable proxy_arp so that ARP requests can be answered correctly
            # by the host
            echo 1 > /proc/sys/net/ipv4/conf/$TAP/proxy_arp

            # Add a route for the tap device
            route add -host $HOST_IP dev $TAP
        else
            log_failure_msg "Interface $TAP already configured"
        fi
    done
}

# Disable/deconfigure tap interfaces
disable_taps() {
    # If NAT is enabled, don't do anything
    use_nat && return

    for TAP in $TAPS; do
        route del -host $HOST_IP dev $TAP
        brctl delif br0 $TAP
        tunctl -d $TAP
    done
}

# Check for running machines every few seconds; return when all machines are
# down
wait_for_closing_machines() {
    RUNNING_MACHINES=`$SU "$VBOXMANAGE list runningvms" | wc -l`
    if [ $RUNNING_MACHINES != 0 ]; then
        sleep 5
        wait_for_closing_machines
    fi
}

################################################################################
# RUN
case "$1" in
    start)
        if [ -f /etc/virtualbox/machines_enabled ]; then
            if [ ! `use_nat` ]; then
                enable_bridge
                enable_taps

                chown root.vboxusers /dev/net/tun
                chmod 0660 /dev/net/tun
            fi

            cat /etc/virtualbox/machines_enabled | while read VM; do
                log_action_msg "Starting VM: $VM ..."
                $SU "$VBOXMANAGE startvm \"$VM\" --type headless"
            done
        fi
        ;;
    stop)
        # NOTE: this stops all running VM's. Not just the ones listed in the
        # config
        $SU "$VBOXMANAGE list runningvms |sed -e 's/ {.*}//'" | while read VM; do
            log_action_msg "Shutting down VM: $VM ..."
            $SU "$VBOXMANAGE controlvm \"$VM\" acpipowerbutton"
        done

        wait_for_closing_machines

        if [ ! `use_nat` ]; then
            disable_taps
            disable_bridge
        fi
        ;;
    savestate)
        # NOTE: this suspends all running VM's. Not just the ones listed in the
        # config
        $SU "$VBOXMANAGE list runningvms |sed -e 's/ {.*}//'" | while read VM; do
            log_action_msg "Suspending VM: $VM ..."
            $SU "$VBOXMANAGE controlvm \"$VM\" savestate"
        done

        wait_for_closing_machines

        if [ ! `use_nat` ]; then
            disable_taps
            disable_bridge
        fi
        ;;
    poweroff)
        # NOTE: this stop all running VM's. Not just the ones listed in the
        # config
        $SU "$VBOXMANAGE list runningvms |sed -e 's/ {.*}//'" | while read VM; do
            log_action_msg "Shutting down VM: $VM ..."
            $SU "$VBOXMANAGE controlvm \"$VM\" savestate"
        done

        wait_for_closing_machines

        if [ ! `use_nat` ]; then
            disable_taps
            disable_bridge
        fi
        ;;

    bridge-up)
        enable_bridge
        ;;
    bridge-down)
        disable_bridge
        ;;
    taps-up)
        enable_taps
        ;;
    taps-down)
        disable_taps
        ;;
    vm-start)
        log_action_msg "Starting VM: $2 ..."
        $SU "$VBOXMANAGE startvm \"$2\" --type headless"
        ;;
    vm-stop)
        log_action_msg "Stopping VM: $2 ..."
        $SU "$VBOXMANAGE controlvm \"$2\" acpipowerbutton"
        ;;
    vm-poweroff)
        log_action_msg "Powering off VM: $2 ..."
        $SU "$VBOXMANAGE controlvm \"$2\" poweroff"
        ;;
    vm-savestate)
        log_action_msg "Savestate VM: $2 ..."
        $SU "$VBOXMANAGE controlvm \"$2\" savestate"
        ;;
    vm-reset)
        log_action_msg "Reset VM: $2 ..."
        $SU "$VBOXMANAGE controlvm \"$2\" reset"
        ;;
    status)
        log_action_msg "The following virtual machines are currently running:"
        $SU "$VBOXMANAGE list runningvms |sed -e 's/ {.*}//'" | while read VM; do
            echo -n "$VM "
     echo
        done
        ;;
    *)
        log_failure_msg "Usage: $0 {start|stop|status|savestate|poweroff|vm-start |vm-stop |vm-poweroff |vm-savestate |vm-reset |bridge-up|bridge-down|taps-up|taps-down}"
        exit 3
esac

exit 0


Update (2012-07-25)
>> ссылки не работают, Таки да, автор удалил статью на своем сайте. Возможные причины:
a) этот скрипт перепостили такое количество раз, что автор больше не видит смысла держать его на своих ресурсах :)
б) Отпала надобность в этом скрипте - за последние 4-5 релизов ораклисты серьезно причесали свой продукт, так что весьма вероятно, что теперь автостарт и т.д. можно достичь стандартными средствами. Я не изучал этот вопрос, потому как вышеописанный скрипт уже пару лет успешно служит на серверах. А, как известно, не надо трогать то, что работает :)

>> инструкцию по установке и настройке выложите пожалуйста.
В исходном архиве лежат 4 файла:
  • 'vboxcontrol' - приведенный выше скрипт.
  • 'README' - см. ниже
  • 'LICENSE' - см. ниже
  • 'ChangeLog' - см. ниже
Содержимое 'README'
This script was developed for use on an Ubuntu host machine. These
instructions are for such a system as well.


INSTALLATION
    * Make sure you have sudo, bridge-utils, uml-utilities and
      obviously VirtualBox installed
    * Put the vboxcontrol script in the /etc/init.d/ directory
    * Make the script executable:
      chmod 0755 /etc/init.d/vboxcontrol
    * If you use 'Host interface' networking, put a br0 entry in
      /etc/network/interfaces: iface br0 inet dhcp
    * Create a new directory called /etc/virtualbox
    * Create a file called /etc/virtualbox/config with the following
      lines:

        HOST_IF="eth0"
        HOST_IP="192.168.1.5"
        VM_USER="jochem"
        USE_NAT="no"
        TAPS="tap0 tap1"

      Change all values appropriately for your system:
        HOST_IF is the interface which VirtualBox uses to bridge.

        HOST_IP is the standard ip address of the host.

        VM_USER is the system user under which all virtual machines
        will run.

        USE_NAT specifies if you want to use NAT networking (set it to
        "yes") or Host interface networking (set it to "no")

        TAPS are the tap interfaces you want to set up. Use one for
        each VM, seperate them by a space. Only applicable if
        USE_NAT is set to "no".

    * Create a file called /etc/virtualbox/machines_enabled with a
      virtual machine name on each line. Example:

        Debian Webserver
        OpenBSD Fileserver

    * Set up VirtualBox to use the specified tap interfaces for your
      machines (in the Network settings screen for your VM, set
      'Attached To' to 'Host Interface', and 'Interface Name' to
      'tap0' for the first VM, 'tap1' for the second VM, etc.

    * Let the script start up at boot:

        # update-rc.d vboxcontrol defaults 99 10

      This command starts the machine at the latest possible time,
      when everything else has already booted, and at reboot/shutdown
      it's executed in the beginning of the shutdown procedure)


USAGE
    Start all specified virtual machines:
    # /etc/init.d/vboxcontrol start

    Stop all virtual machines:
    # /etc/init.d/vboxcontrol stop

    View status of all running virtual machines:
    # /etc/init.d/vboxcontrol status

    Start a specific vm called :
    # /etc/init.d/vboxcontrol start-vm 

    Stop a specific vm called  (this sends an acpi power down
    event):
    # /etc/init.d/vboxcontrol stop-vm 

    Turn off a specific vm called  (this sends an immediate
    power down event):
    # /etc/init.d/vboxcontrol poweroff-vm 

    Bring the configured bridge up:
    # /etc/init.d/vboxcontrol bridge-up

    Bring the configured bridge down:
    # /etc/init.d/vboxcontrol bridge-down

    Bring up the configured tap devices:
    # /etc/init.d/vboxcontrol taps-up

    Bring down the configured tap devices:
    # /etc/init.d/vboxcontrol taps-down


ISSUES
    - The 'stop' command stops all virtual machines, not just the ones
      configured in /etc/virtualbox/machines_enabled

    - Perhaps the main functionality should not be in an init.d
      script; this would make portability easier


CONTACT
The original author is Jochem Kossen. You can reach him at
jochem.kossen@gmail.com

Содержимое 'LICENSE'
This contents of this package is released under the public
domain. This means you can do with it whatever you like, but it comes
without any warranty or whatsoever.

For more information about this, see
http://en.wikipedia.org/wiki/Wikipedia:Copyright_FAQ#Public_domain

Regards,

Jochem Kossen
jochem.kossen@gmail.com
Содержимое 'ChangeLog' (актуально для оригинальной версии)
20080511:
  - add USE_NAT config variable to make the script work with non-tap based networking
  - add stop-vm method which sends an ACPI powerbutton event
  - add poweroff-vm method which powers off the VM instantly
  - more documentation
  - add ChangeLog and README files
  - minor cleanups
  - rename VirtualBoxMachines to vboxcontrol
  - put in Git for revision controll

20080331:
  - cleanups
  - add bridge-up/bridge-down, taps-up/taps-down methods
  - rename setup_* to enable_* for consistency
  - show VM names in 'status' output
  - check if tap interfaces are configured already before bringing them up


20080309:
  - initial version

4 комментария:

  1. Отличный скрипт! Использую уже на втором сервере. Спасибо за адаптацию для Убунуту.

    ОтветитьУдалить
  2. ссылки не работают, инструкцию по установке и настройке выложите пожалуйста.

    ОтветитьУдалить
  3. Присоединяюсь - работает отлично. И я тоже не трогаю, работает и работает.

    ОтветитьУдалить