root/lib/pacemaker/pcmk_sched_nodes.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk__node_available
  2. pcmk__copy_node_table
  3. pcmk__copy_node_list
  4. compare_nodes
  5. pcmk__sort_nodes
  6. pcmk__any_node_available
  7. pcmk__apply_node_health

   1 /*
   2  * Copyright 2004-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/msg_xml.h>
  12 #include <crm/lrmd.h>       // lrmd_event_data_t
  13 #include <crm/common/xml_internal.h>
  14 #include <pacemaker-internal.h>
  15 #include <pacemaker.h>
  16 #include "libpacemaker_private.h"
  17 
  18 /*!
  19  * \internal
  20  * \brief Check whether a node is available to run resources
  21  *
  22  * \param[in] node  Node to check
  23  *
  24  * \return true if node is online and not shutting down, unclean, or in standby
  25  *         or maintenance mode, otherwise false
  26  */
  27 bool
  28 pcmk__node_available(const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30     // @TODO Should we add (node->weight >= 0)?
  31     return (node != NULL) && (node->details != NULL) && node->details->online
  32             && !node->details->shutdown && !node->details->unclean
  33             && !node->details->standby && !node->details->maintenance;
  34 }
  35 
  36 /*!
  37  * \internal
  38  * \brief Copy a hash table of node objects
  39  *
  40  * \param[in] nodes  Hash table to copy
  41  *
  42  * \return New copy of nodes (or NULL if nodes is NULL)
  43  */
  44 GHashTable *
  45 pcmk__copy_node_table(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
  46 {
  47     GHashTable *new_table = NULL;
  48     GHashTableIter iter;
  49     pe_node_t *node = NULL;
  50 
  51     if (nodes == NULL) {
  52         return NULL;
  53     }
  54     new_table = pcmk__strkey_table(NULL, free);
  55     g_hash_table_iter_init(&iter, nodes);
  56     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
  57         pe_node_t *new_node = pe__copy_node(node);
  58 
  59         g_hash_table_insert(new_table, (gpointer) new_node->details->id,
  60                             new_node);
  61     }
  62     return new_table;
  63 }
  64 
  65 /*!
  66  * \internal
  67  * \brief Copy a list of node objects
  68  *
  69  * \param[in] list   List to copy
  70  * \param[in] reset  Set copies' scores to 0
  71  *
  72  * \return New list of shallow copies of nodes in original list
  73  */
  74 GList *
  75 pcmk__copy_node_list(const GList *list, bool reset)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77     GList *result = NULL;
  78 
  79     for (const GList *gIter = list; gIter != NULL; gIter = gIter->next) {
  80         pe_node_t *new_node = NULL;
  81         pe_node_t *this_node = (pe_node_t *) gIter->data;
  82 
  83         new_node = pe__copy_node(this_node);
  84         if (reset) {
  85             new_node->weight = 0;
  86         }
  87         result = g_list_prepend(result, new_node);
  88     }
  89     return result;
  90 }
  91 
  92 struct node_weight_s {
  93     pe_node_t *active;
  94     pe_working_set_t *data_set;
  95 };
  96 
  97 /*!
  98  * \internal
  99  * \brief Compare two nodes for allocation desirability
 100  *
 101  * Given two nodes, check which one is more preferred by allocation criteria
 102  * such as node weight and utilization.
 103  *
 104  * \param[in] a     First node to compare
 105  * \param[in] b     Second node to compare
 106  * \param[in] data  Sort data (as struct node_weight_s *)
 107  *
 108  * \return -1 if \p a is preferred, +1 if \p b is preferred, or 0 if they are
 109  *         equally preferred
 110  */
 111 static gint
 112 compare_nodes(gconstpointer a, gconstpointer b, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 113 {
 114     const pe_node_t *node1 = (const pe_node_t *) a;
 115     const pe_node_t *node2 = (const pe_node_t *) b;
 116     struct node_weight_s *nw = data;
 117 
 118     int node1_weight = 0;
 119     int node2_weight = 0;
 120 
 121     int result = 0;
 122 
 123     if (a == NULL) {
 124         return 1;
 125     }
 126     if (b == NULL) {
 127         return -1;
 128     }
 129 
 130     // Compare node weights
 131 
 132     node1_weight = pcmk__node_available(node1)? node1->weight : -INFINITY;
 133     node2_weight = pcmk__node_available(node2)? node2->weight : -INFINITY;
 134 
 135     if (node1_weight > node2_weight) {
 136         crm_trace("%s (%d) > %s (%d) : weight",
 137                   node1->details->uname, node1_weight, node2->details->uname,
 138                   node2_weight);
 139         return -1;
 140     }
 141 
 142     if (node1_weight < node2_weight) {
 143         crm_trace("%s (%d) < %s (%d) : weight",
 144                   node1->details->uname, node1_weight, node2->details->uname,
 145                   node2_weight);
 146         return 1;
 147     }
 148 
 149     crm_trace("%s (%d) == %s (%d) : weight",
 150               node1->details->uname, node1_weight, node2->details->uname,
 151               node2_weight);
 152 
 153     // If appropriate, compare node utilization
 154 
 155     if (pcmk__str_eq(nw->data_set->placement_strategy, "minimal",
 156                      pcmk__str_casei)) {
 157         goto equal;
 158     }
 159 
 160     if (pcmk__str_eq(nw->data_set->placement_strategy, "balanced",
 161                      pcmk__str_casei)) {
 162         result = pcmk__compare_node_capacities(node1, node2);
 163         if (result < 0) {
 164             crm_trace("%s > %s : capacity (%d)",
 165                       node1->details->uname, node2->details->uname, result);
 166             return -1;
 167         } else if (result > 0) {
 168             crm_trace("%s < %s : capacity (%d)",
 169                       node1->details->uname, node2->details->uname, result);
 170             return 1;
 171         }
 172     }
 173 
 174     // Compare number of allocated resources
 175 
 176     if (node1->details->num_resources < node2->details->num_resources) {
 177         crm_trace("%s (%d) > %s (%d) : resources",
 178                   node1->details->uname, node1->details->num_resources,
 179                   node2->details->uname, node2->details->num_resources);
 180         return -1;
 181 
 182     } else if (node1->details->num_resources > node2->details->num_resources) {
 183         crm_trace("%s (%d) < %s (%d) : resources",
 184                   node1->details->uname, node1->details->num_resources,
 185                   node2->details->uname, node2->details->num_resources);
 186         return 1;
 187     }
 188 
 189     // Check whether one node is already running desired resource
 190 
 191     if (nw->active != NULL) {
 192         if (nw->active->details == node1->details) {
 193             crm_trace("%s (%d) > %s (%d) : active",
 194                       node1->details->uname, node1->details->num_resources,
 195                       node2->details->uname, node2->details->num_resources);
 196             return -1;
 197         } else if (nw->active->details == node2->details) {
 198             crm_trace("%s (%d) < %s (%d) : active",
 199                       node1->details->uname, node1->details->num_resources,
 200                       node2->details->uname, node2->details->num_resources);
 201             return 1;
 202         }
 203     }
 204 
 205     // If all else is equal, prefer node with lowest-sorting name
 206 equal:
 207     crm_trace("%s = %s", node1->details->uname, node2->details->uname);
 208     return strcmp(node1->details->uname, node2->details->uname);
 209 }
 210 
 211 /*!
 212  * \internal
 213  * \brief Sort a list of nodes by allocation desirability
 214  *
 215  * \param[in] nodes        Node list to sort
 216  * \param[in] active_node  If not NULL, node currently running resource
 217  * \param[in] data_set     Cluster working set
 218  *
 219  * \return New head of sorted list
 220  */
 221 GList *
 222 pcmk__sort_nodes(GList *nodes, pe_node_t *active_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 223                  pe_working_set_t *data_set)
 224 {
 225     struct node_weight_s nw = { active_node, data_set };
 226 
 227     return g_list_sort_with_data(nodes, compare_nodes, &nw);
 228 }
 229 
 230 /*!
 231  * \internal
 232  * \brief Check whether any node is available to run resources
 233  *
 234  * \param[in] nodes  Nodes to check
 235  *
 236  * \return true if any node in \p nodes is available to run resources,
 237  *         otherwise false
 238  */
 239 bool
 240 pcmk__any_node_available(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242     GHashTableIter iter;
 243     pe_node_t *node = NULL;
 244 
 245     if (nodes == NULL) {
 246         return false;
 247     }
 248     g_hash_table_iter_init(&iter, nodes);
 249     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 250         if ((node->weight >= 0) && pcmk__node_available(node)) {
 251             return true;
 252         }
 253     }
 254     return false;
 255 }
 256 
 257 /*!
 258  * \internal
 259  * \brief Apply node health values for all nodes in cluster
 260  *
 261  * \param[in] data_set  Cluster working set
 262  */
 263 void
 264 pcmk__apply_node_health(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 265 {
 266     int base_health = 0;
 267     enum pcmk__health_strategy strategy;
 268     const char *strategy_str = pe_pref(data_set->config_hash,
 269                                        PCMK__OPT_NODE_HEALTH_STRATEGY);
 270 
 271     strategy = pcmk__parse_health_strategy(strategy_str);
 272     if (strategy == pcmk__health_strategy_none) {
 273         return;
 274     }
 275     crm_info("Applying node health strategy '%s'", strategy_str);
 276 
 277     // The progressive strategy can use a base health score
 278     if (strategy == pcmk__health_strategy_progressive) {
 279         base_health = pe__health_score(PCMK__OPT_NODE_HEALTH_BASE, data_set);
 280     }
 281 
 282     for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
 283         pe_node_t *node = (pe_node_t *) iter->data;
 284         int health = pe__sum_node_health_scores(node, base_health);
 285 
 286         // An overall health score of 0 has no effect
 287         if (health == 0) {
 288             continue;
 289         }
 290         crm_info("Node %s overall system health is %d",
 291                  node->details->uname, health);
 292 
 293         // Use node health as a location score for each resource on the node
 294         for (GList *r = data_set->resources; r != NULL; r = r->next) {
 295             pe_resource_t *rsc = (pe_resource_t *) r->data;
 296 
 297             bool constrain = true;
 298 
 299             if (health < 0) {
 300                 /* Negative health scores do not apply to resources with
 301                  * allow-unhealthy-nodes=true.
 302                  */
 303                 constrain = !crm_is_true(g_hash_table_lookup(rsc->meta,
 304                                          PCMK__META_ALLOW_UNHEALTHY_NODES));
 305             }
 306             if (constrain) {
 307                 pcmk__new_location(strategy_str, rsc, health, NULL, node,
 308                                    data_set);
 309             } else {
 310                 pe_rsc_trace(rsc, "%s is immune from health ban on %s",
 311                              rsc->id, node->details->uname);
 312             }
 313         }
 314     }
 315 }

/* [previous][next][first][last][top][bottom][index][help] */