3 # Run commands on CTDB nodes.
5 # See http://ctdb.samba.org/ for more information about CTDB.
7 # Copyright (C) Martin Schwenke 2008
9 # Based on an earlier script by Andrew Tridgell and Ronnie Sahlberg.
11 # Copyright (C) Andrew Tridgell 2007
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 3 of the License, or
16 # (at your option) any later version.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, see <http://www.gnu.org/licenses/>.
31 Usage: onnode [OPTION] ... <NODES> <COMMAND> ...
33 -c Run in current working directory on specified nodes.
34 -o <prefix> Save standard output from each node to file <prefix>.<ip>
35 -p Run command in parallel on specified nodes.
36 -q Do not print node addresses (overrides -v).
37 -v Print node address even for a single node.
38 <NODES> "all", "ok" (or "healthy"), "con" (or "connected"),
39 "rm" (or "recmaster"), "lvs" (or "lvsmaster"),
40 "natgw" (or "natgwlist");
41 or a node number (0 base); or
42 list (comma separated) of <NODES>; or
43 range (hyphen separated) of node numbers.
51 echo "Invalid <nodespec>" >&2 ; echo >&2
64 # $POSIXLY_CORRECT means that the command passed to onnode can
65 # take options and getopt won't reorder things to make them
67 local temp=$(POSIXLY_CORRECT=1 getopt -n "$prog" -o "cho:pqv" -l help -- "$@")
75 -c) current=true ; shift ;;
76 -o) prefix="$2" ; shift 2 ;;
77 -p) parallel=true ; shift ;;
78 -q) quiet=true ; shift ;;
79 -v) verbose=true ; shift ;;
81 -h|--help|*) usage ;; # Shouldn't happen, so this is reasonable.
98 if [ -n "$node" -a "$node" != "#DEAD" ] ; then
101 echo "${prog}: \"node ${n}\" does not exist" >&2
108 # Subshell avoids hacks to restore $IFS.
113 *-*) seq "${i%-*}" "${i#*-}" 2>/dev/null || invalid_nodespec ;;
114 # Separate lines for readability.
115 all|ok|healthy|con|connected) echo "$i" ;;
116 rm|recmaster|lvs|lvsmaster|natgw|natgwlist) echo "$i" ;;
118 [ $i -gt -1 ] 2>/dev/null || invalid_nodespec
125 ctdb_status_output="" # cache
126 get_nodes_with_status ()
137 bits="0:[0-1]:[0-1]:[0-1]"
143 if [ -z "$ctdb_status_output" ] ; then
144 # FIXME: need to do something if $CTDB_NODES_SOCKETS is set.
145 ctdb_status_output=$(ctdb -Y status 2>/dev/null)
146 if [ $? -ne 0 ] ; then
147 echo "${prog}: unable to get status of CTDB nodes" >&2
150 ctdb_status_output="${ctdb_status_output#* }"
155 for i in $ctdb_status_output ; do
156 # Try removing bits from end.
157 local t="${i%:${bits}:}"
158 if [ "$t" != "$i" ] ; then
159 # Succeeded. Get address. NOTE: this is an optimisation.
160 # It might be better to get the node number and then get
161 # the nth node to get the address. This would make things
162 # more consistent if /etc/ctdb/nodes actually contained
164 nodes="${nodes} ${t##*:}"
171 ctdb_props="" # cache
172 get_node_with_property ()
178 if [ "${ctdb_props##:${prop}:}" = "$ctdb_props" ] ; then
179 prop_node=$(ctdb "$prop" -Y 2>/dev/null)
180 # We only want the first line.
183 prop_node="${prop_node%%${nl}*}"
184 if [ $? -eq 0 ] ; then
185 ctdb_props="${ctdb_props}${ctdb_props:+ }:${prop}:${prop_node}"
190 prop_node="${ctdb_props##:${prop}:}"
191 prop_node="${prop_node%% *}"
193 if [ -n "$prop_node" ] ; then
194 echo_nth "$prop_node" $all_nodes
196 echo "${prog}: No ${prop} available" >&2
205 if [ -n "$CTDB_NODES_SOCKETS" ] ; then
206 all_nodes="$CTDB_NODES_SOCKETS"
208 [ -f "$CTDB_NODES_FILE" ] || CTDB_NODES_FILE=/etc/ctdb/nodes
209 all_nodes=$(sed -e 's@#.*@@g' -e 's@ *@@g' -e 's@^$@#DEAD@' $CTDB_NODES_FILE)
214 for n in $(parse_nodespec "$1") ; do
215 [ $? != 0 ] && exit 1 # Required to catch exit in above subshell.
218 echo "${all_nodes//#DEAD/}"
221 get_nodes_with_status "$all_nodes" "healthy" || exit 1
224 get_nodes_with_status "$all_nodes" "connected" || exit 1
227 get_node_with_property "$all_nodes" "recmaster" || exit 1
230 get_node_with_property "$all_nodes" "lvsmaster" || exit 1
233 get_node_with_property "$all_nodes" "natgwlist" || exit 1
236 echo_nth $n $all_nodes
244 CTDB_SOCKET="$1" sh -c "$2"
249 if [ -n "$prefix" ] ; then
250 cat >"${prefix}.${n}"
251 elif $verbose && $parallel ; then
260 if $verbose && $parallel ; then
267 ######################################################################
271 $current && command="cd $PWD && $command"
274 if [ -n "$CTDB_NODES_SOCKETS" ] ; then
277 # Could "2>/dev/null || true" but want to see errors from typos in file.
278 [ -r /etc/ctdb/onnode.conf ] && . /etc/ctdb/onnode.conf
279 [ -n "$SSH" ] || SSH=ssh
280 if [ "$SSH" = "ssh" ] ; then
283 : # rsh? All bets are off!
287 ######################################################################
289 nodes=$(get_nodes "$nodespec")
290 [ $? != 0 ] && exit 1 # Required to catch exit in above subshell.
295 # If $nodes contains a space or a newline then assume multiple nodes.
298 [ "$nodes" != "${nodes%[ ${nl}]*}" ] && verbose=true
302 trap 'kill -TERM $pids 2>/dev/null' INT TERM
303 # There's a small race here where the kill can fail if no processes
304 # have been added to $pids and the script is interrupted. However,
305 # the part of the window where it matter is very small.
308 set -o pipefail 2>/dev/null
310 { exec 3>&1 ; { $SSH $ssh_opts $EXTRA_SSH_OPTS $n "$command" | stdout_filter >&3 ; } 2>&1 | stderr_filter ; } &
314 echo >&2 ; echo ">> NODE: $n <<" >&2
317 { exec 3>&1 ; { $SSH $ssh_opts $EXTRA_SSH_OPTS $n "$command" | stdout_filter >&3 ; } 2>&1 | stderr_filter ; }
318 [ $? = 0 ] || retcode=$?
325 [ $? = 0 ] || retcode=$?