#!/bin/bash # Verify that adding/deleting IPs using 'ctdb reloadips' works # Checks that when IPs are added to and deleted from a single node then # those IPs are actually assigned and unassigned from the specified # interface. # Prerequisites: # * An active CTDB cluster with public IP addresses configured # Expected results: # * When IPs are added to a single node then they are assigned to an # interface. # * When IPs are deleted from a single node then they disappear from an # interface. . "${TEST_SCRIPTS_DIR}/cluster.bash" set -e ctdb_test_init select_test_node_and_ips #################### # Search for an unused 10.B.1.0/24 network on which to add public IP # addresses. # The initial search is for a 10.B.0.0/16 network since some # configurations may use a whole class B for the private network. # Check that there are no public IP addresses (as reported by "ctdb ip # all") or other IP addresses (as reported by "ip addr show") with # the provided prefix. Note that this is an IPv4-specific test. echo "Getting public IP information from CTDB..." ctdb_onnode "$test_node" "ip -X -v all" ctdb_ip_info=$(awk -F'|' 'NR > 1 { print $2, $3, $5 }' "$outfile") echo "Getting IP information from interfaces..." try_command_on_node all "ip addr show" ip_addr_info=$(awk '$1 == "inet" { ip = $2; sub(/\/.*/, "", ip); print ip }' \ "$outfile") prefix="" for b in $(seq 0 255) ; do prefix="10.${b}" # Does the prefix match any IP address returned by "ip addr info"? while read ip ; do if [ "${ip#${prefix}.}" != "$ip" ] ; then prefix="" continue 2 fi done <<<"$ip_addr_info" # Does the prefix match any public IP address "ctdb ip all"? while read ip pnn iface ; do if [ "${ip#${prefix}.}" != "$ip" ] ; then prefix="" continue 2 fi done <<<"$ctdb_ip_info" # Got through the IPs without matching prefix - done! break done [ -n "$prefix" ] || die "Unable to find a usable IP address prefix" # We really want a class C: 10.B.1.0/24 prefix="${prefix}.1" #################### iface=$(echo "$ctdb_ip_info" | awk -v pnn=$test_node '$2 == pnn { print $3 ; exit }') #################### # This needs to be set only on the recmaster. All nodes should do the trick. new_takeover_timeout=90 echo "Setting TakeoverTimeout=${new_takeover_timeout} to avoid potential bans" try_command_on_node all "$CTDB setvar TakeoverTimeout ${new_takeover_timeout}" #################### try_command_on_node $test_node $CTDB_TEST_WRAPPER ctdb_base_show addresses="${out}/public_addresses" echo "Public addresses file on node $test_node is \"$addresses\"" backup="${addresses}.$$" backup_public_addresses () { try_command_on_node $test_node "cp -a $addresses $backup" } restore_public_addresses () { try_command_on_node $test_node "mv $backup $addresses >/dev/null 2>&1 || true" } ctdb_test_exit_hook_add restore_public_addresses # Now create that backup backup_public_addresses #################### add_ips_to_original_config () { local test_node="$1" local addresses="$2" local iface="$3" local prefix="$4" local first="$5" local last="$6" echo "Adding new public IPs to original config on node ${test_node}..." echo "IPs will be ${prefix}.${first}/24..${prefix}.${last}/24" # Implement this by completely rebuilding the public_addresses # file. This is easier than deleting entries on a remote node. restore_public_addresses backup_public_addresses # Note that tee is a safe way of creating a file on a remote node. # This avoids potential fragility with quoting or redirection. for i in $(seq $first $last) ; do echo "${prefix}.${i}/24 ${iface}" done | try_command_on_node -i $test_node "tee -a $addresses" } check_ips () { local test_node="$1" local iface="$2" local prefix="$3" local first="$4" local last="$5" # If just 0 specified then this is an empty range local public_ips_file=$(mktemp) if [ "$first" = 0 -a -z "$last" ] ; then echo "Checking that there are no IPs in ${prefix}.0/24" else local prefix_regexp="inet *${prefix//./\.}" echo "Checking IPs in range ${prefix}.${first}/24..${prefix}.${last}/24" local i for i in $(seq $first $last) ; do echo "${prefix}.${i}" done | sort >"$public_ips_file" fi try_command_on_node $test_node "ip addr show dev ${iface}" local ip_addrs_file=$(mktemp) cat "$outfile" | \ sed -n -e "s@.*inet * \(${prefix//./\.}\.[0-9]*\)/.*@\1@p" | \ sort >"$ip_addrs_file" local diffs=$(diff "$public_ips_file" "$ip_addrs_file") || true rm -f "$ip_addrs_file" "$public_ips_file" if [ -z "$diffs" ] ; then echo "GOOD: IP addresses are as expected" else echo "BAD: IP addresses are incorrect:" echo "$diffs" exit 1 fi } # ctdb reloadips will fail if it can't disable takover runs. The most # likely reason for this is that there is already a takeover run in # progress. We can't predict when this will happen, so retry if this # occurs. do_ctdb_reloadips () { local retry_max=10 local retry_count=0 while : ; do if try_command_on_node "$test_node" "$CTDB reloadips" ; then return 0 fi if [ "$out" != "Failed to disable takeover runs" ] ; then return 1 fi if [ $retry_count -ge $retry_max ] ; then return 1 fi retry_count=$((retry_count + 1)) echo "Retrying..." sleep_for 1 done } #################### new_ip_max=100 #################### add_ips_to_original_config \ $test_node "$addresses" "$iface" "$prefix" 1 $new_ip_max do_ctdb_reloadips check_ips $test_node "$iface" "$prefix" 1 $new_ip_max ctdb_onnode "$test_node" sync #################### # This should be the primary. Ensure that no other IPs are lost echo "Using 'ctdb reloadips' to remove the 1st address just added..." add_ips_to_original_config \ $test_node "$addresses" "$iface" "$prefix" 2 $new_ip_max do_ctdb_reloadips check_ips $test_node "$iface" "$prefix" 2 $new_ip_max ctdb_onnode "$test_node" sync #################### # Get rid of about 1/2 the IPs start=$(($new_ip_max / 2 + 1)) echo "Updating to include only about 1/2 of the new IPs..." add_ips_to_original_config \ $test_node "$addresses" "$iface" "$prefix" $start $new_ip_max do_ctdb_reloadips check_ips $test_node "$iface" "$prefix" $start $new_ip_max ctdb_onnode "$test_node" sync #################### # Delete the rest echo "Restoring original IP configuration..." restore_public_addresses do_ctdb_reloadips check_ips $test_node "$iface" "$prefix" 0