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