This source file includes following definitions.
- cmp_colocation_priority
- cmp_dependent_priority
- cmp_primary_priority
- pcmk__add_this_with
- pcmk__add_this_with_list
- pcmk__add_with_this
- pcmk__add_with_this_list
- anti_colocation_order
- pcmk__new_colocation
- unpack_influence
- unpack_colocation_set
- colocate_rsc_sets
- unpack_simple_colocation
- unpack_colocation_tags
- pcmk__unpack_colocation
- mark_action_blocked
- pcmk__block_colocation_dependents
- get_resource_for_role
- pcmk__colocation_affects
- pcmk__apply_coloc_to_scores
- pcmk__apply_coloc_to_priority
- best_node_score_matching_attr
- allowed_on_one
- add_node_scores_matching_attr
- pcmk__add_colocated_node_scores
- pcmk__add_dependent_scores
- pcmk__colocation_intersect_nodes
- pcmk__with_this_colocations
- pcmk__this_with_colocations
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13 #include <glib.h>
14
15 #include <crm/crm.h>
16 #include <crm/common/scheduler_internal.h>
17 #include <crm/pengine/status.h>
18 #include <pacemaker-internal.h>
19
20 #include "crm/common/util.h"
21 #include "crm/common/xml_internal.h"
22 #include "crm/common/xml.h"
23 #include "libpacemaker_private.h"
24
25
26 #define INFINITY_HACK (PCMK_SCORE_INFINITY * -100)
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 static gint
52 cmp_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
84 if (rsc1->variant > rsc2->variant) {
85 return -1;
86 }
87 if (rsc1->variant < rsc2->variant) {
88 return 1;
89 }
90
91
92
93
94
95 if (pcmk__is_clone(rsc1)) {
96 if (pcmk_is_set(rsc1->flags, pcmk_rsc_promotable)
97 && !pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) {
98 return -1;
99 }
100 if (!pcmk_is_set(rsc1->flags, pcmk_rsc_promotable)
101 && pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) {
102 return 1;
103 }
104 }
105
106 return strcmp(rsc1->id, rsc2->id);
107 }
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129 static gint
130 cmp_dependent_priority(gconstpointer a, gconstpointer b)
131 {
132 return cmp_colocation_priority(a, b, true);
133 }
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 static gint
156 cmp_primary_priority(gconstpointer a, gconstpointer b)
157 {
158 return cmp_colocation_priority(a, b, false);
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172 void
173 pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
174 const pcmk_resource_t *rsc)
175 {
176 CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
177
178 pcmk__rsc_trace(rsc,
179 "Adding colocation %s (%s with %s using %s @%s) to "
180 "'this with' list for %s",
181 colocation->id, colocation->dependent->id,
182 colocation->primary->id, colocation->node_attribute,
183 pcmk_readable_score(colocation->score), rsc->id);
184 *list = g_list_insert_sorted(*list, (gpointer) colocation,
185 cmp_primary_priority);
186 }
187
188
189
190
191
192
193
194
195
196
197
198
199 void
200 pcmk__add_this_with_list(GList **list, GList *addition,
201 const pcmk_resource_t *rsc)
202 {
203 CRM_ASSERT((list != NULL) && (rsc != NULL));
204
205 pcmk__if_tracing(
206 {},
207 {
208 if (*list == NULL) {
209
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
221
222
223
224
225
226
227
228
229
230
231
232 void
233 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
234 const pcmk_resource_t *rsc)
235 {
236 CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
237
238 pcmk__rsc_trace(rsc,
239 "Adding colocation %s (%s with %s using %s @%s) to "
240 "'with this' list for %s",
241 colocation->id, colocation->dependent->id,
242 colocation->primary->id, colocation->node_attribute,
243 pcmk_readable_score(colocation->score), rsc->id);
244 *list = g_list_insert_sorted(*list, (gpointer) colocation,
245 cmp_dependent_priority);
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259 void
260 pcmk__add_with_this_list(GList **list, GList *addition,
261 const pcmk_resource_t *rsc)
262 {
263 CRM_ASSERT((list != NULL) && (rsc != NULL));
264
265 pcmk__if_tracing(
266 {},
267 {
268 if (*list == NULL) {
269
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
281
282
283
284
285
286
287
288
289
290 static void
291 anti_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
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
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],
329 pcmk__ar_if_required_on_same_node);
330 }
331 }
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347 void
348 pcmk__new_colocation(const char *id, const char *node_attr, int score,
349 pcmk_resource_t *dependent, pcmk_resource_t *primary,
350 const char *dependent_role, const char *primary_role,
351 uint32_t flags)
352 {
353 pcmk__colocation_t *new_con = NULL;
354
355 CRM_CHECK(id != NULL, return);
356
357 if ((dependent == NULL) || (primary == NULL)) {
358 pcmk__config_err("Ignoring colocation '%s' because resource "
359 "does not exist", id);
360 return;
361 }
362
363 if (score == 0) {
364 pcmk__rsc_trace(dependent,
365 "Ignoring colocation '%s' (%s with %s) because score is 0",
366 id, dependent->id, primary->id);
367 return;
368 }
369
370 new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t));
371
372 if (pcmk__str_eq(dependent_role, PCMK_ROLE_STARTED,
373 pcmk__str_null_matches|pcmk__str_casei)) {
374 dependent_role = PCMK__ROLE_UNKNOWN;
375 }
376
377 if (pcmk__str_eq(primary_role, PCMK_ROLE_STARTED,
378 pcmk__str_null_matches|pcmk__str_casei)) {
379 primary_role = PCMK__ROLE_UNKNOWN;
380 }
381
382 new_con->id = id;
383 new_con->dependent = dependent;
384 new_con->primary = primary;
385 new_con->score = score;
386 new_con->dependent_role = pcmk_parse_role(dependent_role);
387 new_con->primary_role = pcmk_parse_role(primary_role);
388 new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
389 new_con->flags = flags;
390
391 pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent);
392 pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary);
393
394 dependent->cluster->colocation_constraints = g_list_prepend(
395 dependent->cluster->colocation_constraints, new_con);
396
397 if (score <= -PCMK_SCORE_INFINITY) {
398 anti_colocation_order(dependent, new_con->dependent_role, primary,
399 new_con->primary_role);
400 anti_colocation_order(primary, new_con->primary_role, dependent,
401 new_con->dependent_role);
402 }
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416
417 static uint32_t
418 unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
419 const char *influence_s)
420 {
421 if (influence_s != NULL) {
422 int influence_i = 0;
423
424 if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
425 pcmk__config_err("Constraint '%s' has invalid value for "
426 PCMK_XA_INFLUENCE " (using default)",
427 coloc_id);
428 } else {
429 return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
430 }
431 }
432 if (pcmk_is_set(rsc->flags, pcmk_rsc_critical)) {
433 return pcmk__coloc_influence;
434 }
435 return pcmk__coloc_none;
436 }
437
438 static void
439 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
440 const char *influence_s, pcmk_scheduler_t *scheduler)
441 {
442 xmlNode *xml_rsc = NULL;
443 pcmk_resource_t *other = NULL;
444 pcmk_resource_t *resource = NULL;
445 const char *set_id = pcmk__xe_id(set);
446 const char *role = crm_element_value(set, PCMK_XA_ROLE);
447 bool with_previous = false;
448 int local_score = score;
449 bool sequential = false;
450 uint32_t flags = pcmk__coloc_none;
451 const char *xml_rsc_id = NULL;
452 const char *score_s = crm_element_value(set, PCMK_XA_SCORE);
453
454 if (score_s) {
455 local_score = char2score(score_s);
456 }
457 if (local_score == 0) {
458 crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
459 coloc_id, set_id);
460 return;
461 }
462
463
464
465
466
467 if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING),
468 PCMK__VALUE_GROUP,
469 pcmk__str_null_matches|pcmk__str_casei)) {
470 with_previous = true;
471 } else {
472 pcmk__warn_once(pcmk__wo_set_ordering,
473 "Support for '" PCMK__XA_ORDERING "' other than"
474 " '" PCMK__VALUE_GROUP "' in " PCMK_XE_RESOURCE_SET
475 " (such as %s) is deprecated and will be removed in a"
476 " future release",
477 set_id);
478 }
479
480 if ((pcmk__xe_get_bool_attr(set, PCMK_XA_SEQUENTIAL,
481 &sequential) == pcmk_rc_ok)
482 && !sequential) {
483 return;
484 }
485
486 if (local_score > 0) {
487 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
488 NULL);
489 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
490
491 xml_rsc_id = pcmk__xe_id(xml_rsc);
492 resource = pcmk__find_constraint_resource(scheduler->resources,
493 xml_rsc_id);
494 if (resource == NULL) {
495
496 pcmk__config_err("Ignoring %s and later resources in set %s: "
497 "No such resource", xml_rsc_id, set_id);
498 return;
499 }
500 if (other != NULL) {
501 flags = pcmk__coloc_explicit
502 | unpack_influence(coloc_id, resource, influence_s);
503 if (with_previous) {
504 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
505 resource->id, other->id, set_id);
506 pcmk__new_colocation(set_id, NULL, local_score, resource,
507 other, role, role, flags);
508 } else {
509 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
510 other->id, resource->id, set_id);
511 pcmk__new_colocation(set_id, NULL, local_score, other,
512 resource, role, role, flags);
513 }
514 }
515 other = resource;
516 }
517
518 } else {
519
520
521
522
523
524 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
525 NULL);
526 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
527
528 xmlNode *xml_rsc_with = NULL;
529
530 xml_rsc_id = pcmk__xe_id(xml_rsc);
531 resource = pcmk__find_constraint_resource(scheduler->resources,
532 xml_rsc_id);
533 if (resource == NULL) {
534
535 pcmk__config_err("Ignoring %s and later resources in set %s: "
536 "No such resource", xml_rsc_id, set_id);
537 return;
538 }
539 flags = pcmk__coloc_explicit
540 | unpack_influence(coloc_id, resource, influence_s);
541 for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
542 NULL, NULL);
543 xml_rsc_with != NULL;
544 xml_rsc_with = pcmk__xe_next_same(xml_rsc_with)) {
545
546 xml_rsc_id = pcmk__xe_id(xml_rsc_with);
547 if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
548 break;
549 }
550 other = pcmk__find_constraint_resource(scheduler->resources,
551 xml_rsc_id);
552 CRM_ASSERT(other != NULL);
553 pcmk__new_colocation(set_id, NULL, local_score,
554 resource, other, role, role, flags);
555 }
556 }
557 }
558 }
559
560
561
562
563
564
565
566
567
568
569
570
571
572 static void
573 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
574 int score, const char *influence_s,
575 pcmk_scheduler_t *scheduler)
576 {
577 xmlNode *xml_rsc = NULL;
578 pcmk_resource_t *rsc_1 = NULL;
579 pcmk_resource_t *rsc_2 = NULL;
580
581 const char *xml_rsc_id = NULL;
582 const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE);
583 const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE);
584
585 int rc = pcmk_rc_ok;
586 bool sequential = false;
587 uint32_t flags = pcmk__coloc_none;
588
589 if (score == 0) {
590 crm_trace("Ignoring colocation '%s' between sets %s and %s "
591 "because score is 0",
592 id, pcmk__xe_id(set1), pcmk__xe_id(set2));
593 return;
594 }
595
596 rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential);
597 if ((rc != pcmk_rc_ok) || sequential) {
598
599 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL);
600 if (xml_rsc != NULL) {
601 xml_rsc_id = pcmk__xe_id(xml_rsc);
602 rsc_1 = pcmk__find_constraint_resource(scheduler->resources,
603 xml_rsc_id);
604 if (rsc_1 == NULL) {
605
606 pcmk__config_err("Ignoring colocation of set %s with set %s "
607 "because first resource %s not found",
608 pcmk__xe_id(set1), pcmk__xe_id(set2),
609 xml_rsc_id);
610 return;
611 }
612 }
613 }
614
615 rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential);
616 if ((rc != pcmk_rc_ok) || sequential) {
617
618 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
619 NULL);
620 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
621
622 xml_rsc_id = pcmk__xe_id(xml_rsc);
623 }
624 rsc_2 = pcmk__find_constraint_resource(scheduler->resources,
625 xml_rsc_id);
626 if (rsc_2 == NULL) {
627
628 pcmk__config_err("Ignoring colocation of set %s with set %s "
629 "because last resource %s not found",
630 pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id);
631 return;
632 }
633 }
634
635 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
636 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
637 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
638 flags);
639
640 } else if (rsc_1 != NULL) {
641 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
642 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
643 NULL);
644 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
645
646 xml_rsc_id = pcmk__xe_id(xml_rsc);
647 rsc_2 = pcmk__find_constraint_resource(scheduler->resources,
648 xml_rsc_id);
649 if (rsc_2 == NULL) {
650
651 pcmk__config_err("Ignoring set %s colocation with resource %s "
652 "in set %s: No such resource",
653 pcmk__xe_id(set1), xml_rsc_id,
654 pcmk__xe_id(set2));
655 continue;
656 }
657 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
658 role_2, flags);
659 }
660
661 } else if (rsc_2 != NULL) {
662 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
663 NULL);
664 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
665
666 xml_rsc_id = pcmk__xe_id(xml_rsc);
667 rsc_1 = pcmk__find_constraint_resource(scheduler->resources,
668 xml_rsc_id);
669 if (rsc_1 == NULL) {
670
671 pcmk__config_err("Ignoring colocation of set %s resource %s "
672 "with set %s: No such resource",
673 pcmk__xe_id(set1), xml_rsc_id,
674 pcmk__xe_id(set2));
675 continue;
676 }
677 flags = pcmk__coloc_explicit
678 | unpack_influence(id, rsc_1, influence_s);
679 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
680 role_2, flags);
681 }
682
683 } else {
684 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
685 NULL);
686 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
687
688 xmlNode *xml_rsc_2 = NULL;
689
690 xml_rsc_id = pcmk__xe_id(xml_rsc);
691 rsc_1 = pcmk__find_constraint_resource(scheduler->resources,
692 xml_rsc_id);
693 if (rsc_1 == NULL) {
694
695 pcmk__config_err("Ignoring colocation of set %s resource %s "
696 "with set %s: No such resource",
697 pcmk__xe_id(set1), xml_rsc_id,
698 pcmk__xe_id(set2));
699 continue;
700 }
701
702 flags = pcmk__coloc_explicit
703 | unpack_influence(id, rsc_1, influence_s);
704 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
705 NULL, NULL);
706 xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
707
708 xml_rsc_id = pcmk__xe_id(xml_rsc_2);
709 rsc_2 = pcmk__find_constraint_resource(scheduler->resources,
710 xml_rsc_id);
711 if (rsc_2 == NULL) {
712
713 pcmk__config_err("Ignoring colocation of set %s resource "
714 "%s with set %s resource %s: No such "
715 "resource",
716 pcmk__xe_id(set1), pcmk__xe_id(xml_rsc),
717 pcmk__xe_id(set2), xml_rsc_id);
718 continue;
719 }
720 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
721 role_1, role_2, flags);
722 }
723 }
724 }
725 }
726
727 static void
728 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
729 const char *influence_s, pcmk_scheduler_t *scheduler)
730 {
731 int score_i = 0;
732 uint32_t flags = pcmk__coloc_none;
733
734 const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE);
735 const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
736 const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
737 const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
738 const char *primary_role = crm_element_value(xml_obj,
739 PCMK_XA_WITH_RSC_ROLE);
740 const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE);
741
742 const char *primary_instance = NULL;
743 const char *dependent_instance = NULL;
744 pcmk_resource_t *primary = NULL;
745 pcmk_resource_t *dependent = NULL;
746
747 primary = pcmk__find_constraint_resource(scheduler->resources, primary_id);
748 dependent = pcmk__find_constraint_resource(scheduler->resources,
749 dependent_id);
750
751
752 primary_instance = crm_element_value(xml_obj, PCMK__XA_WITH_RSC_INSTANCE);
753 dependent_instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE);
754 if (dependent_instance != NULL) {
755 pcmk__warn_once(pcmk__wo_coloc_inst,
756 "Support for " PCMK__XA_RSC_INSTANCE " is deprecated "
757 "and will be removed in a future release");
758 }
759 if (primary_instance != NULL) {
760 pcmk__warn_once(pcmk__wo_coloc_inst,
761 "Support for " PCMK__XA_WITH_RSC_INSTANCE " is "
762 "deprecated and will be removed in a future release");
763 }
764
765 if (dependent == NULL) {
766 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
767 "does not exist", id, dependent_id);
768 return;
769
770 } else if (primary == NULL) {
771 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
772 "does not exist", id, primary_id);
773 return;
774
775 } else if ((dependent_instance != NULL) && !pcmk__is_clone(dependent)) {
776 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
777 "is not a clone but instance '%s' was requested",
778 id, dependent_id, dependent_instance);
779 return;
780
781 } else if ((primary_instance != NULL) && !pcmk__is_clone(primary)) {
782 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
783 "is not a clone but instance '%s' was requested",
784 id, primary_id, primary_instance);
785 return;
786 }
787
788 if (dependent_instance != NULL) {
789 dependent = find_clone_instance(dependent, dependent_instance);
790 if (dependent == NULL) {
791 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
792 "does not have an instance '%s'",
793 id, dependent_id, dependent_instance);
794 return;
795 }
796 }
797
798 if (primary_instance != NULL) {
799 primary = find_clone_instance(primary, primary_instance);
800 if (primary == NULL) {
801 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
802 "does not have an instance '%s'",
803 id, primary_id, primary_instance);
804 return;
805 }
806 }
807
808 if (pcmk__xe_attr_is_true(xml_obj, PCMK_XA_SYMMETRICAL)) {
809 pcmk__config_warn("The colocation constraint "
810 "'" PCMK_XA_SYMMETRICAL "' attribute has been "
811 "removed");
812 }
813
814 if (score) {
815 score_i = char2score(score);
816 }
817
818 flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
819 pcmk__new_colocation(id, attr, score_i, dependent, primary,
820 dependent_role, primary_role, flags);
821 }
822
823
824 static int
825 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
826 pcmk_scheduler_t *scheduler)
827 {
828 const char *id = NULL;
829 const char *dependent_id = NULL;
830 const char *primary_id = NULL;
831 const char *dependent_role = NULL;
832 const char *primary_role = NULL;
833
834 pcmk_resource_t *dependent = NULL;
835 pcmk_resource_t *primary = NULL;
836
837 pcmk_tag_t *dependent_tag = NULL;
838 pcmk_tag_t *primary_tag = NULL;
839
840 xmlNode *dependent_set = NULL;
841 xmlNode *primary_set = NULL;
842 bool any_sets = false;
843
844 *expanded_xml = NULL;
845
846 CRM_CHECK(xml_obj != NULL, return EINVAL);
847
848 id = pcmk__xe_id(xml_obj);
849 if (id == NULL) {
850 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
851 xml_obj->name);
852 return pcmk_rc_unpack_error;
853 }
854
855
856 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
857 if (*expanded_xml != NULL) {
858 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
859 return pcmk_rc_ok;
860 }
861
862 dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
863 primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
864 if ((dependent_id == NULL) || (primary_id == NULL)) {
865 return pcmk_rc_ok;
866 }
867
868 if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
869 &dependent_tag)) {
870 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
871 "valid resource or tag", id, dependent_id);
872 return pcmk_rc_unpack_error;
873 }
874
875 if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
876 &primary_tag)) {
877 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
878 "valid resource or tag", id, primary_id);
879 return pcmk_rc_unpack_error;
880 }
881
882 if ((dependent != NULL) && (primary != NULL)) {
883
884 return pcmk_rc_ok;
885 }
886
887 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
888
889 pcmk__config_err("Ignoring constraint '%s' because two templates or "
890 "tags cannot be colocated", id);
891 return pcmk_rc_unpack_error;
892 }
893
894 dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
895 primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE);
896
897 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
898
899
900
901
902 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true,
903 scheduler)) {
904 free_xml(*expanded_xml);
905 *expanded_xml = NULL;
906 return pcmk_rc_unpack_error;
907 }
908
909 if (dependent_set != NULL) {
910 if (dependent_role != NULL) {
911
912
913
914 crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role);
915 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
916 }
917 any_sets = true;
918 }
919
920
921
922
923 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true,
924 scheduler)) {
925 free_xml(*expanded_xml);
926 *expanded_xml = NULL;
927 return pcmk_rc_unpack_error;
928 }
929
930 if (primary_set != NULL) {
931 if (primary_role != NULL) {
932
933
934
935 crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role);
936 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_WITH_RSC_ROLE);
937 }
938 any_sets = true;
939 }
940
941 if (any_sets) {
942 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
943 } else {
944 free_xml(*expanded_xml);
945 *expanded_xml = NULL;
946 }
947
948 return pcmk_rc_ok;
949 }
950
951
952
953
954
955
956
957
958 void
959 pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
960 {
961 int score_i = 0;
962 xmlNode *set = NULL;
963 xmlNode *last = NULL;
964
965 xmlNode *orig_xml = NULL;
966 xmlNode *expanded_xml = NULL;
967
968 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
969 const char *score = NULL;
970 const char *influence_s = NULL;
971
972 if (pcmk__str_empty(id)) {
973 pcmk__config_err("Ignoring " PCMK_XE_RSC_COLOCATION
974 " without " CRM_ATTR_ID);
975 return;
976 }
977
978 if (unpack_colocation_tags(xml_obj, &expanded_xml,
979 scheduler) != pcmk_rc_ok) {
980 return;
981 }
982 if (expanded_xml != NULL) {
983 orig_xml = xml_obj;
984 xml_obj = expanded_xml;
985 }
986
987 score = crm_element_value(xml_obj, PCMK_XA_SCORE);
988 if (score != NULL) {
989 score_i = char2score(score);
990 }
991 influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE);
992
993 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
994 set != NULL; set = pcmk__xe_next_same(set)) {
995
996 set = expand_idref(set, scheduler->input);
997 if (set == NULL) {
998 if (expanded_xml != NULL) {
999 free_xml(expanded_xml);
1000 }
1001 return;
1002 }
1003
1004 if (pcmk__str_empty(pcmk__xe_id(set))) {
1005 pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET
1006 " without " CRM_ATTR_ID);
1007 continue;
1008 }
1009 unpack_colocation_set(set, score_i, id, influence_s, scheduler);
1010
1011 if (last != NULL) {
1012 colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
1013 }
1014 last = set;
1015 }
1016
1017 if (expanded_xml) {
1018 free_xml(expanded_xml);
1019 xml_obj = orig_xml;
1020 }
1021
1022 if (last == NULL) {
1023 unpack_simple_colocation(xml_obj, id, influence_s, scheduler);
1024 }
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035 static void
1036 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1037 const pcmk_resource_t *reason)
1038 {
1039 GList *iter = NULL;
1040 char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1041
1042 for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1043 pcmk_action_t *action = iter->data;
1044
1045 if (pcmk_is_set(action->flags, pcmk_action_runnable)
1046 && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1047
1048 pcmk__clear_action_flags(action, pcmk_action_runnable);
1049 pe_action_set_reason(action, reason_text, false);
1050 pcmk__block_colocation_dependents(action);
1051 pcmk__update_action_for_orderings(action, rsc->cluster);
1052 }
1053 }
1054
1055
1056 for (iter = rsc->children; iter != NULL; iter = iter->next) {
1057 mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1058 }
1059 free(reason_text);
1060 }
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 void
1073 pcmk__block_colocation_dependents(pcmk_action_t *action)
1074 {
1075 GList *iter = NULL;
1076 GList *colocations = NULL;
1077 pcmk_resource_t *rsc = NULL;
1078 bool is_start = false;
1079
1080 if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
1081 return;
1082 }
1083
1084 is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1085 if (!is_start
1086 && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1087 return;
1088 }
1089
1090 CRM_ASSERT(action->rsc != NULL);
1091
1092
1093
1094
1095
1096 rsc = uber_parent(action->rsc);
1097 if (rsc->parent != NULL) {
1098 rsc = rsc->parent;
1099 }
1100
1101
1102 for (iter = rsc->children; iter != NULL; iter = iter->next) {
1103 pcmk_resource_t *child = iter->data;
1104 pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1105 action->task, NULL);
1106
1107 if ((child_action == NULL)
1108 || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1109 crm_trace("Not blocking %s colocation dependents because "
1110 "at least %s has runnable %s",
1111 rsc->id, child->id, action->task);
1112 return;
1113 }
1114 }
1115
1116 crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1117 rsc->id, action->rsc->id, action->task);
1118
1119
1120 colocations = pcmk__with_this_colocations(rsc);
1121 for (iter = colocations; iter != NULL; iter = iter->next) {
1122 pcmk__colocation_t *colocation = iter->data;
1123
1124 if (colocation->score < PCMK_SCORE_INFINITY) {
1125 continue;
1126 }
1127
1128
1129
1130
1131
1132
1133
1134 if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1135 continue;
1136 }
1137
1138
1139 if (colocation->dependent_role == pcmk_role_promoted) {
1140 mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1141 action->rsc);
1142 } else {
1143 mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1144 action->rsc);
1145 }
1146 }
1147 g_list_free(colocations);
1148 }
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168 static const pcmk_resource_t *
1169 get_resource_for_role(const pcmk_resource_t *rsc)
1170 {
1171 if (pcmk_is_set(rsc->flags, pcmk_rsc_replica_container)) {
1172 const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1173
1174 if (child != NULL) {
1175 return child;
1176 }
1177 }
1178 return rsc;
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 enum pcmk__coloc_affects
1200 pcmk__colocation_affects(const pcmk_resource_t *dependent,
1201 const pcmk_resource_t *primary,
1202 const pcmk__colocation_t *colocation, bool preview)
1203 {
1204 const pcmk_resource_t *dependent_role_rsc = NULL;
1205 const pcmk_resource_t *primary_role_rsc = NULL;
1206
1207 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1208 && (colocation != NULL));
1209
1210 if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1211
1212 return pcmk__coloc_affects_nothing;
1213 }
1214
1215 dependent_role_rsc = get_resource_for_role(dependent);
1216 primary_role_rsc = get_resource_for_role(primary);
1217
1218 if ((colocation->dependent_role >= pcmk_role_unpromoted)
1219 && (dependent_role_rsc->parent != NULL)
1220 && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1221 && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1222
1223
1224
1225
1226
1227 return pcmk__coloc_affects_role;
1228 }
1229
1230 if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1231
1232
1233
1234
1235
1236 const pcmk_node_t *primary_node = primary->allocated_to;
1237
1238 if (dependent->allocated_to == NULL) {
1239 crm_trace("Skipping colocation '%s': %s will not run anywhere",
1240 colocation->id, dependent->id);
1241
1242 } else if (colocation->score >= PCMK_SCORE_INFINITY) {
1243
1244
1245 if (!pcmk__same_node(primary_node, dependent->allocated_to)) {
1246 pcmk__sched_err("%s must be colocated with %s but is not "
1247 "(%s vs. %s)",
1248 dependent->id, primary->id,
1249 pcmk__node_name(dependent->allocated_to),
1250 pcmk__node_name(primary_node));
1251 }
1252
1253 } else if (colocation->score <= -PCMK_SCORE_INFINITY) {
1254
1255
1256 if (pcmk__same_node(dependent->allocated_to, primary_node)) {
1257 pcmk__sched_err("%s and %s must be anti-colocated but are "
1258 "assigned to the same node (%s)",
1259 dependent->id, primary->id,
1260 pcmk__node_name(primary_node));
1261 }
1262 }
1263 return pcmk__coloc_affects_nothing;
1264 }
1265
1266 if ((colocation->dependent_role != pcmk_role_unknown)
1267 && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1268 crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1269
1270 "but %s next role is %s",
1271 ((colocation->score < 0)? "anti-" : ""),
1272 colocation->id, pcmk_role_text(colocation->dependent_role),
1273 dependent_role_rsc->id,
1274 pcmk_role_text(dependent_role_rsc->next_role));
1275 return pcmk__coloc_affects_nothing;
1276 }
1277
1278 if ((colocation->primary_role != pcmk_role_unknown)
1279 && (colocation->primary_role != primary_role_rsc->next_role)) {
1280 crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1281 "but %s next role is %s",
1282 ((colocation->score < 0)? "anti-" : ""),
1283 colocation->id, pcmk_role_text(colocation->primary_role),
1284 primary_role_rsc->id,
1285 pcmk_role_text(primary_role_rsc->next_role));
1286 return pcmk__coloc_affects_nothing;
1287 }
1288
1289 return pcmk__coloc_affects_location;
1290 }
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303 void
1304 pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent,
1305 const pcmk_resource_t *primary,
1306 const pcmk__colocation_t *colocation)
1307 {
1308 const char *attr = colocation->node_attribute;
1309 const char *value = NULL;
1310 GHashTable *work = NULL;
1311 GHashTableIter iter;
1312 pcmk_node_t *node = NULL;
1313
1314 if (primary->allocated_to != NULL) {
1315 value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1316 primary);
1317
1318 } else if (colocation->score < 0) {
1319
1320 return;
1321 }
1322
1323 work = pcmk__copy_node_table(dependent->allowed_nodes);
1324
1325 g_hash_table_iter_init(&iter, work);
1326 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1327 if (primary->allocated_to == NULL) {
1328 node->weight = pcmk__add_scores(-colocation->score, node->weight);
1329 pcmk__rsc_trace(dependent,
1330 "Applied %s to %s score on %s (now %s after "
1331 "subtracting %s because primary %s inactive)",
1332 colocation->id, dependent->id,
1333 pcmk__node_name(node),
1334 pcmk_readable_score(node->weight),
1335 pcmk_readable_score(colocation->score), primary->id);
1336 continue;
1337 }
1338
1339 if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1340 value, pcmk__str_casei)) {
1341
1342
1343
1344
1345
1346
1347
1348 if (colocation->score < PCMK_SCORE_INFINITY) {
1349 node->weight = pcmk__add_scores(colocation->score,
1350 node->weight);
1351 pcmk__rsc_trace(dependent,
1352 "Applied %s to %s score on %s (now %s after "
1353 "adding %s)",
1354 colocation->id, dependent->id,
1355 pcmk__node_name(node),
1356 pcmk_readable_score(node->weight),
1357 pcmk_readable_score(colocation->score));
1358 }
1359 continue;
1360 }
1361
1362 if (colocation->score >= PCMK_SCORE_INFINITY) {
1363
1364
1365
1366
1367
1368 node->weight = -PCMK_SCORE_INFINITY;
1369 pcmk__rsc_trace(dependent,
1370 "Banned %s from %s because colocation %s attribute %s "
1371 "does not match",
1372 dependent->id, pcmk__node_name(node),
1373 colocation->id, attr);
1374 }
1375 }
1376
1377 if ((colocation->score <= -PCMK_SCORE_INFINITY)
1378 || (colocation->score >= PCMK_SCORE_INFINITY)
1379 || pcmk__any_node_available(work)) {
1380
1381 g_hash_table_destroy(dependent->allowed_nodes);
1382 dependent->allowed_nodes = work;
1383 work = NULL;
1384
1385 } else {
1386 pcmk__rsc_info(dependent,
1387 "%s: Rolling back scores from %s (no available nodes)",
1388 dependent->id, primary->id);
1389 }
1390
1391 if (work != NULL) {
1392 g_hash_table_destroy(work);
1393 }
1394 }
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409 int
1410 pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent,
1411 const pcmk_resource_t *primary,
1412 const pcmk__colocation_t *colocation)
1413 {
1414 const char *dependent_value = NULL;
1415 const char *primary_value = NULL;
1416 const char *attr = colocation->node_attribute;
1417 int score_multiplier = 1;
1418 int priority_delta = 0;
1419
1420 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1421 && (colocation != NULL));
1422
1423 if (dependent->allocated_to == NULL) {
1424 return 0;
1425 }
1426
1427 if ((primary->allocated_to != NULL)
1428 && (colocation->primary_role != pcmk_role_unknown)) {
1429
1430
1431
1432
1433
1434
1435
1436
1437 const pcmk_resource_t *role_rsc = get_resource_for_role(primary);
1438
1439 if (colocation->primary_role != role_rsc->next_role) {
1440 return 0;
1441 }
1442 }
1443
1444 dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1445 dependent);
1446 primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1447 primary);
1448
1449 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1450 if ((colocation->score == PCMK_SCORE_INFINITY)
1451 && (colocation->dependent_role == pcmk_role_promoted)) {
1452
1453
1454
1455
1456
1457 score_multiplier = -1;
1458
1459 } else {
1460
1461 return 0;
1462 }
1463
1464 } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1465
1466
1467
1468 score_multiplier = -1;
1469 }
1470
1471 priority_delta = score_multiplier * colocation->score;
1472 dependent->priority = pcmk__add_scores(priority_delta, dependent->priority);
1473 pcmk__rsc_trace(dependent,
1474 "Applied %s to %s promotion priority (now %s after %s %d)",
1475 colocation->id, dependent->id,
1476 pcmk_readable_score(dependent->priority),
1477 ((score_multiplier == 1)? "adding" : "subtracting"),
1478 colocation->score);
1479
1480 return priority_delta;
1481 }
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492 static int
1493 best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1494 pcmk_resource_t *rsc, const char *attr,
1495 const char *value)
1496 {
1497 GHashTable *allowed_nodes_orig = NULL;
1498 GHashTableIter iter;
1499 pcmk_node_t *node = NULL;
1500 int best_score = -PCMK_SCORE_INFINITY;
1501 const char *best_node = NULL;
1502
1503 if ((colocation != NULL) && (rsc == colocation->dependent)
1504 && pcmk_is_set(colocation->flags, pcmk__coloc_explicit)
1505 && pcmk__is_group(rsc->parent)
1506 && (rsc != rsc->parent->children->data)) {
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518 allowed_nodes_orig = rsc->allowed_nodes;
1519 rsc->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1520 for (GList *loc_iter = rsc->cluster->placement_constraints;
1521 loc_iter != NULL; loc_iter = loc_iter->next) {
1522
1523 pcmk__location_t *location = loc_iter->data;
1524
1525 if (location->rsc == rsc->parent) {
1526 rsc->cmds->apply_location(rsc, location);
1527 }
1528 }
1529 }
1530
1531
1532 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1533 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1534
1535 if ((node->weight > best_score)
1536 && pcmk__node_available(node, false, false)
1537 && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1538 pcmk__str_casei)) {
1539
1540 best_score = node->weight;
1541 best_node = node->details->uname;
1542 }
1543 }
1544
1545 if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1546 if (best_node == NULL) {
1547 crm_info("No allowed node for %s matches node attribute %s=%s",
1548 rsc->id, attr, value);
1549 } else {
1550 crm_info("Allowed node %s for %s had best score (%d) "
1551 "of those matching node attribute %s=%s",
1552 best_node, rsc->id, best_score, attr, value);
1553 }
1554 }
1555
1556 if (allowed_nodes_orig != NULL) {
1557 g_hash_table_destroy(rsc->allowed_nodes);
1558 rsc->allowed_nodes = allowed_nodes_orig;
1559 }
1560 return best_score;
1561 }
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571 static bool
1572 allowed_on_one(const pcmk_resource_t *rsc)
1573 {
1574 GHashTableIter iter;
1575 pcmk_node_t *allowed_node = NULL;
1576 int allowed_nodes = 0;
1577
1578 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1579 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1580 if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1581 pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1582 return false;
1583 }
1584 }
1585 pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1586 ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1587 return (allowed_nodes == 1);
1588 }
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608 static void
1609 add_node_scores_matching_attr(GHashTable *nodes,
1610 pcmk_resource_t *source_rsc,
1611 const pcmk_resource_t *target_rsc,
1612 const pcmk__colocation_t *colocation,
1613 float factor, bool only_positive)
1614 {
1615 GHashTableIter iter;
1616 pcmk_node_t *node = NULL;
1617 const char *attr = colocation->node_attribute;
1618
1619
1620 g_hash_table_iter_init(&iter, nodes);
1621 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1622 float delta_f = 0;
1623 int delta = 0;
1624 int score = 0;
1625 int new_score = 0;
1626 const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1627
1628 score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1629
1630 if ((factor < 0) && (score < 0)) {
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664 if ((colocation->primary->stickiness >= -score)
1665 || !pcmk__colocation_has_influence(colocation, NULL)
1666 || !allowed_on_one(colocation->dependent)) {
1667 crm_trace("%s: Filtering %d + %f * %d "
1668 "(double negative disallowed)",
1669 pcmk__node_name(node), node->weight, factor, score);
1670 continue;
1671 }
1672 }
1673
1674 if (node->weight == INFINITY_HACK) {
1675 crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1676 pcmk__node_name(node), node->weight, factor, score);
1677 continue;
1678 }
1679
1680 delta_f = factor * score;
1681
1682
1683 delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1684
1685
1686
1687
1688
1689 if ((delta == 0) && (score != 0)) {
1690 if (factor > 0.0) {
1691 delta = 1;
1692 } else if (factor < 0.0) {
1693 delta = -1;
1694 }
1695 }
1696
1697 new_score = pcmk__add_scores(delta, node->weight);
1698
1699 if (only_positive && (new_score < 0) && (node->weight > 0)) {
1700 crm_trace("%s: Filtering %d + %f * %d = %d "
1701 "(negative disallowed, marking node unusable)",
1702 pcmk__node_name(node), node->weight, factor, score,
1703 new_score);
1704 node->weight = INFINITY_HACK;
1705 continue;
1706 }
1707
1708 if (only_positive && (new_score < 0) && (node->weight == 0)) {
1709 crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1710 pcmk__node_name(node), node->weight, factor, score,
1711 new_score);
1712 continue;
1713 }
1714
1715 crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1716 node->weight, factor, score, new_score);
1717 node->weight = new_score;
1718 }
1719 }
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751 void
1752 pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc,
1753 const pcmk_resource_t *target_rsc,
1754 const char *log_id,
1755 GHashTable **nodes,
1756 const pcmk__colocation_t *colocation,
1757 float factor, uint32_t flags)
1758 {
1759 GHashTable *work = NULL;
1760
1761 CRM_ASSERT((source_rsc != NULL) && (nodes != NULL)
1762 && ((colocation != NULL)
1763 || ((target_rsc == NULL) && (*nodes == NULL))));
1764
1765 if (log_id == NULL) {
1766 log_id = source_rsc->id;
1767 }
1768
1769
1770 if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1771 pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1772 log_id, source_rsc->id);
1773 return;
1774 }
1775 pcmk__set_rsc_flags(source_rsc, pcmk_rsc_updating_nodes);
1776
1777 if (*nodes == NULL) {
1778 work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1779 target_rsc = source_rsc;
1780 } else {
1781 const bool pos = pcmk_is_set(flags, pcmk__coloc_select_nonnegative);
1782
1783 pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1784 log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1785 work = pcmk__copy_node_table(*nodes);
1786 add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1787 factor, pos);
1788 }
1789
1790 if (work == NULL) {
1791 pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes);
1792 return;
1793 }
1794
1795 if (pcmk__any_node_available(work)) {
1796 GList *colocations = NULL;
1797
1798 if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1799 colocations = pcmk__this_with_colocations(source_rsc);
1800 pcmk__rsc_trace(source_rsc,
1801 "Checking additional %d optional '%s with' "
1802 "constraints",
1803 g_list_length(colocations), source_rsc->id);
1804 } else {
1805 colocations = pcmk__with_this_colocations(source_rsc);
1806 pcmk__rsc_trace(source_rsc,
1807 "Checking additional %d optional 'with %s' "
1808 "constraints",
1809 g_list_length(colocations), source_rsc->id);
1810 }
1811 flags |= pcmk__coloc_select_active;
1812
1813 for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1814 pcmk__colocation_t *constraint = iter->data;
1815
1816 pcmk_resource_t *other = NULL;
1817 float other_factor = factor * constraint->score
1818 / (float) PCMK_SCORE_INFINITY;
1819
1820 if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1821 other = constraint->primary;
1822 } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1823 continue;
1824 } else {
1825 other = constraint->dependent;
1826 }
1827
1828 pcmk__rsc_trace(source_rsc,
1829 "Optionally merging score of '%s' constraint "
1830 "(%s with %s)",
1831 constraint->id, constraint->dependent->id,
1832 constraint->primary->id);
1833 other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1834 &work, constraint,
1835 other_factor, flags);
1836 pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1837 }
1838 g_list_free(colocations);
1839
1840 } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) {
1841 pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1842 log_id, source_rsc->id);
1843 g_hash_table_destroy(work);
1844 pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes);
1845 return;
1846 }
1847
1848
1849 if (pcmk_is_set(flags, pcmk__coloc_select_nonnegative)) {
1850 pcmk_node_t *node = NULL;
1851 GHashTableIter iter;
1852
1853 g_hash_table_iter_init(&iter, work);
1854 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1855 if (node->weight == INFINITY_HACK) {
1856 node->weight = 1;
1857 }
1858 }
1859 }
1860
1861 if (*nodes != NULL) {
1862 g_hash_table_destroy(*nodes);
1863 }
1864 *nodes = work;
1865
1866 pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes);
1867 }
1868
1869
1870
1871
1872
1873
1874
1875
1876 void
1877 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1878 {
1879 pcmk__colocation_t *colocation = data;
1880 pcmk_resource_t *target_rsc = user_data;
1881
1882 pcmk_resource_t *source_rsc = colocation->dependent;
1883 const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1884 uint32_t flags = pcmk__coloc_select_active;
1885
1886 if (!pcmk__colocation_has_influence(colocation, NULL)) {
1887 return;
1888 }
1889 if (pcmk__is_clone(target_rsc)) {
1890 flags |= pcmk__coloc_select_nonnegative;
1891 }
1892 pcmk__rsc_trace(target_rsc,
1893 "%s: Incorporating attenuated %s assignment scores due "
1894 "to colocation %s",
1895 target_rsc->id, source_rsc->id, colocation->id);
1896 source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1897 source_rsc->id,
1898 &target_rsc->allowed_nodes,
1899 colocation, factor, flags);
1900 }
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919 void
1920 pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent,
1921 const pcmk_resource_t *primary,
1922 const pcmk__colocation_t *colocation,
1923 const GList *primary_nodes, bool merge_scores)
1924 {
1925 GHashTableIter iter;
1926 pcmk_node_t *dependent_node = NULL;
1927
1928 CRM_ASSERT((dependent != NULL) && (primary != NULL)
1929 && (colocation != NULL));
1930
1931 g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1932 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1933 const pcmk_node_t *primary_node = NULL;
1934
1935 primary_node = pe_find_node_id(primary_nodes,
1936 dependent_node->details->id);
1937 if (primary_node == NULL) {
1938 dependent_node->weight = -PCMK_SCORE_INFINITY;
1939 pcmk__rsc_trace(dependent,
1940 "Banning %s from %s (no primary instance) for %s",
1941 dependent->id, pcmk__node_name(dependent_node),
1942 colocation->id);
1943
1944 } else if (merge_scores) {
1945 dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1946 primary_node->weight);
1947 pcmk__rsc_trace(dependent,
1948 "Added %s's score %s to %s's score for %s (now %s) "
1949 "for colocation %s",
1950 primary->id, pcmk_readable_score(primary_node->weight),
1951 dependent->id, pcmk__node_name(dependent_node),
1952 pcmk_readable_score(dependent_node->weight),
1953 colocation->id);
1954 }
1955 }
1956 }
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968 GList *
1969 pcmk__with_this_colocations(const pcmk_resource_t *rsc)
1970 {
1971 GList *list = NULL;
1972
1973 rsc->cmds->with_this_colocations(rsc, rsc, &list);
1974 return list;
1975 }
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987 GList *
1988 pcmk__this_with_colocations(const pcmk_resource_t *rsc)
1989 {
1990 GList *list = NULL;
1991
1992 rsc->cmds->this_with_colocations(rsc, rsc, &list);
1993 return list;
1994 }