From 84058571689ab4cf1c81c5a809a4c308944de1b4 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jun 28 2022 10:57:54 +0000 Subject: import pacemaker-2.1.2-4.el8_6.2 --- diff --git a/SOURCES/024-stop_unexpected.patch b/SOURCES/024-stop_unexpected.patch new file mode 100644 index 0000000..0fcf75b --- /dev/null +++ b/SOURCES/024-stop_unexpected.patch @@ -0,0 +1,806 @@ +From 767b5552ab49850204692c2c990dfb41d37589f3 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 28 Mar 2022 18:11:52 -0500 +Subject: [PATCH 1/9] Refactor: libpacemaker: drop unnecessary argument from + "rsc-action" message + +9875cab129 moved the setting of the "moving" variable from LogActions() to a +new "rsc-action" message, but continued to pass the variable unnecessarily + +Also simplify how it's set +--- + lib/pacemaker/pcmk_output.c | 10 ++++------ + lib/pacemaker/pcmk_sched_native.c | 4 +--- + 2 files changed, 5 insertions(+), 9 deletions(-) + +diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c +index d864c8bd2..56963a93f 100644 +--- a/lib/pacemaker/pcmk_output.c ++++ b/lib/pacemaker/pcmk_output.c +@@ -873,19 +873,18 @@ digests_xml(pcmk__output_t *out, va_list args) + } \ + } while(0) + +-PCMK__OUTPUT_ARGS("rsc-action", "pe_resource_t *", "pe_node_t *", "pe_node_t *", +- "gboolean") ++PCMK__OUTPUT_ARGS("rsc-action", "pe_resource_t *", "pe_node_t *", "pe_node_t *") + static int + rsc_action_default(pcmk__output_t *out, va_list args) + { + pe_resource_t *rsc = va_arg(args, pe_resource_t *); + pe_node_t *current = va_arg(args, pe_node_t *); + pe_node_t *next = va_arg(args, pe_node_t *); +- gboolean moving = va_arg(args, gboolean); + + GList *possible_matches = NULL; + char *key = NULL; + int rc = pcmk_rc_no_output; ++ bool moving = false; + + pe_node_t *start_node = NULL; + pe_action_t *start = NULL; +@@ -901,9 +900,8 @@ rsc_action_default(pcmk__output_t *out, va_list args) + return rc; + } + +- if (current != NULL && next != NULL && !pcmk__str_eq(current->details->id, next->details->id, pcmk__str_casei)) { +- moving = TRUE; +- } ++ moving = (current != NULL) && (next != NULL) ++ && (current->details != next->details); + + possible_matches = pe__resource_actions(rsc, next, RSC_START, FALSE); + if (possible_matches) { +diff --git a/lib/pacemaker/pcmk_sched_resource.c b/lib/pacemaker/pcmk_sched_resource.c +index a3d646775..41631da3d 100644 +--- a/lib/pacemaker/pcmk_sched_native.c ++++ b/lib/pacemaker/pcmk_sched_native.c +@@ -2037,8 +2037,6 @@ LogActions(pe_resource_t * rsc, pe_working_set_t * data_set) + pe_node_t *next = NULL; + pe_node_t *current = NULL; + +- gboolean moving = FALSE; +- + if(rsc->variant == pe_container) { + pcmk__bundle_log_actions(rsc, data_set); + return; +@@ -2066,7 +2064,7 @@ LogActions(pe_resource_t * rsc, pe_working_set_t * data_set) + return; + } + +- out->message(out, "rsc-action", rsc, current, next, moving); ++ out->message(out, "rsc-action", rsc, current, next); + } + + gboolean +-- +2.27.0 + + +From 870fb19715618c4ceab9ed4ae13a99658440b662 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 22 Mar 2022 15:22:23 -0500 +Subject: [PATCH 2/9] Refactor: scheduler: functionize scheduling restart + actions + +native_create_actions() is already overlarge, and more needs to be added to it +--- + lib/pacemaker/pcmk_sched_native.c | 85 ++++++++++++++++++++----------- + 1 file changed, 54 insertions(+), 31 deletions(-) + +diff --git a/lib/pacemaker/pcmk_sched_native.c b/lib/pacemaker/pcmk_sched_native.c +index 808e97540..b8a1c1e1a 100644 +--- a/lib/pacemaker/pcmk_sched_native.c ++++ b/lib/pacemaker/pcmk_sched_native.c +@@ -1185,6 +1185,58 @@ handle_migration_actions(pe_resource_t * rsc, pe_node_t *current, pe_node_t *cho + } + } + ++/*! ++ * \internal ++ * \brief Schedule actions to bring resource down and back to current role ++ * ++ * \param[in] rsc Resource to restart ++ * \param[in] current Node that resource should be brought down on ++ * \param[in] chosen Node that resource should be brought up on ++ * \param[in] need_stop Whether the resource must be stopped ++ * \param[in] need_promote Whether the resource must be promoted ++ * ++ * \return Role that resource would have after scheduled actions are taken ++ */ ++static void ++schedule_restart_actions(pe_resource_t *rsc, pe_node_t *current, ++ pe_node_t *chosen, bool need_stop, bool need_promote) ++{ ++ enum rsc_role_e role = rsc->role; ++ enum rsc_role_e next_role; ++ ++ // Bring resource down to a stop on its current node ++ while (role != RSC_ROLE_STOPPED) { ++ next_role = rsc_state_matrix[role][RSC_ROLE_STOPPED]; ++ pe_rsc_trace(rsc, "Creating %s action to take %s down from %s to %s", ++ (need_stop? "required" : "optional"), rsc->id, ++ role2text(role), role2text(next_role)); ++ if (!rsc_action_matrix[role][next_role](rsc, current, !need_stop, ++ rsc->cluster)) { ++ break; ++ } ++ role = next_role; ++ } ++ ++ // Bring resource up to its next role on its next node ++ while ((rsc->role <= rsc->next_role) && (role != rsc->role) ++ && !pcmk_is_set(rsc->flags, pe_rsc_block)) { ++ bool required = need_stop; ++ ++ next_role = rsc_state_matrix[role][rsc->role]; ++ if ((next_role == RSC_ROLE_PROMOTED) && need_promote) { ++ required = true; ++ } ++ pe_rsc_trace(rsc, "Creating %s action to take %s up from %s to %s", ++ (required? "required" : "optional"), rsc->id, ++ role2text(role), role2text(next_role)); ++ if (!rsc_action_matrix[role][next_role](rsc, chosen, !required, ++ rsc->cluster)) { ++ break; ++ } ++ role = next_role; ++ } ++} ++ + void + native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + { +@@ -1332,39 +1384,10 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + /* Create any additional actions required when bringing resource down and + * back up to same level. + */ +- role = rsc->role; +- while (role != RSC_ROLE_STOPPED) { +- next_role = rsc_state_matrix[role][RSC_ROLE_STOPPED]; +- pe_rsc_trace(rsc, "Creating %s action to take %s down from %s to %s", +- (need_stop? "required" : "optional"), rsc->id, +- role2text(role), role2text(next_role)); +- if (rsc_action_matrix[role][next_role] (rsc, current, !need_stop, data_set) == FALSE) { +- break; +- } +- role = next_role; +- } +- +- +- while ((rsc->role <= rsc->next_role) && (role != rsc->role) +- && !pcmk_is_set(rsc->flags, pe_rsc_block)) { +- bool required = need_stop; +- +- next_role = rsc_state_matrix[role][rsc->role]; +- if ((next_role == RSC_ROLE_PROMOTED) && need_promote) { +- required = true; +- } +- pe_rsc_trace(rsc, "Creating %s action to take %s up from %s to %s", +- (required? "required" : "optional"), rsc->id, +- role2text(role), role2text(next_role)); +- if (rsc_action_matrix[role][next_role](rsc, chosen, !required, +- data_set) == FALSE) { +- break; +- } +- role = next_role; +- } +- role = rsc->role; ++ schedule_restart_actions(rsc, current, chosen, need_stop, need_promote); + + /* Required steps from this role to the next */ ++ role = rsc->role; + while (role != rsc->next_role) { + next_role = rsc_state_matrix[role][rsc->next_role]; + pe_rsc_trace(rsc, "Creating action to take %s from %s to %s (ending at %s)", +-- +2.27.0 + + +From 736d4d8f5e432acf12e577d137e9165904c71b3b Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Mon, 28 Mar 2022 17:42:26 -0500 +Subject: [PATCH 3/9] Log: scheduler: improve trace messages when creating + actions + +--- + lib/pacemaker/pcmk_sched_native.c | 22 ++++++++++++++++------ + 1 file changed, 16 insertions(+), 6 deletions(-) + +diff --git a/lib/pacemaker/pcmk_sched_native.c b/lib/pacemaker/pcmk_sched_native.c +index b8a1c1e1a..8b651ebd2 100644 +--- a/lib/pacemaker/pcmk_sched_native.c ++++ b/lib/pacemaker/pcmk_sched_native.c +@@ -1997,7 +1997,6 @@ StopRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_set + GList *gIter = NULL; + + CRM_ASSERT(rsc); +- pe_rsc_trace(rsc, "%s", rsc->id); + + for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { + pe_node_t *current = (pe_node_t *) gIter->data; +@@ -2005,16 +2004,23 @@ StopRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_set + + if (rsc->partial_migration_target) { + if (rsc->partial_migration_target->details == current->details) { +- pe_rsc_trace(rsc, "Filtered %s -> %s %s", current->details->uname, +- next->details->uname, rsc->id); ++ pe_rsc_trace(rsc, ++ "Skipping stop of %s on %s " ++ "because migration to %s in progress", ++ rsc->id, current->details->uname, ++ next->details->uname); + continue; + } else { +- pe_rsc_trace(rsc, "Forced on %s %s", current->details->uname, rsc->id); ++ pe_rsc_trace(rsc, ++ "Forcing stop of %s on %s " ++ "because migration target changed", ++ rsc->id, current->details->uname); + optional = FALSE; + } + } + +- pe_rsc_trace(rsc, "%s on %s", rsc->id, current->details->uname); ++ pe_rsc_trace(rsc, "Scheduling stop of %s on %s", ++ rsc->id, current->details->uname); + stop = stop_action(rsc, current, optional); + + if(rsc->allocated_to == NULL) { +@@ -2048,7 +2054,11 @@ StartRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_se + pe_action_t *start = NULL; + + CRM_ASSERT(rsc); +- pe_rsc_trace(rsc, "%s on %s %d %d", rsc->id, next ? next->details->uname : "N/A", optional, next ? next->weight : 0); ++ ++ pe_rsc_trace(rsc, "Scheduling %s start of %s on %s (weight=%d)", ++ (optional? "optional" : "required"), rsc->id, ++ ((next == NULL)? "N/A" : next->details->uname), ++ ((next == NULL)? 0 : next->weight)); + start = start_action(rsc, next, TRUE); + + pcmk__order_vs_unfence(rsc, next, start, pe_order_implies_then, data_set); +-- +2.27.0 + + +From 6f987234d5246ed50f4fe2db90e5edb6a23e877d Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 1 Mar 2022 16:42:06 -0600 +Subject: [PATCH 4/9] Log: scheduler: log a warning if invalid value is given + for multiple-active + +--- + lib/pengine/complex.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c +index e82af2aae..f2caef831 100644 +--- a/lib/pengine/complex.c ++++ b/lib/pengine/complex.c +@@ -694,7 +694,12 @@ common_unpack(xmlNode * xml_obj, pe_resource_t ** rsc, + (*rsc)->recovery_type = recovery_block; + pe_rsc_trace((*rsc), "\tMultiple running resource recovery: block"); + +- } else { ++ } else { // "stop_start" ++ if (!pcmk__str_eq(value, "stop_start", ++ pcmk__str_casei|pcmk__str_null_matches)) { ++ pe_warn("%s is not a valid value for " XML_RSC_ATTR_MULTIPLE ++ ", using default of \"stop_start\"", value); ++ } + (*rsc)->recovery_type = recovery_stop_start; + pe_rsc_trace((*rsc), "\tMultiple running resource recovery: stop/start"); + } +-- +2.27.0 + + +From 50456c3e229a6021ca0ba7346af41cd234abcc16 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 1 Mar 2022 16:49:31 -0600 +Subject: [PATCH 5/9] API: libpe_status: add recovery_stop_unexpected to enum + rsc_recovery_type + +The behavior is not implemented as of this commit +--- + include/crm/pengine/common.h | 14 ++++++++++++-- + lib/pengine/complex.c | 5 +++++ + lib/pengine/native.c | 7 +++++-- + 3 files changed, 22 insertions(+), 4 deletions(-) + +diff --git a/include/crm/pengine/common.h b/include/crm/pengine/common.h +index efe89a171..9b9f38f3b 100644 +--- a/include/crm/pengine/common.h ++++ b/include/crm/pengine/common.h +@@ -1,5 +1,5 @@ + /* +- * Copyright 2004-2021 the Pacemaker project contributors ++ * Copyright 2004-2022 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * +@@ -78,7 +78,8 @@ enum action_tasks { + enum rsc_recovery_type { + recovery_stop_start, + recovery_stop_only, +- recovery_block ++ recovery_block, ++ recovery_stop_unexpected, + }; + + enum rsc_start_requirement { +@@ -143,6 +144,13 @@ const char *fail2text(enum action_fail_response fail); + const char *pe_pref(GHashTable * options, const char *name); + void calculate_active_ops(GList * sorted_op_list, int *start_index, int *stop_index); + ++/*! ++ * \brief Get readable description of a recovery type ++ * ++ * \param[in] type Recovery type ++ * ++ * \return Static string describing \p type ++ */ + static inline const char * + recovery2text(enum rsc_recovery_type type) + { +@@ -153,6 +161,8 @@ recovery2text(enum rsc_recovery_type type) + return "attempting recovery"; + case recovery_block: + return "waiting for an administrator"; ++ case recovery_stop_unexpected: ++ return "stopping unexpected instances"; + } + return "Unknown"; + } +diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c +index f2caef831..fc9028e81 100644 +--- a/lib/pengine/complex.c ++++ b/lib/pengine/complex.c +@@ -694,6 +694,11 @@ common_unpack(xmlNode * xml_obj, pe_resource_t ** rsc, + (*rsc)->recovery_type = recovery_block; + pe_rsc_trace((*rsc), "\tMultiple running resource recovery: block"); + ++ } else if (pcmk__str_eq(value, "stop_unexpected", pcmk__str_casei)) { ++ (*rsc)->recovery_type = recovery_stop_unexpected; ++ pe_rsc_trace((*rsc), "\tMultiple running resource recovery: " ++ "stop unexpected instances"); ++ + } else { // "stop_start" + if (!pcmk__str_eq(value, "stop_start", + pcmk__str_casei|pcmk__str_null_matches)) { +diff --git a/lib/pengine/native.c b/lib/pengine/native.c +index e16e54bae..fa7dc8960 100644 +--- a/lib/pengine/native.c ++++ b/lib/pengine/native.c +@@ -149,8 +149,6 @@ native_add_running(pe_resource_t * rsc, pe_node_t * node, pe_working_set_t * dat + } + } + break; +- case recovery_stop_start: +- break; + case recovery_block: + pe__clear_resource_flags(rsc, pe_rsc_managed); + pe__set_resource_flags(rsc, pe_rsc_block); +@@ -171,6 +169,11 @@ native_add_running(pe_resource_t * rsc, pe_node_t * node, pe_working_set_t * dat + } + } + break; ++ default: // recovery_stop_start, recovery_stop_unexpected ++ /* The scheduler will do the right thing because the relevant ++ * variables and flags are set when unpacking the history. ++ */ ++ break; + } + crm_debug("%s is active on multiple nodes including %s: %s", + rsc->id, node->details->uname, +-- +2.27.0 + + +From 5e994f0633b27e7a53701e0954466739c8f1acf7 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Wed, 30 Mar 2022 16:26:19 -0500 +Subject: [PATCH 6/9] API: libpe_status: add pe_rsc_stop_unexpected flag + +--- + include/crm/pengine/pe_types.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/crm/pengine/pe_types.h b/include/crm/pengine/pe_types.h +index e3ecaa823..7d5394bff 100644 +--- a/include/crm/pengine/pe_types.h ++++ b/include/crm/pengine/pe_types.h +@@ -277,6 +277,7 @@ struct pe_node_s { + + # define pe_rsc_starting 0x00100000ULL + # define pe_rsc_stopping 0x00200000ULL ++# define pe_rsc_stop_unexpected 0x00400000ULL + # define pe_rsc_allow_migrate 0x00800000ULL + + # define pe_rsc_failure_ignored 0x01000000ULL +-- +2.27.0 + + +From c1acf05be853d99c17761759b8c961f2ec4a55c2 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 31 Mar 2022 09:56:34 -0500 +Subject: [PATCH 7/9] API: libpe_status: add pe_rsc_restarting flag + +This is used to indicate that any actions currently being scheduled are part of +the resource's restart actions (i.e. we are in schedule_restart_actions()). +--- + include/crm/pengine/pe_types.h | 1 + + lib/pacemaker/pcmk_sched_native.c | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/include/crm/pengine/pe_types.h b/include/crm/pengine/pe_types.h +index 7d5394bff..77d28e900 100644 +--- a/include/crm/pengine/pe_types.h ++++ b/include/crm/pengine/pe_types.h +@@ -265,6 +265,7 @@ struct pe_node_s { + # define pe_rsc_provisional 0x00000100ULL + # define pe_rsc_allocating 0x00000200ULL + # define pe_rsc_merging 0x00000400ULL ++# define pe_rsc_restarting 0x00000800ULL + + # define pe_rsc_stop 0x00001000ULL + # define pe_rsc_reload 0x00002000ULL +diff --git a/lib/pacemaker/pcmk_sched_native.c b/lib/pacemaker/pcmk_sched_native.c +index 8b651ebd2..8002938b5 100644 +--- a/lib/pacemaker/pcmk_sched_native.c ++++ b/lib/pacemaker/pcmk_sched_native.c +@@ -1204,6 +1204,8 @@ schedule_restart_actions(pe_resource_t *rsc, pe_node_t *current, + enum rsc_role_e role = rsc->role; + enum rsc_role_e next_role; + ++ pe__set_resource_flags(rsc, pe_rsc_restarting); ++ + // Bring resource down to a stop on its current node + while (role != RSC_ROLE_STOPPED) { + next_role = rsc_state_matrix[role][RSC_ROLE_STOPPED]; +@@ -1235,6 +1237,8 @@ schedule_restart_actions(pe_resource_t *rsc, pe_node_t *current, + } + role = next_role; + } ++ ++ pe__clear_resource_flags(rsc, pe_rsc_restarting); + } + + void +-- +2.27.0 + + +From 871e2201d92520039df45062afc9120fd1fb0f30 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Tue, 1 Mar 2022 17:46:39 -0600 +Subject: [PATCH 8/9] Refactor: scheduler: add expected node to primitive + variant data + +Nothing uses it yet +--- + include/crm/pengine/internal.h | 4 ++++ + lib/pengine/native.c | 38 ++++++++++++++++++++++++++++++++++ + lib/pengine/variant.h | 8 +++++-- + 3 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h +index f949684b4..f69e6bcce 100644 +--- a/include/crm/pengine/internal.h ++++ b/include/crm/pengine/internal.h +@@ -579,4 +579,8 @@ xmlNode *pe__failed_probe_for_rsc(pe_resource_t *rsc, const char *name); + + const char *pe__clone_child_id(pe_resource_t *rsc); + ++void pe__update_expected_node(pe_resource_t *rsc, pe_node_t *node, ++ int execution_status, int exit_status, ++ int expected_exit_status); ++ + #endif +diff --git a/lib/pengine/native.c b/lib/pengine/native.c +index fa7dc8960..591d1c6f5 100644 +--- a/lib/pengine/native.c ++++ b/lib/pengine/native.c +@@ -1376,3 +1376,41 @@ pe__native_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_paren + + return TRUE; + } ++ ++/*! ++ * \internal ++ * \brief Set a resource's expected node if appropriate for a history result ++ * ++ * \param[in] rsc Resource to set expected node for ++ * \param[in] node Node to set as expected node ++ * \param[in] execution_status History entry's execution status ++ * \param[in] exit_status History entry's actual exit status ++ * \param[in] expected_status History entry's expected exit status ++ */ ++void ++pe__update_expected_node(pe_resource_t *rsc, pe_node_t *node, ++ int execution_status, int exit_status, ++ int expected_exit_status) ++{ ++ native_variant_data_t *native_data = NULL; ++ ++ get_native_variant_data(native_data, rsc); ++ ++ if ((rsc->recovery_type == recovery_stop_unexpected) ++ && (rsc->role > RSC_ROLE_STOPPED) ++ && (execution_status == PCMK_EXEC_DONE) ++ && (exit_status == expected_exit_status)) { ++ // Resource is active and was expected on this node ++ pe_rsc_trace(rsc, "Found expected node %s for %s", ++ node->details->uname, rsc->id); ++ native_data->expected_node = node; ++ pe__set_resource_flags(rsc, pe_rsc_stop_unexpected); ++ ++ } else if ((native_data->expected_node != NULL) ++ && (native_data->expected_node->details == node->details)) { ++ // Resource is not cleanly active here ++ pe_rsc_trace(rsc, "Clearing expected node for %s", rsc->id); ++ native_data->expected_node = NULL; ++ pe__clear_resource_flags(rsc, pe_rsc_stop_unexpected); ++ } ++} +diff --git a/lib/pengine/variant.h b/lib/pengine/variant.h +index cabfbe81f..d8fefa9d6 100644 +--- a/lib/pengine/variant.h ++++ b/lib/pengine/variant.h +@@ -1,5 +1,5 @@ + /* +- * Copyright 2004-2021 the Pacemaker project contributors ++ * Copyright 2004-2022 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * +@@ -139,7 +139,11 @@ typedef struct group_variant_data_s { + # elif VARIANT_NATIVE + + typedef struct native_variant_data_s { +- int dummy; ++ /* If the resource is multiply active, and has multiple-active set to ++ * stop_unexpected, this will be set to the node where the resource was ++ * found active by an operation with a expected result. ++ */ ++ pe_node_t *expected_node; + } native_variant_data_t; + + # define get_native_variant_data(data, rsc) \ +-- +2.27.0 + + +From 0e4e17e972f1c3663389f18d8f8c527bd819b3c5 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Thu, 7 Apr 2022 10:20:00 -0500 +Subject: [PATCH 9/9] Feature: scheduler: implement + multiple-active=stop_unexpected + +The default multiple-active policy of restarting the resource on all nodes +requires no special handling, because at least one of the locations will have +an unexpected rc, causing the resource to be marked as failed and restarted, +and StopRsc() creates stops on all nodes running the resource. + +The new stop_unexpected behavior relies on most of the same handling, but +the action creation functions need to skip the node where the resource had the +expected result. For that, we set the new rsc->expected_node when unpacking a +successful result, to be checked by those functions. + +Note that this still schedules a start for the resource, which is a pseudo-op +for the resource itself, but (properly) causes any dependent resources to be +restarted. + +Fixes T23 +--- + lib/pacemaker/pcmk_output.c | 10 ++++ + lib/pacemaker/pcmk_sched_native.c | 94 ++++++++++++++++++++++++++++++- + lib/pengine/unpack.c | 1 + + 3 files changed, 103 insertions(+), 2 deletions(-) + +diff --git a/lib/pacemaker/pcmk_output.c b/lib/pacemaker/pcmk_output.c +index 56963a93f..9a522a3e5 100644 +--- a/lib/pacemaker/pcmk_output.c ++++ b/lib/pacemaker/pcmk_output.c +@@ -918,6 +918,16 @@ rsc_action_default(pcmk__output_t *out, va_list args) + if (possible_matches) { + stop = possible_matches->data; + g_list_free(possible_matches); ++ } else if (pcmk_is_set(rsc->flags, pe_rsc_stop_unexpected)) { ++ /* The resource is multiply active with multiple-active set to ++ * stop_unexpected, and not stopping on its current node, but it should ++ * be stopping elsewhere. ++ */ ++ possible_matches = pe__resource_actions(rsc, NULL, RSC_STOP, FALSE); ++ if (possible_matches != NULL) { ++ stop = possible_matches->data; ++ g_list_free(possible_matches); ++ } + } + + possible_matches = pe__resource_actions(rsc, next, RSC_PROMOTE, FALSE); +diff --git a/lib/pacemaker/pcmk_sched_native.c b/lib/pacemaker/pcmk_sched_native.c +index 8002938b5..c0224849f 100644 +--- a/lib/pacemaker/pcmk_sched_native.c ++++ b/lib/pacemaker/pcmk_sched_native.c +@@ -1259,7 +1259,10 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + enum rsc_role_e role = RSC_ROLE_UNKNOWN; + enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; + +- CRM_ASSERT(rsc); ++ native_variant_data_t *native_data = NULL; ++ ++ get_native_variant_data(native_data, rsc); ++ + chosen = rsc->allocated_to; + next_role = rsc->next_role; + if (next_role == RSC_ROLE_UNKNOWN) { +@@ -1323,6 +1326,7 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + "(will stop on both nodes)", + rsc->id, rsc->partial_migration_source->details->uname, + rsc->partial_migration_target->details->uname); ++ multiply_active = false; + + } else { + const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); +@@ -1345,6 +1349,11 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + allow_migrate = FALSE; + } + ++ if (!multiply_active) { ++ native_data->expected_node = NULL; ++ pe__clear_resource_flags(rsc, pe_rsc_stop_unexpected); ++ } ++ + if (pcmk_is_set(rsc->flags, pe_rsc_start_pending)) { + pe_rsc_trace(rsc, "Creating start action for %s to represent already pending start", + rsc->id); +@@ -1995,6 +2004,32 @@ native_expand(pe_resource_t * rsc, pe_working_set_t * data_set) + out->message(out, "rsc-action", rsc, current, next); + } + ++/*! ++ * \internal ++ * \brief Check whether a node is a multiply active resource's expected node ++ * ++ * \param[in] rsc Resource to check ++ * \param[in] node Node to check ++ * ++ * \return true if \p rsc is multiply active with multiple-active set to ++ * stop_unexpected, and \p node is the node where it will remain active ++ * \note This assumes that the resource's next role cannot be changed to stopped ++ * after this is called, which should be reasonable if status has already ++ * been unpacked and resources have been assigned to nodes. ++ */ ++static bool ++is_expected_node(const pe_resource_t *rsc, const pe_node_t *node) ++{ ++ native_variant_data_t *native_data = NULL; ++ ++ get_native_variant_data(native_data, rsc); ++ return pcmk_all_flags_set(rsc->flags, ++ pe_rsc_stop_unexpected|pe_rsc_restarting) ++ && (rsc->next_role > RSC_ROLE_STOPPED) ++ && (native_data->expected_node != NULL) && (node != NULL) ++ && (native_data->expected_node->details == node->details); ++} ++ + gboolean + StopRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_set_t * data_set) + { +@@ -2006,6 +2041,18 @@ StopRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_set + pe_node_t *current = (pe_node_t *) gIter->data; + pe_action_t *stop; + ++ if (is_expected_node(rsc, current)) { ++ /* We are scheduling restart actions for a multiply active resource ++ * with multiple-active=stop_unexpected, and this is where it should ++ * not be stopped. ++ */ ++ pe_rsc_trace(rsc, ++ "Skipping stop of multiply active resource %s " ++ "on expected node %s", ++ rsc->id, current->details->uname); ++ continue; ++ } ++ + if (rsc->partial_migration_target) { + if (rsc->partial_migration_target->details == current->details) { + pe_rsc_trace(rsc, +@@ -2029,6 +2076,17 @@ StopRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_set + + if(rsc->allocated_to == NULL) { + pe_action_set_reason(stop, "node availability", TRUE); ++ } else if (pcmk_is_set(rsc->flags, pe_rsc_restarting)) { ++ native_variant_data_t *native_data = NULL; ++ ++ get_native_variant_data(native_data, rsc); ++ if (native_data->expected_node != NULL) { ++ /* We are stopping a multiply active resource on a node that is ++ * not its expected node, and we are still scheduling restart ++ * actions, so the stop is for being multiply active. ++ */ ++ pe_action_set_reason(stop, "being multiply active", TRUE); ++ } + } + + if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) { +@@ -2071,6 +2129,16 @@ StartRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_se + pe__clear_action_flags(start, pe_action_optional); + } + ++ if (is_expected_node(rsc, next)) { ++ /* This could be a problem if the start becomes necessary for other ++ * reasons later. ++ */ ++ pe_rsc_trace(rsc, ++ "Start of multiply active resouce %s " ++ "on expected node %s will be a pseudo-action", ++ rsc->id, next->details->uname); ++ pe__set_action_flags(start, pe_action_pseudo); ++ } + + return TRUE; + } +@@ -2084,6 +2152,7 @@ PromoteRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_ + + CRM_ASSERT(rsc); + CRM_CHECK(next != NULL, return FALSE); ++ + pe_rsc_trace(rsc, "%s on %s", rsc->id, next->details->uname); + + action_list = pe__resource_actions(rsc, next, RSC_START, TRUE); +@@ -2098,7 +2167,19 @@ PromoteRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_ + g_list_free(action_list); + + if (runnable) { +- promote_action(rsc, next, optional); ++ pe_action_t *promote = promote_action(rsc, next, optional); ++ ++ if (is_expected_node(rsc, next)) { ++ /* This could be a problem if the promote becomes necessary for ++ * other reasons later. ++ */ ++ pe_rsc_trace(rsc, ++ "Promotion of multiply active resouce %s " ++ "on expected node %s will be a pseudo-action", ++ rsc->id, next->details->uname); ++ pe__set_action_flags(promote, pe_action_pseudo); ++ } ++ + return TRUE; + } + +@@ -2122,6 +2203,15 @@ DemoteRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_s + GList *gIter = NULL; + + CRM_ASSERT(rsc); ++ ++ if (is_expected_node(rsc, next)) { ++ pe_rsc_trace(rsc, ++ "Skipping demote of multiply active resource %s " ++ "on expected node %s", ++ rsc->id, next->details->uname); ++ return TRUE; ++ } ++ + pe_rsc_trace(rsc, "%s", rsc->id); + + /* CRM_CHECK(rsc->next_role == RSC_ROLE_UNPROMOTED, return FALSE); */ +diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c +index d218f523f..edaa9de48 100644 +--- a/lib/pengine/unpack.c ++++ b/lib/pengine/unpack.c +@@ -3974,6 +3974,7 @@ unpack_rsc_op(pe_resource_t *rsc, pe_node_t *node, xmlNode *xml_op, + } + + done: ++ pe__update_expected_node(rsc, node, status, rc, target_rc); + pe_rsc_trace(rsc, "Resource %s after %s: role=%s, next=%s", + rsc->id, task, role2text(rsc->role), + role2text(rsc->next_role)); +-- +2.27.0 + diff --git a/SOURCES/025-stop_unexpected-test.patch b/SOURCES/025-stop_unexpected-test.patch new file mode 100644 index 0000000..65b74dc --- /dev/null +++ b/SOURCES/025-stop_unexpected-test.patch @@ -0,0 +1,495 @@ +From 8a0a16c8ed72c74d656664694ebe36b76ff22498 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Wed, 30 Mar 2022 17:14:33 -0500 +Subject: [PATCH] Test: cts-scheduler: add test for + multiple-active=stop_unexpected + +--- + cts/cts-scheduler.in | 1 + + cts/scheduler/dot/stop-unexpected.dot | 40 ++++ + cts/scheduler/exp/stop-unexpected.exp | 201 ++++++++++++++++++ + cts/scheduler/scores/stop-unexpected.scores | 17 ++ + cts/scheduler/summary/stop-unexpected.summary | 41 ++++ + cts/scheduler/xml/stop-unexpected.xml | 131 ++++++++++++ + 6 files changed, 431 insertions(+) + create mode 100644 cts/scheduler/dot/stop-unexpected.dot + create mode 100644 cts/scheduler/exp/stop-unexpected.exp + create mode 100644 cts/scheduler/scores/stop-unexpected.scores + create mode 100644 cts/scheduler/summary/stop-unexpected.summary + create mode 100644 cts/scheduler/xml/stop-unexpected.xml + +diff --git a/cts/cts-scheduler.in b/cts/cts-scheduler.in +index 3a8aeaca5..8c04687da 100644 +--- a/cts/cts-scheduler.in ++++ b/cts/cts-scheduler.in +@@ -273,6 +273,7 @@ TESTS = [ + [ "rec-rsc-6", "Resource Recover - multiple - restart" ], + [ "rec-rsc-7", "Resource Recover - multiple - stop" ], + [ "rec-rsc-8", "Resource Recover - multiple - block" ], ++ [ "stop-unexpected", "Resource Recover - multiple - stop unexpected" ], + [ "rec-rsc-9", "Resource Recover - group/group" ], + [ "monitor-recovery", "on-fail=block + resource recovery detected by recurring monitor" ], + [ "stop-failure-no-quorum", "Stop failure without quorum" ], +diff --git a/cts/scheduler/dot/stop-unexpected.dot b/cts/scheduler/dot/stop-unexpected.dot +new file mode 100644 +index 000000000..0f67eec54 +--- /dev/null ++++ b/cts/scheduler/dot/stop-unexpected.dot +@@ -0,0 +1,40 @@ ++ digraph "g" { ++"dgroup_running_0" [ style=bold color="green" fontcolor="orange"] ++"dgroup_start_0" -> "dgroup_running_0" [ style = bold] ++"dgroup_start_0" -> "dummy2_start_0 node2" [ style = bold] ++"dgroup_start_0" -> "dummy3_start_0 node2" [ style = bold] ++"dgroup_start_0" -> "dummy_start_0 node2" [ style = bold] ++"dgroup_start_0" [ style=bold color="green" fontcolor="orange"] ++"dgroup_stop_0" -> "dgroup_stopped_0" [ style = bold] ++"dgroup_stop_0" -> "dummy2_stop_0 node2" [ style = bold] ++"dgroup_stop_0" -> "dummy3_stop_0 node2" [ style = bold] ++"dgroup_stop_0" -> "dummy_stop_0 node3" [ style = bold] ++"dgroup_stop_0" [ style=bold color="green" fontcolor="orange"] ++"dgroup_stopped_0" -> "dgroup_start_0" [ style = bold] ++"dgroup_stopped_0" [ style=bold color="green" fontcolor="orange"] ++"dummy2_monitor_10000 node2" [ style=bold color="green" fontcolor="black"] ++"dummy2_start_0 node2" -> "dgroup_running_0" [ style = bold] ++"dummy2_start_0 node2" -> "dummy2_monitor_10000 node2" [ style = bold] ++"dummy2_start_0 node2" -> "dummy3_start_0 node2" [ style = bold] ++"dummy2_start_0 node2" [ style=bold color="green" fontcolor="black"] ++"dummy2_stop_0 node2" -> "dgroup_stopped_0" [ style = bold] ++"dummy2_stop_0 node2" -> "dummy2_start_0 node2" [ style = bold] ++"dummy2_stop_0 node2" -> "dummy_stop_0 node3" [ style = bold] ++"dummy2_stop_0 node2" [ style=bold color="green" fontcolor="black"] ++"dummy3_monitor_10000 node2" [ style=bold color="green" fontcolor="black"] ++"dummy3_start_0 node2" -> "dgroup_running_0" [ style = bold] ++"dummy3_start_0 node2" -> "dummy3_monitor_10000 node2" [ style = bold] ++"dummy3_start_0 node2" [ style=bold color="green" fontcolor="black"] ++"dummy3_stop_0 node2" -> "dgroup_stopped_0" [ style = bold] ++"dummy3_stop_0 node2" -> "dummy2_stop_0 node2" [ style = bold] ++"dummy3_stop_0 node2" -> "dummy3_start_0 node2" [ style = bold] ++"dummy3_stop_0 node2" [ style=bold color="green" fontcolor="black"] ++"dummy_monitor_10000 node2" [ style=bold color="green" fontcolor="black"] ++"dummy_start_0 node2" -> "dgroup_running_0" [ style = bold] ++"dummy_start_0 node2" -> "dummy2_start_0 node2" [ style = bold] ++"dummy_start_0 node2" -> "dummy_monitor_10000 node2" [ style = bold] ++"dummy_start_0 node2" [ style=bold color="green" fontcolor="orange"] ++"dummy_stop_0 node3" -> "dgroup_stopped_0" [ style = bold] ++"dummy_stop_0 node3" -> "dummy_start_0 node2" [ style = bold] ++"dummy_stop_0 node3" [ style=bold color="green" fontcolor="black"] ++} +diff --git a/cts/scheduler/exp/stop-unexpected.exp b/cts/scheduler/exp/stop-unexpected.exp +new file mode 100644 +index 000000000..1f94532f7 +--- /dev/null ++++ b/cts/scheduler/exp/stop-unexpected.exp +@@ -0,0 +1,201 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/cts/scheduler/scores/stop-unexpected.scores b/cts/scheduler/scores/stop-unexpected.scores +new file mode 100644 +index 000000000..68f98e837 +--- /dev/null ++++ b/cts/scheduler/scores/stop-unexpected.scores +@@ -0,0 +1,17 @@ ++ ++pcmk__group_allocate: dgroup allocation score on node2: 0 ++pcmk__group_allocate: dgroup allocation score on node3: 0 ++pcmk__group_allocate: dummy allocation score on node2: 0 ++pcmk__group_allocate: dummy allocation score on node3: 0 ++pcmk__group_allocate: dummy2 allocation score on node2: 100 ++pcmk__group_allocate: dummy2 allocation score on node3: 0 ++pcmk__group_allocate: dummy3 allocation score on node2: 100 ++pcmk__group_allocate: dummy3 allocation score on node3: 0 ++pcmk__native_allocate: dummy allocation score on node2: 200 ++pcmk__native_allocate: dummy allocation score on node3: 0 ++pcmk__native_allocate: dummy2 allocation score on node2: 200 ++pcmk__native_allocate: dummy2 allocation score on node3: -INFINITY ++pcmk__native_allocate: dummy3 allocation score on node2: 100 ++pcmk__native_allocate: dummy3 allocation score on node3: -INFINITY ++pcmk__native_allocate: st-sbd allocation score on node2: 100 ++pcmk__native_allocate: st-sbd allocation score on node3: 0 +diff --git a/cts/scheduler/summary/stop-unexpected.summary b/cts/scheduler/summary/stop-unexpected.summary +new file mode 100644 +index 000000000..7c7fc68b6 +--- /dev/null ++++ b/cts/scheduler/summary/stop-unexpected.summary +@@ -0,0 +1,41 @@ ++Current cluster status: ++ * Node List: ++ * Online: [ node2 node3 ] ++ ++ * Full List of Resources: ++ * st-sbd (stonith:external/sbd): Started node2 ++ * Resource Group: dgroup: ++ * dummy (ocf:heartbeat:DummyTimeout): FAILED [ node2 node3 ] ++ * dummy2 (ocf:heartbeat:Dummy): Started node2 ++ * dummy3 (ocf:heartbeat:Dummy): Started node2 ++ ++Transition Summary: ++ * Recover dummy ( node2 ) due to being multiply active ++ * Restart dummy2 ( node2 ) due to required dummy start ++ * Restart dummy3 ( node2 ) due to required dummy2 start ++ ++Executing Cluster Transition: ++ * Pseudo action: dgroup_stop_0 ++ * Resource action: dummy3 stop on node2 ++ * Resource action: dummy2 stop on node2 ++ * Resource action: dummy stop on node3 ++ * Pseudo action: dgroup_stopped_0 ++ * Pseudo action: dgroup_start_0 ++ * Pseudo action: dummy_start_0 ++ * Resource action: dummy monitor=10000 on node2 ++ * Resource action: dummy2 start on node2 ++ * Resource action: dummy2 monitor=10000 on node2 ++ * Resource action: dummy3 start on node2 ++ * Resource action: dummy3 monitor=10000 on node2 ++ * Pseudo action: dgroup_running_0 ++ ++Revised Cluster Status: ++ * Node List: ++ * Online: [ node2 node3 ] ++ ++ * Full List of Resources: ++ * st-sbd (stonith:external/sbd): Started node2 ++ * Resource Group: dgroup: ++ * dummy (ocf:heartbeat:DummyTimeout): Started node2 ++ * dummy2 (ocf:heartbeat:Dummy): Started node2 ++ * dummy3 (ocf:heartbeat:Dummy): Started node2 +diff --git a/cts/scheduler/xml/stop-unexpected.xml b/cts/scheduler/xml/stop-unexpected.xml +new file mode 100644 +index 000000000..6e61aeba3 +--- /dev/null ++++ b/cts/scheduler/xml/stop-unexpected.xml +@@ -0,0 +1,131 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +-- +2.27.0 + diff --git a/SOURCES/026-stop_unexpected-fix.patch b/SOURCES/026-stop_unexpected-fix.patch new file mode 100644 index 0000000..69dd95d --- /dev/null +++ b/SOURCES/026-stop_unexpected-fix.patch @@ -0,0 +1,589 @@ +From 4a5dcc5210160f7d167bc68142635c1b5a6d4af2 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 22 Apr 2022 10:47:29 -0500 +Subject: [PATCH 1/3] Fix: scheduler: make multiple-active="stop_unexpected" + actually work + +The previous implementation covered the scenario in the regression test and not +much else. It unnecessarily added an expected_node member to the native variant +data, when the resource's allocated_to is sufficient to know the expected node. +--- + lib/pacemaker/pcmk_sched_native.c | 45 +++++++++++++++---------------- + lib/pengine/unpack.c | 1 - + 2 files changed, 22 insertions(+), 24 deletions(-) + +diff --git a/lib/pacemaker/pcmk_sched_native.c b/lib/pacemaker/pcmk_sched_native.c +index c0224849f..a1a51721e 100644 +--- a/lib/pacemaker/pcmk_sched_native.c ++++ b/lib/pacemaker/pcmk_sched_native.c +@@ -1250,7 +1250,7 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + gboolean need_stop = FALSE; + bool need_promote = FALSE; + gboolean is_moving = FALSE; +- gboolean allow_migrate = pcmk_is_set(rsc->flags, pe_rsc_allow_migrate)? TRUE : FALSE; ++ gboolean allow_migrate = FALSE; + + GList *gIter = NULL; + unsigned int num_all_active = 0; +@@ -1259,9 +1259,8 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + enum rsc_role_e role = RSC_ROLE_UNKNOWN; + enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; + +- native_variant_data_t *native_data = NULL; +- +- get_native_variant_data(native_data, rsc); ++ CRM_ASSERT(rsc != NULL); ++ allow_migrate = pcmk_is_set(rsc->flags, pe_rsc_allow_migrate)? TRUE : FALSE; + + chosen = rsc->allocated_to; + next_role = rsc->next_role; +@@ -1338,8 +1337,16 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + crm_notice("See https://wiki.clusterlabs.org/wiki/FAQ#Resource_is_Too_Active for more information"); + } + +- if (rsc->recovery_type == recovery_stop_start) { +- need_stop = TRUE; ++ switch (rsc->recovery_type) { ++ case recovery_stop_start: ++ need_stop = TRUE; ++ break; ++ case recovery_stop_unexpected: ++ need_stop = TRUE; // StopRsc() will skip expected node ++ pe__set_resource_flags(rsc, pe_rsc_stop_unexpected); ++ break; ++ default: ++ break; + } + + /* If by chance a partial migration is in process, but the migration +@@ -1350,7 +1357,6 @@ native_create_actions(pe_resource_t * rsc, pe_working_set_t * data_set) + } + + if (!multiply_active) { +- native_data->expected_node = NULL; + pe__clear_resource_flags(rsc, pe_rsc_stop_unexpected); + } + +@@ -2020,14 +2026,11 @@ native_expand(pe_resource_t * rsc, pe_working_set_t * data_set) + static bool + is_expected_node(const pe_resource_t *rsc, const pe_node_t *node) + { +- native_variant_data_t *native_data = NULL; +- +- get_native_variant_data(native_data, rsc); + return pcmk_all_flags_set(rsc->flags, + pe_rsc_stop_unexpected|pe_rsc_restarting) + && (rsc->next_role > RSC_ROLE_STOPPED) +- && (native_data->expected_node != NULL) && (node != NULL) +- && (native_data->expected_node->details == node->details); ++ && (rsc->allocated_to != NULL) && (node != NULL) ++ && (rsc->allocated_to->details == node->details); + } + + gboolean +@@ -2076,17 +2079,13 @@ StopRsc(pe_resource_t * rsc, pe_node_t * next, gboolean optional, pe_working_set + + if(rsc->allocated_to == NULL) { + pe_action_set_reason(stop, "node availability", TRUE); +- } else if (pcmk_is_set(rsc->flags, pe_rsc_restarting)) { +- native_variant_data_t *native_data = NULL; +- +- get_native_variant_data(native_data, rsc); +- if (native_data->expected_node != NULL) { +- /* We are stopping a multiply active resource on a node that is +- * not its expected node, and we are still scheduling restart +- * actions, so the stop is for being multiply active. +- */ +- pe_action_set_reason(stop, "being multiply active", TRUE); +- } ++ } else if (pcmk_all_flags_set(rsc->flags, pe_rsc_restarting ++ |pe_rsc_stop_unexpected)) { ++ /* We are stopping a multiply active resource on a node that is ++ * not its expected node, and we are still scheduling restart ++ * actions, so the stop is for being multiply active. ++ */ ++ pe_action_set_reason(stop, "being multiply active", TRUE); + } + + if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) { +diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c +index 17dea0d7a..426022013 100644 +--- a/lib/pengine/unpack.c ++++ b/lib/pengine/unpack.c +@@ -3945,7 +3945,6 @@ unpack_rsc_op(pe_resource_t *rsc, pe_node_t *node, xmlNode *xml_op, + } + + done: +- pe__update_expected_node(rsc, node, status, rc, target_rc); + pe_rsc_trace(rsc, "Resource %s after %s: role=%s, next=%s", + rsc->id, task, role2text(rsc->role), + role2text(rsc->next_role)); +-- +2.27.0 + + +From 703d3a09bce389afb4e095e1ac7af29eb5edd189 Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 22 Apr 2022 14:02:34 -0500 +Subject: [PATCH 2/3] Test: scheduler: add a second regression test for + multiple-active=stop_unexpected + +--- + cts/cts-scheduler.in | 3 +- + cts/scheduler/dot/stop-unexpected-2.dot | 7 + + cts/scheduler/exp/stop-unexpected-2.exp | 36 ++++ + cts/scheduler/scores/stop-unexpected-2.scores | 21 ++ + .../summary/stop-unexpected-2.summary | 29 +++ + cts/scheduler/xml/stop-unexpected-2.xml | 204 ++++++++++++++++++ + 6 files changed, 299 insertions(+), 1 deletion(-) + create mode 100644 cts/scheduler/dot/stop-unexpected-2.dot + create mode 100644 cts/scheduler/exp/stop-unexpected-2.exp + create mode 100644 cts/scheduler/scores/stop-unexpected-2.scores + create mode 100644 cts/scheduler/summary/stop-unexpected-2.summary + create mode 100644 cts/scheduler/xml/stop-unexpected-2.xml + +diff --git a/cts/cts-scheduler.in b/cts/cts-scheduler.in +index 8c04687da..7fc76cce4 100644 +--- a/cts/cts-scheduler.in ++++ b/cts/cts-scheduler.in +@@ -273,8 +273,9 @@ TESTS = [ + [ "rec-rsc-6", "Resource Recover - multiple - restart" ], + [ "rec-rsc-7", "Resource Recover - multiple - stop" ], + [ "rec-rsc-8", "Resource Recover - multiple - block" ], +- [ "stop-unexpected", "Resource Recover - multiple - stop unexpected" ], + [ "rec-rsc-9", "Resource Recover - group/group" ], ++ [ "stop-unexpected", "Recover multiply active group with stop_unexpected" ], ++ [ "stop-unexpected-2", "Resource multiply active primitve with stop_unexpected" ], + [ "monitor-recovery", "on-fail=block + resource recovery detected by recurring monitor" ], + [ "stop-failure-no-quorum", "Stop failure without quorum" ], + [ "stop-failure-no-fencing", "Stop failure without fencing available" ], +diff --git a/cts/scheduler/dot/stop-unexpected-2.dot b/cts/scheduler/dot/stop-unexpected-2.dot +new file mode 100644 +index 000000000..cdaebf551 +--- /dev/null ++++ b/cts/scheduler/dot/stop-unexpected-2.dot +@@ -0,0 +1,7 @@ ++ digraph "g" { ++"test_monitor_10000 rhel8-4" [ style=bold color="green" fontcolor="black"] ++"test_start_0 rhel8-4" -> "test_monitor_10000 rhel8-4" [ style = bold] ++"test_start_0 rhel8-4" [ style=bold color="green" fontcolor="orange"] ++"test_stop_0 rhel8-3" -> "test_start_0 rhel8-4" [ style = bold] ++"test_stop_0 rhel8-3" [ style=bold color="green" fontcolor="black"] ++} +diff --git a/cts/scheduler/exp/stop-unexpected-2.exp b/cts/scheduler/exp/stop-unexpected-2.exp +new file mode 100644 +index 000000000..258053c08 +--- /dev/null ++++ b/cts/scheduler/exp/stop-unexpected-2.exp +@@ -0,0 +1,36 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/cts/scheduler/scores/stop-unexpected-2.scores b/cts/scheduler/scores/stop-unexpected-2.scores +new file mode 100644 +index 000000000..0eb549f5e +--- /dev/null ++++ b/cts/scheduler/scores/stop-unexpected-2.scores +@@ -0,0 +1,21 @@ ++ ++pcmk__native_allocate: Fencing allocation score on rhel8-1: 0 ++pcmk__native_allocate: Fencing allocation score on rhel8-2: 0 ++pcmk__native_allocate: Fencing allocation score on rhel8-3: 0 ++pcmk__native_allocate: Fencing allocation score on rhel8-4: 0 ++pcmk__native_allocate: Fencing allocation score on rhel8-5: 0 ++pcmk__native_allocate: FencingFail allocation score on rhel8-1: 0 ++pcmk__native_allocate: FencingFail allocation score on rhel8-2: 0 ++pcmk__native_allocate: FencingFail allocation score on rhel8-3: 0 ++pcmk__native_allocate: FencingFail allocation score on rhel8-4: 0 ++pcmk__native_allocate: FencingFail allocation score on rhel8-5: 0 ++pcmk__native_allocate: FencingPass allocation score on rhel8-1: 0 ++pcmk__native_allocate: FencingPass allocation score on rhel8-2: 0 ++pcmk__native_allocate: FencingPass allocation score on rhel8-3: 0 ++pcmk__native_allocate: FencingPass allocation score on rhel8-4: 0 ++pcmk__native_allocate: FencingPass allocation score on rhel8-5: 0 ++pcmk__native_allocate: test allocation score on rhel8-1: 0 ++pcmk__native_allocate: test allocation score on rhel8-2: 0 ++pcmk__native_allocate: test allocation score on rhel8-3: 0 ++pcmk__native_allocate: test allocation score on rhel8-4: 0 ++pcmk__native_allocate: test allocation score on rhel8-5: 0 +diff --git a/cts/scheduler/summary/stop-unexpected-2.summary b/cts/scheduler/summary/stop-unexpected-2.summary +new file mode 100644 +index 000000000..d6b0c15dc +--- /dev/null ++++ b/cts/scheduler/summary/stop-unexpected-2.summary +@@ -0,0 +1,29 @@ ++Using the original execution date of: 2022-04-22 14:15:37Z ++Current cluster status: ++ * Node List: ++ * Online: [ rhel8-1 rhel8-2 rhel8-3 rhel8-4 rhel8-5 ] ++ ++ * Full List of Resources: ++ * Fencing (stonith:fence_xvm): Started rhel8-1 ++ * FencingPass (stonith:fence_dummy): Started rhel8-2 ++ * FencingFail (stonith:fence_dummy): Started rhel8-3 ++ * test (ocf:pacemaker:Dummy): Started [ rhel8-4 rhel8-3 ] ++ ++Transition Summary: ++ * Restart test ( rhel8-4 ) ++ ++Executing Cluster Transition: ++ * Resource action: test stop on rhel8-3 ++ * Pseudo action: test_start_0 ++ * Resource action: test monitor=10000 on rhel8-4 ++Using the original execution date of: 2022-04-22 14:15:37Z ++ ++Revised Cluster Status: ++ * Node List: ++ * Online: [ rhel8-1 rhel8-2 rhel8-3 rhel8-4 rhel8-5 ] ++ ++ * Full List of Resources: ++ * Fencing (stonith:fence_xvm): Started rhel8-1 ++ * FencingPass (stonith:fence_dummy): Started rhel8-2 ++ * FencingFail (stonith:fence_dummy): Started rhel8-3 ++ * test (ocf:pacemaker:Dummy): Started rhel8-4 +diff --git a/cts/scheduler/xml/stop-unexpected-2.xml b/cts/scheduler/xml/stop-unexpected-2.xml +new file mode 100644 +index 000000000..e103629e9 +--- /dev/null ++++ b/cts/scheduler/xml/stop-unexpected-2.xml +@@ -0,0 +1,204 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +-- +2.27.0 + + +From 60d8bb01ba73dfd1cb25c6764ee2b923dcfc4e8c Mon Sep 17 00:00:00 2001 +From: Ken Gaillot +Date: Fri, 22 Apr 2022 14:09:43 -0500 +Subject: [PATCH 3/3] Revert "Refactor: scheduler: add expected node to + primitive variant data" + +This reverts commit 871e2201d92520039df45062afc9120fd1fb0f30. +--- + include/crm/pengine/internal.h | 4 ---- + lib/pengine/native.c | 38 ---------------------------------- + lib/pengine/variant.h | 8 ++----- + 3 files changed, 2 insertions(+), 48 deletions(-) + +diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h +index a2e4b5bf7..fe9a23b7e 100644 +--- a/include/crm/pengine/internal.h ++++ b/include/crm/pengine/internal.h +@@ -580,8 +580,4 @@ xmlNode *pe__failed_probe_for_rsc(pe_resource_t *rsc, const char *name); + + const char *pe__clone_child_id(pe_resource_t *rsc); + +-void pe__update_expected_node(pe_resource_t *rsc, pe_node_t *node, +- int execution_status, int exit_status, +- int expected_exit_status); +- + #endif +diff --git a/lib/pengine/native.c b/lib/pengine/native.c +index 591d1c6f5..fa7dc8960 100644 +--- a/lib/pengine/native.c ++++ b/lib/pengine/native.c +@@ -1376,41 +1376,3 @@ pe__native_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_paren + + return TRUE; + } +- +-/*! +- * \internal +- * \brief Set a resource's expected node if appropriate for a history result +- * +- * \param[in] rsc Resource to set expected node for +- * \param[in] node Node to set as expected node +- * \param[in] execution_status History entry's execution status +- * \param[in] exit_status History entry's actual exit status +- * \param[in] expected_status History entry's expected exit status +- */ +-void +-pe__update_expected_node(pe_resource_t *rsc, pe_node_t *node, +- int execution_status, int exit_status, +- int expected_exit_status) +-{ +- native_variant_data_t *native_data = NULL; +- +- get_native_variant_data(native_data, rsc); +- +- if ((rsc->recovery_type == recovery_stop_unexpected) +- && (rsc->role > RSC_ROLE_STOPPED) +- && (execution_status == PCMK_EXEC_DONE) +- && (exit_status == expected_exit_status)) { +- // Resource is active and was expected on this node +- pe_rsc_trace(rsc, "Found expected node %s for %s", +- node->details->uname, rsc->id); +- native_data->expected_node = node; +- pe__set_resource_flags(rsc, pe_rsc_stop_unexpected); +- +- } else if ((native_data->expected_node != NULL) +- && (native_data->expected_node->details == node->details)) { +- // Resource is not cleanly active here +- pe_rsc_trace(rsc, "Clearing expected node for %s", rsc->id); +- native_data->expected_node = NULL; +- pe__clear_resource_flags(rsc, pe_rsc_stop_unexpected); +- } +-} +diff --git a/lib/pengine/variant.h b/lib/pengine/variant.h +index d8fefa9d6..cabfbe81f 100644 +--- a/lib/pengine/variant.h ++++ b/lib/pengine/variant.h +@@ -1,5 +1,5 @@ + /* +- * Copyright 2004-2022 the Pacemaker project contributors ++ * Copyright 2004-2021 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * +@@ -139,11 +139,7 @@ typedef struct group_variant_data_s { + # elif VARIANT_NATIVE + + typedef struct native_variant_data_s { +- /* If the resource is multiply active, and has multiple-active set to +- * stop_unexpected, this will be set to the node where the resource was +- * found active by an operation with a expected result. +- */ +- pe_node_t *expected_node; ++ int dummy; + } native_variant_data_t; + + # define get_native_variant_data(data, rsc) \ +-- +2.27.0 + diff --git a/SPECS/pacemaker.spec b/SPECS/pacemaker.spec index d0b0903..5033db5 100644 --- a/SPECS/pacemaker.spec +++ b/SPECS/pacemaker.spec @@ -242,7 +242,7 @@ Name: pacemaker Summary: Scalable High-Availability cluster resource manager Version: %{pcmkversion} -Release: %{pcmk_release}%{?dist} +Release: %{pcmk_release}%{?dist}.2 %if %{defined _unitdir} License: GPLv2+ and LGPLv2+ %else @@ -286,6 +286,9 @@ Patch20: 020-systemd-unit.patch Patch21: 021-failure-messages.patch Patch22: 022-memory-leak.patch Patch23: 023-regression.patch +Patch24: 024-stop_unexpected.patch +Patch25: 025-stop_unexpected-test.patch +Patch26: 026-stop_unexpected-fix.patch # downstream-only commits #Patch1xx: 1xx-xxxx.patch @@ -983,6 +986,14 @@ exit 0 %license %{nagios_name}-%{nagios_hash}/COPYING %changelog +* Fri Apr 22 2022 Ken Gaillot - 2.1.2-4.2 +- Fix issue with "stop_unexpected" value for "multiple-active" meta-attribute +- Resolves: rhbz2062848 + +* Fri Apr 8 2022 Ken Gaillot - 2.1.2-4.1 +- Support "stop_unexpected" value for "multiple-active" meta-attribute +- Resolves: rhbz2062848 + * Wed Jan 26 2022 Ken Gaillot - 2.1.2-4 - Fix regression in down event detection that affects remote nodes - Resolves: rhbz2046446