84f732910e
... with selected backports from upstream master branch - Resolves: rhbz2011974
2477 lines
89 KiB
Diff
2477 lines
89 KiB
Diff
From 8e6362cb2129bd56f817d449a195f3da87a545fa Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Fri, 12 Nov 2021 14:28:56 -0600
|
|
Subject: [PATCH 01/13] Refactor: libcrmcommon,fencer: convenience macro for
|
|
initializing results
|
|
|
|
for future reuse
|
|
---
|
|
daemons/fenced/fenced_commands.c | 14 ++------------
|
|
include/crm/common/results_internal.h | 15 +++++++++++++++
|
|
2 files changed, 17 insertions(+), 12 deletions(-)
|
|
|
|
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
|
|
index 87600573e..9f2f1cc40 100644
|
|
--- a/daemons/fenced/fenced_commands.c
|
|
+++ b/daemons/fenced/fenced_commands.c
|
|
@@ -388,12 +388,7 @@ static void
|
|
report_internal_result(async_command_t *cmd, int exit_status,
|
|
int execution_status, const char *exit_reason)
|
|
{
|
|
- pcmk__action_result_t result = {
|
|
- // Ensure we don't pass garbage to free()
|
|
- .exit_reason = NULL,
|
|
- .action_stdout = NULL,
|
|
- .action_stderr = NULL
|
|
- };
|
|
+ pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
|
|
|
|
pcmk__set_result(&result, exit_status, execution_status, exit_reason);
|
|
cmd->done_cb(0, &result, cmd);
|
|
@@ -2616,12 +2611,7 @@ stonith_fence_get_devices_cb(GList * devices, void *user_data)
|
|
}
|
|
|
|
if (device == NULL) { // No device found
|
|
- pcmk__action_result_t result = {
|
|
- // Ensure we don't pass garbage to free()
|
|
- .exit_reason = NULL,
|
|
- .action_stdout = NULL,
|
|
- .action_stderr = NULL
|
|
- };
|
|
+ pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
|
|
|
|
pcmk__set_result(&result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE,
|
|
"No fence device configured for target");
|
|
diff --git a/include/crm/common/results_internal.h b/include/crm/common/results_internal.h
|
|
index 804bf2a7a..6befaa0ed 100644
|
|
--- a/include/crm/common/results_internal.h
|
|
+++ b/include/crm/common/results_internal.h
|
|
@@ -30,6 +30,21 @@ typedef struct {
|
|
char *action_stderr; // Action error output
|
|
} pcmk__action_result_t;
|
|
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Static initialization for an action result
|
|
+ *
|
|
+ * \note Importantly, this ensures pcmk__reset_result() won't try to free
|
|
+ * garbage.
|
|
+ */
|
|
+#define PCMK__UNKNOWN_RESULT { \
|
|
+ .exit_status = CRM_EX_OK, \
|
|
+ .execution_status = PCMK_EXEC_UNKNOWN, \
|
|
+ .exit_reason = NULL, \
|
|
+ .action_stdout = NULL, \
|
|
+ .action_stderr = NULL, \
|
|
+ }
|
|
+
|
|
void pcmk__set_result(pcmk__action_result_t *result, int exit_status,
|
|
enum pcmk_exec_status exec_status,
|
|
const char *exit_reason);
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 0937c92476ac737a5f5146932824bde8bdd7db98 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Fri, 12 Nov 2021 16:02:27 -0600
|
|
Subject: [PATCH 02/13] Refactor: various: add convenience function for
|
|
checking result success
|
|
|
|
A successful pcmk__action_result_t has both exit status CRM_EX_OK (a.k.a
|
|
PCMK_OCF_OK) and execution status PCMK_EXEC_DONE. Since checking that is
|
|
clunky, we sometimes just check exit status, which is less than ideal.
|
|
|
|
The convenience function makes it easy to check both, and improves readability.
|
|
---
|
|
daemons/controld/controld_remote_ra.c | 4 ++--
|
|
daemons/execd/execd_commands.c | 12 ++++++------
|
|
daemons/fenced/fenced_commands.c | 14 ++++++--------
|
|
include/crm/common/results_internal.h | 16 ++++++++++++++++
|
|
lib/fencing/st_client.c | 4 ++--
|
|
lib/fencing/st_rhcs.c | 2 +-
|
|
6 files changed, 33 insertions(+), 19 deletions(-)
|
|
|
|
diff --git a/daemons/controld/controld_remote_ra.c b/daemons/controld/controld_remote_ra.c
|
|
index 74cbfd673..55ac162c7 100644
|
|
--- a/daemons/controld/controld_remote_ra.c
|
|
+++ b/daemons/controld/controld_remote_ra.c
|
|
@@ -297,7 +297,7 @@ static void
|
|
check_remote_node_state(remote_ra_cmd_t *cmd)
|
|
{
|
|
/* Only successful actions can change node state */
|
|
- if (cmd->result.exit_status != PCMK_OCF_OK) {
|
|
+ if (!pcmk__result_ok(&(cmd->result))) {
|
|
return;
|
|
}
|
|
|
|
@@ -365,7 +365,7 @@ report_remote_ra_result(remote_ra_cmd_t * cmd)
|
|
lrmd__set_result(&op, cmd->result.exit_status, cmd->result.execution_status,
|
|
cmd->result.exit_reason);
|
|
|
|
- if (cmd->reported_success && (cmd->result.exit_status != PCMK_OCF_OK)) {
|
|
+ if (cmd->reported_success && !pcmk__result_ok(&(cmd->result))) {
|
|
op.t_rcchange = (unsigned int) time(NULL);
|
|
/* This edge case will likely never ever occur, but if it does the
|
|
* result is that a failure will not be processed correctly. This is only
|
|
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
|
|
index 667525039..02070bf11 100644
|
|
--- a/daemons/execd/execd_commands.c
|
|
+++ b/daemons/execd/execd_commands.c
|
|
@@ -878,7 +878,7 @@ action_complete(svc_action_t * action)
|
|
}
|
|
|
|
if (pcmk__str_eq(rclass, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) {
|
|
- if ((cmd->result.exit_status == PCMK_OCF_OK)
|
|
+ if (pcmk__result_ok(&(cmd->result))
|
|
&& pcmk__strcase_any_of(cmd->action, "start", "stop", NULL)) {
|
|
/* systemd returns from start and stop actions after the action
|
|
* begins, not after it completes. We have to jump through a few
|
|
@@ -894,7 +894,7 @@ action_complete(svc_action_t * action)
|
|
if (cmd->result.execution_status == PCMK_EXEC_PENDING) {
|
|
goagain = true;
|
|
|
|
- } else if ((cmd->result.exit_status == PCMK_OCF_OK)
|
|
+ } else if (pcmk__result_ok(&(cmd->result))
|
|
&& pcmk__str_eq(cmd->real_action, "stop", pcmk__str_casei)) {
|
|
goagain = true;
|
|
|
|
@@ -927,12 +927,12 @@ action_complete(svc_action_t * action)
|
|
#if SUPPORT_NAGIOS
|
|
if (rsc && pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_NAGIOS, pcmk__str_casei)) {
|
|
if (action_matches(cmd, "monitor", 0)
|
|
- && (cmd->result.exit_status == PCMK_OCF_OK)) {
|
|
+ && pcmk__result_ok(&(cmd->result))) {
|
|
/* Successfully executed --version for the nagios plugin */
|
|
cmd->result.exit_status = PCMK_OCF_NOT_RUNNING;
|
|
|
|
} else if (pcmk__str_eq(cmd->action, "start", pcmk__str_casei)
|
|
- && (cmd->result.exit_status != PCMK_OCF_OK)) {
|
|
+ && !pcmk__result_ok(&(cmd->result))) {
|
|
#ifdef PCMK__TIME_USE_CGT
|
|
goagain = true;
|
|
#endif
|
|
@@ -955,7 +955,7 @@ action_complete(svc_action_t * action)
|
|
cmd->start_delay = delay;
|
|
cmd->timeout = timeout_left;
|
|
|
|
- if (cmd->result.exit_status == PCMK_OCF_OK) {
|
|
+ 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);
|
|
|
|
@@ -1066,7 +1066,7 @@ stonith_action_complete(lrmd_cmd_t * cmd, int rc)
|
|
cmd->interval_ms, rc);
|
|
|
|
// Certain successful actions change the known state of the resource
|
|
- if ((rsc != NULL) && (cmd->result.exit_status == PCMK_OCF_OK)) {
|
|
+ if ((rsc != NULL) && pcmk__result_ok(&(cmd->result))) {
|
|
if (pcmk__str_eq(cmd->action, "start", pcmk__str_casei)) {
|
|
rsc->st_probe_rc = pcmk_ok; // maps to PCMK_OCF_OK
|
|
} else if (pcmk__str_eq(cmd->action, "stop", pcmk__str_casei)) {
|
|
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
|
|
index 9f2f1cc40..26501a4b3 100644
|
|
--- a/daemons/fenced/fenced_commands.c
|
|
+++ b/daemons/fenced/fenced_commands.c
|
|
@@ -1188,8 +1188,7 @@ dynamic_list_search_cb(int pid, const pcmk__action_result_t *result,
|
|
|
|
mainloop_set_trigger(dev->work);
|
|
|
|
- if ((result->execution_status == PCMK_EXEC_DONE)
|
|
- && (result->exit_status == CRM_EX_OK)) {
|
|
+ if (pcmk__result_ok(result)) {
|
|
crm_info("Refreshing target list for %s", dev->id);
|
|
g_list_free_full(dev->targets, free);
|
|
dev->targets = stonith__parse_targets(result->action_stdout);
|
|
@@ -2310,15 +2309,14 @@ log_async_result(async_command_t *cmd, const pcmk__action_result_t *result,
|
|
GString *msg = g_string_sized_new(80); // Reasonable starting size
|
|
|
|
// Choose log levels appropriately if we have a result
|
|
- if ((result->execution_status == PCMK_EXEC_DONE)
|
|
- && (result->exit_status == CRM_EX_OK)) { // Success
|
|
+ if (pcmk__result_ok(result)) {
|
|
log_level = (cmd->victim == NULL)? LOG_DEBUG : LOG_NOTICE;
|
|
if ((result->action_stdout != NULL)
|
|
&& !pcmk__str_eq(cmd->action, "metadata", pcmk__str_casei)) {
|
|
output_log_level = LOG_DEBUG;
|
|
}
|
|
next = NULL;
|
|
- } else { // Failure
|
|
+ } else {
|
|
log_level = (cmd->victim == NULL)? LOG_NOTICE : LOG_ERR;
|
|
if ((result->action_stdout != NULL)
|
|
&& !pcmk__str_eq(cmd->action, "metadata", pcmk__str_casei)) {
|
|
@@ -2482,7 +2480,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
|
|
/* The device is ready to do something else now */
|
|
device = g_hash_table_lookup(device_list, cmd->device);
|
|
if (device) {
|
|
- if (!device->verified && (result->exit_status == CRM_EX_OK) &&
|
|
+ if (!device->verified && pcmk__result_ok(result) &&
|
|
(pcmk__strcase_any_of(cmd->action, "list", "monitor", "status", NULL))) {
|
|
|
|
device->verified = TRUE;
|
|
@@ -2491,7 +2489,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
|
|
mainloop_set_trigger(device->work);
|
|
}
|
|
|
|
- if (result->exit_status == CRM_EX_OK) {
|
|
+ if (pcmk__result_ok(result)) {
|
|
GList *iter;
|
|
/* see if there are any required devices left to execute for this op */
|
|
for (iter = cmd->device_next; iter != NULL; iter = iter->next) {
|
|
@@ -2523,7 +2521,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data)
|
|
|
|
send_async_reply(cmd, result, pid, false);
|
|
|
|
- if (result->exit_status != CRM_EX_OK) {
|
|
+ if (!pcmk__result_ok(result)) {
|
|
goto done;
|
|
}
|
|
|
|
diff --git a/include/crm/common/results_internal.h b/include/crm/common/results_internal.h
|
|
index 6befaa0ed..0c5833937 100644
|
|
--- a/include/crm/common/results_internal.h
|
|
+++ b/include/crm/common/results_internal.h
|
|
@@ -54,4 +54,20 @@ void pcmk__set_result_output(pcmk__action_result_t *result,
|
|
|
|
void pcmk__reset_result(pcmk__action_result_t *result);
|
|
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Check whether a result is OK
|
|
+ *
|
|
+ * \param[in] result
|
|
+ *
|
|
+ * \return true if the result's exit status is CRM_EX_OK and its
|
|
+ * execution status is PCMK_EXEC_DONE, otherwise false
|
|
+ */
|
|
+static inline bool
|
|
+pcmk__result_ok(const pcmk__action_result_t *result)
|
|
+{
|
|
+ return (result != NULL) && (result->exit_status == CRM_EX_OK)
|
|
+ && (result->execution_status == PCMK_EXEC_DONE);
|
|
+}
|
|
+
|
|
#endif // PCMK__COMMON_RESULTS_INTERNAL__H
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index 2fbff7f24..af461d0d4 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -760,7 +760,7 @@ stonith__result2rc(const pcmk__action_result_t *result)
|
|
default: break;
|
|
}
|
|
|
|
- if (result->exit_status == CRM_EX_OK) {
|
|
+ if (pcmk__result_ok(result)) {
|
|
return pcmk_rc_ok;
|
|
}
|
|
|
|
@@ -797,7 +797,7 @@ stonith_action_async_done(svc_action_t *svc_action)
|
|
|
|
log_action(action, action->pid);
|
|
|
|
- if ((action->result.exit_status != CRM_EX_OK)
|
|
+ if (!pcmk__result_ok(&(action->result))
|
|
&& update_remaining_timeout(action)) {
|
|
|
|
int rc = internal_stonith_action_execute(action);
|
|
diff --git a/lib/fencing/st_rhcs.c b/lib/fencing/st_rhcs.c
|
|
index 6c8cbedc7..865e04bc2 100644
|
|
--- a/lib/fencing/st_rhcs.c
|
|
+++ b/lib/fencing/st_rhcs.c
|
|
@@ -148,7 +148,7 @@ stonith__rhcs_get_metadata(const char *agent, int timeout, xmlNode **metadata)
|
|
return rc;
|
|
}
|
|
|
|
- if (result->exit_status != CRM_EX_OK) {
|
|
+ if (!pcmk__result_ok(result)) {
|
|
crm_warn("Metadata action for %s returned error code %d",
|
|
agent, result->exit_status);
|
|
rc = pcmk_rc2legacy(stonith__result2rc(result));
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 4c39ff00a0c028354a9da7f80986f7e34b05ba08 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Fri, 12 Nov 2021 16:07:01 -0600
|
|
Subject: [PATCH 03/13] Low: fencing: improve mapping of execution status to
|
|
legacy return code
|
|
|
|
PCMK_EXEC_PENDING is likely not possible with the current code, but map it to
|
|
EINPROGRESS for completeness.
|
|
|
|
PCMK_EXEC_INVALID is not yet used by the fencer but will be.
|
|
---
|
|
lib/fencing/st_client.c | 30 ++++++++++++++++++++++++++----
|
|
1 file changed, 26 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index af461d0d4..93513e9f3 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -749,7 +749,12 @@ update_remaining_timeout(stonith_action_t * action)
|
|
int
|
|
stonith__result2rc(const pcmk__action_result_t *result)
|
|
{
|
|
+ if (pcmk__result_ok(result)) {
|
|
+ return pcmk_rc_ok;
|
|
+ }
|
|
+
|
|
switch (result->execution_status) {
|
|
+ case PCMK_EXEC_PENDING: return EINPROGRESS;
|
|
case PCMK_EXEC_CANCELLED: return ECANCELED;
|
|
case PCMK_EXEC_TIMEOUT: return ETIME;
|
|
case PCMK_EXEC_NOT_INSTALLED: return ENOENT;
|
|
@@ -757,11 +762,28 @@ stonith__result2rc(const pcmk__action_result_t *result)
|
|
case PCMK_EXEC_NOT_CONNECTED: return ENOTCONN;
|
|
case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
|
|
case PCMK_EXEC_NO_SECRETS: return EACCES;
|
|
- default: break;
|
|
- }
|
|
|
|
- if (pcmk__result_ok(result)) {
|
|
- return pcmk_rc_ok;
|
|
+ /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
|
|
+ * operations that don't involve executing an agent (for example,
|
|
+ * registering devices). This allows us to use the CRM_EX_* codes in the
|
|
+ * exit status for finer-grained responses.
|
|
+ */
|
|
+ case PCMK_EXEC_INVALID:
|
|
+ switch (result->exit_status) {
|
|
+ case CRM_EX_INSUFFICIENT_PRIV: return EACCES;
|
|
+ case CRM_EX_PROTOCOL: return EPROTO;
|
|
+
|
|
+ /* CRM_EX_EXPIRED is used for orphaned fencing operations left
|
|
+ * over from a previous instance of the fencer. For API backward
|
|
+ * compatibility, this is mapped to the previously used code for
|
|
+ * this case, EHOSTUNREACH.
|
|
+ */
|
|
+ case CRM_EX_EXPIRED: return EHOSTUNREACH;
|
|
+ default: break;
|
|
+ }
|
|
+
|
|
+ default:
|
|
+ break;
|
|
}
|
|
|
|
// Try to provide useful error code based on result's error output
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 4e638783d1cd7c9398a603fc6df7e9d868262b16 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Thu, 18 Nov 2021 11:41:12 -0600
|
|
Subject: [PATCH 04/13] Refactor: libstonithd: separate action-related code
|
|
into own source file
|
|
|
|
Everything related to stonith_action_t has been moved from st_client.c to a new
|
|
st_actions.c, since st_client.c was ridiculously large, and the action stuff
|
|
isn't all client-related. No code was changed.
|
|
|
|
Before:
|
|
2804 st_client.c
|
|
|
|
After:
|
|
545 lib/fencing/st_actions.c
|
|
2278 lib/fencing/st_client.c
|
|
---
|
|
lib/fencing/Makefile.am | 2 +-
|
|
lib/fencing/st_actions.c | 545 +++++++++++++++++++++++++++++++++++++++
|
|
lib/fencing/st_client.c | 528 +------------------------------------
|
|
3 files changed, 547 insertions(+), 528 deletions(-)
|
|
create mode 100644 lib/fencing/st_actions.c
|
|
|
|
diff --git a/lib/fencing/Makefile.am b/lib/fencing/Makefile.am
|
|
index 205c4873d..dac215c16 100644
|
|
--- a/lib/fencing/Makefile.am
|
|
+++ b/lib/fencing/Makefile.am
|
|
@@ -22,7 +22,7 @@ libstonithd_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB)
|
|
libstonithd_la_LIBADD = $(top_builddir)/lib/common/libcrmcommon.la
|
|
libstonithd_la_LIBADD += $(top_builddir)/lib/services/libcrmservice.la
|
|
|
|
-libstonithd_la_SOURCES = st_client.c st_output.c st_rhcs.c
|
|
+libstonithd_la_SOURCES = st_actions.c st_client.c st_output.c st_rhcs.c
|
|
if BUILD_LHA_SUPPORT
|
|
libstonithd_la_SOURCES += st_lha.c
|
|
endif
|
|
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
|
|
new file mode 100644
|
|
index 000000000..64d3afd5d
|
|
--- /dev/null
|
|
+++ b/lib/fencing/st_actions.c
|
|
@@ -0,0 +1,545 @@
|
|
+/*
|
|
+ * Copyright 2004-2021 the Pacemaker project contributors
|
|
+ *
|
|
+ * The version control history for this file may have further details.
|
|
+ *
|
|
+ * This source code is licensed under the GNU Lesser General Public License
|
|
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
|
|
+ */
|
|
+
|
|
+#include <crm_internal.h>
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <libgen.h>
|
|
+#include <inttypes.h>
|
|
+#include <sys/types.h>
|
|
+#include <glib.h>
|
|
+
|
|
+#include <crm/crm.h>
|
|
+#include <crm/stonith-ng.h>
|
|
+#include <crm/fencing/internal.h>
|
|
+#include <crm/msg_xml.h>
|
|
+#include <crm/services_internal.h>
|
|
+
|
|
+#include "fencing_private.h"
|
|
+
|
|
+struct stonith_action_s {
|
|
+ /*! user defined data */
|
|
+ char *agent;
|
|
+ char *action;
|
|
+ char *victim;
|
|
+ GHashTable *args;
|
|
+ int timeout;
|
|
+ int async;
|
|
+ void *userdata;
|
|
+ void (*done_cb) (int pid, const pcmk__action_result_t *result,
|
|
+ void *user_data);
|
|
+ void (*fork_cb) (int pid, void *user_data);
|
|
+
|
|
+ svc_action_t *svc_action;
|
|
+
|
|
+ /*! internal timing information */
|
|
+ time_t initial_start_time;
|
|
+ int tries;
|
|
+ int remaining_timeout;
|
|
+ int max_retries;
|
|
+
|
|
+ int pid;
|
|
+ pcmk__action_result_t result;
|
|
+};
|
|
+
|
|
+static int internal_stonith_action_execute(stonith_action_t *action);
|
|
+static void log_action(stonith_action_t *action, pid_t pid);
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Set an action's result based on services library result
|
|
+ *
|
|
+ * \param[in] action Fence action to set result for
|
|
+ * \param[in] svc_action Service action to get result from
|
|
+ */
|
|
+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));
|
|
+ pcmk__set_result_output(&(action->result),
|
|
+ services__grab_stdout(svc_action),
|
|
+ services__grab_stderr(svc_action));
|
|
+}
|
|
+
|
|
+static void
|
|
+log_action(stonith_action_t *action, pid_t pid)
|
|
+{
|
|
+ /* The services library has already logged the output at info or debug
|
|
+ * level, so just raise to warning for stderr.
|
|
+ */
|
|
+ if (action->result.action_stderr != NULL) {
|
|
+ /* Logging the whole string confuses syslog when the string is xml */
|
|
+ char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
|
|
+
|
|
+ crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
|
|
+ free(prefix);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+append_config_arg(gpointer key, gpointer value, gpointer user_data)
|
|
+{
|
|
+ /* The fencer will filter "action" out when it registers the device,
|
|
+ * but ignore it here in case any external API users don't.
|
|
+ *
|
|
+ * Also filter out parameters handled directly by Pacemaker.
|
|
+ */
|
|
+ if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
|
|
+ && !pcmk_stonith_param(key)
|
|
+ && (strstr(key, CRM_META) == NULL)
|
|
+ && !pcmk__str_eq(key, "crm_feature_set", pcmk__str_casei)) {
|
|
+
|
|
+ crm_trace("Passing %s=%s with fence action",
|
|
+ (const char *) key, (const char *) (value? value : ""));
|
|
+ g_hash_table_insert((GHashTable *) user_data,
|
|
+ strdup(key), strdup(value? value : ""));
|
|
+ }
|
|
+}
|
|
+
|
|
+static GHashTable *
|
|
+make_args(const char *agent, const char *action, const char *victim,
|
|
+ uint32_t victim_nodeid, GHashTable * device_args,
|
|
+ GHashTable * port_map, const char *host_arg)
|
|
+{
|
|
+ GHashTable *arg_list = NULL;
|
|
+ const char *value = NULL;
|
|
+
|
|
+ CRM_CHECK(action != NULL, return NULL);
|
|
+
|
|
+ arg_list = pcmk__strkey_table(free, free);
|
|
+
|
|
+ // Add action to arguments (using an alias if requested)
|
|
+ if (device_args) {
|
|
+ char buffer[512];
|
|
+
|
|
+ snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
|
|
+ value = g_hash_table_lookup(device_args, buffer);
|
|
+ if (value) {
|
|
+ crm_debug("Substituting '%s' for fence action %s targeting %s",
|
|
+ value, action, victim);
|
|
+ action = value;
|
|
+ }
|
|
+ }
|
|
+ g_hash_table_insert(arg_list, strdup(STONITH_ATTR_ACTION_OP),
|
|
+ strdup(action));
|
|
+
|
|
+ /* If this is a fencing operation against another node, add more standard
|
|
+ * arguments.
|
|
+ */
|
|
+ if (victim && device_args) {
|
|
+ const char *param = NULL;
|
|
+
|
|
+ /* Always pass the target's name, per
|
|
+ * https://github.com/ClusterLabs/fence-agents/blob/master/doc/FenceAgentAPI.md
|
|
+ */
|
|
+ g_hash_table_insert(arg_list, strdup("nodename"), strdup(victim));
|
|
+
|
|
+ // If the target's node ID was specified, pass it, too
|
|
+ if (victim_nodeid) {
|
|
+ char *nodeid = crm_strdup_printf("%" PRIu32, victim_nodeid);
|
|
+
|
|
+ // cts-fencing looks for this log message
|
|
+ crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
|
|
+ nodeid, action, victim);
|
|
+ g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
|
|
+ }
|
|
+
|
|
+ // Check whether target must be specified in some other way
|
|
+ param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
|
|
+ if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
|
|
+ && !pcmk__str_eq(param, "none", pcmk__str_casei)) {
|
|
+
|
|
+ if (param == NULL) {
|
|
+ /* Use the caller's default for pcmk_host_argument, or "port" if
|
|
+ * none was given
|
|
+ */
|
|
+ param = (host_arg == NULL)? "port" : host_arg;
|
|
+ }
|
|
+ value = g_hash_table_lookup(device_args, param);
|
|
+
|
|
+ if (pcmk__str_eq(value, "dynamic",
|
|
+ pcmk__str_casei|pcmk__str_null_matches)) {
|
|
+ /* If the host argument was "dynamic" or not explicitly specified,
|
|
+ * add it with the target
|
|
+ */
|
|
+ const char *alias = NULL;
|
|
+
|
|
+ if (port_map) {
|
|
+ alias = g_hash_table_lookup(port_map, victim);
|
|
+ }
|
|
+ if (alias == NULL) {
|
|
+ alias = victim;
|
|
+ }
|
|
+ crm_debug("Passing %s='%s' with fence action %s targeting %s",
|
|
+ param, alias, action, victim);
|
|
+ g_hash_table_insert(arg_list, strdup(param), strdup(alias));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (device_args) {
|
|
+ g_hash_table_foreach(device_args, append_config_arg, arg_list);
|
|
+ }
|
|
+
|
|
+ return arg_list;
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Free all memory used by a stonith action
|
|
+ *
|
|
+ * \param[in,out] action Action to free
|
|
+ */
|
|
+void
|
|
+stonith__destroy_action(stonith_action_t *action)
|
|
+{
|
|
+ if (action) {
|
|
+ free(action->agent);
|
|
+ if (action->args) {
|
|
+ g_hash_table_destroy(action->args);
|
|
+ }
|
|
+ free(action->action);
|
|
+ free(action->victim);
|
|
+ if (action->svc_action) {
|
|
+ services_action_free(action->svc_action);
|
|
+ }
|
|
+ pcmk__reset_result(&(action->result));
|
|
+ free(action);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Get the result of an executed stonith action
|
|
+ *
|
|
+ * \param[in] action Executed action
|
|
+ *
|
|
+ * \return Pointer to action's result (or NULL if \p action is NULL)
|
|
+ */
|
|
+pcmk__action_result_t *
|
|
+stonith__action_result(stonith_action_t *action)
|
|
+{
|
|
+ return (action == NULL)? NULL : &(action->result);
|
|
+}
|
|
+
|
|
+#define FAILURE_MAX_RETRIES 2
|
|
+stonith_action_t *
|
|
+stonith_action_create(const char *agent,
|
|
+ const char *_action,
|
|
+ const char *victim,
|
|
+ uint32_t victim_nodeid,
|
|
+ int timeout, GHashTable * device_args,
|
|
+ GHashTable * port_map, const char *host_arg)
|
|
+{
|
|
+ stonith_action_t *action;
|
|
+
|
|
+ action = calloc(1, sizeof(stonith_action_t));
|
|
+ action->args = make_args(agent, _action, victim, victim_nodeid,
|
|
+ device_args, port_map, host_arg);
|
|
+ crm_debug("Preparing '%s' action for %s using agent %s",
|
|
+ _action, (victim? victim : "no target"), agent);
|
|
+ action->agent = strdup(agent);
|
|
+ action->action = strdup(_action);
|
|
+ if (victim) {
|
|
+ action->victim = strdup(victim);
|
|
+ }
|
|
+ action->timeout = action->remaining_timeout = timeout;
|
|
+ action->max_retries = FAILURE_MAX_RETRIES;
|
|
+
|
|
+ pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
|
|
+ "Initialization bug in fencing library");
|
|
+
|
|
+ if (device_args) {
|
|
+ char buffer[512];
|
|
+ const char *value = NULL;
|
|
+
|
|
+ snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", _action);
|
|
+ value = g_hash_table_lookup(device_args, buffer);
|
|
+
|
|
+ if (value) {
|
|
+ action->max_retries = atoi(value);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return action;
|
|
+}
|
|
+
|
|
+static gboolean
|
|
+update_remaining_timeout(stonith_action_t * action)
|
|
+{
|
|
+ int diff = time(NULL) - action->initial_start_time;
|
|
+
|
|
+ if (action->tries >= action->max_retries) {
|
|
+ crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
|
|
+ action->agent, action->action, action->max_retries);
|
|
+ action->remaining_timeout = 0;
|
|
+ } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
|
|
+ && (diff < (action->timeout * 0.7))) {
|
|
+ /* only set remaining timeout period if there is 30%
|
|
+ * or greater of the original timeout period left */
|
|
+ action->remaining_timeout = action->timeout - diff;
|
|
+ } else {
|
|
+ action->remaining_timeout = 0;
|
|
+ }
|
|
+ return action->remaining_timeout ? TRUE : FALSE;
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Map a fencing action result to a standard return code
|
|
+ *
|
|
+ * \param[in] result Fencing action result to map
|
|
+ *
|
|
+ * \return Standard Pacemaker return code that best corresponds to \p result
|
|
+ */
|
|
+int
|
|
+stonith__result2rc(const pcmk__action_result_t *result)
|
|
+{
|
|
+ if (pcmk__result_ok(result)) {
|
|
+ return pcmk_rc_ok;
|
|
+ }
|
|
+
|
|
+ switch (result->execution_status) {
|
|
+ case PCMK_EXEC_PENDING: return EINPROGRESS;
|
|
+ case PCMK_EXEC_CANCELLED: return ECANCELED;
|
|
+ case PCMK_EXEC_TIMEOUT: return ETIME;
|
|
+ case PCMK_EXEC_NOT_INSTALLED: return ENOENT;
|
|
+ case PCMK_EXEC_NOT_SUPPORTED: return EOPNOTSUPP;
|
|
+ case PCMK_EXEC_NOT_CONNECTED: return ENOTCONN;
|
|
+ case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
|
|
+ case PCMK_EXEC_NO_SECRETS: return EACCES;
|
|
+
|
|
+ /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
|
|
+ * operations that don't involve executing an agent (for example,
|
|
+ * registering devices). This allows us to use the CRM_EX_* codes in the
|
|
+ * exit status for finer-grained responses.
|
|
+ */
|
|
+ case PCMK_EXEC_INVALID:
|
|
+ switch (result->exit_status) {
|
|
+ case CRM_EX_INSUFFICIENT_PRIV: return EACCES;
|
|
+ case CRM_EX_PROTOCOL: return EPROTO;
|
|
+
|
|
+ /* CRM_EX_EXPIRED is used for orphaned fencing operations left
|
|
+ * over from a previous instance of the fencer. For API backward
|
|
+ * compatibility, this is mapped to the previously used code for
|
|
+ * this case, EHOSTUNREACH.
|
|
+ */
|
|
+ case CRM_EX_EXPIRED: return EHOSTUNREACH;
|
|
+ default: break;
|
|
+ }
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ // Try to provide useful error code based on result's error output
|
|
+
|
|
+ if (result->action_stderr == NULL) {
|
|
+ return ENODATA;
|
|
+
|
|
+ } else if (strcasestr(result->action_stderr, "timed out")
|
|
+ || strcasestr(result->action_stderr, "timeout")) {
|
|
+ return ETIME;
|
|
+
|
|
+ } else if (strcasestr(result->action_stderr, "unrecognised action")
|
|
+ || strcasestr(result->action_stderr, "unrecognized action")
|
|
+ || strcasestr(result->action_stderr, "unsupported action")) {
|
|
+ return EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ // Oh well, we tried
|
|
+ return pcmk_rc_error;
|
|
+}
|
|
+
|
|
+static void
|
|
+stonith_action_async_done(svc_action_t *svc_action)
|
|
+{
|
|
+ stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
|
|
+
|
|
+ set_result_from_svc_action(action, svc_action);
|
|
+
|
|
+ svc_action->params = NULL;
|
|
+
|
|
+ crm_debug("Child process %d performing action '%s' exited with rc %d",
|
|
+ action->pid, action->action, svc_action->rc);
|
|
+
|
|
+ log_action(action, action->pid);
|
|
+
|
|
+ if (!pcmk__result_ok(&(action->result))
|
|
+ && update_remaining_timeout(action)) {
|
|
+
|
|
+ int rc = internal_stonith_action_execute(action);
|
|
+ if (rc == pcmk_ok) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (action->done_cb) {
|
|
+ action->done_cb(action->pid, &(action->result), action->userdata);
|
|
+ }
|
|
+
|
|
+ action->svc_action = NULL; // don't remove our caller
|
|
+ stonith__destroy_action(action);
|
|
+}
|
|
+
|
|
+static void
|
|
+stonith_action_async_forked(svc_action_t *svc_action)
|
|
+{
|
|
+ stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
|
|
+
|
|
+ action->pid = svc_action->pid;
|
|
+ action->svc_action = svc_action;
|
|
+
|
|
+ if (action->fork_cb) {
|
|
+ (action->fork_cb) (svc_action->pid, action->userdata);
|
|
+ }
|
|
+
|
|
+ crm_trace("Child process %d performing action '%s' successfully forked",
|
|
+ action->pid, action->action);
|
|
+}
|
|
+
|
|
+static int
|
|
+internal_stonith_action_execute(stonith_action_t * action)
|
|
+{
|
|
+ int rc = -EPROTO;
|
|
+ int is_retry = 0;
|
|
+ svc_action_t *svc_action = NULL;
|
|
+ static int stonith_sequence = 0;
|
|
+ char *buffer = NULL;
|
|
+
|
|
+ CRM_CHECK(action != NULL, return -EINVAL);
|
|
+
|
|
+ if ((action->action == NULL) || (action->args == NULL)
|
|
+ || (action->agent == NULL)) {
|
|
+ pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
|
|
+ PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (!action->tries) {
|
|
+ action->initial_start_time = time(NULL);
|
|
+ }
|
|
+ action->tries++;
|
|
+
|
|
+ if (action->tries > 1) {
|
|
+ crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
|
|
+ action->tries, action->agent, action->action, action->remaining_timeout);
|
|
+ is_retry = 1;
|
|
+ }
|
|
+
|
|
+ buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
|
|
+ basename(action->agent));
|
|
+ svc_action = services_action_create_generic(buffer, NULL);
|
|
+ free(buffer);
|
|
+
|
|
+ if (svc_action->rc != PCMK_OCF_UNKNOWN) {
|
|
+ set_result_from_svc_action(action, svc_action);
|
|
+ services_action_free(svc_action);
|
|
+ return -E2BIG;
|
|
+ }
|
|
+
|
|
+ svc_action->timeout = 1000 * action->remaining_timeout;
|
|
+ svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
|
|
+ svc_action->id = crm_strdup_printf("%s_%s_%d", basename(action->agent),
|
|
+ action->action, action->tries);
|
|
+ svc_action->agent = strdup(action->agent);
|
|
+ svc_action->sequence = stonith_sequence++;
|
|
+ svc_action->params = action->args;
|
|
+ svc_action->cb_data = (void *) action;
|
|
+ svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
|
|
+ LOG_TRACE, "Action",
|
|
+ svc_action->id, svc_action->flags,
|
|
+ SVC_ACTION_NON_BLOCKED,
|
|
+ "SVC_ACTION_NON_BLOCKED");
|
|
+
|
|
+ /* keep retries from executing out of control and free previous results */
|
|
+ if (is_retry) {
|
|
+ pcmk__reset_result(&(action->result));
|
|
+ sleep(1);
|
|
+ }
|
|
+
|
|
+ if (action->async) {
|
|
+ /* async */
|
|
+ if (services_action_async_fork_notify(svc_action,
|
|
+ &stonith_action_async_done,
|
|
+ &stonith_action_async_forked)) {
|
|
+ pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN,
|
|
+ PCMK_EXEC_PENDING, NULL);
|
|
+ return pcmk_ok;
|
|
+ }
|
|
+
|
|
+ } else if (services_action_sync(svc_action)) { // sync success
|
|
+ rc = pcmk_ok;
|
|
+
|
|
+ } else { // sync failure
|
|
+ rc = -ECONNABORTED;
|
|
+ }
|
|
+
|
|
+ set_result_from_svc_action(action, svc_action);
|
|
+ svc_action->params = NULL;
|
|
+ services_action_free(svc_action);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Kick off execution of an async stonith action
|
|
+ *
|
|
+ * \param[in,out] action Action to be executed
|
|
+ * \param[in,out] userdata Datapointer to be passed to callbacks
|
|
+ * \param[in] done Callback to notify action has failed/succeeded
|
|
+ * \param[in] fork_callback Callback to notify successful fork of child
|
|
+ *
|
|
+ * \return pcmk_ok if ownership of action has been taken, -errno otherwise
|
|
+ */
|
|
+int
|
|
+stonith_action_execute_async(stonith_action_t * action,
|
|
+ void *userdata,
|
|
+ void (*done) (int pid,
|
|
+ const pcmk__action_result_t *result,
|
|
+ void *user_data),
|
|
+ void (*fork_cb) (int pid, void *user_data))
|
|
+{
|
|
+ if (!action) {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ action->userdata = userdata;
|
|
+ action->done_cb = done;
|
|
+ action->fork_cb = fork_cb;
|
|
+ action->async = 1;
|
|
+
|
|
+ return internal_stonith_action_execute(action);
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Execute a stonith action
|
|
+ *
|
|
+ * \param[in,out] action Action to execute
|
|
+ *
|
|
+ * \return pcmk_ok on success, -errno otherwise
|
|
+ */
|
|
+int
|
|
+stonith__execute(stonith_action_t *action)
|
|
+{
|
|
+ int rc = pcmk_ok;
|
|
+
|
|
+ CRM_CHECK(action != NULL, return -EINVAL);
|
|
+
|
|
+ // Keep trying until success, max retries, or timeout
|
|
+ do {
|
|
+ rc = internal_stonith_action_execute(action);
|
|
+ } while ((rc != pcmk_ok) && update_remaining_timeout(action));
|
|
+
|
|
+ return rc;
|
|
+}
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index 93513e9f3..944cd1863 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -8,28 +8,20 @@
|
|
*/
|
|
|
|
#include <crm_internal.h>
|
|
-#include <unistd.h>
|
|
+
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
-#include <libgen.h>
|
|
#include <inttypes.h>
|
|
-
|
|
-#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
-#include <sys/wait.h>
|
|
-
|
|
#include <glib.h>
|
|
|
|
#include <crm/crm.h>
|
|
#include <crm/stonith-ng.h>
|
|
#include <crm/fencing/internal.h>
|
|
#include <crm/msg_xml.h>
|
|
-#include <crm/common/xml.h>
|
|
-#include <crm/common/xml_internal.h>
|
|
-#include <crm/services_internal.h>
|
|
|
|
#include <crm/common/mainloop.h>
|
|
|
|
@@ -37,31 +29,6 @@
|
|
|
|
CRM_TRACE_INIT_DATA(stonith);
|
|
|
|
-struct stonith_action_s {
|
|
- /*! user defined data */
|
|
- char *agent;
|
|
- char *action;
|
|
- char *victim;
|
|
- GHashTable *args;
|
|
- int timeout;
|
|
- int async;
|
|
- void *userdata;
|
|
- void (*done_cb) (int pid, const pcmk__action_result_t *result,
|
|
- void *user_data);
|
|
- void (*fork_cb) (int pid, void *user_data);
|
|
-
|
|
- svc_action_t *svc_action;
|
|
-
|
|
- /*! internal timing information */
|
|
- time_t initial_start_time;
|
|
- int tries;
|
|
- int remaining_timeout;
|
|
- int max_retries;
|
|
-
|
|
- int pid;
|
|
- pcmk__action_result_t result;
|
|
-};
|
|
-
|
|
typedef struct stonith_private_s {
|
|
char *token;
|
|
crm_ipc_t *ipc;
|
|
@@ -118,8 +85,6 @@ static int stonith_send_command(stonith_t *stonith, const char *op,
|
|
|
|
static void stonith_connection_destroy(gpointer user_data);
|
|
static void stonith_send_notification(gpointer data, gpointer user_data);
|
|
-static int internal_stonith_action_execute(stonith_action_t * action);
|
|
-static void log_action(stonith_action_t *action, pid_t pid);
|
|
|
|
/*!
|
|
* \brief Get agent namespace by name
|
|
@@ -196,23 +161,6 @@ stonith_get_namespace(const char *agent, const char *namespace_s)
|
|
return st_namespace_invalid;
|
|
}
|
|
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Set an action's result based on services library result
|
|
- *
|
|
- * \param[in] action Fence action to set result for
|
|
- * \param[in] svc_action Service action to get result from
|
|
- */
|
|
-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));
|
|
- pcmk__set_result_output(&(action->result),
|
|
- services__grab_stdout(svc_action),
|
|
- services__grab_stderr(svc_action));
|
|
-}
|
|
-
|
|
gboolean
|
|
stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node)
|
|
{
|
|
@@ -273,21 +221,6 @@ stonith__watchdog_fencing_enabled_for_node(const char *node)
|
|
return stonith__watchdog_fencing_enabled_for_node_api(NULL, node);
|
|
}
|
|
|
|
-static void
|
|
-log_action(stonith_action_t *action, pid_t pid)
|
|
-{
|
|
- /* The services library has already logged the output at info or debug
|
|
- * level, so just raise to warning for stderr.
|
|
- */
|
|
- if (action->result.action_stderr != NULL) {
|
|
- /* Logging the whole string confuses syslog when the string is xml */
|
|
- char *prefix = crm_strdup_printf("%s[%d] stderr:", action->agent, pid);
|
|
-
|
|
- crm_log_output(LOG_WARNING, prefix, action->result.action_stderr);
|
|
- free(prefix);
|
|
- }
|
|
-}
|
|
-
|
|
/* when cycling through the list we don't want to delete items
|
|
so just mark them and when we know nobody is using the list
|
|
loop over it to remove the marked items
|
|
@@ -530,465 +463,6 @@ stonith_api_register_level(stonith_t * st, int options, const char *node, int le
|
|
level, device_list);
|
|
}
|
|
|
|
-static void
|
|
-append_config_arg(gpointer key, gpointer value, gpointer user_data)
|
|
-{
|
|
- /* The fencer will filter "action" out when it registers the device,
|
|
- * but ignore it here in case any external API users don't.
|
|
- *
|
|
- * Also filter out parameters handled directly by Pacemaker.
|
|
- */
|
|
- if (!pcmk__str_eq(key, STONITH_ATTR_ACTION_OP, pcmk__str_casei)
|
|
- && !pcmk_stonith_param(key)
|
|
- && (strstr(key, CRM_META) == NULL)
|
|
- && !pcmk__str_eq(key, "crm_feature_set", pcmk__str_casei)) {
|
|
-
|
|
- crm_trace("Passing %s=%s with fence action",
|
|
- (const char *) key, (const char *) (value? value : ""));
|
|
- g_hash_table_insert((GHashTable *) user_data,
|
|
- strdup(key), strdup(value? value : ""));
|
|
- }
|
|
-}
|
|
-
|
|
-static GHashTable *
|
|
-make_args(const char *agent, const char *action, const char *victim,
|
|
- uint32_t victim_nodeid, GHashTable * device_args,
|
|
- GHashTable * port_map, const char *host_arg)
|
|
-{
|
|
- GHashTable *arg_list = NULL;
|
|
- const char *value = NULL;
|
|
-
|
|
- CRM_CHECK(action != NULL, return NULL);
|
|
-
|
|
- arg_list = pcmk__strkey_table(free, free);
|
|
-
|
|
- // Add action to arguments (using an alias if requested)
|
|
- if (device_args) {
|
|
- char buffer[512];
|
|
-
|
|
- snprintf(buffer, sizeof(buffer), "pcmk_%s_action", action);
|
|
- value = g_hash_table_lookup(device_args, buffer);
|
|
- if (value) {
|
|
- crm_debug("Substituting '%s' for fence action %s targeting %s",
|
|
- value, action, victim);
|
|
- action = value;
|
|
- }
|
|
- }
|
|
- g_hash_table_insert(arg_list, strdup(STONITH_ATTR_ACTION_OP),
|
|
- strdup(action));
|
|
-
|
|
- /* If this is a fencing operation against another node, add more standard
|
|
- * arguments.
|
|
- */
|
|
- if (victim && device_args) {
|
|
- const char *param = NULL;
|
|
-
|
|
- /* Always pass the target's name, per
|
|
- * https://github.com/ClusterLabs/fence-agents/blob/master/doc/FenceAgentAPI.md
|
|
- */
|
|
- g_hash_table_insert(arg_list, strdup("nodename"), strdup(victim));
|
|
-
|
|
- // If the target's node ID was specified, pass it, too
|
|
- if (victim_nodeid) {
|
|
- char *nodeid = crm_strdup_printf("%" PRIu32, victim_nodeid);
|
|
-
|
|
- // cts-fencing looks for this log message
|
|
- crm_info("Passing '%s' as nodeid with fence action '%s' targeting %s",
|
|
- nodeid, action, victim);
|
|
- g_hash_table_insert(arg_list, strdup("nodeid"), nodeid);
|
|
- }
|
|
-
|
|
- // Check whether target must be specified in some other way
|
|
- param = g_hash_table_lookup(device_args, PCMK_STONITH_HOST_ARGUMENT);
|
|
- if (!pcmk__str_eq(agent, "fence_legacy", pcmk__str_none)
|
|
- && !pcmk__str_eq(param, "none", pcmk__str_casei)) {
|
|
-
|
|
- if (param == NULL) {
|
|
- /* Use the caller's default for pcmk_host_argument, or "port" if
|
|
- * none was given
|
|
- */
|
|
- param = (host_arg == NULL)? "port" : host_arg;
|
|
- }
|
|
- value = g_hash_table_lookup(device_args, param);
|
|
-
|
|
- if (pcmk__str_eq(value, "dynamic",
|
|
- pcmk__str_casei|pcmk__str_null_matches)) {
|
|
- /* If the host argument was "dynamic" or not explicitly specified,
|
|
- * add it with the target
|
|
- */
|
|
- const char *alias = NULL;
|
|
-
|
|
- if (port_map) {
|
|
- alias = g_hash_table_lookup(port_map, victim);
|
|
- }
|
|
- if (alias == NULL) {
|
|
- alias = victim;
|
|
- }
|
|
- crm_debug("Passing %s='%s' with fence action %s targeting %s",
|
|
- param, alias, action, victim);
|
|
- g_hash_table_insert(arg_list, strdup(param), strdup(alias));
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- if (device_args) {
|
|
- g_hash_table_foreach(device_args, append_config_arg, arg_list);
|
|
- }
|
|
-
|
|
- return arg_list;
|
|
-}
|
|
-
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Free all memory used by a stonith action
|
|
- *
|
|
- * \param[in,out] action Action to free
|
|
- */
|
|
-void
|
|
-stonith__destroy_action(stonith_action_t *action)
|
|
-{
|
|
- if (action) {
|
|
- free(action->agent);
|
|
- if (action->args) {
|
|
- g_hash_table_destroy(action->args);
|
|
- }
|
|
- free(action->action);
|
|
- free(action->victim);
|
|
- if (action->svc_action) {
|
|
- services_action_free(action->svc_action);
|
|
- }
|
|
- pcmk__reset_result(&(action->result));
|
|
- free(action);
|
|
- }
|
|
-}
|
|
-
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Get the result of an executed stonith action
|
|
- *
|
|
- * \param[in] action Executed action
|
|
- *
|
|
- * \return Pointer to action's result (or NULL if \p action is NULL)
|
|
- */
|
|
-pcmk__action_result_t *
|
|
-stonith__action_result(stonith_action_t *action)
|
|
-{
|
|
- return (action == NULL)? NULL : &(action->result);
|
|
-}
|
|
-
|
|
-#define FAILURE_MAX_RETRIES 2
|
|
-stonith_action_t *
|
|
-stonith_action_create(const char *agent,
|
|
- const char *_action,
|
|
- const char *victim,
|
|
- uint32_t victim_nodeid,
|
|
- int timeout, GHashTable * device_args,
|
|
- GHashTable * port_map, const char *host_arg)
|
|
-{
|
|
- stonith_action_t *action;
|
|
-
|
|
- action = calloc(1, sizeof(stonith_action_t));
|
|
- action->args = make_args(agent, _action, victim, victim_nodeid,
|
|
- device_args, port_map, host_arg);
|
|
- crm_debug("Preparing '%s' action for %s using agent %s",
|
|
- _action, (victim? victim : "no target"), agent);
|
|
- action->agent = strdup(agent);
|
|
- action->action = strdup(_action);
|
|
- if (victim) {
|
|
- action->victim = strdup(victim);
|
|
- }
|
|
- action->timeout = action->remaining_timeout = timeout;
|
|
- action->max_retries = FAILURE_MAX_RETRIES;
|
|
-
|
|
- pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN,
|
|
- "Initialization bug in fencing library");
|
|
-
|
|
- if (device_args) {
|
|
- char buffer[512];
|
|
- const char *value = NULL;
|
|
-
|
|
- snprintf(buffer, sizeof(buffer), "pcmk_%s_retries", _action);
|
|
- value = g_hash_table_lookup(device_args, buffer);
|
|
-
|
|
- if (value) {
|
|
- action->max_retries = atoi(value);
|
|
- }
|
|
- }
|
|
-
|
|
- return action;
|
|
-}
|
|
-
|
|
-static gboolean
|
|
-update_remaining_timeout(stonith_action_t * action)
|
|
-{
|
|
- int diff = time(NULL) - action->initial_start_time;
|
|
-
|
|
- if (action->tries >= action->max_retries) {
|
|
- crm_info("Attempted to execute agent %s (%s) the maximum number of times (%d) allowed",
|
|
- action->agent, action->action, action->max_retries);
|
|
- action->remaining_timeout = 0;
|
|
- } else if ((action->result.execution_status != PCMK_EXEC_TIMEOUT)
|
|
- && (diff < (action->timeout * 0.7))) {
|
|
- /* only set remaining timeout period if there is 30%
|
|
- * or greater of the original timeout period left */
|
|
- action->remaining_timeout = action->timeout - diff;
|
|
- } else {
|
|
- action->remaining_timeout = 0;
|
|
- }
|
|
- return action->remaining_timeout ? TRUE : FALSE;
|
|
-}
|
|
-
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Map a fencing action result to a standard return code
|
|
- *
|
|
- * \param[in] result Fencing action result to map
|
|
- *
|
|
- * \return Standard Pacemaker return code that best corresponds to \p result
|
|
- */
|
|
-int
|
|
-stonith__result2rc(const pcmk__action_result_t *result)
|
|
-{
|
|
- if (pcmk__result_ok(result)) {
|
|
- return pcmk_rc_ok;
|
|
- }
|
|
-
|
|
- switch (result->execution_status) {
|
|
- case PCMK_EXEC_PENDING: return EINPROGRESS;
|
|
- case PCMK_EXEC_CANCELLED: return ECANCELED;
|
|
- case PCMK_EXEC_TIMEOUT: return ETIME;
|
|
- case PCMK_EXEC_NOT_INSTALLED: return ENOENT;
|
|
- case PCMK_EXEC_NOT_SUPPORTED: return EOPNOTSUPP;
|
|
- case PCMK_EXEC_NOT_CONNECTED: return ENOTCONN;
|
|
- case PCMK_EXEC_NO_FENCE_DEVICE: return ENODEV;
|
|
- case PCMK_EXEC_NO_SECRETS: return EACCES;
|
|
-
|
|
- /* For the fencing API, PCMK_EXEC_INVALID is used with fencer API
|
|
- * operations that don't involve executing an agent (for example,
|
|
- * registering devices). This allows us to use the CRM_EX_* codes in the
|
|
- * exit status for finer-grained responses.
|
|
- */
|
|
- case PCMK_EXEC_INVALID:
|
|
- switch (result->exit_status) {
|
|
- case CRM_EX_INSUFFICIENT_PRIV: return EACCES;
|
|
- case CRM_EX_PROTOCOL: return EPROTO;
|
|
-
|
|
- /* CRM_EX_EXPIRED is used for orphaned fencing operations left
|
|
- * over from a previous instance of the fencer. For API backward
|
|
- * compatibility, this is mapped to the previously used code for
|
|
- * this case, EHOSTUNREACH.
|
|
- */
|
|
- case CRM_EX_EXPIRED: return EHOSTUNREACH;
|
|
- default: break;
|
|
- }
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- // Try to provide useful error code based on result's error output
|
|
-
|
|
- if (result->action_stderr == NULL) {
|
|
- return ENODATA;
|
|
-
|
|
- } else if (strcasestr(result->action_stderr, "timed out")
|
|
- || strcasestr(result->action_stderr, "timeout")) {
|
|
- return ETIME;
|
|
-
|
|
- } else if (strcasestr(result->action_stderr, "unrecognised action")
|
|
- || strcasestr(result->action_stderr, "unrecognized action")
|
|
- || strcasestr(result->action_stderr, "unsupported action")) {
|
|
- return EOPNOTSUPP;
|
|
- }
|
|
-
|
|
- // Oh well, we tried
|
|
- return pcmk_rc_error;
|
|
-}
|
|
-
|
|
-static void
|
|
-stonith_action_async_done(svc_action_t *svc_action)
|
|
-{
|
|
- stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
|
|
-
|
|
- set_result_from_svc_action(action, svc_action);
|
|
-
|
|
- svc_action->params = NULL;
|
|
-
|
|
- crm_debug("Child process %d performing action '%s' exited with rc %d",
|
|
- action->pid, action->action, svc_action->rc);
|
|
-
|
|
- log_action(action, action->pid);
|
|
-
|
|
- if (!pcmk__result_ok(&(action->result))
|
|
- && update_remaining_timeout(action)) {
|
|
-
|
|
- int rc = internal_stonith_action_execute(action);
|
|
- if (rc == pcmk_ok) {
|
|
- return;
|
|
- }
|
|
- }
|
|
-
|
|
- if (action->done_cb) {
|
|
- action->done_cb(action->pid, &(action->result), action->userdata);
|
|
- }
|
|
-
|
|
- action->svc_action = NULL; // don't remove our caller
|
|
- stonith__destroy_action(action);
|
|
-}
|
|
-
|
|
-static void
|
|
-stonith_action_async_forked(svc_action_t *svc_action)
|
|
-{
|
|
- stonith_action_t *action = (stonith_action_t *) svc_action->cb_data;
|
|
-
|
|
- action->pid = svc_action->pid;
|
|
- action->svc_action = svc_action;
|
|
-
|
|
- if (action->fork_cb) {
|
|
- (action->fork_cb) (svc_action->pid, action->userdata);
|
|
- }
|
|
-
|
|
- crm_trace("Child process %d performing action '%s' successfully forked",
|
|
- action->pid, action->action);
|
|
-}
|
|
-
|
|
-static int
|
|
-internal_stonith_action_execute(stonith_action_t * action)
|
|
-{
|
|
- int rc = -EPROTO;
|
|
- int is_retry = 0;
|
|
- svc_action_t *svc_action = NULL;
|
|
- static int stonith_sequence = 0;
|
|
- char *buffer = NULL;
|
|
-
|
|
- CRM_CHECK(action != NULL, return -EINVAL);
|
|
-
|
|
- if ((action->action == NULL) || (action->args == NULL)
|
|
- || (action->agent == NULL)) {
|
|
- pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR,
|
|
- PCMK_EXEC_ERROR_FATAL, "Bug in fencing library");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (!action->tries) {
|
|
- action->initial_start_time = time(NULL);
|
|
- }
|
|
- action->tries++;
|
|
-
|
|
- if (action->tries > 1) {
|
|
- crm_info("Attempt %d to execute %s (%s). remaining timeout is %d",
|
|
- action->tries, action->agent, action->action, action->remaining_timeout);
|
|
- is_retry = 1;
|
|
- }
|
|
-
|
|
- buffer = crm_strdup_printf(PCMK__FENCE_BINDIR "/%s",
|
|
- basename(action->agent));
|
|
- svc_action = services_action_create_generic(buffer, NULL);
|
|
- free(buffer);
|
|
-
|
|
- if (svc_action->rc != PCMK_OCF_UNKNOWN) {
|
|
- set_result_from_svc_action(action, svc_action);
|
|
- services_action_free(svc_action);
|
|
- return -E2BIG;
|
|
- }
|
|
-
|
|
- svc_action->timeout = 1000 * action->remaining_timeout;
|
|
- svc_action->standard = strdup(PCMK_RESOURCE_CLASS_STONITH);
|
|
- svc_action->id = crm_strdup_printf("%s_%s_%d", basename(action->agent),
|
|
- action->action, action->tries);
|
|
- svc_action->agent = strdup(action->agent);
|
|
- svc_action->sequence = stonith_sequence++;
|
|
- svc_action->params = action->args;
|
|
- svc_action->cb_data = (void *) action;
|
|
- svc_action->flags = pcmk__set_flags_as(__func__, __LINE__,
|
|
- LOG_TRACE, "Action",
|
|
- svc_action->id, svc_action->flags,
|
|
- SVC_ACTION_NON_BLOCKED,
|
|
- "SVC_ACTION_NON_BLOCKED");
|
|
-
|
|
- /* keep retries from executing out of control and free previous results */
|
|
- if (is_retry) {
|
|
- pcmk__reset_result(&(action->result));
|
|
- sleep(1);
|
|
- }
|
|
-
|
|
- if (action->async) {
|
|
- /* async */
|
|
- if (services_action_async_fork_notify(svc_action,
|
|
- &stonith_action_async_done,
|
|
- &stonith_action_async_forked)) {
|
|
- pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN,
|
|
- PCMK_EXEC_PENDING, NULL);
|
|
- return pcmk_ok;
|
|
- }
|
|
-
|
|
- } else if (services_action_sync(svc_action)) { // sync success
|
|
- rc = pcmk_ok;
|
|
-
|
|
- } else { // sync failure
|
|
- rc = -ECONNABORTED;
|
|
- }
|
|
-
|
|
- set_result_from_svc_action(action, svc_action);
|
|
- svc_action->params = NULL;
|
|
- services_action_free(svc_action);
|
|
- return rc;
|
|
-}
|
|
-
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Kick off execution of an async stonith action
|
|
- *
|
|
- * \param[in,out] action Action to be executed
|
|
- * \param[in,out] userdata Datapointer to be passed to callbacks
|
|
- * \param[in] done Callback to notify action has failed/succeeded
|
|
- * \param[in] fork_callback Callback to notify successful fork of child
|
|
- *
|
|
- * \return pcmk_ok if ownership of action has been taken, -errno otherwise
|
|
- */
|
|
-int
|
|
-stonith_action_execute_async(stonith_action_t * action,
|
|
- void *userdata,
|
|
- void (*done) (int pid,
|
|
- const pcmk__action_result_t *result,
|
|
- void *user_data),
|
|
- void (*fork_cb) (int pid, void *user_data))
|
|
-{
|
|
- if (!action) {
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- action->userdata = userdata;
|
|
- action->done_cb = done;
|
|
- action->fork_cb = fork_cb;
|
|
- action->async = 1;
|
|
-
|
|
- return internal_stonith_action_execute(action);
|
|
-}
|
|
-
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Execute a stonith action
|
|
- *
|
|
- * \param[in,out] action Action to execute
|
|
- *
|
|
- * \return pcmk_ok on success, -errno otherwise
|
|
- */
|
|
-int
|
|
-stonith__execute(stonith_action_t *action)
|
|
-{
|
|
- int rc = pcmk_ok;
|
|
-
|
|
- CRM_CHECK(action != NULL, return -EINVAL);
|
|
-
|
|
- // Keep trying until success, max retries, or timeout
|
|
- do {
|
|
- rc = internal_stonith_action_execute(action);
|
|
- } while ((rc != pcmk_ok) && update_remaining_timeout(action));
|
|
-
|
|
- return rc;
|
|
-}
|
|
-
|
|
static int
|
|
stonith_api_device_list(stonith_t * stonith, int call_options, const char *namespace,
|
|
stonith_key_value_t ** devices, int timeout)
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 883a3cf7d3f73d02417d3997a7885dd5a7bebac7 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Wed, 10 Nov 2021 15:39:17 -0600
|
|
Subject: [PATCH 05/13] Low: fencing,executor: improve mapping of legacy return
|
|
code to execution status
|
|
|
|
Move stonith_rc2status() from the executor to the fencing library for future
|
|
reuse, exposing it internally as stonith__legacy2status(). Update it to use
|
|
recently added execution status codes.
|
|
---
|
|
daemons/execd/execd_commands.c | 66 ++++++++--------------------------
|
|
include/crm/fencing/internal.h | 2 ++
|
|
lib/fencing/st_actions.c | 36 +++++++++++++++++++
|
|
3 files changed, 52 insertions(+), 52 deletions(-)
|
|
|
|
diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c
|
|
index 02070bf11..0ccaa1ced 100644
|
|
--- a/daemons/execd/execd_commands.c
|
|
+++ b/daemons/execd/execd_commands.c
|
|
@@ -21,6 +21,7 @@
|
|
#include <unistd.h>
|
|
|
|
#include <crm/crm.h>
|
|
+#include <crm/fencing/internal.h>
|
|
#include <crm/services.h>
|
|
#include <crm/services_internal.h>
|
|
#include <crm/common/mainloop.h>
|
|
@@ -999,56 +1000,6 @@ action_complete(svc_action_t * action)
|
|
cmd_finalize(cmd, rsc);
|
|
}
|
|
|
|
-/*!
|
|
- * \internal
|
|
- * \brief Determine operation status of a stonith operation
|
|
- *
|
|
- * Non-stonith resource operations get their operation status directly from the
|
|
- * service library, but the fencer does not have an equivalent, so we must infer
|
|
- * an operation status from the fencer API's return code.
|
|
- *
|
|
- * \param[in] action Name of action performed on stonith resource
|
|
- * \param[in] interval_ms Action interval
|
|
- * \param[in] rc Action result from fencer
|
|
- *
|
|
- * \return Operation status corresponding to fencer API return code
|
|
- */
|
|
-static int
|
|
-stonith_rc2status(const char *action, guint interval_ms, int rc)
|
|
-{
|
|
- int status = PCMK_EXEC_DONE;
|
|
-
|
|
- switch (rc) {
|
|
- case pcmk_ok:
|
|
- break;
|
|
-
|
|
- case -EOPNOTSUPP:
|
|
- case -EPROTONOSUPPORT:
|
|
- status = PCMK_EXEC_NOT_SUPPORTED;
|
|
- break;
|
|
-
|
|
- case -ETIME:
|
|
- case -ETIMEDOUT:
|
|
- status = PCMK_EXEC_TIMEOUT;
|
|
- break;
|
|
-
|
|
- case -ENOTCONN:
|
|
- case -ECOMM:
|
|
- // Couldn't talk to fencer
|
|
- status = PCMK_EXEC_ERROR;
|
|
- break;
|
|
-
|
|
- case -ENODEV:
|
|
- // The device is not registered with the fencer
|
|
- status = PCMK_EXEC_ERROR;
|
|
- break;
|
|
-
|
|
- default:
|
|
- break;
|
|
- }
|
|
- return status;
|
|
-}
|
|
-
|
|
static void
|
|
stonith_action_complete(lrmd_cmd_t * cmd, int rc)
|
|
{
|
|
@@ -1062,8 +1013,19 @@ stonith_action_complete(lrmd_cmd_t * cmd, int rc)
|
|
* the fencer return code.
|
|
*/
|
|
if (cmd->result.execution_status != PCMK_EXEC_CANCELLED) {
|
|
- cmd->result.execution_status = stonith_rc2status(cmd->action,
|
|
- cmd->interval_ms, rc);
|
|
+ cmd->result.execution_status = stonith__legacy2status(rc);
|
|
+
|
|
+ // Simplify status codes from fencer
|
|
+ switch (cmd->result.execution_status) {
|
|
+ case PCMK_EXEC_NOT_CONNECTED:
|
|
+ case PCMK_EXEC_INVALID:
|
|
+ case PCMK_EXEC_NO_FENCE_DEVICE:
|
|
+ case PCMK_EXEC_NO_SECRETS:
|
|
+ cmd->result.execution_status = PCMK_EXEC_ERROR;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
|
|
// Certain successful actions change the known state of the resource
|
|
if ((rsc != NULL) && pcmk__result_ok(&(cmd->result))) {
|
|
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
|
|
index 6a7e4232c..80f6443be 100644
|
|
--- a/include/crm/fencing/internal.h
|
|
+++ b/include/crm/fencing/internal.h
|
|
@@ -182,6 +182,8 @@ bool stonith__event_state_pending(stonith_history_t *history, void *user_data);
|
|
bool stonith__event_state_eq(stonith_history_t *history, void *user_data);
|
|
bool stonith__event_state_neq(stonith_history_t *history, void *user_data);
|
|
|
|
+int stonith__legacy2status(int rc);
|
|
+
|
|
/*!
|
|
* \internal
|
|
* \brief Is a fencing operation in pending state?
|
|
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
|
|
index 64d3afd5d..9e785595a 100644
|
|
--- a/lib/fencing/st_actions.c
|
|
+++ b/lib/fencing/st_actions.c
|
|
@@ -360,6 +360,42 @@ stonith__result2rc(const pcmk__action_result_t *result)
|
|
return pcmk_rc_error;
|
|
}
|
|
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Determine execution status equivalent of legacy fencer return code
|
|
+ *
|
|
+ * Fence action notifications, and fence action callbacks from older fencers
|
|
+ * (<=2.1.2) in a rolling upgrade, will have only a legacy return code. Map this
|
|
+ * to an execution status as best as possible (essentially, the inverse of
|
|
+ * stonith__result2rc()).
|
|
+ *
|
|
+ * \param[in] rc Legacy return code from fencer
|
|
+ *
|
|
+ * \return Execution status best corresponding to \p rc
|
|
+ */
|
|
+int
|
|
+stonith__legacy2status(int rc)
|
|
+{
|
|
+ if (rc >= 0) {
|
|
+ return PCMK_EXEC_DONE;
|
|
+ }
|
|
+ switch (-rc) {
|
|
+ case EACCES: return PCMK_EXEC_NO_SECRETS;
|
|
+ case ECANCELED: return PCMK_EXEC_CANCELLED;
|
|
+ case EHOSTUNREACH: return PCMK_EXEC_INVALID;
|
|
+ case EINPROGRESS: return PCMK_EXEC_PENDING;
|
|
+ case ENODEV: return PCMK_EXEC_NO_FENCE_DEVICE;
|
|
+ case ENOENT: return PCMK_EXEC_NOT_INSTALLED;
|
|
+ case ENOTCONN: return PCMK_EXEC_NOT_CONNECTED;
|
|
+ case EOPNOTSUPP: return PCMK_EXEC_NOT_SUPPORTED;
|
|
+ case EPROTO: return PCMK_EXEC_INVALID;
|
|
+ case EPROTONOSUPPORT: return PCMK_EXEC_NOT_SUPPORTED;
|
|
+ case ETIME: return PCMK_EXEC_TIMEOUT;
|
|
+ case ETIMEDOUT: return PCMK_EXEC_TIMEOUT;
|
|
+ default: return PCMK_EXEC_ERROR;
|
|
+ }
|
|
+}
|
|
+
|
|
static void
|
|
stonith_action_async_done(svc_action_t *svc_action)
|
|
{
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 639a9f4a2cbeb6cc41b754a1dcb1f360a9500e03 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Thu, 11 Nov 2021 16:54:32 -0600
|
|
Subject: [PATCH 06/13] Refactor: fencing: add functions for getting/setting
|
|
result via XML
|
|
|
|
These will come in handy as we update the various fencer messages to include a
|
|
full result rather than just a legacy return code. The functions are in a new
|
|
source file fenced_messages.c which can have other stuff moved to it later.
|
|
---
|
|
include/crm/fencing/internal.h | 3 +
|
|
lib/fencing/st_actions.c | 107 +++++++++++++++++++++++++++++++++
|
|
2 files changed, 110 insertions(+)
|
|
|
|
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
|
|
index 80f6443be..4b5fd3959 100644
|
|
--- a/include/crm/fencing/internal.h
|
|
+++ b/include/crm/fencing/internal.h
|
|
@@ -60,6 +60,9 @@ stonith_action_t *stonith_action_create(const char *agent,
|
|
void stonith__destroy_action(stonith_action_t *action);
|
|
pcmk__action_result_t *stonith__action_result(stonith_action_t *action);
|
|
int stonith__result2rc(const pcmk__action_result_t *result);
|
|
+void stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result);
|
|
+void stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result);
|
|
+xmlNode *stonith__find_xe_with_result(xmlNode *xml);
|
|
|
|
int
|
|
stonith_action_execute_async(stonith_action_t * action,
|
|
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
|
|
index 9e785595a..d4fc3f5ed 100644
|
|
--- a/lib/fencing/st_actions.c
|
|
+++ b/lib/fencing/st_actions.c
|
|
@@ -396,6 +396,113 @@ stonith__legacy2status(int rc)
|
|
}
|
|
}
|
|
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Add a fencing result to an XML element as attributes
|
|
+ *
|
|
+ * \param[in] xml XML element to add result to
|
|
+ * \param[in] result Fencing result to add (assume success if NULL)
|
|
+ */
|
|
+void
|
|
+stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
|
|
+{
|
|
+ int exit_status = CRM_EX_OK;
|
|
+ enum pcmk_exec_status execution_status = PCMK_EXEC_DONE;
|
|
+ const char *exit_reason = NULL;
|
|
+ const char *action_stdout = NULL;
|
|
+ int rc = pcmk_ok;
|
|
+
|
|
+ CRM_CHECK(xml != NULL, return);
|
|
+
|
|
+ if (result != NULL) {
|
|
+ exit_status = result->exit_status;
|
|
+ execution_status = result->execution_status;
|
|
+ exit_reason = result->exit_reason;
|
|
+ action_stdout = result->action_stdout;
|
|
+ rc = pcmk_rc2legacy(stonith__result2rc(result));
|
|
+ }
|
|
+
|
|
+ crm_xml_add_int(xml, XML_LRM_ATTR_OPSTATUS, (int) execution_status);
|
|
+ crm_xml_add_int(xml, XML_LRM_ATTR_RC, exit_status);
|
|
+ crm_xml_add(xml, XML_LRM_ATTR_EXIT_REASON, exit_reason);
|
|
+ crm_xml_add(xml, "st_output", action_stdout);
|
|
+
|
|
+ /* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external
|
|
+ * code that use libstonithd <=2.1.2 don't check for the full result, and
|
|
+ * need a legacy return code instead.
|
|
+ */
|
|
+ crm_xml_add_int(xml, F_STONITH_RC, rc);
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Find a fencing result beneath an XML element
|
|
+ *
|
|
+ * \param[in] xml XML element to search
|
|
+ *
|
|
+ * \return \p xml or descendent of it that contains a fencing result, else NULL
|
|
+ */
|
|
+xmlNode *
|
|
+stonith__find_xe_with_result(xmlNode *xml)
|
|
+{
|
|
+ xmlNode *match = get_xpath_object("//@" XML_LRM_ATTR_RC, xml, LOG_NEVER);
|
|
+
|
|
+ if (match == NULL) {
|
|
+ /* @COMPAT Peers <=2.1.2 in a rolling upgrade provide only a legacy
|
|
+ * return code, not a full result, so check for that.
|
|
+ */
|
|
+ match = get_xpath_object("//@" F_STONITH_RC, xml, LOG_ERR);
|
|
+ }
|
|
+ return match;
|
|
+}
|
|
+
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Get a fencing result from an XML element's attributes
|
|
+ *
|
|
+ * \param[in] xml XML element with fencing result
|
|
+ * \param[out] result Where to store fencing result
|
|
+ */
|
|
+void
|
|
+stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result)
|
|
+{
|
|
+ int exit_status = CRM_EX_OK;
|
|
+ int execution_status = PCMK_EXEC_DONE;
|
|
+ const char *exit_reason = NULL;
|
|
+ char *action_stdout = NULL;
|
|
+
|
|
+ CRM_CHECK((xml != NULL) && (result != NULL), return);
|
|
+
|
|
+ exit_reason = crm_element_value(xml, XML_LRM_ATTR_EXIT_REASON);
|
|
+ action_stdout = crm_element_value_copy(xml, "st_output");
|
|
+
|
|
+ // A result must include an exit status and execution status
|
|
+ if ((crm_element_value_int(xml, XML_LRM_ATTR_RC, &exit_status) < 0)
|
|
+ || (crm_element_value_int(xml, XML_LRM_ATTR_OPSTATUS,
|
|
+ &execution_status) < 0)) {
|
|
+ int rc = pcmk_ok;
|
|
+ exit_status = CRM_EX_ERROR;
|
|
+
|
|
+ /* @COMPAT Peers <=2.1.2 in rolling upgrades provide only a legacy
|
|
+ * return code, not a full result, so check for that.
|
|
+ */
|
|
+ if (crm_element_value_int(xml, F_STONITH_RC, &rc) == 0) {
|
|
+ if ((rc == pcmk_ok) || (rc == -EINPROGRESS)) {
|
|
+ exit_status = CRM_EX_OK;
|
|
+ }
|
|
+ execution_status = stonith__legacy2status(rc);
|
|
+ exit_reason = pcmk_strerror(rc);
|
|
+
|
|
+ } else {
|
|
+ execution_status = PCMK_EXEC_ERROR;
|
|
+ exit_reason = "Fencer reply contained neither a full result "
|
|
+ "nor a legacy return code (bug?)";
|
|
+ }
|
|
+ }
|
|
+ pcmk__set_result(result, exit_status, execution_status, exit_reason);
|
|
+ pcmk__set_result_output(result, action_stdout, NULL);
|
|
+}
|
|
+
|
|
static void
|
|
stonith_action_async_done(svc_action_t *svc_action)
|
|
{
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 1f0121c6ad0d0235bcf01c8b60f9153592b3db83 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Thu, 11 Nov 2021 10:10:53 -0600
|
|
Subject: [PATCH 07/13] Refactor: fencing: rename functions for invoking fence
|
|
callbacks
|
|
|
|
... to make it clearer what the difference between them is
|
|
---
|
|
lib/fencing/st_client.c | 44 +++++++++++++++++++++++++++++++++--------
|
|
1 file changed, 36 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index 944cd1863..dfc5860fc 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -847,9 +847,21 @@ stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
|
|
return pcmk_ok;
|
|
}
|
|
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Invoke a (single) specified fence action callback
|
|
+ *
|
|
+ * \param[in] st Fencer API connection
|
|
+ * \param[in] call_id If positive, call ID of completed fence action, otherwise
|
|
+ * legacy return code for early action failure
|
|
+ * \param[in] rc Legacy return code for action result
|
|
+ * \param[in] userdata User data to pass to callback
|
|
+ * \param[in] callback Fence action callback to invoke
|
|
+ */
|
|
static void
|
|
-invoke_callback(stonith_t * st, int call_id, int rc, void *userdata,
|
|
- void (*callback) (stonith_t * st, stonith_callback_data_t * data))
|
|
+invoke_fence_action_callback(stonith_t *st, int call_id, int rc, void *userdata,
|
|
+ void (*callback) (stonith_t *st,
|
|
+ stonith_callback_data_t *data))
|
|
{
|
|
stonith_callback_data_t data = { 0, };
|
|
|
|
@@ -860,8 +872,21 @@ invoke_callback(stonith_t * st, int call_id, int rc, void *userdata,
|
|
callback(st, &data);
|
|
}
|
|
|
|
+/*!
|
|
+ * \internal
|
|
+ * \brief Invoke any callbacks registered for a specified fence action result
|
|
+ *
|
|
+ * Given a fence action result from the fencer, invoke any callback registered
|
|
+ * for that action, as well as any global callback registered.
|
|
+ *
|
|
+ * \param[in] st Fencer API connection
|
|
+ * \param[in] msg If non-NULL, fencer reply
|
|
+ * \param[in] call_id If \p msg is NULL, call ID of action that timed out
|
|
+ * \param[in] rc Legacy return code for result of action
|
|
+ */
|
|
static void
|
|
-stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc)
|
|
+invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id,
|
|
+ int rc)
|
|
{
|
|
stonith_private_t *private = NULL;
|
|
stonith_callback_client_t *blob = NULL;
|
|
@@ -899,7 +924,8 @@ stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc
|
|
|
|
if (local_blob.callback != NULL && (rc == pcmk_ok || local_blob.only_success == FALSE)) {
|
|
crm_trace("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
|
|
- invoke_callback(stonith, call_id, rc, local_blob.user_data, local_blob.callback);
|
|
+ invoke_fence_action_callback(stonith, call_id, rc, local_blob.user_data,
|
|
+ local_blob.callback);
|
|
|
|
} else if (private->op_callback == NULL && rc != pcmk_ok) {
|
|
crm_warn("Fencing command failed: %s", pcmk_strerror(rc));
|
|
@@ -908,7 +934,8 @@ stonith_perform_callback(stonith_t * stonith, xmlNode * msg, int call_id, int rc
|
|
|
|
if (private->op_callback != NULL) {
|
|
crm_trace("Invoking global callback for call %d", call_id);
|
|
- invoke_callback(stonith, call_id, rc, NULL, private->op_callback);
|
|
+ invoke_fence_action_callback(stonith, call_id, rc, NULL,
|
|
+ private->op_callback);
|
|
}
|
|
crm_trace("OP callback activated.");
|
|
}
|
|
@@ -919,7 +946,7 @@ stonith_async_timeout_handler(gpointer data)
|
|
struct timer_rec_s *timer = data;
|
|
|
|
crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
|
|
- stonith_perform_callback(timer->stonith, NULL, timer->call_id, -ETIME);
|
|
+ invoke_registered_callbacks(timer->stonith, NULL, timer->call_id, -ETIME);
|
|
|
|
/* Always return TRUE, never remove the handler
|
|
* We do that in stonith_del_callback()
|
|
@@ -994,7 +1021,7 @@ stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
|
|
crm_trace("Activating %s callbacks...", type);
|
|
|
|
if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
|
|
- stonith_perform_callback(st, blob.xml, 0, 0);
|
|
+ invoke_registered_callbacks(st, blob.xml, 0, 0);
|
|
|
|
} else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
|
|
foreach_notify_entry(private, stonith_send_notification, &blob);
|
|
@@ -1229,7 +1256,8 @@ stonith_api_add_callback(stonith_t * stonith, int call_id, int timeout, int opti
|
|
} else if (call_id < 0) {
|
|
if (!(options & st_opt_report_only_success)) {
|
|
crm_trace("Call failed, calling %s: %s", callback_name, pcmk_strerror(call_id));
|
|
- invoke_callback(stonith, call_id, call_id, user_data, callback);
|
|
+ invoke_fence_action_callback(stonith, call_id, call_id, user_data,
|
|
+ callback);
|
|
} else {
|
|
crm_warn("Fencer call failed: %s", pcmk_strerror(call_id));
|
|
}
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From c32f11e70a88244f5a3217608055a4eaf8d28231 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Thu, 11 Nov 2021 10:21:00 -0600
|
|
Subject: [PATCH 08/13] Refactor: fencing: drop unnecessary argument when
|
|
invoking callbacks
|
|
|
|
Refactor invoke_registered_callbacks() to treat a NULL message as a timeout, so
|
|
we can drop the rc argument.
|
|
---
|
|
lib/fencing/st_client.c | 17 +++++++++++------
|
|
1 file changed, 11 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index dfc5860fc..9f2b0c1c1 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -882,15 +882,14 @@ invoke_fence_action_callback(stonith_t *st, int call_id, int rc, void *userdata,
|
|
* \param[in] st Fencer API connection
|
|
* \param[in] msg If non-NULL, fencer reply
|
|
* \param[in] call_id If \p msg is NULL, call ID of action that timed out
|
|
- * \param[in] rc Legacy return code for result of action
|
|
*/
|
|
static void
|
|
-invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id,
|
|
- int rc)
|
|
+invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
|
|
{
|
|
stonith_private_t *private = NULL;
|
|
stonith_callback_client_t *blob = NULL;
|
|
stonith_callback_client_t local_blob;
|
|
+ int rc = pcmk_ok;
|
|
|
|
CRM_CHECK(stonith != NULL, return);
|
|
CRM_CHECK(stonith->st_private != NULL, return);
|
|
@@ -902,7 +901,13 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id,
|
|
local_blob.user_data = NULL;
|
|
local_blob.only_success = FALSE;
|
|
|
|
- if (msg != NULL) {
|
|
+ if (msg == NULL) {
|
|
+ // Fencer didn't reply in time
|
|
+ rc = -ETIME;
|
|
+
|
|
+ } else {
|
|
+ // We have the fencer reply
|
|
+
|
|
crm_element_value_int(msg, F_STONITH_RC, &rc);
|
|
crm_element_value_int(msg, F_STONITH_CALLID, &call_id);
|
|
}
|
|
@@ -946,7 +951,7 @@ stonith_async_timeout_handler(gpointer data)
|
|
struct timer_rec_s *timer = data;
|
|
|
|
crm_err("Async call %d timed out after %dms", timer->call_id, timer->timeout);
|
|
- invoke_registered_callbacks(timer->stonith, NULL, timer->call_id, -ETIME);
|
|
+ invoke_registered_callbacks(timer->stonith, NULL, timer->call_id);
|
|
|
|
/* Always return TRUE, never remove the handler
|
|
* We do that in stonith_del_callback()
|
|
@@ -1021,7 +1026,7 @@ stonith_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata)
|
|
crm_trace("Activating %s callbacks...", type);
|
|
|
|
if (pcmk__str_eq(type, T_STONITH_NG, pcmk__str_casei)) {
|
|
- invoke_registered_callbacks(st, blob.xml, 0, 0);
|
|
+ invoke_registered_callbacks(st, blob.xml, 0);
|
|
|
|
} else if (pcmk__str_eq(type, T_STONITH_NOTIFY, pcmk__str_casei)) {
|
|
foreach_notify_entry(private, stonith_send_notification, &blob);
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 5d8279b51ea9df738354649e4065663f2c16f1e6 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Thu, 11 Nov 2021 10:21:57 -0600
|
|
Subject: [PATCH 09/13] Log: fencing: improve message for callback errors
|
|
|
|
Improve checking of fencer replies, which also allows us to distinguish an
|
|
internal bug from a bad fencer reply in logs. Lower the bad reply message to
|
|
warning.
|
|
---
|
|
lib/fencing/st_client.c | 13 +++++++++----
|
|
1 file changed, 9 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index 9f2b0c1c1..170b9d450 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -904,15 +904,20 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
|
|
if (msg == NULL) {
|
|
// Fencer didn't reply in time
|
|
rc = -ETIME;
|
|
+ CRM_LOG_ASSERT(call_id > 0);
|
|
|
|
} else {
|
|
// We have the fencer reply
|
|
|
|
- crm_element_value_int(msg, F_STONITH_RC, &rc);
|
|
- crm_element_value_int(msg, F_STONITH_CALLID, &call_id);
|
|
- }
|
|
+ if (crm_element_value_int(msg, F_STONITH_RC, &rc) != 0) {
|
|
+ rc = -pcmk_err_generic;
|
|
+ }
|
|
|
|
- CRM_CHECK(call_id > 0, crm_log_xml_err(msg, "Bad result"));
|
|
+ if ((crm_element_value_int(msg, F_STONITH_CALLID, &call_id) != 0)
|
|
+ || (call_id <= 0)) {
|
|
+ crm_log_xml_warn(msg, "Bad fencer reply");
|
|
+ }
|
|
+ }
|
|
|
|
blob = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
|
|
call_id);
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From e03c14d24e8cb011e870b9460930d139705bf0a2 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Tue, 9 Nov 2021 14:59:12 -0600
|
|
Subject: [PATCH 10/13] Doc: fencing: correct stonith_api_operations_t method
|
|
descriptions
|
|
|
|
Many of the methods return a positive call ID on success
|
|
---
|
|
include/crm/stonith-ng.h | 60 ++++++++++++++++++++++------------------
|
|
1 file changed, 33 insertions(+), 27 deletions(-)
|
|
|
|
diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h
|
|
index 8d6ad477d..9643820e9 100644
|
|
--- a/include/crm/stonith-ng.h
|
|
+++ b/include/crm/stonith-ng.h
|
|
@@ -164,39 +164,38 @@ typedef struct stonith_api_operations_s
|
|
int (*disconnect)(stonith_t *st);
|
|
|
|
/*!
|
|
- * \brief Remove a registered stonith device with the local stonith daemon.
|
|
+ * \brief Unregister a fence device with the local fencer
|
|
*
|
|
- * \note Synchronous, guaranteed to occur in daemon before function returns.
|
|
- *
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*remove_device)(
|
|
stonith_t *st, int options, const char *name);
|
|
|
|
/*!
|
|
- * \brief Register a stonith device with the local stonith daemon.
|
|
+ * \brief Register a fence device with the local fencer
|
|
*
|
|
- * \note Synchronous, guaranteed to occur in daemon before function returns.
|
|
- *
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*register_device)(
|
|
stonith_t *st, int options, const char *id,
|
|
const char *provider, const char *agent, stonith_key_value_t *params);
|
|
|
|
/*!
|
|
- * \brief Remove a fencing level for a specific node.
|
|
+ * \brief Unregister a fencing level for specified node with local fencer
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*remove_level)(
|
|
stonith_t *st, int options, const char *node, int level);
|
|
|
|
/*!
|
|
- * \brief Register a fencing level containing the fencing devices to be used
|
|
- * at that level for a specific node.
|
|
+ * \brief Register a fencing level for specified node with local fencer
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*register_level)(
|
|
stonith_t *st, int options, const char *node, int level, stonith_key_value_t *device_list);
|
|
@@ -226,21 +225,24 @@ typedef struct stonith_api_operations_s
|
|
/*!
|
|
* \brief Retrieve string listing hosts and port assignments from a local stonith device.
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*list)(stonith_t *st, int options, const char *id, char **list_output, int timeout);
|
|
|
|
/*!
|
|
* \brief Check to see if a local stonith device is reachable
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*monitor)(stonith_t *st, int options, const char *id, int timeout);
|
|
|
|
/*!
|
|
* \brief Check to see if a local stonith device's port is reachable
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*status)(stonith_t *st, int options, const char *id, const char *port, int timeout);
|
|
|
|
@@ -267,7 +269,8 @@ typedef struct stonith_api_operations_s
|
|
* \param timeout, The default per device timeout to use with each device
|
|
* capable of fencing the target.
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*fence)(stonith_t *st, int options, const char *node, const char *action,
|
|
int timeout, int tolerance);
|
|
@@ -275,7 +278,8 @@ typedef struct stonith_api_operations_s
|
|
/*!
|
|
* \brief Manually confirm that a node is down.
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*confirm)(stonith_t *st, int options, const char *node);
|
|
|
|
@@ -304,9 +308,6 @@ typedef struct stonith_api_operations_s
|
|
* \param[in] callback The callback function to register
|
|
*
|
|
* \return \c TRUE on success, \c FALSE if call_id is negative, -errno otherwise
|
|
- *
|
|
- * \todo This function should return \c pcmk_ok on success, and \c call_id
|
|
- * when negative, but that would break backward compatibility.
|
|
*/
|
|
int (*register_callback)(stonith_t *st,
|
|
int call_id,
|
|
@@ -317,12 +318,14 @@ typedef struct stonith_api_operations_s
|
|
void (*callback)(stonith_t *st, stonith_callback_data_t *data));
|
|
|
|
/*!
|
|
- * \brief Remove a registered callback for a given call id.
|
|
+ * \brief Remove a registered callback for a given call id
|
|
+ *
|
|
+ * \return pcmk_ok
|
|
*/
|
|
int (*remove_callback)(stonith_t *st, int call_id, bool all_callbacks);
|
|
|
|
/*!
|
|
- * \brief Remove fencing level for specific node, node regex or attribute
|
|
+ * \brief Unregister fencing level for specified node, pattern or attribute
|
|
*
|
|
* \param[in] st Fencer connection to use
|
|
* \param[in] options Bitmask of stonith_call_options to pass to the fencer
|
|
@@ -332,7 +335,8 @@ typedef struct stonith_api_operations_s
|
|
* \param[in] value If not NULL, target by this node attribute value
|
|
* \param[in] level Index number of level to remove
|
|
*
|
|
- * \return 0 on success, negative error code otherwise
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*
|
|
* \note The caller should set only one of node, pattern or attr/value.
|
|
*/
|
|
@@ -341,7 +345,7 @@ typedef struct stonith_api_operations_s
|
|
const char *attr, const char *value, int level);
|
|
|
|
/*!
|
|
- * \brief Register fencing level for specific node, node regex or attribute
|
|
+ * \brief Register fencing level for specified node, pattern or attribute
|
|
*
|
|
* \param[in] st Fencer connection to use
|
|
* \param[in] options Bitmask of stonith_call_options to pass to fencer
|
|
@@ -352,7 +356,8 @@ typedef struct stonith_api_operations_s
|
|
* \param[in] level Index number of level to add
|
|
* \param[in] device_list Devices to use in level
|
|
*
|
|
- * \return 0 on success, negative error code otherwise
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*
|
|
* \note The caller should set only one of node, pattern or attr/value.
|
|
*/
|
|
@@ -398,7 +403,8 @@ typedef struct stonith_api_operations_s
|
|
* \param delay, Apply a fencing delay. Value -1 means disable also any
|
|
* static/random fencing delays from pcmk_delay_base/max
|
|
*
|
|
- * \return Legacy Pacemaker return code
|
|
+ * \return pcmk_ok (if synchronous) or positive call ID (if asynchronous)
|
|
+ * on success, otherwise a negative legacy Pacemaker return code
|
|
*/
|
|
int (*fence_with_delay)(stonith_t *st, int options, const char *node, const char *action,
|
|
int timeout, int tolerance, int delay);
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 18c382731889b626b21ba6a14f9213ef1e45a524 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Tue, 23 Nov 2021 11:14:24 -0600
|
|
Subject: [PATCH 11/13] Refactor: fencing: define constant for XML attribute
|
|
for action output
|
|
|
|
---
|
|
daemons/fenced/fenced_commands.c | 4 ++--
|
|
include/crm/fencing/internal.h | 1 +
|
|
lib/fencing/st_actions.c | 4 ++--
|
|
lib/fencing/st_client.c | 2 +-
|
|
4 files changed, 6 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c
|
|
index 26501a4b3..aa14c52af 100644
|
|
--- a/daemons/fenced/fenced_commands.c
|
|
+++ b/daemons/fenced/fenced_commands.c
|
|
@@ -2677,7 +2677,7 @@ stonith_construct_reply(xmlNode * request, const char *output, xmlNode * data, i
|
|
|
|
crm_xml_add(reply, "st_origin", __func__);
|
|
crm_xml_add(reply, F_TYPE, T_STONITH_NG);
|
|
- crm_xml_add(reply, "st_output", output);
|
|
+ crm_xml_add(reply, F_STONITH_OUTPUT, output);
|
|
crm_xml_add_int(reply, F_STONITH_RC, rc);
|
|
|
|
if (request == NULL) {
|
|
@@ -2743,7 +2743,7 @@ construct_async_reply(async_command_t *cmd, const pcmk__action_result_t *result)
|
|
crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options);
|
|
crm_xml_add_int(reply, F_STONITH_RC,
|
|
pcmk_rc2legacy(stonith__result2rc(result)));
|
|
- crm_xml_add(reply, "st_output", result->action_stdout);
|
|
+ crm_xml_add(reply, F_STONITH_OUTPUT, result->action_stdout);
|
|
return reply;
|
|
}
|
|
|
|
diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h
|
|
index 4b5fd3959..f0d294a0b 100644
|
|
--- a/include/crm/fencing/internal.h
|
|
+++ b/include/crm/fencing/internal.h
|
|
@@ -105,6 +105,7 @@ void stonith__device_parameter_flags(uint32_t *device_flags,
|
|
# define F_STONITH_REMOTE_OP_ID "st_remote_op"
|
|
# define F_STONITH_REMOTE_OP_ID_RELAY "st_remote_op_relay"
|
|
# define F_STONITH_RC "st_rc"
|
|
+# define F_STONITH_OUTPUT "st_output"
|
|
/*! Timeout period per a device execution */
|
|
# define F_STONITH_TIMEOUT "st_timeout"
|
|
# define F_STONITH_TOLERANCE "st_tolerance"
|
|
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
|
|
index d4fc3f5ed..5636810a5 100644
|
|
--- a/lib/fencing/st_actions.c
|
|
+++ b/lib/fencing/st_actions.c
|
|
@@ -425,7 +425,7 @@ stonith__xe_set_result(xmlNode *xml, const pcmk__action_result_t *result)
|
|
crm_xml_add_int(xml, XML_LRM_ATTR_OPSTATUS, (int) execution_status);
|
|
crm_xml_add_int(xml, XML_LRM_ATTR_RC, exit_status);
|
|
crm_xml_add(xml, XML_LRM_ATTR_EXIT_REASON, exit_reason);
|
|
- crm_xml_add(xml, "st_output", action_stdout);
|
|
+ crm_xml_add(xml, F_STONITH_OUTPUT, action_stdout);
|
|
|
|
/* @COMPAT Peers in rolling upgrades, Pacemaker Remote nodes, and external
|
|
* code that use libstonithd <=2.1.2 don't check for the full result, and
|
|
@@ -474,7 +474,7 @@ stonith__xe_get_result(xmlNode *xml, pcmk__action_result_t *result)
|
|
CRM_CHECK((xml != NULL) && (result != NULL), return);
|
|
|
|
exit_reason = crm_element_value(xml, XML_LRM_ATTR_EXIT_REASON);
|
|
- action_stdout = crm_element_value_copy(xml, "st_output");
|
|
+ action_stdout = crm_element_value_copy(xml, F_STONITH_OUTPUT);
|
|
|
|
// A result must include an exit status and execution status
|
|
if ((crm_element_value_int(xml, XML_LRM_ATTR_RC, &exit_status) < 0)
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index 170b9d450..2dfadf922 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -600,7 +600,7 @@ stonith_api_list(stonith_t * stonith, int call_options, const char *id, char **l
|
|
if (output && list_info) {
|
|
const char *list_str;
|
|
|
|
- list_str = crm_element_value(output, "st_output");
|
|
+ list_str = crm_element_value(output, F_STONITH_OUTPUT);
|
|
|
|
if (list_str) {
|
|
*list_info = strdup(list_str);
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 9fe9ed5d46c810cb9c12eb07271373ab92d271cd Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Tue, 23 Nov 2021 11:39:32 -0600
|
|
Subject: [PATCH 12/13] Refactor: fencing: simplify invoking callbacks
|
|
|
|
---
|
|
lib/fencing/st_client.c | 42 +++++++++++++++++------------------------
|
|
1 file changed, 17 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
|
|
index 2dfadf922..2ca094566 100644
|
|
--- a/lib/fencing/st_client.c
|
|
+++ b/lib/fencing/st_client.c
|
|
@@ -887,8 +887,7 @@ static void
|
|
invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
|
|
{
|
|
stonith_private_t *private = NULL;
|
|
- stonith_callback_client_t *blob = NULL;
|
|
- stonith_callback_client_t local_blob;
|
|
+ stonith_callback_client_t *cb_info = NULL;
|
|
int rc = pcmk_ok;
|
|
|
|
CRM_CHECK(stonith != NULL, return);
|
|
@@ -896,11 +895,6 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
|
|
|
|
private = stonith->st_private;
|
|
|
|
- local_blob.id = NULL;
|
|
- local_blob.callback = NULL;
|
|
- local_blob.user_data = NULL;
|
|
- local_blob.only_success = FALSE;
|
|
-
|
|
if (msg == NULL) {
|
|
// Fencer didn't reply in time
|
|
rc = -ETIME;
|
|
@@ -919,26 +913,21 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
|
|
}
|
|
}
|
|
|
|
- blob = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
|
|
- call_id);
|
|
- if (blob != NULL) {
|
|
- local_blob = *blob;
|
|
- blob = NULL;
|
|
-
|
|
- stonith_api_del_callback(stonith, call_id, FALSE);
|
|
-
|
|
- } else {
|
|
- crm_trace("No callback found for call %d", call_id);
|
|
- local_blob.callback = NULL;
|
|
+ if (call_id > 0) {
|
|
+ cb_info = pcmk__intkey_table_lookup(private->stonith_op_callback_table,
|
|
+ call_id);
|
|
}
|
|
|
|
- if (local_blob.callback != NULL && (rc == pcmk_ok || local_blob.only_success == FALSE)) {
|
|
- crm_trace("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
|
|
- invoke_fence_action_callback(stonith, call_id, rc, local_blob.user_data,
|
|
- local_blob.callback);
|
|
+ if ((cb_info != NULL) && (cb_info->callback != NULL)
|
|
+ && (rc == pcmk_ok || !(cb_info->only_success))) {
|
|
+ crm_trace("Invoking callback %s for call %d",
|
|
+ crm_str(cb_info->id), call_id);
|
|
+ invoke_fence_action_callback(stonith, call_id, rc, cb_info->user_data,
|
|
+ cb_info->callback);
|
|
|
|
- } else if (private->op_callback == NULL && rc != pcmk_ok) {
|
|
- crm_warn("Fencing command failed: %s", pcmk_strerror(rc));
|
|
+ } else if ((private->op_callback == NULL) && (rc != pcmk_ok)) {
|
|
+ crm_warn("Fencing action without registered callback failed: %s",
|
|
+ pcmk_strerror(rc));
|
|
crm_log_xml_debug(msg, "Failed fence update");
|
|
}
|
|
|
|
@@ -947,7 +936,10 @@ invoke_registered_callbacks(stonith_t *stonith, xmlNode *msg, int call_id)
|
|
invoke_fence_action_callback(stonith, call_id, rc, NULL,
|
|
private->op_callback);
|
|
}
|
|
- crm_trace("OP callback activated.");
|
|
+
|
|
+ if (cb_info != NULL) {
|
|
+ stonith_api_del_callback(stonith, call_id, FALSE);
|
|
+ }
|
|
}
|
|
|
|
static gboolean
|
|
--
|
|
2.27.0
|
|
|
|
|
|
From 8113b800ce677ba17a16ca176e8f6f9b4a042316 Mon Sep 17 00:00:00 2001
|
|
From: Ken Gaillot <kgaillot@redhat.com>
|
|
Date: Tue, 23 Nov 2021 18:14:48 -0600
|
|
Subject: [PATCH 13/13] Refactor: fencing: add a missing "break" statement
|
|
|
|
No effect, but more correct
|
|
---
|
|
lib/fencing/st_actions.c | 1 +
|
|
1 file changed, 1 insertion(+)
|
|
|
|
diff --git a/lib/fencing/st_actions.c b/lib/fencing/st_actions.c
|
|
index 5636810a5..7eaa8b0f2 100644
|
|
--- a/lib/fencing/st_actions.c
|
|
+++ b/lib/fencing/st_actions.c
|
|
@@ -336,6 +336,7 @@ stonith__result2rc(const pcmk__action_result_t *result)
|
|
case CRM_EX_EXPIRED: return EHOSTUNREACH;
|
|
default: break;
|
|
}
|
|
+ break;
|
|
|
|
default:
|
|
break;
|
|
--
|
|
2.27.0
|
|
|