pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_sched_colocation.c
Go to the documentation of this file.
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 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>
17#include <crm/pengine/status.h>
18#include <pacemaker-internal.h>
19
20#include "crm/common/util.h"
22#include "crm/common/xml.h"
24
25// Used to temporarily mark a node as unusable
26#define INFINITY_HACK (PCMK_SCORE_INFINITY * -100)
27
51static gint
52cmp_colocation_priority(const pcmk__colocation_t *colocation1,
53 const pcmk__colocation_t *colocation2, bool dependent)
54{
55 const pcmk_resource_t *rsc1 = NULL;
56 const pcmk_resource_t *rsc2 = NULL;
57
58 if (colocation1 == NULL) {
59 return 1;
60 }
61 if (colocation2 == NULL) {
62 return -1;
63 }
64
65 if (dependent) {
66 rsc1 = colocation1->dependent;
67 rsc2 = colocation2->dependent;
68 CRM_ASSERT(colocation1->primary != NULL);
69 } else {
70 rsc1 = colocation1->primary;
71 rsc2 = colocation2->primary;
72 CRM_ASSERT(colocation1->dependent != NULL);
73 }
74 CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
75
76 if (rsc1->priority > rsc2->priority) {
77 return -1;
78 }
79 if (rsc1->priority < rsc2->priority) {
80 return 1;
81 }
82
83 // Process clones before primitives and groups
84 if (rsc1->variant > rsc2->variant) {
85 return -1;
86 }
87 if (rsc1->variant < rsc2->variant) {
88 return 1;
89 }
90
91 /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
92 * clones (probably unnecessary, but avoids having to update regression
93 * tests)
94 */
95 if (pcmk__is_clone(rsc1)) {
98 return -1;
99 }
102 return 1;
103 }
104 }
105
106 return strcmp(rsc1->id, rsc2->id);
107}
108
129static gint
130cmp_dependent_priority(gconstpointer a, gconstpointer b)
131{
132 return cmp_colocation_priority(a, b, true);
133}
134
155static gint
156cmp_primary_priority(gconstpointer a, gconstpointer b)
157{
158 return cmp_colocation_priority(a, b, false);
159}
160
172void
173pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
174 const pcmk_resource_t *rsc)
175{
176 CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
177
178 pcmk__rsc_trace(rsc,
179 "Adding colocation %s (%s with %s using %s @%s) to "
180 "'this with' list for %s",
181 colocation->id, colocation->dependent->id,
182 colocation->primary->id, colocation->node_attribute,
183 pcmk_readable_score(colocation->score), rsc->id);
184 *list = g_list_insert_sorted(*list, (gpointer) colocation,
185 cmp_primary_priority);
186}
187
199void
200pcmk__add_this_with_list(GList **list, GList *addition,
201 const pcmk_resource_t *rsc)
202{
203 CRM_ASSERT((list != NULL) && (rsc != NULL));
204
206 {}, // Always add each colocation individually if tracing
207 {
208 if (*list == NULL) {
209 // Trivial case for efficiency if not tracing
210 *list = g_list_copy(addition);
211 return;
212 }
213 }
214 );
215
216 for (const GList *iter = addition; iter != NULL; iter = iter->next) {
217 pcmk__add_this_with(list, addition->data, rsc);
218 }
219}
220
232void
233pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
234 const pcmk_resource_t *rsc)
235{
236 CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
237
238 pcmk__rsc_trace(rsc,
239 "Adding colocation %s (%s with %s using %s @%s) to "
240 "'with this' list for %s",
241 colocation->id, colocation->dependent->id,
242 colocation->primary->id, colocation->node_attribute,
243 pcmk_readable_score(colocation->score), rsc->id);
244 *list = g_list_insert_sorted(*list, (gpointer) colocation,
245 cmp_dependent_priority);
246}
247
259void
260pcmk__add_with_this_list(GList **list, GList *addition,
261 const pcmk_resource_t *rsc)
262{
263 CRM_ASSERT((list != NULL) && (rsc != NULL));
264
266 {}, // Always add each colocation individually if tracing
267 {
268 if (*list == NULL) {
269 // Trivial case for efficiency if not tracing
270 *list = g_list_copy(addition);
271 return;
272 }
273 }
274 );
275
276 for (const GList *iter = addition; iter != NULL; iter = iter->next) {
277 pcmk__add_with_this(list, addition->data, rsc);
278 }
279}
280
290static void
291anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
292 pcmk_resource_t *then_rsc, int then_role)
293{
294 const char *first_tasks[] = { NULL, NULL };
295 const char *then_tasks[] = { NULL, NULL };
296
297 /* Actions to make first_rsc lose first_role */
298 if (first_role == pcmk_role_promoted) {
299 first_tasks[0] = PCMK_ACTION_DEMOTE;
300
301 } else {
302 first_tasks[0] = PCMK_ACTION_STOP;
303
304 if (first_role == pcmk_role_unpromoted) {
305 first_tasks[1] = PCMK_ACTION_PROMOTE;
306 }
307 }
308
309 /* Actions to make then_rsc gain then_role */
310 if (then_role == pcmk_role_promoted) {
311 then_tasks[0] = PCMK_ACTION_PROMOTE;
312
313 } else {
314 then_tasks[0] = PCMK_ACTION_START;
315
316 if (then_role == pcmk_role_unpromoted) {
317 then_tasks[1] = PCMK_ACTION_DEMOTE;
318 }
319 }
320
321 for (int first_lpc = 0;
322 (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
323
324 for (int then_lpc = 0;
325 (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
326
327 pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
328 then_rsc, then_tasks[then_lpc],
330 }
331 }
332}
333
347void
348pcmk__new_colocation(const char *id, const char *node_attr, int score,
349 pcmk_resource_t *dependent, pcmk_resource_t *primary,
350 const char *dependent_role, const char *primary_role,
351 uint32_t flags)
352{
353 pcmk__colocation_t *new_con = NULL;
354
355 CRM_CHECK(id != NULL, return);
356
357 if ((dependent == NULL) || (primary == NULL)) {
358 pcmk__config_err("Ignoring colocation '%s' because resource "
359 "does not exist", id);
360 return;
361 }
362
363 if (score == 0) {
364 pcmk__rsc_trace(dependent,
365 "Ignoring colocation '%s' (%s with %s) because score is 0",
366 id, dependent->id, primary->id);
367 return;
368 }
369
370 new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t));
371
372 if (pcmk__str_eq(dependent_role, PCMK_ROLE_STARTED,
374 dependent_role = PCMK__ROLE_UNKNOWN;
375 }
376
377 if (pcmk__str_eq(primary_role, PCMK_ROLE_STARTED,
379 primary_role = PCMK__ROLE_UNKNOWN;
380 }
381
382 new_con->id = id;
383 new_con->dependent = dependent;
384 new_con->primary = primary;
385 new_con->score = score;
386 new_con->dependent_role = pcmk_parse_role(dependent_role);
387 new_con->primary_role = pcmk_parse_role(primary_role);
388 new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
389 new_con->flags = flags;
390
391 pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent);
392 pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary);
393
394 dependent->cluster->colocation_constraints = g_list_prepend(
395 dependent->cluster->colocation_constraints, new_con);
396
397 if (score <= -PCMK_SCORE_INFINITY) {
398 anti_colocation_order(dependent, new_con->dependent_role, primary,
399 new_con->primary_role);
400 anti_colocation_order(primary, new_con->primary_role, dependent,
401 new_con->dependent_role);
402 }
403}
404
417static uint32_t
418unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
419 const char *influence_s)
420{
421 if (influence_s != NULL) {
422 int influence_i = 0;
423
424 if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
425 pcmk__config_err("Constraint '%s' has invalid value for "
426 PCMK_XA_INFLUENCE " (using default)",
427 coloc_id);
428 } else {
429 return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
430 }
431 }
434 }
435 return pcmk__coloc_none;
436}
437
438static void
439unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
440 const char *influence_s, pcmk_scheduler_t *scheduler)
441{
442 xmlNode *xml_rsc = NULL;
443 pcmk_resource_t *other = NULL;
444 pcmk_resource_t *resource = NULL;
445 const char *set_id = pcmk__xe_id(set);
446 const char *role = crm_element_value(set, PCMK_XA_ROLE);
447 bool with_previous = false;
448 int local_score = score;
449 bool sequential = false;
450 uint32_t flags = pcmk__coloc_none;
451 const char *xml_rsc_id = NULL;
452 const char *score_s = crm_element_value(set, PCMK_XA_SCORE);
453
454 if (score_s) {
455 local_score = char2score(score_s);
456 }
457 if (local_score == 0) {
458 crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
459 coloc_id, set_id);
460 return;
461 }
462
463 /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether
464 * resources in a positive-score set are colocated with the previous or next
465 * resource.
466 */
467 if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING),
470 with_previous = true;
471 } else {
473 "Support for '" PCMK__XA_ORDERING "' other than"
475 " (such as %s) is deprecated and will be removed in a"
476 " future release",
477 set_id);
478 }
479
481 &sequential) == pcmk_rc_ok)
482 && !sequential) {
483 return;
484 }
485
486 if (local_score > 0) {
487 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
488 NULL);
489 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
490
491 xml_rsc_id = pcmk__xe_id(xml_rsc);
493 xml_rsc_id);
494 if (resource == NULL) {
495 // Should be possible only with validation disabled
496 pcmk__config_err("Ignoring %s and later resources in set %s: "
497 "No such resource", xml_rsc_id, set_id);
498 return;
499 }
500 if (other != NULL) {
502 | unpack_influence(coloc_id, resource, influence_s);
503 if (with_previous) {
504 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
505 resource->id, other->id, set_id);
506 pcmk__new_colocation(set_id, NULL, local_score, resource,
507 other, role, role, flags);
508 } else {
509 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
510 other->id, resource->id, set_id);
511 pcmk__new_colocation(set_id, NULL, local_score, other,
512 resource, role, role, flags);
513 }
514 }
515 other = resource;
516 }
517
518 } else {
519 /* Anti-colocating with every prior resource is
520 * the only way to ensure the intuitive result
521 * (i.e. that no one in the set can run with anyone else in the set)
522 */
523
524 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
525 NULL);
526 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
527
528 xmlNode *xml_rsc_with = NULL;
529
530 xml_rsc_id = pcmk__xe_id(xml_rsc);
532 xml_rsc_id);
533 if (resource == NULL) {
534 // Should be possible only with validation disabled
535 pcmk__config_err("Ignoring %s and later resources in set %s: "
536 "No such resource", xml_rsc_id, set_id);
537 return;
538 }
540 | unpack_influence(coloc_id, resource, influence_s);
541 for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
542 NULL, NULL);
543 xml_rsc_with != NULL;
544 xml_rsc_with = pcmk__xe_next_same(xml_rsc_with)) {
545
546 xml_rsc_id = pcmk__xe_id(xml_rsc_with);
547 if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
548 break;
549 }
551 xml_rsc_id);
552 CRM_ASSERT(other != NULL); // We already processed it
553 pcmk__new_colocation(set_id, NULL, local_score,
554 resource, other, role, role, flags);
555 }
556 }
557 }
558}
559
572static void
573colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
574 int score, const char *influence_s,
576{
577 xmlNode *xml_rsc = NULL;
578 pcmk_resource_t *rsc_1 = NULL;
579 pcmk_resource_t *rsc_2 = NULL;
580
581 const char *xml_rsc_id = NULL;
582 const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE);
583 const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE);
584
585 int rc = pcmk_rc_ok;
586 bool sequential = false;
587 uint32_t flags = pcmk__coloc_none;
588
589 if (score == 0) {
590 crm_trace("Ignoring colocation '%s' between sets %s and %s "
591 "because score is 0",
592 id, pcmk__xe_id(set1), pcmk__xe_id(set2));
593 return;
594 }
595
596 rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential);
597 if ((rc != pcmk_rc_ok) || sequential) {
598 // Get the first one
599 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL);
600 if (xml_rsc != NULL) {
601 xml_rsc_id = pcmk__xe_id(xml_rsc);
603 xml_rsc_id);
604 if (rsc_1 == NULL) {
605 // Should be possible only with validation disabled
606 pcmk__config_err("Ignoring colocation of set %s with set %s "
607 "because first resource %s not found",
608 pcmk__xe_id(set1), pcmk__xe_id(set2),
609 xml_rsc_id);
610 return;
611 }
612 }
613 }
614
615 rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential);
616 if ((rc != pcmk_rc_ok) || sequential) {
617 // Get the last one
618 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
619 NULL);
620 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
621
622 xml_rsc_id = pcmk__xe_id(xml_rsc);
623 }
625 xml_rsc_id);
626 if (rsc_2 == NULL) {
627 // Should be possible only with validation disabled
628 pcmk__config_err("Ignoring colocation of set %s with set %s "
629 "because last resource %s not found",
630 pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id);
631 return;
632 }
633 }
634
635 if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
636 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
637 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
638 flags);
639
640 } else if (rsc_1 != NULL) { // Only set1 is sequential
641 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
642 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
643 NULL);
644 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
645
646 xml_rsc_id = pcmk__xe_id(xml_rsc);
648 xml_rsc_id);
649 if (rsc_2 == NULL) {
650 // Should be possible only with validation disabled
651 pcmk__config_err("Ignoring set %s colocation with resource %s "
652 "in set %s: No such resource",
653 pcmk__xe_id(set1), xml_rsc_id,
654 pcmk__xe_id(set2));
655 continue;
656 }
657 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
658 role_2, flags);
659 }
660
661 } else if (rsc_2 != NULL) { // Only set2 is sequential
662 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
663 NULL);
664 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
665
666 xml_rsc_id = pcmk__xe_id(xml_rsc);
668 xml_rsc_id);
669 if (rsc_1 == NULL) {
670 // Should be possible only with validation disabled
671 pcmk__config_err("Ignoring colocation of set %s resource %s "
672 "with set %s: No such resource",
673 pcmk__xe_id(set1), xml_rsc_id,
674 pcmk__xe_id(set2));
675 continue;
676 }
678 | unpack_influence(id, rsc_1, influence_s);
679 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
680 role_2, flags);
681 }
682
683 } else { // Neither set is sequential
684 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
685 NULL);
686 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
687
688 xmlNode *xml_rsc_2 = NULL;
689
690 xml_rsc_id = pcmk__xe_id(xml_rsc);
692 xml_rsc_id);
693 if (rsc_1 == NULL) {
694 // Should be possible only with validation disabled
695 pcmk__config_err("Ignoring colocation of set %s resource %s "
696 "with set %s: No such resource",
697 pcmk__xe_id(set1), xml_rsc_id,
698 pcmk__xe_id(set2));
699 continue;
700 }
701
703 | unpack_influence(id, rsc_1, influence_s);
704 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
705 NULL, NULL);
706 xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
707
708 xml_rsc_id = pcmk__xe_id(xml_rsc_2);
710 xml_rsc_id);
711 if (rsc_2 == NULL) {
712 // Should be possible only with validation disabled
713 pcmk__config_err("Ignoring colocation of set %s resource "
714 "%s with set %s resource %s: No such "
715 "resource",
716 pcmk__xe_id(set1), pcmk__xe_id(xml_rsc),
717 pcmk__xe_id(set2), xml_rsc_id);
718 continue;
719 }
720 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
721 role_1, role_2, flags);
722 }
723 }
724 }
725}
726
727static void
728unpack_simple_colocation(xmlNode *xml_obj, const char *id,
729 const char *influence_s, pcmk_scheduler_t *scheduler)
730{
731 int score_i = 0;
732 uint32_t flags = pcmk__coloc_none;
733
734 const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE);
735 const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
736 const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
737 const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
738 const char *primary_role = crm_element_value(xml_obj,
740 const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE);
741
742 const char *primary_instance = NULL;
743 const char *dependent_instance = NULL;
744 pcmk_resource_t *primary = NULL;
745 pcmk_resource_t *dependent = NULL;
746
747 primary = pcmk__find_constraint_resource(scheduler->resources, primary_id);
749 dependent_id);
750
751 // @COMPAT: Deprecated since 2.1.5
752 primary_instance = crm_element_value(xml_obj, PCMK__XA_WITH_RSC_INSTANCE);
753 dependent_instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE);
754 if (dependent_instance != NULL) {
756 "Support for " PCMK__XA_RSC_INSTANCE " is deprecated "
757 "and will be removed in a future release");
758 }
759 if (primary_instance != NULL) {
761 "Support for " PCMK__XA_WITH_RSC_INSTANCE " is "
762 "deprecated and will be removed in a future release");
763 }
764
765 if (dependent == NULL) {
766 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
767 "does not exist", id, dependent_id);
768 return;
769
770 } else if (primary == NULL) {
771 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
772 "does not exist", id, primary_id);
773 return;
774
775 } else if ((dependent_instance != NULL) && !pcmk__is_clone(dependent)) {
776 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
777 "is not a clone but instance '%s' was requested",
778 id, dependent_id, dependent_instance);
779 return;
780
781 } else if ((primary_instance != NULL) && !pcmk__is_clone(primary)) {
782 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
783 "is not a clone but instance '%s' was requested",
784 id, primary_id, primary_instance);
785 return;
786 }
787
788 if (dependent_instance != NULL) {
789 dependent = find_clone_instance(dependent, dependent_instance);
790 if (dependent == NULL) {
791 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
792 "does not have an instance '%s'",
793 id, dependent_id, dependent_instance);
794 return;
795 }
796 }
797
798 if (primary_instance != NULL) {
799 primary = find_clone_instance(primary, primary_instance);
800 if (primary == NULL) {
801 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
802 "does not have an instance '%s'",
803 id, primary_id, primary_instance);
804 return;
805 }
806 }
807
809 pcmk__config_warn("The colocation constraint "
810 "'" PCMK_XA_SYMMETRICAL "' attribute has been "
811 "removed");
812 }
813
814 if (score) {
815 score_i = char2score(score);
816 }
817
818 flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
819 pcmk__new_colocation(id, attr, score_i, dependent, primary,
820 dependent_role, primary_role, flags);
821}
822
823// \return Standard Pacemaker return code
824static int
825unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
827{
828 const char *id = NULL;
829 const char *dependent_id = NULL;
830 const char *primary_id = NULL;
831 const char *dependent_role = NULL;
832 const char *primary_role = NULL;
833
834 pcmk_resource_t *dependent = NULL;
835 pcmk_resource_t *primary = NULL;
836
837 pcmk_tag_t *dependent_tag = NULL;
838 pcmk_tag_t *primary_tag = NULL;
839
840 xmlNode *dependent_set = NULL;
841 xmlNode *primary_set = NULL;
842 bool any_sets = false;
843
844 *expanded_xml = NULL;
845
846 CRM_CHECK(xml_obj != NULL, return EINVAL);
847
848 id = pcmk__xe_id(xml_obj);
849 if (id == NULL) {
850 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
851 xml_obj->name);
853 }
854
855 // Check whether there are any resource sets with template or tag references
856 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
857 if (*expanded_xml != NULL) {
858 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
859 return pcmk_rc_ok;
860 }
861
862 dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
863 primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
864 if ((dependent_id == NULL) || (primary_id == NULL)) {
865 return pcmk_rc_ok;
866 }
867
868 if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
869 &dependent_tag)) {
870 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
871 "valid resource or tag", id, dependent_id);
873 }
874
875 if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
876 &primary_tag)) {
877 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
878 "valid resource or tag", id, primary_id);
880 }
881
882 if ((dependent != NULL) && (primary != NULL)) {
883 /* Neither side references any template/tag. */
884 return pcmk_rc_ok;
885 }
886
887 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
888 // A colocation constraint between two templates/tags makes no sense
889 pcmk__config_err("Ignoring constraint '%s' because two templates or "
890 "tags cannot be colocated", id);
892 }
893
894 dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
895 primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE);
896
897 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
898
899 /* Convert dependent's template/tag reference into constraint
900 * PCMK_XE_RESOURCE_SET
901 */
902 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true,
903 scheduler)) {
904 free_xml(*expanded_xml);
905 *expanded_xml = NULL;
907 }
908
909 if (dependent_set != NULL) {
910 if (dependent_role != NULL) {
911 /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
912 * PCMK_XA_ROLE
913 */
914 crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role);
916 }
917 any_sets = true;
918 }
919
920 /* Convert primary's template/tag reference into constraint
921 * PCMK_XE_RESOURCE_SET
922 */
923 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true,
924 scheduler)) {
925 free_xml(*expanded_xml);
926 *expanded_xml = NULL;
928 }
929
930 if (primary_set != NULL) {
931 if (primary_role != NULL) {
932 /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
933 * PCMK_XA_ROLE
934 */
935 crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role);
937 }
938 any_sets = true;
939 }
940
941 if (any_sets) {
942 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
943 } else {
944 free_xml(*expanded_xml);
945 *expanded_xml = NULL;
946 }
947
948 return pcmk_rc_ok;
949}
950
958void
960{
961 int score_i = 0;
962 xmlNode *set = NULL;
963 xmlNode *last = NULL;
964
965 xmlNode *orig_xml = NULL;
966 xmlNode *expanded_xml = NULL;
967
968 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
969 const char *score = NULL;
970 const char *influence_s = NULL;
971
972 if (pcmk__str_empty(id)) {
974 " without " CRM_ATTR_ID);
975 return;
976 }
977
978 if (unpack_colocation_tags(xml_obj, &expanded_xml,
979 scheduler) != pcmk_rc_ok) {
980 return;
981 }
982 if (expanded_xml != NULL) {
983 orig_xml = xml_obj;
984 xml_obj = expanded_xml;
985 }
986
987 score = crm_element_value(xml_obj, PCMK_XA_SCORE);
988 if (score != NULL) {
989 score_i = char2score(score);
990 }
991 influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE);
992
993 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
994 set != NULL; set = pcmk__xe_next_same(set)) {
995
996 set = expand_idref(set, scheduler->input);
997 if (set == NULL) { // Configuration error, message already logged
998 if (expanded_xml != NULL) {
999 free_xml(expanded_xml);
1000 }
1001 return;
1002 }
1003
1004 if (pcmk__str_empty(pcmk__xe_id(set))) {
1006 " without " CRM_ATTR_ID);
1007 continue;
1008 }
1009 unpack_colocation_set(set, score_i, id, influence_s, scheduler);
1010
1011 if (last != NULL) {
1012 colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
1013 }
1014 last = set;
1015 }
1016
1017 if (expanded_xml) {
1018 free_xml(expanded_xml);
1019 xml_obj = orig_xml;
1020 }
1021
1022 if (last == NULL) {
1023 unpack_simple_colocation(xml_obj, id, influence_s, scheduler);
1024 }
1025}
1026
1035static void
1036mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1037 const pcmk_resource_t *reason)
1038{
1039 GList *iter = NULL;
1040 char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1041
1042 for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1043 pcmk_action_t *action = iter->data;
1044
1046 && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1047
1049 pe_action_set_reason(action, reason_text, false);
1052 }
1053 }
1054
1055 // If parent resource can't perform an action, neither can any children
1056 for (iter = rsc->children; iter != NULL; iter = iter->next) {
1057 mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1058 }
1059 free(reason_text);
1060}
1061
1072void
1074{
1075 GList *iter = NULL;
1076 GList *colocations = NULL;
1077 pcmk_resource_t *rsc = NULL;
1078 bool is_start = false;
1079
1081 return; // Only unrunnable actions block dependents
1082 }
1083
1084 is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1085 if (!is_start
1086 && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1087 return; // Only unrunnable starts and promotes block dependents
1088 }
1089
1090 CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions
1091
1092 /* If this resource is part of a collective resource, dependents are blocked
1093 * only if all instances of the collective are unrunnable, so check the
1094 * collective resource.
1095 */
1096 rsc = uber_parent(action->rsc);
1097 if (rsc->parent != NULL) {
1098 rsc = rsc->parent; // Bundle
1099 }
1100
1101 // Colocation fails only if entire primary can't reach desired role
1102 for (iter = rsc->children; iter != NULL; iter = iter->next) {
1103 pcmk_resource_t *child = iter->data;
1104 pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1105 action->task, NULL);
1106
1107 if ((child_action == NULL)
1108 || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1109 crm_trace("Not blocking %s colocation dependents because "
1110 "at least %s has runnable %s",
1111 rsc->id, child->id, action->task);
1112 return; // At least one child can reach desired role
1113 }
1114 }
1115
1116 crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1117 rsc->id, action->rsc->id, action->task);
1118
1119 // Check each colocation where this resource is primary
1120 colocations = pcmk__with_this_colocations(rsc);
1121 for (iter = colocations; iter != NULL; iter = iter->next) {
1122 pcmk__colocation_t *colocation = iter->data;
1123
1124 if (colocation->score < PCMK_SCORE_INFINITY) {
1125 continue; // Only mandatory colocations block dependent
1126 }
1127
1128 /* If the primary can't start, the dependent can't reach its colocated
1129 * role, regardless of what the primary or dependent colocation role is.
1130 *
1131 * If the primary can't be promoted, the dependent can't reach its
1132 * colocated role if the primary's colocation role is promoted.
1133 */
1134 if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1135 continue;
1136 }
1137
1138 // Block the dependent from reaching its colocated role
1139 if (colocation->dependent_role == pcmk_role_promoted) {
1140 mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1141 action->rsc);
1142 } else {
1143 mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1144 action->rsc);
1145 }
1146 }
1147 g_list_free(colocations);
1148}
1149
1168static const pcmk_resource_t *
1169get_resource_for_role(const pcmk_resource_t *rsc)
1170{
1172 const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1173
1174 if (child != NULL) {
1175 return child;
1176 }
1177 }
1178 return rsc;
1179}
1180
1201 const pcmk_resource_t *primary,
1202 const pcmk__colocation_t *colocation, bool preview)
1203{
1204 const pcmk_resource_t *dependent_role_rsc = NULL;
1205 const pcmk_resource_t *primary_role_rsc = NULL;
1206
1207 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1208 && (colocation != NULL));
1209
1210 if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1211 // Primary resource has not been assigned yet, so we can't do anything
1213 }
1214
1215 dependent_role_rsc = get_resource_for_role(dependent);
1216 primary_role_rsc = get_resource_for_role(primary);
1217
1218 if ((colocation->dependent_role >= pcmk_role_unpromoted)
1219 && (dependent_role_rsc->parent != NULL)
1220 && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1221 && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1222
1223 /* This is a colocation by role, and the dependent is a promotable clone
1224 * that has already been assigned, so the colocation should now affect
1225 * the role.
1226 */
1228 }
1229
1230 if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1231 /* The dependent resource has already been through assignment, so the
1232 * constraint no longer has any effect. Log an error if a mandatory
1233 * colocation constraint has been violated.
1234 */
1235
1236 const pcmk_node_t *primary_node = primary->allocated_to;
1237
1238 if (dependent->allocated_to == NULL) {
1239 crm_trace("Skipping colocation '%s': %s will not run anywhere",
1240 colocation->id, dependent->id);
1241
1242 } else if (colocation->score >= PCMK_SCORE_INFINITY) {
1243 // Dependent resource must colocate with primary resource
1244
1245 if (!pcmk__same_node(primary_node, dependent->allocated_to)) {
1246 pcmk__sched_err("%s must be colocated with %s but is not "
1247 "(%s vs. %s)",
1248 dependent->id, primary->id,
1249 pcmk__node_name(dependent->allocated_to),
1250 pcmk__node_name(primary_node));
1251 }
1252
1253 } else if (colocation->score <= -PCMK_SCORE_INFINITY) {
1254 // Dependent resource must anti-colocate with primary resource
1255
1256 if (pcmk__same_node(dependent->allocated_to, primary_node)) {
1257 pcmk__sched_err("%s and %s must be anti-colocated but are "
1258 "assigned to the same node (%s)",
1259 dependent->id, primary->id,
1260 pcmk__node_name(primary_node));
1261 }
1262 }
1264 }
1265
1266 if ((colocation->dependent_role != pcmk_role_unknown)
1267 && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1268 crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1269
1270 "but %s next role is %s",
1271 ((colocation->score < 0)? "anti-" : ""),
1272 colocation->id, pcmk_role_text(colocation->dependent_role),
1273 dependent_role_rsc->id,
1274 pcmk_role_text(dependent_role_rsc->next_role));
1276 }
1277
1278 if ((colocation->primary_role != pcmk_role_unknown)
1279 && (colocation->primary_role != primary_role_rsc->next_role)) {
1280 crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1281 "but %s next role is %s",
1282 ((colocation->score < 0)? "anti-" : ""),
1283 colocation->id, pcmk_role_text(colocation->primary_role),
1284 primary_role_rsc->id,
1285 pcmk_role_text(primary_role_rsc->next_role));
1287 }
1288
1290}
1291
1303void
1305 const pcmk_resource_t *primary,
1306 const pcmk__colocation_t *colocation)
1307{
1308 const char *attr = colocation->node_attribute;
1309 const char *value = NULL;
1310 GHashTable *work = NULL;
1311 GHashTableIter iter;
1312 pcmk_node_t *node = NULL;
1313
1314 if (primary->allocated_to != NULL) {
1315 value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1316 primary);
1317
1318 } else if (colocation->score < 0) {
1319 // Nothing to do (anti-colocation with something that is not running)
1320 return;
1321 }
1322
1323 work = pcmk__copy_node_table(dependent->allowed_nodes);
1324
1325 g_hash_table_iter_init(&iter, work);
1326 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1327 if (primary->allocated_to == NULL) {
1328 node->weight = pcmk__add_scores(-colocation->score, node->weight);
1329 pcmk__rsc_trace(dependent,
1330 "Applied %s to %s score on %s (now %s after "
1331 "subtracting %s because primary %s inactive)",
1332 colocation->id, dependent->id,
1333 pcmk__node_name(node),
1335 pcmk_readable_score(colocation->score), primary->id);
1336 continue;
1337 }
1338
1339 if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1340 value, pcmk__str_casei)) {
1341
1342 /* Add colocation score only if optional (or minus infinity). A
1343 * mandatory colocation is a requirement rather than a preference,
1344 * so we don't need to consider it for relative assignment purposes.
1345 * The resource will simply be forbidden from running on the node if
1346 * the primary isn't active there (via the condition above).
1347 */
1348 if (colocation->score < PCMK_SCORE_INFINITY) {
1349 node->weight = pcmk__add_scores(colocation->score,
1350 node->weight);
1351 pcmk__rsc_trace(dependent,
1352 "Applied %s to %s score on %s (now %s after "
1353 "adding %s)",
1354 colocation->id, dependent->id,
1355 pcmk__node_name(node),
1357 pcmk_readable_score(colocation->score));
1358 }
1359 continue;
1360 }
1361
1362 if (colocation->score >= PCMK_SCORE_INFINITY) {
1363 /* Only mandatory colocations are relevant when the colocation
1364 * attribute doesn't match, because an attribute not matching is not
1365 * a negative preference -- the colocation is simply relevant only
1366 * where it matches.
1367 */
1368 node->weight = -PCMK_SCORE_INFINITY;
1369 pcmk__rsc_trace(dependent,
1370 "Banned %s from %s because colocation %s attribute %s "
1371 "does not match",
1372 dependent->id, pcmk__node_name(node),
1373 colocation->id, attr);
1374 }
1375 }
1376
1377 if ((colocation->score <= -PCMK_SCORE_INFINITY)
1378 || (colocation->score >= PCMK_SCORE_INFINITY)
1379 || pcmk__any_node_available(work)) {
1380
1381 g_hash_table_destroy(dependent->allowed_nodes);
1382 dependent->allowed_nodes = work;
1383 work = NULL;
1384
1385 } else {
1386 pcmk__rsc_info(dependent,
1387 "%s: Rolling back scores from %s (no available nodes)",
1388 dependent->id, primary->id);
1389 }
1390
1391 if (work != NULL) {
1392 g_hash_table_destroy(work);
1393 }
1394}
1395
1409int
1411 const pcmk_resource_t *primary,
1412 const pcmk__colocation_t *colocation)
1413{
1414 const char *dependent_value = NULL;
1415 const char *primary_value = NULL;
1416 const char *attr = colocation->node_attribute;
1417 int score_multiplier = 1;
1418 int priority_delta = 0;
1419
1420 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1421 && (colocation != NULL));
1422
1423 if (dependent->allocated_to == NULL) {
1424 return 0;
1425 }
1426
1427 if ((primary->allocated_to != NULL)
1428 && (colocation->primary_role != pcmk_role_unknown)) {
1429 /* Colocation applies only if the primary's next role matches.
1430 *
1431 * If primary->allocated_to == NULL, we want to proceed past this block,
1432 * so that dependent->allocated_to is marked ineligible for promotion.
1433 *
1434 * @TODO Why ignore a mandatory colocation in this case when we apply
1435 * its negation in the mismatched value case?
1436 */
1437 const pcmk_resource_t *role_rsc = get_resource_for_role(primary);
1438
1439 if (colocation->primary_role != role_rsc->next_role) {
1440 return 0;
1441 }
1442 }
1443
1444 dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1445 dependent);
1446 primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1447 primary);
1448
1449 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1450 if ((colocation->score == PCMK_SCORE_INFINITY)
1451 && (colocation->dependent_role == pcmk_role_promoted)) {
1452
1453 /* For a mandatory promoted-role colocation, mark the dependent node
1454 * ineligible to promote the dependent if its attribute value
1455 * doesn't match the primary node's
1456 */
1457 score_multiplier = -1;
1458
1459 } else {
1460 // Otherwise, ignore the colocation if attribute values don't match
1461 return 0;
1462 }
1463
1464 } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1465 /* Node attribute values matched, so we want to avoid promoting the
1466 * dependent on this node
1467 */
1468 score_multiplier = -1;
1469 }
1470
1471 priority_delta = score_multiplier * colocation->score;
1472 dependent->priority = pcmk__add_scores(priority_delta, dependent->priority);
1473 pcmk__rsc_trace(dependent,
1474 "Applied %s to %s promotion priority (now %s after %s %d)",
1475 colocation->id, dependent->id,
1476 pcmk_readable_score(dependent->priority),
1477 ((score_multiplier == 1)? "adding" : "subtracting"),
1478 colocation->score);
1479
1480 return priority_delta;
1481}
1482
1492static int
1493best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1494 pcmk_resource_t *rsc, const char *attr,
1495 const char *value)
1496{
1497 GHashTable *allowed_nodes_orig = NULL;
1498 GHashTableIter iter;
1499 pcmk_node_t *node = NULL;
1500 int best_score = -PCMK_SCORE_INFINITY;
1501 const char *best_node = NULL;
1502
1503 if ((colocation != NULL) && (rsc == colocation->dependent)
1504 && pcmk_is_set(colocation->flags, pcmk__coloc_explicit)
1505 && pcmk__is_group(rsc->parent)
1506 && (rsc != rsc->parent->children->data)) {
1507 /* The resource is a user-configured colocation's explicit dependent,
1508 * and a group member other than the first, which means the group's
1509 * location constraint scores were not applied to it (see
1510 * pcmk__group_apply_location()). Explicitly consider those scores now.
1511 *
1512 * @TODO This does leave one suboptimal case: if the group itself or
1513 * another member other than the first is explicitly colocated with
1514 * the same primary, the primary will count the group's location scores
1515 * multiple times. This is much less likely than a single member being
1516 * explicitly colocated, so it's an acceptable tradeoff for now.
1517 */
1518 allowed_nodes_orig = rsc->allowed_nodes;
1519 rsc->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1520 for (GList *loc_iter = rsc->cluster->placement_constraints;
1521 loc_iter != NULL; loc_iter = loc_iter->next) {
1522
1523 pcmk__location_t *location = loc_iter->data;
1524
1525 if (location->rsc == rsc->parent) {
1526 rsc->cmds->apply_location(rsc, location);
1527 }
1528 }
1529 }
1530
1531 // Find best allowed node with matching attribute
1532 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1533 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1534
1535 if ((node->weight > best_score)
1536 && pcmk__node_available(node, false, false)
1537 && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1538 pcmk__str_casei)) {
1539
1540 best_score = node->weight;
1541 best_node = node->details->uname;
1542 }
1543 }
1544
1545 if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1546 if (best_node == NULL) {
1547 crm_info("No allowed node for %s matches node attribute %s=%s",
1548 rsc->id, attr, value);
1549 } else {
1550 crm_info("Allowed node %s for %s had best score (%d) "
1551 "of those matching node attribute %s=%s",
1552 best_node, rsc->id, best_score, attr, value);
1553 }
1554 }
1555
1556 if (allowed_nodes_orig != NULL) {
1557 g_hash_table_destroy(rsc->allowed_nodes);
1558 rsc->allowed_nodes = allowed_nodes_orig;
1559 }
1560 return best_score;
1561}
1562
1571static bool
1572allowed_on_one(const pcmk_resource_t *rsc)
1573{
1574 GHashTableIter iter;
1575 pcmk_node_t *allowed_node = NULL;
1576 int allowed_nodes = 0;
1577
1578 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1579 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1580 if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1581 pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1582 return false;
1583 }
1584 }
1585 pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1586 ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1587 return (allowed_nodes == 1);
1588}
1589
1608static void
1609add_node_scores_matching_attr(GHashTable *nodes,
1610 pcmk_resource_t *source_rsc,
1611 const pcmk_resource_t *target_rsc,
1612 const pcmk__colocation_t *colocation,
1613 float factor, bool only_positive)
1614{
1615 GHashTableIter iter;
1616 pcmk_node_t *node = NULL;
1617 const char *attr = colocation->node_attribute;
1618
1619 // Iterate through each node
1620 g_hash_table_iter_init(&iter, nodes);
1621 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1622 float delta_f = 0;
1623 int delta = 0;
1624 int score = 0;
1625 int new_score = 0;
1626 const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1627
1628 score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1629
1630 if ((factor < 0) && (score < 0)) {
1631 /* If the dependent is anti-colocated, we generally don't want the
1632 * primary to prefer nodes that the dependent avoids. That could
1633 * lead to unnecessary shuffling of the primary when the dependent
1634 * hits its migration threshold somewhere, for example.
1635 *
1636 * However, there are cases when it is desirable. If the dependent
1637 * can't run anywhere but where the primary is, it would be
1638 * worthwhile to move the primary for the sake of keeping the
1639 * dependent active.
1640 *
1641 * We can't know that exactly at this point since we don't know
1642 * where the primary will be assigned, but we can limit considering
1643 * the preference to when the dependent is allowed only on one node.
1644 * This is less than ideal for multiple reasons:
1645 *
1646 * - the dependent could be allowed on more than one node but have
1647 * anti-colocation primaries on each;
1648 * - the dependent could be a clone or bundle with multiple
1649 * instances, and the dependent as a whole is allowed on multiple
1650 * nodes but some instance still can't run
1651 * - the dependent has considered node-specific criteria such as
1652 * location constraints and stickiness by this point, but might
1653 * have other factors that end up disallowing a node
1654 *
1655 * but the alternative is making the primary move when it doesn't
1656 * need to.
1657 *
1658 * We also consider the primary's stickiness and influence, so the
1659 * user has some say in the matter. (This is the configured primary,
1660 * not a particular instance of the primary, but that doesn't matter
1661 * unless stickiness uses a rule to vary by node, and that seems
1662 * acceptable to ignore.)
1663 */
1664 if ((colocation->primary->stickiness >= -score)
1665 || !pcmk__colocation_has_influence(colocation, NULL)
1666 || !allowed_on_one(colocation->dependent)) {
1667 crm_trace("%s: Filtering %d + %f * %d "
1668 "(double negative disallowed)",
1669 pcmk__node_name(node), node->weight, factor, score);
1670 continue;
1671 }
1672 }
1673
1674 if (node->weight == INFINITY_HACK) {
1675 crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1676 pcmk__node_name(node), node->weight, factor, score);
1677 continue;
1678 }
1679
1680 delta_f = factor * score;
1681
1682 // Round the number; see http://c-faq.com/fp/round.html
1683 delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1684
1685 /* Small factors can obliterate the small scores that are often actually
1686 * used in configurations. If the score and factor are nonzero, ensure
1687 * that the result is nonzero as well.
1688 */
1689 if ((delta == 0) && (score != 0)) {
1690 if (factor > 0.0) {
1691 delta = 1;
1692 } else if (factor < 0.0) {
1693 delta = -1;
1694 }
1695 }
1696
1697 new_score = pcmk__add_scores(delta, node->weight);
1698
1699 if (only_positive && (new_score < 0) && (node->weight > 0)) {
1700 crm_trace("%s: Filtering %d + %f * %d = %d "
1701 "(negative disallowed, marking node unusable)",
1702 pcmk__node_name(node), node->weight, factor, score,
1703 new_score);
1704 node->weight = INFINITY_HACK;
1705 continue;
1706 }
1707
1708 if (only_positive && (new_score < 0) && (node->weight == 0)) {
1709 crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1710 pcmk__node_name(node), node->weight, factor, score,
1711 new_score);
1712 continue;
1713 }
1714
1715 crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1716 node->weight, factor, score, new_score);
1717 node->weight = new_score;
1718 }
1719}
1720
1751void
1753 const pcmk_resource_t *target_rsc,
1754 const char *log_id,
1755 GHashTable **nodes,
1756 const pcmk__colocation_t *colocation,
1757 float factor, uint32_t flags)
1758{
1759 GHashTable *work = NULL;
1760
1761 CRM_ASSERT((source_rsc != NULL) && (nodes != NULL)
1762 && ((colocation != NULL)
1763 || ((target_rsc == NULL) && (*nodes == NULL))));
1764
1765 if (log_id == NULL) {
1766 log_id = source_rsc->id;
1767 }
1768
1769 // Avoid infinite recursion
1770 if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1771 pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1772 log_id, source_rsc->id);
1773 return;
1774 }
1776
1777 if (*nodes == NULL) {
1778 work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1779 target_rsc = source_rsc;
1780 } else {
1782
1783 pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1784 log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1785 work = pcmk__copy_node_table(*nodes);
1786 add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1787 factor, pos);
1788 }
1789
1790 if (work == NULL) {
1792 return;
1793 }
1794
1795 if (pcmk__any_node_available(work)) {
1796 GList *colocations = NULL;
1797
1799 colocations = pcmk__this_with_colocations(source_rsc);
1800 pcmk__rsc_trace(source_rsc,
1801 "Checking additional %d optional '%s with' "
1802 "constraints",
1803 g_list_length(colocations), source_rsc->id);
1804 } else {
1805 colocations = pcmk__with_this_colocations(source_rsc);
1806 pcmk__rsc_trace(source_rsc,
1807 "Checking additional %d optional 'with %s' "
1808 "constraints",
1809 g_list_length(colocations), source_rsc->id);
1810 }
1812
1813 for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1814 pcmk__colocation_t *constraint = iter->data;
1815
1816 pcmk_resource_t *other = NULL;
1817 float other_factor = factor * constraint->score
1818 / (float) PCMK_SCORE_INFINITY;
1819
1821 other = constraint->primary;
1822 } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1823 continue;
1824 } else {
1825 other = constraint->dependent;
1826 }
1827
1828 pcmk__rsc_trace(source_rsc,
1829 "Optionally merging score of '%s' constraint "
1830 "(%s with %s)",
1831 constraint->id, constraint->dependent->id,
1832 constraint->primary->id);
1833 other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1834 &work, constraint,
1835 other_factor, flags);
1836 pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1837 }
1838 g_list_free(colocations);
1839
1841 pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1842 log_id, source_rsc->id);
1843 g_hash_table_destroy(work);
1845 return;
1846 }
1847
1848
1850 pcmk_node_t *node = NULL;
1851 GHashTableIter iter;
1852
1853 g_hash_table_iter_init(&iter, work);
1854 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1855 if (node->weight == INFINITY_HACK) {
1856 node->weight = 1;
1857 }
1858 }
1859 }
1860
1861 if (*nodes != NULL) {
1862 g_hash_table_destroy(*nodes);
1863 }
1864 *nodes = work;
1865
1867}
1868
1876void
1877pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1878{
1879 pcmk__colocation_t *colocation = data;
1880 pcmk_resource_t *target_rsc = user_data;
1881
1882 pcmk_resource_t *source_rsc = colocation->dependent;
1883 const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1885
1886 if (!pcmk__colocation_has_influence(colocation, NULL)) {
1887 return;
1888 }
1889 if (pcmk__is_clone(target_rsc)) {
1891 }
1892 pcmk__rsc_trace(target_rsc,
1893 "%s: Incorporating attenuated %s assignment scores due "
1894 "to colocation %s",
1895 target_rsc->id, source_rsc->id, colocation->id);
1896 source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1897 source_rsc->id,
1898 &target_rsc->allowed_nodes,
1899 colocation, factor, flags);
1900}
1901
1919void
1921 const pcmk_resource_t *primary,
1922 const pcmk__colocation_t *colocation,
1923 const GList *primary_nodes, bool merge_scores)
1924{
1925 GHashTableIter iter;
1926 pcmk_node_t *dependent_node = NULL;
1927
1928 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1929 && (colocation != NULL));
1930
1931 g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1932 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1933 const pcmk_node_t *primary_node = NULL;
1934
1935 primary_node = pe_find_node_id(primary_nodes,
1936 dependent_node->details->id);
1937 if (primary_node == NULL) {
1938 dependent_node->weight = -PCMK_SCORE_INFINITY;
1939 pcmk__rsc_trace(dependent,
1940 "Banning %s from %s (no primary instance) for %s",
1941 dependent->id, pcmk__node_name(dependent_node),
1942 colocation->id);
1943
1944 } else if (merge_scores) {
1945 dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1946 primary_node->weight);
1947 pcmk__rsc_trace(dependent,
1948 "Added %s's score %s to %s's score for %s (now %s) "
1949 "for colocation %s",
1950 primary->id, pcmk_readable_score(primary_node->weight),
1951 dependent->id, pcmk__node_name(dependent_node),
1952 pcmk_readable_score(dependent_node->weight),
1953 colocation->id);
1954 }
1955 }
1956}
1957
1968GList *
1970{
1971 GList *list = NULL;
1972
1973 rsc->cmds->with_this_colocations(rsc, rsc, &list);
1974 return list;
1975}
1976
1987GList *
1989{
1990 GList *list = NULL;
1991
1992 rsc->cmds->this_with_colocations(rsc, rsc, &list);
1993 return list;
1994}
@ pcmk__ar_if_required_on_same_node
Ordering applies only if 'first' is required and on same node as 'then'.
#define PCMK_ACTION_STOP
Definition actions.h:75
#define PCMK_ACTION_PROMOTE
Definition actions.h:66
#define PCMK_ACTION_START
Definition actions.h:72
@ pcmk_action_runnable
Definition actions.h:207
#define PCMK_ACTION_DEMOTE
Definition actions.h:49
#define pcmk__clear_action_flags(action, flags_to_clear)
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition nvpair.c:909
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition nvpair.c:936
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
uint64_t flags
Definition remote.c:3
Utility functions.
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:496
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition complex.c:1007
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
A dumping ground.
#define CRM_ATTR_UNAME
Definition crm.h:99
#define CRM_ATTR_ID
Definition crm.h:100
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag)
@ pcmk__coloc_select_active
@ pcmk__coloc_select_this_with
@ pcmk__coloc_select_nonnegative
G_GNUC_INTERNAL bool pcmk__any_node_available(GHashTable *nodes)
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler)
pcmk__coloc_affects
@ pcmk__coloc_affects_nothing
@ pcmk__coloc_affects_location
@ pcmk__coloc_affects_role
@ pcmk__coloc_none
@ pcmk__coloc_influence
@ pcmk__coloc_explicit
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
G_GNUC_INTERNAL GHashTable * pcmk__copy_node_table(GHashTable *nodes)
#define crm_info(fmt, args...)
Definition logging.h:397
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
@ pcmk__wo_coloc_inst
@ pcmk__wo_set_ordering
#define pcmk__if_tracing(if_action, else_action)
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_scheduler_t * scheduler
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
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:301
#define PCMK__VALUE_GROUP
const char * action
Definition pcmk_fence.c:30
void pcmk__add_this_with_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
#define INFINITY_HACK
GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
void pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, const GList *primary_nodes, bool merge_scores)
void pcmk__new_colocation(const char *id, const char *node_attr, int score, pcmk_resource_t *dependent, pcmk_resource_t *primary, const char *dependent_role, const char *primary_role, uint32_t flags)
void pcmk__add_dependent_scores(gpointer data, gpointer user_data)
void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
int pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
enum pcmk__coloc_affects pcmk__colocation_affects(const pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool preview)
void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
void pcmk__block_colocation_dependents(pcmk_action_t *action)
void pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
void pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
pcmk_resource_t rsc2
pcmk_resource_t rsc1
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition internal.h:176
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition clone.c:229
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition bundle.c:129
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
@ pcmk_rsc_promotable
Definition resources.h:106
@ pcmk_rsc_unassigned
Definition resources.h:109
@ pcmk_rsc_critical
Definition resources.h:130
@ pcmk_rsc_replica_container
Definition resources.h:163
@ pcmk_rsc_updating_nodes
Definition resources.h:115
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_unpack_error
Definition results.h:125
#define PCMK_ROLE_STARTED
Definition roles.h:26
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition roles.c:59
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
@ pcmk_role_unpromoted
Unpromoted.
Definition roles.h:38
@ pcmk_role_promoted
Promoted.
Definition roles.h:39
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
#define PCMK__ROLE_UNKNOWN
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__sched_err(fmt...)
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition scores.c:86
int char2score(const char *score)
Get the integer value of a score string.
Definition scores.c:36
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:24
int pcmk__add_scores(int score1, int score2)
Definition scores.c:116
Cluster status and scheduling.
pcmk_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition status.c:487
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
const char * node_attribute
pcmk_resource_t * primary
pcmk_resource_t * dependent
Location constraint object.
pcmk_resource_t * rsc
enum pe_action_flags flags
Definition actions.h:349
int weight
Definition nodes.h:162
struct pe_node_shared_s * details
Definition nodes.h:167
const char * id
Definition nodes.h:72
pcmk_assignment_methods_t * cmds
Definition resources.h:413
GList * actions
Definition resources.h:444
enum pe_obj_types variant
Definition resources.h:410
GList * rsc_cons
Definition resources.h:442
GList * rsc_cons_lhs
Definition resources.h:441
GList * children
Definition resources.h:471
pcmk_scheduler_t * cluster
Definition resources.h:408
pcmk_node_t * allocated_to
Definition resources.h:447
GHashTable * allowed_nodes
Definition resources.h:462
unsigned long long flags
Definition resources.h:428
enum rsc_role_e next_role
Definition resources.h:465
pcmk_resource_t * parent
Definition resources.h:409
GList * colocation_constraints
Definition scheduler.h:234
xmlNode * input
Definition scheduler.h:196
GList * resources
Definition scheduler.h:231
GList * placement_constraints
Definition scheduler.h:232
void(* this_with_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
void(* with_this_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
void(* add_colocated_node_scores)(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
void(* apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location)
Wrappers for and extensions to libxml2.
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2152
void free_xml(xmlNode *child)
Definition xml.c:867
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition xml.c:652
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
#define PCMK_XA_RSC_ROLE
Definition xml_names.h:385
#define PCMK_XA_SCORE
Definition xml_names.h:391
#define PCMK_XA_SYMMETRICAL
Definition xml_names.h:410
#define PCMK_XA_WITH_RSC_ROLE
Definition xml_names.h:448
#define PCMK_XE_RESOURCE_REF
Definition xml_names.h:173
#define PCMK_XA_NODE_ATTRIBUTE
Definition xml_names.h:331
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_WITH_RSC
Definition xml_names.h:447
#define PCMK_XA_ROLE
Definition xml_names.h:382
#define PCMK_XE_RSC_COLOCATION
Definition xml_names.h:181
#define PCMK_XA_INFLUENCE
Definition xml_names.h:301
#define PCMK_XE_RESOURCE_SET
Definition xml_names.h:174
#define PCMK_XA_SEQUENTIAL
Definition xml_names.h:393
#define PCMK_XA_RSC
Definition xml_names.h:383
#define PCMK__XA_RSC_INSTANCE
#define PCMK__XA_ORDERING
#define PCMK__XA_WITH_RSC_INSTANCE