summaryrefslogtreecommitdiffstats
path: root/ctdb/tools/ctdb_natgw
diff options
context:
space:
mode:
Diffstat (limited to 'ctdb/tools/ctdb_natgw')
-rwxr-xr-xctdb/tools/ctdb_natgw194
1 files changed, 194 insertions, 0 deletions
diff --git a/ctdb/tools/ctdb_natgw b/ctdb/tools/ctdb_natgw
new file mode 100755
index 0000000..728cd9c
--- /dev/null
+++ b/ctdb/tools/ctdb_natgw
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+if [ -z "$CTDB_BASE" ] ; then
+ export CTDB_BASE="/usr/local/etc/ctdb"
+fi
+
+. "${CTDB_BASE}/functions"
+
+load_script_options "failover" "11.natgw"
+
+# Default NAT gateway nodes file location
+[ -n "$CTDB_NATGW_NODES" ] || CTDB_NATGW_NODES="${CTDB_BASE}/natgw_nodes"
+
+if [ -z "$CTDB" ] ; then
+ CTDB=ctdb
+fi
+
+############################################################
+
+usage ()
+{
+cat <<EOF
+$0 <option>
+
+<option> is one of:
+ leader Display node number and private IP address of leader node
+ list List private IP addresses of nodes in group, annotate leader
+ status Show status of nodes in NAT gateway group
+EOF
+ exit 1
+}
+
+nodestatus_X=""
+# Fields are:
+# Node|IP|Disconnected|Unknown|Banned|Disabled|Unhealthy|Stopped|Inactive|PartiallyOnline|ThisNode
+get_nodestatus_X ()
+{
+ # Result is cached in global variable nodestatus_X
+ [ -n "$nodestatus_X" ] || \
+ nodestatus_X=$($CTDB -X nodestatus all |
+ sed -e '1d' -e 's@^|@@' -e 's@|$@@')
+}
+
+get_nodestatus ()
+{
+ # Result is cached in global variable nodestatus
+ [ -n "$nodestatus" ] || nodestatus=$($CTDB nodestatus all)
+ [ $? -ne 255 ] # ctdb nodestatus returns 255 on failure
+}
+
+get_natgw_nodes ()
+{
+ # Result is cached in global variable natgw_nodes
+ if [ -n "$natgw_nodes" ] ; then
+ return
+ fi
+
+ if [ ! -r "$CTDB_NATGW_NODES" ] ; then
+ return 1
+ fi
+
+ natgw_nodes=$(cat "$CTDB_NATGW_NODES") || return 1
+
+ # Sanity check file contents here
+ while read _ip _options ; do
+ # Skip comments
+ case "$_ip" in
+ \#*) continue ;;
+ esac
+ case "$_options" in
+ follower-only|"") : ;;
+ *) die "${prog}: Invalid options \"${_options}\" in \"$CTDB_NATGW_NODES\""
+ esac
+ done <<EOF
+$natgw_nodes
+EOF
+
+ return 0
+}
+
+# Print the PNN and IP address of the NAT gateway leader node
+find_leader ()
+{
+ get_natgw_nodes || \
+ die "${prog}: NAT gateway nodes file \"$CTDB_NATGW_NODES\" not found"
+ get_nodestatus_X || \
+ die "${prog}: Unable to get status of nodes"
+
+ # $_ms is an @-delimited list of nodes that are allowed to be the leader
+ _ms="@"
+ while read _ip _options ; do
+ case "$_options" in
+ "") _ms="${_ms}${_ip}@" ;;
+ esac
+ done <<EOF
+$natgw_nodes
+EOF
+
+ # Now filter by $ms and by status of nodes...
+
+ # Note that the 3 awk invocations below have "||" between them, so
+ # the first to succeed will select the leader node.
+
+ # First try for a fully active and healthy node, so must not be
+ # UNKNOWN, DISABLED, UNHEALTHY or INACTIVE (last covers DISCONNECTED,
+ # BANNED or STOPPED)
+ awk -F '|' -v ms="$_ms" \
+ 'BEGIN { ret = 2 }
+ ms ~ "@" $2 "@" &&
+ $4 == 0 && $6 == 0 && $7 == 0 && $9 == 0 { print $1, $2 ; ret=0 ; exit }
+ END { exit ret }' <<EOF ||
+$nodestatus_X
+EOF
+ # Not found? UNHEALTHY/BANNED will do, so node must not be
+ # DISCONNECTED, DISABLED or STOPPED
+ awk -F '|' -v ms="$_ms" \
+ 'BEGIN { ret = 2 }
+ ms ~ "@" $2 "@" &&
+ $3 == 0 && $6 == 0 && $8 == 0 { print $1, $2 ; ret=0 ; exit }
+ END { exit ret }' <<EOF ||
+$nodestatus_X
+EOF
+ # Not found? STOPPED will do, so node must not be DISCONNECTED or
+ # DISABLED
+ awk -F '|' -v ms="$_ms" \
+ 'BEGIN { ret = 2 }
+ ms ~ "@" $2 "@" &&
+ $3 == 0 && $6 == 0 { print $1, $2 ; ret=0 ; exit }
+ END { exit ret }' <<EOF
+$nodestatus_X
+EOF
+}
+
+# List all nodes in the NAT gateway group, annotating the leader node
+nodes_list ()
+{
+ get_natgw_nodes || \
+ die "${prog}: NAT gateway nodes file \"$CTDB_NATGW_NODES\" not found"
+ # Intentional word splitting here
+ # shellcheck disable=SC2046
+ set -- $(find_leader) || \
+ die "${prog}: Unable to determine NAT gateway leader node"
+ _leader_ip="$2"
+
+ # Annotate the leader node
+ while read _ip _options ; do
+ if [ "$_ip" = "$_leader_ip" ] ; then
+ _options="LEADER${_options:+,}${_options}"
+ fi
+ # There is no other way to do this and keep shellcheck happy.
+ # The tab character must be in the format string and the
+ # format string must contain no variables. Some shells will
+ # expand a tab if it is in an argument but others won't.
+ if [ -n "$_options" ] ; then
+ printf '%s\t%s\n' "$_ip" "$_options"
+ else
+ echo "$_ip"
+ fi
+ done <<EOF
+$natgw_nodes
+EOF
+}
+
+# Print the status of all nodes in the NAT gateway group, along with a count
+nodes_status ()
+{
+ get_natgw_nodes || \
+ die "${prog}: NAT gateway nodes file \"$CTDB_NATGW_NODES\" not found"
+ get_nodestatus || \
+ die "${prog}: Unable to get status of nodes"
+
+ # $_ns is a @-delimited list of nodes in the NAT gateway group
+ _ns="@"
+ while read _ip _options ; do
+ _ns="${_ns}${_ip}@"
+ done <<EOF
+$natgw_nodes
+EOF
+
+ # Print status of nodes in $_ns, along with node count
+ awk -v ns="$_ns" 'ns ~ "@" $2 "@" { print $0 }' <<EOF
+$nodestatus
+EOF
+}
+
+prog=$(basename "$0")
+cmd="$1"
+
+case "$cmd" in
+ leader) find_leader ;;
+ list) nodes_list ;;
+ status) nodes_status ;;
+ *) usage ;;
+esac