summaryrefslogtreecommitdiffstats
path: root/ctdb/tools/ctdb_natgw
blob: 728cd9c9b32aaa7534cd2a9b434961c56749504d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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