Eventscripts - 10.interfaces should not check orphaned interfaces.
[vlendec/samba-autobuild/.git] / ctdb / config / events.d / 10.interface
index 15e55618d1538a906c1423a3537cd427ffd518bf..ecaa323c5e63408c1d2c480eab699aa13f37eec4 100755 (executable)
 # public interface
 
 . $CTDB_BASE/functions
-loadconfig ctdb
-
-cmd="$1"
-shift
+loadconfig
 
 [ -z "$CTDB_PUBLIC_ADDRESSES" ] && {
        CTDB_PUBLIC_ADDRESSES=$CTDB_BASE/public_addresses
 }
 
 [ ! -f "$CTDB_PUBLIC_ADDRESSES" ] && {
-       echo "No public addresses file found. Nothing to do for 10.interfaces"
        exit 0
 }
 
-case $cmd in 
+monitor_interfaces()
+{
+       local INTERFACES=`cat $CTDB_PUBLIC_ADDRESSES |
+               sed -e "s/^[^\t ]*[\t ]*//" -e "s/,/ /g" -e "s/[\t ]*$//"`
+
+       [ "$CTDB_PUBLIC_INTERFACE" ] && INTERFACES="$CTDB_PUBLIC_INTERFACE $INTERFACES"
+       [ "$CTDB_NATGW_PUBLIC_IFACE" ] && INTERFACES="$CTDB_NATGW_PUBLIC_IFACE $INTERFACES"
+
+
+       # For all but the 1st line, get the 2nd last field with commas
+       # changes to spaces.
+       local IFACES=`ctdb -Y ip -v | sed -e '1d' -e 's/:[^:]*:$//' -e 's/^.*://' -e 's/,/ /g'`
+
+       local IFACE
+
+       INTERFACES=`for IFACE in $INTERFACES $IFACES ; do echo $IFACE ; done | sort | uniq`
+
+       local fail=0
+       local ok=0
+       for IFACE in $INTERFACES ; do
+
+           ip addr show $IFACE 2>/dev/null >/dev/null || {
+               echo Interface $IFACE does not exist but it is used by public addresses.
+               continue
+           }
+
+           # These interfaces are sometimes bond devices
+           # When we use VLANs for bond interfaces, there will only
+           # be an entry in /proc for the underlying real interface
+           local REALIFACE=`echo $IFACE |sed -e 's/\..*$//'`
+           [ -f /proc/net/bonding/$REALIFACE ] && {
+               grep -q 'Currently Active Slave: None' /proc/net/bonding/$REALIFACE && {
+                       echo "ERROR: No active slaves for bond device $REALIFACE"
+                       fail=1
+                       ctdb setifacelink $IFACE down >/dev/null 2>/dev/null
+                       continue;
+               }
+               grep -q '^MII Status: up' /proc/net/bonding/$REALIFACE || {
+                       echo "ERROR: public network interface $REALIFACE is down"
+                       fail=1
+                       ctdb setifacelink $IFACE down >/dev/null 2>/dev/null
+                       continue;
+               }
+               grep -q '^Bonding Mode: IEEE 802.3ad Dynamic link aggregation' /proc/net/bonding/$REALIFACE && {
+                       grep 'MII Status:' /proc/net/bonding/$REALIFACE | tail -n +2 | grep -q '^MII Status: up' || {
+                               echo No active slaves for 802.ad bond device $REALIFACE
+                               ctdb setifacelink $IFACE down >/dev/null 2>/dev/null
+                               fail=1
+                               continue
+                       }
+               }
+               ok=1 # we only set ok for interfaces known to ctdbd
+               ctdb setifacelink $IFACE up >/dev/null 2>/dev/null
+               continue;
+           }
+
+           case $IFACE in
+           lo*)
+               # loopback is always working
+               ok=1 # we only set ok for interfaces known to ctdbd
+               ctdb setifacelink $IFACE up >/dev/null 2>/dev/null
+               ;;
+           ib*)
+               # we dont know how to test ib links
+               ok=1 # we only set ok for interfaces known to ctdbd
+               ctdb setifacelink $IFACE up >/dev/null 2>/dev/null
+               ;;
+           *)
+               [ -z "$IFACE" ] || {
+                   [ "$(basename $(readlink /sys/class/net/$IFACE/device/driver) 2>/dev/null)" = virtio_net ] ||
+                   ethtool $IFACE | grep -q 'Link detected: yes' || {
+                       # On some systems, this is not successful when a
+                       # cable is plugged but the interface has not been
+                       # brought up previously. Bring the interface up and
+                       # try again...
+                       /sbin/ip link set $IFACE up
+                       ethtool $IFACE | grep -q 'Link detected: yes' || {
+                           echo "ERROR: No link on the public network interface $IFACE"
+                           fail=1
+                           ctdb setifacelink $IFACE down >/dev/null 2>/dev/null
+                           continue
+                       }
+                   }
+                   ok=1 # we only set ok for interfaces known to ctdbd
+                   ctdb setifacelink $IFACE up >/dev/null 2>/dev/null
+               }
+               ;;
+           esac
+
+       done
+
+       test x"$fail" = x"0" && {
+               return 0;
+       }
+
+       test x"$ok" = x"1" && {
+               return 2;
+       }
+
+       return 1;
+}
+
+case "$1" in 
      #############################
      # called when ctdbd starts up
