|
|
5ee7c6 |
From 85a3a174e1fc4cd4b055eb22827c1c3d0b288a85 Mon Sep 17 00:00:00 2001
|
|
|
5ee7c6 |
From: Ken Gaillot <kgaillot@redhat.com>
|
|
|
5ee7c6 |
Date: Tue, 22 May 2018 11:00:22 -0500
|
|
|
5ee7c6 |
Subject: [PATCH 1/3] Low: libpe_status: handle pending migrations correctly
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
This is mainly a refactor of unpack_rsc_migration() for readability.
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
The one significant change is that previously, a migrate_from operation that
|
|
|
5ee7c6 |
was *recorded* as pending (record-pending=true) was treated differently from an
|
|
|
5ee7c6 |
unrecorded pending migrate_from (record-pending=false).
|
|
|
5ee7c6 |
---
|
|
|
5ee7c6 |
include/crm/pengine/status.h | 3 +
|
|
|
5ee7c6 |
lib/pengine/unpack.c | 162 ++++++++++++++++++++++++-------------------
|
|
|
5ee7c6 |
2 files changed, 94 insertions(+), 71 deletions(-)
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
diff --git a/include/crm/pengine/status.h b/include/crm/pengine/status.h
|
|
|
5ee7c6 |
index fca7f12..a8c90e2 100644
|
|
|
5ee7c6 |
--- a/include/crm/pengine/status.h
|
|
|
5ee7c6 |
+++ b/include/crm/pengine/status.h
|
|
|
5ee7c6 |
@@ -30,6 +30,9 @@ typedef struct pe_action_s pe_action_t;
|
|
|
5ee7c6 |
typedef struct resource_s resource_t;
|
|
|
5ee7c6 |
typedef struct ticket_s ticket_t;
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
+// forward-compatible with Pacemaker 2.0.0
|
|
|
5ee7c6 |
+typedef struct resource_s pe_resource_t;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
typedef enum no_quorum_policy_e {
|
|
|
5ee7c6 |
no_quorum_freeze,
|
|
|
5ee7c6 |
no_quorum_stop,
|
|
|
5ee7c6 |
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
|
|
|
5ee7c6 |
index 1b8ca22..73bbe27 100644
|
|
|
5ee7c6 |
--- a/lib/pengine/unpack.c
|
|
|
5ee7c6 |
+++ b/lib/pengine/unpack.c
|
|
|
5ee7c6 |
@@ -2414,94 +2414,114 @@ find_lrm_op(const char *resource, const char *op, const char *node, const char *
|
|
|
5ee7c6 |
return get_xpath_object(xpath, data_set->input, LOG_DEBUG);
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
+static bool
|
|
|
5ee7c6 |
+stop_happened_after(pe_resource_t *rsc, pe_node_t *node, xmlNode *xml_op,
|
|
|
5ee7c6 |
+ pe_working_set_t *data_set)
|
|
|
5ee7c6 |
+{
|
|
|
5ee7c6 |
+ xmlNode *stop_op = find_lrm_op(rsc->id, CRMD_ACTION_STOP, node->details->id,
|
|
|
5ee7c6 |
+ NULL, data_set);
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (stop_op) {
|
|
|
5ee7c6 |
+ int stop_id = 0;
|
|
|
5ee7c6 |
+ int task_id = 0;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
|
|
|
5ee7c6 |
+ crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &task_id);
|
|
|
5ee7c6 |
+ if (stop_id > task_id) {
|
|
|
5ee7c6 |
+ return TRUE;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ return FALSE;
|
|
|
5ee7c6 |
+}
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
static void
|
|
|
5ee7c6 |
unpack_rsc_migration(resource_t *rsc, node_t *node, xmlNode *xml_op, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
{
|
|
|
5ee7c6 |
-
|
|
|
5ee7c6 |
- /*
|
|
|
5ee7c6 |
- * The normal sequence is (now): migrate_to(Src) -> migrate_from(Tgt) -> stop(Src)
|
|
|
5ee7c6 |
- *
|
|
|
5ee7c6 |
- * So if a migrate_to is followed by a stop, then we don't need to care what
|
|
|
5ee7c6 |
- * happened on the target node
|
|
|
5ee7c6 |
+ /* A successful migration sequence is:
|
|
|
5ee7c6 |
+ * migrate_to on source node
|
|
|
5ee7c6 |
+ * migrate_from on target node
|
|
|
5ee7c6 |
+ * stop on source node
|
|
|
5ee7c6 |
*
|
|
|
5ee7c6 |
- * Without the stop, we need to look for a successful migrate_from.
|
|
|
5ee7c6 |
- * This would also imply we're no longer running on the source
|
|
|
5ee7c6 |
+ * If a migrate_to is followed by a stop, the entire migration (successful
|
|
|
5ee7c6 |
+ * or failed) is complete, and we don't care what happened on the target.
|
|
|
5ee7c6 |
*
|
|
|
5ee7c6 |
- * Without the stop, and without a migrate_from op we make sure the resource
|
|
|
5ee7c6 |
- * gets stopped on both source and target (assuming the target is up)
|
|
|
5ee7c6 |
+ * If no migrate_from has happened, the migration is considered to be
|
|
|
5ee7c6 |
+ * "partial". If the migrate_from failed, make sure the resource gets
|
|
|
5ee7c6 |
+ * stopped on both source and target (if up).
|
|
|
5ee7c6 |
*
|
|
|
5ee7c6 |
+ * If the migrate_to and migrate_from both succeeded (which also implies the
|
|
|
5ee7c6 |
+ * resource is no longer running on the source), but there is no stop, the
|
|
|
5ee7c6 |
+ * migration is considered to be "dangling".
|
|
|
5ee7c6 |
*/
|
|
|
5ee7c6 |
- int stop_id = 0;
|
|
|
5ee7c6 |
- int task_id = 0;
|
|
|
5ee7c6 |
- xmlNode *stop_op =
|
|
|
5ee7c6 |
- find_lrm_op(rsc->id, CRMD_ACTION_STOP, node->details->id, NULL, data_set);
|
|
|
5ee7c6 |
-
|
|
|
5ee7c6 |
- if (stop_op) {
|
|
|
5ee7c6 |
- crm_element_value_int(stop_op, XML_LRM_ATTR_CALLID, &stop_id);
|
|
|
5ee7c6 |
+ int from_rc = 0;
|
|
|
5ee7c6 |
+ int from_status = 0;
|
|
|
5ee7c6 |
+ const char *migrate_source = NULL;
|
|
|
5ee7c6 |
+ const char *migrate_target = NULL;
|
|
|
5ee7c6 |
+ pe_node_t *target = NULL;
|
|
|
5ee7c6 |
+ pe_node_t *source = NULL;
|
|
|
5ee7c6 |
+ xmlNode *migrate_from = NULL;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (stop_happened_after(rsc, node, xml_op, data_set)) {
|
|
|
5ee7c6 |
+ return;
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- crm_element_value_int(xml_op, XML_LRM_ATTR_CALLID, &task_id);
|
|
|
5ee7c6 |
+ // Clones are not allowed to migrate, so role can't be master
|
|
|
5ee7c6 |
+ rsc->role = RSC_ROLE_STARTED;
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- if (stop_op == NULL || stop_id < task_id) {
|
|
|
5ee7c6 |
- int from_rc = 0, from_status = 0;
|
|
|
5ee7c6 |
- const char *migrate_source =
|
|
|
5ee7c6 |
- crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
|
|
|
5ee7c6 |
- const char *migrate_target =
|
|
|
5ee7c6 |
- crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
|
|
|
5ee7c6 |
+ migrate_source = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_SOURCE);
|
|
|
5ee7c6 |
+ migrate_target = crm_element_value(xml_op, XML_LRM_ATTR_MIGRATE_TARGET);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- node_t *target = pe_find_node(data_set->nodes, migrate_target);
|
|
|
5ee7c6 |
- node_t *source = pe_find_node(data_set->nodes, migrate_source);
|
|
|
5ee7c6 |
- xmlNode *migrate_from =
|
|
|
5ee7c6 |
- find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target, migrate_source,
|
|
|
5ee7c6 |
- data_set);
|
|
|
5ee7c6 |
+ target = pe_find_node(data_set->nodes, migrate_target);
|
|
|
5ee7c6 |
+ source = pe_find_node(data_set->nodes, migrate_source);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- rsc->role = RSC_ROLE_STARTED; /* can be master? */
|
|
|
5ee7c6 |
- if (migrate_from) {
|
|
|
5ee7c6 |
- crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc);
|
|
|
5ee7c6 |
- crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, &from_status);
|
|
|
5ee7c6 |
- pe_rsc_trace(rsc, "%s op on %s exited with status=%d, rc=%d",
|
|
|
5ee7c6 |
- ID(migrate_from), migrate_target, from_status, from_rc);
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
-
|
|
|
5ee7c6 |
- if (migrate_from && from_rc == PCMK_OCF_OK
|
|
|
5ee7c6 |
- && from_status == PCMK_LRM_OP_DONE) {
|
|
|
5ee7c6 |
- pe_rsc_trace(rsc, "Detected dangling migration op: %s on %s", ID(xml_op),
|
|
|
5ee7c6 |
- migrate_source);
|
|
|
5ee7c6 |
+ // Check whether there was a migrate_from action
|
|
|
5ee7c6 |
+ migrate_from = find_lrm_op(rsc->id, CRMD_ACTION_MIGRATED, migrate_target,
|
|
|
5ee7c6 |
+ migrate_source, data_set);
|
|
|
5ee7c6 |
+ if (migrate_from) {
|
|
|
5ee7c6 |
+ crm_element_value_int(migrate_from, XML_LRM_ATTR_RC, &from_rc);
|
|
|
5ee7c6 |
+ crm_element_value_int(migrate_from, XML_LRM_ATTR_OPSTATUS, &from_status);
|
|
|
5ee7c6 |
+ pe_rsc_trace(rsc, "%s op on %s exited with status=%d, rc=%d",
|
|
|
5ee7c6 |
+ ID(migrate_from), migrate_target, from_status, from_rc);
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- /* all good
|
|
|
5ee7c6 |
- * just need to arrange for the stop action to get sent
|
|
|
5ee7c6 |
- * but _without_ affecting the target somehow
|
|
|
5ee7c6 |
- */
|
|
|
5ee7c6 |
- rsc->role = RSC_ROLE_STOPPED;
|
|
|
5ee7c6 |
- rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node);
|
|
|
5ee7c6 |
+ if (migrate_from && from_rc == PCMK_OCF_OK
|
|
|
5ee7c6 |
+ && from_status == PCMK_LRM_OP_DONE) {
|
|
|
5ee7c6 |
+ /* The migrate_to and migrate_from both succeeded, so mark the migration
|
|
|
5ee7c6 |
+ * as "dangling". This will be used to schedule a stop action on the
|
|
|
5ee7c6 |
+ * source without affecting the target.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+ pe_rsc_trace(rsc, "Detected dangling migration op: %s on %s", ID(xml_op),
|
|
|
5ee7c6 |
+ migrate_source);
|
|
|
5ee7c6 |
+ rsc->role = RSC_ROLE_STOPPED;
|
|
|
5ee7c6 |
+ rsc->dangling_migrations = g_list_prepend(rsc->dangling_migrations, node);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- } else if (migrate_from) { /* Failed */
|
|
|
5ee7c6 |
- if (target && target->details->online) {
|
|
|
5ee7c6 |
- pe_rsc_trace(rsc, "Marking active on %s %p %d", migrate_target, target,
|
|
|
5ee7c6 |
- target->details->online);
|
|
|
5ee7c6 |
- native_add_running(rsc, target, data_set);
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
+ } else if (migrate_from && (from_status != PCMK_LRM_OP_PENDING)) { // Failed
|
|
|
5ee7c6 |
+ if (target && target->details->online) {
|
|
|
5ee7c6 |
+ pe_rsc_trace(rsc, "Marking active on %s %p %d", migrate_target, target,
|
|
|
5ee7c6 |
+ target->details->online);
|
|
|
5ee7c6 |
+ native_add_running(rsc, target, data_set);
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- } else { /* Pending or complete but erased */
|
|
|
5ee7c6 |
- if (target && target->details->online) {
|
|
|
5ee7c6 |
- pe_rsc_trace(rsc, "Marking active on %s %p %d", migrate_target, target,
|
|
|
5ee7c6 |
- target->details->online);
|
|
|
5ee7c6 |
+ } else { // Pending, or complete but erased
|
|
|
5ee7c6 |
+ if (target && target->details->online) {
|
|
|
5ee7c6 |
+ pe_rsc_trace(rsc, "Marking active on %s %p %d", migrate_target, target,
|
|
|
5ee7c6 |
+ target->details->online);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- native_add_running(rsc, target, data_set);
|
|
|
5ee7c6 |
- if (source && source->details->online) {
|
|
|
5ee7c6 |
- /* If we make it here we have a partial migration. The migrate_to
|
|
|
5ee7c6 |
- * has completed but the migrate_from on the target has not. Hold on
|
|
|
5ee7c6 |
- * to the target and source on the resource. Later on if we detect that
|
|
|
5ee7c6 |
- * the resource is still going to run on that target, we may continue
|
|
|
5ee7c6 |
- * the migration */
|
|
|
5ee7c6 |
- rsc->partial_migration_target = target;
|
|
|
5ee7c6 |
- rsc->partial_migration_source = source;
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
- } else {
|
|
|
5ee7c6 |
- /* Consider it failed here - forces a restart, prevents migration */
|
|
|
5ee7c6 |
- set_bit(rsc->flags, pe_rsc_failed);
|
|
|
5ee7c6 |
- clear_bit(rsc->flags, pe_rsc_allow_migrate);
|
|
|
5ee7c6 |
+ native_add_running(rsc, target, data_set);
|
|
|
5ee7c6 |
+ if (source && source->details->online) {
|
|
|
5ee7c6 |
+ /* This is a partial migration: the migrate_to completed
|
|
|
5ee7c6 |
+ * successfully on the source, but the migrate_from has not
|
|
|
5ee7c6 |
+ * completed. Remember the source and target; if the newly
|
|
|
5ee7c6 |
+ * chosen target remains the same when we schedule actions
|
|
|
5ee7c6 |
+ * later, we may continue with the migration.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+ rsc->partial_migration_target = target;
|
|
|
5ee7c6 |
+ rsc->partial_migration_source = source;
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
+ } else {
|
|
|
5ee7c6 |
+ /* Consider it failed here - forces a restart, prevents migration */
|
|
|
5ee7c6 |
+ set_bit(rsc->flags, pe_rsc_failed);
|
|
|
5ee7c6 |
+ clear_bit(rsc->flags, pe_rsc_allow_migrate);
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
--
|
|
|
5ee7c6 |
1.8.3.1
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
From 37913a1dec2bda66476bddb5559817d23058be59 Mon Sep 17 00:00:00 2001
|
|
|
5ee7c6 |
From: Ken Gaillot <kgaillot@redhat.com>
|
|
|
5ee7c6 |
Date: Mon, 21 May 2018 12:43:09 -0500
|
|
|
5ee7c6 |
Subject: [PATCH 2/3] Refactor: libpe_status: new functions for finding
|
|
|
5ee7c6 |
resource's active nodes
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
Existing code often grabs rsc->running_on->data (i.e. the first node in the
|
|
|
5ee7c6 |
list) as the resource's current node, and often uses
|
|
|
5ee7c6 |
g_list_length(rsc->running_on).
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
However, if the resource is in the middle of a partial migration, the migration
|
|
|
5ee7c6 |
source should be preferred as the current node. Also, if a resource has
|
|
|
5ee7c6 |
"requires" set to "nothing" or "quorum", a clean, online node should be
|
|
|
5ee7c6 |
preferred as the current node, and a caller should ignore unclean and offline
|
|
|
5ee7c6 |
nodes when counting in certain cases.
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
These functions will allow those issues to be addressed in later commits.
|
|
|
5ee7c6 |
---
|
|
|
5ee7c6 |
include/crm/pengine/internal.h | 34 +++++++-----
|
|
|
5ee7c6 |
lib/pengine/complex.c | 121 ++++++++++++++++++++++++++++++++++++-----
|
|
|
5ee7c6 |
2 files changed, 127 insertions(+), 28 deletions(-)
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
diff --git a/include/crm/pengine/internal.h b/include/crm/pengine/internal.h
|
|
|
5ee7c6 |
index e9d7582..fe8f6a1 100644
|
|
|
5ee7c6 |
--- a/include/crm/pengine/internal.h
|
|
|
5ee7c6 |
+++ b/include/crm/pengine/internal.h
|
|
|
5ee7c6 |
@@ -1,20 +1,10 @@
|
|
|
5ee7c6 |
/*
|
|
|
5ee7c6 |
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
|
|
|
5ee7c6 |
+ * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
|
|
|
5ee7c6 |
*
|
|
|
5ee7c6 |
- * This program is free software; you can redistribute it and/or
|
|
|
5ee7c6 |
- * modify it under the terms of the GNU Lesser General Public
|
|
|
5ee7c6 |
- * License as published by the Free Software Foundation; either
|
|
|
5ee7c6 |
- * version 2 of the License, or (at your option) any later version.
|
|
|
5ee7c6 |
- *
|
|
|
5ee7c6 |
- * This software is distributed in the hope that it will be useful,
|
|
|
5ee7c6 |
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
5ee7c6 |
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
5ee7c6 |
- * General Public License for more details.
|
|
|
5ee7c6 |
- *
|
|
|
5ee7c6 |
- * You should have received a copy of the GNU Lesser General Public
|
|
|
5ee7c6 |
- * License along with this library; if not, write to the Free Software
|
|
|
5ee7c6 |
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
5ee7c6 |
+ * This source code is licensed under the GNU Lesser General Public License
|
|
|
5ee7c6 |
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
|
|
|
5ee7c6 |
*/
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
#ifndef PE_INTERNAL__H
|
|
|
5ee7c6 |
# define PE_INTERNAL__H
|
|
|
5ee7c6 |
# include <string.h>
|
|
|
5ee7c6 |
@@ -125,6 +115,22 @@ int pe_get_failcount(node_t *node, resource_t *rsc, time_t *last_failure,
|
|
|
5ee7c6 |
uint32_t flags, xmlNode *xml_op,
|
|
|
5ee7c6 |
pe_working_set_t *data_set);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+/* Functions for finding/counting a resource's active nodes */
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+pe_node_t *pe__find_active_on(const resource_t *rsc,
|
|
|
5ee7c6 |
+ unsigned int *count_all,
|
|
|
5ee7c6 |
+ unsigned int *count_clean);
|
|
|
5ee7c6 |
+pe_node_t *pe__find_active_requires(const resource_t *rsc,
|
|
|
5ee7c6 |
+ unsigned int *count);
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+static inline pe_node_t *
|
|
|
5ee7c6 |
+pe__current_node(const resource_t *rsc)
|
|
|
5ee7c6 |
+{
|
|
|
5ee7c6 |
+ return pe__find_active_on(rsc, NULL, NULL);
|
|
|
5ee7c6 |
+}
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
/* Binary like operators for lists of nodes */
|
|
|
5ee7c6 |
extern void node_list_exclude(GHashTable * list, GListPtr list2, gboolean merge_scores);
|
|
|
5ee7c6 |
extern GListPtr node_list_dup(GListPtr list, gboolean reset, gboolean filter);
|
|
|
5ee7c6 |
diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c
|
|
|
5ee7c6 |
index 86f290c..cdd409a 100644
|
|
|
5ee7c6 |
--- a/lib/pengine/complex.c
|
|
|
5ee7c6 |
+++ b/lib/pengine/complex.c
|
|
|
5ee7c6 |
@@ -1,19 +1,8 @@
|
|
|
5ee7c6 |
/*
|
|
|
5ee7c6 |
- * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
|
|
|
5ee7c6 |
+ * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
|
|
|
5ee7c6 |
*
|
|
|
5ee7c6 |
- * This library is free software; you can redistribute it and/or
|
|
|
5ee7c6 |
- * modify it under the terms of the GNU Lesser General Public
|
|
|
5ee7c6 |
- * License as published by the Free Software Foundation; either
|
|
|
5ee7c6 |
- * version 2.1 of the License, or (at your option) any later version.
|
|
|
5ee7c6 |
- *
|
|
|
5ee7c6 |
- * This library is distributed in the hope that it will be useful,
|
|
|
5ee7c6 |
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
5ee7c6 |
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
5ee7c6 |
- * Lesser General Public License for more details.
|
|
|
5ee7c6 |
- *
|
|
|
5ee7c6 |
- * You should have received a copy of the GNU Lesser General Public
|
|
|
5ee7c6 |
- * License along with this library; if not, write to the Free Software
|
|
|
5ee7c6 |
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
5ee7c6 |
+ * This source code is licensed under the GNU Lesser General Public License
|
|
|
5ee7c6 |
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
|
|
|
5ee7c6 |
*/
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
#include <crm_internal.h>
|
|
|
5ee7c6 |
@@ -981,3 +970,107 @@ common_free(resource_t * rsc)
|
|
|
5ee7c6 |
free(rsc->pending_task);
|
|
|
5ee7c6 |
free(rsc);
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+/*!
|
|
|
5ee7c6 |
+ * \brief
|
|
|
5ee7c6 |
+ * \internal Find a node (and optionally count all) where resource is active
|
|
|
5ee7c6 |
+ *
|
|
|
5ee7c6 |
+ * \param[in] rsc Resource to check
|
|
|
5ee7c6 |
+ * \param[out] count_all If not NULL, will be set to count of active nodes
|
|
|
5ee7c6 |
+ * \param[out] count_clean If not NULL, will be set to count of clean nodes
|
|
|
5ee7c6 |
+ *
|
|
|
5ee7c6 |
+ * \return An active node (or NULL if resource is not active anywhere)
|
|
|
5ee7c6 |
+ *
|
|
|
5ee7c6 |
+ * \note The order of preference is: an active node that is the resource's
|
|
|
5ee7c6 |
+ * partial migration source; if the resource's "requires" is "quorum" or
|
|
|
5ee7c6 |
+ * "nothing", the first active node in the list that is clean and online;
|
|
|
5ee7c6 |
+ * the first active node in the list.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+pe_node_t *
|
|
|
5ee7c6 |
+pe__find_active_on(const resource_t *rsc, unsigned int *count_all,
|
|
|
5ee7c6 |
+ unsigned int *count_clean)
|
|
|
5ee7c6 |
+{
|
|
|
5ee7c6 |
+ pe_node_t *active = NULL;
|
|
|
5ee7c6 |
+ pe_node_t *node = NULL;
|
|
|
5ee7c6 |
+ bool keep_looking = FALSE;
|
|
|
5ee7c6 |
+ bool is_happy = FALSE;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (count_all) {
|
|
|
5ee7c6 |
+ *count_all = 0;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ if (count_clean) {
|
|
|
5ee7c6 |
+ *count_clean = 0;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ if (rsc == NULL) {
|
|
|
5ee7c6 |
+ return NULL;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ for (GList *node_iter = rsc->running_on; node_iter != NULL;
|
|
|
5ee7c6 |
+ node_iter = node_iter->next) {
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ node = node_iter->data;
|
|
|
5ee7c6 |
+ keep_looking = FALSE;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ is_happy = node->details->online && !node->details->unclean;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (count_all) {
|
|
|
5ee7c6 |
+ ++*count_all;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ if (count_clean && is_happy) {
|
|
|
5ee7c6 |
+ ++*count_clean;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ if (count_all || count_clean) {
|
|
|
5ee7c6 |
+ // If we're counting, we need to go through entire list
|
|
|
5ee7c6 |
+ keep_looking = TRUE;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (rsc->partial_migration_source != NULL) {
|
|
|
5ee7c6 |
+ if (node->details == rsc->partial_migration_source->details) {
|
|
|
5ee7c6 |
+ // This is the migration source
|
|
|
5ee7c6 |
+ active = node;
|
|
|
5ee7c6 |
+ } else {
|
|
|
5ee7c6 |
+ keep_looking = TRUE;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ } else if (is_not_set(rsc->flags, pe_rsc_needs_fencing)) {
|
|
|
5ee7c6 |
+ if (is_happy && (!active || !active->details->online
|
|
|
5ee7c6 |
+ || active->details->unclean)) {
|
|
|
5ee7c6 |
+ // This is the first clean node
|
|
|
5ee7c6 |
+ active = node;
|
|
|
5ee7c6 |
+ } else {
|
|
|
5ee7c6 |
+ keep_looking = TRUE;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ if (active == NULL) {
|
|
|
5ee7c6 |
+ // This is first node in list
|
|
|
5ee7c6 |
+ active = node;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (keep_looking == FALSE) {
|
|
|
5ee7c6 |
+ // Don't waste time iterating if we don't have to
|
|
|
5ee7c6 |
+ break;
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ return active;
|
|
|
5ee7c6 |
+}
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+/*!
|
|
|
5ee7c6 |
+ * \brief
|
|
|
5ee7c6 |
+ * \internal Find and count active nodes according to "requires"
|
|
|
5ee7c6 |
+ *
|
|
|
5ee7c6 |
+ * \param[in] rsc Resource to check
|
|
|
5ee7c6 |
+ * \param[out] count If not NULL, will be set to count of active nodes
|
|
|
5ee7c6 |
+ *
|
|
|
5ee7c6 |
+ * \return An active node (or NULL if resource is not active anywhere)
|
|
|
5ee7c6 |
+ *
|
|
|
5ee7c6 |
+ * \note This is a convenience wrapper for pe__find_active_on() where the count
|
|
|
5ee7c6 |
+ * of all active nodes or only clean active nodes is desired according to
|
|
|
5ee7c6 |
+ * the "requires" meta-attribute.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+pe_node_t *
|
|
|
5ee7c6 |
+pe__find_active_requires(const resource_t *rsc, unsigned int *count)
|
|
|
5ee7c6 |
+{
|
|
|
5ee7c6 |
+ if (rsc && is_not_set(rsc->flags, pe_rsc_needs_fencing)) {
|
|
|
5ee7c6 |
+ return pe__find_active_on(rsc, NULL, count);
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ return pe__find_active_on(rsc, count, NULL);
|
|
|
5ee7c6 |
+}
|
|
|
5ee7c6 |
--
|
|
|
5ee7c6 |
1.8.3.1
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
From e752fcfa10ee68f8a8de48122ae0f73190ae30af Mon Sep 17 00:00:00 2001
|
|
|
5ee7c6 |
From: Ken Gaillot <kgaillot@redhat.com>
|
|
|
5ee7c6 |
Date: Mon, 21 May 2018 09:36:00 -0500
|
|
|
5ee7c6 |
Subject: [PATCH 3/3] Fix: libpe_status: find active instances properly
|
|
|
5ee7c6 |
according to requires
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
If a resource has "requires" set to "nothing" or "quorum", that means we can
|
|
|
5ee7c6 |
properly start it elsewhere, even if the node believed to be initially running
|
|
|
5ee7c6 |
the resource is unclean and waiting to be fenced.
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
Previously, if we did start the resource elsewhere before fencing completed,
|
|
|
5ee7c6 |
the cluster would then consider the resource multiply active, and recover it.
|
|
|
5ee7c6 |
Now, we don't consider such a resource multiply active if it's active on
|
|
|
5ee7c6 |
only one clean node.
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
Status displays still show the resource as started on the unclean node, to give
|
|
|
5ee7c6 |
the administrator a better idea of the actual situation. However, the clean
|
|
|
5ee7c6 |
node will be considered the "current" node.
|
|
|
5ee7c6 |
---
|
|
|
5ee7c6 |
lib/pengine/native.c | 21 ++++++++--
|
|
|
5ee7c6 |
pengine/native.c | 107 +++++++++++++++++++++++++--------------------------
|
|
|
5ee7c6 |
2 files changed, 70 insertions(+), 58 deletions(-)
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
diff --git a/lib/pengine/native.c b/lib/pengine/native.c
|
|
|
5ee7c6 |
index f6d1653..e01ef17 100644
|
|
|
5ee7c6 |
--- a/lib/pengine/native.c
|
|
|
5ee7c6 |
+++ b/lib/pengine/native.c
|
|
|
5ee7c6 |
@@ -17,6 +17,21 @@
|
|
|
5ee7c6 |
#define VARIANT_NATIVE 1
|
|
|
5ee7c6 |
#include "./variant.h"
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
+/*!
|
|
|
5ee7c6 |
+ * \internal
|
|
|
5ee7c6 |
+ * \brief Check whether a resource is active on multiple nodes
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+static bool
|
|
|
5ee7c6 |
+is_multiply_active(pe_resource_t *rsc)
|
|
|
5ee7c6 |
+{
|
|
|
5ee7c6 |
+ unsigned int count = 0;
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ if (rsc->variant == pe_native) {
|
|
|
5ee7c6 |
+ pe__find_active_requires(rsc, &count);
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
+ return count > 1;
|
|
|
5ee7c6 |
+}
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
void
|
|
|
5ee7c6 |
native_add_running(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
{
|
|
|
5ee7c6 |
@@ -58,7 +73,7 @@ native_add_running(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
return;
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- if (rsc->variant == pe_native && g_list_length(rsc->running_on) > 1) {
|
|
|
5ee7c6 |
+ if (is_multiply_active(rsc)) {
|
|
|
5ee7c6 |
switch (rsc->recovery_type) {
|
|
|
5ee7c6 |
case recovery_stop_only:
|
|
|
5ee7c6 |
{
|
|
|
5ee7c6 |
@@ -99,8 +114,8 @@ native_add_running(resource_t * rsc, node_t * node, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
break;
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
- crm_debug("%s is active on %d nodes including %s: %s",
|
|
|
5ee7c6 |
- rsc->id, g_list_length(rsc->running_on), node->details->uname,
|
|
|
5ee7c6 |
+ crm_debug("%s is active on multiple nodes including %s: %s",
|
|
|
5ee7c6 |
+ rsc->id, node->details->uname,
|
|
|
5ee7c6 |
recovery2text(rsc->recovery_type));
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
} else {
|
|
|
5ee7c6 |
diff --git a/pengine/native.c b/pengine/native.c
|
|
|
5ee7c6 |
index e3e0c59..37ac2e4 100644
|
|
|
5ee7c6 |
--- a/pengine/native.c
|
|
|
5ee7c6 |
+++ b/pengine/native.c
|
|
|
5ee7c6 |
@@ -1163,7 +1163,9 @@ native_create_actions(resource_t * rsc, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
gboolean allow_migrate = is_set(rsc->flags, pe_rsc_allow_migrate) ? TRUE : FALSE;
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
GListPtr gIter = NULL;
|
|
|
5ee7c6 |
- int num_active_nodes = 0;
|
|
|
5ee7c6 |
+ unsigned int num_all_active = 0;
|
|
|
5ee7c6 |
+ unsigned int num_clean_active = 0;
|
|
|
5ee7c6 |
+ bool multiply_active = FALSE;
|
|
|
5ee7c6 |
enum rsc_role_e role = RSC_ROLE_UNKNOWN;
|
|
|
5ee7c6 |
enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
@@ -1181,18 +1183,7 @@ native_create_actions(resource_t * rsc, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
pe_rsc_trace(rsc, "Processing state transition for %s %p: %s->%s", rsc->id, rsc,
|
|
|
5ee7c6 |
role2text(rsc->role), role2text(rsc->next_role));
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- if (rsc->running_on) {
|
|
|
5ee7c6 |
- current = rsc->running_on->data;
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
-
|
|
|
5ee7c6 |
- for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) {
|
|
|
5ee7c6 |
- node_t *n = (node_t *) gIter->data;
|
|
|
5ee7c6 |
- if (rsc->partial_migration_source &&
|
|
|
5ee7c6 |
- (n->details == rsc->partial_migration_source->details)) {
|
|
|
5ee7c6 |
- current = rsc->partial_migration_source;
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
- num_active_nodes++;
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
+ current = pe__find_active_on(rsc, &num_all_active, &num_clean_active);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
for (gIter = rsc->dangling_migrations; gIter != NULL; gIter = gIter->next) {
|
|
|
5ee7c6 |
node_t *current = (node_t *) gIter->data;
|
|
|
5ee7c6 |
@@ -1207,47 +1198,57 @@ native_create_actions(resource_t * rsc, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- if (num_active_nodes > 1) {
|
|
|
5ee7c6 |
+ if ((num_all_active == 2) && (num_clean_active == 2) && chosen
|
|
|
5ee7c6 |
+ && rsc->partial_migration_source && rsc->partial_migration_target
|
|
|
5ee7c6 |
+ && (current->details == rsc->partial_migration_source->details)
|
|
|
5ee7c6 |
+ && (chosen->details == rsc->partial_migration_target->details)) {
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- if (num_active_nodes == 2
|
|
|
5ee7c6 |
- && chosen
|
|
|
5ee7c6 |
- && rsc->partial_migration_target
|
|
|
5ee7c6 |
- && rsc->partial_migration_source
|
|
|
5ee7c6 |
- && (current->details == rsc->partial_migration_source->details)
|
|
|
5ee7c6 |
- && (chosen->details == rsc->partial_migration_target->details)) {
|
|
|
5ee7c6 |
- /* Here the chosen node is still the migration target from a partial
|
|
|
5ee7c6 |
- * migration. Attempt to continue the migration instead of recovering
|
|
|
5ee7c6 |
- * by stopping the resource everywhere and starting it on a single node. */
|
|
|
5ee7c6 |
- pe_rsc_trace(rsc,
|
|
|
5ee7c6 |
- "Will attempt to continue with a partial migration to target %s from %s",
|
|
|
5ee7c6 |
- rsc->partial_migration_target->details->id,
|
|
|
5ee7c6 |
- rsc->partial_migration_source->details->id);
|
|
|
5ee7c6 |
- } else {
|
|
|
5ee7c6 |
- const char *type = crm_element_value(rsc->xml, XML_ATTR_TYPE);
|
|
|
5ee7c6 |
- const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
|
|
|
5ee7c6 |
+ /* The chosen node is still the migration target from a partial
|
|
|
5ee7c6 |
+ * migration. Attempt to continue the migration instead of recovering
|
|
|
5ee7c6 |
+ * by stopping the resource everywhere and starting it on a single node.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+ pe_rsc_trace(rsc,
|
|
|
5ee7c6 |
+ "Will attempt to continue with a partial migration to target %s from %s",
|
|
|
5ee7c6 |
+ rsc->partial_migration_target->details->id,
|
|
|
5ee7c6 |
+ rsc->partial_migration_source->details->id);
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ } else if (is_not_set(rsc->flags, pe_rsc_needs_fencing)) {
|
|
|
5ee7c6 |
+ /* If a resource has "requires" set to nothing or quorum, don't consider
|
|
|
5ee7c6 |
+ * it active on unclean nodes (similar to how all resources behave when
|
|
|
5ee7c6 |
+ * stonith-enabled is false). We can start such resources elsewhere
|
|
|
5ee7c6 |
+ * before fencing completes, and if we considered the resource active on
|
|
|
5ee7c6 |
+ * the failed node, we would attempt recovery for being active on
|
|
|
5ee7c6 |
+ * multiple nodes.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+ multiply_active = (num_clean_active > 1);
|
|
|
5ee7c6 |
+ } else {
|
|
|
5ee7c6 |
+ multiply_active = (num_all_active > 1);
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- if(rsc->partial_migration_target && rsc->partial_migration_source) {
|
|
|
5ee7c6 |
- crm_notice("Resource %s can no longer migrate to %s. Stopping on %s too", rsc->id,
|
|
|
5ee7c6 |
- rsc->partial_migration_target->details->uname,
|
|
|
5ee7c6 |
- rsc->partial_migration_source->details->uname);
|
|
|
5ee7c6 |
+ if (multiply_active) {
|
|
|
5ee7c6 |
+ if (rsc->partial_migration_target && rsc->partial_migration_source) {
|
|
|
5ee7c6 |
+ // Migration was in progress, but we've chosen a different target
|
|
|
5ee7c6 |
+ crm_notice("Resource %s can no longer migrate to %s. Stopping on %s too",
|
|
|
5ee7c6 |
+ rsc->id, rsc->partial_migration_target->details->uname,
|
|
|
5ee7c6 |
+ rsc->partial_migration_source->details->uname);
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- } else {
|
|
|
5ee7c6 |
- pe_proc_err("Resource %s (%s::%s) is active on %d nodes %s",
|
|
|
5ee7c6 |
- rsc->id, class, type, num_active_nodes, recovery2text(rsc->recovery_type));
|
|
|
5ee7c6 |
- crm_warn("See %s for more information.",
|
|
|
5ee7c6 |
- "http://clusterlabs.org/wiki/FAQ#Resource_is_Too_Active");
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
-
|
|
|
5ee7c6 |
- if (rsc->recovery_type == recovery_stop_start) {
|
|
|
5ee7c6 |
- need_stop = TRUE;
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
+ } else {
|
|
|
5ee7c6 |
+ // Resource was incorrectly multiply active
|
|
|
5ee7c6 |
+ pe_proc_err("Resource %s is active on %u nodes (%s)",
|
|
|
5ee7c6 |
+ rsc->id, num_all_active,
|
|
|
5ee7c6 |
+ recovery2text(rsc->recovery_type));
|
|
|
5ee7c6 |
+ crm_notice("See https://wiki.clusterlabs.org/wiki/FAQ#Resource_is_Too_Active for more information");
|
|
|
5ee7c6 |
+ }
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
- /* If by chance a partial migration is in process,
|
|
|
5ee7c6 |
- * but the migration target is not chosen still, clear all
|
|
|
5ee7c6 |
- * partial migration data. */
|
|
|
5ee7c6 |
- rsc->partial_migration_source = rsc->partial_migration_target = NULL;
|
|
|
5ee7c6 |
- allow_migrate = FALSE;
|
|
|
5ee7c6 |
+ if (rsc->recovery_type == recovery_stop_start) {
|
|
|
5ee7c6 |
+ need_stop = TRUE;
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
+
|
|
|
5ee7c6 |
+ /* If by chance a partial migration is in process, but the migration
|
|
|
5ee7c6 |
+ * target is not chosen still, clear all partial migration data.
|
|
|
5ee7c6 |
+ */
|
|
|
5ee7c6 |
+ rsc->partial_migration_source = rsc->partial_migration_target = NULL;
|
|
|
5ee7c6 |
+ allow_migrate = FALSE;
|
|
|
5ee7c6 |
}
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
if (is_set(rsc->flags, pe_rsc_start_pending)) {
|
|
|
5ee7c6 |
@@ -1339,7 +1341,7 @@ native_create_actions(resource_t * rsc, pe_working_set_t * data_set)
|
|
|
5ee7c6 |
is_not_set(rsc->flags, pe_rsc_managed) ||
|
|
|
5ee7c6 |
is_set(rsc->flags, pe_rsc_failed) ||
|
|
|
5ee7c6 |
is_set(rsc->flags, pe_rsc_start_pending) ||
|
|
|
5ee7c6 |
- (current->details->unclean == TRUE) ||
|
|
|
5ee7c6 |
+ (current && current->details->unclean) ||
|
|
|
5ee7c6 |
rsc->next_role < RSC_ROLE_STARTED) {
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
allow_migrate = FALSE;
|
|
|
5ee7c6 |
@@ -2329,12 +2331,7 @@ LogActions(resource_t * rsc, pe_working_set_t * data_set, gboolean terminal)
|
|
|
5ee7c6 |
|
|
|
5ee7c6 |
next = rsc->allocated_to;
|
|
|
5ee7c6 |
if (rsc->running_on) {
|
|
|
5ee7c6 |
- if (g_list_length(rsc->running_on) > 1 && rsc->partial_migration_source) {
|
|
|
5ee7c6 |
- current = rsc->partial_migration_source;
|
|
|
5ee7c6 |
- } else {
|
|
|
5ee7c6 |
- current = rsc->running_on->data;
|
|
|
5ee7c6 |
- }
|
|
|
5ee7c6 |
-
|
|
|
5ee7c6 |
+ current = pe__current_node(rsc);
|
|
|
5ee7c6 |
if (rsc->role == RSC_ROLE_STOPPED) {
|
|
|
5ee7c6 |
/*
|
|
|
5ee7c6 |
* This can occur when resources are being recovered
|
|
|
5ee7c6 |
--
|
|
|
5ee7c6 |
1.8.3.1
|
|
|
5ee7c6 |
|