#!/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' 1.0 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. IPVS connection synchronization An IPv4 address reachable through the interface that should carry IPVS synchronization traffic. Used only for automatic interface discovery. Interface discovery IP Optional explicit synchronization interface. If omitted, the interface is discovered using the route to probe_ip. Synchronization interface 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