ctdb-tests: New test to ensure "ctdb reloadips" manipulates IPs correctly
[vlendec/samba-autobuild/.git] / ctdb / tests / complex / 18_ctdb_reloadips.sh
1 #!/bin/bash
2
3 test_info()
4 {
5     cat <<EOF
6 Verify that adding/deleting IPs using 'ctdb reloadips' works
7
8 Checks that when IPs are added to and deleted from a single node then
9 those IPs are actually assigned and unassigned from the specified
10 interface.
11
12 Prerequisites:
13
14 * An active CTDB cluster with public IP addresses configured
15
16 Expected results:
17
18 * When IPs are added to a single node then they are assigned to an
19   interface.
20
21 * When IPs are deleted from a single node then they disappear from an
22   interface.
23 EOF
24 }
25
26 . "${TEST_SCRIPTS_DIR}/integration.bash"
27
28 set -e
29
30 ctdb_test_init "$@"
31
32 ctdb_test_check_real_cluster
33
34 cluster_is_healthy
35
36 # Reset configuration
37 ctdb_restart_when_done
38
39 select_test_node_and_ips
40
41 ####################
42
43 # Search for an unused 10.B.1.0/24 network on which to add public IP
44 # addresses.
45
46 # The initial search is for a 10.B.0.0/16 network since some
47 # configurations may use a whole class B for the private network.
48 # Check that there are no public IP addresses (as reported by "ctdb ip
49 # -n -all") or other IP addresses (as reported by "ip addr show") with
50 # the provided prefix.  Note that this is an IPv4-specific test.
51
52 echo "Getting public IP information from CTDB..."
53 try_command_on_node any "$CTDB ip -Y -v -n all"
54 ctdb_ip_info=$(echo "$out" | awk -F: 'NR > 1 { print $2, $3, $5 }')
55
56 echo "Getting IP information from interfaces..."
57 try_command_on_node all "ip addr show"
58 ip_addr_info=$(echo "$out" | \
59     awk '$1 == "inet" { print gensub(/\/.*/, "", "", $2)}')
60
61 prefix=""
62 for b in $(seq 0 255) ; do
63     prefix="10.${b}"
64
65     # Does the prefix match any IP address returned by "ip addr info"?
66     while read ip ; do
67         if [ "${ip#${prefix}.}" != "$ip" ] ; then
68             prefix=""
69             continue 2
70         fi
71     done <<<"$ip_addr_info"
72
73     # Does the prefix match any public IP address "ctdb ip -n all"?
74     while read ip pnn iface ; do
75         if [ "${ip#${prefix}.}" != "$ip" ] ; then
76             prefix=""
77             continue 2
78         fi
79     done <<<"$ctdb_ip_info"
80
81     # Got through the IPs without matching prefix - done!
82     break
83 done
84
85 [ -n "$prefix" ] || die "Unable to find a usable IP address prefix"
86
87 # We really want a class C: 10.B.1.0/24
88 prefix="${prefix}.1"
89
90 ####################
91
92 iface=$(echo "$ctdb_ip_info" | awk -v pnn=$test_node '$2 == pnn { print $3 ; exit }')
93
94 ####################
95
96 new_takeover_timeout=90
97 echo "Setting TakeoverTimeout=${new_takeover_timeout} to avoid potential bans"
98 try_command_on_node $test_node "$CTDB setvar TakeoverTimeout ${new_takeover_timeout}"
99
100 ####################
101
102 addresses=$(get_ctdbd_command_line_option $test_node "public-addresses")
103 echo "Public addresses file on node $test_node is \"$addresses\""
104 backup="${addresses}.$$"
105
106 backup_public_addresses ()
107 {
108     try_command_on_node $test_node "cp -a $addresses $backup"
109 }
110
111 restore_public_addresses ()
112 {
113     try_command_on_node $test_node "mv $backup $addresses >/dev/null 2>&1 || true"
114 }
115 ctdb_test_exit_hook_add restore_public_addresses
116
117 # Now create that backup
118 backup_public_addresses
119
120 ####################
121
122 add_ips_to_original_config ()
123 {
124     local test_node="$1"
125     local addresses="$2"
126     local iface="$3"
127     local prefix="$4"
128     local first="$5"
129     local last="$6"
130
131     echo "Adding new public IPs to original config on node ${test_node}..."
132     echo "IPs will be ${prefix}.${first}/24..${prefix}.${last}/24"
133
134     # Implement this by completely rebuilding the public_addresses
135     # file.  This is easier than deleting entries on a remote node.
136     restore_public_addresses
137     backup_public_addresses
138
139     # Note that tee is a safe way of creating a file on a remote node.
140     # This avoids potential fragility with quoting or redirection.
141     for i in $(seq $first $last) ; do
142         echo "${prefix}.${i}/24 ${iface}"
143     done |
144     try_command_on_node -i $test_node "tee -a $addresses"
145 }
146
147 check_ips ()
148 {
149     local test_node="$1"
150     local iface="$2"
151     local prefix="$3"
152     local first="$4"
153     local last="$5"
154
155     # If just 0 specified then this is an empty range
156     local public_ips_file=$(mktemp)
157     if [ "$first" = 0 -a -z "$last" ] ; then
158         echo "Checking that there are no IPs in ${prefix}.0/24"
159     else
160         local prefix_regexp="inet *${prefix//./\.}"
161
162         echo "Checking IPs in range ${prefix}.${first}/24..${prefix}.${last}/24"
163
164         local i
165         for i in $(seq $first $last) ; do
166             echo "${prefix}.${i}"
167         done | sort >"$public_ips_file"
168     fi
169
170     try_command_on_node $test_node "ip addr show dev ${iface}"
171     local ip_addrs_file=$(mktemp)
172     echo "$out" | \
173         sed -n -e "s@.*inet * \(${prefix//./\.}\.[0-9]*\)/.*@\1@p" | \
174         sort >"$ip_addrs_file"
175
176     local diffs=$(diff "$public_ips_file" "$ip_addrs_file") || true
177     rm -f "$ip_addrs_file" "$public_ips_file"
178
179     if [ -z "$diffs" ] ; then
180         echo "GOOD: IP addresses are as expected"
181     else
182         echo "BAD: IP addresses are incorrect:"
183         echo "$diffs"
184         exit 1
185     fi
186 }
187
188 ####################
189
190 new_ip_max=100
191
192 ####################
193
194 add_ips_to_original_config \
195     $test_node "$addresses" "$iface" "$prefix" 1 $new_ip_max
196
197 try_command_on_node $test_node "$CTDB reloadips"
198
199 check_ips $test_node "$iface" "$prefix" 1 $new_ip_max
200
201 ####################
202
203 # This should be the primary.  Ensure that no other IPs are lost
204 echo "Using 'ctdb reloadips' to remove the 1st address just added..."
205
206 add_ips_to_original_config \
207     $test_node "$addresses" "$iface" "$prefix" 2 $new_ip_max
208
209 try_command_on_node $test_node "$CTDB reloadips"
210
211 check_ips $test_node "$iface" "$prefix" 2 $new_ip_max
212
213 ####################
214
215 # Get rid of about 1/2 the IPs
216 start=$(($new_ip_max / 2 + 1))
217 echo "Updating to include only about 1/2 of the new IPs..."
218
219 add_ips_to_original_config \
220     $test_node "$addresses" "$iface" "$prefix" $start $new_ip_max
221
222 try_command_on_node $test_node "$CTDB reloadips"
223
224 check_ips $test_node "$iface" "$prefix" $start $new_ip_max
225
226 ####################
227
228 # Delete the rest
229 echo "Restoring original IP configuration..."
230 restore_public_addresses
231
232 try_command_on_node $test_node "$CTDB reloadips"
233
234 check_ips $test_node "$iface" "$prefix" 0