Remove development candidate files
This commit is contained in:
@@ -18,3 +18,6 @@ __pycache__/
|
|||||||
# Temporary files
|
# Temporary files
|
||||||
*.tmp
|
*.tmp
|
||||||
*.temp
|
*.temp
|
||||||
|
|
||||||
|
# Development candidate files
|
||||||
|
*.new
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
# 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}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# managed-containerd is authoritative for recurring monitor/status results.
|
|
||||||
# The legacy application monitor remains temporarily available as a shadow
|
|
||||||
# comparison only. Promotion scoring is still supplied by the existing
|
|
||||||
# application scoring hook during this migration stage.
|
|
||||||
mc_promotion_monitor()
|
|
||||||
{
|
|
||||||
local monitor_rc legacy_rc proxy_result proxy_rc cached_rc
|
|
||||||
|
|
||||||
proxy_result="$(mc_proxy_monitor)"
|
|
||||||
proxy_rc=$?
|
|
||||||
|
|
||||||
if [ "${proxy_rc}" -ne 0 ]; then
|
|
||||||
|
|
||||||
case "${proxy_rc}" in
|
|
||||||
"${MC_PROXY_RC_TRANSPORT}")
|
|
||||||
# Transport loss is a control-plane failure, not proof that
|
|
||||||
# the managed application itself has failed.
|
|
||||||
cached_rc="$(mc_proxy_cached_monitor 2>/dev/null)" || cached_rc=""
|
|
||||||
|
|
||||||
if [ -n "${cached_rc}" ]; then
|
|
||||||
monitor_rc="${cached_rc}"
|
|
||||||
|
|
||||||
case "${monitor_rc}" in
|
|
||||||
"${OCF_SUCCESS}")
|
|
||||||
# A standby with an unavailable control plane must
|
|
||||||
# never remain promotion-eligible during grace.
|
|
||||||
mc_promotion_score_set -INFINITY || true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"${OCF_RUNNING_PROMOTED}")
|
|
||||||
# Preserve the established primary during a brief
|
|
||||||
# bounded transport outage.
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
ocf_log warn \
|
|
||||||
"managed-containerd unavailable; using recent cached authoritative rc=${monitor_rc} for bounded control-plane grace"
|
|
||||||
|
|
||||||
mc_application_monitor
|
|
||||||
legacy_rc=$?
|
|
||||||
|
|
||||||
mc_proxy_legacy_shadow_monitor \
|
|
||||||
"${monitor_rc}" \
|
|
||||||
"${legacy_rc}" || true
|
|
||||||
|
|
||||||
return "${monitor_rc}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mc_promotion_score_set -INFINITY || true
|
|
||||||
ocf_exit_reason \
|
|
||||||
"managed-containerd authoritative monitor transport failure and no valid control-plane grace observation exists"
|
|
||||||
return "${OCF_ERR_GENERIC}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
# Protocol/result/configuration/client failures are hard.
|
|
||||||
# Never conceal them behind cached authoritative health.
|
|
||||||
mc_proxy_monitor_cache_clear || true
|
|
||||||
mc_promotion_score_set -INFINITY || true
|
|
||||||
ocf_exit_reason \
|
|
||||||
"managed-containerd authoritative monitor hard failure; cached grace is not permitted"
|
|
||||||
return "${OCF_ERR_GENERIC}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
monitor_rc="${proxy_result%%|*}"
|
|
||||||
|
|
||||||
case "${monitor_rc}" in
|
|
||||||
"${OCF_SUCCESS}"|\
|
|
||||||
"${OCF_ERR_GENERIC}"|\
|
|
||||||
"${OCF_NOT_RUNNING}"|\
|
|
||||||
"${OCF_RUNNING_PROMOTED}")
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
mc_promotion_score_set -INFINITY || true
|
|
||||||
ocf_exit_reason "managed-containerd returned invalid monitor rc ${monitor_rc}"
|
|
||||||
return "${OCF_ERR_GENERIC}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
ocf_log info "managed-containerd authoritative monitor: resource=${OCF_RESOURCE_INSTANCE:-unknown} proxy=${proxy_result}"
|
|
||||||
|
|
||||||
# Legacy PostgreSQL monitoring remains observational only.
|
|
||||||
mc_application_monitor
|
|
||||||
legacy_rc=$?
|
|
||||||
|
|
||||||
mc_proxy_legacy_shadow_monitor "${monitor_rc}" "${legacy_rc}" || true
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Any lifecycle mutation invalidates the previous daemon-observed role.
|
|
||||||
# A fresh daemon monitor must establish the next cache entry.
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
|
|
||||||
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_proxy_failure_grace_seconds:-15}" =~ ^(0|[1-9][0-9]{0,2})$ ]]; then
|
|
||||||
ocf_exit_reason "proxy_failure_grace_seconds must be an integer from 0 to 999 seconds"
|
|
||||||
return "${OCF_ERR_CONFIGURED}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${OCF_RESKEY_proxy_failure_grace_seconds:-15}" -gt 300 ]; then
|
|
||||||
ocf_exit_reason "proxy_failure_grace_seconds must be between 0 and 300 seconds"
|
|
||||||
return "${OCF_ERR_CONFIGURED}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,464 +0,0 @@
|
|||||||
# Generic managed-containerd client helpers.
|
|
||||||
# Shadow calls are observational only and must never affect OCF action results.
|
|
||||||
|
|
||||||
MC_PROXY_SOCKET_DEFAULT="/run/user/1000/managed-containerd/managed-containerd.sock"
|
|
||||||
|
|
||||||
mc_proxy_shadow_monitor()
|
|
||||||
{
|
|
||||||
local authoritative_rc="$1"
|
|
||||||
local socket_path="${MC_PROXY_SOCKET:-${MC_PROXY_SOCKET_DEFAULT}}"
|
|
||||||
local resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
||||||
local response
|
|
||||||
|
|
||||||
resource="${resource%%:*}"
|
|
||||||
|
|
||||||
[ -n "${resource}" ] || return 0
|
|
||||||
[ -S "${socket_path}" ] || return 0
|
|
||||||
command -v python3 >/dev/null 2>&1 || return 0
|
|
||||||
|
|
||||||
response="$(
|
|
||||||
timeout --kill-after=1s 3s python3 - "${socket_path}" "${resource}" "${authoritative_rc}" <<'PY'
|
|
||||||
import json
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
|
|
||||||
path, resource, authoritative_raw = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
||||||
authoritative_rc = int(authoritative_raw)
|
|
||||||
request = {"action": "monitor", "resource": resource}
|
|
||||||
|
|
||||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
||||||
s.settimeout(2.0)
|
|
||||||
s.connect(path)
|
|
||||||
s.sendall(json.dumps(request).encode("utf-8") + b"\n")
|
|
||||||
|
|
||||||
raw = b""
|
|
||||||
while b"\n" not in raw:
|
|
||||||
chunk = s.recv(65536)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
raw += chunk
|
|
||||||
s.close()
|
|
||||||
|
|
||||||
obj = json.loads(raw.decode("utf-8").strip())
|
|
||||||
if not obj.get("ok"):
|
|
||||||
print("ERROR|" + str(obj.get("error", "unknown proxy error")))
|
|
||||||
raise SystemExit(0)
|
|
||||||
|
|
||||||
result = obj.get("result") or {}
|
|
||||||
ha = result.get("ha") or {}
|
|
||||||
proxy_rc = ha.get("ocf_rc")
|
|
||||||
|
|
||||||
if proxy_rc is None:
|
|
||||||
comparison = "UNCLASSIFIED"
|
|
||||||
else:
|
|
||||||
comparison = "MATCH" if int(proxy_rc) == authoritative_rc else "MISMATCH"
|
|
||||||
|
|
||||||
receiver = result.get("wal_receiver") or {}
|
|
||||||
print("OK|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}".format(
|
|
||||||
comparison,
|
|
||||||
"null" if proxy_rc is None else proxy_rc,
|
|
||||||
ha.get("health", "unknown"),
|
|
||||||
ha.get("ha_role", "unknown"),
|
|
||||||
ha.get("state", "unknown"),
|
|
||||||
str(ha.get("promotion_eligible", "unknown")).lower(),
|
|
||||||
result.get("role", "unknown"),
|
|
||||||
str(result.get("streaming", "unknown")).lower(),
|
|
||||||
receiver.get("slot_name") or "",
|
|
||||||
receiver.get("sender_host") or "",
|
|
||||||
))
|
|
||||||
PY
|
|
||||||
)" || response="TRANSPORT_ERROR"
|
|
||||||
|
|
||||||
case "${response}" in
|
|
||||||
OK\|MATCH\|*)
|
|
||||||
ocf_log info \
|
|
||||||
"managed-containerd shadow monitor: resource=${resource} authoritative_rc=${authoritative_rc} proxy=${response}"
|
|
||||||
;;
|
|
||||||
OK\|UNCLASSIFIED\|*)
|
|
||||||
ocf_log warn \
|
|
||||||
"managed-containerd shadow monitor unclassified: resource=${resource} authoritative_rc=${authoritative_rc} proxy=${response}"
|
|
||||||
;;
|
|
||||||
OK\|MISMATCH\|*)
|
|
||||||
ocf_log err \
|
|
||||||
"managed-containerd shadow monitor MISMATCH: resource=${resource} authoritative_rc=${authoritative_rc} proxy=${response}"
|
|
||||||
;;
|
|
||||||
ERROR\|*)
|
|
||||||
ocf_log warn \
|
|
||||||
"managed-containerd shadow monitor failed: resource=${resource} authoritative_rc=${authoritative_rc} proxy=${response}"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
ocf_log warn \
|
|
||||||
"managed-containerd shadow monitor transport failure: resource=${resource} authoritative_rc=${authoritative_rc}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Query managed-containerd for the authoritative monitor result.
|
|
||||||
#
|
|
||||||
# stdout:
|
|
||||||
# <OCF rc>|<normalized diagnostic payload>
|
|
||||||
#
|
|
||||||
# return:
|
|
||||||
# 0 valid classified daemon result
|
|
||||||
# MC_PROXY_RC_TRANSPORT daemon transport/control-plane failure; bounded cache grace allowed
|
|
||||||
# MC_PROXY_RC_HARD_FAILURE configuration/protocol/result failure; fail closed, no cache grace
|
|
||||||
mc_proxy_monitor()
|
|
||||||
{
|
|
||||||
local socket_path="${MC_PROXY_SOCKET:-${MC_PROXY_SOCKET_DEFAULT}}"
|
|
||||||
local resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
||||||
local response rc
|
|
||||||
|
|
||||||
resource="${resource%%:*}"
|
|
||||||
|
|
||||||
if [ -z "${resource}" ]; then
|
|
||||||
ocf_log err \
|
|
||||||
"managed-containerd monitor requires OCF_RESOURCE_INSTANCE"
|
|
||||||
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -S "${socket_path}" ]; then
|
|
||||||
ocf_log warn \
|
|
||||||
"managed-containerd socket is unavailable: ${socket_path}"
|
|
||||||
return "${MC_PROXY_RC_TRANSPORT}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v python3 >/dev/null 2>&1; then
|
|
||||||
ocf_log err \
|
|
||||||
"python3 is required for managed-containerd monitor transport"
|
|
||||||
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
response="$(
|
|
||||||
timeout --kill-after=1s 3s \
|
|
||||||
python3 - \
|
|
||||||
"${socket_path}" \
|
|
||||||
"${resource}" \
|
|
||||||
"${MC_PROXY_RC_TRANSPORT}" \
|
|
||||||
"${MC_PROXY_RC_HARD_FAILURE}" <<'PY'
|
|
||||||
import json
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
|
|
||||||
path, resource = sys.argv[1], sys.argv[2]
|
|
||||||
TRANSPORT_FAILURE = int(sys.argv[3])
|
|
||||||
PROTOCOL_FAILURE = int(sys.argv[4])
|
|
||||||
|
|
||||||
request = {
|
|
||||||
"action": "monitor",
|
|
||||||
"resource": resource,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
||||||
s.settimeout(2.0)
|
|
||||||
|
|
||||||
try:
|
|
||||||
s.connect(path)
|
|
||||||
s.sendall(json.dumps(request).encode("utf-8") + b"\n")
|
|
||||||
|
|
||||||
raw = b""
|
|
||||||
|
|
||||||
while b"\n" not in raw:
|
|
||||||
chunk = s.recv(65536)
|
|
||||||
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
|
|
||||||
raw += chunk
|
|
||||||
finally:
|
|
||||||
s.close()
|
|
||||||
|
|
||||||
except (socket.timeout, ConnectionError, OSError):
|
|
||||||
# Local daemon/control-plane reachability failure.
|
|
||||||
# Recent authoritative state may be used for bounded grace.
|
|
||||||
raise SystemExit(TRANSPORT_FAILURE)
|
|
||||||
|
|
||||||
# Transport succeeded. Invalid daemon output is therefore a
|
|
||||||
# protocol/result failure and must never be hidden by cached health.
|
|
||||||
if not raw:
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
try:
|
|
||||||
obj = json.loads(raw.decode("utf-8").strip())
|
|
||||||
except (UnicodeDecodeError, json.JSONDecodeError):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
if not isinstance(obj, dict):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
if not obj.get("ok"):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
result = obj.get("result")
|
|
||||||
if not isinstance(result, dict):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
ha = result.get("ha")
|
|
||||||
if not isinstance(ha, dict):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
rc = ha.get("ocf_rc")
|
|
||||||
|
|
||||||
try:
|
|
||||||
rc = int(rc)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
if rc not in (0, 1, 7, 8):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
receiver = result.get("wal_receiver") or {}
|
|
||||||
|
|
||||||
if not isinstance(receiver, dict):
|
|
||||||
raise SystemExit(PROTOCOL_FAILURE)
|
|
||||||
|
|
||||||
payload = "|".join([
|
|
||||||
str(rc),
|
|
||||||
str(ha.get("health", "unknown")),
|
|
||||||
str(ha.get("ha_role", "unknown")),
|
|
||||||
str(ha.get("state", "unknown")),
|
|
||||||
str(ha.get("promotion_eligible", "unknown")).lower(),
|
|
||||||
str(result.get("role", "unknown")),
|
|
||||||
str(result.get("streaming", "unknown")).lower(),
|
|
||||||
str(receiver.get("slot_name") or ""),
|
|
||||||
str(receiver.get("sender_host") or ""),
|
|
||||||
])
|
|
||||||
|
|
||||||
print(payload)
|
|
||||||
PY
|
|
||||||
)"
|
|
||||||
rc=$?
|
|
||||||
|
|
||||||
case "${rc}" in
|
|
||||||
0)
|
|
||||||
;;
|
|
||||||
|
|
||||||
"${MC_PROXY_RC_TRANSPORT}"|124|137)
|
|
||||||
# MC_PROXY_RC_TRANSPORT = classified Python transport failure.
|
|
||||||
# 124/137 = timeout(1) expiry / forced termination.
|
|
||||||
ocf_log warn \
|
|
||||||
"managed-containerd authoritative monitor transport failure"
|
|
||||||
return "${MC_PROXY_RC_TRANSPORT}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"${MC_PROXY_RC_HARD_FAILURE}")
|
|
||||||
ocf_log err \
|
|
||||||
"managed-containerd authoritative monitor protocol or result failure"
|
|
||||||
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
ocf_log err \
|
|
||||||
"managed-containerd authoritative monitor client failure rc=${rc}"
|
|
||||||
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -z "${response}" ]; then
|
|
||||||
ocf_log err \
|
|
||||||
"managed-containerd authoritative monitor returned an empty result"
|
|
||||||
return "${MC_PROXY_RC_HARD_FAILURE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
rc="${response%%|*}"
|
|
||||||
|
|
||||||
# Cache only healthy daemon-authoritative role observations. An
|
|
||||||
# application-level failure automatically invalidates any previous cache.
|
|
||||||
mc_proxy_monitor_cache_write "${rc}" || {
|
|
||||||
ocf_log warn \
|
|
||||||
"Unable to update managed-containerd authoritative monitor cache"
|
|
||||||
}
|
|
||||||
|
|
||||||
printf '%s\n' "${response}"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Compare the legacy application monitor against a daemon-authoritative rc.
|
|
||||||
# The legacy result is diagnostic only and can never change the returned
|
|
||||||
# Pacemaker monitor status.
|
|
||||||
mc_proxy_legacy_shadow_monitor()
|
|
||||||
{
|
|
||||||
local authoritative_rc="$1"
|
|
||||||
local legacy_rc="$2"
|
|
||||||
local resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
||||||
|
|
||||||
resource="${resource%%:*}"
|
|
||||||
|
|
||||||
if [ "${legacy_rc}" -eq "${authoritative_rc}" ]; then
|
|
||||||
ocf_log info \
|
|
||||||
"legacy application shadow monitor: resource=${resource} authoritative_rc=${authoritative_rc} legacy_rc=${legacy_rc} comparison=MATCH"
|
|
||||||
else
|
|
||||||
ocf_log err \
|
|
||||||
"legacy application shadow monitor MISMATCH: resource=${resource} authoritative_rc=${authoritative_rc} legacy_rc=${legacy_rc}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Generic short-lived cache of the last healthy daemon-authoritative OCF role.
|
|
||||||
# This is control-plane state only; it contains no application-specific data.
|
|
||||||
#
|
|
||||||
# Use HA_RSCTMP when available because it is the standard resource-agent
|
|
||||||
# runtime location and is preferable to inventing a persistent filesystem path.
|
|
||||||
MC_PROXY_STATE_DIR_DEFAULT="${HA_RSCTMP:-/run/resource-agents}/managed-container"
|
|
||||||
|
|
||||||
|
|
||||||
mc_proxy_state_file()
|
|
||||||
{
|
|
||||||
local resource safe_resource
|
|
||||||
resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
||||||
resource="${resource%%:*}"
|
|
||||||
|
|
||||||
[ -n "${resource}" ] || return 1
|
|
||||||
|
|
||||||
safe_resource="$(
|
|
||||||
printf '%s' "${resource}" |
|
|
||||||
tr -c 'A-Za-z0-9_.-' '_'
|
|
||||||
)"
|
|
||||||
|
|
||||||
printf '%s/%s.monitor\n' \
|
|
||||||
"${MC_PROXY_STATE_DIR:-${MC_PROXY_STATE_DIR_DEFAULT}}" \
|
|
||||||
"${safe_resource}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mc_proxy_monitor_cache_clear()
|
|
||||||
{
|
|
||||||
local state_file
|
|
||||||
|
|
||||||
state_file="$(mc_proxy_state_file)" || return 0
|
|
||||||
rm -f "${state_file}" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mc_proxy_monitor_cache_write()
|
|
||||||
{
|
|
||||||
local rc="$1"
|
|
||||||
local state_file state_dir tmp now
|
|
||||||
|
|
||||||
# Only healthy authoritative role observations are eligible for
|
|
||||||
# temporary control-plane grace.
|
|
||||||
case "${rc}" in
|
|
||||||
"${OCF_SUCCESS}"|"${OCF_RUNNING_PROMOTED}")
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
state_file="$(mc_proxy_state_file)" || return 1
|
|
||||||
state_dir="${state_file%/*}"
|
|
||||||
|
|
||||||
mkdir -p -m 700 "${state_dir}" || return 1
|
|
||||||
|
|
||||||
now="$(date +%s)"
|
|
||||||
tmp="${state_file}.$$"
|
|
||||||
|
|
||||||
umask 077
|
|
||||||
printf '%s|%s|0
|
|
||||||
' "${now}" "${rc}" > "${tmp}" || {
|
|
||||||
rm -f "${tmp}" 2>/dev/null || true
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
chmod 600 "${tmp}" 2>/dev/null || true
|
|
||||||
mv -f "${tmp}" "${state_file}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Print a cached healthy OCF rc when its observation is still inside the
|
|
||||||
# configured daemon control-plane failure grace.
|
|
||||||
mc_proxy_cached_monitor()
|
|
||||||
{
|
|
||||||
local state_file observed rc failure_started
|
|
||||||
local now failure_age observation_age grace max_observation_age
|
|
||||||
local tmp
|
|
||||||
|
|
||||||
grace="${OCF_RESKEY_proxy_failure_grace_seconds:-15}"
|
|
||||||
|
|
||||||
case "${grace}" in
|
|
||||||
""|*[!0-9]*)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ "${grace}" -gt 0 ] || return 1
|
|
||||||
|
|
||||||
state_file="$(mc_proxy_state_file)" || return 1
|
|
||||||
[ -f "${state_file}" ] || return 1
|
|
||||||
|
|
||||||
IFS='|' read -r observed rc failure_started < "${state_file}" || {
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
case "${observed}" in
|
|
||||||
""|*[!0-9]*)
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "${failure_started}" in
|
|
||||||
""|*[!0-9]*)
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "${rc}" in
|
|
||||||
"${OCF_SUCCESS}"|"${OCF_RUNNING_PROMOTED}")
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
now="$(date +%s)"
|
|
||||||
observation_age=$((now - observed))
|
|
||||||
|
|
||||||
# A cached role must come from a reasonably recent successful daemon
|
|
||||||
# observation. This protects against resurrecting stale runtime state.
|
|
||||||
max_observation_age=$((grace + 60))
|
|
||||||
|
|
||||||
if [ "${observation_age}" -lt 0 ] ||
|
|
||||||
[ "${observation_age}" -gt "${max_observation_age}" ]; then
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# First failed daemon observation starts the bounded grace period.
|
|
||||||
if [ "${failure_started}" -eq 0 ]; then
|
|
||||||
failure_started="${now}"
|
|
||||||
tmp="${state_file}.$$"
|
|
||||||
|
|
||||||
umask 077
|
|
||||||
printf '%s|%s|%s
|
|
||||||
' "${observed}" "${rc}" "${failure_started}" > "${tmp}" || {
|
|
||||||
rm -f "${tmp}" 2>/dev/null || true
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
chmod 600 "${tmp}" 2>/dev/null || true
|
|
||||||
mv -f "${tmp}" "${state_file}" || return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
failure_age=$((now - failure_started))
|
|
||||||
|
|
||||||
if [ "${failure_age}" -lt 0 ] ||
|
|
||||||
[ "${failure_age}" -ge "${grace}" ]; then
|
|
||||||
mc_proxy_monitor_cache_clear
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf '%s
|
|
||||||
' "${rc}"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# OCF Resource Agent:
|
|
||||||
# loxthelion:managed-container
|
|
||||||
#
|
|
||||||
# v0.4
|
|
||||||
# Container lifecycle: systemd user Quadlet
|
|
||||||
# Application profile: PostgreSQL
|
|
||||||
#
|
|
||||||
# Version 0.4 adds explicit PostgreSQL demotion scaffolding.
|
|
||||||
# Healthy primaries report OCF_RUNNING_PROMOTED while healthy standbys
|
|
||||||
# report OCF_SUCCESS.
|
|
||||||
|
|
||||||
: "${OCF_ROOT:=/usr/lib/ocf}"
|
|
||||||
OCF_FUNCTIONS_DIR="${OCF_ROOT}/lib/heartbeat"
|
|
||||||
# Preserve explicit resource identity before ocf-shellfuncs supplies its
|
|
||||||
# standalone-invocation default; never publish a score for that dummy resource.
|
|
||||||
MC_PROMOTION_RESOURCE_INSTANCE="${OCF_RESOURCE_INSTANCE:-}"
|
|
||||||
. "${OCF_FUNCTIONS_DIR}/ocf-shellfuncs"
|
|
||||||
|
|
||||||
# Pacemaker uses "Promoted" terminology, while many OCF shell libraries
|
|
||||||
# still expose the historical OCF_RUNNING_MASTER constant for return code 8.
|
|
||||||
# Normalize both forms here so the agent remains portable.
|
|
||||||
: "${OCF_RUNNING_PROMOTED:=${OCF_RUNNING_MASTER:-8}}"
|
|
||||||
|
|
||||||
# Internal managed-containerd client return classes.
|
|
||||||
# Private RA implementation statuses, not OCF action return codes.
|
|
||||||
readonly MC_PROXY_RC_TRANSPORT=10
|
|
||||||
readonly MC_PROXY_RC_HARD_FAILURE=11
|
|
||||||
|
|
||||||
|
|
||||||
# Resolve modules relative to this entrypoint, independent of Pacemaker's cwd.
|
|
||||||
MC_MODULE_DIR="$(CDPATH= cd -- "$(command dirname -- "${BASH_SOURCE[0]}")" && pwd)/managed-container.d"
|
|
||||||
for MC_MODULE in common container engine; do
|
|
||||||
if ! . "${MC_MODULE_DIR}/${MC_MODULE}.sh"; then
|
|
||||||
ocf_exit_reason "Unable to load resource-agent module ${MC_MODULE_DIR}/${MC_MODULE}.sh"
|
|
||||||
exit "${OCF_ERR_CONFIGURED}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
unset MC_MODULE
|
|
||||||
mc_engine_load || exit $?
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
meta-data)
|
|
||||||
mc_engine_metadata
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
validate-all)
|
|
||||||
mc_promotion_validate
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
monitor|status)
|
|
||||||
mc_promotion_monitor
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
start)
|
|
||||||
mc_promotion_action start
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
stop)
|
|
||||||
mc_promotion_action stop
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
promote)
|
|
||||||
mc_promotion_action promote
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
demote)
|
|
||||||
mc_promotion_action demote
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
notify)
|
|
||||||
mc_application_notify
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
rejoin)
|
|
||||||
mc_promotion_action rejoin
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 {start|stop|promote|demote|notify|rejoin|monitor|status|validate-all|meta-data}"
|
|
||||||
exit "${OCF_ERR_UNIMPLEMENTED}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
Reference in New Issue
Block a user