Dont check linkstatus for loopback. This interface never has
[rusty/ctdb.git] / config / events.d / 10.interface
1 #!/bin/sh
2
3 #################################
4 # interface event script for ctdb
5 # this adds/removes IPs from your 
6 # public interface
7
8 . $CTDB_BASE/functions
9 loadconfig
10
11 [ -z "$CTDB_PUBLIC_ADDRESSES" ] && {
12         CTDB_PUBLIC_ADDRESSES=$CTDB_BASE/public_addresses
13 }
14
15 [ ! -f "$CTDB_PUBLIC_ADDRESSES" ] && {
16         echo "No public addresses file found. Nothing to do for 10.interfaces"
17         exit 0
18 }
19
20 monitor_interfaces()
21 {
22         local INTERFACES=`cat $CTDB_PUBLIC_ADDRESSES |
23                 sed -e "s/^[^\t ]*[\t ]*//" -e "s/,/ /g" -e "s/[\t ]*$//"`
24
25         [ "$CTDB_PUBLIC_INTERFACE" ] && INTERFACES="$CTDB_PUBLIC_INTERFACE $INTERFACES"
26         [ "$CTDB_NATGW_PUBLIC_IFACE" ] && INTERFACES="$CTDB_NATGW_PUBLIC_IFACE $INTERFACES"
27
28         local IFACES=`ctdb ifaces -Y | grep -v '^:Name:LinkStatus:References:'`
29
30         local I
31         local IFACE
32
33         for I in $IFACES; do
34                 IFACE=`echo -n "$I" | cut -d ':' -f2`
35                 INTERFACES="$IFACE $INTERFACES"
36         done
37
38         INTERFACES=`for IFACE in $INTERFACES ; do echo $IFACE ; done | sort | uniq`
39
40         local fail=0
41         local force_fail=0
42         local ok=0
43         for IFACE in $INTERFACES ; do
44
45             local OLDLINK=`echo -n "$IFACES" | grep "^:$IFACE:" | cut -d ':' -f3 | xargs`
46             test -z "$OLDLINK" && {
47                 force_fail=1
48             }
49
50             # These interfaces are sometimes bond devices
51             # When we use VLANs for bond interfaces, there will only
52             # be an entry in /proc for the underlying real interface
53             local REALIFACE=`echo $IFACE |sed -e 's/\..*$//'`
54             [ -f /proc/net/bonding/$REALIFACE ] && {
55                 grep -q 'Currently Active Slave: None' /proc/net/bonding/$REALIFACE && {
56                         echo "ERROR: No active slaves for bond device $REALIFACE"
57                         fail=1
58                         test -n "$OLDLINK" && {
59                                 ctdb setifacelink $IFACE down
60                         }
61                         continue;
62                 }
63                 grep -q '^MII Status: up' /proc/net/bonding/$REALIFACE || {
64                         echo "ERROR: public network interface $REALIFACE is down"
65                         fail=1
66                         test -n "$OLDLINK" && {
67                                 ctdb setifacelink $IFACE down
68                         }
69                         continue;
70                 }
71                 test -n "$OLDLINK" && {
72                         ok=1 # we only set ok for interfaces known to ctdbd
73                         ctdb setifacelink $IFACE up
74                 }
75                 return 0;
76             }
77
78             case $IFACE in
79             lo*)
80                 # loopback is always working
81                 ;;
82             ib*)
83                 # we dont know how to test ib links
84                 ;;
85             *)
86                 [ -z "$IFACE" ] || {
87                     [ "$(basename $(readlink /sys/class/net/$IFACE/device/driver))" = virtio_net ] ||
88                     ethtool $IFACE | grep -q 'Link detected: yes' || {
89                         # On some systems, this is not successful when a
90                         # cable is plugged but the interface has not been
91                         # brought up previously. Bring the interface up and
92                         # try again...
93                         /sbin/ip link set $IFACE up
94                         ethtool $IFACE | grep -q 'Link detected: yes' || {
95                             echo "ERROR: No link on the public network interface $IFACE"
96                             fail=1
97                             test -n "$OLDLINK" && {
98                                 ctdb setifacelink $IFACE down
99                             }
100                             continue
101                         }
102                     }
103                     test -n "$OLDLINK" && {
104                         ok=1 # we only set ok for interfaces known to ctdbd
105                         ctdb setifacelink $IFACE up
106                     }
107                 }
108                 ;;
109             esac
110
111         done
112
113         test x"$fail" = x"0" && {
114                 return 0;
115         }
116
117         test x"$force_fail" != x"0" && {
118                 return 1;
119         }
120
121         test x"$ok" = x"1" && {
122                 return 2;
123         }
124
125         return 1;
126 }
127
128 case "$1" in 
129      #############################
130      # called when ctdbd starts up
131      init)
132         # make sure that we only respond to ARP messages from the NIC where
133         # a particular ip address is associated.
134         [ -f /proc/sys/net/ipv4/conf/all/arp_filter ] && {
135             echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
136         }
137         cat "$CTDB_PUBLIC_ADDRESSES" | cut -d/ -f1 | while read _IP; do
138                 _IP_HELD=`/sbin/ip addr show | grep "inet $_IP/"`
139                 [ -z "$_IP_HELD" ] || {
140                         _IFACE=`echo $_IP_HELD | sed -e "s/.*\s//"`
141                         _NM=`echo $_IP_HELD | sed -e "s/.*$_IP\///" -e "s/\s.*//"`
142                         echo "Removing public address $_IP/$_NM from device $_IFACE"
143                         delete_ip_from_iface $_IFACE $_IP $_NM
144                 }
145         done
146         ;;
147
148      #############################
149      # called after ctdbd has done its initial recovery
150      # and we start the services to become healthy
151      startup)
152         monitor_interfaces
153
154         ;;
155
156
157      ################################################
158      # called when ctdbd wants to claim an IP address
159      takeip)
160         if [ $# != 4 ]; then
161            echo "must supply interface, IP and maskbits"
162            exit 1
163         fi
164         iface=$2
165         ip=$3
166         maskbits=$4
167
168         add_ip_to_iface $iface $ip $maskbits || {
169                 exit 1;
170         }
171
172         # cope with the script being killed while we have the interface blocked
173         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
174
175         # flush our route cache
176         echo 1 > /proc/sys/net/ipv4/route/flush
177         ;;
178
179
180      ##################################################
181      # called when ctdbd wants to release an IP address
182      releaseip)
183         if [ $# != 4 ]; then
184            echo "must supply interface, IP and maskbits"
185            exit 1
186         fi
187
188         # releasing an IP is a bit more complex than it seems. Once the IP
189         # is released, any open tcp connections to that IP on this host will end
190         # up being stuck. Some of them (such as NFS connections) will be unkillable
191         # so we need to use the killtcp ctdb function to kill them off. We also
192         # need to make sure that no new connections get established while we are 
193         # doing this! So what we do is this:
194         # 1) firewall this IP, so no new external packets arrive for it
195         # 2) use netstat -tn to find existing connections, and kill them 
196         # 3) remove the IP from the interface
197         # 4) remove the firewall rule
198         iface=$2
199         ip=$3
200         maskbits=$4
201
202         failed=0
203         # we do an extra delete to cope with the script being killed
204         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
205         iptables -I INPUT -i $iface -d $ip -j DROP
206         kill_tcp_connections $ip
207
208         delete_ip_from_iface $iface $ip $maskbits || {
209                 iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
210                 exit 1;
211         }
212
213         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
214
215         # flush our route cache
216         echo 1 > /proc/sys/net/ipv4/route/flush
217         ;;
218
219      ##################################################
220      # called when ctdbd wants to update an IP address
221      updateip)
222         if [ $# != 5 ]; then
223            echo "must supply old interface, new interface, IP and maskbits"
224            exit 1
225         fi
226
227         # moving an IP is a bit more complex than it seems.
228         # First we drop all traffic on the old interface.
229         # Then we try to add the ip to the new interface and before
230         # we finally remove it from the old interface.
231         #
232         # 1) firewall this IP, so no new external packets arrive for it
233         # 2) add the IP to the new interface
234         # 3) remove the IP from the old interface
235         # 4) remove the firewall rule
236         # 5) use ctdb gratiousarp to propagate the new mac address
237         # 6) use netstat -tn to find existing connections, and tickle them
238         oiface=$2
239         niface=$3
240         ip=$4
241         maskbits=$5
242
243         failed=0
244         # we do an extra delete to cope with the script being killed
245         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
246         iptables -I INPUT -i $oiface -d $ip -j DROP
247
248         # we make sure the interface is up first
249         add_ip_to_iface $niface $ip $maskbits || {
250                 iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
251                 exit 1;
252         }
253
254         delete_ip_from_iface $oiface $ip $maskbits || {
255                 delete_ip_from_iface $niface $ip $maskbits
256                 iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
257                 exit 1;
258         }
259
260         # cope with the script being killed while we have the interface blocked
261         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
262
263         # flush our route cache
264         echo 1 > /proc/sys/net/ipv4/route/flush
265
266         # propagate the new mac address
267         ctdb gratiousarp $ip $niface
268
269         # tickle all existing connections, so that dropped packets
270         # are retransmited and the tcp streams work
271
272         tickle_tcp_connections $ip
273
274         ;;
275
276
277      ###########################################
278      # called when ctdbd has finished a recovery
279      recovered)
280         ;;
281
282      ####################################
283      # called when ctdbd is shutting down
284      shutdown)
285         ;;
286
287      monitor)
288         monitor_interfaces
289         ret=$?
290
291         test x"$ret" = x"2" && {
292                 test x"$CTDB_PARTIALLY_ONLINE_INTERFACES" != x"yes" && {
293                         exit 1;
294                 }
295                 # as long as we have one interface available don't become
296                 # unhealthy
297                 ret=0
298         }
299
300         test x"$ret" != x"0" && {
301                 exit 1;
302         }
303         ;;
304     *)
305         ctdb_standard_event_handler "$@"
306         ;;
307 esac
308
309 exit 0
310