244 lines
7.8 KiB
Bash
Executable File
244 lines
7.8 KiB
Bash
Executable File
# Sourced by managed-container; uses the shared OCF and parameter environment.
|
|
|
|
mc_pg_notify_target_daemon_shadow()
|
|
{
|
|
local expected_node="$1"
|
|
local expected_successor="$2"
|
|
local socket_path resource response rc
|
|
|
|
socket_path="${MC_PROXY_SOCKET:-/run/user/1000/managed-containerd/managed-containerd.sock}"
|
|
resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
resource="${resource%%:*}"
|
|
|
|
[ -n "${resource}" ] || return 0
|
|
|
|
response="$(
|
|
timeout --kill-after=1s 3s \
|
|
python3 \
|
|
"${MC_PROXY_CLIENT:-/usr/lib/ocf/resource.d/loxthelion/managed-container.d/proxy_client.py}" \
|
|
"${socket_path}" \
|
|
"${resource}" \
|
|
"notify.target" \
|
|
10 \
|
|
11
|
|
)"
|
|
rc=$?
|
|
|
|
if [ "${rc}" -ne 0 ]; then
|
|
ocf_log warn \
|
|
"managed-containerd notify.target shadow unavailable rc=${rc}"
|
|
return 0
|
|
fi
|
|
|
|
if MC_DAEMON_RESPONSE="${response}" \
|
|
MC_EXPECTED_NODE="${expected_node}" \
|
|
MC_EXPECTED_SUCCESSOR="${expected_successor}" \
|
|
python3 -c '
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
try:
|
|
response = json.loads(os.environ["MC_DAEMON_RESPONSE"])
|
|
except (KeyError, TypeError, json.JSONDecodeError):
|
|
raise SystemExit(1)
|
|
|
|
if (
|
|
response.get("ocf_rc") != 0
|
|
or response.get("target") is not True
|
|
or response.get("target_node", "").lower()
|
|
!= os.environ["MC_EXPECTED_NODE"].lower()
|
|
or response.get("successor_node", "").lower()
|
|
!= os.environ["MC_EXPECTED_SUCCESSOR"].lower()
|
|
):
|
|
raise SystemExit(1)
|
|
'; then
|
|
ocf_log info \
|
|
"managed-containerd notify.target shadow: comparison=MATCH target=${expected_node} successor=${expected_successor}"
|
|
else
|
|
ocf_log err \
|
|
"managed-containerd notify.target shadow MISMATCH: expected_target=${expected_node} expected_successor=${expected_successor} response=${response}"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
mc_pg_demote_notification_target()
|
|
{
|
|
local allow_self="${1:-false}"
|
|
local state_file
|
|
local type operation promote_uname demote_uname local_node
|
|
local now mtime age
|
|
local promote_norm local_norm demote_norm
|
|
|
|
state_file="/run/loxthelion/${OCF_RESOURCE_INSTANCE:-managed-container}.notify"
|
|
local_node="${OCF_RESKEY_CRM_meta_on_node:-$(hostname)}"
|
|
|
|
[ -r "${state_file}" ] || return 1
|
|
|
|
now="$(date +%s)" || return 1
|
|
mtime="$(stat -c %Y "${state_file}" 2>/dev/null)" || return 1
|
|
age=$((now - mtime))
|
|
|
|
[ "${age}" -ge 0 ] || return 1
|
|
[ "${age}" -le 120 ] || return 1
|
|
|
|
type="$(sed -n 's/^type=//p' "${state_file}" | head -n1)"
|
|
operation="$(sed -n 's/^operation=//p' "${state_file}" | head -n1)"
|
|
promote_uname="$(sed -n 's/^promote_uname=//p' "${state_file}" | head -n1)"
|
|
demote_uname="$(sed -n 's/^demote_uname=//p' "${state_file}" | head -n1)"
|
|
|
|
[ "${type}" = "pre" ] || return 1
|
|
[ "${operation}" = "demote" ] || return 1
|
|
|
|
local_norm="$(printf '%s' "${local_node}" | tr '[:upper:]' '[:lower:]')"
|
|
demote_norm="$(printf '%s' "${demote_uname}" | tr '[:upper:]' '[:lower:]')"
|
|
promote_norm="$(printf '%s' "${promote_uname}" | tr '[:upper:]' '[:lower:]')"
|
|
|
|
[ "${demote_norm}" = "${local_norm}" ] || return 1
|
|
[ -n "${promote_uname}" ] || return 1
|
|
|
|
case "${promote_uname}" in
|
|
*" "*) return 1 ;;
|
|
esac
|
|
|
|
if [ "${allow_self}" != true ] && [ "${promote_norm}" = "${local_norm}" ]; then
|
|
return 1
|
|
fi
|
|
|
|
mc_pg_notify_target_daemon_shadow "${local_node}" "${promote_uname}"
|
|
|
|
printf '%s\n' "${promote_uname}"
|
|
}
|
|
|
|
# Legacy callers retain the historical requirement for a distinct successor.
|
|
demote_target()
|
|
{
|
|
mc_pg_demote_notification_target false
|
|
}
|
|
|
|
# Native-peer demotion can distinguish a true handoff from Pacemaker's
|
|
# demote/restart/re-promote of the same node after a resource-definition change.
|
|
mc_pg_native_demote_target()
|
|
{
|
|
mc_pg_demote_notification_target true
|
|
}
|
|
|
|
mc_pg_notify_daemon_shadow()
|
|
{
|
|
local socket_path resource parameters response rc
|
|
|
|
socket_path="${MC_PROXY_SOCKET:-/run/user/1000/managed-containerd/managed-containerd.sock}"
|
|
resource="${OCF_RESOURCE_INSTANCE:-}"
|
|
resource="${resource%%:*}"
|
|
|
|
[ -n "${resource}" ] || return 0
|
|
|
|
parameters="$(
|
|
python3 -c '
|
|
import json
|
|
import os
|
|
|
|
print(json.dumps({
|
|
"type": os.environ.get("OCF_RESKEY_CRM_meta_notify_type", ""),
|
|
"operation": os.environ.get("OCF_RESKEY_CRM_meta_notify_operation", ""),
|
|
"promote_uname": os.environ.get("OCF_RESKEY_CRM_meta_notify_promote_uname", ""),
|
|
"demote_uname": os.environ.get("OCF_RESKEY_CRM_meta_notify_demote_uname", ""),
|
|
"promoted_uname": os.environ.get("OCF_RESKEY_CRM_meta_notify_promoted_uname", ""),
|
|
"unpromoted_uname": os.environ.get("OCF_RESKEY_CRM_meta_notify_unpromoted_uname", ""),
|
|
}, separators=(",", ":")))
|
|
'
|
|
)" || {
|
|
ocf_log warn "unable to encode daemon notification parameters"
|
|
return 0
|
|
}
|
|
|
|
response="$(
|
|
timeout --kill-after=1s 3s \
|
|
python3 \
|
|
"${MC_PROXY_CLIENT:-/usr/lib/ocf/resource.d/loxthelion/managed-container.d/proxy_client.py}" \
|
|
"${socket_path}" \
|
|
"${resource}" \
|
|
"notify" \
|
|
10 \
|
|
11 \
|
|
"${parameters}"
|
|
)"
|
|
rc=$?
|
|
|
|
if [ "${rc}" -ne 0 ]; then
|
|
ocf_log warn \
|
|
"managed-containerd notify shadow unavailable rc=${rc}"
|
|
return 0
|
|
fi
|
|
|
|
ocf_log info \
|
|
"managed-containerd notify shadow accepted: ${response}"
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
managed_notify()
|
|
{
|
|
local state_dir state_file
|
|
|
|
state_dir="/run/loxthelion"
|
|
state_file="${state_dir}/${OCF_RESOURCE_INSTANCE:-managed-container}.notify"
|
|
|
|
ocf_log info \
|
|
"notify: type=${OCF_RESKEY_CRM_meta_notify_type:-unset} operation=${OCF_RESKEY_CRM_meta_notify_operation:-unset}"
|
|
|
|
ocf_log info \
|
|
"notify: promote_uname=${OCF_RESKEY_CRM_meta_notify_promote_uname:-none} demote_uname=${OCF_RESKEY_CRM_meta_notify_demote_uname:-none}"
|
|
|
|
ocf_log info \
|
|
"notify: promoted_uname=${OCF_RESKEY_CRM_meta_notify_promoted_uname:-none} unpromoted_uname=${OCF_RESKEY_CRM_meta_notify_unpromoted_uname:-none}"
|
|
|
|
if ! mkdir -p -m 0755 "${state_dir}"; then
|
|
ocf_exit_reason \
|
|
"Unable to create notification state directory ${state_dir}"
|
|
return "${OCF_ERR_GENERIC}"
|
|
fi
|
|
|
|
if ! {
|
|
printf 'timestamp=%s\n' "$(date --iso-8601=seconds)"
|
|
printf 'node=%s\n' "$(hostname)"
|
|
printf 'type=%s\n' "${OCF_RESKEY_CRM_meta_notify_type:-}"
|
|
printf 'operation=%s\n' "${OCF_RESKEY_CRM_meta_notify_operation:-}"
|
|
printf 'promote_uname=%s\n' "${OCF_RESKEY_CRM_meta_notify_promote_uname:-}"
|
|
printf 'demote_uname=%s\n' "${OCF_RESKEY_CRM_meta_notify_demote_uname:-}"
|
|
printf 'promoted_uname=%s\n' "${OCF_RESKEY_CRM_meta_notify_promoted_uname:-}"
|
|
printf 'unpromoted_uname=%s\n' "${OCF_RESKEY_CRM_meta_notify_unpromoted_uname:-}"
|
|
} > "${state_file}"
|
|
then
|
|
ocf_exit_reason \
|
|
"Unable to write notification state ${state_file}"
|
|
return "${OCF_ERR_GENERIC}"
|
|
fi
|
|
|
|
chmod 0600 "${state_file}" || {
|
|
ocf_exit_reason \
|
|
"Unable to secure notification state ${state_file}"
|
|
return "${OCF_ERR_GENERIC}"
|
|
}
|
|
|
|
{
|
|
printf '%s node=%s type=%s operation=%s promote=[%s] demote=[%s] promoted=[%s] unpromoted=[%s]\n' \
|
|
"$(date --iso-8601=seconds)" \
|
|
"$(hostname)" \
|
|
"${OCF_RESKEY_CRM_meta_notify_type:-}" \
|
|
"${OCF_RESKEY_CRM_meta_notify_operation:-}" \
|
|
"${OCF_RESKEY_CRM_meta_notify_promote_uname:-}" \
|
|
"${OCF_RESKEY_CRM_meta_notify_demote_uname:-}" \
|
|
"${OCF_RESKEY_CRM_meta_notify_promoted_uname:-}" \
|
|
"${OCF_RESKEY_CRM_meta_notify_unpromoted_uname:-}" \
|
|
>> "${state_dir}/${OCF_RESOURCE_INSTANCE:-managed-container}.notify.log"
|
|
} || true
|
|
|
|
mc_pg_notify_daemon_shadow
|
|
|
|
return "${OCF_SUCCESS}"
|
|
}
|