Import from AlmaLinux stable repository

This commit is contained in:
eabdullin 2025-12-04 12:42:22 +00:00
parent 7911776c52
commit bc3490c2fa
7 changed files with 13 additions and 2478 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
SOURCES/nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz
SOURCES/pacemaker-49aab9983.tar.gz
SOURCES/pacemaker-5693eaeee.tar.gz

View File

@ -1,2 +1,2 @@
2cbec94ad67dfbeba75e38d2c3c5c44961b3cd16 SOURCES/nagios-agents-metadata-105ab8a7b2c16b9a29cf1c1596b80136eeef332b.tar.gz
08d3f921f145e91371dd540735ebe5aa1a7a766a SOURCES/pacemaker-49aab9983.tar.gz
1b0699e6243e4fb441229e6cab0b333c95f2d391 SOURCES/pacemaker-5693eaeee.tar.gz

File diff suppressed because it is too large Load Diff

View File

@ -1,252 +0,0 @@
From 031e8a5bac5df472796574868f31af700bfa2b2a Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Mon, 6 Jan 2025 12:59:40 -0500
Subject: [PATCH 1/4] Refactor: libcrmservice: systemd_init should return a
bool.
---
lib/services/systemd.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index e3545a5b123..ed517c83e3e 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -14,6 +14,7 @@
#include <crm/services_internal.h>
#include <crm/common/mainloop.h>
+#include <stdbool.h>
#include <stdio.h> // fopen(), NULL, etc.
#include <sys/stat.h>
#include <gio/gio.h>
@@ -134,7 +135,7 @@ systemd_call_simple_method(const char *method)
return reply;
}
-static gboolean
+static bool
systemd_init(void)
{
static int need_init = 1;
@@ -153,9 +154,9 @@ systemd_init(void)
systemd_proxy = pcmk_dbus_connect();
}
if (systemd_proxy == NULL) {
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
static inline char *
@@ -546,7 +547,7 @@ systemd_unit_listall(void)
DBusMessageIter elem;
DBusMessage *reply = NULL;
- if (systemd_init() == FALSE) {
+ if (!systemd_init()) {
return NULL;
}
From 656ec99b060e56b89da08adaba454ce9ae9583ee Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Mon, 13 Jan 2025 14:40:16 -0500
Subject: [PATCH 2/4] Refactor: daemons: Unindent the goagain block in
action_complete.
If goagain is not set, just bail to the end of the function. Having
done that, everything that was in the previous block can be unindented.
I think this is a little more legible. No other code changes.
---
daemons/execd/execd_commands.c | 90 ++++++++++++++++++----------------
1 file changed, 48 insertions(+), 42 deletions(-)
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index 131b7bef6f3..454f8756916 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.c
@@ -820,6 +820,9 @@ action_complete(svc_action_t * action)
#ifdef PCMK__TIME_USE_CGT
const char *rclass = NULL;
bool goagain = false;
+ int time_sum = 0;
+ int timeout_left = 0;
+ int delay = 0;
#endif
if (!cmd) {
@@ -922,59 +925,62 @@ action_complete(svc_action_t * action)
#endif
#ifdef PCMK__TIME_USE_CGT
- if (goagain) {
- int time_sum = time_diff_ms(NULL, &(cmd->t_first_run));
- int timeout_left = cmd->timeout_orig - time_sum;
- int delay = cmd->timeout_orig / 10;
+ if (!goagain) {
+ goto finalize;
+ }
- if(delay >= timeout_left && timeout_left > 20) {
- delay = timeout_left/2;
- }
+ time_sum = time_diff_ms(NULL, &(cmd->t_first_run));
+ timeout_left = cmd->timeout_orig - time_sum;
+ delay = cmd->timeout_orig / 10;
- delay = QB_MIN(2000, delay);
- if (delay < timeout_left) {
- cmd->start_delay = delay;
- cmd->timeout = timeout_left;
+ if (delay >= timeout_left && timeout_left > 20) {
+ delay = timeout_left/2;
+ }
- if (pcmk__result_ok(&(cmd->result))) {
- crm_debug("%s %s may still be in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
- cmd->rsc_id, cmd->real_action, time_sum, timeout_left, delay);
+ delay = QB_MIN(2000, delay);
+ if (delay < timeout_left) {
+ cmd->start_delay = delay;
+ cmd->timeout = timeout_left;
- } else if (cmd->result.execution_status == PCMK_EXEC_PENDING) {
- crm_info("%s %s is still in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
- cmd->rsc_id, cmd->action, time_sum, timeout_left, delay);
+ if (pcmk__result_ok(&(cmd->result))) {
+ crm_debug("%s %s may still be in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
+ cmd->rsc_id, cmd->real_action, time_sum, timeout_left, delay);
- } else {
- crm_notice("%s %s failed '%s' (%d): re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
- cmd->rsc_id, cmd->action,
- services_ocf_exitcode_str(cmd->result.exit_status),
- cmd->result.exit_status, time_sum, timeout_left,
- delay);
- }
-
- cmd_reset(cmd);
- if(rsc) {
- rsc->active = NULL;
- }
- schedule_lrmd_cmd(rsc, cmd);
-
- /* Don't finalize cmd, we're not done with it yet */
- return;
+ } else if (cmd->result.execution_status == PCMK_EXEC_PENDING) {
+ crm_info("%s %s is still in progress: re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
+ cmd->rsc_id, cmd->action, time_sum, timeout_left, delay);
} else {
- crm_notice("Giving up on %s %s (rc=%d): timeout (elapsed=%dms, remaining=%dms)",
- cmd->rsc_id,
- (cmd->real_action? cmd->real_action : cmd->action),
- cmd->result.exit_status, time_sum, timeout_left);
- pcmk__set_result(&(cmd->result), PCMK_OCF_UNKNOWN_ERROR,
- PCMK_EXEC_TIMEOUT,
- "Investigate reason for timeout, and adjust "
- "configured operation timeout if necessary");
- cmd_original_times(cmd);
+ crm_notice("%s %s failed '%s' (%d): re-scheduling (elapsed=%dms, remaining=%dms, start_delay=%dms)",
+ cmd->rsc_id, cmd->action,
+ services_ocf_exitcode_str(cmd->result.exit_status),
+ cmd->result.exit_status, time_sum, timeout_left,
+ delay);
}
+
+ cmd_reset(cmd);
+ if (rsc) {
+ rsc->active = NULL;
+ }
+ schedule_lrmd_cmd(rsc, cmd);
+
+ /* Don't finalize cmd, we're not done with it yet */
+ return;
+
+ } else {
+ crm_notice("Giving up on %s %s (rc=%d): timeout (elapsed=%dms, remaining=%dms)",
+ cmd->rsc_id,
+ (cmd->real_action? cmd->real_action : cmd->action),
+ cmd->result.exit_status, time_sum, timeout_left);
+ pcmk__set_result(&(cmd->result), PCMK_OCF_UNKNOWN_ERROR,
+ PCMK_EXEC_TIMEOUT,
+ "Investigate reason for timeout, and adjust "
+ "configured operation timeout if necessary");
+ cmd_original_times(cmd);
}
#endif
+finalize:
pcmk__set_result_output(&(cmd->result), services__grab_stdout(action),
services__grab_stderr(action));
cmd_finalize(cmd, rsc);
From e182eb7d61fb82df2a3703b348a8f0e687985313 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Mon, 3 Feb 2025 12:25:30 -0500
Subject: [PATCH 3/4] Low: libcrmservices: Don't leak msg if systemd_proxy is
NULL.
---
lib/services/systemd.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index ed517c83e3e..19878efaa35 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -105,13 +105,15 @@ systemd_send_recv(DBusMessage *msg, DBusError *error, int timeout)
static DBusMessage *
systemd_call_simple_method(const char *method)
{
- DBusMessage *msg = systemd_new_method(method);
+ DBusMessage *msg = NULL;
DBusMessage *reply = NULL;
DBusError error;
/* Don't call systemd_init() here, because that calls this */
CRM_CHECK(systemd_proxy, return NULL);
+ msg = systemd_new_method(method);
+
if (msg == NULL) {
crm_err("Could not create message to send %s to systemd", method);
return NULL;
From fc0ef6cd7a35c8a38957e1f0a5353cfa5c397979 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Mon, 3 Feb 2025 12:46:41 -0500
Subject: [PATCH 4/4] Refactor: libcrmservices: Unref the dbus connection...
...when we disconnect from the bus. We aren't allowed to close the
connection since we acquired it with dbus_bus_get which makes it a
shared connection. So, this is the best cleanup we can do.
---
lib/services/dbus.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/services/dbus.c b/lib/services/dbus.c
index 0f98ddbd294..44e2386701f 100644
--- a/lib/services/dbus.c
+++ b/lib/services/dbus.c
@@ -294,12 +294,12 @@ pcmk_dbus_connect(void)
void
pcmk_dbus_disconnect(DBusConnection *connection)
{
- /* Per the DBus documentation, connections created with
- * dbus_connection_open() are owned by libdbus and should never be closed.
- *
- * @TODO Should we call dbus_connection_unref() here?
+ /* We acquire our dbus connection with dbus_bus_get(), which makes it a
+ * shared connection. Therefore, we can't close or free it here. The
+ * best we can do is decrement the reference count so dbus knows when
+ * there are no more clients connected to it.
*/
- return;
+ dbus_connection_unref(connection);
}
// Custom DBus error names to use

View File

@ -1,641 +0,0 @@
From dac88ca0fa3d7fbcc4924599e90a3b3465cd8525 Mon Sep 17 00:00:00 2001
From: Reid Wahl <nrwahl@protonmail.com>
Date: Wed, 5 Feb 2025 16:42:44 -0800
Subject: [PATCH 1/4] Refactor: libcrmservice: New services__copy_result()
function
Ref T25
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
daemons/execd/execd_commands.c | 9 +++------
include/crm/services_internal.h | 7 ++++++-
lib/fencing/st_actions.c | 5 ++---
lib/lrmd/lrmd_client.c | 6 ++----
lib/services/services.c | 16 +++++++++++++++-
5 files changed, 28 insertions(+), 15 deletions(-)
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index 454f8756916..afb5f599b33 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2024 the Pacemaker project contributors
+ * Copyright 2012-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -1344,8 +1344,7 @@ execute_nonstonith_action(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
}
if (action->rc != PCMK_OCF_UNKNOWN) {
- pcmk__set_result(&(cmd->result), action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &(cmd->result));
services_action_free(action);
cmd_finalize(cmd, rsc);
return;
@@ -1370,9 +1369,7 @@ execute_nonstonith_action(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd)
* called cmd_finalize(), which in this case should only reset (not
* free) cmd.
*/
-
- pcmk__set_result(&(cmd->result), action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &(cmd->result));
services_action_free(action);
}
}
diff --git a/include/crm/services_internal.h b/include/crm/services_internal.h
index ada97e1e711..63b034dcd24 100644
--- a/include/crm/services_internal.h
+++ b/include/crm/services_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2022 the Pacemaker project contributors
+ * Copyright 2010-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -10,6 +10,9 @@
#ifndef PCMK__SERVICES_INTERNAL__H
# define PCMK__SERVICES_INTERNAL__H
+#include <crm/common/results_internal.h> // pcmk__action_result_t
+#include <crm/services.h> // svc_action_t
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -50,6 +53,8 @@ char *services__grab_stderr(svc_action_t *action);
void services__set_result(svc_action_t *action, int agent_status,
enum pcmk_exec_status exec_status,
const char *exit_reason);
+void services__copy_result(const svc_action_t *action,
+ pcmk__action_result_t *result);
void services__format_result(svc_action_t *action, int agent_status,
enum pcmk_exec_status exec_status,
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
index 6b4d1e73527..a0f3e0377c0 100644
--- a/lib/fencing/st_actions.c
+++ b/lib/fencing/st_actions.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2024 the Pacemaker project contributors
+ * Copyright 2004-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -63,8 +63,7 @@ static void log_action(stonith_action_t *action, pid_t pid);
static void
set_result_from_svc_action(stonith_action_t *action, svc_action_t *svc_action)
{
- pcmk__set_result(&(action->result), svc_action->rc, svc_action->status,
- services__exit_reason(svc_action));
+ services__copy_result(svc_action, &(action->result));
pcmk__set_result_output(&(action->result),
services__grab_stdout(svc_action),
services__grab_stderr(svc_action));
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index ae4c100def0..5fcdf2377e8 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -2555,8 +2555,7 @@ metadata_complete(svc_action_t *action)
struct metadata_cb *metadata_cb = (struct metadata_cb *) action->cb_data;
pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
- pcmk__set_result(&result, action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &result);
pcmk__set_result_output(&result, action->stdout_data, action->stderr_data);
metadata_cb->callback(0, &result, metadata_cb->user_data);
@@ -2624,8 +2623,7 @@ lrmd__metadata_async(const lrmd_rsc_info_t *rsc,
return ENOMEM;
}
if (action->rc != PCMK_OCF_UNKNOWN) {
- pcmk__set_result(&result, action->rc, action->status,
- services__exit_reason(action));
+ services__copy_result(action, &result);
callback(0, &result, user_data);
pcmk__reset_result(&result);
services_action_free(action);
diff --git a/lib/services/services.c b/lib/services/services.c
index 2ba0eb24b9d..0d0eb46d342 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2010-2024 the Pacemaker project contributors
+ * Copyright 2010-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -1336,6 +1336,20 @@ services__set_result(svc_action_t *action, int agent_status,
}
}
+/*!
+ * \internal
+ * \brief Set a \c pcmk__action_result_t based on a \c svc_action_t
+ *
+ * \param[in] action Service action whose result to copy
+ * \param[in,out] result Action result object to set
+ */
+void
+services__copy_result(const svc_action_t *action, pcmk__action_result_t *result)
+{
+ pcmk__set_result(result, action->rc, action->status,
+ action->opaque->exit_reason);
+}
+
/*!
* \internal
* \brief Set the result of an action, with a formatted exit reason
From 283225383916358f6fc905afdc40e423583bc9bd Mon Sep 17 00:00:00 2001
From: Reid Wahl <nrwahl@protonmail.com>
Date: Wed, 5 Feb 2025 03:35:04 -0800
Subject: [PATCH 2/4] Refactor: libcrmservice: Store systemd job path in
svc_action_private_t
We will use this later for D-Bus signal message filtering.
Ref T25
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
lib/services/services.c | 5 +++++
lib/services/services_private.h | 6 +++++-
lib/services/systemd.c | 2 ++
3 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/lib/services/services.c b/lib/services/services.c
index 0d0eb46d342..e1e98120c09 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -635,6 +635,11 @@ services_action_free(svc_action_t * op)
}
free(op->opaque->exit_reason);
+
+#if SUPPORT_SYSTEMD
+ free(op->opaque->job_path);
+#endif // SUPPORT_SYSTEMD
+
free(op->opaque);
free(op->rsc);
free(op->action);
diff --git a/lib/services/services_private.h b/lib/services/services_private.h
index 48269b80308..91b49b4248c 100644
--- a/lib/services/services_private.h
+++ b/lib/services/services_private.h
@@ -1,6 +1,6 @@
/*
* Copyright 2010-2011 Red Hat, Inc.
- * Later changes copyright 2012-2022 the Pacemaker project contributors
+ * Later changes copyright 2012-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -42,6 +42,10 @@ struct svc_action_private_s {
DBusPendingCall* pending;
unsigned timerid;
#endif
+
+#if SUPPORT_SYSTEMD
+ char *job_path;
+#endif // SUPPORT_SYSTEMD
};
G_GNUC_INTERNAL
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index 19878efaa35..090a30bb1ca 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -760,8 +760,10 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
dbus_message_get_args(reply, NULL,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
+
crm_debug("DBus request for %s of %s using %s succeeded",
op->action, pcmk__s(op->rsc, "unknown resource"), path);
+ pcmk__str_update(&(op->opaque->job_path), path);
services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
}
}
From 67c1821648f314b510a24ed505a560fa34f2d5c0 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Wed, 12 Mar 2025 10:54:54 -0400
Subject: [PATCH 3/4] Refactor: libcrmservice: Subscribe to systemd D-Bus
signals
We'll add a filter later. This does not meaningfully change behavior.
Ref T25
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
lib/services/systemd.c | 58 +++++++++++++++++++++++++++++++++++++++---
1 file changed, 54 insertions(+), 4 deletions(-)
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index 090a30bb1ca..df843edce05 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -25,6 +25,9 @@
static void invoke_unit_by_path(svc_action_t *op, const char *unit);
+/* Systemd D-Bus interface
+ * https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html
+ */
#define BUS_NAME "org.freedesktop.systemd1"
#define BUS_NAME_MANAGER BUS_NAME ".Manager"
#define BUS_NAME_UNIT BUS_NAME ".Unit"
@@ -137,6 +140,50 @@ systemd_call_simple_method(const char *method)
return reply;
}
+/*!
+ * \internal
+ * \brief Subscribe to D-Bus signals from systemd
+ *
+ * Systemd does not broadcast signal messages unless at least one client has
+ * called the \c Subscribe() method. Also, a D-Bus client ignores broadcast
+ * messages unless an appropriate match rule is set, so we set one here.
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+subscribe_to_signals(void)
+{
+ const char *match_rule = "type='signal',"
+ "sender='" BUS_NAME "',"
+ "interface='" BUS_NAME_MANAGER "',"
+ "path='" BUS_PATH "'";
+ DBusMessage *reply = NULL;
+ DBusError error;
+
+ /* Tell D-Bus to accept signal messages from systemd.
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
+ */
+ dbus_error_init(&error);
+ dbus_bus_add_match(systemd_proxy, match_rule, &error);
+
+ if (dbus_error_is_set(&error)) {
+ crm_err("Could not listen for systemd DBus signals: %s " QB_XS " (%s)",
+ error.message, error.name);
+ dbus_error_free(&error);
+ return ECOMM;
+ }
+
+ // Tell systemd to broadcast signals
+ reply = systemd_call_simple_method("Subscribe");
+ if (reply == NULL) {
+ dbus_bus_remove_match(systemd_proxy, match_rule, &error);
+ return ECOMM;
+ }
+
+ dbus_message_unref(reply);
+ return pcmk_rc_ok;
+}
+
static bool
systemd_init(void)
{
@@ -154,11 +201,14 @@ systemd_init(void)
if (need_init) {
need_init = 0;
systemd_proxy = pcmk_dbus_connect();
+
+ if (subscribe_to_signals() != pcmk_rc_ok) {
+ pcmk_dbus_disconnect(systemd_proxy);
+ systemd_proxy = NULL;
+ }
}
- if (systemd_proxy == NULL) {
- return false;
- }
- return true;
+
+ return (systemd_proxy != NULL);
}
static inline char *
From 373a7a317023f9d530cd43ab80bdb62a72bb2b05 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Thu, 16 Jan 2025 16:21:44 -0500
Subject: [PATCH 4/4] Fix: various: Correctly detect completion of systemd
start/stop actions
When systemd receives a StartUnit() or StopUnit() method call, it
returns almost immediately, as soon as a start/stop job is enqueued. A
successful return code does NOT indicate that the start/stop has
finished.
Previously, we worked around this in action_complete() with a hack that
scheduled a follow-up monitor after a successful start/stop method call,
which polled the service after 2 seconds to see whether it was actually
running. However, this was not a robust solution. Timing issues could
result in Pacemaker having an incorrect view of the resource's status or
prematurely declaring the action as failed.
Now, we follow the best practice as documented in the systemd D-Bus API
doc (see StartUnit()):
https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html#Methods
After kicking off a systemd start/stop action, we make note of the job's
D-Bus object path. Then we register a D-Bus message filter that looks
for a JobRemoved signal whose bus path matches. This signal indicates
that the job has completed and includes its result. When we find the
matching signal, we set the action's result. We then remove the filter,
which causes the action to be finalized and freed. In the case of the
executor daemon, the action has a callback (action_complete()) that runs
during finalization and sets the executor's view of the action result.
Monitor actions still need much of the existing workaround code in
action_complete(), so we keep it for now. We bail out for start/stop
actions after setting the result as described above.
Ref T25
Co-authored-by: Reid Wahl <nrwahl@protonmail.com>
Signed-off-by: Reid Wahl <nrwahl@protonmail.com>
---
daemons/execd/execd_commands.c | 13 ++-
lib/services/services_private.h | 2 +-
lib/services/systemd.c | 167 ++++++++++++++++++++++++++++++--
3 files changed, 167 insertions(+), 15 deletions(-)
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
index afb5f599b33..67283ce1012 100644
--- a/daemons/execd/execd_commands.c
+++ b/daemons/execd/execd_commands.c
@@ -861,14 +861,13 @@ action_complete(svc_action_t * action)
if (pcmk__result_ok(&(cmd->result))
&& pcmk__strcase_any_of(cmd->action, PCMK_ACTION_START,
PCMK_ACTION_STOP, NULL)) {
- /* systemd returns from start and stop actions after the action
- * begins, not after it completes. We have to jump through a few
- * hoops so that we don't report 'complete' to the rest of pacemaker
- * until it's actually done.
+ /* Getting results for when a start or stop action completes is now
+ * handled by watching for JobRemoved() signals from systemd and
+ * reacting to them. So, we can bypass the rest of the code in this
+ * function for those actions, and simply finalize cmd.
*/
- goagain = true;
- cmd->real_action = cmd->action;
- cmd->action = pcmk__str_copy(PCMK_ACTION_MONITOR);
+ services__copy_result(action, &(cmd->result));
+ goto finalize;
} else if (cmd->real_action != NULL) {
// This is follow-up monitor to check whether start/stop completed
diff --git a/lib/services/services_private.h b/lib/services/services_private.h
index 91b49b4248c..b29833c3988 100644
--- a/lib/services/services_private.h
+++ b/lib/services/services_private.h
@@ -44,7 +44,7 @@ struct svc_action_private_s {
#endif
#if SUPPORT_SYSTEMD
- char *job_path;
+ char *job_path; // D-Bus object path for enqueued start/stop job
#endif // SUPPORT_SYSTEMD
};
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index df843edce05..f10d24a050e 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -14,7 +14,9 @@
#include <crm/services_internal.h>
#include <crm/common/mainloop.h>
+#include <inttypes.h> // PRIu32
#include <stdbool.h>
+#include <stdint.h> // uint32_t
#include <stdio.h> // fopen(), NULL, etc.
#include <sys/stat.h>
#include <gio/gio.h>
@@ -785,6 +787,8 @@ systemd_unit_metadata(const char *name, int timeout)
static void
process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
{
+ bool start_stop = pcmk__strcase_any_of(op->action, PCMK_ACTION_START,
+ PCMK_ACTION_STOP, NULL);
DBusError error;
dbus_error_init(&error);
@@ -798,11 +802,25 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
} else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
__func__, __LINE__)) {
+ const char *reason = "systemd D-Bus method had unexpected reply";
+
crm_info("DBus request for %s of %s succeeded but "
"return type was unexpected",
op->action, pcmk__s(op->rsc, "unknown resource"));
- services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE,
- "systemd DBus method had unexpected reply");
+
+ if (!op->synchronous && start_stop) {
+ /* The start or stop job is enqueued but is not complete. We need a
+ * job path to detect completion in job_removed_filter().
+ */
+ services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
+ reason);
+
+ } else {
+ /* Something weird happened, but the action is finished and there
+ * was no D-Bus error. So call it a success.
+ */
+ services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, reason);
+ }
} else {
const char *path = NULL;
@@ -813,9 +831,114 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op)
crm_debug("DBus request for %s of %s using %s succeeded",
op->action, pcmk__s(op->rsc, "unknown resource"), path);
- pcmk__str_update(&(op->opaque->job_path), path);
- services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+
+ if (!op->synchronous && start_stop) {
+ // Should be set to unknown/pending already
+ services__set_result(op, PCMK_OCF_UNKNOWN, PCMK_EXEC_PENDING, NULL);
+ pcmk__str_update(&(op->opaque->job_path), path);
+
+ } else {
+ services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+ }
+ }
+}
+
+/*!
+ * \internal
+ * \brief Process a systemd \c JobRemoved signal for a given service action
+ *
+ * This filter is expected to be added with \c finalize_async_action_dbus() as
+ * the \c free_data_function. Then if \p message is a \c JobRemoved signal for
+ * the action specified by \p user_data, the action's result is set, the filter
+ * is removed, and the action is finalized.
+ *
+ * \param[in,out] connection D-Bus connection
+ * \param[in] message D-Bus message
+ * \param[in,out] user_data Service action (\c svc_action_t)
+ *
+ * \retval \c DBUS_HANDLER_RESULT_HANDLED if \p message is a \c JobRemoved
+ * signal for \p user_data
+ * \retval \c DBUS_HANDLER_RESULT_NOT_YET_HANDLED otherwise (on error, if
+ * \p message is not a \c JobRemoved signal, or if the signal is for
+ * some other action's job)
+ */
+static DBusHandlerResult
+job_removed_filter(DBusConnection *connection, DBusMessage *message,
+ void *user_data)
+{
+ svc_action_t *action = user_data;
+ const char *action_name = NULL;
+ uint32_t job_id = 0;
+ const char *bus_path = NULL;
+ const char *unit_name = NULL;
+ const char *result = NULL;
+ DBusError error;
+
+ CRM_CHECK((connection != NULL) && (message != NULL),
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+ // action should always be set when the filter is added
+ if ((action == NULL)
+ || !dbus_message_is_signal(message, BUS_NAME_MANAGER, "JobRemoved")) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ dbus_error_init(&error);
+ if (!dbus_message_get_args(message, &error,
+ DBUS_TYPE_UINT32, &job_id,
+ DBUS_TYPE_OBJECT_PATH, &bus_path,
+ DBUS_TYPE_STRING, &unit_name,
+ DBUS_TYPE_STRING, &result,
+ DBUS_TYPE_INVALID)) {
+ crm_err("Could not interpret systemd DBus signal: %s " QB_XS " (%s)",
+ error.message, error.name);
+ dbus_error_free(&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!pcmk__str_eq(bus_path, action->opaque->job_path, pcmk__str_none)) {
+ // This filter is not for this job
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+
+ action_name = pcmk__s(action->action, "(unknown)");
+
+ crm_trace("Setting %s result for %s (JobRemoved id=%" PRIu32 ", result=%s",
+ action_name, unit_name, job_id, result);
+
+ if (pcmk__str_eq(result, "done", pcmk__str_none)) {
+ services__set_result(action, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
+
+ } else if (pcmk__str_eq(result, "timeout", pcmk__str_none)) {
+ services__format_result(action, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT,
+ "systemd %s job for %s timed out",
+ action_name, unit_name);
+
+ } else {
+ services__format_result(action, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
+ "systemd %s job for %s failed with result '%s'",
+ action_name, unit_name, result);
+ }
+
+ /* This instance of the filter was specifically for the given action.
+ *
+ * The action gets finalized by services__finalize_async_op() via the
+ * filter's free_data_function.
+ */
+ dbus_connection_remove_filter(systemd_proxy, job_removed_filter, action);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/*!
+ * \internal
+ * \brief \c DBusFreeFunction wrapper for \c services__finalize_async_op()
+ *
+ * \param[in,out] action Asynchronous service action to finalize
+ */
+static void
+finalize_async_action_dbus(void *action)
+{
+ services__finalize_async_op((svc_action_t *) action);
}
/*!
@@ -842,12 +965,34 @@ unit_method_complete(DBusPendingCall *pending, void *user_data)
CRM_LOG_ASSERT(pending == op->opaque->pending);
services_set_op_pending(op, NULL);
- // Determine result and finalize action
process_unit_method_reply(reply, op);
- services__finalize_async_op(op);
+
if (reply != NULL) {
dbus_message_unref(reply);
}
+
+ if ((op->status == PCMK_EXEC_PENDING)
+ && pcmk__strcase_any_of(op->action, PCMK_ACTION_START, PCMK_ACTION_STOP,
+ NULL)) {
+ /* Start and stop method calls return when the job is enqueued, not when
+ * it's complete. Start and stop actions must be finalized after the job
+ * is complete, because the action callback function may use it. We add
+ * a message filter to process the JobRemoved signal, which indicates
+ * completion.
+ *
+ * The filter takes ownership of op, which will be finalized when the
+ * filter is later removed.
+ */
+ if (dbus_connection_add_filter(systemd_proxy, job_removed_filter, op,
+ finalize_async_action_dbus)) {
+ return;
+ }
+ crm_err("Could not add D-Bus filter for systemd JobRemoved signals");
+ services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
+ "Failed to add D-Bus filter for systemd "
+ "JobRemoved signal");
+ }
+ services__finalize_async_op(op);
}
/* When the cluster manages a systemd resource, we create a unit file override
@@ -1193,7 +1338,14 @@ systemd_timeout_callback(gpointer p)
services__format_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT,
"%s action for systemd unit %s "
"did not complete in time", op->action, op->agent);
- services__finalize_async_op(op);
+
+ if (op->opaque->job_path != NULL) {
+ // A filter owns this op
+ dbus_connection_remove_filter(systemd_proxy, job_removed_filter, op);
+
+ } else {
+ services__finalize_async_op(op);
+ }
return FALSE;
}
@@ -1247,6 +1399,7 @@ services__execute_systemd(svc_action_t *op)
"Bug in service library");
if (invoke_unit_by_name(op->agent, op, NULL) == pcmk_rc_ok) {
+ // @TODO Why plus 5000? No explanation in fccd046.
op->opaque->timerid = g_timeout_add(op->timeout + 5000,
systemd_timeout_callback, op);
services_add_inflight_op(op);

View File

@ -1,430 +0,0 @@
From 89d6e036039f285eccb538370aac8f7ea0b03ec6 Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Wed, 2 Apr 2025 13:27:27 -0400
Subject: [PATCH 1/5] Refactor: scheduler: Fix formatting in pe_can_fence.
---
lib/pengine/utils.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 87466eb..3e388b9 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2024 the Pacemaker project contributors
+ * Copyright 2004-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -63,10 +63,10 @@ pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node)
} else if (scheduler->no_quorum_policy == pcmk_no_quorum_ignore) {
return true;
- } else if(node == NULL) {
+ } else if (node == NULL) {
return false;
- } else if(node->details->online) {
+ } else if (node->details->online) {
crm_notice("We can fence %s without quorum because they're in our membership",
pcmk__node_name(node));
return true;
--
2.43.0
From e3d62eec24673bf61b1cb988629b258a31ad1fbc Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Wed, 2 Apr 2025 13:29:44 -0400
Subject: [PATCH 2/5] Med: scheduler: Don't always fence online remote nodes.
Let's assume you have a cluster configured as follows:
* Three nodes, plus one Pacemaker Remote node.
* At least two NICs on each node.
* Multiple layers of fencing, including fence_kdump.
* The timeout for fence_kdump is set higher on the real nodes than it is
on the remote node.
* A resource is configured that can only be run on the remote node.
Now, let's assume that the node running the connection resource for the
remote node is disconnect from the rest of the cluster. In testing,
this disconnection was done by bringing one network interface down.
Due to the fence timeouts, the following things will occur:
* The node whose interface was brought down will split off into its own
cluster partition without quorum, while the other two nodes maintain
quorum.
* The partition with quorum will restart the remote node resource on
another real node in the partition.
* The node by itself will be fenced. However, due to the long
fence_kdump timeout, it will continue to make decisions regarding
resources.
* The node by itself will re-assign resources, including the remote
connection resource. This resource will be assigned back to the same
node again.
* The node by itself will decide to fence the remote node, which will
hit the "in our membership" clause of pe_can_fence. This is because
remote nodes are marked as online when they are assigned, not when
they are actually running.
* When the fence_kdump timeout expires, the node by itself will fence
the remote node. This succeeds because there is still a secondary
network connection it can use. This fencing will succeed, causing the
remote node to reboot and then causing a loss of service.
* The node by itself will then be fenced.
The bug to me seems to be that the remote resource is marked as online
when it isn't yet. I think with that changed, all the other remote
fencing related code would then work as intended. However, it probably
has to remain as-is in order to schedule resources on the remote node -
resources probably can't be assigned to an offline node. Making changes
in pe_can_fence seems like the least invasive way to deal with this
problem.
I also think this probably has probably been here for a very long time -
perhaps always - but we just haven't seen it due to the number of things
that have to be configured before it can show up. In particular, the
fencing timeouts and secondary network connection are what allow this
behavior to happen.
I can't think of a good reason why a node without quorum would ever want
to fence a remote node, especially if the connection resource has been
moved to the wquochanges in pe_can_fence seems like the least invasive
way to deal with this problem.
My fix here therefore is just to test whether there is another node it
could have been moved to and if so, don't fence it.
Fixes T978
Fixes RHEL-84018
---
lib/pengine/utils.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 3e388b9..18fd850 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -67,6 +67,39 @@ pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node)
return false;
} else if (node->details->online) {
+ /* Remote nodes are marked online when we assign their resource to a
+ * node, not when they are actually started (see remote_connection_assigned)
+ * so the above test by itself isn't good enough.
+ */
+ if (pcmk__is_pacemaker_remote_node(node)) {
+ /* If we're on a system without quorum, it's entirely possible that
+ * the remote resource was automatically moved to a node on the
+ * partition with quorum. We can't tell that from this node - the
+ * best we can do is check if it's possible for the resource to run
+ * on another node in the partition with quorum. If so, it has
+ * likely been moved and we shouldn't fence it.
+ *
+ * NOTE: This condition appears to only come up in very limited
+ * circumstances. It at least requires some very lengthy fencing
+ * timeouts set, some way for fencing to still take place (a second
+ * NIC is how I've reproduced it in testing, but fence_scsi or
+ * sbd could work too), and a resource that runs on the remote node.
+ */
+ pcmk_resource_t *rsc = node->details->remote_rsc;
+ pcmk_node_t *n = NULL;
+ GHashTableIter iter;
+
+ g_hash_table_iter_init(&iter, rsc->allowed_nodes);
+ while (g_hash_table_iter_next(&iter, NULL, (void **) &n)) {
+ /* A node that's not online according to this non-quorum node
+ * is a node that's in another partition.
+ */
+ if (!n->details->online) {
+ return false;
+ }
+ }
+ }
+
crm_notice("We can fence %s without quorum because they're in our membership",
pcmk__node_name(node));
return true;
--
2.43.0
From f6d995f5c60649c5686600650e6f636ed8b6937c Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Wed, 2 Apr 2025 13:39:21 -0400
Subject: [PATCH 3/5] Med: scheduler: Require a cluster option for new remote
fencing behavior.
We don't have a ton of confidence that the previous patch is the right
thing to do for everyone, so we are going to hide it behind this
undocumented cluster config option. By default, if the option is
missing (or is set to "true"), the existing remote fencing behavior will
be what happens. That is, a node without quorum will be allowed to
fence remote nodes in the same partition even if they've been restarted
elsewhere.
However, with fence-remote-without-quorum="false", we will check to see
if the remote node could possibly have been started on another node and
if so, it will not be fenced.
---
cts/cli/regression.daemons.exp | 9 +++++++++
cts/cli/regression.tools.exp | 14 ++++++++++++++
include/crm/common/options_internal.h | 5 ++++-
include/crm/common/scheduler.h | 6 +++++-
lib/common/options.c | 13 ++++++++++++-
lib/pengine/unpack.c | 11 ++++++++++-
lib/pengine/utils.c | 5 ++++-
7 files changed, 58 insertions(+), 5 deletions(-)
diff --git a/cts/cli/regression.daemons.exp b/cts/cli/regression.daemons.exp
index 74eedee..fe53044 100644
--- a/cts/cli/regression.daemons.exp
+++ b/cts/cli/regression.daemons.exp
@@ -514,6 +514,15 @@
</shortdesc>
<content type="boolean" default=""/>
</parameter>
+ <parameter name="fence-remote-without-quorum">
+ <longdesc lang="en">
+ By default, inquorate nodes can fence Pacemaker Remote nodes that are part of its partition regardless of whether the resource was successfully restarted elsewhere. If false, an additional check will be added to only fence remote nodes if the cluster thinks they were unable to be restarted.
+ </longdesc>
+ <shortdesc lang="en">
+ *** Advanced Use Only *** Whether remote nodes can be fenced without quorum
+ </shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
<parameter name="stonith-enabled">
<longdesc lang="en">
If false, unresponsive nodes are immediately assumed to be harmless, and resources that were active on them may be recovered elsewhere. This can result in a "split-brain" situation, potentially leading to data loss and/or service unavailability.
diff --git a/cts/cli/regression.tools.exp b/cts/cli/regression.tools.exp
index 94b6330..5448ae3 100644
--- a/cts/cli/regression.tools.exp
+++ b/cts/cli/regression.tools.exp
@@ -300,6 +300,11 @@ Also known as properties, these are options that affect behavior across the enti
<shortdesc lang="en">Whether the cluster should check for active resources during start-up</shortdesc>
<content type="boolean" default=""/>
</parameter>
+ <parameter name="fence-remote-without-quorum" advanced="1" generated="0">
+ <longdesc lang="en">By default, inquorate nodes can fence Pacemaker Remote nodes that are part of its partition regardless of whether the resource was successfully restarted elsewhere. If false, an additional check will be added to only fence remote nodes if the cluster thinks they were unable to be restarted.</longdesc>
+ <shortdesc lang="en">Whether remote nodes can be fenced without quorum</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
<parameter name="stonith-enabled" advanced="1" generated="0">
<longdesc lang="en">If false, unresponsive nodes are immediately assumed to be harmless, and resources that were active on them may be recovered elsewhere. This can result in a "split-brain" situation, potentially leading to data loss and/or service unavailability.</longdesc>
<shortdesc lang="en">Whether nodes may be fenced as part of recovery</shortdesc>
@@ -635,6 +640,10 @@ Also known as properties, these are options that affect behavior across the enti
* Delay cluster recovery for this much time to allow for additional events to occur. Useful if your configuration is sensitive to the order in which ping updates arrive.
* Possible values: duration (default: )
+ * fence-remote-without-quorum: Whether remote nodes can be fenced without quorum
+ * By default, inquorate nodes can fence Pacemaker Remote nodes that are part of its partition regardless of whether the resource was successfully restarted elsewhere. If false, an additional check will be added to only fence remote nodes if the cluster thinks they were unable to be restarted.
+ * Possible values: boolean (default: )
+
* stonith-enabled: Whether nodes may be fenced as part of recovery
* If false, unresponsive nodes are immediately assumed to be harmless, and resources that were active on them may be recovered elsewhere. This can result in a "split-brain" situation, potentially leading to data loss and/or service unavailability.
* Possible values: boolean (default: )
@@ -762,6 +771,11 @@ Also known as properties, these are options that affect behavior across the enti
<shortdesc lang="en">Whether the cluster should check for active resources during start-up</shortdesc>
<content type="boolean" default=""/>
</parameter>
+ <parameter name="fence-remote-without-quorum" advanced="1" generated="0">
+ <longdesc lang="en">By default, inquorate nodes can fence Pacemaker Remote nodes that are part of its partition regardless of whether the resource was successfully restarted elsewhere. If false, an additional check will be added to only fence remote nodes if the cluster thinks they were unable to be restarted.</longdesc>
+ <shortdesc lang="en">Whether remote nodes can be fenced without quorum</shortdesc>
+ <content type="boolean" default=""/>
+ </parameter>
<parameter name="stonith-enabled" advanced="1" generated="0">
<longdesc lang="en">If false, unresponsive nodes are immediately assumed to be harmless, and resources that were active on them may be recovered elsewhere. This can result in a "split-brain" situation, potentially leading to data loss and/or service unavailability.</longdesc>
<shortdesc lang="en">Whether nodes may be fenced as part of recovery</shortdesc>
diff --git a/include/crm/common/options_internal.h b/include/crm/common/options_internal.h
index 92506a0..6137b94 100644
--- a/include/crm/common/options_internal.h
+++ b/include/crm/common/options_internal.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2024 the Pacemaker project contributors
+ * Copyright 2006-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -260,5 +260,8 @@ bool pcmk__valid_stonith_watchdog_timeout(const char *value);
// @COMPAT Drop when daemon metadata commands are dropped
#define PCMK__VALUE_TIME "time"
+
+// Cluster options
+#define PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM "fence-remote-without-quorum"
#endif // PCMK__OPTIONS_INTERNAL__H
diff --git a/include/crm/common/scheduler.h b/include/crm/common/scheduler.h
index fe8d8fe..c7b989d 100644
--- a/include/crm/common/scheduler.h
+++ b/include/crm/common/scheduler.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2024 the Pacemaker project contributors
+ * Copyright 2004-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -216,6 +216,10 @@ struct pe_working_set_s {
//! \deprecated Call pcmk_get_no_quorum_policy() to get no-quorum policy
enum pe_quorum_policy no_quorum_policy; // Response to loss of quorum
+ // Can Pacemaker Remote nodes be fenced even from a node that doesn't
+ // have quorum?
+ bool fence_remote_without_quorum;
+
GHashTable *config_hash; // Cluster properties
// Ticket constraints unpacked from ticket state
diff --git a/lib/common/options.c b/lib/common/options.c
index aab5bb3..5e55c67 100644
--- a/lib/common/options.c
+++ b/lib/common/options.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2024 the Pacemaker project contributors
+ * Copyright 2004-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -236,6 +236,17 @@ static const pcmk__cluster_option_t cluster_options[] = {
},
// Fencing-related options
+ {
+ PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM, NULL, PCMK_VALUE_BOOLEAN, NULL,
+ PCMK_VALUE_TRUE, pcmk__valid_boolean,
+ pcmk__opt_schedulerd|pcmk__opt_advanced,
+ N_("Whether remote nodes can be fenced without quorum"),
+ N_("By default, inquorate nodes can fence Pacemaker Remote nodes that "
+ "are part of its partition regardless of whether the resource "
+ "was successfully restarted elsewhere. If false, an additional "
+ "check will be added to only fence remote nodes if the cluster "
+ "thinks they were unable to be restarted.")
+ },
{
PCMK_OPT_STONITH_ENABLED, NULL, PCMK_VALUE_BOOLEAN, NULL,
PCMK_VALUE_TRUE, pcmk__valid_boolean,
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
index 256fe81..0eb7088 100644
--- a/lib/pengine/unpack.c
+++ b/lib/pengine/unpack.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2024 the Pacemaker project contributors
+ * Copyright 2004-2025 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -449,6 +449,15 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler)
* 1000));
}
+ value = pcmk__cluster_option(config_hash, PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM);
+ if ((value != NULL) && !crm_is_true(value)) {
+ crm_warn(PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM " disabled - remote "
+ "nodes may not be fenced in inquorate partition");
+ scheduler->fence_remote_without_quorum = false;
+ } else {
+ scheduler->fence_remote_without_quorum = true;
+ }
+
return TRUE;
}
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 18fd850..36fcb6e 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -70,8 +70,11 @@ pe_can_fence(const pcmk_scheduler_t *scheduler, const pcmk_node_t *node)
/* Remote nodes are marked online when we assign their resource to a
* node, not when they are actually started (see remote_connection_assigned)
* so the above test by itself isn't good enough.
+ *
+ * This is experimental behavior, so the user has to opt into it by
+ * adding fence-remote-without-quorum="false" to their CIB.
*/
- if (pcmk__is_pacemaker_remote_node(node)) {
+ if (pcmk__is_pacemaker_remote_node(node) && !scheduler->fence_remote_without_quorum) {
/* If we're on a system without quorum, it's entirely possible that
* the remote resource was automatically moved to a node on the
* partition with quorum. We can't tell that from this node - the
--
2.43.0
From 0d122ecd73cebb27d4bc0474de72de9bff63acb6 Mon Sep 17 00:00:00 2001
From: "Gao,Yan" <ygao@suse.com>
Date: Thu, 10 Apr 2025 12:51:57 +0200
Subject: [PATCH 4/5] Refactor: libcrmcommon: move the new struct member to
the end for backward compatibility
Commit f342b77561 broke backward compatibility by inserting the new
member `fence_remote_without_quorum` into the middle of the
`pe_working_set_s` struct.
---
include/crm/common/scheduler.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/crm/common/scheduler.h b/include/crm/common/scheduler.h
index c7b989d..7596cb7 100644
--- a/include/crm/common/scheduler.h
+++ b/include/crm/common/scheduler.h
@@ -216,10 +216,6 @@ struct pe_working_set_s {
//! \deprecated Call pcmk_get_no_quorum_policy() to get no-quorum policy
enum pe_quorum_policy no_quorum_policy; // Response to loss of quorum
- // Can Pacemaker Remote nodes be fenced even from a node that doesn't
- // have quorum?
- bool fence_remote_without_quorum;
-
GHashTable *config_hash; // Cluster properties
// Ticket constraints unpacked from ticket state
@@ -268,6 +264,10 @@ struct pe_working_set_s {
void *priv; // For Pacemaker use only
guint node_pending_timeout; // Pending join times out after this (ms)
+
+ // Can Pacemaker Remote nodes be fenced even from a node that doesn't
+ // have quorum?
+ bool fence_remote_without_quorum;
};
//!@}
--
2.43.0
From 6e5f8472eea018f751c6fa38945f9f28ed013d2f Mon Sep 17 00:00:00 2001
From: Chris Lumens <clumens@redhat.com>
Date: Tue, 29 Apr 2025 12:49:45 -0400
Subject: [PATCH 5/5] Refactor: scheduler: Lower fencing log message to debug
level.
Most other things in unpack_config are logged at debug or trace level.
Having the fencing message at the warn level makes it come up quite
often.
---
lib/pengine/unpack.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
index 0eb7088..1466695 100644
--- a/lib/pengine/unpack.c
+++ b/lib/pengine/unpack.c
@@ -451,8 +451,8 @@ unpack_config(xmlNode *config, pcmk_scheduler_t *scheduler)
value = pcmk__cluster_option(config_hash, PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM);
if ((value != NULL) && !crm_is_true(value)) {
- crm_warn(PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM " disabled - remote "
- "nodes may not be fenced in inquorate partition");
+ crm_debug(PCMK__OPT_FENCE_REMOTE_WITHOUT_QUORUM " disabled - remote "
+ "nodes may not be fenced in inquorate partition");
scheduler->fence_remote_without_quorum = false;
} else {
scheduler->fence_remote_without_quorum = true;
--
2.43.0

View File

@ -35,11 +35,11 @@
## Upstream pacemaker version, and its package version (specversion
## can be incremented to build packages reliably considered "newer"
## than previously built packages with the same pcmkversion)
%global pcmkversion 2.1.9
%global pcmkversion 2.1.10
%global specversion 1
## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build
%global commit 49aab998399b9fec21631ff610ae5bdcc4ffb7a4
%global commit 5693eaeeef06faa1622515963082b5a1731d9fc0
## Since git v2.11, the extent of abbreviation is autoscaled by default
## (used to be constant of 7), so we need to convey it for non-tags, too.
@ -230,7 +230,7 @@
Name: pacemaker
Summary: Scalable High-Availability cluster resource manager
Version: %{pcmkversion}
Release: %{pcmk_release}.2%{?dist}
Release: %{pcmk_release}%{?dist}
License: GPL-2.0-or-later AND LGPL-2.1-or-later
Url: https://www.clusterlabs.org/
@ -247,10 +247,7 @@ Source1: https://codeload.github.com/%{github_owner}/%{nagios_name}/tar.gz
Source2: pacemaker.sysusers
# upstream commits
Patch001: 001-systemd-overrides.patch
Patch002: 002-systemd-dbus-prep.patch
Patch003: 003-systemd-dbus.patch
Patch004: 004-remote-fencing.patch
#Patch001: 001-xxxx.patch
Requires: resource-agents
Requires: %{pkgname_pcmk_libs}%{?_isa} = %{version}-%{release}
@ -917,16 +914,13 @@ exit 0
%license %{nagios_name}-%{nagios_hash}/COPYING
%changelog
* Wed May 21 2025 Chris Lumens <clumens@redhat.com> - 2.1.9-1.2
- Rebuild in proper build root
- Related: RHEL-92505
- Related: RHEL-92513
* Tue May 20 2025 Chris Lumens <clumens@redhat.com> - 2.1.9-1.1
- Use dbus signalling for systemd resource start/stop
- Add an option to disable default remote node fencing behavior
- Resolves: RHEL-92505
- Resolves: RHEL-92513
* Mon Jun 23 2025 Chris Lumens <clumens@redhat.com> - 2.1.10-1
- Rebase on upstream 2.1.10-rc1 release
- Use dbus to detect completion of systemd resource start/stop actions
- Add an option for controlling remote node fencing behavior
- Related: RHEL-89134
- Resolves: RHEL-86143
- Resolves: RHEL-84018
* Fri Nov 1 2024 Chris Lumens <clumens@redhat.com> - 2.1.9-1
- Rebase on upstream 2.1.9 final release