pacemaker  2.1.2-ada5c3b36
Scalable High-Availability cluster resource manager
pcmk_sched_colocation.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU General Public License version 2
7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <stdbool.h>
13 #include <glib.h>
14 
15 #include <crm/crm.h>
16 #include <crm/pengine/status.h>
17 #include <pacemaker-internal.h>
18 
19 #include "libpacemaker_private.h"
20 
21 #define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
22  __rsc = pcmk__find_constraint_resource(data_set->resources, __name); \
23  if (__rsc == NULL) { \
24  pcmk__config_err("%s: No resource found for %s", __set, __name); \
25  return; \
26  } \
27  } while(0)
28 
29 static gint
30 cmp_dependent_priority(gconstpointer a, gconstpointer b)
31 {
32  const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
33  const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
34 
35  if (a == NULL) {
36  return 1;
37  }
38  if (b == NULL) {
39  return -1;
40  }
41 
42  CRM_ASSERT(rsc_constraint1->dependent != NULL);
43  CRM_ASSERT(rsc_constraint1->primary != NULL);
44 
45  if (rsc_constraint1->dependent->priority > rsc_constraint2->dependent->priority) {
46  return -1;
47  }
48 
49  if (rsc_constraint1->dependent->priority < rsc_constraint2->dependent->priority) {
50  return 1;
51  }
52 
53  /* Process clones before primitives and groups */
54  if (rsc_constraint1->dependent->variant > rsc_constraint2->dependent->variant) {
55  return -1;
56  }
57  if (rsc_constraint1->dependent->variant < rsc_constraint2->dependent->variant) {
58  return 1;
59  }
60 
61  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
62  * clones (probably unnecessary, but avoids having to update regression
63  * tests)
64  */
65  if (rsc_constraint1->dependent->variant == pe_clone) {
66  if (pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
67  && !pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
68  return -1;
69  } else if (!pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
70  && pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
71  return 1;
72  }
73  }
74 
75  return strcmp(rsc_constraint1->dependent->id,
76  rsc_constraint2->dependent->id);
77 }
78 
79 static gint
80 cmp_primary_priority(gconstpointer a, gconstpointer b)
81 {
82  const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
83  const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
84 
85  if (a == NULL) {
86  return 1;
87  }
88  if (b == NULL) {
89  return -1;
90  }
91 
92  CRM_ASSERT(rsc_constraint1->dependent != NULL);
93  CRM_ASSERT(rsc_constraint1->primary != NULL);
94 
95  if (rsc_constraint1->primary->priority > rsc_constraint2->primary->priority) {
96  return -1;
97  }
98 
99  if (rsc_constraint1->primary->priority < rsc_constraint2->primary->priority) {
100  return 1;
101  }
102 
103  /* Process clones before primitives and groups */
104  if (rsc_constraint1->primary->variant > rsc_constraint2->primary->variant) {
105  return -1;
106  } else if (rsc_constraint1->primary->variant < rsc_constraint2->primary->variant) {
107  return 1;
108  }
109 
110  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
111  * clones (probably unnecessary, but avoids having to update regression
112  * tests)
113  */
114  if (rsc_constraint1->primary->variant == pe_clone) {
115  if (pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
116  && !pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
117  return -1;
118  } else if (!pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
119  && pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
120  return 1;
121  }
122  }
123 
124  return strcmp(rsc_constraint1->primary->id, rsc_constraint2->primary->id);
125 }
126 
131 static void
132 anti_colocation_order(pe_resource_t *first_rsc, int first_role,
133  pe_resource_t *then_rsc, int then_role,
134  pe_working_set_t *data_set)
135 {
136  const char *first_tasks[] = { NULL, NULL };
137  const char *then_tasks[] = { NULL, NULL };
138 
139  /* Actions to make first_rsc lose first_role */
140  if (first_role == RSC_ROLE_PROMOTED) {
141  first_tasks[0] = CRMD_ACTION_DEMOTE;
142 
143  } else {
144  first_tasks[0] = CRMD_ACTION_STOP;
145 
146  if (first_role == RSC_ROLE_UNPROMOTED) {
147  first_tasks[1] = CRMD_ACTION_PROMOTE;
148  }
149  }
150 
151  /* Actions to make then_rsc gain then_role */
152  if (then_role == RSC_ROLE_PROMOTED) {
153  then_tasks[0] = CRMD_ACTION_PROMOTE;
154 
155  } else {
156  then_tasks[0] = CRMD_ACTION_START;
157 
158  if (then_role == RSC_ROLE_UNPROMOTED) {
159  then_tasks[1] = CRMD_ACTION_DEMOTE;
160  }
161  }
162 
163  for (int first_lpc = 0;
164  (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
165 
166  for (int then_lpc = 0;
167  (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
168 
169  pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
170  then_rsc, then_tasks[then_lpc],
171  pe_order_anti_colocation, data_set);
172  }
173  }
174 }
175 
190 void
191 pcmk__new_colocation(const char *id, const char *node_attr, int score,
192  pe_resource_t *dependent, pe_resource_t *primary,
193  const char *dependent_role, const char *primary_role,
194  bool influence, pe_working_set_t *data_set)
195 {
196  pcmk__colocation_t *new_con = NULL;
197 
198  if (score == 0) {
199  crm_trace("Ignoring colocation '%s' because score is 0", id);
200  return;
201  }
202  if ((dependent == NULL) || (primary == NULL)) {
203  pcmk__config_err("Ignoring colocation '%s' because resource "
204  "does not exist", id);
205  return;
206  }
207 
208  new_con = calloc(1, sizeof(pcmk__colocation_t));
209  if (new_con == NULL) {
210  return;
211  }
212 
213  if (pcmk__str_eq(dependent_role, RSC_ROLE_STARTED_S,
215  dependent_role = RSC_ROLE_UNKNOWN_S;
216  }
217 
218  if (pcmk__str_eq(primary_role, RSC_ROLE_STARTED_S,
220  primary_role = RSC_ROLE_UNKNOWN_S;
221  }
222 
223  new_con->id = id;
224  new_con->dependent = dependent;
225  new_con->primary = primary;
226  new_con->score = score;
227  new_con->dependent_role = text2role(dependent_role);
228  new_con->primary_role = text2role(primary_role);
229  new_con->node_attribute = node_attr;
230  new_con->influence = influence;
231 
232  if (node_attr == NULL) {
233  node_attr = CRM_ATTR_UNAME;
234  }
235 
236  pe_rsc_trace(dependent, "%s ==> %s (%s %d)",
237  dependent->id, primary->id, node_attr, score);
238 
239  dependent->rsc_cons = g_list_insert_sorted(dependent->rsc_cons, new_con,
240  cmp_primary_priority);
241 
242  primary->rsc_cons_lhs = g_list_insert_sorted(primary->rsc_cons_lhs, new_con,
243  cmp_dependent_priority);
244 
245  data_set->colocation_constraints = g_list_append(data_set->colocation_constraints,
246  new_con);
247 
248  if (score <= -INFINITY) {
249  anti_colocation_order(dependent, new_con->dependent_role, primary,
250  new_con->primary_role, data_set);
251  anti_colocation_order(primary, new_con->primary_role, dependent,
252  new_con->dependent_role, data_set);
253  }
254 }
255 
267 static bool
268 unpack_influence(const char *coloc_id, const pe_resource_t *rsc,
269  const char *influence_s)
270 {
271  if (influence_s != NULL) {
272  int influence_i = 0;
273 
274  if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
275  pcmk__config_err("Constraint '%s' has invalid value for "
276  XML_COLOC_ATTR_INFLUENCE " (using default)",
277  coloc_id);
278  } else {
279  return (influence_i != 0);
280  }
281  }
282  return pcmk_is_set(rsc->flags, pe_rsc_critical);
283 }
284 
285 static void
286 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
287  const char *influence_s, pe_working_set_t *data_set)
288 {
289  xmlNode *xml_rsc = NULL;
290  pe_resource_t *with = NULL;
291  pe_resource_t *resource = NULL;
292  const char *set_id = ID(set);
293  const char *role = crm_element_value(set, "role");
294  const char *sequential = crm_element_value(set, "sequential");
295  const char *ordering = crm_element_value(set, "ordering");
296  int local_score = score;
297 
298  const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
299 
300  if (score_s) {
301  local_score = char2score(score_s);
302  }
303  if (local_score == 0) {
304  crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
305  coloc_id, set_id);
306  return;
307  }
308 
309  if (ordering == NULL) {
310  ordering = "group";
311  }
312 
313  if ((sequential != NULL) && !crm_is_true(sequential)) {
314  return;
315 
316  } else if ((local_score > 0)
317  && pcmk__str_eq(ordering, "group", pcmk__str_casei)) {
318  for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
319  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
320 
321  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
322  if (with != NULL) {
323  pe_rsc_trace(resource, "Colocating %s with %s", resource->id, with->id);
324  pcmk__new_colocation(set_id, NULL, local_score, resource,
325  with, role, role,
326  unpack_influence(coloc_id, resource,
327  influence_s), data_set);
328  }
329  with = resource;
330  }
331 
332  } else if (local_score > 0) {
333  pe_resource_t *last = NULL;
334 
335  for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
336  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
337 
338  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
339  if (last != NULL) {
340  pe_rsc_trace(resource, "Colocating %s with %s",
341  last->id, resource->id);
342  pcmk__new_colocation(set_id, NULL, local_score, last,
343  resource, role, role,
344  unpack_influence(coloc_id, last,
345  influence_s), data_set);
346  }
347 
348  last = resource;
349  }
350 
351  } else {
352  /* Anti-colocating with every prior resource is
353  * the only way to ensure the intuitive result
354  * (i.e. that no one in the set can run with anyone else in the set)
355  */
356 
357  for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
358  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
359 
360  xmlNode *xml_rsc_with = NULL;
361  bool influence = true;
362 
363  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
364  influence = unpack_influence(coloc_id, resource, influence_s);
365 
366  for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
367  xml_rsc_with != NULL;
368  xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
369 
370  if (pcmk__str_eq(resource->id, ID(xml_rsc_with),
371  pcmk__str_casei)) {
372  break;
373  }
374  EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
375  pe_rsc_trace(resource, "Anti-Colocating %s with %s", resource->id,
376  with->id);
377  pcmk__new_colocation(set_id, NULL, local_score,
378  resource, with, role, role,
379  influence, data_set);
380  }
381  }
382  }
383 }
384 
385 static void
386 colocate_rsc_sets(const char *id, xmlNode *set1, xmlNode *set2, int score,
387  const char *influence_s, pe_working_set_t *data_set)
388 {
389  xmlNode *xml_rsc = NULL;
390  pe_resource_t *rsc_1 = NULL;
391  pe_resource_t *rsc_2 = NULL;
392 
393  const char *role_1 = crm_element_value(set1, "role");
394  const char *role_2 = crm_element_value(set2, "role");
395 
396  const char *sequential_1 = crm_element_value(set1, "sequential");
397  const char *sequential_2 = crm_element_value(set2, "sequential");
398 
399  if (score == 0) {
400  crm_trace("Ignoring colocation '%s' between sets because score is 0",
401  id);
402  return;
403  }
404  if ((sequential_1 == NULL) || crm_is_true(sequential_1)) {
405  // Get the first one
406  xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
407  if (xml_rsc != NULL) {
408  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
409  }
410  }
411 
412  if ((sequential_2 == NULL) || crm_is_true(sequential_2)) {
413  // Get the last one
414  const char *rid = NULL;
415 
416  for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
417  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
418 
419  rid = ID(xml_rsc);
420  }
421  EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
422  }
423 
424  if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
425  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
426  unpack_influence(id, rsc_1, influence_s),
427  data_set);
428 
429  } else if (rsc_1 != NULL) {
430  bool influence = unpack_influence(id, rsc_1, influence_s);
431 
432  for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
433  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
434 
435  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
436  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
437  role_2, influence, data_set);
438  }
439 
440  } else if (rsc_2 != NULL) {
441  for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
442  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
443 
444  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
445  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
446  role_2,
447  unpack_influence(id, rsc_1, influence_s),
448  data_set);
449  }
450 
451  } else {
452  for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
453  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
454 
455  xmlNode *xml_rsc_2 = NULL;
456  bool influence = true;
457 
458  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
459  influence = unpack_influence(id, rsc_1, influence_s);
460 
461  for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
462  xml_rsc_2 != NULL;
463  xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
464 
465  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
466  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
467  role_1, role_2, influence,
468  data_set);
469  }
470  }
471  }
472 }
473 
474 static void
475 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
476  const char *influence_s, pe_working_set_t *data_set)
477 {
478  int score_i = 0;
479 
480  const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
481  const char *dependent_id = crm_element_value(xml_obj,
483  const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
484  const char *dependent_role = crm_element_value(xml_obj,
486  const char *primary_role = crm_element_value(xml_obj,
488  const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
489  const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
490 
491  // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
492  const char *dependent_instance = crm_element_value(xml_obj,
494  const char *primary_instance = crm_element_value(xml_obj,
496 
498  dependent_id);
500  primary_id);
501 
502  if (dependent == NULL) {
503  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
504  "does not exist", id, dependent_id);
505  return;
506 
507  } else if (primary == NULL) {
508  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
509  "does not exist", id, primary_id);
510  return;
511 
512  } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
513  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
514  "is not a clone but instance '%s' was requested",
515  id, dependent_id, dependent_instance);
516  return;
517 
518  } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
519  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
520  "is not a clone but instance '%s' was requested",
521  id, primary_id, primary_instance);
522  return;
523  }
524 
525  if (dependent_instance != NULL) {
526  dependent = find_clone_instance(dependent, dependent_instance, data_set);
527  if (dependent == NULL) {
528  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
529  "does not have an instance '%s'",
530  id, dependent_id, dependent_instance);
531  return;
532  }
533  }
534 
535  if (primary_instance != NULL) {
536  primary = find_clone_instance(primary, primary_instance, data_set);
537  if (primary == NULL) {
538  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
539  "does not have an instance '%s'",
540  "'%s'", id, primary_id, primary_instance);
541  return;
542  }
543  }
544 
545  if (crm_is_true(symmetrical)) {
546  pcmk__config_warn("The colocation constraint '"
548  "' attribute has been removed");
549  }
550 
551  if (score) {
552  score_i = char2score(score);
553  }
554 
555  pcmk__new_colocation(id, attr, score_i, dependent, primary,
556  dependent_role, primary_role,
557  unpack_influence(id, dependent, influence_s), data_set);
558 }
559 
560 // \return Standard Pacemaker return code
561 static int
562 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
563  pe_working_set_t *data_set)
564 {
565  const char *id = NULL;
566  const char *dependent_id = NULL;
567  const char *primary_id = NULL;
568  const char *dependent_role = NULL;
569  const char *primary_role = NULL;
570 
571  pe_resource_t *dependent = NULL;
572  pe_resource_t *primary = NULL;
573 
574  pe_tag_t *dependent_tag = NULL;
575  pe_tag_t *primary_tag = NULL;
576 
577  xmlNode *dependent_set = NULL;
578  xmlNode *primary_set = NULL;
579  bool any_sets = false;
580 
581  *expanded_xml = NULL;
582 
583  CRM_CHECK(xml_obj != NULL, return pcmk_rc_schema_validation);
584 
585  id = ID(xml_obj);
586  if (id == NULL) {
587  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
588  crm_element_name(xml_obj));
590  }
591 
592  // Check whether there are any resource sets with template or tag references
593  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
594  if (*expanded_xml != NULL) {
595  crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
596  return pcmk_rc_ok;
597  }
598 
599  dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
600  primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
601  if ((dependent_id == NULL) || (primary_id == NULL)) {
602  return pcmk_rc_ok;
603  }
604 
605  if (!pcmk__valid_resource_or_tag(data_set, dependent_id, &dependent,
606  &dependent_tag)) {
607  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
608  "valid resource or tag", id, dependent_id);
610  }
611 
612  if (!pcmk__valid_resource_or_tag(data_set, primary_id, &primary,
613  &primary_tag)) {
614  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
615  "valid resource or tag", id, primary_id);
617  }
618 
619  if ((dependent != NULL) && (primary != NULL)) {
620  /* Neither side references any template/tag. */
621  return pcmk_rc_ok;
622  }
623 
624  if ((dependent_tag != NULL) && (primary_tag != NULL)) {
625  // A colocation constraint between two templates/tags makes no sense
626  pcmk__config_err("Ignoring constraint '%s' because two templates or "
627  "tags cannot be colocated", id);
629  }
630 
631  dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
632  primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
633 
634  *expanded_xml = copy_xml(xml_obj);
635 
636  // Convert template/tag reference in "rsc" into resource_set under constraint
637  if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
638  true, data_set)) {
639  free_xml(*expanded_xml);
640  *expanded_xml = NULL;
642  }
643 
644  if (dependent_set != NULL) {
645  if (dependent_role != NULL) {
646  // Move "rsc-role" into converted resource_set as "role"
647  crm_xml_add(dependent_set, "role", dependent_role);
649  }
650  any_sets = true;
651  }
652 
653  // Convert template/tag reference in "with-rsc" into resource_set under constraint
654  if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
655  true, data_set)) {
656  free_xml(*expanded_xml);
657  *expanded_xml = NULL;
659  }
660 
661  if (primary_set != NULL) {
662  if (primary_role != NULL) {
663  // Move "with-rsc-role" into converted resource_set as "role"
664  crm_xml_add(primary_set, "role", primary_role);
666  }
667  any_sets = true;
668  }
669 
670  if (any_sets) {
671  crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
672  } else {
673  free_xml(*expanded_xml);
674  *expanded_xml = NULL;
675  }
676 
677  return pcmk_rc_ok;
678 }
679 
687 void
688 pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
689 {
690  int score_i = 0;
691  xmlNode *set = NULL;
692  xmlNode *last = NULL;
693 
694  xmlNode *orig_xml = NULL;
695  xmlNode *expanded_xml = NULL;
696 
697  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
698  const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
699  const char *influence_s = crm_element_value(xml_obj,
701 
702  if (score) {
703  score_i = char2score(score);
704  }
705 
706  if (unpack_colocation_tags(xml_obj, &expanded_xml,
707  data_set) != pcmk_rc_ok) {
708  return;
709  }
710  if (expanded_xml) {
711  orig_xml = xml_obj;
712  xml_obj = expanded_xml;
713  }
714 
715  for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
716  set = crm_next_same_xml(set)) {
717 
718  set = expand_idref(set, data_set->input);
719  if (set == NULL) { // Configuration error, message already logged
720  if (expanded_xml != NULL) {
721  free_xml(expanded_xml);
722  }
723  return;
724  }
725 
726  unpack_colocation_set(set, score_i, id, influence_s, data_set);
727 
728  if (last != NULL) {
729  colocate_rsc_sets(id, last, set, score_i, influence_s, data_set);
730  }
731  last = set;
732  }
733 
734  if (expanded_xml) {
735  free_xml(expanded_xml);
736  xml_obj = orig_xml;
737  }
738 
739  if (last == NULL) {
740  unpack_simple_colocation(xml_obj, id, influence_s, data_set);
741  }
742 }
743 
744 static void
745 mark_start_blocked(pe_resource_t *rsc, pe_resource_t *reason,
746  pe_working_set_t *data_set)
747 {
748  char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
749 
750  for (GList *gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
751  pe_action_t *action = (pe_action_t *) gIter->data;
752 
754  && pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)) {
755 
757  pe_action_set_reason(action, reason_text, false);
759  update_action(action, data_set);
760  }
761  }
762  free(reason_text);
763 }
764 
772 void
774 {
775  GList *gIter = NULL;
776  pe_resource_t *rsc = NULL;
777 
779  && pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)) {
780 
781  rsc = uber_parent(action->rsc);
782  if (rsc->parent) {
783  /* For bundles, uber_parent() returns the clone, not the bundle, so
784  * the existence of rsc->parent implies this is a bundle.
785  * In this case, we need the bundle resource, so that we can check
786  * if all containers are stopped/stopping.
787  */
788  rsc = rsc->parent;
789  }
790  }
791 
792  if ((rsc == NULL) || (rsc->rsc_cons_lhs == NULL)) {
793  return;
794  }
795 
796  // Block colocated starts only if all children (if any) have unrunnable starts
797  for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
798  pe_resource_t *child = (pe_resource_t *)gIter->data;
799  pe_action_t *start = find_first_action(child->actions, NULL, RSC_START, NULL);
800 
801  if ((start == NULL) || pcmk_is_set(start->flags, pe_action_runnable)) {
802  return;
803  }
804  }
805 
806  for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
807  pcmk__colocation_t *colocate_with = (pcmk__colocation_t *) gIter->data;
808 
809  if (colocate_with->score == INFINITY) {
810  mark_start_blocked(colocate_with->dependent, action->rsc, data_set);
811  }
812  }
813 }
814 
835  pcmk__colocation_t *constraint, bool preview)
836 {
837  if (!preview && pcmk_is_set(primary->flags, pe_rsc_provisional)) {
838  // Primary resource has not been allocated yet, so we can't do anything
840  }
841 
842  if ((constraint->dependent_role >= RSC_ROLE_UNPROMOTED)
843  && (dependent->parent != NULL)
844  && pcmk_is_set(dependent->parent->flags, pe_rsc_promotable)
845  && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
846 
847  /* This is a colocation by role, and the dependent is a promotable clone
848  * that has already been allocated, so the colocation should now affect
849  * the role.
850  */
852  }
853 
854  if (!preview && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
855  /* The dependent resource has already been through allocation, so the
856  * constraint no longer has any effect. Log an error if a mandatory
857  * colocation constraint has been violated.
858  */
859 
860  const pe_node_t *primary_node = primary->allocated_to;
861 
862  if (dependent->allocated_to == NULL) {
863  crm_trace("Skipping colocation '%s': %s will not run anywhere",
864  constraint->id, dependent->id);
865 
866  } else if (constraint->score >= INFINITY) {
867  // Dependent resource must colocate with primary resource
868 
869  if ((primary_node == NULL) ||
870  (primary_node->details != dependent->allocated_to->details)) {
871  crm_err("%s must be colocated with %s but is not (%s vs. %s)",
872  dependent->id, primary->id,
873  dependent->allocated_to->details->uname,
874  (primary_node == NULL)? "unallocated" : primary_node->details->uname);
875  }
876 
877  } else if (constraint->score <= -CRM_SCORE_INFINITY) {
878  // Dependent resource must anti-colocate with primary resource
879 
880  if ((primary_node != NULL) &&
881  (dependent->allocated_to->details == primary_node->details)) {
882  crm_err("%s and %s must be anti-colocated but are allocated "
883  "to the same node (%s)",
884  dependent->id, primary->id, primary_node->details->uname);
885  }
886  }
888  }
889 
890  if ((constraint->score > 0)
891  && (constraint->dependent_role != RSC_ROLE_UNKNOWN)
892  && (constraint->dependent_role != dependent->next_role)) {
893 
894  crm_trace("Skipping colocation '%s': dependent limited to %s role "
895  "but %s next role is %s",
896  constraint->id, role2text(constraint->dependent_role),
897  dependent->id, role2text(dependent->next_role));
899  }
900 
901  if ((constraint->score > 0)
902  && (constraint->primary_role != RSC_ROLE_UNKNOWN)
903  && (constraint->primary_role != primary->next_role)) {
904 
905  crm_trace("Skipping colocation '%s': primary limited to %s role "
906  "but %s next role is %s",
907  constraint->id, role2text(constraint->primary_role),
908  primary->id, role2text(primary->next_role));
910  }
911 
912  if ((constraint->score < 0)
913  && (constraint->dependent_role != RSC_ROLE_UNKNOWN)
914  && (constraint->dependent_role == dependent->next_role)) {
915  crm_trace("Skipping anti-colocation '%s': dependent role %s matches",
916  constraint->id, role2text(constraint->dependent_role));
918  }
919 
920  if ((constraint->score < 0)
921  && (constraint->primary_role != RSC_ROLE_UNKNOWN)
922  && (constraint->primary_role == primary->next_role)) {
923  crm_trace("Skipping anti-colocation '%s': primary role %s matches",
924  constraint->id, role2text(constraint->primary_role));
926  }
927 
929 }
930 
942 void
944  pcmk__colocation_t *constraint)
945 {
946  const char *attribute = CRM_ATTR_ID;
947  const char *value = NULL;
948  GHashTable *work = NULL;
949  GHashTableIter iter;
950  pe_node_t *node = NULL;
951 
952  if (constraint->node_attribute != NULL) {
953  attribute = constraint->node_attribute;
954  }
955 
956  if (primary->allocated_to != NULL) {
957  value = pe_node_attribute_raw(primary->allocated_to, attribute);
958 
959  } else if (constraint->score < 0) {
960  // Nothing to do (anti-colocation with something that is not running)
961  return;
962  }
963 
964  work = pcmk__copy_node_table(dependent->allowed_nodes);
965 
966  g_hash_table_iter_init(&iter, work);
967  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
968  if (primary->allocated_to == NULL) {
969  pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s inactive)",
970  constraint->id, dependent->id, node->details->uname,
971  constraint->score, primary->id);
972  node->weight = pe__add_scores(-constraint->score, node->weight);
973 
974  } else if (pcmk__str_eq(pe_node_attribute_raw(node, attribute), value,
975  pcmk__str_casei)) {
976  if (constraint->score < CRM_SCORE_INFINITY) {
977  pe_rsc_trace(dependent, "%s: %s@%s += %d",
978  constraint->id, dependent->id,
979  node->details->uname, constraint->score);
980  node->weight = pe__add_scores(constraint->score, node->weight);
981  }
982 
983  } else if (constraint->score >= CRM_SCORE_INFINITY) {
984  pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s mismatch)",
985  constraint->id, dependent->id, node->details->uname,
986  constraint->score, attribute);
987  node->weight = pe__add_scores(-constraint->score, node->weight);
988  }
989  }
990 
991  if (can_run_any(work) || (constraint->score <= -INFINITY)
992  || (constraint->score >= INFINITY)) {
993 
994  g_hash_table_destroy(dependent->allowed_nodes);
995  dependent->allowed_nodes = work;
996  work = NULL;
997 
998  } else {
999  pe_rsc_info(dependent,
1000  "%s: Rolling back scores from %s (no available nodes)",
1001  dependent->id, primary->id);
1002  }
1003 
1004  if (work != NULL) {
1005  g_hash_table_destroy(work);
1006  }
1007 }
1008 
1020 void
1022  pcmk__colocation_t *constraint)
1023 {
1024  const char *dependent_value = NULL;
1025  const char *primary_value = NULL;
1026  const char *attribute = CRM_ATTR_ID;
1027  int score_multiplier = 1;
1028 
1029  if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1030  return;
1031  }
1032 
1033  if (constraint->node_attribute != NULL) {
1034  attribute = constraint->node_attribute;
1035  }
1036 
1037  dependent_value = pe_node_attribute_raw(dependent->allocated_to, attribute);
1038  primary_value = pe_node_attribute_raw(primary->allocated_to, attribute);
1039 
1040  if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1041  if ((constraint->score == INFINITY)
1042  && (constraint->dependent_role == RSC_ROLE_PROMOTED)) {
1043  dependent->priority = -INFINITY;
1044  }
1045  return;
1046  }
1047 
1048  if ((constraint->primary_role != RSC_ROLE_UNKNOWN)
1049  && (constraint->primary_role != primary->next_role)) {
1050  return;
1051  }
1052 
1053  if (constraint->dependent_role == RSC_ROLE_UNPROMOTED) {
1054  score_multiplier = -1;
1055  }
1056 
1057  dependent->priority = pe__add_scores(score_multiplier * constraint->score,
1058  dependent->priority);
1059 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:225
A dumping ground.
#define INFINITY
Definition: crm.h:99
GList * rsc_cons
Definition: pe_types.h:357
pcmk__coloc_affects
void pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
#define XML_COLOC_ATTR_TARGET_INSTANCE
Definition: msg_xml.h:360
#define pcmk__config_warn(fmt...)
#define RSC_ROLE_STARTED_S
Definition: common.h:112
GList * children
Definition: pe_types.h:377
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:333
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2789
enum rsc_role_e next_role
Definition: pe_types.h:371
int char2score(const char *score)
Definition: utils.c:61
#define pcmk__config_err(fmt...)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:323
#define CRMD_ACTION_PROMOTE
Definition: crm.h:182
pe_resource_t * dependent
#define pe_rsc_critical
Definition: pe_types.h:265
GList * rsc_cons_lhs
Definition: pe_types.h:356
#define RSC_START
Definition: crm.h:201
#define XML_COLOC_ATTR_TARGET_ROLE
Definition: msg_xml.h:357
pe_node_t * allocated_to
Definition: pe_types.h:363
#define CRM_SCORE_INFINITY
Definition: crm.h:85
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:349
const char * action
Definition: pcmk_fence.c:30
void pcmk__block_colocated_starts(pe_action_t *action, pe_working_set_t *data_set)
GList * resources
Definition: pe_types.h:158
#define CRMD_ACTION_START
Definition: crm.h:176
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
#define pe_rsc_provisional
Definition: pe_types.h:258
const char * role2text(enum rsc_role_e role)
Definition: common.c:459
#define CRMD_ACTION_STOP
Definition: crm.h:179
#define CRMD_ACTION_DEMOTE
Definition: crm.h:184
#define XML_COLOC_ATTR_TARGET
Definition: msg_xml.h:356
void pe_action_set_reason(pe_action_t *action, const char *reason, bool overwrite)
Definition: utils.c:2347
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:903
#define XML_ATTR_ID
Definition: msg_xml.h:129
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
pe_resource_t * primary
gboolean update_action(pe_action_t *action, pe_working_set_t *data_set)
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:68
#define CRM_ATTR_UNAME
Definition: crm.h:114
#define crm_trace(fmt, args...)
Definition: logging.h:363
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:114
struct pe_node_shared_s * details
Definition: pe_types.h:244
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2862
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:84
unsigned long long flags
Definition: pe_types.h:348
const char * uname
Definition: pe_types.h:209
#define pe_rsc_promotable
Definition: pe_types.h:256
#define pcmk__order_resource_actions(lh_rsc, lh_task, rh_rsc, rh_task, flags, data_set)
void pcmk__new_colocation(const char *id, const char *node_attr, int score, pe_resource_t *dependent, pe_resource_t *primary, const char *dependent_role, const char *primary_role, bool influence, pe_working_set_t *data_set)
const char * pe_node_attribute_raw(pe_node_t *node, const char *name)
Definition: common.c:635
GList * actions
Definition: pe_types.h:359
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:359
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:213
void free_xml(xmlNode *child)
Definition: xml.c:824
enum rsc_role_e text2role(const char *role)
Definition: common.c:488
enum pe_obj_types variant
Definition: pe_types.h:331
xmlNode * input
Definition: pe_types.h:137
G_GNUC_INTERNAL pe_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
uint32_t id
Definition: cpg.c:45
gboolean can_run_any(GHashTable *nodes)
enum pcmk__coloc_affects pcmk__colocation_affects(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint, bool preview)
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:355
GHashTable * pcmk__copy_node_table(GHashTable *nodes)
#define XML_COLOC_ATTR_NODE_ATTR
Definition: msg_xml.h:358
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:427
Cluster status and scheduling.
#define XML_COLOC_ATTR_INFLUENCE
Definition: msg_xml.h:361
void pcmk__apply_coloc_to_priority(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint)
int pe__add_scores(int score1, int score2)
Definition: common.c:516
#define crm_err(fmt, args...)
Definition: logging.h:357
#define CRM_ASSERT(expr)
Definition: results.h:42
#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name)
GList * colocation_constraints
Definition: pe_types.h:161
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2032
void pcmk__apply_coloc_to_weights(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint)
const char * node_attribute
#define RSC_ROLE_UNKNOWN_S
Definition: common.h:110
#define crm_log_xml_trace(xml, text)
Definition: logging.h:371
gboolean crm_is_true(const char *s)
Definition: strings.c:416
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:20
#define ID(x)
Definition: msg_xml.h:456
#define CRM_ATTR_ID
Definition: crm.h:115
#define XML_CONS_ATTR_SYMMETRICAL
Definition: msg_xml.h:350
pe_action_t * find_first_action(GList *input, const char *uuid, const char *task, pe_node_t *on_node)
Definition: utils.c:1540
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:354
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag)
pe_resource_t * parent
Definition: pe_types.h:329
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:18
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, pe_working_set_t *data_set)
char * id
Definition: pe_types.h:322
GHashTable * allowed_nodes
Definition: pe_types.h:368
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2815