This source file includes following definitions.
- cmp_dependent_priority
- cmp_primary_priority
- pcmk__add_this_with
- pcmk__add_with_this
- 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
- pcmk__colocation_affects
- pcmk__apply_coloc_to_weights
- pcmk__apply_coloc_to_priority
- best_node_score_matching_attr
- add_node_scores_matching_attr
- init_group_colocated_nodes
- init_nongroup_colocated_nodes
- pcmk__add_colocated_node_scores
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/pengine/status.h>
17 #include <pacemaker-internal.h>
18
19 #include "crm/common/util.h"
20 #include "crm/common/xml_internal.h"
21 #include "crm/msg_xml.h"
22 #include "libpacemaker_private.h"
23
24 #define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
25 __rsc = pcmk__find_constraint_resource(data_set->resources, __name); \
26 if (__rsc == NULL) { \
27 pcmk__config_err("%s: No resource found for %s", __set, __name); \
28 return; \
29 } \
30 } while(0)
31
32
33 #define INFINITY_HACK (INFINITY * -100)
34
35 static gint
36 cmp_dependent_priority(gconstpointer a, gconstpointer b)
37 {
38 const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
39 const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
40
41 if (a == NULL) {
42 return 1;
43 }
44 if (b == NULL) {
45 return -1;
46 }
47
48 CRM_ASSERT(rsc_constraint1->dependent != NULL);
49 CRM_ASSERT(rsc_constraint1->primary != NULL);
50
51 if (rsc_constraint1->dependent->priority > rsc_constraint2->dependent->priority) {
52 return -1;
53 }
54
55 if (rsc_constraint1->dependent->priority < rsc_constraint2->dependent->priority) {
56 return 1;
57 }
58
59
60 if (rsc_constraint1->dependent->variant > rsc_constraint2->dependent->variant) {
61 return -1;
62 }
63 if (rsc_constraint1->dependent->variant < rsc_constraint2->dependent->variant) {
64 return 1;
65 }
66
67
68
69
70
71 if (rsc_constraint1->dependent->variant == pe_clone) {
72 if (pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
73 && !pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
74 return -1;
75 } else if (!pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
76 && pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
77 return 1;
78 }
79 }
80
81 return strcmp(rsc_constraint1->dependent->id,
82 rsc_constraint2->dependent->id);
83 }
84
85 static gint
86 cmp_primary_priority(gconstpointer a, gconstpointer b)
87 {
88 const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
89 const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
90
91 if (a == NULL) {
92 return 1;
93 }
94 if (b == NULL) {
95 return -1;
96 }
97
98 CRM_ASSERT(rsc_constraint1->dependent != NULL);
99 CRM_ASSERT(rsc_constraint1->primary != NULL);
100
101 if (rsc_constraint1->primary->priority > rsc_constraint2->primary->priority) {
102 return -1;
103 }
104
105 if (rsc_constraint1->primary->priority < rsc_constraint2->primary->priority) {
106 return 1;
107 }
108
109
110 if (rsc_constraint1->primary->variant > rsc_constraint2->primary->variant) {
111 return -1;
112 } else if (rsc_constraint1->primary->variant < rsc_constraint2->primary->variant) {
113 return 1;
114 }
115
116
117
118
119
120 if (rsc_constraint1->primary->variant == pe_clone) {
121 if (pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
122 && !pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
123 return -1;
124 } else if (!pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
125 && pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
126 return 1;
127 }
128 }
129
130 return strcmp(rsc_constraint1->primary->id, rsc_constraint2->primary->id);
131 }
132
133
134
135
136
137
138
139
140 void
141 pcmk__add_this_with(pe_resource_t *rsc, pcmk__colocation_t *colocation)
142 {
143 rsc->rsc_cons = g_list_insert_sorted(rsc->rsc_cons, colocation,
144 cmp_primary_priority);
145 }
146
147
148
149
150
151
152
153
154 void
155 pcmk__add_with_this(pe_resource_t *rsc, pcmk__colocation_t *colocation)
156 {
157 rsc->rsc_cons_lhs = g_list_insert_sorted(rsc->rsc_cons_lhs, colocation,
158 cmp_dependent_priority);
159 }
160
161
162
163
164
165 static void
166 anti_colocation_order(pe_resource_t *first_rsc, int first_role,
167 pe_resource_t *then_rsc, int then_role,
168 pe_working_set_t *data_set)
169 {
170 const char *first_tasks[] = { NULL, NULL };
171 const char *then_tasks[] = { NULL, NULL };
172
173
174 if (first_role == RSC_ROLE_PROMOTED) {
175 first_tasks[0] = CRMD_ACTION_DEMOTE;
176
177 } else {
178 first_tasks[0] = CRMD_ACTION_STOP;
179
180 if (first_role == RSC_ROLE_UNPROMOTED) {
181 first_tasks[1] = CRMD_ACTION_PROMOTE;
182 }
183 }
184
185
186 if (then_role == RSC_ROLE_PROMOTED) {
187 then_tasks[0] = CRMD_ACTION_PROMOTE;
188
189 } else {
190 then_tasks[0] = CRMD_ACTION_START;
191
192 if (then_role == RSC_ROLE_UNPROMOTED) {
193 then_tasks[1] = CRMD_ACTION_DEMOTE;
194 }
195 }
196
197 for (int first_lpc = 0;
198 (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
199
200 for (int then_lpc = 0;
201 (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
202
203 pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
204 then_rsc, then_tasks[then_lpc],
205 pe_order_anti_colocation);
206 }
207 }
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224 void
225 pcmk__new_colocation(const char *id, const char *node_attr, int score,
226 pe_resource_t *dependent, pe_resource_t *primary,
227 const char *dependent_role, const char *primary_role,
228 bool influence, pe_working_set_t *data_set)
229 {
230 pcmk__colocation_t *new_con = NULL;
231
232 if (score == 0) {
233 crm_trace("Ignoring colocation '%s' because score is 0", id);
234 return;
235 }
236 if ((dependent == NULL) || (primary == NULL)) {
237 pcmk__config_err("Ignoring colocation '%s' because resource "
238 "does not exist", id);
239 return;
240 }
241
242 new_con = calloc(1, sizeof(pcmk__colocation_t));
243 if (new_con == NULL) {
244 return;
245 }
246
247 if (pcmk__str_eq(dependent_role, RSC_ROLE_STARTED_S,
248 pcmk__str_null_matches|pcmk__str_casei)) {
249 dependent_role = RSC_ROLE_UNKNOWN_S;
250 }
251
252 if (pcmk__str_eq(primary_role, RSC_ROLE_STARTED_S,
253 pcmk__str_null_matches|pcmk__str_casei)) {
254 primary_role = RSC_ROLE_UNKNOWN_S;
255 }
256
257 new_con->id = id;
258 new_con->dependent = dependent;
259 new_con->primary = primary;
260 new_con->score = score;
261 new_con->dependent_role = text2role(dependent_role);
262 new_con->primary_role = text2role(primary_role);
263 new_con->node_attribute = node_attr;
264 new_con->influence = influence;
265
266 if (node_attr == NULL) {
267 node_attr = CRM_ATTR_UNAME;
268 }
269
270 pe_rsc_trace(dependent, "%s ==> %s (%s %d)",
271 dependent->id, primary->id, node_attr, score);
272
273 pcmk__add_this_with(dependent, new_con);
274 pcmk__add_with_this(primary, new_con);
275
276 data_set->colocation_constraints = g_list_append(data_set->colocation_constraints,
277 new_con);
278
279 if (score <= -INFINITY) {
280 anti_colocation_order(dependent, new_con->dependent_role, primary,
281 new_con->primary_role, data_set);
282 anti_colocation_order(primary, new_con->primary_role, dependent,
283 new_con->dependent_role, data_set);
284 }
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298 static bool
299 unpack_influence(const char *coloc_id, const pe_resource_t *rsc,
300 const char *influence_s)
301 {
302 if (influence_s != NULL) {
303 int influence_i = 0;
304
305 if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
306 pcmk__config_err("Constraint '%s' has invalid value for "
307 XML_COLOC_ATTR_INFLUENCE " (using default)",
308 coloc_id);
309 } else {
310 return (influence_i != 0);
311 }
312 }
313 return pcmk_is_set(rsc->flags, pe_rsc_critical);
314 }
315
316 static void
317 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
318 const char *influence_s, pe_working_set_t *data_set)
319 {
320 xmlNode *xml_rsc = NULL;
321 pe_resource_t *with = NULL;
322 pe_resource_t *resource = NULL;
323 const char *set_id = ID(set);
324 const char *role = crm_element_value(set, "role");
325 const char *ordering = crm_element_value(set, "ordering");
326 int local_score = score;
327 bool sequential = false;
328
329 const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
330
331 if (score_s) {
332 local_score = char2score(score_s);
333 }
334 if (local_score == 0) {
335 crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
336 coloc_id, set_id);
337 return;
338 }
339
340 if (ordering == NULL) {
341 ordering = "group";
342 }
343
344 if (pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok && !sequential) {
345 return;
346
347 } else if ((local_score > 0)
348 && pcmk__str_eq(ordering, "group", pcmk__str_casei)) {
349 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
350 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
351
352 EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
353 if (with != NULL) {
354 pe_rsc_trace(resource, "Colocating %s with %s", resource->id, with->id);
355 pcmk__new_colocation(set_id, NULL, local_score, resource,
356 with, role, role,
357 unpack_influence(coloc_id, resource,
358 influence_s), data_set);
359 }
360 with = resource;
361 }
362
363 } else if (local_score > 0) {
364 pe_resource_t *last = NULL;
365
366 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
367 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
368
369 EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
370 if (last != NULL) {
371 pe_rsc_trace(resource, "Colocating %s with %s",
372 last->id, resource->id);
373 pcmk__new_colocation(set_id, NULL, local_score, last,
374 resource, role, role,
375 unpack_influence(coloc_id, last,
376 influence_s), data_set);
377 }
378
379 last = resource;
380 }
381
382 } else {
383
384
385
386
387
388 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
389 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
390
391 xmlNode *xml_rsc_with = NULL;
392 bool influence = true;
393
394 EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
395 influence = unpack_influence(coloc_id, resource, influence_s);
396
397 for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
398 xml_rsc_with != NULL;
399 xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
400
401 if (pcmk__str_eq(resource->id, ID(xml_rsc_with),
402 pcmk__str_casei)) {
403 break;
404 }
405 EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
406 pe_rsc_trace(resource, "Anti-Colocating %s with %s", resource->id,
407 with->id);
408 pcmk__new_colocation(set_id, NULL, local_score,
409 resource, with, role, role,
410 influence, data_set);
411 }
412 }
413 }
414 }
415
416 static void
417 colocate_rsc_sets(const char *id, xmlNode *set1, xmlNode *set2, int score,
418 const char *influence_s, pe_working_set_t *data_set)
419 {
420 xmlNode *xml_rsc = NULL;
421 pe_resource_t *rsc_1 = NULL;
422 pe_resource_t *rsc_2 = NULL;
423
424 const char *role_1 = crm_element_value(set1, "role");
425 const char *role_2 = crm_element_value(set2, "role");
426
427 int rc = pcmk_rc_ok;
428 bool sequential = false;
429
430 if (score == 0) {
431 crm_trace("Ignoring colocation '%s' between sets because score is 0",
432 id);
433 return;
434 }
435
436 rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential);
437 if (rc != pcmk_rc_ok || sequential) {
438
439 xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
440 if (xml_rsc != NULL) {
441 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
442 }
443 }
444
445 rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential);
446 if (rc != pcmk_rc_ok || sequential) {
447
448 const char *rid = NULL;
449
450 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
451 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
452
453 rid = ID(xml_rsc);
454 }
455 EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
456 }
457
458 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
459 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
460 unpack_influence(id, rsc_1, influence_s),
461 data_set);
462
463 } else if (rsc_1 != NULL) {
464 bool influence = unpack_influence(id, rsc_1, influence_s);
465
466 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
467 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
468
469 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
470 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
471 role_2, influence, data_set);
472 }
473
474 } else if (rsc_2 != NULL) {
475 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
476 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
477
478 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
479 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
480 role_2,
481 unpack_influence(id, rsc_1, influence_s),
482 data_set);
483 }
484
485 } else {
486 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
487 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
488
489 xmlNode *xml_rsc_2 = NULL;
490 bool influence = true;
491
492 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
493 influence = unpack_influence(id, rsc_1, influence_s);
494
495 for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
496 xml_rsc_2 != NULL;
497 xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
498
499 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
500 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
501 role_1, role_2, influence,
502 data_set);
503 }
504 }
505 }
506 }
507
508 static void
509 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
510 const char *influence_s, pe_working_set_t *data_set)
511 {
512 int score_i = 0;
513
514 const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
515 const char *dependent_id = crm_element_value(xml_obj,
516 XML_COLOC_ATTR_SOURCE);
517 const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
518 const char *dependent_role = crm_element_value(xml_obj,
519 XML_COLOC_ATTR_SOURCE_ROLE);
520 const char *primary_role = crm_element_value(xml_obj,
521 XML_COLOC_ATTR_TARGET_ROLE);
522 const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
523
524
525 const char *dependent_instance = crm_element_value(xml_obj,
526 XML_COLOC_ATTR_SOURCE_INSTANCE);
527
528 const char *primary_instance = crm_element_value(xml_obj,
529 XML_COLOC_ATTR_TARGET_INSTANCE);
530
531 pe_resource_t *dependent = pcmk__find_constraint_resource(data_set->resources,
532 dependent_id);
533 pe_resource_t *primary = pcmk__find_constraint_resource(data_set->resources,
534 primary_id);
535
536 if (dependent_instance != NULL) {
537 pe_warn_once(pe_wo_coloc_inst,
538 "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
539 "deprecated and will be removed in a future release.");
540 }
541
542 if (primary_instance != NULL) {
543 pe_warn_once(pe_wo_coloc_inst,
544 "Support for " XML_COLOC_ATTR_TARGET_INSTANCE " is "
545 "deprecated and will be removed in a future release.");
546 }
547
548 if (dependent == NULL) {
549 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
550 "does not exist", id, dependent_id);
551 return;
552
553 } else if (primary == NULL) {
554 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
555 "does not exist", id, primary_id);
556 return;
557
558 } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
559 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
560 "is not a clone but instance '%s' was requested",
561 id, dependent_id, dependent_instance);
562 return;
563
564 } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
565 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
566 "is not a clone but instance '%s' was requested",
567 id, primary_id, primary_instance);
568 return;
569 }
570
571 if (dependent_instance != NULL) {
572 dependent = find_clone_instance(dependent, dependent_instance, data_set);
573 if (dependent == NULL) {
574 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
575 "does not have an instance '%s'",
576 id, dependent_id, dependent_instance);
577 return;
578 }
579 }
580
581 if (primary_instance != NULL) {
582 primary = find_clone_instance(primary, primary_instance, data_set);
583 if (primary == NULL) {
584 pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
585 "does not have an instance '%s'",
586 "'%s'", id, primary_id, primary_instance);
587 return;
588 }
589 }
590
591 if (pcmk__xe_attr_is_true(xml_obj, XML_CONS_ATTR_SYMMETRICAL)) {
592 pcmk__config_warn("The colocation constraint '"
593 XML_CONS_ATTR_SYMMETRICAL
594 "' attribute has been removed");
595 }
596
597 if (score) {
598 score_i = char2score(score);
599 }
600
601 pcmk__new_colocation(id, attr, score_i, dependent, primary,
602 dependent_role, primary_role,
603 unpack_influence(id, dependent, influence_s), data_set);
604 }
605
606
607 static int
608 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
609 pe_working_set_t *data_set)
610 {
611 const char *id = NULL;
612 const char *dependent_id = NULL;
613 const char *primary_id = NULL;
614 const char *dependent_role = NULL;
615 const char *primary_role = NULL;
616
617 pe_resource_t *dependent = NULL;
618 pe_resource_t *primary = NULL;
619
620 pe_tag_t *dependent_tag = NULL;
621 pe_tag_t *primary_tag = NULL;
622
623 xmlNode *dependent_set = NULL;
624 xmlNode *primary_set = NULL;
625 bool any_sets = false;
626
627 *expanded_xml = NULL;
628
629 CRM_CHECK(xml_obj != NULL, return EINVAL);
630
631 id = ID(xml_obj);
632 if (id == NULL) {
633 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
634 crm_element_name(xml_obj));
635 return pcmk_rc_unpack_error;
636 }
637
638
639 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
640 if (*expanded_xml != NULL) {
641 crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
642 return pcmk_rc_ok;
643 }
644
645 dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
646 primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
647 if ((dependent_id == NULL) || (primary_id == NULL)) {
648 return pcmk_rc_ok;
649 }
650
651 if (!pcmk__valid_resource_or_tag(data_set, dependent_id, &dependent,
652 &dependent_tag)) {
653 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
654 "valid resource or tag", id, dependent_id);
655 return pcmk_rc_unpack_error;
656 }
657
658 if (!pcmk__valid_resource_or_tag(data_set, primary_id, &primary,
659 &primary_tag)) {
660 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
661 "valid resource or tag", id, primary_id);
662 return pcmk_rc_unpack_error;
663 }
664
665 if ((dependent != NULL) && (primary != NULL)) {
666
667 return pcmk_rc_ok;
668 }
669
670 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
671
672 pcmk__config_err("Ignoring constraint '%s' because two templates or "
673 "tags cannot be colocated", id);
674 return pcmk_rc_unpack_error;
675 }
676
677 dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
678 primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
679
680 *expanded_xml = copy_xml(xml_obj);
681
682
683 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
684 true, data_set)) {
685 free_xml(*expanded_xml);
686 *expanded_xml = NULL;
687 return pcmk_rc_unpack_error;
688 }
689
690 if (dependent_set != NULL) {
691 if (dependent_role != NULL) {
692
693 crm_xml_add(dependent_set, "role", dependent_role);
694 xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
695 }
696 any_sets = true;
697 }
698
699
700 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
701 true, data_set)) {
702 free_xml(*expanded_xml);
703 *expanded_xml = NULL;
704 return pcmk_rc_unpack_error;
705 }
706
707 if (primary_set != NULL) {
708 if (primary_role != NULL) {
709
710 crm_xml_add(primary_set, "role", primary_role);
711 xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_TARGET_ROLE);
712 }
713 any_sets = true;
714 }
715
716 if (any_sets) {
717 crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
718 } else {
719 free_xml(*expanded_xml);
720 *expanded_xml = NULL;
721 }
722
723 return pcmk_rc_ok;
724 }
725
726
727
728
729
730
731
732
733 void
734 pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
735 {
736 int score_i = 0;
737 xmlNode *set = NULL;
738 xmlNode *last = NULL;
739
740 xmlNode *orig_xml = NULL;
741 xmlNode *expanded_xml = NULL;
742
743 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
744 const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
745 const char *influence_s = crm_element_value(xml_obj,
746 XML_COLOC_ATTR_INFLUENCE);
747
748 if (score) {
749 score_i = char2score(score);
750 }
751
752 if (unpack_colocation_tags(xml_obj, &expanded_xml,
753 data_set) != pcmk_rc_ok) {
754 return;
755 }
756 if (expanded_xml) {
757 orig_xml = xml_obj;
758 xml_obj = expanded_xml;
759 }
760
761 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
762 set = crm_next_same_xml(set)) {
763
764 set = expand_idref(set, data_set->input);
765 if (set == NULL) {
766 if (expanded_xml != NULL) {
767 free_xml(expanded_xml);
768 }
769 return;
770 }
771
772 unpack_colocation_set(set, score_i, id, influence_s, data_set);
773
774 if (last != NULL) {
775 colocate_rsc_sets(id, last, set, score_i, influence_s, data_set);
776 }
777 last = set;
778 }
779
780 if (expanded_xml) {
781 free_xml(expanded_xml);
782 xml_obj = orig_xml;
783 }
784
785 if (last == NULL) {
786 unpack_simple_colocation(xml_obj, id, influence_s, data_set);
787 }
788 }
789
790
791
792
793
794
795
796
797
798 static void
799 mark_action_blocked(pe_resource_t *rsc, const char *task,
800 const pe_resource_t *reason)
801 {
802 char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
803
804 for (GList *gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
805 pe_action_t *action = (pe_action_t *) gIter->data;
806
807 if (pcmk_is_set(action->flags, pe_action_runnable)
808 && pcmk__str_eq(action->task, task, pcmk__str_casei)) {
809
810 pe__clear_action_flags(action, pe_action_runnable);
811 pe_action_set_reason(action, reason_text, false);
812 pcmk__block_colocation_dependents(action, rsc->cluster);
813 pcmk__update_action_for_orderings(action, rsc->cluster);
814 }
815 }
816
817
818 for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
819 mark_action_blocked((pe_resource_t *) (iter->data), task, reason);
820 }
821 free(reason_text);
822 }
823
824
825
826
827
828
829
830
831
832
833
834
835 void
836 pcmk__block_colocation_dependents(pe_action_t *action,
837 pe_working_set_t *data_set)
838 {
839 GList *gIter = NULL;
840 pe_resource_t *rsc = NULL;
841 bool is_start = false;
842
843 if (pcmk_is_set(action->flags, pe_action_runnable)) {
844 return;
845 }
846
847 is_start = pcmk__str_eq(action->task, RSC_START, pcmk__str_none);
848 if (!is_start && !pcmk__str_eq(action->task, RSC_PROMOTE, pcmk__str_none)) {
849 return;
850 }
851
852 CRM_ASSERT(action->rsc != NULL);
853
854
855
856
857
858 rsc = uber_parent(action->rsc);
859 if (rsc->parent != NULL) {
860 rsc = rsc->parent;
861 }
862
863 if (rsc->rsc_cons_lhs == NULL) {
864 return;
865 }
866
867
868 for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
869 pe_resource_t *child = (pe_resource_t *) gIter->data;
870 pe_action_t *child_action = find_first_action(child->actions, NULL,
871 action->task, NULL);
872
873 if ((child_action == NULL)
874 || pcmk_is_set(child_action->flags, pe_action_runnable)) {
875 crm_trace("Not blocking %s colocation dependents because "
876 "at least %s has runnable %s",
877 rsc->id, child->id, action->task);
878 return;
879 }
880 }
881
882 crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
883 rsc->id, action->rsc->id, action->task);
884
885
886 for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
887 pcmk__colocation_t *colocation = (pcmk__colocation_t *) gIter->data;
888
889 if (colocation->score < INFINITY) {
890 continue;
891 }
892
893
894
895
896
897
898
899 if (!is_start && (colocation->primary_role != RSC_ROLE_PROMOTED)) {
900 continue;
901 }
902
903
904 if (colocation->dependent_role == RSC_ROLE_PROMOTED) {
905 mark_action_blocked(colocation->dependent, RSC_PROMOTE,
906 action->rsc);
907 } else {
908 mark_action_blocked(colocation->dependent, RSC_START, action->rsc);
909 }
910 }
911 }
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931 enum pcmk__coloc_affects
932 pcmk__colocation_affects(const pe_resource_t *dependent,
933 const pe_resource_t *primary,
934 const pcmk__colocation_t *colocation, bool preview)
935 {
936 if (!preview && pcmk_is_set(primary->flags, pe_rsc_provisional)) {
937
938 return pcmk__coloc_affects_nothing;
939 }
940
941 if ((colocation->dependent_role >= RSC_ROLE_UNPROMOTED)
942 && (dependent->parent != NULL)
943 && pcmk_is_set(dependent->parent->flags, pe_rsc_promotable)
944 && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
945
946
947
948
949
950 return pcmk__coloc_affects_role;
951 }
952
953 if (!preview && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
954
955
956
957
958
959 const pe_node_t *primary_node = primary->allocated_to;
960
961 if (dependent->allocated_to == NULL) {
962 crm_trace("Skipping colocation '%s': %s will not run anywhere",
963 colocation->id, dependent->id);
964
965 } else if (colocation->score >= INFINITY) {
966
967
968 if ((primary_node == NULL) ||
969 (primary_node->details != dependent->allocated_to->details)) {
970 crm_err("%s must be colocated with %s but is not (%s vs. %s)",
971 dependent->id, primary->id,
972 pe__node_name(dependent->allocated_to),
973 pe__node_name(primary_node));
974 }
975
976 } else if (colocation->score <= -CRM_SCORE_INFINITY) {
977
978
979 if ((primary_node != NULL) &&
980 (dependent->allocated_to->details == primary_node->details)) {
981 crm_err("%s and %s must be anti-colocated but are allocated "
982 "to the same node (%s)",
983 dependent->id, primary->id, pe__node_name(primary_node));
984 }
985 }
986 return pcmk__coloc_affects_nothing;
987 }
988
989 if ((colocation->score > 0)
990 && (colocation->dependent_role != RSC_ROLE_UNKNOWN)
991 && (colocation->dependent_role != dependent->next_role)) {
992
993 crm_trace("Skipping colocation '%s': dependent limited to %s role "
994 "but %s next role is %s",
995 colocation->id, role2text(colocation->dependent_role),
996 dependent->id, role2text(dependent->next_role));
997 return pcmk__coloc_affects_nothing;
998 }
999
1000 if ((colocation->score > 0)
1001 && (colocation->primary_role != RSC_ROLE_UNKNOWN)
1002 && (colocation->primary_role != primary->next_role)) {
1003
1004 crm_trace("Skipping colocation '%s': primary limited to %s role "
1005 "but %s next role is %s",
1006 colocation->id, role2text(colocation->primary_role),
1007 primary->id, role2text(primary->next_role));
1008 return pcmk__coloc_affects_nothing;
1009 }
1010
1011 if ((colocation->score < 0)
1012 && (colocation->dependent_role != RSC_ROLE_UNKNOWN)
1013 && (colocation->dependent_role == dependent->next_role)) {
1014 crm_trace("Skipping anti-colocation '%s': dependent role %s matches",
1015 colocation->id, role2text(colocation->dependent_role));
1016 return pcmk__coloc_affects_nothing;
1017 }
1018
1019 if ((colocation->score < 0)
1020 && (colocation->primary_role != RSC_ROLE_UNKNOWN)
1021 && (colocation->primary_role == primary->next_role)) {
1022 crm_trace("Skipping anti-colocation '%s': primary role %s matches",
1023 colocation->id, role2text(colocation->primary_role));
1024 return pcmk__coloc_affects_nothing;
1025 }
1026
1027 return pcmk__coloc_affects_location;
1028 }
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041 void
1042 pcmk__apply_coloc_to_weights(pe_resource_t *dependent,
1043 const pe_resource_t *primary,
1044 const pcmk__colocation_t *colocation)
1045 {
1046 const char *attribute = CRM_ATTR_ID;
1047 const char *value = NULL;
1048 GHashTable *work = NULL;
1049 GHashTableIter iter;
1050 pe_node_t *node = NULL;
1051
1052 if (colocation->node_attribute != NULL) {
1053 attribute = colocation->node_attribute;
1054 }
1055
1056 if (primary->allocated_to != NULL) {
1057 value = pe_node_attribute_raw(primary->allocated_to, attribute);
1058
1059 } else if (colocation->score < 0) {
1060
1061 return;
1062 }
1063
1064 work = pcmk__copy_node_table(dependent->allowed_nodes);
1065
1066 g_hash_table_iter_init(&iter, work);
1067 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1068 if (primary->allocated_to == NULL) {
1069 pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s inactive)",
1070 colocation->id, dependent->id, pe__node_name(node),
1071 colocation->score, primary->id);
1072 node->weight = pcmk__add_scores(-colocation->score, node->weight);
1073
1074 } else if (pcmk__str_eq(pe_node_attribute_raw(node, attribute), value,
1075 pcmk__str_casei)) {
1076 if (colocation->score < CRM_SCORE_INFINITY) {
1077 pe_rsc_trace(dependent, "%s: %s@%s += %d",
1078 colocation->id, dependent->id,
1079 pe__node_name(node), colocation->score);
1080 node->weight = pcmk__add_scores(colocation->score,
1081 node->weight);
1082 }
1083
1084 } else if (colocation->score >= CRM_SCORE_INFINITY) {
1085 pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s mismatch)",
1086 colocation->id, dependent->id, pe__node_name(node),
1087 colocation->score, attribute);
1088 node->weight = pcmk__add_scores(-colocation->score, node->weight);
1089 }
1090 }
1091
1092 if ((colocation->score <= -INFINITY) || (colocation->score >= INFINITY)
1093 || pcmk__any_node_available(work)) {
1094
1095 g_hash_table_destroy(dependent->allowed_nodes);
1096 dependent->allowed_nodes = work;
1097 work = NULL;
1098
1099 } else {
1100 pe_rsc_info(dependent,
1101 "%s: Rolling back scores from %s (no available nodes)",
1102 dependent->id, primary->id);
1103 }
1104
1105 if (work != NULL) {
1106 g_hash_table_destroy(work);
1107 }
1108 }
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121 void
1122 pcmk__apply_coloc_to_priority(pe_resource_t *dependent,
1123 const pe_resource_t *primary,
1124 const pcmk__colocation_t *colocation)
1125 {
1126 const char *dependent_value = NULL;
1127 const char *primary_value = NULL;
1128 const char *attribute = CRM_ATTR_ID;
1129 int score_multiplier = 1;
1130
1131 if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1132 return;
1133 }
1134
1135 if (colocation->node_attribute != NULL) {
1136 attribute = colocation->node_attribute;
1137 }
1138
1139 dependent_value = pe_node_attribute_raw(dependent->allocated_to, attribute);
1140 primary_value = pe_node_attribute_raw(primary->allocated_to, attribute);
1141
1142 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1143 if ((colocation->score == INFINITY)
1144 && (colocation->dependent_role == RSC_ROLE_PROMOTED)) {
1145 dependent->priority = -INFINITY;
1146 }
1147 return;
1148 }
1149
1150 if ((colocation->primary_role != RSC_ROLE_UNKNOWN)
1151 && (colocation->primary_role != primary->next_role)) {
1152 return;
1153 }
1154
1155 if (colocation->dependent_role == RSC_ROLE_UNPROMOTED) {
1156 score_multiplier = -1;
1157 }
1158
1159 dependent->priority = pcmk__add_scores(score_multiplier * colocation->score,
1160 dependent->priority);
1161 }
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171 static int
1172 best_node_score_matching_attr(const pe_resource_t *rsc, const char *attr,
1173 const char *value)
1174 {
1175 GHashTableIter iter;
1176 pe_node_t *node = NULL;
1177 int best_score = -INFINITY;
1178 const char *best_node = NULL;
1179
1180
1181 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1182 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1183
1184 if ((node->weight > best_score) && pcmk__node_available(node, false, false)
1185 && pcmk__str_eq(value, pe_node_attribute_raw(node, attr), pcmk__str_casei)) {
1186
1187 best_score = node->weight;
1188 best_node = node->details->uname;
1189 }
1190 }
1191
1192 if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_casei)) {
1193 if (best_node == NULL) {
1194 crm_info("No allowed node for %s matches node attribute %s=%s",
1195 rsc->id, attr, value);
1196 } else {
1197 crm_info("Allowed node %s for %s had best score (%d) "
1198 "of those matching node attribute %s=%s",
1199 best_node, rsc->id, best_score, attr, value);
1200 }
1201 }
1202 return best_score;
1203 }
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219 static void
1220 add_node_scores_matching_attr(GHashTable *nodes, const pe_resource_t *rsc,
1221 const char *attr, float factor,
1222 bool only_positive)
1223 {
1224 GHashTableIter iter;
1225 pe_node_t *node = NULL;
1226
1227 if (attr == NULL) {
1228 attr = CRM_ATTR_UNAME;
1229 }
1230
1231
1232 g_hash_table_iter_init(&iter, nodes);
1233 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1234 float weight_f = 0;
1235 int weight = 0;
1236 int score = 0;
1237 int new_score = 0;
1238
1239 score = best_node_score_matching_attr(rsc, attr,
1240 pe_node_attribute_raw(node, attr));
1241
1242 if ((factor < 0) && (score < 0)) {
1243
1244
1245
1246
1247
1248 crm_trace("%s: Filtering %d + %f * %d (double negative disallowed)",
1249 pe__node_name(node), node->weight, factor, score);
1250 continue;
1251 }
1252
1253 if (node->weight == INFINITY_HACK) {
1254 crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1255 pe__node_name(node), node->weight, factor, score);
1256 continue;
1257 }
1258
1259 weight_f = factor * score;
1260
1261
1262 weight = (int) ((weight_f < 0)? (weight_f - 0.5) : (weight_f + 0.5));
1263
1264
1265
1266
1267
1268 if ((weight == 0) && (score != 0)) {
1269 if (factor > 0.0) {
1270 weight = 1;
1271 } else if (factor < 0.0) {
1272 weight = -1;
1273 }
1274 }
1275
1276 new_score = pcmk__add_scores(weight, node->weight);
1277
1278 if (only_positive && (new_score < 0) && (node->weight > 0)) {
1279 crm_trace("%s: Filtering %d + %f * %d = %d "
1280 "(negative disallowed, marking node unusable)",
1281 pe__node_name(node), node->weight, factor, score,
1282 new_score);
1283 node->weight = INFINITY_HACK;
1284 continue;
1285 }
1286
1287 if (only_positive && (new_score < 0) && (node->weight == 0)) {
1288 crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1289 pe__node_name(node), node->weight, factor, score,
1290 new_score);
1291 continue;
1292 }
1293
1294 crm_trace("%s: %d + %f * %d = %d", pe__node_name(node),
1295 node->weight, factor, score, new_score);
1296 node->weight = new_score;
1297 }
1298 }
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316 static GHashTable *
1317 init_group_colocated_nodes(const pe_resource_t *rsc, const char *log_id,
1318 GHashTable **nodes, const char *attr, float factor,
1319 uint32_t flags)
1320 {
1321 GHashTable *work = NULL;
1322 pe_resource_t *member = NULL;
1323
1324
1325 if (rsc->children == NULL) {
1326 return NULL;
1327 }
1328
1329 if (*nodes == NULL) {
1330
1331 member = pe__last_group_member(rsc);
1332 } else {
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 member = rsc->children->data;
1344 }
1345
1346 pe_rsc_trace(rsc, "%s: Merging scores from group %s using member %s "
1347 "(at %.6f)", log_id, rsc->id, member->id, factor);
1348 work = pcmk__copy_node_table(*nodes);
1349 pcmk__add_colocated_node_scores(member, log_id, &work, attr, factor, flags);
1350 return work;
1351 }
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369 static GHashTable *
1370 init_nongroup_colocated_nodes(const pe_resource_t *rsc, const char *log_id,
1371 GHashTable **nodes, const char *attr,
1372 float factor, uint32_t flags)
1373 {
1374 GHashTable *work = NULL;
1375
1376 if (*nodes == NULL) {
1377
1378
1379
1380 work = pcmk__copy_node_table(rsc->allowed_nodes);
1381
1382 } else {
1383 pe_rsc_trace(rsc, "%s: Merging scores from %s (at %.6f)",
1384 log_id, rsc->id, factor);
1385 work = pcmk__copy_node_table(*nodes);
1386 add_node_scores_matching_attr(work, rsc, attr, factor,
1387 pcmk_is_set(flags,
1388 pcmk__coloc_select_nonnegative));
1389 }
1390 return work;
1391 }
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410 void
1411 pcmk__add_colocated_node_scores(pe_resource_t *rsc, const char *log_id,
1412 GHashTable **nodes, const char *attr,
1413 float factor, uint32_t flags)
1414 {
1415 GHashTable *work = NULL;
1416
1417 CRM_CHECK((rsc != NULL) && (nodes != NULL), return);
1418
1419 if (log_id == NULL) {
1420 log_id = rsc->id;
1421 }
1422
1423
1424 if (pcmk_is_set(rsc->flags, pe_rsc_merging)) {
1425 pe_rsc_info(rsc, "%s: Breaking dependency loop at %s",
1426 log_id, rsc->id);
1427 return;
1428 }
1429 pe__set_resource_flags(rsc, pe_rsc_merging);
1430
1431 if (rsc->variant == pe_group) {
1432 work = init_group_colocated_nodes(rsc, log_id, nodes, attr, factor,
1433 flags);
1434 } else {
1435 work = init_nongroup_colocated_nodes(rsc, log_id, nodes, attr, factor,
1436 flags);
1437 }
1438 if (work == NULL) {
1439 pe__clear_resource_flags(rsc, pe_rsc_merging);
1440 return;
1441 }
1442
1443 if (pcmk__any_node_available(work)) {
1444 GList *gIter = NULL;
1445 float multiplier = (factor < 0.0)? -1.0 : 1.0;
1446
1447 if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1448 gIter = rsc->rsc_cons;
1449 pe_rsc_trace(rsc,
1450 "Checking additional %d optional '%s with' constraints",
1451 g_list_length(gIter), rsc->id);
1452
1453 } else if (rsc->variant == pe_group) {
1454 pe_resource_t *last_rsc = pe__last_group_member(rsc);
1455
1456 gIter = last_rsc->rsc_cons_lhs;
1457 pe_rsc_trace(rsc, "Checking additional %d optional 'with group %s' "
1458 "constraints using last member %s",
1459 g_list_length(gIter), rsc->id, last_rsc->id);
1460
1461 } else {
1462 gIter = rsc->rsc_cons_lhs;
1463 pe_rsc_trace(rsc,
1464 "Checking additional %d optional 'with %s' constraints",
1465 g_list_length(gIter), rsc->id);
1466 }
1467
1468 for (; gIter != NULL; gIter = gIter->next) {
1469 pe_resource_t *other = NULL;
1470 pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
1471
1472 if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1473 other = constraint->primary;
1474 } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1475 continue;
1476 } else {
1477 other = constraint->dependent;
1478 }
1479
1480 pe_rsc_trace(rsc, "Optionally merging score of '%s' constraint (%s with %s)",
1481 constraint->id, constraint->dependent->id,
1482 constraint->primary->id);
1483 factor = multiplier * constraint->score / (float) INFINITY;
1484 pcmk__add_colocated_node_scores(other, log_id, &work,
1485 constraint->node_attribute, factor,
1486 flags|pcmk__coloc_select_active);
1487 pe__show_node_weights(true, NULL, log_id, work, rsc->cluster);
1488 }
1489
1490 } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) {
1491 pe_rsc_info(rsc, "%s: Rolling back optional scores from %s",
1492 log_id, rsc->id);
1493 g_hash_table_destroy(work);
1494 pe__clear_resource_flags(rsc, pe_rsc_merging);
1495 return;
1496 }
1497
1498
1499 if (pcmk_is_set(flags, pcmk__coloc_select_nonnegative)) {
1500 pe_node_t *node = NULL;
1501 GHashTableIter iter;
1502
1503 g_hash_table_iter_init(&iter, work);
1504 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1505 if (node->weight == INFINITY_HACK) {
1506 node->weight = 1;
1507 }
1508 }
1509 }
1510
1511 if (*nodes != NULL) {
1512 g_hash_table_destroy(*nodes);
1513 }
1514 *nodes = work;
1515
1516 pe__clear_resource_flags(rsc, pe_rsc_merging);
1517 }