1    	/*
2    	 * Copyright 2004-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 Lesser General Public License
7    	 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdbool.h>
13   	
14   	#include <libxml/tree.h>                    // xmlNode
15   	
16   	#include <crm/pengine/internal.h>
17   	#include <crm/common/xml.h>
18   	
19   	#include "pe_status_private.h"
20   	
21   	void populate_hash(xmlNode * nvpair_list, GHashTable * hash, const char **attrs, int attrs_length);
22   	
23   	static pcmk_node_t *active_node(const pcmk_resource_t *rsc,
24   	                                unsigned int *count_all,
25   	                                unsigned int *count_clean);
26   	
27   	static pcmk__rsc_methods_t resource_class_functions[] = {
28   	    {
29   	         native_unpack,
30   	         native_find_rsc,
31   	         native_active,
32   	         native_resource_state,
33   	         native_location,
34   	         native_free,
35   	         pe__count_common,
36   	         pe__native_is_filtered,
37   	         active_node,
38   	         pe__primitive_max_per_node,
39   	    },
40   	    {
41   	         group_unpack,
42   	         native_find_rsc,
43   	         group_active,
44   	         group_resource_state,
45   	         native_location,
46   	         group_free,
47   	         pe__count_common,
48   	         pe__group_is_filtered,
49   	         active_node,
50   	         pe__group_max_per_node,
51   	    },
52   	    {
53   	         clone_unpack,
54   	         native_find_rsc,
55   	         clone_active,
56   	         clone_resource_state,
57   	         native_location,
58   	         clone_free,
59   	         pe__count_common,
60   	         pe__clone_is_filtered,
61   	         active_node,
62   	         pe__clone_max_per_node,
63   	    },
64   	    {
65   	         pe__unpack_bundle,
66   	         native_find_rsc,
67   	         pe__bundle_active,
68   	         pe__bundle_resource_state,
69   	         native_location,
70   	         pe__free_bundle,
71   	         pe__count_bundle,
72   	         pe__bundle_is_filtered,
73   	         pe__bundle_active_node,
74   	         pe__bundle_max_per_node,
75   	    }
76   	};
77   	
78   	static enum pcmk__rsc_variant
79   	get_resource_type(const char *name)
80   	{
81   	    if (pcmk__str_eq(name, PCMK_XE_PRIMITIVE, pcmk__str_casei)) {
82   	        return pcmk__rsc_variant_primitive;
83   	
84   	    } else if (pcmk__str_eq(name, PCMK_XE_GROUP, pcmk__str_casei)) {
85   	        return pcmk__rsc_variant_group;
86   	
87   	    } else if (pcmk__str_eq(name, PCMK_XE_CLONE, pcmk__str_casei)) {
88   	        return pcmk__rsc_variant_clone;
89   	
90   	    } else if (pcmk__str_eq(name, PCMK_XE_BUNDLE, pcmk__str_casei)) {
91   	        return pcmk__rsc_variant_bundle;
92   	    }
93   	
94   	    return pcmk__rsc_variant_unknown;
95   	}
96   	
97   	/*!
98   	 * \internal
99   	 * \brief Insert a meta-attribute if not already present
100  	 *
101  	 * \param[in]     key    Meta-attribute name
102  	 * \param[in]     value  Meta-attribute value to add if not already present
103  	 * \param[in,out] table  Meta-attribute hash table to insert into
104  	 *
105  	 * \note This is like pcmk__insert_meta() except it won't overwrite existing
106  	 *       values.
107  	 */
108  	static void
109  	dup_attr(gpointer key, gpointer value, gpointer user_data)
110  	{
111  	    GHashTable *table = user_data;
112  	
113  	    CRM_CHECK((key != NULL) && (table != NULL), return);
114  	
115  	    if (value == NULL) {
116  	        return;
117  	    }
118  	
119  	    if (pcmk__str_eq((const char *) value, "#default", pcmk__str_casei)) {
120  	        // @COMPAT Deprecated since 2.1.8
121  	        pcmk__config_warn("Support for setting meta-attributes (such as %s) to "
122  	                          "the explicit value '#default' is deprecated and "
123  	                          "will be removed in a future release",
124  	                          (const char *) key);
125  	        return;
126  	    }
127  	
128  	    if (!g_hash_table_contains(table, key)) {
129  	        pcmk__insert_dup(table, (const char *) key, (const char *) value);
130  	    }
131  	}
132  	
133  	static void
134  	expand_parents_fixed_nvpairs(const pcmk_resource_t *rsc,
135  	                             const pcmk_rule_input_t *rule_input,
136  	                             GHashTable *meta_hash, pcmk_scheduler_t *scheduler)
137  	{
138  	    GHashTable *parent_orig_meta = pcmk__strkey_table(free, free);
139  	
140  	    /* Search all parent resources, get the fixed value of
141  	     * PCMK_XE_META_ATTRIBUTES set only in the original xml, and stack it in the
142  	     * hash table. The fixed value of the lower parent resource takes precedence
143  	     * and is not overwritten.
144  	     */
145  	    for (const pcmk_resource_t *parent = rsc->priv->parent; parent != NULL;
146  	         parent = parent->priv->parent) {
147  	
148  	        /* A hash table for comparison is generated, including the id-ref. */
149  	        pe__unpack_dataset_nvpairs(parent->priv->xml, PCMK_XE_META_ATTRIBUTES,
150  	                                   rule_input, parent_orig_meta, NULL,
151  	                                   scheduler);
152  	    }
153  	
154  	    // This will not overwrite any values already existing for child
155  	    g_hash_table_foreach(parent_orig_meta, dup_attr, meta_hash);
156  	    g_hash_table_destroy(parent_orig_meta);
157  	}
158  	
159  	/*
160  	 * \brief Get fully evaluated resource meta-attributes
161  	 *
162  	 * \param[in,out] meta_hash  Where to store evaluated meta-attributes
163  	 * \param[in]     rsc        Resource to get meta-attributes for
164  	 * \param[in]     node       Ignored
165  	 * \param[in,out] scheduler  Scheduler data
166  	 */
167  	void
168  	get_meta_attributes(GHashTable *meta_hash, const pcmk_resource_t *rsc,
169  	                    pcmk_node_t *node, pcmk_scheduler_t *scheduler)
170  	{
171  	    pcmk_rule_input_t rule_input = { NULL, };
172  	
173  	    CRM_CHECK((meta_hash != NULL) && (rsc != NULL) && (scheduler != NULL),
174  	              return);
175  	
176  	    for (const xmlAttr *attr = pcmk__xe_first_attr(rsc->priv->xml);
177  	         attr != NULL; attr = attr->next) {
178  	
179  	        if (attr->children == NULL) {
180  	            continue;
181  	        }
182  	
183  	        dup_attr((void *) attr->name, (void *) attr->children->content,
184  	                 meta_hash);
185  	    }
186  	
187  	    rule_input.now = scheduler->priv->now;
188  	    rule_input.rsc_standard = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
189  	    rule_input.rsc_provider = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
190  	    rule_input.rsc_agent = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
191  	
192  	    pe__unpack_dataset_nvpairs(rsc->priv->xml, PCMK_XE_META_ATTRIBUTES,
193  	                               &rule_input, meta_hash, NULL, scheduler);
194  	
195  	    /* Set the PCMK_XE_META_ATTRIBUTES explicitly set in the parent resource to
196  	     * the hash table of the child resource. If it is already explicitly set as
197  	     * a child, it will not be overwritten.
198  	     */
199  	    if (rsc->priv->parent != NULL) {
200  	        expand_parents_fixed_nvpairs(rsc, &rule_input, meta_hash, scheduler);
201  	    }
202  	
203  	    /* check the defaults */
204  	    pe__unpack_dataset_nvpairs(scheduler->priv->rsc_defaults,
205  	                               PCMK_XE_META_ATTRIBUTES, &rule_input, meta_hash,
206  	                               NULL, scheduler);
207  	
208  	    /* If there is PCMK_XE_META_ATTRIBUTES that the parent resource has not
209  	     * explicitly set, set a value that is not set from PCMK_XE_RSC_DEFAULTS
210  	     * either. The values already set up to this point will not be overwritten.
211  	     */
212  	    if (rsc->priv->parent != NULL) {
213  	        g_hash_table_foreach(rsc->priv->parent->priv->meta, dup_attr,
214  	                             meta_hash);
215  	    }
216  	}
217  	
218  	/*!
219  	 * \brief Get final values of a resource's instance attributes
220  	 *
221  	 * \param[in,out] instance_attrs  Where to store the instance attributes
222  	 * \param[in]     rsc             Resource to get instance attributes for
223  	 * \param[in]     node            If not NULL, evaluate rules for this node
224  	 * \param[in,out] scheduler       Scheduler data
225  	 */
226  	void
227  	get_rsc_attributes(GHashTable *instance_attrs, const pcmk_resource_t *rsc,
228  	                   const pcmk_node_t *node, pcmk_scheduler_t *scheduler)
229  	{
230  	    pcmk_rule_input_t rule_input = {
231  	        .now = NULL,
232  	    };
233  	
234  	    CRM_CHECK((instance_attrs != NULL) && (rsc != NULL) && (scheduler != NULL),
235  	              return);
236  	
237  	    rule_input.now = scheduler->priv->now;
238  	    if (node != NULL) {
239  	        rule_input.node_attrs = node->priv->attrs;
240  	    }
241  	
242  	    // Evaluate resource's own values, then its ancestors' values
243  	    pe__unpack_dataset_nvpairs(rsc->priv->xml, PCMK_XE_INSTANCE_ATTRIBUTES,
244  	                               &rule_input, instance_attrs, NULL, scheduler);
245  	    if (rsc->priv->parent != NULL) {
246  	        get_rsc_attributes(instance_attrs, rsc->priv->parent, node, scheduler);
247  	    }
248  	}
249  	
250  	static char *
251  	template_op_key(const xmlNode *op)
252  	{
253  	    const char *name = pcmk__xe_get(op, PCMK_XA_NAME);
254  	    const char *role = pcmk__xe_get(op, PCMK_XA_ROLE);
255  	    char *key = NULL;
256  	
257  	    if ((role == NULL)
258  	        || pcmk__strcase_any_of(role, PCMK_ROLE_STARTED, PCMK_ROLE_UNPROMOTED,
259  	                                PCMK__ROLE_UNPROMOTED_LEGACY, NULL)) {
260  	        role = PCMK__ROLE_UNKNOWN;
261  	    }
262  	
263  	    key = pcmk__assert_asprintf("%s-%s", name, role);
264  	    return key;
265  	}
266  	
267  	static int
268  	unpack_template(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
269  	{
270  	    xmlNode *cib_resources = NULL;
271  	    xmlNode *template = NULL;
272  	    xmlNode *rsc_ops = NULL;
273  	    xmlNode *template_ops = NULL;
274  	    GHashTable *rsc_ops_hash = NULL;
275  	    const char *template_ref = NULL;
276  	    const char *id = NULL;
277  	    int rc = pcmk_rc_ok;
278  	
279  	    template_ref = pcmk__xe_get(rsc->priv->orig_xml, PCMK_XA_TEMPLATE);
280  	    if (template_ref == NULL) {
281  	        goto done;
282  	    }
283  	
284  	    id = pcmk__xe_id(rsc->priv->orig_xml);
285  	
286  	    if (pcmk__str_eq(template_ref, id, pcmk__str_none)) {
287  	        pcmk__config_err("The resource object '%s' should not reference itself",
288  	                         id);
289  	        rc = pcmk_rc_unpack_error;
290  	        goto done;
291  	    }
292  	
293  	    cib_resources = pcmk_find_cib_element(scheduler->input, PCMK_XE_RESOURCES);
294  	    if (cib_resources == NULL) {
295  	        pcmk__config_err("No " PCMK_XE_RESOURCES " section");
296  	        rc = pcmk_rc_unpack_error;
297  	        goto done;
298  	    }
299  	
300  	    template = pcmk__xe_first_child(cib_resources, PCMK_XE_TEMPLATE,
301  	                                    PCMK_XA_ID, template_ref);
302  	    if (template == NULL) {
303  	        pcmk__config_err("No template named '%s'", template_ref);
304  	        rc = pcmk_rc_unpack_error;
305  	        goto done;
306  	    }
307  	
308  	    rsc->priv->xml = pcmk__xml_copy(NULL, template);
309  	    xmlNodeSetName(rsc->priv->xml, rsc->priv->orig_xml->name);
310  	    pcmk__xe_set(rsc->priv->xml, PCMK_XA_ID, id);
311  	    pcmk__xe_set(rsc->priv->xml, PCMK__META_CLONE,
312  	                 pcmk__xe_get(rsc->priv->orig_xml, PCMK__META_CLONE));
313  	
314  	    template_ops = pcmk__xe_first_child(rsc->priv->xml, PCMK_XE_OPERATIONS,
315  	                                        NULL, NULL);
316  	
317  	    for (xmlNode *child_xml = pcmk__xe_first_child(rsc->priv->orig_xml, NULL,
318  	                                                   NULL, NULL);
319  	         child_xml != NULL; child_xml = pcmk__xe_next(child_xml, NULL)) {
320  	
321  	        xmlNode *new_child = pcmk__xml_copy(rsc->priv->xml, child_xml);
322  	
323  	        if ((rsc_ops == NULL) && pcmk__xe_is(new_child, PCMK_XE_OPERATIONS)) {
324  	            /* Multiple PCMK_XE_OPERATIONS children are not possible with schema
325  	             * validation enabled. However, in pe__unpack_resource(), we use
326  	             * only the first in case of multiple. Do the same here.
327  	             */
328  	            rsc_ops = new_child;
329  	        }
330  	    }
331  	
332  	    if ((template_ops == NULL) || (rsc_ops == NULL)) {
333  	        goto done;
334  	    }
335  	
336  	    // Operations configured in the resource override those in the template
337  	    rsc_ops_hash = pcmk__strkey_table(free, NULL);
338  	
339  	    for (const xmlNode *op = pcmk__xe_first_child(rsc_ops, NULL, NULL, NULL);
340  	         op != NULL; op = pcmk__xe_next(op, NULL)) {
341  	
342  	        g_hash_table_insert(rsc_ops_hash, template_op_key(op), (void *) op);
343  	    }
344  	
345  	    for (xmlNode *op = pcmk__xe_first_child(template_ops, NULL, NULL, NULL);
346  	         op != NULL; op = pcmk__xe_next(op, NULL)) {
347  	
348  	        char *key = template_op_key(op);
349  	
350  	        if (g_hash_table_lookup(rsc_ops_hash, key) == NULL) {
351  	            pcmk__xml_copy(rsc_ops, op);
352  	        }
353  	
354  	        free(key);
355  	    }
356  	
357  	    // template_ops has been replaced by rsc_ops
358  	    pcmk__xml_free(template_ops);
359  	
360  	done:
361  	    if (rc == pcmk_rc_ok) {
362  	        pcmk__log_xml_trace(rsc->priv->xml, "[expanded XML]");
363  	    }
364  	
365  	    g_clear_pointer(&rsc_ops_hash, g_hash_table_destroy);
366  	    return rc;
367  	}
368  	
369  	static bool
370  	add_template_rsc(const xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
371  	{
372  	    const char *template_ref = NULL;
373  	    const char *id = NULL;
374  	
375  	    if (xml_obj == NULL) {
376  	        pcmk__config_err("No resource object for processing resource list "
377  	                         "of template");
378  	        return false;
379  	    }
380  	
381  	    template_ref = pcmk__xe_get(xml_obj, PCMK_XA_TEMPLATE);
382  	    if (template_ref == NULL) {
383  	        return true;
384  	    }
385  	
386  	    id = pcmk__xe_id(xml_obj);
387  	    if (id == NULL) {
388  	        pcmk__config_err("'%s' object must have a id", xml_obj->name);
389  	        return false;
390  	    }
391  	
392  	    if (pcmk__str_eq(template_ref, id, pcmk__str_none)) {
393  	        pcmk__config_err("The resource object '%s' should not reference itself",
394  	                         id);
395  	        return false;
396  	    }
397  	
398  	    pcmk__add_idref(scheduler->priv->templates, template_ref, id);
399  	    return true;
400  	}
401  	
402  	/*!
403  	 * \internal
404  	 * \brief Check whether a clone or instance being unpacked is globally unique
405  	 *
406  	 * \param[in] rsc  Clone or clone instance to check
407  	 *
408  	 * \return \c true if \p rsc is globally unique according to its
409  	 *         meta-attributes, otherwise \c false
410  	 */
411  	static bool
412  	detect_unique(const pcmk_resource_t *rsc)
413  	{
414  	    const char *value = g_hash_table_lookup(rsc->priv->meta,
415  	                                            PCMK_META_GLOBALLY_UNIQUE);
416  	
417  	    if (value == NULL) { // Default to true if clone-node-max > 1
418  	        value = g_hash_table_lookup(rsc->priv->meta,
419  	                                    PCMK_META_CLONE_NODE_MAX);
420  	        if (value != NULL) {
421  	            int node_max = 1;
422  	
423  	            if ((pcmk__scan_min_int(value, &node_max, 0) == pcmk_rc_ok)
424  	                && (node_max > 1)) {
425  	                return true;
426  	            }
427  	        }
428  	        return false;
429  	    }
430  	    return pcmk__is_true(value);
431  	}
432  	
433  	/*!
434  	 * \brief Get a table of resource parameters
435  	 *
436  	 * \param[in,out] rsc        Resource to query
437  	 * \param[in]     node       Node for evaluating rules (NULL for defaults)
438  	 * \param[in,out] scheduler  Scheduler data
439  	 *
440  	 * \return Hash table containing resource parameter names and values
441  	 *         (or NULL if \p rsc or \p scheduler is NULL)
442  	 * \note The returned table will be destroyed when the resource is freed, so
443  	 *       callers should not destroy it.
444  	 */
445  	GHashTable *
446  	pe_rsc_params(pcmk_resource_t *rsc, const pcmk_node_t *node,
447  	              pcmk_scheduler_t *scheduler)
448  	{
449  	    GHashTable *params_on_node = NULL;
450  	
451  	    /* A NULL node is used to request the resource's default parameters
452  	     * (not evaluated for node), but we always want something non-NULL
453  	     * as a hash table key.
454  	     */
455  	    const char *node_name = "";
456  	
457  	    // Sanity check
458  	    if ((rsc == NULL) || (scheduler == NULL)) {
459  	        return NULL;
460  	    }
461  	    if ((node != NULL) && (node->priv->name != NULL)) {
462  	        node_name = node->priv->name;
463  	    }
464  	
465  	    // Find the parameter table for given node
466  	    if (rsc->priv->parameter_cache == NULL) {
467  	        rsc->priv->parameter_cache =
468  	            pcmk__strikey_table(free, (GDestroyNotify) g_hash_table_destroy);
469  	
470  	    } else {
471  	        params_on_node = g_hash_table_lookup(rsc->priv->parameter_cache,
472  	                                             node_name);
473  	    }
474  	
475  	    // If none exists yet, create one with parameters evaluated for node
476  	    if (params_on_node == NULL) {
477  	        params_on_node = pcmk__strkey_table(free, free);
478  	        get_rsc_attributes(params_on_node, rsc, node, scheduler);
479  	        g_hash_table_insert(rsc->priv->parameter_cache, strdup(node_name),
480  	                            params_on_node);
481  	    }
482  	    return params_on_node;
483  	}
484  	
485  	/*!
486  	 * \internal
487  	 * \brief Unpack a resource's \c PCMK_META_REQUIRES meta-attribute
488  	 *
489  	 * \param[in,out] rsc         Resource being unpacked
490  	 * \param[in]     value       Value of \c PCMK_META_REQUIRES meta-attribute
491  	 * \param[in]     is_default  Whether \p value was selected by default
492  	 */
493  	static void
494  	unpack_requires(pcmk_resource_t *rsc, const char *value, bool is_default)
495  	{
496  	    const pcmk_scheduler_t *scheduler = rsc->priv->scheduler;
497  	
498  	    if (pcmk__str_eq(value, PCMK_VALUE_NOTHING, pcmk__str_casei)) {
499  	
500  	    } else if (pcmk__str_eq(value, PCMK_VALUE_QUORUM, pcmk__str_casei)) {
501  	        pcmk__set_rsc_flags(rsc, pcmk__rsc_needs_quorum);
502  	
503  	    } else if (pcmk__str_eq(value, PCMK_VALUE_FENCING, pcmk__str_casei)) {
504  	        pcmk__set_rsc_flags(rsc, pcmk__rsc_needs_fencing);
505  	        if (!pcmk__is_set(scheduler->flags, pcmk__sched_fencing_enabled)) {
506  	            pcmk__config_warn("%s requires fencing but fencing is disabled",
507  	                              rsc->id);
508  	        }
509  	
510  	    } else if (pcmk__str_eq(value, PCMK_VALUE_UNFENCING, pcmk__str_casei)) {
511  	        if (pcmk__is_set(rsc->flags, pcmk__rsc_fence_device)) {
512  	            pcmk__config_warn("Resetting \"" PCMK_META_REQUIRES "\" for %s "
513  	                              "to \"" PCMK_VALUE_QUORUM "\" because fencing "
514  	                              "devices cannot require unfencing", rsc->id);
515  	            unpack_requires(rsc, PCMK_VALUE_QUORUM, true);
516  	            return;
517  	        }
518  	
519  	        if (!pcmk__is_set(scheduler->flags, pcmk__sched_fencing_enabled)) {
520  	            pcmk__config_warn("Resetting \"" PCMK_META_REQUIRES "\" for %s "
521  	                              "to \"" PCMK_VALUE_QUORUM "\" because fencing is "
522  	                              "disabled", rsc->id);
523  	            unpack_requires(rsc, PCMK_VALUE_QUORUM, true);
524  	            return;
525  	        }
526  	
527  	        pcmk__set_rsc_flags(rsc,
528  	                            pcmk__rsc_needs_fencing|pcmk__rsc_needs_unfencing);
529  	
530  	    } else {
531  	        const char *orig_value = value;
532  	
533  	        if (pcmk__is_set(rsc->flags, pcmk__rsc_fence_device)) {
534  	            value = PCMK_VALUE_QUORUM;
535  	
536  	        } else if (pcmk__is_primitive(rsc)
537  	                   && xml_contains_remote_node(rsc->priv->xml)) {
538  	            value = PCMK_VALUE_QUORUM;
539  	
540  	        } else if (pcmk__is_set(scheduler->flags,
541  	                                pcmk__sched_enable_unfencing)) {
542  	            value = PCMK_VALUE_UNFENCING;
543  	
544  	        } else if (pcmk__is_set(scheduler->flags,
545  	                                pcmk__sched_fencing_enabled)) {
546  	            value = PCMK_VALUE_FENCING;
547  	
548  	        } else if (scheduler->no_quorum_policy == pcmk_no_quorum_ignore) {
549  	            value = PCMK_VALUE_NOTHING;
550  	
551  	        } else {
552  	            value = PCMK_VALUE_QUORUM;
553  	        }
554  	
555  	        if (orig_value != NULL) {
556  	            pcmk__config_err("Resetting '" PCMK_META_REQUIRES "' for %s "
557  	                             "to '%s' because '%s' is not valid",
558  	                              rsc->id, value, orig_value);
559  	        }
560  	        unpack_requires(rsc, value, true);
561  	        return;
562  	    }
563  	
564  	    pcmk__rsc_trace(rsc, "\tRequired to start: %s%s", value,
565  	                    (is_default? " (default)" : ""));
566  	}
567  	
568  	/*!
569  	 * \internal
570  	 * \brief Parse resource priority from meta-attribute
571  	 *
572  	 * \param[in,out] rsc  Resource being unpacked
573  	 */
574  	static void
575  	unpack_priority(pcmk_resource_t *rsc)
576  	{
577  	    const char *value = g_hash_table_lookup(rsc->priv->meta,
578  	                                            PCMK_META_PRIORITY);
579  	    int rc = pcmk_parse_score(value, &(rsc->priv->priority), 0);
580  	
581  	    if (rc != pcmk_rc_ok) {
582  	        pcmk__config_warn("Using default (0) for resource %s "
583  	                          PCMK_META_PRIORITY
584  	                          " because '%s' is not a valid value: %s",
585  	                          rsc->id, value, pcmk_rc_str(rc));
586  	    }
587  	}
588  	
589  	/*!
590  	 * \internal
591  	 * \brief Parse resource stickiness from meta-attribute
592  	 *
593  	 * \param[in,out] rsc  Resource being unpacked
594  	 */
595  	static void
596  	unpack_stickiness(pcmk_resource_t *rsc)
597  	{
598  	    const char *value = g_hash_table_lookup(rsc->priv->meta,
599  	                                            PCMK_META_RESOURCE_STICKINESS);
600  	
601  	    if (pcmk__str_eq(value, PCMK_VALUE_DEFAULT, pcmk__str_casei)) {
602  	        // @COMPAT Deprecated since 2.1.8
603  	        pcmk__config_warn("Support for setting "
604  	                          PCMK_META_RESOURCE_STICKINESS
605  	                          " to the explicit value '" PCMK_VALUE_DEFAULT
606  	                          "' is deprecated and will be removed in a "
607  	                          "future release (just leave it unset)");
608  	    } else {
609  	        int rc = pcmk_parse_score(value, &(rsc->priv->stickiness), 0);
610  	
611  	        if (rc != pcmk_rc_ok) {
612  	            pcmk__config_warn("Using default (0) for resource %s "
613  	                              PCMK_META_RESOURCE_STICKINESS
614  	                              " because '%s' is not a valid value: %s",
615  	                              rsc->id, value, pcmk_rc_str(rc));
616  	        }
617  	    }
618  	}
619  	
620  	/*!
621  	 * \internal
622  	 * \brief Parse resource migration threshold from meta-attribute
623  	 *
624  	 * \param[in,out] rsc  Resource being unpacked
625  	 */
626  	static void
627  	unpack_migration_threshold(pcmk_resource_t *rsc)
628  	{
629  	    const char *value = g_hash_table_lookup(rsc->priv->meta,
630  	                                            PCMK_META_MIGRATION_THRESHOLD);
631  	
632  	    if (pcmk__str_eq(value, PCMK_VALUE_DEFAULT, pcmk__str_casei)) {
633  	        // @COMPAT Deprecated since 2.1.8
634  	        pcmk__config_warn("Support for setting "
635  	                          PCMK_META_MIGRATION_THRESHOLD
636  	                          " to the explicit value '" PCMK_VALUE_DEFAULT
637  	                          "' is deprecated and will be removed in a "
638  	                          "future release (just leave it unset)");
639  	        rsc->priv->ban_after_failures = PCMK_SCORE_INFINITY;
640  	    } else {
641  	        int rc = pcmk_parse_score(value, &(rsc->priv->ban_after_failures),
642  	                                  PCMK_SCORE_INFINITY);
643  	
644  	        if ((rc != pcmk_rc_ok) || (rsc->priv->ban_after_failures < 0)) {
645  	            pcmk__config_warn("Using default (" PCMK_VALUE_INFINITY
646  	                              ") for resource %s meta-attribute "
647  	                              PCMK_META_MIGRATION_THRESHOLD
648  	                              " because '%s' is not a valid value: %s",
649  	                              rsc->id, value, pcmk_rc_str(rc));
650  	            rsc->priv->ban_after_failures = PCMK_SCORE_INFINITY;
651  	        }
652  	    }
653  	}
654  	
655  	/*!
656  	 * \internal
657  	 * \brief Unpack configuration XML for a given resource
658  	 *
659  	 * Unpack the XML object containing a resource's configuration into a new
660  	 * \c pcmk_resource_t object.
661  	 *
662  	 * \param[in]     xml_obj    XML node containing the resource's configuration
663  	 * \param[out]    rsc        Where to store the unpacked resource information
664  	 * \param[in]     parent     Resource's parent, if any
665  	 * \param[in,out] scheduler  Scheduler data
666  	 *
667  	 * \return Standard Pacemaker return code
668  	 * \note If pcmk_rc_ok is returned, \p *rsc is guaranteed to be non-NULL, and
669  	 *       the caller is responsible for freeing it using its variant-specific
670  	 *       free() method. Otherwise, \p *rsc is guaranteed to be NULL.
671  	 */
672  	int
673  	pe__unpack_resource(xmlNode *xml_obj, pcmk_resource_t **rsc,
674  	                    pcmk_resource_t *parent, pcmk_scheduler_t *scheduler)
675  	{
676  	    int rc = pcmk_rc_ok;
677  	    xmlNode *ops = NULL;
678  	    const char *value = NULL;
679  	    const char *id = NULL;
680  	    bool guest_node = false;
681  	    bool remote_node = false;
682  	    pcmk__resource_private_t *rsc_private = NULL;
683  	
684  	    pcmk_rule_input_t rule_input = {
685  	        .now = NULL,
686  	    };
687  	
688  	    pcmk__assert((xml_obj != NULL) && (rsc != NULL) && (scheduler != NULL));
689  	
690  	    rule_input.now = scheduler->priv->now;
691  	
692  	    pcmk__log_xml_trace(xml_obj, "[raw XML]");
693  	
694  	    id = pcmk__xe_get(xml_obj, PCMK_XA_ID);
695  	    if (id == NULL) {
696  	        pcmk__config_err("Ignoring <%s> configuration without " PCMK_XA_ID,
697  	                         xml_obj->name);
698  	        rc = pcmk_rc_unpack_error;
699  	        goto done;
700  	    }
701  	
702  	    *rsc = pcmk__assert_alloc(1, sizeof(pcmk_resource_t));
703  	    (*rsc)->priv = pcmk__assert_alloc(1, sizeof(pcmk__resource_private_t));
704  	
705  	    rsc_private = (*rsc)->priv;
706  	    rsc_private->scheduler = scheduler;
707  	    rsc_private->orig_xml = pcmk__xml_copy(NULL, xml_obj);
708  	    rsc_private->xml = rsc_private->orig_xml;
709  	
710  	    rc = unpack_template(*rsc, scheduler);
711  	    if (rc != pcmk_rc_ok) {
712  	        goto done;
713  	    }
714  	
715  	    /* Do not use xml_obj from here on, use (*rsc)->xml in case templates are involved */
716  	
717  	    rsc_private->parent = parent;
718  	
719  	    ops = pcmk__xe_first_child(rsc_private->xml, PCMK_XE_OPERATIONS, NULL,
720  	                               NULL);
721  	    rsc_private->ops_xml = pcmk__xe_resolve_idref(ops, scheduler->input->doc);
722  	
723  	    rsc_private->variant = get_resource_type((const char *)
724  	                                             rsc_private->xml->name);
725  	    if (rsc_private->variant == pcmk__rsc_variant_unknown) {
726  	        pcmk__config_err("Ignoring resource '%s' of unknown type '%s'",
727  	                         id, rsc_private->xml->name);
728  	        rc = pcmk_rc_unpack_error;
729  	        goto done;
730  	    }
731  	
732  	    rsc_private->meta = pcmk__strkey_table(free, free);
733  	    rsc_private->utilization = pcmk__strkey_table(free, free);
734  	    rsc_private->probed_nodes = pcmk__strkey_table(NULL, pcmk__free_node_copy);
735  	    rsc_private->allowed_nodes = pcmk__strkey_table(NULL, pcmk__free_node_copy);
736  	
737  	    value = pcmk__xe_get(rsc_private->xml, PCMK__META_CLONE);
738  	    if (value != NULL) {
739  	        (*rsc)->id = pcmk__assert_asprintf("%s:%s", id, value);
740  	        pcmk__insert_meta(rsc_private, PCMK__META_CLONE, value);
741  	
742  	    } else {
743  	        (*rsc)->id = pcmk__str_copy(id);
744  	    }
745  	
746  	    rsc_private->fns = &resource_class_functions[rsc_private->variant];
747  	
748  	    get_meta_attributes(rsc_private->meta, *rsc, NULL, scheduler);
749  	
750  	    (*rsc)->flags = 0;
751  	    pcmk__set_rsc_flags(*rsc, pcmk__rsc_unassigned);
752  	
753  	    if (!pcmk__is_set(scheduler->flags, pcmk__sched_in_maintenance)) {
754  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_managed);
755  	    }
756  	
757  	    rsc_private->orig_role = pcmk_role_stopped;
758  	    rsc_private->next_role = pcmk_role_unknown;
759  	
760  	    unpack_priority(*rsc);
761  	
762  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_CRITICAL);
763  	    if ((value == NULL) || pcmk__is_true(value)) {
764  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_critical);
765  	    }
766  	
767  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_NOTIFY);
768  	    if (pcmk__is_true(value)) {
769  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_notify);
770  	    }
771  	
772  	    if (xml_contains_remote_node(rsc_private->xml)) {
773  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_is_remote_connection);
774  	        if (g_hash_table_lookup(rsc_private->meta, PCMK__META_CONTAINER)) {
775  	            guest_node = true;
776  	        } else {
777  	            remote_node = true;
778  	        }
779  	    }
780  	
781  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_ALLOW_MIGRATE);
782  	    if (pcmk__is_true(value)) {
783  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_migratable);
784  	    } else if ((value == NULL) && remote_node) {
785  	        /* By default, we want remote nodes to be able
786  	         * to float around the cluster without having to stop all the
787  	         * resources within the remote-node before moving. Allowing
788  	         * migration support enables this feature. If this ever causes
789  	         * problems, migration support can be explicitly turned off with
790  	         * PCMK_META_ALLOW_MIGRATE=false.
791  	         */
792  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_migratable);
793  	    }
794  	
795  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_IS_MANAGED);
796  	    if (value != NULL) {
797  	        if (pcmk__str_eq(PCMK_VALUE_DEFAULT, value, pcmk__str_casei)) {
798  	            // @COMPAT Deprecated since 2.1.8
799  	            pcmk__config_warn("Support for setting " PCMK_META_IS_MANAGED
800  	                              " to the explicit value '" PCMK_VALUE_DEFAULT
801  	                              "' is deprecated and will be removed in a "
802  	                              "future release (just leave it unset)");
803  	        } else if (pcmk__is_true(value)) {
804  	            pcmk__set_rsc_flags(*rsc, pcmk__rsc_managed);
805  	        } else {
806  	            pcmk__clear_rsc_flags(*rsc, pcmk__rsc_managed);
807  	        }
808  	    }
809  	
810  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_MAINTENANCE);
811  	    if (pcmk__is_true(value)) {
812  	        pcmk__clear_rsc_flags(*rsc, pcmk__rsc_managed);
813  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_maintenance);
814  	    }
815  	    if (pcmk__is_set(scheduler->flags, pcmk__sched_in_maintenance)) {
816  	        pcmk__clear_rsc_flags(*rsc, pcmk__rsc_managed);
817  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_maintenance);
818  	    }
819  	
820  	    if (pcmk__is_clone(pe__const_top_resource(*rsc, false))) {
821  	        if (detect_unique(*rsc)) {
822  	            pcmk__set_rsc_flags(*rsc, pcmk__rsc_unique);
823  	        }
824  	        if (pcmk__is_true(g_hash_table_lookup((*rsc)->priv->meta,
825  	                                              PCMK_META_PROMOTABLE))) {
826  	            pcmk__set_rsc_flags(*rsc, pcmk__rsc_promotable);
827  	        }
828  	    } else {
829  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_unique);
830  	    }
831  	
832  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_MULTIPLE_ACTIVE);
833  	    if (pcmk__str_eq(value, PCMK_VALUE_STOP_ONLY, pcmk__str_casei)) {
834  	        rsc_private->multiply_active_policy = pcmk__multiply_active_stop;
835  	        pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: stop only",
836  	                        (*rsc)->id);
837  	
838  	    } else if (pcmk__str_eq(value, PCMK_VALUE_BLOCK, pcmk__str_casei)) {
839  	        rsc_private->multiply_active_policy = pcmk__multiply_active_block;
840  	        pcmk__rsc_trace(*rsc, "%s multiple running resource recovery: block",
841  	                        (*rsc)->id);
842  	
843  	    } else if (pcmk__str_eq(value, PCMK_VALUE_STOP_UNEXPECTED,
844  	                            pcmk__str_casei)) {
845  	        rsc_private->multiply_active_policy = pcmk__multiply_active_unexpected;
846  	        pcmk__rsc_trace(*rsc,
847  	                        "%s multiple running resource recovery: "
848  	                        "stop unexpected instances",
849  	                        (*rsc)->id);
850  	
851  	    } else { // PCMK_VALUE_STOP_START
852  	        if (!pcmk__str_eq(value, PCMK_VALUE_STOP_START,
853  	                          pcmk__str_casei|pcmk__str_null_matches)) {
854  	            pcmk__config_warn("%s is not a valid value for "
855  	                              PCMK_META_MULTIPLE_ACTIVE
856  	                              ", using default of "
857  	                              "\"" PCMK_VALUE_STOP_START "\"",
858  	                              value);
859  	        }
860  	        rsc_private->multiply_active_policy = pcmk__multiply_active_restart;
861  	        pcmk__rsc_trace(*rsc,
862  	                        "%s multiple running resource recovery: stop/start",
863  	                        (*rsc)->id);
864  	    }
865  	
866  	    unpack_stickiness(*rsc);
867  	    unpack_migration_threshold(*rsc);
868  	
869  	    if (pcmk__str_eq(pcmk__xe_get(rsc_private->xml, PCMK_XA_CLASS),
870  	                     PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
871  	        pcmk__set_scheduler_flags(scheduler, pcmk__sched_have_fencing);
872  	        pcmk__set_rsc_flags(*rsc, pcmk__rsc_fence_device);
873  	    }
874  	
875  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_REQUIRES);
876  	    unpack_requires(*rsc, value, false);
877  	
878  	    value = g_hash_table_lookup(rsc_private->meta, PCMK_META_FAILURE_TIMEOUT);
879  	    if (value != NULL) {
880  	        pcmk_parse_interval_spec(value, &(rsc_private->failure_expiration_ms));
881  	    }
882  	
883  	    if (remote_node) {
884  	        GHashTable *params = pe_rsc_params(*rsc, NULL, scheduler);
885  	
886  	        /* Grabbing the value now means that any rules based on node attributes
887  	         * will evaluate to false, so such rules should not be used with
888  	         * PCMK_REMOTE_RA_RECONNECT_INTERVAL.
889  	         *
890  	         * @TODO Evaluate per node before using
891  	         */
892  	        value = g_hash_table_lookup(params, PCMK_REMOTE_RA_RECONNECT_INTERVAL);
893  	        if (value) {
894  	            /* reconnect delay works by setting failure_timeout and preventing the
895  	             * connection from starting until the failure is cleared. */
896  	            pcmk_parse_interval_spec(value,
897  	                                     &(rsc_private->remote_reconnect_ms));
898  	
899  	            /* We want to override any default failure_timeout in use when remote
900  	             * PCMK_REMOTE_RA_RECONNECT_INTERVAL is in use.
901  	             */
902  	            rsc_private->failure_expiration_ms =
903  	                rsc_private->remote_reconnect_ms;
904  	        }
905  	    }
906  	
907  	    get_target_role(*rsc, &(rsc_private->next_role));
908  	    pcmk__rsc_trace(*rsc, "%s desired next state: %s", (*rsc)->id,
909  	                    (rsc_private->next_role == pcmk_role_unknown)?
910  	                        "default" : pcmk_role_text(rsc_private->next_role));
911  	
912  	    if (!rsc_private->fns->unpack(*rsc)) {
913  	        rc = pcmk_rc_unpack_error;
914  	        goto done;
915  	    }
916  	
917  	    if (pcmk__is_set(scheduler->flags, pcmk__sched_symmetric_cluster)) {
918  	        // This tag must stay exactly the same because it is tested elsewhere
919  	        resource_location(*rsc, NULL, 0, "symmetric_default", scheduler);
920  	    } else if (guest_node) {
921  	        /* remote resources tied to a container resource must always be allowed
922  	         * to opt-in to the cluster. Whether the connection resource is actually
923  	         * allowed to be placed on a node is dependent on the container resource */
924  	        resource_location(*rsc, NULL, 0, "remote_connection_default",
925  	                          scheduler);
926  	    }
927  	
928  	    if (pcmk__is_set((*rsc)->flags, pcmk__rsc_notify)) {
929  	        pcmk__rsc_trace(*rsc, "%s action notification: required", (*rsc)->id);
930  	    } else {
931  	        pcmk__rsc_trace(*rsc, "%s action notification: not required",
932  	                        (*rsc)->id);
933  	    }
934  	
935  	    pe__unpack_dataset_nvpairs(rsc_private->xml, PCMK_XE_UTILIZATION,
936  	                               &rule_input, rsc_private->utilization, NULL,
937  	                               scheduler);
938  	
939  	    // First condition means resource was expanded from a template
940  	    if ((rsc_private->xml != rsc_private->orig_xml)
941  	        && !add_template_rsc(rsc_private->orig_xml, scheduler)) {
942  	
943  	        rc = pcmk_rc_unpack_error;
944  	    }
945  	
946  	done:
947  	    if (rc != pcmk_rc_ok) {
948  	        g_clear_pointer(rsc, pcmk__free_resource);
949  	    }
950  	    return rc;
951  	}
952  	
953  	gboolean
954  	is_parent(pcmk_resource_t *child, pcmk_resource_t *rsc)
955  	{
956  	    pcmk_resource_t *parent = child;
957  	
958  	    if (parent == NULL || rsc == NULL) {
959  	        return FALSE;
960  	    }
961  	    while (parent->priv->parent != NULL) {
962  	        if (parent->priv->parent == rsc) {
963  	            return TRUE;
964  	        }
965  	        parent = parent->priv->parent;
966  	    }
967  	    return FALSE;
968  	}
969  	
970  	pcmk_resource_t *
971  	uber_parent(pcmk_resource_t *rsc)
972  	{
973  	    pcmk_resource_t *parent = rsc;
974  	
975  	    if (parent == NULL) {
976  	        return NULL;
977  	    }
978  	    while ((parent->priv->parent != NULL)
979  	           && !pcmk__is_bundle(parent->priv->parent)) {
980  	        parent = parent->priv->parent;
981  	    }
982  	    return parent;
983  	}
984  	
985  	/*!
986  	 * \internal
987  	 * \brief Get the topmost parent of a resource as a const pointer
988  	 *
989  	 * \param[in] rsc             Resource to check
990  	 * \param[in] include_bundle  If true, go all the way to bundle
991  	 *
992  	 * \return \p NULL if \p rsc is NULL, \p rsc if \p rsc has no parent,
993  	 *         the bundle if \p rsc is bundled and \p include_bundle is true,
994  	 *         otherwise the topmost parent of \p rsc up to a clone
995  	 */
996  	const pcmk_resource_t *
997  	pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
998  	{
999  	    const pcmk_resource_t *parent = rsc;
1000 	
1001 	    if (parent == NULL) {
1002 	        return NULL;
1003 	    }
1004 	    while (parent->priv->parent != NULL) {
1005 	        if (!include_bundle && pcmk__is_bundle(parent->priv->parent)) {
1006 	            break;
1007 	        }
1008 	        parent = parent->priv->parent;
1009 	    }
1010 	    return parent;
1011 	}
1012 	
1013 	void
1014 	common_free(pcmk_resource_t * rsc)
1015 	{
(1) Event path: Condition "rsc == NULL", taking false branch.
1016 	    if (rsc == NULL) {
1017 	        return;
1018 	    }
1019 	
(2) Event path: Switch case default.
(3) Event path: Condition "trace_tag_cs == NULL", taking true branch.
(4) Event path: Condition "crm_is_callsite_active(trace_tag_cs, _level, converted_tag)", taking false branch.
1020 	    pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
1021 	
(5) Event path: Condition "_p", taking true branch.
1022 	    g_clear_pointer(&rsc->priv->parameter_cache, g_hash_table_destroy);
1023 	
1024 	    free(rsc->id);
1025 	
1026 	    free(rsc->priv->variant_opaque);
1027 	    free(rsc->priv->history_id);
1028 	    free(rsc->priv->pending_action);
1029 	    pcmk__free_node_copy(rsc->priv->assigned_node);
1030 	
(6) Event path: Condition "rsc->priv->orig_xml != rsc->priv->xml", taking true branch.
1031 	    if (rsc->priv->orig_xml != rsc->priv->xml) {
1032 	        pcmk__xml_free(rsc->priv->orig_xml);
1033 	    }
1034 	    pcmk__xml_free(rsc->priv->xml);
1035 	
1036 	    g_list_free(rsc->priv->actions);
1037 	    g_list_free(rsc->priv->active_nodes);
1038 	    g_list_free(rsc->priv->launched);
1039 	    g_list_free(rsc->priv->dangling_migration_sources);
1040 	    g_list_free(rsc->priv->with_this_colocations);
1041 	    g_list_free(rsc->priv->this_with_colocations);
1042 	    g_list_free(rsc->priv->location_constraints);
1043 	    g_list_free_full(rsc->priv->ticket_constraints, free);
1044 	
(7) Event path: Condition "_p", taking true branch.
1045 	    g_clear_pointer(&rsc->priv->meta, g_hash_table_destroy);
CID (unavailable; MK=ec988721f11324ef1a87e9a02af888a9) (#3 of 5): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(8) Event assign_union_field: The union field "in" of "_pp" is written.
(9) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
1046 	    g_clear_pointer(&rsc->priv->utilization, g_hash_table_destroy);
1047 	    g_clear_pointer(&rsc->priv->probed_nodes, g_hash_table_destroy);
1048 	    g_clear_pointer(&rsc->priv->allowed_nodes, g_hash_table_destroy);
1049 	
1050 	    free(rsc->priv);
1051 	    free(rsc);
1052 	}
1053 	
1054 	/*!
1055 	 * \internal
1056 	 * \brief Count a node and update most preferred to it as appropriate
1057 	 *
1058 	 * \param[in]     rsc          An active resource
1059 	 * \param[in]     node         A node that \p rsc is active on
1060 	 * \param[in,out] active       This will be set to \p node if \p node is more
1061 	 *                             preferred than the current value
1062 	 * \param[in,out] count_all    If not NULL, this will be incremented
1063 	 * \param[in,out] count_clean  If not NULL, this will be incremented if \p node
1064 	 *                             is online and clean
1065 	 *
1066 	 * \return true if the count should continue, or false if sufficiently known
1067 	 */
1068 	bool
1069 	pe__count_active_node(const pcmk_resource_t *rsc, pcmk_node_t *node,
1070 	                      pcmk_node_t **active, unsigned int *count_all,
1071 	                      unsigned int *count_clean)
1072 	{
1073 	    bool keep_looking = false;
1074 	    bool is_happy = false;
1075 	
1076 	    CRM_CHECK((rsc != NULL) && (node != NULL) && (active != NULL),
1077 	              return false);
1078 	
1079 	    is_happy = node->details->online && !node->details->unclean;
1080 	
1081 	    if (count_all != NULL) {
1082 	        ++*count_all;
1083 	    }
1084 	    if ((count_clean != NULL) && is_happy) {
1085 	        ++*count_clean;
1086 	    }
1087 	    if ((count_all != NULL) || (count_clean != NULL)) {
1088 	        keep_looking = true; // We're counting, so go through entire list
1089 	    }
1090 	
1091 	    if (rsc->priv->partial_migration_source != NULL) {
1092 	        if (pcmk__same_node(node, rsc->priv->partial_migration_source)) {
1093 	            *active = node; // This is the migration source
1094 	        } else {
1095 	            keep_looking = true;
1096 	        }
1097 	    } else if (!pcmk__is_set(rsc->flags, pcmk__rsc_needs_fencing)) {
1098 	        if (is_happy && ((*active == NULL) || !(*active)->details->online
1099 	                         || (*active)->details->unclean)) {
1100 	            *active = node; // This is the first clean node
1101 	        } else {
1102 	            keep_looking = true;
1103 	        }
1104 	    }
1105 	    if (*active == NULL) {
1106 	        *active = node; // This is the first node checked
1107 	    }
1108 	    return keep_looking;
1109 	}
1110 	
1111 	// Shared implementation of pcmk__rsc_methods_t:active_node()
1112 	static pcmk_node_t *
1113 	active_node(const pcmk_resource_t *rsc, unsigned int *count_all,
1114 	            unsigned int *count_clean)
1115 	{
1116 	    pcmk_node_t *active = NULL;
1117 	
1118 	    if (count_all != NULL) {
1119 	        *count_all = 0;
1120 	    }
1121 	    if (count_clean != NULL) {
1122 	        *count_clean = 0;
1123 	    }
1124 	    if (rsc == NULL) {
1125 	        return NULL;
1126 	    }
1127 	    for (GList *iter = rsc->priv->active_nodes;
1128 	         iter != NULL; iter = iter->next) {
1129 	
1130 	        if (!pe__count_active_node(rsc, (pcmk_node_t *) iter->data, &active,
1131 	                                   count_all, count_clean)) {
1132 	            break; // Don't waste time iterating if we don't have to
1133 	        }
1134 	    }
1135 	    return active;
1136 	}
1137 	
1138 	/*!
1139 	 * \brief
1140 	 * \internal Find and count active nodes according to \c PCMK_META_REQUIRES
1141 	 *
1142 	 * \param[in]  rsc    Resource to check
1143 	 * \param[out] count  If not NULL, will be set to count of active nodes
1144 	 *
1145 	 * \return An active node (or NULL if resource is not active anywhere)
1146 	 *
1147 	 * \note This is a convenience wrapper for active_node() where the count of all
1148 	 *       active nodes or only clean active nodes is desired according to the
1149 	 *       \c PCMK_META_REQUIRES meta-attribute.
1150 	 */
1151 	pcmk_node_t *
1152 	pe__find_active_requires(const pcmk_resource_t *rsc, unsigned int *count)
1153 	{
1154 	    if (rsc == NULL) {
1155 	        if (count != NULL) {
1156 	            *count = 0;
1157 	        }
1158 	        return NULL;
1159 	    }
1160 	
1161 	    if (pcmk__is_set(rsc->flags, pcmk__rsc_needs_fencing)) {
1162 	        return rsc->priv->fns->active_node(rsc, count, NULL);
1163 	    } else {
1164 	        return rsc->priv->fns->active_node(rsc, NULL, count);
1165 	    }
1166 	}
1167 	
1168 	void
1169 	pe__count_common(pcmk_resource_t *rsc)
1170 	{
1171 	    if (rsc->priv->children != NULL) {
1172 	        for (GList *item = rsc->priv->children;
1173 	             item != NULL; item = item->next) {
1174 	            pcmk_resource_t *child = item->data;
1175 	
1176 	            child->priv->fns->count(item->data);
1177 	        }
1178 	
1179 	    } else if (!pcmk__is_set(rsc->flags, pcmk__rsc_removed)
1180 	               || (rsc->priv->orig_role > pcmk_role_stopped)) {
1181 	        rsc->priv->scheduler->priv->ninstances++;
1182 	        if (pe__resource_is_disabled(rsc)) {
1183 	            rsc->priv->scheduler->priv->disabled_resources++;
1184 	        }
1185 	        if (pcmk__is_set(rsc->flags, pcmk__rsc_blocked)) {
1186 	            rsc->priv->scheduler->priv->blocked_resources++;
1187 	        }
1188 	    }
1189 	}
1190 	
1191 	/*!
1192 	 * \internal
1193 	 * \brief Update a resource's next role
1194 	 *
1195 	 * \param[in,out] rsc   Resource to be updated
1196 	 * \param[in]     role  Resource's new next role
1197 	 * \param[in]     why   Human-friendly reason why role is changing (for logs)
1198 	 */
1199 	void
1200 	pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
1201 	{
1202 	    pcmk__assert((rsc != NULL) && (why != NULL));
1203 	    if (rsc->priv->next_role != role) {
1204 	        pcmk__rsc_trace(rsc, "Resetting next role for %s from %s to %s (%s)",
1205 	                        rsc->id, pcmk_role_text(rsc->priv->next_role),
1206 	                        pcmk_role_text(role), why);
1207 	        rsc->priv->next_role = role;
1208 	    }
1209 	}
1210