diff --git a/SOURCES/003-pacemakerd-output.patch b/SOURCES/003-pacemakerd-output.patch index dc51945..167e22b 100644 --- a/SOURCES/003-pacemakerd-output.patch +++ b/SOURCES/003-pacemakerd-output.patch @@ -1,7 +1,7 @@ From 7c35387a9896cb968cf4087b5cbed94af44e1ea5 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 14 May 2021 12:03:46 -0400 -Subject: [PATCH 1/4] Feature: daemons: Convert pacemakerd to formatted output. +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 @@ -135,7 +135,7 @@ index ce194bf..bd59729 100644 From 35e6da64381fcb092d81ce16835cc28670b077cb Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 17 May 2021 10:04:04 -0400 -Subject: [PATCH 2/4] Features: daemons: Output the pacemakerd feature list in +Subject: [PATCH 2/5] Features: daemons: Output the pacemakerd feature list in XML. --- @@ -220,7 +220,7 @@ index bd59729..93cf743 100644 From 5b7f5eb35b025b59805cf3c7c3dcb6a3cf4b71b3 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 17 May 2021 11:09:53 -0400 -Subject: [PATCH 3/4] Low: daemons: Conditionally enable logging in pacemakerd. +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 @@ -257,7 +257,7 @@ index 93cf743..c20bde7 100644 From 2393362bb7489e86d937ed46a1c5cfb93d9bf3ab Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 17 May 2021 11:58:06 -0400 -Subject: [PATCH 4/4] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd +Subject: [PATCH 4/5] Fix: include: Bump CRM_FEATURE_SET for new pacemakerd args. --- @@ -280,3 +280,64 @@ index fdfc825..92a98fa 100644 -- 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 new file mode 100644 index 0000000..1683026 --- /dev/null +++ b/SOURCES/005-crm_resource.patch @@ -0,0 +1,866 @@ +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 new file mode 100644 index 0000000..c8d4e3f --- /dev/null +++ b/SOURCES/006-crm_simulate.patch @@ -0,0 +1,896 @@ +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 new file mode 100644 index 0000000..d4950c8 --- /dev/null +++ b/SOURCES/007-unfencing-loop.patch @@ -0,0 +1,733 @@ +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/SPECS/pacemaker.spec b/SPECS/pacemaker.spec index 62e1312..531910b 100644 --- a/SPECS/pacemaker.spec +++ b/SPECS/pacemaker.spec @@ -36,7 +36,7 @@ ## can be incremented to build packages reliably considered "newer" ## than previously built packages with the same pcmkversion) %global pcmkversion 2.1.0 -%global specversion 2 +%global specversion 3 ## Upstream commit (full commit ID, abbreviated commit ID, or tag) to build %global commit 7c3f660707a495a1331716ad32cd3ac9d9f8ff58 @@ -271,6 +271,9 @@ Patch1: 001-ping-agent.patch Patch2: 002-pacemakerd-options.patch Patch3: 003-pacemakerd-output.patch Patch4: 004-check-level.patch +Patch5: 005-crm_resource.patch +Patch6: 006-crm_simulate.patch +Patch7: 007-unfencing-loop.patch # downstream-only commits #Patch1xx: 1xx-xxxx.patch @@ -946,6 +949,14 @@ exit 0 %license %{nagios_name}-%{nagios_hash}/COPYING %changelog +* Tue Jun 22 2021 Ken Gaillot - 2.1.0-3 +- crm_resource now supports XML output from resource agent actions +- Correct output for crm_simulate --show-failcounts +- Avoid remote node unfencing loop +- Resolves: rhbz1644628 +- Resolves: rhbz1686426 +- Resolves: rhbz1961857 + * Wed Jun 9 2021 Ken Gaillot - 2.1.0-2 - Rebase on upstream 2.1.0 final release - Correct schema for crm_resource XML output