Files

373 lines
7.8 KiB
Bash
Executable File

#!/bin/bash
#
# OCF resource agent for Linux IPVS connection synchronization
#
# Promoted = IPVS sync master
# Unpromoted = IPVS sync backup
#
OCF_SUCCESS=0
OCF_ERR_GENERIC=1
OCF_ERR_ARGS=2
OCF_ERR_UNIMPLEMENTED=3
OCF_ERR_PERM=4
OCF_ERR_INSTALLED=5
OCF_ERR_CONFIGURED=6
OCF_NOT_RUNNING=7
OCF_RUNNING_PROMOTED=8
OCF_FAILED_PROMOTED=9
IPVSADM="${IPVSADM:-/usr/sbin/ipvsadm}"
IP="${IP:-/usr/sbin/ip}"
# The IP is used only to discover the LAN interface.
# Different nodes may have different interface names.
OCF_RESKEY_probe_ip="${OCF_RESKEY_probe_ip:-192.168.1.1}"
OCF_RESKEY_interface="${OCF_RESKEY_interface:-}"
meta_data()
{
cat <<'XML'
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="ipvs-sync" version="1.0">
<version>1.0</version>
<longdesc lang="en">
Manages the Linux IPVS connection synchronization daemon as a
Pacemaker promotable resource.
Unpromoted instances run the IPVS backup synchronization daemon.
The promoted instance runs the IPVS master synchronization daemon.
</longdesc>
<shortdesc lang="en">
IPVS connection synchronization
</shortdesc>
<parameters>
<parameter name="probe_ip" unique="0" required="0">
<longdesc lang="en">
An IPv4 address reachable through the interface that should
carry IPVS synchronization traffic. Used only for automatic
interface discovery.
</longdesc>
<shortdesc lang="en">Interface discovery IP</shortdesc>
<content type="string" default="192.168.1.1"/>
</parameter>
<parameter name="interface" unique="0" required="0">
<longdesc lang="en">
Optional explicit synchronization interface. If omitted,
the interface is discovered using the route to probe_ip.
</longdesc>
<shortdesc lang="en">Synchronization interface</shortdesc>
<content type="string"/>
</parameter>
</parameters>
<actions>
<action name="start" timeout="20s"/>
<action name="stop" timeout="20s"/>
<action name="monitor" timeout="10s" interval="10s"/>
<action name="promote" timeout="20s"/>
<action name="demote" timeout="20s"/>
<action name="validate-all" timeout="10s"/>
<action name="meta-data" timeout="5s"/>
</actions>
</resource-agent>
XML
}
get_iface()
{
if [ -n "$OCF_RESKEY_interface" ]; then
printf '%s\n' "$OCF_RESKEY_interface"
return 0
fi
"$IP" route get "$OCF_RESKEY_probe_ip" 2>/dev/null |
awk '
{
for (i=1; i<=NF; i++) {
if ($i == "dev" && (i+1) <= NF) {
print $(i+1)
exit
}
}
}'
}
daemon_status()
{
"$IPVSADM" -L --daemon 2>/dev/null
}
set_promotion_score()
{
/usr/sbin/crm_attribute --promotion --update 100 >/dev/null 2>&1 || true
}
monitor_resource()
{
local output
local have_master=0
local have_backup=0
output="$(daemon_status)"
printf '%s\n' "$output" |
grep -q '^master sync daemon' &&
have_master=1
printf '%s\n' "$output" |
grep -q '^backup sync daemon' &&
have_backup=1
# Both roles simultaneously is an invalid state.
if [ "$have_master" -eq 1 ] &&
[ "$have_backup" -eq 1 ]; then
return "$OCF_ERR_GENERIC"
fi
if [ "$have_master" -eq 1 ]; then
set_promotion_score
return "$OCF_RUNNING_PROMOTED"
fi
if [ "$have_backup" -eq 1 ]; then
set_promotion_score
return "$OCF_SUCCESS"
fi
return "$OCF_NOT_RUNNING"
}
stop_master()
{
"$IPVSADM" --stop-daemon master >/dev/null 2>&1 || true
}
stop_backup()
{
"$IPVSADM" --stop-daemon backup >/dev/null 2>&1 || true
}
start_master()
{
local iface
iface="$(get_iface)"
if [ -z "$iface" ]; then
echo "ERROR: unable to determine synchronization interface" >&2
return "$OCF_ERR_GENERIC"
fi
"$IPVSADM" \
--start-daemon master \
--mcast-interface "$iface"
}
start_backup()
{
local iface
iface="$(get_iface)"
if [ -z "$iface" ]; then
echo "ERROR: unable to determine synchronization interface" >&2
return "$OCF_ERR_GENERIC"
fi
"$IPVSADM" \
--start-daemon backup \
--mcast-interface "$iface"
}
start_resource()
{
monitor_resource
rc=$?
case "$rc" in
"$OCF_SUCCESS")
# Already running correctly as backup.
return "$OCF_SUCCESS"
;;
"$OCF_RUNNING_PROMOTED")
# A start operation must leave a promotable resource
# in its unpromoted state.
stop_master
start_backup || return "$OCF_ERR_GENERIC"
;;
"$OCF_NOT_RUNNING")
start_backup || return "$OCF_ERR_GENERIC"
;;
*)
return "$OCF_ERR_GENERIC"
;;
esac
monitor_resource
[ $? -eq "$OCF_SUCCESS" ] &&
return "$OCF_SUCCESS"
return "$OCF_ERR_GENERIC"
}
stop_resource()
{
stop_master
stop_backup
monitor_resource
rc=$?
[ "$rc" -eq "$OCF_NOT_RUNNING" ] &&
return "$OCF_SUCCESS"
return "$OCF_ERR_GENERIC"
}
promote_resource()
{
monitor_resource
rc=$?
case "$rc" in
"$OCF_RUNNING_PROMOTED")
return "$OCF_SUCCESS"
;;
"$OCF_SUCCESS")
stop_backup
if ! start_master; then
# Try to return to a safe unpromoted state.
start_backup >/dev/null 2>&1 || true
return "$OCF_ERR_GENERIC"
fi
;;
*)
echo "ERROR: cannot promote IPVS sync resource from state rc=$rc" >&2
return "$OCF_ERR_GENERIC"
;;
esac
monitor_resource
[ $? -eq "$OCF_RUNNING_PROMOTED" ] &&
return "$OCF_SUCCESS"
return "$OCF_ERR_GENERIC"
}
demote_resource()
{
monitor_resource
rc=$?
case "$rc" in
"$OCF_SUCCESS")
return "$OCF_SUCCESS"
;;
"$OCF_RUNNING_PROMOTED")
stop_master
if ! start_backup; then
return "$OCF_ERR_GENERIC"
fi
;;
"$OCF_NOT_RUNNING")
# Be defensive: demote should result in a running
# unpromoted instance.
start_backup || return "$OCF_ERR_GENERIC"
;;
*)
return "$OCF_ERR_GENERIC"
;;
esac
monitor_resource
[ $? -eq "$OCF_SUCCESS" ] &&
return "$OCF_SUCCESS"
return "$OCF_ERR_GENERIC"
}
validate_all()
{
if [ ! -x "$IPVSADM" ]; then
echo "ERROR: ipvsadm not found at $IPVSADM" >&2
return "$OCF_ERR_INSTALLED"
fi
if [ ! -x "$IP" ]; then
echo "ERROR: ip command not found at $IP" >&2
return "$OCF_ERR_INSTALLED"
fi
iface="$(get_iface)"
if [ -z "$iface" ]; then
echo "ERROR: unable to determine synchronization interface" >&2
return "$OCF_ERR_ARGS"
fi
if ! "$IP" link show dev "$iface" >/dev/null 2>&1; then
echo "ERROR: interface '$iface' does not exist" >&2
return "$OCF_ERR_ARGS"
fi
return "$OCF_SUCCESS"
}
case "$1" in
meta-data)
meta_data
exit "$OCF_SUCCESS"
;;
validate-all)
validate_all
exit $?
;;
start)
start_resource
exit $?
;;
stop)
stop_resource
exit $?
;;
monitor|status)
monitor_resource
exit $?
;;
promote)
promote_resource
exit $?
;;
demote)
demote_resource
exit $?
;;
*)
echo "Usage: $0 {start|stop|monitor|promote|demote|validate-all|meta-data}" >&2
exit "$OCF_ERR_UNIMPLEMENTED"
;;
esac