pacemaker 2.1.4-dc6eb4362e
Scalable High-Availability cluster resource manager
pcmk_sched_colocation.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11
12#include <stdbool.h>
13#include <glib.h>
14
15#include <crm/crm.h>
16#include <crm/pengine/status.h>
17#include <pacemaker-internal.h>
18
19#include "crm/common/util.h"
21#include "crm/msg_xml.h"
23
24#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
25 __rsc = pcmk__find_constraint_resource(data_set->resources, __name); \
26 if (__rsc == NULL) { \
27 pcmk__config_err("%s: No resource found for %s", __set, __name); \
28 return; \
29 } \
30 } while(0)
31
32static gint
33cmp_dependent_priority(gconstpointer a, gconstpointer b)
34{
35 const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
36 const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
37
38 if (a == NULL) {
39 return 1;
40 }
41 if (b == NULL) {
42 return -1;
43 }
44
45 CRM_ASSERT(rsc_constraint1->dependent != NULL);
46 CRM_ASSERT(rsc_constraint1->primary != NULL);
47
48 if (rsc_constraint1->dependent->priority > rsc_constraint2->dependent->priority) {
49 return -1;
50 }
51
52 if (rsc_constraint1->dependent->priority < rsc_constraint2->dependent->priority) {
53 return 1;
54 }
55
56 /* Process clones before primitives and groups */
57 if (rsc_constraint1->dependent->variant > rsc_constraint2->dependent->variant) {
58 return -1;
59 }
60 if (rsc_constraint1->dependent->variant < rsc_constraint2->dependent->variant) {
61 return 1;
62 }
63
64 /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
65 * clones (probably unnecessary, but avoids having to update regression
66 * tests)
67 */
68 if (rsc_constraint1->dependent->variant == pe_clone) {
69 if (pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
70 && !pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
71 return -1;
72 } else if (!pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
73 && pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
74 return 1;
75 }
76 }
77
78 return strcmp(rsc_constraint1->dependent->id,
79 rsc_constraint2->dependent->id);
80}
81
82static gint
83cmp_primary_priority(gconstpointer a, gconstpointer b)
84{
85 const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
86 const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
87
88 if (a == NULL) {
89 return 1;
90 }
91 if (b == NULL) {
92 return -1;
93 }
94
95 CRM_ASSERT(rsc_constraint1->dependent != NULL);
96 CRM_ASSERT(rsc_constraint1->primary != NULL);
97
98 if (rsc_constraint1->primary->priority > rsc_constraint2->primary->priority) {
99 return -1;
100 }
101
102 if (rsc_constraint1->primary->priority < rsc_constraint2->primary->priority) {
103 return 1;
104 }
105
106 /* Process clones before primitives and groups */
107 if (rsc_constraint1->primary->variant > rsc_constraint2->primary->variant) {
108 return -1;
109 } else if (rsc_constraint1->primary->variant < rsc_constraint2->primary->variant) {
110 return 1;
111 }
112
113 /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
114 * clones (probably unnecessary, but avoids having to update regression
115 * tests)
116 */
117 if (rsc_constraint1->primary->variant == pe_clone) {
118 if (pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
119 && !pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
120 return -1;
121 } else if (!pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
122 && pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
123 return 1;
124 }
125 }
126
127 return strcmp(rsc_constraint1->primary->id, rsc_constraint2->primary->id);
128}
129
134static void
135anti_colocation_order(pe_resource_t *first_rsc, int first_role,
136 pe_resource_t *then_rsc, int then_role,
138{
139 const char *first_tasks[] = { NULL, NULL };
140 const char *then_tasks[] = { NULL, NULL };
141
142 /* Actions to make first_rsc lose first_role */
143 if (first_role == RSC_ROLE_PROMOTED) {
144 first_tasks[0] = CRMD_ACTION_DEMOTE;
145
146 } else {
147 first_tasks[0] = CRMD_ACTION_STOP;
148
149 if (first_role == RSC_ROLE_UNPROMOTED) {
150 first_tasks[1] = CRMD_ACTION_PROMOTE;
151 }
152 }
153
154 /* Actions to make then_rsc gain then_role */
155 if (then_role == RSC_ROLE_PROMOTED) {
156 then_tasks[0] = CRMD_ACTION_PROMOTE;
157
158 } else {
159 then_tasks[0] = CRMD_ACTION_START;
160
161 if (then_role == RSC_ROLE_UNPROMOTED) {
162 then_tasks[1] = CRMD_ACTION_DEMOTE;
163 }
164 }
165
166 for (int first_lpc = 0;
167 (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
168
169 for (int then_lpc = 0;
170 (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
171
172 pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
173 then_rsc, then_tasks[then_lpc],
175 }
176 }
177}
178
193void
194pcmk__new_colocation(const char *id, const char *node_attr, int score,
195 pe_resource_t *dependent, pe_resource_t *primary,
196 const char *dependent_role, const char *primary_role,
197 bool influence, pe_working_set_t *data_set)
198{
199 pcmk__colocation_t *new_con = NULL;
200
201 if (score == 0) {
202 crm_trace("Ignoring colocation '%s' because score is 0", id);
203 return;
204 }
205 if ((dependent == NULL) || (primary == NULL)) {
206 pcmk__config_err("Ignoring colocation '%s' because resource "
207 "does not exist", id);
208 return;
209 }
210
211 new_con = calloc(1, sizeof(pcmk__colocation_t));
212 if (new_con == NULL) {
213 return;
214 }
215
216 if (pcmk__str_eq(dependent_role, RSC_ROLE_STARTED_S,
218 dependent_role = RSC_ROLE_UNKNOWN_S;
219 }
220
221 if (pcmk__str_eq(primary_role, RSC_ROLE_STARTED_S,
223 primary_role = RSC_ROLE_UNKNOWN_S;
224 }
225
226 new_con->id = id;
227 new_con->dependent = dependent;
228 new_con->primary = primary;
229 new_con->score = score;
230 new_con->dependent_role = text2role(dependent_role);
231 new_con->primary_role = text2role(primary_role);
232 new_con->node_attribute = node_attr;
233 new_con->influence = influence;
234
235 if (node_attr == NULL) {
236 node_attr = CRM_ATTR_UNAME;
237 }
238
239 pe_rsc_trace(dependent, "%s ==> %s (%s %d)",
240 dependent->id, primary->id, node_attr, score);
241
242 dependent->rsc_cons = g_list_insert_sorted(dependent->rsc_cons, new_con,
243 cmp_primary_priority);
244
245 primary->rsc_cons_lhs = g_list_insert_sorted(primary->rsc_cons_lhs, new_con,
246 cmp_dependent_priority);
247
249 new_con);
250
251 if (score <= -INFINITY) {
252 anti_colocation_order(dependent, new_con->dependent_role, primary,
253 new_con->primary_role, data_set);
254 anti_colocation_order(primary, new_con->primary_role, dependent,
255 new_con->dependent_role, data_set);
256 }
257}
258
270static bool
271unpack_influence(const char *coloc_id, const pe_resource_t *rsc,
272 const char *influence_s)
273{
274 if (influence_s != NULL) {
275 int influence_i = 0;
276
277 if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
278 pcmk__config_err("Constraint '%s' has invalid value for "
279 XML_COLOC_ATTR_INFLUENCE " (using default)",
280 coloc_id);
281 } else {
282 return (influence_i != 0);
283 }
284 }
285 return pcmk_is_set(rsc->flags, pe_rsc_critical);
286}
287
288static void
289unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
290 const char *influence_s, pe_working_set_t *data_set)
291{
292 xmlNode *xml_rsc = NULL;
293 pe_resource_t *with = NULL;
294 pe_resource_t *resource = NULL;
295 const char *set_id = ID(set);
296 const char *role = crm_element_value(set, "role");
297 const char *ordering = crm_element_value(set, "ordering");
298 int local_score = score;
299 bool sequential = false;
300
301 const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
302
303 if (score_s) {
304 local_score = char2score(score_s);
305 }
306 if (local_score == 0) {
307 crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
308 coloc_id, set_id);
309 return;
310 }
311
312 if (ordering == NULL) {
313 ordering = "group";
314 }
315
316 if (pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok && !sequential) {
317 return;
318
319 } else if ((local_score > 0)
320 && pcmk__str_eq(ordering, "group", pcmk__str_casei)) {
321 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
322 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
323
324 EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
325 if (with != NULL) {
326 pe_rsc_trace(resource, "Colocating %s with %s", resource->id, with->id);
327 pcmk__new_colocation(set_id, NULL, local_score, resource,
328 with, role, role,
329 unpack_influence(coloc_id, resource,
330 influence_s), data_set);
331 }
332 with = resource;
333 }
334
335 } else if (local_score > 0) {
336 pe_resource_t *last = NULL;
337
338 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
339 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
340
341 EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
342 if (last != NULL) {
343 pe_rsc_trace(resource, "Colocating %s with %s",
344 last->id, resource->id);
345 pcmk__new_colocation(set_id, NULL, local_score, last,
346 resource, role, role,
347 unpack_influence(coloc_id, last,
348 influence_s), data_set);
349 }
350
351 last = resource;
352 }
353
354 } else {
355 /* Anti-colocating with every prior resource is
356 * the only way to ensure the intuitive result
357 * (i.e. that no one in the set can run with anyone else in the set)
358 */
359
360 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
361 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
362
363 xmlNode *xml_rsc_with = NULL;
364 bool influence = true;
365
366 EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
367 influence = unpack_influence(coloc_id, resource, influence_s);
368
369 for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
370 xml_rsc_with != NULL;
371 xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
372
373 if (pcmk__str_eq(resource->id, ID(xml_rsc_with),
375 break;
376 }
377 EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
378 pe_rsc_trace(resource, "Anti-Colocating %s with %s", resource->id,
379 with->id);
380 pcmk__new_colocation(set_id, NULL, local_score,
381 resource, with, role, role,
382 influence, data_set);
383 }
384 }
385 }
386}
387
388static void
389colocate_rsc_sets(const char *id, xmlNode *set1, xmlNode *set2, int score,
390 const char *influence_s, pe_working_set_t *data_set)
391{
392 xmlNode *xml_rsc = NULL;
393 pe_resource_t *rsc_1 = NULL;
394 pe_resource_t *rsc_2 = NULL;
395
396 const char *role_1 = crm_element_value(set1, "role");
397 const char *role_2 = crm_element_value(set2, "role");
398
399 int rc = pcmk_rc_ok;
400 bool sequential = false;
401
402 if (score == 0) {
403 crm_trace("Ignoring colocation '%s' between sets because score is 0",
404 id);
405 return;
406 }
407
408 rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential);
409 if (rc != pcmk_rc_ok || sequential) {
410 // Get the first one
412 if (xml_rsc != NULL) {
413 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
414 }
415 }
416
417 rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential);
418 if (rc != pcmk_rc_ok || sequential) {
419 // Get the last one
420 const char *rid = NULL;
421
422 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
423 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
424
425 rid = ID(xml_rsc);
426 }
427 EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
428 }
429
430 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
431 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
432 unpack_influence(id, rsc_1, influence_s),
433 data_set);
434
435 } else if (rsc_1 != NULL) {
436 bool influence = unpack_influence(id, rsc_1, influence_s);
437
438 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
439 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
440
441 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
442 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
443 role_2, influence, data_set);
444 }
445
446 } else if (rsc_2 != NULL) {
447 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
448 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
449
450 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
451 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
452 role_2,
453 unpack_influence(id, rsc_1, influence_s),
454 data_set);
455 }
456
457 } else {
458 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
459 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
460
461 xmlNode *xml_rsc_2 = NULL;
462 bool influence = true;
463
464 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
465 influence = unpack_influence(id, rsc_1, influence_s);
466
467 for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
468 xml_rsc_2 != NULL;
469 xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
470
471 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
472 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
473 role_1, role_2, influence,
474 data_set);
475 }
476 }
477 }
478}
479
480static void
481unpack_simple_colocation(xmlNode *xml_obj, const char *id,
482 const char *influence_s, pe_working_set_t *data_set)
483{
484 int score_i = 0;
485
486 const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
487 const char *dependent_id = crm_element_value(xml_obj,
489 const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
490 const char *dependent_role = crm_element_value(xml_obj,
492 const char *primary_role = crm_element_value(xml_obj,
494 const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
495
496 // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
497 const char *dependent_instance = crm_element_value(xml_obj,
499 const char *primary_instance = crm_element_value(xml_obj,
501
503 dependent_id);
505 primary_id);
506
507 if (dependent == NULL) {
508 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
509 "does not exist", id, dependent_id);
510 return;
511
512 } else if (primary == NULL) {
513 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
514 "does not exist", id, primary_id);
515 return;
516
517 } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
518 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
519 "is not a clone but instance '%s' was requested",
520 id, dependent_id, dependent_instance);
521 return;
522
523 } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
524 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
525 "is not a clone but instance '%s' was requested",
526 id, primary_id, primary_instance);
527 return;
528 }
529
530 if (dependent_instance != NULL) {
531 dependent = find_clone_instance(dependent, dependent_instance, data_set);
532 if (dependent == NULL) {
533 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
534 "does not have an instance '%s'",
535 id, dependent_id, dependent_instance);
536 return;
537 }
538 }
539
540 if (primary_instance != NULL) {
541 primary = find_clone_instance(primary, primary_instance, data_set);
542 if (primary == NULL) {
543 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
544 "does not have an instance '%s'",
545 "'%s'", id, primary_id, primary_instance);
546 return;
547 }
548 }
549
551 pcmk__config_warn("The colocation constraint '"
553 "' attribute has been removed");
554 }
555
556 if (score) {
557 score_i = char2score(score);
558 }
559
560 pcmk__new_colocation(id, attr, score_i, dependent, primary,
561 dependent_role, primary_role,
562 unpack_influence(id, dependent, influence_s), data_set);
563}
564
565// \return Standard Pacemaker return code
566static int
567unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
569{
570 const char *id = NULL;
571 const char *dependent_id = NULL;
572 const char *primary_id = NULL;
573 const char *dependent_role = NULL;
574 const char *primary_role = NULL;
575
576 pe_resource_t *dependent = NULL;
577 pe_resource_t *primary = NULL;
578
579 pe_tag_t *dependent_tag = NULL;
580 pe_tag_t *primary_tag = NULL;
581
582 xmlNode *dependent_set = NULL;
583 xmlNode *primary_set = NULL;
584 bool any_sets = false;
585
586 *expanded_xml = NULL;
587
588 CRM_CHECK(xml_obj != NULL, return pcmk_rc_schema_validation);
589
590 id = ID(xml_obj);
591 if (id == NULL) {
592 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
593 crm_element_name(xml_obj));
595 }
596
597 // Check whether there are any resource sets with template or tag references
598 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
599 if (*expanded_xml != NULL) {
600 crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
601 return pcmk_rc_ok;
602 }
603
604 dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
605 primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
606 if ((dependent_id == NULL) || (primary_id == NULL)) {
607 return pcmk_rc_ok;
608 }
609
610 if (!pcmk__valid_resource_or_tag(data_set, dependent_id, &dependent,
611 &dependent_tag)) {
612 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
613 "valid resource or tag", id, dependent_id);
615 }
616
617 if (!pcmk__valid_resource_or_tag(data_set, primary_id, &primary,
618 &primary_tag)) {
619 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
620 "valid resource or tag", id, primary_id);
622 }
623
624 if ((dependent != NULL) && (primary != NULL)) {
625 /* Neither side references any template/tag. */
626 return pcmk_rc_ok;
627 }
628
629 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
630 // A colocation constraint between two templates/tags makes no sense
631 pcmk__config_err("Ignoring constraint '%s' because two templates or "
632 "tags cannot be colocated", id);
634 }
635
636 dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
637 primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
638
639 *expanded_xml = copy_xml(xml_obj);
640
641 // Convert template/tag reference in "rsc" into resource_set under constraint
642 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
643 true, data_set)) {
644 free_xml(*expanded_xml);
645 *expanded_xml = NULL;
647 }
648
649 if (dependent_set != NULL) {
650 if (dependent_role != NULL) {
651 // Move "rsc-role" into converted resource_set as "role"
652 crm_xml_add(dependent_set, "role", dependent_role);
654 }
655 any_sets = true;
656 }
657
658 // Convert template/tag reference in "with-rsc" into resource_set under constraint
659 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
660 true, data_set)) {
661 free_xml(*expanded_xml);
662 *expanded_xml = NULL;
664 }
665
666 if (primary_set != NULL) {
667 if (primary_role != NULL) {
668 // Move "with-rsc-role" into converted resource_set as "role"
669 crm_xml_add(primary_set, "role", primary_role);
671 }
672 any_sets = true;
673 }
674
675 if (any_sets) {
676 crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
677 } else {
678 free_xml(*expanded_xml);
679 *expanded_xml = NULL;
680 }
681
682 return pcmk_rc_ok;
683}
684
692void
694{
695 int score_i = 0;
696 xmlNode *set = NULL;
697 xmlNode *last = NULL;
698
699 xmlNode *orig_xml = NULL;
700 xmlNode *expanded_xml = NULL;
701
702 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
703 const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
704 const char *influence_s = crm_element_value(xml_obj,
706
707 if (score) {
708 score_i = char2score(score);
709 }
710
711 if (unpack_colocation_tags(xml_obj, &expanded_xml,
712 data_set) != pcmk_rc_ok) {
713 return;
714 }
715 if (expanded_xml) {
716 orig_xml = xml_obj;
717 xml_obj = expanded_xml;
718 }
719
720 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
721 set = crm_next_same_xml(set)) {
722
723 set = expand_idref(set, data_set->input);
724 if (set == NULL) { // Configuration error, message already logged
725 if (expanded_xml != NULL) {
726 free_xml(expanded_xml);
727 }
728 return;
729 }
730
731 unpack_colocation_set(set, score_i, id, influence_s, data_set);
732
733 if (last != NULL) {
734 colocate_rsc_sets(id, last, set, score_i, influence_s, data_set);
735 }
736 last = set;
737 }
738
739 if (expanded_xml) {
740 free_xml(expanded_xml);
741 xml_obj = orig_xml;
742 }
743
744 if (last == NULL) {
745 unpack_simple_colocation(xml_obj, id, influence_s, data_set);
746 }
747}
748
749static void
750mark_start_blocked(pe_resource_t *rsc, pe_resource_t *reason,
752{
753 char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
754
755 for (GList *gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
756 pe_action_t *action = (pe_action_t *) gIter->data;
757
759 && pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)) {
760
762 pe_action_set_reason(action, reason_text, false);
765 }
766 }
767 free(reason_text);
768}
769
777void
779{
780 GList *gIter = NULL;
781 pe_resource_t *rsc = NULL;
782
784 && pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)) {
785
786 rsc = uber_parent(action->rsc);
787 if (rsc->parent) {
788 /* For bundles, uber_parent() returns the clone, not the bundle, so
789 * the existence of rsc->parent implies this is a bundle.
790 * In this case, we need the bundle resource, so that we can check
791 * if all containers are stopped/stopping.
792 */
793 rsc = rsc->parent;
794 }
795 }
796
797 if ((rsc == NULL) || (rsc->rsc_cons_lhs == NULL)) {
798 return;
799 }
800
801 // Block colocated starts only if all children (if any) have unrunnable starts
802 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
803 pe_resource_t *child = (pe_resource_t *)gIter->data;
804 pe_action_t *start = find_first_action(child->actions, NULL, RSC_START, NULL);
805
806 if ((start == NULL) || pcmk_is_set(start->flags, pe_action_runnable)) {
807 return;
808 }
809 }
810
811 for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
812 pcmk__colocation_t *colocate_with = (pcmk__colocation_t *) gIter->data;
813
814 if (colocate_with->score == INFINITY) {
815 mark_start_blocked(colocate_with->dependent, action->rsc, data_set);
816 }
817 }
818}
819
840 pcmk__colocation_t *constraint, bool preview)
841{
842 if (!preview && pcmk_is_set(primary->flags, pe_rsc_provisional)) {
843 // Primary resource has not been allocated yet, so we can't do anything
845 }
846
847 if ((constraint->dependent_role >= RSC_ROLE_UNPROMOTED)
848 && (dependent->parent != NULL)
850 && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
851
852 /* This is a colocation by role, and the dependent is a promotable clone
853 * that has already been allocated, so the colocation should now affect
854 * the role.
855 */
857 }
858
859 if (!preview && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
860 /* The dependent resource has already been through allocation, so the
861 * constraint no longer has any effect. Log an error if a mandatory
862 * colocation constraint has been violated.
863 */
864
865 const pe_node_t *primary_node = primary->allocated_to;
866
867 if (dependent->allocated_to == NULL) {
868 crm_trace("Skipping colocation '%s': %s will not run anywhere",
869 constraint->id, dependent->id);
870
871 } else if (constraint->score >= INFINITY) {
872 // Dependent resource must colocate with primary resource
873
874 if ((primary_node == NULL) ||
875 (primary_node->details != dependent->allocated_to->details)) {
876 crm_err("%s must be colocated with %s but is not (%s vs. %s)",
877 dependent->id, primary->id,
878 dependent->allocated_to->details->uname,
879 (primary_node == NULL)? "unallocated" : primary_node->details->uname);
880 }
881
882 } else if (constraint->score <= -CRM_SCORE_INFINITY) {
883 // Dependent resource must anti-colocate with primary resource
884
885 if ((primary_node != NULL) &&
886 (dependent->allocated_to->details == primary_node->details)) {
887 crm_err("%s and %s must be anti-colocated but are allocated "
888 "to the same node (%s)",
889 dependent->id, primary->id, primary_node->details->uname);
890 }
891 }
893 }
894
895 if ((constraint->score > 0)
896 && (constraint->dependent_role != RSC_ROLE_UNKNOWN)
897 && (constraint->dependent_role != dependent->next_role)) {
898
899 crm_trace("Skipping colocation '%s': dependent limited to %s role "
900 "but %s next role is %s",
901 constraint->id, role2text(constraint->dependent_role),
902 dependent->id, role2text(dependent->next_role));
904 }
905
906 if ((constraint->score > 0)
907 && (constraint->primary_role != RSC_ROLE_UNKNOWN)
908 && (constraint->primary_role != primary->next_role)) {
909
910 crm_trace("Skipping colocation '%s': primary limited to %s role "
911 "but %s next role is %s",
912 constraint->id, role2text(constraint->primary_role),
913 primary->id, role2text(primary->next_role));
915 }
916
917 if ((constraint->score < 0)
918 && (constraint->dependent_role != RSC_ROLE_UNKNOWN)
919 && (constraint->dependent_role == dependent->next_role)) {
920 crm_trace("Skipping anti-colocation '%s': dependent role %s matches",
921 constraint->id, role2text(constraint->dependent_role));
923 }
924
925 if ((constraint->score < 0)
926 && (constraint->primary_role != RSC_ROLE_UNKNOWN)
927 && (constraint->primary_role == primary->next_role)) {
928 crm_trace("Skipping anti-colocation '%s': primary role %s matches",
929 constraint->id, role2text(constraint->primary_role));
931 }
932
934}
935
947void
949 pcmk__colocation_t *constraint)
950{
951 const char *attribute = CRM_ATTR_ID;
952 const char *value = NULL;
953 GHashTable *work = NULL;
954 GHashTableIter iter;
955 pe_node_t *node = NULL;
956
957 if (constraint->node_attribute != NULL) {
958 attribute = constraint->node_attribute;
959 }
960
961 if (primary->allocated_to != NULL) {
962 value = pe_node_attribute_raw(primary->allocated_to, attribute);
963
964 } else if (constraint->score < 0) {
965 // Nothing to do (anti-colocation with something that is not running)
966 return;
967 }
968
969 work = pcmk__copy_node_table(dependent->allowed_nodes);
970
971 g_hash_table_iter_init(&iter, work);
972 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
973 if (primary->allocated_to == NULL) {
974 pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s inactive)",
975 constraint->id, dependent->id, node->details->uname,
976 constraint->score, primary->id);
977 node->weight = pcmk__add_scores(-constraint->score, node->weight);
978
979 } else if (pcmk__str_eq(pe_node_attribute_raw(node, attribute), value,
981 if (constraint->score < CRM_SCORE_INFINITY) {
982 pe_rsc_trace(dependent, "%s: %s@%s += %d",
983 constraint->id, dependent->id,
984 node->details->uname, constraint->score);
985 node->weight = pcmk__add_scores(constraint->score,
986 node->weight);
987 }
988
989 } else if (constraint->score >= CRM_SCORE_INFINITY) {
990 pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s mismatch)",
991 constraint->id, dependent->id, node->details->uname,
992 constraint->score, attribute);
993 node->weight = pcmk__add_scores(-constraint->score, node->weight);
994 }
995 }
996
997 if ((constraint->score <= -INFINITY) || (constraint->score >= INFINITY)
998 || pcmk__any_node_available(work)) {
999
1000 g_hash_table_destroy(dependent->allowed_nodes);
1001 dependent->allowed_nodes = work;
1002 work = NULL;
1003
1004 } else {
1005 pe_rsc_info(dependent,
1006 "%s: Rolling back scores from %s (no available nodes)",
1007 dependent->id, primary->id);
1008 }
1009
1010 if (work != NULL) {
1011 g_hash_table_destroy(work);
1012 }
1013}
1014
1026void
1028 pcmk__colocation_t *constraint)
1029{
1030 const char *dependent_value = NULL;
1031 const char *primary_value = NULL;
1032 const char *attribute = CRM_ATTR_ID;
1033 int score_multiplier = 1;
1034
1035 if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1036 return;
1037 }
1038
1039 if (constraint->node_attribute != NULL) {
1040 attribute = constraint->node_attribute;
1041 }
1042
1043 dependent_value = pe_node_attribute_raw(dependent->allocated_to, attribute);
1044 primary_value = pe_node_attribute_raw(primary->allocated_to, attribute);
1045
1046 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1047 if ((constraint->score == INFINITY)
1048 && (constraint->dependent_role == RSC_ROLE_PROMOTED)) {
1049 dependent->priority = -INFINITY;
1050 }
1051 return;
1052 }
1053
1054 if ((constraint->primary_role != RSC_ROLE_UNKNOWN)
1055 && (constraint->primary_role != primary->next_role)) {
1056 return;
1057 }
1058
1059 if (constraint->dependent_role == RSC_ROLE_UNPROMOTED) {
1060 score_multiplier = -1;
1061 }
1062
1063 dependent->priority = pcmk__add_scores(score_multiplier * constraint->score,
1064 dependent->priority);
1065}
int pcmk__xe_get_bool_attr(xmlNodePtr node, const char *name, bool *value)
Definition: nvpair.c:960
bool pcmk__xe_attr_is_true(xmlNodePtr node, const char *name)
Definition: nvpair.c:986
Utility functions.
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:140
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:122
#define RSC_ROLE_STARTED_S
Definition: common.h:112
const char * role2text(enum rsc_role_e role)
Definition: common.c:454
@ RSC_ROLE_PROMOTED
Definition: common.h:97
@ RSC_ROLE_UNKNOWN
Definition: common.h:93
@ RSC_ROLE_UNPROMOTED
Definition: common.h:96
enum rsc_role_e text2role(const char *role)
Definition: common.c:483
#define RSC_ROLE_UNKNOWN_S
Definition: common.h:110
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:913
uint32_t id
Definition: cpg.c:0
A dumping ground.
#define CRMD_ACTION_STOP
Definition: crm.h:179
#define CRM_SCORE_INFINITY
Definition: crm.h:85
#define RSC_START
Definition: crm.h:201
#define CRMD_ACTION_DEMOTE
Definition: crm.h:184
#define INFINITY
Definition: crm.h:99
#define CRMD_ACTION_START
Definition: crm.h:176
#define CRMD_ACTION_PROMOTE
Definition: crm.h:182
#define CRM_ATTR_UNAME
Definition: crm.h:114
#define CRM_ATTR_ID
Definition: crm.h:115
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
G_GNUC_INTERNAL bool pcmk__any_node_available(GHashTable *nodes)
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag)
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pe_action_t *action, pe_working_set_t *data_set)
#define pcmk__order_resource_actions(lh_rsc, lh_task, rh_rsc, rh_task, flags, data_set)
pcmk__coloc_affects
@ pcmk__coloc_affects_nothing
@ pcmk__coloc_affects_location
@ pcmk__coloc_affects_role
G_GNUC_INTERNAL pe_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, pe_working_set_t *data_set)
G_GNUC_INTERNAL GHashTable * pcmk__copy_node_table(GHashTable *nodes)
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
#define crm_err(fmt, args...)
Definition: logging.h:358
#define crm_log_xml_trace(xml, text)
Definition: logging.h:372
#define crm_trace(fmt, args...)
Definition: logging.h:364
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:361
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:219
#define ID(x)
Definition: msg_xml.h:460
#define XML_COLOC_ATTR_NODE_ATTR
Definition: msg_xml.h:364
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:339
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:355
#define XML_COLOC_ATTR_TARGET_ROLE
Definition: msg_xml.h:363
#define XML_COLOC_ATTR_TARGET_INSTANCE
Definition: msg_xml.h:366
#define XML_ATTR_ID
Definition: msg_xml.h:135
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:365
#define XML_COLOC_ATTR_INFLUENCE
Definition: msg_xml.h:367
#define XML_CONS_ATTR_SYMMETRICAL
Definition: msg_xml.h:356
#define XML_COLOC_ATTR_TARGET
Definition: msg_xml.h:362
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:360
pe_working_set_t * data_set
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
const char * action
Definition: pcmk_fence.c:29
void pcmk__apply_coloc_to_weights(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint)
enum pcmk__coloc_affects pcmk__colocation_affects(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint, bool preview)
#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name)
void pcmk__block_colocated_starts(pe_action_t *action, pe_working_set_t *data_set)
void pcmk__apply_coloc_to_priority(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint)
void pcmk__new_colocation(const char *id, const char *node_attr, int score, pe_resource_t *dependent, pe_resource_t *primary, const char *dependent_role, const char *primary_role, bool influence, pe_working_set_t *data_set)
void pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
@ pe_order_anti_colocation
Definition: pe_types.h:532
#define pe_rsc_provisional
Definition: pe_types.h:266
@ pe_action_runnable
Definition: pe_types.h:307
@ pe_clone
Definition: pe_types.h:40
#define pe_rsc_promotable
Definition: pe_types.h:264
#define pe_rsc_critical
Definition: pe_types.h:274
pe_action_t * find_first_action(GList *input, const char *uuid, const char *task, pe_node_t *on_node)
Definition: utils.c:1541
const char * pe_node_attribute_raw(pe_node_t *node, const char *name)
Definition: common.c:560
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:22
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:20
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:135
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:70
void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
Definition: utils.c:2349
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_ok
Definition: results.h:146
@ pcmk_rc_schema_validation
Definition: results.h:126
Cluster status and scheduling.
@ pcmk__str_null_matches
@ pcmk__str_casei
pe_resource_t * primary
const char * node_attribute
pe_resource_t * dependent
int weight
Definition: pe_types.h:249
struct pe_node_shared_s * details
Definition: pe_types.h:252
const char * uname
Definition: pe_types.h:216
GList * actions
Definition: pe_types.h:373
enum pe_obj_types variant
Definition: pe_types.h:345
GList * rsc_cons
Definition: pe_types.h:371
GList * rsc_cons_lhs
Definition: pe_types.h:370
GList * children
Definition: pe_types.h:391
char * id
Definition: pe_types.h:336
GHashTable * allowed_nodes
Definition: pe_types.h:382
pe_node_t * allocated_to
Definition: pe_types.h:377
unsigned long long flags
Definition: pe_types.h:362
pe_resource_t * parent
Definition: pe_types.h:343
enum rsc_role_e next_role
Definition: pe_types.h:385
GList * colocation_constraints
Definition: pe_types.h:168
xmlNode * input
Definition: pe_types.h:144
GList * resources
Definition: pe_types.h:165
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2867
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2794
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2820
void free_xml(xmlNode *child)
Definition: xml.c:824
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2037