373 lines
7.8 KiB
Bash
Executable File
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
|