root/lib/pacemaker/pcmk_sched_resource.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__rsc_agent_changed
  2. add_rsc_if_matching
  3. pcmk__rscs_matching_id
  4. set_allocation_methods_for_rsc
  5. pcmk__set_allocation_methods
  6. pcmk__colocated_resources
  7. pcmk__noop_add_graph_meta
  8. pcmk__output_resource_actions
  9. pcmk__finalize_assignment
  10. pcmk__assign_resource
  11. pcmk__unassign_resource
  12. pcmk__threshold_reached
  13. convert_const_pointer
  14. get_node_weight
  15. cmp_resources
  16. pcmk__sort_resources

   1 /*
   2  * Copyright 2014-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 
  12 #include <stdlib.h>
  13 #include <string.h>
  14 #include <crm/msg_xml.h>
  15 #include <pacemaker-internal.h>
  16 
  17 #include "libpacemaker_private.h"
  18 
  19 // Resource allocation methods that vary by resource variant
  20 static resource_alloc_functions_t allocation_methods[] = {
  21     {
  22         pcmk__primitive_assign,
  23         pcmk__primitive_create_actions,
  24         pcmk__probe_rsc_on_node,
  25         pcmk__primitive_internal_constraints,
  26         pcmk__primitive_apply_coloc_score,
  27         pcmk__colocated_resources,
  28         pcmk__with_primitive_colocations,
  29         pcmk__primitive_with_colocations,
  30         pcmk__add_colocated_node_scores,
  31         pcmk__apply_location,
  32         pcmk__primitive_action_flags,
  33         pcmk__update_ordered_actions,
  34         pcmk__output_resource_actions,
  35         pcmk__add_rsc_actions_to_graph,
  36         pcmk__primitive_add_graph_meta,
  37         pcmk__primitive_add_utilization,
  38         pcmk__primitive_shutdown_lock,
  39     },
  40     {
  41         pcmk__group_assign,
  42         pcmk__group_create_actions,
  43         pcmk__probe_rsc_on_node,
  44         pcmk__group_internal_constraints,
  45         pcmk__group_apply_coloc_score,
  46         pcmk__group_colocated_resources,
  47         pcmk__with_group_colocations,
  48         pcmk__group_with_colocations,
  49         pcmk__group_add_colocated_node_scores,
  50         pcmk__group_apply_location,
  51         pcmk__group_action_flags,
  52         pcmk__group_update_ordered_actions,
  53         pcmk__output_resource_actions,
  54         pcmk__add_rsc_actions_to_graph,
  55         pcmk__noop_add_graph_meta,
  56         pcmk__group_add_utilization,
  57         pcmk__group_shutdown_lock,
  58     },
  59     {
  60         pcmk__clone_assign,
  61         clone_create_actions,
  62         clone_create_probe,
  63         clone_internal_constraints,
  64         pcmk__clone_apply_coloc_score,
  65         pcmk__colocated_resources,
  66         pcmk__with_clone_colocations,
  67         pcmk__clone_with_colocations,
  68         pcmk__add_colocated_node_scores,
  69         clone_rsc_location,
  70         clone_action_flags,
  71         pcmk__instance_update_ordered_actions,
  72         pcmk__output_resource_actions,
  73         clone_expand,
  74         clone_append_meta,
  75         pcmk__clone_add_utilization,
  76         pcmk__clone_shutdown_lock,
  77     },
  78     {
  79         pcmk__bundle_allocate,
  80         pcmk__bundle_create_actions,
  81         pcmk__bundle_create_probe,
  82         pcmk__bundle_internal_constraints,
  83         pcmk__bundle_apply_coloc_score,
  84         pcmk__colocated_resources,
  85         pcmk__with_bundle_colocations,
  86         pcmk__bundle_with_colocations,
  87         pcmk__add_colocated_node_scores,
  88         pcmk__bundle_rsc_location,
  89         pcmk__bundle_action_flags,
  90         pcmk__instance_update_ordered_actions,
  91         pcmk__output_bundle_actions,
  92         pcmk__bundle_expand,
  93         pcmk__noop_add_graph_meta,
  94         pcmk__bundle_add_utilization,
  95         pcmk__bundle_shutdown_lock,
  96     }
  97 };
  98 
  99 /*!
 100  * \internal
 101  * \brief Check whether a resource's agent standard, provider, or type changed
 102  *
 103  * \param[in,out] rsc             Resource to check
 104  * \param[in,out] node            Node needing unfencing if agent changed
 105  * \param[in]     rsc_entry       XML with previously known agent information
 106  * \param[in]     active_on_node  Whether \p rsc is active on \p node
 107  *
 108  * \return true if agent for \p rsc changed, otherwise false
 109  */
 110 bool
 111 pcmk__rsc_agent_changed(pe_resource_t *rsc, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 112                         const xmlNode *rsc_entry, bool active_on_node)
 113 {
 114     bool changed = false;
 115     const char *attr_list[] = {
 116         XML_ATTR_TYPE,
 117         XML_AGENT_ATTR_CLASS,
 118         XML_AGENT_ATTR_PROVIDER
 119     };
 120 
 121     for (int i = 0; i < PCMK__NELEM(attr_list); i++) {
 122         const char *value = crm_element_value(rsc->xml, attr_list[i]);
 123         const char *old_value = crm_element_value(rsc_entry, attr_list[i]);
 124 
 125         if (!pcmk__str_eq(value, old_value, pcmk__str_none)) {
 126             changed = true;
 127             trigger_unfencing(rsc, node, "Device definition changed", NULL,
 128                               rsc->cluster);
 129             if (active_on_node) {
 130                 crm_notice("Forcing restart of %s on %s "
 131                            "because %s changed from '%s' to '%s'",
 132                            rsc->id, pe__node_name(node), attr_list[i],
 133                            pcmk__s(old_value, ""), pcmk__s(value, ""));
 134             }
 135         }
 136     }
 137     if (changed && active_on_node) {
 138         // Make sure the resource is restarted
 139         custom_action(rsc, stop_key(rsc), CRMD_ACTION_STOP, node, FALSE, TRUE,
 140                       rsc->cluster);
 141         pe__set_resource_flags(rsc, pe_rsc_start_pending);
 142     }
 143     return changed;
 144 }
 145 
 146 /*!
 147  * \internal
 148  * \brief Add resource (and any matching children) to list if it matches ID
 149  *
 150  * \param[in] result  List to add resource to
 151  * \param[in] rsc     Resource to check
 152  * \param[in] id      ID to match
 153  *
 154  * \return (Possibly new) head of list
 155  */
 156 static GList *
 157 add_rsc_if_matching(GList *result, pe_resource_t *rsc, const char *id)
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159     if ((strcmp(rsc->id, id) == 0)
 160         || ((rsc->clone_name != NULL) && (strcmp(rsc->clone_name, id) == 0))) {
 161         result = g_list_prepend(result, rsc);
 162     }
 163     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 164         pe_resource_t *child = (pe_resource_t *) iter->data;
 165 
 166         result = add_rsc_if_matching(result, child, id);
 167     }
 168     return result;
 169 }
 170 
 171 /*!
 172  * \internal
 173  * \brief Find all resources matching a given ID by either ID or clone name
 174  *
 175  * \param[in] id        Resource ID to check
 176  * \param[in] data_set  Cluster working set
 177  *
 178  * \return List of all resources that match \p id
 179  * \note The caller is responsible for freeing the return value with
 180  *       g_list_free().
 181  */
 182 GList *
 183 pcmk__rscs_matching_id(const char *id, const pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185     GList *result = NULL;
 186 
 187     CRM_CHECK((id != NULL) && (data_set != NULL), return NULL);
 188     for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
 189         result = add_rsc_if_matching(result, (pe_resource_t *) iter->data, id);
 190     }
 191     return result;
 192 }
 193 
 194 /*!
 195  * \internal
 196  * \brief Set the variant-appropriate allocation methods for a resource
 197  *
 198  * \param[in,out] rsc      Resource to set allocation methods for
 199  * \param[in]     ignored  Here so function can be used with g_list_foreach()
 200  */
 201 static void
 202 set_allocation_methods_for_rsc(pe_resource_t *rsc, void *ignored)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204     rsc->cmds = &allocation_methods[rsc->variant];
 205     g_list_foreach(rsc->children, (GFunc) set_allocation_methods_for_rsc, NULL);
 206 }
 207 
 208 /*!
 209  * \internal
 210  * \brief Set the variant-appropriate allocation methods for all resources
 211  *
 212  * \param[in,out] data_set  Cluster working set
 213  */
 214 void
 215 pcmk__set_allocation_methods(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 216 {
 217     g_list_foreach(data_set->resources, (GFunc) set_allocation_methods_for_rsc,
 218                    NULL);
 219 }
 220 
 221 // Shared implementation of resource_alloc_functions_t:colocated_resources()
 222 GList *
 223 pcmk__colocated_resources(const pe_resource_t *rsc, const pe_resource_t *orig_rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 224                           GList *colocated_rscs)
 225 {
 226     const GList *iter = NULL;
 227     GList *colocations = NULL;
 228 
 229     if (orig_rsc == NULL) {
 230         orig_rsc = rsc;
 231     }
 232 
 233     if ((rsc == NULL) || (g_list_find(colocated_rscs, rsc) != NULL)) {
 234         return colocated_rscs;
 235     }
 236 
 237     pe_rsc_trace(orig_rsc, "%s is in colocation chain with %s",
 238                  rsc->id, orig_rsc->id);
 239     colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
 240 
 241     // Follow colocations where this resource is the dependent resource
 242     colocations = pcmk__this_with_colocations(rsc);
 243     for (iter = colocations; iter != NULL; iter = iter->next) {
 244         const pcmk__colocation_t *constraint = iter->data;
 245         const pe_resource_t *primary = constraint->primary;
 246 
 247         if (primary == orig_rsc) {
 248             continue; // Break colocation loop
 249         }
 250 
 251         if ((constraint->score == INFINITY) &&
 252             (pcmk__colocation_affects(rsc, primary, constraint,
 253                                       true) == pcmk__coloc_affects_location)) {
 254 
 255             colocated_rscs = primary->cmds->colocated_resources(primary,
 256                                                                 orig_rsc,
 257                                                                 colocated_rscs);
 258         }
 259     }
 260     g_list_free(colocations);
 261 
 262     // Follow colocations where this resource is the primary resource
 263     colocations = pcmk__with_this_colocations(rsc);
 264     for (iter = colocations; iter != NULL; iter = iter->next) {
 265         const pcmk__colocation_t *constraint = iter->data;
 266         const pe_resource_t *dependent = constraint->dependent;
 267 
 268         if (dependent == orig_rsc) {
 269             continue; // Break colocation loop
 270         }
 271 
 272         if (pe_rsc_is_clone(rsc) && !pe_rsc_is_clone(dependent)) {
 273             continue; // We can't be sure whether dependent will be colocated
 274         }
 275 
 276         if ((constraint->score == INFINITY) &&
 277             (pcmk__colocation_affects(dependent, rsc, constraint,
 278                                       true) == pcmk__coloc_affects_location)) {
 279 
 280             colocated_rscs = dependent->cmds->colocated_resources(dependent,
 281                                                                   orig_rsc,
 282                                                                   colocated_rscs);
 283         }
 284     }
 285     g_list_free(colocations);
 286 
 287     return colocated_rscs;
 288 }
 289 
 290 // No-op function for variants that don't need to implement add_graph_meta()
 291 void
 292 pcmk__noop_add_graph_meta(const pe_resource_t *rsc, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294 }
 295 
 296 void
 297 pcmk__output_resource_actions(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 298 {
 299     pcmk__output_t *out = rsc->cluster->priv;
 300 
 301     pe_node_t *next = NULL;
 302     pe_node_t *current = NULL;
 303 
 304     if (rsc->children != NULL) {
 305         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 306             pe_resource_t *child = (pe_resource_t *) iter->data;
 307 
 308             child->cmds->output_actions(child);
 309         }
 310         return;
 311     }
 312 
 313     next = rsc->allocated_to;
 314     if (rsc->running_on) {
 315         current = pe__current_node(rsc);
 316         if (rsc->role == RSC_ROLE_STOPPED) {
 317             /* This can occur when resources are being recovered because
 318              * the current role can change in pcmk__primitive_create_actions()
 319              */
 320             rsc->role = RSC_ROLE_STARTED;
 321         }
 322     }
 323 
 324     if ((current == NULL) && pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 325         /* Don't log stopped orphans */
 326         return;
 327     }
 328 
 329     out->message(out, "rsc-action", rsc, current, next);
 330 }
 331 
 332 /*!
 333  * \internal
 334  * \brief Assign a specified primitive resource to a node
 335  *
 336  * Assign a specified primitive resource to a specified node, if the node can
 337  * run the resource (or unconditionally, if \p force is true). Mark the resource
 338  * as no longer provisional. If the primitive can't be assigned (or \p chosen is
 339  * NULL), unassign any previous assignment for it, set its next role to stopped,
 340  * and update any existing actions scheduled for it. This is not done
 341  * recursively for children, so it should be called only for primitives.
 342  *
 343  * \param[in,out] rsc     Resource to assign
 344  * \param[in,out] chosen  Node to assign \p rsc to
 345  * \param[in]     force   If true, assign to \p chosen even if unavailable
 346  *
 347  * \return true if \p rsc could be assigned, otherwise false
 348  *
 349  * \note Assigning a resource to the NULL node using this function is different
 350  *       from calling pcmk__unassign_resource(), in that it will also update any
 351  *       actions created for the resource.
 352  */
 353 bool
 354 pcmk__finalize_assignment(pe_resource_t *rsc, pe_node_t *chosen, bool force)
     /* [previous][next][first][last][top][bottom][index][help] */
 355 {
 356     pcmk__output_t *out = rsc->cluster->priv;
 357 
 358     CRM_ASSERT(rsc->variant == pe_native);
 359 
 360     if (!force && (chosen != NULL)) {
 361         if ((chosen->weight < 0)
 362             // Allow the graph to assume that guest node connections will come up
 363             || (!pcmk__node_available(chosen, true, false)
 364                 && !pe__is_guest_node(chosen))) {
 365 
 366             crm_debug("All nodes for resource %s are unavailable, unclean or "
 367                       "shutting down (%s can%s run resources, with weight %d)",
 368                       rsc->id, pe__node_name(chosen),
 369                       (pcmk__node_available(chosen, true, false)? "" : "not"),
 370                       chosen->weight);
 371             pe__set_next_role(rsc, RSC_ROLE_STOPPED, "node availability");
 372             chosen = NULL;
 373         }
 374     }
 375 
 376     pcmk__unassign_resource(rsc);
 377     pe__clear_resource_flags(rsc, pe_rsc_provisional);
 378 
 379     if (chosen == NULL) {
 380         crm_debug("Could not allocate a node for %s", rsc->id);
 381         pe__set_next_role(rsc, RSC_ROLE_STOPPED, "unable to allocate");
 382 
 383         for (GList *iter = rsc->actions; iter != NULL; iter = iter->next) {
 384             pe_action_t *op = (pe_action_t *) iter->data;
 385 
 386             crm_debug("Updating %s for allocation failure", op->uuid);
 387 
 388             if (pcmk__str_eq(op->task, RSC_STOP, pcmk__str_casei)) {
 389                 pe__clear_action_flags(op, pe_action_optional);
 390 
 391             } else if (pcmk__str_eq(op->task, RSC_START, pcmk__str_casei)) {
 392                 pe__clear_action_flags(op, pe_action_runnable);
 393                 //pe__set_resource_flags(rsc, pe_rsc_block);
 394 
 395             } else {
 396                 // Cancel recurring actions, unless for stopped state
 397                 const char *interval_ms_s = NULL;
 398                 const char *target_rc_s = NULL;
 399                 char *rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
 400 
 401                 interval_ms_s = g_hash_table_lookup(op->meta,
 402                                                     XML_LRM_ATTR_INTERVAL_MS);
 403                 target_rc_s = g_hash_table_lookup(op->meta,
 404                                                   XML_ATTR_TE_TARGET_RC);
 405                 if ((interval_ms_s != NULL)
 406                     && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_none)
 407                     && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) {
 408                     pe__clear_action_flags(op, pe_action_runnable);
 409                 }
 410                 free(rc_stopped);
 411             }
 412         }
 413         return false;
 414     }
 415 
 416     crm_debug("Assigning %s to %s", rsc->id, pe__node_name(chosen));
 417     rsc->allocated_to = pe__copy_node(chosen);
 418 
 419     chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc,
 420                                                     rsc);
 421     chosen->details->num_resources++;
 422     chosen->count++;
 423     pcmk__consume_node_capacity(chosen->details->utilization, rsc);
 424 
 425     if (pcmk_is_set(rsc->cluster->flags, pe_flag_show_utilization)) {
 426         out->message(out, "resource-util", rsc, chosen, __func__);
 427     }
 428     return true;
 429 }
 430 
 431 /*!
 432  * \internal
 433  * \brief Assign a specified resource (of any variant) to a node
 434  *
 435  * Assign a specified resource and its children (if any) to a specified node, if
 436  * the node can run the resource (or unconditionally, if \p force is true). Mark
 437  * the resources as no longer provisional. If the resources can't be assigned
 438  * (or \p chosen is NULL), unassign any previous assignments, set next role to
 439  * stopped, and update any existing actions scheduled for them.
 440  *
 441  * \param[in,out] rsc     Resource to assign
 442  * \param[in,out] chosen  Node to assign \p rsc to
 443  * \param[in]     force   If true, assign to \p chosen even if unavailable
 444  *
 445  * \return true if \p rsc could be assigned, otherwise false
 446  *
 447  * \note Assigning a resource to the NULL node using this function is different
 448  *       from calling pcmk__unassign_resource(), in that it will also update any
 449  *       actions created for the resource.
 450  */
 451 bool
 452 pcmk__assign_resource(pe_resource_t *rsc, pe_node_t *node, bool force)
     /* [previous][next][first][last][top][bottom][index][help] */
 453 {
 454     bool changed = false;
 455 
 456     if (rsc->children == NULL) {
 457         if (rsc->allocated_to != NULL) {
 458             changed = true;
 459         }
 460         pcmk__finalize_assignment(rsc, node, force);
 461 
 462     } else {
 463         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 464             pe_resource_t *child_rsc = (pe_resource_t *) iter->data;
 465 
 466             changed |= pcmk__assign_resource(child_rsc, node, force);
 467         }
 468     }
 469     return changed;
 470 }
 471 
 472 /*!
 473  * \internal
 474  * \brief Remove any assignment of a specified resource to a node
 475  *
 476  * If a specified resource has been assigned to a node, remove that assignment
 477  * and mark the resource as provisional again. This is not done recursively for
 478  * children, so it should be called only for primitives.
 479  *
 480  * \param[in,out] rsc  Resource to unassign
 481  */
 482 void
 483 pcmk__unassign_resource(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 484 {
 485     pe_node_t *old = rsc->allocated_to;
 486 
 487     if (old == NULL) {
 488         return;
 489     }
 490 
 491     crm_info("Unassigning %s from %s", rsc->id, pe__node_name(old));
 492     pe__set_resource_flags(rsc, pe_rsc_provisional);
 493     rsc->allocated_to = NULL;
 494 
 495     /* We're going to free the pe_node_t, but its details member is shared and
 496      * will remain, so update that appropriately first.
 497      */
 498     old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc,
 499                                                 rsc);
 500     old->details->num_resources--;
 501     pcmk__release_node_capacity(old->details->utilization, rsc);
 502     free(old);
 503 }
 504 
 505 /*!
 506  * \internal
 507  * \brief Check whether a resource has reached its migration threshold on a node
 508  *
 509  * \param[in,out] rsc       Resource to check
 510  * \param[in]     node      Node to check
 511  * \param[out]    failed    If threshold has been reached, this will be set to
 512  *                          resource that failed (possibly a parent of \p rsc)
 513  *
 514  * \return true if the migration threshold has been reached, false otherwise
 515  */
 516 bool
 517 pcmk__threshold_reached(pe_resource_t *rsc, const pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 518                         pe_resource_t **failed)
 519 {
 520     int fail_count, remaining_tries;
 521     pe_resource_t *rsc_to_ban = rsc;
 522 
 523     // Migration threshold of 0 means never force away
 524     if (rsc->migration_threshold == 0) {
 525         return false;
 526     }
 527 
 528     // If we're ignoring failures, also ignore the migration threshold
 529     if (pcmk_is_set(rsc->flags, pe_rsc_failure_ignored)) {
 530         return false;
 531     }
 532 
 533     // If there are no failures, there's no need to force away
 534     fail_count = pe_get_failcount(node, rsc, NULL,
 535                                   pe_fc_effective|pe_fc_fillers, NULL);
 536     if (fail_count <= 0) {
 537         return false;
 538     }
 539 
 540     // If failed resource is anonymous clone instance, we'll force clone away
 541     if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 542         rsc_to_ban = uber_parent(rsc);
 543     }
 544 
 545     // How many more times recovery will be tried on this node
 546     remaining_tries = rsc->migration_threshold - fail_count;
 547 
 548     if (remaining_tries <= 0) {
 549         crm_warn("%s cannot run on %s due to reaching migration threshold "
 550                  "(clean up resource to allow again)"
 551                  CRM_XS " failures=%d migration-threshold=%d",
 552                  rsc_to_ban->id, pe__node_name(node), fail_count,
 553                  rsc->migration_threshold);
 554         if (failed != NULL) {
 555             *failed = rsc_to_ban;
 556         }
 557         return true;
 558     }
 559 
 560     crm_info("%s can fail %d more time%s on "
 561              "%s before reaching migration threshold (%d)",
 562              rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries),
 563              pe__node_name(node), rsc->migration_threshold);
 564     return false;
 565 }
 566 
 567 static void *
 568 convert_const_pointer(const void *ptr)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     /* Worst function ever */
 571     return (void *)ptr;
 572 }
 573 
 574 /*!
 575  * \internal
 576  * \brief Get a node's weight
 577  *
 578  * \param[in] node     Unweighted node to check (for node ID)
 579  * \param[in] nodes    List of weighted nodes to look for \p node in
 580  *
 581  * \return Node's weight, or -INFINITY if not found
 582  */
 583 static int
 584 get_node_weight(const pe_node_t *node, GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 585 {
 586     pe_node_t *weighted_node = NULL;
 587 
 588     if ((node != NULL) && (nodes != NULL)) {
 589         weighted_node = g_hash_table_lookup(nodes, node->details->id);
 590     }
 591     return (weighted_node == NULL)? -INFINITY : weighted_node->weight;
 592 }
 593 
 594 /*!
 595  * \internal
 596  * \brief Compare two resources according to which should be allocated first
 597  *
 598  * \param[in] a     First resource to compare
 599  * \param[in] b     Second resource to compare
 600  * \param[in] data  Sorted list of all nodes in cluster
 601  *
 602  * \return -1 if \p a should be allocated before \b, 0 if they are equal,
 603  *         or +1 if \p a should be allocated after \b
 604  */
 605 static gint
 606 cmp_resources(gconstpointer a, gconstpointer b, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 607 {
 608     const pe_resource_t *resource1 = a;
 609     const pe_resource_t *resource2 = b;
 610     const GList *nodes = (const GList *) data;
 611 
 612     int rc = 0;
 613     int r1_weight = -INFINITY;
 614     int r2_weight = -INFINITY;
 615     pe_node_t *r1_node = NULL;
 616     pe_node_t *r2_node = NULL;
 617     GHashTable *r1_nodes = NULL;
 618     GHashTable *r2_nodes = NULL;
 619     const char *reason = NULL;
 620 
 621     // Resources with highest priority should be allocated first
 622     reason = "priority";
 623     r1_weight = resource1->priority;
 624     r2_weight = resource2->priority;
 625     if (r1_weight > r2_weight) {
 626         rc = -1;
 627         goto done;
 628     }
 629     if (r1_weight < r2_weight) {
 630         rc = 1;
 631         goto done;
 632     }
 633 
 634     // We need nodes to make any other useful comparisons
 635     reason = "no node list";
 636     if (nodes == NULL) {
 637         goto done;
 638     }
 639 
 640     // Calculate and log node weights
 641     resource1->cmds->add_colocated_node_scores(convert_const_pointer(resource1),
 642                                                resource1->id, &r1_nodes, NULL,
 643                                                1, pcmk__coloc_select_this_with);
 644     resource2->cmds->add_colocated_node_scores(convert_const_pointer(resource2),
 645                                                resource2->id, &r2_nodes, NULL,
 646                                                1, pcmk__coloc_select_this_with);
 647     pe__show_node_weights(true, NULL, resource1->id, r1_nodes,
 648                           resource1->cluster);
 649     pe__show_node_weights(true, NULL, resource2->id, r2_nodes,
 650                           resource2->cluster);
 651 
 652     // The resource with highest score on its current node goes first
 653     reason = "current location";
 654     if (resource1->running_on != NULL) {
 655         r1_node = pe__current_node(resource1);
 656     }
 657     if (resource2->running_on != NULL) {
 658         r2_node = pe__current_node(resource2);
 659     }
 660     r1_weight = get_node_weight(r1_node, r1_nodes);
 661     r2_weight = get_node_weight(r2_node, r2_nodes);
 662     if (r1_weight > r2_weight) {
 663         rc = -1;
 664         goto done;
 665     }
 666     if (r1_weight < r2_weight) {
 667         rc = 1;
 668         goto done;
 669     }
 670 
 671     // Otherwise a higher weight on any node will do
 672     reason = "score";
 673     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 674         const pe_node_t *node = (const pe_node_t *) iter->data;
 675 
 676         r1_weight = get_node_weight(node, r1_nodes);
 677         r2_weight = get_node_weight(node, r2_nodes);
 678         if (r1_weight > r2_weight) {
 679             rc = -1;
 680             goto done;
 681         }
 682         if (r1_weight < r2_weight) {
 683             rc = 1;
 684             goto done;
 685         }
 686     }
 687 
 688 done:
 689     crm_trace("%s (%d)%s%s %c %s (%d)%s%s: %s",
 690               resource1->id, r1_weight,
 691               ((r1_node == NULL)? "" : " on "),
 692               ((r1_node == NULL)? "" : r1_node->details->id),
 693               ((rc < 0)? '>' : ((rc > 0)? '<' : '=')),
 694               resource2->id, r2_weight,
 695               ((r2_node == NULL)? "" : " on "),
 696               ((r2_node == NULL)? "" : r2_node->details->id),
 697               reason);
 698     if (r1_nodes != NULL) {
 699         g_hash_table_destroy(r1_nodes);
 700     }
 701     if (r2_nodes != NULL) {
 702         g_hash_table_destroy(r2_nodes);
 703     }
 704     return rc;
 705 }
 706 
 707 /*!
 708  * \internal
 709  * \brief Sort resources in the order they should be allocated to nodes
 710  *
 711  * \param[in,out] data_set  Cluster working set
 712  */
 713 void
 714 pcmk__sort_resources(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 715 {
 716     GList *nodes = g_list_copy(data_set->nodes);
 717 
 718     nodes = pcmk__sort_nodes(nodes, NULL);
 719     data_set->resources = g_list_sort_with_data(data_set->resources,
 720                                                 cmp_resources, nodes);
 721     g_list_free(nodes);
 722 }

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