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_assignment_methods_for_rsc
  5. pcmk__set_assignment_methods
  6. add_colocated_resources
  7. pcmk__colocated_resources
  8. pcmk__noop_add_graph_meta
  9. pcmk__output_resource_actions
  10. add_assigned_resource
  11. pcmk__assign_resource
  12. pcmk__unassign_resource
  13. pcmk__threshold_reached
  14. get_node_score
  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 assignment methods by resource variant
  20 static pcmk_assignment_methods_t assignment_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         pcmk__clone_create_actions,
  62         pcmk__clone_create_probe,
  63         pcmk__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         pcmk__clone_apply_location,
  70         pcmk__clone_action_flags,
  71         pcmk__instance_update_ordered_actions,
  72         pcmk__output_resource_actions,
  73         pcmk__clone_add_actions_to_graph,
  74         pcmk__clone_add_graph_meta,
  75         pcmk__clone_add_utilization,
  76         pcmk__clone_shutdown_lock,
  77     },
  78     {
  79         pcmk__bundle_assign,
  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_apply_location,
  89         pcmk__bundle_action_flags,
  90         pcmk__instance_update_ordered_actions,
  91         pcmk__output_bundle_actions,
  92         pcmk__bundle_add_actions_to_graph,
  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(pcmk_resource_t *rsc, pcmk_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), PCMK_ACTION_STOP, node, FALSE,
 140                       rsc->cluster);
 141         pe__set_resource_flags(rsc, pcmk_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, pcmk_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         pcmk_resource_t *child = (pcmk_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] scheduler  Scheduler data
 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 pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185     GList *result = NULL;
 186 
 187     CRM_CHECK((id != NULL) && (scheduler != NULL), return NULL);
 188     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
 189         result = add_rsc_if_matching(result, (pcmk_resource_t *) iter->data,
 190                                      id);
 191     }
 192     return result;
 193 }
 194 
 195 /*!
 196  * \internal
 197  * \brief Set the variant-appropriate assignment methods for a resource
 198  *
 199  * \param[in,out] data       Resource to set assignment methods for
 200  * \param[in]     user_data  Ignored
 201  */
 202 static void
 203 set_assignment_methods_for_rsc(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 204 {
 205     pcmk_resource_t *rsc = data;
 206 
 207     rsc->cmds = &assignment_methods[rsc->variant];
 208     g_list_foreach(rsc->children, set_assignment_methods_for_rsc, NULL);
 209 }
 210 
 211 /*!
 212  * \internal
 213  * \brief Set the variant-appropriate assignment methods for all resources
 214  *
 215  * \param[in,out] scheduler  Scheduler data
 216  */
 217 void
 218 pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220     g_list_foreach(scheduler->resources, set_assignment_methods_for_rsc, NULL);
 221 }
 222 
 223 /*!
 224  * \internal
 225  * \brief Wrapper for colocated_resources() method for readability
 226  *
 227  * \param[in]      rsc       Resource to add to colocated list
 228  * \param[in]      orig_rsc  Resource originally requested
 229  * \param[in,out]  list      Pointer to list to add to
 230  *
 231  * \return (Possibly new) head of list
 232  */
 233 static inline void
 234 add_colocated_resources(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 235                         const pcmk_resource_t *orig_rsc, GList **list)
 236 {
 237     *list = rsc->cmds->colocated_resources(rsc, orig_rsc, *list);
 238 }
 239 
 240 // Shared implementation of pcmk_assignment_methods_t:colocated_resources()
 241 GList *
 242 pcmk__colocated_resources(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 243                           const pcmk_resource_t *orig_rsc,
 244                           GList *colocated_rscs)
 245 {
 246     const GList *iter = NULL;
 247     GList *colocations = NULL;
 248 
 249     if (orig_rsc == NULL) {
 250         orig_rsc = rsc;
 251     }
 252 
 253     if ((rsc == NULL) || (g_list_find(colocated_rscs, rsc) != NULL)) {
 254         return colocated_rscs;
 255     }
 256 
 257     pe_rsc_trace(orig_rsc, "%s is in colocation chain with %s",
 258                  rsc->id, orig_rsc->id);
 259     colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
 260 
 261     // Follow colocations where this resource is the dependent resource
 262     colocations = pcmk__this_with_colocations(rsc);
 263     for (iter = colocations; iter != NULL; iter = iter->next) {
 264         const pcmk__colocation_t *constraint = iter->data;
 265         const pcmk_resource_t *primary = constraint->primary;
 266 
 267         if (primary == orig_rsc) {
 268             continue; // Break colocation loop
 269         }
 270 
 271         if ((constraint->score == INFINITY) &&
 272             (pcmk__colocation_affects(rsc, primary, constraint,
 273                                       true) == pcmk__coloc_affects_location)) {
 274             add_colocated_resources(primary, orig_rsc, &colocated_rscs);
 275         }
 276     }
 277     g_list_free(colocations);
 278 
 279     // Follow colocations where this resource is the primary resource
 280     colocations = pcmk__with_this_colocations(rsc);
 281     for (iter = colocations; iter != NULL; iter = iter->next) {
 282         const pcmk__colocation_t *constraint = iter->data;
 283         const pcmk_resource_t *dependent = constraint->dependent;
 284 
 285         if (dependent == orig_rsc) {
 286             continue; // Break colocation loop
 287         }
 288 
 289         if (pe_rsc_is_clone(rsc) && !pe_rsc_is_clone(dependent)) {
 290             continue; // We can't be sure whether dependent will be colocated
 291         }
 292 
 293         if ((constraint->score == INFINITY) &&
 294             (pcmk__colocation_affects(dependent, rsc, constraint,
 295                                       true) == pcmk__coloc_affects_location)) {
 296             add_colocated_resources(dependent, orig_rsc, &colocated_rscs);
 297         }
 298     }
 299     g_list_free(colocations);
 300 
 301     return colocated_rscs;
 302 }
 303 
 304 // No-op function for variants that don't need to implement add_graph_meta()
 305 void
 306 pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308 }
 309 
 310 /*!
 311  * \internal
 312  * \brief Output a summary of scheduled actions for a resource
 313  *
 314  * \param[in,out] rsc  Resource to output actions for
 315  */
 316 void
 317 pcmk__output_resource_actions(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 318 {
 319     pcmk_node_t *next = NULL;
 320     pcmk_node_t *current = NULL;
 321     pcmk__output_t *out = NULL;
 322 
 323     CRM_ASSERT(rsc != NULL);
 324 
 325     out = rsc->cluster->priv;
 326     if (rsc->children != NULL) {
 327         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 328             pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 329 
 330             child->cmds->output_actions(child);
 331         }
 332         return;
 333     }
 334 
 335     next = rsc->allocated_to;
 336     if (rsc->running_on) {
 337         current = pe__current_node(rsc);
 338         if (rsc->role == pcmk_role_stopped) {
 339             /* This can occur when resources are being recovered because
 340              * the current role can change in pcmk__primitive_create_actions()
 341              */
 342             rsc->role = pcmk_role_started;
 343         }
 344     }
 345 
 346     if ((current == NULL) && pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
 347         /* Don't log stopped orphans */
 348         return;
 349     }
 350 
 351     out->message(out, "rsc-action", rsc, current, next);
 352 }
 353 
 354 /*!
 355  * \internal
 356  * \brief Add a resource to a node's list of assigned resources
 357  *
 358  * \param[in,out] node  Node to add resource to
 359  * \param[in]     rsc   Resource to add
 360  */
 361 static inline void
 362 add_assigned_resource(pcmk_node_t *node, pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 363 {
 364     node->details->allocated_rsc = g_list_prepend(node->details->allocated_rsc,
 365                                                   rsc);
 366 }
 367 
 368 /*!
 369  * \internal
 370  * \brief Assign a specified resource (of any variant) to a node
 371  *
 372  * Assign a specified resource and its children (if any) to a specified node, if
 373  * the node can run the resource (or unconditionally, if \p force is true). Mark
 374  * the resources as no longer provisional.
 375  *
 376  * If a resource can't be assigned (or \p node is \c NULL), unassign any
 377  * previous assignment. If \p stop_if_fail is \c true, set next role to stopped
 378  * and update any existing actions scheduled for the resource.
 379  *
 380  * \param[in,out] rsc           Resource to assign
 381  * \param[in,out] node          Node to assign \p rsc to
 382  * \param[in]     force         If true, assign to \p node even if unavailable
 383  * \param[in]     stop_if_fail  If \c true and either \p rsc can't be assigned
 384  *                              or \p chosen is \c NULL, set next role to
 385  *                              stopped and update existing actions (if \p rsc
 386  *                              is not a primitive, this applies to its
 387  *                              primitive descendants instead)
 388  *
 389  * \return \c true if the assignment of \p rsc changed, or \c false otherwise
 390  *
 391  * \note Assigning a resource to the NULL node using this function is different
 392  *       from calling pcmk__unassign_resource(), in that it may also update any
 393  *       actions created for the resource.
 394  * \note The \c pcmk_assignment_methods_t:assign() method is preferred, unless
 395  *       a resource should be assigned to the \c NULL node or every resource in
 396  *       a tree should be assigned to the same node.
 397  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
 398  *       completely undo the assignment. A successful assignment can be either
 399  *       undone or left alone as final. A failed assignment has the same effect
 400  *       as calling pcmk__unassign_resource(); there are no side effects on
 401  *       roles or actions.
 402  */
 403 bool
 404 pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force,
     /* [previous][next][first][last][top][bottom][index][help] */
 405                       bool stop_if_fail)
 406 {
 407     bool changed = false;
 408 
 409     CRM_ASSERT(rsc != NULL);
 410 
 411     if (rsc->children != NULL) {
 412         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 413             pcmk_resource_t *child_rsc = iter->data;
 414 
 415             changed |= pcmk__assign_resource(child_rsc, node, force,
 416                                              stop_if_fail);
 417         }
 418         return changed;
 419     }
 420 
 421     // Assigning a primitive
 422 
 423     if (!force && (node != NULL)
 424         && ((node->weight < 0)
 425             // Allow graph to assume that guest node connections will come up
 426             || (!pcmk__node_available(node, true, false)
 427                 && !pe__is_guest_node(node)))) {
 428 
 429         pe_rsc_debug(rsc,
 430                      "All nodes for resource %s are unavailable, unclean or "
 431                      "shutting down (%s can%s run resources, with score %s)",
 432                      rsc->id, pe__node_name(node),
 433                      (pcmk__node_available(node, true, false)? "" : "not"),
 434                      pcmk_readable_score(node->weight));
 435 
 436         if (stop_if_fail) {
 437             pe__set_next_role(rsc, pcmk_role_stopped, "node availability");
 438         }
 439         node = NULL;
 440     }
 441 
 442     if (rsc->allocated_to != NULL) {
 443         changed = !pe__same_node(rsc->allocated_to, node);
 444     } else {
 445         changed = (node != NULL);
 446     }
 447     pcmk__unassign_resource(rsc);
 448     pe__clear_resource_flags(rsc, pcmk_rsc_unassigned);
 449 
 450     if (node == NULL) {
 451         char *rc_stopped = NULL;
 452 
 453         pe_rsc_debug(rsc, "Could not assign %s to a node", rsc->id);
 454 
 455         if (!stop_if_fail) {
 456             return changed;
 457         }
 458         pe__set_next_role(rsc, pcmk_role_stopped, "unable to assign");
 459 
 460         for (GList *iter = rsc->actions; iter != NULL; iter = iter->next) {
 461             pcmk_action_t *op = (pcmk_action_t *) iter->data;
 462 
 463             pe_rsc_debug(rsc, "Updating %s for %s assignment failure",
 464                          op->uuid, rsc->id);
 465 
 466             if (pcmk__str_eq(op->task, PCMK_ACTION_STOP, pcmk__str_none)) {
 467                 pe__clear_action_flags(op, pcmk_action_optional);
 468 
 469             } else if (pcmk__str_eq(op->task, PCMK_ACTION_START,
 470                                     pcmk__str_none)) {
 471                 pe__clear_action_flags(op, pcmk_action_runnable);
 472 
 473             } else {
 474                 // Cancel recurring actions, unless for stopped state
 475                 const char *interval_ms_s = NULL;
 476                 const char *target_rc_s = NULL;
 477 
 478                 interval_ms_s = g_hash_table_lookup(op->meta,
 479                                                     XML_LRM_ATTR_INTERVAL_MS);
 480                 target_rc_s = g_hash_table_lookup(op->meta,
 481                                                   XML_ATTR_TE_TARGET_RC);
 482                 if (rc_stopped == NULL) {
 483                     rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
 484                 }
 485 
 486                 if (!pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)
 487                     && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) {
 488 
 489                     pe__clear_action_flags(op, pcmk_action_runnable);
 490                 }
 491             }
 492         }
 493         free(rc_stopped);
 494         return changed;
 495     }
 496 
 497     pe_rsc_debug(rsc, "Assigning %s to %s", rsc->id, pe__node_name(node));
 498     rsc->allocated_to = pe__copy_node(node);
 499 
 500     add_assigned_resource(node, rsc);
 501     node->details->num_resources++;
 502     node->count++;
 503     pcmk__consume_node_capacity(node->details->utilization, rsc);
 504 
 505     if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_show_utilization)) {
 506         pcmk__output_t *out = rsc->cluster->priv;
 507 
 508         out->message(out, "resource-util", rsc, node, __func__);
 509     }
 510     return changed;
 511 }
 512 
 513 /*!
 514  * \internal
 515  * \brief Remove any node assignment from a specified resource and its children
 516  *
 517  * If a specified resource has been assigned to a node, remove that assignment
 518  * and mark the resource as provisional again.
 519  *
 520  * \param[in,out] rsc  Resource to unassign
 521  *
 522  * \note This function is called recursively on \p rsc and its children.
 523  */
 524 void
 525 pcmk__unassign_resource(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 526 {
 527     pcmk_node_t *old = rsc->allocated_to;
 528 
 529     if (old == NULL) {
 530         crm_info("Unassigning %s", rsc->id);
 531     } else {
 532         crm_info("Unassigning %s from %s", rsc->id, pe__node_name(old));
 533     }
 534 
 535     pe__set_resource_flags(rsc, pcmk_rsc_unassigned);
 536 
 537     if (rsc->children == NULL) {
 538         if (old == NULL) {
 539             return;
 540         }
 541         rsc->allocated_to = NULL;
 542 
 543         /* We're going to free the pcmk_node_t, but its details member is shared
 544          * and will remain, so update that appropriately first.
 545          */
 546         old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc,
 547                                                     rsc);
 548         old->details->num_resources--;
 549         pcmk__release_node_capacity(old->details->utilization, rsc);
 550         free(old);
 551         return;
 552     }
 553 
 554     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 555         pcmk__unassign_resource((pcmk_resource_t *) iter->data);
 556     }
 557 }
 558 
 559 /*!
 560  * \internal
 561  * \brief Check whether a resource has reached its migration threshold on a node
 562  *
 563  * \param[in,out] rsc       Resource to check
 564  * \param[in]     node      Node to check
 565  * \param[out]    failed    If threshold has been reached, this will be set to
 566  *                          resource that failed (possibly a parent of \p rsc)
 567  *
 568  * \return true if the migration threshold has been reached, false otherwise
 569  */
 570 bool
 571 pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 572                         pcmk_resource_t **failed)
 573 {
 574     int fail_count, remaining_tries;
 575     pcmk_resource_t *rsc_to_ban = rsc;
 576 
 577     // Migration threshold of 0 means never force away
 578     if (rsc->migration_threshold == 0) {
 579         return false;
 580     }
 581 
 582     // If we're ignoring failures, also ignore the migration threshold
 583     if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
 584         return false;
 585     }
 586 
 587     // If there are no failures, there's no need to force away
 588     fail_count = pe_get_failcount(node, rsc, NULL,
 589                                   pcmk__fc_effective|pcmk__fc_fillers, NULL);
 590     if (fail_count <= 0) {
 591         return false;
 592     }
 593 
 594     // If failed resource is anonymous clone instance, we'll force clone away
 595     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
 596         rsc_to_ban = uber_parent(rsc);
 597     }
 598 
 599     // How many more times recovery will be tried on this node
 600     remaining_tries = rsc->migration_threshold - fail_count;
 601 
 602     if (remaining_tries <= 0) {
 603         crm_warn("%s cannot run on %s due to reaching migration threshold "
 604                  "(clean up resource to allow again)"
 605                  CRM_XS " failures=%d migration-threshold=%d",
 606                  rsc_to_ban->id, pe__node_name(node), fail_count,
 607                  rsc->migration_threshold);
 608         if (failed != NULL) {
 609             *failed = rsc_to_ban;
 610         }
 611         return true;
 612     }
 613 
 614     crm_info("%s can fail %d more time%s on "
 615              "%s before reaching migration threshold (%d)",
 616              rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries),
 617              pe__node_name(node), rsc->migration_threshold);
 618     return false;
 619 }
 620 
 621 /*!
 622  * \internal
 623  * \brief Get a node's score
 624  *
 625  * \param[in] node     Node with ID to check
 626  * \param[in] nodes    List of nodes to look for \p node score in
 627  *
 628  * \return Node's score, or -INFINITY if not found
 629  */
 630 static int
 631 get_node_score(const pcmk_node_t *node, GHashTable *nodes)
     /* [previous][next][first][last][top][bottom][index][help] */
 632 {
 633     pcmk_node_t *found_node = NULL;
 634 
 635     if ((node != NULL) && (nodes != NULL)) {
 636         found_node = g_hash_table_lookup(nodes, node->details->id);
 637     }
 638     return (found_node == NULL)? -INFINITY : found_node->weight;
 639 }
 640 
 641 /*!
 642  * \internal
 643  * \brief Compare two resources according to which should be assigned first
 644  *
 645  * \param[in] a     First resource to compare
 646  * \param[in] b     Second resource to compare
 647  * \param[in] data  Sorted list of all nodes in cluster
 648  *
 649  * \return -1 if \p a should be assigned before \b, 0 if they are equal,
 650  *         or +1 if \p a should be assigned after \b
 651  */
 652 static gint
 653 cmp_resources(gconstpointer a, gconstpointer b, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 654 {
 655     /* GLib insists that this function require gconstpointer arguments, but we
 656      * make a small, temporary change to each argument (setting the
 657      * pe_rsc_merging flag) during comparison
 658      */
 659     pcmk_resource_t *resource1 = (pcmk_resource_t *) a;
 660     pcmk_resource_t *resource2 = (pcmk_resource_t *) b;
 661     const GList *nodes = data;
 662 
 663     int rc = 0;
 664     int r1_score = -INFINITY;
 665     int r2_score = -INFINITY;
 666     pcmk_node_t *r1_node = NULL;
 667     pcmk_node_t *r2_node = NULL;
 668     GHashTable *r1_nodes = NULL;
 669     GHashTable *r2_nodes = NULL;
 670     const char *reason = NULL;
 671 
 672     // Resources with highest priority should be assigned first
 673     reason = "priority";
 674     r1_score = resource1->priority;
 675     r2_score = resource2->priority;
 676     if (r1_score > r2_score) {
 677         rc = -1;
 678         goto done;
 679     }
 680     if (r1_score < r2_score) {
 681         rc = 1;
 682         goto done;
 683     }
 684 
 685     // We need nodes to make any other useful comparisons
 686     reason = "no node list";
 687     if (nodes == NULL) {
 688         goto done;
 689     }
 690 
 691     // Calculate and log node scores
 692     resource1->cmds->add_colocated_node_scores(resource1, NULL, resource1->id,
 693                                                &r1_nodes, NULL, 1,
 694                                                pcmk__coloc_select_this_with);
 695     resource2->cmds->add_colocated_node_scores(resource2, NULL, resource2->id,
 696                                                &r2_nodes, NULL, 1,
 697                                                pcmk__coloc_select_this_with);
 698     pe__show_node_scores(true, NULL, resource1->id, r1_nodes,
 699                          resource1->cluster);
 700     pe__show_node_scores(true, NULL, resource2->id, r2_nodes,
 701                          resource2->cluster);
 702 
 703     // The resource with highest score on its current node goes first
 704     reason = "current location";
 705     if (resource1->running_on != NULL) {
 706         r1_node = pe__current_node(resource1);
 707     }
 708     if (resource2->running_on != NULL) {
 709         r2_node = pe__current_node(resource2);
 710     }
 711     r1_score = get_node_score(r1_node, r1_nodes);
 712     r2_score = get_node_score(r2_node, r2_nodes);
 713     if (r1_score > r2_score) {
 714         rc = -1;
 715         goto done;
 716     }
 717     if (r1_score < r2_score) {
 718         rc = 1;
 719         goto done;
 720     }
 721 
 722     // Otherwise a higher score on any node will do
 723     reason = "score";
 724     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 725         const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
 726 
 727         r1_score = get_node_score(node, r1_nodes);
 728         r2_score = get_node_score(node, r2_nodes);
 729         if (r1_score > r2_score) {
 730             rc = -1;
 731             goto done;
 732         }
 733         if (r1_score < r2_score) {
 734             rc = 1;
 735             goto done;
 736         }
 737     }
 738 
 739 done:
 740     crm_trace("%s (%d)%s%s %c %s (%d)%s%s: %s",
 741               resource1->id, r1_score,
 742               ((r1_node == NULL)? "" : " on "),
 743               ((r1_node == NULL)? "" : r1_node->details->id),
 744               ((rc < 0)? '>' : ((rc > 0)? '<' : '=')),
 745               resource2->id, r2_score,
 746               ((r2_node == NULL)? "" : " on "),
 747               ((r2_node == NULL)? "" : r2_node->details->id),
 748               reason);
 749     if (r1_nodes != NULL) {
 750         g_hash_table_destroy(r1_nodes);
 751     }
 752     if (r2_nodes != NULL) {
 753         g_hash_table_destroy(r2_nodes);
 754     }
 755     return rc;
 756 }
 757 
 758 /*!
 759  * \internal
 760  * \brief Sort resources in the order they should be assigned to nodes
 761  *
 762  * \param[in,out] scheduler  Scheduler data
 763  */
 764 void
 765 pcmk__sort_resources(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 766 {
 767     GList *nodes = g_list_copy(scheduler->nodes);
 768 
 769     nodes = pcmk__sort_nodes(nodes, NULL);
 770     scheduler->resources = g_list_sort_with_data(scheduler->resources,
 771                                                  cmp_resources, nodes);
 772     g_list_free(nodes);
 773 }

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