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__output_resource_actions
  8. pcmk__assign_primitive
  9. pcmk__assign_resource
  10. pcmk__unassign_resource
  11. pcmk__threshold_reached
  12. convert_const_pointer
  13. get_node_weight
  14. cmp_resources
  15. pcmk__sort_resources

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

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