pacemaker 3.0.1-16e74fc4da
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>
18#include <crm/pengine/status.h>
19#include <pacemaker-internal.h>
20
21#include "crm/common/util.h"
23#include "crm/common/xml.h"
25
26// Used to temporarily mark a node as unusable
27#define INFINITY_HACK (PCMK_SCORE_INFINITY * -100)
28
39const char *
40pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr,
41 const pcmk_resource_t *rsc)
42{
43 const char *target = NULL;
44
45 /* A resource colocated with a bundle or its primitive can't run on the
46 * bundle node itself (where only the primitive, if any, can run). Instead,
47 * we treat it as a colocation with the bundle's containers, so always look
48 * up colocation node attributes on the container host.
49 */
50 if (pcmk__is_bundle_node(node) && pcmk__is_bundled(rsc)
51 && (pe__const_top_resource(rsc, false) == pe__bundled_resource(rsc))) {
53
54 } else if (rsc != NULL) {
55 target = g_hash_table_lookup(rsc->priv->meta,
57 }
58
60}
61
85static gint
86cmp_colocation_priority(const pcmk__colocation_t *colocation1,
87 const pcmk__colocation_t *colocation2, bool dependent)
88{
89 const pcmk_resource_t *rsc1 = NULL;
90 const pcmk_resource_t *rsc2 = NULL;
91
92 if (colocation1 == NULL) {
93 return 1;
94 }
95 if (colocation2 == NULL) {
96 return -1;
97 }
98
99 if (dependent) {
100 rsc1 = colocation1->dependent;
101 rsc2 = colocation2->dependent;
102 pcmk__assert(colocation1->primary != NULL);
103 } else {
104 rsc1 = colocation1->primary;
105 rsc2 = colocation2->primary;
106 pcmk__assert(colocation1->dependent != NULL);
107 }
108 pcmk__assert((rsc1 != NULL) && (rsc2 != NULL));
109
110 if (rsc1->priv->priority > rsc2->priv->priority) {
111 return -1;
112 }
113 if (rsc1->priv->priority < rsc2->priv->priority) {
114 return 1;
115 }
116
117 // Process clones before primitives and groups
118 if (rsc1->priv->variant > rsc2->priv->variant) {
119 return -1;
120 }
121 if (rsc1->priv->variant < rsc2->priv->variant) {
122 return 1;
123 }
124
125 /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
126 * clones (probably unnecessary, but avoids having to update regression
127 * tests)
128 */
129 if (pcmk__is_clone(rsc1)) {
132 return -1;
133 }
136 return 1;
137 }
138 }
139
140 return strcmp(rsc1->id, rsc2->id);
141}
142
163static gint
164cmp_dependent_priority(gconstpointer a, gconstpointer b)
165{
166 return cmp_colocation_priority(a, b, true);
167}
168
189static gint
190cmp_primary_priority(gconstpointer a, gconstpointer b)
191{
192 return cmp_colocation_priority(a, b, false);
193}
194
206void
207pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
208 const pcmk_resource_t *rsc)
209{
210 pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
211
212 pcmk__rsc_trace(rsc,
213 "Adding colocation %s (%s with %s using %s @%s) to "
214 "'this with' list for %s",
215 colocation->id, colocation->dependent->id,
216 colocation->primary->id, colocation->node_attribute,
217 pcmk_readable_score(colocation->score), rsc->id);
218 *list = g_list_insert_sorted(*list, (gpointer) colocation,
219 cmp_primary_priority);
220}
221
233void
234pcmk__add_this_with_list(GList **list, GList *addition,
235 const pcmk_resource_t *rsc)
236{
237 pcmk__assert((list != NULL) && (rsc != NULL));
238
240 {}, // Always add each colocation individually if tracing
241 {
242 if (*list == NULL) {
243 // Trivial case for efficiency if not tracing
244 *list = g_list_copy(addition);
245 return;
246 }
247 }
248 );
249
250 for (const GList *iter = addition; iter != NULL; iter = iter->next) {
251 pcmk__add_this_with(list, addition->data, rsc);
252 }
253}
254
266void
267pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
268 const pcmk_resource_t *rsc)
269{
270 pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
271
272 pcmk__rsc_trace(rsc,
273 "Adding colocation %s (%s with %s using %s @%s) to "
274 "'with this' list for %s",
275 colocation->id, colocation->dependent->id,
276 colocation->primary->id, colocation->node_attribute,
277 pcmk_readable_score(colocation->score), rsc->id);
278 *list = g_list_insert_sorted(*list, (gpointer) colocation,
279 cmp_dependent_priority);
280}
281
293void
294pcmk__add_with_this_list(GList **list, GList *addition,
295 const pcmk_resource_t *rsc)
296{
297 pcmk__assert((list != NULL) && (rsc != NULL));
298
300 {}, // Always add each colocation individually if tracing
301 {
302 if (*list == NULL) {
303 // Trivial case for efficiency if not tracing
304 *list = g_list_copy(addition);
305 return;
306 }
307 }
308 );
309
310 for (const GList *iter = addition; iter != NULL; iter = iter->next) {
311 pcmk__add_with_this(list, addition->data, rsc);
312 }
313}
314
324static void
325anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
326 pcmk_resource_t *then_rsc, int then_role)
327{
328 const char *first_tasks[] = { NULL, NULL };
329 const char *then_tasks[] = { NULL, NULL };
330
331 /* Actions to make first_rsc lose first_role */
332 if (first_role == pcmk_role_promoted) {
333 first_tasks[0] = PCMK_ACTION_DEMOTE;
334
335 } else {
336 first_tasks[0] = PCMK_ACTION_STOP;
337
338 if (first_role == pcmk_role_unpromoted) {
339 first_tasks[1] = PCMK_ACTION_PROMOTE;
340 }
341 }
342
343 /* Actions to make then_rsc gain then_role */
344 if (then_role == pcmk_role_promoted) {
345 then_tasks[0] = PCMK_ACTION_PROMOTE;
346
347 } else {
348 then_tasks[0] = PCMK_ACTION_START;
349
350 if (then_role == pcmk_role_unpromoted) {
351 then_tasks[1] = PCMK_ACTION_DEMOTE;
352 }
353 }
354
355 for (int first_lpc = 0;
356 (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
357
358 for (int then_lpc = 0;
359 (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
360
361 pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
362 then_rsc, then_tasks[then_lpc],
364 }
365 }
366}
367
383void
384pcmk__new_colocation(const char *id, const char *node_attr, int score,
385 pcmk_resource_t *dependent, pcmk_resource_t *primary,
386 const char *dependent_role_spec,
387 const char *primary_role_spec, uint32_t flags)
388{
389 pcmk__colocation_t *new_con = NULL;
390 enum rsc_role_e dependent_role = pcmk_role_unknown;
391 enum rsc_role_e primary_role = pcmk_role_unknown;
392
393 CRM_CHECK(id != NULL, return);
394
395 if ((dependent == NULL) || (primary == NULL)) {
396 pcmk__config_err("Ignoring colocation '%s' because resource "
397 "does not exist", id);
398 return;
399 }
400 if ((pcmk__parse_constraint_role(id, dependent_role_spec,
401 &dependent_role) != pcmk_rc_ok)
402 || (pcmk__parse_constraint_role(id, primary_role_spec,
403 &primary_role) != pcmk_rc_ok)) {
404 // Not possible with schema validation enabled (error already logged)
405 return;
406 }
407
408 if (score == 0) {
409 pcmk__rsc_trace(dependent,
410 "Ignoring colocation '%s' (%s with %s) because score is 0",
411 id, dependent->id, primary->id);
412 return;
413 }
414
415 new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t));
416 new_con->id = id;
417 new_con->dependent = dependent;
418 new_con->primary = primary;
419 new_con->score = score;
420 new_con->dependent_role = dependent_role;
421 new_con->primary_role = primary_role;
422
423 new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
424 new_con->flags = flags;
425
426 pcmk__add_this_with(&(dependent->priv->this_with_colocations), new_con,
427 dependent);
428 pcmk__add_with_this(&(primary->priv->with_this_colocations), new_con,
429 primary);
430
432 g_list_prepend(dependent->priv->scheduler->priv->colocation_constraints,
433 new_con);
434
435 if (score <= -PCMK_SCORE_INFINITY) {
436 anti_colocation_order(dependent, new_con->dependent_role, primary,
437 new_con->primary_role);
438 anti_colocation_order(primary, new_con->primary_role, dependent,
439 new_con->dependent_role);
440 }
441}
442
455static uint32_t
456unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
457 const char *influence_s)
458{
459 if (influence_s != NULL) {
460 int influence_i = 0;
461
462 if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
463 pcmk__config_err("Constraint '%s' has invalid value for "
464 PCMK_XA_INFLUENCE " (using default)",
465 coloc_id);
466 } else {
467 return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
468 }
469 }
472 }
473 return pcmk__coloc_none;
474}
475
476static void
477unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
478 const char *influence_s, pcmk_scheduler_t *scheduler)
479{
480 xmlNode *xml_rsc = NULL;
481 pcmk_resource_t *other = NULL;
482 pcmk_resource_t *resource = NULL;
483 const char *set_id = pcmk__xe_id(set);
484 const char *role = crm_element_value(set, PCMK_XA_ROLE);
485 bool with_previous = false;
486 int local_score = score;
487 bool sequential = false;
488 uint32_t flags = pcmk__coloc_none;
489 const char *xml_rsc_id = NULL;
490 const char *score_s = crm_element_value(set, PCMK_XA_SCORE);
491
492 if (score_s != NULL) {
493 int rc = pcmk_parse_score(score_s, &local_score, 0);
494
495 if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
496 pcmk__config_err("Ignoring colocation '%s' for set '%s' "
497 "because '%s' is not a valid score",
498 coloc_id, set_id, score_s);
499 return;
500 }
501 }
502 if (local_score == 0) {
503 crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
504 coloc_id, set_id);
505 return;
506 }
507
508 /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether
509 * resources in a positive-score set are colocated with the previous or next
510 * resource.
511 */
512 if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING),
515 with_previous = true;
516 } else {
518 "Support for '" PCMK__XA_ORDERING "' other than"
520 " (such as %s) is deprecated and will be removed in a"
521 " future release",
522 set_id);
523 }
524
526 &sequential) == pcmk_rc_ok)
527 && !sequential) {
528 return;
529 }
530
531 if (local_score > 0) {
532 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
533 NULL);
534 xml_rsc != NULL;
535 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
536
537 xml_rsc_id = pcmk__xe_id(xml_rsc);
538 resource =
540 xml_rsc_id);
541 if (resource == NULL) {
542 // Should be possible only with validation disabled
543 pcmk__config_err("Ignoring %s and later resources in set %s: "
544 "No such resource", xml_rsc_id, set_id);
545 return;
546 }
547 if (other != NULL) {
549 | unpack_influence(coloc_id, resource, influence_s);
550 if (with_previous) {
551 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
552 resource->id, other->id, set_id);
553 pcmk__new_colocation(set_id, NULL, local_score, resource,
554 other, role, role, flags);
555 } else {
556 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
557 other->id, resource->id, set_id);
558 pcmk__new_colocation(set_id, NULL, local_score, other,
559 resource, role, role, flags);
560 }
561 }
562 other = resource;
563 }
564
565 } else {
566 /* Anti-colocating with every prior resource is
567 * the only way to ensure the intuitive result
568 * (i.e. that no one in the set can run with anyone else in the set)
569 */
570
571 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
572 NULL);
573 xml_rsc != NULL;
574 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
575
576 xmlNode *xml_rsc_with = NULL;
577
578 xml_rsc_id = pcmk__xe_id(xml_rsc);
579 resource =
581 xml_rsc_id);
582 if (resource == NULL) {
583 // Should be possible only with validation disabled
584 pcmk__config_err("Ignoring %s and later resources in set %s: "
585 "No such resource", xml_rsc_id, set_id);
586 return;
587 }
589 | unpack_influence(coloc_id, resource, influence_s);
590 for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
591 NULL, NULL);
592 xml_rsc_with != NULL;
593 xml_rsc_with = pcmk__xe_next(xml_rsc_with,
595
596 xml_rsc_id = pcmk__xe_id(xml_rsc_with);
597 if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
598 break;
599 }
600 other =
602 xml_rsc_id);
603 pcmk__assert(other != NULL); // We already processed it
604 pcmk__new_colocation(set_id, NULL, local_score,
605 resource, other, role, role, flags);
606 }
607 }
608 }
609}
610
623static void
624colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
625 int score, const char *influence_s,
627{
628 xmlNode *xml_rsc = NULL;
629 pcmk_resource_t *rsc_1 = NULL;
630 pcmk_resource_t *rsc_2 = NULL;
631
632 const char *xml_rsc_id = NULL;
633 const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE);
634 const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE);
635
636 int rc = pcmk_rc_ok;
637 bool sequential = false;
638 uint32_t flags = pcmk__coloc_none;
639
640 if (score == 0) {
641 crm_trace("Ignoring colocation '%s' between sets %s and %s "
642 "because score is 0",
643 id, pcmk__xe_id(set1), pcmk__xe_id(set2));
644 return;
645 }
646
647 rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential);
648 if ((rc != pcmk_rc_ok) || sequential) {
649 // Get the first one
650 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL);
651 if (xml_rsc != NULL) {
652 xml_rsc_id = pcmk__xe_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 with set %s "
658 "because first resource %s not found",
659 pcmk__xe_id(set1), pcmk__xe_id(set2),
660 xml_rsc_id);
661 return;
662 }
663 }
664 }
665
666 rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential);
667 if ((rc != pcmk_rc_ok) || sequential) {
668 // Get the last one
669 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
670 NULL);
671 xml_rsc != NULL;
672 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
673
674 xml_rsc_id = pcmk__xe_id(xml_rsc);
675 }
677 xml_rsc_id);
678 if (rsc_2 == NULL) {
679 // Should be possible only with validation disabled
680 pcmk__config_err("Ignoring colocation of set %s with set %s "
681 "because last resource %s not found",
682 pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id);
683 return;
684 }
685 }
686
687 if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
688 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
689 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
690 flags);
691
692 } else if (rsc_1 != NULL) { // Only set1 is sequential
693 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
694 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
695 NULL);
696 xml_rsc != NULL;
697 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
698
699 xml_rsc_id = pcmk__xe_id(xml_rsc);
701 xml_rsc_id);
702 if (rsc_2 == NULL) {
703 // Should be possible only with validation disabled
704 pcmk__config_err("Ignoring set %s colocation with resource %s "
705 "in set %s: No such resource",
706 pcmk__xe_id(set1), xml_rsc_id,
707 pcmk__xe_id(set2));
708 continue;
709 }
710 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
711 role_2, flags);
712 }
713
714 } else if (rsc_2 != NULL) { // Only set2 is sequential
715 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
716 NULL);
717 xml_rsc != NULL;
718 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
719
720 xml_rsc_id = pcmk__xe_id(xml_rsc);
722 xml_rsc_id);
723 if (rsc_1 == NULL) {
724 // Should be possible only with validation disabled
725 pcmk__config_err("Ignoring colocation of set %s resource %s "
726 "with set %s: No such resource",
727 pcmk__xe_id(set1), xml_rsc_id,
728 pcmk__xe_id(set2));
729 continue;
730 }
732 | unpack_influence(id, rsc_1, influence_s);
733 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
734 role_2, flags);
735 }
736
737 } else { // Neither set is sequential
738 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
739 NULL);
740 xml_rsc != NULL;
741 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
742
743 xmlNode *xml_rsc_2 = NULL;
744
745 xml_rsc_id = pcmk__xe_id(xml_rsc);
747 xml_rsc_id);
748 if (rsc_1 == NULL) {
749 // Should be possible only with validation disabled
750 pcmk__config_err("Ignoring colocation of set %s resource %s "
751 "with set %s: No such resource",
752 pcmk__xe_id(set1), xml_rsc_id,
753 pcmk__xe_id(set2));
754 continue;
755 }
756
758 | unpack_influence(id, rsc_1, influence_s);
759 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
760 NULL, NULL);
761 xml_rsc_2 != NULL;
762 xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
763
764 xml_rsc_id = pcmk__xe_id(xml_rsc_2);
765 rsc_2 =
767 xml_rsc_id);
768 if (rsc_2 == NULL) {
769 // Should be possible only with validation disabled
770 pcmk__config_err("Ignoring colocation of set %s resource "
771 "%s with set %s resource %s: No such "
772 "resource",
773 pcmk__xe_id(set1), pcmk__xe_id(xml_rsc),
774 pcmk__xe_id(set2), xml_rsc_id);
775 continue;
776 }
777 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
778 role_1, role_2, flags);
779 }
780 }
781 }
782}
783
794static void
795unpack_simple_colocation(const xmlNode *xml_obj, const char *id, int score,
796 const char *influence_s, pcmk_scheduler_t *scheduler)
797{
798 uint32_t flags = pcmk__coloc_none;
799
800 const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
801 const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
802 const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
803 const char *primary_role = crm_element_value(xml_obj,
805 const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE);
806
807 pcmk_resource_t *primary = NULL;
808 pcmk_resource_t *dependent = NULL;
809
811 primary_id);
813 dependent_id);
814
815 if (dependent == NULL) {
816 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
817 "does not exist", id, dependent_id);
818 return;
819
820 } else if (primary == NULL) {
821 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
822 "does not exist", id, primary_id);
823 return;
824 }
825
827 pcmk__config_warn("The colocation constraint "
828 "'" PCMK_XA_SYMMETRICAL "' attribute has been "
829 "removed");
830 }
831
832 flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
833 pcmk__new_colocation(id, attr, score, dependent, primary,
834 dependent_role, primary_role, flags);
835}
836
837// \return Standard Pacemaker return code
838static int
839unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
841{
842 const char *id = NULL;
843 const char *dependent_id = NULL;
844 const char *primary_id = NULL;
845 const char *dependent_role = NULL;
846 const char *primary_role = NULL;
847
848 pcmk_resource_t *dependent = NULL;
849 pcmk_resource_t *primary = NULL;
850
851 pcmk__idref_t *dependent_tag = NULL;
852 pcmk__idref_t *primary_tag = NULL;
853
854 xmlNode *dependent_set = NULL;
855 xmlNode *primary_set = NULL;
856 bool any_sets = false;
857
858 *expanded_xml = NULL;
859
860 CRM_CHECK(xml_obj != NULL, return EINVAL);
861
862 id = pcmk__xe_id(xml_obj);
863 if (id == NULL) {
864 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
865 xml_obj->name);
867 }
868
869 // Check whether there are any resource sets with template or tag references
870 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
871 if (*expanded_xml != NULL) {
872 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
873 return pcmk_rc_ok;
874 }
875
876 dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
877 primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
878 if ((dependent_id == NULL) || (primary_id == NULL)) {
879 return pcmk_rc_ok;
880 }
881
882 if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
883 &dependent_tag)) {
884 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
885 "valid resource or tag", id, dependent_id);
887 }
888
889 if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
890 &primary_tag)) {
891 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
892 "valid resource or tag", id, primary_id);
894 }
895
896 if ((dependent != NULL) && (primary != NULL)) {
897 /* Neither side references any template/tag. */
898 return pcmk_rc_ok;
899 }
900
901 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
902 // A colocation constraint between two templates/tags makes no sense
903 pcmk__config_err("Ignoring constraint '%s' because two templates or "
904 "tags cannot be colocated", id);
906 }
907
908 dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
909 primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE);
910
911 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
912
913 /* Convert dependent's template/tag reference into constraint
914 * PCMK_XE_RESOURCE_SET
915 */
916 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true,
917 scheduler)) {
918 pcmk__xml_free(*expanded_xml);
919 *expanded_xml = NULL;
921 }
922
923 if (dependent_set != NULL) {
924 if (dependent_role != NULL) {
925 /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
926 * PCMK_XA_ROLE
927 */
928 crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role);
930 }
931 any_sets = true;
932 }
933
934 /* Convert primary's template/tag reference into constraint
935 * PCMK_XE_RESOURCE_SET
936 */
937 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true,
938 scheduler)) {
939 pcmk__xml_free(*expanded_xml);
940 *expanded_xml = NULL;
942 }
943
944 if (primary_set != NULL) {
945 if (primary_role != NULL) {
946 /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
947 * PCMK_XA_ROLE
948 */
949 crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role);
951 }
952 any_sets = true;
953 }
954
955 if (any_sets) {
956 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
957 } else {
958 pcmk__xml_free(*expanded_xml);
959 *expanded_xml = NULL;
960 }
961
962 return pcmk_rc_ok;
963}
964
972void
974{
975 int score_i = 0;
976 xmlNode *set = NULL;
977 xmlNode *last = NULL;
978
979 xmlNode *orig_xml = NULL;
980 xmlNode *expanded_xml = NULL;
981
982 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
983 const char *score = NULL;
984 const char *influence_s = NULL;
985
986 if (pcmk__str_empty(id)) {
988 " without " CRM_ATTR_ID);
989 return;
990 }
991
992 if (unpack_colocation_tags(xml_obj, &expanded_xml,
993 scheduler) != pcmk_rc_ok) {
994 return;
995 }
996 if (expanded_xml != NULL) {
997 orig_xml = xml_obj;
998 xml_obj = expanded_xml;
999 }
1000
1001 score = crm_element_value(xml_obj, PCMK_XA_SCORE);
1002 if (score != NULL) {
1003 int rc = pcmk_parse_score(score, &score_i, 0);
1004
1005 if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
1006 pcmk__config_err("Ignoring colocation %s because '%s' "
1007 "is not a valid score", id, score);
1008 return;
1009 }
1010 }
1011 influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE);
1012
1013 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1014 set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
1015
1017 if (set == NULL) { // Configuration error, message already logged
1018 if (expanded_xml != NULL) {
1019 pcmk__xml_free(expanded_xml);
1020 }
1021 return;
1022 }
1023
1024 if (pcmk__str_empty(pcmk__xe_id(set))) {
1026 " without " CRM_ATTR_ID);
1027 continue;
1028 }
1029 unpack_colocation_set(set, score_i, id, influence_s, scheduler);
1030
1031 if (last != NULL) {
1032 colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
1033 }
1034 last = set;
1035 }
1036
1037 if (expanded_xml) {
1038 pcmk__xml_free(expanded_xml);
1039 xml_obj = orig_xml;
1040 }
1041
1042 if (last == NULL) {
1043 unpack_simple_colocation(xml_obj, id, score_i, influence_s, scheduler);
1044 }
1045}
1046
1059bool
1061 const pcmk_resource_t *rsc)
1062{
1063 if (rsc == NULL) {
1064 rsc = colocation->primary;
1065 }
1066
1067 /* A bundle replica colocates its remote connection with its container,
1068 * using a finite score so that the container can run on Pacemaker Remote
1069 * nodes.
1070 *
1071 * Moving a connection is lightweight and does not interrupt the service,
1072 * while moving a container is heavyweight and does interrupt the service,
1073 * so don't move a clean, active container based solely on the preferences
1074 * of its connection.
1075 *
1076 * This also avoids problematic scenarios where two containers want to
1077 * perpetually swap places.
1078 */
1079 if (pcmk_is_set(colocation->dependent->flags,
1082 && pcmk__list_of_1(rsc->priv->active_nodes)) {
1083 return false;
1084 }
1085
1086 /* The dependent in a colocation influences the primary's location
1087 * if the PCMK_XA_INFLUENCE option is true or the primary is not yet active.
1088 */
1089 return pcmk_is_set(colocation->flags, pcmk__coloc_influence)
1090 || (rsc->priv->active_nodes == NULL);
1091}
1092
1101static void
1102mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1103 const pcmk_resource_t *reason)
1104{
1105 GList *iter = NULL;
1106 char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1107
1108 for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
1109 pcmk_action_t *action = iter->data;
1110
1112 && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1113
1115 pe_action_set_reason(action, reason_text, false);
1118 }
1119 }
1120
1121 // If parent resource can't perform an action, neither can any children
1122 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
1123 mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1124 }
1125 free(reason_text);
1126}
1127
1138void
1140{
1141 GList *iter = NULL;
1142 GList *colocations = NULL;
1143 pcmk_resource_t *rsc = NULL;
1144 bool is_start = false;
1145
1147 return; // Only unrunnable actions block dependents
1148 }
1149
1150 is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1151 if (!is_start
1152 && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1153 return; // Only unrunnable starts and promotes block dependents
1154 }
1155
1156 pcmk__assert(action->rsc != NULL); // Start and promote are resource actions
1157
1158 /* If this resource is part of a collective resource, dependents are blocked
1159 * only if all instances of the collective are unrunnable, so check the
1160 * collective resource.
1161 */
1162 rsc = uber_parent(action->rsc);
1163 if (rsc->priv->parent != NULL) {
1164 rsc = rsc->priv->parent; // Bundle
1165 }
1166
1167 // Colocation fails only if entire primary can't reach desired role
1168 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
1169 pcmk_resource_t *child = iter->data;
1170 pcmk_action_t *child_action = NULL;
1171
1172 child_action = find_first_action(child->priv->actions, NULL,
1173 action->task, NULL);
1174 if ((child_action == NULL)
1175 || pcmk_is_set(child_action->flags, pcmk__action_runnable)) {
1176 crm_trace("Not blocking %s colocation dependents because "
1177 "at least %s has runnable %s",
1178 rsc->id, child->id, action->task);
1179 return; // At least one child can reach desired role
1180 }
1181 }
1182
1183 crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1184 rsc->id, action->rsc->id, action->task);
1185
1186 // Check each colocation where this resource is primary
1187 colocations = pcmk__with_this_colocations(rsc);
1188 for (iter = colocations; iter != NULL; iter = iter->next) {
1189 pcmk__colocation_t *colocation = iter->data;
1190
1191 if (colocation->score < PCMK_SCORE_INFINITY) {
1192 continue; // Only mandatory colocations block dependent
1193 }
1194
1195 /* If the primary can't start, the dependent can't reach its colocated
1196 * role, regardless of what the primary or dependent colocation role is.
1197 *
1198 * If the primary can't be promoted, the dependent can't reach its
1199 * colocated role if the primary's colocation role is promoted.
1200 */
1201 if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1202 continue;
1203 }
1204
1205 // Block the dependent from reaching its colocated role
1206 if (colocation->dependent_role == pcmk_role_promoted) {
1207 mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1208 action->rsc);
1209 } else {
1210 mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1211 action->rsc);
1212 }
1213 }
1214 g_list_free(colocations);
1215}
1216
1235static const pcmk_resource_t *
1236get_resource_for_role(const pcmk_resource_t *rsc)
1237{
1239 const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1240
1241 if (child != NULL) {
1242 return child;
1243 }
1244 }
1245 return rsc;
1246}
1247
1268 const pcmk_resource_t *primary,
1269 const pcmk__colocation_t *colocation, bool preview)
1270{
1271 const pcmk_resource_t *dependent_role_rsc = NULL;
1272 const pcmk_resource_t *primary_role_rsc = NULL;
1273
1274 pcmk__assert((dependent != NULL) && (primary != NULL)
1275 && (colocation != NULL));
1276
1277 if (!preview && pcmk_is_set(primary->flags, pcmk__rsc_unassigned)) {
1278 // Primary resource has not been assigned yet, so we can't do anything
1280 }
1281
1282 dependent_role_rsc = get_resource_for_role(dependent);
1283
1284 primary_role_rsc = get_resource_for_role(primary);
1285
1286 if ((colocation->dependent_role >= pcmk_role_unpromoted)
1287 && (dependent_role_rsc->priv->parent != NULL)
1288 && pcmk_is_set(dependent_role_rsc->priv->parent->flags,
1290 && !pcmk_is_set(dependent_role_rsc->flags, pcmk__rsc_unassigned)) {
1291
1292 /* This is a colocation by role, and the dependent is a promotable clone
1293 * that has already been assigned, so the colocation should now affect
1294 * the role.
1295 */
1297 }
1298
1299 if (!preview && !pcmk_is_set(dependent->flags, pcmk__rsc_unassigned)) {
1300 /* The dependent resource has already been through assignment, so the
1301 * constraint no longer matters.
1302 */
1304 }
1305
1306 if ((colocation->dependent_role != pcmk_role_unknown)
1307 && (colocation->dependent_role != dependent_role_rsc->priv->next_role)) {
1308 crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1309
1310 "but %s next role is %s",
1311 ((colocation->score < 0)? "anti-" : ""),
1312 colocation->id, pcmk_role_text(colocation->dependent_role),
1313 dependent_role_rsc->id,
1314 pcmk_role_text(dependent_role_rsc->priv->next_role));
1316 }
1317
1318 if ((colocation->primary_role != pcmk_role_unknown)
1319 && (colocation->primary_role != primary_role_rsc->priv->next_role)) {
1320 crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1321 "but %s next role is %s",
1322 ((colocation->score < 0)? "anti-" : ""),
1323 colocation->id, pcmk_role_text(colocation->primary_role),
1324 primary_role_rsc->id,
1325 pcmk_role_text(primary_role_rsc->priv->next_role));
1327 }
1328
1330}
1331
1343void
1345 const pcmk_resource_t *primary,
1346 const pcmk__colocation_t *colocation)
1347{
1348 const char *attr = colocation->node_attribute;
1349 const char *value = NULL;
1350 GHashTable *work = NULL;
1351 GHashTableIter iter;
1352 pcmk_node_t *node = NULL;
1353
1354 if (primary->priv->assigned_node != NULL) {
1356 attr, primary);
1357
1358 } else if (colocation->score < 0) {
1359 // Nothing to do (anti-colocation with something that is not running)
1360 return;
1361 }
1362
1363 work = pcmk__copy_node_table(dependent->priv->allowed_nodes);
1364
1365 g_hash_table_iter_init(&iter, work);
1366 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1367 if (primary->priv->assigned_node == NULL) {
1368 node->assign->score = pcmk__add_scores(-colocation->score,
1369 node->assign->score);
1370 pcmk__rsc_trace(dependent,
1371 "Applied %s to %s score on %s (now %s after "
1372 "subtracting %s because primary %s inactive)",
1373 colocation->id, dependent->id,
1374 pcmk__node_name(node),
1376 pcmk_readable_score(colocation->score), primary->id);
1377 continue;
1378 }
1379
1380 if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1381 value, pcmk__str_casei)) {
1382
1383 /* Add colocation score only if optional (or minus infinity). A
1384 * mandatory colocation is a requirement rather than a preference,
1385 * so we don't need to consider it for relative assignment purposes.
1386 * The resource will simply be forbidden from running on the node if
1387 * the primary isn't active there (via the condition above).
1388 */
1389 if (colocation->score < PCMK_SCORE_INFINITY) {
1390 node->assign->score = pcmk__add_scores(colocation->score,
1391 node->assign->score);
1392 pcmk__rsc_trace(dependent,
1393 "Applied %s to %s score on %s (now %s after "
1394 "adding %s)",
1395 colocation->id, dependent->id,
1396 pcmk__node_name(node),
1398 pcmk_readable_score(colocation->score));
1399 }
1400 continue;
1401 }
1402
1403 if (colocation->score >= PCMK_SCORE_INFINITY) {
1404 /* Only mandatory colocations are relevant when the colocation
1405 * attribute doesn't match, because an attribute not matching is not
1406 * a negative preference -- the colocation is simply relevant only
1407 * where it matches.
1408 */
1410 pcmk__rsc_trace(dependent,
1411 "Banned %s from %s because colocation %s attribute %s "
1412 "does not match",
1413 dependent->id, pcmk__node_name(node),
1414 colocation->id, attr);
1415 }
1416 }
1417
1418 if ((colocation->score <= -PCMK_SCORE_INFINITY)
1419 || (colocation->score >= PCMK_SCORE_INFINITY)
1420 || pcmk__any_node_available(work)) {
1421
1422 g_hash_table_destroy(dependent->priv->allowed_nodes);
1423 dependent->priv->allowed_nodes = work;
1424 work = NULL;
1425
1426 } else {
1427 pcmk__rsc_info(dependent,
1428 "%s: Rolling back scores from %s (no available nodes)",
1429 dependent->id, primary->id);
1430 }
1431
1432 if (work != NULL) {
1433 g_hash_table_destroy(work);
1434 }
1435}
1436
1450int
1452 const pcmk_resource_t *primary,
1453 const pcmk__colocation_t *colocation)
1454{
1455 const char *dependent_value = NULL;
1456 const char *primary_value = NULL;
1457 const char *attr = colocation->node_attribute;
1458 int score_multiplier = 1;
1459 int priority_delta = 0;
1460 const pcmk_node_t *primary_node = NULL;
1461 const pcmk_node_t *dependent_node = NULL;
1462
1463 pcmk__assert((dependent != NULL) && (primary != NULL)
1464 && (colocation != NULL));
1465
1466 primary_node = primary->priv->assigned_node;
1467 dependent_node = dependent->priv->assigned_node;
1468
1469 if (dependent_node == NULL) {
1470 return 0;
1471 }
1472
1473 if ((primary_node != NULL)
1474 && (colocation->primary_role != pcmk_role_unknown)) {
1475 /* Colocation applies only if the primary's next role matches.
1476 *
1477 * If primary_node == NULL, we want to proceed past this block, so that
1478 * dependent_node is marked ineligible for promotion.
1479 *
1480 * @TODO Why ignore a mandatory colocation in this case when we apply
1481 * its negation in the mismatched value case?
1482 */
1483 const pcmk_resource_t *role_rsc = get_resource_for_role(primary);
1484
1485 if (colocation->primary_role != role_rsc->priv->next_role) {
1486 return 0;
1487 }
1488 }
1489
1490 dependent_value = pcmk__colocation_node_attr(dependent_node, attr,
1491 dependent);
1492 primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1493
1494 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1495 if ((colocation->score == PCMK_SCORE_INFINITY)
1496 && (colocation->dependent_role == pcmk_role_promoted)) {
1497 /* For a mandatory promoted-role colocation, mark the dependent node
1498 * ineligible to promote the dependent if its attribute value
1499 * doesn't match the primary node's
1500 */
1501 score_multiplier = -1;
1502
1503 } else {
1504 // Otherwise, ignore the colocation if attribute values don't match
1505 return 0;
1506 }
1507
1508 } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1509 /* Node attribute values matched, so we want to avoid promoting the
1510 * dependent on this node
1511 */
1512 score_multiplier = -1;
1513 }
1514
1515 priority_delta = score_multiplier * colocation->score;
1516 dependent->priv->priority = pcmk__add_scores(priority_delta,
1517 dependent->priv->priority);
1518 pcmk__rsc_trace(dependent,
1519 "Applied %s to %s promotion priority (now %s after %s %d)",
1520 colocation->id, dependent->id,
1521 pcmk_readable_score(dependent->priv->priority),
1522 ((score_multiplier == 1)? "adding" : "subtracting"),
1523 colocation->score);
1524
1525 return priority_delta;
1526}
1527
1537static int
1538best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1539 pcmk_resource_t *rsc, const char *attr,
1540 const char *value)
1541{
1542 GHashTable *allowed_nodes_orig = NULL;
1543 GHashTableIter iter;
1544 pcmk_node_t *node = NULL;
1545 int best_score = -PCMK_SCORE_INFINITY;
1546 const char *best_node = NULL;
1547
1548 if ((colocation != NULL) && (rsc == colocation->dependent)
1549 && pcmk_is_set(colocation->flags, pcmk__coloc_explicit)
1550 && pcmk__is_group(rsc->priv->parent)
1551 && (rsc != rsc->priv->parent->priv->children->data)) {
1552 /* The resource is a user-configured colocation's explicit dependent,
1553 * and a group member other than the first, which means the group's
1554 * location constraint scores were not applied to it (see
1555 * pcmk__group_apply_location()). Explicitly consider those scores now.
1556 *
1557 * @TODO This does leave one suboptimal case: if the group itself or
1558 * another member other than the first is explicitly colocated with
1559 * the same primary, the primary will count the group's location scores
1560 * multiple times. This is much less likely than a single member being
1561 * explicitly colocated, so it's an acceptable tradeoff for now.
1562 */
1563 allowed_nodes_orig = rsc->priv->allowed_nodes;
1564 rsc->priv->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1565 for (GList *loc_iter = rsc->priv->scheduler->priv->location_constraints;
1566 loc_iter != NULL; loc_iter = loc_iter->next) {
1567
1568 pcmk__location_t *location = loc_iter->data;
1569
1570 if (location->rsc == rsc->priv->parent) {
1571 rsc->priv->cmds->apply_location(rsc, location);
1572 }
1573 }
1574 }
1575
1576 // Find best allowed node with matching attribute
1577 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1578 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1579
1580 if ((node->assign->score > best_score)
1581 && pcmk__node_available(node, false, false)
1582 && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1583 pcmk__str_casei)) {
1584
1585 best_score = node->assign->score;
1586 best_node = node->priv->name;
1587 }
1588 }
1589
1590 if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1591 if (best_node == NULL) {
1592 crm_info("No allowed node for %s matches node attribute %s=%s",
1593 rsc->id, attr, value);
1594 } else {
1595 crm_info("Allowed node %s for %s had best score (%d) "
1596 "of those matching node attribute %s=%s",
1597 best_node, rsc->id, best_score, attr, value);
1598 }
1599 }
1600
1601 if (allowed_nodes_orig != NULL) {
1602 g_hash_table_destroy(rsc->priv->allowed_nodes);
1603 rsc->priv->allowed_nodes = allowed_nodes_orig;
1604 }
1605 return best_score;
1606}
1607
1616static bool
1617allowed_on_one(const pcmk_resource_t *rsc)
1618{
1619 GHashTableIter iter;
1620 pcmk_node_t *allowed_node = NULL;
1621 int allowed_nodes = 0;
1622
1623 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1624 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1625 if ((allowed_node->assign->score >= 0) && (++allowed_nodes > 1)) {
1626 pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1627 return false;
1628 }
1629 }
1630 pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1631 ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1632 return (allowed_nodes == 1);
1633}
1634
1653static void
1654add_node_scores_matching_attr(GHashTable *nodes,
1655 pcmk_resource_t *source_rsc,
1656 const pcmk_resource_t *target_rsc,
1657 const pcmk__colocation_t *colocation,
1658 float factor, bool only_positive)
1659{
1660 GHashTableIter iter;
1661 pcmk_node_t *node = NULL;
1662 const char *attr = colocation->node_attribute;
1663
1664 // Iterate through each node
1665 g_hash_table_iter_init(&iter, nodes);
1666 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1667 float delta_f = 0;
1668 int delta = 0;
1669 int score = 0;
1670 int new_score = 0;
1671 const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1672
1673 score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1674
1675 if ((factor < 0) && (score < 0)) {
1676 /* If the dependent is anti-colocated, we generally don't want the
1677 * primary to prefer nodes that the dependent avoids. That could
1678 * lead to unnecessary shuffling of the primary when the dependent
1679 * hits its migration threshold somewhere, for example.
1680 *
1681 * However, there are cases when it is desirable. If the dependent
1682 * can't run anywhere but where the primary is, it would be
1683 * worthwhile to move the primary for the sake of keeping the
1684 * dependent active.
1685 *
1686 * We can't know that exactly at this point since we don't know
1687 * where the primary will be assigned, but we can limit considering
1688 * the preference to when the dependent is allowed only on one node.
1689 * This is less than ideal for multiple reasons:
1690 *
1691 * - the dependent could be allowed on more than one node but have
1692 * anti-colocation primaries on each;
1693 * - the dependent could be a clone or bundle with multiple
1694 * instances, and the dependent as a whole is allowed on multiple
1695 * nodes but some instance still can't run
1696 * - the dependent has considered node-specific criteria such as
1697 * location constraints and stickiness by this point, but might
1698 * have other factors that end up disallowing a node
1699 *
1700 * but the alternative is making the primary move when it doesn't
1701 * need to.
1702 *
1703 * We also consider the primary's stickiness and influence, so the
1704 * user has some say in the matter. (This is the configured primary,
1705 * not a particular instance of the primary, but that doesn't matter
1706 * unless stickiness uses a rule to vary by node, and that seems
1707 * acceptable to ignore.)
1708 */
1709 if ((colocation->primary->priv->stickiness >= -score)
1710 || !pcmk__colocation_has_influence(colocation, NULL)
1711 || !allowed_on_one(colocation->dependent)) {
1712 crm_trace("%s: Filtering %d + %f * %d "
1713 "(double negative disallowed)",
1714 pcmk__node_name(node), node->assign->score, factor,
1715 score);
1716 continue;
1717 }
1718 }
1719
1720 if (node->assign->score == INFINITY_HACK) {
1721 crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1722 pcmk__node_name(node), node->assign->score, factor,
1723 score);
1724 continue;
1725 }
1726
1727 delta_f = factor * score;
1728
1729 // Round the number; see http://c-faq.com/fp/round.html
1730 delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1731
1732 /* Small factors can obliterate the small scores that are often actually
1733 * used in configurations. If the score and factor are nonzero, ensure
1734 * that the result is nonzero as well.
1735 */
1736 if ((delta == 0) && (score != 0)) {
1737 if (factor > 0.0) {
1738 delta = 1;
1739 } else if (factor < 0.0) {
1740 delta = -1;
1741 }
1742 }
1743
1744 new_score = pcmk__add_scores(delta, node->assign->score);
1745
1746 if (only_positive && (new_score < 0) && (node->assign->score > 0)) {
1747 crm_trace("%s: Filtering %d + %f * %d = %d "
1748 "(negative disallowed, marking node unusable)",
1749 pcmk__node_name(node), node->assign->score, factor, score,
1750 new_score);
1751 node->assign->score = INFINITY_HACK;
1752 continue;
1753 }
1754
1755 if (only_positive && (new_score < 0) && (node->assign->score == 0)) {
1756 crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1757 pcmk__node_name(node), node->assign->score, factor, score,
1758 new_score);
1759 continue;
1760 }
1761
1762 crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1763 node->assign->score, factor, score, new_score);
1764 node->assign->score = new_score;
1765 }
1766}
1767
1798void
1800 const pcmk_resource_t *target_rsc,
1801 const char *log_id,
1802 GHashTable **nodes,
1803 const pcmk__colocation_t *colocation,
1804 float factor, uint32_t flags)
1805{
1806 GHashTable *work = NULL;
1807
1808 pcmk__assert((source_rsc != NULL) && (nodes != NULL)
1809 && ((colocation != NULL)
1810 || ((target_rsc == NULL) && (*nodes == NULL))));
1811
1812 if (log_id == NULL) {
1813 log_id = source_rsc->id;
1814 }
1815
1816 // Avoid infinite recursion
1817 if (pcmk_is_set(source_rsc->flags, pcmk__rsc_updating_nodes)) {
1818 pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1819 log_id, source_rsc->id);
1820 return;
1821 }
1823
1824 if (*nodes == NULL) {
1825 work = pcmk__copy_node_table(source_rsc->priv->allowed_nodes);
1826 target_rsc = source_rsc;
1827 } else {
1829
1830 pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1831 log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1832 work = pcmk__copy_node_table(*nodes);
1833 add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1834 factor, pos);
1835 }
1836
1837 if (work == NULL) {
1839 return;
1840 }
1841
1842 if (pcmk__any_node_available(work)) {
1843 GList *colocations = NULL;
1844
1846 colocations = pcmk__this_with_colocations(source_rsc);
1847 pcmk__rsc_trace(source_rsc,
1848 "Checking additional %d optional '%s with' "
1849 "constraints",
1850 g_list_length(colocations), source_rsc->id);
1851 } else {
1852 colocations = pcmk__with_this_colocations(source_rsc);
1853 pcmk__rsc_trace(source_rsc,
1854 "Checking additional %d optional 'with %s' "
1855 "constraints",
1856 g_list_length(colocations), source_rsc->id);
1857 }
1859
1860 for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1861 pcmk__colocation_t *constraint = iter->data;
1862
1863 pcmk_resource_t *other = NULL;
1864 float other_factor = factor * constraint->score
1865 / (float) PCMK_SCORE_INFINITY;
1866
1868 other = constraint->primary;
1869 } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1870 continue;
1871 } else {
1872 other = constraint->dependent;
1873 }
1874
1875 pcmk__rsc_trace(source_rsc,
1876 "Optionally merging score of '%s' constraint "
1877 "(%s with %s)",
1878 constraint->id, constraint->dependent->id,
1879 constraint->primary->id);
1880 other->priv->cmds->add_colocated_node_scores(other, target_rsc,
1881 log_id, &work,
1882 constraint,
1883 other_factor, flags);
1884 pe__show_node_scores(true, NULL, log_id, work,
1885 source_rsc->priv->scheduler);
1886 }
1887 g_list_free(colocations);
1888
1890 pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1891 log_id, source_rsc->id);
1892 g_hash_table_destroy(work);
1894 return;
1895 }
1896
1897
1899 pcmk_node_t *node = NULL;
1900 GHashTableIter iter;
1901
1902 g_hash_table_iter_init(&iter, work);
1903 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1904 if (node->assign->score == INFINITY_HACK) {
1905 node->assign->score = 1;
1906 }
1907 }
1908 }
1909
1910 if (*nodes != NULL) {
1911 g_hash_table_destroy(*nodes);
1912 }
1913 *nodes = work;
1914
1916}
1917
1925void
1926pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1927{
1928 pcmk__colocation_t *colocation = data;
1929 pcmk_resource_t *primary = user_data;
1930
1931 pcmk_resource_t *dependent = colocation->dependent;
1932 const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1934
1935 if (!pcmk__colocation_has_influence(colocation, NULL)) {
1936 return;
1937 }
1938 if (pcmk__is_clone(primary)) {
1940 }
1941 pcmk__rsc_trace(primary,
1942 "%s: Incorporating attenuated %s assignment scores due "
1943 "to colocation %s",
1944 primary->id, dependent->id, colocation->id);
1945 dependent->priv->cmds->add_colocated_node_scores(dependent, primary,
1946 dependent->id,
1947 &(primary->priv->allowed_nodes),
1948 colocation, factor, flags);
1949}
1950
1968void
1970 const pcmk_resource_t *primary,
1971 const pcmk__colocation_t *colocation,
1972 const GList *primary_nodes, bool merge_scores)
1973{
1974 GHashTableIter iter;
1975 pcmk_node_t *dependent_node = NULL;
1976
1977 pcmk__assert((dependent != NULL) && (primary != NULL)
1978 && (colocation != NULL));
1979
1980 g_hash_table_iter_init(&iter, dependent->priv->allowed_nodes);
1981 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1982 const pcmk_node_t *primary_node = NULL;
1983
1984 primary_node = pe_find_node_id(primary_nodes,
1985 dependent_node->priv->id);
1986 if (primary_node == NULL) {
1987 dependent_node->assign->score = -PCMK_SCORE_INFINITY;
1988 pcmk__rsc_trace(dependent,
1989 "Banning %s from %s (no primary instance) for %s",
1990 dependent->id, pcmk__node_name(dependent_node),
1991 colocation->id);
1992
1993 } else if (merge_scores) {
1994 dependent_node->assign->score =
1995 pcmk__add_scores(dependent_node->assign->score,
1996 primary_node->assign->score);
1997 pcmk__rsc_trace(dependent,
1998 "Added %s's score %s to %s's score for %s (now %d) "
1999 "for colocation %s",
2000 primary->id,
2001 pcmk_readable_score(primary_node->assign->score),
2002 dependent->id, pcmk__node_name(dependent_node),
2003 dependent_node->assign->score, colocation->id);
2004 }
2005 }
2006}
2007
2018GList *
2020{
2021 GList *list = NULL;
2022
2023 rsc->priv->cmds->with_this_colocations(rsc, rsc, &list);
2024 return list;
2025}
2026
2037GList *
2039{
2040 GList *list = NULL;
2041
2042 rsc->priv->cmds->this_with_colocations(rsc, rsc, &list);
2043 return list;
2044}
@ 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:66
#define PCMK_ACTION_PROMOTE
Definition actions.h:57
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_DEMOTE
Definition actions.h:40
@ pcmk__action_runnable
#define pcmk__clear_action_flags(action, flags_to_clear)
const char * pcmk__node_attr(const pcmk_node_t *node, const char *name, const char *target, enum pcmk__rsc_node node_type)
Definition attrs.c:114
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
uint64_t flags
Definition remote.c:3
Utility functions.
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition complex.c:999
char data[0]
Definition cpg.c:10
uint32_t id
Definition cpg.c:0
A dumping ground.
#define CRM_ATTR_UNAME
Definition crm.h:92
#define CRM_ATTR_ID
Definition crm.h:93
@ 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 int pcmk__parse_constraint_role(const char *id, const char *role_spec, enum rsc_role_e *role)
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__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk__idref_t **tag)
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:365
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_log_xml_trace(xml, text)
Definition logging.h:378
#define crm_trace(fmt, args...)
Definition logging.h:370
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
@ pcmk__wo_set_ordering
#define pcmk__if_tracing(if_action, else_action)
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_scheduler_t * scheduler
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET
Definition options.h:86
#define PCMK_VALUE_HOST
Definition options.h:162
#define PCMK__VALUE_GROUP
const char * action
Definition pcmk_fence.c:32
const char * target
Definition pcmk_fence.c:31
void pcmk__add_this_with_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
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_spec, const char *primary_role_spec, uint32_t flags)
void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
#define INFINITY_HACK
bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
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__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)
const char * pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr, 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)
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition complex.c:1025
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition internal.h:158
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition bundle.c:131
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_resource_t * pe__bundled_resource(const pcmk_resource_t *rsc)
Definition bundle.c:113
@ pcmk__rsc_unassigned
@ pcmk__rsc_promotable
@ pcmk__rsc_replica_container
@ pcmk__rsc_updating_nodes
@ pcmk__rsc_remote_nesting_allowed
@ pcmk__rsc_critical
@ pcmk__rsc_failed
#define pcmk__set_rsc_flags(resource, flags_to_set)
@ pcmk__rsc_node_assigned
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_unpack_error
Definition results.h:122
#define pcmk__assert(expr)
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
rsc_role_e
Definition roles.h:34
@ 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
Scheduler API.
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__rsc_trace(rsc, fmt, args...)
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition scores.c:34
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition scores.c:102
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:26
int pcmk__add_scores(int score1, int score2)
Definition scores.c:159
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:248
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int crm_str_to_boolean(const char *s, int *ret)
Definition strings.c:498
@ pcmk__str_none
@ pcmk__str_null_matches
@ pcmk__str_casei
void(* this_with_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(* with_this_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
void(* apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location)
const char * node_attribute
pcmk_resource_t * primary
pcmk_resource_t * dependent
Location constraint object.
pcmk_resource_t * rsc
pcmk_scheduler_t * scheduler
enum pcmk__rsc_variant variant
const pcmk__assignment_methods_t * cmds
unsigned long long flags
Definition resources.h:69
pcmk__resource_private_t * priv
Definition resources.h:61
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
xmlNode * input
Definition scheduler.h:81
pcmk__node_private_t * priv
Definition nodes.h:85
struct pcmk__node_assignment * assign
Definition nodes.h:79
Wrappers for and extensions to libxml2.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
xmlNode * pcmk__xe_resolve_idref(xmlNode *xml, xmlNode *search)
Definition xml_idref.c:85
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
#define PCMK_XA_RSC_ROLE
Definition xml_names.h:390
#define PCMK_XA_SCORE
Definition xml_names.h:396
#define PCMK_XA_SYMMETRICAL
Definition xml_names.h:415
#define PCMK_XA_WITH_RSC_ROLE
Definition xml_names.h:453
#define PCMK_XE_RESOURCE_REF
Definition xml_names.h:177
#define PCMK_XA_NODE_ATTRIBUTE
Definition xml_names.h:336
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_WITH_RSC
Definition xml_names.h:452
#define PCMK_XA_ROLE
Definition xml_names.h:387
#define PCMK_XE_RSC_COLOCATION
Definition xml_names.h:185
#define PCMK_XA_INFLUENCE
Definition xml_names.h:306
#define PCMK_XE_RESOURCE_SET
Definition xml_names.h:178
#define PCMK_XA_SEQUENTIAL
Definition xml_names.h:398
#define PCMK_XA_RSC
Definition xml_names.h:388
#define PCMK__XA_ORDERING