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. destroy_node_tables
  4. pcmk__copy_node_tables
  5. pcmk__restore_node_tables
  6. pcmk__copy_node_list
  7. compare_nodes
  8. pcmk__sort_nodes
  9. pcmk__any_node_available
  10. pcmk__apply_node_health
  11. pcmk__top_allowed_node

   1 /*
   2  * Copyright 2004-2024 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/common/xml.h>
  12 #include <crm/common/xml_internal.h>
  13 #include <pacemaker-internal.h>
  14 #include <pacemaker.h>
  15 #include "libpacemaker_private.h"
  16 
  17 /*!
  18  * \internal
  19  * \brief Check whether a node is available to run resources
  20  *
  21  * \param[in] node            Node to check
  22  * \param[in] consider_score  If true, consider a negative score unavailable
  23  * \param[in] consider_guest  If true, consider a guest node unavailable whose
  24  *                            resource will not be active
  25  *
  26  * \return true if node is online and not shutting down, unclean, or in standby
  27  *         or maintenance mode, otherwise false
  28  */
  29 bool
  30 pcmk__node_available(const pcmk_node_t *node, bool consider_score,
     /* [previous][next][first][last][top][bottom][index][help] */
  31                      bool consider_guest)
  32 {
  33     if ((node == NULL) || (node->details == NULL) || !node->details->online
  34             || node->details->shutdown || node->details->unclean
  35             || pcmk_is_set(node->priv->flags, pcmk__node_standby)
  36             || node->details->maintenance) {
  37         return false;
  38     }
  39 
  40     if (consider_score && (node->assign->score < 0)) {
  41         return false;
  42     }
  43 
  44     // @TODO Go through all callers to see which should set consider_guest
  45     if (consider_guest && pcmk__is_guest_or_bundle_node(node)) {
  46         pcmk_resource_t *guest = node->priv->remote->priv->launcher;
  47 
  48         if (guest->priv->fns->location(guest, NULL,
  49                                        pcmk__rsc_node_assigned) == NULL) {
  50             return false;
  51         }
  52     }
  53 
  54     return true;
  55 }
  56 
  57 /*!
  58  * \internal
  59  * \brief Create a hash table with copies of another table's nodes
  60  *
  61  * \param[in] nodes  Hash table to copy
  62  *
  63  * \return New table with copies of nodes in \p nodes, or \c NULL if \p nodes is
  64  *         \c NULL
  65  */
  66 GHashTable *
  67 pcmk__copy_node_table(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69     GHashTable *new_table = NULL;
  70     GHashTableIter iter;
  71     pcmk_node_t *node = NULL;
  72 
  73     if (nodes == NULL) {
  74         return NULL;
  75     }
  76     new_table = pcmk__strkey_table(NULL, pcmk__free_node_copy);
  77     g_hash_table_iter_init(&iter, nodes);
  78     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
  79         pcmk_node_t *new_node = pe__copy_node(node);
  80 
  81         g_hash_table_insert(new_table, (gpointer) new_node->priv->id,
  82                             new_node);
  83     }
  84     return new_table;
  85 }
  86 
  87 /*!
  88  * \internal
  89  * \brief Free a table of node tables
  90  *
  91  * \param[in,out] data  Table to free
  92  *
  93  * \note This is a \c GDestroyNotify wrapper for \c g_hash_table_destroy().
  94  */
  95 static void
  96 destroy_node_tables(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  97 {
  98     g_hash_table_destroy((GHashTable *) data);
  99 }
 100 
 101 /*!
 102  * \internal
 103  * \brief Recursively copy the node tables of a resource
 104  *
 105  * Build a hash table containing copies of the allowed nodes tables of \p rsc
 106  * and its entire tree of descendants. The key is the resource ID, and the value
 107  * is a copy of the resource's node table.
 108  *
 109  * \param[in]     rsc   Resource whose node table to copy
 110  * \param[in,out] copy  Where to store the copied node tables
 111  *
 112  * \note \p *copy should be \c NULL for the top-level call.
 113  * \note The caller is responsible for freeing \p copy using
 114  *       \c g_hash_table_destroy().
 115  */
 116 void
 117 pcmk__copy_node_tables(const pcmk_resource_t *rsc, GHashTable **copy)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119     pcmk__assert((rsc != NULL) && (copy != NULL));
 120 
 121     if (*copy == NULL) {
 122         *copy = pcmk__strkey_table(NULL, destroy_node_tables);
 123     }
 124 
 125     g_hash_table_insert(*copy, rsc->id,
 126                         pcmk__copy_node_table(rsc->priv->allowed_nodes));
 127 
 128     for (const GList *iter = rsc->priv->children;
 129          iter != NULL; iter = iter->next) {
 130 
 131         pcmk__copy_node_tables((const pcmk_resource_t *) iter->data, copy);
 132     }
 133 }
 134 
 135 /*!
 136  * \internal
 137  * \brief Recursively restore the node tables of a resource from backup
 138  *
 139  * Given a hash table containing backup copies of the allowed nodes tables of
 140  * \p rsc and its entire tree of descendants, replace the resources' current
 141  * node tables with the backed-up copies.
 142  *
 143  * \param[in,out] rsc     Resource whose node tables to restore
 144  * \param[in]     backup  Table of backup node tables (created by
 145  *                        \c pcmk__copy_node_tables())
 146  *
 147  * \note This function frees the resources' current node tables.
 148  */
 149 void
 150 pcmk__restore_node_tables(pcmk_resource_t *rsc, GHashTable *backup)
     /* [previous][next][first][last][top][bottom][index][help] */
 151 {
 152     pcmk__assert((rsc != NULL) && (backup != NULL));
 153 
 154     g_hash_table_destroy(rsc->priv->allowed_nodes);
 155 
 156     // Copy to avoid danger with multiple restores
 157     rsc->priv->allowed_nodes =
 158         pcmk__copy_node_table(g_hash_table_lookup(backup, rsc->id));
 159 
 160     for (GList *iter = rsc->priv->children;
 161          iter != NULL; iter = iter->next) {
 162 
 163         pcmk__restore_node_tables((pcmk_resource_t *) iter->data, backup);
 164     }
 165 }
 166 
 167 /*!
 168  * \internal
 169  * \brief Copy a list of node objects
 170  *
 171  * \param[in] list   List to copy
 172  * \param[in] reset  Set copies' scores to 0
 173  *
 174  * \return New list of shallow copies of nodes in original list
 175  */
 176 GList *
 177 pcmk__copy_node_list(const GList *list, bool reset)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179     GList *result = NULL;
 180 
 181     for (const GList *iter = list; iter != NULL; iter = iter->next) {
 182         pcmk_node_t *new_node = NULL;
 183         pcmk_node_t *this_node = iter->data;
 184 
 185         new_node = pe__copy_node(this_node);
 186         if (reset) {
 187             new_node->assign->score = 0;
 188         }
 189         result = g_list_prepend(result, new_node);
 190     }
 191     return result;
 192 }
 193 
 194 /*!
 195  * \internal
 196  * \brief Compare two nodes for assignment preference
 197  *
 198  * Given two nodes, check which one is more preferred by assignment criteria
 199  * such as node score and utilization.
 200  *
 201  * \param[in] a     First node to compare
 202  * \param[in] b     Second node to compare
 203  * \param[in] data  Node to prefer if all else equal
 204  *
 205  * \return -1 if \p a is preferred, +1 if \p b is preferred, or 0 if they are
 206  *         equally preferred
 207  */
 208 static gint
 209 compare_nodes(gconstpointer a, gconstpointer b, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211     const pcmk_node_t *node1 = (const pcmk_node_t *) a;
 212     const pcmk_node_t *node2 = (const pcmk_node_t *) b;
 213     const pcmk_node_t *preferred = (const pcmk_node_t *) data;
 214 
 215     int node1_score = -PCMK_SCORE_INFINITY;
 216     int node2_score = -PCMK_SCORE_INFINITY;
 217 
 218     int result = 0;
 219 
 220     if (a == NULL) {
 221         return 1;
 222     }
 223     if (b == NULL) {
 224         return -1;
 225     }
 226 
 227     // Compare node scores
 228 
 229     if (pcmk__node_available(node1, false, false)) {
 230         node1_score = node1->assign->score;
 231     }
 232     if (pcmk__node_available(node2, false, false)) {
 233         node2_score = node2->assign->score;
 234     }
 235 
 236     if (node1_score > node2_score) {
 237         crm_trace("%s before %s (score %d > %d)",
 238                   pcmk__node_name(node1), pcmk__node_name(node2),
 239                   node1_score, node2_score);
 240         return -1;
 241     }
 242 
 243     if (node1_score < node2_score) {
 244         crm_trace("%s after %s (score %d < %d)",
 245                   pcmk__node_name(node1), pcmk__node_name(node2),
 246                   node1_score, node2_score);
 247         return 1;
 248     }
 249 
 250     // If appropriate, compare node utilization
 251 
 252     if (pcmk__str_eq(node1->priv->scheduler->priv->placement_strategy,
 253                      PCMK_VALUE_MINIMAL, pcmk__str_casei)) {
 254         goto equal;
 255     }
 256 
 257     if (pcmk__str_eq(node1->priv->scheduler->priv->placement_strategy,
 258                      PCMK_VALUE_BALANCED, pcmk__str_casei)) {
 259 
 260         result = pcmk__compare_node_capacities(node1, node2);
 261         if (result < 0) {
 262             crm_trace("%s before %s (greater capacity by %d attributes)",
 263                       pcmk__node_name(node1), pcmk__node_name(node2),
 264                       result * -1);
 265             return -1;
 266         } else if (result > 0) {
 267             crm_trace("%s after %s (lower capacity by %d attributes)",
 268                       pcmk__node_name(node1), pcmk__node_name(node2), result);
 269             return 1;
 270         }
 271     }
 272 
 273     // Compare number of resources already assigned to node
 274 
 275     if (node1->priv->num_resources < node2->priv->num_resources) {
 276         crm_trace("%s before %s (%d resources < %d)",
 277                   pcmk__node_name(node1), pcmk__node_name(node2),
 278                   node1->priv->num_resources, node2->priv->num_resources);
 279         return -1;
 280 
 281     } else if (node1->priv->num_resources > node2->priv->num_resources) {
 282         crm_trace("%s after %s (%d resources > %d)",
 283                   pcmk__node_name(node1), pcmk__node_name(node2),
 284                   node1->priv->num_resources, node2->priv->num_resources);
 285         return 1;
 286     }
 287 
 288     // Check whether one node is already running desired resource
 289 
 290     if (preferred != NULL) {
 291         if (pcmk__same_node(preferred, node1)) {
 292             crm_trace("%s before %s (preferred node)",
 293                       pcmk__node_name(node1), pcmk__node_name(node2));
 294             return -1;
 295         } else if (pcmk__same_node(preferred, node2)) {
 296             crm_trace("%s after %s (not preferred node)",
 297                       pcmk__node_name(node1), pcmk__node_name(node2));
 298             return 1;
 299         }
 300     }
 301 
 302     // If all else is equal, prefer node with lowest-sorting name
 303 equal:
 304     result = strcmp(node1->priv->name, node2->priv->name);
 305     if (result < 0) {
 306         crm_trace("%s before %s (name)",
 307                   pcmk__node_name(node1), pcmk__node_name(node2));
 308         return -1;
 309     } else if (result > 0) {
 310         crm_trace("%s after %s (name)",
 311                   pcmk__node_name(node1), pcmk__node_name(node2));
 312         return 1;
 313     }
 314 
 315     crm_trace("%s == %s", pcmk__node_name(node1), pcmk__node_name(node2));
 316     return 0;
 317 }
 318 
 319 /*!
 320  * \internal
 321  * \brief Sort a list of nodes by assigment preference
 322  *
 323  * \param[in,out] nodes        Node list to sort
 324  * \param[in]     active_node  Node where resource being assigned is active
 325  *
 326  * \return New head of sorted list
 327  */
 328 GList *
 329 pcmk__sort_nodes(GList *nodes, pcmk_node_t *active_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 330 {
 331     return g_list_sort_with_data(nodes, compare_nodes, active_node);
 332 }
 333 
 334 /*!
 335  * \internal
 336  * \brief Check whether any node is available to run resources
 337  *
 338  * \param[in] nodes  Nodes to check
 339  *
 340  * \return true if any node in \p nodes is available to run resources,
 341  *         otherwise false
 342  */
 343 bool
 344 pcmk__any_node_available(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 345 {
 346     GHashTableIter iter;
 347     const pcmk_node_t *node = NULL;
 348 
 349     if (nodes == NULL) {
 350         return false;
 351     }
 352     g_hash_table_iter_init(&iter, nodes);
 353     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 354         if (pcmk__node_available(node, true, false)) {
 355             return true;
 356         }
 357     }
 358     return false;
 359 }
 360 
 361 /*!
 362  * \internal
 363  * \brief Apply node health values for all nodes in cluster
 364  *
 365  * \param[in,out] scheduler  Scheduler data
 366  */
 367 void
 368 pcmk__apply_node_health(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 369 {
 370     int base_health = 0;
 371     enum pcmk__health_strategy strategy;
 372     const char *strategy_str =
 373         pcmk__cluster_option(scheduler->priv->options,
 374                              PCMK_OPT_NODE_HEALTH_STRATEGY);
 375 
 376     strategy = pcmk__parse_health_strategy(strategy_str);
 377     if (strategy == pcmk__health_strategy_none) {
 378         return;
 379     }
 380     crm_info("Applying node health strategy '%s'", strategy_str);
 381 
 382     // The progressive strategy can use a base health score
 383     if (strategy == pcmk__health_strategy_progressive) {
 384         base_health = pcmk__health_score(PCMK_OPT_NODE_HEALTH_BASE, scheduler);
 385     }
 386 
 387     for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
 388         pcmk_node_t *node = (pcmk_node_t *) iter->data;
 389         int health = pe__sum_node_health_scores(node, base_health);
 390 
 391         // An overall health score of 0 has no effect
 392         if (health == 0) {
 393             continue;
 394         }
 395         crm_info("Overall system health of %s is %d",
 396                  pcmk__node_name(node), health);
 397 
 398         // Use node health as a location score for each resource on the node
 399         for (GList *r = scheduler->priv->resources; r != NULL; r = r->next) {
 400             pcmk_resource_t *rsc = (pcmk_resource_t *) r->data;
 401 
 402             bool constrain = true;
 403 
 404             if (health < 0) {
 405                 /* Negative health scores do not apply to resources with
 406                  * PCMK_META_ALLOW_UNHEALTHY_NODES=true.
 407                  */
 408                 constrain = !crm_is_true(g_hash_table_lookup(rsc->priv->meta,
 409                                                              PCMK_META_ALLOW_UNHEALTHY_NODES));
 410             }
 411             if (constrain) {
 412                 pcmk__new_location(strategy_str, rsc, health, NULL, node);
 413             } else {
 414                 pcmk__rsc_trace(rsc, "%s is immune from health ban on %s",
 415                                 rsc->id, pcmk__node_name(node));
 416             }
 417         }
 418     }
 419 }
 420 
 421 /*!
 422  * \internal
 423  * \brief Check for a node in a resource's parent's allowed nodes
 424  *
 425  * \param[in] rsc   Resource whose parent should be checked
 426  * \param[in] node  Node to check for
 427  *
 428  * \return Equivalent of \p node from \p rsc's parent's allowed nodes if any,
 429  *         otherwise NULL
 430  */
 431 pcmk_node_t *
 432 pcmk__top_allowed_node(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 433 {
 434     GHashTable *allowed_nodes = NULL;
 435 
 436     if ((rsc == NULL) || (node == NULL)) {
 437         return NULL;
 438     }
 439 
 440     if (rsc->priv->parent == NULL) {
 441         allowed_nodes = rsc->priv->allowed_nodes;
 442     } else {
 443         allowed_nodes = rsc->priv->parent->priv->allowed_nodes;
 444     }
 445     return g_hash_table_lookup(allowed_nodes, node->priv->id);
 446 }

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