-     startup)
+     init)
        # make sure that we only respond to ARP messages from the NIC where
        # a particular ip address is associated.
        [ -f /proc/sys/net/ipv4/conf/all/arp_filter ] && {
            echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
        }
-       cat "$CTDB_PUBLIC_ADDRESSES" | cut -d/ -f1 | while read _IP; do
-               _IP_HELD=`/sbin/ip addr show | grep "inet $_IP/"`
-               [ -z "$_IP_HELD" ] || {
-                       _IFACE=`echo $_IP_HELD | sed -e "s/.*\s//"`
-                       _NM=`echo $_IP_HELD | sed -e "s/.*$_IP\///" -e "s/\s.*//"`
-                       echo "Removing public address $_IP/$_NM from device $_IFACE"
-                       /sbin/ip addr del $_IP/$_NM dev $_IFACE
-               }
+       ;;
+
+     #############################
+     # called after ctdbd has done its initial recovery
+     # and we start the services to become healthy
+     startup)
+       # Assume all links are good initially
+       INTERFACES=`for IFACE in $INTERFACES ; do echo $IFACE ; done | sort | uniq`
+
+       for IFACE in $INTERFACES ; do
+               ctdb setifacelink $IFACE down >/dev/null 2>/dev/null
        done
+       
+       monitor_interfaces
+
        ;;
 
 
      ################################################
      # called when ctdbd wants to claim an IP address
      takeip)
