root/lib/pacemaker/pcmk_sched_utilization.c

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

DEFINITIONS

This source file includes following definitions.
  1. do_compare_capacity1
  2. do_compare_capacity2
  3. compare_capacity
  4. do_calculate_utilization
  5. calculate_utilization
  6. check_capacity
  7. have_enough_capacity
  8. native_add_unallocated_utilization
  9. add_unallocated_utilization
  10. sum_unallocated_utilization
  11. find_colocated_rscs
  12. process_utilization
  13. group_find_colocated_rscs
  14. group_add_unallocated_utilization

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

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