/usr/lib/drbd/rhcs_fence is in drbd-utils 8.9.6-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
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 | #!/bin/bash
Author="Lars Ellenberg <lars.ellenberg@linbit.com>"
Date="2014-09-18"
Version="1.0"
License="GPL v2+"
#
# Inspired by the rhcs_fence perl version
# by Digimer (digimer@alteeve.ca)
# Alteeve's Niche! - https://alteeve.ca/w/
# As found at https://github.com/digimer/rhcs_fence/releases/tag/0.2.8
#
# This program ties Linbit's DRBD into Red Hat's RHCS's fence daemon via the
# 'fence_node' shell call.
# from environment
# if tools do not yet export the plural,
# use the (deprecated) singular.
: ${DRBD_PEERS:=$DRBD_PEER}
PROG=${0##*/}
: ${DEBUG:=1}
LOG_PRIO=daemon.warning
LOG_TAG="$PROG[$$] $DRBD_PEERS: $DRBD_RESOURCE(minor $DRBD_MINOR)"
##############################################################################
# helper functions
##############################################################################
die() { echo "$*"; exit 1; }
all_minors_up_to_date()
{
set -- $DRBD_MINOR
local n_minors=$#
[[ $n_minors != 0 ]] ||
die "Resource minor numbers unknown! Unable to proceed."
# build a "grep extended regex"
local _OLDIFS=$IFS
IFS="|"
local minor_regex="^ *($*): cs:"
IFS=$_OLDIFS
# grep -c -Ee '^ *(m|i|n|o|r|s): cs:.* ds:UpToDate' /proc/drbd
local proc_drbd=$(</proc/drbd)
local minors_of_resource=$(echo "$proc_drbd" | grep -E -e "$minor_regex")
local n_up_to_date=$(echo "$minors_of_resource" | grep -c -e "ds:UpToDate")
debug "n_minors: $n_minors; n_up_to_date: $n_up_to_date"
[[ $n_up_to_date = $n_minors ]] # return code is propagated
}
wait_for_fence_domain_state_change()
{
local retries=$1 i
for (( i=0; $i < $retries; i++ )); do
sleep 1
# canonicalize white space by word splitting
# append one space (last line is "members")
# for easier pattern matching on member id)
set -- $(fence_tool ls)
fence_tool_ls="$* "
debug "$i: fence_tool ls: $fence_tool_ls"
wait_condition && return 0
done
echo "still not met wait condition after $i retries"
return 1 # timed out
}
wait_for_id_to_become_victim()
{
wait_condition() [[ $fence_tool_ls = *"victim now $id "* ]]
echo "waiting for $id to become victim"
wait_for_fence_domain_state_change 30 # return value propagates
}
wait_for_id_to_drop_out_of_membership()
{
wait_condition() [[ $fence_tool_ls != *"members"*" $id "* ]]
echo "waiting for $id to drop out of membership"
wait_for_fence_domain_state_change 240 || return 1
echo "successfully fenced $name (by fenced)"
return 0
}
eject_target()
{
# tell cman to eject this node
debug "call: cman_tool kill -n $name"
cman_tool kill -n $name
# if it was member in the fence domain,
# wait for it to become victim,
# wait for it to drop out of the membership.
if [[ $fence_tool_ls = *"members"*" $id "* ]]; then
wait_for_id_to_become_victim &&
wait_for_id_to_drop_out_of_membership &&
return 0 # Yes!
fi
return 1
}
##############################################################################
# logging preparations
##############################################################################
# Funky redirection to avoid logger feeding its own output to itself accidentally.
# Funky double exec to avoid an intermediate sub-shell.
# Sometimes, the sub-shell lingers around, keeps file descriptors open,
# and logger then won't notice the main script has finished,
# forever waiting for further input.
# The second exec replaces the subshell, and logger will notice directly
# when its stdin is closed once the main script exits.
# This avoids the spurious logger processes.
if test -t 2 ; then
[[ $DEBUG != 0 ]] &&
exec 3> >( exec 1>&- logger -s -p ${LOG_PRIO%.*}.debug -t "$LOG_TAG: DEBUG" )
exec 1> >( exec 1>&- logger -s -p $LOG_PRIO -t "$LOG_TAG" )
else
[[ $DEBUG != 0 ]] &&
exec 3> >( exec 1>&- 2>&- logger -p ${LOG_PRIO%.*}.debug -t "$LOG_TAG: DEBUG" )
exec 1> >( exec 1>&- 2>&- logger -p $LOG_PRIO -t "$LOG_TAG" )
fi
# and now point stderr to logger, too
exec 2>&1
if [[ $DEBUG = 0 ]]; then
debug() { :; }
else
debug() { echo >&3 "$*" ; }
if [[ $DEBUG -gt 1 ]]; then
BASH_XTRACEFD=3
set -x
fi
fi
##############################################################################
# "main"
##############################################################################
[[ $DRBD_PEERS ]] || die "No target list specified. You need to pass DRBD_PEERS via environment."
all_minors_up_to_date || die "some minor device is NOT 'UpToDate', will not fence peer"
for peer in $DRBD_PEERS; do
for name in $peer ${peer%%.*}; do
set -- $(cman_tool -F id,type nodes -n $name)
id=$1 state=$2
[[ $id ]] && break
done
if [[ -z $id ]] || [[ $id = *[!0-9]* ]] ; then
die "could not resolve cman node id of $peer, giving up"
fi
echo "resolved $peer as cman node $name, id $id, state $state"
# record fence domain state now
set -- $(fence_tool ls)
fence_tool_ls="$* "
debug "fence_tool ls: $fence_tool_ls"
if [[ $state = M ]] ; then
eject_target && continue # with next peer, if any
else
# maybe cman noticed before the handler triggered,
# and fencing is already active anyways.
if [[ $fence_tool_ls = *"victim now $id "* ]]; then
wait_for_id_to_drop_out_of_membership && continue # with next peer, if any
fi
fi
# apparently it was not in the member list.
# or we timed out waiting for fenced
debug "trying direct fence of $name"
dash_v=-v
[[ $DEBUG -gt 1 ]] && dash_v=-vv
echo "fence_node $dash_v $name"
if fence_node $dash_v $name ; then
echo "successfully fenced $name"
continue # with next peer, if any
else
die "fencing $name failed, giving up"
fi
done
# if we fenced more than one peer,
# add an other log line
[[ $peer != $DRBD_PEERS ]] &&
echo "SUCCESSFULLY FENCED $DRBD_PEERS"
exit 7
|