Service/Cluster/RHCluster/FenceVBox

De TartareFR
Aller à la navigation Aller à la recherche
#!/bin/bash
##############################################################################
#       ____              ____    _       _   _                              #
#      /# /_\_           |  _ \  (_)   __| | (_)   ___   _ __                #
#     |  |/o\o\          | | | | | |  / _` | | |  / _ \ | '__|               #
#     |  \\_/_/          | |_| | | | | (_| | | | |  __/ | |                  #
#    / |_   |            |____/  |_|  \__,_| |_|  \___| |_|                  #
#   |  ||\_ ~|                                                               #
#   |  ||| \/                                                                #
#   |  |||       Project : RedHat Cluster with VirtualBox virtual machines   #
#   \//  |                                                                   #
#    ||  |       Developper : Didier FABERT <dfabert@b2pweb.com>             #
#    ||_  \      Date : 2012, June                                           #
#    \_|  o|                                             ,__,                #
#     \___/      Copyright (C) 2012 by B2PWeb.com        (oo)____            #
#      ||||__                                            (__)    )\          #
#      (___)_)   File : fence_vbox                         ||--|| *          #
#                                                                            #
#    This script is the cluster's fence agent.                               #
#                                                                            #
#    This program is free software; you can redistribute it and/or modify    #
#    it under the terms of the GNU General Public License as published by    #
#    the Free Software Foundation; either version 3 of the License, or       #
#    (at your option) any later version.                                     #
#                                                                            #
#    This program is distributed in the hope that it will be useful,         #
#    but WITHOUT ANY WARRANTY; without even the implied warranty of          #
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           #
#    GNU General Public License for more details.                            #
#                                                                            #
#    You should have received a copy of the GNU General Public License       #
#    along with this program; if not, write to the                           #
#    Free Software Foundation, Inc.,                                         #
#    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .          #
#                                                                            #
#    Script supply minimal actions:                                          #
#      - stop a node with VBoxManage command ( poweroff )                    #
#      - start a node with VBoxManage command                                #
#      - restart a node with VBoxManage command ( tipycally an immediate     #
#        stop and a start after a sleep time )                               #
#      - metadata which is called by cman or css_config_validate and must    #
#        supply a XML string to define fence options.                        #
#                                                                            #
#    Script return 0 on success, 1 otherwise.                                #
#                                                                            #
#    Script must be placed on /usr/sbin directory ( with other fence exec )  #
#                                                                            #
#    cluster.conf example                                                    #
#    <clusternodes>                                                          #
#      <clusternode name="node1.example.com" nodeid="1" votes="1">           #
#        <fence>                                                             #
#          <method name="1">                                                 #
#            <device host="node1.example.com" user="vbox" name="vbox"        #
#                                           vmname="node1" action="reboot"/> #
#          </method>                                                         #
#        </fence>                                                            #
#      </clusternode>                                                        #
#    </clusternodes>                                                         #
#    <fencedevices>                                                          #
#      <fencedevice agent="fence_vbox" name="vbox"/>                         #
#    </fencedevices>                                                         #
#                                                                            #
##############################################################################

VERSION="1.0.0"
VERBOSE=0
logstd=0
logsyslog=1

vboxmanage="/usr/bin/VBoxManage"
action="reboot"
sleeptime="5"
user="vbox"
timeout="30"
host=
vmname=

function show_version() {
        echo "$(basename $0) ${VERSION}"
}

function show_usage() {
        echo "Usage: $(basename $0) [args]"
}

function show_help() {
        show_usage
        echo
        cat << 'EOF'
Options:u:n
  -H HOSTNAME           Host which run VirtualBox
  -u USER               User who run VirtualBox (default=vbox)
  -n VBOXNAME           Name of the virtual machine (as known by VirtualBox) to
                        be fenced
  -o ACTION             Action to process on the virtual machine: on,off,reboot
                        (default=reboot)
  -t <timeout>          Fencing timeout (in seconds; default=30)
  -s <seconds>          Sleep time between stop and start actions for reboot
                        (in seconds; default=5)
  -?                    Help (alternate)
  -h                    Help
  -v                    Display version and exit
  -U                    Usage
  -V                    VBoxManage executable file (eventually with full path)

With no command line argument, arguments are read from standard input.
Arguments read from standard input take the form of:

    arg1=value1
    arg2=value2

  vmname                Virtual Machine name (as known by virtualbox) to fence
  action                Fencing action (null, off, on, [reboot], status, list,
                        monitor, metadata)
  timeout               Fencing timeout (in seconds; default=30)
  sleeptime             Sleep time between stop and start actions for reboot
  vboxmanage            VBoxManage executable file ( eventually with full path )
EOF
}

