pacemaker 2.1.7-2.1.7
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-2023 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/msg_xml.h"
24
25// Used to temporarily mark a node as unusable
26#define INFINITY_HACK (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 */
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 pe_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 pe_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 pe_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 = calloc(1, sizeof(pcmk__colocation_t));
371 CRM_ASSERT(new_con != NULL);
372
373 if (pcmk__str_eq(dependent_role, PCMK__ROLE_STARTED,
375 dependent_role = PCMK__ROLE_UNKNOWN;
376 }
377
378 if (pcmk__str_eq(primary_role, PCMK__ROLE_STARTED,
380 primary_role = PCMK__ROLE_UNKNOWN;
381 }
382
383 new_con->id = id;
384 new_con->dependent = dependent;
385 new_con->primary = primary;
386 new_con->score = score;
387 new_con->dependent_role = text2role(dependent_role);
388 new_con->primary_role = text2role(primary_role);
389 new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
390 new_con->flags = flags;
391
392 pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent);
393 pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary);
394
395 dependent->cluster->colocation_constraints = g_list_prepend(
396 dependent->cluster->colocation_constraints, new_con);
397
398 if (score <= -INFINITY) {
399 anti_colocation_order(dependent, new_con->dependent_role, primary,
400 new_con->primary_role);
401 anti_colocation_order(primary, new_con->primary_role, dependent,
402 new_con->dependent_role);
403 }
404}
405
418static uint32_t
419unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
420 const char *influence_s)
421{
422 if (influence_s != NULL) {
423 int influence_i = 0;
424
425 if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
426 pcmk__config_err("Constraint '%s' has invalid value for "
427 XML_COLOC_ATTR_INFLUENCE " (using default)",
428 coloc_id);
429 } else {
430 return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
431 }
432 }
435 }
436 return pcmk__coloc_none;
437}
438
439static void
440unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
441 const char *influence_s, pcmk_scheduler_t *scheduler)
442{
443 xmlNode *xml_rsc = NULL;
444 pcmk_resource_t *other = NULL;
445 pcmk_resource_t *resource = NULL;
446 const char *set_id = ID(set);
447 const char *role = crm_element_value(set, "role");
448 bool with_previous = false;
449 int local_score = score;
450 bool sequential = false;
451 uint32_t flags = pcmk__coloc_none;
452 const char *xml_rsc_id = NULL;
453 const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
454
455 if (score_s) {
456 local_score = char2score(score_s);
457 }
458 if (local_score == 0) {
459 crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
460 coloc_id, set_id);
461 return;
462 }
463
464 /* @COMPAT The deprecated "ordering" attribute specifies whether resources
465 * in a positive-score set are colocated with the previous or next resource.
466 */
467 if (pcmk__str_eq(crm_element_value(set, "ordering"), "group",
469 with_previous = true;
470 } else {
472 "Support for 'ordering' other than 'group' in "
473 XML_CONS_TAG_RSC_SET " (such as %s) is deprecated and "
474 "will be removed in a future release", set_id);
475 }
476
477 if ((pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok)
478 && !sequential) {
479 return;
480 }
481
482 if (local_score > 0) {
483 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
484 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
485
486 xml_rsc_id = ID(xml_rsc);
488 xml_rsc_id);
489 if (resource == NULL) {
490 // Should be possible only with validation disabled
491 pcmk__config_err("Ignoring %s and later resources in set %s: "
492 "No such resource", xml_rsc_id, set_id);
493 return;
494 }
495 if (other != NULL) {
497 | unpack_influence(coloc_id, resource, influence_s);
498 if (with_previous) {
499 pe_rsc_trace(resource, "Colocating %s with %s in set %s",
500 resource->id, other->id, set_id);
501 pcmk__new_colocation(set_id, NULL, local_score, resource,
502 other, role, role, flags);
503 } else {
504 pe_rsc_trace(resource, "Colocating %s with %s in set %s",
505 other->id, resource->id, set_id);
506 pcmk__new_colocation(set_id, NULL, local_score, other,
507 resource, role, role, flags);
508 }
509 }
510 other = resource;
511 }
512
513 } else {
514 /* Anti-colocating with every prior resource is
515 * the only way to ensure the intuitive result
516 * (i.e. that no one in the set can run with anyone else in the set)
517 */
518
519 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
520 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
521
522 xmlNode *xml_rsc_with = NULL;
523
524 xml_rsc_id = ID(xml_rsc);
526 xml_rsc_id);
527 if (resource == NULL) {
528 // Should be possible only with validation disabled
529 pcmk__config_err("Ignoring %s and later resources in set %s: "
530 "No such resource", xml_rsc_id, set_id);
531 return;
532 }
534 | unpack_influence(coloc_id, resource, influence_s);
535 for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
536 xml_rsc_with != NULL;
537 xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
538
539 xml_rsc_id = ID(xml_rsc_with);
540 if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
541 break;
542 }
544 xml_rsc_id);
545 CRM_ASSERT(other != NULL); // We already processed it
546 pcmk__new_colocation(set_id, NULL, local_score,
547 resource, other, role, role, flags);
548 }
549 }
550 }
551}
552
564static void
565colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
566 int score, const char *influence_s,
568{
569 xmlNode *xml_rsc = NULL;
570 pcmk_resource_t *rsc_1 = NULL;
571 pcmk_resource_t *rsc_2 = NULL;
572
573 const char *xml_rsc_id = NULL;
574 const char *role_1 = crm_element_value(set1, "role");
575 const char *role_2 = crm_element_value(set2, "role");
576
577 int rc = pcmk_rc_ok;
578 bool sequential = false;
579 uint32_t flags = pcmk__coloc_none;
580
581 if (score == 0) {
582 crm_trace("Ignoring colocation '%s' between sets %s and %s "
583 "because score is 0", id, ID(set1), ID(set2));
584 return;
585 }
586
587 rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential);
588 if ((rc != pcmk_rc_ok) || sequential) {
589 // Get the first one
591 if (xml_rsc != NULL) {
592 xml_rsc_id = ID(xml_rsc);
594 xml_rsc_id);
595 if (rsc_1 == NULL) {
596 // Should be possible only with validation disabled
597 pcmk__config_err("Ignoring colocation of set %s with set %s "
598 "because first resource %s not found",
599 ID(set1), ID(set2), xml_rsc_id);
600 return;
601 }
602 }
603 }
604
605 rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential);
606 if ((rc != pcmk_rc_ok) || sequential) {
607 // Get the last one
608 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
609 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
610
611 xml_rsc_id = ID(xml_rsc);
612 }
614 xml_rsc_id);
615 if (rsc_2 == NULL) {
616 // Should be possible only with validation disabled
617 pcmk__config_err("Ignoring colocation of set %s with set %s "
618 "because last resource %s not found",
619 ID(set1), ID(set2), xml_rsc_id);
620 return;
621 }
622 }
623
624 if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
625 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
626 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
627 flags);
628
629 } else if (rsc_1 != NULL) { // Only set1 is sequential
630 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
631 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
632 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
633
634 xml_rsc_id = ID(xml_rsc);
636 xml_rsc_id);
637 if (rsc_2 == NULL) {
638 // Should be possible only with validation disabled
639 pcmk__config_err("Ignoring set %s colocation with resource %s "
640 "in set %s: No such resource",
641 ID(set1), xml_rsc_id, ID(set2));
642 continue;
643 }
644 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
645 role_2, flags);
646 }
647
648 } else if (rsc_2 != NULL) { // Only set2 is sequential
649 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
650 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
651
652 xml_rsc_id = ID(xml_rsc);
654 xml_rsc_id);
655 if (rsc_1 == NULL) {
656 // Should be possible only with validation disabled
657 pcmk__config_err("Ignoring colocation of set %s resource %s "
658 "with set %s: No such resource",
659 ID(set1), xml_rsc_id, ID(set2));
660 continue;
661 }
663 | unpack_influence(id, rsc_1, influence_s);
664 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
665 role_2, flags);
666 }
667
668 } else { // Neither set is sequential
669 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
670 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
671
672 xmlNode *xml_rsc_2 = NULL;
673
674 xml_rsc_id = ID(xml_rsc);
676 xml_rsc_id);
677 if (rsc_1 == NULL) {
678 // Should be possible only with validation disabled
679 pcmk__config_err("Ignoring colocation of set %s resource %s "
680 "with set %s: No such resource",
681 ID(set1), xml_rsc_id, ID(set2));
682 continue;
683 }
684
686 | unpack_influence(id, rsc_1, influence_s);
687 for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
688 xml_rsc_2 != NULL;
689 xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
690
691 xml_rsc_id = ID(xml_rsc_2);
693 xml_rsc_id);
694 if (rsc_2 == NULL) {
695 // Should be possible only with validation disabled
696 pcmk__config_err("Ignoring colocation of set %s resource "
697 "%s with set %s resource %s: No such "
698 "resource", ID(set1), ID(xml_rsc),
699 ID(set2), xml_rsc_id);
700 continue;
701 }
702 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
703 role_1, role_2, flags);
704 }
705 }
706 }
707}
708
709static void
710unpack_simple_colocation(xmlNode *xml_obj, const char *id,
711 const char *influence_s, pcmk_scheduler_t *scheduler)
712{
713 int score_i = 0;
714 uint32_t flags = pcmk__coloc_none;
715
716 const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
717 const char *dependent_id = crm_element_value(xml_obj,
719 const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
720 const char *dependent_role = crm_element_value(xml_obj,
722 const char *primary_role = crm_element_value(xml_obj,
724 const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
725
726 const char *primary_instance = NULL;
727 const char *dependent_instance = NULL;
728 pcmk_resource_t *primary = NULL;
729 pcmk_resource_t *dependent = NULL;
730
731 primary = pcmk__find_constraint_resource(scheduler->resources, primary_id);
733 dependent_id);
734
735 // @COMPAT: Deprecated since 2.1.5
736 primary_instance = crm_element_value(xml_obj,
738 dependent_instance = crm_element_value(xml_obj,
740 if (dependent_instance != NULL) {
742 "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
743 "deprecated and will be removed in a future release.");
744 }
745 if (primary_instance != NULL) {
747 "Support for " XML_COLOC_ATTR_TARGET_INSTANCE " is "
748 "deprecated and will be removed in a future release.");
749 }
750
751 if (dependent == NULL) {
752 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
753 "does not exist", id, dependent_id);
754 return;
755
756 } else if (primary == NULL) {
757 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
758 "does not exist", id, primary_id);
759 return;
760
761 } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
762 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
763 "is not a clone but instance '%s' was requested",
764 id, dependent_id, dependent_instance);
765 return;
766
767 } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
768 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
769 "is not a clone but instance '%s' was requested",
770 id, primary_id, primary_instance);
771 return;
772 }
773
774 if (dependent_instance != NULL) {
775 dependent = find_clone_instance(dependent, dependent_instance);
776 if (dependent == NULL) {
777 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
778 "does not have an instance '%s'",
779 id, dependent_id, dependent_instance);
780 return;
781 }
782 }
783
784 if (primary_instance != NULL) {
785 primary = find_clone_instance(primary, primary_instance);
786 if (primary == NULL) {
787 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
788 "does not have an instance '%s'",
789 "'%s'", id, primary_id, primary_instance);
790 return;
791 }
792 }
793
795 pcmk__config_warn("The colocation constraint '"
797 "' attribute has been removed");
798 }
799
800 if (score) {
801 score_i = char2score(score);
802 }
803
804 flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
805 pcmk__new_colocation(id, attr, score_i, dependent, primary,
806 dependent_role, primary_role, flags);
807}
808
809// \return Standard Pacemaker return code
810static int
811unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
813{
814 const char *id = NULL;
815 const char *dependent_id = NULL;
816 const char *primary_id = NULL;
817 const char *dependent_role = NULL;
818 const char *primary_role = NULL;
819
820 pcmk_resource_t *dependent = NULL;
821 pcmk_resource_t *primary = NULL;
822
823 pcmk_tag_t *dependent_tag = NULL;
824 pcmk_tag_t *primary_tag = NULL;
825
826 xmlNode *dependent_set = NULL;
827 xmlNode *primary_set = NULL;
828 bool any_sets = false;
829
830 *expanded_xml = NULL;
831
832 CRM_CHECK(xml_obj != NULL, return EINVAL);
833
834 id = ID(xml_obj);
835 if (id == NULL) {
836 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
837 xml_obj->name);
839 }
840
841 // Check whether there are any resource sets with template or tag references
842 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
843 if (*expanded_xml != NULL) {
844 crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
845 return pcmk_rc_ok;
846 }
847
848 dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
849 primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
850 if ((dependent_id == NULL) || (primary_id == NULL)) {
851 return pcmk_rc_ok;
852 }
853
854 if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
855 &dependent_tag)) {
856 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
857 "valid resource or tag", id, dependent_id);
859 }
860
861 if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
862 &primary_tag)) {
863 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
864 "valid resource or tag", id, primary_id);
866 }
867
868 if ((dependent != NULL) && (primary != NULL)) {
869 /* Neither side references any template/tag. */
870 return pcmk_rc_ok;
871 }
872
873 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
874 // A colocation constraint between two templates/tags makes no sense
875 pcmk__config_err("Ignoring constraint '%s' because two templates or "
876 "tags cannot be colocated", id);
878 }
879
880 dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
881 primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
882
883 *expanded_xml = copy_xml(xml_obj);
884
885 // Convert dependent's template/tag reference into constraint resource_set
886 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
887 true, scheduler)) {
888 free_xml(*expanded_xml);
889 *expanded_xml = NULL;
891 }
892
893 if (dependent_set != NULL) {
894 if (dependent_role != NULL) {
895 // Move "rsc-role" into converted resource_set as "role"
896 crm_xml_add(dependent_set, "role", dependent_role);
898 }
899 any_sets = true;
900 }
901
902 // Convert primary's template/tag reference into constraint resource_set
903 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
904 true, scheduler)) {
905 free_xml(*expanded_xml);
906 *expanded_xml = NULL;
908 }
909
910 if (primary_set != NULL) {
911 if (primary_role != NULL) {
912 // Move "with-rsc-role" into converted resource_set as "role"
913 crm_xml_add(primary_set, "role", primary_role);
915 }
916 any_sets = true;
917 }
918
919 if (any_sets) {
920 crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
921 } else {
922 free_xml(*expanded_xml);
923 *expanded_xml = NULL;
924 }
925
926 return pcmk_rc_ok;
927}
928
936void
938{
939 int score_i = 0;
940 xmlNode *set = NULL;
941 xmlNode *last = NULL;
942
943 xmlNode *orig_xml = NULL;
944 xmlNode *expanded_xml = NULL;
945
946 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
947 const char *score = NULL;
948 const char *influence_s = NULL;
949
950 if (pcmk__str_empty(id)) {
952 " without " CRM_ATTR_ID);
953 return;
954 }
955
956 if (unpack_colocation_tags(xml_obj, &expanded_xml,
957 scheduler) != pcmk_rc_ok) {
958 return;
959 }
960 if (expanded_xml != NULL) {
961 orig_xml = xml_obj;
962 xml_obj = expanded_xml;
963 }
964
965 score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
966 if (score != NULL) {
967 score_i = char2score(score);
968 }
969 influence_s = crm_element_value(xml_obj, XML_COLOC_ATTR_INFLUENCE);
970
971 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
972 set = crm_next_same_xml(set)) {
973
974 set = expand_idref(set, scheduler->input);
975 if (set == NULL) { // Configuration error, message already logged
976 if (expanded_xml != NULL) {
977 free_xml(expanded_xml);
978 }
979 return;
980 }
981
982 if (pcmk__str_empty(ID(set))) {
984 " without " CRM_ATTR_ID);
985 continue;
986 }
987 unpack_colocation_set(set, score_i, id, influence_s, scheduler);
988
989 if (last != NULL) {
990 colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
991 }
992 last = set;
993 }
994
995 if (expanded_xml) {
996 free_xml(expanded_xml);
997 xml_obj = orig_xml;
998 }
999
1000 if (last == NULL) {
1001 unpack_simple_colocation(xml_obj, id, influence_s, scheduler);
1002 }
1003}
1004
1013static void
1014mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1015 const pcmk_resource_t *reason)
1016{
1017 GList *iter = NULL;
1018 char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1019
1020 for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1021 pcmk_action_t *action = iter->data;
1022
1024 && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1025
1027 pe_action_set_reason(action, reason_text, false);
1030 }
1031 }
1032
1033 // If parent resource can't perform an action, neither can any children
1034 for (iter = rsc->children; iter != NULL; iter = iter->next) {
1035 mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1036 }
1037 free(reason_text);
1038}
1039
1050void
1052{
1053 GList *iter = NULL;
1054 GList *colocations = NULL;
1055 pcmk_resource_t *rsc = NULL;
1056 bool is_start = false;
1057
1059 return; // Only unrunnable actions block dependents
1060 }
1061
1062 is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1063 if (!is_start
1064 && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1065 return; // Only unrunnable starts and promotes block dependents
1066 }
1067
1068 CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions
1069
1070 /* If this resource is part of a collective resource, dependents are blocked
1071 * only if all instances of the collective are unrunnable, so check the
1072 * collective resource.
1073 */
1074 rsc = uber_parent(action->rsc);
1075 if (rsc->parent != NULL) {
1076 rsc = rsc->parent; // Bundle
1077 }
1078
1079 // Colocation fails only if entire primary can't reach desired role
1080 for (iter = rsc->children; iter != NULL; iter = iter->next) {
1081 pcmk_resource_t *child = iter->data;
1082 pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1083 action->task, NULL);
1084
1085 if ((child_action == NULL)
1086 || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1087 crm_trace("Not blocking %s colocation dependents because "
1088 "at least %s has runnable %s",
1089 rsc->id, child->id, action->task);
1090 return; // At least one child can reach desired role
1091 }
1092 }
1093
1094 crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1095 rsc->id, action->rsc->id, action->task);
1096
1097 // Check each colocation where this resource is primary
1098 colocations = pcmk__with_this_colocations(rsc);
1099 for (iter = colocations; iter != NULL; iter = iter->next) {
1100 pcmk__colocation_t *colocation = iter->data;
1101
1102 if (colocation->score < INFINITY) {
1103 continue; // Only mandatory colocations block dependent
1104 }
1105
1106 /* If the primary can't start, the dependent can't reach its colocated
1107 * role, regardless of what the primary or dependent colocation role is.
1108 *
1109 * If the primary can't be promoted, the dependent can't reach its
1110 * colocated role if the primary's colocation role is promoted.
1111 */
1112 if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1113 continue;
1114 }
1115
1116 // Block the dependent from reaching its colocated role
1117 if (colocation->dependent_role == pcmk_role_promoted) {
1118 mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1119 action->rsc);
1120 } else {
1121 mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1122 action->rsc);
1123 }
1124 }
1125 g_list_free(colocations);
1126}
1127
1146static const pcmk_resource_t *
1147get_resource_for_role(const pcmk_resource_t *rsc)
1148{
1150 const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1151
1152 if (child != NULL) {
1153 return child;
1154 }
1155 }
1156 return rsc;
1157}
1158
1179 const pcmk_resource_t *primary,
1180 const pcmk__colocation_t *colocation, bool preview)
1181{
1182 const pcmk_resource_t *dependent_role_rsc = NULL;
1183 const pcmk_resource_t *primary_role_rsc = NULL;
1184
1185 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1186 && (colocation != NULL));
1187
1188 if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1189 // Primary resource has not been assigned yet, so we can't do anything
1191 }
1192
1193 dependent_role_rsc = get_resource_for_role(dependent);
1194 primary_role_rsc = get_resource_for_role(primary);
1195
1196 if ((colocation->dependent_role >= pcmk_role_unpromoted)
1197 && (dependent_role_rsc->parent != NULL)
1198 && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1199 && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1200
1201 /* This is a colocation by role, and the dependent is a promotable clone
1202 * that has already been assigned, so the colocation should now affect
1203 * the role.
1204 */
1206 }
1207
1208 if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1209 /* The dependent resource has already been through assignment, so the
1210 * constraint no longer has any effect. Log an error if a mandatory
1211 * colocation constraint has been violated.
1212 */
1213
1214 const pcmk_node_t *primary_node = primary->allocated_to;
1215
1216 if (dependent->allocated_to == NULL) {
1217 crm_trace("Skipping colocation '%s': %s will not run anywhere",
1218 colocation->id, dependent->id);
1219
1220 } else if (colocation->score >= INFINITY) {
1221 // Dependent resource must colocate with primary resource
1222
1223 if (!pe__same_node(primary_node, dependent->allocated_to)) {
1224 crm_err("%s must be colocated with %s but is not (%s vs. %s)",
1225 dependent->id, primary->id,
1226 pe__node_name(dependent->allocated_to),
1227 pe__node_name(primary_node));
1228 }
1229
1230 } else if (colocation->score <= -CRM_SCORE_INFINITY) {
1231 // Dependent resource must anti-colocate with primary resource
1232
1233 if (pe__same_node(dependent->allocated_to, primary_node)) {
1234 crm_err("%s and %s must be anti-colocated but are assigned "
1235 "to the same node (%s)",
1236 dependent->id, primary->id,
1237 pe__node_name(primary_node));
1238 }
1239 }
1241 }
1242
1243 if ((colocation->dependent_role != pcmk_role_unknown)
1244 && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1245 crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1246
1247 "but %s next role is %s",
1248 ((colocation->score < 0)? "anti-" : ""),
1249 colocation->id, role2text(colocation->dependent_role),
1250 dependent_role_rsc->id,
1251 role2text(dependent_role_rsc->next_role));
1253 }
1254
1255 if ((colocation->primary_role != pcmk_role_unknown)
1256 && (colocation->primary_role != primary_role_rsc->next_role)) {
1257 crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1258 "but %s next role is %s",
1259 ((colocation->score < 0)? "anti-" : ""),
1260 colocation->id, role2text(colocation->primary_role),
1261 primary_role_rsc->id, role2text(primary_role_rsc->next_role));
1263 }
1264
1266}
1267
1279void
1281 const pcmk_resource_t *primary,
1282 const pcmk__colocation_t *colocation)
1283{
1284 const char *attr = colocation->node_attribute;
1285 const char *value = NULL;
1286 GHashTable *work = NULL;
1287 GHashTableIter iter;
1288 pcmk_node_t *node = NULL;
1289
1290 if (primary->allocated_to != NULL) {
1291 value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1292 primary);
1293
1294 } else if (colocation->score < 0) {
1295 // Nothing to do (anti-colocation with something that is not running)
1296 return;
1297 }
1298
1299 work = pcmk__copy_node_table(dependent->allowed_nodes);
1300
1301 g_hash_table_iter_init(&iter, work);
1302 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1303 if (primary->allocated_to == NULL) {
1304 node->weight = pcmk__add_scores(-colocation->score, node->weight);
1305 pe_rsc_trace(dependent,
1306 "Applied %s to %s score on %s (now %s after "
1307 "subtracting %s because primary %s inactive)",
1308 colocation->id, dependent->id, pe__node_name(node),
1310 pcmk_readable_score(colocation->score), primary->id);
1311 continue;
1312 }
1313
1314 if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1315 value, pcmk__str_casei)) {
1316
1317 /* Add colocation score only if optional (or minus infinity). A
1318 * mandatory colocation is a requirement rather than a preference,
1319 * so we don't need to consider it for relative assignment purposes.
1320 * The resource will simply be forbidden from running on the node if
1321 * the primary isn't active there (via the condition above).
1322 */
1323 if (colocation->score < CRM_SCORE_INFINITY) {
1324 node->weight = pcmk__add_scores(colocation->score,
1325 node->weight);
1326 pe_rsc_trace(dependent,
1327 "Applied %s to %s score on %s (now %s after "
1328 "adding %s)",
1329 colocation->id, dependent->id, pe__node_name(node),
1331 pcmk_readable_score(colocation->score));
1332 }
1333 continue;
1334 }
1335
1336 if (colocation->score >= CRM_SCORE_INFINITY) {
1337 /* Only mandatory colocations are relevant when the colocation
1338 * attribute doesn't match, because an attribute not matching is not
1339 * a negative preference -- the colocation is simply relevant only
1340 * where it matches.
1341 */
1342 node->weight = -CRM_SCORE_INFINITY;
1343 pe_rsc_trace(dependent,
1344 "Banned %s from %s because colocation %s attribute %s "
1345 "does not match",
1346 dependent->id, pe__node_name(node), colocation->id,
1347 attr);
1348 }
1349 }
1350
1351 if ((colocation->score <= -INFINITY) || (colocation->score >= INFINITY)
1352 || pcmk__any_node_available(work)) {
1353
1354 g_hash_table_destroy(dependent->allowed_nodes);
1355 dependent->allowed_nodes = work;
1356 work = NULL;
1357
1358 } else {
1359 pe_rsc_info(dependent,
1360 "%s: Rolling back scores from %s (no available nodes)",
1361 dependent->id, primary->id);
1362 }
1363
1364 if (work != NULL) {
1365 g_hash_table_destroy(work);
1366 }
1367}
1368
1380void
1382 const pcmk_resource_t *primary,
1383 const pcmk__colocation_t *colocation)
1384{
1385 const char *dependent_value = NULL;
1386 const char *primary_value = NULL;
1387 const char *attr = colocation->node_attribute;
1388 int score_multiplier = 1;
1389
1390 const pcmk_resource_t *primary_role_rsc = NULL;
1391
1392 CRM_ASSERT((dependent != NULL) && (primary != NULL) &&
1393 (colocation != NULL));
1394
1395 if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1396 return;
1397 }
1398
1399 dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1400 dependent);
1401 primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1402 primary);
1403
1404 primary_role_rsc = get_resource_for_role(primary);
1405
1406 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1407 if ((colocation->score == INFINITY)
1408 && (colocation->dependent_role == pcmk_role_promoted)) {
1409 dependent->priority = -INFINITY;
1410 }
1411 return;
1412 }
1413
1414 if ((colocation->primary_role != pcmk_role_unknown)
1415 && (colocation->primary_role != primary_role_rsc->next_role)) {
1416 return;
1417 }
1418
1419 if (colocation->dependent_role == pcmk_role_unpromoted) {
1420 score_multiplier = -1;
1421 }
1422
1423 dependent->priority = pcmk__add_scores(score_multiplier * colocation->score,
1424 dependent->priority);
1425 pe_rsc_trace(dependent,
1426 "Applied %s to %s promotion priority (now %s after %s %s)",
1427 colocation->id, dependent->id,
1428 pcmk_readable_score(dependent->priority),
1429 ((score_multiplier == 1)? "adding" : "subtracting"),
1430 pcmk_readable_score(colocation->score));
1431}
1432
1441static int
1442best_node_score_matching_attr(const pcmk_resource_t *rsc, const char *attr,
1443 const char *value)
1444{
1445 GHashTableIter iter;
1446 pcmk_node_t *node = NULL;
1447 int best_score = -INFINITY;
1448 const char *best_node = NULL;
1449
1450 // Find best allowed node with matching attribute
1451 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1452 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1453
1454 if ((node->weight > best_score)
1455 && pcmk__node_available(node, false, false)
1456 && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1457 pcmk__str_casei)) {
1458
1459 best_score = node->weight;
1460 best_node = node->details->uname;
1461 }
1462 }
1463
1464 if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1465 if (best_node == NULL) {
1466 crm_info("No allowed node for %s matches node attribute %s=%s",
1467 rsc->id, attr, value);
1468 } else {
1469 crm_info("Allowed node %s for %s had best score (%d) "
1470 "of those matching node attribute %s=%s",
1471 best_node, rsc->id, best_score, attr, value);
1472 }
1473 }
1474 return best_score;
1475}
1476
1485static bool
1486allowed_on_one(const pcmk_resource_t *rsc)
1487{
1488 GHashTableIter iter;
1489 pcmk_node_t *allowed_node = NULL;
1490 int allowed_nodes = 0;
1491
1492 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1493 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1494 if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1495 pe_rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1496 return false;
1497 }
1498 }
1499 pe_rsc_trace(rsc, "%s is allowed %s", rsc->id,
1500 ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1501 return (allowed_nodes == 1);
1502}
1503
1522static void
1523add_node_scores_matching_attr(GHashTable *nodes,
1524 const pcmk_resource_t *source_rsc,
1525 const pcmk_resource_t *target_rsc,
1526 const pcmk__colocation_t *colocation,
1527 float factor, bool only_positive)
1528{
1529 GHashTableIter iter;
1530 pcmk_node_t *node = NULL;
1531 const char *attr = colocation->node_attribute;
1532
1533 // Iterate through each node
1534 g_hash_table_iter_init(&iter, nodes);
1535 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1536 float delta_f = 0;
1537 int delta = 0;
1538 int score = 0;
1539 int new_score = 0;
1540 const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1541
1542 score = best_node_score_matching_attr(source_rsc, attr, value);
1543
1544 if ((factor < 0) && (score < 0)) {
1545 /* If the dependent is anti-colocated, we generally don't want the
1546 * primary to prefer nodes that the dependent avoids. That could
1547 * lead to unnecessary shuffling of the primary when the dependent
1548 * hits its migration threshold somewhere, for example.
1549 *
1550 * However, there are cases when it is desirable. If the dependent
1551 * can't run anywhere but where the primary is, it would be
1552 * worthwhile to move the primary for the sake of keeping the
1553 * dependent active.
1554 *
1555 * We can't know that exactly at this point since we don't know
1556 * where the primary will be assigned, but we can limit considering
1557 * the preference to when the dependent is allowed only on one node.
1558 * This is less than ideal for multiple reasons:
1559 *
1560 * - the dependent could be allowed on more than one node but have
1561 * anti-colocation primaries on each;
1562 * - the dependent could be a clone or bundle with multiple
1563 * instances, and the dependent as a whole is allowed on multiple
1564 * nodes but some instance still can't run
1565 * - the dependent has considered node-specific criteria such as
1566 * location constraints and stickiness by this point, but might
1567 * have other factors that end up disallowing a node
1568 *
1569 * but the alternative is making the primary move when it doesn't
1570 * need to.
1571 *
1572 * We also consider the primary's stickiness and influence, so the
1573 * user has some say in the matter. (This is the configured primary,
1574 * not a particular instance of the primary, but that doesn't matter
1575 * unless stickiness uses a rule to vary by node, and that seems
1576 * acceptable to ignore.)
1577 */
1578 if ((colocation->primary->stickiness >= -score)
1579 || !pcmk__colocation_has_influence(colocation, NULL)
1580 || !allowed_on_one(colocation->dependent)) {
1581 crm_trace("%s: Filtering %d + %f * %d "
1582 "(double negative disallowed)",
1583 pe__node_name(node), node->weight, factor, score);
1584 continue;
1585 }
1586 }
1587
1588 if (node->weight == INFINITY_HACK) {
1589 crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1590 pe__node_name(node), node->weight, factor, score);
1591 continue;
1592 }
1593
1594 delta_f = factor * score;
1595
1596 // Round the number; see http://c-faq.com/fp/round.html
1597 delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1598
1599 /* Small factors can obliterate the small scores that are often actually
1600 * used in configurations. If the score and factor are nonzero, ensure
1601 * that the result is nonzero as well.
1602 */
1603 if ((delta == 0) && (score != 0)) {
1604 if (factor > 0.0) {
1605 delta = 1;
1606 } else if (factor < 0.0) {
1607 delta = -1;
1608 }
1609 }
1610
1611 new_score = pcmk__add_scores(delta, node->weight);
1612
1613 if (only_positive && (new_score < 0) && (node->weight > 0)) {
1614 crm_trace("%s: Filtering %d + %f * %d = %d "
1615 "(negative disallowed, marking node unusable)",
1616 pe__node_name(node), node->weight, factor, score,
1617 new_score);
1618 node->weight = INFINITY_HACK;
1619 continue;
1620 }
1621
1622 if (only_positive && (new_score < 0) && (node->weight == 0)) {
1623 crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1624 pe__node_name(node), node->weight, factor, score,
1625 new_score);
1626 continue;
1627 }
1628
1629 crm_trace("%s: %d + %f * %d = %d", pe__node_name(node),
1630 node->weight, factor, score, new_score);
1631 node->weight = new_score;
1632 }
1633}
1634
1665void
1667 const pcmk_resource_t *target_rsc,
1668 const char *log_id,
1669 GHashTable **nodes,
1670 const pcmk__colocation_t *colocation,
1671 float factor, uint32_t flags)
1672{
1673 GHashTable *work = NULL;
1674
1675 CRM_ASSERT((source_rsc != NULL) && (nodes != NULL)
1676 && ((colocation != NULL)
1677 || ((target_rsc == NULL) && (*nodes == NULL))));
1678
1679 if (log_id == NULL) {
1680 log_id = source_rsc->id;
1681 }
1682
1683 // Avoid infinite recursion
1684 if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1685 pe_rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1686 log_id, source_rsc->id);
1687 return;
1688 }
1690
1691 if (*nodes == NULL) {
1692 work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1693 target_rsc = source_rsc;
1694 } else {
1696
1697 pe_rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1698 log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1699 work = pcmk__copy_node_table(*nodes);
1700 add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1701 factor, pos);
1702 }
1703
1704 if (work == NULL) {
1706 return;
1707 }
1708
1709 if (pcmk__any_node_available(work)) {
1710 GList *colocations = NULL;
1711
1713 colocations = pcmk__this_with_colocations(source_rsc);
1714 pe_rsc_trace(source_rsc,
1715 "Checking additional %d optional '%s with' "
1716 "constraints",
1717 g_list_length(colocations), source_rsc->id);
1718 } else {
1719 colocations = pcmk__with_this_colocations(source_rsc);
1720 pe_rsc_trace(source_rsc,
1721 "Checking additional %d optional 'with %s' "
1722 "constraints",
1723 g_list_length(colocations), source_rsc->id);
1724 }
1726
1727 for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1728 pcmk__colocation_t *constraint = iter->data;
1729
1730 pcmk_resource_t *other = NULL;
1731 float other_factor = factor * constraint->score / (float) INFINITY;
1732
1734 other = constraint->primary;
1735 } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1736 continue;
1737 } else {
1738 other = constraint->dependent;
1739 }
1740
1741 pe_rsc_trace(source_rsc,
1742 "Optionally merging score of '%s' constraint "
1743 "(%s with %s)",
1744 constraint->id, constraint->dependent->id,
1745 constraint->primary->id);
1746 other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1747 &work, constraint,
1748 other_factor, flags);
1749 pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1750 }
1751 g_list_free(colocations);
1752
1754 pe_rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1755 log_id, source_rsc->id);
1756 g_hash_table_destroy(work);
1758 return;
1759 }
1760
1761
1763 pcmk_node_t *node = NULL;
1764 GHashTableIter iter;
1765
1766 g_hash_table_iter_init(&iter, work);
1767 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1768 if (node->weight == INFINITY_HACK) {
1769 node->weight = 1;
1770 }
1771 }
1772 }
1773
1774 if (*nodes != NULL) {
1775 g_hash_table_destroy(*nodes);
1776 }
1777 *nodes = work;
1778
1780}
1781
1789void
1790pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1791{
1792 pcmk__colocation_t *colocation = data;
1793 pcmk_resource_t *target_rsc = user_data;
1794
1795 pcmk_resource_t *source_rsc = colocation->dependent;
1796 const float factor = colocation->score / (float) INFINITY;
1798
1799 if (!pcmk__colocation_has_influence(colocation, NULL)) {
1800 return;
1801 }
1802 if (target_rsc->variant == pcmk_rsc_variant_clone) {
1804 }
1805 pe_rsc_trace(target_rsc,
1806 "%s: Incorporating attenuated %s assignment scores due "
1807 "to colocation %s",
1808 target_rsc->id, source_rsc->id, colocation->id);
1809 source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1810 source_rsc->id,
1811 &target_rsc->allowed_nodes,
1812 colocation, factor, flags);
1813}
1814
1832void
1834 const pcmk_resource_t *primary,
1835 const pcmk__colocation_t *colocation,
1836 const GList *primary_nodes, bool merge_scores)
1837{
1838 GHashTableIter iter;
1839 pcmk_node_t *dependent_node = NULL;
1840
1841 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1842 && (colocation != NULL));
1843
1844 g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1845 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1846 const pcmk_node_t *primary_node = NULL;
1847
1848 primary_node = pe_find_node_id(primary_nodes,
1849 dependent_node->details->id);
1850 if (primary_node == NULL) {
1851 dependent_node->weight = -INFINITY;
1852 pe_rsc_trace(dependent,
1853 "Banning %s from %s (no primary instance) for %s",
1854 dependent->id, pe__node_name(dependent_node),
1855 colocation->id);
1856
1857 } else if (merge_scores) {
1858 dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1859 primary_node->weight);
1860 pe_rsc_trace(dependent,
1861 "Added %s's score %s to %s's score for %s (now %s) "
1862 "for colocation %s",
1863 primary->id, pcmk_readable_score(primary_node->weight),
1864 dependent->id, pe__node_name(dependent_node),
1865 pcmk_readable_score(dependent_node->weight),
1866 colocation->id);
1867 }
1868 }
1869}
1870
1881GList *
1883{
1884 GList *list = NULL;
1885
1886 rsc->cmds->with_this_colocations(rsc, rsc, &list);
1887 return list;
1888}
1889
1900GList *
1902{
1903 GList *list = NULL;
1904
1905 rsc->cmds->this_with_colocations(rsc, rsc, &list);
1906 return list;
1907}
@ 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:74
#define PCMK_ACTION_PROMOTE
Definition actions.h:65
#define PCMK_ACTION_START
Definition actions.h:71
@ pcmk_action_runnable
Whether action is runnable.
Definition actions.h:241
#define PCMK_ACTION_DEMOTE
Definition actions.h:49
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition nvpair.c:878
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition nvpair.c:905
uint64_t flags
Definition remote.c:3
Utility functions.
int pcmk__add_scores(int score1, int score2)
Definition scores.c:116
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
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:424
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:99
const char * role2text(enum rsc_role_e role)
Definition common.c:458
enum rsc_role_e text2role(const char *role)
Definition common.c:487
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition complex.c:936
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
A dumping ground.
#define CRM_SCORE_INFINITY
Definition crm.h:84
#define INFINITY
Definition crm.h:98
#define CRM_ATTR_UNAME
Definition crm.h:113
#define CRM_ATTR_ID
Definition crm.h:114
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:382
#define CRM_CHECK(expr, failure_action)
Definition logging.h:238
#define crm_err(fmt, args...)
Definition logging.h:379
#define crm_log_xml_trace(xml, text)
Definition logging.h:393
#define crm_trace(fmt, args...)
Definition logging.h:385
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
#define pcmk__if_tracing(if_action, else_action)
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition msg_xml.h:363
#define XML_TAG_RESOURCE_REF
Definition msg_xml.h:234
#define ID(x)
Definition msg_xml.h:474
#define XML_COLOC_ATTR_NODE_ATTR
Definition msg_xml.h:366
#define XML_RULE_ATTR_SCORE
Definition msg_xml.h:341
#define XML_CONS_TAG_RSC_DEPEND
Definition msg_xml.h:353
#define XML_CONS_TAG_RSC_SET
Definition msg_xml.h:357
#define XML_COLOC_ATTR_TARGET_ROLE
Definition msg_xml.h:365
#define XML_COLOC_ATTR_TARGET_INSTANCE
Definition msg_xml.h:373
#define XML_ATTR_ID
Definition msg_xml.h:156
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition msg_xml.h:370
#define XML_COLOC_ATTR_INFLUENCE
Definition msg_xml.h:367
#define XML_CONS_ATTR_SYMMETRICAL
Definition msg_xml.h:358
#define XML_COLOC_ATTR_TARGET
Definition msg_xml.h:364
#define XML_COLOC_ATTR_SOURCE
Definition msg_xml.h:362
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:447
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:302
const char * action
Definition pcmk_fence.c:30
void pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
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)
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_warn_once(pe_wo_bit, fmt...)
Definition internal.h:142
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition internal.h:341
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition clone.c:227
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition bundle.c:131
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition internal.h:70
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:37
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
#define pe__set_resource_flags(resource, flags_to_set)
Definition internal.h:64
#define pe_rsc_info(rsc, fmt, args...)
Definition internal.h:35
#define pe__clear_action_flags(action, flags_to_clear)
Definition internal.h:85
@ pcmk_rsc_variant_clone
Clone resource.
Definition resources.h:36
@ pcmk_rsc_promotable
Whether resource can be promoted and demoted.
Definition resources.h:124
@ pcmk_rsc_unassigned
Whether resource has not yet been assigned to a node.
Definition resources.h:127
@ pcmk_rsc_critical
Whether resource has "critical" meta-attribute enabled.
Definition resources.h:148
@ pcmk_rsc_replica_container
Whether resource is an implicit container resource for a bundle replica.
Definition resources.h:178
@ pcmk_rsc_updating_nodes
Whether resource is in the process of modifying allowed node scores.
Definition resources.h:133
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:154
@ pcmk_rc_unpack_error
Definition results.h:118
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:28
@ pcmk_role_unpromoted
Unpromoted.
Definition roles.h:31
@ pcmk_role_promoted
Promoted.
Definition roles.h:32
#define PCMK__ROLE_STARTED
#define PCMK__ROLE_UNKNOWN
@ pcmk__wo_coloc_inst
@ pcmk__wo_set_ordering
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:448
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
const char * node_attribute
pcmk_resource_t * primary
pcmk_resource_t * dependent
Implementation of pcmk_action_t.
Definition actions.h:390
enum pe_action_flags flags
Group of enum pe_action_flags.
Definition actions.h:409
Implementation of pcmk_node_t.
Definition nodes.h:130
int weight
Node score for a given resource.
Definition nodes.h:131
struct pe_node_shared_s * details
Basic node information.
Definition nodes.h:134
const char * id
Node ID at the cluster layer.
Definition nodes.h:67
Implementation of pcmk_resource_t.
Definition resources.h:399
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition resources.h:417
GList * actions
Definition resources.h:447
enum pe_obj_types variant
Resource variant.
Definition resources.h:414
GList * rsc_cons
Definition resources.h:445
GList * rsc_cons_lhs
Definition resources.h:444
GList * children
Resource's child resources, if any.
Definition resources.h:475
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition resources.h:412
int priority
Configured priority.
Definition resources.h:422
char * id
Resource ID in configuration.
Definition resources.h:400
pcmk_node_t * allocated_to
Node resource is assigned to.
Definition resources.h:451
GHashTable * allowed_nodes
Nodes where resource may run (key is node ID, not name)
Definition resources.h:466
unsigned long long flags
Group of enum pcmk_rsc_flags.
Definition resources.h:429
enum rsc_role_e next_role
Resource's scheduled next role.
Definition resources.h:469
int stickiness
Extra preference for current node.
Definition resources.h:423
pcmk_resource_t * parent
Resource's parent resource, if any.
Definition resources.h:413
Configuration tag object.
Definition tags.h:26
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
GList * colocation_constraints
Colocation constraints.
Definition scheduler.h:199
xmlNode * input
CIB XML.
Definition scheduler.h:175
GList * resources
Resources in cluster.
Definition scheduler.h:196
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)
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2555
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition xml.c:2484
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition xml.c:2510
void free_xml(xmlNode *child)
Definition xml.c:783
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:789
void xml_remove_prop(xmlNode *obj, const char *name)
Definition xml.c:1696