-       if [ $# != 3 ]; then
+       if [ $# != 4 ]; then
           echo "must supply interface, IP and maskbits"
           exit 1
        fi
-       iface=$1
-       ip=$2
-       maskbits=$3
-
-       # we make sure the interface is up first
-       /sbin/ip link set $iface up || {
-                echo "Failed to bringup interface $iface"
-                exit 1
-       }
-       /sbin/ip addr add $ip/$maskbits dev $iface || {
-                echo "Failed to add $ip/$maskbits on dev $iface"
-                exit 1
+       iface=$2
+       ip=$3
+       maskbits=$4
+
+       add_ip_to_iface $iface $ip $maskbits || {
+               exit 1;
        }
+
        # cope with the script being killed while we have the interface blocked
        iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
 
@@ -72,7 +171,7 @@ case $cmd in
      ##################################################
      # called when ctdbd wants to release an IP address
      releaseip)
-       if [ $# != 3 ]; then
+       if [ $# != 4 ]; then
           echo "must supply interface, IP and maskbits"
           exit 1
        fi
@@ -87,9 +186,9 @@ case $cmd in
        # 2) use netstat -tn to find existing connections, and kill them 
        # 3) remove the IP from the interface
        # 4) remove the firewall rule
-       iface=$1
-       ip=$2
-       maskbits=$3
+       iface=$2
+       ip=$3
+       maskbits=$4
 
        failed=0
        # we do an extra delete to cope with the script being killed
@@ -97,32 +196,68 @@ case $cmd in
        iptables -I INPUT -i $iface -d $ip -j DROP
        kill_tcp_connections $ip
 
-       # the ip tool will delete all secondary IPs if this is the primary. To work around
-       # this _very_ annoying behaviour we have to keep a record of the secondaries and re-add
-       # them afterwards. yuck
-       secondaries=""
-       if /sbin/ip addr list dev $iface primary | grep "inet $ip/$maskbits " > /dev/null; then
-           secondaries=`/sbin/ip addr list dev $iface secondary | grep " inet " | awk '{print $2}'`
-       fi
-       /sbin/ip addr del $ip/$maskbits dev $iface || failed=1
-       [ -z "$secondaries" ] || {
-           for i in $secondaries; do
-               if /sbin/ip addr list dev $iface | grep "inet $i" > /dev/null; then
-                   echo "kept secondary $i on dev $iface"
-               else 
-                   echo "re-adding secondary address $i to dev $iface"
-                   /sbin/ip addr add $i dev $iface || failed=1         
-               fi
-           done
+       delete_ip_from_iface $iface $ip $maskbits || {
+               iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
+               exit 1;
        }
-       iptables -D INPUT -i $iface -d $ip -j DROP
-       [ $failed = 0 ] || {
-                echo "Failed to del $ip on dev $iface"
-                exit 1
+
+       iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
+
+       # flush our route cache
+       echo 1 > /proc/sys/net/ipv4/route/flush
+       ;;
+
+     ##################################################
+     # called when ctdbd wants to update an IP address
+     updateip)
+       if [ $# != 5 ]; then
+          echo "must supply old interface, new interface, IP and maskbits"
+          exit 1
+       fi
+
+       # moving an IP is a bit more complex than it seems.
+       # First we drop all traffic on the old interface.
+       # Then we try to add the ip to the new interface and before
+       # we finally remove it from the old interface.
+       #
+       # 1) firewall this IP, so no new external packets arrive for it
+       # 2) add the IP to the new interface
+       # 3) remove the IP from the old interface
+       # 4) remove the firewall rule
+       # 5) use ctdb gratiousarp to propagate the new mac address
+       # 6) use netstat -tn to find existing connections, and tickle them
+       oiface=$2
+       niface=$3
+       ip=$4
+       maskbits=$5
+
+       failed=0
+       # we do an extra delete to cope with the script being killed
+       iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
+       iptables -I INPUT -i $oiface -d $ip -j DROP
+
+       delete_ip_from_iface $oiface $ip $maskbits 2>/dev/null
+       delete_ip_from_iface $niface $ip $maskbits 2>/dev/null
+
+       add_ip_to_iface $niface $ip $maskbits || {
+               iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
+               exit 1;
        }
 
+       # cope with the script being killed while we have the interface blocked
+       iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
+
        # flush our route cache
        echo 1 > /proc/sys/net/ipv4/route/flush
+
+       # propagate the new mac address
+       ctdb gratiousarp $ip $niface
+
+       # tickle all existing connections, so that dropped packets
+       # are retransmited and the tcp streams work
+
+       tickle_tcp_connections $ip
+
        ;;
 
 
@@ -137,40 +272,26 @@ case $cmd in
        ;;
 
      monitor)
-       INTERFACES=`cat $CTDB_PUBLIC_ADDRESSES | 
-               sed -e "s/^[^\t ]*[\t ]*//" -e "s/[\t ]*$//"`
-
-       [ "$CTDB_PUBLIC_INTERFACE" ] && INTERFACES="$CTDB_PUBLIC_INTERFACE $INTERFACES"
+       monitor_interfaces
+       ret=$?
 
-       INTERFACES=`for IFACE in $INTERFACES ; do echo $IFACE ; done | sort | uniq`
-
-       for IFACE in $INTERFACES ; do
-           case $IFACE in 
-           bond*)
-               IFACE=`echo $IFACE |sed -e 's/\....$//'`
-               grep '^MII Status: up' /proc/net/bonding/$IFACE > /dev/null || {
-                       echo "ERROR: public network interface $IFACE is down"
-                       exit 1
+       test x"$ret" = x"2" && {
+               test x"$CTDB_PARTIALLY_ONLINE_INTERFACES" != x"yes" && {
+                       exit 1;
                }
-               ;;
-           ib*)
-               # we dont know how to test ib links
-               ;;
-           *)
-               [ -z "$IFACE" ] || {
-                   /usr/sbin/ethtool $IFACE | grep 'Link detected: yes' > /dev/null || {
-                       echo "ERROR: No link on the public network interface $IFACE"
-                       exit 1
-                   }
-               }
-               ;;
-           esac
-       done
-       ;;
+               # as long as we have one interface available don't become
+               # unhealthy
+               ret=0
+       }
 
+       test x"$ret" != x"0" && {
+               exit 1;
+       }
+       ;;
+    *)
+       ctdb_standard_event_handler "$@"
+       ;;
 esac
 
 exit 0
 
-
-