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

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