Blob Blame History Raw
From f5d88938955f63935058b7cc2d706a12e6ea1121 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Fri, 6 Dec 2019 11:57:59 -0600
Subject: [PATCH 07/18] Low: scheduler: respect shutdown locks when placing
 active resources

Use new pe_resource_t members to indicate that a resource is locked to a
particular node.

For active resources (i.e. in the transition where the node is scheduled for
shutdown), these are connected by checking each lockable resource for whether
it is running on a single clean node that is shutting down.

When applying constraints, place -INFINITY location constraints for locked
resources on all nodes other than the lock node.

(Inactive resources -- i.e. in later transitions after the node is shut down --
are not yet locked.)
---
 include/crm/pengine/pe_types.h      |  2 +
 lib/pacemaker/pcmk_sched_allocate.c | 87 +++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/include/crm/pengine/pe_types.h b/include/crm/pengine/pe_types.h
index 8a735a3..123d8ef 100644
--- a/include/crm/pengine/pe_types.h
+++ b/include/crm/pengine/pe_types.h
@@ -354,6 +354,8 @@ struct pe_resource_s {
     GListPtr fillers;
 
     pe_node_t *pending_node;    // Node on which pending_task is happening
+    pe_node_t *lock_node;       // Resource is shutdown-locked to this node
+    time_t lock_time;           // When shutdown lock started
 
 #if ENABLE_VERSIONED_ATTRS
     xmlNode *versioned_parameters;
diff --git a/lib/pacemaker/pcmk_sched_allocate.c b/lib/pacemaker/pcmk_sched_allocate.c
index fc2f4cf..0314f1b 100644
--- a/lib/pacemaker/pcmk_sched_allocate.c
+++ b/lib/pacemaker/pcmk_sched_allocate.c
@@ -977,6 +977,87 @@ rsc_discover_filter(resource_t *rsc, node_t *node)
     }
 }
 
+static time_t
+shutdown_time(pe_node_t *node, pe_working_set_t *data_set)
+{
+    const char *shutdown = pe_node_attribute_raw(node, XML_CIB_ATTR_SHUTDOWN);
+    time_t result = 0;
+
+    if (shutdown) {
+        errno = 0;
+        result = (time_t) crm_int_helper(shutdown, NULL);
+        if (errno != 0) {
+            result = 0;
+        }
+    }
+    return result? result : get_effective_time(data_set);
+}
+
+static void
+apply_shutdown_lock(pe_resource_t *rsc, pe_working_set_t *data_set)
+{
+    const char *class;
+
+    // Only primitives and (uncloned) groups may be locked
+    if (rsc->variant == pe_group) {
+        for (GList *item = rsc->children; item != NULL;
+             item = item->next) {
+            apply_shutdown_lock((pe_resource_t *) item->data, data_set);
+        }
+    } else if (rsc->variant != pe_native) {
+        return;
+    }
+
+    // Fence devices and remote connections can't be locked
+    class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
+    if ((class == NULL) || !strcmp(class, PCMK_RESOURCE_CLASS_STONITH)
+        || pe__resource_is_remote_conn(rsc, data_set)) {
+        return;
+    }
+
+    // Only a resource active on exactly one node can be locked
+    if (pcmk__list_of_1(rsc->running_on)) {
+        pe_node_t *node = rsc->running_on->data;
+
+        if (node->details->shutdown) {
+            if (node->details->unclean) {
+                pe_rsc_debug(rsc, "Not locking %s to unclean %s for shutdown",
+                             rsc->id, node->details->uname);
+            } else {
+                rsc->lock_node = node;
+                rsc->lock_time = shutdown_time(node, data_set);
+            }
+        }
+    }
+
+    if (rsc->lock_node == NULL) {
+        // No lock needed
+        return;
+    }
+
+    if (data_set->shutdown_lock > 0) {
+        time_t lock_expiration = rsc->lock_time + data_set->shutdown_lock;
+
+        pe_rsc_info(rsc, "Locking %s to %s due to shutdown (expires @%lld)",
+                    rsc->id, rsc->lock_node->details->uname,
+                    (long long) lock_expiration);
+        pe__update_recheck_time(++lock_expiration, data_set);
+    } else {
+        pe_rsc_info(rsc, "Locking %s to %s due to shutdown",
+                    rsc->id, rsc->lock_node->details->uname);
+    }
+
+    // If resource is locked to one node, ban it from all other nodes
+    for (GList *item = data_set->nodes; item != NULL; item = item->next) {
+        pe_node_t *node = item->data;
+
+        if (strcmp(node->details->uname, rsc->lock_node->details->uname)) {
+            resource_location(rsc, node, -CRM_SCORE_INFINITY,
+                              XML_CONFIG_ATTR_SHUTDOWN_LOCK, data_set);
+        }
+    }
+}
+
 /*
  * Count how many valid nodes we have (so we know the maximum number of
  *  colors we can resolve).
@@ -988,6 +1069,12 @@ stage2(pe_working_set_t * data_set)
 {
     GListPtr gIter = NULL;
 
+    if (is_set(data_set->flags, pe_flag_shutdown_lock)) {
+        for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
+            apply_shutdown_lock((pe_resource_t *) gIter->data, data_set);
+        }
+    }
+
     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
         node_t *node = (node_t *) gIter->data;
 
-- 
1.8.3.1