pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
pcmk_sched_colocation.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 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>
17 #include <crm/pengine/status.h>
18 #include <pacemaker-internal.h>
19 
20 #include "crm/common/util.h"
22 #include "crm/msg_xml.h"
23 #include "libpacemaker_private.h"
24 
25 // Used to temporarily mark a node as unusable
26 #define INFINITY_HACK (INFINITY * -100)
27 
51 static gint
52 cmp_colocation_priority(const pcmk__colocation_t *colocation1,
53  const pcmk__colocation_t *colocation2, bool dependent)
54 {
55  const pcmk_resource_t *rsc1 = NULL;
56  const pcmk_resource_t *rsc2 = NULL;
57 
58  if (colocation1 == NULL) {
59  return 1;
60  }
61  if (colocation2 == NULL) {
62  return -1;
63  }
64 
65  if (dependent) {
66  rsc1 = colocation1->dependent;
67  rsc2 = colocation2->dependent;
68  CRM_ASSERT(colocation1->primary != NULL);
69  } else {
70  rsc1 = colocation1->primary;
71  rsc2 = colocation2->primary;
72  CRM_ASSERT(colocation1->dependent != NULL);
73  }
74  CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
75 
76  if (rsc1->priority > rsc2->priority) {
77  return -1;
78  }
79  if (rsc1->priority < rsc2->priority) {
80  return 1;
81  }
82 
83  // Process clones before primitives and groups
84  if (rsc1->variant > rsc2->variant) {
85  return -1;
86  }
87  if (rsc1->variant < rsc2->variant) {
88  return 1;
89  }
90 
91  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
92  * clones (probably unnecessary, but avoids having to update regression
93  * tests)
94  */
98  return -1;
99  }
102  return 1;
103  }
104  }
105 
106  return strcmp(rsc1->id, rsc2->id);
107 }
108 
129 static gint
130 cmp_dependent_priority(gconstpointer a, gconstpointer b)
131 {
132  return cmp_colocation_priority(a, b, true);
133 }
134 
155 static gint
156 cmp_primary_priority(gconstpointer a, gconstpointer b)
157 {
158  return cmp_colocation_priority(a, b, false);
159 }
160 
172 void
173 pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
174  const pcmk_resource_t *rsc)
175 {
176  CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
177 
178  pe_rsc_trace(rsc,
179  "Adding colocation %s (%s with %s using %s @%s) to "
180  "'this with' list for %s",
181  colocation->id, colocation->dependent->id,
182  colocation->primary->id, colocation->node_attribute,
183  pcmk_readable_score(colocation->score), rsc->id);
184  *list = g_list_insert_sorted(*list, (gpointer) colocation,
185  cmp_primary_priority);
186 }
187 
199 void
200 pcmk__add_this_with_list(GList **list, GList *addition,
201  const pcmk_resource_t *rsc)
202 {
203  CRM_ASSERT((list != NULL) && (rsc != NULL));
204 
206  {}, // Always add each colocation individually if tracing
207  {
208  if (*list == NULL) {
209  // Trivial case for efficiency if not tracing
210  *list = g_list_copy(addition);
211  return;
212  }
213  }
214  );
215 
216  for (const GList *iter = addition; iter != NULL; iter = iter->next) {
217  pcmk__add_this_with(list, addition->data, rsc);
218  }
219 }
220 
232 void
233 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
234  const pcmk_resource_t *rsc)
235 {
236  CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
237 
238  pe_rsc_trace(rsc,
239  "Adding colocation %s (%s with %s using %s @%s) to "
240  "'with this' list for %s",
241  colocation->id, colocation->dependent->id,
242  colocation->primary->id, colocation->node_attribute,
243  pcmk_readable_score(colocation->score), rsc->id);
244  *list = g_list_insert_sorted(*list, (gpointer) colocation,
245  cmp_dependent_priority);
246 }
247 
259 void
260 pcmk__add_with_this_list(GList **list, GList *addition,
261  const pcmk_resource_t *rsc)
262 {
263  CRM_ASSERT((list != NULL) && (rsc != NULL));
264 
266  {}, // Always add each colocation individually if tracing
267  {
268  if (*list == NULL) {
269  // Trivial case for efficiency if not tracing
270  *list = g_list_copy(addition);
271  return;
272  }
273  }
274  );
275 
276  for (const GList *iter = addition; iter != NULL; iter = iter->next) {
277  pcmk__add_with_this(list, addition->data, rsc);
278  }
279 }
280 
290 static void
291 anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
292  pcmk_resource_t *then_rsc, int then_role)
293 {
294  const char *first_tasks[] = { NULL, NULL };
295  const char *then_tasks[] = { NULL, NULL };
296 
297  /* Actions to make first_rsc lose first_role */
298  if (first_role == pcmk_role_promoted) {
299  first_tasks[0] = PCMK_ACTION_DEMOTE;
300 
301  } else {
302  first_tasks[0] = PCMK_ACTION_STOP;
303 
304  if (first_role == pcmk_role_unpromoted) {
305  first_tasks[1] = PCMK_ACTION_PROMOTE;
306  }
307  }
308 
309  /* Actions to make then_rsc gain then_role */
310  if (then_role == pcmk_role_promoted) {
311  then_tasks[0] = PCMK_ACTION_PROMOTE;
312 
313  } else {
314  then_tasks[0] = PCMK_ACTION_START;
315 
316  if (then_role == pcmk_role_unpromoted) {
317  then_tasks[1] = PCMK_ACTION_DEMOTE;
318  }
319  }
320 
321  for (int first_lpc = 0;
322  (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
323 
324  for (int then_lpc = 0;
325  (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
326 
327  pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
328  then_rsc, then_tasks[then_lpc],
330  }
331  }
332 }
333 
347 void
348 pcmk__new_colocation(const char *id, const char *node_attr, int score,
349  pcmk_resource_t *dependent, pcmk_resource_t *primary,
350  const char *dependent_role, const char *primary_role,
351  uint32_t flags)
352 {
353  pcmk__colocation_t *new_con = NULL;
354 
355  CRM_CHECK(id != NULL, return);
356 
357  if ((dependent == NULL) || (primary == NULL)) {
358  pcmk__config_err("Ignoring colocation '%s' because resource "
359  "does not exist", id);
360  return;
361  }
362 
363  if (score == 0) {
364  pe_rsc_trace(dependent,
365  "Ignoring colocation '%s' (%s with %s) because score is 0",
366  id, dependent->id, primary->id);
367  return;
368  }
369 
370  new_con = calloc(1, sizeof(pcmk__colocation_t));
371  CRM_ASSERT(new_con != NULL);
372 
373  if (pcmk__str_eq(dependent_role, PCMK__ROLE_STARTED,
375  dependent_role = PCMK__ROLE_UNKNOWN;
376  }
377 
378  if (pcmk__str_eq(primary_role, PCMK__ROLE_STARTED,
380  primary_role = PCMK__ROLE_UNKNOWN;
381  }
382 
383  new_con->id = id;
384  new_con->dependent = dependent;
385  new_con->primary = primary;
386  new_con->score = score;
387  new_con->dependent_role = text2role(dependent_role);
388  new_con->primary_role = text2role(primary_role);
389  new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
390  new_con->flags = flags;
391 
392  pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent);
393  pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary);
394 
395  dependent->cluster->colocation_constraints = g_list_prepend(
396  dependent->cluster->colocation_constraints, new_con);
397 
398  if (score <= -INFINITY) {
399  anti_colocation_order(dependent, new_con->dependent_role, primary,
400  new_con->primary_role);
401  anti_colocation_order(primary, new_con->primary_role, dependent,
402  new_con->dependent_role);
403  }
404 }
405 
418 static uint32_t
419 unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
420  const char *influence_s)
421 {
422  if (influence_s != NULL) {
423  int influence_i = 0;
424 
425  if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
426  pcmk__config_err("Constraint '%s' has invalid value for "
427  XML_COLOC_ATTR_INFLUENCE " (using default)",
428  coloc_id);
429  } else {
430  return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
431  }
432  }
433  if (pcmk_is_set(rsc->flags, pcmk_rsc_critical)) {
434  return pcmk__coloc_influence;
435  }
436  return pcmk__coloc_none;
437 }
438 
439 static void
440 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
441  const char *influence_s, pcmk_scheduler_t *scheduler)
442 {
443  xmlNode *xml_rsc = NULL;
444  pcmk_resource_t *other = NULL;
445  pcmk_resource_t *resource = NULL;
446  const char *set_id = ID(set);
447  const char *role = crm_element_value(set, "role");
448  bool with_previous = false;
449  int local_score = score;
450  bool sequential = false;
451  uint32_t flags = pcmk__coloc_none;
452  const char *xml_rsc_id = NULL;
453  const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
454 
455  if (score_s) {
456  local_score = char2score(score_s);
457  }
458  if (local_score == 0) {
459  crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
460  coloc_id, set_id);
461  return;
462  }
463 
464  /* @COMPAT The deprecated "ordering" attribute specifies whether resources
465  * in a positive-score set are colocated with the previous or next resource.
466  */
467  if (pcmk__str_eq(crm_element_value(set, "ordering"), "group",
469  with_previous = true;
470  } else {
472  "Support for 'ordering' other than 'group' in "
473  XML_CONS_TAG_RSC_SET " (such as %s) is deprecated and "
474  "will be removed in a future release", set_id);
475  }
476 
477  if ((pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok)
478  && !sequential) {
479  return;
480  }
481 
482  if (local_score > 0) {
483  for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
484  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
485 
486  xml_rsc_id = ID(xml_rsc);
488  xml_rsc_id);
489  if (resource == NULL) {
490  // Should be possible only with validation disabled
491  pcmk__config_err("Ignoring %s and later resources in set %s: "
492  "No such resource", xml_rsc_id, set_id);
493  return;
494  }
495  if (other != NULL) {
497  | unpack_influence(coloc_id, resource, influence_s);
498  if (with_previous) {
499  pe_rsc_trace(resource, "Colocating %s with %s in set %s",
500  resource->id, other->id, set_id);
501  pcmk__new_colocation(set_id, NULL, local_score, resource,
502  other, role, role, flags);
503  } else {
504  pe_rsc_trace(resource, "Colocating %s with %s in set %s",
505  other->id, resource->id, set_id);
506  pcmk__new_colocation(set_id, NULL, local_score, other,
507  resource, role, role, flags);
508  }
509  }
510  other = resource;
511  }
512 
513  } else {
514  /* Anti-colocating with every prior resource is
515  * the only way to ensure the intuitive result
516  * (i.e. that no one in the set can run with anyone else in the set)
517  */
518 
519  for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
520  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
521 
522  xmlNode *xml_rsc_with = NULL;
523 
524  xml_rsc_id = ID(xml_rsc);
526  xml_rsc_id);
527  if (resource == NULL) {
528  // Should be possible only with validation disabled
529  pcmk__config_err("Ignoring %s and later resources in set %s: "
530  "No such resource", xml_rsc_id, set_id);
531  return;
532  }
534  | unpack_influence(coloc_id, resource, influence_s);
535  for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
536  xml_rsc_with != NULL;
537  xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
538 
539  xml_rsc_id = ID(xml_rsc_with);
540  if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
541  break;
542  }
544  xml_rsc_id);
545  CRM_ASSERT(other != NULL); // We already processed it
546  pcmk__new_colocation(set_id, NULL, local_score,
547  resource, other, role, role, flags);
548  }
549  }
550  }
551 }
552 
564 static void
565 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
566  int score, const char *influence_s,
568 {
569  xmlNode *xml_rsc = NULL;
570  pcmk_resource_t *rsc_1 = NULL;
571  pcmk_resource_t *rsc_2 = NULL;
572 
573  const char *xml_rsc_id = NULL;
574  const char *role_1 = crm_element_value(set1, "role");
575  const char *role_2 = crm_element_value(set2, "role");
576 
577  int rc = pcmk_rc_ok;
578  bool sequential = false;
579  uint32_t flags = pcmk__coloc_none;
580 
581  if (score == 0) {
582  crm_trace("Ignoring colocation '%s' between sets %s and %s "
583  "because score is 0", id, ID(set1), ID(set2));
584  return;
585  }
586 
587  rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential);
588  if ((rc != pcmk_rc_ok) || sequential) {
589  // Get the first one
590  xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
591  if (xml_rsc != NULL) {
592  xml_rsc_id = ID(xml_rsc);
594  xml_rsc_id);
595  if (rsc_1 == NULL) {
596  // Should be possible only with validation disabled
597  pcmk__config_err("Ignoring colocation of set %s with set %s "
598  "because first resource %s not found",
599  ID(set1), ID(set2), xml_rsc_id);
600  return;
601  }
602  }
603  }
604 
605  rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential);
606  if ((rc != pcmk_rc_ok) || sequential) {
607  // Get the last one
608  for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
609  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
610 
611  xml_rsc_id = ID(xml_rsc);
612  }
614  xml_rsc_id);
615  if (rsc_2 == NULL) {
616  // Should be possible only with validation disabled
617  pcmk__config_err("Ignoring colocation of set %s with set %s "
618  "because last resource %s not found",
619  ID(set1), ID(set2), xml_rsc_id);
620  return;
621  }
622  }
623 
624  if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
625  flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
626  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
627  flags);
628 
629  } else if (rsc_1 != NULL) { // Only set1 is sequential
630  flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
631  for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
632  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
633 
634  xml_rsc_id = ID(xml_rsc);
636  xml_rsc_id);
637  if (rsc_2 == NULL) {
638  // Should be possible only with validation disabled
639  pcmk__config_err("Ignoring set %s colocation with resource %s "
640  "in set %s: No such resource",
641  ID(set1), xml_rsc_id, ID(set2));
642  continue;
643  }
644  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
645  role_2, flags);
646  }
647 
648  } else if (rsc_2 != NULL) { // Only set2 is sequential
649  for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
650  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
651 
652  xml_rsc_id = ID(xml_rsc);
654  xml_rsc_id);
655  if (rsc_1 == NULL) {
656  // Should be possible only with validation disabled
657  pcmk__config_err("Ignoring colocation of set %s resource %s "
658  "with set %s: No such resource",
659  ID(set1), xml_rsc_id, ID(set2));
660  continue;
661  }
663  | unpack_influence(id, rsc_1, influence_s);
664  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
665  role_2, flags);
666  }
667 
668  } else { // Neither set is sequential
669  for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
670  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
671 
672  xmlNode *xml_rsc_2 = NULL;
673 
674  xml_rsc_id = ID(xml_rsc);
676  xml_rsc_id);
677  if (rsc_1 == NULL) {
678  // Should be possible only with validation disabled
679  pcmk__config_err("Ignoring colocation of set %s resource %s "
680  "with set %s: No such resource",
681  ID(set1), xml_rsc_id, ID(set2));
682  continue;
683  }
684 
686  | unpack_influence(id, rsc_1, influence_s);
687  for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
688  xml_rsc_2 != NULL;
689  xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
690 
691  xml_rsc_id = ID(xml_rsc_2);
693  xml_rsc_id);
694  if (rsc_2 == NULL) {
695  // Should be possible only with validation disabled
696  pcmk__config_err("Ignoring colocation of set %s resource "
697  "%s with set %s resource %s: No such "
698  "resource", ID(set1), ID(xml_rsc),
699  ID(set2), xml_rsc_id);
700  continue;
701  }
702  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
703  role_1, role_2, flags);
704  }
705  }
706  }
707 }
708 
709 static void
710 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
711  const char *influence_s, pcmk_scheduler_t *scheduler)
712 {
713  int score_i = 0;
714  uint32_t flags = pcmk__coloc_none;
715 
716  const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
717  const char *dependent_id = crm_element_value(xml_obj,
719  const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
720  const char *dependent_role = crm_element_value(xml_obj,
722  const char *primary_role = crm_element_value(xml_obj,
724  const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
725 
726  const char *primary_instance = NULL;
727  const char *dependent_instance = NULL;
728  pcmk_resource_t *primary = NULL;
729  pcmk_resource_t *dependent = NULL;
730 
731  primary = pcmk__find_constraint_resource(scheduler->resources, primary_id);
733  dependent_id);
734 
735  // @COMPAT: Deprecated since 2.1.5
736  primary_instance = crm_element_value(xml_obj,
738  dependent_instance = crm_element_value(xml_obj,
740  if (dependent_instance != NULL) {
742  "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
743  "deprecated and will be removed in a future release.");
744  }
745  if (primary_instance != NULL) {
747  "Support for " XML_COLOC_ATTR_TARGET_INSTANCE " is "
748  "deprecated and will be removed in a future release.");
749  }
750 
751  if (dependent == NULL) {
752  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
753  "does not exist", id, dependent_id);
754  return;
755 
756  } else if (primary == NULL) {
757  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
758  "does not exist", id, primary_id);
759  return;
760 
761  } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
762  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
763  "is not a clone but instance '%s' was requested",
764  id, dependent_id, dependent_instance);
765  return;
766 
767  } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
768  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
769  "is not a clone but instance '%s' was requested",
770  id, primary_id, primary_instance);
771  return;
772  }
773 
774  if (dependent_instance != NULL) {
775  dependent = find_clone_instance(dependent, dependent_instance);
776  if (dependent == NULL) {
777  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
778  "does not have an instance '%s'",
779  id, dependent_id, dependent_instance);
780  return;
781  }
782  }
783 
784  if (primary_instance != NULL) {
785  primary = find_clone_instance(primary, primary_instance);
786  if (primary == NULL) {
787  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
788  "does not have an instance '%s'",
789  "'%s'", id, primary_id, primary_instance);
790  return;
791  }
792  }
793 
795  pcmk__config_warn("The colocation constraint '"
797  "' attribute has been removed");
798  }
799 
800  if (score) {
801  score_i = char2score(score);
802  }
803 
804  flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
805  pcmk__new_colocation(id, attr, score_i, dependent, primary,
806  dependent_role, primary_role, flags);
807 }
808 
809 // \return Standard Pacemaker return code
810 static int
811 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
813 {
814  const char *id = NULL;
815  const char *dependent_id = NULL;
816  const char *primary_id = NULL;
817  const char *dependent_role = NULL;
818  const char *primary_role = NULL;
819 
820  pcmk_resource_t *dependent = NULL;
821  pcmk_resource_t *primary = NULL;
822 
823  pcmk_tag_t *dependent_tag = NULL;
824  pcmk_tag_t *primary_tag = NULL;
825 
826  xmlNode *dependent_set = NULL;
827  xmlNode *primary_set = NULL;
828  bool any_sets = false;
829 
830  *expanded_xml = NULL;
831 
832  CRM_CHECK(xml_obj != NULL, return EINVAL);
833 
834  id = ID(xml_obj);
835  if (id == NULL) {
836  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
837  xml_obj->name);
838  return pcmk_rc_unpack_error;
839  }
840 
841  // Check whether there are any resource sets with template or tag references
842  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
843  if (*expanded_xml != NULL) {
844  crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
845  return pcmk_rc_ok;
846  }
847 
848  dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
849  primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
850  if ((dependent_id == NULL) || (primary_id == NULL)) {
851  return pcmk_rc_ok;
852  }
853 
854  if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
855  &dependent_tag)) {
856  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
857  "valid resource or tag", id, dependent_id);
858  return pcmk_rc_unpack_error;
859  }
860 
861  if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
862  &primary_tag)) {
863  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
864  "valid resource or tag", id, primary_id);
865  return pcmk_rc_unpack_error;
866  }
867 
868  if ((dependent != NULL) && (primary != NULL)) {
869  /* Neither side references any template/tag. */
870  return pcmk_rc_ok;
871  }
872 
873  if ((dependent_tag != NULL) && (primary_tag != NULL)) {
874  // A colocation constraint between two templates/tags makes no sense
875  pcmk__config_err("Ignoring constraint '%s' because two templates or "
876  "tags cannot be colocated", id);
877  return pcmk_rc_unpack_error;
878  }
879 
880  dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
881  primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
882 
883  *expanded_xml = copy_xml(xml_obj);
884 
885  // Convert dependent's template/tag reference into constraint resource_set
886  if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
887  true, scheduler)) {
888  free_xml(*expanded_xml);
889  *expanded_xml = NULL;
890  return pcmk_rc_unpack_error;
891  }
892 
893  if (dependent_set != NULL) {
894  if (dependent_role != NULL) {
895  // Move "rsc-role" into converted resource_set as "role"
896  crm_xml_add(dependent_set, "role", dependent_role);
898  }
899  any_sets = true;
900  }
901 
902  // Convert primary's template/tag reference into constraint resource_set
903  if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
904  true, scheduler)) {
905  free_xml(*expanded_xml);
906  *expanded_xml = NULL;
907  return pcmk_rc_unpack_error;
908  }
909 
910  if (primary_set != NULL) {
911  if (primary_role != NULL) {
912  // Move "with-rsc-role" into converted resource_set as "role"
913  crm_xml_add(primary_set, "role", primary_role);
915  }
916  any_sets = true;
917  }
918 
919  if (any_sets) {
920  crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
921  } else {
922  free_xml(*expanded_xml);
923  *expanded_xml = NULL;
924  }
925 
926  return pcmk_rc_ok;
927 }
928 
936 void
938 {
939  int score_i = 0;
940  xmlNode *set = NULL;
941  xmlNode *last = NULL;
942 
943  xmlNode *orig_xml = NULL;
944  xmlNode *expanded_xml = NULL;
945 
946  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
947  const char *score = NULL;
948  const char *influence_s = NULL;
949 
950  if (pcmk__str_empty(id)) {
952  " without " CRM_ATTR_ID);
953  return;
954  }
955 
956  if (unpack_colocation_tags(xml_obj, &expanded_xml,
957  scheduler) != pcmk_rc_ok) {
958  return;
959  }
960  if (expanded_xml != NULL) {
961  orig_xml = xml_obj;
962  xml_obj = expanded_xml;
963  }
964 
965  score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
966  if (score != NULL) {
967  score_i = char2score(score);
968  }
969  influence_s = crm_element_value(xml_obj, XML_COLOC_ATTR_INFLUENCE);
970 
971  for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
972  set = crm_next_same_xml(set)) {
973 
974  set = expand_idref(set, scheduler->input);
975  if (set == NULL) { // Configuration error, message already logged
976  if (expanded_xml != NULL) {
977  free_xml(expanded_xml);
978  }
979  return;
980  }
981 
982  if (pcmk__str_empty(ID(set))) {
984  " without " CRM_ATTR_ID);
985  continue;
986  }
987  unpack_colocation_set(set, score_i, id, influence_s, scheduler);
988 
989  if (last != NULL) {
990  colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
991  }
992  last = set;
993  }
994 
995  if (expanded_xml) {
996  free_xml(expanded_xml);
997  xml_obj = orig_xml;
998  }
999 
1000  if (last == NULL) {
1001  unpack_simple_colocation(xml_obj, id, influence_s, scheduler);
1002  }
1003 }
1004 
1013 static void
1014 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1015  const pcmk_resource_t *reason)
1016 {
1017  GList *iter = NULL;
1018  char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1019 
1020  for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1021  pcmk_action_t *action = iter->data;
1022 
1024  && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1025 
1027  pe_action_set_reason(action, reason_text, false);
1030  }
1031  }
1032 
1033  // If parent resource can't perform an action, neither can any children
1034  for (iter = rsc->children; iter != NULL; iter = iter->next) {
1035  mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1036  }
1037  free(reason_text);
1038 }
1039 
1050 void
1052 {
1053  GList *iter = NULL;
1054  GList *colocations = NULL;
1055  pcmk_resource_t *rsc = NULL;
1056  bool is_start = false;
1057 
1058  if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
1059  return; // Only unrunnable actions block dependents
1060  }
1061 
1062  is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1063  if (!is_start
1064  && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1065  return; // Only unrunnable starts and promotes block dependents
1066  }
1067 
1068  CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions
1069 
1070  /* If this resource is part of a collective resource, dependents are blocked
1071  * only if all instances of the collective are unrunnable, so check the
1072  * collective resource.
1073  */
1074  rsc = uber_parent(action->rsc);
1075  if (rsc->parent != NULL) {
1076  rsc = rsc->parent; // Bundle
1077  }
1078 
1079  // Colocation fails only if entire primary can't reach desired role
1080  for (iter = rsc->children; iter != NULL; iter = iter->next) {
1081  pcmk_resource_t *child = iter->data;
1082  pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1083  action->task, NULL);
1084 
1085  if ((child_action == NULL)
1086  || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1087  crm_trace("Not blocking %s colocation dependents because "
1088  "at least %s has runnable %s",
1089  rsc->id, child->id, action->task);
1090  return; // At least one child can reach desired role
1091  }
1092  }
1093 
1094  crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1095  rsc->id, action->rsc->id, action->task);
1096 
1097  // Check each colocation where this resource is primary
1098  colocations = pcmk__with_this_colocations(rsc);
1099  for (iter = colocations; iter != NULL; iter = iter->next) {
1100  pcmk__colocation_t *colocation = iter->data;
1101 
1102  if (colocation->score < INFINITY) {
1103  continue; // Only mandatory colocations block dependent
1104  }
1105 
1106  /* If the primary can't start, the dependent can't reach its colocated
1107  * role, regardless of what the primary or dependent colocation role is.
1108  *
1109  * If the primary can't be promoted, the dependent can't reach its
1110  * colocated role if the primary's colocation role is promoted.
1111  */
1112  if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1113  continue;
1114  }
1115 
1116  // Block the dependent from reaching its colocated role
1117  if (colocation->dependent_role == pcmk_role_promoted) {
1118  mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1119  action->rsc);
1120  } else {
1121  mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1122  action->rsc);
1123  }
1124  }
1125  g_list_free(colocations);
1126 }
1127 
1146 static const pcmk_resource_t *
1147 get_resource_for_role(const pcmk_resource_t *rsc)
1148 {
1150  const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1151 
1152  if (child != NULL) {
1153  return child;
1154  }
1155  }
1156  return rsc;
1157 }
1158 
1179  const pcmk_resource_t *primary,
1180  const pcmk__colocation_t *colocation, bool preview)
1181 {
1182  const pcmk_resource_t *dependent_role_rsc = NULL;
1183  const pcmk_resource_t *primary_role_rsc = NULL;
1184 
1185  CRM_ASSERT((dependent != NULL) && (primary != NULL)
1186  && (colocation != NULL));
1187 
1188  if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1189  // Primary resource has not been assigned yet, so we can't do anything
1191  }
1192 
1193  dependent_role_rsc = get_resource_for_role(dependent);
1194  primary_role_rsc = get_resource_for_role(primary);
1195 
1196  if ((colocation->dependent_role >= pcmk_role_unpromoted)
1197  && (dependent_role_rsc->parent != NULL)
1198  && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1199  && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1200 
1201  /* This is a colocation by role, and the dependent is a promotable clone
1202  * that has already been assigned, so the colocation should now affect
1203  * the role.
1204  */
1205  return pcmk__coloc_affects_role;
1206  }
1207 
1208  if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1209  /* The dependent resource has already been through assignment, so the
1210  * constraint no longer has any effect. Log an error if a mandatory
1211  * colocation constraint has been violated.
1212  */
1213 
1214  const pcmk_node_t *primary_node = primary->allocated_to;
1215 
1216  if (dependent->allocated_to == NULL) {
1217  crm_trace("Skipping colocation '%s': %s will not run anywhere",
1218  colocation->id, dependent->id);
1219 
1220  } else if (colocation->score >= INFINITY) {
1221  // Dependent resource must colocate with primary resource
1222 
1223  if (!pe__same_node(primary_node, dependent->allocated_to)) {
1224  crm_err("%s must be colocated with %s but is not (%s vs. %s)",
1225  dependent->id, primary->id,
1226  pe__node_name(dependent->allocated_to),
1227  pe__node_name(primary_node));
1228  }
1229 
1230  } else if (colocation->score <= -CRM_SCORE_INFINITY) {
1231  // Dependent resource must anti-colocate with primary resource
1232 
1233  if (pe__same_node(dependent->allocated_to, primary_node)) {
1234  crm_err("%s and %s must be anti-colocated but are assigned "
1235  "to the same node (%s)",
1236  dependent->id, primary->id,
1237  pe__node_name(primary_node));
1238  }
1239  }
1241  }
1242 
1243  if ((colocation->dependent_role != pcmk_role_unknown)
1244  && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1245  crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1246 
1247  "but %s next role is %s",
1248  ((colocation->score < 0)? "anti-" : ""),
1249  colocation->id, role2text(colocation->dependent_role),
1250  dependent_role_rsc->id,
1251  role2text(dependent_role_rsc->next_role));
1253  }
1254 
1255  if ((colocation->primary_role != pcmk_role_unknown)
1256  && (colocation->primary_role != primary_role_rsc->next_role)) {
1257  crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1258  "but %s next role is %s",
1259  ((colocation->score < 0)? "anti-" : ""),
1260  colocation->id, role2text(colocation->primary_role),
1261  primary_role_rsc->id, role2text(primary_role_rsc->next_role));
1263  }
1264 
1266 }
1267 
1279 void
1281  const pcmk_resource_t *primary,
1282  const pcmk__colocation_t *colocation)
1283 {
1284  const char *attr = colocation->node_attribute;
1285  const char *value = NULL;
1286  GHashTable *work = NULL;
1287  GHashTableIter iter;
1288  pcmk_node_t *node = NULL;
1289 
1290  if (primary->allocated_to != NULL) {
1291  value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1292  primary);
1293 
1294  } else if (colocation->score < 0) {
1295  // Nothing to do (anti-colocation with something that is not running)
1296  return;
1297  }
1298 
1299  work = pcmk__copy_node_table(dependent->allowed_nodes);
1300 
1301  g_hash_table_iter_init(&iter, work);
1302  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1303  if (primary->allocated_to == NULL) {
1304  node->weight = pcmk__add_scores(-colocation->score, node->weight);
1305  pe_rsc_trace(dependent,
1306  "Applied %s to %s score on %s (now %s after "
1307  "subtracting %s because primary %s inactive)",
1308  colocation->id, dependent->id, pe__node_name(node),
1309  pcmk_readable_score(node->weight),
1310  pcmk_readable_score(colocation->score), primary->id);
1311  continue;
1312  }
1313 
1314  if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1315  value, pcmk__str_casei)) {
1316 
1317  /* Add colocation score only if optional (or minus infinity). A
1318  * mandatory colocation is a requirement rather than a preference,
1319  * so we don't need to consider it for relative assignment purposes.
1320  * The resource will simply be forbidden from running on the node if
1321  * the primary isn't active there (via the condition above).
1322  */
1323  if (colocation->score < CRM_SCORE_INFINITY) {
1324  node->weight = pcmk__add_scores(colocation->score,
1325  node->weight);
1326  pe_rsc_trace(dependent,
1327  "Applied %s to %s score on %s (now %s after "
1328  "adding %s)",
1329  colocation->id, dependent->id, pe__node_name(node),
1330  pcmk_readable_score(node->weight),
1331  pcmk_readable_score(colocation->score));
1332  }
1333  continue;
1334  }
1335 
1336  if (colocation->score >= CRM_SCORE_INFINITY) {
1337  /* Only mandatory colocations are relevant when the colocation
1338  * attribute doesn't match, because an attribute not matching is not
1339  * a negative preference -- the colocation is simply relevant only
1340  * where it matches.
1341  */
1342  node->weight = -CRM_SCORE_INFINITY;
1343  pe_rsc_trace(dependent,
1344  "Banned %s from %s because colocation %s attribute %s "
1345  "does not match",
1346  dependent->id, pe__node_name(node), colocation->id,
1347  attr);
1348  }
1349  }
1350 
1351  if ((colocation->score <= -INFINITY) || (colocation->score >= INFINITY)
1352  || pcmk__any_node_available(work)) {
1353 
1354  g_hash_table_destroy(dependent->allowed_nodes);
1355  dependent->allowed_nodes = work;
1356  work = NULL;
1357 
1358  } else {
1359  pe_rsc_info(dependent,
1360  "%s: Rolling back scores from %s (no available nodes)",
1361  dependent->id, primary->id);
1362  }
1363 
1364  if (work != NULL) {
1365  g_hash_table_destroy(work);
1366  }
1367 }
1368 
1380 void
1382  const pcmk_resource_t *primary,
1383  const pcmk__colocation_t *colocation)
1384 {
1385  const char *dependent_value = NULL;
1386  const char *primary_value = NULL;
1387  const char *attr = colocation->node_attribute;
1388  int score_multiplier = 1;
1389 
1390  const pcmk_resource_t *primary_role_rsc = NULL;
1391 
1392  CRM_ASSERT((dependent != NULL) && (primary != NULL) &&
1393  (colocation != NULL));
1394 
1395  if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1396  return;
1397  }
1398 
1399  dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1400  dependent);
1401  primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1402  primary);
1403 
1404  primary_role_rsc = get_resource_for_role(primary);
1405 
1406  if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1407  if ((colocation->score == INFINITY)
1408  && (colocation->dependent_role == pcmk_role_promoted)) {
1409  dependent->priority = -INFINITY;
1410  }
1411  return;
1412  }
1413 
1414  if ((colocation->primary_role != pcmk_role_unknown)
1415  && (colocation->primary_role != primary_role_rsc->next_role)) {
1416  return;
1417  }
1418 
1419  if (colocation->dependent_role == pcmk_role_unpromoted) {
1420  score_multiplier = -1;
1421  }
1422 
1423  dependent->priority = pcmk__add_scores(score_multiplier * colocation->score,
1424  dependent->priority);
1425  pe_rsc_trace(dependent,
1426  "Applied %s to %s promotion priority (now %s after %s %s)",
1427  colocation->id, dependent->id,
1428  pcmk_readable_score(dependent->priority),
1429  ((score_multiplier == 1)? "adding" : "subtracting"),
1430  pcmk_readable_score(colocation->score));
1431 }
1432 
1441 static int
1442 best_node_score_matching_attr(const pcmk_resource_t *rsc, const char *attr,
1443  const char *value)
1444 {
1445  GHashTableIter iter;
1446  pcmk_node_t *node = NULL;
1447  int best_score = -INFINITY;
1448  const char *best_node = NULL;
1449 
1450  // Find best allowed node with matching attribute
1451  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1452  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1453 
1454  if ((node->weight > best_score)
1455  && pcmk__node_available(node, false, false)
1456  && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1457  pcmk__str_casei)) {
1458 
1459  best_score = node->weight;
1460  best_node = node->details->uname;
1461  }
1462  }
1463 
1464  if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1465  if (best_node == NULL) {
1466  crm_info("No allowed node for %s matches node attribute %s=%s",
1467  rsc->id, attr, value);
1468  } else {
1469  crm_info("Allowed node %s for %s had best score (%d) "
1470  "of those matching node attribute %s=%s",
1471  best_node, rsc->id, best_score, attr, value);
1472  }
1473  }
1474  return best_score;
1475 }
1476 
1485 static bool
1486 allowed_on_one(const pcmk_resource_t *rsc)
1487 {
1488  GHashTableIter iter;
1489  pcmk_node_t *allowed_node = NULL;
1490  int allowed_nodes = 0;
1491 
1492  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1493  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1494  if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1495  pe_rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1496  return false;
1497  }
1498  }
1499  pe_rsc_trace(rsc, "%s is allowed %s", rsc->id,
1500  ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1501  return (allowed_nodes == 1);
1502 }
1503 
1522 static void
1523 add_node_scores_matching_attr(GHashTable *nodes,
1524  const pcmk_resource_t *source_rsc,
1525  const pcmk_resource_t *target_rsc,
1526  const pcmk__colocation_t *colocation,
1527  float factor, bool only_positive)
1528 {
1529  GHashTableIter iter;
1530  pcmk_node_t *node = NULL;
1531  const char *attr = colocation->node_attribute;
1532 
1533  // Iterate through each node
1534  g_hash_table_iter_init(&iter, nodes);
1535  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1536  float delta_f = 0;
1537  int delta = 0;
1538  int score = 0;
1539  int new_score = 0;
1540  const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1541 
1542  score = best_node_score_matching_attr(source_rsc, attr, value);
1543 
1544  if ((factor < 0) && (score < 0)) {
1545  /* If the dependent is anti-colocated, we generally don't want the
1546  * primary to prefer nodes that the dependent avoids. That could
1547  * lead to unnecessary shuffling of the primary when the dependent
1548  * hits its migration threshold somewhere, for example.
1549  *
1550  * However, there are cases when it is desirable. If the dependent
1551  * can't run anywhere but where the primary is, it would be
1552  * worthwhile to move the primary for the sake of keeping the
1553  * dependent active.
1554  *
1555  * We can't know that exactly at this point since we don't know
1556  * where the primary will be assigned, but we can limit considering
1557  * the preference to when the dependent is allowed only on one node.
1558  * This is less than ideal for multiple reasons:
1559  *
1560  * - the dependent could be allowed on more than one node but have
1561  * anti-colocation primaries on each;
1562  * - the dependent could be a clone or bundle with multiple
1563  * instances, and the dependent as a whole is allowed on multiple
1564  * nodes but some instance still can't run
1565  * - the dependent has considered node-specific criteria such as
1566  * location constraints and stickiness by this point, but might
1567  * have other factors that end up disallowing a node
1568  *
1569  * but the alternative is making the primary move when it doesn't
1570  * need to.
1571  *
1572  * We also consider the primary's stickiness and influence, so the
1573  * user has some say in the matter. (This is the configured primary,
1574  * not a particular instance of the primary, but that doesn't matter
1575  * unless stickiness uses a rule to vary by node, and that seems
1576  * acceptable to ignore.)
1577  */
1578  if ((colocation->primary->stickiness >= -score)
1579  || !pcmk__colocation_has_influence(colocation, NULL)
1580  || !allowed_on_one(colocation->dependent)) {
1581  crm_trace("%s: Filtering %d + %f * %d "
1582  "(double negative disallowed)",
1583  pe__node_name(node), node->weight, factor, score);
1584  continue;
1585  }
1586  }
1587 
1588  if (node->weight == INFINITY_HACK) {
1589  crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1590  pe__node_name(node), node->weight, factor, score);
1591  continue;
1592  }
1593 
1594  delta_f = factor * score;
1595 
1596  // Round the number; see http://c-faq.com/fp/round.html
1597  delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1598 
1599  /* Small factors can obliterate the small scores that are often actually
1600  * used in configurations. If the score and factor are nonzero, ensure
1601  * that the result is nonzero as well.
1602  */
1603  if ((delta == 0) && (score != 0)) {
1604  if (factor > 0.0) {
1605  delta = 1;
1606  } else if (factor < 0.0) {
1607  delta = -1;
1608  }
1609  }
1610 
1611  new_score = pcmk__add_scores(delta, node->weight);
1612 
1613  if (only_positive && (new_score < 0) && (node->weight > 0)) {
1614  crm_trace("%s: Filtering %d + %f * %d = %d "
1615  "(negative disallowed, marking node unusable)",
1616  pe__node_name(node), node->weight, factor, score,
1617  new_score);
1618  node->weight = INFINITY_HACK;
1619  continue;
1620  }
1621 
1622  if (only_positive && (new_score < 0) && (node->weight == 0)) {
1623  crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1624  pe__node_name(node), node->weight, factor, score,
1625  new_score);
1626  continue;
1627  }
1628 
1629  crm_trace("%s: %d + %f * %d = %d", pe__node_name(node),
1630  node->weight, factor, score, new_score);
1631  node->weight = new_score;
1632  }
1633 }
1634 
1665 void
1667  const pcmk_resource_t *target_rsc,
1668  const char *log_id,
1669  GHashTable **nodes,
1670  const pcmk__colocation_t *colocation,
1671  float factor, uint32_t flags)
1672 {
1673  GHashTable *work = NULL;
1674 
1675  CRM_ASSERT((source_rsc != NULL) && (nodes != NULL)
1676  && ((colocation != NULL)
1677  || ((target_rsc == NULL) && (*nodes == NULL))));
1678 
1679  if (log_id == NULL) {
1680  log_id = source_rsc->id;
1681  }
1682 
1683  // Avoid infinite recursion
1684  if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1685  pe_rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1686  log_id, source_rsc->id);
1687  return;
1688  }
1690 
1691  if (*nodes == NULL) {
1692  work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1693  target_rsc = source_rsc;
1694  } else {
1696 
1697  pe_rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1698  log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1699  work = pcmk__copy_node_table(*nodes);
1700  add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1701  factor, pos);
1702  }
1703 
1704  if (work == NULL) {
1706  return;
1707  }
1708 
1709  if (pcmk__any_node_available(work)) {
1710  GList *colocations = NULL;
1711 
1713  colocations = pcmk__this_with_colocations(source_rsc);
1714  pe_rsc_trace(source_rsc,
1715  "Checking additional %d optional '%s with' "
1716  "constraints",
1717  g_list_length(colocations), source_rsc->id);
1718  } else {
1719  colocations = pcmk__with_this_colocations(source_rsc);
1720  pe_rsc_trace(source_rsc,
1721  "Checking additional %d optional 'with %s' "
1722  "constraints",
1723  g_list_length(colocations), source_rsc->id);
1724  }
1726 
1727  for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1728  pcmk__colocation_t *constraint = iter->data;
1729 
1730  pcmk_resource_t *other = NULL;
1731  float other_factor = factor * constraint->score / (float) INFINITY;
1732 
1734  other = constraint->primary;
1735  } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1736  continue;
1737  } else {
1738  other = constraint->dependent;
1739  }
1740 
1741  pe_rsc_trace(source_rsc,
1742  "Optionally merging score of '%s' constraint "
1743  "(%s with %s)",
1744  constraint->id, constraint->dependent->id,
1745  constraint->primary->id);
1746  other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1747  &work, constraint,
1748  other_factor, flags);
1749  pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1750  }
1751  g_list_free(colocations);
1752 
1754  pe_rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1755  log_id, source_rsc->id);
1756  g_hash_table_destroy(work);
1758  return;
1759  }
1760 
1761 
1763  pcmk_node_t *node = NULL;
1764  GHashTableIter iter;
1765 
1766  g_hash_table_iter_init(&iter, work);
1767  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1768  if (node->weight == INFINITY_HACK) {
1769  node->weight = 1;
1770  }
1771  }
1772  }
1773 
1774  if (*nodes != NULL) {
1775  g_hash_table_destroy(*nodes);
1776  }
1777  *nodes = work;
1778 
1780 }
1781 
1789 void
1790 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1791 {
1792  pcmk__colocation_t *colocation = data;
1793  pcmk_resource_t *target_rsc = user_data;
1794 
1795  pcmk_resource_t *source_rsc = colocation->dependent;
1796  const float factor = colocation->score / (float) INFINITY;
1797  uint32_t flags = pcmk__coloc_select_active;
1798 
1799  if (!pcmk__colocation_has_influence(colocation, NULL)) {
1800  return;
1801  }
1802  if (target_rsc->variant == pcmk_rsc_variant_clone) {
1804  }
1805  pe_rsc_trace(target_rsc,
1806  "%s: Incorporating attenuated %s assignment scores due "
1807  "to colocation %s",
1808  target_rsc->id, source_rsc->id, colocation->id);
1809  source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1810  source_rsc->id,
1811  &target_rsc->allowed_nodes,
1812  colocation, factor, flags);
1813 }
1814 
1832 void
1834  const pcmk_resource_t *primary,
1835  const pcmk__colocation_t *colocation,
1836  const GList *primary_nodes, bool merge_scores)
1837 {
1838  GHashTableIter iter;
1839  pcmk_node_t *dependent_node = NULL;
1840 
1841  CRM_ASSERT((dependent != NULL) && (primary != NULL)
1842  && (colocation != NULL));
1843 
1844  g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1845  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1846  const pcmk_node_t *primary_node = NULL;
1847 
1848  primary_node = pe_find_node_id(primary_nodes,
1849  dependent_node->details->id);
1850  if (primary_node == NULL) {
1851  dependent_node->weight = -INFINITY;
1852  pe_rsc_trace(dependent,
1853  "Banning %s from %s (no primary instance) for %s",
1854  dependent->id, pe__node_name(dependent_node),
1855  colocation->id);
1856 
1857  } else if (merge_scores) {
1858  dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1859  primary_node->weight);
1860  pe_rsc_trace(dependent,
1861  "Added %s's score %s to %s's score for %s (now %s) "
1862  "for colocation %s",
1863  primary->id, pcmk_readable_score(primary_node->weight),
1864  dependent->id, pe__node_name(dependent_node),
1865  pcmk_readable_score(dependent_node->weight),
1866  colocation->id);
1867  }
1868  }
1869 }
1870 
1881 GList *
1883 {
1884  GList *list = NULL;
1885 
1886  rsc->cmds->with_this_colocations(rsc, rsc, &list);
1887  return list;
1888 }
1889 
1900 GList *
1902 {
1903  GList *list = NULL;
1904 
1905  rsc->cmds->this_with_colocations(rsc, rsc, &list);
1906  return list;
1907 }
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition: resources.h:417
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
void pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
void pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
A dumping ground.
void pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition: resources.h:412
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:98
#define pcmk__if_tracing(if_action, else_action)
GList * rsc_cons
Definition: resources.h:445
pcmk__coloc_affects
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:86
#define PCMK__ROLE_STARTED
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition: complex.c:936
Whether resource is an implicit container resource for a bundle replica.
Definition: resources.h:178
pcmk_resource_t rsc2
#define XML_COLOC_ATTR_TARGET_INSTANCE
Definition: msg_xml.h:373
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__any_node_available(GHashTable *nodes)
#define pcmk__config_warn(fmt...)
GList * children
Resource&#39;s child resources, if any.
Definition: resources.h:475
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:341
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
enum rsc_role_e next_role
Resource&#39;s scheduled next role.
Definition: resources.h:469
Implementation of pcmk_action_t.
Definition: actions.h:390
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:302
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1639
void(* add_colocated_node_scores)(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
Promoted.
Definition: roles.h:32
#define XML_CONS_TAG_RSC_DEPEND
Definition: msg_xml.h:353
enum pcmk__coloc_affects pcmk__colocation_affects(const pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool preview)
GList * rsc_cons_lhs
Definition: resources.h:444
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition: nvpair.c:905
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
#define XML_COLOC_ATTR_TARGET_ROLE
Definition: msg_xml.h:365
void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
#define CRM_SCORE_INFINITY
Definition: crm.h:84
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:357
void pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
const char * action
Definition: pcmk_fence.c:30
void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:64
GList * resources
Resources in cluster.
Definition: scheduler.h:196
void pcmk__block_colocation_dependents(pcmk_action_t *action)
void(* with_this_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
const char * role2text(enum rsc_role_e role)
Definition: common.c:458
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
int weight
Node score for a given resource.
Definition: nodes.h:131
pcmk_resource_t * parent
Resource&#39;s parent resource, if any.
Definition: resources.h:413
#define XML_COLOC_ATTR_TARGET
Definition: msg_xml.h:364
Implementation of pcmk_resource_t.
Definition: resources.h:399
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition: bundle.c:131
Utility functions.
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
pcmk_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition: status.c:448
int priority
Configured priority.
Definition: resources.h:422
#define pe_warn_once(pe_wo_bit, fmt...)
Definition: internal.h:142
void(* this_with_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:85
#define CRM_ATTR_UNAME
Definition: crm.h:113
#define crm_trace(fmt, args...)
Definition: logging.h:387
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:99
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2555
#define PCMK_ACTION_START
Definition: actions.h:71
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: nvpair.c:878
unsigned long long flags
Group of enum pcmk_rsc_flags.
Definition: resources.h:429
Unpromoted.
Definition: roles.h:31
void pcmk__add_this_with_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
Ordering applies only if &#39;first&#39; is required and on same node as &#39;then&#39;.
#define PCMK_ACTION_STOP
Definition: actions.h:74
GList * actions
Definition: resources.h:447
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:370
void pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, const GList *primary_nodes, bool merge_scores)
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:234
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag)
void free_xml(xmlNode *child)
Definition: xml.c:783
Implementation of pcmk_node_t.
Definition: nodes.h:130
enum rsc_role_e text2role(const char *role)
Definition: common.c:487
enum pe_obj_types variant
Resource variant.
Definition: resources.h:414
xmlNode * input
CIB XML.
Definition: scheduler.h:175
uint32_t id
Definition: cpg.c:45
pcmk_resource_t rsc1
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:363
pcmk_resource_t * primary
Whether resource has "critical" meta-attribute enabled.
Definition: resources.h:148
Whether resource has not yet been assigned to a node.
Definition: resources.h:127
#define XML_COLOC_ATTR_NODE_ATTR
Definition: msg_xml.h:366
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:424
Cluster status and scheduling.
Whether resource is in the process of modifying allowed node scores.
Definition: resources.h:133
#define XML_COLOC_ATTR_INFLUENCE
Definition: msg_xml.h:367
#define PCMK__ROLE_UNKNOWN
Whether action is runnable.
Definition: actions.h:241
G_GNUC_INTERNAL GHashTable * pcmk__copy_node_table(GHashTable *nodes)
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
Definition: pe_actions.c:1486
#define crm_err(fmt, args...)
Definition: logging.h:381
pcmk_scheduler_t * scheduler
#define CRM_ASSERT(expr)
Definition: results.h:42
GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
GList * colocation_constraints
Colocation constraints.
Definition: scheduler.h:199
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:1696
void pcmk__new_colocation(const char *id, const char *node_attr, int score, pcmk_resource_t *dependent, pcmk_resource_t *primary, const char *dependent_role, const char *primary_role, uint32_t flags)
#define INFINITY_HACK
Configuration tag object.
Definition: tags.h:26
pcmk_node_t * allocated_to
Node resource is assigned to.
Definition: resources.h:451
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:70
#define PCMK_ACTION_PROMOTE
Definition: actions.h:65
pcmk_resource_t * dependent
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:341
enum pe_action_flags flags
Group of enum pe_action_flags.
Definition: actions.h:409
const char * node_attribute
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:116
#define crm_log_xml_trace(xml, text)
Definition: logging.h:395
void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
Whether resource can be promoted and demoted.
Definition: resources.h:124
Resource role is unknown.
Definition: roles.h:28
void pcmk__add_dependent_scores(gpointer data, gpointer user_data)
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:37
#define ID(x)
Definition: msg_xml.h:474
int stickiness
Extra preference for current node.
Definition: resources.h:423
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition: clone.c:227
Clone resource.
Definition: resources.h:36
#define CRM_ATTR_ID
Definition: crm.h:114
#define XML_CONS_ATTR_SYMMETRICAL
Definition: msg_xml.h:358
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
#define crm_info(fmt, args...)
Definition: logging.h:384
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:362
uint64_t flags
Definition: remote.c:215
GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:35
char * id
Resource ID in configuration.
Definition: resources.h:400
GHashTable * allowed_nodes
Nodes where resource may run (key is node ID, not name)
Definition: resources.h:466
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510