Add read-only PostgreSQL notification target action

This commit is contained in:
Lyncoln
2026-09-17 16:51:47 -04:00
parent 263c03aa32
commit 6f90b66700
4 changed files with 165 additions and 3 deletions
+4 -2
View File
@@ -4,7 +4,8 @@
"module": "plugin.py",
"actions": [
"monitor",
"notify"
"notify",
"notify.target"
],
"required_config": [
"PGHOST",
@@ -15,6 +16,7 @@
"PG_REPLICATION_PEERS"
],
"optional_config": [
"PG_RECONNECT_GRACE_SECONDS"
"PG_RECONNECT_GRACE_SECONDS",
"PACEMAKER_NODE_NAME"
]
}
+79
View File
@@ -435,3 +435,82 @@ def notify(config, runtime_state, request_parameters):
"ocf_rc": OCF_SUCCESS,
"state": dict(state),
}
NOTIFY_TARGET_MAX_AGE_SECONDS = 120
def notify_target(config, runtime_state, request_parameters):
"""
Report whether the local daemon is the target of the last notification.
This is deliberately read-only. Promotion and demotion execution remain
separate actions until target selection is proven in production.
"""
del request_parameters
node_name = str(
config.get("PACEMAKER_NODE_NAME", "")
).strip().lower()
if not node_name:
raise ValueError(
"PACEMAKER_NODE_NAME is required for notify.target"
)
notification = _postgres_runtime_state(
runtime_state
).get("notification")
if not isinstance(notification, dict):
return {
"ocf_rc": 1,
"target": False,
"reason": "notification_missing",
"node": node_name,
}
received_at = notification.get("received_at")
if not isinstance(received_at, (int, float)):
return {
"ocf_rc": 1,
"target": False,
"reason": "notification_timestamp_invalid",
"node": node_name,
}
age = time.time() - received_at
if age < 0 or age > NOTIFY_TARGET_MAX_AGE_SECONDS:
return {
"ocf_rc": 1,
"target": False,
"reason": "notification_stale",
"age_seconds": age,
"node": node_name,
}
operation = notification.get("operation")
if operation == "demote":
target_node = notification.get("demote_uname", "").strip().lower()
elif operation == "promote":
target_node = notification.get("promote_uname", "").strip().lower()
else:
return {
"ocf_rc": 1,
"target": False,
"reason": "operation_unsupported",
"operation": operation,
"node": node_name,
}
return {
"ocf_rc": 0,
"target": target_node == node_name,
"operation": operation,
"target_node": target_node,
"node": node_name,
"age_seconds": age,
}