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