This source file includes following definitions.
- pcmk__colocation_node_attr
- 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
- pcmk__colocation_has_influence
- 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.h>
17 #include <crm/common/scheduler_internal.h>
18 #include <crm/pengine/status.h>
19 #include <pacemaker-internal.h>
20
21 #include "crm/common/util.h"
22 #include "crm/common/xml_internal.h"
23 #include "crm/common/xml.h"
24 #include "libpacemaker_private.h"
25
26
27 #define INFINITY_HACK (PCMK_SCORE_INFINITY * -100)
28
29
30
31
32
33
34
35
36
37
38
39 const char *
40 pcmk__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
46
47
48
49
50 if (pcmk__is_bundle_node(node) && pcmk__is_bundled(rsc)
51 && (pe__const_top_resource(rsc, false) == pe__bundled_resource(rsc))) {
52 target = PCMK_VALUE_HOST;
53
54 } else if (rsc != NULL) {
55 target = g_hash_table_lookup(rsc->priv->meta,
56 PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
57 }
58
59 return pcmk__node_attr(node, attr, target, pcmk__rsc_node_assigned);
60 }
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 static gint
86 cmp_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
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
126
127
128
129 if (pcmk__is_clone(rsc1)) {
130 if (pcmk_is_set(rsc1->flags, pcmk__rsc_promotable)
131 && !pcmk_is_set(rsc2->flags, pcmk__rsc_promotable)) {
132 return -1;
133 }
134 if (!pcmk_is_set(rsc1->flags, pcmk__rsc_promotable)
135 && pcmk_is_set(rsc2->flags, pcmk__rsc_promotable)) {
136 return 1;
137 }
138 }
139
140 return strcmp(rsc1->id, rsc2->id);
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 static gint
164 cmp_dependent_priority(gconstpointer a, gconstpointer b)
165 {
166 return cmp_colocation_priority(a, b, true);
167 }
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 static gint
190 cmp_primary_priority(gconstpointer a, gconstpointer b)
191 {
192 return cmp_colocation_priority(a, b, false);
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206 void
207 pcmk__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
222
223
224
225
226
227
228
229
230
231
232
233 void
234 pcmk__add_this_with_list(GList **list, GList *addition,
235 const pcmk_resource_t *rsc)
236 {
237 pcmk__assert((list != NULL) && (rsc != NULL));
238
239 pcmk__if_tracing(
240 {},
241 {
242 if (*list == NULL) {
243
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
255
256
257
258
259
260
261
262
263
264
265
266 void
267 pcmk__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
282
283
284
285
286
287
288
289
290
291
292
293 void
294 pcmk__add_with_this_list(GList **list, GList *addition,
295 const pcmk_resource_t *rsc)
296 {
297 pcmk__assert((list != NULL) && (rsc != NULL));
298
299 pcmk__if_tracing(
300 {},
301 {
302 if (*list == NULL) {
303
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
315
316
317
318
319
320
321
322
323
324 static void
325 anti_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
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
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],
363 pcmk__ar_if_required_on_same_node);
364 }
365 }
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 void
384 pcmk__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
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
431 dependent->priv->scheduler->priv->colocation_constraints =
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
443
444
445
446
447
448
449
450
451
452
453
454
455 static uint32_t
456 unpack_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 }
470 if (pcmk_is_set(rsc->flags, pcmk__rsc_critical)) {
471 return pcmk__coloc_influence;
472 }
473 return pcmk__coloc_none;
474 }
475
476 static void
477 unpack_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) {
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
509
510
511
512 if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING),
513 PCMK__VALUE_GROUP,
514 pcmk__str_null_matches|pcmk__str_casei)) {
515 with_previous = true;
516 } else {
517 pcmk__warn_once(pcmk__wo_set_ordering,
518 "Support for '" PCMK__XA_ORDERING "' other than"
519 " '" PCMK__VALUE_GROUP "' in " PCMK_XE_RESOURCE_SET
520 " (such as %s) is deprecated and will be removed in a"
521 " future release",
522 set_id);
523 }
524
525 if ((pcmk__xe_get_bool_attr(set, PCMK_XA_SEQUENTIAL,
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 =
539 pcmk__find_constraint_resource(scheduler->priv->resources,
540 xml_rsc_id);
541 if (resource == NULL) {
542
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) {
548 flags = pcmk__coloc_explicit
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
567
568
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 =
580 pcmk__find_constraint_resource(scheduler->priv->resources,
581 xml_rsc_id);
582 if (resource == NULL) {
583
584 pcmk__config_err("Ignoring %s and later resources in set %s: "
585 "No such resource", xml_rsc_id, set_id);
586 return;
587 }
588 flags = pcmk__coloc_explicit
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,
594 PCMK_XE_RESOURCE_REF)) {
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 =
601 pcmk__find_constraint_resource(scheduler->priv->resources,
602 xml_rsc_id);
603 pcmk__assert(other != NULL);
604 pcmk__new_colocation(set_id, NULL, local_score,
605 resource, other, role, role, flags);
606 }
607 }
608 }
609 }
610
611
612
613
614
615
616
617
618
619
620
621
622
623 static void
624 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
625 int score, const char *influence_s,
626 pcmk_scheduler_t *scheduler)
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
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);
653 rsc_1 = pcmk__find_constraint_resource(scheduler->priv->resources,
654 xml_rsc_id);
655 if (rsc_1 == NULL) {
656
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
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 }
676 rsc_2 = pcmk__find_constraint_resource(scheduler->priv->resources,
677 xml_rsc_id);
678 if (rsc_2 == NULL) {
679
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)) {
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) {
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);
700 rsc_2 = pcmk__find_constraint_resource(scheduler->priv->resources,
701 xml_rsc_id);
702 if (rsc_2 == NULL) {
703
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) {
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);
721 rsc_1 = pcmk__find_constraint_resource(scheduler->priv->resources,
722 xml_rsc_id);
723 if (rsc_1 == NULL) {
724
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 }
731 flags = pcmk__coloc_explicit
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 {
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);
746 rsc_1 = pcmk__find_constraint_resource(scheduler->priv->resources,
747 xml_rsc_id);
748 if (rsc_1 == NULL) {
749
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
757 flags = pcmk__coloc_explicit
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 =
766 pcmk__find_constraint_resource(scheduler->priv->resources,
767 xml_rsc_id);
768 if (rsc_2 == NULL) {
769
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
784
785
786
787
788
789
790
791
792
793
794 static void
795 unpack_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,
804 PCMK_XA_WITH_RSC_ROLE);
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
810 primary = pcmk__find_constraint_resource(scheduler->priv->resources,
811 primary_id);
812 dependent = pcmk__find_constraint_resource(scheduler->priv->resources,
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
826 if (pcmk__xe_attr_is_true(xml_obj, PCMK_XA_SYMMETRICAL)) {
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
838 static int
839 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
840 pcmk_scheduler_t *scheduler)
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);
866 return pcmk_rc_unpack_error;
867 }
868
869
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);
886 return pcmk_rc_unpack_error;
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);
893 return pcmk_rc_unpack_error;
894 }
895
896 if ((dependent != NULL) && (primary != NULL)) {
897
898 return pcmk_rc_ok;
899 }
900
901 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
902
903 pcmk__config_err("Ignoring constraint '%s' because two templates or "
904 "tags cannot be colocated", id);
905 return pcmk_rc_unpack_error;
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
914
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;
920 return pcmk_rc_unpack_error;
921 }
922
923 if (dependent_set != NULL) {
924 if (dependent_role != NULL) {
925
926
927
928 crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role);
929 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
930 }
931 any_sets = true;
932 }
933
934
935
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;
941 return pcmk_rc_unpack_error;
942 }
943
944 if (primary_set != NULL) {
945 if (primary_role != NULL) {
946
947
948
949 crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role);
950 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_WITH_RSC_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
965
966
967
968
969
970
971
972 void
973 pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
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)) {
987 pcmk__config_err("Ignoring " PCMK_XE_RSC_COLOCATION
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) {
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
1016 set = pcmk__xe_resolve_idref(set, scheduler->input);
1017 if (set == NULL) {
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))) {
1025 pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_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
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059 bool
1060 pcmk__colocation_has_influence(const pcmk__colocation_t *colocation,
1061 const pcmk_resource_t *rsc)
1062 {
1063 if (rsc == NULL) {
1064 rsc = colocation->primary;
1065 }
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 if (pcmk_is_set(colocation->dependent->flags,
1080 pcmk__rsc_remote_nesting_allowed)
1081 && !pcmk_is_set(rsc->flags, pcmk__rsc_failed)
1082 && pcmk__list_of_1(rsc->priv->active_nodes)) {
1083 return false;
1084 }
1085
1086
1087
1088
1089 return pcmk_is_set(colocation->flags, pcmk__coloc_influence)
1090 || (rsc->priv->active_nodes == NULL);
1091 }
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101 static void
1102 mark_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
1111 if (pcmk_is_set(action->flags, pcmk__action_runnable)
1112 && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1113
1114 pcmk__clear_action_flags(action, pcmk__action_runnable);
1115 pe_action_set_reason(action, reason_text, false);
1116 pcmk__block_colocation_dependents(action);
1117 pcmk__update_action_for_orderings(action, rsc->priv->scheduler);
1118 }
1119 }
1120
1121
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
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138 void
1139 pcmk__block_colocation_dependents(pcmk_action_t *action)
1140 {
1141 GList *iter = NULL;
1142 GList *colocations = NULL;
1143 pcmk_resource_t *rsc = NULL;
1144 bool is_start = false;
1145
1146 if (pcmk_is_set(action->flags, pcmk__action_runnable)) {
1147 return;
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;
1154 }
1155
1156 pcmk__assert(action->rsc != NULL);
1157
1158
1159
1160
1161
1162 rsc = uber_parent(action->rsc);
1163 if (rsc->priv->parent != NULL) {
1164 rsc = rsc->priv->parent;
1165 }
1166
1167
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;
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
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;
1193 }
1194
1195
1196
1197
1198
1199
1200
1201 if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1202 continue;
1203 }
1204
1205
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
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235 static const pcmk_resource_t *
1236 get_resource_for_role(const pcmk_resource_t *rsc)
1237 {
1238 if (pcmk_is_set(rsc->flags, pcmk__rsc_replica_container)) {
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
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266 enum pcmk__coloc_affects
1267 pcmk__colocation_affects(const pcmk_resource_t *dependent,
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
1279 return pcmk__coloc_affects_nothing;
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,
1289 pcmk__rsc_promotable)
1290 && !pcmk_is_set(dependent_role_rsc->flags, pcmk__rsc_unassigned)) {
1291
1292
1293
1294
1295
1296 return pcmk__coloc_affects_role;
1297 }
1298
1299 if (!preview && !pcmk_is_set(dependent->flags, pcmk__rsc_unassigned)) {
1300
1301
1302
1303 return pcmk__coloc_affects_nothing;
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));
1315 return pcmk__coloc_affects_nothing;
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));
1326 return pcmk__coloc_affects_nothing;
1327 }
1328
1329 return pcmk__coloc_affects_location;
1330 }
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 void
1344 pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent,
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) {
1355 value = pcmk__colocation_node_attr(primary->priv->assigned_node,
1356 attr, primary);
1357
1358 } else if (colocation->score < 0) {
1359
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),
1375 pcmk_readable_score(node->assign->score),
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
1384
1385
1386
1387
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),
1397 pcmk_readable_score(node->assign->score),
1398 pcmk_readable_score(colocation->score));
1399 }
1400 continue;
1401 }
1402
1403 if (colocation->score >= PCMK_SCORE_INFINITY) {
1404
1405
1406
1407
1408
1409 node->assign->score = -PCMK_SCORE_INFINITY;
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
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450 int
1451 pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent,
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
1476
1477
1478
1479
1480
1481
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
1498
1499
1500
1501 score_multiplier = -1;
1502
1503 } else {
1504
1505 return 0;
1506 }
1507
1508 } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1509
1510
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
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537 static int
1538 best_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
1553
1554
1555
1556
1557
1558
1559
1560
1561
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
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
1608
1609
1610
1611
1612
1613
1614
1615
1616 static bool
1617 allowed_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
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653 static void
1654 add_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
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
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
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
1730 delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1731
1732
1733
1734
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
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798 void
1799 pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc,
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
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 }
1822 pcmk__set_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1823
1824 if (*nodes == NULL) {
1825 work = pcmk__copy_node_table(source_rsc->priv->allowed_nodes);
1826 target_rsc = source_rsc;
1827 } else {
1828 const bool pos = pcmk_is_set(flags, pcmk__coloc_select_nonnegative);
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) {
1838 pcmk__clear_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1839 return;
1840 }
1841
1842 if (pcmk__any_node_available(work)) {
1843 GList *colocations = NULL;
1844
1845 if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
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 }
1858 flags |= pcmk__coloc_select_active;
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
1867 if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
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
1889 } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) {
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);
1893 pcmk__clear_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1894 return;
1895 }
1896
1897
1898 if (pcmk_is_set(flags, pcmk__coloc_select_nonnegative)) {
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
1915 pcmk__clear_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1916 }
1917
1918
1919
1920
1921
1922
1923
1924
1925 void
1926 pcmk__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;
1933 uint32_t flags = pcmk__coloc_select_active;
1934
1935 if (!pcmk__colocation_has_influence(colocation, NULL)) {
1936 return;
1937 }
1938 if (pcmk__is_clone(primary)) {
1939 flags |= pcmk__coloc_select_nonnegative;
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
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968 void
1969 pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent,
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
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018 GList *
2019 pcmk__with_this_colocations(const pcmk_resource_t *rsc)
2020 {
2021 GList *list = NULL;
2022
2023 rsc->priv->cmds->with_this_colocations(rsc, rsc, &list);
2024 return list;
2025 }
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037 GList *
2038 pcmk__this_with_colocations(const pcmk_resource_t *rsc)
2039 {
2040 GList *list = NULL;
2041
2042 rsc->priv->cmds->this_with_colocations(rsc, rsc, &list);
2043 return list;
2044 }