diff options
Diffstat (limited to 'scripts/ocf-IPaddr2-vyatta')
-rwxr-xr-x | scripts/ocf-IPaddr2-vyatta | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/scripts/ocf-IPaddr2-vyatta b/scripts/ocf-IPaddr2-vyatta new file mode 100755 index 0000000..0d1101c --- /dev/null +++ b/scripts/ocf-IPaddr2-vyatta @@ -0,0 +1,805 @@ +#!/bin/sh +# +# $Id: IPaddr2.in,v 1.24 2006/08/09 13:01:54 lars Exp $ +# +# OCF Resource Agent compliant IPaddr2 script. +# +# Based on work by Tuomo Soini, ported to the OCF RA API by Lars +# Marowsky-Brée. Implements Cluster Alias IP functionality too. +# +# Cluster Alias IP cleanup, fixes and testing by Michael Schwartzkopff +# +# +# Copyright (c) 2003 Tuomo Soini +# Copyright (c) 2004-2006 SUSE LINUX AG, Lars Marowsky-Brée +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# + + +# TODO: +# - There ought to be an ocf_run_cmd function which does all logging, +# timeout handling etc for us +# - Make this the standard IP address agent on Linux; the other +# platforms simply should ignore the additional parameters OR can use +# the legacy heartbeat resource script... +# - Check LVS <-> clusterip incompatibilities. +# +# OCF parameters are as below +# OCF_RESKEY_ip +# OCF_RESKEY_broadcast +# OCF_RESKEY_nic +# OCF_RESKEY_cidr_netmask +# OCF_RESKEY_iflabel +# OCF_RESKEY_mac +# OCF_RESKEY_clusterip_hash +# OCF_RESKEY_arp_interval +# OCF_RESKEY_arp_count +# OCF_RESKEY_arp_bg +# OCF_RESKEY_arp_mac +# +# OCF_RESKEY_CRM_meta_clone +# OCF_RESKEY_CRM_meta_clone_max + + +####################################################################### +# Initialization: + +. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs + +SENDARP=$HA_BIN/send_arp +FINDIF=$HA_BIN/findif +VLDIR=$HA_RSCTMP/IPaddr +SENDARPPIDDIR=$HA_RSCTMP/send_arp +CIP_lockfile=$HA_RSCTMP/IPaddr2-CIP-${OCF_RESKEY_ip} + +####################################################################### + +meta_data() { + cat <<END +<?xml version="1.0"?> +<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd"> +<resource-agent name="IPaddr2"> +<version>1.0</version> + +<longdesc lang="en"> +This Linux-specific resource manages IP alias IP addresses. +It can add an IP alias, or remove one. +In addition, it can implement Cluster Alias IP functionality +if invoked as a clone resource. +</longdesc> + +<shortdesc lang="en">Manages virtual IPv4 addresses</shortdesc> + +<parameters> +<parameter name="ip" unique="1" required="1"> +<longdesc lang="en"> +The IPv4 address to be configured in dotted quad notation, for example +"192.168.1.1". +</longdesc> +<shortdesc lang="en">IPv4 address</shortdesc> +<content type="string" default="" /> +</parameter> +<parameter name="nic" unique="0"> +<longdesc lang="en"> +The base network interface on which the IP address will be brought +online. + +If left empty, the script will try and determine this from the +routing table. + +Do NOT specify an alias interface in the form eth0:1 or anything here; +rather, specify the base interface only. + +</longdesc> +<shortdesc lang="en">Network interface</shortdesc> +<content type="string" default="eth0"/> +</parameter> + +<parameter name="cidr_netmask"> +<longdesc lang="en"> +The netmask for the interface in CIDR format +(e.g., 24 and not 255.255.255.0) + +If unspecified, the script will also try to determine this from the +routing table. +</longdesc> +<shortdesc lang="en">CIDR netmask</shortdesc> +<content type="string" default=""/> +</parameter> + +<parameter name="broadcast"> +<longdesc lang="en"> +Broadcast address associated with the IP. If left empty, the script will +determine this from the netmask. +</longdesc> +<shortdesc lang="en">Broadcast address</shortdesc> +<content type="string" default=""/> +</parameter> + +<parameter name="iflabel"> +<longdesc lang="en"> +You can specify an additional label for your IP address here. +This label is appended to your interface name. +If a label is specified in nic name, this parameter has no effect. +</longdesc> +<shortdesc lang="en">Interface label</shortdesc> +<content type="string" default=""/> +</parameter> + +<parameter name="lvs_support"> +<longdesc lang="en"> +Enable support for LVS Direct Routing configurations. In case a IP +address is stopped, only move it to the loopback device to allow the +local node to continue to service requests, but no longer advertise it +on the network. +</longdesc> +<shortdesc lang="en">Enable support for LVS DR</shortdesc> +<content type="boolean" default="false"/> +</parameter> + +<parameter name="mac"> +<longdesc lang="en"> +Set the interface MAC address explicitly. Currently only used in case of +the Cluster IP Alias. Leave empty to chose automatically. + +</longdesc> +<shortdesc lang="en">Cluster IP MAC address</shortdesc> +<content type="string" default=""/> +</parameter> + +<parameter name="clusterip_hash"> +<longdesc lang="en"> +Specify the hashing algorithm used for the Cluster IP functionality. + +</longdesc> +<shortdesc lang="en">Cluster IP hashing function</shortdesc> +<content type="string" default="sourceip-sourceport-destport"/> +</parameter> + +<parameter name="arp_interval"> +<longdesc lang="en"> +Specify the interval between unsolicited ARP packets in milliseconds. +</longdesc> +<shortdesc lang="en">ARP packet interval in ms</shortdesc> +<content type="integer" default="200"/> +</parameter> + +<parameter name="arp_count"> +<longdesc lang="en"> +Number of unsolicited ARP packets to send. +</longdesc> +<shortdesc lang="en">ARP packet count</shortdesc> +<content type="integer" default="5"/> +</parameter> + +<parameter name="arp_bg"> +<longdesc lang="en"> +Whether or not to send the arp packets in the background. +</longdesc> +<shortdesc lang="en">ARP from background</shortdesc> +<content type="string" default="yes"/> +</parameter> + +<parameter name="arp_mac"> +<longdesc lang="en"> +MAC address to send the ARP packets too. + +You really shouldn't be touching this. + +</longdesc> +<shortdesc lang="en">ARP MAC</shortdesc> +<content type="string" default="ffffffffffff"/> +</parameter> + +</parameters> + +<actions> +<action name="start" timeout="90" /> +<action name="stop" timeout="100" /> +<action name="status" depth="10" timeout="20s" interval="10s" start-delay="5s" /> +<action name="monitor" depth="10" timeout="20s" interval="10s" start-delay="5s" /> +<action name="meta-data" timeout="5s" /> +<action name="validate-all" timeout="20s" /> +</actions> +</resource-agent> +END + + exit $OCF_SUCCESS +} + +ip_init() { + if [ X`uname -s` != "XLinux" ]; then + ocf_log err "IPaddr2 only supported Linux." + exit $OCF_ERR_INSTALLED + fi + + if + case $__OCF_ACTION in + start|stop) ocf_is_root;; + *) true;; + esac + then + : YAY! + else + ocf_log err "You must be root for $__OCF_ACTION operation." + exit $OCF_ERR_PERM + fi + + BASEIP="$OCF_RESKEY_ip" + BRDCAST="$OCF_RESKEY_broadcast" + NIC="$OCF_RESKEY_nic" + # Note: We had a version out there for a while which used + # netmask instead of cidr_netmask. Don't remove this aliasing code! + if + [ ! -z "$OCF_RESKEY_netmask" -a -z "$OCF_RESKEY_cidr_netmask" ] + then + OCF_RESKEY_cidr_netmask=$OCF_RESKEY_netmask + export OCF_RESKEY_cidr_netmask + fi + NETMASK="$OCF_RESKEY_cidr_netmask" + IFLABEL="$OCF_RESKEY_iflabel" + IF_MAC="$OCF_RESKEY_mac" + + LVS_SUPPORT=0 + if [ x"${OCF_RESKEY_lvs_support}" = x"true" \ + -o x"${OCF_RESKEY_lvs_support}" = x"on" \ + -o x"${OCF_RESKEY_lvs_support}" = x"1" ]; then + LVS_SUPPORT=1 + fi + + IP_INC_GLOBAL=${OCF_RESKEY_CRM_meta_clone_max:-1} + IP_INC_NO=`expr $OCF_RESKEY_CRM_meta_clone + 1` + IP_CIP_HASH="${OCF_RESKEY_clusterip_hash}" + + if [ $LVS_SUPPORT -gt 0 ] && [ $IP_INC_GLOBAL -gt 1 ]; then + ocf_log err "LVS and load sharing do not go together well" + exit OCF_ERR_ARGS + fi + + ARP_INTERVAL_MS=${OCF_RESKEY_arp_interval:-200} + ARP_REPEAT=${OCF_RESKEY_arp_count:-5} + ARP_BACKGROUND=${OCF_RESKEY_arp_bg:-yes} + ARP_NETMASK=${OCF_RESKEY_arp_mac:-ffffffffffff} + + if ocf_is_decimal "$IP_INC_GLOBAL" && [ $IP_INC_GLOBAL -gt 0 ]; then + : + else + ocf_log err "Invalid OCF_RESKEY_incarnations_max_global [$IP_INC_GLOBAL], should be positive integer" + exit $OCF_ERR_ARGS + fi + + + # Validation is performed in ip_validate()... + # + # $FINDIF now takes its parameters from the environment + # + if + NICINFO=`$FINDIF -C` + then + NICINFO=`echo $NICINFO | sed -e 's/netmask\ //;s/broadcast\ //'` + NIC=`echo "$NICINFO" | cut -d" " -f1` + NETMASK=`echo "$NICINFO" | cut -d" " -f2` + BRDCAST=`echo "$NICINFO" | cut -d" " -f3` + else + ocf_log err "[$FINDIF -C] failed" + exit $OCF_ERR_ARGS + fi + + SENDARPPIDFILE="$SENDARPPIDDIR/send_arp-$BASEIP" + + case $NIC in + *:*) + IFLABEL=$NIC + NIC=`echo $NIC | sed 's/:.*//'` + ;; + *) + if [ -n "$IFLABEL" ]; then + IFLABEL=${NIC}:${IFLABEL} + fi + ;; + esac + + IP_CIP= + + if [ "$IP_INC_GLOBAL" -gt 1 ]; then + IP_CIP="yes" + if [ -z "$IF_MAC" ]; then + # Choose a MAC + # 1. Concatenate some input together + # 2. This doesn't need to be a cryptographically + # secure hash. + # 3. Drop everything after the first 6 octets (12 chars) + # 4. Delimit the octets with ':' + # 5. Make sure the first octet is odd, + # so the result is a multicast MAC + IF_MAC=`echo $BASEIP $NETMASK $BRDCAST | \ + md5sum | \ + sed -e 's#\(............\).*#\1#' \ + -e 's#..#&:#g; s#:$##' \ + -e 's#^\(.\)[02468aAcCeE]#\11#'` + fi + IP_CIP_FILE="/proc/net/ipt_CLUSTERIP/$BASEIP" + fi +} + +# +# Find out which interface serves the given IP address +# The argument is an IP address, and its output +# is an interface name (e.g., "eth0"). +# +find_interface() { + # + # List interfaces but exclude FreeS/WAN ipsecN virtual interfaces + # + local iface=`$IP2UTIL -o -f inet addr show | grep "\ $BASEIP/" \ + | cut -d ' ' -f2 | grep -v '^ipsec[0-9][0-9]*$'` + echo $iface + return 0 +} + +# +# Delete an interface +# +delete_interface () { + ipaddr="$1" + iface="$2" + netmask="$3" + + CMD="$IP2UTIL -f inet addr delete $ipaddr/$netmask dev $iface" + + ocf_log info "$CMD" + $CMD + + if [ $? -ne 0 ]; then + return $OCF_ERR_GENERIC + fi + + CMD="$IP2UTIL -o -f inet addr show $iface" + + ocf_log info "$CMD" + ADDR=`$CMD` + + if [ $? -ne 0 -o ! -z "$ADDR" ]; then + return $? + fi + + CMD="$IP2UTIL link set $iface down" + + ocf_log info "$CMD" + $CMD + return $? +} + +# +# Add an interface +# +add_interface () { + ipaddr="$1" + netmask="$2" + broadcast="$3" + iface="$4" + label="$5" + + CMD="$IP2UTIL -f inet addr add $ipaddr/$netmask brd $broadcast dev $iface" + + if [ ! -z "$label" ]; then + CMD="$CMD label $label" + fi + + ocf_log info "$CMD" + $CMD + + if [ $? -ne 0 ]; then + return $OCF_ERR_GENERIC + fi + + CMD="$IP2UTIL link set $iface up" + + ocf_log info "$CMD" + $CMD + + return $? +} + +# +# Delete a route +# +delete_route () { + prefix="$1" + iface="$2" + + CMD="$IP2UTIL route delete $prefix dev $iface" + + ocf_log info "$CMD" + $CMD + + return $? +} + +# On Linux systems the (hidden) loopback interface may +# conflict with the requested IP address. If so, this +# unoriginal code will remove the offending loopback address +# and save it in VLDIR so it can be added back in later +# when the IPaddr is released. +# +# TODO: This is very ugly and should be controlled by an additional +# instance parameter. Or even: multi-state, with the IP only being +# "active" on the master!? +# +remove_conflicting_loopback() { + ipaddr="$1" + netmask="$2" + broadcast="$3" + ifname="$4" + + ocf_log info "Removing conflicting loopback $ifname." + if [ -d "$VLDIR/" ] || mkdir -p "$VLDIR/"; then + : Directory $VLDIR now exists + else + ocf_log err "Could not create \"$VLDIR/\" conflicting" \ + " loopback $ifname cannot be restored." + fi + if + echo "$ipaddr $netmask $broadcast $ifname" > "$VLDIR/$ipaddr" + then + : Saved loopback information in $VLDIR/$ipaddr + else + ocf_log err "Could not save conflicting loopback $ifname." \ + "it will not be restored." + fi + delete_interface "$ipaddr" "$ifname" "$netmask" + # Forcibly remove the route (if it exists) to the loopback. + delete_route "$ipaddr" "$ifname" +} + +# +# On Linux systems the (hidden) loopback interface may +# need to be restored if it has been taken down previously +# by remove_conflicting_loopback() +# +restore_loopback() { + ipaddr="$1" + + if [ -s "$VLDIR/$ipaddr" ]; then + ifinfo=`cat "$VLDIR/$ipaddr"` + ocf_log info "Restoring loopback IP Address " \ + "$ifinfo." + add_interface $ifinfo + rm -f "$VLDIR/$ipaddr" + fi +} + +# +# Run send_arp to note peers about new mac address +# +run_send_arp() { + ARGS="-i $ARP_INTERVAL_MS -r $ARP_REPEAT -p $SENDARPPIDFILE $NIC $BASEIP auto not_used not_used" + if [ "x$IP_CIP" = "xyes" ] ; then + if [ x = "x$IF_MAC" ] ; then + MY_MAC=auto + else + MY_MAC=`echo ${IF_MAC} | sed -e 's/://'` + fi + ARGS="-i $ARP_INTERVAL_MS -r $ARP_REPEAT -p $SENDARPPIDFILE $NIC $BASEIP $MY_MAC not_used not_used" + fi + ocf_log info "$SENDARP $ARGS" + case $ARP_BACKGROUND in + yes) + ($SENDARP $ARGS || ocf_log err "Could not send gratuitous arps" &) >&2 + ;; + *) + $SENDARP $ARGS || ocf_log err "Could not send gratuitous arps" + ;; + esac +} + +# Do we already serve this IP address? +# +# returns: +# ok = served (for CIP: + hash bucket) +# partial = served and no hash bucket (CIP only) +# no = nothing +# +ip_served() { + cur_nic="`find_interface $BASEIP`" + + if [ -z "$cur_nic" ]; then + echo "no" + return 0 + fi + + if [ -z "$IP_CIP" ]; then + case $cur_nic in + lo*) if [ "$LVS_SUPPORT" = "1" ]; then + echo "no" + return 0 + fi + ;; + esac + + echo "ok" + return 0 + fi + + # Special handling for the CIP: + if grep -q "^${IP_INC_NO},\|,${IP_INC_NO},\|,${IP_INC_NO}$\|^${IP_INC_NO}$" $IP_CIP_FILE ; then + echo "ok" + return 0 + else + echo "partial" + return 0 + fi + + exit $OCF_ERR_GENERIC +} + +####################################################################### + +ip_usage() { + cat <<END +usage: $0 {start|stop|status|monitor|validate-all|meta-data} + +Expects to have a fully populated OCF RA-compliant environment set. +END +} + +ip_start() { + if [ -n "$IP_CIP" ]; then + # Cluster IPs need special processing when the first bucket + # is added to the node... take a lock to make sure only one + # process executes that code + ocf_take_lock $CIP_lockfile + ocf_release_lock_on_exit $CIP_lockfile + fi + + ip_init + + # + # Do we already service this IP address? + # + local ip_status=`ip_served` + + if [ "$ip_status" = "ok" ]; then + exit $OCF_SUCCESS + fi + + if [ -n "$IP_CIP" ] && [ $ip_status = "no" ]; then + $MODPROBE ip_conntrack + $IPTABLES -I INPUT -d $BASEIP -i $NIC -j CLUSTERIP \ + --new \ + --clustermac $IF_MAC \ + --total-nodes $IP_INC_GLOBAL \ + --local-node $IP_INC_NO \ + --hashmode $IP_CIP_HASH + if [ $? -ne 0 ]; then + ocf_log err "iptables failed" + exit $OCF_ERR_GENERIC + fi + fi + + if [ -n "$IP_CIP" ] && [ $ip_status = "partial" ]; then + echo "+$IP_INC_NO" >$IP_CIP_FILE + fi + + if [ "$ip_status" = "no" ]; then + if [ "$LVS_SUPPORT" = "1" ]; then + case `find_interface $BASEIP` in + lo*) + remove_conflicting_loopback $BASEIP 32 255.255.255.255 lo + ;; + esac + fi + + add_interface $BASEIP $NETMASK $BRDCAST $NIC $IFLABEL + + if [ $? -ne 0 ]; then + ocf_log err "$CMD failed." + exit $OCF_ERR_GENERIC + fi + fi + + case $NIC in + lo*) + : no need to run send_arp on loopback + ;; + *) + run_send_arp + ;; + esac + exit $OCF_SUCCESS +} + +ip_stop() { + ip_init + local ip_del_if="yes" + if [ -n "$IP_CIP" ]; then + # Cluster IPs need special processing when the last bucket + # is removed from the node... take a lock to make sure only one + # process executes that code + ocf_take_lock $CIP_lockfile + ocf_release_lock_on_exit $CIP_lockfile + fi + + if [ -f "$SENDARPPIDFILE" ] ; then + kill `cat "$SENDARPPIDFILE"` + if [ $? -ne 0 ]; then + ocf_log warn "Could not kill previously running send_arp for $BASEIP" + else + ocf_log info "killed previously running send_arp for $BASEIP" + rm -f "$SENDARPPIDFILE" + fi + fi + local ip_status=`ip_served` + + if [ $ip_status = "no" ]; then + : Requested interface not in use + exit $OCF_SUCCESS + fi + + if [ -n "$IP_CIP" ]; then + if [ $ip_status = "partial" ]; then + exit $OCF_SUCCESS + fi + echo "-$IP_INC_NO" >$IP_CIP_FILE + if [ "x$(cat $IP_CIP_FILE)" = "x" ]; then + ocf_log info $BASEIP, $IP_CIP_HASH + i=1 + while [ $i -le $IP_INC_GLOBAL ]; do + ocf_log info $i + $IPTABLES -D INPUT -d $BASEIP -i $NIC -j CLUSTERIP \ + --new \ + --clustermac $IF_MAC \ + --total-nodes $IP_INC_GLOBAL \ + --local-node $i \ + --hashmode $IP_CIP_HASH + i=`expr $i + 1` + done + else + ip_del_if="no" + fi + fi + + if [ "$ip_del_if" = "yes" ]; then + delete_interface $BASEIP $NIC $NETMASK + if [ $? -ne 0 ]; then + exit $OCF_ERR_GENERIC + fi + + if [ "$LVS_SUPPORT" = 1 ]; then + restore_loopback "$BASEIP" + fi + fi + + exit $OCF_SUCCESS +} + +ip_monitor() { + ip_init + # TODO: Implement more elaborate monitoring like checking for + # interface health maybe via a daemon like FailSafe etc... + + local ip_status=`ip_served` + case $ip_status in + ok) + return $OCF_SUCCESS + ;; + partial|no) + exit $OCF_NOT_RUNNING + ;; + *) + # Errors on this interface? + return $OCF_ERR_GENERIC + ;; + esac +} + +ip_validate() { + check_binary $IP2UTIL + if [ -n "$IP_CIP" ]; then + check_binary $IPTABLES + check_binary $MODPROBE + fi + + ip_init + +# $BASEIP, $NETMASK, $NIC , $IP_INC_GLOBAL, and $BRDCAST have been checked within ip_init, +# do not bother here. + + if ocf_is_decimal "$ARP_INTERVAL_MS" && [ $ARP_INTERVAL_MS -gt 0 ]; then + : + else + ocf_log err "Invalid OCF_RESKEY_arp_interval [$ARP_INTERVAL_MS]" + exit $OCF_ERR_ARGS + fi + + if ocf_is_decimal "$ARP_REPEAT" && [ $ARP_REPEAT -gt 0 ]; then + : + else + ocf_log err "Invalid OCF_RESKEY_arp_count [$ARP_REPEAT]" + exit $OCF_ERR_ARGS + fi + + if [ -n "$IP_CIP" ]; then + + local valid=1 + + case $IP_CIP_HASH in + sourceip|sourceip-sourceport|sourceip-sourceport-destport) + ;; + *) + ocf_log err "Invalid OCF_RESKEY_clusterip_hash [$IP_CIP_HASH]" + exit $OCF_ERR_ARGS + ;; + esac + + if [ "$LVS_SUPPORT" = 1 ]; then + ecf_log err "LVS and load sharing not advised to try" + exit $OCF_ERR_ARGS + fi + + case $IF_MAC in + [0-9a-zA-Z][1379bBdDfF][^0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][^0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][^0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][^0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z][^0-9a-zA-Z][0-9a-zA-Z][0-9a-zA-Z]) + ;; + *) + valid=0 + ;; + esac + + if [ $valid -eq 0 ]; then + ocf_log err "Invalid IF_MAC [$IF_MAC]" + exit $OCF_ERR_ARGS + fi + + fi + + exit $OCF_SUCCESS +} + +case $__OCF_ACTION in +meta-data) meta_data + ;; +start) ip_start + ;; +stop) ip_stop + ;; +status) ip_init + ip_status=`ip_served` + if [ $ip_status = "ok" ]; then + echo "running" + exit $OCF_SUCCESS + else + echo "stopped" + exit $OCF_NOT_RUNNING + fi + ;; +monitor) ip_monitor + ;; +validate-all) ip_validate + ;; +usage|help) ip_usage + exit $OCF_SUCCESS + ;; +*) ip_usage + exit $OCF_ERR_UNIMPLEMENTED + ;; +esac |