blob: d79703946241c16a256319f9d14bbc414f59f308 [file] [log] [blame]
#!/bin/bash
# ^^ for better syntax highlighting
# to be sourced from ocf:linbit:drbd and crm-fence-peer.sh, maybe others.
_sh_status_process() {
# _volume not present should not happen,
# but may help make this agent work even if it talks to drbd 8.3.
: ${_volume:=0}
# not-yet-created volumes are reported as -1
(( _volume >= 0 )) || _volume=$[1 << 16]
DRBD_ROLE_LOCAL[$_volume]=${_role:-Unconfigured}
DRBD_ROLE_REMOTE[$_volume]=${_peer:-Unknown}
DRBD_CSTATE[$_volume]=$_cstate
DRBD_DSTATE_LOCAL[$_volume]=${_disk:-Unconfigured}
DRBD_DSTATE_REMOTE[$_volume]=${_pdsk:-DUnknown}
}
_drbd_set_status_variables_from_sh_status() {
DRBD_ROLE_LOCAL=()
DRBD_ROLE_REMOTE=()
DRBD_CSTATE=()
DRBD_DSTATE_LOCAL=()
DRBD_DSTATE_REMOTE=()
# drbdsetup sh-status prints these values to stdout,
# and then prints _sh_status_process.
#
# if we eval that, we do not need several drbdadm/drbdsetup commands
# to figure out the various aspects of the state.
local _minor _res_name _known _cstate _role _peer _disk _pdsk
local _volume
local _flags_susp _flags_aftr_isp _flags_peer_isp _flags_user_isp
local _resynced_percent
local out
if $DRBD_HAS_MULTI_VOLUME ; then
out="$($DRBDSETUP sh-status "$DRBD_RESOURCE")" && eval "$out"
else
# without "MULTI_VOLUME", the DRBD_DEVICES array
# should contain exactly one value
out="$($DRBDSETUP "$DRBD_DEVICES" sh-status)" && eval "$out"
fi
# if there was no output at all, or a weird output
# make sure the status arrays won't be empty.
[[ ${#DRBD_ROLE_LOCAL[@]} != 0 ]] || DRBD_ROLE_LOCAL=(Unconfigured)
[[ ${#DRBD_ROLE_REMOTE[@]} != 0 ]] || DRBD_ROLE_REMOTE=(Unknown)
[[ ${#DRBD_CSTATE[@]} != 0 ]] || DRBD_CSTATE=(Unconfigured)
[[ ${#DRBD_DSTATE_LOCAL[@]} != 0 ]] || DRBD_DSTATE_LOCAL=(Unconfigured)
[[ ${#DRBD_DSTATE_REMOTE[@]} != 0 ]] || DRBD_DSTATE_REMOTE=(DUnknown)
: == DEBUG == DRBD_ROLE_LOCAL == "${DRBD_ROLE_LOCAL[@]}" ==
: == DEBUG == DRBD_ROLE_REMOTE == "${DRBD_ROLE_REMOTE[@]}" ==
: == DEBUG == DRBD_CSTATE == "${DRBD_CSTATE[@]}" ==
: == DEBUG == DRBD_DSTATE_LOCAL == "${DRBD_DSTATE_LOCAL[@]}" ==
: == DEBUG == DRBD_DSTATE_REMOTE == "${DRBD_DSTATE_REMOTE[@]}" ==
status_primary=false
status_unconfigured=false
case ${DRBD_ROLE_LOCAL[*]} in
*Primary*) status_primary=true ;;
*Secondary*) : "at least it is configured" ;;
*) status_unconfigured=true ;;
esac
local tmp
status_pdsk_all_up_to_date=true
status_pdsk_any_unknown=false
for tmp in ${DRBD_DSTATE_REMOTE[*]}; do
: $tmp
case $tmp in
UpToDate) continue ;;
DUnknown) status_pdsk_all_up_to_date=false
status_pdsk_any_unknown=true
break
;;
*) status_pdsk_all_up_to_date=false
esac
done
status_disk_all_up_to_date=true
status_disk_all_consistent=true
status_disk_transitional_state=false
for tmp in ${DRBD_DSTATE_LOCAL[*]}; do
: $tmp
case $tmp in
UpToDate) continue ;;
Diskless|Failed|Inconsistent|Outdated)
status_disk_all_consistent=false ;;
Attaching|Negotiating)
status_disk_transitional_state=true ;;
esac
status_disk_all_up_to_date=false
done
status_some_peer_all_up_to_date=$status_pdsk_all_up_to_date
status_some_peer_any_unknown=$status_pdsk_any_unknown
$status_pdsk_all_up_to_date \
&& status_some_peer_not_all_up_to_date=false \
|| status_some_peer_not_all_up_to_date=true
}
# Note:
# using ":" builtin WITH SIDE EFFECTS of numerical expansion of arguments,
# because I find it convenient for debugging when running under set -x.
#
_drbd_set_status_variables_from_events2()
{
local up_to_date_mask=0
local line action object resname kv k v
local OUT
local IFS=$'\n'
OUT=( $(drbdsetup events2 --now $DRBD_RESOURCE) ) || return
IFS=$' \t\n'
local _id _vol
local _n_connections=0 _n_volumes=0 _n_diskless_client=0
local _role=Unconfigured
local _peer_ids=() _peer_role=() _conn_name=() _cstate=()
local _disk_all_up_to_date=true
local _disk_all_consistent=true
local _disk_transitional_state=false
local _pdsk_n_up_to_date=()
local _pdsk_n_diskless_client=()
local _pdsk_n_known=()
DRBD_disk=()
for line in "${OUT[@]}"; do
: === DEBUG === line == $line ===
set -- $line
[[ $1 = exists ]] || continue
[[ $2 = - ]] && break
object=$2
[[ $3 = name:$DRBD_RESOURCE ]] || continue
shift 3
case $object in
resource)
for kv ; do
[[ $kv = role:* ]] && { _role=${kv#*:} ; break ; }
done
;;
connection)
: _n_connections = $(( _n_connections+=1 ))
_id="-"
for kv ; do
v=${kv#*:}
case $kv in
peer-node-id:*) _id=$v; _peer_ids[_id]=$v ;;
# connection:*) _cstate[_id]=$v ;;
# conn-name:*) _conn_name[_id]=$v ;;
role:*) _peer_role[_id]=$v ;;
esac
done
_pdsk_n_diskless_client[_id]=0
_pdsk_n_up_to_date[_id]=0
_pdsk_n_known[_id]=0
;;
device)
: _n_volumes = $(( _n_volumes+=1 ))
# _vol="-"
if [[ "$*" = *" disk:Diskless"*" client:yes"* ]]; then
: _n_diskless_client = $(( _n_diskless_client+=1 ))
DRBD_disk+=(DisklessClient) # used in informational log lines later
else
for kv ; do
v=${kv#*:}
case $kv in
# volume:*) _vol=$v; _volume_ids[_vol]=$v ;;
# uninteresting, but if you want it,
# remember to add it to "local" as well
# minor:*) _minor[_vol]=$v ;;
disk:*)
DRBD_disk+=($v)
case $v in
UpToDate) :;;
Diskless|Failed|Inconsistent|Outdated|Detaching)
_disk_all_consistent=false
_disk_all_up_to_date=false
;;
Attaching|Negotiating)
_disk_transitional_state=true ;;
Consistent)
_disk_all_up_to_date=false ;;
# DUnknown: impossible for local disk.
esac
;;
esac
done
fi
;;
peer-device)
_id="-" _vol="-"
for kv ; do
v=${kv#*:}
case $kv in
peer-node-id:*) _id=$v ;;
volume:*) _vol=$v ;;
peer-client:yes)
: _pdsk_n_diskless_client[$_id] = $(( _pdsk_n_diskless_client[_id] += 1 )) ;;
peer-disk:*)
case $v in
UpToDate)
: _pdsk_n_up_to_date[$_id] = $(( _pdsk_n_up_to_date[_id] += 1 ))
: _pdsk_n_known[$_id] = $(( _pdsk_n_known[_id] += 1 )) ;;
DUnknown) : ;;
*) : _pdsk_n_known[$_id] = $(( _pdsk_n_known[_id] += 1 )) ;;
esac
esac
done
;;
esac
done
status_primary=false
status_unconfigured=false
case $_role in
Primary) status_primary=true ;;
Unconfigured) status_unconfigured=true ;;
esac
status_disk_all_up_to_date=$_disk_all_up_to_date
status_disk_all_consistent=$_disk_all_consistent
status_disk_transitional_state=$_disk_transitional_state
status_some_peer_all_up_to_date=false
status_some_peer_not_all_up_to_date=false
status_some_peer_any_unknown=false
$status_disk_all_up_to_date \
&& : up_to_date_mask = $(( up_to_date_mask |= (1 << DRBD_MY_NODE_ID) ))
: _n_volumes=$_n_volumes
: _n_diskless_client=$_n_diskless_client
(( _n_volumes == _n_diskless_client )) && status_diskless_client=true || status_diskless_client=false
for _id in ${_peer_ids[*]}; do
: "_pdsk_n_up_to_date[$_id]=${_pdsk_n_up_to_date[_id]}"
: "_pdsk_n_known[$_id]=${_pdsk_n_known[_id]}"
: "_pdsk_n_diskless_client[$_id]=${_pdsk_n_diskless_client[_id]}"
# if this peer is a diskless client,
# ignore it for determining status_some_peer*up_to_date
if : "diskless client?"; (( _n_volumes == _pdsk_n_diskless_client[_id] )) ; then
# TODO decide on bits of diskless clients
if $status_disk_all_up_to_date && (( _n_volumes == _pdsk_n_known[_id] )) ; then
: up_to_date_mask = $(( up_to_date_mask |= (1 << _id) ))
fi
# skip "status_pdsk_any_unknown" check for diskless clients.
continue
elif : "all up to date?"; (( _n_volumes == _pdsk_n_up_to_date[_id] )) ; then
: up_to_date_mask = $(( up_to_date_mask |= (1 << _id) ))
status_some_peer_all_up_to_date=true
else
status_some_peer_not_all_up_to_date=true
fi
: "any unknown?";
(( _n_volumes != _pdsk_n_known[_id] )) && status_pdsk_any_unknown=true
done
: status_some_peer_all_up_to_date=$status_some_peer_all_up_to_date
: status_some_peer_not_all_up_to_date=$status_some_peer_not_all_up_to_date
: status_some_peer_any_unknown=$status_some_peer_any_unknown
if $status_some_peer_all_up_to_date &&
! $status_some_peer_not_all_up_to_date &&
! $status_some_peer_any_unknown ; then
status_pdsk_all_up_to_date=true
else
status_pdsk_all_up_to_date=false
fi
FAKE_UP_TO_DATE_NODE_MASK=$(printf "0x%x" $up_to_date_mask)
}
# What we really are interested in is
# Are we actively being used (aka Primary)?
# Do we have access to good _local_ data?
# What do we know about the remote data, is it as good as ours, better,
# worse, or could it potentially be better -- but we don't know?
#
drbd_set_status_variables()
{
# "return" values:
status_primary=false
status_diskless_client=false
# single peer, respectively aggregated
status_pdsk_all_up_to_date=false
status_pdsk_any_unknown=false
# slightly more detail for multiple peers
status_some_peer_any_unknown=false
status_some_peer_all_up_to_date=false
status_some_peer_not_all_up_to_date=false
status_disk_all_up_to_date=false
status_disk_all_consistent=false
status_disk_transitional_state=false
# maybe we can use it to "unfence"
# a "stale" fencing constraint.
FAKE_UP_TO_DATE_NODE_MASK=""
if [[ $DRBD_HAS_EVENTS2 = true ]] ; then
# role is per resource (scalar)
_drbd_set_status_variables_from_events2
else
# role is still per volume (array)
_drbd_set_status_variables_from_sh_status
fi
: == DEBUG == status_primary == $status_primary ==
: == DEBUG == status_diskless_client == $status_diskless_client ==
: == DEBUG == status_disk_all_up_to_date == $status_disk_all_up_to_date ==
: == DEBUG == status_disk_all_consistent == $status_disk_all_consistent ==
: == DEBUG == status_disk_transitional_state == $status_disk_transitional_state ==
: == DEBUG == status_pdsk_all_up_to_date == $status_pdsk_all_up_to_date ==
: == DEBUG == status_pdsk_any_unknown == $status_pdsk_any_unknown ==
: == DEBUG == status_some_peer_any_unknown == $status_some_peer_any_unknown ==
: == DEBUG == status_some_peer_all_up_to_date == $status_some_peer_all_up_to_date ==
: == DEBUG == status_some_peer_not_all_up_to_date == $status_some_peer_not_all_up_to_date ==
}