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. do_compare_capacity1
  3. do_compare_capacity2
  4. compare_capacity
  5. do_calculate_utilization
  6. calculate_utilization
  7. check_capacity
  8. have_enough_capacity
  9. native_add_unallocated_utilization
  10. add_unallocated_utilization
  11. sum_unallocated_utilization
  12. process_utilization
  13. group_add_unallocated_utilization

   1 /*
   2  * Copyright 2014-2021 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 static void group_add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc,
  17                                               GList *all_rscs);
  18 
  19 struct compare_data {
  20     const pe_node_t *node1;
  21     const pe_node_t *node2;
  22     int result;
  23 };
  24 
  25 static int
  26 utilization_value(const char *s)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     int value = 0;
  29 
  30     /* @TODO It would make sense to restrict utilization values to nonnegative
  31      * integers, but the documentation just says "integers" and we didn't
  32      * restrict them initially, so for backward compatibility, allow any
  33      * integer.
  34      */
  35     if (s != NULL) {
  36         pcmk__scan_min_int(s, &value, INT_MIN);
  37     }
  38     return value;
  39 }
  40 
  41 static void
  42 do_compare_capacity1(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  43 {
  44     int node1_capacity = 0;
  45     int node2_capacity = 0;
  46     struct compare_data *data = user_data;
  47 
  48     node1_capacity = utilization_value(value);
  49     node2_capacity = utilization_value(g_hash_table_lookup(data->node2->details->utilization, key));
  50 
  51     if (node1_capacity > node2_capacity) {
  52         data->result--;
  53     } else if (node1_capacity < node2_capacity) {
  54         data->result++;
  55     }
  56 }
  57 
  58 static void
  59 do_compare_capacity2(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61     int node1_capacity = 0;
  62     int node2_capacity = 0;
  63     struct compare_data *data = user_data;
  64 
  65     if (g_hash_table_lookup_extended(data->node1->details->utilization, key, NULL, NULL)) {
  66         return;
  67     }
  68 
  69     node1_capacity = 0;
  70     node2_capacity = utilization_value(value);
  71 
  72     if (node1_capacity > node2_capacity) {
  73         data->result--;
  74     } else if (node1_capacity < node2_capacity) {
  75         data->result++;
  76     }
  77 }
  78 
  79 /* rc < 0 if 'node1' has more capacity remaining
  80  * rc > 0 if 'node1' has less capacity remaining
  81  */
  82 int
  83 compare_capacity(const pe_node_t * node1, const pe_node_t * node2)
     /* [previous][next][first][last][top][bottom][index][help] */
  84 {
  85     struct compare_data data;
  86 
  87     data.node1 = node1;
  88     data.node2 = node2;
  89     data.result = 0;
  90 
  91     g_hash_table_foreach(node1->details->utilization, do_compare_capacity1, &data);
  92     g_hash_table_foreach(node2->details->utilization, do_compare_capacity2, &data);
  93 
  94     return data.result;
  95 }
  96 
  97 struct calculate_data {
  98     GHashTable *current_utilization;
  99     gboolean plus;
 100 };
 101 
 102 static void
 103 do_calculate_utilization(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105     const char *current = NULL;
 106     char *result = NULL;
 107     struct calculate_data *data = user_data;
 108 
 109     current = g_hash_table_lookup(data->current_utilization, key);
 110     if (data->plus) {
 111         result = pcmk__itoa(utilization_value(current) + utilization_value(value));
 112         g_hash_table_replace(data->current_utilization, strdup(key), result);
 113 
 114     } else if (current) {
 115         result = pcmk__itoa(utilization_value(current) - utilization_value(value));
 116         g_hash_table_replace(data->current_utilization, strdup(key), result);
 117     }
 118 }
 119 
 120 /* Specify 'plus' to FALSE when allocating
 121  * Otherwise to TRUE when deallocating
 122  */
 123 void
 124 calculate_utilization(GHashTable * current_utilization,
     /* [previous][next][first][last][top][bottom][index][help] */
 125                       GHashTable * utilization, gboolean plus)
 126 {
 127     struct calculate_data data;
 128 
 129     data.current_utilization = current_utilization;
 130     data.plus = plus;
 131 
 132     g_hash_table_foreach(utilization, do_calculate_utilization, &data);
 133 }
 134 
 135 
 136 struct capacity_data {
 137     pe_node_t *node;
 138     const char *rsc_id;
 139     gboolean is_enough;
 140 };
 141 
 142 static void
 143 check_capacity(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145     int required = 0;
 146     int remaining = 0;
 147     struct capacity_data *data = user_data;
 148 
 149     required = utilization_value(value);
 150     remaining = utilization_value(g_hash_table_lookup(data->node->details->utilization, key));
 151 
 152     if (required > remaining) {
 153         CRM_ASSERT(data->rsc_id);
 154         CRM_ASSERT(data->node);
 155 
 156         crm_debug("Node %s does not have enough %s for %s: required=%d remaining=%d",
 157                   data->node->details->uname, (char *)key, data->rsc_id, required, remaining);
 158         data->is_enough = FALSE;
 159     }
 160 }
 161 
 162 static gboolean
 163 have_enough_capacity(pe_node_t * node, const char * rsc_id, GHashTable * utilization)
     /* [previous][next][first][last][top][bottom][index][help] */
 164 {
 165     struct capacity_data data;
 166 
 167     data.node = node;
 168     data.rsc_id = rsc_id;
 169     data.is_enough = TRUE;
 170 
 171     g_hash_table_foreach(utilization, check_capacity, &data);
 172 
 173     return data.is_enough;
 174 }
 175 
 176 
 177 static void
 178 native_add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
 181         return;
 182     }
 183 
 184     calculate_utilization(all_utilization, rsc->utilization, TRUE);
 185 }
 186 
 187 static void
 188 add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 189                     GList *all_rscs, pe_resource_t * orig_rsc)
 190 {
 191     if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
 192         return;
 193     }
 194 
 195     if (rsc->variant == pe_native) {
 196         pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
 197                      orig_rsc->id, rsc->id);
 198         native_add_unallocated_utilization(all_utilization, rsc);
 199 
 200     } else if (rsc->variant == pe_group) {
 201         pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
 202                      orig_rsc->id, rsc->id);
 203         group_add_unallocated_utilization(all_utilization, rsc, all_rscs);
 204 
 205     } else if (pe_rsc_is_clone(rsc)) {
 206         GList *gIter1 = NULL;
 207         gboolean existing = FALSE;
 208 
 209         /* Check if there's any child already existing in the list */
 210         gIter1 = rsc->children;
 211         for (; gIter1 != NULL; gIter1 = gIter1->next) {
 212             pe_resource_t *child = (pe_resource_t *) gIter1->data;
 213             GList *gIter2 = NULL;
 214 
 215             if (g_list_find(all_rscs, child)) {
 216                 existing = TRUE;
 217 
 218             } else {
 219                 /* Check if there's any child of another cloned group already existing in the list */
 220                 gIter2 = child->children;
 221                 for (; gIter2 != NULL; gIter2 = gIter2->next) {
 222                     pe_resource_t *grandchild = (pe_resource_t *) gIter2->data;
 223 
 224                     if (g_list_find(all_rscs, grandchild)) {
 225                         pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
 226                                      orig_rsc->id, child->id);
 227                         add_unallocated_utilization(all_utilization, child, all_rscs, orig_rsc);
 228                         existing = TRUE;
 229                         break;
 230                     }
 231                 }
 232             }
 233         }
 234 
 235         // rsc->children is always non-NULL but this makes static analysis happy
 236         if (!existing && (rsc->children != NULL)) {
 237             pe_resource_t *first_child = (pe_resource_t *) rsc->children->data;
 238 
 239             pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
 240                          orig_rsc->id, ID(first_child->xml));
 241             add_unallocated_utilization(all_utilization, first_child, all_rscs, orig_rsc);
 242         }
 243     }
 244 }
 245 
 246 static GHashTable *
 247 sum_unallocated_utilization(pe_resource_t * rsc, GList *colocated_rscs)
     /* [previous][next][first][last][top][bottom][index][help] */
 248 {
 249     GList *gIter = NULL;
 250     GList *all_rscs = NULL;
 251     GHashTable *all_utilization = pcmk__strkey_table(free, free);
 252 
 253     all_rscs = g_list_copy(colocated_rscs);
 254     if (g_list_find(all_rscs, rsc) == FALSE) {
 255         all_rscs = g_list_append(all_rscs, rsc);
 256     }
 257 
 258     for (gIter = all_rscs; gIter != NULL; gIter = gIter->next) {
 259         pe_resource_t *listed_rsc = (pe_resource_t *) gIter->data;
 260 
 261         if (!pcmk_is_set(listed_rsc->flags, pe_rsc_provisional)) {
 262             continue;
 263         }
 264 
 265         pe_rsc_trace(rsc, "%s: Processing unallocated colocated %s", rsc->id, listed_rsc->id);
 266         add_unallocated_utilization(all_utilization, listed_rsc, all_rscs, rsc);
 267     }
 268 
 269     g_list_free(all_rscs);
 270 
 271     return all_utilization;
 272 }
 273 
 274 void
 275 process_utilization(pe_resource_t * rsc, pe_node_t ** prefer, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 276 {
 277     CRM_CHECK(rsc && prefer && data_set, return);
 278     if (!pcmk__str_eq(data_set->placement_strategy, "default", pcmk__str_casei)) {
 279         GHashTableIter iter;
 280         GList *colocated_rscs = NULL;
 281         gboolean any_capable = FALSE;
 282         pe_node_t *node = NULL;
 283 
 284         colocated_rscs = rsc->cmds->colocated_resources(rsc, NULL, NULL);
 285         if (colocated_rscs) {
 286             GHashTable *unallocated_utilization = NULL;
 287             char *rscs_id = crm_strdup_printf("%s and its colocated resources",
 288                                               rsc->id);
 289             pe_node_t *most_capable_node = NULL;
 290 
 291             unallocated_utilization = sum_unallocated_utilization(rsc, colocated_rscs);
 292 
 293             g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 294             while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 295                 if (can_run_resources(node) == FALSE || node->weight < 0) {
 296                     continue;
 297                 }
 298 
 299                 if (have_enough_capacity(node, rscs_id, unallocated_utilization)) {
 300                     any_capable = TRUE;
 301                 }
 302 
 303                 if (most_capable_node == NULL ||
 304                     compare_capacity(node, most_capable_node) < 0) {
 305                     /* < 0 means 'node' is more capable */
 306                     most_capable_node = node;
 307                 }
 308             }
 309 
 310             if (any_capable) {
 311                 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 312                 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 313                     if (can_run_resources(node) == FALSE || node->weight < 0) {
 314                         continue;
 315                     }
 316 
 317                     if (have_enough_capacity(node, rscs_id, unallocated_utilization) == FALSE) {
 318                         pe_rsc_debug(rsc,
 319                                      "Resource %s and its colocated resources"
 320                                      " cannot be allocated to node %s: not enough capacity",
 321                                      rsc->id, node->details->uname);
 322                         resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set);
 323                     }
 324                 }
 325 
 326             } else if (*prefer == NULL) {
 327                 *prefer = most_capable_node;
 328             }
 329 
 330             if (unallocated_utilization) {
 331                 g_hash_table_destroy(unallocated_utilization);
 332             }
 333 
 334             g_list_free(colocated_rscs);
 335             free(rscs_id);
 336         }
 337 
 338         if (any_capable == FALSE) {
 339             g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 340             while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 341                 if (can_run_resources(node) == FALSE || node->weight < 0) {
 342                     continue;
 343                 }
 344 
 345                 if (have_enough_capacity(node, rsc->id, rsc->utilization) == FALSE) {
 346                     pe_rsc_debug(rsc,
 347                                  "Resource %s cannot be allocated to node %s:"
 348                                  " not enough capacity",
 349                                  rsc->id, node->details->uname);
 350                     resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set);
 351                 }
 352             }
 353         }
 354         pe__show_node_weights(true, rsc, "Post-utilization", rsc->allowed_nodes, data_set);
 355     }
 356 }
 357 
 358 #define VARIANT_GROUP 1
 359 #include <lib/pengine/variant.h>
 360 
 361 static void
 362 group_add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 363                                   GList *all_rscs)
 364 {
 365     group_variant_data_t *group_data = NULL;
 366 
 367     get_group_variant_data(group_data, rsc);
 368     if (group_data->colocated || pe_rsc_is_clone(rsc->parent)) {
 369         GList *gIter = rsc->children;
 370 
 371         for (; gIter != NULL; gIter = gIter->next) {
 372             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 373 
 374             if (pcmk_is_set(child_rsc->flags, pe_rsc_provisional) &&
 375                 g_list_find(all_rscs, child_rsc) == FALSE) {
 376                 native_add_unallocated_utilization(all_utilization, child_rsc);
 377             }
 378         }
 379 
 380     } else {
 381         if (group_data->first_child &&
 382             pcmk_is_set(group_data->first_child->flags, pe_rsc_provisional) &&
 383             g_list_find(all_rscs, group_data->first_child) == FALSE) {
 384             native_add_unallocated_utilization(all_utilization, group_data->first_child);
 385         }
 386     }
 387 }
 388 
 389 

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