1    	/*
2    	 * Copyright 2014-2026 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 <stdbool.h>
13   	#include <stdlib.h>
14   	#include <string.h>
15   	#include <crm/common/xml.h>
16   	#include <pacemaker-internal.h>
17   	
18   	#include "libpacemaker_private.h"
19   	
20   	// Resource assignment methods by resource variant
21   	static pcmk__assignment_methods_t assignment_methods[] = {
22   	    {
23   	        pcmk__primitive_assign,
24   	        pcmk__primitive_create_actions,
25   	        pcmk__probe_rsc_on_node,
26   	        pcmk__primitive_internal_constraints,
27   	        pcmk__primitive_apply_coloc_score,
28   	        pcmk__colocated_resources,
29   	        pcmk__with_primitive_colocations,
30   	        pcmk__primitive_with_colocations,
31   	        pcmk__add_colocated_node_scores,
32   	        pcmk__apply_location,
33   	        pcmk__primitive_action_flags,
34   	        pcmk__update_ordered_actions,
35   	        pcmk__output_resource_actions,
36   	        pcmk__add_rsc_actions_to_graph,
37   	        pcmk__primitive_add_graph_meta,
38   	        pcmk__primitive_add_utilization,
39   	        pcmk__primitive_shutdown_lock,
40   	    },
41   	    {
42   	        pcmk__group_assign,
43   	        pcmk__group_create_actions,
44   	        pcmk__probe_rsc_on_node,
45   	        pcmk__group_internal_constraints,
46   	        pcmk__group_apply_coloc_score,
47   	        pcmk__group_colocated_resources,
48   	        pcmk__with_group_colocations,
49   	        pcmk__group_with_colocations,
50   	        pcmk__group_add_colocated_node_scores,
51   	        pcmk__group_apply_location,
52   	        pcmk__group_action_flags,
53   	        pcmk__group_update_ordered_actions,
54   	        pcmk__output_resource_actions,
55   	        pcmk__add_rsc_actions_to_graph,
56   	        pcmk__noop_add_graph_meta,
57   	        pcmk__group_add_utilization,
58   	        pcmk__group_shutdown_lock,
59   	    },
60   	    {
61   	        pcmk__clone_assign,
62   	        pcmk__clone_create_actions,
63   	        pcmk__clone_create_probe,
64   	        pcmk__clone_internal_constraints,
65   	        pcmk__clone_apply_coloc_score,
66   	        pcmk__colocated_resources,
67   	        pcmk__with_clone_colocations,
68   	        pcmk__clone_with_colocations,
69   	        pcmk__add_colocated_node_scores,
70   	        pcmk__clone_apply_location,
71   	        pcmk__clone_action_flags,
72   	        pcmk__instance_update_ordered_actions,
73   	        pcmk__output_resource_actions,
74   	        pcmk__clone_add_actions_to_graph,
75   	        pcmk__clone_add_graph_meta,
76   	        pcmk__clone_add_utilization,
77   	        pcmk__clone_shutdown_lock,
78   	    },
79   	    {
80   	        pcmk__bundle_assign,
81   	        pcmk__bundle_create_actions,
82   	        pcmk__bundle_create_probe,
83   	        pcmk__bundle_internal_constraints,
84   	        pcmk__bundle_apply_coloc_score,
85   	        pcmk__colocated_resources,
86   	        pcmk__with_bundle_colocations,
87   	        pcmk__bundle_with_colocations,
88   	        pcmk__add_colocated_node_scores,
89   	        pcmk__bundle_apply_location,
90   	        pcmk__bundle_action_flags,
91   	        pcmk__instance_update_ordered_actions,
92   	        pcmk__output_bundle_actions,
93   	        pcmk__bundle_add_actions_to_graph,
94   	        pcmk__noop_add_graph_meta,
95   	        pcmk__bundle_add_utilization,
96   	        pcmk__bundle_shutdown_lock,
97   	    }
98   	};
99   	
100  	/*!
101  	 * \internal
102  	 * \brief Check whether a resource's agent standard, provider, or type changed
103  	 *
104  	 * \param[in,out] rsc             Resource to check
105  	 * \param[in,out] node            Node needing unfencing if agent changed
106  	 * \param[in]     rsc_entry       XML with previously known agent information
107  	 * \param[in]     active_on_node  Whether \p rsc is active on \p node
108  	 *
109  	 * \return true if agent for \p rsc changed, otherwise false
110  	 */
111  	bool
112  	pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node,
113  	                        const xmlNode *rsc_entry, bool active_on_node)
114  	{
115  	    bool changed = false;
116  	    const char *attr_list[] = {
117  	        PCMK_XA_TYPE,
118  	        PCMK_XA_CLASS,
119  	        PCMK_XA_PROVIDER,
120  	    };
121  	
122  	    for (int i = 0; i < PCMK__NELEM(attr_list); i++) {
123  	        const char *value = pcmk__xe_get(rsc->priv->xml, attr_list[i]);
124  	        const char *old_value = pcmk__xe_get(rsc_entry, attr_list[i]);
125  	
126  	        if (!pcmk__str_eq(value, old_value, pcmk__str_none)) {
127  	            changed = true;
128  	            trigger_unfencing(rsc, node, "Device definition changed", NULL,
129  	                              rsc->priv->scheduler);
130  	            if (active_on_node) {
131  	                pcmk__notice("Forcing restart of %s on %s because %s changed "
132  	                             "from '%s' to '%s'",
133  	                             rsc->id, pcmk__node_name(node), attr_list[i],
134  	                             pcmk__s(old_value, ""), pcmk__s(value, ""));
135  	            }
136  	        }
137  	    }
138  	    if (changed && active_on_node) {
139  	        // Make sure the resource is restarted
140  	        custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
141  	                      rsc->priv->scheduler);
142  	        pcmk__set_rsc_flags(rsc, pcmk__rsc_start_pending);
143  	    }
144  	    return changed;
145  	}
146  	
147  	/*!
148  	 * \internal
149  	 * \brief Add resource (and any matching children) to list if it matches ID
150  	 *
151  	 * \param[in] result  List to add resource to
152  	 * \param[in] rsc     Resource to check
153  	 * \param[in] id      ID to match
154  	 *
155  	 * \return (Possibly new) head of list
156  	 */
157  	static GList *
158  	add_rsc_if_matching(GList *result, pcmk_resource_t *rsc, const char *id)
159  	{
160  	    if (pcmk__str_eq(id, rsc->id, pcmk__str_none)
161  	        || pcmk__str_eq(id, rsc->priv->history_id, pcmk__str_none)) {
162  	        result = g_list_prepend(result, rsc);
163  	    }
164  	
165  	    for (GList *iter = rsc->priv->children;
166  	         iter != NULL; iter = iter->next) {
167  	
168  	        pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
169  	
170  	        result = add_rsc_if_matching(result, child, id);
171  	    }
172  	    return result;
173  	}
174  	
175  	/*!
176  	 * \internal
177  	 * \brief Find all resources matching a given ID by either ID or clone name
178  	 *
179  	 * \param[in] id         Resource ID to check
180  	 * \param[in] scheduler  Scheduler data
181  	 *
182  	 * \return List of all resources that match \p id
183  	 * \note The caller is responsible for freeing the return value with
184  	 *       g_list_free().
185  	 */
186  	GList *
187  	pcmk__rscs_matching_id(const char *id, const pcmk_scheduler_t *scheduler)
188  	{
189  	    GList *result = NULL;
190  	
191  	    CRM_CHECK((id != NULL) && (scheduler != NULL), return NULL);
192  	
193  	    for (GList *iter = scheduler->priv->resources;
194  	         iter != NULL; iter = iter->next) {
195  	
196  	        result = add_rsc_if_matching(result, (pcmk_resource_t *) iter->data,
197  	                                     id);
198  	    }
199  	    return result;
200  	}
201  	
202  	/*!
203  	 * \internal
204  	 * \brief Set the variant-appropriate assignment methods for a resource
205  	 *
206  	 * \param[in,out] data       Resource to set assignment methods for
207  	 * \param[in]     user_data  Ignored
208  	 */
209  	static void
210  	set_assignment_methods_for_rsc(gpointer data, gpointer user_data)
211  	{
212  	    pcmk_resource_t *rsc = data;
213  	
214  	    rsc->priv->cmds = &assignment_methods[rsc->priv->variant];
215  	    g_list_foreach(rsc->priv->children, set_assignment_methods_for_rsc,
216  	                   NULL);
217  	}
218  	
219  	/*!
220  	 * \internal
221  	 * \brief Set the variant-appropriate assignment methods for all resources
222  	 *
223  	 * \param[in,out] scheduler  Scheduler data
224  	 */
225  	void
226  	pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler)
227  	{
228  	    g_list_foreach(scheduler->priv->resources, set_assignment_methods_for_rsc,
229  	                   NULL);
230  	}
231  	
232  	/*!
233  	 * \internal
234  	 * \brief Wrapper for colocated_resources() method for readability
235  	 *
236  	 * \param[in]      rsc       Resource to add to colocated list
237  	 * \param[in]      orig_rsc  Resource originally requested
238  	 * \param[in,out]  list      Pointer to list to add to
239  	 *
240  	 * \return (Possibly new) head of list
241  	 */
242  	static inline void
243  	add_colocated_resources(const pcmk_resource_t *rsc,
244  	                        const pcmk_resource_t *orig_rsc, GList **list)
245  	{
246  	    *list = rsc->priv->cmds->colocated_resources(rsc, orig_rsc, *list);
247  	}
248  	
249  	// Shared implementation of pcmk__assignment_methods_t:colocated_resources()
250  	GList *
251  	pcmk__colocated_resources(const pcmk_resource_t *rsc,
252  	                          const pcmk_resource_t *orig_rsc,
253  	                          GList *colocated_rscs)
254  	{
255  	    const GList *iter = NULL;
256  	    GList *colocations = NULL;
257  	
258  	    if (orig_rsc == NULL) {
259  	        orig_rsc = rsc;
260  	    }
261  	
262  	    if ((rsc == NULL) || (g_list_find(colocated_rscs, rsc) != NULL)) {
263  	        return colocated_rscs;
264  	    }
265  	
266  	    pcmk__rsc_trace(orig_rsc, "%s is in colocation chain with %s",
267  	                    rsc->id, orig_rsc->id);
268  	    colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
269  	
270  	    // Follow colocations where this resource is the dependent resource
271  	    colocations = pcmk__this_with_colocations(rsc);
272  	    for (iter = colocations; iter != NULL; iter = iter->next) {
273  	        const pcmk__colocation_t *constraint = iter->data;
274  	        const pcmk_resource_t *primary = constraint->primary;
275  	
276  	        if (primary == orig_rsc) {
277  	            continue; // Break colocation loop
278  	        }
279  	
280  	        if ((constraint->score == PCMK_SCORE_INFINITY) &&
281  	            (pcmk__colocation_affects(rsc, primary, constraint,
282  	                                      true) == pcmk__coloc_affects_location)) {
283  	            add_colocated_resources(primary, orig_rsc, &colocated_rscs);
284  	        }
285  	    }
286  	    g_list_free(colocations);
287  	
288  	    // Follow colocations where this resource is the primary resource
289  	    colocations = pcmk__with_this_colocations(rsc);
290  	    for (iter = colocations; iter != NULL; iter = iter->next) {
291  	        const pcmk__colocation_t *constraint = iter->data;
292  	        const pcmk_resource_t *dependent = constraint->dependent;
293  	
294  	        if (dependent == orig_rsc) {
295  	            continue; // Break colocation loop
296  	        }
297  	
298  	        if (pcmk__is_clone(rsc) && !pcmk__is_clone(dependent)) {
299  	            continue; // We can't be sure whether dependent will be colocated
300  	        }
301  	
302  	        if ((constraint->score == PCMK_SCORE_INFINITY) &&
303  	            (pcmk__colocation_affects(dependent, rsc, constraint,
304  	                                      true) == pcmk__coloc_affects_location)) {
305  	            add_colocated_resources(dependent, orig_rsc, &colocated_rscs);
306  	        }
307  	    }
308  	    g_list_free(colocations);
309  	
310  	    return colocated_rscs;
311  	}
312  	
313  	// No-op function for variants that don't need to implement add_graph_meta()
314  	void
315  	pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
316  	{
317  	}
318  	
319  	/*!
320  	 * \internal
321  	 * \brief Output a summary of scheduled actions for a resource
322  	 *
323  	 * \param[in,out] rsc  Resource to output actions for
324  	 */
325  	void
326  	pcmk__output_resource_actions(pcmk_resource_t *rsc)
327  	{
328  	    pcmk_node_t *next = NULL;
329  	    pcmk_node_t *current = NULL;
330  	    pcmk__output_t *out = NULL;
331  	
332  	    pcmk__assert(rsc != NULL);
333  	
334  	    out = rsc->priv->scheduler->priv->out;
335  	    if (rsc->priv->children != NULL) {
336  	
337  	        for (GList *iter = rsc->priv->children;
338  	             iter != NULL; iter = iter->next) {
339  	
340  	            pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
341  	
342  	            child->priv->cmds->output_actions(child);
343  	        }
344  	        return;
345  	    }
346  	
347  	    next = rsc->priv->assigned_node;
348  	    if (rsc->priv->active_nodes != NULL) {
349  	        current = pcmk__current_node(rsc);
350  	        if (rsc->priv->orig_role == pcmk_role_stopped) {
351  	            /* This can occur when resources are being recovered because
352  	             * the current role can change in pcmk__primitive_create_actions()
353  	             */
354  	            rsc->priv->orig_role = pcmk_role_started;
355  	        }
356  	    }
357  	
358  	    if ((current == NULL) && pcmk__is_set(rsc->flags, pcmk__rsc_removed)) {
359  	        // Don't log stopped removed resources
360  	        return;
361  	    }
362  	
363  	    out->message(out, "rsc-action", rsc, current, next);
364  	}
365  	
366  	/*!
367  	 * \internal
368  	 * \brief Add a resource to a node's list of assigned resources
369  	 *
370  	 * \param[in,out] node  Node to add resource to
371  	 * \param[in]     rsc   Resource to add
372  	 */
373  	static inline void
374  	add_assigned_resource(pcmk_node_t *node, pcmk_resource_t *rsc)
375  	{
376  	    node->priv->assigned_resources =
377  	        g_list_prepend(node->priv->assigned_resources, rsc);
378  	}
379  	
380  	/*!
381  	 * \internal
382  	 * \brief Assign a specified resource (of any variant) to a node
383  	 *
384  	 * Assign a specified resource and its children (if any) to a specified node, if
385  	 * the node can run the resource (or unconditionally, if \p force is true). Mark
386  	 * the resources as no longer provisional.
387  	 *
388  	 * If a resource can't be assigned (or \p node is \c NULL), unassign any
389  	 * previous assignment. If \p stop_if_fail is \c true, set next role to stopped
390  	 * and update any existing actions scheduled for the resource.
391  	 *
392  	 * \param[in,out] rsc           Resource to assign
393  	 * \param[in,out] node          Node to assign \p rsc to
394  	 * \param[in]     force         If true, assign to \p node even if unavailable
395  	 * \param[in]     stop_if_fail  If \c true and either \p rsc can't be assigned
396  	 *                              or \p chosen is \c NULL, set next role to
397  	 *                              stopped and update existing actions (if \p rsc
398  	 *                              is not a primitive, this applies to its
399  	 *                              primitive descendants instead)
400  	 *
401  	 * \return \c true if the assignment of \p rsc changed, or \c false otherwise
402  	 *
403  	 * \note Assigning a resource to the NULL node using this function is different
404  	 *       from calling pcmk__unassign_resource(), in that it may also update any
405  	 *       actions created for the resource.
406  	 * \note The \c pcmk__assignment_methods_t:assign() method is preferred, unless
407  	 *       a resource should be assigned to the \c NULL node or every resource in
408  	 *       a tree should be assigned to the same node.
409  	 * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
410  	 *       completely undo the assignment. A successful assignment can be either
411  	 *       undone or left alone as final. A failed assignment has the same effect
412  	 *       as calling pcmk__unassign_resource(); there are no side effects on
413  	 *       roles or actions.
414  	 */
415  	bool
416  	pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force,
417  	                      bool stop_if_fail)
418  	{
419  	    bool changed = false;
420  	    pcmk_scheduler_t *scheduler = NULL;
421  	
422  	    pcmk__assert(rsc != NULL);
423  	    scheduler = rsc->priv->scheduler;
424  	
425  	    if (rsc->priv->children != NULL) {
426  	
427  	        for (GList *iter = rsc->priv->children;
428  	             iter != NULL; iter = iter->next) {
429  	
430  	            pcmk_resource_t *child_rsc = iter->data;
431  	
432  	            changed |= pcmk__assign_resource(child_rsc, node, force,
433  	                                             stop_if_fail);
434  	        }
435  	        return changed;
436  	    }
437  	
438  	    // Assigning a primitive
439  	
440  	    if (!force && (node != NULL)
441  	        && ((node->assign->score < 0)
442  	            // Allow graph to assume that guest node connections will come up
443  	            || (!pcmk__node_available(node, true, false)
444  	                && !pcmk__is_guest_or_bundle_node(node)))) {
445  	
446  	        pcmk__rsc_debug(rsc,
447  	                        "All nodes for resource %s are unavailable, unclean or "
448  	                        "shutting down (%s can%s run resources, with score %s)",
449  	                        rsc->id, pcmk__node_name(node),
450  	                        (pcmk__node_available(node, true, false)? "" : "not"),
451  	                        pcmk_readable_score(node->assign->score));
452  	
453  	        if (stop_if_fail) {
454  	            pe__set_next_role(rsc, pcmk_role_stopped, "node availability");
455  	        }
456  	        node = NULL;
457  	    }
458  	
459  	    if (rsc->priv->assigned_node != NULL) {
460  	        changed = !pcmk__same_node(rsc->priv->assigned_node, node);
461  	    } else {
462  	        changed = (node != NULL);
463  	    }
464  	    pcmk__unassign_resource(rsc);
465  	    pcmk__clear_rsc_flags(rsc, pcmk__rsc_unassigned);
466  	
467  	    if (node == NULL) {
468  	        char *rc_stopped = NULL;
469  	
470  	        pcmk__rsc_debug(rsc, "Could not assign %s to a node", rsc->id);
471  	
472  	        if (!stop_if_fail) {
473  	            return changed;
474  	        }
475  	        pe__set_next_role(rsc, pcmk_role_stopped, "unable to assign");
476  	
477  	        for (GList *iter = rsc->priv->actions;
478  	             iter != NULL; iter = iter->next) {
479  	
480  	            pcmk_action_t *op = (pcmk_action_t *) iter->data;
481  	
482  	            pcmk__rsc_debug(rsc, "Updating %s for %s assignment failure",
483  	                            op->uuid, rsc->id);
484  	
485  	            if (pcmk__str_eq(op->task, PCMK_ACTION_STOP, pcmk__str_none)) {
486  	                pcmk__clear_action_flags(op, pcmk__action_optional);
487  	
488  	            } else if (pcmk__str_eq(op->task, PCMK_ACTION_START,
489  	                                    pcmk__str_none)) {
490  	                pcmk__clear_action_flags(op, pcmk__action_runnable);
491  	
492  	            } else {
493  	                // Cancel recurring actions, unless for stopped state
494  	                const char *interval_ms_s = NULL;
495  	                const char *target_rc_s = NULL;
496  	
497  	                interval_ms_s = g_hash_table_lookup(op->meta,
498  	                                                    PCMK_META_INTERVAL);
499  	                target_rc_s = g_hash_table_lookup(op->meta,
500  	                                                  PCMK__META_OP_TARGET_RC);
501  	                if (rc_stopped == NULL) {
502  	                    rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
503  	                }
504  	
505  	                if (!pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)
506  	                    && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) {
507  	
508  	                    pcmk__clear_action_flags(op, pcmk__action_runnable);
509  	                }
510  	            }
511  	        }
512  	        free(rc_stopped);
513  	        return changed;
514  	    }
515  	
516  	    pcmk__rsc_debug(rsc, "Assigning %s to %s", rsc->id, pcmk__node_name(node));
517  	    rsc->priv->assigned_node = pe__copy_node(node);
518  	
519  	    add_assigned_resource(node, rsc);
520  	    node->priv->num_resources++;
521  	    node->assign->count++;
522  	    pcmk__consume_node_capacity(node->priv->utilization, rsc);
523  	
524  	    if (pcmk__is_set(scheduler->flags, pcmk__sched_show_utilization)) {
525  	        pcmk__output_t *out = scheduler->priv->out;
526  	
527  	        out->message(out, "resource-util", rsc, node, __func__);
528  	    }
529  	    return changed;
530  	}
531  	
532  	/*!
533  	 * \internal
534  	 * \brief Remove any node assignment from a specified resource and its children
535  	 *
536  	 * If a specified resource has been assigned to a node, remove that assignment
537  	 * and mark the resource as provisional again.
538  	 *
539  	 * \param[in,out] rsc  Resource to unassign
540  	 *
541  	 * \note This function is called recursively on \p rsc and its children.
542  	 */
543  	void
544  	pcmk__unassign_resource(pcmk_resource_t *rsc)
545  	{
546  	    pcmk_node_t *old = rsc->priv->assigned_node;
547  	
548  	    if (old == NULL) {
549  	        pcmk__info("Unassigning %s", rsc->id);
550  	    } else {
551  	        pcmk__info("Unassigning %s from %s", rsc->id, pcmk__node_name(old));
552  	    }
553  	
554  	    pcmk__set_rsc_flags(rsc, pcmk__rsc_unassigned);
555  	
556  	    if (rsc->priv->children == NULL) {
557  	        if (old == NULL) {
558  	            return;
559  	        }
560  	        rsc->priv->assigned_node = NULL;
561  	
562  	        /* We're going to free the pcmk_node_t copy, but its priv member is
563  	         * shared and will remain, so update that appropriately first.
564  	         */
565  	        old->priv->assigned_resources =
566  	            g_list_remove(old->priv->assigned_resources, rsc);
567  	        old->priv->num_resources--;
568  	        pcmk__release_node_capacity(old->priv->utilization, rsc);
569  	        pcmk__free_node_copy(old);
570  	        return;
571  	    }
572  	
573  	    for (GList *iter = rsc->priv->children;
574  	         iter != NULL; iter = iter->next) {
575  	
576  	        pcmk__unassign_resource((pcmk_resource_t *) iter->data);
577  	    }
578  	}
579  	
580  	/*!
581  	 * \internal
582  	 * \brief Check whether a resource has reached its migration threshold on a node
583  	 *
584  	 * \param[in,out] rsc       Resource to check
585  	 * \param[in]     node      Node to check
586  	 * \param[out]    failed    If threshold has been reached, this will be set to
587  	 *                          resource that failed (possibly a parent of \p rsc)
588  	 *
589  	 * \return true if the migration threshold has been reached, false otherwise
590  	 */
591  	bool
592  	pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node,
593  	                        pcmk_resource_t **failed)
594  	{
595  	    int fail_count, remaining_tries;
596  	    pcmk_resource_t *rsc_to_ban = rsc;
597  	
598  	    // Migration threshold of 0 means never force away
599  	    if (rsc->priv->ban_after_failures == 0) {
600  	        return false;
601  	    }
602  	
603  	    // If we're ignoring failures, also ignore the migration threshold
604  	    if (pcmk__is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
605  	        return false;
606  	    }
607  	
608  	    // If there are no failures, there's no need to force away
609  	    fail_count = pe_get_failcount(node, rsc, NULL,
610  	                                  pcmk__fc_effective|pcmk__fc_launched, NULL);
611  	    if (fail_count <= 0) {
612  	        return false;
613  	    }
614  	
615  	    // If failed resource is anonymous clone instance, we'll force clone away
616  	    if (!pcmk__is_set(rsc->flags, pcmk__rsc_unique)) {
617  	        rsc_to_ban = uber_parent(rsc);
618  	    }
619  	
620  	    // How many more times recovery will be tried on this node
621  	    remaining_tries = rsc->priv->ban_after_failures - fail_count;
622  	
623  	    if (remaining_tries <= 0) {
624  	        pcmk__sched_warn(rsc->priv->scheduler,
625  	                         "%s cannot run on %s due to reaching migration "
626  	                         "threshold (clean up resource to allow again) "
627  	                         QB_XS " failures=%d "
628  	                         PCMK_META_MIGRATION_THRESHOLD "=%d",
629  	                         rsc_to_ban->id, pcmk__node_name(node), fail_count,
630  	                         rsc->priv->ban_after_failures);
631  	        if (failed != NULL) {
632  	            *failed = rsc_to_ban;
633  	        }
634  	        return true;
635  	    }
636  	
637  	    pcmk__info("%s can fail %d more time%s on %s before reaching migration "
638  	               "threshold (%d)",
639  	               rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries),
640  	               pcmk__node_name(node), rsc->priv->ban_after_failures);
641  	    return false;
642  	}
643  	
644  	/*!
645  	 * \internal
646  	 * \brief Get a node's score
647  	 *
648  	 * \param[in] node     Node with ID to check
649  	 * \param[in] nodes    List of nodes to look for \p node score in
650  	 *
651  	 * \return Node's score, or -INFINITY if not found
652  	 */
653  	static int
654  	get_node_score(const pcmk_node_t *node, GHashTable *nodes)
655  	{
656  	    pcmk_node_t *found_node = NULL;
657  	
658  	    if ((node != NULL) && (nodes != NULL)) {
659  	        found_node = g_hash_table_lookup(nodes, node->priv->id);
660  	    }
661  	    if (found_node == NULL) {
662  	        return -PCMK_SCORE_INFINITY;
663  	    }
664  	    return found_node->assign->score;
665  	}
666  	
667  	/*!
668  	 * \internal
669  	 * \brief Compare two resources according to which should be assigned first
670  	 *
671  	 * \param[in] a     First resource to compare
672  	 * \param[in] b     Second resource to compare
673  	 * \param[in] data  Sorted list of all nodes in cluster
674  	 *
675  	 * \return -1 if \p a should be assigned before \b, 0 if they are equal,
676  	 *         or +1 if \p a should be assigned after \b
677  	 */
678  	static gint
679  	cmp_resources(gconstpointer a, gconstpointer b, gpointer data)
680  	{
681  	    /* GLib insists that this function require gconstpointer arguments, but we
682  	     * make a small, temporary change to each argument (setting the
683  	     * pe_rsc_merging flag) during comparison
684  	     */
685  	    pcmk_resource_t *resource1 = (pcmk_resource_t *) a;
686  	    pcmk_resource_t *resource2 = (pcmk_resource_t *) b;
687  	    const GList *nodes = data;
688  	
689  	    int rc = 0;
690  	    int r1_score = -PCMK_SCORE_INFINITY;
691  	    int r2_score = -PCMK_SCORE_INFINITY;
692  	    pcmk_node_t *r1_node = NULL;
693  	    pcmk_node_t *r2_node = NULL;
694  	    GHashTable *r1_nodes = NULL;
695  	    GHashTable *r2_nodes = NULL;
696  	    const char *reason = NULL;
697  	
698  	    // Resources with highest priority should be assigned first
699  	    reason = "priority";
700  	    r1_score = resource1->priv->priority;
701  	    r2_score = resource2->priv->priority;
(1) Event path: Condition "r1_score > r2_score", taking true branch.
702  	    if (r1_score > r2_score) {
703  	        rc = -1;
(2) Event path: Jumping to label "done".
704  	        goto done;
705  	    }
706  	    if (r1_score < r2_score) {
707  	        rc = 1;
708  	        goto done;
709  	    }
710  	
711  	    // We need nodes to make any other useful comparisons
712  	    reason = "no node list";
713  	    if (nodes == NULL) {
714  	        goto done;
715  	    }
716  	
717  	    // Calculate and log node scores
718  	    resource1->priv->cmds->add_colocated_node_scores(resource1, NULL,
719  	                                                     resource1->id,
720  	                                                     &r1_nodes, NULL, 1,
721  	                                                     pcmk__coloc_select_this_with);
722  	    resource2->priv->cmds->add_colocated_node_scores(resource2, NULL,
723  	                                                     resource2->id,
724  	                                                     &r2_nodes, NULL, 1,
725  	                                                     pcmk__coloc_select_this_with);
726  	    pe__show_node_scores(true, NULL, resource1->id, r1_nodes,
727  	                         resource1->priv->scheduler);
728  	    pe__show_node_scores(true, NULL, resource2->id, r2_nodes,
729  	                         resource2->priv->scheduler);
730  	
731  	    // The resource with highest score on its current node goes first
732  	    reason = "current location";
733  	    if (resource1->priv->active_nodes != NULL) {
734  	        r1_node = pcmk__current_node(resource1);
735  	    }
736  	    if (resource2->priv->active_nodes != NULL) {
737  	        r2_node = pcmk__current_node(resource2);
738  	    }
739  	    r1_score = get_node_score(r1_node, r1_nodes);
740  	    r2_score = get_node_score(r2_node, r2_nodes);
741  	    if (r1_score > r2_score) {
742  	        rc = -1;
743  	        goto done;
744  	    }
745  	    if (r1_score < r2_score) {
746  	        rc = 1;
747  	        goto done;
748  	    }
749  	
750  	    // Otherwise a higher score on any node will do
751  	    reason = "score";
752  	    for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
753  	        const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
754  	
755  	        r1_score = get_node_score(node, r1_nodes);
756  	        r2_score = get_node_score(node, r2_nodes);
757  	        if (r1_score > r2_score) {
758  	            rc = -1;
759  	            goto done;
760  	        }
761  	        if (r1_score < r2_score) {
762  	            rc = 1;
763  	            goto done;
764  	        }
765  	    }
766  	
767  	done:
(3) Event path: Switch case default.
(4) Event path: Condition "trace_cs == NULL", taking true branch.
(5) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(6) Event path: Breaking from switch.
768  	    pcmk__trace("%s (%d)%s%s %c %s (%d)%s%s: %s",
769  	                resource1->id, r1_score,
770  	                ((r1_node == NULL)? "" : " on "),
771  	                ((r1_node == NULL)? "" : r1_node->priv->id),
772  	                ((rc < 0)? '>' : ((rc > 0)? '<' : '=')),
773  	                resource2->id, r2_score,
774  	                ((r2_node == NULL)? "" : " on "),
775  	                ((r2_node == NULL)? "" : r2_node->priv->id),
776  	                reason);
777  	
CID (unavailable; MK=d3a8f7b05320d43983ee8df22f460c27) (#1 of 2): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(7) Event assign_union_field: The union field "in" of "_pp" is written.
(8) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
778  	    g_clear_pointer(&r1_nodes, g_hash_table_destroy);
779  	    g_clear_pointer(&r2_nodes, g_hash_table_destroy);
780  	    return rc;
781  	}
782  	
783  	/*!
784  	 * \internal
785  	 * \brief Sort resources in the order they should be assigned to nodes
786  	 *
787  	 * \param[in,out] scheduler  Scheduler data
788  	 */
789  	void
790  	pcmk__sort_resources(pcmk_scheduler_t *scheduler)
791  	{
792  	    GList *nodes = g_list_copy(scheduler->nodes);
793  	
794  	    nodes = pcmk__sort_nodes(nodes, NULL);
795  	    scheduler->priv->resources =
796  	        g_list_sort_with_data(scheduler->priv->resources, cmp_resources, nodes);
797  	    g_list_free(nodes);
798  	}
799