# Return metadata about this fencing method. This is called by cman tools or
# ccs_config_validate binary to validate clusternode's fence definition.
# eq to "echo 'action=metadata' | fence_vbox"
function get_metadata() {
        cat << 'EOF'
<?xml version="1.0" ?>
<resource-agent name="fence_vbox" shortdesc="Fence agent for virtual machines">
<longdesc>fence_vbox is an I/O Fencing agent which can be used with virtual machines.</longdesc>
<parameters>
        <parameter name="host">
                <getopt mixed="-H" />
                <content type="string" />
                <shortdesc lang="en">VirtualBox hostname which hosted vmname</shortdesc>
        </parameter>
        <parameter name="vmname">
                <getopt mixed="-n" />
                <content type="string" />
                <shortdesc lang="en">Virtual Machine (as known by virtualbox) to fence</shortdesc>
        </parameter>
        <parameter name="action">
                <getopt mixed="-o" />
                <content type="string" default="reboot" />
                <shortdesc lang="en">Fencing action (null, off, on, [reboot], status, metadata)</shortdesc>
        </parameter>
        <parameter name="timeout">
                <getopt mixed="-t" />
                <content type="string" default="30" />
                <shortdesc lang="en">Fencing timeout (in seconds; default=30)</shortdesc>
        </parameter>
        <parameter name="sleeptime">
                <getopt mixed="-s" />
                <content type="string" default="5" />
                <shortdesc lang="en">Sleep time between stop and start actions for reboot (in seconds; default=5)</shortdesc>
        </parameter>
        <parameter name="vboxmanage">
                <getopt mixed="-V" />
                <content type="string" default="/usr/bin/VBoxManage" />
                <shortdesc lang="en">VBoxManage executable file ( eventually with full path )</shortdesc>
        </parameter>
        <parameter name="user">
                <getopt mixed="-u" />
                <content type="string" default="vbox" />
                <shortdesc lang="en">VirtualBox user ( default=vbox )</shortdesc>
        </parameter>
</parameters>
<actions>
        <action name="null" />
        <action name="on" />
        <action name="off" />
        <action name="reboot" />
        <action name="metadata" />
        <action name="status" />
</actions>
</resource-agent>
EOF
}

# Check if vm can be fenced:
#   - check if vm name is defined
#   - check if virtualbox host accept ssh connection without password ( public
#     key of vm must be inserted in vbox user authorized_keys file on
#     virtualbox host )
#   - check if vm is managed by the specified virtualbox host
function check_vm() {
        # Check if all required arguments are defined
        if [ ! "${vmname}" ]
        then
                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: error, vm name not specified."
                [ ${logstd} -ne 0 ] && echo "Error, vm name not specified."
                return 1
        fi

        # Check ssh connection without password ( key only )
        ssh -q -o 'BatchMode=yes' ${user}@${host} "uptime 1>/dev/null 2>&1"
        RETVAL=$?
        if [ ${RETVAL} -ne 0 ]
        then
                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: error, can't connect to ${host} with user ${user} ( key only )."
                [ ${logstd} -ne 0 ] && echo "Can't connect to ${host} with user ${user} ( key only )."
                return 1
        fi

        # Check if vbox manage our virtual machine
        RETVAL=$(ssh ${user}@${host} "${vboxmanage} showvminfo ${vmname} 1>/dev/null 2>&1 ; echo \$?")
        if [ ${RETVAL} -ne 0 ]
        then
                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: error, virtual machine ${vmname} not found on ${host}."
                if [ ${logstd} -ne 0 ]
                then
                        echo -e "Error, virtual machine ${vmname} not found on ${host}.\nChoose one of them:"
                        ssh ${user}@${host} "${vboxmanage} list vms | sed -e 's/{.*}//g' -e 's/\"//g'"
                fi
                return 1
        fi
        return 0
}

# Get vm status
function status_vm() {
        local status=$(ssh ${user}@${host} "${vboxmanage} showvminfo ${vmname} | grep State | sed -e 's/^\s*State\s*:\s*//g'")
        [ ${logsyslog} -ne 0 ] && logger "${vmname} status: ${status}"
        [ ${logstd} -ne 0 ] && echo "${vmname} status: ${status}"
        return 0
}

