# 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}" }