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
  8. pcmk__top_allowed_node

   1 /*
   2  * Copyright 2004-2023 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  * \param[in] consider_score  If true, consider a negative score unavailable
  24  * \param[in] consider_guest  If true, consider a guest node unavailable whose
  25  *                            resource will not be active
  26  *
  27  * \return true if node is online and not shutting down, unclean, or in standby
  28  *         or maintenance mode, otherwise false
  29  */
  30 bool
  31 pcmk__node_available(const pe_node_t *node, bool consider_score,
     /* [previous][next][first][last][top][bottom][index][help] */
  32                      bool consider_guest)
  33 {
  34     if ((node == NULL) || (node->details == NULL) || !node->details->online
  35             || node->details->shutdown || node->details->unclean
  36             || node->details->standby || node->details->maintenance) {
  37         return false;
  38     }
  39 
  40     if (consider_score && (node->weight < 0)) {
  41         return false;
  42     }
  43 
  44     // @TODO Go through all callers to see which should set consider_guest
  45     if (consider_guest && pe__is_guest_node(node)) {
  46         pe_resource_t *guest = node->details->remote_rsc->container;
  47 
  48         if (guest->fns->location(guest, NULL, FALSE) == NULL) {
  49             return false;
  50         }
  51     }
  52 
  53     return true;
  54 }
  55 
  56 /*!
  57  * \internal
  58  * \brief Copy a hash table of node objects
  59  *
  60  * \param[in] nodes  Hash table to copy
  61  *
  62  * \return New copy of nodes (or NULL if nodes is NULL)
  63  */
  64 GHashTable *
  65 pcmk__copy_node_table(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
  66 {
  67     GHashTable *new_table = NULL;
  68     GHashTableIter iter;
  69     pe_node_t *node = NULL;
  70 
  71     if (nodes == NULL) {
  72         return NULL;
  73     }
  74     new_table = pcmk__strkey_table(NULL, free);
  75     g_hash_table_iter_init(&iter, nodes);
  76     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
  77         pe_node_t *new_node = pe__copy_node(node);
  78 
  79         g_hash_table_insert(new_table, (gpointer) new_node->details->id,
  80                             new_node);
  81     }
  82     return new_table;
  83 }
  84 
  85 /*!
  86  * \internal
  87  * \brief Copy a list of node objects
  88  *
  89  * \param[in] list   List to copy
  90  * \param[in] reset  Set copies' scores to 0
  91  *
  92  * \return New list of shallow copies of nodes in original list
  93  */
  94 GList *
  95 pcmk__copy_node_list(const GList *list, bool reset)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97     GList *result = NULL;
  98 
  99     for (const GList *gIter = list; gIter != NULL; gIter = gIter->next) {
 100         pe_node_t *new_node = NULL;
 101         pe_node_t *this_node = (pe_node_t *) gIter->data;
 102 
 103         new_node = pe__copy_node(this_node);
 104         if (reset) {
 105             new_node->weight = 0;
 106         }
 107         result = g_list_prepend(result, new_node);
 108     }
 109     return result;
 110 }
 111 
 112 /*!
 113  * \internal
 114  * \brief Compare two nodes for allocation desirability
 115  *
 116  * Given two nodes, check which one is more preferred by allocation criteria
 117  * such as node weight and utilization.
 118  *
 119  * \param[in] a     First node to compare
 120  * \param[in] b     Second node to compare
 121  * \param[in] data  Node that resource being assigned is active on, if any
 122  *
 123  * \return -1 if \p a is preferred, +1 if \p b is preferred, or 0 if they are
 124  *         equally preferred
 125  */
 126 static gint
 127 compare_nodes(gconstpointer a, gconstpointer b, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 128 {
 129     const pe_node_t *node1 = (const pe_node_t *) a;
 130     const pe_node_t *node2 = (const pe_node_t *) b;
 131     const pe_node_t *active = (const pe_node_t *) data;
 132 
 133     int node1_weight = 0;
 134     int node2_weight = 0;
 135 
 136     int result = 0;
 137 
 138     if (a == NULL) {
 139         return 1;
 140     }
 141     if (b == NULL) {
 142         return -1;
 143     }
 144 
 145     // Compare node weights
 146 
 147     node1_weight = pcmk__node_available(node1, false, false)? node1->weight : -INFINITY;
 148     node2_weight = pcmk__node_available(node2, false, false)? node2->weight : -INFINITY;
 149 
 150     if (node1_weight > node2_weight) {
 151         crm_trace("%s (%d) > %s (%d) : weight",
 152                   pe__node_name(node1), node1_weight, pe__node_name(node2),
 153                   node2_weight);
 154         return -1;
 155     }
 156 
 157     if (node1_weight < node2_weight) {
 158         crm_trace("%s (%d) < %s (%d) : weight",
 159                   pe__node_name(node1), node1_weight, pe__node_name(node2),
 160                   node2_weight);
 161         return 1;
 162     }
 163 
 164     crm_trace("%s (%d) == %s (%d) : weight",
 165               pe__node_name(node1), node1_weight, pe__node_name(node2),
 166               node2_weight);
 167 
 168     // If appropriate, compare node utilization
 169 
 170     if (pcmk__str_eq(node1->details->data_set->placement_strategy, "minimal",
 171                      pcmk__str_casei)) {
 172         goto equal;
 173     }
 174 
 175     if (pcmk__str_eq(node1->details->data_set->placement_strategy, "balanced",
 176                      pcmk__str_casei)) {
 177         result = pcmk__compare_node_capacities(node1, node2);
 178         if (result < 0) {
 179             crm_trace("%s > %s : capacity (%d)",
 180                       pe__node_name(node1), pe__node_name(node2), result);
 181             return -1;
 182         } else if (result > 0) {
 183             crm_trace("%s < %s : capacity (%d)",
 184                       pe__node_name(node1), pe__node_name(node2), result);
 185             return 1;
 186         }
 187     }
 188 
 189     // Compare number of allocated resources
 190 
 191     if (node1->details->num_resources < node2->details->num_resources) {
 192         crm_trace("%s (%d) > %s (%d) : resources",
 193                   pe__node_name(node1), node1->details->num_resources,
 194                   pe__node_name(node2), node2->details->num_resources);
 195         return -1;
 196 
 197     } else if (node1->details->num_resources > node2->details->num_resources) {
 198         crm_trace("%s (%d) < %s (%d) : resources",
 199                   pe__node_name(node1), node1->details->num_resources,
 200                   pe__node_name(node2), node2->details->num_resources);
 201         return 1;
 202     }
 203 
 204     // Check whether one node is already running desired resource
 205 
 206     if (active != NULL) {
 207         if (active->details == node1->details) {
 208             crm_trace("%s (%d) > %s (%d) : active",
 209                       pe__node_name(node1), node1->details->num_resources,
 210                       pe__node_name(node2), node2->details->num_resources);
 211             return -1;
 212         } else if (active->details == node2->details) {
 213             crm_trace("%s (%d) < %s (%d) : active",
 214                       pe__node_name(node1), node1->details->num_resources,
 215                       pe__node_name(node2), node2->details->num_resources);
 216             return 1;
 217         }
 218     }
 219 
 220     // If all else is equal, prefer node with lowest-sorting name
 221 equal:
 222     crm_trace("%s = %s", pe__node_name(node1), pe__node_name(node2));
 223     return strcmp(node1->details->uname, node2->details->uname);
 224 }
 225 
 226 /*!
 227  * \internal
 228  * \brief Sort a list of nodes by allocation desirability
 229  *
 230  * \param[in,out] nodes        Node list to sort
 231  * \param[in]     active_node  Node where resource being assigned is active
 232  *
 233  * \return New head of sorted list
 234  */
 235 GList *
 236 pcmk__sort_nodes(GList *nodes, pe_node_t *active_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 237 {
 238     return g_list_sort_with_data(nodes, compare_nodes, active_node);
 239 }
 240 
 241 /*!
 242  * \internal
 243  * \brief Check whether any node is available to run resources
 244  *
 245  * \param[in] nodes  Nodes to check
 246  *
 247  * \return true if any node in \p nodes is available to run resources,
 248  *         otherwise false
 249  */
 250 bool
 251 pcmk__any_node_available(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 252 {
 253     GHashTableIter iter;
 254     const pe_node_t *node = NULL;
 255 
 256     if (nodes == NULL) {
 257         return false;
 258     }
 259     g_hash_table_iter_init(&iter, nodes);
 260     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 261         if (pcmk__node_available(node, true, false)) {
 262             return true;
 263         }
 264     }
 265     return false;
 266 }
 267 
 268 /*!
 269  * \internal
 270  * \brief Apply node health values for all nodes in cluster
 271  *
 272  * \param[in,out] data_set  Cluster working set
 273  */
 274 void
 275 pcmk__apply_node_health(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277     int base_health = 0;
 278     enum pcmk__health_strategy strategy;
 279     const char *strategy_str = pe_pref(data_set->config_hash,
 280                                        PCMK__OPT_NODE_HEALTH_STRATEGY);
 281 
 282     strategy = pcmk__parse_health_strategy(strategy_str);
 283     if (strategy == pcmk__health_strategy_none) {
 284         return;
 285     }
 286     crm_info("Applying node health strategy '%s'", strategy_str);
 287 
 288     // The progressive strategy can use a base health score
 289     if (strategy == pcmk__health_strategy_progressive) {
 290         base_health = pe__health_score(PCMK__OPT_NODE_HEALTH_BASE, data_set);
 291     }
 292 
 293     for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
 294         pe_node_t *node = (pe_node_t *) iter->data;
 295         int health = pe__sum_node_health_scores(node, base_health);
 296 
 297         // An overall health score of 0 has no effect
 298         if (health == 0) {
 299             continue;
 300         }
 301         crm_info("Overall system health of %s is %d",
 302                  pe__node_name(node), health);
 303 
 304         // Use node health as a location score for each resource on the node
 305         for (GList *r = data_set->resources; r != NULL; r = r->next) {
 306             pe_resource_t *rsc = (pe_resource_t *) r->data;
 307 
 308             bool constrain = true;
 309 
 310             if (health < 0) {
 311                 /* Negative health scores do not apply to resources with
 312                  * allow-unhealthy-nodes=true.
 313                  */
 314                 constrain = !crm_is_true(g_hash_table_lookup(rsc->meta,
 315                                          PCMK__META_ALLOW_UNHEALTHY_NODES));
 316             }
 317             if (constrain) {
 318                 pcmk__new_location(strategy_str, rsc, health, NULL, node,
 319                                    data_set);
 320             } else {
 321                 pe_rsc_trace(rsc, "%s is immune from health ban on %s",
 322                              rsc->id, pe__node_name(node));
 323             }
 324         }
 325     }
 326 }
 327 
 328 /*!
 329  * \internal
 330  * \brief Check for a node in a resource's parent's allowed nodes
 331  *
 332  * \param[in] rsc   Resource whose parent should be checked
 333  * \param[in] node  Node to check for
 334  *
 335  * \return Equivalent of \p node from \p rsc's parent's allowed nodes if any,
 336  *         otherwise NULL
 337  */
 338 pe_node_t *
 339 pcmk__top_allowed_node(const pe_resource_t *rsc, const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341     GHashTable *allowed_nodes = NULL;
 342 
 343     if ((rsc == NULL) || (node == NULL)) {
 344         return NULL;
 345     } else if (rsc->parent == NULL) {
 346         allowed_nodes = rsc->allowed_nodes;
 347     } else {
 348         allowed_nodes = rsc->parent->allowed_nodes;
 349     }
 350     return pe_hash_table_lookup(allowed_nodes, node->details->id);
 351 }

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