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-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  * \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  Sort data (as struct node_weight_s *)
 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     pe_node_t *active = (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] nodes        Node list to sort
 231  * \param[in] active_node  If not NULL, node currently running resource
 232  * \param[in] data_set     Cluster working set
 233  *
 234  * \return New head of sorted list
 235  */
 236 GList *
 237 pcmk__sort_nodes(GList *nodes, pe_node_t *active_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 238 {
 239     return g_list_sort_with_data(nodes, compare_nodes, active_node);
 240 }
 241 
 242 /*!
 243  * \internal
 244  * \brief Check whether any node is available to run resources
 245  *
 246  * \param[in] nodes  Nodes to check
 247  *
 248  * \return true if any node in \p nodes is available to run resources,
 249  *         otherwise false
 250  */
 251 bool
 252 pcmk__any_node_available(GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     GHashTableIter iter;
 255     pe_node_t *node = NULL;
 256 
 257     if (nodes == NULL) {
 258         return false;
 259     }
 260     g_hash_table_iter_init(&iter, nodes);
 261     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 262         if (pcmk__node_available(node, true, false)) {
 263             return true;
 264         }
 265     }
 266     return false;
 267 }
 268 
 269 /*!
 270  * \internal
 271  * \brief Apply node health values for all nodes in cluster
 272  *
 273  * \param[in] data_set  Cluster working set
 274  */
 275 void
 276 pcmk__apply_node_health(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 277 {
 278     int base_health = 0;
 279     enum pcmk__health_strategy strategy;
 280     const char *strategy_str = pe_pref(data_set->config_hash,
 281                                        PCMK__OPT_NODE_HEALTH_STRATEGY);
 282 
 283     strategy = pcmk__parse_health_strategy(strategy_str);
 284     if (strategy == pcmk__health_strategy_none) {
 285         return;
 286     }
 287     crm_info("Applying node health strategy '%s'", strategy_str);
 288 
 289     // The progressive strategy can use a base health score
 290     if (strategy == pcmk__health_strategy_progressive) {
 291         base_health = pe__health_score(PCMK__OPT_NODE_HEALTH_BASE, data_set);
 292     }
 293 
 294     for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
 295         pe_node_t *node = (pe_node_t *) iter->data;
 296         int health = pe__sum_node_health_scores(node, base_health);
 297 
 298         // An overall health score of 0 has no effect
 299         if (health == 0) {
 300             continue;
 301         }
 302         crm_info("Overall system health of %s is %d",
 303                  pe__node_name(node), health);
 304 
 305         // Use node health as a location score for each resource on the node
 306         for (GList *r = data_set->resources; r != NULL; r = r->next) {
 307             pe_resource_t *rsc = (pe_resource_t *) r->data;
 308 
 309             bool constrain = true;
 310 
 311             if (health < 0) {
 312                 /* Negative health scores do not apply to resources with
 313                  * allow-unhealthy-nodes=true.
 314                  */
 315                 constrain = !crm_is_true(g_hash_table_lookup(rsc->meta,
 316                                          PCMK__META_ALLOW_UNHEALTHY_NODES));
 317             }
 318             if (constrain) {
 319                 pcmk__new_location(strategy_str, rsc, health, NULL, node,
 320                                    data_set);
 321             } else {
 322                 pe_rsc_trace(rsc, "%s is immune from health ban on %s",
 323                              rsc->id, pe__node_name(node));
 324             }
 325         }
 326     }
 327 }
 328 
 329 /*!
 330  * \internal
 331  * \brief Check for a node in a resource's parent's allowed nodes
 332  *
 333  * \param[in] rsc   Resource whose parent should be checked
 334  * \param[in] node  Node to check for
 335  *
 336  * \return Equivalent of \p node from \p rsc's parent's allowed nodes if any,
 337  *         otherwise NULL
 338  */
 339 pe_node_t *
 340 pcmk__top_allowed_node(const pe_resource_t *rsc, const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 341 {
 342     GHashTable *allowed_nodes = NULL;
 343 
 344     if ((rsc == NULL) || (node == NULL)) {
 345         return NULL;
 346     } else if (rsc->parent == NULL) {
 347         allowed_nodes = rsc->allowed_nodes;
 348     } else {
 349         allowed_nodes = rsc->parent->allowed_nodes;
 350     }
 351     return pe_hash_table_lookup(allowed_nodes, node->details->id);
 352 }

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