events/10.interface: we need to mark interfaces as "up" if we don't know how to monit...
[metze/ctdb/wip.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                 test -n "$OLDLINK" && {
82                     ok=1 # we only set ok for interfaces known to ctdbd
83                     ctdb setifacelink $IFACE up
84                 }
85                 ;;
86             ib*)
87                 # we dont know how to test ib links
88                 test -n "$OLDLINK" && {
89                     ok=1 # we only set ok for interfaces known to ctdbd
90                     ctdb setifacelink $IFACE up
91                 }
92                 ;;
93             *)
94                 [ -z "$IFACE" ] || {
95                     [ "$(basename $(readlink /sys/class/net/$IFACE/device/driver))" = virtio_net ] ||
96                     ethtool $IFACE | grep -q 'Link detected: yes' || {
97                         # On some systems, this is not successful when a
98                         # cable is plugged but the interface has not been
99                         # brought up previously. Bring the interface up and
100                         # try again...
101                         /sbin/ip link set $IFACE up
102                         ethtool $IFACE | grep -q 'Link detected: yes' || {
103                             echo "ERROR: No link on the public network interface $IFACE"
104                             fail=1
105                             test -n "$OLDLINK" && {
106                                 ctdb setifacelink $IFACE down
107                             }
108                             continue
109                         }
110                     }
111                     test -n "$OLDLINK" && {
112                         ok=1 # we only set ok for interfaces known to ctdbd
113                         ctdb setifacelink $IFACE up
114                     }
115                 }
116                 ;;
117             esac
118
119         done
120
121         test x"$fail" = x"0" && {
122                 return 0;
123         }
124
125         test x"$force_fail" != x"0" && {
126                 return 1;
127         }
128
129         test x"$ok" = x"1" && {
130                 return 2;
131         }
132
133         return 1;
134 }
135
136 case "$1" in 
137      #############################
138      # called when ctdbd starts up
139      init)
140         # make sure that we only respond to ARP messages from the NIC where
141         # a particular ip address is associated.
142         [ -f /proc/sys/net/ipv4/conf/all/arp_filter ] && {
143             echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
144         }
145         cat "$CTDB_PUBLIC_ADDRESSES" | cut -d/ -f1 | while read _IP; do
146                 _IP_HELD=`/sbin/ip addr show | grep "inet $_IP/"`
147                 [ -z "$_IP_HELD" ] || {
148                         _IFACE=`echo $_IP_HELD | sed -e "s/.*\s//"`
149                         _NM=`echo $_IP_HELD | sed -e "s/.*$_IP\///" -e "s/\s.*//"`
150                         echo "Removing public address $_IP/$_NM from device $_IFACE"
151                         delete_ip_from_iface $_IFACE $_IP $_NM
152                 }
153         done
154         ;;
155
156      #############################
157      # called after ctdbd has done its initial recovery
158      # and we start the services to become healthy
159      startup)
160         monitor_interfaces
161
162         ;;
163
164
165      ################################################
166      # called when ctdbd wants to claim an IP address
167      takeip)
168         if [ $# != 4 ]; then
169            echo "must supply interface, IP and maskbits"
170            exit 1
171         fi
172         iface=$2
173         ip=$3
174         maskbits=$4
175
176         add_ip_to_iface $iface $ip $maskbits || {
177                 exit 1;
178         }
179
180         # cope with the script being killed while we have the interface blocked
181         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
182
183         # flush our route cache
184         echo 1 > /proc/sys/net/ipv4/route/flush
185         ;;
186
187
188      ##################################################
189      # called when ctdbd wants to release an IP address
190      releaseip)
191         if [ $# != 4 ]; then
192            echo "must supply interface, IP and maskbits"
193            exit 1
194         fi
195
196         # releasing an IP is a bit more complex than it seems. Once the IP
197         # is released, any open tcp connections to that IP on this host will end
198         # up being stuck. Some of them (such as NFS connections) will be unkillable
199         # so we need to use the killtcp ctdb function to kill them off. We also
200         # need to make sure that no new connections get established while we are 
201         # doing this! So what we do is this:
202         # 1) firewall this IP, so no new external packets arrive for it
203         # 2) use netstat -tn to find existing connections, and kill them 
204         # 3) remove the IP from the interface
205         # 4) remove the firewall rule
206         iface=$2
207         ip=$3
208         maskbits=$4
209
210         failed=0
211         # we do an extra delete to cope with the script being killed
212         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
213         iptables -I INPUT -i $iface -d $ip -j DROP
214         kill_tcp_connections $ip
215
216         delete_ip_from_iface $iface $ip $maskbits || {
217                 iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
218                 exit 1;
219         }
220
221         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
222
223         # flush our route cache
224         echo 1 > /proc/sys/net/ipv4/route/flush
225         ;;
226
227      ##################################################
228      # called when ctdbd wants to update an IP address
229      updateip)
230         if [ $# != 5 ]; then
231            echo "must supply old interface, new interface, IP and maskbits"
232            exit 1
233         fi
234
235         # moving an IP is a bit more complex than it seems.
236         # First we drop all traffic on the old interface.
237         # Then we try to add the ip to the new interface and before
238         # we finally remove it from the old interface.
239         #
240         # 1) firewall this IP, so no new external packets arrive for it
241         # 2) add the IP to the new interface
242         # 3) remove the IP from the old interface
243         # 4) remove the firewall rule
244         # 5) use ctdb gratiousarp to propagate the new mac address
245         # 6) use netstat -tn to find existing connections, and tickle them
246         oiface=$2
247         niface=$3
248         ip=$4
249         maskbits=$5
250
251         failed=0
252         # we do an extra delete to cope with the script being killed
253         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
254         iptables -I INPUT -i $oiface -d $ip -j DROP
255
256         # we make sure the interface is up first
257         add_ip_to_iface $niface $ip $maskbits || {
258                 iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
259                 exit 1;
260         }
261
262         delete_ip_from_iface $oiface $ip $maskbits || {
263                 delete_ip_from_iface $niface $ip $maskbits
264                 iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
265                 exit 1;
266         }
267
268         # cope with the script being killed while we have the interface blocked
269         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
270
271         # flush our route cache
272         echo 1 > /proc/sys/net/ipv4/route/flush
273
274         # propagate the new mac address
275         ctdb gratiousarp $ip $niface
276
277         # tickle all existing connections, so that dropped packets
278         # are retransmited and the tcp streams work
279
280         tickle_tcp_connections $ip
281
282         ;;
283
284
285      ###########################################
286      # called when ctdbd has finished a recovery
287      recovered)
288         ;;
289
290      ####################################
291      # called when ctdbd is shutting down
292      shutdown)
293         ;;
294
295      monitor)
296         monitor_interfaces
297         ret=$?
298
299         test x"$ret" = x"2" && {
300                 test x"$CTDB_PARTIALLY_ONLINE_INTERFACES" != x"yes" && {
301                         exit 1;
302                 }
303                 # as long as we have one interface available don't become
304                 # unhealthy
305                 ret=0
306         }
307
308         test x"$ret" != x"0" && {
309                 exit 1;
310         }
311         ;;
312     *)
313         ctdb_standard_event_handler "$@"
314         ;;
315 esac
316
317 exit 0
318