# Stop vm with poweroff action
function stop_vm() {
        RETVAL=$(ssh ${user}@${host} "${vboxmanage} controlvm ${vmname} poweroff 1>/dev/null 2>&1 ; echo \$?")
        if [ ${RETVAL} -ne 0 ]
        then
                # Check if vm is already stopped
                local status=$(ssh ${user}@${host} "${vboxmanage} showvminfo ${vmname} | grep State | sed -e 's/^\s*State\s*:\s*//g' -e 's/\s*(.*)//g'")
                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: notice: ${vmname} current status \"${status}\""
                [ ${logstd} -ne 0 ] && echo "Notice: ${vmname} current status \"${status}\""
                if [[ "${status}" == "powered off" ]]
                then
                        [ ${logsyslog} -ne 0 ] && logger "fence_vbox: notice: ${vmname} is already switched off, no need to do more."
                        [ ${logstd} -ne 0 ] && echo "Notice: ${vmname} is already switched off, no need to do more"
                        # no need to fence, vm is already stopped
                        return 0
                fi

                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: error, virtual machine ${vmname} not stopped."
                [ ${logstd} -ne 0 ] && echo "Error, virtual machine ${vmname} not stopped."
                return 1
        fi
        return 0
}

# Start vm
function start_vm() {
        RETVAL=$(ssh ${user}@${host} "${vboxmanage} startvm ${vmname} --type headless 1>/dev/null 2>&1 ; echo \$?")
        if [ ${RETVAL} -ne 0 ]
        then
                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: error, virtual machine ${vmname} not started."
                [ ${logstd} -ne 0 ] && echo "Error, virtual machine ${vmname} not started."
                return 1
        fi
        return 0
}
# Restart vm: Only a stop failure report an error
#   - stop vm
#   - sleep during configured time
#   - start vm if it's possible
function restart_vm() {
        stop_vm
        RETVAL=$?
        if [ ${RETVAL} -ne 0 ]
        then
                return 1
        fi

        sleep ${sleeptime}
        start_vm
        # A fencing success is a nested host stopped, start failure is not a cluster
        # error
        return 0
}

# Usefull for debug only
function dump() {
        local message="try to execute action \"${action}\""
        message="${message} on vm named \"${vmname}\""
        message="${message} which is hosted by \"${host}\""
        message="${message} and running under \"${user}\" user"
        message="${message}."
        [ ${logsyslog} -ne 0 ] && logger "fence_vbox: debug, ${message}"
        [ ${logstd} -ne 0 ] && echo ${message}
}

# check if data is available on stdin
if ! [ -t 0 ]
then
        # Parse data
        eval $(cat -)
        logstd=0
        logsyslog=1
else
        # process command line arguments
        while getopts "?hUVt:o:H:s:v:u:n:" opt
        do
                case "${opt}" in
                        H)
                                host=${OPTARG}
                                ;;
                        n)
                                vmname=${OPTARG}
                                ;;
                        o)
                                action=${OPTARG}
                                ;;
                        t)
                                timeout=${OPTARG}
                                ;;
                        V)
                                vboxmanage=${OPTARG}
                                ;;
                        s)
                                sleeptime=${OPTARG}
                                ;;
                        u)
                                user=${OPTARG}
                                ;;
                        U)
                                show_usage
                                exit 0
                                ;;
                        h|\?)
                                show_help
                                exit 0
                                ;;
                        v)
                                show_version
                                exit 0
                                ;;
                        *)
                                show_help
                                exit 1
                                ;;
                esac
        done
        logstd=1
        logsyslog=0
fi

[ ${VERBOSE} -ne 0 ] && dump

case ${action} in
        #Turn on a virtual machine.
        on)
                check_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi

                start_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi
                ;;
        # Destroy or turn off virtual machine.
        off)
                check_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi

                stop_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi
                ;;
        # Reboot virtual machine; i.e. restart the virtual machine.
        reboot)
                check_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi

                restart_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi
                ;;
        # Do absolutely nothing, but do the job perfectly.
        null)
                ;;
        # Print XML metadata to standard output.
        # Required by cman tools for validate config
        metadata)
                get_metadata
                ;;
        # Check whether a virtual machine is running or not.
        status)
                check_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi

                status_vm
                RETVAL=$?
                if [ ${RETVAL} -ne 0 ]
                then
                        exit 1
                fi
                ;;
        # List virtual machines which may be fenced by fence_vbox
        #list)
        #       ;;
        # Check the health of fence_vbox backend plugin.
        #monitor)
        #       ;;
        *)
                show_usage
                [ ${logsyslog} -ne 0 ] && logger "fence_vbox: error,try to execute an unknow action \"${action}\"."
                exit 1
                ;;
esac

exit 0