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