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