Freeze HA scoring and document reported production failover

This commit is contained in:
Lyncoln
2026-09-12 18:07:28 -04:00
parent c8636ed6d4
commit 488a1672d7
29 changed files with 2285 additions and 46 deletions
+123
View File
@@ -225,3 +225,126 @@ validate_all()
return "${OCF_SUCCESS}"
}
# Promotion scores are scoped by Pacemaker to OCF_RESOURCE_INSTANCE and the
# executing node. Do not construct a legacy attribute name or cache locally:
# querying attrd on every observation also repairs a lost transient attribute.
mc_promotion_score_set()
{
local score="$1" current
local resource="${MC_PROMOTION_RESOURCE_INSTANCE:-}"
if [ -z "${resource}" ]; then
ocf_log warn "Cannot publish promotion score without OCF_RESOURCE_INSTANCE"
return 1
fi
case "${score}" in
1000|100|50|-INFINITY) ;;
*) return 1 ;;
esac
current="$(
timeout --kill-after=1s 2s crm_attribute \
--promotion="${resource}" --lifetime=reboot \
--query --quiet 2>/dev/null
)" || current=""
[ "${current}" = "${score}" ] && return 0
if ! timeout --kill-after=1s 2s crm_attribute \
--promotion="${resource}" --lifetime=reboot \
--update="${score}" >/dev/null 2>&1
then
ocf_log err "Unable to publish promotion score ${score} for ${resource}; prior score may remain"
return 1
fi
return 0
}
mc_promotion_score_from_monitor()
{
local monitor_rc="$1" score=-INFINITY
case "${monitor_rc}" in
"${OCF_RUNNING_PROMOTED}") score=1000 ;;
"${OCF_SUCCESS}")
score="$(mc_pg_standby_score)" || score=-INFINITY
;;
esac
case "${score}" in
1000|100|50|-INFINITY) ;;
*) score=-INFINITY ;;
esac
mc_promotion_score_set "${score}"
}
# Score publication must not change OCF role detection or its return code.
mc_promotion_monitor()
{
local monitor_rc
managed_monitor
monitor_rc=$?
mc_promotion_score_from_monitor "${monitor_rc}" || true
return "${monitor_rc}"
}
# Lifecycle functions retain their original checks, commands and return codes.
# Withdraw the previous score before any transition (including validation
# failure), then reassess only after success. Their internal monitor calls do
# not publish scores, so they cannot restore a positive score mid-transition.
mc_promotion_action()
{
local action="$1" action_rc monitor_rc
case "${action}" in
start|stop|promote|demote|rejoin) ;;
*) return "${OCF_ERR_UNIMPLEMENTED}" ;;
esac
mc_promotion_score_set -INFINITY || true
if [ "${action}" != stop ]; then
mc_promotion_validate
action_rc=$?
[ "${action_rc}" -eq "${OCF_SUCCESS}" ] || return "${action_rc}"
fi
"managed_${action}"
action_rc=$?
if [ "${action_rc}" -eq "${OCF_SUCCESS}" ] && [ "${action}" != stop ]; then
managed_monitor
monitor_rc=$?
mc_promotion_score_from_monitor "${monitor_rc}" || true
fi
return "${action_rc}"
}
mc_promotion_validate()
{
local utility
if ! [[ "${OCF_RESKEY_disconnected_max_age:-300}" =~ ^(0|[1-9][0-9]{0,4})$ ]]; then
ocf_exit_reason "disconnected_max_age must be an integer from 0 to 99999 seconds"
return "${OCF_ERR_CONFIGURED}"
fi
for utility in crm_attribute timeout python3; do
if ! command -v "${utility}" >/dev/null 2>&1; then
ocf_exit_reason "Required promotion scoring command '${utility}' is not installed"
return "${OCF_ERR_INSTALLED}"
fi
done
validate_all
}
# Keep the original metadata function intact; advertise the added policy here.
mc_promotion_metadata()
{
meta_data | sed '/<\/parameters>/i\
<parameter name="disconnected_max_age" unique="0" required="0">\
<longdesc lang="en">Maximum age in seconds of a verified streaming observation for disconnected promotion eligibility. Default 300; 0 disables disconnected eligibility. Requires fencing managed by Pacemaker. PostgreSQL restart requires fresh streaming verification.</longdesc>\
<shortdesc lang="en">Disconnected standby observation lifetime</shortdesc>\
<content type="integer" default="300"/>\
</parameter>'
}