From 994e6bf05176a35057377ee2557e79736768fffc Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Sat, 4 Dec 2021 06:56:05 +0000 Subject: [PATCH] import pacemaker-2.1.2-1.el8 --- .gitignore | 2 + .pacemaker.metadata | 2 + SOURCES/001-acl-group-schema.patch | 230 ++ SOURCES/002-fencing-reasons.patch | 2100 ++++++++++++++ SOURCES/003-fencing-reasons.patch | 2476 +++++++++++++++++ SOURCES/003-pacemakerd-output.patch | 343 --- SOURCES/005-crm_resource.patch | 866 ------ SOURCES/006-crm_simulate.patch | 896 ------ SOURCES/007-unfencing-loop.patch | 733 ----- SOURCES/008-dynamic-list-fencing.patch | 140 - SOURCES/009-crm_resource-messages.patch | 229 -- SOURCES/010-probe-pending.patch | 715 ----- SOURCES/011-crm_attribute-regression.patch | 150 - SOURCES/012-string-arguments.patch | 221 -- SOURCES/013-leaks.patch | 241 -- SOURCES/014-str-list.patch | 465 ---- SOURCES/015-sbd.patch | 1312 --------- SOURCES/016-cts.patch | 59 - SOURCES/017-watchdog-fixes.patch | 58 - SOURCES/018-controller.patch | 122 - SOURCES/019-crm_resource.patch | 114 - SOURCES/020-fence_watchdog.patch | 25 - SOURCES/nagios-agents-metadata-105ab8a.tar.gz | Bin 17861 -> 0 bytes SOURCES/pacemaker-ba59be7.tar.gz | Bin 5433050 -> 0 bytes SPECS/pacemaker.spec | 63 +- 25 files changed, 4841 insertions(+), 6721 deletions(-) create mode 100644 .gitignore create mode 100644 .pacemaker.metadata create mode 100644 SOURCES/001-acl-group-schema.patch create mode 100644 SOURCES/002-fencing-reasons.patch create mode 100644 SOURCES/003-fencing-reasons.patch delete mode 100644 SOURCES/003-pacemakerd-output.patch delete mode 100644 SOURCES/005-crm_resource.patch delete mode 100644 SOURCES/006-crm_simulate.patch delete mode 100644 SOURCES/007-unfencing-loop.patch delete mode 100644 SOURCES/008-dynamic-list-fencing.patch delete mode 100644 SOURCES/009-crm_resource-messages.patch delete mode 100644 SOURCES/010-probe-pending.patch delete mode 100644 SOURCES/011-crm_attribute-regression.patch delete mode 100644 SOURCES/012-string-arguments.patch delete mode 100644 SOURCES/013-leaks.patch delete mode 100644 SOURCES/014-str-list.patch delete mode 100644 SOURCES/015-sbd.patch delete mode 100644 SOURCES/016-cts.patch delete mode 100644 SOURCES/017-watchdog-fixes.patch delete mode 100644 SOURCES/018-controller.patch delete mode 100644 SOURCES/019-crm_resource.patch delete mode 100644 SOURCES/020-fence_watchdog.patch delete mode 100644 SOURCES/nagios-agents-metadata-105ab8a.tar.gz delete mode 100644 SOURCES/pacemaker-ba59be7.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fad0d0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +SOURCES/nagios-agents-metadata-105ab8a.tar.gz +SOURCES/pacemaker-ada5c3b.tar.gz diff --git a/.pacemaker.metadata b/.pacemaker.metadata new file mode 100644 index 0000000..5af6b2a --- /dev/null +++ b/.pacemaker.metadata @@ -0,0 +1,2 @@ +ea6c0a27fd0ae8ce02f84a11f08a0d79377041c3 SOURCES/nagios-agents-metadata-105ab8a.tar.gz +f9fd69263d5b21446b530f9750c262f7b492cad4 SOURCES/pacemaker-ada5c3b.tar.gz diff --git a/SOURCES/001-acl-group-schema.patch b/SOURCES/001-acl-group-schema.patch new file mode 100644 index 0000000..4835e3e --- /dev/null +++ b/SOURCES/001-acl-group-schema.patch @@ -0,0 +1,230 @@ +From f5ffbaf1f537d3d5b00e594211cd322f97df51ac Mon Sep 17 00:00:00 2001 +From: Grace Chin +Date: Fri, 5 Nov 2021 11:39:39 -0400 +Subject: [PATCH 1/3] Low: xml: clone acls schema in preparation for changes + +--- + xml/acls-3.8.rng | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 80 insertions(+) + create mode 100644 xml/acls-3.8.rng + +diff --git a/xml/acls-3.8.rng b/xml/acls-3.8.rng +new file mode 100644 +index 000000000..0fe6eed96 +--- /dev/null ++++ b/xml/acls-3.8.rng +@@ -0,0 +1,80 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ read ++ write ++ deny ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +-- +2.27.0 + + +From 7838213fc639236bdedf5f15320152d973f1bdad Mon Sep 17 00:00:00 2001 +From: Grace Chin +Date: Fri, 5 Nov 2021 11:40:48 -0400 +Subject: [PATCH 2/3] Add a 'name' attribute to acl_target and acl_group + elements + +--- + xml/acls-3.8.rng | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/xml/acls-3.8.rng b/xml/acls-3.8.rng +index 0fe6eed96..48bcdffe3 100644 +--- a/xml/acls-3.8.rng ++++ b/xml/acls-3.8.rng +@@ -13,6 +13,9 @@ + + + ++ ++ ++ + + + +@@ -22,6 +25,9 @@ + + + ++ ++ ++ + + + +-- +2.27.0 + + +From c3c498f4636f57e29670f8e385b625024ed222d7 Mon Sep 17 00:00:00 2001 +From: Grace Chin +Date: Fri, 5 Nov 2021 11:42:48 -0400 +Subject: [PATCH 3/3] Changes made by run of 'cts/cts-cli -s' + +--- + cts/cli/regression.upgrade.exp | 7 +++++-- + cts/cli/regression.validity.exp | 22 ++++++++++++++++++---- + 2 files changed, 23 insertions(+), 6 deletions(-) + +diff --git a/cts/cli/regression.upgrade.exp b/cts/cli/regression.upgrade.exp +index e38adebdd..7ce7ec13b 100644 +--- a/cts/cli/regression.upgrade.exp ++++ b/cts/cli/regression.upgrade.exp +@@ -91,8 +91,11 @@ update_validation debug: Configuration valid for schema: pacemaker-3.6 + update_validation debug: pacemaker-3.6-style configuration is also valid for pacemaker-3.7 + update_validation debug: Testing 'pacemaker-3.7' validation (21 of X) + update_validation debug: Configuration valid for schema: pacemaker-3.7 +-update_validation trace: Stopping at pacemaker-3.7 +-update_validation info: Transformed the configuration from pacemaker-2.10 to pacemaker-3.7 ++update_validation debug: pacemaker-3.7-style configuration is also valid for pacemaker-3.8 ++update_validation debug: Testing 'pacemaker-3.8' validation (22 of X) ++update_validation debug: Configuration valid for schema: pacemaker-3.8 ++update_validation trace: Stopping at pacemaker-3.8 ++update_validation info: Transformed the configuration from pacemaker-2.10 to pacemaker-3.8 + =#=#=#= Current cib after: Upgrade to latest CIB schema (trigger 2.10.xsl + the wrapping) =#=#=#= + + +diff --git a/cts/cli/regression.validity.exp b/cts/cli/regression.validity.exp +index 5ace430e7..125035a47 100644 +--- a/cts/cli/regression.validity.exp ++++ b/cts/cli/regression.validity.exp +@@ -121,7 +121,11 @@ update_validation debug: Testing 'pacemaker-3.7' validation (21 of X) + element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order + element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order + update_validation trace: pacemaker-3.7 validation failed +-Cannot upgrade configuration (claiming schema pacemaker-1.2) to at least pacemaker-3.0 because it does not validate with any schema from pacemaker-1.2 to pacemaker-3.7 ++update_validation debug: Testing 'pacemaker-3.8' validation (22 of X) ++element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order ++element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order ++update_validation trace: pacemaker-3.8 validation failed ++Cannot upgrade configuration (claiming schema pacemaker-1.2) to at least pacemaker-3.0 because it does not validate with any schema from pacemaker-1.2 to pacemaker-3.8 + =#=#=#= End test: Run crm_simulate with invalid CIB (enum violation) - Invalid configuration (78) =#=#=#= + * Passed: crm_simulate - Run crm_simulate with invalid CIB (enum violation) + =#=#=#= Begin test: Try to make resulting CIB invalid (unrecognized validate-with) =#=#=#= +@@ -226,7 +230,10 @@ update_validation trace: pacemaker-3.6 validation failed + update_validation debug: Testing 'pacemaker-3.7' validation (21 of X) + element cib: Relax-NG validity error : Invalid attribute validate-with for element cib + update_validation trace: pacemaker-3.7 validation failed +-Cannot upgrade configuration (claiming schema pacemaker-9999.0) to at least pacemaker-3.0 because it does not validate with any schema from unknown to pacemaker-3.7 ++update_validation debug: Testing 'pacemaker-3.8' validation (22 of X) ++element cib: Relax-NG validity error : Invalid attribute validate-with for element cib ++update_validation trace: pacemaker-3.8 validation failed ++Cannot upgrade configuration (claiming schema pacemaker-9999.0) to at least pacemaker-3.0 because it does not validate with any schema from unknown to pacemaker-3.8 + =#=#=#= End test: Run crm_simulate with invalid CIB (unrecognized validate-with) - Invalid configuration (78) =#=#=#= + * Passed: crm_simulate - Run crm_simulate with invalid CIB (unrecognized validate-with) + =#=#=#= Begin test: Try to make resulting CIB invalid, but possibly recoverable (valid with X.Y+1) =#=#=#= +@@ -326,8 +333,11 @@ update_validation debug: Configuration valid for schema: pacemaker-3.6 + update_validation debug: pacemaker-3.6-style configuration is also valid for pacemaker-3.7 + update_validation debug: Testing 'pacemaker-3.7' validation (21 of X) + update_validation debug: Configuration valid for schema: pacemaker-3.7 +-update_validation trace: Stopping at pacemaker-3.7 +-update_validation info: Transformed the configuration from pacemaker-1.2 to pacemaker-3.7 ++update_validation debug: pacemaker-3.7-style configuration is also valid for pacemaker-3.8 ++update_validation debug: Testing 'pacemaker-3.8' validation (22 of X) ++update_validation debug: Configuration valid for schema: pacemaker-3.8 ++update_validation trace: Stopping at pacemaker-3.8 ++update_validation info: Transformed the configuration from pacemaker-1.2 to pacemaker-3.8 + unpack_resources error: Resource start-up disabled since no STONITH resources have been defined + unpack_resources error: Either configure some or disable STONITH with the stonith-enabled option + unpack_resources error: NOTE: Clusters with shared data need STONITH to ensure data integrity +@@ -437,6 +447,8 @@ element rsc_order: Relax-NG validity error : Invalid attribute first-action for + element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order + element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order + element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order ++element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order ++element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order + =#=#=#= Current cib after: Make resulting CIB invalid, and without validate-with attribute =#=#=#= + + +@@ -502,6 +514,8 @@ validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attrib + validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order + validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order + validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order ++validity.bad.xml:10: element rsc_order: Relax-NG validity error : Invalid attribute first-action for element rsc_order ++validity.bad.xml:10: element rsc_order: Relax-NG validity error : Element constraints has extra content: rsc_order + unpack_resources error: Resource start-up disabled since no STONITH resources have been defined + unpack_resources error: Either configure some or disable STONITH with the stonith-enabled option + unpack_resources error: NOTE: Clusters with shared data need STONITH to ensure data integrity +-- +2.27.0 + diff --git a/SOURCES/002-fencing-reasons.patch b/SOURCES/002-fencing-reasons.patch new file mode 100644 index 0000000..f89cbec --- /dev/null +++ b/SOURCES/002-fencing-reasons.patch @@ -0,0 +1,2100 @@ +From 95b4f87aae5fb2cf771cf9a8f8e5420b65fb213f Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 21 Sep 2021 10:47:51 -0500 +Subject: [PATCH 01/12] Refactor: fencing: use pcmk__action_result_t in + stonith_action_t + +stonith_action_t previously had an rc member for a legacy return code, along +with output and error members for action stdout/stderr. When setting rc based +on the svc_action_t result, it used a mapping function svc_action_to_errno(). + +This replaces those with a pcmk__action_result_t member, which means we now +track the exit status and execution status as originally set by libcrmservice, +rather than the mapped rc. The library now calls the mapping function, now +returning standard codes and called result2rc(), when calling the client +callback. + +The exit_reason member is unused as of this commit. + +The behavior should be identical, with the small exception of +services_action_async() failure leaving the exit status as set by the services +library, which means callers will get the result2rc() mapping of the actual +result instead of the former -ECONNABORTED. +--- + lib/fencing/st_client.c | 118 +++++++++++++++++++++++----------------- + 1 file changed, 68 insertions(+), 50 deletions(-) + +diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c +index 08adb51c6..6c607b010 100644 +--- a/lib/fencing/st_client.c ++++ b/lib/fencing/st_client.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + #include + +@@ -57,9 +58,7 @@ struct stonith_action_s { + int max_retries; + + int pid; +- int rc; +- char *output; +- char *error; ++ pcmk__action_result_t result; + }; + + typedef struct stonith_private_s { +@@ -120,6 +119,7 @@ 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); ++static int result2rc(const pcmk__action_result_t *result); + + /*! + * \brief Get agent namespace by name +@@ -196,6 +196,23 @@ 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, ++ NULL); ++ 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) + { +@@ -259,19 +276,19 @@ stonith__watchdog_fencing_enabled_for_node(const char *node) + static void + log_action(stonith_action_t *action, pid_t pid) + { +- if (action->output) { ++ if (action->result.action_stdout != NULL) { + /* Logging the whole string confuses syslog when the string is xml */ + char *prefix = crm_strdup_printf("%s[%d] stdout:", action->agent, pid); + +- crm_log_output(LOG_TRACE, prefix, action->output); ++ crm_log_output(LOG_TRACE, prefix, action->result.action_stdout); + free(prefix); + } + +- if (action->error) { ++ 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->error); ++ crm_log_output(LOG_WARNING, prefix, action->result.action_stderr); + free(prefix); + } + } +@@ -645,8 +662,7 @@ stonith__destroy_action(stonith_action_t *action) + if (action->svc_action) { + services_action_free(action->svc_action); + } +- free(action->output); +- free(action->error); ++ pcmk__reset_result(&(action->result)); + free(action); + } + } +@@ -678,15 +694,15 @@ stonith__action_result(stonith_action_t *action, int *rc, char **output, + } + if (action != NULL) { + if (rc) { +- *rc = action->rc; ++ *rc = pcmk_rc2legacy(result2rc(&(action->result))); + } +- if (output && action->output) { +- *output = action->output; +- action->output = NULL; // hand off memory management to caller ++ if ((output != NULL) && (action->result.action_stdout != NULL)) { ++ *output = action->result.action_stdout; ++ action->result.action_stdout = NULL; // hand off ownership to caller + } +- if (error_output && action->error) { +- *error_output = action->error; +- action->error = NULL; // hand off memory management to caller ++ if ((error_output != NULL) && (action->result.action_stderr != NULL)) { ++ *error_output = action->result.action_stderr; ++ action->result.action_stderr = NULL; // hand off ownership to caller + } + } + } +@@ -715,6 +731,9 @@ stonith_action_create(const char *agent, + action->timeout = action->remaining_timeout = timeout; + action->max_retries = FAILURE_MAX_RETRIES; + ++ pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN, ++ NULL); ++ + if (device_args) { + char buffer[512]; + const char *value = NULL; +@@ -739,7 +758,8 @@ update_remaining_timeout(stonith_action_t * action) + 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->rc != -ETIME) && diff < (action->timeout * 0.7)) { ++ } 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; +@@ -750,31 +770,31 @@ update_remaining_timeout(stonith_action_t * action) + } + + static int +-svc_action_to_errno(svc_action_t *svc_action) { +- int rv = pcmk_ok; ++result2rc(const pcmk__action_result_t *result) { ++ int rc = pcmk_rc_ok; + +- if (svc_action->status == PCMK_EXEC_TIMEOUT) { +- rv = -ETIME; ++ if (result->execution_status == PCMK_EXEC_TIMEOUT) { ++ rc = ETIME; + +- } else if (svc_action->rc != PCMK_OCF_OK) { ++ } else if (result->exit_status != CRM_EX_OK) { + /* Try to provide a useful error code based on the fence agent's + * error output. + */ +- if (svc_action->stderr_data == NULL) { +- rv = -ENODATA; ++ if (result->action_stderr == NULL) { ++ rc = ENODATA; + +- } else if (strstr(svc_action->stderr_data, "imed out")) { ++ } else if (strstr(result->action_stderr, "imed out")) { + /* Some agents have their own internal timeouts */ +- rv = -ETIME; ++ rc = ETIME; + +- } else if (strstr(svc_action->stderr_data, "Unrecognised action")) { +- rv = -EOPNOTSUPP; ++ } else if (strstr(result->action_stderr, "Unrecognised action")) { ++ rc = EOPNOTSUPP; + + } else { +- rv = -pcmk_err_generic; ++ rc = pcmk_rc_error; + } + } +- return rv; ++ return rc; + } + + static void +@@ -782,11 +802,7 @@ stonith_action_async_done(svc_action_t *svc_action) + { + stonith_action_t *action = (stonith_action_t *) svc_action->cb_data; + +- action->rc = svc_action_to_errno(svc_action); +- action->output = svc_action->stdout_data; +- svc_action->stdout_data = NULL; +- action->error = svc_action->stderr_data; +- svc_action->stderr_data = NULL; ++ set_result_from_svc_action(action, svc_action); + + svc_action->params = NULL; + +@@ -795,7 +811,9 @@ stonith_action_async_done(svc_action_t *svc_action) + + log_action(action, action->pid); + +- if (action->rc != pcmk_ok && update_remaining_timeout(action)) { ++ if ((action->result.exit_status != CRM_EX_OK) ++ && update_remaining_timeout(action)) { ++ + int rc = internal_stonith_action_execute(action); + if (rc == pcmk_ok) { + return; +@@ -803,7 +821,8 @@ stonith_action_async_done(svc_action_t *svc_action) + } + + if (action->done_cb) { +- action->done_cb(action->pid, action->rc, action->output, action->userdata); ++ action->done_cb(action->pid, pcmk_rc2legacy(result2rc(&(action->result))), ++ action->result.action_stdout, action->userdata); + } + + action->svc_action = NULL; // don't remove our caller +@@ -835,9 +854,13 @@ internal_stonith_action_execute(stonith_action_t * action) + static int stonith_sequence = 0; + char *buffer = NULL; + +- if ((action == NULL) || (action->action == NULL) || (action->args == NULL) ++ CRM_CHECK(action != NULL, return -EINVAL); ++ ++ if ((action->action == NULL) || (action->args == NULL) + || (action->agent == NULL)) { +- return -EPROTO; ++ pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR, ++ PCMK_EXEC_ERROR_FATAL, NULL); ++ return -EINVAL; + } + + if (!action->tries) { +@@ -857,6 +880,7 @@ internal_stonith_action_execute(stonith_action_t * action) + free(buffer); + + if (svc_action->rc != PCMK_OCF_UNKNOWN) { ++ set_result_from_svc_action(action, svc_action); + services_action_free(svc_action); + return -E2BIG; + } +@@ -877,10 +901,7 @@ internal_stonith_action_execute(stonith_action_t * action) + + /* keep retries from executing out of control and free previous results */ + if (is_retry) { +- free(action->output); +- action->output = NULL; +- free(action->error); +- action->error = NULL; ++ pcmk__reset_result(&(action->result)); + sleep(1); + } + +@@ -889,22 +910,19 @@ internal_stonith_action_execute(stonith_action_t * action) + 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; +- action->rc = svc_action_to_errno(svc_action); +- action->output = svc_action->stdout_data; +- svc_action->stdout_data = NULL; +- action->error = svc_action->stderr_data; +- svc_action->stderr_data = NULL; + + } else { // sync failure +- action->rc = -ECONNABORTED; +- rc = action->rc; ++ rc = -ECONNABORTED; + } + ++ set_result_from_svc_action(action, svc_action); + svc_action->params = NULL; + services_action_free(svc_action); + return rc; +-- +2.27.0 + + +From 4c8e0b0ecc53cb3883f0da0eede20b900fff48d1 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 21 Sep 2021 11:14:31 -0500 +Subject: [PATCH 02/12] Low: fencing: improve return code given back to library + callers + +Expose result2rc() internally for future reuse, and expand it to handle more +cases. In theory, this can give us better log messages and status output for +failures. +--- + include/crm/fencing/internal.h | 1 + + lib/fencing/st_client.c | 63 +++++++++++++++++++++------------- + 2 files changed, 41 insertions(+), 23 deletions(-) + +diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h +index fa9059e6f..0d23967bb 100644 +--- a/include/crm/fencing/internal.h ++++ b/include/crm/fencing/internal.h +@@ -60,6 +60,7 @@ stonith_action_t *stonith_action_create(const char *agent, + void stonith__destroy_action(stonith_action_t *action); + void stonith__action_result(stonith_action_t *action, int *rc, char **output, + char **error_output); ++int stonith__result2rc(const pcmk__action_result_t *result); + + int + stonith_action_execute_async(stonith_action_t * action, +diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c +index 6c607b010..809be1640 100644 +--- a/lib/fencing/st_client.c ++++ b/lib/fencing/st_client.c +@@ -119,7 +119,6 @@ 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); +-static int result2rc(const pcmk__action_result_t *result); + + /*! + * \brief Get agent namespace by name +@@ -694,7 +693,7 @@ stonith__action_result(stonith_action_t *action, int *rc, char **output, + } + if (action != NULL) { + if (rc) { +- *rc = pcmk_rc2legacy(result2rc(&(action->result))); ++ *rc = pcmk_rc2legacy(stonith__result2rc(&(action->result))); + } + if ((output != NULL) && (action->result.action_stdout != NULL)) { + *output = action->result.action_stdout; +@@ -769,32 +768,49 @@ update_remaining_timeout(stonith_action_t * action) + return action->remaining_timeout ? TRUE : FALSE; + } + +-static int +-result2rc(const pcmk__action_result_t *result) { +- int rc = pcmk_rc_ok; ++/*! ++ * \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) ++{ ++ switch (result->execution_status) { ++ 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; ++ default: break; ++ } + +- if (result->execution_status == PCMK_EXEC_TIMEOUT) { +- rc = ETIME; ++ if (result->exit_status == CRM_EX_OK) { ++ return pcmk_rc_ok; ++ } + +- } else if (result->exit_status != CRM_EX_OK) { +- /* Try to provide a useful error code based on the fence agent's +- * error output. +- */ +- if (result->action_stderr == NULL) { +- rc = ENODATA; ++ // Try to provide useful error code based on result's error output + +- } else if (strstr(result->action_stderr, "imed out")) { +- /* Some agents have their own internal timeouts */ +- rc = ETIME; ++ if (result->action_stderr == NULL) { ++ return ENODATA; + +- } else if (strstr(result->action_stderr, "Unrecognised action")) { +- rc = EOPNOTSUPP; ++ } else if (strcasestr(result->action_stderr, "timed out") ++ || strcasestr(result->action_stderr, "timeout")) { ++ return ETIME; + +- } else { +- rc = pcmk_rc_error; +- } ++ } else if (strcasestr(result->action_stderr, "unrecognised action") ++ || strcasestr(result->action_stderr, "unrecognized action") ++ || strcasestr(result->action_stderr, "unsupported action")) { ++ return EOPNOTSUPP; + } +- return rc; ++ ++ // Oh well, we tried ++ return pcmk_rc_error; + } + + static void +@@ -821,7 +837,8 @@ stonith_action_async_done(svc_action_t *svc_action) + } + + if (action->done_cb) { +- action->done_cb(action->pid, pcmk_rc2legacy(result2rc(&(action->result))), ++ action->done_cb(action->pid, ++ pcmk_rc2legacy(stonith__result2rc(&(action->result))), + action->result.action_stdout, action->userdata); + } + +-- +2.27.0 + + +From 153c9b552a5bad9dd36e8635fa478ed9cad1f240 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 7 Oct 2021 11:35:44 -0500 +Subject: [PATCH 03/12] Refactor: fencing: return full result from + stonith__action_result() + +Previously, stonith__action_result() grabbed an action's legacy rc, stdout, and +stderr separately. Now, directly return a pointer to the action's result +object, and map that to a legacy rc in the callers when needed. +--- + include/crm/fencing/internal.h | 3 +-- + lib/fencing/st_client.c | 36 ++++--------------------- + lib/fencing/st_rhcs.c | 48 ++++++++++++++++++++++++---------- + 3 files changed, 40 insertions(+), 47 deletions(-) + +diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h +index 0d23967bb..4e9f50fe8 100644 +--- a/include/crm/fencing/internal.h ++++ b/include/crm/fencing/internal.h +@@ -58,8 +58,7 @@ stonith_action_t *stonith_action_create(const char *agent, + GHashTable * port_map, + const char * host_arg); + void stonith__destroy_action(stonith_action_t *action); +-void stonith__action_result(stonith_action_t *action, int *rc, char **output, +- char **error_output); ++pcmk__action_result_t *stonith__action_result(stonith_action_t *action); + int stonith__result2rc(const pcmk__action_result_t *result); + + int +diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c +index 809be1640..b9df18465 100644 +--- a/lib/fencing/st_client.c ++++ b/lib/fencing/st_client.c +@@ -670,40 +670,14 @@ stonith__destroy_action(stonith_action_t *action) + * \internal + * \brief Get the result of an executed stonith action + * +- * \param[in,out] action Executed action +- * \param[out] rc Where to store result code (or NULL) +- * \param[out] output Where to store standard output (or NULL) +- * \param[out] error_output Where to store standard error output (or NULL) ++ * \param[in] action Executed action + * +- * \note If output or error_output is not NULL, the caller is responsible for +- * freeing the memory. ++ * \return Pointer to action's result (or NULL if \p action is NULL) + */ +-void +-stonith__action_result(stonith_action_t *action, int *rc, char **output, +- char **error_output) ++pcmk__action_result_t * ++stonith__action_result(stonith_action_t *action) + { +- if (rc) { +- *rc = pcmk_ok; +- } +- if (output) { +- *output = NULL; +- } +- if (error_output) { +- *error_output = NULL; +- } +- if (action != NULL) { +- if (rc) { +- *rc = pcmk_rc2legacy(stonith__result2rc(&(action->result))); +- } +- if ((output != NULL) && (action->result.action_stdout != NULL)) { +- *output = action->result.action_stdout; +- action->result.action_stdout = NULL; // hand off ownership to caller +- } +- if ((error_output != NULL) && (action->result.action_stderr != NULL)) { +- *error_output = action->result.action_stderr; +- action->result.action_stderr = NULL; // hand off ownership to caller +- } +- } ++ return (action == NULL)? NULL : &(action->result); + } + + #define FAILURE_MAX_RETRIES 2 +diff --git a/lib/fencing/st_rhcs.c b/lib/fencing/st_rhcs.c +index 89a2625bd..23e694975 100644 +--- a/lib/fencing/st_rhcs.c ++++ b/lib/fencing/st_rhcs.c +@@ -1,5 +1,5 @@ + /* +- * Copyright 2004-2020 the Pacemaker project contributors ++ * Copyright 2004-2021 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * +@@ -123,10 +123,10 @@ stonith_rhcs_parameter_not_required(xmlNode *metadata, const char *parameter) + static int + stonith__rhcs_get_metadata(const char *agent, int timeout, xmlNode **metadata) + { +- char *buffer = NULL; + xmlNode *xml = NULL; + xmlNode *actions = NULL; + xmlXPathObject *xpathObj = NULL; ++ pcmk__action_result_t *result = NULL; + stonith_action_t *action = stonith_action_create(agent, "metadata", NULL, 0, + 5, NULL, NULL, NULL); + int rc = stonith__execute(action); +@@ -138,23 +138,31 @@ stonith__rhcs_get_metadata(const char *agent, int timeout, xmlNode **metadata) + return rc; + } + +- stonith__action_result(action, &rc, &buffer, NULL); +- stonith__destroy_action(action); +- if (rc < 0) { +- crm_warn("Metadata action for %s failed: %s " CRM_XS "rc=%d", +- agent, pcmk_strerror(rc), rc); +- free(buffer); +- return rc; ++ result = stonith__action_result(action); ++ ++ if (result->execution_status != PCMK_EXEC_DONE) { ++ crm_warn("Could not execute metadata action for %s: %s", ++ agent, pcmk_exec_status_str(result->execution_status)); ++ stonith__destroy_action(action); ++ return pcmk_rc2legacy(stonith__result2rc(result)); + } + +- if (buffer == NULL) { ++ if (result->exit_status != CRM_EX_OK) { ++ crm_warn("Metadata action for %s returned error code %d", ++ agent, result->exit_status); ++ stonith__destroy_action(action); ++ return pcmk_rc2legacy(stonith__result2rc(result)); ++ } ++ ++ if (result->action_stdout == NULL) { + crm_warn("Metadata action for %s returned no data", agent); ++ stonith__destroy_action(action); + return -ENODATA; + } + +- xml = string2xml(buffer); +- free(buffer); +- buffer = NULL; ++ xml = string2xml(result->action_stdout); ++ stonith__destroy_action(action); ++ + if (xml == NULL) { + crm_warn("Metadata for %s is invalid", agent); + return -pcmk_err_schema_validation; +@@ -289,7 +297,19 @@ stonith__rhcs_validate(stonith_t *st, int call_options, const char *target, + + rc = stonith__execute(action); + if (rc == pcmk_ok) { +- stonith__action_result(action, &rc, output, error_output); ++ pcmk__action_result_t *result = stonith__action_result(action); ++ ++ rc = pcmk_rc2legacy(stonith__result2rc(result)); ++ ++ // Take ownership of output so stonith__destroy_action() doesn't free it ++ if (output != NULL) { ++ *output = result->action_stdout; ++ result->action_stdout = NULL; ++ } ++ if (error_output != NULL) { ++ *error_output = result->action_stderr; ++ result->action_stderr = NULL; ++ } + } + stonith__destroy_action(action); + return rc; +-- +2.27.0 + + +From 7f7067014357cccb229a0bef091e234eb3765f7a Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 21 Sep 2021 13:05:54 -0500 +Subject: [PATCH 04/12] Refactor: fencing: pass full result to async action + callback + +When executing an asynchronous fence agent command, the fencing library gets +the full result (exit status, execution status, and exit reason) from the +services library, then maps that to a legacy return code. + +Now, pass the full result object to the fencing async callback, rather than +separate arguments for legacy code and stdout. The mapping to a legacy code now +happens in the fencer rather than the fencing library. + +The goal of this and following commits is to push the full result object +further down the code path, so that ultimately the full result is always +available internally, and the legacy code mapping is only done for backward +compatibility when sending the result back to a client. + +This commit focuses on the async callback (done_cb() in both the fencer's +async_command_t and the fencing library's stonith_action_t). Later commits will +follow the chain: + + st_child_done() and stonith_fence_get_devices_cb() + -> stonith_send_async_reply() + -> stonith_construct_async_reply() and log_async_result() +--- + daemons/fenced/fenced_commands.c | 78 +++++++++++++++++++++----------- + include/crm/fencing/internal.h | 3 +- + lib/fencing/st_client.c | 10 ++-- + 3 files changed, 58 insertions(+), 33 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index b5ae28d90..d5d04ae69 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -62,7 +62,8 @@ struct device_search_s { + }; + + static gboolean stonith_device_dispatch(gpointer user_data); +-static void st_child_done(int pid, int rc, const char *output, void *user_data); ++static void st_child_done(int pid, const pcmk__action_result_t *result, ++ void *user_data); + static void stonith_send_reply(xmlNode * reply, int call_options, const char *remote_peer, + const char *client_id); + +@@ -99,7 +100,8 @@ typedef struct async_command_s { + GList *device_next; + + void *internal_user_data; +- void (*done_cb) (int pid, int rc, const char *output, void *user_data); ++ void (*done_cb) (int pid, const pcmk__action_result_t *result, ++ void *user_data); + guint timer_sigterm; + guint timer_sigkill; + /*! If the operation timed out, this is the last signal +@@ -377,13 +379,25 @@ get_agent_metadata_cb(gpointer data) { + * \internal + * \brief Call a command's action callback for an internal (not library) result + * +- * \param[in] cmd Command to report result for +- * \param[in] rc Legacy return code to pass to callback ++ * \param[in] cmd Command to report result for ++ * \param[in] execution_status Execution status to use for result ++ * \param[in] exit_status Exit status to use for result ++ * \param[in] exit_reason Exit reason to use for result + */ + static void +-report_internal_result(async_command_t *cmd, int rc) ++report_internal_result(async_command_t *cmd, int exit_status, ++ int execution_status, const char *exit_reason) + { +- cmd->done_cb(0, rc, NULL, cmd); ++ pcmk__action_result_t result = { ++ // Ensure we don't pass garbage to free() ++ .exit_reason = NULL, ++ .action_stdout = NULL, ++ .action_stderr = NULL ++ }; ++ ++ pcmk__set_result(&result, exit_status, execution_status, exit_reason); ++ cmd->done_cb(0, &result, cmd); ++ pcmk__reset_result(&result); + } + + static gboolean +@@ -446,7 +460,7 @@ stonith_device_execute(stonith_device_t * device) + } + } else { + crm_info("Faking success for %s watchdog operation", cmd->action); +- report_internal_result(cmd, pcmk_ok); ++ report_internal_result(cmd, CRM_EX_OK, PCMK_EXEC_DONE, NULL); + goto done; + } + } +@@ -462,7 +476,8 @@ stonith_device_execute(stonith_device_t * device) + crm_err("Considering %s unconfigured " + "because unable to load CIB secrets: %s", + device->id, pcmk_rc_str(exec_rc)); +- report_internal_result(cmd, -EACCES); ++ report_internal_result(cmd, CRM_EX_ERROR, PCMK_EXEC_NO_SECRETS, ++ NULL); + goto done; + } + } +@@ -501,7 +516,7 @@ stonith_device_execute(stonith_device_t * device) + cmd->done_cb, fork_cb); + if (exec_rc < 0) { + cmd->activating_on = NULL; +- report_internal_result(cmd, exec_rc); ++ cmd->done_cb(0, stonith__action_result(action), cmd); + stonith__destroy_action(action); + } + +@@ -625,7 +640,8 @@ free_device(gpointer data) + async_command_t *cmd = gIter->data; + + crm_warn("Removal of device '%s' purged operation '%s'", device->id, cmd->action); +- report_internal_result(cmd, -ENODEV); ++ report_internal_result(cmd, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE, ++ NULL); + } + g_list_free(device->pending_ops); + +@@ -1079,7 +1095,8 @@ schedule_internal_command(const char *origin, + const char *victim, + int timeout, + void *internal_user_data, +- void (*done_cb) (int pid, int rc, const char *output, ++ void (*done_cb) (int pid, ++ const pcmk__action_result_t *result, + void *user_data)) + { + async_command_t *cmd = NULL; +@@ -1111,7 +1128,7 @@ enum fence_status_code { + }; + + static void +-status_search_cb(int pid, int rc, const char *output, void *user_data) ++status_search_cb(int pid, const pcmk__action_result_t *result, void *user_data) + { + async_command_t *cmd = user_data; + struct device_search_s *search = cmd->internal_user_data; +@@ -1127,7 +1144,7 @@ status_search_cb(int pid, int rc, const char *output, void *user_data) + + mainloop_set_trigger(dev->work); + +- switch (rc) { ++ switch (result->exit_status) { + case fence_status_unknown: + crm_trace("%s reported it cannot fence %s", dev->id, search->host); + break; +@@ -1141,14 +1158,15 @@ status_search_cb(int pid, int rc, const char *output, void *user_data) + default: + crm_warn("Assuming %s cannot fence %s " + "(status returned unknown code %d)", +- dev->id, search->host, rc); ++ dev->id, search->host, result->exit_status); + break; + } + search_devices_record_result(search, dev->id, can); + } + + static void +-dynamic_list_search_cb(int pid, int rc, const char *output, void *user_data) ++dynamic_list_search_cb(int pid, const pcmk__action_result_t *result, ++ void *user_data) + { + async_command_t *cmd = user_data; + struct device_search_s *search = cmd->internal_user_data; +@@ -1169,21 +1187,21 @@ dynamic_list_search_cb(int pid, int rc, const char *output, void *user_data) + + mainloop_set_trigger(dev->work); + +- if (rc == CRM_EX_OK) { ++ if (result->exit_status == CRM_EX_OK) { + crm_info("Refreshing target list for %s", dev->id); + g_list_free_full(dev->targets, free); +- dev->targets = stonith__parse_targets(output); ++ dev->targets = stonith__parse_targets(result->action_stdout); + dev->targets_age = time(NULL); + + } else if (dev->targets != NULL) { + crm_info("Reusing most recent target list for %s " + "because list returned error code %d", +- dev->id, rc); ++ dev->id, result->exit_status); + + } else { // We have never successfully executed list + crm_warn("Assuming %s cannot fence %s " + "because list returned error code %d", +- dev->id, search->host, rc); ++ dev->id, search->host, result->exit_status); + + /* Fall back to pcmk_host_check="status" if the user didn't explicitly + * specify "dynamic-list". +@@ -2407,7 +2425,7 @@ cancel_stonith_command(async_command_t * cmd) + } + + static void +-st_child_done(int pid, int rc, const char *output, void *user_data) ++st_child_done(int pid, const pcmk__action_result_t *result, void *user_data) + { + stonith_device_t *device = NULL; + stonith_device_t *next_device = NULL; +@@ -2423,7 +2441,7 @@ st_child_done(int pid, int rc, const char *output, 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 && (rc == pcmk_ok) && ++ if (!device->verified && (result->exit_status == CRM_EX_OK) && + (pcmk__strcase_any_of(cmd->action, "list", "monitor", "status", NULL))) { + + device->verified = TRUE; +@@ -2432,7 +2450,7 @@ st_child_done(int pid, int rc, const char *output, void *user_data) + mainloop_set_trigger(device->work); + } + +- if (rc == 0) { ++ if (result->exit_status == CRM_EX_OK) { + 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) { +@@ -2445,7 +2463,8 @@ st_child_done(int pid, int rc, const char *output, void *user_data) + next_device = NULL; + } + +- } else if (rc != 0 && cmd->device_next && (is_action_required(cmd->action, device) == FALSE)) { ++ } else if ((cmd->device_next != NULL) ++ && !is_action_required(cmd->action, device)) { + /* if this device didn't work out, see if there are any others we can try. + * if the failed device was 'required', we can't pick another device. */ + next_device = g_hash_table_lookup(device_list, cmd->device_next->data); +@@ -2454,16 +2473,19 @@ st_child_done(int pid, int rc, const char *output, void *user_data) + + /* this operation requires more fencing, hooray! */ + if (next_device) { +- log_async_result(cmd, rc, pid, next_device->id, output, FALSE); ++ log_async_result(cmd, pcmk_rc2legacy(stonith__result2rc(result)), pid, ++ next_device->id, result->action_stdout, FALSE); + schedule_stonith_command(cmd, next_device); + /* Prevent cmd from being freed */ + cmd = NULL; + goto done; + } + +- stonith_send_async_reply(cmd, output, rc, pid, false); ++ stonith_send_async_reply(cmd, result->action_stdout, ++ pcmk_rc2legacy(stonith__result2rc(result)), pid, ++ false); + +- if (rc != 0) { ++ if (result->exit_status != CRM_EX_OK) { + goto done; + } + +@@ -2509,7 +2531,9 @@ st_child_done(int pid, int rc, const char *output, void *user_data) + + cmd_list = g_list_remove_link(cmd_list, gIter); + +- stonith_send_async_reply(cmd_other, output, rc, pid, true); ++ stonith_send_async_reply(cmd_other, result->action_stdout, ++ pcmk_rc2legacy(stonith__result2rc(result)), ++ pid, true); + cancel_stonith_command(cmd_other); + + free_async_command(cmd_other); +diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h +index 4e9f50fe8..6a7e4232c 100644 +--- a/include/crm/fencing/internal.h ++++ b/include/crm/fencing/internal.h +@@ -64,7 +64,8 @@ int stonith__result2rc(const pcmk__action_result_t *result); + int + stonith_action_execute_async(stonith_action_t * action, + void *userdata, +- void (*done) (int pid, int rc, const char *output, ++ void (*done) (int pid, ++ const pcmk__action_result_t *result, + void *user_data), + void (*fork_cb) (int pid, void *user_data)); + +diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c +index b9df18465..59dcab9a3 100644 +--- a/lib/fencing/st_client.c ++++ b/lib/fencing/st_client.c +@@ -46,7 +46,8 @@ struct stonith_action_s { + int timeout; + int async; + void *userdata; +- void (*done_cb) (int pid, int status, const char *output, void *user_data); ++ 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; +@@ -811,9 +812,7 @@ stonith_action_async_done(svc_action_t *svc_action) + } + + if (action->done_cb) { +- action->done_cb(action->pid, +- pcmk_rc2legacy(stonith__result2rc(&(action->result))), +- action->result.action_stdout, action->userdata); ++ action->done_cb(action->pid, &(action->result), action->userdata); + } + + action->svc_action = NULL; // don't remove our caller +@@ -933,7 +932,8 @@ internal_stonith_action_execute(stonith_action_t * action) + int + stonith_action_execute_async(stonith_action_t * action, + void *userdata, +- void (*done) (int pid, int rc, const char *output, ++ void (*done) (int pid, ++ const pcmk__action_result_t *result, + void *user_data), + void (*fork_cb) (int pid, void *user_data)) + { +-- +2.27.0 + + +From bbd022306df7a873c0ecb2be2d33c56fbf327b8c Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 21 Sep 2021 11:51:28 -0500 +Subject: [PATCH 05/12] Feature: fencing: set exit reason for internal + execution errors + +... most importantly, copying any exit reason set by the services library. +This ensures that the stonith_action_t exit reason is set when appropriate. +However, nothing uses it as of this commit. +--- + daemons/fenced/fenced_commands.c | 4 ++-- + lib/fencing/st_client.c | 6 +++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index d5d04ae69..f55a32649 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -477,7 +477,7 @@ stonith_device_execute(stonith_device_t * device) + "because unable to load CIB secrets: %s", + device->id, pcmk_rc_str(exec_rc)); + report_internal_result(cmd, CRM_EX_ERROR, PCMK_EXEC_NO_SECRETS, +- NULL); ++ "Failed to get CIB secrets"); + goto done; + } + } +@@ -641,7 +641,7 @@ free_device(gpointer data) + + crm_warn("Removal of device '%s' purged operation '%s'", device->id, cmd->action); + report_internal_result(cmd, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE, +- NULL); ++ "Device was removed before action could be executed"); + } + g_list_free(device->pending_ops); + +diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c +index 59dcab9a3..3d4127eff 100644 +--- a/lib/fencing/st_client.c ++++ b/lib/fencing/st_client.c +@@ -207,7 +207,7 @@ 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, +- NULL); ++ services__exit_reason(svc_action)); + pcmk__set_result_output(&(action->result), + services__grab_stdout(svc_action), + services__grab_stderr(svc_action)); +@@ -706,7 +706,7 @@ stonith_action_create(const char *agent, + action->max_retries = FAILURE_MAX_RETRIES; + + pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN, PCMK_EXEC_UNKNOWN, +- NULL); ++ "Initialization bug in fencing library"); + + if (device_args) { + char buffer[512]; +@@ -849,7 +849,7 @@ internal_stonith_action_execute(stonith_action_t * action) + if ((action->action == NULL) || (action->args == NULL) + || (action->agent == NULL)) { + pcmk__set_result(&(action->result), PCMK_OCF_UNKNOWN_ERROR, +- PCMK_EXEC_ERROR_FATAL, NULL); ++ PCMK_EXEC_ERROR_FATAL, "Bug in fencing library"); + return -EINVAL; + } + +-- +2.27.0 + + +From ed08f600688af1d25412d2427502ba5d4a55c0d6 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 7 Oct 2021 12:06:10 -0500 +Subject: [PATCH 06/12] Fix: fencer: handle dynamic target query failures + better + +Previously, the callbacks for list and status queries checked only the result's +exit status. However, the services library will use PCMK_OCF_UNKNOWN_ERROR (1) +as the exit status for internal failures, and that value signifies a recognized +node (not an error) for fence list actions. + +Now, the callbacks check the execution status as well. +--- + daemons/fenced/fenced_commands.c | 46 +++++++++++++++++++++++++++----- + 1 file changed, 39 insertions(+), 7 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index f55a32649..7b3fb25a1 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -1144,6 +1144,18 @@ status_search_cb(int pid, const pcmk__action_result_t *result, void *user_data) + + mainloop_set_trigger(dev->work); + ++ if (result->execution_status != PCMK_EXEC_DONE) { ++ crm_warn("Assuming %s cannot fence %s " ++ "because status could not be executed: %s%s%s%s", ++ dev->id, search->host, ++ pcmk_exec_status_str(result->execution_status), ++ ((result->exit_reason == NULL)? "" : " ("), ++ ((result->exit_reason == NULL)? "" : result->exit_reason), ++ ((result->exit_reason == NULL)? "" : ")")); ++ search_devices_record_result(search, dev->id, FALSE); ++ return; ++ } ++ + switch (result->exit_status) { + case fence_status_unknown: + crm_trace("%s reported it cannot fence %s", dev->id, search->host); +@@ -1187,21 +1199,41 @@ dynamic_list_search_cb(int pid, const pcmk__action_result_t *result, + + mainloop_set_trigger(dev->work); + +- if (result->exit_status == CRM_EX_OK) { ++ if ((result->execution_status == PCMK_EXEC_DONE) ++ && (result->exit_status == CRM_EX_OK)) { + crm_info("Refreshing target list for %s", dev->id); + g_list_free_full(dev->targets, free); + dev->targets = stonith__parse_targets(result->action_stdout); + dev->targets_age = time(NULL); + + } else if (dev->targets != NULL) { +- crm_info("Reusing most recent target list for %s " +- "because list returned error code %d", +- dev->id, result->exit_status); ++ if (result->execution_status == PCMK_EXEC_DONE) { ++ crm_info("Reusing most recent target list for %s " ++ "because list returned error code %d", ++ dev->id, result->exit_status); ++ } else { ++ crm_info("Reusing most recent target list for %s " ++ "because list could not be executed: %s%s%s%s", ++ dev->id, pcmk_exec_status_str(result->execution_status), ++ ((result->exit_reason == NULL)? "" : " ("), ++ ((result->exit_reason == NULL)? "" : result->exit_reason), ++ ((result->exit_reason == NULL)? "" : ")")); ++ } + + } else { // We have never successfully executed list +- crm_warn("Assuming %s cannot fence %s " +- "because list returned error code %d", +- dev->id, search->host, result->exit_status); ++ if (result->execution_status == PCMK_EXEC_DONE) { ++ crm_warn("Assuming %s cannot fence %s " ++ "because list returned error code %d", ++ dev->id, search->host, result->exit_status); ++ } else { ++ crm_warn("Assuming %s cannot fence %s " ++ "because list could not be executed: %s%s%s%s", ++ dev->id, search->host, ++ pcmk_exec_status_str(result->execution_status), ++ ((result->exit_reason == NULL)? "" : " ("), ++ ((result->exit_reason == NULL)? "" : result->exit_reason), ++ ((result->exit_reason == NULL)? "" : ")")); ++ } + + /* Fall back to pcmk_host_check="status" if the user didn't explicitly + * specify "dynamic-list". +-- +2.27.0 + + +From 5a30238a3b8691a5fc20f53906c0efcc50193306 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 21 Sep 2021 15:57:50 -0500 +Subject: [PATCH 07/12] Refactor: fencer: pass result object when sending an + async reply + +... via stonith_send_async_reply(), instead of sending the mapped legacy code +and action stdout separately. Also, drop the "stonith_" prefix since the +function is static. + +This moves the mapping from the stonith_send_async_reply() callers to the +function itself, so we use the result object and standard codes as long as +possible, and map to a legacy code only where needed. +--- + daemons/fenced/fenced_commands.c | 62 +++++++++++++++++++------------- + daemons/fenced/fenced_remote.c | 2 +- + 2 files changed, 39 insertions(+), 25 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index 7b3fb25a1..e5f8162ce 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -2376,12 +2376,28 @@ log_async_result(async_command_t *cmd, int rc, int pid, const char *next, + } + } + ++/*! ++ * \internal ++ * \brief Reply to requester after asynchronous command completion ++ * ++ * \param[in] cmd Command that completed ++ * \param[in] result Result of command ++ * \param[in] pid Process ID of command, if available ++ * \param[in] merged If true, command was merged with another, not executed ++ */ + static void +-stonith_send_async_reply(async_command_t *cmd, const char *output, int rc, +- int pid, bool merged) ++send_async_reply(async_command_t *cmd, const pcmk__action_result_t *result, ++ int pid, bool merged) + { + xmlNode *reply = NULL; + gboolean bcast = FALSE; ++ const char *output = NULL; ++ int rc = pcmk_ok; ++ ++ CRM_CHECK((cmd != NULL) && (result != NULL), return); ++ ++ output = result->action_stdout; ++ rc = pcmk_rc2legacy(stonith__result2rc(result)); + + reply = stonith_construct_async_reply(cmd, output, NULL, rc); + +@@ -2513,9 +2529,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data) + goto done; + } + +- stonith_send_async_reply(cmd, result->action_stdout, +- pcmk_rc2legacy(stonith__result2rc(result)), pid, +- false); ++ send_async_reply(cmd, result, pid, false); + + if (result->exit_status != CRM_EX_OK) { + goto done; +@@ -2563,9 +2577,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data) + + cmd_list = g_list_remove_link(cmd_list, gIter); + +- stonith_send_async_reply(cmd_other, result->action_stdout, +- pcmk_rc2legacy(stonith__result2rc(result)), +- pid, true); ++ send_async_reply(cmd_other, result, pid, true); + cancel_stonith_command(cmd_other); + + free_async_command(cmd_other); +@@ -2604,26 +2616,28 @@ stonith_fence_get_devices_cb(GList * devices, void *user_data) + /* Order based on priority */ + devices = g_list_sort(devices, sort_device_priority); + device = g_hash_table_lookup(device_list, devices->data); +- +- if (device) { +- cmd->device_list = devices; +- cmd->device_next = devices->next; +- devices = NULL; /* list owned by cmd now */ +- } + } + +- /* we have a device, schedule it for fencing. */ +- if (device) { +- schedule_stonith_command(cmd, device); +- /* in progress */ +- return; +- } ++ 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 ++ }; + +- /* no device found! */ +- stonith_send_async_reply(cmd, NULL, -ENODEV, 0, false); ++ pcmk__set_result(&result, CRM_EX_ERROR, PCMK_EXEC_NO_FENCE_DEVICE, ++ "No fence device configured for target"); ++ send_async_reply(cmd, &result, 0, false); ++ pcmk__reset_result(&result); ++ free_async_command(cmd); ++ g_list_free_full(devices, free); + +- free_async_command(cmd); +- g_list_free_full(devices, free); ++ } else { // Device found, schedule it for fencing ++ cmd->device_list = devices; ++ cmd->device_next = devices->next; ++ schedule_stonith_command(cmd, device); ++ } + } + + static int +diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c +index ffaf60018..b09d2865e 100644 +--- a/daemons/fenced/fenced_remote.c ++++ b/daemons/fenced/fenced_remote.c +@@ -996,7 +996,7 @@ stonith_manual_ack(xmlNode * msg, remote_fencing_op_t * op) + + remote_op_done(op, msg, pcmk_ok, FALSE); + +- /* Replies are sent via done_cb->stonith_send_async_reply()->do_local_reply() */ ++ // Replies are sent via done_cb -> send_async_reply() -> do_local_reply() + return -EINPROGRESS; + } + +-- +2.27.0 + + +From c67b6bfbe0baa1253058417ddfb9bc4cf0844e27 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 7 Oct 2021 17:25:38 -0500 +Subject: [PATCH 08/12] Refactor: fencer: pass result object when building + async reply + +... via stonith_construct_async_reply(), instead of passing a mapped legacy rc +and action output separately, which will be helpful when we add the exit reason +to the reply. Also, drop the "stonith_" prefix since the function is static, and +drop an unused argument. +--- + daemons/fenced/fenced_commands.c | 33 +++++++++++++++----------------- + 1 file changed, 15 insertions(+), 18 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index e5f8162ce..6bc12e6c4 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -112,8 +112,8 @@ typedef struct async_command_s { + stonith_device_t *activating_on; + } async_command_t; + +-static xmlNode *stonith_construct_async_reply(async_command_t * cmd, const char *output, +- xmlNode * data, int rc); ++static xmlNode *construct_async_reply(async_command_t *cmd, ++ const pcmk__action_result_t *result); + + static gboolean + is_action_required(const char *action, stonith_device_t *device) +@@ -2399,7 +2399,7 @@ send_async_reply(async_command_t *cmd, const pcmk__action_result_t *result, + output = result->action_stdout; + rc = pcmk_rc2legacy(stonith__result2rc(result)); + +- reply = stonith_construct_async_reply(cmd, output, NULL, rc); ++ reply = construct_async_reply(cmd, result); + + // Only replies for certain actions are broadcast + if (pcmk__str_any_of(cmd->action, "metadata", "monitor", "list", "status", +@@ -2732,17 +2732,20 @@ stonith_construct_reply(xmlNode * request, const char *output, xmlNode * data, i + return reply; + } + ++/*! ++ * \internal ++ * \brief Build an XML reply to an asynchronous fencing command ++ * ++ * \param[in] cmd Fencing command that reply is for ++ * \param[in] result Command result ++ */ + static xmlNode * +-stonith_construct_async_reply(async_command_t * cmd, const char *output, xmlNode * data, int rc) ++construct_async_reply(async_command_t *cmd, const pcmk__action_result_t *result) + { +- xmlNode *reply = NULL; +- +- crm_trace("Creating a basic reply"); +- reply = create_xml_node(NULL, T_STONITH_REPLY); ++ xmlNode *reply = create_xml_node(NULL, T_STONITH_REPLY); + + crm_xml_add(reply, "st_origin", __func__); + crm_xml_add(reply, F_TYPE, T_STONITH_NG); +- + crm_xml_add(reply, F_STONITH_OPERATION, cmd->op); + crm_xml_add(reply, F_STONITH_DEVICE, cmd->device); + crm_xml_add(reply, F_STONITH_REMOTE_OP_ID, cmd->remote_op_id); +@@ -2753,15 +2756,9 @@ stonith_construct_async_reply(async_command_t * cmd, const char *output, xmlNode + crm_xml_add(reply, F_STONITH_ORIGIN, cmd->origin); + crm_xml_add_int(reply, F_STONITH_CALLID, cmd->id); + crm_xml_add_int(reply, F_STONITH_CALLOPTS, cmd->options); +- +- crm_xml_add_int(reply, F_STONITH_RC, rc); +- +- crm_xml_add(reply, "st_output", output); +- +- if (data != NULL) { +- crm_info("Attaching reply output"); +- add_message_xml(reply, F_STONITH_CALLDATA, data); +- } ++ crm_xml_add_int(reply, F_STONITH_RC, ++ pcmk_rc2legacy(stonith__result2rc(result))); ++ crm_xml_add(reply, "st_output", result->action_stdout); + return reply; + } + +-- +2.27.0 + + +From 2686caeb3b74f687ddd86a4e483250ca8096ba7c Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 19 Oct 2021 18:27:31 -0500 +Subject: [PATCH 09/12] Log: fencer: improve messages for asynchronous results + +Now that we have the full result object, pass it to log_async_result(). +Instead of logging a mapped legacy rc, log the execution status or exit status +as appropriate, along with the exit reason. +--- + daemons/fenced/fenced_commands.c | 43 +++++++++++++++++--------------- + 1 file changed, 23 insertions(+), 20 deletions(-) + +diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c +index 6bc12e6c4..9d06c68dc 100644 +--- a/daemons/fenced/fenced_commands.c ++++ b/daemons/fenced/fenced_commands.c +@@ -2305,15 +2305,14 @@ stonith_query(xmlNode * msg, const char *remote_peer, const char *client_id, int + * \brief Log the result of an asynchronous command + * + * \param[in] cmd Command the result is for +- * \param[in] rc Legacy return code corresponding to result ++ * \param[in] result Result of command + * \param[in] pid Process ID of command, if available + * \param[in] next Alternate device that will be tried if command failed +- * \param[in] output Command output, if any + * \param[in] op_merged Whether this command was merged with an earlier one + */ + static void +-log_async_result(async_command_t *cmd, int rc, int pid, const char *next, +- const char *output, gboolean op_merged) ++log_async_result(async_command_t *cmd, const pcmk__action_result_t *result, ++ int pid, const char *next, bool op_merged) + { + int log_level = LOG_ERR; + int output_log_level = LOG_NEVER; +@@ -2321,17 +2320,18 @@ log_async_result(async_command_t *cmd, int rc, int pid, const char *next, + + GString *msg = g_string_sized_new(80); // Reasonable starting size + +- // Choose log levels appropriately +- if (rc == 0) { // Success ++ // Choose log levels appropriately if we have a result ++ if ((result->execution_status == PCMK_EXEC_DONE) ++ && (result->exit_status == CRM_EX_OK)) { // Success + log_level = (cmd->victim == NULL)? LOG_DEBUG : LOG_NOTICE; +- if ((output != NULL) ++ if ((result->action_stdout != NULL) + && !pcmk__str_eq(cmd->action, "metadata", pcmk__str_casei)) { + output_log_level = LOG_DEBUG; + } + next = NULL; + } else { // Failure + log_level = (cmd->victim == NULL)? LOG_NOTICE : LOG_ERR; +- if ((output != NULL) ++ if ((result->action_stdout != NULL) + && !pcmk__str_eq(cmd->action, "metadata", pcmk__str_casei)) { + output_log_level = LOG_WARNING; + } +@@ -2347,10 +2347,18 @@ log_async_result(async_command_t *cmd, int rc, int pid, const char *next, + } + g_string_append_printf(msg, "using %s ", cmd->device); + +- // Add result +- g_string_append_printf(msg, "returned %d (%s)", rc, pcmk_strerror(rc)); ++ // Add exit status or execution status as appropriate ++ if (result->execution_status == PCMK_EXEC_DONE) { ++ g_string_append_printf(msg, "returned %d", result->exit_status); ++ } else { ++ g_string_append_printf(msg, "could not be executed: %s", ++ pcmk_exec_status_str(result->execution_status)); ++ } + +- // Add next device if appropriate ++ // Add exit reason and next device if appropriate ++ if (result->exit_reason != NULL) { ++ g_string_append_printf(msg, " (%s)", result->exit_reason); ++ } + if (next != NULL) { + g_string_append_printf(msg, ", retrying with %s", next); + } +@@ -2371,7 +2379,7 @@ log_async_result(async_command_t *cmd, int rc, int pid, const char *next, + if (output_log_level != LOG_NEVER) { + char *prefix = crm_strdup_printf("%s[%d]", cmd->device, pid); + +- crm_log_output(output_log_level, prefix, output); ++ crm_log_output(output_log_level, prefix, result->action_stdout); + free(prefix); + } + } +@@ -2391,14 +2399,9 @@ send_async_reply(async_command_t *cmd, const pcmk__action_result_t *result, + { + xmlNode *reply = NULL; + gboolean bcast = FALSE; +- const char *output = NULL; +- int rc = pcmk_ok; + + CRM_CHECK((cmd != NULL) && (result != NULL), return); + +- output = result->action_stdout; +- rc = pcmk_rc2legacy(stonith__result2rc(result)); +- + reply = construct_async_reply(cmd, result); + + // Only replies for certain actions are broadcast +@@ -2412,7 +2415,7 @@ send_async_reply(async_command_t *cmd, const pcmk__action_result_t *result, + bcast = TRUE; + } + +- log_async_result(cmd, rc, pid, NULL, output, merged); ++ log_async_result(cmd, result, pid, NULL, merged); + crm_log_xml_trace(reply, "Reply"); + + if (merged) { +@@ -2436,6 +2439,7 @@ send_async_reply(async_command_t *cmd, const pcmk__action_result_t *result, + if (stand_alone) { + /* Do notification with a clean data object */ + xmlNode *notify_data = create_xml_node(NULL, T_STONITH_NOTIFY_FENCE); ++ int rc = pcmk_rc2legacy(stonith__result2rc(result)); + + crm_xml_add_int(notify_data, F_STONITH_RC, rc); + crm_xml_add(notify_data, F_STONITH_TARGET, cmd->victim); +@@ -2521,8 +2525,7 @@ st_child_done(int pid, const pcmk__action_result_t *result, void *user_data) + + /* this operation requires more fencing, hooray! */ + if (next_device) { +- log_async_result(cmd, pcmk_rc2legacy(stonith__result2rc(result)), pid, +- next_device->id, result->action_stdout, FALSE); ++ log_async_result(cmd, result, pid, next_device->id, false); + schedule_stonith_command(cmd, next_device); + /* Prevent cmd from being freed */ + cmd = NULL; +-- +2.27.0 + + +From 9f9dea518da50f629589d505ea0f330a47111d76 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 28 Oct 2021 13:29:31 -0500 +Subject: [PATCH 10/12] Test: cts-fencing: update expected log messages + +... which now log the original exit status rather than a mapped legacy rc +--- + cts/cts-fencing.in | 28 ++++++++++++++-------------- + 1 file changed, 14 insertions(+), 14 deletions(-) + +diff --git a/cts/cts-fencing.in b/cts/cts-fencing.in +index babfb6351..5cd9f7b8f 100644 +--- a/cts/cts-fencing.in ++++ b/cts/cts-fencing.in +@@ -886,7 +886,7 @@ class Tests(object): + test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 20") + + test.add_stonith_log_pattern("Total timeout set to 40") +- test.add_stonith_log_pattern("targeting node3 using false returned -201") ++ test.add_stonith_log_pattern("targeting node3 using false returned 1") + test.add_stonith_log_pattern("targeting node3 using true returned 0") + + # test what happens when the first fencing level fails. +@@ -920,8 +920,8 @@ class Tests(object): + test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 3") + + test.add_stonith_log_pattern("Total timeout set to 18") +- test.add_stonith_log_pattern("targeting node3 using false1 returned -201") +- test.add_stonith_log_pattern("targeting node3 using false2 returned -201") ++ test.add_stonith_log_pattern("targeting node3 using false1 returned 1") ++ test.add_stonith_log_pattern("targeting node3 using false2 returned 1") + test.add_stonith_log_pattern("targeting node3 using true3 returned 0") + test.add_stonith_log_pattern("targeting node3 using true4 returned 0") + +@@ -987,7 +987,7 @@ class Tests(object): + test.add_cmd("stonith_admin", "--output-as=xml -F node3 -t 20") + + test.add_stonith_log_pattern("Total timeout set to 8") +- test.add_stonith_log_pattern("targeting node3 using false1 returned -201") ++ test.add_stonith_log_pattern("targeting node3 using false1 returned 1") + test.add_stonith_neg_log_pattern("targeting node3 using false2 returned ") + test.add_stonith_log_pattern("targeting node3 using true3 returned 0") + test.add_stonith_log_pattern("targeting node3 using true4 returned 0") +@@ -1147,7 +1147,7 @@ class Tests(object): + "--output-as=xml -R true1 -a fence_dummy_no_reboot -o \"mode=pass\" -o \"pcmk_host_list=node1 node2 node3\"") + test.add_cmd("stonith_admin", "--output-as=xml -B node1 -t 5 -V") + test.add_stonith_log_pattern("does not support reboot") +- test.add_stonith_log_pattern("using true1 returned 0 (OK)") ++ test.add_stonith_log_pattern("using true1 returned 0") + + # make sure reboot is used when reboot action is advertised + for test_type in test_types: +@@ -1158,7 +1158,7 @@ class Tests(object): + "--output-as=xml -R true1 -a fence_dummy -o \"mode=pass\" -o \"pcmk_host_list=node1 node2 node3\"") + test.add_cmd("stonith_admin", "--output-as=xml -B node1 -t 5 -V") + test.add_stonith_neg_log_pattern("does not advertise support for 'reboot', performing 'off'") +- test.add_stonith_log_pattern("using true1 returned 0 (OK)") ++ test.add_stonith_log_pattern("using true1 returned 0") + + # make sure requested fencing delay is applied only for the first device in the first level + # make sure static delay from pcmk_delay_base is added +@@ -1240,8 +1240,8 @@ class Tests(object): + '--output-as=xml -R true2 -a fence_dummy_auto_unfence -o "mode=pass" -o "pcmk_host_list=%s"' % (our_uname)) + test.add_cmd("stonith_admin", "--output-as=xml -U %s -t 3" % (our_uname)) + # both devices should be executed +- test.add_stonith_log_pattern("using true1 returned 0 (OK)") +- test.add_stonith_log_pattern("using true2 returned 0 (OK)") ++ test.add_stonith_log_pattern("using true1 returned 0") ++ test.add_stonith_log_pattern("using true2 returned 0") + + ### verify unfencing using automatic unfencing fails if any of the required agents fail + test = self.new_test("cpg_unfence_required_2", +@@ -1264,8 +1264,8 @@ class Tests(object): + test.add_cmd("stonith_admin", "--output-as=xml -r %s -i 1 -v true1" % (our_uname)) + test.add_cmd("stonith_admin", "--output-as=xml -r %s -i 2 -v true2" % (our_uname)) + test.add_cmd("stonith_admin", "--output-as=xml -U %s -t 3" % (our_uname)) +- test.add_stonith_log_pattern("using true1 returned 0 (OK)") +- test.add_stonith_log_pattern("using true2 returned 0 (OK)") ++ test.add_stonith_log_pattern("using true1 returned 0") ++ test.add_stonith_log_pattern("using true2 returned 0") + + ### verify unfencing using automatic devices with topology + test = self.new_test("cpg_unfence_required_4", +@@ -1296,10 +1296,10 @@ class Tests(object): + test.add_cmd("stonith_admin", "--output-as=xml -r %s -i 3 -v false4" % (our_uname)) + test.add_cmd("stonith_admin", "--output-as=xml -r %s -i 4 -v true4" % (our_uname)) + test.add_cmd("stonith_admin", "--output-as=xml -U %s -t 3" % (our_uname)) +- test.add_stonith_log_pattern("using true1 returned 0 (OK)") +- test.add_stonith_log_pattern("using true2 returned 0 (OK)") +- test.add_stonith_log_pattern("using true3 returned 0 (OK)") +- test.add_stonith_log_pattern("using true4 returned 0 (OK)") ++ test.add_stonith_log_pattern("using true1 returned 0") ++ test.add_stonith_log_pattern("using true2 returned 0") ++ test.add_stonith_log_pattern("using true3 returned 0") ++ test.add_stonith_log_pattern("using true4 returned 0") + + def build_unfence_on_target_tests(self): + """ Register tests that verify unfencing that runs on the target """ +-- +2.27.0 + + +From be72166ed9ccb53c218529783660503df95da719 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 16 Sep 2021 16:50:23 -0500 +Subject: [PATCH 11/12] Log: libcrmservice: downgrade failed action messages + +Previously, we would often get duplicate log messages for failed actions, +from the service library and again from its callers. + +Now that the service library tracks and provides exit reasons, callers can log +sufficient detail with better context, so downgrade the library's messages to +info level or lower. Similarly, avoid duplicate logs of process output. + +Certain messages (such as out-of-memory) remain at higher severity. +--- + daemons/controld/controld_execd.c | 15 +++--- + lib/fencing/st_client.c | 11 ++--- + lib/services/services.c | 14 +++--- + lib/services/services_linux.c | 80 ++++++++++++++++--------------- + lib/services/systemd.c | 20 ++++---- + lib/services/upstart.c | 19 ++++---- + 6 files changed, 80 insertions(+), 79 deletions(-) + +diff --git a/daemons/controld/controld_execd.c b/daemons/controld/controld_execd.c +index bded6e6b6..3ddff6e13 100644 +--- a/daemons/controld/controld_execd.c ++++ b/daemons/controld/controld_execd.c +@@ -2684,16 +2684,15 @@ log_executor_event(lrmd_event_data_t *op, const char *op_key, + do_crm_log(log_level, "%s", str->str); + g_string_free(str, TRUE); + +- if (op->output != NULL) { +- char *prefix = crm_strdup_printf("%s-" PCMK__OP_FMT ":%d", node_name, ++ /* The services library has already logged the output at info or debug ++ * level, so just raise to notice if it looks like a failure. ++ */ ++ if ((op->output != NULL) && (op->rc != PCMK_OCF_OK)) { ++ char *prefix = crm_strdup_printf(PCMK__OP_FMT "@%s output", + op->rsc_id, op->op_type, +- op->interval_ms, op->call_id); ++ op->interval_ms, node_name); + +- if (op->rc) { +- crm_log_output(LOG_NOTICE, prefix, op->output); +- } else { +- crm_log_output(LOG_DEBUG, prefix, op->output); +- } ++ crm_log_output(LOG_NOTICE, prefix, op->output); + free(prefix); + } + } +diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c +index 3d4127eff..2fbff7f24 100644 +--- a/lib/fencing/st_client.c ++++ b/lib/fencing/st_client.c +@@ -276,14 +276,9 @@ stonith__watchdog_fencing_enabled_for_node(const char *node) + static void + log_action(stonith_action_t *action, pid_t pid) + { +- if (action->result.action_stdout != NULL) { +- /* Logging the whole string confuses syslog when the string is xml */ +- char *prefix = crm_strdup_printf("%s[%d] stdout:", action->agent, pid); +- +- crm_log_output(LOG_TRACE, prefix, action->result.action_stdout); +- free(prefix); +- } +- ++ /* 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); +diff --git a/lib/services/services.c b/lib/services/services.c +index 86a0a213c..cf8bbc70e 100644 +--- a/lib/services/services.c ++++ b/lib/services/services.c +@@ -319,13 +319,13 @@ services__create_resource_action(const char *name, const char *standard, + rc = services__nagios_prepare(op); + #endif + } else { +- crm_err("Unknown resource standard: %s", op->standard); ++ crm_info("Unknown resource standard: %s", op->standard); + rc = ENOENT; + } + + if (rc != pcmk_rc_ok) { +- crm_err("Cannot prepare %s operation for %s: %s", +- action, name, strerror(rc)); ++ crm_info("Cannot prepare %s operation for %s: %s", ++ action, name, strerror(rc)); + services__handle_exec_error(op, rc); + } + return op; +@@ -967,14 +967,14 @@ execute_metadata_action(svc_action_t *op) + const char *class = op->standard; + + if (op->agent == NULL) { +- crm_err("meta-data requested without specifying agent"); ++ crm_info("Meta-data requested without specifying agent"); + services__set_result(op, services__generic_error(op), + PCMK_EXEC_ERROR_FATAL, "Agent not specified"); + return EINVAL; + } + + if (class == NULL) { +- crm_err("meta-data requested for agent %s without specifying class", ++ crm_info("Meta-data requested for agent %s without specifying class", + op->agent); + services__set_result(op, services__generic_error(op), + PCMK_EXEC_ERROR_FATAL, +@@ -986,8 +986,8 @@ execute_metadata_action(svc_action_t *op) + class = resources_find_service_class(op->agent); + } + if (class == NULL) { +- crm_err("meta-data requested for %s, but could not determine class", +- op->agent); ++ crm_info("Meta-data requested for %s, but could not determine class", ++ op->agent); + services__set_result(op, services__generic_error(op), + PCMK_EXEC_ERROR_HARD, + "Agent standard could not be determined"); +diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c +index b2ff27a0d..9a4c6cf80 100644 +--- a/lib/services/services_linux.c ++++ b/lib/services/services_linux.c +@@ -64,8 +64,8 @@ sigchld_setup(struct sigchld_data_s *data) + + // Block SIGCHLD (saving previous set of blocked signals to restore later) + if (sigprocmask(SIG_BLOCK, &(data->mask), &(data->old_mask)) < 0) { +- crm_err("Wait for child process completion failed: %s " +- CRM_XS " source=sigprocmask", pcmk_strerror(errno)); ++ crm_info("Wait for child process completion failed: %s " ++ CRM_XS " source=sigprocmask", pcmk_strerror(errno)); + return false; + } + return true; +@@ -81,8 +81,8 @@ sigchld_open(struct sigchld_data_s *data) + + fd = signalfd(-1, &(data->mask), SFD_NONBLOCK); + if (fd < 0) { +- crm_err("Wait for child process completion failed: %s " +- CRM_XS " source=signalfd", pcmk_strerror(errno)); ++ crm_info("Wait for child process completion failed: %s " ++ CRM_XS " source=signalfd", pcmk_strerror(errno)); + } + return fd; + } +@@ -108,8 +108,8 @@ sigchld_received(int fd) + } + s = read(fd, &fdsi, sizeof(struct signalfd_siginfo)); + if (s != sizeof(struct signalfd_siginfo)) { +- crm_err("Wait for child process completion failed: %s " +- CRM_XS " source=read", pcmk_strerror(errno)); ++ crm_info("Wait for child process completion failed: %s " ++ CRM_XS " source=read", pcmk_strerror(errno)); + + } else if (fdsi.ssi_signo == SIGCHLD) { + return true; +@@ -149,8 +149,8 @@ sigchld_handler() + if ((last_sigchld_data != NULL) + && (last_sigchld_data->pipe_fd[1] >= 0) + && (write(last_sigchld_data->pipe_fd[1], "", 1) == -1)) { +- crm_err("Wait for child process completion failed: %s " +- CRM_XS " source=write", pcmk_strerror(errno)); ++ crm_info("Wait for child process completion failed: %s " ++ CRM_XS " source=write", pcmk_strerror(errno)); + } + } + +@@ -162,19 +162,19 @@ sigchld_setup(struct sigchld_data_s *data) + data->pipe_fd[0] = data->pipe_fd[1] = -1; + + if (pipe(data->pipe_fd) == -1) { +- crm_err("Wait for child process completion failed: %s " +- CRM_XS " source=pipe", pcmk_strerror(errno)); ++ crm_info("Wait for child process completion failed: %s " ++ CRM_XS " source=pipe", pcmk_strerror(errno)); + return false; + } + + rc = pcmk__set_nonblocking(data->pipe_fd[0]); + if (rc != pcmk_rc_ok) { +- crm_warn("Could not set pipe input non-blocking: %s " CRM_XS " rc=%d", ++ crm_info("Could not set pipe input non-blocking: %s " CRM_XS " rc=%d", + pcmk_rc_str(rc), rc); + } + rc = pcmk__set_nonblocking(data->pipe_fd[1]); + if (rc != pcmk_rc_ok) { +- crm_warn("Could not set pipe output non-blocking: %s " CRM_XS " rc=%d", ++ crm_info("Could not set pipe output non-blocking: %s " CRM_XS " rc=%d", + pcmk_rc_str(rc), rc); + } + +@@ -183,8 +183,8 @@ sigchld_setup(struct sigchld_data_s *data) + data->sa.sa_flags = 0; + sigemptyset(&(data->sa.sa_mask)); + if (sigaction(SIGCHLD, &(data->sa), &(data->old_sa)) < 0) { +- crm_err("Wait for child process completion failed: %s " +- CRM_XS " source=sigaction", pcmk_strerror(errno)); ++ crm_info("Wait for child process completion failed: %s " ++ CRM_XS " source=sigaction", pcmk_strerror(errno)); + } + + // Remember data for use in signal handler +@@ -585,7 +585,11 @@ log_op_output(svc_action_t *op) + { + char *prefix = crm_strdup_printf("%s[%d] error output", op->id, op->pid); + +- crm_log_output(LOG_NOTICE, prefix, op->stderr_data); ++ /* The library caller has better context to know how important the output ++ * is, so log it at info and debug severity here. They can log it again at ++ * higher severity if appropriate. ++ */ ++ crm_log_output(LOG_INFO, prefix, op->stderr_data); + strcpy(prefix + strlen(prefix) - strlen("error output"), "output"); + crm_log_output(LOG_DEBUG, prefix, op->stdout_data); + free(prefix); +@@ -673,7 +677,7 @@ async_action_complete(mainloop_child_t *p, pid_t pid, int core, int signo, + parse_exit_reason_from_stderr(op); + + } else if (mainloop_child_timeout(p)) { +- crm_warn("%s[%d] timed out after %dms", op->id, op->pid, op->timeout); ++ crm_info("%s[%d] timed out after %dms", op->id, op->pid, op->timeout); + services__set_result(op, services__generic_error(op), PCMK_EXEC_TIMEOUT, + "Process did not exit within specified timeout"); + +@@ -686,7 +690,7 @@ async_action_complete(mainloop_child_t *p, pid_t pid, int core, int signo, + services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_CANCELLED, NULL); + + } else { +- crm_warn("%s[%d] terminated with signal %d (%s)", ++ crm_info("%s[%d] terminated with signal %d (%s)", + op->id, op->pid, signo, strsignal(signo)); + services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR, + "Process interrupted by signal"); +@@ -908,12 +912,12 @@ action_launch_child(svc_action_t *op) + sp.sched_priority = 0; + + if (sched_setscheduler(0, SCHED_OTHER, &sp) == -1) { +- crm_warn("Could not reset scheduling policy for %s", op->id); ++ crm_info("Could not reset scheduling policy for %s", op->id); + } + } + #endif + if (setpriority(PRIO_PROCESS, 0, 0) == -1) { +- crm_warn("Could not reset process priority for %s", op->id); ++ crm_info("Could not reset process priority for %s", op->id); + } + + /* Man: The call setpgrp() is equivalent to setpgid(0,0) +@@ -941,7 +945,7 @@ action_launch_child(svc_action_t *op) + } else { + crm_err("Considering %s unconfigured " + "because unable to load CIB secrets: %s", +- op->rsc, pcmk_rc_str(rc)); ++ op->rsc, pcmk_rc_str(rc)); + exit_child(op, services__configuration_error(op, false), + "Unable to load CIB secrets"); + } +@@ -1043,7 +1047,7 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) + + } else if (wait_rc < 0) { + wait_reason = pcmk_rc_str(errno); +- crm_warn("Wait for completion of %s[%d] failed: %s " ++ crm_info("Wait for completion of %s[%d] failed: %s " + CRM_XS " source=waitpid", + op->id, op->pid, wait_reason); + wait_rc = 0; // Act as if process is still running +@@ -1057,8 +1061,8 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) + + } else if ((poll_rc < 0) && (errno != EINTR)) { + wait_reason = pcmk_rc_str(errno); +- crm_err("Wait for completion of %s[%d] failed: %s " +- CRM_XS " source=poll", op->id, op->pid, wait_reason); ++ crm_info("Wait for completion of %s[%d] failed: %s " ++ CRM_XS " source=poll", op->id, op->pid, wait_reason); + break; + } + +@@ -1078,7 +1082,7 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) + services__set_result(op, services__generic_error(op), + PCMK_EXEC_TIMEOUT, + "Process did not exit within specified timeout"); +- crm_warn("%s[%d] timed out after %dms", ++ crm_info("%s[%d] timed out after %dms", + op->id, op->pid, op->timeout); + + } else { +@@ -1110,8 +1114,8 @@ wait_for_sync_result(svc_action_t *op, struct sigchld_data_s *data) + + services__set_result(op, services__generic_error(op), PCMK_EXEC_ERROR, + "Process interrupted by signal"); +- crm_err("%s[%d] terminated with signal %d (%s)", +- op->id, op->pid, signo, strsignal(signo)); ++ crm_info("%s[%d] terminated with signal %d (%s)", ++ op->id, op->pid, signo, strsignal(signo)); + + #ifdef WCOREDUMP + if (WCOREDUMP(status)) { +@@ -1155,7 +1159,7 @@ services__execute_file(svc_action_t *op) + // Catch common failure conditions early + if (stat(op->opaque->exec, &st) != 0) { + rc = errno; +- crm_warn("Cannot execute '%s': %s " CRM_XS " stat rc=%d", ++ crm_info("Cannot execute '%s': %s " CRM_XS " stat rc=%d", + op->opaque->exec, pcmk_strerror(rc), rc); + services__handle_exec_error(op, rc); + goto done; +@@ -1163,8 +1167,8 @@ services__execute_file(svc_action_t *op) + + if (pipe(stdout_fd) < 0) { + rc = errno; +- crm_err("Cannot execute '%s': %s " CRM_XS " pipe(stdout) rc=%d", +- op->opaque->exec, pcmk_strerror(rc), rc); ++ crm_info("Cannot execute '%s': %s " CRM_XS " pipe(stdout) rc=%d", ++ op->opaque->exec, pcmk_strerror(rc), rc); + services__handle_exec_error(op, rc); + goto done; + } +@@ -1174,8 +1178,8 @@ services__execute_file(svc_action_t *op) + + close_pipe(stdout_fd); + +- crm_err("Cannot execute '%s': %s " CRM_XS " pipe(stderr) rc=%d", +- op->opaque->exec, pcmk_strerror(rc), rc); ++ crm_info("Cannot execute '%s': %s " CRM_XS " pipe(stderr) rc=%d", ++ op->opaque->exec, pcmk_strerror(rc), rc); + services__handle_exec_error(op, rc); + goto done; + } +@@ -1187,8 +1191,8 @@ services__execute_file(svc_action_t *op) + close_pipe(stdout_fd); + close_pipe(stderr_fd); + +- crm_err("Cannot execute '%s': %s " CRM_XS " pipe(stdin) rc=%d", +- op->opaque->exec, pcmk_strerror(rc), rc); ++ crm_info("Cannot execute '%s': %s " CRM_XS " pipe(stdin) rc=%d", ++ op->opaque->exec, pcmk_strerror(rc), rc); + services__handle_exec_error(op, rc); + goto done; + } +@@ -1212,8 +1216,8 @@ services__execute_file(svc_action_t *op) + close_pipe(stdout_fd); + close_pipe(stderr_fd); + +- crm_err("Cannot execute '%s': %s " CRM_XS " fork rc=%d", +- op->opaque->exec, pcmk_strerror(rc), rc); ++ crm_info("Cannot execute '%s': %s " CRM_XS " fork rc=%d", ++ op->opaque->exec, pcmk_strerror(rc), rc); + services__handle_exec_error(op, rc); + if (op->synchronous) { + sigchld_cleanup(&data); +@@ -1271,7 +1275,7 @@ services__execute_file(svc_action_t *op) + op->opaque->stdout_fd = stdout_fd[0]; + rc = pcmk__set_nonblocking(op->opaque->stdout_fd); + if (rc != pcmk_rc_ok) { +- crm_warn("Could not set '%s' output non-blocking: %s " ++ crm_info("Could not set '%s' output non-blocking: %s " + CRM_XS " rc=%d", + op->opaque->exec, pcmk_rc_str(rc), rc); + } +@@ -1279,7 +1283,7 @@ services__execute_file(svc_action_t *op) + op->opaque->stderr_fd = stderr_fd[0]; + rc = pcmk__set_nonblocking(op->opaque->stderr_fd); + if (rc != pcmk_rc_ok) { +- crm_warn("Could not set '%s' error output non-blocking: %s " ++ crm_info("Could not set '%s' error output non-blocking: %s " + CRM_XS " rc=%d", + op->opaque->exec, pcmk_rc_str(rc), rc); + } +@@ -1290,7 +1294,7 @@ services__execute_file(svc_action_t *op) + // as long as no other standard uses stdin_fd assume stonith + rc = pcmk__set_nonblocking(op->opaque->stdin_fd); + if (rc != pcmk_rc_ok) { +- crm_warn("Could not set '%s' input non-blocking: %s " ++ crm_info("Could not set '%s' input non-blocking: %s " + CRM_XS " fd=%d,rc=%d", op->opaque->exec, + pcmk_rc_str(rc), op->opaque->stdin_fd, rc); + } +diff --git a/lib/services/systemd.c b/lib/services/systemd.c +index 6f5bef960..8e9fff484 100644 +--- a/lib/services/systemd.c ++++ b/lib/services/systemd.c +@@ -232,7 +232,8 @@ systemd_daemon_reload_complete(DBusPendingCall *pending, void *user_data) + } + + if (pcmk_dbus_find_error(pending, reply, &error)) { +- crm_err("Could not issue systemd reload %d: %s", reload_count, error.message); ++ crm_warn("Could not issue systemd reload %d: %s", ++ reload_count, error.message); + dbus_error_free(&error); + + } else { +@@ -291,8 +292,8 @@ set_result_from_method_error(svc_action_t *op, const DBusError *error) + PCMK_EXEC_NOT_INSTALLED, "systemd unit not found"); + } + +- crm_err("DBus request for %s of systemd unit %s for resource %s failed: %s", +- op->action, op->agent, crm_str(op->rsc), error->message); ++ crm_info("DBus request for %s of systemd unit %s for resource %s failed: %s", ++ op->action, op->agent, crm_str(op->rsc), error->message); + } + + /*! +@@ -325,11 +326,11 @@ execute_after_loadunit(DBusMessage *reply, svc_action_t *op) + if (op != NULL) { + services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR, + "systemd DBus method had unexpected reply"); +- crm_err("Could not load systemd unit %s for %s: " +- "DBus reply has unexpected type", op->agent, op->id); ++ crm_info("Could not load systemd unit %s for %s: " ++ "DBus reply has unexpected type", op->agent, op->id); + } else { +- crm_err("Could not load systemd unit: " +- "DBus reply has unexpected type"); ++ crm_info("Could not load systemd unit: " ++ "DBus reply has unexpected type"); + } + + } else { +@@ -688,7 +689,7 @@ process_unit_method_reply(DBusMessage *reply, svc_action_t *op) + + } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, + __func__, __LINE__)) { +- crm_warn("DBus request for %s of %s succeeded but " ++ crm_info("DBus request for %s of %s succeeded but " + "return type was unexpected", op->action, crm_str(op->rsc)); + services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, + "systemd DBus method had unexpected reply"); +@@ -981,7 +982,8 @@ systemd_timeout_callback(gpointer p) + svc_action_t * op = p; + + op->opaque->timerid = 0; +- crm_warn("%s operation on systemd unit %s named '%s' timed out", op->action, op->agent, op->rsc); ++ crm_info("%s action for systemd unit %s named '%s' timed out", ++ op->action, op->agent, op->rsc); + services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_TIMEOUT, + "Systemd action did not complete within specified timeout"); + services__finalize_async_op(op); +diff --git a/lib/services/upstart.c b/lib/services/upstart.c +index 2fdc229ad..2ece803e1 100644 +--- a/lib/services/upstart.c ++++ b/lib/services/upstart.c +@@ -308,21 +308,21 @@ get_first_instance(const gchar * job, int timeout) + dbus_message_unref(msg); + + if (dbus_error_is_set(&error)) { +- crm_err("Call to %s failed: %s", method, error.message); ++ crm_info("Call to %s failed: %s", method, error.message); + dbus_error_free(&error); + goto done; + + } else if(reply == NULL) { +- crm_err("Call to %s failed: no reply", method); ++ crm_info("Call to %s failed: no reply", method); + goto done; + + } else if (!dbus_message_iter_init(reply, &args)) { +- crm_err("Call to %s failed: Message has no arguments", method); ++ crm_info("Call to %s failed: Message has no arguments", method); + goto done; + } + + if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) { +- crm_err("Call to %s failed: Message has invalid arguments", method); ++ crm_info("Call to %s failed: Message has invalid arguments", method); + goto done; + } + +@@ -432,8 +432,8 @@ set_result_from_method_error(svc_action_t *op, const DBusError *error) + return; + } + +- crm_err("DBus request for %s of Upstart job %s for resource %s failed: %s", +- op->action, op->agent, crm_str(op->rsc), error->message); ++ crm_info("DBus request for %s of Upstart job %s for resource %s failed: %s", ++ op->action, op->agent, crm_str(op->rsc), error->message); + } + + /*! +@@ -468,7 +468,7 @@ job_method_complete(DBusPendingCall *pending, void *user_data) + + } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, + __func__, __LINE__)) { +- crm_warn("DBus request for %s of %s succeeded but " ++ crm_info("DBus request for %s of %s succeeded but " + "return type was unexpected", op->action, crm_str(op->rsc)); + services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL); + +@@ -667,7 +667,8 @@ services__execute_upstart(svc_action_t *op) + + } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, + __func__, __LINE__)) { +- crm_warn("Call to %s passed but return type was unexpected", op->action); ++ crm_info("Call to %s passed but return type was unexpected", ++ op->action); + services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL); + + } else { +@@ -675,7 +676,7 @@ services__execute_upstart(svc_action_t *op) + + dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); +- crm_info("Call to %s passed: %s", op->action, path); ++ crm_debug("Call to %s passed: %s", op->action, path); + services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL); + } + +-- +2.27.0 + + +From 39f6861c72eb9dd76d2cf3da287fe7485615631b Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 8 Nov 2021 09:43:38 -0600 +Subject: [PATCH 12/12] Low: fencing: avoid use-after-free with new result + object + +itnroduced by 153c9b552 (not released) +--- + lib/fencing/st_rhcs.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/lib/fencing/st_rhcs.c b/lib/fencing/st_rhcs.c +index 23e694975..6c8cbedc7 100644 +--- a/lib/fencing/st_rhcs.c ++++ b/lib/fencing/st_rhcs.c +@@ -143,15 +143,17 @@ stonith__rhcs_get_metadata(const char *agent, int timeout, xmlNode **metadata) + if (result->execution_status != PCMK_EXEC_DONE) { + crm_warn("Could not execute metadata action for %s: %s", + agent, pcmk_exec_status_str(result->execution_status)); ++ rc = pcmk_rc2legacy(stonith__result2rc(result)); + stonith__destroy_action(action); +- return pcmk_rc2legacy(stonith__result2rc(result)); ++ return rc; + } + + if (result->exit_status != CRM_EX_OK) { + crm_warn("Metadata action for %s returned error code %d", + agent, result->exit_status); ++ rc = pcmk_rc2legacy(stonith__result2rc(result)); + stonith__destroy_action(action); +- return pcmk_rc2legacy(stonith__result2rc(result)); ++ return rc; + } + + if (result->action_stdout == NULL) { +-- +2.27.0 + diff --git a/SOURCES/003-fencing-reasons.patch b/SOURCES/003-fencing-reasons.patch new file mode 100644 index 0000000..666a12a --- /dev/null +++ b/SOURCES/003-fencing-reasons.patch @@ -0,0 +1,2476 @@ +From 8e6362cb2129bd56f817d449a195f3da87a545fa Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +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 +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 +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 +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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 +-#include ++ + #include + #include + #include + #include + #include +-#include + #include +- +-#include + #include +-#include +- + #include + + #include + #include + #include + #include +-#include +-#include +-#include + + #include + +@@ -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 +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 + + #include ++#include + #include + #include + #include +@@ -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 +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 +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 +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 +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 +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 +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 +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 +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 + diff --git a/SOURCES/003-pacemakerd-output.patch b/SOURCES/003-pacemakerd-output.patch deleted file mode 100644 index 167e22b..0000000 --- a/SOURCES/003-pacemakerd-output.patch +++ /dev/null @@ -1,343 +0,0 @@ -From 7c35387a9896cb968cf4087b5cbed94af44e1ea5 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 14 May 2021 12:03:46 -0400 -Subject: [PATCH 1/5] Feature: daemons: Convert pacemakerd to formatted output. - -The main purpose of this is to finish getting pacemakerd moved off the -existing command line handling code (pcmk__cli_help in particular) so -that code can eventually be deprecated or removed. pacemakerd itself -does fairly little printing. ---- - daemons/pacemakerd/pacemakerd.c | 58 ++++++++++++++++++++++++++++++----------- - 1 file changed, 43 insertions(+), 15 deletions(-) - -diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c -index ce194bf..bd59729 100644 ---- a/daemons/pacemakerd/pacemakerd.c -+++ b/daemons/pacemakerd/pacemakerd.c -@@ -25,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -37,6 +38,14 @@ struct { - gboolean standby; - } options; - -+static pcmk__output_t *out = NULL; -+ -+static pcmk__supported_format_t formats[] = { -+ PCMK__SUPPORTED_FORMAT_NONE, -+ PCMK__SUPPORTED_FORMAT_TEXT, -+ { NULL, NULL, NULL } -+}; -+ - static gboolean - pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) { - return TRUE; -@@ -1167,10 +1176,10 @@ pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api, - } - - static GOptionContext * --build_arg_context(pcmk__common_args_t *args) { -+build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { - GOptionContext *context = NULL; - -- context = pcmk__build_arg_context(args, NULL, NULL, NULL); -+ context = pcmk__build_arg_context(args, "text", group, NULL); - pcmk__add_main_args(context, entries); - return context; - } -@@ -1182,9 +1191,11 @@ main(int argc, char **argv) - - GError *error = NULL; - -+ int rc = pcmk_rc_ok; -+ GOptionGroup *output_group = NULL; - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); - gchar **processed_args = pcmk__cmdline_preproc(argv, "p"); -- GOptionContext *context = build_arg_context(args); -+ GOptionContext *context = build_arg_context(args, &output_group); - - bool old_instance_connected = false; - -@@ -1195,23 +1205,30 @@ main(int argc, char **argv) - mainloop_add_signal(SIGHUP, pcmk_ignore); - mainloop_add_signal(SIGQUIT, pcmk_sigquit); - -+ pcmk__register_formats(output_group, formats); - if (!g_option_context_parse_strv(context, &processed_args, &error)) { - exit_code = CRM_EX_USAGE; - goto done; - } - -+ rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv); -+ if (rc != pcmk_rc_ok) { -+ exit_code = CRM_EX_ERROR; -+ g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s", -+ args->output_ty, pcmk_rc_str(rc)); -+ goto done; -+ } -+ - if (options.features) { -- printf("Pacemaker %s (Build: %s)\n Supporting v%s: %s\n", PACEMAKER_VERSION, BUILD_VERSION, -- CRM_FEATURE_SET, CRM_FEATURES); -+ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION, -+ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES); - exit_code = CRM_EX_OK; - goto done; - } - - if (args->version) { -- g_strfreev(processed_args); -- pcmk__free_arg_context(context); -- /* FIXME: When pacemakerd is converted to use formatted output, this can go. */ -- pcmk__cli_help('v', CRM_EX_USAGE); -+ out->version(out, false); -+ goto done; - } - - setenv("LC_ALL", "C", 1); -@@ -1248,6 +1265,13 @@ main(int argc, char **argv) - crm_ipc_close(old_instance); - crm_ipc_destroy(old_instance); - -+ /* Don't allow any accidental output after this point. */ -+ if (out != NULL) { -+ out->finish(out, exit_code, true, NULL); -+ pcmk__output_free(out); -+ out = NULL; -+ } -+ - #ifdef SUPPORT_COROSYNC - if (mcp_read_config() == FALSE) { - exit_code = CRM_EX_UNAVAILABLE; -@@ -1333,6 +1357,11 @@ done: - g_strfreev(processed_args); - pcmk__free_arg_context(context); - -- pcmk__output_and_clear_error(error, NULL); -+ pcmk__output_and_clear_error(error, out); -+ -+ if (out != NULL) { -+ out->finish(out, exit_code, true, NULL); -+ pcmk__output_free(out); -+ } - crm_exit(exit_code); - } --- -1.8.3.1 - - -From 35e6da64381fcb092d81ce16835cc28670b077cb Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 17 May 2021 10:04:04 -0400 -Subject: [PATCH 2/5] Features: daemons: Output the pacemakerd feature list in - XML. - ---- - daemons/pacemakerd/pacemakerd.c | 45 ++++++++++++++++++++++++++++++++++++++--- - 1 file changed, 42 insertions(+), 3 deletions(-) - -diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c -index bd59729..93cf743 100644 ---- a/daemons/pacemakerd/pacemakerd.c -+++ b/daemons/pacemakerd/pacemakerd.c -@@ -43,6 +43,42 @@ static pcmk__output_t *out = NULL; - static pcmk__supported_format_t formats[] = { - PCMK__SUPPORTED_FORMAT_NONE, - PCMK__SUPPORTED_FORMAT_TEXT, -+ PCMK__SUPPORTED_FORMAT_XML, -+ { NULL, NULL, NULL } -+}; -+ -+static int -+pacemakerd_features(pcmk__output_t *out, va_list args) { -+ out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION, -+ BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES); -+ return pcmk_rc_ok; -+} -+ -+static int -+pacemakerd_features_xml(pcmk__output_t *out, va_list args) { -+ gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0); -+ -+ pcmk__output_xml_create_parent(out, "pacemakerd", -+ "version", PACEMAKER_VERSION, -+ "build", BUILD_VERSION, -+ "feature_set", CRM_FEATURE_SET, -+ NULL); -+ out->begin_list(out, NULL, NULL, "features"); -+ -+ for (char **s = feature_list; *s != NULL; s++) { -+ pcmk__output_create_xml_text_node(out, "feature", *s); -+ } -+ -+ out->end_list(out); -+ -+ g_strfreev(feature_list); -+ return pcmk_rc_ok; -+} -+ -+static pcmk__message_entry_t fmt_functions[] = { -+ { "features", "default", pacemakerd_features }, -+ { "features", "xml", pacemakerd_features_xml }, -+ - { NULL, NULL, NULL } - }; - -@@ -200,7 +236,7 @@ static GOptionContext * - build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) { - GOptionContext *context = NULL; - -- context = pcmk__build_arg_context(args, "text", group, NULL); -+ context = pcmk__build_arg_context(args, "text (default), xml", group, NULL); - pcmk__add_main_args(context, entries); - return context; - } -@@ -241,9 +277,12 @@ main(int argc, char **argv) - goto done; - } - -+ pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname()); -+ -+ pcmk__register_messages(out, fmt_functions); -+ - if (options.features) { -- out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION, -- BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES); -+ out->message(out, "features"); - exit_code = CRM_EX_OK; - goto done; - } --- -1.8.3.1 - - -From 5b7f5eb35b025b59805cf3c7c3dcb6a3cf4b71b3 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 17 May 2021 11:09:53 -0400 -Subject: [PATCH 3/5] Low: daemons: Conditionally enable logging in pacemakerd. - -If we're doing an interactive command-line call, use -pcmk__cli_init_logging. At the moment, all command line calls except -for --shutdown do their work before logging would even come up, so we -really only need to do this for --shutdown. - -If we're doing a daemon call, use crm_log_init. ---- - daemons/pacemakerd/pacemakerd.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/daemons/pacemakerd/pacemakerd.c b/daemons/pacemakerd/pacemakerd.c -index 93cf743..c20bde7 100644 ---- a/daemons/pacemakerd/pacemakerd.c -+++ b/daemons/pacemakerd/pacemakerd.c -@@ -296,8 +296,11 @@ main(int argc, char **argv) - - pcmk__set_env_option("mcp", "true"); - -- pcmk__cli_init_logging("pacemakerd", args->verbosity); -- crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); -+ if (options.shutdown) { -+ pcmk__cli_init_logging("pacemakerd", args->verbosity); -+ } else { -+ crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE); -+ } - - crm_debug("Checking for existing Pacemaker instance"); - old_instance = crm_ipc_new(CRM_SYSTEM_MCP, 0); --- -1.8.3.1 - - -From 2393362bb7489e86d937ed46a1c5cfb93d9bf3ab Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 17 May 2021 11:58:06 -0400 -Subject: [PATCH 4/5] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd - args. - ---- - include/crm/crm.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/crm/crm.h b/include/crm/crm.h -index fdfc825..92a98fa 100644 ---- a/include/crm/crm.h -+++ b/include/crm/crm.h -@@ -66,7 +66,7 @@ extern "C" { - * >=3.0.13: Fail counts include operation name and interval - * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED - */ --# define CRM_FEATURE_SET "3.10.0" -+# define CRM_FEATURE_SET "3.10.1" - - /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and - * recipient of a CPG message. This imposes an arbitrary limit on cluster node --- -1.8.3.1 - - -From 3ad8edbd91631b87ef5f53fa2d68f0c8bbb9ee2b Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 17 May 2021 11:57:09 -0400 -Subject: [PATCH 5/5] Feature: xml: Add schema for pacemakerd. - ---- - xml/Makefile.am | 1 + - xml/api/pacemakerd-2.10.rng | 28 ++++++++++++++++++++++++++++ - 2 files changed, 29 insertions(+) - create mode 100644 xml/api/pacemakerd-2.10.rng - -diff --git a/xml/Makefile.am b/xml/Makefile.am -index 12a51c5..b9448d4 100644 ---- a/xml/Makefile.am -+++ b/xml/Makefile.am -@@ -56,6 +56,7 @@ API_request_base = command-output \ - crm_simulate \ - crmadmin \ - digests \ -+ pacemakerd \ - stonith_admin \ - version - -diff --git a/xml/api/pacemakerd-2.10.rng b/xml/api/pacemakerd-2.10.rng -new file mode 100644 -index 0000000..41a11e7 ---- /dev/null -+++ b/xml/api/pacemakerd-2.10.rng -@@ -0,0 +1,28 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ --- -1.8.3.1 - diff --git a/SOURCES/005-crm_resource.patch b/SOURCES/005-crm_resource.patch deleted file mode 100644 index 1683026..0000000 --- a/SOURCES/005-crm_resource.patch +++ /dev/null @@ -1,866 +0,0 @@ -From a5a507d4e1abf242903472719a19977811e6f164 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Thu, 20 May 2021 11:59:36 -0400 -Subject: [PATCH 01/10] Feature: libcrmcommon: Add OCF_OUTPUT_FORMAT to - crm_resource environment. - -See: rhbz#1644628 ---- - lib/common/output.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/lib/common/output.c b/lib/common/output.c -index 6cb49b5..58872e0 100644 ---- a/lib/common/output.c -+++ b/lib/common/output.c -@@ -71,6 +71,8 @@ pcmk__output_new(pcmk__output_t **out, const char *fmt_name, const char *filenam - return ENOMEM; - } - -+ setenv("OCF_OUTPUT_FORMAT", (*out)->fmt_name, 1); -+ - return pcmk_rc_ok; - } - --- -1.8.3.1 - - -From acc6ecdbfb797d69794e68f75a734d6252434e01 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 21 May 2021 14:20:30 -0400 -Subject: [PATCH 02/10] Feature: schemas: Copy crm_resource schema in - preparation for changes. - -See: rhbz#1644628 ---- - xml/api/crm_resource-2.11.rng | 238 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 238 insertions(+) - create mode 100644 xml/api/crm_resource-2.11.rng - -diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng -new file mode 100644 -index 0000000..8e386db ---- /dev/null -+++ b/xml/api/crm_resource-2.11.rng -@@ -0,0 +1,238 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ promoted -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ ocf -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ true -+ false -+ -+ -+ -+ true -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ Stopped -+ Started -+ Master -+ Slave -+ -+ -+ --- -1.8.3.1 - - -From 1bbdf2149a111e9e19c388834f82001e0d31c427 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 24 May 2021 12:23:55 -0400 -Subject: [PATCH 03/10] Feature: xml: Update the crm_resource schema for XML - output. - -See: rhbz#1644628 ---- - xml/api/crm_resource-2.11.rng | 50 +++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 50 insertions(+) - -diff --git a/xml/api/crm_resource-2.11.rng b/xml/api/crm_resource-2.11.rng -index 8e386db..aaa54d6 100644 ---- a/xml/api/crm_resource-2.11.rng -+++ b/xml/api/crm_resource-2.11.rng -@@ -20,6 +20,7 @@ - - - -+ - - - -@@ -227,6 +228,55 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - Stopped --- -1.8.3.1 - - -From d89f5bc7fec856fdcd32fa14edbd0019507d5d15 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Tue, 1 Jun 2021 15:26:58 -0400 -Subject: [PATCH 04/10] Low: libcrmcommon: Increase PCMK__API_VERSION for new - crm_resource output. - -See: rhbz#1644628 ---- - include/crm/common/output_internal.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h -index 10b315b..0436cde 100644 ---- a/include/crm/common/output_internal.h -+++ b/include/crm/common/output_internal.h -@@ -27,7 +27,7 @@ extern "C" { - # include - # include - --# define PCMK__API_VERSION "2.9" -+# define PCMK__API_VERSION "2.11" - - #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS) - # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS))) --- -1.8.3.1 - - -From 30bd2ddf43ee2a911681e51f40ed9ba20ec250b0 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Thu, 27 May 2021 13:57:12 -0400 -Subject: [PATCH 05/10] Low: tools: Pass NULL to - cli_resource_execute_from_params... - -if no resource name is given. This happens if we are validating based -on the --class/--agent/--provider command line options instead. ---- - tools/crm_resource.c | 2 +- - tools/crm_resource_runtime.c | 8 ++++---- - 2 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/tools/crm_resource.c b/tools/crm_resource.c -index 24f1121..37a0bb0 100644 ---- a/tools/crm_resource.c -+++ b/tools/crm_resource.c -@@ -1840,7 +1840,7 @@ main(int argc, char **argv) - - case cmd_execute_agent: - if (options.cmdline_config) { -- exit_code = cli_resource_execute_from_params(out, "test", -+ exit_code = cli_resource_execute_from_params(out, NULL, - options.v_class, options.v_provider, options.v_agent, - "validate-all", options.cmdline_params, - options.override_params, options.timeout_ms, -diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c -index 48a4b40..ebf48bb 100644 ---- a/tools/crm_resource_runtime.c -+++ b/tools/crm_resource_runtime.c -@@ -1717,14 +1717,14 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - */ - params_copy = pcmk__str_table_dup(params); - -- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0, -- timeout_ms, params_copy, 0); -+ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov, -+ rsc_type, action, 0, timeout_ms, params_copy, 0); - if (op == NULL) { - /* Re-run with stderr enabled so we can display a sane error message */ - crm_enable_stderr(TRUE); - params_copy = pcmk__str_table_dup(params); -- op = resources_action_create(rsc_name, rsc_class, rsc_prov, rsc_type, action, 0, -- timeout_ms, params_copy, 0); -+ op = resources_action_create(rsc_name ? rsc_name : "test", rsc_class, rsc_prov, -+ rsc_type, action, 0, timeout_ms, params_copy, 0); - - /* Callers of cli_resource_execute expect that the params hash table will - * be freed. That function uses this one, so for that reason and for --- -1.8.3.1 - - -From ee56efd53d14cfc4f902769540b72b3bb6096a73 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 24 May 2021 12:08:52 -0400 -Subject: [PATCH 06/10] Feature: tools: Add an agent-status message for - crm_resource. - -This moves what was previously only done in an out->info call to its own -output message, which means it will appear in XML output as well. Also, -note that if --class/--agent/--provider are given, the resource name -will be set to "test". In that case, do not display the resource name -in the output. - -This message will be used for --validate and the --force-* command line -options to crm_resource. - -See: rhbz#1644628 ---- - tools/crm_resource_print.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 53 insertions(+) - -diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c -index 9d82cf8..88d5878 100644 ---- a/tools/crm_resource_print.c -+++ b/tools/crm_resource_print.c -@@ -152,6 +152,57 @@ attribute_list_default(pcmk__output_t *out, va_list args) { - return pcmk_rc_ok; - } - -+PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *", -+ "const char *", "const char *", "int") -+static int -+agent_status_default(pcmk__output_t *out, va_list args) { -+ int status = va_arg(args, int); -+ const char *action = va_arg(args, const char *); -+ const char *name = va_arg(args, const char *); -+ const char *class = va_arg(args, const char *); -+ const char *provider = va_arg(args, const char *); -+ const char *type = va_arg(args, const char *); -+ int rc = va_arg(args, int); -+ -+ if (status == PCMK_LRM_OP_DONE) { -+ out->info(out, "Operation %s%s%s (%s%s%s:%s) returned: '%s' (%d)", -+ action, name ? " for " : "", name ? name : "", -+ class, provider ? ":" : "", provider ? provider : "", type, -+ services_ocf_exitcode_str(rc), rc); -+ } else { -+ out->err(out, "Operation %s%s%s (%s%s%s:%s) failed: '%s' (%d)", -+ action, name ? " for " : "", name ? name : "", -+ class, provider ? ":" : "", provider ? provider : "", type, -+ services_lrm_status_str(status), status); -+ } -+ -+ return pcmk_rc_ok; -+} -+ -+PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *", -+ "const char *", "const char *", "int") -+static int -+agent_status_xml(pcmk__output_t *out, va_list args) { -+ int status G_GNUC_UNUSED = va_arg(args, int); -+ const char *action G_GNUC_UNUSED = va_arg(args, const char *); -+ const char *name G_GNUC_UNUSED = va_arg(args, const char *); -+ const char *class G_GNUC_UNUSED = va_arg(args, const char *); -+ const char *provider G_GNUC_UNUSED = va_arg(args, const char *); -+ const char *type G_GNUC_UNUSED = va_arg(args, const char *); -+ int rc = va_arg(args, int); -+ -+ char *status_str = pcmk__itoa(rc); -+ -+ pcmk__output_create_xml_node(out, "agent-status", -+ "code", status_str, -+ "message", services_ocf_exitcode_str(rc), -+ NULL); -+ -+ free(status_str); -+ -+ return pcmk_rc_ok; -+} -+ - PCMK__OUTPUT_ARGS("attribute-list", "pe_resource_t *", "char *", "GHashTable *") - static int - attribute_list_text(pcmk__output_t *out, va_list args) { -@@ -562,6 +613,8 @@ resource_names(pcmk__output_t *out, va_list args) { - } - - static pcmk__message_entry_t fmt_functions[] = { -+ { "agent-status", "default", agent_status_default }, -+ { "agent-status", "xml", agent_status_xml }, - { "attribute-list", "default", attribute_list_default }, - { "attribute-list", "text", attribute_list_text }, - { "property-list", "default", property_list_default }, --- -1.8.3.1 - - -From 85cb6b6bff96b18c5174d11e4de4d49cbfb20bb7 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Tue, 1 Jun 2021 14:47:30 -0400 -Subject: [PATCH 07/10] Feature: tools: Add an overridden params output - message. - -This also replaces what was previously being done in an out->info call -with an output message. This means it shows up in XML output as well. -Also, note that if --class/--agent/--provider are given, the resource -name will be set to "test". In that case, do not display the resource -name in the output. - -See: rhbz#1644628 ---- - tools/crm_resource_print.c | 39 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 39 insertions(+) - -diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c -index 88d5878..119d83f 100644 ---- a/tools/crm_resource_print.c -+++ b/tools/crm_resource_print.c -@@ -224,6 +224,43 @@ attribute_list_text(pcmk__output_t *out, va_list args) { - return pcmk_rc_ok; - } - -+PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *") -+static int -+override_default(pcmk__output_t *out, va_list args) { -+ const char *rsc_name = va_arg(args, const char *); -+ const char *name = va_arg(args, const char *); -+ const char *value = va_arg(args, const char *); -+ -+ if (rsc_name == NULL) { -+ out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'", -+ name, value); -+ } else { -+ out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'", -+ rsc_name, name, value); -+ } -+ -+ return pcmk_rc_ok; -+} -+ -+PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *") -+static int -+override_xml(pcmk__output_t *out, va_list args) { -+ const char *rsc_name = va_arg(args, const char *); -+ const char *name = va_arg(args, const char *); -+ const char *value = va_arg(args, const char *); -+ -+ xmlNodePtr node = pcmk__output_create_xml_node(out, "override", -+ "name", name, -+ "value", value, -+ NULL); -+ -+ if (rsc_name != NULL) { -+ crm_xml_add(node, "rsc", rsc_name); -+ } -+ -+ return pcmk_rc_ok; -+} -+ - PCMK__OUTPUT_ARGS("property-list", "pe_resource_t *", "char *") - static int - property_list_default(pcmk__output_t *out, va_list args) { -@@ -617,6 +654,8 @@ static pcmk__message_entry_t fmt_functions[] = { - { "agent-status", "xml", agent_status_xml }, - { "attribute-list", "default", attribute_list_default }, - { "attribute-list", "text", attribute_list_text }, -+ { "override", "default", override_default }, -+ { "override", "xml", override_xml }, - { "property-list", "default", property_list_default }, - { "property-list", "text", property_list_text }, - { "resource-check-list", "default", resource_check_list_default }, --- -1.8.3.1 - - -From e5e24592c7c3231c619fb5253e7925ffbc634a99 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 4 Jun 2021 10:24:51 -0400 -Subject: [PATCH 08/10] Low: tools: Use simple XML lists for resource actions - as well. - -See: rhbz#1644628 ---- - tools/crm_resource.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tools/crm_resource.c b/tools/crm_resource.c -index 37a0bb0..e957011 100644 ---- a/tools/crm_resource.c -+++ b/tools/crm_resource.c -@@ -1643,6 +1643,7 @@ main(int argc, char **argv) - * saves from having to write custom messages to build the lists around all these things - */ - switch (options.rsc_cmd) { -+ case cmd_execute_agent: - case cmd_list_resources: - case cmd_query_xml: - case cmd_query_raw_xml: --- -1.8.3.1 - - -From 3e75174d0bc31b261adb1994214a5878b79da85b Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 4 Jun 2021 10:30:10 -0400 -Subject: [PATCH 09/10] Feature: tools: Add an output message for resource - actions. - -This wraps up the override and agent-status messages into a single -message, along with any stdout/stderr from the resource action. This -message should be called after taking the action. - -This also implements handling XML output from resource actions. Check -to see if the validate-all action returns XML. If so, output it as a -CDATA block under a "command" element. If not, treat it as plain text -and output it as stdout/stderr from a command. - -See: rhbz#1644628 ---- - tools/crm_resource_print.c | 122 +++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 122 insertions(+) - -diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c -index 119d83f..19a366d 100644 ---- a/tools/crm_resource_print.c -+++ b/tools/crm_resource_print.c -@@ -293,6 +293,126 @@ property_list_text(pcmk__output_t *out, va_list args) { - return pcmk_rc_ok; - } - -+PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *", -+ "const char *", "const char *", "const char *", "GHashTable *", -+ "int", "int", "char *", "char *") -+static int -+resource_agent_action_default(pcmk__output_t *out, va_list args) { -+ int verbose = va_arg(args, int); -+ -+ const char *class = va_arg(args, const char *); -+ const char *provider = va_arg(args, const char *); -+ const char *type = va_arg(args, const char *); -+ const char *rsc_name = va_arg(args, const char *); -+ const char *action = va_arg(args, const char *); -+ GHashTable *overrides = va_arg(args, GHashTable *); -+ int rc = va_arg(args, int); -+ int status = va_arg(args, int); -+ char *stdout_data = va_arg(args, char *); -+ char *stderr_data = va_arg(args, char *); -+ -+ if (overrides) { -+ GHashTableIter iter; -+ char *name = NULL; -+ char *value = NULL; -+ -+ out->begin_list(out, NULL, NULL, "overrides"); -+ -+ g_hash_table_iter_init(&iter, overrides); -+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { -+ out->message(out, "override", rsc_name, name, value); -+ } -+ -+ out->end_list(out); -+ } -+ -+ out->message(out, "agent-status", status, action, rsc_name, class, provider, -+ type, rc); -+ -+ /* hide output for validate-all if not in verbose */ -+ if (verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei)) { -+ return pcmk_rc_ok; -+ } -+ -+ if (stdout_data || stderr_data) { -+ xmlNodePtr doc = string2xml(stdout_data); -+ -+ if (doc != NULL) { -+ out->output_xml(out, "command", stdout_data); -+ xmlFreeNode(doc); -+ } else { -+ out->subprocess_output(out, rc, stdout_data, stderr_data); -+ } -+ } -+ -+ return pcmk_rc_ok; -+} -+ -+PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *", -+ "const char *", "const char *", "const char *", "GHashTable *", -+ "int", "int", "char *", "char *") -+static int -+resource_agent_action_xml(pcmk__output_t *out, va_list args) { -+ int verbose G_GNUC_UNUSED = va_arg(args, int); -+ -+ const char *class = va_arg(args, const char *); -+ const char *provider = va_arg(args, const char *); -+ const char *type = va_arg(args, const char *); -+ const char *rsc_name = va_arg(args, const char *); -+ const char *action = va_arg(args, const char *); -+ GHashTable *overrides = va_arg(args, GHashTable *); -+ int rc = va_arg(args, int); -+ int status = va_arg(args, int); -+ char *stdout_data = va_arg(args, char *); -+ char *stderr_data = va_arg(args, char *); -+ -+ xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource-agent-action", -+ "action", action, -+ "class", class, -+ "type", type, -+ NULL); -+ -+ if (rsc_name) { -+ crm_xml_add(node, "rsc", rsc_name); -+ } -+ -+ if (provider) { -+ crm_xml_add(node, "provider", provider); -+ } -+ -+ if (overrides) { -+ GHashTableIter iter; -+ char *name = NULL; -+ char *value = NULL; -+ -+ out->begin_list(out, NULL, NULL, "overrides"); -+ -+ g_hash_table_iter_init(&iter, overrides); -+ while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) { -+ out->message(out, "override", rsc_name, name, value); -+ } -+ -+ out->end_list(out); -+ } -+ -+ out->message(out, "agent-status", status, action, rsc_name, class, provider, -+ type, rc); -+ -+ if (stdout_data || stderr_data) { -+ xmlNodePtr doc = string2xml(stdout_data); -+ -+ if (doc != NULL) { -+ out->output_xml(out, "command", stdout_data); -+ xmlFreeNode(doc); -+ } else { -+ out->subprocess_output(out, rc, stdout_data, stderr_data); -+ } -+ } -+ -+ pcmk__output_xml_pop_parent(out); -+ return pcmk_rc_ok; -+} -+ - PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *") - static int - resource_check_list_default(pcmk__output_t *out, va_list args) { -@@ -658,6 +778,8 @@ static pcmk__message_entry_t fmt_functions[] = { - { "override", "xml", override_xml }, - { "property-list", "default", property_list_default }, - { "property-list", "text", property_list_text }, -+ { "resource-agent-action", "default", resource_agent_action_default }, -+ { "resource-agent-action", "xml", resource_agent_action_xml }, - { "resource-check-list", "default", resource_check_list_default }, - { "resource-check-list", "xml", resource_check_list_xml }, - { "resource-search-list", "default", resource_search_list_default }, --- -1.8.3.1 - - -From b50b2418e1e997b42f5370b4672a3f105d74634f Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 4 Jun 2021 10:40:16 -0400 -Subject: [PATCH 10/10] Feature: tools: Use the new resource-agent-action - message. - -See: rhbz#1644628 ---- - tools/crm_resource_runtime.c | 21 +++------------------ - 1 file changed, 3 insertions(+), 18 deletions(-) - -diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c -index ebf48bb..755be9f 100644 ---- a/tools/crm_resource_runtime.c -+++ b/tools/crm_resource_runtime.c -@@ -1765,28 +1765,13 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - if (services_action_sync(op)) { - exit_code = op->rc; - -- if (op->status == PCMK_LRM_OP_DONE) { -- out->info(out, "Operation %s for %s (%s:%s:%s) returned: '%s' (%d)", -- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type, -- services_ocf_exitcode_str(op->rc), op->rc); -- } else { -- out->err(out, "Operation %s for %s (%s:%s:%s) failed: '%s' (%d)", -- action, rsc_name, rsc_class, rsc_prov ? rsc_prov : "", rsc_type, -- services_lrm_status_str(op->status), op->status); -- } -- -- /* hide output for validate-all if not in verbose */ -- if (resource_verbose == 0 && pcmk__str_eq(action, "validate-all", pcmk__str_casei)) -- goto done; -- -- if (op->stdout_data || op->stderr_data) { -- out->subprocess_output(out, op->rc, op->stdout_data, op->stderr_data); -- } -+ out->message(out, "resource-agent-action", resource_verbose, rsc_class, -+ rsc_prov, rsc_type, rsc_name, action, override_hash, op->rc, -+ op->status, op->stdout_data, op->stderr_data); - } else { - exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc; - } - --done: - services_action_free(op); - /* See comment above about why we free params here. */ - g_hash_table_destroy(params); --- -1.8.3.1 - diff --git a/SOURCES/006-crm_simulate.patch b/SOURCES/006-crm_simulate.patch deleted file mode 100644 index c8d4e3f..0000000 --- a/SOURCES/006-crm_simulate.patch +++ /dev/null @@ -1,896 +0,0 @@ -From 97571e6ccc9b7fa339a7e27d9b0b9ab782ff3003 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Wed, 16 Jun 2021 13:54:10 -0400 -Subject: [PATCH 1/5] Low: schemas: Copy crm_mon.rng in preparation for - changes. - ---- - xml/api/crm_mon-2.12.rng | 243 +++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 243 insertions(+) - create mode 100644 xml/api/crm_mon-2.12.rng - -diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng -new file mode 100644 -index 0000000..ffec923 ---- /dev/null -+++ b/xml/api/crm_mon-2.12.rng -@@ -0,0 +1,243 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ granted -+ revoked -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ --- -1.8.3.1 - - -From da394983f106f974274ddd94675a04c85086010e Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 18 Jun 2021 15:06:34 -0400 -Subject: [PATCH 2/5] Refactor: Split node history out into its own XML schema. - -This allows for sharing it between crm_mon and crm_simulate. ---- - xml/Makefile.am | 2 +- - xml/api/crm_mon-2.12.rng | 64 +-------------------------------------- - xml/api/node-history-2.12.rng | 70 +++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 72 insertions(+), 64 deletions(-) - create mode 100644 xml/api/node-history-2.12.rng - -diff --git a/xml/Makefile.am b/xml/Makefile.am -index b9448d4..8e7b6d3 100644 ---- a/xml/Makefile.am -+++ b/xml/Makefile.am -@@ -64,7 +64,7 @@ API_request_base = command-output \ - CIB_cfg_base = options nodes resources constraints fencing acls tags alerts - - # Names of all schemas (including top level and those included by others) --API_base = $(API_request_base) fence-event failure generic-list item node-attrs nodes resources status -+API_base = $(API_request_base) fence-event failure generic-list item node-attrs node-history nodes resources status - CIB_base = cib $(CIB_cfg_base) status score rule nvset - - # Static schema files and transforms (only CIB has transforms) -diff --git a/xml/api/crm_mon-2.12.rng b/xml/api/crm_mon-2.12.rng -index ffec923..be14412 100644 ---- a/xml/api/crm_mon-2.12.rng -+++ b/xml/api/crm_mon-2.12.rng -@@ -20,7 +20,7 @@ - - - -- -+ - - - -@@ -113,14 +113,6 @@ - - - -- -- -- -- -- -- -- -- - - - -@@ -156,60 +148,6 @@ - - - -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - - - -diff --git a/xml/api/node-history-2.12.rng b/xml/api/node-history-2.12.rng -new file mode 100644 -index 0000000..9628000 ---- /dev/null -+++ b/xml/api/node-history-2.12.rng -@@ -0,0 +1,70 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ --- -1.8.3.1 - - -From bf72b2615630eef7876e443d60b34d5a316de847 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Wed, 16 Jun 2021 14:09:31 -0400 -Subject: [PATCH 3/5] Low: schemas: Copy crm_simulate.rng in preparation for - changes. - ---- - xml/api/crm_simulate-2.12.rng | 335 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 335 insertions(+) - create mode 100644 xml/api/crm_simulate-2.12.rng - -diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng -new file mode 100644 -index 0000000..9a7612d ---- /dev/null -+++ b/xml/api/crm_simulate-2.12.rng -@@ -0,0 +1,335 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ --- -1.8.3.1 - - -From c46e07788788acf5669e3f89b9344190a91c7331 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 18 Jun 2021 15:10:19 -0400 -Subject: [PATCH 4/5] Feature: tools: Add the node-summary to crm_simulate - output. - -If --show-failcounts is given to crm_simulate, it should also display -the node-summary message. - -See: rhbz#1686426 ---- - tools/crm_simulate.c | 7 +++++-- - xml/api/crm_simulate-2.12.rng | 3 +++ - 2 files changed, 8 insertions(+), 2 deletions(-) - -diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c -index b4aa9d1..2ea292c 100644 ---- a/tools/crm_simulate.c -+++ b/tools/crm_simulate.c -@@ -409,11 +409,14 @@ print_cluster_status(pe_working_set_t * data_set, unsigned int print_opts) - FALSE, FALSE, all, all, FALSE); - - if (options.show_attrs) { -- out->message(out, "node-attribute-list", data_set, -- 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all); -+ rc = out->message(out, "node-attribute-list", data_set, -+ 0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all); - } - - if (options.show_failcounts) { -+ rc = out->message(out, "node-summary", data_set, all, all, -+ 0, print_opts, FALSE, FALSE, FALSE, FALSE, rc == pcmk_rc_ok); -+ - out->message(out, "failed-action-list", data_set, all, all, - rc == pcmk_rc_ok); - } -diff --git a/xml/api/crm_simulate-2.12.rng b/xml/api/crm_simulate-2.12.rng -index 9a7612d..f90bd36 100644 ---- a/xml/api/crm_simulate-2.12.rng -+++ b/xml/api/crm_simulate-2.12.rng -@@ -67,6 +67,9 @@ - - - -+ -+ -+ - - - --- -1.8.3.1 - - -From bac50336e0264604716e5997b87ee7e65311b982 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Fri, 18 Jun 2021 15:21:52 -0400 -Subject: [PATCH 5/5] Low: libcrmcommon: Increase PCMK__API_VERSION for new - crm_resource output. - -See: rhbz#1686426 ---- - include/crm/common/output_internal.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/crm/common/output_internal.h b/include/crm/common/output_internal.h -index 0436cde..ba9c423 100644 ---- a/include/crm/common/output_internal.h -+++ b/include/crm/common/output_internal.h -@@ -27,7 +27,7 @@ extern "C" { - # include - # include - --# define PCMK__API_VERSION "2.11" -+# define PCMK__API_VERSION "2.12" - - #if defined(PCMK__WITH_ATTRIBUTE_OUTPUT_ARGS) - # define PCMK__OUTPUT_ARGS(ARGS...) __attribute__((output_args(ARGS))) --- -1.8.3.1 - diff --git a/SOURCES/007-unfencing-loop.patch b/SOURCES/007-unfencing-loop.patch deleted file mode 100644 index d4950c8..0000000 --- a/SOURCES/007-unfencing-loop.patch +++ /dev/null @@ -1,733 +0,0 @@ -From 6dcd6b51d7d3993bc483588d6ed75077518ed600 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Fri, 4 Jun 2021 16:30:55 -0500 -Subject: [PATCH 01/11] Low: controller: check whether unfenced node was remote - node - -... so the controller can indicate the node is remote (if known at that point, -which is not guaranteed) when setting unfencing-related node attributes. ---- - daemons/controld/controld_fencing.c | 21 ++++++++++++++++++--- - 1 file changed, 18 insertions(+), 3 deletions(-) - -diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c -index 23dff28..0fba661 100644 ---- a/daemons/controld/controld_fencing.c -+++ b/daemons/controld/controld_fencing.c -@@ -757,15 +757,30 @@ tengine_stonith_callback(stonith_t *stonith, stonith_callback_data_t *data) - if (pcmk__str_eq("on", op, pcmk__str_casei)) { - const char *value = NULL; - char *now = pcmk__ttoa(time(NULL)); -+ gboolean is_remote_node = FALSE; -+ -+ /* This check is not 100% reliable, since this node is not -+ * guaranteed to have the remote node cached. However, it -+ * doesn't have to be reliable, since the attribute manager can -+ * learn a node's "remoteness" by other means sooner or later. -+ * This allows it to learn more quickly if this node does have -+ * the information. -+ */ -+ if (g_hash_table_lookup(crm_remote_peer_cache, uuid) != NULL) { -+ is_remote_node = TRUE; -+ } - -- update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, FALSE); -+ update_attrd(target, CRM_ATTR_UNFENCED, now, NULL, -+ is_remote_node); - free(now); - - value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_ALL); -- update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, FALSE); -+ update_attrd(target, CRM_ATTR_DIGESTS_ALL, value, NULL, -+ is_remote_node); - - value = crm_meta_value(action->params, XML_OP_ATTR_DIGESTS_SECURE); -- update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, FALSE); -+ update_attrd(target, CRM_ATTR_DIGESTS_SECURE, value, NULL, -+ is_remote_node); - - } else if (action->sent_update == FALSE) { - send_stonith_update(action, target, uuid); --- -1.8.3.1 - - -From 3ef6d9403f68ab8559c45cc99f5a8da05ca6420b Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Mon, 7 Jun 2021 10:50:36 -0500 -Subject: [PATCH 02/11] Refactor: pacemaker-attrd: functionize adding remote - node to cache - -... for future reuse ---- - daemons/attrd/attrd_commands.c | 34 +++++++++++++++++++++++----------- - 1 file changed, 23 insertions(+), 11 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index 731c243..93a165b 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -102,6 +102,28 @@ free_attribute(gpointer data) - } - } - -+/*! -+ * \internal -+ * \brief Ensure a Pacemaker Remote node is in the correct peer cache -+ * -+ * \param[in] -+ */ -+static void -+cache_remote_node(const char *node_name) -+{ -+ /* If we previously assumed this node was an unseen cluster node, -+ * remove its entry from the cluster peer cache. -+ */ -+ crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name); -+ -+ if (dup && (dup->uuid == NULL)) { -+ reap_crm_member(0, node_name); -+ } -+ -+ // Ensure node is in the remote peer cache -+ CRM_ASSERT(crm_remote_peer_get(node_name) != NULL); -+} -+ - static xmlNode * - build_attribute_xml( - xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user, -@@ -709,17 +731,7 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml) - - crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote); - if (is_remote) { -- /* If we previously assumed this node was an unseen cluster node, -- * remove its entry from the cluster peer cache. -- */ -- crm_node_t *dup = pcmk__search_cluster_node_cache(0, host); -- -- if (dup && (dup->uuid == NULL)) { -- reap_crm_member(0, host); -- } -- -- /* Ensure this host is in the remote peer cache */ -- CRM_ASSERT(crm_remote_peer_get(host) != NULL); -+ cache_remote_node(host); - } - - if (v == NULL) { --- -1.8.3.1 - - -From 6fac2c71bc2c56870ac828d7cd7b7c799279c47e Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Mon, 7 Jun 2021 10:39:34 -0500 -Subject: [PATCH 03/11] Refactor: pacemaker-attrd: don't try to remove votes - for remote nodes - -Remote nodes never vote. - -This has no effect in practice since the removal would simply do nothing, -but we might as well not waste time trying. ---- - daemons/attrd/attrd_commands.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index 93a165b..dbe777e 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -976,7 +976,8 @@ attrd_election_cb(gpointer user_data) - void - attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data) - { -- bool remove_voter = FALSE; -+ bool gone = false; -+ bool is_remote = pcmk_is_set(peer->flags, crm_remote_node); - - switch (kind) { - case crm_status_uname: -@@ -984,7 +985,7 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da - - case crm_status_processes: - if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) { -- remove_voter = TRUE; -+ gone = true; - } - break; - -@@ -1000,13 +1001,13 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da - } else { - // Remove all attribute values associated with lost nodes - attrd_peer_remove(peer->uname, FALSE, "loss"); -- remove_voter = TRUE; -+ gone = true; - } - break; - } - -- // In case an election is in progress, remove any vote by the node -- if (remove_voter) { -+ // Remove votes from cluster nodes that leave, in case election in progress -+ if (gone && !is_remote) { - attrd_remove_voter(peer); - } - } --- -1.8.3.1 - - -From 54089fc663d6aaf10ca164c6c94b3b17237788de Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Mon, 7 Jun 2021 10:40:06 -0500 -Subject: [PATCH 04/11] Low: pacemaker-attrd: check for remote nodes in peer - update callback - -If a remote node was started before the local cluster node joined the cluster, -the cluster node will assume its node attributes are for a cluster node until -it learns otherwise. Check for remoteness in the peer update callback, to have -another way we can learn it. ---- - daemons/attrd/attrd_commands.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index dbe777e..5f6a754 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -1009,6 +1009,10 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da - // Remove votes from cluster nodes that leave, in case election in progress - if (gone && !is_remote) { - attrd_remove_voter(peer); -+ -+ // Ensure remote nodes that come up are in the remote node cache -+ } else if (!gone && is_remote) { -+ cache_remote_node(peer->uname); - } - } - --- -1.8.3.1 - - -From 8c048df0312d0d9c857d87b570a352429a710928 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Mon, 7 Jun 2021 11:29:12 -0500 -Subject: [PATCH 05/11] Log: pacemaker-attrd: log peer status changes - ---- - daemons/attrd/attrd_commands.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index 5f6a754..d6d179b 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -972,6 +972,7 @@ attrd_election_cb(gpointer user_data) - return FALSE; - } - -+#define state_text(state) ((state)? (const char *)(state) : "in unknown state") - - void - attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data) -@@ -981,15 +982,23 @@ attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *da - - switch (kind) { - case crm_status_uname: -+ crm_debug("%s node %s is now %s", -+ (is_remote? "Remote" : "Cluster"), -+ peer->uname, state_text(peer->state)); - break; - - case crm_status_processes: - if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) { - gone = true; - } -+ crm_debug("Node %s is %s a peer", -+ peer->uname, (gone? "no longer" : "now")); - break; - - case crm_status_nstate: -+ crm_debug("%s node %s is now %s (was %s)", -+ (is_remote? "Remote" : "Cluster"), -+ peer->uname, state_text(peer->state), state_text(data)); - if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) { - /* If we're the writer, send new peers a list of all attributes - * (unless it's a remote node, which doesn't run its own attrd) --- -1.8.3.1 - - -From 1dcc8dee4990cf0dbdec0e14db6d9a3ad67a41d5 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Mon, 7 Jun 2021 11:13:53 -0500 -Subject: [PATCH 06/11] Low: pacemaker-attrd: ensure node ID is only set for - attributes when known - -In most cases, attribute updates contained the node ID, and the node ID was -used by other code, only if known (i.e. positive). However a couple places did -not check this, so add that. - -I am unsure whether the missing check caused problems in practice, but there -appears to be the possibility that a remote node would wrongly be added to the -cluster node cache. ---- - daemons/attrd/attrd_commands.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index d6d179b..b3f441c 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -136,7 +136,9 @@ build_attribute_xml( - crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid); - crm_xml_add(xml, PCMK__XA_ATTR_USER, user); - crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer); -- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid); -+ if (peerid > 0) { -+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid); -+ } - crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value); - crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000); - crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private); -@@ -937,7 +939,7 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter) - /* If this is a cluster node whose node ID we are learning, remember it */ - if ((v->nodeid == 0) && (v->is_remote == FALSE) - && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, -- (int*)&v->nodeid) == 0)) { -+ (int*)&v->nodeid) == 0) && (v->nodeid > 0)) { - - crm_node_t *known_peer = crm_get_peer(v->nodeid, host); - --- -1.8.3.1 - - -From 8d12490e88b558d01db37a38f7d35175c6d2d69a Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Thu, 10 Jun 2021 17:25:57 -0500 -Subject: [PATCH 07/11] Refactor: pacemaker-attrd: functionize processing a - sync response - -... for code isolation, and because we need to add more to it ---- - daemons/attrd/attrd_commands.c | 59 ++++++++++++++++++++++++++++-------------- - 1 file changed, 39 insertions(+), 20 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index b3f441c..d02d3e6 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -572,6 +572,43 @@ attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml) - } - - /*! -+ * \internal -+ * \brief Load attributes from a peer sync response -+ * -+ * \param[in] peer Peer that sent clear request -+ * \param[in] peer_won Whether peer is the attribute writer -+ * \param[in] xml Request XML -+ */ -+static void -+process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml) -+{ -+ crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s", -+ peer->uname); -+ -+ if (peer_won) { -+ /* Initialize the "seen" flag for all attributes to cleared, so we can -+ * detect attributes that local node has but the writer doesn't. -+ */ -+ clear_attribute_value_seen(); -+ } -+ -+ // Process each attribute update in the sync response -+ for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL; -+ child = pcmk__xml_next(child)) { -+ attrd_peer_update(peer, child, -+ crm_element_value(child, PCMK__XA_ATTR_NODE_NAME), -+ TRUE); -+ } -+ -+ if (peer_won) { -+ /* If any attributes are still not marked as seen, the writer doesn't -+ * know about them, so send all peers an update with them. -+ */ -+ attrd_current_only_attribute_update(peer, xml); -+ } -+} -+ -+/*! - \internal - \brief Broadcast private attribute for local node with protocol version - */ -@@ -596,7 +633,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml) - const char *op = crm_element_value(xml, PCMK__XA_TASK); - const char *election_op = crm_element_value(xml, F_CRM_TASK); - const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME); -- bool peer_won = FALSE; -+ bool peer_won = false; - - if (election_op) { - attrd_handle_election_op(peer, xml); -@@ -631,25 +668,7 @@ attrd_peer_message(crm_node_t *peer, xmlNode *xml) - - } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei) - && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) { -- xmlNode *child = NULL; -- -- crm_info("Processing %s from %s", op, peer->uname); -- -- /* Clear the seen flag for attribute processing held only in the own node. */ -- if (peer_won) { -- clear_attribute_value_seen(); -- } -- -- for (child = pcmk__xml_first_child(xml); child != NULL; -- child = pcmk__xml_next(child)) { -- host = crm_element_value(child, PCMK__XA_ATTR_NODE_NAME); -- attrd_peer_update(peer, child, host, TRUE); -- } -- -- if (peer_won) { -- /* Synchronize if there is an attribute held only by own node that Writer does not have. */ -- attrd_current_only_attribute_update(peer, xml); -- } -+ process_peer_sync_response(peer, peer_won, xml); - - } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) { - /* Ignore. The flush command was removed in 2.0.0 but may be --- -1.8.3.1 - - -From a890a0e5bbbcabf907f51ed0460868035f72464d Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Fri, 11 Jun 2021 14:40:39 -0500 -Subject: [PATCH 08/11] Refactor: pacemaker-attrd: functionize broadcasting - local override - -... for code isolation ---- - daemons/attrd/attrd_commands.c | 42 +++++++++++++++++++++++++++++------------- - 1 file changed, 29 insertions(+), 13 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index d02d3e6..4783427 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -804,6 +804,34 @@ attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml) - free_xml(sync); - } - -+/*! -+ * \internal -+ * \brief Override an attribute sync with a local value -+ * -+ * Broadcast the local node's value for an attribute that's different from the -+ * value provided in a peer's attribute synchronization response. This ensures a -+ * node's values for itself take precedence and all peers are kept in sync. -+ * -+ * \param[in] a Attribute entry to override -+ * -+ * \return Local instance of attribute value -+ */ -+static attribute_value_t * -+broadcast_local_value(attribute_t *a) -+{ -+ attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname); -+ xmlNode *sync = create_xml_node(NULL, __func__); -+ -+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); -+ build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, -+ a->user, a->is_private, v->nodename, v->nodeid, -+ v->current, FALSE); -+ attrd_xml_add_writer(sync); -+ send_attrd_message(NULL, sync); -+ free_xml(sync); -+ return v; -+} -+ - void - attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter) - { -@@ -899,21 +927,9 @@ attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter) - if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei) - && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) { - -- xmlNode *sync = create_xml_node(NULL, __func__); -- - crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s", - attr, host, v->current, value, peer->uname); -- -- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); -- v = g_hash_table_lookup(a->values, host); -- build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user, -- a->is_private, v->nodename, v->nodeid, v->current, FALSE); -- -- attrd_xml_add_writer(sync); -- -- /* Broadcast in case any other nodes had the inconsistent value */ -- send_attrd_message(NULL, sync); -- free_xml(sync); -+ v = broadcast_local_value(a); - - } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) { - crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s", --- -1.8.3.1 - - -From f6f65e3dab070f1bbdf6d1383f4d6173a8840bc9 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Fri, 11 Jun 2021 14:50:29 -0500 -Subject: [PATCH 09/11] Log: pacemaker-attrd: improve messages when - broadcasting local-only values - -The traces aren't necessary since build_attribute_xml() already logs the same -info at debug. Also, rename function for clarity, and make static. ---- - daemons/attrd/attrd_commands.c | 35 ++++++++++++++++------------------- - 1 file changed, 16 insertions(+), 19 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index 4783427..356defb 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -51,11 +51,12 @@ GHashTable *attributes = NULL; - - void write_attribute(attribute_t *a, bool ignore_delay); - void write_or_elect_attribute(attribute_t *a); --void attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml); - void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter); - void attrd_peer_sync(crm_node_t *peer, xmlNode *xml); - void attrd_peer_remove(const char *host, gboolean uncache, const char *source); - -+static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml); -+ - static gboolean - send_attrd_message(crm_node_t * node, xmlNode * data) - { -@@ -604,7 +605,7 @@ process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml) - /* If any attributes are still not marked as seen, the writer doesn't - * know about them, so send all peers an update with them. - */ -- attrd_current_only_attribute_update(peer, xml); -+ broadcast_unseen_local_values(peer, xml); - } - } - -@@ -768,40 +769,36 @@ attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml) - return(v); - } - --void --attrd_current_only_attribute_update(crm_node_t *peer, xmlNode *xml) -+void -+broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml) - { - GHashTableIter aIter; - GHashTableIter vIter; -- attribute_t *a; -+ attribute_t *a = NULL; - attribute_value_t *v = NULL; -- xmlNode *sync = create_xml_node(NULL, __func__); -- gboolean build = FALSE; -- -- crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); -+ xmlNode *sync = NULL; - - g_hash_table_iter_init(&aIter, attributes); - while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) { - g_hash_table_iter_init(&vIter, a->values); - while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) { -- if (pcmk__str_eq(v->nodename, attrd_cluster->uname, pcmk__str_casei) && v->seen == FALSE) { -- crm_trace("Syncing %s[%s] = %s to everyone.(from local only attributes)", a->id, v->nodename, v->current); -- -- build = TRUE; -+ if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname, -+ pcmk__str_casei)) { -+ if (sync == NULL) { -+ sync = create_xml_node(NULL, __func__); -+ crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); -+ } - build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private, - v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE)); -- } else { -- crm_trace("Local attribute(%s[%s] = %s) was ignore.(another host) : [%s]", a->id, v->nodename, v->current, attrd_cluster->uname); -- continue; - } - } - } - -- if (build) { -- crm_debug("Syncing values to everyone.(from local only attributes)"); -+ if (sync != NULL) { -+ crm_debug("Broadcasting local-only values"); - send_attrd_message(NULL, sync); -+ free_xml(sync); - } -- free_xml(sync); - } - - /*! --- -1.8.3.1 - - -From ab90ffb785ea018556f216b8f540f8c3429a3947 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Fri, 11 Jun 2021 15:04:20 -0500 -Subject: [PATCH 10/11] Refactor: pacemaker-attrd: simplify attribute XML - creation function - -... and rename for clarity ---- - daemons/attrd/attrd_commands.c | 48 ++++++++++++++++++++++++------------------ - 1 file changed, 27 insertions(+), 21 deletions(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index 356defb..5b32a77 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -125,25 +125,35 @@ cache_remote_node(const char *node_name) - CRM_ASSERT(crm_remote_peer_get(node_name) != NULL); - } - -+/*! -+ * \internal -+ * \brief Create an XML representation of an attribute for use in peer messages -+ * -+ * \param[in] parent Create attribute XML as child element of this element -+ * \param[in] a Attribute to represent -+ * \param[in] v Attribute value to represent -+ * \param[in] force_write If true, value should be written even if unchanged -+ * -+ * \return XML representation of attribute -+ */ - static xmlNode * --build_attribute_xml( -- xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user, -- gboolean is_private, const char *peer, uint32_t peerid, const char *value, gboolean is_force_write) -+add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v, -+ bool force_write) - { - xmlNode *xml = create_xml_node(parent, __func__); - -- crm_xml_add(xml, PCMK__XA_ATTR_NAME, name); -- crm_xml_add(xml, PCMK__XA_ATTR_SET, set); -- crm_xml_add(xml, PCMK__XA_ATTR_UUID, uuid); -- crm_xml_add(xml, PCMK__XA_ATTR_USER, user); -- crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, peer); -- if (peerid > 0) { -- crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, peerid); -+ crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id); -+ crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set); -+ crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid); -+ crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user); -+ crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename); -+ if (v->nodeid > 0) { -+ crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid); - } -- crm_xml_add(xml, PCMK__XA_ATTR_VALUE, value); -- crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, timeout_ms/1000); -- crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, is_private); -- crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, is_force_write); -+ crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current); -+ crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000); -+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private); -+ crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write); - - return xml; - } -@@ -695,8 +705,7 @@ attrd_peer_sync(crm_node_t *peer, xmlNode *xml) - g_hash_table_iter_init(&vIter, a->values); - while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) { - crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone"); -- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private, -- v->nodename, v->nodeid, v->current, FALSE); -+ add_attribute_value_xml(sync, a, v, false); - } - } - -@@ -788,8 +797,7 @@ broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml) - sync = create_xml_node(NULL, __func__); - crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); - } -- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private, -- v->nodename, v->nodeid, v->current, (a->timeout_ms && a->timer ? TRUE : FALSE)); -+ add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer); - } - } - } -@@ -820,9 +828,7 @@ broadcast_local_value(attribute_t *a) - xmlNode *sync = create_xml_node(NULL, __func__); - - crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE); -- build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, -- a->user, a->is_private, v->nodename, v->nodeid, -- v->current, FALSE); -+ add_attribute_value_xml(sync, a, v, false); - attrd_xml_add_writer(sync); - send_attrd_message(NULL, sync); - free_xml(sync); --- -1.8.3.1 - - -From 540d74130c5c8d9c626d6c50475e4dc4f64234e7 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Fri, 4 Jun 2021 16:34:26 -0500 -Subject: [PATCH 11/11] Fix: pacemaker-attrd: avoid repeated unfencing of - remote nodes - -The attribute manager can't record a remote node's attributes to the CIB until -it knows the node is remote. Normally, this is learned when the remote node -starts, because the controller clears the CRM_OP_PROBED attribute and indicates -that it is for a remote node. - -However, if a cluster node is down when a remote node starts, and later comes -up, it learns the remote node's existing attributes as part of the attribute -sync. Previously, this did not include whether each value is for a cluster or -remote node, so the newly joined attribute manager couldn't write out remote -nodes' attributes until it learned that via some other event -- which might not -happen before the node becomes DC, in which case its scheduler will not see any -unfencing-related node attributes and may wrongly schedule unfencing. - -The sync response handling already calls attrd_lookup_or_create_value(), which -checks PCMK__XA_ATTR_IS_REMOTE, so all we need to do is add that to the sync -response. ---- - daemons/attrd/attrd_commands.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/daemons/attrd/attrd_commands.c b/daemons/attrd/attrd_commands.c -index 5b32a77..0142383 100644 ---- a/daemons/attrd/attrd_commands.c -+++ b/daemons/attrd/attrd_commands.c -@@ -43,8 +43,9 @@ - * 1 1.1.15 PCMK__ATTRD_CMD_UPDATE_BOTH, - * PCMK__ATTRD_CMD_UPDATE_DELAY - * 2 1.1.17 PCMK__ATTRD_CMD_CLEAR_FAILURE -+ * 3 2.1.1 PCMK__ATTRD_CMD_SYNC_RESPONSE indicates remote nodes - */ --#define ATTRD_PROTOCOL_VERSION "2" -+#define ATTRD_PROTOCOL_VERSION "3" - - int last_cib_op_done = 0; - GHashTable *attributes = NULL; -@@ -150,6 +151,9 @@ add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v, - if (v->nodeid > 0) { - crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid); - } -+ if (v->is_remote != 0) { -+ crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1); -+ } - crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current); - crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000); - crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private); --- -1.8.3.1 - diff --git a/SOURCES/008-dynamic-list-fencing.patch b/SOURCES/008-dynamic-list-fencing.patch deleted file mode 100644 index 4a56117..0000000 --- a/SOURCES/008-dynamic-list-fencing.patch +++ /dev/null @@ -1,140 +0,0 @@ -From 2d15fb37525f88ec8d5acb689b698044c4bb69b1 Mon Sep 17 00:00:00 2001 -From: Hideo Yamauchi -Date: Thu, 17 Jun 2021 22:39:12 +0900 -Subject: [PATCH 1/2] Low: fenced: Low: fenced: Remove unnecessary release. - ---- - daemons/fenced/fenced_commands.c | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c -index fee55a7..35aec06 100644 ---- a/daemons/fenced/fenced_commands.c -+++ b/daemons/fenced/fenced_commands.c -@@ -1104,9 +1104,6 @@ dynamic_list_search_cb(GPid pid, int rc, const char *output, gpointer user_data) - /* Fall back to status */ - g_hash_table_replace(dev->params, - strdup(PCMK_STONITH_HOST_CHECK), strdup("status")); -- -- g_list_free_full(dev->targets, free); -- dev->targets = NULL; - } else if (!rc) { - crm_info("Refreshing port list for %s", dev->id); - g_list_free_full(dev->targets, free); --- -1.8.3.1 - - -From a29f88f6020aac5f1ac32072942eb5713d7be50d Mon Sep 17 00:00:00 2001 -From: Hideo Yamauchi -Date: Thu, 17 Jun 2021 22:40:40 +0900 -Subject: [PATCH 2/2] High: fenced: Wrong device may be selected when - "dynamic-list" is specified. - ---- - daemons/fenced/fenced_commands.c | 67 +++++++++++++++++++++++----------------- - 1 file changed, 38 insertions(+), 29 deletions(-) - -diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c -index 35aec06..da076fb 100644 ---- a/daemons/fenced/fenced_commands.c -+++ b/daemons/fenced/fenced_commands.c -@@ -904,6 +904,31 @@ xml2device_params(const char *name, xmlNode *dev) - return params; - } - -+static const char * -+target_list_type(stonith_device_t * dev) -+{ -+ const char *check_type = NULL; -+ -+ check_type = g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_CHECK); -+ -+ if (check_type == NULL) { -+ -+ if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_LIST)) { -+ check_type = "static-list"; -+ } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)) { -+ check_type = "static-list"; -+ } else if (pcmk_is_set(dev->flags, st_device_supports_list)) { -+ check_type = "dynamic-list"; -+ } else if (pcmk_is_set(dev->flags, st_device_supports_status)) { -+ check_type = "status"; -+ } else { -+ check_type = "none"; -+ } -+ } -+ -+ return check_type; -+} -+ - static stonith_device_t * - build_device_from_xml(xmlNode * msg) - { -@@ -931,6 +956,12 @@ build_device_from_xml(xmlNode * msg) - value = g_hash_table_lookup(device->params, PCMK_STONITH_HOST_MAP); - device->aliases = build_port_aliases(value, &(device->targets)); - -+ value = target_list_type(device); -+ if (!pcmk__str_eq(value, "static-list", pcmk__str_casei) && device->targets) { -+ /* Other than "static-list", dev-> targets is unnecessary. */ -+ g_list_free_full(device->targets, free); -+ device->targets = NULL; -+ } - device->agent_metadata = get_agent_metadata(device->agent); - if (device->agent_metadata) { - read_action_metadata(device); -@@ -971,31 +1002,6 @@ build_device_from_xml(xmlNode * msg) - return device; - } - --static const char * --target_list_type(stonith_device_t * dev) --{ -- const char *check_type = NULL; -- -- check_type = g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_CHECK); -- -- if (check_type == NULL) { -- -- if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_LIST)) { -- check_type = "static-list"; -- } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP)) { -- check_type = "static-list"; -- } else if (pcmk_is_set(dev->flags, st_device_supports_list)) { -- check_type = "dynamic-list"; -- } else if (pcmk_is_set(dev->flags, st_device_supports_status)) { -- check_type = "status"; -- } else { -- check_type = "none"; -- } -- } -- -- return check_type; --} -- - static void - schedule_internal_command(const char *origin, - stonith_device_t * device, -@@ -1099,11 +1105,14 @@ dynamic_list_search_cb(GPid pid, int rc, const char *output, gpointer user_data) - - /* If we successfully got the targets earlier, don't disable. */ - if (rc != 0 && !dev->targets) { -- crm_notice("Disabling port list queries for %s: %s " -- CRM_XS " rc=%d", dev->id, output, rc); -- /* Fall back to status */ -- g_hash_table_replace(dev->params, -+ if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_CHECK) == NULL) { -+ /* -+ If the operation fails if the user does not explicitly specify "dynamic-list", it will fall back to "status". -+ */ -+ crm_notice("Disabling port list queries for %s (%d): %s", dev->id, rc, output); -+ g_hash_table_replace(dev->params, - strdup(PCMK_STONITH_HOST_CHECK), strdup("status")); -+ } - } else if (!rc) { - crm_info("Refreshing port list for %s", dev->id); - g_list_free_full(dev->targets, free); --- -1.8.3.1 - diff --git a/SOURCES/009-crm_resource-messages.patch b/SOURCES/009-crm_resource-messages.patch deleted file mode 100644 index bdbcf03..0000000 --- a/SOURCES/009-crm_resource-messages.patch +++ /dev/null @@ -1,229 +0,0 @@ -From 5bcab230ad4c647ca78b18bd4a66e30a4bb4417f Mon Sep 17 00:00:00 2001 -From: Oyvind Albrigtsen -Date: Wed, 16 Jun 2021 11:19:03 +0200 -Subject: [PATCH 1/2] Feature: crm_resource: report not supported for --force-* - w/systemd, upstart, nagios and bundled resources - ---- - tools/crm_resource.c | 21 ++++---------- - tools/crm_resource_runtime.c | 67 +++++++++++++++++++++++++++++--------------- - 2 files changed, 51 insertions(+), 37 deletions(-) - -diff --git a/tools/crm_resource.c b/tools/crm_resource.c -index 4abdd03..fa7902c 100644 ---- a/tools/crm_resource.c -+++ b/tools/crm_resource.c -@@ -660,21 +660,12 @@ attr_set_type_cb(const gchar *option_name, const gchar *optarg, gpointer data, G - - gboolean - class_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) { -- if (!(pcmk_get_ra_caps(optarg) & pcmk_ra_cap_params)) { -- if (!args->quiet) { -- g_set_error(error, G_OPTION_ERROR, CRM_EX_INVALID_PARAM, -- "Standard %s does not support parameters\n", optarg); -- } -- return FALSE; -- -- } else { -- if (options.v_class != NULL) { -- free(options.v_class); -- } -- -- options.v_class = strdup(optarg); -+ if (options.v_class != NULL) { -+ free(options.v_class); - } - -+ options.v_class = strdup(optarg); -+ - options.cmdline_config = TRUE; - options.require_resource = FALSE; - return TRUE; -@@ -1422,7 +1413,7 @@ validate_cmdline_config(void) - } else if (options.rsc_cmd != cmd_execute_agent) { - g_set_error(&error, PCMK__EXITC_ERROR, CRM_EX_USAGE, - "--class, --agent, and --provider can only be used with " -- "--validate"); -+ "--validate and --force-*"); - - // Not all of --class, --agent, and --provider need to be given. Not all - // classes support the concept of a provider. Check that what we were given -@@ -1841,7 +1832,7 @@ main(int argc, char **argv) - if (options.cmdline_config) { - exit_code = cli_resource_execute_from_params(out, NULL, - options.v_class, options.v_provider, options.v_agent, -- "validate-all", options.cmdline_params, -+ options.operation, options.cmdline_params, - options.override_params, options.timeout_ms, - args->verbosity, options.force, options.check_level); - } else { -diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c -index fe42e60..59e6df5 100644 ---- a/tools/crm_resource_runtime.c -+++ b/tools/crm_resource_runtime.c -@@ -1674,24 +1674,59 @@ wait_till_stable(pcmk__output_t *out, int timeout_ms, cib_t * cib) - return rc; - } - -+static const char * -+get_action(const char *rsc_action) { -+ const char *action = NULL; -+ -+ if (pcmk__str_eq(rsc_action, "validate", pcmk__str_casei)) { -+ action = "validate-all"; -+ -+ } else if (pcmk__str_eq(rsc_action, "force-check", pcmk__str_casei)) { -+ action = "monitor"; -+ -+ } else if (pcmk__strcase_any_of(rsc_action, "force-start", "force-stop", -+ "force-demote", "force-promote", NULL)) { -+ action = rsc_action+6; -+ } else { -+ action = rsc_action; -+ } -+ -+ return action; -+} -+ - crm_exit_t - cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - const char *rsc_class, const char *rsc_prov, -- const char *rsc_type, const char *action, -+ const char *rsc_type, const char *rsc_action, - GHashTable *params, GHashTable *override_hash, - int timeout_ms, int resource_verbose, gboolean force, - int check_level) - { -+ const char *action = NULL; - GHashTable *params_copy = NULL; - crm_exit_t exit_code = CRM_EX_OK; - svc_action_t *op = NULL; - - if (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) { - out->err(out, "Sorry, the %s option doesn't support %s resources yet", -- action, rsc_class); -+ rsc_action, rsc_class); -+ crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); -+ } else if (pcmk__strcase_any_of(rsc_class, PCMK_RESOURCE_CLASS_SYSTEMD, -+ PCMK_RESOURCE_CLASS_UPSTART, PCMK_RESOURCE_CLASS_NAGIOS, NULL)) { -+ out->err(out, "Sorry, the %s option doesn't support %s resources", -+ rsc_action, rsc_class); -+ crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); -+ } else if (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_SERVICE, -+ pcmk__str_casei) && !pcmk__str_eq( -+ resources_find_service_class(rsc_name), PCMK_RESOURCE_CLASS_LSB, -+ pcmk__str_casei)) { -+ out->err(out, "Sorry, the %s option doesn't support %s resources", -+ rsc_action, resources_find_service_class(rsc_name)); - crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); - } - -+ action = get_action(rsc_action); -+ - /* If no timeout was provided, grab the default. */ - if (timeout_ms == 0) { - timeout_ms = crm_get_msec(CRM_DEFAULT_OP_TIMEOUT_S); -@@ -1766,7 +1801,7 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - exit_code = op->rc; - - out->message(out, "resource-agent-action", resource_verbose, rsc_class, -- rsc_prov, rsc_type, rsc_name, action, override_hash, op->rc, -+ rsc_prov, rsc_type, rsc_name, rsc_action, override_hash, op->rc, - op->status, op->stdout_data, op->stderr_data); - } else { - exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc; -@@ -1790,27 +1825,15 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name, - const char *rtype = NULL; - const char *rprov = NULL; - const char *rclass = NULL; -- const char *action = NULL; - GHashTable *params = NULL; - -- if (pcmk__str_eq(rsc_action, "validate", pcmk__str_casei)) { -- action = "validate-all"; -- -- } else if (pcmk__str_eq(rsc_action, "force-check", pcmk__str_casei)) { -- action = "monitor"; -- -- } else if (pcmk__str_eq(rsc_action, "force-stop", pcmk__str_casei)) { -- action = rsc_action+6; -- -- } else if (pcmk__strcase_any_of(rsc_action, "force-start", "force-demote", -+ if (pcmk__strcase_any_of(rsc_action, "force-start", "force-demote", - "force-promote", NULL)) { -- action = rsc_action+6; -- - if(pe_rsc_is_clone(rsc)) { - GList *nodes = cli_resource_search(rsc, requested_name, data_set); - if(nodes != NULL && force == FALSE) { - out->err(out, "It is not safe to %s %s here: the cluster claims it is already active", -- action, rsc->id); -+ rsc_action, rsc->id); - out->err(out, "Try setting target-role=Stopped first or specifying " - "the force option"); - return CRM_EX_UNSAFE; -@@ -1818,9 +1841,6 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name, - - g_list_free_full(nodes, free); - } -- -- } else { -- action = rsc_action; - } - - if(pe_rsc_is_clone(rsc)) { -@@ -1831,6 +1851,9 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name, - if(rsc->variant == pe_group) { - out->err(out, "Sorry, the %s option doesn't support group resources", rsc_action); - return CRM_EX_UNIMPLEMENT_FEATURE; -+ } else if (rsc->variant == pe_container || pe_rsc_is_bundled(rsc)) { -+ out->err(out, "Sorry, the %s option doesn't support bundled resources", rsc_action); -+ return CRM_EX_UNIMPLEMENT_FEATURE; - } - - rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); -@@ -1841,12 +1864,12 @@ cli_resource_execute(pe_resource_t *rsc, const char *requested_name, - data_set); - - if (timeout_ms == 0) { -- timeout_ms = pe_get_configured_timeout(rsc, action, data_set); -+ timeout_ms = pe_get_configured_timeout(rsc, get_action(rsc_action), data_set); - } - - rid = pe_rsc_is_anon_clone(rsc->parent)? requested_name : rsc->id; - -- exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, action, -+ exit_code = cli_resource_execute_from_params(out, rid, rclass, rprov, rtype, rsc_action, - params, override_hash, timeout_ms, - resource_verbose, force, check_level); - return exit_code; --- -1.8.3.1 - - -From 289cd231186755d99c1262eb9f968dc852409588 Mon Sep 17 00:00:00 2001 -From: Oyvind Albrigtsen -Date: Fri, 16 Jul 2021 13:20:55 +0200 -Subject: [PATCH 2/2] Refactor: crm_resource: remove duplicate Overriding - message that's handled elsewhere - ---- - tools/crm_resource_runtime.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c -index 59e6df5..ce037c5 100644 ---- a/tools/crm_resource_runtime.c -+++ b/tools/crm_resource_runtime.c -@@ -1791,8 +1791,6 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - - g_hash_table_iter_init(&iter, override_hash); - while (g_hash_table_iter_next(&iter, (gpointer *) & name, (gpointer *) & value)) { -- out->info(out, "Overriding the cluster configuration for '%s' with '%s' = '%s'", -- rsc_name, name, value); - g_hash_table_replace(op->params, strdup(name), strdup(value)); - } - } --- -1.8.3.1 - diff --git a/SOURCES/010-probe-pending.patch b/SOURCES/010-probe-pending.patch deleted file mode 100644 index 336c33e..0000000 --- a/SOURCES/010-probe-pending.patch +++ /dev/null @@ -1,715 +0,0 @@ -From b0347f7b8e609420a7055d5fe537cc40ac0d1bb2 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Fri, 16 Jul 2021 11:08:05 -0500 -Subject: [PATCH 1/3] Fix: scheduler: don't schedule probes of unmanaged - resources on pending nodes - -Previously, custom_action() would set an action's optional or runnable flag in -the same, exclusive if-else sequence. This means that if an action should be -optional *and* runnable, only one would be set. In particular, this meant that -if a resource is unmanaged *and* its allocated node is pending, any probe would -be set to optional, but not unrunnable, and the controller could wrongly -attempt the probe before the join completed. - -Now, optional is checked separately. ---- - lib/pengine/utils.c | 22 ++++++++++++++-------- - 1 file changed, 14 insertions(+), 8 deletions(-) - -diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c -index 5ef742e..965824b 100644 ---- a/lib/pengine/utils.c -+++ b/lib/pengine/utils.c -@@ -541,6 +541,20 @@ custom_action(pe_resource_t * rsc, char *key, const char *task, - FALSE, data_set); - } - -+ // Make the action optional if its resource is unmanaged -+ if (!pcmk_is_set(action->flags, pe_action_pseudo) -+ && (action->node != NULL) -+ && !pcmk_is_set(action->rsc->flags, pe_rsc_managed) -+ && (g_hash_table_lookup(action->meta, -+ XML_LRM_ATTR_INTERVAL_MS) == NULL)) { -+ pe_rsc_debug(rsc, "%s on %s is optional (%s is unmanaged)", -+ action->uuid, action->node->details->uname, -+ action->rsc->id); -+ pe__set_action_flags(action, pe_action_optional); -+ // We shouldn't clear runnable here because ... something -+ } -+ -+ // Make the action runnable or unrunnable as appropriate - if (pcmk_is_set(action->flags, pe_action_pseudo)) { - /* leave untouched */ - -@@ -549,14 +563,6 @@ custom_action(pe_resource_t * rsc, char *key, const char *task, - action->uuid); - pe__clear_action_flags(action, pe_action_runnable); - -- } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed) -- && g_hash_table_lookup(action->meta, -- XML_LRM_ATTR_INTERVAL_MS) == NULL) { -- pe_rsc_debug(rsc, "%s on %s is optional (%s is unmanaged)", -- action->uuid, action->node->details->uname, rsc->id); -- pe__set_action_flags(action, pe_action_optional); -- //pe__clear_action_flags(action, pe_action_runnable); -- - } else if (!pcmk_is_set(action->flags, pe_action_dc) - && !(action->node->details->online) - && (!pe__is_guest_node(action->node) --- -1.8.3.1 - - -From 520303b90eb707f5b7a9afa9b106e4a38b90f0f9 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Wed, 14 Jul 2021 17:18:44 -0500 -Subject: [PATCH 2/3] Test: scheduler: update existing tests for probe - scheduling change - -This is an improvement. Looking at bundle-probe-order-2 for example, -the bundle's first instance has this status to start: - - * Replica[0] - * galera (ocf::heartbeat:galera): Stopped (unmanaged) - * galera-bundle-docker-0 (ocf::heartbeat:docker): Started centos2 (unmanaged) - * galera-bundle-0 (ocf::pacemaker:remote): Started centos2 (unmanaged) - -After the changes, we now schedule recurring monitors for -galera-bundle-docker-0 and galera-bundle-0 on centos2, and a probe of galera:0 -on galera-bundle-0, all of which are possible. ---- - cts/scheduler/dot/bundle-probe-order-2.dot | 3 ++ - cts/scheduler/dot/bundle-probe-order-3.dot | 1 + - cts/scheduler/exp/bundle-probe-order-2.exp | 33 ++++++++++++++++++++-- - cts/scheduler/exp/bundle-probe-order-3.exp | 21 ++++++++++---- - cts/scheduler/summary/bundle-probe-order-2.summary | 3 ++ - cts/scheduler/summary/bundle-probe-order-3.summary | 1 + - 6 files changed, 53 insertions(+), 9 deletions(-) - -diff --git a/cts/scheduler/dot/bundle-probe-order-2.dot b/cts/scheduler/dot/bundle-probe-order-2.dot -index 0cce3fd..7706195 100644 ---- a/cts/scheduler/dot/bundle-probe-order-2.dot -+++ b/cts/scheduler/dot/bundle-probe-order-2.dot -@@ -1,6 +1,9 @@ - digraph "g" { -+"galera-bundle-0_monitor_30000 centos2" [ style=bold color="green" fontcolor="black"] -+"galera-bundle-docker-0_monitor_60000 centos2" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-1_monitor_0 centos2" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-2_monitor_0 centos1" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-2_monitor_0 centos2" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-2_monitor_0 centos3" [ style=bold color="green" fontcolor="black"] -+"galera:0_monitor_0 galera-bundle-0" [ style=bold color="green" fontcolor="black"] - } -diff --git a/cts/scheduler/dot/bundle-probe-order-3.dot b/cts/scheduler/dot/bundle-probe-order-3.dot -index a4b109f..53a384b 100644 ---- a/cts/scheduler/dot/bundle-probe-order-3.dot -+++ b/cts/scheduler/dot/bundle-probe-order-3.dot -@@ -2,6 +2,7 @@ - "galera-bundle-0_monitor_0 centos1" [ style=bold color="green" fontcolor="black"] - "galera-bundle-0_monitor_0 centos2" [ style=bold color="green" fontcolor="black"] - "galera-bundle-0_monitor_0 centos3" [ style=bold color="green" fontcolor="black"] -+"galera-bundle-docker-0_monitor_60000 centos2" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-1_monitor_0 centos2" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-2_monitor_0 centos1" [ style=bold color="green" fontcolor="black"] - "galera-bundle-docker-2_monitor_0 centos2" [ style=bold color="green" fontcolor="black"] -diff --git a/cts/scheduler/exp/bundle-probe-order-2.exp b/cts/scheduler/exp/bundle-probe-order-2.exp -index d6174e7..5b28050 100644 ---- a/cts/scheduler/exp/bundle-probe-order-2.exp -+++ b/cts/scheduler/exp/bundle-probe-order-2.exp -@@ -1,6 +1,33 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -@@ -8,7 +35,7 @@ - - - -- -+ - - - -@@ -17,7 +44,7 @@ - - - -- -+ - - - -@@ -26,7 +53,7 @@ - - - -- -+ - - - -diff --git a/cts/scheduler/exp/bundle-probe-order-3.exp b/cts/scheduler/exp/bundle-probe-order-3.exp -index e1f60e7..69140a4 100644 ---- a/cts/scheduler/exp/bundle-probe-order-3.exp -+++ b/cts/scheduler/exp/bundle-probe-order-3.exp -@@ -1,6 +1,15 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ - - - -@@ -8,7 +17,7 @@ - - - -- -+ - - - -@@ -17,7 +26,7 @@ - - - -- -+ - - - -@@ -26,7 +35,7 @@ - - - -- -+ - - - -@@ -35,7 +44,7 @@ - - - -- -+ - - - -@@ -44,7 +53,7 @@ - - - -- -+ - - - -@@ -53,7 +62,7 @@ - - - -- -+ - - - -diff --git a/cts/scheduler/summary/bundle-probe-order-2.summary b/cts/scheduler/summary/bundle-probe-order-2.summary -index 681d607..024c472 100644 ---- a/cts/scheduler/summary/bundle-probe-order-2.summary -+++ b/cts/scheduler/summary/bundle-probe-order-2.summary -@@ -13,6 +13,9 @@ Current cluster status: - Transition Summary: - - Executing Cluster Transition: -+ * Resource action: galera:0 monitor on galera-bundle-0 -+ * Resource action: galera-bundle-docker-0 monitor=60000 on centos2 -+ * Resource action: galera-bundle-0 monitor=30000 on centos2 - * Resource action: galera-bundle-docker-1 monitor on centos2 - * Resource action: galera-bundle-docker-2 monitor on centos3 - * Resource action: galera-bundle-docker-2 monitor on centos2 -diff --git a/cts/scheduler/summary/bundle-probe-order-3.summary b/cts/scheduler/summary/bundle-probe-order-3.summary -index f089618..331bd87 100644 ---- a/cts/scheduler/summary/bundle-probe-order-3.summary -+++ b/cts/scheduler/summary/bundle-probe-order-3.summary -@@ -12,6 +12,7 @@ Current cluster status: - Transition Summary: - - Executing Cluster Transition: -+ * Resource action: galera-bundle-docker-0 monitor=60000 on centos2 - * Resource action: galera-bundle-0 monitor on centos3 - * Resource action: galera-bundle-0 monitor on centos2 - * Resource action: galera-bundle-0 monitor on centos1 --- -1.8.3.1 - - -From cb9c294a7ef22916866e0e42e51e88c2b1a61c2e Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Wed, 14 Jul 2021 17:23:11 -0500 -Subject: [PATCH 3/3] Test: scheduler: add test for probe of unmanaged resource - on pending node - -No probes should be scheduled in this case ---- - cts/cts-scheduler.in | 1 + - cts/scheduler/dot/probe-pending-node.dot | 2 + - cts/scheduler/exp/probe-pending-node.exp | 1 + - cts/scheduler/scores/probe-pending-node.scores | 61 ++++++ - cts/scheduler/summary/probe-pending-node.summary | 55 +++++ - cts/scheduler/xml/probe-pending-node.xml | 247 +++++++++++++++++++++++ - 6 files changed, 367 insertions(+) - create mode 100644 cts/scheduler/dot/probe-pending-node.dot - create mode 100644 cts/scheduler/exp/probe-pending-node.exp - create mode 100644 cts/scheduler/scores/probe-pending-node.scores - create mode 100644 cts/scheduler/summary/probe-pending-node.summary - create mode 100644 cts/scheduler/xml/probe-pending-node.xml - -diff --git a/cts/cts-scheduler.in b/cts/cts-scheduler.in -index fc9790b..7ba2415 100644 ---- a/cts/cts-scheduler.in -+++ b/cts/cts-scheduler.in -@@ -110,6 +110,7 @@ TESTS = [ - [ "probe-2", "Correctly re-probe cloned groups" ], - [ "probe-3", "Probe (pending node)" ], - [ "probe-4", "Probe (pending node + stopped resource)" ], -+ [ "probe-pending-node", "Probe (pending node + unmanaged resource)" ], - [ "standby", "Standby" ], - [ "comments", "Comments" ], - ], -diff --git a/cts/scheduler/dot/probe-pending-node.dot b/cts/scheduler/dot/probe-pending-node.dot -new file mode 100644 -index 0000000..d8f1c9f ---- /dev/null -+++ b/cts/scheduler/dot/probe-pending-node.dot -@@ -0,0 +1,2 @@ -+ digraph "g" { -+} -diff --git a/cts/scheduler/exp/probe-pending-node.exp b/cts/scheduler/exp/probe-pending-node.exp -new file mode 100644 -index 0000000..56e315f ---- /dev/null -+++ b/cts/scheduler/exp/probe-pending-node.exp -@@ -0,0 +1 @@ -+ -diff --git a/cts/scheduler/scores/probe-pending-node.scores b/cts/scheduler/scores/probe-pending-node.scores -new file mode 100644 -index 0000000..020a1a0 ---- /dev/null -+++ b/cts/scheduler/scores/probe-pending-node.scores -@@ -0,0 +1,61 @@ -+ -+pcmk__clone_allocate: fs_UC5_SAPMNT-clone allocation score on gcdoubwap01: 0 -+pcmk__clone_allocate: fs_UC5_SAPMNT-clone allocation score on gcdoubwap02: 0 -+pcmk__clone_allocate: fs_UC5_SAPMNT:0 allocation score on gcdoubwap01: 0 -+pcmk__clone_allocate: fs_UC5_SAPMNT:0 allocation score on gcdoubwap02: 0 -+pcmk__clone_allocate: fs_UC5_SAPMNT:1 allocation score on gcdoubwap01: 0 -+pcmk__clone_allocate: fs_UC5_SAPMNT:1 allocation score on gcdoubwap02: 0 -+pcmk__clone_allocate: fs_UC5_SYS-clone allocation score on gcdoubwap01: 0 -+pcmk__clone_allocate: fs_UC5_SYS-clone allocation score on gcdoubwap02: 0 -+pcmk__clone_allocate: fs_UC5_SYS:0 allocation score on gcdoubwap01: 0 -+pcmk__clone_allocate: fs_UC5_SYS:0 allocation score on gcdoubwap02: 0 -+pcmk__clone_allocate: fs_UC5_SYS:1 allocation score on gcdoubwap01: 0 -+pcmk__clone_allocate: fs_UC5_SYS:1 allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: fs_UC5_ascs allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: fs_UC5_ascs allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: fs_UC5_ers allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: fs_UC5_ers allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: grp_UC5_ascs allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: grp_UC5_ascs allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: grp_UC5_ers allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: grp_UC5_ers allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: rsc_sap_UC5_ASCS11 allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: rsc_sap_UC5_ASCS11 allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: rsc_sap_UC5_ERS12 allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: rsc_sap_UC5_ERS12 allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: rsc_vip_gcp_ascs allocation score on gcdoubwap01: INFINITY -+pcmk__group_allocate: rsc_vip_gcp_ascs allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: rsc_vip_gcp_ers allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: rsc_vip_gcp_ers allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: rsc_vip_init_ers allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: rsc_vip_init_ers allocation score on gcdoubwap02: 0 -+pcmk__group_allocate: rsc_vip_int_ascs allocation score on gcdoubwap01: 0 -+pcmk__group_allocate: rsc_vip_int_ascs allocation score on gcdoubwap02: 0 -+pcmk__native_allocate: fs_UC5_SAPMNT:0 allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: fs_UC5_SAPMNT:0 allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: fs_UC5_SAPMNT:1 allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: fs_UC5_SAPMNT:1 allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: fs_UC5_SYS:0 allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: fs_UC5_SYS:0 allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: fs_UC5_SYS:1 allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: fs_UC5_SYS:1 allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: fs_UC5_ascs allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: fs_UC5_ascs allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: fs_UC5_ers allocation score on gcdoubwap01: -INFINITY -+pcmk__native_allocate: fs_UC5_ers allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: rsc_sap_UC5_ASCS11 allocation score on gcdoubwap01: -INFINITY -+pcmk__native_allocate: rsc_sap_UC5_ASCS11 allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: rsc_sap_UC5_ERS12 allocation score on gcdoubwap01: -INFINITY -+pcmk__native_allocate: rsc_sap_UC5_ERS12 allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: rsc_vip_gcp_ascs allocation score on gcdoubwap01: -INFINITY -+pcmk__native_allocate: rsc_vip_gcp_ascs allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: rsc_vip_gcp_ers allocation score on gcdoubwap01: -INFINITY -+pcmk__native_allocate: rsc_vip_gcp_ers allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: rsc_vip_init_ers allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: rsc_vip_init_ers allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: rsc_vip_int_ascs allocation score on gcdoubwap01: INFINITY -+pcmk__native_allocate: rsc_vip_int_ascs allocation score on gcdoubwap02: -INFINITY -+pcmk__native_allocate: stonith_gcdoubwap01 allocation score on gcdoubwap01: -INFINITY -+pcmk__native_allocate: stonith_gcdoubwap01 allocation score on gcdoubwap02: 0 -+pcmk__native_allocate: stonith_gcdoubwap02 allocation score on gcdoubwap01: 0 -+pcmk__native_allocate: stonith_gcdoubwap02 allocation score on gcdoubwap02: -INFINITY -diff --git a/cts/scheduler/summary/probe-pending-node.summary b/cts/scheduler/summary/probe-pending-node.summary -new file mode 100644 -index 0000000..208186b ---- /dev/null -+++ b/cts/scheduler/summary/probe-pending-node.summary -@@ -0,0 +1,55 @@ -+Using the original execution date of: 2021-06-11 13:55:24Z -+ -+ *** Resource management is DISABLED *** -+ The cluster will not attempt to start, stop or recover services -+ -+Current cluster status: -+ * Node List: -+ * Node gcdoubwap02: pending -+ * Online: [ gcdoubwap01 ] -+ -+ * Full List of Resources: -+ * stonith_gcdoubwap01 (stonith:fence_gce): Stopped (unmanaged) -+ * stonith_gcdoubwap02 (stonith:fence_gce): Stopped (unmanaged) -+ * Clone Set: fs_UC5_SAPMNT-clone [fs_UC5_SAPMNT] (unmanaged): -+ * Stopped: [ gcdoubwap01 gcdoubwap02 ] -+ * Clone Set: fs_UC5_SYS-clone [fs_UC5_SYS] (unmanaged): -+ * Stopped: [ gcdoubwap01 gcdoubwap02 ] -+ * Resource Group: grp_UC5_ascs (unmanaged): -+ * rsc_vip_int_ascs (ocf:heartbeat:IPaddr2): Stopped (unmanaged) -+ * rsc_vip_gcp_ascs (ocf:heartbeat:gcp-vpc-move-vip): Started gcdoubwap01 (unmanaged) -+ * fs_UC5_ascs (ocf:heartbeat:Filesystem): Stopped (unmanaged) -+ * rsc_sap_UC5_ASCS11 (ocf:heartbeat:SAPInstance): Stopped (unmanaged) -+ * Resource Group: grp_UC5_ers (unmanaged): -+ * rsc_vip_init_ers (ocf:heartbeat:IPaddr2): Stopped (unmanaged) -+ * rsc_vip_gcp_ers (ocf:heartbeat:gcp-vpc-move-vip): Stopped (unmanaged) -+ * fs_UC5_ers (ocf:heartbeat:Filesystem): Stopped (unmanaged) -+ * rsc_sap_UC5_ERS12 (ocf:heartbeat:SAPInstance): Stopped (unmanaged) -+ -+Transition Summary: -+ -+Executing Cluster Transition: -+Using the original execution date of: 2021-06-11 13:55:24Z -+ -+Revised Cluster Status: -+ * Node List: -+ * Node gcdoubwap02: pending -+ * Online: [ gcdoubwap01 ] -+ -+ * Full List of Resources: -+ * stonith_gcdoubwap01 (stonith:fence_gce): Stopped (unmanaged) -+ * stonith_gcdoubwap02 (stonith:fence_gce): Stopped (unmanaged) -+ * Clone Set: fs_UC5_SAPMNT-clone [fs_UC5_SAPMNT] (unmanaged): -+ * Stopped: [ gcdoubwap01 gcdoubwap02 ] -+ * Clone Set: fs_UC5_SYS-clone [fs_UC5_SYS] (unmanaged): -+ * Stopped: [ gcdoubwap01 gcdoubwap02 ] -+ * Resource Group: grp_UC5_ascs (unmanaged): -+ * rsc_vip_int_ascs (ocf:heartbeat:IPaddr2): Stopped (unmanaged) -+ * rsc_vip_gcp_ascs (ocf:heartbeat:gcp-vpc-move-vip): Started gcdoubwap01 (unmanaged) -+ * fs_UC5_ascs (ocf:heartbeat:Filesystem): Stopped (unmanaged) -+ * rsc_sap_UC5_ASCS11 (ocf:heartbeat:SAPInstance): Stopped (unmanaged) -+ * Resource Group: grp_UC5_ers (unmanaged): -+ * rsc_vip_init_ers (ocf:heartbeat:IPaddr2): Stopped (unmanaged) -+ * rsc_vip_gcp_ers (ocf:heartbeat:gcp-vpc-move-vip): Stopped (unmanaged) -+ * fs_UC5_ers (ocf:heartbeat:Filesystem): Stopped (unmanaged) -+ * rsc_sap_UC5_ERS12 (ocf:heartbeat:SAPInstance): Stopped (unmanaged) -diff --git a/cts/scheduler/xml/probe-pending-node.xml b/cts/scheduler/xml/probe-pending-node.xml -new file mode 100644 -index 0000000..9f55c92 ---- /dev/null -+++ b/cts/scheduler/xml/probe-pending-node.xml -@@ -0,0 +1,247 @@ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ --- -1.8.3.1 - diff --git a/SOURCES/011-crm_attribute-regression.patch b/SOURCES/011-crm_attribute-regression.patch deleted file mode 100644 index 7263313..0000000 --- a/SOURCES/011-crm_attribute-regression.patch +++ /dev/null @@ -1,150 +0,0 @@ -From ea5510dd979bb6d375324cda26925d9e7c4362f5 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 19 Jul 2021 10:04:16 -0400 -Subject: [PATCH 1/2] Low: tools: The --get-value option does not require an - arg. - -Regression in 2.1.0 introduced by 15f5c2901. ---- - tools/crm_attribute.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c -index 2cc8d26..8a5b4e4 100644 ---- a/tools/crm_attribute.c -+++ b/tools/crm_attribute.c -@@ -242,7 +242,7 @@ static GOptionEntry deprecated_entries[] = { - NULL, NULL - }, - -- { "get-value", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, value_cb, -+ { "get-value", 0, G_OPTION_FLAG_HIDDEN|G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, value_cb, - NULL, NULL - }, - --- -1.8.3.1 - - -From ef054d943afe8e60017f6adc4e25f88a59ac91a4 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Mon, 19 Jul 2021 11:37:04 -0400 -Subject: [PATCH 2/2] Low: libcrmcommon: Allow negative numbers as cmdline - options. - -The bug here is that negative numbers (for instance, negative scores) -are not supported as command line arguments. Because we break up a -string that starts with a single dash into multiple arguments, "-1000" -becomes "-1", "-0", "-0", and "-0". - -Because we don't have enough information about what is happening on the -command line, the best we can do here is recognize something as a -negative number and pass it on. Any errors will have to be detected at -a later step. - -Also note that we only recognize negative numbers if they start with -1-9. Starting with 0 will be recognized as some sort of string. - -Regression in 2.1.0 caused by a long-standing bug in -pcmk__cmdline_preproc_test. ---- - lib/common/cmdline.c | 29 ++++++++++++++++++++++ - .../tests/cmdline/pcmk__cmdline_preproc_test.c | 24 +++++++++++++++++- - 2 files changed, 52 insertions(+), 1 deletion(-) - -diff --git a/lib/common/cmdline.c b/lib/common/cmdline.c -index 7c95d02..9c1b810 100644 ---- a/lib/common/cmdline.c -+++ b/lib/common/cmdline.c -@@ -9,6 +9,7 @@ - - #include - -+#include - #include - - #include -@@ -189,6 +190,34 @@ pcmk__cmdline_preproc(char **argv, const char *special) { - /* Skip over leading dash */ - char *ch = argv[i]+1; - -+ /* This looks like the start of a number, which means it is a negative -+ * number. It's probably the argument to the preceeding option, but -+ * we can't know that here. Copy it over and let whatever handles -+ * arguments next figure it out. -+ */ -+ if (*ch != '\0' && *ch >= '1' && *ch <= '9') { -+ bool is_numeric = true; -+ -+ while (*ch != '\0') { -+ if (!isdigit(*ch)) { -+ is_numeric = false; -+ break; -+ } -+ -+ ch++; -+ } -+ -+ if (is_numeric) { -+ g_ptr_array_add(arr, g_strdup_printf("%s", argv[i])); -+ continue; -+ } else { -+ /* This argument wasn't entirely numeric. Reset ch to the -+ * beginning so we can process it one character at a time. -+ */ -+ ch = argv[i]+1; -+ } -+ } -+ - while (*ch != '\0') { - /* This is a special short argument that takes an option. getopt - * allows values to be interspersed with a list of arguments, but -diff --git a/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c b/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c -index b8506c6..9a752ef 100644 ---- a/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c -+++ b/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c -@@ -1,5 +1,5 @@ - /* -- * Copyright 2020 the Pacemaker project contributors -+ * Copyright 2020-2021 the Pacemaker project contributors - * - * The version control history for this file may have further details. - * -@@ -86,6 +86,26 @@ long_arg(void) { - g_strfreev(processed); - } - -+static void -+negative_score(void) { -+ const char *argv[] = { "-v", "-1000", NULL }; -+ const gchar *expected[] = { "-v", "-1000", NULL }; -+ -+ gchar **processed = pcmk__cmdline_preproc((char **) argv, "v"); -+ LISTS_EQ(processed, expected); -+ g_strfreev(processed); -+} -+ -+static void -+negative_score_2(void) { -+ const char *argv[] = { "-1i3", NULL }; -+ const gchar *expected[] = { "-1", "-i", "-3", NULL }; -+ -+ gchar **processed = pcmk__cmdline_preproc((char **) argv, NULL); -+ LISTS_EQ(processed, expected); -+ g_strfreev(processed); -+} -+ - int - main(int argc, char **argv) - { -@@ -98,5 +118,7 @@ main(int argc, char **argv) - g_test_add_func("/common/cmdline/preproc/special_args", special_args); - g_test_add_func("/common/cmdline/preproc/special_arg_at_end", special_arg_at_end); - g_test_add_func("/common/cmdline/preproc/long_arg", long_arg); -+ g_test_add_func("/common/cmdline/preproc/negative_score", negative_score); -+ g_test_add_func("/common/cmdline/preproc/negative_score_2", negative_score_2); - return g_test_run(); - } --- -1.8.3.1 - diff --git a/SOURCES/012-string-arguments.patch b/SOURCES/012-string-arguments.patch deleted file mode 100644 index 6419117..0000000 --- a/SOURCES/012-string-arguments.patch +++ /dev/null @@ -1,221 +0,0 @@ -From 2eee93e8f9ea2daa81769bc69843d63ced1a7112 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Tue, 20 Jul 2021 16:39:07 -0400 -Subject: [PATCH 1/2] Low: tools: Audit command line options. - -This just goes through and makes sure the command line options that take -arguments are in the special parameter to pcmk__cmdline_preproc, and -that options that do not take arguments are not. ---- - tools/crm_attribute.c | 2 +- - tools/crm_error.c | 2 +- - tools/crm_resource.c | 2 +- - tools/crm_rule.c | 2 +- - tools/crm_simulate.c | 2 +- - tools/crmadmin.c | 2 +- - tools/stonith_admin.c | 2 +- - 7 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/tools/crm_attribute.c b/tools/crm_attribute.c -index 8a5b4e4..6bd4e2a 100644 ---- a/tools/crm_attribute.c -+++ b/tools/crm_attribute.c -@@ -312,7 +312,7 @@ main(int argc, char **argv) - - GOptionGroup *output_group = NULL; - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); -- gchar **processed_args = pcmk__cmdline_preproc(argv, "DGNPdilnpstv"); -+ gchar **processed_args = pcmk__cmdline_preproc(argv, "NPUdilnpstv"); - GOptionContext *context = build_arg_context(args, &output_group); - - if (!g_option_context_parse_strv(context, &processed_args, &error)) { -diff --git a/tools/crm_error.c b/tools/crm_error.c -index b4328ce..923f393 100644 ---- a/tools/crm_error.c -+++ b/tools/crm_error.c -@@ -79,7 +79,7 @@ main(int argc, char **argv) - - GOptionGroup *output_group = NULL; - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); -- gchar **processed_args = pcmk__cmdline_preproc(argv, "lrnX"); -+ gchar **processed_args = pcmk__cmdline_preproc(argv, NULL); - GOptionContext *context = build_arg_context(args, &output_group); - - if (!g_option_context_parse_strv(context, &processed_args, &error)) { -diff --git a/tools/crm_resource.c b/tools/crm_resource.c -index fa7902c..d8e140f 100644 ---- a/tools/crm_resource.c -+++ b/tools/crm_resource.c -@@ -1530,7 +1530,7 @@ main(int argc, char **argv) - */ - - args = pcmk__new_common_args(SUMMARY); -- processed_args = pcmk__cmdline_preproc(argv, "GINSTdginpstuv"); -+ processed_args = pcmk__cmdline_preproc(argv, "GHINSTdginpstuvx"); - context = build_arg_context(args, &output_group); - - pcmk__register_formats(output_group, formats); -diff --git a/tools/crm_rule.c b/tools/crm_rule.c -index 8b19bcd..30c5155 100644 ---- a/tools/crm_rule.c -+++ b/tools/crm_rule.c -@@ -239,7 +239,7 @@ main(int argc, char **argv) - - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); - GOptionContext *context = build_arg_context(args); -- gchar **processed_args = pcmk__cmdline_preproc(argv, "nopNO"); -+ gchar **processed_args = pcmk__cmdline_preproc(argv, "drX"); - - if (!g_option_context_parse_strv(context, &processed_args, &error)) { - exit_code = CRM_EX_USAGE; -diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c -index 0406bff..c83b1b1 100644 ---- a/tools/crm_simulate.c -+++ b/tools/crm_simulate.c -@@ -865,7 +865,7 @@ main(int argc, char **argv) - - GOptionGroup *output_group = NULL; - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); -- gchar **processed_args = pcmk__cmdline_preproc(argv, "bdefgiqrtuwxDFGINO"); -+ gchar **processed_args = pcmk__cmdline_preproc(argv, "bdefgiqrtuwxDFGINOP"); - GOptionContext *context = build_arg_context(args, &output_group); - - /* This must come before g_option_context_parse_strv. */ -diff --git a/tools/crmadmin.c b/tools/crmadmin.c -index 5cbde1b..b98f282 100644 ---- a/tools/crmadmin.c -+++ b/tools/crmadmin.c -@@ -188,7 +188,7 @@ main(int argc, char **argv) - - GOptionGroup *output_group = NULL; - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); -- gchar **processed_args = pcmk__cmdline_preproc(argv, "itBDEHKNPS"); -+ gchar **processed_args = pcmk__cmdline_preproc(argv, "itKNS"); - GOptionContext *context = build_arg_context(args, &output_group); - - pcmk__register_formats(output_group, formats); -diff --git a/tools/stonith_admin.c b/tools/stonith_admin.c -index 6773cea..2d48326 100644 ---- a/tools/stonith_admin.c -+++ b/tools/stonith_admin.c -@@ -349,7 +349,7 @@ main(int argc, char **argv) - - GOptionGroup *output_group = NULL; - pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY); -- gchar **processed_args = pcmk__cmdline_preproc(argv, "adehilorstvBCDFHQRTU"); -+ gchar **processed_args = pcmk__cmdline_preproc(argv, "adehilorstvyBCDFHQRTU"); - GOptionContext *context = build_arg_context(args, &output_group); - - pcmk__register_formats(output_group, formats); --- -1.8.3.1 - - -From 8301678ad1162450814d2fea5288aefe47a67a74 Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Tue, 20 Jul 2021 16:40:58 -0400 -Subject: [PATCH 2/2] Low: libcrmcommon: Also allow string arguments that start - with a dash. - -There's various places where an option to a command line argument could -itself be a valid command line argument. For instance: - - crm_attribute -n crm_mon_options -v "-1i3" - -The previous patching to pcmk__cmdline_preproc did not take this into -account. With this patch, options that are last in a string (or by -themselves) and take an argument will have the next command line option -grabbed and copied straight through without processing. - -Regression in 2.1.0 caused by a long-standing bug in pcmk__cmdline_preproc. ---- - lib/common/cmdline.c | 8 ++++++ - .../tests/cmdline/pcmk__cmdline_preproc_test.c | 33 ++++++++++++++++++++++ - 2 files changed, 41 insertions(+) - -diff --git a/lib/common/cmdline.c b/lib/common/cmdline.c -index 9c1b810..1ca6147 100644 ---- a/lib/common/cmdline.c -+++ b/lib/common/cmdline.c -@@ -146,6 +146,7 @@ gchar ** - pcmk__cmdline_preproc(char **argv, const char *special) { - GPtrArray *arr = NULL; - bool saw_dash_dash = false; -+ bool copy_option = false; - - if (argv == NULL) { - return NULL; -@@ -175,6 +176,12 @@ pcmk__cmdline_preproc(char **argv, const char *special) { - continue; - } - -+ if (copy_option == true) { -+ g_ptr_array_add(arr, g_strdup(argv[i])); -+ copy_option = false; -+ continue; -+ } -+ - /* This is just a dash by itself. That could indicate stdin/stdout, or - * it could be user error. Copy it over and let glib figure it out. - */ -@@ -239,6 +246,7 @@ pcmk__cmdline_preproc(char **argv, const char *special) { - */ - } else { - g_ptr_array_add(arr, g_strdup_printf("-%c", *ch)); -+ copy_option = true; - ch++; - } - -diff --git a/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c b/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c -index 9a752ef..edc5640 100644 ---- a/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c -+++ b/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c -@@ -106,6 +106,36 @@ negative_score_2(void) { - g_strfreev(processed); - } - -+static void -+string_arg_with_dash(void) { -+ const char *argv[] = { "-n", "crm_mon_options", "-v", "--opt1 --opt2", NULL }; -+ const gchar *expected[] = { "-n", "crm_mon_options", "-v", "--opt1 --opt2", NULL }; -+ -+ gchar **processed = pcmk__cmdline_preproc((char **) argv, "v"); -+ LISTS_EQ(processed, expected); -+ g_strfreev(processed); -+} -+ -+static void -+string_arg_with_dash_2(void) { -+ const char *argv[] = { "-n", "crm_mon_options", "-v", "-1i3", NULL }; -+ const gchar *expected[] = { "-n", "crm_mon_options", "-v", "-1i3", NULL }; -+ -+ gchar **processed = pcmk__cmdline_preproc((char **) argv, "v"); -+ LISTS_EQ(processed, expected); -+ g_strfreev(processed); -+} -+ -+static void -+string_arg_with_dash_3(void) { -+ const char *argv[] = { "-abc", "-1i3", NULL }; -+ const gchar *expected[] = { "-a", "-b", "-c", "-1i3", NULL }; -+ -+ gchar **processed = pcmk__cmdline_preproc((char **) argv, "c"); -+ LISTS_EQ(processed, expected); -+ g_strfreev(processed); -+} -+ - int - main(int argc, char **argv) - { -@@ -120,5 +150,8 @@ main(int argc, char **argv) - g_test_add_func("/common/cmdline/preproc/long_arg", long_arg); - g_test_add_func("/common/cmdline/preproc/negative_score", negative_score); - g_test_add_func("/common/cmdline/preproc/negative_score_2", negative_score_2); -+ g_test_add_func("/common/cmdline/preproc/string_arg_with_dash", string_arg_with_dash); -+ g_test_add_func("/common/cmdline/preproc/string_arg_with_dash_2", string_arg_with_dash_2); -+ g_test_add_func("/common/cmdline/preproc/string_arg_with_dash_3", string_arg_with_dash_3); - return g_test_run(); - } --- -1.8.3.1 - diff --git a/SOURCES/013-leaks.patch b/SOURCES/013-leaks.patch deleted file mode 100644 index daa42b8..0000000 --- a/SOURCES/013-leaks.patch +++ /dev/null @@ -1,241 +0,0 @@ -From bee54eba4d9c28d3a7907a3e13a5deeee6bc0916 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Tue, 27 Jul 2021 11:01:04 -0500 -Subject: [PATCH 1/2] Low: tools: avoid (insignificant) memory leaks - -detected by valgrind ---- - lib/pacemaker/pcmk_cluster_queries.c | 2 ++ - tools/crm_diff.c | 2 +- - tools/crm_resource.c | 33 ++++++++++++++++++++------------- - tools/crm_resource_ban.c | 2 +- - 4 files changed, 24 insertions(+), 15 deletions(-) - -diff --git a/lib/pacemaker/pcmk_cluster_queries.c b/lib/pacemaker/pcmk_cluster_queries.c -index c68cf9d..46e5538 100644 ---- a/lib/pacemaker/pcmk_cluster_queries.c -+++ b/lib/pacemaker/pcmk_cluster_queries.c -@@ -440,6 +440,7 @@ pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT) - } - rc = the_cib->cmds->signon(the_cib, crm_system_name, cib_command); - if (rc != pcmk_ok) { -+ cib_delete(the_cib); - return pcmk_legacy2rc(rc); - } - -@@ -488,6 +489,7 @@ pcmk__list_nodes(pcmk__output_t *out, char *node_types, gboolean BASH_EXPORT) - free_xml(xml_node); - } - the_cib->cmds->signoff(the_cib); -+ cib_delete(the_cib); - return pcmk_legacy2rc(rc); - } - -diff --git a/tools/crm_diff.c b/tools/crm_diff.c -index b37f0ea..9890c10 100644 ---- a/tools/crm_diff.c -+++ b/tools/crm_diff.c -@@ -383,5 +383,5 @@ done: - free_xml(object_2); - - pcmk__output_and_clear_error(error, NULL); -- return exit_code; -+ crm_exit(exit_code); - } -diff --git a/tools/crm_resource.c b/tools/crm_resource.c -index d8e140f..8ca90cb 100644 ---- a/tools/crm_resource.c -+++ b/tools/crm_resource.c -@@ -1081,6 +1081,8 @@ clear_constraints(pcmk__output_t *out, xmlNodePtr *cib_xml_copy) - g_set_error(&error, PCMK__RC_ERROR, rc, - "Could not get modified CIB: %s\n", pcmk_strerror(rc)); - g_list_free(before); -+ free_xml(*cib_xml_copy); -+ *cib_xml_copy = NULL; - return rc; - } - -@@ -1232,29 +1234,34 @@ populate_working_set(xmlNodePtr *cib_xml_copy) - - if (options.xml_file != NULL) { - *cib_xml_copy = filename2xml(options.xml_file); -+ if (*cib_xml_copy == NULL) { -+ rc = pcmk_rc_cib_corrupt; -+ } - } else { - rc = cib_conn->cmds->query(cib_conn, NULL, cib_xml_copy, cib_scope_local | cib_sync_call); - rc = pcmk_legacy2rc(rc); - } - -- if(rc != pcmk_rc_ok) { -- return rc; -+ if (rc == pcmk_rc_ok) { -+ data_set = pe_new_working_set(); -+ if (data_set == NULL) { -+ rc = ENOMEM; -+ } else { -+ pe__set_working_set_flags(data_set, -+ pe_flag_no_counts|pe_flag_no_compat); -+ data_set->priv = out; -+ rc = update_working_set_xml(data_set, cib_xml_copy); -+ } - } - -- /* Populate the working set instance */ -- data_set = pe_new_working_set(); -- if (data_set == NULL) { -- rc = ENOMEM; -+ if (rc != pcmk_rc_ok) { -+ free_xml(*cib_xml_copy); -+ *cib_xml_copy = NULL; - return rc; - } - -- pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat); -- data_set->priv = out; -- rc = update_working_set_xml(data_set, cib_xml_copy); -- if (rc == pcmk_rc_ok) { -- cluster_status(data_set); -- } -- return rc; -+ cluster_status(data_set); -+ return pcmk_rc_ok; - } - - static int -diff --git a/tools/crm_resource_ban.c b/tools/crm_resource_ban.c -index a297d49..2c4f48d 100644 ---- a/tools/crm_resource_ban.c -+++ b/tools/crm_resource_ban.c -@@ -292,7 +292,7 @@ resource_clear_node_in_location(const char *rsc_id, const char *host, cib_t * ci - rc = pcmk_legacy2rc(rc); - } - -- free(fragment); -+ free_xml(fragment); - return rc; - } - --- -1.8.3.1 - - -From a30ff4a87f291a0c9e03c4efb9c9046d2ac594f1 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Tue, 27 Jul 2021 11:26:59 -0500 -Subject: [PATCH 2/2] Fix: tools: avoid memory leaks in crm_mon - -could be significant in an interactive session - -regressions introduced in 2.0.4 and 2.0.5 ---- - lib/pengine/bundle.c | 3 ++- - lib/pengine/clone.c | 5 ++--- - lib/pengine/pe_output.c | 3 +++ - 3 files changed, 7 insertions(+), 4 deletions(-) - -diff --git a/lib/pengine/bundle.c b/lib/pengine/bundle.c -index 6ba786a..7e1d428 100644 ---- a/lib/pengine/bundle.c -+++ b/lib/pengine/bundle.c -@@ -1497,7 +1497,7 @@ pe__bundle_xml(pcmk__output_t *out, va_list args) - for (GList *gIter = bundle_data->replicas; gIter != NULL; - gIter = gIter->next) { - pe__bundle_replica_t *replica = gIter->data; -- char *id = pcmk__itoa(replica->offset); -+ char *id = NULL; - gboolean print_ip, print_child, print_ctnr, print_remote; - - CRM_ASSERT(replica); -@@ -1531,6 +1531,7 @@ pe__bundle_xml(pcmk__output_t *out, va_list args) - CRM_ASSERT(rc == pcmk_rc_ok); - } - -+ id = pcmk__itoa(replica->offset); - rc = pe__name_and_nvpairs_xml(out, true, "replica", 1, "id", id); - free(id); - CRM_ASSERT(rc == pcmk_rc_ok); -diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c -index 6323692..ab91fd1 100644 ---- a/lib/pengine/clone.c -+++ b/lib/pengine/clone.c -@@ -807,10 +807,10 @@ pe__clone_html(pcmk__output_t *out, va_list args) - pcmk__add_word(&list_text, &list_text_len, host->details->uname); - active_instances++; - } -+ g_list_free(promoted_list); - - if (list_text != NULL) { - out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]", list_text); -- g_list_free(promoted_list); - free(list_text); - list_text = NULL; - list_text_len = 0; -@@ -828,6 +828,7 @@ pe__clone_html(pcmk__output_t *out, va_list args) - pcmk__add_word(&list_text, &list_text_len, host->details->uname); - active_instances++; - } -+ g_list_free(started_list); - - if (list_text != NULL) { - if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) { -@@ -847,7 +848,6 @@ pe__clone_html(pcmk__output_t *out, va_list args) - out->list_item(out, NULL, "Started: [ %s ]", list_text); - } - -- g_list_free(started_list); - free(list_text); - list_text = NULL; - list_text_len = 0; -@@ -1048,10 +1048,10 @@ pe__clone_text(pcmk__output_t *out, va_list args) - pcmk__add_word(&list_text, &list_text_len, host->details->uname); - active_instances++; - } -+ g_list_free(promoted_list); - - if (list_text != NULL) { - out->list_item(out, PROMOTED_INSTANCES, "[ %s ]", list_text); -- g_list_free(promoted_list); - free(list_text); - list_text = NULL; - list_text_len = 0; -@@ -1069,6 +1069,7 @@ pe__clone_text(pcmk__output_t *out, va_list args) - pcmk__add_word(&list_text, &list_text_len, host->details->uname); - active_instances++; - } -+ g_list_free(started_list); - - if (list_text != NULL) { - if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) { -@@ -1084,7 +1085,6 @@ pe__clone_text(pcmk__output_t *out, va_list args) - out->list_item(out, "Started", "[ %s ]", list_text); - } - -- g_list_free(started_list); - free(list_text); - list_text = NULL; - } -diff --git a/lib/pengine/pe_output.c b/lib/pengine/pe_output.c -index b8997c4..20bd1a9 100644 ---- a/lib/pengine/pe_output.c -+++ b/lib/pengine/pe_output.c -@@ -1410,6 +1410,8 @@ node_text(pcmk__output_t *out, va_list args) { - - out->end_list(out); - out->end_list(out); -+ -+ g_list_free(rscs); - } - - } else { -@@ -1739,6 +1741,7 @@ node_attribute_list(pcmk__output_t *out, va_list args) { - } - - if (!pcmk__str_in_list(only_node, node->details->uname)) { -+ g_list_free(attr_list); - continue; - } - --- -1.8.3.1 - diff --git a/SOURCES/014-str-list.patch b/SOURCES/014-str-list.patch deleted file mode 100644 index e6993ab..0000000 --- a/SOURCES/014-str-list.patch +++ /dev/null @@ -1,465 +0,0 @@ -From 45813df3eb4c8ad8b1744fa5dd56af86ad0fb3dd Mon Sep 17 00:00:00 2001 -From: Chris Lumens -Date: Thu, 17 Jun 2021 16:07:55 -0400 -Subject: [PATCH] Refactor: libs: pcmk__str_in_list should support pcmk__str_* - flags. - ---- - include/crm/common/strings_internal.h | 2 +- - lib/common/strings.c | 34 +++++++++++++++++++++++---- - lib/fencing/st_output.c | 10 ++++---- - lib/pengine/bundle.c | 8 +++---- - lib/pengine/clone.c | 28 +++++++++++----------- - lib/pengine/group.c | 18 +++++++------- - lib/pengine/native.c | 4 ++-- - lib/pengine/pe_output.c | 22 ++++++++--------- - lib/pengine/utils.c | 6 ++--- - 9 files changed, 79 insertions(+), 53 deletions(-) - -diff --git a/include/crm/common/strings_internal.h b/include/crm/common/strings_internal.h -index 94982cb4e..687079814 100644 ---- a/include/crm/common/strings_internal.h -+++ b/include/crm/common/strings_internal.h -@@ -117,7 +117,7 @@ pcmk__intkey_table_remove(GHashTable *hash_table, int key) - return g_hash_table_remove(hash_table, GINT_TO_POINTER(key)); - } - --gboolean pcmk__str_in_list(GList *lst, const gchar *s); -+gboolean pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags); - - bool pcmk__strcase_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED; - bool pcmk__str_any_of(const char *s, ...) G_GNUC_NULL_TERMINATED; -diff --git a/lib/common/strings.c b/lib/common/strings.c -index 3264db5b6..e1e98803b 100644 ---- a/lib/common/strings.c -+++ b/lib/common/strings.c -@@ -872,14 +872,30 @@ pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end) - * Search \p lst for \p s, taking case into account. As a special case, - * if "*" is the only element of \p lst, the search is successful. - * -- * \param[in] lst List to search -- * \param[in] s String to search for -+ * Behavior can be changed with various flags: -+ * -+ * - pcmk__str_casei - By default, comparisons are done taking case into -+ * account. This flag makes comparisons case-insensitive. -+ * - pcmk__str_null_matches - If the input string is NULL, return TRUE. -+ * -+ * \note The special "*" matching rule takes precedence over flags. In -+ * particular, "*" will match a NULL input string even without -+ * pcmk__str_null_matches being specified. -+ * -+ * \note No matter what input string or flags are provided, an empty -+ * list will always return FALSE. -+ * -+ * \param[in] lst List to search -+ * \param[in] s String to search for -+ * \param[in] flags A bitfield of pcmk__str_flags to modify operation - * - * \return \c TRUE if \p s is in \p lst, or \c FALSE otherwise - */ - gboolean --pcmk__str_in_list(GList *lst, const gchar *s) -+pcmk__str_in_list(GList *lst, const gchar *s, uint32_t flags) - { -+ GCompareFunc fn; -+ - if (lst == NULL) { - return FALSE; - } -@@ -888,7 +904,17 @@ pcmk__str_in_list(GList *lst, const gchar *s) - return TRUE; - } - -- return g_list_find_custom(lst, s, (GCompareFunc) strcmp) != NULL; -+ if (s == NULL) { -+ return pcmk_is_set(flags, pcmk__str_null_matches); -+ } -+ -+ if (pcmk_is_set(flags, pcmk__str_casei)) { -+ fn = (GCompareFunc) strcasecmp; -+ } else { -+ fn = (GCompareFunc) strcmp; -+ } -+ -+ return g_list_find_custom(lst, s, fn) != NULL; - } - - static bool -diff --git a/lib/fencing/st_output.c b/lib/fencing/st_output.c -index 568ae46a8..e1ae8ac87 100644 ---- a/lib/fencing/st_output.c -+++ b/lib/fencing/st_output.c -@@ -47,7 +47,7 @@ stonith__failed_history(pcmk__output_t *out, va_list args) { - continue; - } - -- if (!pcmk__str_in_list(only_node, hp->target)) { -+ if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_none)) { - continue; - } - -@@ -72,7 +72,7 @@ stonith__history(pcmk__output_t *out, va_list args) { - int rc = pcmk_rc_no_output; - - for (stonith_history_t *hp = history; hp; hp = hp->next) { -- if (!pcmk__str_in_list(only_node, hp->target)) { -+ if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_none)) { - continue; - } - -@@ -101,7 +101,7 @@ stonith__full_history(pcmk__output_t *out, va_list args) { - int rc = pcmk_rc_no_output; - - for (stonith_history_t *hp = history; hp; hp = hp->next) { -- if (!pcmk__str_in_list(only_node, hp->target)) { -+ if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_none)) { - continue; - } - -@@ -129,7 +129,7 @@ full_history_xml(pcmk__output_t *out, va_list args) { - - if (history_rc == 0) { - for (stonith_history_t *hp = history; hp; hp = hp->next) { -- if (!pcmk__str_in_list(only_node, hp->target)) { -+ if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_none)) { - continue; - } - -@@ -218,7 +218,7 @@ stonith__pending_actions(pcmk__output_t *out, va_list args) { - int rc = pcmk_rc_no_output; - - for (stonith_history_t *hp = history; hp; hp = hp->next) { -- if (!pcmk__str_in_list(only_node, hp->target)) { -+ if (!pcmk__str_in_list(only_node, hp->target, pcmk__str_none)) { - continue; - } - -diff --git a/lib/pengine/bundle.c b/lib/pengine/bundle.c -index 9237392e4..6ba786ae6 100644 ---- a/lib/pengine/bundle.c -+++ b/lib/pengine/bundle.c -@@ -1492,7 +1492,7 @@ pe__bundle_xml(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc->id); -+ print_everything = pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none); - - for (GList *gIter = bundle_data->replicas; gIter != NULL; - gIter = gIter->next) { -@@ -1614,7 +1614,7 @@ pe__bundle_html(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc->id); -+ print_everything = pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none); - - for (GList *gIter = bundle_data->replicas; gIter != NULL; - gIter = gIter->next) { -@@ -1742,7 +1742,7 @@ pe__bundle_text(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc->id); -+ print_everything = pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none); - - for (GList *gIter = bundle_data->replicas; gIter != NULL; - gIter = gIter->next) { -@@ -2044,7 +2044,7 @@ pe__bundle_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_paren - gboolean passes = FALSE; - pe__bundle_variant_data_t *bundle_data = NULL; - -- if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc))) { -+ if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none)) { - passes = TRUE; - } else { - get_bundle_variant_data(bundle_data, rsc); -diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c -index 5662338f3..5a6bfa61f 100644 ---- a/lib/pengine/clone.c -+++ b/lib/pengine/clone.c -@@ -624,8 +624,8 @@ pe__clone_xml(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)); -+ print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)); - - for (; gIter != NULL; gIter = gIter->next) { - pe_resource_t *child_rsc = (pe_resource_t *) gIter->data; -@@ -693,8 +693,8 @@ pe__clone_html(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)); -+ print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)); - - out->begin_list(out, NULL, NULL, "Clone Set: %s [%s]%s%s%s%s", - rsc->id, ID(clone_data->xml_obj_child), -@@ -801,7 +801,7 @@ pe__clone_html(pcmk__output_t *out, va_list args) - for (gIter = promoted_list; gIter; gIter = gIter->next) { - pe_node_t *host = gIter->data; - -- if (!pcmk__str_in_list(only_node, host->details->uname)) { -+ if (!pcmk__str_in_list(only_node, host->details->uname, pcmk__str_none)) { - continue; - } - -@@ -822,7 +822,7 @@ pe__clone_html(pcmk__output_t *out, va_list args) - for (gIter = started_list; gIter; gIter = gIter->next) { - pe_node_t *host = gIter->data; - -- if (!pcmk__str_in_list(only_node, host->details->uname)) { -+ if (!pcmk__str_in_list(only_node, host->details->uname, pcmk__str_none)) { - continue; - } - -@@ -884,7 +884,7 @@ pe__clone_html(pcmk__output_t *out, va_list args) - pe_node_t *node = (pe_node_t *)nIter->data; - - if (pe_find_node(rsc->running_on, node->details->uname) == NULL && -- pcmk__str_in_list(only_node, node->details->uname)) { -+ pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - pcmk__add_word(&stopped_list, &stopped_list_len, - node->details->uname); - } -@@ -933,8 +933,8 @@ pe__clone_text(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)); -+ print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)); - - out->begin_list(out, NULL, NULL, "Clone Set: %s [%s]%s%s%s%s", - rsc->id, ID(clone_data->xml_obj_child), -@@ -1041,7 +1041,7 @@ pe__clone_text(pcmk__output_t *out, va_list args) - for (gIter = promoted_list; gIter; gIter = gIter->next) { - pe_node_t *host = gIter->data; - -- if (!pcmk__str_in_list(only_node, host->details->uname)) { -+ if (!pcmk__str_in_list(only_node, host->details->uname, pcmk__str_none)) { - continue; - } - -@@ -1062,7 +1062,7 @@ pe__clone_text(pcmk__output_t *out, va_list args) - for (gIter = started_list; gIter; gIter = gIter->next) { - pe_node_t *host = gIter->data; - -- if (!pcmk__str_in_list(only_node, host->details->uname)) { -+ if (!pcmk__str_in_list(only_node, host->details->uname, pcmk__str_none)) { - continue; - } - -@@ -1120,7 +1120,7 @@ pe__clone_text(pcmk__output_t *out, va_list args) - pe_node_t *node = (pe_node_t *)nIter->data; - - if (pe_find_node(rsc->running_on, node->details->uname) == NULL && -- pcmk__str_in_list(only_node, node->details->uname)) { -+ pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - pcmk__add_word(&stopped_list, &stopped_list_len, - node->details->uname); - } -@@ -1220,11 +1220,11 @@ pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent - gboolean passes = FALSE; - clone_variant_data_t *clone_data = NULL; - -- if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc))) { -+ if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none)) { - passes = TRUE; - } else { - get_clone_variant_data(clone_data, rsc); -- passes = pcmk__str_in_list(only_rsc, ID(clone_data->xml_obj_child)); -+ passes = pcmk__str_in_list(only_rsc, ID(clone_data->xml_obj_child), pcmk__str_none); - - if (!passes) { - for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) { -diff --git a/lib/pengine/group.c b/lib/pengine/group.c -index 23a72cff7..5f9aa83ce 100644 ---- a/lib/pengine/group.c -+++ b/lib/pengine/group.c -@@ -201,8 +201,8 @@ pe__group_xml(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)); -+ print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)); - - for (; gIter != NULL; gIter = gIter->next) { - pe_resource_t *child_rsc = (pe_resource_t *) gIter->data; -@@ -248,8 +248,8 @@ pe__group_html(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)); -+ print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)); - - if (options & pe_print_brief) { - GList *rscs = pe__filter_rsc_list(rsc->children, only_rsc); -@@ -303,8 +303,8 @@ pe__group_text(pcmk__output_t *out, va_list args) - return rc; - } - -- print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)); -+ print_everything = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)); - - if (options & pe_print_brief) { - GList *rscs = pe__filter_rsc_list(rsc->children, only_rsc); -@@ -387,11 +387,11 @@ pe__group_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent - { - gboolean passes = FALSE; - -- if (check_parent && pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(rsc)))) { -+ if (check_parent && pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(rsc)), pcmk__str_none)) { - passes = TRUE; -- } else if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc))) { -+ } else if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none)) { - passes = TRUE; -- } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id)) { -+ } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)) { - passes = TRUE; - } else { - for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) { -diff --git a/lib/pengine/native.c b/lib/pengine/native.c -index c2333d0d2..56054fc4a 100644 ---- a/lib/pengine/native.c -+++ b/lib/pengine/native.c -@@ -1338,8 +1338,8 @@ pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, unsigned int show_op - gboolean - pe__native_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent) - { -- if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) || -- pcmk__str_in_list(only_rsc, rsc->id)) { -+ if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) || -+ pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)) { - return FALSE; - } else if (check_parent) { - pe_resource_t *up = uber_parent(rsc); -diff --git a/lib/pengine/pe_output.c b/lib/pengine/pe_output.c -index 727475735..a6dc4ade8 100644 ---- a/lib/pengine/pe_output.c -+++ b/lib/pengine/pe_output.c -@@ -670,8 +670,8 @@ ban_list(pcmk__output_t *out, va_list args) { - continue; - } - -- if (!pcmk__str_in_list(only_rsc, rsc_printable_id(location->rsc_lh)) && -- !pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(location->rsc_lh)))) { -+ if (!pcmk__str_in_list(only_rsc, rsc_printable_id(location->rsc_lh), pcmk__str_none) && -+ !pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(location->rsc_lh)), pcmk__str_none)) { - continue; - } - -@@ -1254,7 +1254,7 @@ failed_action_list(pcmk__output_t *out, va_list args) { - xml_op = pcmk__xml_next(xml_op)) { - char *rsc = NULL; - -- if (!pcmk__str_in_list(only_node, crm_element_value(xml_op, XML_ATTR_UNAME))) { -+ if (!pcmk__str_in_list(only_node, crm_element_value(xml_op, XML_ATTR_UNAME), pcmk__str_none)) { - continue; - } - -@@ -1263,7 +1263,7 @@ failed_action_list(pcmk__output_t *out, va_list args) { - continue; - } - -- if (!pcmk__str_in_list(only_rsc, rsc)) { -+ if (!pcmk__str_in_list(only_rsc, rsc, pcmk__str_none)) { - free(rsc); - continue; - } -@@ -1738,7 +1738,7 @@ node_attribute_list(pcmk__output_t *out, va_list args) { - continue; - } - -- if (!pcmk__str_in_list(only_node, node->details->uname)) { -+ if (!pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - g_list_free(attr_list); - continue; - } -@@ -1835,8 +1835,8 @@ node_history_list(pcmk__output_t *out, va_list args) { - * For other resource types, is_filtered is okay. - */ - if (uber_parent(rsc)->variant == pe_group) { -- if (!pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) && -- !pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(rsc)))) { -+ if (!pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) && -+ !pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(rsc)), pcmk__str_none)) { - continue; - } - } else { -@@ -1899,7 +1899,7 @@ node_list_html(pcmk__output_t *out, va_list args) { - for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) { - pe_node_t *node = (pe_node_t *) gIter->data; - -- if (!pcmk__str_in_list(only_node, node->details->uname)) { -+ if (!pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - continue; - } - -@@ -1940,7 +1940,7 @@ pe__node_list_text(pcmk__output_t *out, va_list args) { - const char *node_mode = NULL; - char *node_name = pe__node_display_name(node, print_clone_detail); - -- if (!pcmk__str_in_list(only_node, node->details->uname)) { -+ if (!pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - free(node_name); - continue; - } -@@ -2059,7 +2059,7 @@ node_list_xml(pcmk__output_t *out, va_list args) { - for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) { - pe_node_t *node = (pe_node_t *) gIter->data; - -- if (!pcmk__str_in_list(only_node, node->details->uname)) { -+ if (!pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - continue; - } - -@@ -2097,7 +2097,7 @@ node_summary(pcmk__output_t *out, va_list args) { - continue; - } - -- if (!pcmk__str_in_list(only_node, node->details->uname)) { -+ if (!pcmk__str_in_list(only_node, node->details->uname, pcmk__str_none)) { - continue; - } - -diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c -index 450d8348c..d1be9e4ca 100644 ---- a/lib/pengine/utils.c -+++ b/lib/pengine/utils.c -@@ -2394,7 +2394,7 @@ pe__rsc_running_on_any_node_in_list(pe_resource_t *rsc, GList *node_list) - { - for (GList *ele = rsc->running_on; ele; ele = ele->next) { - pe_node_t *node = (pe_node_t *) ele->data; -- if (pcmk__str_in_list(node_list, node->details->uname)) { -+ if (pcmk__str_in_list(node_list, node->details->uname, pcmk__str_none)) { - return true; - } - } -@@ -2419,8 +2419,8 @@ pe__filter_rsc_list(GList *rscs, GList *filter) - /* I think the second condition is safe here for all callers of this - * function. If not, it needs to move into pe__node_text. - */ -- if (pcmk__str_in_list(filter, rsc_printable_id(rsc)) || -- (rsc->parent && pcmk__str_in_list(filter, rsc_printable_id(rsc->parent)))) { -+ if (pcmk__str_in_list(filter, rsc_printable_id(rsc), pcmk__str_none) || -+ (rsc->parent && pcmk__str_in_list(filter, rsc_printable_id(rsc->parent), pcmk__str_none))) { - retval = g_list_prepend(retval, rsc); - } - } --- -2.27.0 - diff --git a/SOURCES/015-sbd.patch b/SOURCES/015-sbd.patch deleted file mode 100644 index 9f47c35..0000000 --- a/SOURCES/015-sbd.patch +++ /dev/null @@ -1,1312 +0,0 @@ -From b49f49576ef9d801a48ce7a01a78c72e65be7880 Mon Sep 17 00:00:00 2001 -From: Klaus Wenninger -Date: Fri, 30 Jul 2021 18:07:25 +0200 -Subject: [PATCH 1/3] Fix, Refactor: fenced: add return value to - get_agent_metadata - -Used to distinguish between empty metadata per design, -case of failed getting metadata that might succeed on a -retry and fatal failure. -Fixes as well regression that leads to endless retries getting -metadata for #watchdog - not superserious as it happens with -delays in between but still undesirable. ---- - daemons/fenced/fenced_commands.c | 92 +++++++++++++++++++------------- - 1 file changed, 55 insertions(+), 37 deletions(-) - -diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c -index a778801b1..cd9968f1a 100644 ---- a/daemons/fenced/fenced_commands.c -+++ b/daemons/fenced/fenced_commands.c -@@ -69,7 +69,7 @@ static void stonith_send_reply(xmlNode * reply, int call_options, const char *re - static void search_devices_record_result(struct device_search_s *search, const char *device, - gboolean can_fence); - --static xmlNode * get_agent_metadata(const char *agent); -+static int get_agent_metadata(const char *agent, xmlNode **metadata); - static void read_action_metadata(stonith_device_t *device); - - typedef struct async_command_s { -@@ -323,19 +323,26 @@ fork_cb(GPid pid, gpointer user_data) - static int - get_agent_metadata_cb(gpointer data) { - stonith_device_t *device = data; -+ guint period_ms; - -- device->agent_metadata = get_agent_metadata(device->agent); -- if (device->agent_metadata) { -- read_action_metadata(device); -- stonith__device_parameter_flags(&(device->flags), device->id, -+ switch (get_agent_metadata(device->agent, &device->agent_metadata)) { -+ case pcmk_rc_ok: -+ if (device->agent_metadata) { -+ read_action_metadata(device); -+ stonith__device_parameter_flags(&(device->flags), device->id, - device->agent_metadata); -- return G_SOURCE_REMOVE; -- } else { -- guint period_ms = pcmk__mainloop_timer_get_period(device->timer); -- if (period_ms < 160 * 1000) { -- mainloop_timer_set_period(device->timer, 2 * period_ms); -- } -- return G_SOURCE_CONTINUE; -+ } -+ return G_SOURCE_REMOVE; -+ -+ case EAGAIN: -+ period_ms = pcmk__mainloop_timer_get_period(device->timer); -+ if (period_ms < 160 * 1000) { -+ mainloop_timer_set_period(device->timer, 2 * period_ms); -+ } -+ return G_SOURCE_CONTINUE; -+ -+ default: -+ return G_SOURCE_REMOVE; - } - } - -@@ -700,38 +707,41 @@ init_metadata_cache(void) { - } - } - --static xmlNode * --get_agent_metadata(const char *agent) -+int -+get_agent_metadata(const char *agent, xmlNode ** metadata) - { -- xmlNode *xml = NULL; - char *buffer = NULL; - -+ if (metadata == NULL) { -+ return EINVAL; -+ } -+ *metadata = NULL; -+ if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT, pcmk__str_none)) { -+ return pcmk_rc_ok; -+ } - init_metadata_cache(); - buffer = g_hash_table_lookup(metadata_cache, agent); -- if(pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT, pcmk__str_casei)) { -- return NULL; -- -- } else if(buffer == NULL) { -+ if (buffer == NULL) { - stonith_t *st = stonith_api_new(); - int rc; - - if (st == NULL) { - crm_warn("Could not get agent meta-data: " - "API memory allocation failed"); -- return NULL; -+ return EAGAIN; - } -- rc = st->cmds->metadata(st, st_opt_sync_call, agent, NULL, &buffer, 10); -+ rc = st->cmds->metadata(st, st_opt_sync_call, agent, -+ NULL, &buffer, 10); - stonith_api_delete(st); - if (rc || !buffer) { - crm_err("Could not retrieve metadata for fencing agent %s", agent); -- return NULL; -+ return EAGAIN; - } - g_hash_table_replace(metadata_cache, strdup(agent), buffer); - } - -- xml = string2xml(buffer); -- -- return xml; -+ *metadata = string2xml(buffer); -+ return pcmk_rc_ok; - } - - static gboolean -@@ -962,19 +972,27 @@ build_device_from_xml(xmlNode * msg) - g_list_free_full(device->targets, free); - device->targets = NULL; - } -- device->agent_metadata = get_agent_metadata(device->agent); -- if (device->agent_metadata) { -- read_action_metadata(device); -- stonith__device_parameter_flags(&(device->flags), device->id, -- device->agent_metadata); -- } else { -- if (device->timer == NULL) { -- device->timer = mainloop_timer_add("get_agent_metadata", 10 * 1000, -+ switch (get_agent_metadata(device->agent, &device->agent_metadata)) { -+ case pcmk_rc_ok: -+ if (device->agent_metadata) { -+ read_action_metadata(device); -+ stonith__device_parameter_flags(&(device->flags), device->id, -+ device->agent_metadata); -+ } -+ break; -+ -+ case EAGAIN: -+ if (device->timer == NULL) { -+ device->timer = mainloop_timer_add("get_agent_metadata", 10 * 1000, - TRUE, get_agent_metadata_cb, device); -- } -- if (!mainloop_timer_running(device->timer)) { -- mainloop_timer_start(device->timer); -- } -+ } -+ if (!mainloop_timer_running(device->timer)) { -+ mainloop_timer_start(device->timer); -+ } -+ break; -+ -+ default: -+ break; - } - - value = g_hash_table_lookup(device->params, "nodeid"); --- -2.27.0 - - -From 5dd1e4459335764e0adf5fa78d81c875ae2332e9 Mon Sep 17 00:00:00 2001 -From: Klaus Wenninger -Date: Fri, 30 Jul 2021 18:15:10 +0200 -Subject: [PATCH 2/3] feature: watchdog-fencing: allow restriction to certain - nodes - -Bump CRM_FEATURE_SET to 3.11.0 to encourage cluster being -fully upgraded to a version that supports the feature -before explicitly adding a watchdog-fence-device. ---- - configure.ac | 1 + - daemons/controld/controld_control.c | 2 +- - daemons/controld/controld_fencing.c | 14 ++ - daemons/controld/controld_fencing.h | 1 + - daemons/fenced/Makefile.am | 2 +- - daemons/fenced/fence_watchdog.in | 283 ++++++++++++++++++++++++++++ - daemons/fenced/fenced_commands.c | 141 +++++++++++--- - daemons/fenced/fenced_remote.c | 71 ++++--- - daemons/fenced/pacemaker-fenced.c | 131 +++++++++---- - daemons/fenced/pacemaker-fenced.h | 5 +- - include/crm/crm.h | 2 +- - include/crm/fencing/internal.h | 8 +- - lib/fencing/st_client.c | 61 ++++++ - lib/lrmd/lrmd_client.c | 6 +- - rpm/pacemaker.spec.in | 3 + - 16 files changed, 635 insertions(+), 97 deletions(-) - create mode 100755 daemons/fenced/fence_watchdog.in - -diff --git a/configure.ac b/configure.ac -index 436100c81..013562e46 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -1972,6 +1972,7 @@ CONFIG_FILES_EXEC([cts/cts-cli], - [cts/support/fence_dummy], - [cts/support/pacemaker-cts-dummyd], - [daemons/fenced/fence_legacy], -+ [daemons/fenced/fence_watchdog], - [doc/abi-check], - [extra/resources/ClusterMon], - [extra/resources/HealthSMART], -diff --git a/daemons/controld/controld_control.c b/daemons/controld/controld_control.c -index 45a70bb92..b5da6a46c 100644 ---- a/daemons/controld/controld_control.c -+++ b/daemons/controld/controld_control.c -@@ -615,7 +615,7 @@ static pcmk__cluster_option_t crmd_opts[] = { - }, - { - "stonith-watchdog-timeout", NULL, "time", NULL, -- "0", pcmk__valid_sbd_timeout, -+ "0", controld_verify_stonith_watchdog_timeout, - "How long to wait before we can assume nodes are safely down " - "when watchdog-based self-fencing via SBD is in use", - "If nonzero, along with `have-watchdog=true` automatically set by the " -diff --git a/daemons/controld/controld_fencing.c b/daemons/controld/controld_fencing.c -index 0fba6613b..6c2a6c550 100644 ---- a/daemons/controld/controld_fencing.c -+++ b/daemons/controld/controld_fencing.c -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -886,6 +887,19 @@ te_fence_node(crm_graph_t *graph, crm_action_t *action) - return TRUE; - } - -+bool -+controld_verify_stonith_watchdog_timeout(const char *value) -+{ -+ gboolean rv = TRUE; -+ -+ if (stonith_api && (stonith_api->state != stonith_disconnected) && -+ stonith__watchdog_fencing_enabled_for_node_api(stonith_api, -+ fsa_our_uname)) { -+ rv = pcmk__valid_sbd_timeout(value); -+ } -+ return rv; -+} -+ - /* end stonith API client functions */ - - -diff --git a/daemons/controld/controld_fencing.h b/daemons/controld/controld_fencing.h -index d0ecc8234..ef68a0c83 100644 ---- a/daemons/controld/controld_fencing.h -+++ b/daemons/controld/controld_fencing.h -@@ -24,6 +24,7 @@ void update_stonith_max_attempts(const char* value); - void controld_trigger_fencer_connect(void); - void controld_disconnect_fencer(bool destroy); - gboolean te_fence_node(crm_graph_t *graph, crm_action_t *action); -+bool controld_verify_stonith_watchdog_timeout(const char *value); - - // stonith cleanup list - void add_stonith_cleanup(const char *target); -diff --git a/daemons/fenced/Makefile.am b/daemons/fenced/Makefile.am -index 43413e11d..2923d7c9b 100644 ---- a/daemons/fenced/Makefile.am -+++ b/daemons/fenced/Makefile.am -@@ -15,7 +15,7 @@ halibdir = $(CRM_DAEMON_DIR) - - halib_PROGRAMS = pacemaker-fenced cts-fence-helper - --sbin_SCRIPTS = fence_legacy -+sbin_SCRIPTS = fence_legacy fence_watchdog - - noinst_HEADERS = pacemaker-fenced.h - -diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in -new file mode 100755 -index 000000000..c83304f1d ---- /dev/null -+++ b/daemons/fenced/fence_watchdog.in -@@ -0,0 +1,283 @@ -+#!@PYTHON@ -+"""Dummy watchdog fence agent for providing meta-data for the pacemaker internal agent -+""" -+ -+__copyright__ = "Copyright 2012-2021 the Pacemaker project contributors" -+__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY" -+ -+import io -+import os -+import re -+import sys -+import atexit -+import getopt -+ -+SHORT_DESC = "Dummy watchdog fence agent" -+LONG_DESC = """fence_watchdog just provides -+meta-data - actual fencing is done by the pacemaker internal watchdog agent.""" -+ -+ALL_OPT = { -+ "version" : { -+ "getopt" : "V", -+ "longopt" : "version", -+ "help" : "-V, --version Display version information and exit", -+ "required" : "0", -+ "shortdesc" : "Display version information and exit", -+ "order" : 53 -+ }, -+ "help" : { -+ "getopt" : "h", -+ "longopt" : "help", -+ "help" : "-h, --help Display this help and exit", -+ "required" : "0", -+ "shortdesc" : "Display help and exit", -+ "order" : 54 -+ }, -+ "action" : { -+ "getopt" : "o:", -+ "longopt" : "action", -+ "help" : "-o, --action=[action] Action: metadata", -+ "required" : "1", -+ "shortdesc" : "Fencing Action", -+ "default" : "metadata", -+ "order" : 1 -+ }, -+ "nodename" : { -+ "getopt" : "N:", -+ "longopt" : "nodename", -+ "help" : "-N, --nodename Node name of fence victim (ignored)", -+ "required" : "0", -+ "shortdesc" : "Ignored", -+ "order" : 2 -+ }, -+ "plug" : { -+ "getopt" : "n:", -+ "longopt" : "plug", -+ "help" : "-n, --plug=[id] Physical plug number on device (ignored)", -+ "required" : "1", -+ "shortdesc" : "Ignored", -+ "order" : 4 -+ } -+} -+ -+ -+def agent(): -+ """ Return name this file was run as. """ -+ -+ return os.path.basename(sys.argv[0]) -+ -+ -+def fail_usage(message): -+ """ Print a usage message and exit. """ -+ -+ sys.exit("%s\nPlease use '-h' for usage" % message) -+ -+ -+def show_docs(options): -+ """ Handle informational options (display info and exit). """ -+ -+ device_opt = options["device_opt"] -+ -+ if "-h" in options: -+ usage(device_opt) -+ sys.exit(0) -+ -+ if "-o" in options and options["-o"].lower() == "metadata": -+ metadata(device_opt, options) -+ sys.exit(0) -+ -+ if "-V" in options: -+ print(AGENT_VERSION) -+ sys.exit(0) -+ -+ -+def sorted_options(avail_opt): -+ """ Return a list of all options, in their internally specified order. """ -+ -+ sorted_list = [(key, ALL_OPT[key]) for key in avail_opt] -+ sorted_list.sort(key=lambda x: x[1]["order"]) -+ return sorted_list -+ -+ -+def usage(avail_opt): -+ """ Print a usage message. """ -+ print(LONG_DESC) -+ print() -+ print("Usage:") -+ print("\t" + agent() + " [options]") -+ print("Options:") -+ -+ for dummy, value in sorted_options(avail_opt): -+ if len(value["help"]) != 0: -+ print(" " + value["help"]) -+ -+ -+def metadata(avail_opt, options): -+ """ Print agent metadata. """ -+ -+ print(""" -+ -+%s -+""" % (agent(), SHORT_DESC, LONG_DESC)) -+ -+ for option, dummy in sorted_options(avail_opt): -+ if "shortdesc" in ALL_OPT[option]: -+ print(' ') -+ -+ default = "" -+ default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1] -+ default_name_no_arg = "-" + ALL_OPT[option]["getopt"] -+ -+ if "default" in ALL_OPT[option]: -+ default = 'default="%s"' % str(ALL_OPT[option]["default"]) -+ elif default_name_arg in options: -+ if options[default_name_arg]: -+ try: -+ default = 'default="%s"' % options[default_name_arg] -+ except TypeError: -+ ## @todo/@note: Currently there is no clean way how to handle lists -+ ## we can create a string from it but we can't set it on command line -+ default = 'default="%s"' % str(options[default_name_arg]) -+ elif default_name_no_arg in options: -+ default = 'default="true"' -+ -+ mixed = ALL_OPT[option]["help"] -+ ## split it between option and help text -+ res = re.compile(r"^(.*--\S+)\s+", re.IGNORECASE | re.S).search(mixed) -+ if None != res: -+ mixed = res.group(1) -+ mixed = mixed.replace("<", "<").replace(">", ">") -+ print(' ') -+ -+ if ALL_OPT[option]["getopt"].count(":") > 0: -+ print(' ') -+ else: -+ print(' ') -+ -+ print(' ' + ALL_OPT[option]["shortdesc"] + '') -+ print(' ') -+ -+ print(' \n ') -+ print(' ') -+ print(' ') -+ print(' ') -+ print(' ') -+ print(' ') -+ print(' ') -+ print(' ') -+ print('') -+ -+ -+def option_longopt(option): -+ """ Return the getopt-compatible long-option name of the given option. """ -+ -+ if ALL_OPT[option]["getopt"].endswith(":"): -+ return ALL_OPT[option]["longopt"] + "=" -+ else: -+ return ALL_OPT[option]["longopt"] -+ -+ -+def opts_from_command_line(argv, avail_opt): -+ """ Read options from command-line arguments. """ -+ -+ # Prepare list of options for getopt -+ getopt_string = "" -+ longopt_list = [] -+ for k in avail_opt: -+ if k in ALL_OPT: -+ getopt_string += ALL_OPT[k]["getopt"] -+ else: -+ fail_usage("Parse error: unknown option '" + k + "'") -+ -+ if k in ALL_OPT and "longopt" in ALL_OPT[k]: -+ longopt_list.append(option_longopt(k)) -+ -+ try: -+ opt, dummy = getopt.gnu_getopt(argv, getopt_string, longopt_list) -+ except getopt.GetoptError as error: -+ fail_usage("Parse error: " + error.msg) -+ -+ # Transform longopt to short one which are used in fencing agents -+ old_opt = opt -+ opt = {} -+ for old_option in dict(old_opt).keys(): -+ if old_option.startswith("--"): -+ for option in ALL_OPT.keys(): -+ if "longopt" in ALL_OPT[option] and "--" + ALL_OPT[option]["longopt"] == old_option: -+ opt["-" + ALL_OPT[option]["getopt"].rstrip(":")] = dict(old_opt)[old_option] -+ else: -+ opt[old_option] = dict(old_opt)[old_option] -+ -+ return opt -+ -+ -+def opts_from_stdin(avail_opt): -+ """ Read options from standard input. """ -+ -+ opt = {} -+ name = "" -+ for line in sys.stdin.readlines(): -+ line = line.strip() -+ if line.startswith("#") or (len(line) == 0): -+ continue -+ -+ (name, value) = (line + "=").split("=", 1) -+ value = value[:-1] -+ -+ if name not in avail_opt: -+ print("Parse error: Ignoring unknown option '%s'" % line, -+ file=sys.stderr) -+ continue -+ -+ if ALL_OPT[name]["getopt"].endswith(":"): -+ opt["-"+ALL_OPT[name]["getopt"].rstrip(":")] = value -+ elif value.lower() in ["1", "yes", "on", "true"]: -+ opt["-"+ALL_OPT[name]["getopt"]] = "1" -+ -+ return opt -+ -+ -+def process_input(avail_opt): -+ """ Set standard environment variables, and parse all options. """ -+ -+ # Set standard environment -+ os.putenv("LANG", "C") -+ os.putenv("LC_ALL", "C") -+ -+ # Read options from command line or standard input -+ if len(sys.argv) > 1: -+ return opts_from_command_line(sys.argv[1:], avail_opt) -+ else: -+ return opts_from_stdin(avail_opt) -+ -+ -+def atexit_handler(): -+ """ Close stdout on exit. """ -+ -+ try: -+ sys.stdout.close() -+ os.close(1) -+ except IOError: -+ sys.exit("%s failed to close standard output" % agent()) -+ -+ -+def main(): -+ """ Make it so! """ -+ -+ device_opt = ALL_OPT.keys() -+ -+ ## Defaults for fence agent -+ atexit.register(atexit_handler) -+ options = process_input(device_opt) -+ options["device_opt"] = device_opt -+ show_docs(options) -+ -+ print("Watchdog fencing may be initiated only by the cluster, not this agent.", -+ file=sys.stderr) -+ -+ sys.exit(1) -+ -+ -+if __name__ == "__main__": -+ main() -diff --git a/daemons/fenced/fenced_commands.c b/daemons/fenced/fenced_commands.c -index cd9968f1a..9470ea2c1 100644 ---- a/daemons/fenced/fenced_commands.c -+++ b/daemons/fenced/fenced_commands.c -@@ -397,15 +397,13 @@ stonith_device_execute(stonith_device_t * device) - return TRUE; - } - -- if(pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT, pcmk__str_casei)) { -- if(pcmk__str_eq(cmd->action, "reboot", pcmk__str_casei)) { -- pcmk__panic(__func__); -- goto done; -- -- } else if(pcmk__str_eq(cmd->action, "off", pcmk__str_casei)) { -- pcmk__panic(__func__); -- goto done; -- -+ if (pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT, -+ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) { -+ if (pcmk__strcase_any_of(cmd->action, "reboot", "off", NULL)) { -+ if (node_does_watchdog_fencing(stonith_our_uname)) { -+ pcmk__panic(__func__); -+ goto done; -+ } - } else { - crm_info("Faking success for %s watchdog operation", cmd->action); - cmd->done_cb(0, 0, NULL, cmd); -@@ -716,7 +714,7 @@ get_agent_metadata(const char *agent, xmlNode ** metadata) - return EINVAL; - } - *metadata = NULL; -- if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT, pcmk__str_none)) { -+ if (pcmk__str_eq(agent, STONITH_WATCHDOG_AGENT_INTERNAL, pcmk__str_none)) { - return pcmk_rc_ok; - } - init_metadata_cache(); -@@ -1050,24 +1048,6 @@ schedule_internal_command(const char *origin, - schedule_stonith_command(cmd, device); - } - --gboolean --string_in_list(GList *list, const char *item) --{ -- int lpc = 0; -- int max = g_list_length(list); -- -- for (lpc = 0; lpc < max; lpc++) { -- const char *value = g_list_nth_data(list, lpc); -- -- if (pcmk__str_eq(item, value, pcmk__str_casei)) { -- return TRUE; -- } else { -- crm_trace("%d: '%s' != '%s'", lpc, item, value); -- } -- } -- return FALSE; --} -- - static void - status_search_cb(GPid pid, int rc, const char *output, gpointer user_data) - { -@@ -1144,7 +1124,7 @@ dynamic_list_search_cb(GPid pid, int rc, const char *output, gpointer user_data) - if (!alias) { - alias = search->host; - } -- if (string_in_list(dev->targets, alias)) { -+ if (pcmk__str_in_list(dev->targets, alias, pcmk__str_casei)) { - can_fence = TRUE; - } - } -@@ -1215,9 +1195,62 @@ stonith_device_register(xmlNode * msg, const char **desc, gboolean from_cib) - stonith_device_t *dup = NULL; - stonith_device_t *device = build_device_from_xml(msg); - guint ndevices = 0; -+ int rv = pcmk_ok; - - CRM_CHECK(device != NULL, return -ENOMEM); - -+ /* do we have a watchdog-device? */ -+ if (pcmk__str_eq(device->id, STONITH_WATCHDOG_ID, pcmk__str_none) || -+ pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT, -+ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) do { -+ if (stonith_watchdog_timeout_ms <= 0) { -+ crm_err("Ignoring watchdog fence device without " -+ "stonith-watchdog-timeout set."); -+ rv = -ENODEV; -+ /* fall through to cleanup & return */ -+ } else if (!pcmk__str_any_of(device->agent, STONITH_WATCHDOG_AGENT, -+ STONITH_WATCHDOG_AGENT_INTERNAL, NULL)) { -+ crm_err("Ignoring watchdog fence device with unknown " -+ "agent '%s' unequal '" STONITH_WATCHDOG_AGENT "'.", -+ device->agent?device->agent:""); -+ rv = -ENODEV; -+ /* fall through to cleanup & return */ -+ } else if (!pcmk__str_eq(device->id, STONITH_WATCHDOG_ID, -+ pcmk__str_none)) { -+ crm_err("Ignoring watchdog fence device " -+ "named %s !='"STONITH_WATCHDOG_ID"'.", -+ device->id?device->id:""); -+ rv = -ENODEV; -+ /* fall through to cleanup & return */ -+ } else { -+ if (pcmk__str_eq(device->agent, STONITH_WATCHDOG_AGENT, -+ pcmk__str_none)) { -+ /* this either has an empty list or the targets -+ configured for watchdog-fencing -+ */ -+ g_list_free_full(stonith_watchdog_targets, free); -+ stonith_watchdog_targets = device->targets; -+ device->targets = NULL; -+ } -+ if (node_does_watchdog_fencing(stonith_our_uname)) { -+ g_list_free_full(device->targets, free); -+ device->targets = stonith__parse_targets(stonith_our_uname); -+ g_hash_table_replace(device->params, -+ strdup(PCMK_STONITH_HOST_LIST), -+ strdup(stonith_our_uname)); -+ /* proceed as with any other stonith-device */ -+ break; -+ } -+ -+ crm_debug("Skip registration of watchdog fence device on node not in host-list."); -+ /* cleanup and fall through to more cleanup and return */ -+ device->targets = NULL; -+ stonith_device_remove(device->id, from_cib); -+ } -+ free_device(device); -+ return rv; -+ } while (0); -+ - dup = device_has_duplicate(device); - if (dup) { - ndevices = g_hash_table_size(device_list); -@@ -1598,6 +1631,39 @@ stonith_level_remove(xmlNode *msg, char **desc) - * (CIB registration is not sufficient), because monitor should not be - * possible unless the device is "started" (API registered). - */ -+ -+static char * -+list_to_string(GList *list, const char *delim, gboolean terminate_with_delim) -+{ -+ int max = g_list_length(list); -+ size_t delim_len = delim?strlen(delim):0; -+ size_t alloc_size = 1 + (max?((max-1+(terminate_with_delim?1:0))*delim_len):0); -+ char *rv; -+ GList *gIter; -+ -+ for (gIter = list; gIter != NULL; gIter = gIter->next) { -+ const char *value = (const char *) gIter->data; -+ -+ alloc_size += strlen(value); -+ } -+ rv = calloc(alloc_size, sizeof(char)); -+ if (rv) { -+ char *pos = rv; -+ const char *lead_delim = ""; -+ -+ for (gIter = list; gIter != NULL; gIter = gIter->next) { -+ const char *value = (const char *) gIter->data; -+ -+ pos = &pos[sprintf(pos, "%s%s", lead_delim, value)]; -+ lead_delim = delim; -+ } -+ if (max && terminate_with_delim) { -+ sprintf(pos, "%s", delim); -+ } -+ } -+ return rv; -+} -+ - static int - stonith_device_action(xmlNode * msg, char **output) - { -@@ -1615,6 +1681,19 @@ stonith_device_action(xmlNode * msg, char **output) - return -EPROTO; - } - -+ if (pcmk__str_eq(id, STONITH_WATCHDOG_ID, pcmk__str_none)) { -+ if (stonith_watchdog_timeout_ms <= 0) { -+ return -ENODEV; -+ } else { -+ if (pcmk__str_eq(action, "list", pcmk__str_casei)) { -+ *output = list_to_string(stonith_watchdog_targets, "\n", TRUE); -+ return pcmk_ok; -+ } else if (pcmk__str_eq(action, "monitor", pcmk__str_casei)) { -+ return pcmk_ok; -+ } -+ } -+ } -+ - device = g_hash_table_lookup(device_list, id); - if ((device == NULL) - || (!device->api_registered && !strcmp(action, "monitor"))) { -@@ -1742,7 +1821,7 @@ can_fence_host_with_device(stonith_device_t * dev, struct device_search_s *searc - * Only use if all hosts on which the device can be active can always fence all listed hosts - */ - -- if (string_in_list(dev->targets, host)) { -+ if (pcmk__str_in_list(dev->targets, host, pcmk__str_casei)) { - can = TRUE; - } else if (g_hash_table_lookup(dev->params, PCMK_STONITH_HOST_MAP) - && g_hash_table_lookup(dev->aliases, host)) { -@@ -1763,7 +1842,7 @@ can_fence_host_with_device(stonith_device_t * dev, struct device_search_s *searc - return; - } - -- if (string_in_list(dev->targets, alias)) { -+ if (pcmk__str_in_list(dev->targets, alias, pcmk__str_casei)) { - can = TRUE; - } - -diff --git a/daemons/fenced/fenced_remote.c b/daemons/fenced/fenced_remote.c -index cf91acaed..224f2baba 100644 ---- a/daemons/fenced/fenced_remote.c -+++ b/daemons/fenced/fenced_remote.c -@@ -1522,6 +1522,25 @@ advance_topology_device_in_level(remote_fencing_op_t *op, const char *device, - } - } - -+static gboolean -+check_watchdog_fencing_and_wait(remote_fencing_op_t * op) -+{ -+ if (node_does_watchdog_fencing(op->target)) { -+ -+ crm_notice("Waiting %lds for %s to self-fence (%s) for " -+ "client %s " CRM_XS " id=%.8s", -+ (stonith_watchdog_timeout_ms / 1000), -+ op->target, op->action, op->client_name, op->id); -+ op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, -+ remote_op_watchdog_done, op); -+ return TRUE; -+ } else { -+ crm_debug("Skipping fallback to watchdog-fencing as %s is " -+ "not in host-list", op->target); -+ } -+ return FALSE; -+} -+ - void - call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc) - { -@@ -1592,26 +1611,33 @@ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc) - g_source_remove(op->op_timer_one); - } - -- if(stonith_watchdog_timeout_ms > 0 && device && pcmk__str_eq(device, "watchdog", pcmk__str_casei)) { -- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s " -- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000), -- op->target, op->action, op->client_name, op->id); -- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op); -- -- /* TODO check devices to verify watchdog will be in use */ -- } else if(stonith_watchdog_timeout_ms > 0 -- && pcmk__str_eq(peer->host, op->target, pcmk__str_casei) -- && !pcmk__str_eq(op->action, "on", pcmk__str_casei)) { -- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s " -- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000), -- op->target, op->action, op->client_name, op->id); -- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op); -- -- } else { -+ if (!(stonith_watchdog_timeout_ms > 0 && ( -+ (pcmk__str_eq(device, STONITH_WATCHDOG_ID, -+ pcmk__str_none)) || -+ (pcmk__str_eq(peer->host, op->target, pcmk__str_casei) -+ && !pcmk__str_eq(op->action, "on", pcmk__str_casei))) && -+ check_watchdog_fencing_and_wait(op))) { -+ -+ /* Some thoughts about self-fencing cases reaching this point: -+ - Actually check in check_watchdog_fencing_and_wait -+ shouldn't fail if STONITH_WATCHDOG_ID is -+ chosen as fencing-device and it being present implies -+ watchdog-fencing is enabled anyway -+ - If watchdog-fencing is disabled either in general or for -+ a specific target - detected in check_watchdog_fencing_and_wait - -+ for some other kind of self-fencing we can't expect -+ a success answer but timeout is fine if the node doesn't -+ come back in between -+ - Delicate might be the case where we have watchdog-fencing -+ enabled for a node but the watchdog-fencing-device isn't -+ explicitly chosen for suicide. Local pe-execution in sbd -+ may detect the node as unclean and lead to timely suicide. -+ Otherwise the selection of stonith-watchdog-timeout at -+ least is questionable. -+ */ - op->op_timer_one = g_timeout_add((1000 * timeout_one), remote_op_timeout_one, op); - } - -- - send_cluster_message(crm_get_peer(0, peer->host), crm_msg_stonith_ng, remote_op, FALSE); - peer->tried = TRUE; - free_xml(remote_op); -@@ -1645,12 +1671,11 @@ call_remote_stonith(remote_fencing_op_t * op, st_query_result_t * peer, int rc) - * but we have all the expected replies, then no devices - * are available to execute the fencing operation. */ - -- if(stonith_watchdog_timeout_ms && pcmk__str_eq(device, "watchdog", pcmk__str_null_matches | pcmk__str_casei)) { -- crm_notice("Waiting %lds for %s to self-fence (%s) for client %s " -- CRM_XS " id=%.8s", (stonith_watchdog_timeout_ms / 1000), -- op->target, op->action, op->client_name, op->id); -- op->op_timer_one = g_timeout_add(stonith_watchdog_timeout_ms, remote_op_watchdog_done, op); -- return; -+ if(stonith_watchdog_timeout_ms > 0 && pcmk__str_eq(device, -+ STONITH_WATCHDOG_ID, pcmk__str_null_matches)) { -+ if (check_watchdog_fencing_and_wait(op)) { -+ return; -+ } - } - - if (op->state == st_query) { -diff --git a/daemons/fenced/pacemaker-fenced.c b/daemons/fenced/pacemaker-fenced.c -index 39738d8be..7f8b427d9 100644 ---- a/daemons/fenced/pacemaker-fenced.c -+++ b/daemons/fenced/pacemaker-fenced.c -@@ -42,6 +42,7 @@ - - char *stonith_our_uname = NULL; - long stonith_watchdog_timeout_ms = 0; -+GList *stonith_watchdog_targets = NULL; - - static GMainLoop *mainloop = NULL; - -@@ -578,7 +579,44 @@ our_node_allowed_for(pe_resource_t *rsc) - } - - static void --watchdog_device_update(xmlNode *cib) -+watchdog_device_update(void) -+{ -+ if (stonith_watchdog_timeout_ms > 0) { -+ if (!g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID) && -+ !stonith_watchdog_targets) { -+ /* getting here watchdog-fencing enabled, no device there yet -+ and reason isn't stonith_watchdog_targets preventing that -+ */ -+ int rc; -+ xmlNode *xml; -+ -+ xml = create_device_registration_xml( -+ STONITH_WATCHDOG_ID, -+ st_namespace_internal, -+ STONITH_WATCHDOG_AGENT, -+ NULL, /* stonith_device_register will add our -+ own name as PCMK_STONITH_HOST_LIST param -+ so we can skip that here -+ */ -+ NULL); -+ rc = stonith_device_register(xml, NULL, TRUE); -+ free_xml(xml); -+ if (rc != pcmk_ok) { -+ crm_crit("Cannot register watchdog pseudo fence agent"); -+ crm_exit(CRM_EX_FATAL); -+ } -+ } -+ -+ } else { -+ /* be silent if no device - todo parameter to stonith_device_remove */ -+ if (g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID)) { -+ stonith_device_remove(STONITH_WATCHDOG_ID, TRUE); -+ } -+ } -+} -+ -+static void -+update_stonith_watchdog_timeout_ms(xmlNode *cib) - { - xmlNode *stonith_enabled_xml = NULL; - const char *stonith_enabled_s = NULL; -@@ -608,33 +646,7 @@ watchdog_device_update(xmlNode *cib) - } - } - -- if (timeout_ms != stonith_watchdog_timeout_ms) { -- crm_notice("New watchdog timeout %lds (was %lds)", timeout_ms/1000, stonith_watchdog_timeout_ms/1000); -- stonith_watchdog_timeout_ms = timeout_ms; -- -- if (stonith_watchdog_timeout_ms > 0) { -- int rc; -- xmlNode *xml; -- stonith_key_value_t *params = NULL; -- -- params = stonith_key_value_add(params, PCMK_STONITH_HOST_LIST, -- stonith_our_uname); -- -- xml = create_device_registration_xml("watchdog", st_namespace_internal, -- STONITH_WATCHDOG_AGENT, params, -- NULL); -- stonith_key_value_freeall(params, 1, 1); -- rc = stonith_device_register(xml, NULL, FALSE); -- free_xml(xml); -- if (rc != pcmk_ok) { -- crm_crit("Cannot register watchdog pseudo fence agent"); -- crm_exit(CRM_EX_FATAL); -- } -- -- } else { -- stonith_device_remove("watchdog", FALSE); -- } -- } -+ stonith_watchdog_timeout_ms = timeout_ms; - } - - /*! -@@ -677,6 +689,16 @@ static void cib_device_update(pe_resource_t *rsc, pe_working_set_t *data_set) - return; - } - -+ /* if watchdog-fencing is disabled handle any watchdog-fence -+ resource as if it was disabled -+ */ -+ if ((stonith_watchdog_timeout_ms <= 0) && -+ pcmk__str_eq(rsc->id, STONITH_WATCHDOG_ID, pcmk__str_none)) { -+ crm_info("Watchdog-fencing disabled thus handling " -+ "device %s as disabled", rsc->id); -+ return; -+ } -+ - /* Check whether our node is allowed for this resource (and its parent if in a group) */ - node = our_node_allowed_for(rsc); - if (rsc->parent && (rsc->parent->variant == pe_group)) { -@@ -772,6 +794,12 @@ cib_devices_update(void) - } - } - -+ /* have list repopulated if cib has a watchdog-fencing-resource -+ TODO: keep a cached list for queries happening while we are refreshing -+ */ -+ g_list_free_full(stonith_watchdog_targets, free); -+ stonith_watchdog_targets = NULL; -+ - for (gIter = fenced_data_set->resources; gIter != NULL; gIter = gIter->next) { - cib_device_update(gIter->data, fenced_data_set); - } -@@ -825,6 +853,8 @@ update_cib_stonith_devices_v2(const char *event, xmlNode * msg) - if (search != NULL) { - *search = 0; - stonith_device_remove(rsc_id, TRUE); -+ /* watchdog_device_update called afterwards -+ to fall back to implicit definition if needed */ - } else { - crm_warn("Ignoring malformed CIB update (resource deletion)"); - } -@@ -968,6 +998,24 @@ node_has_attr(const char *node, const char *name, const char *value) - return (match != NULL); - } - -+/*! -+ * \internal -+ * \brief Check whether a node does watchdog-fencing -+ * -+ * \param[in] node Name of node to check -+ * -+ * \return TRUE if node found in stonith_watchdog_targets -+ * or stonith_watchdog_targets is empty indicating -+ * all nodes are doing watchdog-fencing -+ */ -+gboolean -+node_does_watchdog_fencing(const char *node) -+{ -+ return ((stonith_watchdog_targets == NULL) || -+ pcmk__str_in_list(stonith_watchdog_targets, node, pcmk__str_casei)); -+} -+ -+ - static void - update_fencing_topology(const char *event, xmlNode * msg) - { -@@ -1073,6 +1121,8 @@ update_cib_cache_cb(const char *event, xmlNode * msg) - xmlNode *stonith_enabled_xml = NULL; - const char *stonith_enabled_s = NULL; - static gboolean stonith_enabled_saved = TRUE; -+ long timeout_ms_saved = stonith_watchdog_timeout_ms; -+ gboolean need_full_refresh = FALSE; - - if(!have_cib_devices) { - crm_trace("Skipping updates until we get a full dump"); -@@ -1127,6 +1177,7 @@ update_cib_cache_cb(const char *event, xmlNode * msg) - } - - pcmk__refresh_node_caches_from_cib(local_cib); -+ update_stonith_watchdog_timeout_ms(local_cib); - - stonith_enabled_xml = get_xpath_object("//nvpair[@name='stonith-enabled']", - local_cib, LOG_NEVER); -@@ -1134,23 +1185,30 @@ update_cib_cache_cb(const char *event, xmlNode * msg) - stonith_enabled_s = crm_element_value(stonith_enabled_xml, XML_NVPAIR_ATTR_VALUE); - } - -- watchdog_device_update(local_cib); -- - if (stonith_enabled_s && crm_is_true(stonith_enabled_s) == FALSE) { - crm_trace("Ignoring CIB updates while fencing is disabled"); - stonith_enabled_saved = FALSE; -- return; - - } else if (stonith_enabled_saved == FALSE) { - crm_info("Updating fencing device and topology lists " - "now that fencing is enabled"); - stonith_enabled_saved = TRUE; -- fencing_topology_init(); -- cib_devices_update(); -+ need_full_refresh = TRUE; - - } else { -- update_fencing_topology(event, msg); -- update_cib_stonith_devices(event, msg); -+ if (timeout_ms_saved != stonith_watchdog_timeout_ms) { -+ need_full_refresh = TRUE; -+ } else { -+ update_fencing_topology(event, msg); -+ update_cib_stonith_devices(event, msg); -+ watchdog_device_update(); -+ } -+ } -+ -+ if (need_full_refresh) { -+ fencing_topology_init(); -+ cib_devices_update(); -+ watchdog_device_update(); - } - } - -@@ -1162,10 +1220,11 @@ init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *us - local_cib = copy_xml(output); - - pcmk__refresh_node_caches_from_cib(local_cib); -+ update_stonith_watchdog_timeout_ms(local_cib); - - fencing_topology_init(); -- watchdog_device_update(local_cib); - cib_devices_update(); -+ watchdog_device_update(); - } - - static void -diff --git a/daemons/fenced/pacemaker-fenced.h b/daemons/fenced/pacemaker-fenced.h -index d330fda4d..14e085e98 100644 ---- a/daemons/fenced/pacemaker-fenced.h -+++ b/daemons/fenced/pacemaker-fenced.h -@@ -260,14 +260,15 @@ bool fencing_peer_active(crm_node_t *peer); - - int stonith_manual_ack(xmlNode * msg, remote_fencing_op_t * op); - --gboolean string_in_list(GList *list, const char *item); -- - gboolean node_has_attr(const char *node, const char *name, const char *value); - -+gboolean node_does_watchdog_fencing(const char *node); -+ - extern char *stonith_our_uname; - extern gboolean stand_alone; - extern GHashTable *device_list; - extern GHashTable *topology; - extern long stonith_watchdog_timeout_ms; -+extern GList *stonith_watchdog_targets; - - extern GHashTable *stonith_remote_op_list; -diff --git a/include/crm/crm.h b/include/crm/crm.h -index ee52c3630..7861c160e 100644 ---- a/include/crm/crm.h -+++ b/include/crm/crm.h -@@ -66,7 +66,7 @@ extern "C" { - * >=3.0.13: Fail counts include operation name and interval - * >=3.2.0: DC supports PCMK_LRM_OP_INVALID and PCMK_LRM_OP_NOT_CONNECTED - */ --# define CRM_FEATURE_SET "3.10.2" -+# define CRM_FEATURE_SET "3.11.0" - - /* Pacemaker's CPG protocols use fixed-width binary fields for the sender and - * recipient of a CPG message. This imposes an arbitrary limit on cluster node -diff --git a/include/crm/fencing/internal.h b/include/crm/fencing/internal.h -index 8bcb544d8..f222edba3 100644 ---- a/include/crm/fencing/internal.h -+++ b/include/crm/fencing/internal.h -@@ -164,7 +164,10 @@ void stonith__device_parameter_flags(uint32_t *device_flags, - # define STONITH_OP_LEVEL_ADD "st_level_add" - # define STONITH_OP_LEVEL_DEL "st_level_remove" - --# define STONITH_WATCHDOG_AGENT "#watchdog" -+# define STONITH_WATCHDOG_AGENT "fence_watchdog" -+/* Don't change 2 below as it would break rolling upgrade */ -+# define STONITH_WATCHDOG_AGENT_INTERNAL "#watchdog" -+# define STONITH_WATCHDOG_ID "watchdog" - - # ifdef HAVE_STONITH_STONITH_H - // utilities from st_lha.c -@@ -211,4 +214,7 @@ stonith__op_state_pending(enum op_state state) - return state != st_failed && state != st_done; - } - -+gboolean stonith__watchdog_fencing_enabled_for_node(const char *node); -+gboolean stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node); -+ - #endif -diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c -index e285f51e2..0ff98157b 100644 ---- a/lib/fencing/st_client.c -+++ b/lib/fencing/st_client.c -@@ -195,6 +195,67 @@ stonith_get_namespace(const char *agent, const char *namespace_s) - return st_namespace_invalid; - } - -+gboolean -+stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node) -+{ -+ gboolean rv = FALSE; -+ stonith_t *stonith_api = st?st:stonith_api_new(); -+ char *list = NULL; -+ -+ if(stonith_api) { -+ if (stonith_api->state == stonith_disconnected) { -+ int rc = stonith_api->cmds->connect(stonith_api, "stonith-api", NULL); -+ -+ if (rc != pcmk_ok) { -+ crm_err("Failed connecting to Stonith-API for watchdog-fencing-query."); -+ } -+ } -+ -+ if (stonith_api->state != stonith_disconnected) { -+ /* caveat!!! -+ * this might fail when when stonithd is just updating the device-list -+ * probably something we should fix as well for other api-calls */ -+ int rc = stonith_api->cmds->list(stonith_api, st_opt_sync_call, STONITH_WATCHDOG_ID, &list, 0); -+ if ((rc != pcmk_ok) || (list == NULL)) { -+ /* due to the race described above it can happen that -+ * we drop in here - so as not to make remote nodes -+ * panic on that answer -+ */ -+ crm_warn("watchdog-fencing-query failed"); -+ } else if (list[0] == '\0') { -+ crm_warn("watchdog-fencing-query returned an empty list - any node"); -+ rv = TRUE; -+ } else { -+ GList *targets = stonith__parse_targets(list); -+ rv = pcmk__str_in_list(targets, node, pcmk__str_casei); -+ g_list_free_full(targets, free); -+ } -+ free(list); -+ if (!st) { -+ /* if we're provided the api we still might have done the -+ * connection - but let's assume the caller won't bother -+ */ -+ stonith_api->cmds->disconnect(stonith_api); -+ } -+ } -+ -+ if (!st) { -+ stonith_api_delete(stonith_api); -+ } -+ } else { -+ crm_err("Stonith-API for watchdog-fencing-query couldn't be created."); -+ } -+ crm_trace("Pacemaker assumes node %s %sto do watchdog-fencing.", -+ node, rv?"":"not "); -+ return rv; -+} -+ -+gboolean -+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) - { -diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c -index 87d050ed1..bf4bceb42 100644 ---- a/lib/lrmd/lrmd_client.c -+++ b/lib/lrmd/lrmd_client.c -@@ -34,6 +34,7 @@ - #include - - #include -+#include - - #ifdef HAVE_GNUTLS_GNUTLS_H - # undef KEYFILE -@@ -934,7 +935,10 @@ lrmd__validate_remote_settings(lrmd_t *lrmd, GHashTable *hash) - crm_xml_add(data, F_LRMD_ORIGIN, __func__); - - value = g_hash_table_lookup(hash, "stonith-watchdog-timeout"); -- crm_xml_add(data, F_LRMD_WATCHDOG, value); -+ if ((value) && -+ (stonith__watchdog_fencing_enabled_for_node(native->remote_nodename))) { -+ crm_xml_add(data, F_LRMD_WATCHDOG, value); -+ } - - rc = lrmd_send_command(lrmd, LRMD_OP_CHECK, data, NULL, 0, 0, - (native->type == pcmk__client_ipc)); -diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in -index 79e78ede9..f58357a77 100644 ---- a/rpm/pacemaker.spec.in -+++ b/rpm/pacemaker.spec.in -@@ -744,6 +744,7 @@ exit 0 - %doc %{_mandir}/man8/crm_attribute.* - %doc %{_mandir}/man8/crm_master.* - %doc %{_mandir}/man8/fence_legacy.* -+%doc %{_mandir}/man8/fence_watchdog.* - %doc %{_mandir}/man8/pacemakerd.* - - %doc %{_datadir}/pacemaker/alerts -@@ -796,6 +797,7 @@ exit 0 - %{_sbindir}/crm_simulate - %{_sbindir}/crm_report - %{_sbindir}/crm_ticket -+%{_sbindir}/fence_watchdog - %{_sbindir}/stonith_admin - # "dirname" is owned by -schemas, which is a prerequisite - %{_datadir}/pacemaker/report.collector -@@ -822,6 +824,7 @@ exit 0 - %exclude %{_mandir}/man8/crm_attribute.* - %exclude %{_mandir}/man8/crm_master.* - %exclude %{_mandir}/man8/fence_legacy.* -+%exclude %{_mandir}/man8/fence_watchdog.* - %exclude %{_mandir}/man8/pacemakerd.* - %exclude %{_mandir}/man8/pacemaker-remoted.* - --- -2.27.0 - - -From 53dd360f096e5f005e3221e8d44d82d3654b5172 Mon Sep 17 00:00:00 2001 -From: Klaus Wenninger -Date: Wed, 4 Aug 2021 15:57:23 +0200 -Subject: [PATCH 3/3] Fix: watchdog-fencing: Silence warning without node - restriction - ---- - lib/fencing/st_client.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c -index 0ff98157b..14fa7b2a6 100644 ---- a/lib/fencing/st_client.c -+++ b/lib/fencing/st_client.c -@@ -223,7 +223,6 @@ stonith__watchdog_fencing_enabled_for_node_api(stonith_t *st, const char *node) - */ - crm_warn("watchdog-fencing-query failed"); - } else if (list[0] == '\0') { -- crm_warn("watchdog-fencing-query returned an empty list - any node"); - rv = TRUE; - } else { - GList *targets = stonith__parse_targets(list); --- -2.27.0 - diff --git a/SOURCES/016-cts.patch b/SOURCES/016-cts.patch deleted file mode 100644 index 195afc3..0000000 --- a/SOURCES/016-cts.patch +++ /dev/null @@ -1,59 +0,0 @@ -From b37391fef92548f31822f9df2a9b5fa2a61b4514 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Wed, 23 Jun 2021 15:17:54 -0500 -Subject: [PATCH] Fix: CTS: handle longer Corosync token timeouts - -Previously, startall() would call cluster_stable() immediately after detecting -the "controller successfully started" message. If the Corosync token timeout is -small enough, this will be fine. However with a token timeout of more than -about 1 second, the controllers will not have formed a membership by this -point, causing cluster_stable() to think there are multiple partitions, and -wait for a DC to be elected in each one, when really they will unite into a -single partition in a short time, and only elect a single DC. - -Now, startall() waits until seeing that each node is a cluster member before -calling cluster_stable(). ---- - cts/lab/CTS.py.in | 3 ++- - cts/lab/patterns.py | 2 ++ - 2 files changed, 4 insertions(+), 1 deletion(-) - -diff --git a/cts/lab/CTS.py.in b/cts/lab/CTS.py.in -index abcb9d285..d9924437b 100644 ---- a/cts/lab/CTS.py.in -+++ b/cts/lab/CTS.py.in -@@ -628,9 +628,10 @@ class ClusterManager(UserDict): - watchpats = [ ] - watchpats.append(self.templates["Pat:DC_IDLE"]) - for node in nodelist: -- watchpats.append(self.templates["Pat:Local_started"] % node) - watchpats.append(self.templates["Pat:InfraUp"] % node) - watchpats.append(self.templates["Pat:PacemakerUp"] % node) -+ watchpats.append(self.templates["Pat:Local_started"] % node) -+ watchpats.append(self.templates["Pat:They_up"] % (nodelist[0], node)) - - # Start all the nodes - at about the same time... - watch = LogWatcher(self.Env["LogFileName"], watchpats, "fast-start", self.Env["DeadTime"]+10, hosts=self.Env["nodes"], kind=self.Env["LogWatcher"]) -diff --git a/cts/lab/patterns.py b/cts/lab/patterns.py -index e21a016ff..400fd3dc8 100644 ---- a/cts/lab/patterns.py -+++ b/cts/lab/patterns.py -@@ -61,6 +61,7 @@ class BasePatterns(object): - "Pat:We_stopped" : "%s\W.*OVERRIDE THIS PATTERN", - "Pat:They_stopped" : "%s\W.*LOST:.* %s ", - "Pat:They_dead" : "node %s.*: is dead", -+ "Pat:They_up" : "%s %s\W.*OVERRIDE THIS PATTERN", - "Pat:TransitionComplete" : "Transition status: Complete: complete", - - "Pat:Fencing_start" : r"Requesting peer fencing .* targeting %s", -@@ -130,6 +131,7 @@ class crm_corosync(BasePatterns): - "Pat:We_stopped" : "%s\W.*Unloading all Corosync service engines", - "Pat:They_stopped" : "%s\W.*pacemaker-controld.*Node %s(\[|\s).*state is now lost", - "Pat:They_dead" : "pacemaker-controld.*Node %s(\[|\s).*state is now lost", -+ "Pat:They_up" : "\W%s\W.*pacemaker-controld.*Node %s state is now member", - - "Pat:ChildExit" : r"\[[0-9]+\] exited with status [0-9]+ \(", - # "with signal 9" == pcmk_child_exit(), "$" == check_active_before_startup_processes() --- -2.27.0 - diff --git a/SOURCES/017-watchdog-fixes.patch b/SOURCES/017-watchdog-fixes.patch deleted file mode 100644 index d3df876..0000000 --- a/SOURCES/017-watchdog-fixes.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 61eb9c240004d1dbd0b5973e2fecda3686bb4c53 Mon Sep 17 00:00:00 2001 -From: Klaus Wenninger -Date: Tue, 10 Aug 2021 09:06:55 +0200 -Subject: [PATCH 1/2] Build: rpm: package fence_watchdog in base-package - ---- - rpm/pacemaker.spec.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/rpm/pacemaker.spec.in b/rpm/pacemaker.spec.in -index f58357a77..0c569b9ca 100644 ---- a/rpm/pacemaker.spec.in -+++ b/rpm/pacemaker.spec.in -@@ -734,6 +734,7 @@ exit 0 - %{_sbindir}/crm_attribute - %{_sbindir}/crm_master - %{_sbindir}/fence_legacy -+%{_sbindir}/fence_watchdog - - %doc %{_mandir}/man7/pacemaker-controld.* - %doc %{_mandir}/man7/pacemaker-schedulerd.* -@@ -797,7 +798,6 @@ exit 0 - %{_sbindir}/crm_simulate - %{_sbindir}/crm_report - %{_sbindir}/crm_ticket --%{_sbindir}/fence_watchdog - %{_sbindir}/stonith_admin - # "dirname" is owned by -schemas, which is a prerequisite - %{_datadir}/pacemaker/report.collector --- -2.27.0 - - -From 88e75d5b98df197fa731e7642434951a24a67095 Mon Sep 17 00:00:00 2001 -From: Klaus Wenninger -Date: Tue, 10 Aug 2021 09:10:23 +0200 -Subject: [PATCH 2/2] Fix: fence_watchdog: fix version output needed for - help2man - ---- - daemons/fenced/fence_watchdog.in | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in -index c83304f1d..700065e0e 100755 ---- a/daemons/fenced/fence_watchdog.in -+++ b/daemons/fenced/fence_watchdog.in -@@ -12,6 +12,7 @@ import sys - import atexit - import getopt - -+AGENT_VERSION = "1.0.0" - SHORT_DESC = "Dummy watchdog fence agent" - LONG_DESC = """fence_watchdog just provides - meta-data - actual fencing is done by the pacemaker internal watchdog agent.""" --- -2.27.0 - diff --git a/SOURCES/018-controller.patch b/SOURCES/018-controller.patch deleted file mode 100644 index a2094e3..0000000 --- a/SOURCES/018-controller.patch +++ /dev/null @@ -1,122 +0,0 @@ -From ee7eba6a7a05bdf0a12d60ebabb334d8ee021101 Mon Sep 17 00:00:00 2001 -From: Ken Gaillot -Date: Mon, 9 Aug 2021 14:48:57 -0500 -Subject: [PATCH] Fix: controller: ensure lost node's transient attributes are - cleared without DC - -Previously, peer_update_callback() cleared a lost node's transient attributes -if either the local node is DC, or there is no DC. - -However, that left the possibility of the DC being lost at the same time as -another node -- the local node would still have fsa_our_dc set while processing -the leave notifications, so no node would clear the attributes for the non-DC -node. - -Now, the controller has its own CPG configuration change callback, which sets a -global boolean before calling the usual one, so that peer_update_callback() can -know when the DC has been lost. ---- - daemons/controld/controld_callbacks.c | 4 +- - daemons/controld/controld_corosync.c | 57 ++++++++++++++++++++++++++- - 2 files changed, 59 insertions(+), 2 deletions(-) - -diff --git a/daemons/controld/controld_callbacks.c b/daemons/controld/controld_callbacks.c -index af24856ae..e564b3dcd 100644 ---- a/daemons/controld/controld_callbacks.c -+++ b/daemons/controld/controld_callbacks.c -@@ -99,6 +99,8 @@ node_alive(const crm_node_t *node) - - #define state_text(state) ((state)? (const char *)(state) : "in unknown state") - -+bool controld_dc_left = false; -+ - void - peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data) - { -@@ -217,7 +219,7 @@ peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *d - cib_scope_local); - } - -- } else if (AM_I_DC || (fsa_our_dc == NULL)) { -+ } else if (AM_I_DC || controld_dc_left || (fsa_our_dc == NULL)) { - /* This only needs to be done once, so normally the DC should do - * it. However if there is no DC, every node must do it, since - * there is no other way to ensure some one node does it. -diff --git a/daemons/controld/controld_corosync.c b/daemons/controld/controld_corosync.c -index db99630fb..c5ab6580a 100644 ---- a/daemons/controld/controld_corosync.c -+++ b/daemons/controld/controld_corosync.c -@@ -87,6 +87,61 @@ crmd_cs_destroy(gpointer user_data) - } - } - -+extern bool controld_dc_left; -+ -+/*! -+ * \brief Handle a Corosync notification of a CPG configuration change -+ * -+ * \param[in] handle CPG connection -+ * \param[in] cpg_name CPG group name -+ * \param[in] member_list List of current CPG members -+ * \param[in] member_list_entries Number of entries in \p member_list -+ * \param[in] left_list List of CPG members that left -+ * \param[in] left_list_entries Number of entries in \p left_list -+ * \param[in] joined_list List of CPG members that joined -+ * \param[in] joined_list_entries Number of entries in \p joined_list -+ */ -+static void -+cpg_membership_callback(cpg_handle_t handle, const struct cpg_name *cpg_name, -+ const struct cpg_address *member_list, -+ size_t member_list_entries, -+ const struct cpg_address *left_list, -+ size_t left_list_entries, -+ const struct cpg_address *joined_list, -+ size_t joined_list_entries) -+{ -+ /* When nodes leave CPG, the DC clears their transient node attributes. -+ * -+ * However if there is no DC, or the DC is among the nodes that left, each -+ * remaining node needs to do the clearing, to ensure it gets done. -+ * Otherwise, the attributes would persist when the nodes rejoin, which -+ * could have serious consequences for unfencing, agents that use attributes -+ * for internal logic, etc. -+ * -+ * Here, we set a global boolean if the DC is among the nodes that left, for -+ * use by the peer callback. -+ */ -+ if (fsa_our_dc != NULL) { -+ crm_node_t *peer = pcmk__search_cluster_node_cache(0, fsa_our_dc); -+ -+ if (peer != NULL) { -+ for (int i = 0; i < left_list_entries; ++i) { -+ if (left_list[i].nodeid == peer->id) { -+ controld_dc_left = true; -+ break; -+ } -+ } -+ } -+ } -+ -+ // Process the change normally, which will call the peer callback as needed -+ pcmk_cpg_membership(handle, cpg_name, member_list, member_list_entries, -+ left_list, left_list_entries, -+ joined_list, joined_list_entries); -+ -+ controld_dc_left = false; -+} -+ - extern gboolean crm_connect_corosync(crm_cluster_t * cluster); - - gboolean -@@ -95,7 +150,7 @@ crm_connect_corosync(crm_cluster_t * cluster) - if (is_corosync_cluster()) { - crm_set_status_callback(&peer_update_callback); - cluster->cpg.cpg_deliver_fn = crmd_cs_dispatch; -- cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership; -+ cluster->cpg.cpg_confchg_fn = cpg_membership_callback; - cluster->destroy = crmd_cs_destroy; - - if (crm_cluster_connect(cluster)) { --- -2.27.0 - diff --git a/SOURCES/019-crm_resource.patch b/SOURCES/019-crm_resource.patch deleted file mode 100644 index 237dde2..0000000 --- a/SOURCES/019-crm_resource.patch +++ /dev/null @@ -1,114 +0,0 @@ -From b4e426a016a4d7c9ade39e60a83644fc537bce26 Mon Sep 17 00:00:00 2001 -From: Oyvind Albrigtsen -Date: Wed, 11 Aug 2021 12:10:32 +0200 -Subject: [PATCH 1/2] Fix: crm_resource: translate LSB rc to exit code and fix - resources_find_service_class() call - ---- - tools/crm_resource_runtime.c | 16 ++++++++++++---- - 1 file changed, 12 insertions(+), 4 deletions(-) - -diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c -index ce037c514..e9d8aa687 100644 ---- a/tools/crm_resource_runtime.c -+++ b/tools/crm_resource_runtime.c -@@ -1718,10 +1718,10 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); - } else if (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_SERVICE, - pcmk__str_casei) && !pcmk__str_eq( -- resources_find_service_class(rsc_name), PCMK_RESOURCE_CLASS_LSB, -+ resources_find_service_class(rsc_type), PCMK_RESOURCE_CLASS_LSB, - pcmk__str_casei)) { - out->err(out, "Sorry, the %s option doesn't support %s resources", -- rsc_action, resources_find_service_class(rsc_name)); -+ rsc_action, resources_find_service_class(rsc_type)); - crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); - } - -@@ -1798,9 +1798,17 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - if (services_action_sync(op)) { - exit_code = op->rc; - -+ /* Lookup exit code based on rc for LSB resources */ -+ if (( pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei) || -+ (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_SERVICE, pcmk__str_casei) && -+ pcmk__str_eq(resources_find_service_class(rsc_type), PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) ) && -+ pcmk__str_eq(rsc_action, "force-check", pcmk__str_casei)) { -+ exit_code = services_get_ocf_exitcode(action, exit_code); -+ } -+ - out->message(out, "resource-agent-action", resource_verbose, rsc_class, -- rsc_prov, rsc_type, rsc_name, rsc_action, override_hash, op->rc, -- op->status, op->stdout_data, op->stderr_data); -+ rsc_prov, rsc_type, rsc_name, rsc_action, override_hash, -+ exit_code, op->status, op->stdout_data, op->stderr_data); - } else { - exit_code = op->rc == 0 ? CRM_EX_ERROR : op->rc; - } --- -2.27.0 - - -From 9a6beb74adfb4710fb3a4e588bef79a562c101f3 Mon Sep 17 00:00:00 2001 -From: Oyvind Albrigtsen -Date: Thu, 12 Aug 2021 18:54:30 +0200 -Subject: [PATCH 2/2] Refactor: crm_resource: simplify rsc_class logic by - getting actual class early if it's of class "service" - ---- - tools/crm_resource_runtime.c | 23 +++++++++-------------- - 1 file changed, 9 insertions(+), 14 deletions(-) - -diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c -index e9d8aa687..13b78b6b9 100644 ---- a/tools/crm_resource_runtime.c -+++ b/tools/crm_resource_runtime.c -@@ -1702,26 +1702,23 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - int timeout_ms, int resource_verbose, gboolean force, - int check_level) - { -+ const char *class = NULL; - const char *action = NULL; - GHashTable *params_copy = NULL; - crm_exit_t exit_code = CRM_EX_OK; - svc_action_t *op = NULL; - -- if (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) { -+ class = !pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_SERVICE, pcmk__str_casei) ? -+ rsc_class : resources_find_service_class(rsc_type); -+ -+ if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) { - out->err(out, "Sorry, the %s option doesn't support %s resources yet", -- rsc_action, rsc_class); -+ rsc_action, class); - crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); -- } else if (pcmk__strcase_any_of(rsc_class, PCMK_RESOURCE_CLASS_SYSTEMD, -+ } else if (pcmk__strcase_any_of(class, PCMK_RESOURCE_CLASS_SYSTEMD, - PCMK_RESOURCE_CLASS_UPSTART, PCMK_RESOURCE_CLASS_NAGIOS, NULL)) { - out->err(out, "Sorry, the %s option doesn't support %s resources", -- rsc_action, rsc_class); -- crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); -- } else if (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_SERVICE, -- pcmk__str_casei) && !pcmk__str_eq( -- resources_find_service_class(rsc_type), PCMK_RESOURCE_CLASS_LSB, -- pcmk__str_casei)) { -- out->err(out, "Sorry, the %s option doesn't support %s resources", -- rsc_action, resources_find_service_class(rsc_type)); -+ rsc_action, class); - crm_exit(CRM_EX_UNIMPLEMENT_FEATURE); - } - -@@ -1799,9 +1796,7 @@ cli_resource_execute_from_params(pcmk__output_t *out, const char *rsc_name, - exit_code = op->rc; - - /* Lookup exit code based on rc for LSB resources */ -- if (( pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei) || -- (pcmk__str_eq(rsc_class, PCMK_RESOURCE_CLASS_SERVICE, pcmk__str_casei) && -- pcmk__str_eq(resources_find_service_class(rsc_type), PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) ) && -+ if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei) && - pcmk__str_eq(rsc_action, "force-check", pcmk__str_casei)) { - exit_code = services_get_ocf_exitcode(action, exit_code); - } --- -2.27.0 - diff --git a/SOURCES/020-fence_watchdog.patch b/SOURCES/020-fence_watchdog.patch deleted file mode 100644 index 76abe27..0000000 --- a/SOURCES/020-fence_watchdog.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 46dd1118cae948649e000b2159e8e92623520ad9 Mon Sep 17 00:00:00 2001 -From: Klaus Wenninger -Date: Thu, 19 Aug 2021 09:28:54 +0200 -Subject: [PATCH] Fix: fence_watchdog: fix malformed xml in metadata - ---- - daemons/fenced/fence_watchdog.in | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/daemons/fenced/fence_watchdog.in b/daemons/fenced/fence_watchdog.in -index 700065e0e..eefa7395e 100755 ---- a/daemons/fenced/fence_watchdog.in -+++ b/daemons/fenced/fence_watchdog.in -@@ -124,7 +124,7 @@ def metadata(avail_opt, options): - for option, dummy in sorted_options(avail_opt): - if "shortdesc" in ALL_OPT[option]: - print(' ') -+ '" required="' + ALL_OPT[option]["required"] + '">') - - default = "" - default_name_arg = "-" + ALL_OPT[option]["getopt"][:-1] --- -2.27.0 - diff --git a/SOURCES/nagios-agents-metadata-105ab8a.tar.gz b/SOURCES/nagios-agents-metadata-105ab8a.tar.gz deleted file mode 100644 index f493e14f0d79d86022b8ece5e59aeb6324d150d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17861 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�eGT${}SQUhAva)J%(KTVQwte4j12xri~kh zYc{coQAa{Tl}{}Anu#YmfChC^z35la9Av_DQGs^h7Nvlev?|B{X=%}(EKC9UA>m5yl?;JS z8Z67{1|y%tCiD<7ebUl{ekZsPirLqHjyfiE#Bq$d-|a;bJ}G<=Cq}r#B)1`5=xg~^ zX&DipFG!K0?=^E{8Gx7&&l;FI?T^H7BM?*}QrPTy#P5d%9h_p&+xF#2!matD^p=hD zuS_4-^lc2&1VXebv+_DtLDFJg72e?MPNN>Dy)NTI z-bS@F6CiJbq10e)o?M4fMYI?jI~RO0u&2R3*q%JuUo|x-xhUwKDOl($D~aIzEIrCN z=0$l;fel#pAl*%{N>=QUO>rO{%m){yc?8&D z9|mGP&tVF>R#qJ(ODqj&o(*r0S#545>%o|>v>zk3>9i39m0z8nmVn#apd1f@cFxui_pc9? zS$0!`x&)}QW8}R5Z6**n7l(@8rftv`O?RI)9x}kj`c1HjcE-6GF6I@M5Wh)St~ub{ zjuF0CI3w$`+GggniY%mB%4NM^X@oUY_raEFBXF9@Vj?=ZXt+?)F-xD32HzrC!-%WB ziBNlC-gtY#QIiidG=8cN*zU5zG+IC|*q!KN&>@4>nbA*i80fdAtohbeC9VC;(P(T>xO|ieWzTSDq+IjRID!*5p3ew<=noASVxZ3nBlGmyx@Y z_{>=p>=fHPL{v-QDnzHEbFoD6_&gi0w}b$Vl~n%ui{+yPtux)ipYrl?aQN%=h~ho` z*A75!>7oMv9Is@7q6b;@>A+%*O_h+PYZ-b-?lfP!Z<2j+Nwte*{Y1%BnZGIrBEaJ@ z%?*q9>~Cp4+~nAY2Bgn3x(B0P&4$Ju1fl|}IJ=89SK^uk@{jl||^DUU+9 zKgoRl=MmaQP+9?7m}81|dk%gS25C}lM954hY-oi|SYLLN=^Z~3tA+rv+VElAJKF)sn&9$?`A#PLs81gKIFw>S)UUr~*<-B^r`i`hhXI43 z;STN(l2s{3BS-$>XxL$aIzOLcMAwS{wjtV{!Wh9BQsPCsLw!n_*?{8M<}pWz^WOP2 z%cn^yQ^36Jj)>_ZmirbOl_c9Tw%*ULtP2NbsAj%CWrD52D1Ay^(xZDh+sC3jvP=|} zWqh#*4QQAOG*_+o%b+i7W3pq;(QINm9is3IOXCrd025TnP!(OU#b(sB0y^aYTj+|d zlqOK-UnB#He$GmU(`CVp=vz_>{s5k))pPvXRvbPJ;tuAa+aBfj0@Q*f)iY zM&f}HM}&K&1FxytEB=AJ-tagp*F2TO%+QMynZ zN!DUzl9@FkYiZy?`7n;i6_A9gGK#KFava4n{u)iTw#EoWKBKTZrzqLBpqgMr*NauI z&TOojzO}Ez1YQf-ND7P{mh4GN|1g#=%z9*rFsw&xDci>4jPqc1x3(r3 z(p>~4$93ul1fN%i@ohbk5!g1Q3{Kn?P+$-D2+j}cZc)xWf}#~rBct9MNIc9ftb|r! zd7tct#c$-66BZ6V;`c0+8#Re$a9DI~T`&LS&tB+I4EOQ@nsaSbpp3lEO9ihTV!Mu# zT1K*NWo1=u7S@^o+qI@EwQ0~+^eec}i7%tgSS#C%)SDj)l9^Dkzo9tH3&RS;0!U)1 z;l+bYfg@UqQTeZ)(p0f$34hD^D9bsI>yjU0xK=B1VX=|fGq&MRbn~iN z8qdX**WahC0vA`ny&hKO^GuHq;c!8H5K8n;3OOj(zh5Gj@WDV`31uK-5^Txdp>P6N~v%$IfdDQ z5p^ggm-<0S6iHIyyfQ{MCM$$m2XBksuxPsmkRzgQa7*^5AQ2CN4<7?B%0jBVHO+qmu$+XJgE`{1sz){&L%`)u`Qw8i3m0&nWhrHBKG*&Oj9 z=^>917&XG78Sr9qDBdbw&38Eq{+?5^9B=_lxnqR-&75sN#T$hR5i<~Vx~9Zi5XhC0ddOl!o`@yA zJV;5^M1R#~Nubqd1x_{CHVzKD6=b^?5}U<$4=JAQ4bN{RQ>Wl-z#pyQXRX1%|GtUc zd11uL!qDbUYbqBm&+K@*1^l{=IA3?n3{tlEg~`E#kP*4r@j~9#mGrB5@eRlW1-`R! zo)K|2T89T0n#WT#P3RmO5qjslyL>$BPsCyO*oN1dXPcm&-8tYUiSr66#6L?{d z7GkGEsE!-5Z^8(S61EZh+h+Qgbe+#HV&mk|%+IwyE*q_+g?AXVKF$J{kw71gP83lC7nb)G3Z zLjkB+FlHev_A043bS!_I4` zT$;j@>mHbvS`JZ3bOwV8ioH48xgLY!ID79l@>{Rr^luPqlEd*!c>Em`O^ER>+K24} zYdc=3ZW^9B@kBWJn7anjV8r*xV)6Td;AZ$^i(}kT%Nji}lHguHjv~>iA2qcSqb`pu z<^0WDc9EH_k9~~lbQZ#E2~!%rO2O`_6qX{hSCTvrE%Io`axxE!l1Kn<6_*CHrx5Gd}p@Y;KTq9U%vWvlK9uAkx3AV zdvy-O4nmn?^C4L-{MmveSt&}(c+O*x@%kE}(}50$8j~8T{G|7oTnU?msKtI^ab^tc z-)uD5fW%IwV2^GoC?a9CX~#1?z%8SB5r&9KoHo1$kWn8+icRT)RLzP7m>`<)?U9gg z;q5^pQje%neJ#I{V+?S1*C@ewH@B*!*b%ZTrGG1lDti+P%^snMQ(^3l=dl`qqzbMu z46RMQVbop<4d_{y5*Wt=^u02ra4FzN6)AdQp??0Z<|bi4PL(YM&K%gA9;e?GAvuIe zT67uO*QlKr!bc;r%8!)ue*lO;cfTTvG|e3tnAyn*vNq>)swb?wjW~gnxIQWy^gqJ} z{Z^#_)eq8*bog_54^SXc?$TufxXk`55hAqA^JrPk^zC%;?cBh-9}@U_3V6BfR<1uO z0Z|RIeeMv+S3$}0S)5BR1Q4P<$}!(sxR_50_+A(chv<`Bkff&`qc-Ob>Rl1Z=()$V zG=+acUI|P>@2kw89nU#e9v@pbeFA!x$5<^webk8F+^i%!ZgFbdVFkbeCeO%=BDtV; zZRZM%^~FmF(MXTL&o3A7E<;RVjVFZcYcR%#lwlXU$6?ke6MUOa(_$pN-nY+ABJfO8 z3@pc^&fdg_6$1scs{}P(^P6#-*Pz(U8WfdjP`u1e%_EU4RBq2PtK-B#oy6Lx{(LEr z29=LB<<2>YYL8K$f4=EQ=%vYlYcSq9By}r;5I_3V`6N23f&uL5` zsq6vloK%hkUt67`OY(DGn|=XqzRy|m5Dgaxv%7dxqT6184BBWf+6$&jA-mhomARi> z$j$0R`#4gKw_Nyq_+mv{Mx6P0xi&uij78d);>O~2_LqU@`WJ%d#utF+m*NgyxSMtH zN_So>-L!^SAWWsny9sg zA&KPD|7@KI>^>kYo#FhNiPg0L$J;!gWUKJ2BjBqh{9N(;>If)B^iyl>FGpp6nL7KP zbrjqR-6(WMSA`1rbe`i<+_KJ#KD_^%icM|ac zLafq+Sev}tyM2NKP&1%+IBWxfD z7dXiU#QEp9i~&VIuhYNA+nDC}c76sEvB+;VXJ)gxkoez)?5X^3iSuZt^Gg3~$r+S) z_W7PA)A=#`@u2`d**chtR6pqV5YB)xlnw?KJQT-m7e51JlKuH-g6tD8d{oq%EAPhj zKkZsD|AK|`dkZAps$6HVymLN(pDQ>lDx(`I$2H7sAz@MX)tq zey7;lqEo<$zBjQWE40FZ@$^R1k>yWE7j7t7Ix-q0p`I;mfov?{F3J#5&Ij$wL^Pd{>&vCovJ^*se#*B5}R?JoFHY^tnU z%X}m7PD9*R+OS&Rh?Imojo|XKA$Fj48p<>&a3o#-O=od!=7ZKkj@n$zig&}kwF^L4U(dPe<|PnK#*Oao?8Wf>j96GF@s zaa}#@qXkY9hWi(t7TZpBzhr0YgE?Gr7 zX|SQ4!HpYEyl9p~Te&mG55y|A7fr{A?flaxiTJ?1bf!bjmk;)5mF{6g)R&Qsj@T@w z;it;7@l@^>DtT;#X7mCM0+48UFMy)IB{+e&Yngktyoz3HcrZdiNV&~xwNes6VKt9` zT1l#r5lkPzBvif|k1vm62KqRYgD#%>u+u}2eB$J_?!*~56DRIbWPNH!c^#wd3*i4u zpzfD22f((8&{rQDBSYtoIDb4IdM<7!4eyxe(|L_O{frgGvl`n#JqT701E$LfXyE8~ z@4|^(o@ay4_s}q*x`-LNP-4?|LLMpq~y;AXf+nZJok1w z@Qmjld>J|K;O|N;6W~}afT|O%Gt$FiH7TD9<*RJyu@eF~ zJUOtWlgfPQ8Uei%J*|4>THH28Y3X;taVQ0*TM>ouWP?PA?_Wz`RZ}i6R(C%@2MAbB zO*J4Z^IWF&AmS86r4WtN^chS$8Q?BBwFzvyLh})f1&5A ziQEG7{POeR+|a!WZNy@(8}y!83XV*kmCLIc;aY`X75FsU7BVUh3XU_5g8ObfL=mdp z;D)p*VMgsDxkd1#ZP-bHD?5P}nHPdV)~U+RqK1Y?ki7JdZvm88?a;AT%v+73cR5nC zVAfmVmlLeBoCBq5*mVyPmDH$ks_#M6Sgyq3^P!nu#eOg}D@??fr4#WU-{CA7K-}M} zU7uOeda3B=n-N&I#A#S-iivx|yi3dR?_~Z6IhmN(BPuh?T9l2%2{AJ}U&%47qp&KM(D$Z!vxl%R!_+ z`~s|!EZP<9lUdPTJm__W_Qt|tZ^Y>#|H?MtjKJ0x32Y1jCi zBigFHcEt|v;{31*(e&ND<)(7n$I4#VxOF0L-1rXX@mtL`cE9+IfqAE0Gw@e1rdsn) zrg#E%V9t6;3=B>9?64vHnf~rHRMlfZlBNfR-Mfe#>n~;4-@rE851Am zV!Sx>lrtRKh!5TcWYZ6Se!G9sshVlSrCPVTzxh?dX`A+nbgB0jFwe$=j}Mp;%?EsYGlw6IKs4QTJDP_$I*p07c=%UDX6E5OO*2mlH#*&p#k`WHmsxS(N@> zvuje>5Q?!oyz+yP+M^kOO)7h-=?7Nd8DmCW8qR2Cu3=0x)%~DNz4QkG)+BPqx+(`| zWtFA2fvZPwD7}=_SgSVp;IE=rzDni_l6jHB zC>LfXj60@|UrwA_kaf{qM?NyO-qnek&XJFv53TeAe;|%L)AdCdSLz3tM^)6)4@Hy1 zVNrhF97!~5jKGW%=EE&I=0{=f^}u4~Z00QOqL%%iR(Pu^E;2?`kb&Z^xv%=+i_s5X zP4F31OpU_;IVwi>JFRNQVbFKo0r|Bi*Ip=|{{>XbtPy5k)!Tt*l1 zowJ$suloDXqrb%!x0T^ny^(?rzZPFeQLVuRE%oN*p>jJXv*e3m@IG!jYe(cxrsJOL z-*{o*kAQ--bF^wv{wQ&fKedl^61rnNr+|c|GyoSabVTk|(Xk5uPHZwIc8*|~r-RL8 z{vzW59Y8RWs#LFkd6mul)x386!4S+da~&YT3hn~~mt1;7w>5ej;rAvd`PympCy^~Y z*mjk~TGc+$^pc>rR0agVBb9*#KS9*$4}-`RoyZo@`wrdkf>tngVKCR+Nq2oSXiEpxx(&UF+uVJ5e*DA1QE%_~^!V(zqrKkw!HbjUyXObEf2+j-77@3kA?{xK zV)A=>7P(uO4!cmk2!9d=Lwuhoj|l>t;Sj7u0Xo$^!Y+d8GEuncO-9oTGWjB)HiCIY zqb}1k0=YaNDi4R4B27(){?K*&x)Wj#D_PTVJR_V-+-Hp^#K4W{m`~kFVIsjCvqqM_ zal;E3plkvBjA0=Grum*s$HO|)z*_hLUcd>aF=;N(tb(MQEStgL#?sL9i5&#UR_Hnd zd=@oV%!tOwVlm)^&>J@moT=fd7&HAIrXUi^Xi*R*+P94mlj-Hh@0o%I74q>gHA051}o7R!8~Yw zU_-gWBGGklYfmFEMnthxjD{#EgbJW_lM*Ip?!=N~gUP}u^n-50_6Ii25vVjB(TV$n zAc4m?nh5e$XZuZz-&=2-Ko}>Rhchj7{42NFUWJ9Q-^A)GD_NZkwX@SN150FothJvFtqCSAZAxe6 zvUYq!JItD;&1f84)=J;diku~-?IfmCSxcdzB`FF@TS`b4hViiTCXZvwP#G7XBBfGg zloDdB0&1~Q4qVyQoESq5TGaHLO_4B~<NPd}$42e+q*)QdipM6?4X3FLJTqz--Ax($(y1JuX-b0O-9WeRWeORyPcV*pWQZ z+R6!e(tYPmnli-s_$PizN;L^wx7uRhR7O*EC9s#YnQ?{_64|)bYBln@ByF9z#T47M zHMVPI9bt(Te@+_)RsVNfWu!?6<#+&kBWKdT2GpBnfL4s70FxGPNonTNTC<08F+p$Q z(9OYM=9aVcj$4Gu0PmVJLXNFT6Kvc>#J3fp>wb^a;FLnp_{HQYN^(qGmUtN@vxkdv zRL8v@8JE2t%IRKDj1mcLDePC4{_9KrjGexV-oDVja5kS@xR2KQlL-H_hryM7i?hHD zTb@5p8*TV(ZEetB_@w`CJZg9TtF!)StMh1Mv%Rqi&%5pQHk4mfCyPE)v>KqvFbF0^ z<;vctJNJcr?*0Ayli$vNJU;qr%)k^O;)f^;(Z+Q5b`Y~WMs)a&B5g*NOY)*C+{PA*Pn)zMoqlhV-qt% zoWKl5bjXV)!YKScT}rz|*3Z2dQ?Wl2QHTf>x$uTyFR+<+ayPjKgR0wlM0a_tx4I9P z2(@1CatesxwD(4IUkpo@OK1jLS+%CV*T=0I0Op3y5VN#iM8R+h_7>MzwFcgehX&iy zQV*oe_%8IWt|ur+tj3=FX?5G34IBP>OkIw#hE*JT4%yl6T}&rI7&QRU(CfQ?T4b->GzCRge3*C+O!D zNe!0QZk%up#&>N5wl3Hh(K5ruCHLgoi*}B{YNRK)Jl|1vw)CG>bCn4<83IhY2C~h_ zPxj`Tv@c@W9xkVR5zDyr1%fQ+w32jgFzt3MG?9;%YwtMx=Y0o06of`0TU?mT(M! ztzm)ILu>gt-m8t`b=!I{SY-~_!Eb!2;Ddo>gP_3z7!5{QkJ_Txs?}aG7~t|)tk^%< zp)H;mrR7j;=*az*$aUW$Ou+T*;NYj;!O=b!@ZH?!|P(I=jOYkjvPiYCdMz$;xnX$u+%+FX8yn_EH~U|=!|^9K}m@L<4MqX_1z z#A7A3{{$E|#j9^Rt@h@M^$#PSSsEtc++$>M*AyiA1q|Pv^{*)gNz-;CEbDKvek67t z9qo4Y6de9Y`4ekSz|wO4L8|om@&56)g{(NnW+-Z6;*7?m-v(aPcftYr*rb59f*~sR z38+%boACJFv-bIVfML*o%V)=LF^t9OUt2KRENM|8wCLZaBn0tUfE&^L_F3=j;QZNh z;;!S~(ecsK=f`_;Wk@Wp)WYpG4%~S-brWJb$$iK)VsN(M{RLq3HaVeLhGJhCJXD}Z z+oG^4O&c>inZ3(ql2>73o@B#b(>F(K&y_4PZAHQF^2PSlK?CBBM z;5V-C(P6LK5aW4yT>t{tmw`kg+jwlsrd^i;l4b#St6IQR_53UVukQ0A zvTR^|e#e)nTuws(9$Eu;g2z)@0uJ6d6imZDdg%vrcb_n?2cULYEV@<|BO9PqGqbVf zrI%;BKOFSV4}Lx0X}siJpCqlVSN{;cl<(faccj{P=sJ6|B)4mpEU`7CKKouY#{0ae z_Y?OWNDDl9j~PBDQ2OUpE8ZTktV6#KGAI%rcYY4bA}Dx61)bp)$gKhU`KgT9z={NA zWQnCE^8Q*)aUZbsPKU0w9QCi=!4$(B`a^FS?V$LH{OocNoqDl^WmAfFPv1?S1i>&` zqF9||spl981WK#5ESBfxm&6wh0EFKJ}iu#EOvZVUNtg~FInFROC!dd>Q7ZQ$Ol z`BP}V8-K^u*(>Y*|3I1Ft+w^%TVep^*6LjcBX_59zd7(iviaew4z_W>-B^MDEurO7 zR11qOU3yDPk_UER$t_q5zY+)R@G>0cYJ{^3gJf`TVuUu(By_;8=!c^oGG}kX`$W;w z-sy|p!LML4@3iH2%EJM+w)lzHCLJD~o$o$>ez3o@DIe2>+S%GzT9ONN7{p7kM6CbB zI`|S$|K_#dSo%XN=7wPqX3Ftceq&MVXli6Dq7TxEdZ-Lk^}QGSK&cLgtVGw|C1=*z zJ^kSfo`2H-!^(-G>4@@1%iWp*nOeRJGUxYcfKGC0gpp%qL^2XB5tUtpPKLl(*8~3?ygb}Dun>M?NMvoj zvh06b_s>sve?C0Rl)VQmJqT2yAv`#UGmCaOE&`BF(KPcC7~{r^Z{c$L*MZeI@FzkN zBe5+Ex`*-u+)0SnKU02b_buvQLt+`Z)u3VDLE%u4atbnZ!nV2V_y8Cm_yA*g=}3$m zdy2!f&JWJcnFV+ZP{KCuo1=AtOin4~-aiJ#wotA2iyY?@{Y2E4uXgIm@K+a`cW=F9^iLag*sSmx*`W~~{ z=#&%L@d3$WR3`?~ptV|23SrTO%O3>#ai~ISWuc0I;bgyaN@?qUm{4Pp!G1{n?49mu zPoC`_V$bLaf8P-e;EIE60kESQ_rGs^3)YqDvTTnP_R`-3V0}>7_!BQI8&+Cb`!DVk zn;n^;Bs`i5hHO*9SxJtT#P~jcNt+L1j0SS^h1nCk!{r+pgydA>X4B|^eIhExgb>4^ zFE5s~wo~tqf5OpX=Oj?)o@HN6kS>;@t}yXB3Hy%M<|+HBh8YCS8km(J|NLGpgFt7W+ zIR2-*vEEtF#{aZ8+h6_vAK{aU|4FO%RQOLUAHKMVn>I$=blaaz#ElnZ18zds5RU^K zI#J)Rhwi)h$8_Qio zkyr2*Z2aZ$p_bT%+FBC6QfgNmcEPl(cW;$A7W z`O0c=QsfRE@jSk~#G0TcSFyx~Ztu>^nZ)P-KSxIIaZc zJiDZkCvox6?E}`~of(;STyTs>2xy0|s6sr-LJjr8U}zr6jy6VkR>*2zh0YjdR5E6OiGh-QD6bei10_VSr_t$=o_mvUU1+h$pWmm9i;W z)hh10P!k5!_;K2(hvyD`*s_ATEHwgEn$05SxYp zci)6x4Yv;iijdF>6wA~w>i3{?G~ZrK*+ZAcOq5xdWpdtc>M-zD6=SJKs#IPHmKIc` z3HFD81Fc^uP z-56|4ZxV9~?WKo~=TC~qCkV&aj&Bo5j_JGQVb;OSJQW2yT0uHFX)e}H6{CcUC=_rG z5OWt|g%;T;VysT+`?ht2Vh`iB(@l!P9lh`#mCQ_FQM9WURq-7I_YxCp7-X}kww|g+ z5(E=_xXysHM-2mq_K5ivPHCKFW@sBpXjnC;38En10ecG-WA*IGLR?J^P92bg|;`aL1dB*$dNM^KS6L@L|ij87HbAv zT4bp>fjV+s1M(lW2#Eg?TE{_{n;v~%9`Tk{Em_qPRU;A68fJ$j;*hkynUR?&HfXM3 z`4{1Kscu}f4QC3|}UN`}n6?g)k^F0Npz!o6VkncHiCU*wcFd>$J=cKU=lGx8k4kX;$-J}o>5m^n58ibK#MAw z%Mt1Jf)HbpFiC=Ivpgf%gB;tAtFCMK2vJMJoP*IuPH{YlFHAK#4YO-WJd1vDC)VKf zHB|!R9;NxVC%+deUf%^f3}i?sD0_gb6|(49ePnvA*XXc4s~1;hER6bYQC;R%GvQXQOQk@l01?Q36de`i8^cmPGGW zINomK3dUr7(WFU0AUuMSBHQp2k>mAoG>o(Ecxxq0j(!dfrW!s zU2(x~=2bc54HJqWpY)E(+XO+z=*9_67E#Aws%6{SG!Rra)u~R^EfdOOJ6e7$u@5o-9wT3y>RP@;MVeUyC8TcnYXR$}-uSnx~Ww^l5qHeqehT zKRLv?z$~~YX{8N0_fsHM7iu!0cnn{RMPzkWt?sIE>;;*17O36)db;`47}BCY(Sq(0 z2z)|>m)B~{! zApb`s9VjFKMHYytKAp7aQhc&GxZ6B0#`ab-oixv>Ho)(;!u+Ne*_k z2{Z?Sg%pH#)GRHS7KeiWawccJIbmwkOpqCxt?OPY2VsMJ=fly$o%eTU`>kMs(1v(a4Cmi|?(3i;=D z+Bp@Jmoi=9@aWm$(c$@TnR?1K{YV56;L=+}dkA8aRW7{K`Ad3gpGwing^H`q)A+hx zI$wuuB^sh8IG{!C=5b?LPYyMKSHi09(Z%<4W<+>`>1|HE&~t{~zlG~l{7^O7h{YtS zo`3CKc(jOQf4O06BLxzXN`iGrq>v|Z7Wll7;#S5XrYFFN2{ouZ7-B6da$Lm43Sf1% z5^EoEdz2a`z_+s{orntaV&1ghx_5ZUPh&Z?Eg)XyFstHz(~QWiD;aj zS5L1HBgAyw{V<s9Cl7*9cVuR}ECMU$dhOgSoLtk1jjX#i%AH`j=U=zGA(86s;kf`*%;B0@?b z7fI>Fn=l3OC)|QFnS?2n)J7w_D<&NEnB^%3;EQ;dxOXZ7CXH%NoC3oE?h73$jSl6H zlK2^#P3xPf4$#%xqD6@w?+%%pW-$X3;ttH`KFce8dWwh%o>}5xHf~=DeX=&76AT08 z(sVR^KnL56u3K!ncRB7++B!|7U5e$3#mkkxGwe@?ypO}Ttf+pXzZ;K8oBWtis>dwQ zyY2Z-wp0D#z0LNcHZY)Q7mqmy1Xso3IOK1UAuY0Hl>W?IXm>Jo!>DgWfw=QSV8Co& zh(IdfjP*zsh0fzgxPd38O|0}h^v$KC*7#sO`}kdqnFYg3KcI}<=v&3PQ-+Y( zCnb@0<@l(4x#JiC*5?sN6G2crg<1kuW^$(6B6^iK#bmA3lIv5P5EbdL^uL9fWQph2 zQ=W>umNeuHJ85)fq^QtCKvm$hQ0K}ZtZUbq*sg!&`Md`dWwbBPbulwxx|77DS)Ys; z#W3Jo*SoroN<;X}AkmgK#!AM3qiXS~z|>?k!au*;c;#0jqT>w3oDI_M_bG5LSZ_PL zeUuG9ih-yNrUEF`P#zFvCZ_?A*_>M~X7zNv7sgrz-{}}HWhl7Eg<&mb z?-AY=4g*c&awbD&UT~xg@}w!ET~_MmRHE6JQ)fM=j*Jc{Y;dEX!8J1k?BCjNy+tZctX?ZLoy~W)@k2iQcscxfYJ3c+ehl@w%7cy!DwJ5^B*8Y(zGsh-+n#^X=}&G3`Q z=rKcWoKTO$ET>@{WT-`ci7JH>iHAx?7>SX~o!+8!H(mNNblrdFm0nMmHq9SVb|YJs zoJIZ}n1eZWHjC=u`3)F|j7Ao9w~FhUW8QG_*&o`Q-Hs$gym%o_Q!~UK2Scxaw_~PH zH1+y0#_Y64H%vN@(S+&qh{kDcwe%PA8j4%%OoUw$Fr{*ka|LtGft5 zw*)|O?R7)#A9p#Rs8aJAB>;+RZyIXvoxTv&D>T1V0-(6|BSUS!RGz8ok4vhXC$Osi zq@;SD#lU>iu29>$T`ML)$Hab$ez$94JzGY{j)@7y5k(p9R0_kyiYgt8P-hP*=8cdm zg=gX|nK5G+j$r-TH?7cP02`@Yjv<}4!_%(IC94$F*Wx&nFyMBX2u&Q7wbX{05d;t{ z0qx}jNl-RY(J_Va#E^jS9Jq7@^A=ssVyhuOTWKfD7q-7ywHFjASpndd0pKJktn$cM zr5+t07t%2S?E_HpNwpr7(hz;aV-AkuMbq&x0Z=ZV0C0BZ(y(D$c^+>i$0oc*_d?Ab zk38kLfg;={+4XQ-S)6pr)E1Y=mDCQ#y*v=Y5l{(;E&S95Fyc*}{#1fi8MUe)N5M@w zK$#~(ZN;K!9fL@frg)lJSjp1eMCpi<8|M~YPZYjP z&x+i78%6byxidA@`O5Crll9shov5vMu_8|=>kAXhxAS0dqzG(|VNxj&w6JyFc{+W1{Qxi`=*f*m{A8gF2@be21 zB2VWo!P?H>=!s?Jg$rizKz1ACCa15{VJrKV%UY_eOdsD5eBW%Yc5KJSlxz0Ih_;dJ z3)%=9++f^`Vokp3tYz(TCjX}`^&)eup=&!M0g(YZ`2bfHz*Y5PSGnR8BW2oHkJ}hA zu{GnVI__9A4SFL1nilk2oP2kcdh(Q<2U@LE6PtPyPUR*NJ;2~OM1PeIr53feC214; z6!W?uf96c8M@dNJMV$IN>biAO)`-Xg;k{EiB`}0XHbMJJYsP(}$4T{F;+CD6TYf5Et%_r5rgl4RZIa4aCHFACh)oDEkq zEpzOv)5&exmQsH<9G*(kfKk}xa7lo;mPZE1W{;<<#EUlRg*Q~fXHW%V%CR|y#t4gF z#h`+58j=Ea&-$Kkx;ng!Q)E+Mort0n$*6!Jaa0o@DCl2C+usyA8<-vNytb{M%%*eD z`Xafh@;9uc5;#ytC@q`Grq95qPViYYT+0kGhC&?c7Ll2$OcB{gvKVBXE(A3y-n$)V z5aAG&GGH8yLckdA$RU&XCEa>%sb|QeV>iUr7;+X?Bcn;qjDTW(Wn?PDn3%@oJW+9q;k zv?`Lhn0D2n_PPkxb3>p-3`0hHuGWLLF*`I2xu8(gjKvO+Hs^sWvuSipkhc~Axod(< z9tw_d$u~=(+~c@hNrE*f{6t#qQ{Vf2>Y5F1{XewrsLR_w;; z=dB+(raQKm;>25Yz*iS(@pZz9Y!V_dK=S0P0lIQiXi3RP5&u5+26#PU6o|v>S3{c1 zbfKW<8)gFMP8@7%UJkBmxK$o|Xth=mAE zR>7)95x{hlS>|FjCpHw)QQhJ`?|xdv2O=Cb9>IQ+HROW1oJ%*`EWNd4ILrjo{zOf(X=IpXWlX!dLAvy*Gi2+Z zQ5+8)=KkC+Ziv9EffotuSTe2%Y7jH&JF6qSODbx1$I3_;h0UXlm^=GY*ppbytMRxK z*SHe-K$)3^48ZG;hm;UZ=%&zJF`7g&Qlqo=I8UzRR(T{cAaQ88$ZAP9t4SA|NHn2E zTA7)VeD)knr18b7NSqnxa};Sm$slESB)59)L{*weX5r8!I>|44J(-CIEzbVvt=*AS zjq49$hHfTYV>xp;nW-agPo01GK#isIa_G%os8~0VIB)LeL-&HGlrW5J!9X-=#49Ud zGsRk6%pglW)DA zIvr7HQ6|Gm0-5sV>VS7>dLp)*k0xAtVKmYG6!f*#YUyu!BhTN-oT*I!o!kbC1saSO zk0`)gD4zJL7qhrdUww>gH^v)^SO!H>+<@`aKmjJy*-ws%9LdiCkxxdEDpOC{k2N{4 z$_$(xHc$=LP#+5PhoVU7RIdoyBu%G2#Ze_r_W}?27QZm~mShbr87?}@WO24d&=}?0 zkU2eRMs-Oj+R2Y1C~>hefCKl&W9vNy43{obIv2;b5tz?Z!{QnxF&KTy%vN9Q&<1>- zH3pDzK#1~NdF8EU)~hAzJWAK0TeI_8cx*0>-(HwJBcj?9osbU&TI8;OXsKB5ypc|` zg;raRA6q~gPPk|GTVVI&|i>8v;$EzJJ!vx7aKr%jEoCA8G_S@s-eC$Dw zgnxrF;Zafc=_k*%=783-k#3rWFz` z`4Q3V;Z=d5a)QPqm3DUKEU7z>H@n&OFN0lU>cmSG%v#MM@SY*8lTl&&UK6B~Wx_3QR*j@tQ|1W(DI}{&-kvAh{%k-d(FX6Cd;!j#@xt>Q6AY zkX|UOyF~M$>r5sNCM?3$q0K*V<>{#N;)R^fTktA$O(Y2sb4oPNIj_A-%bRdSq00Xd zJC}37>wbvDmN6yo5b$H3U#26;se%_!?C-WyHr$#{2Vy~Qw_AtdM|S@>sv0FBWI~*7+PK< ziAm1`uWlddgWZakzu*m<=9^^Xn2GcDeazR z7GFYh*B zls_!}qiba_=WU3HZB<>vq__HQnVkg*BQ{)(x$ha?lNh~tM<FeSK_c~$I8i~z zCNK}TG#~Pa6JM$13{8oEo_X&`!e6-8&W(rnw8$ggHt%$&anj)BuE|8(V#Tj4hU`&c zc<<#ZVwx+uLkE*sgDe0It4}s86=&*ue)__?_99AAmyVB0*xUNx;L_vCMKbxMNPC- z+$#Mp5-sVAUC0RIgaCR7MM1T$UaeY{^_(iEZ3V3wJB^f$eW+FB?X|%)6vy5(*IXwd zRRNKalv;glU6%`D5Xb$e6H=LmkIhJ1gyzyFgc`N1QlzpStt{V;ol~%zDY(jcg~H4x ziKwxz)j$VKL3y1)A%C|2+`0!pfTSx*8n# z0dRIFn7ozuae|n+TJ7T90@$!Bk&cJTdMd7{pcZRHwo;=ckKEe3z z-6(R=ts8n*R~Rd|4a@83ZOa8yQyRLl_k!a5a^m};EnDMLZd$k%vU)XLyR**G=v*0p zsl224VSJs9$J)5zFQ_EeVLK-N>1;enR3IE>E6#;CObbXdE!0AXZ??*f5!Xs~lO?lv zQHWWk0wZXOVJdY%D8oszaK}(M7joB7IK%j|@VZ`DdO&i@Zs=t>Awgo9C5jLFHV2mI zo4xycw3QoKafAt;g5;ae`F1%+tR>V9ea7wH%|~53>5~9^P8>o>1m5c#uep%Abs@!J zHnF3eGo|r)Jfnw>RjKJ$ONEm*UBTG<^znz#oKr!b5^;x@qBj% z1I~!zkXJf45dqfLNfe(465b0;)hM>eox~=&37T)OKYsLxz2t@<@#0-Uti0L9z5c9M znbVFby!c>IoNW41$~P3;V9arPQlbP%+?z=FKE;9?(^co)W1=IHUK5Jduy!6wU8tS7 z&sE*%_H)4b%}6sAvAp;rduf63hzuwrn~VY*^TOF9JgF+2v)XgI zb)1gC^r%FS@_*-_8QR=rVS0Xiba?(_9F|V0*$tXQ*4yIS*`@eH3*K|(CRtvk4L+8U ziikjy0xt$di)!M$b!i8=?+z%oj;GnUCvFEZ_9H^lJZeAcY$i@m+>GpYCly4KInT9L zByKkG=IULM>*i@37$I&CAlbJo?L&1}>y#6n#qN4@mh?me!C1mi=VVQ{g@*y3#(UPh zgGA977?tP@Lzr22>blPqE|oBWiB8DylrKO=vncu2SHwV_^>7EPNkp|9UUzFEwue!w zyZFyjd#@a|J&tN+47G8@VjUL$ zsr{eIq*6G1Se}{-$sbOSU!KfED0<9@sKZ+GArkZb2O`+Q@%#w$b1t0qtD5x9P;n!K z0dG-5T&EL+Z&MAHiqgC$`6i3mS~MMH;0D`|gw4u#&g6x+iKfH_^qRI8JE&~*Q%soJ z%Es}EH?|2ew7gB*!)DypAE#nUrLQ-0dOE(bj$+XUE=IjYd2qX%yxiEGPJ$71qfaJV zpCkUqapnxMv8r@D=L{FI_wtC>FmTMb)MGQIK{O*pv-fWwUYLZJ6gt{MBS|? zID{@ab0{a#In>Qil`cMzxFL&3=OIPtaNPp%1I}}lfp1rVuPUcm;sVbWzb36fwD4#n6hz_!Sz`5}%fPLD?>()Ldl8 z(#lyJ&?1p7j_EsN;nKTR{<^r9zHA>4r&0ciH13ZOYVpv?Y^1R~BIN+OL23k!8cG38 zY|zn&_k_!^hwgZJmv4HnxV^b$TY^KzXI_CmDFggi=52!^>k%o2n*@+C-n;12m_#eMvupmE@?-1ejMH<&Mv zwzboKWz!aRbO)R@;&4v(mC^(2M|xs4SFGgcUUu6`gV3!4A-k8QL0GQ>A-ln)LGVr8 zID)yPgDLKYZ|X+|L}5RCQ%5o&3Oh0~bwnmbKK89scd$~@k&&q*84!gX8JRkg0a4hI zz|@grGz)(SLCUOD7?Hr#kqn5!js&KTWIz;lWNhllNf=z=F3F8sr6XfgM=~G^J2EzP zBm-i-aN18N-q8EE%9?bdPOqFzaA2=6I!G^*7WiNyjreYIV^#S+y$llhvvTXH0!6s1 zqc*k{Ga$B{M&7?cPa0w82N=WvRrpjnIAf$$0+^nbssvcxDaROC-x)joYM7~_6&b*m zW}DS`89%jnbzoa(#z41B{cc|UJ#mXd8G!XX07BT50@%m{@Ei?~ILUqU!tdYPda}8( z(azX#I#6{IE4v2`%WxVR;DXV?yOSWAT!rr0|32S;+NDVC{-nFg_l#vrbVP}aq(Oh7 za)INzFJ24AQDeLyRL7ef&UJ)N@Ftl|8ylHEW#F0uoVyfeB|WO~+EeT=gE7HplqX>@ zn2IyXiRSI^5*HAq_Z>|M1JOaxO zk26c)6Asr=Ob|{va3SN{Fb#0%3cH?+)Lz)ve3deuQ@s;+eX^NP`CH548ASR-3={U_ ze46C(>Y^i&t~b-(m)7qV*M}(C})Ug&e-vcE=p~%MYbU?k{6yC z0&q1LlOPz9WE^jeUvd}nE%mCB4ToGbSdXd|S+%oYc((*b@qYF9%q+H~Ew#;C(j0RQd@~9F* z)!{uh!yB2+jZF0?#T~0URGl0SjyqJBfjT+tObnC(TM8|Qt@H7-np%dC9Z@qY8&mCc zOJ{O9&vpxXj!)Q&Sb>Dy+_}t8st!drcP=B(=ENOlfaFYOjnk^Z$(hmmNY?CL&g4!{ z$*KtE%Md-u2+C*GfY@425+mt+^B-*Kpcs$ z-22TsP%HO;s}9u46nInxDlG+S5a4kQpq2aoqz2H+-De}G6e`^aUV{LgN|Q>0txSSW zrFkX6RwqK0sU@Z>o{bn>+|@z4(j-d=1Jz6g=#?g03is-isWs_lpiQOeml73qs8eYs zX7I00pE{GWK6$#;W@kc>)Sync+AK|BUzs@FTC;Tq>QtJxDIrsbJe4MH2LB9w3ak*m zjVU<{nX(*A(5hJt0$k}NsAw$+aHaF0qLm=Pl}&}3)`5UpIw|T|1p;d6 zboMCO0;Z;$KGqlaB&?@zF zt17fgJ$+OLt(q2RV2>*TtJKdY6@gXiW=e~jpjFf20$k}NsHnvSxYBu0QHu+3WmBQ1 z78g)UCq+FiE})i9j(S>LK&?DUYSN%|y42O;0&eNdsjI~W+^UnP9vQmjv#FjI7hub0 zRXr^(z*e4JHK|ZK&FX4#0k?Fb&85Zl>yK0;B9U=@J+5DmQ~_EEVf9;+il9}~<@y~; zg}7DJ=K7sVmEcwD?N()Y)%yFW6kau*u3y+x1zfe>KB)?>T4z&=-3YImW=~x5R0&|U z4tL7uMn&zOxQnU6WUDI9B;4|eQcuH6xaAY3o{pDrt52M|lqjDgs$6xqR}?rS&mL6SHun^!{ml_IeUo z&5p&zONFHZ*R2MwguDvedNpt*e4xN>6oKmsa8dQ%ZdQa>wYysjhWDr^=G>90%7>`a z2=ghbd|bI!8dt8Bk0#ejqsg`MF=Wn8RpN<|TPLM@<9ce|oZ-C5?_f&V<^jm>Yf8Q5 z0m$!mN}=Wf$Ru-*nSwkU@%>{ozbSufd|M1(wu9X)>0Q> zbm_W$4Sjr`b2f6~Tt$N0b;gCKNq$Fo%vVi5%9x$8X-M5tNJ+v|D61S6gG501`e{%B zy0B*&t8?iTo10{zx@kuqos$>us4)7B0#G%&=^8TbmCuaiS6ok5q;m#kwKmeVLf65( z@l{e(#Za`smKc`j3x2H7Yb~0^2%stvv`7{soJus&LRgGIW+IUn#rG}n!81BEoV=XS z({E`?UE1VDmJT4*khl@C(|+=3W0UOPpYUFwX+-yqU3xxUFe_lK;Wrm^Z4~9DrJL+F z2-Xi;4kXUbL7Ay3EL*=|krLdiX0kvr$(oqkGVM7)f68}Pi6;d(Y1Ks{XH!nk9B|@F z%V6ZjH-g5c~Z{4<0#?q~`a+K?0BlEVYhJ)G!42lM!3e|Fsm1OY5 z^y~2*Ab4IwCe(?KB2vn$3v3x1pN=JYeAjr9iHW7+^9C7R89zmArV2KBe4^C*+p8_{BdO3CGNXcCzM5cctPHnenkmAH6&I39G$mW z<%$ce1a+D!Y_hdkw3?i{Krd2ckiEhWQ@lV-2<;8(gAtS5=mG z1!;T;9i@v5jVJmOVAdrVU4upP;s89ljc4Hnrzbm_VR8jn$MNwQFz}1~qXL_`;yZX%rE*$RDhWil`6kYgi6%k+y+vy(!=sAyCyCS}-V!C5_-Bf3J`*@ePG(z*LcH z*ocNb@K|po;7Z(t{6>J6jP(^}lXh;MOkxQQuE-3EP+1#mKodk4ck8fPj)_J3$_$@S z#=H?kHF?|IuDCl5fND*lF4yjulRcmt8#%Kzae?C_M_st-fiS5ZL2qei$=rcpW+G28^5@@>DQUy90Bu#hdvVhJTe?Gu z=_tB6bT233M)jCw5D2b4#htY*2+Zq3Ir3tWD|3nDARvmx4xLCWS2!uWuu3oqSAv*E z+tu81w{p`W5tGPpe@P;WGcu*dyLc$cbf$@?n&NB_G6R6PcTiiV(JcU>tKE!M$TRH! z;AYHyb5c#J`x1bmFi`=Fqt-k~-3;$KVL2XV;YnENUPD4?2B4c>zrEICzrv6(WZ$r#z zk~f#s^~~2S)w4 zo;?<7O1B-9HjBZJbM7%#dE-)dl_+IT*;06UAT?Ma49@ zRRxr~gn4o|&T_RFVO+a{s7s0`Y9jlJE_$*WMnFOT5)cAAV8DJp2bRR}D0r*j?H~Mb zy1Rd{Pd8ff#c_dk{8NVN8lawttSf}qd$D_VesG!wo5Yg-xo3TUe!PFYZ5>{M?b3(x zZ*gx5_j5zwrG-YWr@QCHQI)MPhK>WZ*? zp<|2ksQ?uTO#$PSImN&zgKG*FKC2E&{>|%j9M2-IKL%Rq@aR@a zycmkE8H)2op|9V`QE-K=bZfSvt{Y-^)tTjKIqk!RJ=82o*+v~+%O zc7E2|+IV_+ZtV!+n-IZwVBpzZaVOdyrE--istC9@LdF}7BY6uHmbKd*yYs|quWxrY zw%d=bm*;!Bn1OchS$hG6X5sDd(+e=oCR5>vLVPc#-Vnn_0zf%h>JObLvYz6zpPevj z1{ePV{k*~n%+8)39_=5V0``sXqYDoNUif{3-aLJI_|xGkZ<6fA@lRIvvxEP%>HWd!>G7%YeTUv39iR6OkIv3_pFamds=wb9?+=eo zUY=*`Z*4)JKw-ertmnQ1D<0j_ZgcERt_5n1M#JKP2Mdwz+jFc-ym8x^pwrO9H3k+Z zueO5F!g@;t02aw{uDQ^iOhexaq856ixSdz{wRLvhdvUyf@W%RYi+}eHe|>p&N;Tbg zeWdU4*+CcvVFubWyxCCnW8ht0HW9zhDxuk3wZ8f0?JcZ7BfUeQzo;lJes2RR8pSvZ zz`&Y<30t^yXu^cRN;ntnDgApy|BCCiNGnFDK@!%d;S*Q67&`u2dx*K?8;r3;9HvT) zy69aLgp(e=r7rr;$kjS^1~1j%49C~bg)5gls$5nfj5lU$QgS7Hj-c9JaOMmcn4Gr1*Ny}A~; zK_o#BCPL))P}DUecL=S**Bgy>5cmm}!C5l`3xq@_mYTAOuE61kpoK6f@SeW6c11yg z5jG{E0MVWOgR{MrblsLS81y9c=Cb|s@~X9LqvdrGL@xZ`ZM&Uiy4`NMSlnN`!|~4Y zKHcj8Bk13e>TyF{AVw@J<c{kyeo?uPFs1DfdR9*43m$baE1mF8r}oM2`Hz**IV0lUv4xbPM|t}m zx9)?II&hYNv zAYg0Y7QOYxwvaY6qj-2J13FX4f!g3JCk3CrDqa)Sw|(*8oa3e2%x9ld)>dEi#%@LIDhFKML^7 z)dg97v0??4)?#tn!*)o~p{4dFw&dUuJZZ<)bAW-fR}`B8x-e;bh|=`up;!aT513-~ zcE=K0B5SrZ5#p*DtrdVv?_X^r_=lZ0tc+jrukANkqvitav$sph-ijWILDtke!6GoR z_IJ_h7K%U8wEh@aY0_-)k8gMtybWI;L=EeK73m;&L%$rxv)w~jB5xaE;9W72fqTG% z1TAHRobt+0@=mP#@Tw8KDJej=`(%ZLEwF`}tqB7K#z;}AC5vR1E3SXzg@ONS`G?-w z^TWM^Dv53& zoBTjKcd1K&;xZwafp-7e8xFwm+3^E&F_8GTHXTMlH$rZ)A#faDK(uo?%|pwdj%-~2 zx?!|~N8OF>=^cC!ttt69?vLKK^tZ?^#1So;E4V3^oC$7iFFM@!k?H>sH_%%~vDo2qmKitpPJWzpB$0}Z_}HirZmpl(JA+>f z4Gi+Qzf!K7e+8c3%rf+<)p$1=Hdb$8FOsa`eJ4ud6;tSlMyf_ULLKta^ z(gExT+>*jrFI>!-@q%?h6~tt*oeZ+s3Tz7Ylz+IaH?4S4Eb6w%%dfeIhmi@hDh=t_ zS87XV_`SoYz3AE*1h-h7I6VV&DGwzm95Xo&&>4Gth-L6nL92-&a_;X_3Ikusmp%8a zm*`N#m`&XAVF-AB!lI4~Z@l;c>klsA`LN{68Bny1V|tMocHd1D=Ob*I3W*vkD=-Pt z(yh5G_vA^)ty<@IWB$65b>!p4KD1oBbvSG4vK(RJr zVUk&NtfUYTIy_q|X#gcKBn3;ZNwBSw@WAL2i>1VJQ@vxqExwASBkxqm4I7q`lIuJ1 z*KSH7!n&Kho$Q;|T%JHC6@bm|xrFLVxU3s%1NUalpK{op^zOs69zIKsu&3?+rE$ZI zCFoG!kyTf$ze6vxTBjUBdZy6}Fl}PcX0uJJBDMh