root/lib/pacemaker/pcmk_sched_utilization.c

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

DEFINITIONS

This source file includes following definitions.
  1. utilization_value
  2. compare_utilization_value
  3. pcmk__compare_node_capacities
  4. update_utilization_value
  5. pcmk__consume_node_capacity
  6. pcmk__release_node_capacity
  7. check_capacity
  8. have_enough_capacity
  9. sum_resource_utilization
  10. pcmk__ban_insufficient_capacity
  11. new_load_stopped_op
  12. pcmk__create_utilization_constraints
  13. pcmk__show_node_capacities

   1 /*
   2  * Copyright 2014-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 <pacemaker-internal.h>
  13 
  14 #include "libpacemaker_private.h"
  15 
  16 /*!
  17  * \internal
  18  * \brief Get integer utilization from a string
  19  *
  20  * \param[in] s  String representation of a node utilization value
  21  *
  22  * \return Integer equivalent of \p s
  23  * \todo It would make sense to restrict utilization values to nonnegative
  24  *       integers, but the documentation just says "integers" and we didn't
  25  *       restrict them initially, so for backward compatibility, allow any
  26  *       integer.
  27  */
  28 static int
  29 utilization_value(const char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31     int value = 0;
  32 
  33     if ((s != NULL) && (pcmk__scan_min_int(s, &value, INT_MIN) == EINVAL)) {
  34         pe_warn("Using 0 for utilization instead of invalid value '%s'", value);
  35         value = 0;
  36     }
  37     return value;
  38 }
  39 
  40 
  41 /*
  42  * Functions for comparing node capacities
  43  */
  44 
  45 struct compare_data {
  46     const pe_node_t *node1;
  47     const pe_node_t *node2;
  48     bool node2_only;
  49     int result;
  50 };
  51 
  52 /*!
  53  * \internal
  54  * \brief Compare a single utilization attribute for two nodes
  55  *
  56  * Compare one utilization attribute for two nodes, incrementing the result if
  57  * the first node has greater capacity, and decrementing it if the second node
  58  * has greater capacity.
  59  *
  60  * \param[in] key        Utilization attribute name to compare
  61  * \param[in] value      Utilization attribute value to compare
  62  * \param[in] user_data  Comparison data (as struct compare_data*)
  63  */
  64 static void
  65 compare_utilization_value(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  66 {
  67     int node1_capacity = 0;
  68     int node2_capacity = 0;
  69     struct compare_data *data = user_data;
  70     const char *node2_value = NULL;
  71 
  72     if (data->node2_only) {
  73         if (g_hash_table_lookup(data->node1->details->utilization, key)) {
  74             return; // We've already compared this attribute
  75         }
  76     } else {
  77         node1_capacity = utilization_value((const char *) value);
  78     }
  79 
  80     node2_value = g_hash_table_lookup(data->node2->details->utilization, key);
  81     node2_capacity = utilization_value(node2_value);
  82 
  83     if (node1_capacity > node2_capacity) {
  84         data->result--;
  85     } else if (node1_capacity < node2_capacity) {
  86         data->result++;
  87     }
  88 }
  89 
  90 /*!
  91  * \internal
  92  * \brief Compare utilization capacities of two nodes
  93  *
  94  * \param[in] node1  First node to compare
  95  * \param[in] node2  Second node to compare
  96  *
  97  * \return Negative integer if node1 has more free capacity,
  98  *         0 if the capacities are equal, or a positive integer
  99  *         if node2 has more free capacity
 100  */
 101 int
 102 pcmk__compare_node_capacities(const pe_node_t *node1, const pe_node_t *node2)
     /* [previous][next][first][last][top][bottom][index][help] */
 103 {
 104     struct compare_data data = {
 105         .node1      = node1,
 106         .node2      = node2,
 107         .node2_only = false,
 108         .result     = 0,
 109     };
 110 
 111     // Compare utilization values that node1 and maybe node2 have
 112     g_hash_table_foreach(node1->details->utilization, compare_utilization_value,
 113                          &data);
 114 
 115     // Compare utilization values that only node2 has
 116     data.node2_only = true;
 117     g_hash_table_foreach(node2->details->utilization, compare_utilization_value,
 118                          &data);
 119 
 120     return data.result;
 121 }
 122 
 123 
 124 /*
 125  * Functions for updating node capacities
 126  */
 127 
 128 struct calculate_data {
 129     GHashTable *current_utilization;
 130     bool plus;
 131 };
 132 
 133 /*!
 134  * \internal
 135  * \brief Update a single utilization attribute with a new value
 136  *
 137  * \param[in] key        Name of utilization attribute to update
 138  * \param[in] value      Value to add or substract
 139  * \param[in] user_data  Calculation data (as struct calculate_data *)
 140  */
 141 static void
 142 update_utilization_value(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     int result = 0;
 145     const char *current = NULL;
 146     struct calculate_data *data = user_data;
 147 
 148     current = g_hash_table_lookup(data->current_utilization, key);
 149     if (data->plus) {
 150         result = utilization_value(current) + utilization_value(value);
 151     } else if (current) {
 152         result = utilization_value(current) - utilization_value(value);
 153     }
 154     g_hash_table_replace(data->current_utilization,
 155                          strdup(key), pcmk__itoa(result));
 156 }
 157 
 158 /*!
 159  * \internal
 160  * \brief Subtract a resource's utilization from node capacity
 161  *
 162  * \param[in] current_utilization  Current node utilization attributes
 163  * \param[in] rsc                  Resource with utilization to subtract
 164  */
 165 void
 166 pcmk__consume_node_capacity(GHashTable *current_utilization, pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168     struct calculate_data data = {
 169         .current_utilization = current_utilization,
 170         .plus = false,
 171     };
 172 
 173     g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
 174 }
 175 
 176 /*!
 177  * \internal
 178  * \brief Add a resource's utilization to node capacity
 179  *
 180  * \param[in] current_utilization  Current node utilization attributes
 181  * \param[in] rsc                  Resource with utilization to add
 182  */
 183 void
 184 pcmk__release_node_capacity(GHashTable *current_utilization, pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     struct calculate_data data = {
 187         .current_utilization = current_utilization,
 188         .plus = true,
 189     };
 190 
 191     g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
 192 }
 193 
 194 
 195 /*
 196  * Functions for checking for sufficient node capacity
 197  */
 198 
 199 struct capacity_data {
 200     pe_node_t *node;
 201     const char *rsc_id;
 202     bool is_enough;
 203 };
 204 
 205 /*!
 206  * \internal
 207  * \brief Check whether a single utilization attribute has sufficient capacity
 208  *
 209  * \param[in] key        Name of utilization attribute to check
 210  * \param[in] value      Amount of utilization required
 211  * \param[in] user_data  Capacity data (as struct capacity_data *)
 212  */
 213 static void
 214 check_capacity(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216     int required = 0;
 217     int remaining = 0;
 218     const char *node_value_s = NULL;
 219     struct capacity_data *data = user_data;
 220 
 221     node_value_s = g_hash_table_lookup(data->node->details->utilization, key);
 222 
 223     required = utilization_value(value);
 224     remaining = utilization_value(node_value_s);
 225 
 226     if (required > remaining) {
 227         crm_debug("Remaining capacity for %s on %s (%d) is insufficient "
 228                   "for resource %s usage (%d)",
 229                   (const char *) key, data->node->details->uname, remaining,
 230                   data->rsc_id, required);
 231         data->is_enough = false;
 232     }
 233 }
 234 
 235 /*!
 236  * \internal
 237  * \brief Check whether a node has sufficient capacity for a resource
 238  *
 239  * \param[in] node         Node to check
 240  * \param[in] rsc_id       ID of resource to check (for debug logs only)
 241  * \param[in] utilization  Required utilization amounts
 242  *
 243  * \return true if node has sufficient capacity for resource, otherwise false
 244  */
 245 static bool
 246 have_enough_capacity(pe_node_t *node, const char *rsc_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 247                      GHashTable *utilization)
 248 {
 249     struct capacity_data data = {
 250         .node = node,
 251         .rsc_id = rsc_id,
 252         .is_enough = true,
 253     };
 254 
 255     g_hash_table_foreach(utilization, check_capacity, &data);
 256     return data.is_enough;
 257 }
 258 
 259 /*!
 260  * \internal
 261  * \brief Sum the utilization requirements of a list of resources
 262  *
 263  * \param[in] orig_rsc  Resource being allocated (for logging purposes)
 264  * \param[in] rscs      Resources whose utilization should be summed
 265  *
 266  * \return Newly allocated hash table with sum of all utilization values
 267  * \note It is the caller's responsibility to free the return value using
 268  *       g_hash_table_destroy().
 269  */
 270 static GHashTable *
 271 sum_resource_utilization(pe_resource_t *orig_rsc, GList *rscs)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273     GHashTable *utilization = pcmk__strkey_table(free, free);
 274 
 275     for (GList *iter = rscs; iter != NULL; iter = iter->next) {
 276         pe_resource_t *rsc = (pe_resource_t *) iter->data;
 277 
 278         rsc->cmds->add_utilization(rsc, orig_rsc, rscs, utilization);
 279     }
 280     return utilization;
 281 }
 282 
 283 /*!
 284  * \internal
 285  * \brief Ban resource from nodes with insufficient utilization capacity
 286  *
 287  * \param[in]     rsc       Resource to check
 288  * \param[in,out] prefer    Resource's preferred node (might be updated)
 289  * \param[in]     data_set  Cluster working set
 290  */
 291 void
 292 pcmk__ban_insufficient_capacity(pe_resource_t *rsc, pe_node_t **prefer,
     /* [previous][next][first][last][top][bottom][index][help] */
 293                                 pe_working_set_t *data_set)
 294 {
 295     bool any_capable = false;
 296     char *rscs_id = NULL;
 297     pe_node_t *node = NULL;
 298     pe_node_t *most_capable_node = NULL;
 299     GList *colocated_rscs = NULL;
 300     GHashTable *unallocated_utilization = NULL;
 301     GHashTableIter iter;
 302 
 303     CRM_CHECK((rsc != NULL) && (prefer != NULL) && (data_set != NULL), return);
 304 
 305     // The default placement strategy ignores utilization
 306     if (pcmk__str_eq(data_set->placement_strategy, "default",
 307                      pcmk__str_casei)) {
 308         return;
 309     }
 310 
 311     // Check whether any resources are colocated with this one
 312     colocated_rscs = rsc->cmds->colocated_resources(rsc, NULL, NULL);
 313     if (colocated_rscs == NULL) {
 314         return;
 315     }
 316 
 317     rscs_id = crm_strdup_printf("%s and its colocated resources", rsc->id);
 318 
 319     // If rsc isn't in the list, add it so we include its utilization
 320     if (g_list_find(colocated_rscs, rsc) == NULL) {
 321         colocated_rscs = g_list_append(colocated_rscs, rsc);
 322     }
 323 
 324     // Sum utilization of colocated resources that haven't been allocated yet
 325     unallocated_utilization = sum_resource_utilization(rsc, colocated_rscs);
 326 
 327     // Check whether any node has enough capacity for all the resources
 328     g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 329     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 330         if (!pcmk__node_available(node) || (node->weight < 0)) {
 331             continue;
 332         }
 333 
 334         if (have_enough_capacity(node, rscs_id, unallocated_utilization)) {
 335             any_capable = true;
 336         }
 337 
 338         // Keep track of node with most free capacity
 339         if ((most_capable_node == NULL)
 340             || (pcmk__compare_node_capacities(node, most_capable_node) < 0)) {
 341             most_capable_node = node;
 342         }
 343     }
 344 
 345     if (any_capable) {
 346         // If so, ban resource from any node with insufficient capacity
 347         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 348         while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 349             if ((node->weight >= 0) && pcmk__node_available(node)
 350                 && !have_enough_capacity(node, rscs_id,
 351                                          unallocated_utilization)) {
 352                 pe_rsc_debug(rsc, "%s does not have enough capacity for %s",
 353                              node->details->uname, rscs_id);
 354                 resource_location(rsc, node, -INFINITY, "__limit_utilization__",
 355                                   data_set);
 356             }
 357         }
 358 
 359     } else {
 360         // Otherwise, ban from nodes with insufficient capacity for rsc alone
 361         if (*prefer == NULL) {
 362             *prefer = most_capable_node;
 363         }
 364         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 365         while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 366             if ((node->weight >= 0) && pcmk__node_available(node)
 367                 && !have_enough_capacity(node, rsc->id, rsc->utilization)) {
 368                 pe_rsc_debug(rsc, "%s does not have enough capacity for %s",
 369                              node->details->uname, rsc->id);
 370                 resource_location(rsc, node, -INFINITY, "__limit_utilization__",
 371                                   data_set);
 372             }
 373         }
 374     }
 375 
 376     g_hash_table_destroy(unallocated_utilization);
 377     g_list_free(colocated_rscs);
 378     free(rscs_id);
 379 
 380     pe__show_node_weights(true, rsc, "Post-utilization",
 381                           rsc->allowed_nodes, data_set);
 382 }
 383 
 384 /*!
 385  * \internal
 386  * \brief Create a new load_stopped pseudo-op for a node
 387  *
 388  * \param[in] node      Node to create op for
 389  * \param[in] data_set  Cluster working set
 390  *
 391  * \return Newly created load_stopped op
 392  */
 393 static pe_action_t *
 394 new_load_stopped_op(const pe_node_t *node, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 395 {
 396     char *load_stopped_task = crm_strdup_printf(LOAD_STOPPED "_%s",
 397                                                 node->details->uname);
 398     pe_action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set);
 399 
 400     if (load_stopped->node == NULL) {
 401         load_stopped->node = pe__copy_node(node);
 402         pe__clear_action_flags(load_stopped, pe_action_optional);
 403     }
 404     free(load_stopped_task);
 405     return load_stopped;
 406 }
 407 
 408 /*!
 409  * \internal
 410  * \brief Create utilization-related internal constraints for a resource
 411  *
 412  * \param[in] rsc            Resource to create constraints for
 413  * \param[in] allowed_nodes  List of allowed next nodes for \p rsc
 414  */
 415 void
 416 pcmk__create_utilization_constraints(pe_resource_t *rsc, GList *allowed_nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 417 {
 418     GList *iter = NULL;
 419     pe_node_t *node = NULL;
 420     pe_action_t *load_stopped = NULL;
 421 
 422     pe_rsc_trace(rsc, "Creating utilization constraints for %s - strategy: %s",
 423                  rsc->id, rsc->cluster->placement_strategy);
 424 
 425     // "stop rsc then load_stopped" constraints for current nodes
 426     for (iter = rsc->running_on; iter != NULL; iter = iter->next) {
 427         node = (pe_node_t *) iter->data;
 428         load_stopped = new_load_stopped_op(node, rsc->cluster);
 429         pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, NULL, load_stopped,
 430                            pe_order_load, rsc->cluster);
 431     }
 432 
 433     // "load_stopped then start/migrate_to rsc" constraints for allowed nodes
 434     for (GList *iter = allowed_nodes; iter; iter = iter->next) {
 435         node = (pe_node_t *) iter->data;
 436         load_stopped = new_load_stopped_op(node, rsc->cluster);
 437         pcmk__new_ordering(NULL, NULL, load_stopped, rsc, start_key(rsc), NULL,
 438                            pe_order_load, rsc->cluster);
 439         pcmk__new_ordering(NULL, NULL, load_stopped,
 440                            rsc, pcmk__op_key(rsc->id, RSC_MIGRATE, 0), NULL,
 441                            pe_order_load, rsc->cluster);
 442     }
 443 }
 444 
 445 /*!
 446  * \internal
 447  * \brief Output node capacities if enabled
 448  *
 449  * \param[in] desc      Prefix for output
 450  * \param[in] data_set  Cluster working set
 451  */
 452 void
 453 pcmk__show_node_capacities(const char *desc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 454 {
 455     if (!pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
 456         return;
 457     }
 458     for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
 459         pe_node_t *node = (pe_node_t *) iter->data;
 460         pcmk__output_t *out = data_set->priv;
 461 
 462         out->message(out, "node-capacity", node, desc);
 463     }
 464 }

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