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