Service/Cluster/RHCluster/FenceVBox
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