IFACE handling. Assume links are always good on nstartup (they almost always
[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 ok=0
42         for IFACE in $INTERFACES ; do
43
44             ip addr show $IFACE 2>/dev/null >/dev/null || {
45                 echo Interface $IFACE does not exist but it is used by public addresses.
46                 exit 1
47             }
48
49             # These interfaces are sometimes bond devices
50             # When we use VLANs for bond interfaces, there will only
51             # be an entry in /proc for the underlying real interface
52             local REALIFACE=`echo $IFACE |sed -e 's/\..*$//'`
53             [ -f /proc/net/bonding/$REALIFACE ] && {
54                 grep -q 'Currently Active Slave: None' /proc/net/bonding/$REALIFACE && {
55                         echo "ERROR: No active slaves for bond device $REALIFACE"
56                         fail=1
57                         ctdb setifacelink $IFACE down
58                         continue;
59                 }
60                 grep -q '^MII Status: up' /proc/net/bonding/$REALIFACE || {
61                         echo "ERROR: public network interface $REALIFACE is down"
62                         fail=1
63                         ctdb setifacelink $IFACE down
64                         continue;
65                 }
66                 ok=1 # we only set ok for interfaces known to ctdbd
67                 ctdb setifacelink $IFACE up
68                 continue;
69             }
70
71             case $IFACE in
72             lo*)
73                 # loopback is always working
74                 ok=1 # we only set ok for interfaces known to ctdbd
75                 ctdb setifacelink $IFACE up
76                 ;;
77             ib*)
78                 # we dont know how to test ib links
79                 ok=1 # we only set ok for interfaces known to ctdbd
80                 ctdb setifacelink $IFACE up
81                 ;;
82             *)
83                 [ -z "$IFACE" ] || {
84                     [ "$(basename $(readlink /sys/class/net/$IFACE/device/driver) 2>/dev/null)" = 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                             ctdb setifacelink $IFACE down
95                             continue
96                         }
97                     }
98                     ok=1 # we only set ok for interfaces known to ctdbd
99                     ctdb setifacelink $IFACE up
100                 }
101                 ;;
102             esac
103
104         done
105
106         test x"$fail" = x"0" && {
107                 return 0;
108         }
109
110         test x"$ok" = x"1" && {
111                 return 2;
112         }
113
114         return 1;
115 }
116
117 case "$1" in 
118      #############################
119      # called when ctdbd starts up
120      init)
121         # make sure that we only respond to ARP messages from the NIC where
122         # a particular ip address is associated.
123         [ -f /proc/sys/net/ipv4/conf/all/arp_filter ] && {
124             echo 1 > /proc/sys/net/ipv4/conf/all/arp_filter
125         }
126         ;;
127
128      #############################
129      # called after ctdbd has done its initial recovery
130      # and we start the services to become healthy
131      startup)
132         # Assume all links are good initially
133         INTERFACES=`for IFACE in $INTERFACES ; do echo $IFACE ; done | sort | uniq`
134
135         for IFACE in $INTERFACES ; do
136                 ctdb setifacelink $IFACE down
137         done
138         
139         monitor_interfaces
140
141         ;;
142
143
144      ################################################
145      # called when ctdbd wants to claim an IP address
146      takeip)
147         if [ $# != 4 ]; then
148            echo "must supply interface, IP and maskbits"
149            exit 1
150         fi
151         iface=$2
152         ip=$3
153         maskbits=$4
154
155         add_ip_to_iface $iface $ip $maskbits || {
156                 exit 1;
157         }
158
159         # cope with the script being killed while we have the interface blocked
160         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
161
162         # flush our route cache
163         echo 1 > /proc/sys/net/ipv4/route/flush
164         ;;
165
166
167      ##################################################
168      # called when ctdbd wants to release an IP address
169      releaseip)
170         if [ $# != 4 ]; then
171            echo "must supply interface, IP and maskbits"
172            exit 1
173         fi
174
175         # releasing an IP is a bit more complex than it seems. Once the IP
176         # is released, any open tcp connections to that IP on this host will end
177         # up being stuck. Some of them (such as NFS connections) will be unkillable
178         # so we need to use the killtcp ctdb function to kill them off. We also
179         # need to make sure that no new connections get established while we are 
180         # doing this! So what we do is this:
181         # 1) firewall this IP, so no new external packets arrive for it
182         # 2) use netstat -tn to find existing connections, and kill them 
183         # 3) remove the IP from the interface
184         # 4) remove the firewall rule
185         iface=$2
186         ip=$3
187         maskbits=$4
188
189         failed=0
190         # we do an extra delete to cope with the script being killed
191         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
192         iptables -I INPUT -i $iface -d $ip -j DROP
193         kill_tcp_connections $ip
194
195         delete_ip_from_iface $iface $ip $maskbits || {
196                 iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
197                 exit 1;
198         }
199
200         iptables -D INPUT -i $iface -d $ip -j DROP 2> /dev/null
201
202         # flush our route cache
203         echo 1 > /proc/sys/net/ipv4/route/flush
204         ;;
205
206      ##################################################
207      # called when ctdbd wants to update an IP address
208      updateip)
209         if [ $# != 5 ]; then
210            echo "must supply old interface, new interface, IP and maskbits"
211            exit 1
212         fi
213
214         # moving an IP is a bit more complex than it seems.
215         # First we drop all traffic on the old interface.
216         # Then we try to add the ip to the new interface and before
217         # we finally remove it from the old interface.
218         #
219         # 1) firewall this IP, so no new external packets arrive for it
220         # 2) add the IP to the new interface
221         # 3) remove the IP from the old interface
222         # 4) remove the firewall rule
223         # 5) use ctdb gratiousarp to propagate the new mac address
224         # 6) use netstat -tn to find existing connections, and tickle them
225         oiface=$2
226         niface=$3
227         ip=$4
228         maskbits=$5
229
230         failed=0
231         # we do an extra delete to cope with the script being killed
232         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
233         iptables -I INPUT -i $oiface -d $ip -j DROP
234
235         delete_ip_from_iface $oiface $ip $maskbits 2>/dev/null
236         delete_ip_from_iface $niface $ip $maskbits 2>/dev/null
237
238         add_ip_to_iface $niface $ip $maskbits || {
239                 iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
240                 exit 1;
241         }
242
243         # cope with the script being killed while we have the interface blocked
244         iptables -D INPUT -i $oiface -d $ip -j DROP 2> /dev/null
245
246         # flush our route cache
247         echo 1 > /proc/sys/net/ipv4/route/flush
248
249         # propagate the new mac address
250         ctdb gratiousarp $ip $niface
251
252         # tickle all existing connections, so that dropped packets
253         # are retransmited and the tcp streams work
254
255         tickle_tcp_connections $ip
256
257         ;;
258
259
260      ###########################################
261      # called when ctdbd has finished a recovery
262      recovered)
263         ;;
264
265      ####################################
266      # called when ctdbd is shutting down
267      shutdown)
268         ;;
269
270      monitor)
271         monitor_interfaces
272         ret=$?
273
274         test x"$ret" = x"2" && {
275                 test x"$CTDB_PARTIALLY_ONLINE_INTERFACES" != x"yes" && {
276                         exit 1;
277                 }
278                 # as long as we have one interface available don't become
279                 # unhealthy
280                 ret=0
281         }
282
283         test x"$ret" != x"0" && {
284                 exit 1;
285         }
286         ;;
287     *)
288         ctdb_standard_event_handler "$@"
289         ;;
290 esac
291
292 exit 0
293