pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_sched_colocation.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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/common/scheduler.h>
18 #include <crm/pengine/status.h>
19 #include <pacemaker-internal.h>
20 
21 #include "crm/common/util.h"
23 #include "crm/common/xml.h"
24 #include "libpacemaker_private.h"
25 
26 // Used to temporarily mark a node as unusable
27 #define INFINITY_HACK (PCMK_SCORE_INFINITY * -100)
28 
39 const char *
40 pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr,
41  const pcmk_resource_t *rsc)
42 {
43  const char *target = NULL;
44 
45  /* A resource colocated with a bundle or its primitive can't run on the
46  * bundle node itself (where only the primitive, if any, can run). Instead,
47  * we treat it as a colocation with the bundle's containers, so always look
48  * up colocation node attributes on the container host.
49  */
50  if (pcmk__is_bundle_node(node) && pcmk__is_bundled(rsc)
51  && (pe__const_top_resource(rsc, false) == pe__bundled_resource(rsc))) {
53 
54  } else if (rsc != NULL) {
55  target = g_hash_table_lookup(rsc->priv->meta,
57  }
58 
59  return pcmk__node_attr(node, attr, target, pcmk__rsc_node_assigned);
60 }
61 
85 static gint
86 cmp_colocation_priority(const pcmk__colocation_t *colocation1,
87  const pcmk__colocation_t *colocation2, bool dependent)
88 {
89  const pcmk_resource_t *rsc1 = NULL;
90  const pcmk_resource_t *rsc2 = NULL;
91 
92  if (colocation1 == NULL) {
93  return 1;
94  }
95  if (colocation2 == NULL) {
96  return -1;
97  }
98 
99  if (dependent) {
100  rsc1 = colocation1->dependent;
101  rsc2 = colocation2->dependent;
102  pcmk__assert(colocation1->primary != NULL);
103  } else {
104  rsc1 = colocation1->primary;
105  rsc2 = colocation2->primary;
106  pcmk__assert(colocation1->dependent != NULL);
107  }
108  pcmk__assert((rsc1 != NULL) && (rsc2 != NULL));
109 
110  if (rsc1->priv->priority > rsc2->priv->priority) {
111  return -1;
112  }
113  if (rsc1->priv->priority < rsc2->priv->priority) {
114  return 1;
115  }
116 
117  // Process clones before primitives and groups
118  if (rsc1->priv->variant > rsc2->priv->variant) {
119  return -1;
120  }
121  if (rsc1->priv->variant < rsc2->priv->variant) {
122  return 1;
123  }
124 
125  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
126  * clones (probably unnecessary, but avoids having to update regression
127  * tests)
128  */
129  if (pcmk__is_clone(rsc1)) {
131  && !pcmk_is_set(rsc2->flags, pcmk__rsc_promotable)) {
132  return -1;
133  }
136  return 1;
137  }
138  }
139 
140  return strcmp(rsc1->id, rsc2->id);
141 }
142 
163 static gint
164 cmp_dependent_priority(gconstpointer a, gconstpointer b)
165 {
166  return cmp_colocation_priority(a, b, true);
167 }
168 
189 static gint
190 cmp_primary_priority(gconstpointer a, gconstpointer b)
191 {
192  return cmp_colocation_priority(a, b, false);
193 }
194 
206 void
207 pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
208  const pcmk_resource_t *rsc)
209 {
210  pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
211 
212  pcmk__rsc_trace(rsc,
213  "Adding colocation %s (%s with %s using %s @%s) to "
214  "'this with' list for %s",
215  colocation->id, colocation->dependent->id,
216  colocation->primary->id, colocation->node_attribute,
217  pcmk_readable_score(colocation->score), rsc->id);
218  *list = g_list_insert_sorted(*list, (gpointer) colocation,
219  cmp_primary_priority);
220 }
221 
233 void
234 pcmk__add_this_with_list(GList **list, GList *addition,
235  const pcmk_resource_t *rsc)
236 {
237  pcmk__assert((list != NULL) && (rsc != NULL));
238 
240  {}, // Always add each colocation individually if tracing
241  {
242  if (*list == NULL) {
243  // Trivial case for efficiency if not tracing
244  *list = g_list_copy(addition);
245  return;
246  }
247  }
248  );
249 
250  for (const GList *iter = addition; iter != NULL; iter = iter->next) {
251  pcmk__add_this_with(list, addition->data, rsc);
252  }
253 }
254 
266 void
267 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
268  const pcmk_resource_t *rsc)
269 {
270  pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
271 
272  pcmk__rsc_trace(rsc,
273  "Adding colocation %s (%s with %s using %s @%s) to "
274  "'with this' list for %s",
275  colocation->id, colocation->dependent->id,
276  colocation->primary->id, colocation->node_attribute,
277  pcmk_readable_score(colocation->score), rsc->id);
278  *list = g_list_insert_sorted(*list, (gpointer) colocation,
279  cmp_dependent_priority);
280 }
281 
293 void
294 pcmk__add_with_this_list(GList **list, GList *addition,
295  const pcmk_resource_t *rsc)
296 {
297  pcmk__assert((list != NULL) && (rsc != NULL));
298 
300  {}, // Always add each colocation individually if tracing
301  {
302  if (*list == NULL) {
303  // Trivial case for efficiency if not tracing
304  *list = g_list_copy(addition);
305  return;
306  }
307  }
308  );
309 
310  for (const GList *iter = addition; iter != NULL; iter = iter->next) {
311  pcmk__add_with_this(list, addition->data, rsc);
312  }
313 }
314 
324 static void
325 anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
326  pcmk_resource_t *then_rsc, int then_role)
327 {
328  const char *first_tasks[] = { NULL, NULL };
329  const char *then_tasks[] = { NULL, NULL };
330 
331  /* Actions to make first_rsc lose first_role */
332  if (first_role == pcmk_role_promoted) {
333  first_tasks[0] = PCMK_ACTION_DEMOTE;
334 
335  } else {
336  first_tasks[0] = PCMK_ACTION_STOP;
337 
338  if (first_role == pcmk_role_unpromoted) {
339  first_tasks[1] = PCMK_ACTION_PROMOTE;
340  }
341  }
342 
343  /* Actions to make then_rsc gain then_role */
344  if (then_role == pcmk_role_promoted) {
345  then_tasks[0] = PCMK_ACTION_PROMOTE;
346 
347  } else {
348  then_tasks[0] = PCMK_ACTION_START;
349 
350  if (then_role == pcmk_role_unpromoted) {
351  then_tasks[1] = PCMK_ACTION_DEMOTE;
352  }
353  }
354 
355  for (int first_lpc = 0;
356  (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
357 
358  for (int then_lpc = 0;
359  (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
360 
361  pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
362  then_rsc, then_tasks[then_lpc],
364  }
365  }
366 }
367 
383 void
384 pcmk__new_colocation(const char *id, const char *node_attr, int score,
385  pcmk_resource_t *dependent, pcmk_resource_t *primary,
386  const char *dependent_role_spec,
387  const char *primary_role_spec, uint32_t flags)
388 {
389  pcmk__colocation_t *new_con = NULL;
390  enum rsc_role_e dependent_role = pcmk_role_unknown;
391  enum rsc_role_e primary_role = pcmk_role_unknown;
392 
393  CRM_CHECK(id != NULL, return);
394 
395  if ((dependent == NULL) || (primary == NULL)) {
396  pcmk__config_err("Ignoring colocation '%s' because resource "
397  "does not exist", id);
398  return;
399  }
400  if ((pcmk__parse_constraint_role(id, dependent_role_spec,
401  &dependent_role) != pcmk_rc_ok)
402  || (pcmk__parse_constraint_role(id, primary_role_spec,
403  &primary_role) != pcmk_rc_ok)) {
404  // Not possible with schema validation enabled (error already logged)
405  return;
406  }
407 
408  if (score == 0) {
409  pcmk__rsc_trace(dependent,
410  "Ignoring colocation '%s' (%s with %s) because score is 0",
411  id, dependent->id, primary->id);
412  return;
413  }
414 
415  new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t));
416  new_con->id = id;
417  new_con->dependent = dependent;
418  new_con->primary = primary;
419  new_con->score = score;
420  new_con->dependent_role = dependent_role;
421  new_con->primary_role = primary_role;
422 
423  new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
424  new_con->flags = flags;
425 
426  pcmk__add_this_with(&(dependent->priv->this_with_colocations), new_con,
427  dependent);
428  pcmk__add_with_this(&(primary->priv->with_this_colocations), new_con,
429  primary);
430 
431  dependent->priv->scheduler->priv->colocation_constraints =
432  g_list_prepend(dependent->priv->scheduler->priv->colocation_constraints,
433  new_con);
434 
435  if (score <= -PCMK_SCORE_INFINITY) {
436  anti_colocation_order(dependent, new_con->dependent_role, primary,
437  new_con->primary_role);
438  anti_colocation_order(primary, new_con->primary_role, dependent,
439  new_con->dependent_role);
440  }
441 }
442 
455 static uint32_t
456 unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
457  const char *influence_s)
458 {
459  if (influence_s != NULL) {
460  int influence_i = 0;
461 
462  if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
463  pcmk__config_err("Constraint '%s' has invalid value for "
464  PCMK_XA_INFLUENCE " (using default)",
465  coloc_id);
466  } else {
467  return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
468  }
469  }
470  if (pcmk_is_set(rsc->flags, pcmk__rsc_critical)) {
471  return pcmk__coloc_influence;
472  }
473  return pcmk__coloc_none;
474 }
475 
476 static void
477 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
478  const char *influence_s, pcmk_scheduler_t *scheduler)
479 {
480  xmlNode *xml_rsc = NULL;
481  pcmk_resource_t *other = NULL;
482  pcmk_resource_t *resource = NULL;
483  const char *set_id = pcmk__xe_id(set);
484  const char *role = crm_element_value(set, PCMK_XA_ROLE);
485  bool with_previous = false;
486  int local_score = score;
487  bool sequential = false;
488  uint32_t flags = pcmk__coloc_none;
489  const char *xml_rsc_id = NULL;
490  const char *score_s = crm_element_value(set, PCMK_XA_SCORE);
491 
492  if (score_s != NULL) {
493  int rc = pcmk_parse_score(score_s, &local_score, 0);
494 
495  if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
496  pcmk__config_err("Ignoring colocation '%s' for set '%s' "
497  "because '%s' is not a valid score",
498  coloc_id, set_id, score_s);
499  return;
500  }
501  }
502  if (local_score == 0) {
503  crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
504  coloc_id, set_id);
505  return;
506  }
507 
508  /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether
509  * resources in a positive-score set are colocated with the previous or next
510  * resource.
511  */
512  if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING),
515  with_previous = true;
516  } else {
518  "Support for '" PCMK__XA_ORDERING "' other than"
520  " (such as %s) is deprecated and will be removed in a"
521  " future release",
522  set_id);
523  }
524 
526  &sequential) == pcmk_rc_ok)
527  && !sequential) {
528  return;
529  }
530 
531  if (local_score > 0) {
532  for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
533  NULL);
534  xml_rsc != NULL;
535  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
536 
537  xml_rsc_id = pcmk__xe_id(xml_rsc);
538  resource =
540  xml_rsc_id);
541  if (resource == NULL) {
542  // Should be possible only with validation disabled
543  pcmk__config_err("Ignoring %s and later resources in set %s: "
544  "No such resource", xml_rsc_id, set_id);
545  return;
546  }
547  if (other != NULL) {
549  | unpack_influence(coloc_id, resource, influence_s);
550  if (with_previous) {
551  pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
552  resource->id, other->id, set_id);
553  pcmk__new_colocation(set_id, NULL, local_score, resource,
554  other, role, role, flags);
555  } else {
556  pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
557  other->id, resource->id, set_id);
558  pcmk__new_colocation(set_id, NULL, local_score, other,
559  resource, role, role, flags);
560  }
561  }
562  other = resource;
563  }
564 
565  } else {
566  /* Anti-colocating with every prior resource is
567  * the only way to ensure the intuitive result
568  * (i.e. that no one in the set can run with anyone else in the set)
569  */
570 
571  for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
572  NULL);
573  xml_rsc != NULL;
574  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
575 
576  xmlNode *xml_rsc_with = NULL;
577 
578  xml_rsc_id = pcmk__xe_id(xml_rsc);
579  resource =
581  xml_rsc_id);
582  if (resource == NULL) {
583  // Should be possible only with validation disabled
584  pcmk__config_err("Ignoring %s and later resources in set %s: "
585  "No such resource", xml_rsc_id, set_id);
586  return;
587  }
589  | unpack_influence(coloc_id, resource, influence_s);
590  for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
591  NULL, NULL);
592  xml_rsc_with != NULL;
593  xml_rsc_with = pcmk__xe_next(xml_rsc_with,
595 
596  xml_rsc_id = pcmk__xe_id(xml_rsc_with);
597  if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
598  break;
599  }
600  other =
602  xml_rsc_id);
603  pcmk__assert(other != NULL); // We already processed it
604  pcmk__new_colocation(set_id, NULL, local_score,
605  resource, other, role, role, flags);
606  }
607  }
608  }
609 }
610 
623 static void
624 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
625  int score, const char *influence_s,
627 {
628  xmlNode *xml_rsc = NULL;
629  pcmk_resource_t *rsc_1 = NULL;
630  pcmk_resource_t *rsc_2 = NULL;
631 
632  const char *xml_rsc_id = NULL;
633  const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE);
634  const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE);
635 
636  int rc = pcmk_rc_ok;
637  bool sequential = false;
638  uint32_t flags = pcmk__coloc_none;
639 
640  if (score == 0) {
641  crm_trace("Ignoring colocation '%s' between sets %s and %s "
642  "because score is 0",
643  id, pcmk__xe_id(set1), pcmk__xe_id(set2));
644  return;
645  }
646 
647  rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential);
648  if ((rc != pcmk_rc_ok) || sequential) {
649  // Get the first one
650  xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL);
651  if (xml_rsc != NULL) {
652  xml_rsc_id = pcmk__xe_id(xml_rsc);
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 with set %s "
658  "because first resource %s not found",
659  pcmk__xe_id(set1), pcmk__xe_id(set2),
660  xml_rsc_id);
661  return;
662  }
663  }
664  }
665 
666  rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential);
667  if ((rc != pcmk_rc_ok) || sequential) {
668  // Get the last one
669  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
670  NULL);
671  xml_rsc != NULL;
672  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
673 
674  xml_rsc_id = pcmk__xe_id(xml_rsc);
675  }
677  xml_rsc_id);
678  if (rsc_2 == NULL) {
679  // Should be possible only with validation disabled
680  pcmk__config_err("Ignoring colocation of set %s with set %s "
681  "because last resource %s not found",
682  pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id);
683  return;
684  }
685  }
686 
687  if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
688  flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
689  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
690  flags);
691 
692  } else if (rsc_1 != NULL) { // Only set1 is sequential
693  flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
694  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
695  NULL);
696  xml_rsc != NULL;
697  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
698 
699  xml_rsc_id = pcmk__xe_id(xml_rsc);
701  xml_rsc_id);
702  if (rsc_2 == NULL) {
703  // Should be possible only with validation disabled
704  pcmk__config_err("Ignoring set %s colocation with resource %s "
705  "in set %s: No such resource",
706  pcmk__xe_id(set1), xml_rsc_id,
707  pcmk__xe_id(set2));
708  continue;
709  }
710  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
711  role_2, flags);
712  }
713 
714  } else if (rsc_2 != NULL) { // Only set2 is sequential
715  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
716  NULL);
717  xml_rsc != NULL;
718  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
719 
720  xml_rsc_id = pcmk__xe_id(xml_rsc);
722  xml_rsc_id);
723  if (rsc_1 == NULL) {
724  // Should be possible only with validation disabled
725  pcmk__config_err("Ignoring colocation of set %s resource %s "
726  "with set %s: No such resource",
727  pcmk__xe_id(set1), xml_rsc_id,
728  pcmk__xe_id(set2));
729  continue;
730  }
732  | unpack_influence(id, rsc_1, influence_s);
733  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
734  role_2, flags);
735  }
736 
737  } else { // Neither set is sequential
738  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
739  NULL);
740  xml_rsc != NULL;
741  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
742 
743  xmlNode *xml_rsc_2 = NULL;
744 
745  xml_rsc_id = pcmk__xe_id(xml_rsc);
747  xml_rsc_id);
748  if (rsc_1 == NULL) {
749  // Should be possible only with validation disabled
750  pcmk__config_err("Ignoring colocation of set %s resource %s "
751  "with set %s: No such resource",
752  pcmk__xe_id(set1), xml_rsc_id,
753  pcmk__xe_id(set2));
754  continue;
755  }
756 
758  | unpack_influence(id, rsc_1, influence_s);
759  for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
760  NULL, NULL);
761  xml_rsc_2 != NULL;
762  xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
763 
764  xml_rsc_id = pcmk__xe_id(xml_rsc_2);
765  rsc_2 =
767  xml_rsc_id);
768  if (rsc_2 == NULL) {
769  // Should be possible only with validation disabled
770  pcmk__config_err("Ignoring colocation of set %s resource "
771  "%s with set %s resource %s: No such "
772  "resource",
773  pcmk__xe_id(set1), pcmk__xe_id(xml_rsc),
774  pcmk__xe_id(set2), xml_rsc_id);
775  continue;
776  }
777  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
778  role_1, role_2, flags);
779  }
780  }
781  }
782 }
783 
794 static void
795 unpack_simple_colocation(const xmlNode *xml_obj, const char *id, int score,
796  const char *influence_s, pcmk_scheduler_t *scheduler)
797 {
798  uint32_t flags = pcmk__coloc_none;
799 
800  const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
801  const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
802  const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
803  const char *primary_role = crm_element_value(xml_obj,
805  const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE);
806 
807  pcmk_resource_t *primary = NULL;
808  pcmk_resource_t *dependent = NULL;
809 
811  primary_id);
813  dependent_id);
814 
815  if (dependent == NULL) {
816  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
817  "does not exist", id, dependent_id);
818  return;
819 
820  } else if (primary == NULL) {
821  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
822  "does not exist", id, primary_id);
823  return;
824  }
825 
827  pcmk__config_warn("The colocation constraint "
828  "'" PCMK_XA_SYMMETRICAL "' attribute has been "
829  "removed");
830  }
831 
832  flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
833  pcmk__new_colocation(id, attr, score, dependent, primary,
834  dependent_role, primary_role, flags);
835 }
836 
837 // \return Standard Pacemaker return code
838 static int
839 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
841 {
842  const char *id = NULL;
843  const char *dependent_id = NULL;
844  const char *primary_id = NULL;
845  const char *dependent_role = NULL;
846  const char *primary_role = NULL;
847 
848  pcmk_resource_t *dependent = NULL;
849  pcmk_resource_t *primary = NULL;
850 
851  pcmk__idref_t *dependent_tag = NULL;
852  pcmk__idref_t *primary_tag = NULL;
853 
854  xmlNode *dependent_set = NULL;
855  xmlNode *primary_set = NULL;
856  bool any_sets = false;
857 
858  *expanded_xml = NULL;
859 
860  CRM_CHECK(xml_obj != NULL, return EINVAL);
861 
862  id = pcmk__xe_id(xml_obj);
863  if (id == NULL) {
864  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
865  xml_obj->name);
866  return pcmk_rc_unpack_error;
867  }
868 
869  // Check whether there are any resource sets with template or tag references
870  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
871  if (*expanded_xml != NULL) {
872  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
873  return pcmk_rc_ok;
874  }
875 
876  dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
877  primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
878  if ((dependent_id == NULL) || (primary_id == NULL)) {
879  return pcmk_rc_ok;
880  }
881 
882  if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
883  &dependent_tag)) {
884  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
885  "valid resource or tag", id, dependent_id);
886  return pcmk_rc_unpack_error;
887  }
888 
889  if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
890  &primary_tag)) {
891  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
892  "valid resource or tag", id, primary_id);
893  return pcmk_rc_unpack_error;
894  }
895 
896  if ((dependent != NULL) && (primary != NULL)) {
897  /* Neither side references any template/tag. */
898  return pcmk_rc_ok;
899  }
900 
901  if ((dependent_tag != NULL) && (primary_tag != NULL)) {
902  // A colocation constraint between two templates/tags makes no sense
903  pcmk__config_err("Ignoring constraint '%s' because two templates or "
904  "tags cannot be colocated", id);
905  return pcmk_rc_unpack_error;
906  }
907 
908  dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
909  primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE);
910 
911  *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
912 
913  /* Convert dependent's template/tag reference into constraint
914  * PCMK_XE_RESOURCE_SET
915  */
916  if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true,
917  scheduler)) {
918  pcmk__xml_free(*expanded_xml);
919  *expanded_xml = NULL;
920  return pcmk_rc_unpack_error;
921  }
922 
923  if (dependent_set != NULL) {
924  if (dependent_role != NULL) {
925  /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
926  * PCMK_XA_ROLE
927  */
928  crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role);
929  pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
930  }
931  any_sets = true;
932  }
933 
934  /* Convert primary's template/tag reference into constraint
935  * PCMK_XE_RESOURCE_SET
936  */
937  if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true,
938  scheduler)) {
939  pcmk__xml_free(*expanded_xml);
940  *expanded_xml = NULL;
941  return pcmk_rc_unpack_error;
942  }
943 
944  if (primary_set != NULL) {
945  if (primary_role != NULL) {
946  /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
947  * PCMK_XA_ROLE
948  */
949  crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role);
951  }
952  any_sets = true;
953  }
954 
955  if (any_sets) {
956  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
957  } else {
958  pcmk__xml_free(*expanded_xml);
959  *expanded_xml = NULL;
960  }
961 
962  return pcmk_rc_ok;
963 }
964 
972 void
974 {
975  int score_i = 0;
976  xmlNode *set = NULL;
977  xmlNode *last = NULL;
978 
979  xmlNode *orig_xml = NULL;
980  xmlNode *expanded_xml = NULL;
981 
982  const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
983  const char *score = NULL;
984  const char *influence_s = NULL;
985 
986  if (pcmk__str_empty(id)) {
988  " without " CRM_ATTR_ID);
989  return;
990  }
991 
992  if (unpack_colocation_tags(xml_obj, &expanded_xml,
993  scheduler) != pcmk_rc_ok) {
994  return;
995  }
996  if (expanded_xml != NULL) {
997  orig_xml = xml_obj;
998  xml_obj = expanded_xml;
999  }
1000 
1001  score = crm_element_value(xml_obj, PCMK_XA_SCORE);
1002  if (score != NULL) {
1003  int rc = pcmk_parse_score(score, &score_i, 0);
1004 
1005  if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
1006  pcmk__config_err("Ignoring colocation %s because '%s' "
1007  "is not a valid score", id, score);
1008  return;
1009  }
1010  }
1011  influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE);
1012 
1013  for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1014  set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
1015 
1016  set = pcmk__xe_resolve_idref(set, scheduler->input);
1017  if (set == NULL) { // Configuration error, message already logged
1018  if (expanded_xml != NULL) {
1019  pcmk__xml_free(expanded_xml);
1020  }
1021  return;
1022  }
1023 
1024  if (pcmk__str_empty(pcmk__xe_id(set))) {
1026  " without " CRM_ATTR_ID);
1027  continue;
1028  }
1029  unpack_colocation_set(set, score_i, id, influence_s, scheduler);
1030 
1031  if (last != NULL) {
1032  colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
1033  }
1034  last = set;
1035  }
1036 
1037  if (expanded_xml) {
1038  pcmk__xml_free(expanded_xml);
1039  xml_obj = orig_xml;
1040  }
1041 
1042  if (last == NULL) {
1043  unpack_simple_colocation(xml_obj, id, score_i, influence_s, scheduler);
1044  }
1045 }
1046 
1059 bool
1061  const pcmk_resource_t *rsc)
1062 {
1063  if (rsc == NULL) {
1064  rsc = colocation->primary;
1065  }
1066 
1067  /* A bundle replica colocates its remote connection with its container,
1068  * using a finite score so that the container can run on Pacemaker Remote
1069  * nodes.
1070  *
1071  * Moving a connection is lightweight and does not interrupt the service,
1072  * while moving a container is heavyweight and does interrupt the service,
1073  * so don't move a clean, active container based solely on the preferences
1074  * of its connection.
1075  *
1076  * This also avoids problematic scenarios where two containers want to
1077  * perpetually swap places.
1078  */
1079  if (pcmk_is_set(colocation->dependent->flags,
1082  && pcmk__list_of_1(rsc->priv->active_nodes)) {
1083  return false;
1084  }
1085 
1086  /* The dependent in a colocation influences the primary's location
1087  * if the PCMK_XA_INFLUENCE option is true or the primary is not yet active.
1088  */
1089  return pcmk_is_set(colocation->flags, pcmk__coloc_influence)
1090  || (rsc->priv->active_nodes == NULL);
1091 }
1092 
1101 static void
1102 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1103  const pcmk_resource_t *reason)
1104 {
1105  GList *iter = NULL;
1106  char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1107 
1108  for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
1109  pcmk_action_t *action = iter->data;
1110 
1112  && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1113 
1115  pe_action_set_reason(action, reason_text, false);
1118  }
1119  }
1120 
1121  // If parent resource can't perform an action, neither can any children
1122  for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
1123  mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1124  }
1125  free(reason_text);
1126 }
1127 
1138 void
1140 {
1141  GList *iter = NULL;
1142  GList *colocations = NULL;
1143  pcmk_resource_t *rsc = NULL;
1144  bool is_start = false;
1145 
1146  if (pcmk_is_set(action->flags, pcmk__action_runnable)) {
1147  return; // Only unrunnable actions block dependents
1148  }
1149 
1150  is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1151  if (!is_start
1152  && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1153  return; // Only unrunnable starts and promotes block dependents
1154  }
1155 
1156  pcmk__assert(action->rsc != NULL); // Start and promote are resource actions
1157 
1158  /* If this resource is part of a collective resource, dependents are blocked
1159  * only if all instances of the collective are unrunnable, so check the
1160  * collective resource.
1161  */
1162  rsc = uber_parent(action->rsc);
1163  if (rsc->priv->parent != NULL) {
1164  rsc = rsc->priv->parent; // Bundle
1165  }
1166 
1167  // Colocation fails only if entire primary can't reach desired role
1168  for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
1169  pcmk_resource_t *child = iter->data;
1170  pcmk_action_t *child_action = NULL;
1171 
1172  child_action = find_first_action(child->priv->actions, NULL,
1173  action->task, NULL);
1174  if ((child_action == NULL)
1175  || pcmk_is_set(child_action->flags, pcmk__action_runnable)) {
1176  crm_trace("Not blocking %s colocation dependents because "
1177  "at least %s has runnable %s",
1178  rsc->id, child->id, action->task);
1179  return; // At least one child can reach desired role
1180  }
1181  }
1182 
1183  crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1184  rsc->id, action->rsc->id, action->task);
1185 
1186  // Check each colocation where this resource is primary
1187  colocations = pcmk__with_this_colocations(rsc);
1188  for (iter = colocations; iter != NULL; iter = iter->next) {
1189  pcmk__colocation_t *colocation = iter->data;
1190 
1191  if (colocation->score < PCMK_SCORE_INFINITY) {
1192  continue; // Only mandatory colocations block dependent
1193  }
1194 
1195  /* If the primary can't start, the dependent can't reach its colocated
1196  * role, regardless of what the primary or dependent colocation role is.
1197  *
1198  * If the primary can't be promoted, the dependent can't reach its
1199  * colocated role if the primary's colocation role is promoted.
1200  */
1201  if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1202  continue;
1203  }
1204 
1205  // Block the dependent from reaching its colocated role
1206  if (colocation->dependent_role == pcmk_role_promoted) {
1207  mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1208  action->rsc);
1209  } else {
1210  mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1211  action->rsc);
1212  }
1213  }
1214  g_list_free(colocations);
1215 }
1216 
1235 static const pcmk_resource_t *
1236 get_resource_for_role(const pcmk_resource_t *rsc)
1237 {
1239  const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1240 
1241  if (child != NULL) {
1242  return child;
1243  }
1244  }
1245  return rsc;
1246 }
1247 
1268  const pcmk_resource_t *primary,
1269  const pcmk__colocation_t *colocation, bool preview)
1270 {
1271  const pcmk_resource_t *dependent_role_rsc = NULL;
1272  const pcmk_resource_t *primary_role_rsc = NULL;
1273 
1274  pcmk__assert((dependent != NULL) && (primary != NULL)
1275  && (colocation != NULL));
1276 
1277  if (!preview && pcmk_is_set(primary->flags, pcmk__rsc_unassigned)) {
1278  // Primary resource has not been assigned yet, so we can't do anything
1280  }
1281 
1282  dependent_role_rsc = get_resource_for_role(dependent);
1283 
1284  primary_role_rsc = get_resource_for_role(primary);
1285 
1286  if ((colocation->dependent_role >= pcmk_role_unpromoted)
1287  && (dependent_role_rsc->priv->parent != NULL)
1288  && pcmk_is_set(dependent_role_rsc->priv->parent->flags,
1290  && !pcmk_is_set(dependent_role_rsc->flags, pcmk__rsc_unassigned)) {
1291 
1292  /* This is a colocation by role, and the dependent is a promotable clone
1293  * that has already been assigned, so the colocation should now affect
1294  * the role.
1295  */
1296  return pcmk__coloc_affects_role;
1297  }
1298 
1299  if (!preview && !pcmk_is_set(dependent->flags, pcmk__rsc_unassigned)) {
1300  /* The dependent resource has already been through assignment, so the
1301  * constraint no longer matters.
1302  */
1304  }
1305 
1306  if ((colocation->dependent_role != pcmk_role_unknown)
1307  && (colocation->dependent_role != dependent_role_rsc->priv->next_role)) {
1308  crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1309 
1310  "but %s next role is %s",
1311  ((colocation->score < 0)? "anti-" : ""),
1312  colocation->id, pcmk_role_text(colocation->dependent_role),
1313  dependent_role_rsc->id,
1314  pcmk_role_text(dependent_role_rsc->priv->next_role));
1316  }
1317 
1318  if ((colocation->primary_role != pcmk_role_unknown)
1319  && (colocation->primary_role != primary_role_rsc->priv->next_role)) {
1320  crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1321  "but %s next role is %s",
1322  ((colocation->score < 0)? "anti-" : ""),
1323  colocation->id, pcmk_role_text(colocation->primary_role),
1324  primary_role_rsc->id,
1325  pcmk_role_text(primary_role_rsc->priv->next_role));
1327  }
1328 
1330 }
1331 
1343 void
1345  const pcmk_resource_t *primary,
1346  const pcmk__colocation_t *colocation)
1347 {
1348  const char *attr = colocation->node_attribute;
1349  const char *value = NULL;
1350  GHashTable *work = NULL;
1351  GHashTableIter iter;
1352  pcmk_node_t *node = NULL;
1353 
1354  if (primary->priv->assigned_node != NULL) {
1355  value = pcmk__colocation_node_attr(primary->priv->assigned_node,
1356  attr, primary);
1357 
1358  } else if (colocation->score < 0) {
1359  // Nothing to do (anti-colocation with something that is not running)
1360  return;
1361  }
1362 
1363  work = pcmk__copy_node_table(dependent->priv->allowed_nodes);
1364 
1365  g_hash_table_iter_init(&iter, work);
1366  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1367  if (primary->priv->assigned_node == NULL) {
1368  node->assign->score = pcmk__add_scores(-colocation->score,
1369  node->assign->score);
1370  pcmk__rsc_trace(dependent,
1371  "Applied %s to %s score on %s (now %s after "
1372  "subtracting %s because primary %s inactive)",
1373  colocation->id, dependent->id,
1374  pcmk__node_name(node),
1375  pcmk_readable_score(node->assign->score),
1376  pcmk_readable_score(colocation->score), primary->id);
1377  continue;
1378  }
1379 
1380  if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1381  value, pcmk__str_casei)) {
1382 
1383  /* Add colocation score only if optional (or minus infinity). A
1384  * mandatory colocation is a requirement rather than a preference,
1385  * so we don't need to consider it for relative assignment purposes.
1386  * The resource will simply be forbidden from running on the node if
1387  * the primary isn't active there (via the condition above).
1388  */
1389  if (colocation->score < PCMK_SCORE_INFINITY) {
1390  node->assign->score = pcmk__add_scores(colocation->score,
1391  node->assign->score);
1392  pcmk__rsc_trace(dependent,
1393  "Applied %s to %s score on %s (now %s after "
1394  "adding %s)",
1395  colocation->id, dependent->id,
1396  pcmk__node_name(node),
1397  pcmk_readable_score(node->assign->score),
1398  pcmk_readable_score(colocation->score));
1399  }
1400  continue;
1401  }
1402 
1403  if (colocation->score >= PCMK_SCORE_INFINITY) {
1404  /* Only mandatory colocations are relevant when the colocation
1405  * attribute doesn't match, because an attribute not matching is not
1406  * a negative preference -- the colocation is simply relevant only
1407  * where it matches.
1408  */
1409  node->assign->score = -PCMK_SCORE_INFINITY;
1410  pcmk__rsc_trace(dependent,
1411  "Banned %s from %s because colocation %s attribute %s "
1412  "does not match",
1413  dependent->id, pcmk__node_name(node),
1414  colocation->id, attr);
1415  }
1416  }
1417 
1418  if ((colocation->score <= -PCMK_SCORE_INFINITY)
1419  || (colocation->score >= PCMK_SCORE_INFINITY)
1420  || pcmk__any_node_available(work)) {
1421 
1422  g_hash_table_destroy(dependent->priv->allowed_nodes);
1423  dependent->priv->allowed_nodes = work;
1424  work = NULL;
1425 
1426  } else {
1427  pcmk__rsc_info(dependent,
1428  "%s: Rolling back scores from %s (no available nodes)",
1429  dependent->id, primary->id);
1430  }
1431 
1432  if (work != NULL) {
1433  g_hash_table_destroy(work);
1434  }
1435 }
1436 
1450 int
1452  const pcmk_resource_t *primary,
1453  const pcmk__colocation_t *colocation)
1454 {
1455  const char *dependent_value = NULL;
1456  const char *primary_value = NULL;
1457  const char *attr = colocation->node_attribute;
1458  int score_multiplier = 1;
1459  int priority_delta = 0;
1460  const pcmk_node_t *primary_node = NULL;
1461  const pcmk_node_t *dependent_node = NULL;
1462 
1463  pcmk__assert((dependent != NULL) && (primary != NULL)
1464  && (colocation != NULL));
1465 
1466  primary_node = primary->priv->assigned_node;
1467  dependent_node = dependent->priv->assigned_node;
1468 
1469  if (dependent_node == NULL) {
1470  return 0;
1471  }
1472 
1473  if ((primary_node != NULL)
1474  && (colocation->primary_role != pcmk_role_unknown)) {
1475  /* Colocation applies only if the primary's next role matches.
1476  *
1477  * If primary_node == NULL, we want to proceed past this block, so that
1478  * dependent_node is marked ineligible for promotion.
1479  *
1480  * @TODO Why ignore a mandatory colocation in this case when we apply
1481  * its negation in the mismatched value case?
1482  */
1483  const pcmk_resource_t *role_rsc = get_resource_for_role(primary);
1484 
1485  if (colocation->primary_role != role_rsc->priv->next_role) {
1486  return 0;
1487  }
1488  }
1489 
1490  dependent_value = pcmk__colocation_node_attr(dependent_node, attr,
1491  dependent);
1492  primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1493 
1494  if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1495  if ((colocation->score == PCMK_SCORE_INFINITY)
1496  && (colocation->dependent_role == pcmk_role_promoted)) {
1497  /* For a mandatory promoted-role colocation, mark the dependent node
1498  * ineligible to promote the dependent if its attribute value
1499  * doesn't match the primary node's
1500  */
1501  score_multiplier = -1;
1502 
1503  } else {
1504  // Otherwise, ignore the colocation if attribute values don't match
1505  return 0;
1506  }
1507 
1508  } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1509  /* Node attribute values matched, so we want to avoid promoting the
1510  * dependent on this node
1511  */
1512  score_multiplier = -1;
1513  }
1514 
1515  priority_delta = score_multiplier * colocation->score;
1516  dependent->priv->priority = pcmk__add_scores(priority_delta,
1517  dependent->priv->priority);
1518  pcmk__rsc_trace(dependent,
1519  "Applied %s to %s promotion priority (now %s after %s %d)",
1520  colocation->id, dependent->id,
1521  pcmk_readable_score(dependent->priv->priority),
1522  ((score_multiplier == 1)? "adding" : "subtracting"),
1523  colocation->score);
1524 
1525  return priority_delta;
1526 }
1527 
1537 static int
1538 best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1539  pcmk_resource_t *rsc, const char *attr,
1540  const char *value)
1541 {
1542  GHashTable *allowed_nodes_orig = NULL;
1543  GHashTableIter iter;
1544  pcmk_node_t *node = NULL;
1545  int best_score = -PCMK_SCORE_INFINITY;
1546  const char *best_node = NULL;
1547 
1548  if ((colocation != NULL) && (rsc == colocation->dependent)
1549  && pcmk_is_set(colocation->flags, pcmk__coloc_explicit)
1550  && pcmk__is_group(rsc->priv->parent)
1551  && (rsc != rsc->priv->parent->priv->children->data)) {
1552  /* The resource is a user-configured colocation's explicit dependent,
1553  * and a group member other than the first, which means the group's
1554  * location constraint scores were not applied to it (see
1555  * pcmk__group_apply_location()). Explicitly consider those scores now.
1556  *
1557  * @TODO This does leave one suboptimal case: if the group itself or
1558  * another member other than the first is explicitly colocated with
1559  * the same primary, the primary will count the group's location scores
1560  * multiple times. This is much less likely than a single member being
1561  * explicitly colocated, so it's an acceptable tradeoff for now.
1562  */
1563  allowed_nodes_orig = rsc->priv->allowed_nodes;
1564  rsc->priv->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1565  for (GList *loc_iter = rsc->priv->scheduler->priv->location_constraints;
1566  loc_iter != NULL; loc_iter = loc_iter->next) {
1567 
1568  pcmk__location_t *location = loc_iter->data;
1569 
1570  if (location->rsc == rsc->priv->parent) {
1571  rsc->priv->cmds->apply_location(rsc, location);
1572  }
1573  }
1574  }
1575 
1576  // Find best allowed node with matching attribute
1577  g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1578  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1579 
1580  if ((node->assign->score > best_score)
1581  && pcmk__node_available(node, false, false)
1582  && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1583  pcmk__str_casei)) {
1584 
1585  best_score = node->assign->score;
1586  best_node = node->priv->name;
1587  }
1588  }
1589 
1590  if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1591  if (best_node == NULL) {
1592  crm_info("No allowed node for %s matches node attribute %s=%s",
1593  rsc->id, attr, value);
1594  } else {
1595  crm_info("Allowed node %s for %s had best score (%d) "
1596  "of those matching node attribute %s=%s",
1597  best_node, rsc->id, best_score, attr, value);
1598  }
1599  }
1600 
1601  if (allowed_nodes_orig != NULL) {
1602  g_hash_table_destroy(rsc->priv->allowed_nodes);
1603  rsc->priv->allowed_nodes = allowed_nodes_orig;
1604  }
1605  return best_score;
1606 }
1607 
1616 static bool
1617 allowed_on_one(const pcmk_resource_t *rsc)
1618 {
1619  GHashTableIter iter;
1620  pcmk_node_t *allowed_node = NULL;
1621  int allowed_nodes = 0;
1622 
1623  g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1624  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1625  if ((allowed_node->assign->score >= 0) && (++allowed_nodes > 1)) {
1626  pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1627  return false;
1628  }
1629  }
1630  pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1631  ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1632  return (allowed_nodes == 1);
1633 }
1634 
1653 static void
1654 add_node_scores_matching_attr(GHashTable *nodes,
1655  pcmk_resource_t *source_rsc,
1656  const pcmk_resource_t *target_rsc,
1657  const pcmk__colocation_t *colocation,
1658  float factor, bool only_positive)
1659 {
1660  GHashTableIter iter;
1661  pcmk_node_t *node = NULL;
1662  const char *attr = colocation->node_attribute;
1663 
1664  // Iterate through each node
1665  g_hash_table_iter_init(&iter, nodes);
1666  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1667  float delta_f = 0;
1668  int delta = 0;
1669  int score = 0;
1670  int new_score = 0;
1671  const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1672 
1673  score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1674 
1675  if ((factor < 0) && (score < 0)) {
1676  /* If the dependent is anti-colocated, we generally don't want the
1677  * primary to prefer nodes that the dependent avoids. That could
1678  * lead to unnecessary shuffling of the primary when the dependent
1679  * hits its migration threshold somewhere, for example.
1680  *
1681  * However, there are cases when it is desirable. If the dependent
1682  * can't run anywhere but where the primary is, it would be
1683  * worthwhile to move the primary for the sake of keeping the
1684  * dependent active.
1685  *
1686  * We can't know that exactly at this point since we don't know
1687  * where the primary will be assigned, but we can limit considering
1688  * the preference to when the dependent is allowed only on one node.
1689  * This is less than ideal for multiple reasons:
1690  *
1691  * - the dependent could be allowed on more than one node but have
1692  * anti-colocation primaries on each;
1693  * - the dependent could be a clone or bundle with multiple
1694  * instances, and the dependent as a whole is allowed on multiple
1695  * nodes but some instance still can't run
1696  * - the dependent has considered node-specific criteria such as
1697  * location constraints and stickiness by this point, but might
1698  * have other factors that end up disallowing a node
1699  *
1700  * but the alternative is making the primary move when it doesn't
1701  * need to.
1702  *
1703  * We also consider the primary's stickiness and influence, so the
1704  * user has some say in the matter. (This is the configured primary,
1705  * not a particular instance of the primary, but that doesn't matter
1706  * unless stickiness uses a rule to vary by node, and that seems
1707  * acceptable to ignore.)
1708  */
1709  if ((colocation->primary->priv->stickiness >= -score)
1710  || !pcmk__colocation_has_influence(colocation, NULL)
1711  || !allowed_on_one(colocation->dependent)) {
1712  crm_trace("%s: Filtering %d + %f * %d "
1713  "(double negative disallowed)",
1714  pcmk__node_name(node), node->assign->score, factor,
1715  score);
1716  continue;
1717  }
1718  }
1719 
1720  if (node->assign->score == INFINITY_HACK) {
1721  crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1722  pcmk__node_name(node), node->assign->score, factor,
1723  score);
1724  continue;
1725  }
1726 
1727  delta_f = factor * score;
1728 
1729  // Round the number; see http://c-faq.com/fp/round.html
1730  delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1731 
1732  /* Small factors can obliterate the small scores that are often actually
1733  * used in configurations. If the score and factor are nonzero, ensure
1734  * that the result is nonzero as well.
1735  */
1736  if ((delta == 0) && (score != 0)) {
1737  if (factor > 0.0) {
1738  delta = 1;
1739  } else if (factor < 0.0) {
1740  delta = -1;
1741  }
1742  }
1743 
1744  new_score = pcmk__add_scores(delta, node->assign->score);
1745 
1746  if (only_positive && (new_score < 0) && (node->assign->score > 0)) {
1747  crm_trace("%s: Filtering %d + %f * %d = %d "
1748  "(negative disallowed, marking node unusable)",
1749  pcmk__node_name(node), node->assign->score, factor, score,
1750  new_score);
1751  node->assign->score = INFINITY_HACK;
1752  continue;
1753  }
1754 
1755  if (only_positive && (new_score < 0) && (node->assign->score == 0)) {
1756  crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1757  pcmk__node_name(node), node->assign->score, factor, score,
1758  new_score);
1759  continue;
1760  }
1761 
1762  crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1763  node->assign->score, factor, score, new_score);
1764  node->assign->score = new_score;
1765  }
1766 }
1767 
1798 void
1800  const pcmk_resource_t *target_rsc,
1801  const char *log_id,
1802  GHashTable **nodes,
1803  const pcmk__colocation_t *colocation,
1804  float factor, uint32_t flags)
1805 {
1806  GHashTable *work = NULL;
1807 
1808  pcmk__assert((source_rsc != NULL) && (nodes != NULL)
1809  && ((colocation != NULL)
1810  || ((target_rsc == NULL) && (*nodes == NULL))));
1811 
1812  if (log_id == NULL) {
1813  log_id = source_rsc->id;
1814  }
1815 
1816  // Avoid infinite recursion
1817  if (pcmk_is_set(source_rsc->flags, pcmk__rsc_updating_nodes)) {
1818  pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1819  log_id, source_rsc->id);
1820  return;
1821  }
1823 
1824  if (*nodes == NULL) {
1825  work = pcmk__copy_node_table(source_rsc->priv->allowed_nodes);
1826  target_rsc = source_rsc;
1827  } else {
1829 
1830  pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1831  log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1832  work = pcmk__copy_node_table(*nodes);
1833  add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1834  factor, pos);
1835  }
1836 
1837  if (work == NULL) {
1839  return;
1840  }
1841 
1842  if (pcmk__any_node_available(work)) {
1843  GList *colocations = NULL;
1844 
1846  colocations = pcmk__this_with_colocations(source_rsc);
1847  pcmk__rsc_trace(source_rsc,
1848  "Checking additional %d optional '%s with' "
1849  "constraints",
1850  g_list_length(colocations), source_rsc->id);
1851  } else {
1852  colocations = pcmk__with_this_colocations(source_rsc);
1853  pcmk__rsc_trace(source_rsc,
1854  "Checking additional %d optional 'with %s' "
1855  "constraints",
1856  g_list_length(colocations), source_rsc->id);
1857  }
1859 
1860  for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1861  pcmk__colocation_t *constraint = iter->data;
1862 
1863  pcmk_resource_t *other = NULL;
1864  float other_factor = factor * constraint->score
1865  / (float) PCMK_SCORE_INFINITY;
1866 
1868  other = constraint->primary;
1869  } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1870  continue;
1871  } else {
1872  other = constraint->dependent;
1873  }
1874 
1875  pcmk__rsc_trace(source_rsc,
1876  "Optionally merging score of '%s' constraint "
1877  "(%s with %s)",
1878  constraint->id, constraint->dependent->id,
1879  constraint->primary->id);
1880  other->priv->cmds->add_colocated_node_scores(other, target_rsc,
1881  log_id, &work,
1882  constraint,
1883  other_factor, flags);
1884  pe__show_node_scores(true, NULL, log_id, work,
1885  source_rsc->priv->scheduler);
1886  }
1887  g_list_free(colocations);
1888 
1890  pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1891  log_id, source_rsc->id);
1892  g_hash_table_destroy(work);
1894  return;
1895  }
1896 
1897 
1899  pcmk_node_t *node = NULL;
1900  GHashTableIter iter;
1901 
1902  g_hash_table_iter_init(&iter, work);
1903  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1904  if (node->assign->score == INFINITY_HACK) {
1905  node->assign->score = 1;
1906  }
1907  }
1908  }
1909 
1910  if (*nodes != NULL) {
1911  g_hash_table_destroy(*nodes);
1912  }
1913  *nodes = work;
1914 
1916 }
1917 
1925 void
1926 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1927 {
1928  pcmk__colocation_t *colocation = data;
1929  pcmk_resource_t *primary = user_data;
1930 
1931  pcmk_resource_t *dependent = colocation->dependent;
1932  const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1933  uint32_t flags = pcmk__coloc_select_active;
1934 
1935  if (!pcmk__colocation_has_influence(colocation, NULL)) {
1936  return;
1937  }
1938  if (pcmk__is_clone(primary)) {
1940  }
1941  pcmk__rsc_trace(primary,
1942  "%s: Incorporating attenuated %s assignment scores due "
1943  "to colocation %s",
1944  primary->id, dependent->id, colocation->id);
1945  dependent->priv->cmds->add_colocated_node_scores(dependent, primary,
1946  dependent->id,
1947  &(primary->priv->allowed_nodes),
1948  colocation, factor, flags);
1949 }
1950 
1968 void
1970  const pcmk_resource_t *primary,
1971  const pcmk__colocation_t *colocation,
1972  const GList *primary_nodes, bool merge_scores)
1973 {
1974  GHashTableIter iter;
1975  pcmk_node_t *dependent_node = NULL;
1976 
1977  pcmk__assert((dependent != NULL) && (primary != NULL)
1978  && (colocation != NULL));
1979 
1980  g_hash_table_iter_init(&iter, dependent->priv->allowed_nodes);
1981  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1982  const pcmk_node_t *primary_node = NULL;
1983 
1984  primary_node = pe_find_node_id(primary_nodes,
1985  dependent_node->priv->id);
1986  if (primary_node == NULL) {
1987  dependent_node->assign->score = -PCMK_SCORE_INFINITY;
1988  pcmk__rsc_trace(dependent,
1989  "Banning %s from %s (no primary instance) for %s",
1990  dependent->id, pcmk__node_name(dependent_node),
1991  colocation->id);
1992 
1993  } else if (merge_scores) {
1994  dependent_node->assign->score =
1995  pcmk__add_scores(dependent_node->assign->score,
1996  primary_node->assign->score);
1997  pcmk__rsc_trace(dependent,
1998  "Added %s's score %s to %s's score for %s (now %d) "
1999  "for colocation %s",
2000  primary->id,
2001  pcmk_readable_score(primary_node->assign->score),
2002  dependent->id, pcmk__node_name(dependent_node),
2003  dependent_node->assign->score, colocation->id);
2004  }
2005  }
2006 }
2007 
2018 GList *
2020 {
2021  GList *list = NULL;
2022 
2023  rsc->priv->cmds->with_this_colocations(rsc, rsc, &list);
2024  return list;
2025 }
2026 
2037 GList *
2039 {
2040  GList *list = NULL;
2041 
2042  rsc->priv->cmds->this_with_colocations(rsc, rsc, &list);
2043  return list;
2044 }
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition: complex.c:1043
bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
xmlNode * pcmk__xe_resolve_idref(xmlNode *xml, xmlNode *search)
Definition: xml_idref.c:85
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)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:805
A dumping ground.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition: roles.c:23
char data[0]
Definition: cpg.c:58
#define pcmk__if_tracing(if_action, else_action)
pcmk__coloc_affects
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition: complex.c:1017
pcmk_resource_t * parent
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:102
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...)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET
Definition: options.h:85
#define PCMK_XE_RSC_COLOCATION
Definition: xml_names.h:185
#define PCMK_XE_RESOURCE_REF
Definition: xml_names.h:177
#define pcmk__rsc_info(rsc, fmt, args...)
#define PCMK_XA_WITH_RSC_ROLE
Definition: xml_names.h:453
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__config_err(fmt...)
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1568
Promoted.
Definition: roles.h:39
#define PCMK_XA_RSC
Definition: xml_names.h:388
void(* with_this_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
enum pcmk__coloc_affects pcmk__colocation_affects(const pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool preview)
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
#define PCMK__XA_ORDERING
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml_element.c:339
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition: xml_element.c:1538
void(* this_with_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
#define PCMK__VALUE_GROUP
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:32
void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
void pcmk__block_colocation_dependents(pcmk_action_t *action)
Scheduler API.
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: xml_element.c:1501
#define PCMK_VALUE_HOST
Definition: options.h:161
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
pcmk_scheduler_t * scheduler
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:128
#define pcmk__clear_action_flags(action, flags_to_clear)
Utility functions.
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:510
enum pcmk__rsc_variant variant
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
#define CRM_ATTR_UNAME
Definition: crm.h:92
#define crm_trace(fmt, args...)
Definition: logging.h:372
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)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
void(* apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location)
#define PCMK_ACTION_START
Definition: actions.h:63
pcmk__resource_private_t * priv
Definition: resources.h:61
int pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
Unpromoted.
Definition: roles.h:38
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
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_spec, const char *primary_role_spec, uint32_t flags)
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:66
#define PCMK_XA_INFLUENCE
Definition: xml_names.h:306
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
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 PCMK_XA_ID
Definition: xml_names.h:301
G_GNUC_INTERNAL int pcmk__parse_constraint_role(const char *id, const char *role_spec, enum rsc_role_e *role)
pcmk_resource_t * rsc
#define PCMK_XA_SCORE
Definition: xml_names.h:396
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:159
#define pcmk__warn_once(wo_flag, fmt...)
uint32_t id
Definition: cpg.c:48
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
Definition: xml_element.c:106
pcmk_resource_t * primary
#define pcmk__assert(expr)
const char * target
Definition: pcmk_fence.c:31
#define PCMK_XA_NODE_ATTRIBUTE
Definition: xml_names.h:336
Cluster status and scheduling.
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:1416
pcmk_scheduler_t * scheduler
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk__idref_t **tag)
GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:178
xmlNode * input
Definition: scheduler.h:81
const char * pcmk__node_attr(const pcmk_node_t *node, const char *name, const char *target, enum pcmk__rsc_node node_type)
Definition: attrs.c:114
#define INFINITY_HACK
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition: scores.c:34
pcmk_resource_t * dependent
#define PCMK_XA_RSC_ROLE
Definition: xml_names.h:390
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:164
unsigned long long flags
Definition: resources.h:69
const char * node_attribute
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:498
#define PCMK_XA_SYMMETRICAL
Definition: xml_names.h:415
#define crm_log_xml_trace(xml, text)
Definition: logging.h:380
pcmk_resource_t * pe__bundled_resource(const pcmk_resource_t *rsc)
Definition: bundle.c:110
void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
#define PCMK_XA_WITH_RSC
Definition: xml_names.h:452
Resource role is unknown.
Definition: roles.h:35
void pcmk__add_dependent_scores(gpointer data, gpointer user_data)
Location constraint object.
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:257
#define CRM_ATTR_ID
Definition: crm.h:93
#define PCMK_XA_SEQUENTIAL
Definition: xml_names.h:398
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
#define PCMK_XA_ROLE
Definition: xml_names.h:387
#define crm_info(fmt, args...)
Definition: logging.h:367
const char * pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr, const pcmk_resource_t *rsc)
uint64_t flags
Definition: remote.c:211
const pcmk__assignment_methods_t * cmds
GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
struct pcmk__node_assignment * assign
Definition: nodes.h:79