# Shared OCF scoring wrappers; application policy is supplied by the plugin. # 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_application_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 mc_application_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 mc_application_action "${action}" action_rc=$? if [ "${action_rc}" -eq "${OCF_SUCCESS}" ] && [ "${action}" != stop ]; then mc_application_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 mc_application_validate }