pacemaker  2.1.9-49aab99839
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>
17 #include <crm/pengine/status.h>
18 #include <pacemaker-internal.h>
19 
20 #include "crm/common/util.h"
22 #include "crm/common/xml.h"
23 #include "libpacemaker_private.h"
24 
25 // Used to temporarily mark a node as unusable
26 #define INFINITY_HACK (PCMK_SCORE_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  pcmk__assert(colocation1->primary != NULL);
69  } else {
70  rsc1 = colocation1->primary;
71  rsc2 = colocation2->primary;
72  pcmk__assert(colocation1->dependent != NULL);
73  }
74  pcmk__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  */
95  if (pcmk__is_clone(rsc1)) {
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  pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
177 
178  pcmk__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  pcmk__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  pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
237 
238  pcmk__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  pcmk__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  pcmk__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 = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t));
371 
372  if (pcmk__str_eq(dependent_role, PCMK_ROLE_STARTED,
374  dependent_role = PCMK__ROLE_UNKNOWN;
375  }
376 
377  if (pcmk__str_eq(primary_role, PCMK_ROLE_STARTED,
379  primary_role = PCMK__ROLE_UNKNOWN;
380  }
381 
382  new_con->id = id;
383  new_con->dependent = dependent;
384  new_con->primary = primary;
385  new_con->score = score;
386  new_con->dependent_role = pcmk_parse_role(dependent_role);
387  new_con->primary_role = pcmk_parse_role(primary_role);
388  new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
389  new_con->flags = flags;
390 
391  pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent);
392  pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary);
393 
394  dependent->cluster->colocation_constraints = g_list_prepend(
395  dependent->cluster->colocation_constraints, new_con);
396 
397  if (score <= -PCMK_SCORE_INFINITY) {
398  anti_colocation_order(dependent, new_con->dependent_role, primary,
399  new_con->primary_role);
400  anti_colocation_order(primary, new_con->primary_role, dependent,
401  new_con->dependent_role);
402  }
403 }
404 
417 static uint32_t
418 unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
419  const char *influence_s)
420 {
421  if (influence_s != NULL) {
422  int influence_i = 0;
423 
424  if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
425  pcmk__config_err("Constraint '%s' has invalid value for "
426  PCMK_XA_INFLUENCE " (using default)",
427  coloc_id);
428  } else {
429  return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
430  }
431  }
432  if (pcmk_is_set(rsc->flags, pcmk_rsc_critical)) {
433  return pcmk__coloc_influence;
434  }
435  return pcmk__coloc_none;
436 }
437 
438 static void
439 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
440  const char *influence_s, pcmk_scheduler_t *scheduler)
441 {
442  xmlNode *xml_rsc = NULL;
443  pcmk_resource_t *other = NULL;
444  pcmk_resource_t *resource = NULL;
445  const char *set_id = pcmk__xe_id(set);
446  const char *role = crm_element_value(set, PCMK_XA_ROLE);
447  bool with_previous = false;
448  int local_score = score;
449  bool sequential = false;
450  uint32_t flags = pcmk__coloc_none;
451  const char *xml_rsc_id = NULL;
452  const char *score_s = crm_element_value(set, PCMK_XA_SCORE);
453 
454  if (score_s) {
455  local_score = char2score(score_s);
456  }
457  if (local_score == 0) {
458  crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
459  coloc_id, set_id);
460  return;
461  }
462 
463  /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether
464  * resources in a positive-score set are colocated with the previous or next
465  * resource.
466  */
467  if (pcmk__str_eq(crm_element_value(set, PCMK__XA_ORDERING),
470  with_previous = true;
471  } else {
473  "Support for '" PCMK__XA_ORDERING "' other than"
475  " (such as %s) is deprecated and will be removed in a"
476  " future release",
477  set_id);
478  }
479 
481  &sequential) == pcmk_rc_ok)
482  && !sequential) {
483  return;
484  }
485 
486  if (local_score > 0) {
487  for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
488  NULL);
489  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
490 
491  xml_rsc_id = pcmk__xe_id(xml_rsc);
493  xml_rsc_id);
494  if (resource == NULL) {
495  // Should be possible only with validation disabled
496  pcmk__config_err("Ignoring %s and later resources in set %s: "
497  "No such resource", xml_rsc_id, set_id);
498  return;
499  }
500  if (other != NULL) {
502  | unpack_influence(coloc_id, resource, influence_s);
503  if (with_previous) {
504  pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
505  resource->id, other->id, set_id);
506  pcmk__new_colocation(set_id, NULL, local_score, resource,
507  other, role, role, flags);
508  } else {
509  pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
510  other->id, resource->id, set_id);
511  pcmk__new_colocation(set_id, NULL, local_score, other,
512  resource, role, role, flags);
513  }
514  }
515  other = resource;
516  }
517 
518  } else {
519  /* Anti-colocating with every prior resource is
520  * the only way to ensure the intuitive result
521  * (i.e. that no one in the set can run with anyone else in the set)
522  */
523 
524  for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
525  NULL);
526  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
527 
528  xmlNode *xml_rsc_with = NULL;
529 
530  xml_rsc_id = pcmk__xe_id(xml_rsc);
532  xml_rsc_id);
533  if (resource == NULL) {
534  // Should be possible only with validation disabled
535  pcmk__config_err("Ignoring %s and later resources in set %s: "
536  "No such resource", xml_rsc_id, set_id);
537  return;
538  }
540  | unpack_influence(coloc_id, resource, influence_s);
541  for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
542  NULL, NULL);
543  xml_rsc_with != NULL;
544  xml_rsc_with = pcmk__xe_next_same(xml_rsc_with)) {
545 
546  xml_rsc_id = pcmk__xe_id(xml_rsc_with);
547  if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
548  break;
549  }
551  xml_rsc_id);
552  pcmk__assert(other != NULL); // We already processed it
553  pcmk__new_colocation(set_id, NULL, local_score,
554  resource, other, role, role, flags);
555  }
556  }
557  }
558 }
559 
572 static void
573 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
574  int score, const char *influence_s,
576 {
577  xmlNode *xml_rsc = NULL;
578  pcmk_resource_t *rsc_1 = NULL;
579  pcmk_resource_t *rsc_2 = NULL;
580 
581  const char *xml_rsc_id = NULL;
582  const char *role_1 = crm_element_value(set1, PCMK_XA_ROLE);
583  const char *role_2 = crm_element_value(set2, PCMK_XA_ROLE);
584 
585  int rc = pcmk_rc_ok;
586  bool sequential = false;
587  uint32_t flags = pcmk__coloc_none;
588 
589  if (score == 0) {
590  crm_trace("Ignoring colocation '%s' between sets %s and %s "
591  "because score is 0",
592  id, pcmk__xe_id(set1), pcmk__xe_id(set2));
593  return;
594  }
595 
596  rc = pcmk__xe_get_bool_attr(set1, PCMK_XA_SEQUENTIAL, &sequential);
597  if ((rc != pcmk_rc_ok) || sequential) {
598  // Get the first one
599  xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL);
600  if (xml_rsc != NULL) {
601  xml_rsc_id = pcmk__xe_id(xml_rsc);
603  xml_rsc_id);
604  if (rsc_1 == NULL) {
605  // Should be possible only with validation disabled
606  pcmk__config_err("Ignoring colocation of set %s with set %s "
607  "because first resource %s not found",
608  pcmk__xe_id(set1), pcmk__xe_id(set2),
609  xml_rsc_id);
610  return;
611  }
612  }
613  }
614 
615  rc = pcmk__xe_get_bool_attr(set2, PCMK_XA_SEQUENTIAL, &sequential);
616  if ((rc != pcmk_rc_ok) || sequential) {
617  // Get the last one
618  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
619  NULL);
620  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
621 
622  xml_rsc_id = pcmk__xe_id(xml_rsc);
623  }
625  xml_rsc_id);
626  if (rsc_2 == NULL) {
627  // Should be possible only with validation disabled
628  pcmk__config_err("Ignoring colocation of set %s with set %s "
629  "because last resource %s not found",
630  pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id);
631  return;
632  }
633  }
634 
635  if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
636  flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
637  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
638  flags);
639 
640  } else if (rsc_1 != NULL) { // Only set1 is sequential
641  flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
642  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
643  NULL);
644  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
645 
646  xml_rsc_id = pcmk__xe_id(xml_rsc);
648  xml_rsc_id);
649  if (rsc_2 == NULL) {
650  // Should be possible only with validation disabled
651  pcmk__config_err("Ignoring set %s colocation with resource %s "
652  "in set %s: No such resource",
653  pcmk__xe_id(set1), xml_rsc_id,
654  pcmk__xe_id(set2));
655  continue;
656  }
657  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
658  role_2, flags);
659  }
660 
661  } else if (rsc_2 != NULL) { // Only set2 is sequential
662  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
663  NULL);
664  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
665 
666  xml_rsc_id = pcmk__xe_id(xml_rsc);
668  xml_rsc_id);
669  if (rsc_1 == NULL) {
670  // Should be possible only with validation disabled
671  pcmk__config_err("Ignoring colocation of set %s resource %s "
672  "with set %s: No such resource",
673  pcmk__xe_id(set1), xml_rsc_id,
674  pcmk__xe_id(set2));
675  continue;
676  }
678  | unpack_influence(id, rsc_1, influence_s);
679  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
680  role_2, flags);
681  }
682 
683  } else { // Neither set is sequential
684  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
685  NULL);
686  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
687 
688  xmlNode *xml_rsc_2 = NULL;
689 
690  xml_rsc_id = pcmk__xe_id(xml_rsc);
692  xml_rsc_id);
693  if (rsc_1 == NULL) {
694  // Should be possible only with validation disabled
695  pcmk__config_err("Ignoring colocation of set %s resource %s "
696  "with set %s: No such resource",
697  pcmk__xe_id(set1), xml_rsc_id,
698  pcmk__xe_id(set2));
699  continue;
700  }
701 
703  | unpack_influence(id, rsc_1, influence_s);
704  for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
705  NULL, NULL);
706  xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
707 
708  xml_rsc_id = pcmk__xe_id(xml_rsc_2);
710  xml_rsc_id);
711  if (rsc_2 == NULL) {
712  // Should be possible only with validation disabled
713  pcmk__config_err("Ignoring colocation of set %s resource "
714  "%s with set %s resource %s: No such "
715  "resource",
716  pcmk__xe_id(set1), pcmk__xe_id(xml_rsc),
717  pcmk__xe_id(set2), xml_rsc_id);
718  continue;
719  }
720  pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
721  role_1, role_2, flags);
722  }
723  }
724  }
725 }
726 
727 static void
728 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
729  const char *influence_s, pcmk_scheduler_t *scheduler)
730 {
731  int score_i = 0;
732  uint32_t flags = pcmk__coloc_none;
733 
734  const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE);
735  const char *dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
736  const char *primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
737  const char *dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
738  const char *primary_role = crm_element_value(xml_obj,
740  const char *attr = crm_element_value(xml_obj, PCMK_XA_NODE_ATTRIBUTE);
741 
742  const char *primary_instance = NULL;
743  const char *dependent_instance = NULL;
744  pcmk_resource_t *primary = NULL;
745  pcmk_resource_t *dependent = NULL;
746 
747  primary = pcmk__find_constraint_resource(scheduler->resources, primary_id);
749  dependent_id);
750 
751  // @COMPAT: Deprecated since 2.1.5
752  primary_instance = crm_element_value(xml_obj, PCMK__XA_WITH_RSC_INSTANCE);
753  dependent_instance = crm_element_value(xml_obj, PCMK__XA_RSC_INSTANCE);
754  if (dependent_instance != NULL) {
756  "Support for " PCMK__XA_RSC_INSTANCE " is deprecated "
757  "and will be removed in a future release");
758  }
759  if (primary_instance != NULL) {
761  "Support for " PCMK__XA_WITH_RSC_INSTANCE " is "
762  "deprecated and will be removed in a future release");
763  }
764 
765  if (dependent == NULL) {
766  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
767  "does not exist", id, dependent_id);
768  return;
769 
770  } else if (primary == NULL) {
771  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
772  "does not exist", id, primary_id);
773  return;
774 
775  } else if ((dependent_instance != NULL) && !pcmk__is_clone(dependent)) {
776  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
777  "is not a clone but instance '%s' was requested",
778  id, dependent_id, dependent_instance);
779  return;
780 
781  } else if ((primary_instance != NULL) && !pcmk__is_clone(primary)) {
782  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
783  "is not a clone but instance '%s' was requested",
784  id, primary_id, primary_instance);
785  return;
786  }
787 
788  if (dependent_instance != NULL) {
789  dependent = find_clone_instance(dependent, dependent_instance);
790  if (dependent == NULL) {
791  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
792  "does not have an instance '%s'",
793  id, dependent_id, dependent_instance);
794  return;
795  }
796  }
797 
798  if (primary_instance != NULL) {
799  primary = find_clone_instance(primary, primary_instance);
800  if (primary == NULL) {
801  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
802  "does not have an instance '%s'",
803  id, primary_id, primary_instance);
804  return;
805  }
806  }
807 
809  pcmk__config_warn("The colocation constraint "
810  "'" PCMK_XA_SYMMETRICAL "' attribute has been "
811  "removed");
812  }
813 
814  if (score) {
815  score_i = char2score(score);
816  }
817 
818  flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
819  pcmk__new_colocation(id, attr, score_i, dependent, primary,
820  dependent_role, primary_role, flags);
821 }
822 
823 // \return Standard Pacemaker return code
824 static int
825 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
827 {
828  const char *id = NULL;
829  const char *dependent_id = NULL;
830  const char *primary_id = NULL;
831  const char *dependent_role = NULL;
832  const char *primary_role = NULL;
833 
834  pcmk_resource_t *dependent = NULL;
835  pcmk_resource_t *primary = NULL;
836 
837  pcmk_tag_t *dependent_tag = NULL;
838  pcmk_tag_t *primary_tag = NULL;
839 
840  xmlNode *dependent_set = NULL;
841  xmlNode *primary_set = NULL;
842  bool any_sets = false;
843 
844  *expanded_xml = NULL;
845 
846  CRM_CHECK(xml_obj != NULL, return EINVAL);
847 
848  id = pcmk__xe_id(xml_obj);
849  if (id == NULL) {
850  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
851  xml_obj->name);
852  return pcmk_rc_unpack_error;
853  }
854 
855  // Check whether there are any resource sets with template or tag references
856  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
857  if (*expanded_xml != NULL) {
858  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
859  return pcmk_rc_ok;
860  }
861 
862  dependent_id = crm_element_value(xml_obj, PCMK_XA_RSC);
863  primary_id = crm_element_value(xml_obj, PCMK_XA_WITH_RSC);
864  if ((dependent_id == NULL) || (primary_id == NULL)) {
865  return pcmk_rc_ok;
866  }
867 
868  if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
869  &dependent_tag)) {
870  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
871  "valid resource or tag", id, dependent_id);
872  return pcmk_rc_unpack_error;
873  }
874 
875  if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
876  &primary_tag)) {
877  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
878  "valid resource or tag", id, primary_id);
879  return pcmk_rc_unpack_error;
880  }
881 
882  if ((dependent != NULL) && (primary != NULL)) {
883  /* Neither side references any template/tag. */
884  return pcmk_rc_ok;
885  }
886 
887  if ((dependent_tag != NULL) && (primary_tag != NULL)) {
888  // A colocation constraint between two templates/tags makes no sense
889  pcmk__config_err("Ignoring constraint '%s' because two templates or "
890  "tags cannot be colocated", id);
891  return pcmk_rc_unpack_error;
892  }
893 
894  dependent_role = crm_element_value(xml_obj, PCMK_XA_RSC_ROLE);
895  primary_role = crm_element_value(xml_obj, PCMK_XA_WITH_RSC_ROLE);
896 
897  *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
898 
899  /* Convert dependent's template/tag reference into constraint
900  * PCMK_XE_RESOURCE_SET
901  */
902  if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true,
903  scheduler)) {
904  free_xml(*expanded_xml);
905  *expanded_xml = NULL;
906  return pcmk_rc_unpack_error;
907  }
908 
909  if (dependent_set != NULL) {
910  if (dependent_role != NULL) {
911  /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
912  * PCMK_XA_ROLE
913  */
914  crm_xml_add(dependent_set, PCMK_XA_ROLE, dependent_role);
915  pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
916  }
917  any_sets = true;
918  }
919 
920  /* Convert primary's template/tag reference into constraint
921  * PCMK_XE_RESOURCE_SET
922  */
923  if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true,
924  scheduler)) {
925  free_xml(*expanded_xml);
926  *expanded_xml = NULL;
927  return pcmk_rc_unpack_error;
928  }
929 
930  if (primary_set != NULL) {
931  if (primary_role != NULL) {
932  /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
933  * PCMK_XA_ROLE
934  */
935  crm_xml_add(primary_set, PCMK_XA_ROLE, primary_role);
937  }
938  any_sets = true;
939  }
940 
941  if (any_sets) {
942  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
943  } else {
944  free_xml(*expanded_xml);
945  *expanded_xml = NULL;
946  }
947 
948  return pcmk_rc_ok;
949 }
950 
958 void
960 {
961  int score_i = 0;
962  xmlNode *set = NULL;
963  xmlNode *last = NULL;
964 
965  xmlNode *orig_xml = NULL;
966  xmlNode *expanded_xml = NULL;
967 
968  const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
969  const char *score = NULL;
970  const char *influence_s = NULL;
971 
972  if (pcmk__str_empty(id)) {
974  " without " CRM_ATTR_ID);
975  return;
976  }
977 
978  if (unpack_colocation_tags(xml_obj, &expanded_xml,
979  scheduler) != pcmk_rc_ok) {
980  return;
981  }
982  if (expanded_xml != NULL) {
983  orig_xml = xml_obj;
984  xml_obj = expanded_xml;
985  }
986 
987  score = crm_element_value(xml_obj, PCMK_XA_SCORE);
988  if (score != NULL) {
989  score_i = char2score(score);
990  }
991  influence_s = crm_element_value(xml_obj, PCMK_XA_INFLUENCE);
992 
993  for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
994  set != NULL; set = pcmk__xe_next_same(set)) {
995 
996  set = expand_idref(set, scheduler->input);
997  if (set == NULL) { // Configuration error, message already logged
998  if (expanded_xml != NULL) {
999  free_xml(expanded_xml);
1000  }
1001  return;
1002  }
1003 
1004  if (pcmk__str_empty(pcmk__xe_id(set))) {
1006  " without " CRM_ATTR_ID);
1007  continue;
1008  }
1009  unpack_colocation_set(set, score_i, id, influence_s, scheduler);
1010 
1011  if (last != NULL) {
1012  colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
1013  }
1014  last = set;
1015  }
1016 
1017  if (expanded_xml) {
1018  free_xml(expanded_xml);
1019  xml_obj = orig_xml;
1020  }
1021 
1022  if (last == NULL) {
1023  unpack_simple_colocation(xml_obj, id, influence_s, scheduler);
1024  }
1025 }
1026 
1039 bool
1041  const pcmk_resource_t *rsc)
1042 {
1043  if (rsc == NULL) {
1044  rsc = colocation->primary;
1045  }
1046 
1047  /* A bundle replica colocates its remote connection with its container,
1048  * using a finite score so that the container can run on Pacemaker Remote
1049  * nodes.
1050  *
1051  * Moving a connection is lightweight and does not interrupt the service,
1052  * while moving a container is heavyweight and does interrupt the service,
1053  * so don't move a clean, active container based solely on the preferences
1054  * of its connection.
1055  *
1056  * This also avoids problematic scenarios where two containers want to
1057  * perpetually swap places.
1058  */
1059  if (pcmk_is_set(colocation->dependent->flags,
1061  && !pcmk_is_set(rsc->flags, pcmk_rsc_failed)
1062  && pcmk__list_of_1(rsc->running_on)) {
1063  return false;
1064  }
1065 
1066  /* The dependent in a colocation influences the primary's location
1067  * if the PCMK_XA_INFLUENCE option is true or the primary is not yet active.
1068  */
1069  return pcmk_is_set(colocation->flags, pcmk__coloc_influence)
1070  || (rsc->running_on == NULL);
1071 }
1072 
1081 static void
1082 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1083  const pcmk_resource_t *reason)
1084 {
1085  GList *iter = NULL;
1086  char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1087 
1088  for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1089  pcmk_action_t *action = iter->data;
1090 
1092  && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1093 
1095  pe_action_set_reason(action, reason_text, false);
1098  }
1099  }
1100 
1101  // If parent resource can't perform an action, neither can any children
1102  for (iter = rsc->children; iter != NULL; iter = iter->next) {
1103  mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1104  }
1105  free(reason_text);
1106 }
1107 
1118 void
1120 {
1121  GList *iter = NULL;
1122  GList *colocations = NULL;
1123  pcmk_resource_t *rsc = NULL;
1124  bool is_start = false;
1125 
1126  if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
1127  return; // Only unrunnable actions block dependents
1128  }
1129 
1130  is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1131  if (!is_start
1132  && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1133  return; // Only unrunnable starts and promotes block dependents
1134  }
1135 
1136  pcmk__assert(action->rsc != NULL); // Start and promote are resource actions
1137 
1138  /* If this resource is part of a collective resource, dependents are blocked
1139  * only if all instances of the collective are unrunnable, so check the
1140  * collective resource.
1141  */
1142  rsc = uber_parent(action->rsc);
1143  if (rsc->parent != NULL) {
1144  rsc = rsc->parent; // Bundle
1145  }
1146 
1147  // Colocation fails only if entire primary can't reach desired role
1148  for (iter = rsc->children; iter != NULL; iter = iter->next) {
1149  pcmk_resource_t *child = iter->data;
1150  pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1151  action->task, NULL);
1152 
1153  if ((child_action == NULL)
1154  || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1155  crm_trace("Not blocking %s colocation dependents because "
1156  "at least %s has runnable %s",
1157  rsc->id, child->id, action->task);
1158  return; // At least one child can reach desired role
1159  }
1160  }
1161 
1162  crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1163  rsc->id, action->rsc->id, action->task);
1164 
1165  // Check each colocation where this resource is primary
1166  colocations = pcmk__with_this_colocations(rsc);
1167  for (iter = colocations; iter != NULL; iter = iter->next) {
1168  pcmk__colocation_t *colocation = iter->data;
1169 
1170  if (colocation->score < PCMK_SCORE_INFINITY) {
1171  continue; // Only mandatory colocations block dependent
1172  }
1173 
1174  /* If the primary can't start, the dependent can't reach its colocated
1175  * role, regardless of what the primary or dependent colocation role is.
1176  *
1177  * If the primary can't be promoted, the dependent can't reach its
1178  * colocated role if the primary's colocation role is promoted.
1179  */
1180  if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1181  continue;
1182  }
1183 
1184  // Block the dependent from reaching its colocated role
1185  if (colocation->dependent_role == pcmk_role_promoted) {
1186  mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1187  action->rsc);
1188  } else {
1189  mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1190  action->rsc);
1191  }
1192  }
1193  g_list_free(colocations);
1194 }
1195 
1214 static const pcmk_resource_t *
1215 get_resource_for_role(const pcmk_resource_t *rsc)
1216 {
1218  const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1219 
1220  if (child != NULL) {
1221  return child;
1222  }
1223  }
1224  return rsc;
1225 }
1226 
1247  const pcmk_resource_t *primary,
1248  const pcmk__colocation_t *colocation, bool preview)
1249 {
1250  const pcmk_resource_t *dependent_role_rsc = NULL;
1251  const pcmk_resource_t *primary_role_rsc = NULL;
1252 
1253  pcmk__assert((dependent != NULL) && (primary != NULL)
1254  && (colocation != NULL));
1255 
1256  if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1257  // Primary resource has not been assigned yet, so we can't do anything
1259  }
1260 
1261  dependent_role_rsc = get_resource_for_role(dependent);
1262  primary_role_rsc = get_resource_for_role(primary);
1263 
1264  if ((colocation->dependent_role >= pcmk_role_unpromoted)
1265  && (dependent_role_rsc->parent != NULL)
1266  && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1267  && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1268 
1269  /* This is a colocation by role, and the dependent is a promotable clone
1270  * that has already been assigned, so the colocation should now affect
1271  * the role.
1272  */
1273  return pcmk__coloc_affects_role;
1274  }
1275 
1276  if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1277  /* The dependent resource has already been through assignment, so the
1278  * constraint no longer has any effect. Log an error if a mandatory
1279  * colocation constraint has been violated.
1280  */
1281 
1282  const pcmk_node_t *primary_node = primary->allocated_to;
1283 
1284  if (dependent->allocated_to == NULL) {
1285  crm_trace("Skipping colocation '%s': %s will not run anywhere",
1286  colocation->id, dependent->id);
1287 
1288  } else if (colocation->score >= PCMK_SCORE_INFINITY) {
1289  // Dependent resource must colocate with primary resource
1290 
1291  if (!pcmk__same_node(primary_node, dependent->allocated_to)) {
1292  pcmk__sched_err("%s must be colocated with %s but is not "
1293  "(%s vs. %s)",
1294  dependent->id, primary->id,
1295  pcmk__node_name(dependent->allocated_to),
1296  pcmk__node_name(primary_node));
1297  }
1298 
1299  } else if (colocation->score <= -PCMK_SCORE_INFINITY) {
1300  // Dependent resource must anti-colocate with primary resource
1301 
1302  if (pcmk__same_node(dependent->allocated_to, primary_node)) {
1303  pcmk__sched_err("%s and %s must be anti-colocated but are "
1304  "assigned to the same node (%s)",
1305  dependent->id, primary->id,
1306  pcmk__node_name(primary_node));
1307  }
1308  }
1310  }
1311 
1312  if ((colocation->dependent_role != pcmk_role_unknown)
1313  && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1314  crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1315 
1316  "but %s next role is %s",
1317  ((colocation->score < 0)? "anti-" : ""),
1318  colocation->id, pcmk_role_text(colocation->dependent_role),
1319  dependent_role_rsc->id,
1320  pcmk_role_text(dependent_role_rsc->next_role));
1322  }
1323 
1324  if ((colocation->primary_role != pcmk_role_unknown)
1325  && (colocation->primary_role != primary_role_rsc->next_role)) {
1326  crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1327  "but %s next role is %s",
1328  ((colocation->score < 0)? "anti-" : ""),
1329  colocation->id, pcmk_role_text(colocation->primary_role),
1330  primary_role_rsc->id,
1331  pcmk_role_text(primary_role_rsc->next_role));
1333  }
1334 
1336 }
1337 
1349 void
1351  const pcmk_resource_t *primary,
1352  const pcmk__colocation_t *colocation)
1353 {
1354  const char *attr = colocation->node_attribute;
1355  const char *value = NULL;
1356  GHashTable *work = NULL;
1357  GHashTableIter iter;
1358  pcmk_node_t *node = NULL;
1359 
1360  if (primary->allocated_to != NULL) {
1361  value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1362  primary);
1363 
1364  } else if (colocation->score < 0) {
1365  // Nothing to do (anti-colocation with something that is not running)
1366  return;
1367  }
1368 
1369  work = pcmk__copy_node_table(dependent->allowed_nodes);
1370 
1371  g_hash_table_iter_init(&iter, work);
1372  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1373  if (primary->allocated_to == NULL) {
1374  node->weight = pcmk__add_scores(-colocation->score, node->weight);
1375  pcmk__rsc_trace(dependent,
1376  "Applied %s to %s score on %s (now %s after "
1377  "subtracting %s because primary %s inactive)",
1378  colocation->id, dependent->id,
1379  pcmk__node_name(node),
1380  pcmk_readable_score(node->weight),
1381  pcmk_readable_score(colocation->score), primary->id);
1382  continue;
1383  }
1384 
1385  if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1386  value, pcmk__str_casei)) {
1387 
1388  /* Add colocation score only if optional (or minus infinity). A
1389  * mandatory colocation is a requirement rather than a preference,
1390  * so we don't need to consider it for relative assignment purposes.
1391  * The resource will simply be forbidden from running on the node if
1392  * the primary isn't active there (via the condition above).
1393  */
1394  if (colocation->score < PCMK_SCORE_INFINITY) {
1395  node->weight = pcmk__add_scores(colocation->score,
1396  node->weight);
1397  pcmk__rsc_trace(dependent,
1398  "Applied %s to %s score on %s (now %s after "
1399  "adding %s)",
1400  colocation->id, dependent->id,
1401  pcmk__node_name(node),
1402  pcmk_readable_score(node->weight),
1403  pcmk_readable_score(colocation->score));
1404  }
1405  continue;
1406  }
1407 
1408  if (colocation->score >= PCMK_SCORE_INFINITY) {
1409  /* Only mandatory colocations are relevant when the colocation
1410  * attribute doesn't match, because an attribute not matching is not
1411  * a negative preference -- the colocation is simply relevant only
1412  * where it matches.
1413  */
1414  node->weight = -PCMK_SCORE_INFINITY;
1415  pcmk__rsc_trace(dependent,
1416  "Banned %s from %s because colocation %s attribute %s "
1417  "does not match",
1418  dependent->id, pcmk__node_name(node),
1419  colocation->id, attr);
1420  }
1421  }
1422 
1423  if ((colocation->score <= -PCMK_SCORE_INFINITY)
1424  || (colocation->score >= PCMK_SCORE_INFINITY)
1425  || pcmk__any_node_available(work)) {
1426 
1427  g_hash_table_destroy(dependent->allowed_nodes);
1428  dependent->allowed_nodes = work;
1429  work = NULL;
1430 
1431  } else {
1432  pcmk__rsc_info(dependent,
1433  "%s: Rolling back scores from %s (no available nodes)",
1434  dependent->id, primary->id);
1435  }
1436 
1437  if (work != NULL) {
1438  g_hash_table_destroy(work);
1439  }
1440 }
1441 
1455 int
1457  const pcmk_resource_t *primary,
1458  const pcmk__colocation_t *colocation)
1459 {
1460  const char *dependent_value = NULL;
1461  const char *primary_value = NULL;
1462  const char *attr = colocation->node_attribute;
1463  int score_multiplier = 1;
1464  int priority_delta = 0;
1465 
1466  pcmk__assert((dependent != NULL) && (primary != NULL)
1467  && (colocation != NULL));
1468 
1469  if (dependent->allocated_to == NULL) {
1470  return 0;
1471  }
1472 
1473  if ((primary->allocated_to != NULL)
1474  && (colocation->primary_role != pcmk_role_unknown)) {
1475  /* Colocation applies only if the primary's next role matches.
1476  *
1477  * If primary->allocated_to == NULL, we want to proceed past this block,
1478  * so that dependent->allocated_to 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->next_role) {
1486  return 0;
1487  }
1488  }
1489 
1490  dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1491  dependent);
1492  primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1493  primary);
1494 
1495  if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1496  if ((colocation->score == PCMK_SCORE_INFINITY)
1497  && (colocation->dependent_role == pcmk_role_promoted)) {
1498 
1499  /* For a mandatory promoted-role colocation, mark the dependent node
1500  * ineligible to promote the dependent if its attribute value
1501  * doesn't match the primary node's
1502  */
1503  score_multiplier = -1;
1504 
1505  } else {
1506  // Otherwise, ignore the colocation if attribute values don't match
1507  return 0;
1508  }
1509 
1510  } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1511  /* Node attribute values matched, so we want to avoid promoting the
1512  * dependent on this node
1513  */
1514  score_multiplier = -1;
1515  }
1516 
1517  priority_delta = score_multiplier * colocation->score;
1518  dependent->priority = pcmk__add_scores(priority_delta, dependent->priority);
1519  pcmk__rsc_trace(dependent,
1520  "Applied %s to %s promotion priority (now %s after %s %d)",
1521  colocation->id, dependent->id,
1522  pcmk_readable_score(dependent->priority),
1523  ((score_multiplier == 1)? "adding" : "subtracting"),
1524  colocation->score);
1525 
1526  return priority_delta;
1527 }
1528 
1538 static int
1539 best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1540  pcmk_resource_t *rsc, const char *attr,
1541  const char *value)
1542 {
1543  GHashTable *allowed_nodes_orig = NULL;
1544  GHashTableIter iter;
1545  pcmk_node_t *node = NULL;
1546  int best_score = -PCMK_SCORE_INFINITY;
1547  const char *best_node = NULL;
1548 
1549  if ((colocation != NULL) && (rsc == colocation->dependent)
1550  && pcmk_is_set(colocation->flags, pcmk__coloc_explicit)
1551  && pcmk__is_group(rsc->parent)
1552  && (rsc != rsc->parent->children->data)) {
1553  /* The resource is a user-configured colocation's explicit dependent,
1554  * and a group member other than the first, which means the group's
1555  * location constraint scores were not applied to it (see
1556  * pcmk__group_apply_location()). Explicitly consider those scores now.
1557  *
1558  * @TODO This does leave one suboptimal case: if the group itself or
1559  * another member other than the first is explicitly colocated with
1560  * the same primary, the primary will count the group's location scores
1561  * multiple times. This is much less likely than a single member being
1562  * explicitly colocated, so it's an acceptable tradeoff for now.
1563  */
1564  allowed_nodes_orig = rsc->allowed_nodes;
1565  rsc->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1566  for (GList *loc_iter = rsc->cluster->placement_constraints;
1567  loc_iter != NULL; loc_iter = loc_iter->next) {
1568 
1569  pcmk__location_t *location = loc_iter->data;
1570 
1571  if (location->rsc == rsc->parent) {
1572  rsc->cmds->apply_location(rsc, location);
1573  }
1574  }
1575  }
1576 
1577  // Find best allowed node with matching attribute
1578  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1579  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1580 
1581  if ((node->weight > best_score)
1582  && pcmk__node_available(node, false, false)
1583  && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1584  pcmk__str_casei)) {
1585 
1586  best_score = node->weight;
1587  best_node = node->details->uname;
1588  }
1589  }
1590 
1591  if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1592  if (best_node == NULL) {
1593  crm_info("No allowed node for %s matches node attribute %s=%s",
1594  rsc->id, attr, value);
1595  } else {
1596  crm_info("Allowed node %s for %s had best score (%d) "
1597  "of those matching node attribute %s=%s",
1598  best_node, rsc->id, best_score, attr, value);
1599  }
1600  }
1601 
1602  if (allowed_nodes_orig != NULL) {
1603  g_hash_table_destroy(rsc->allowed_nodes);
1604  rsc->allowed_nodes = allowed_nodes_orig;
1605  }
1606  return best_score;
1607 }
1608 
1617 static bool
1618 allowed_on_one(const pcmk_resource_t *rsc)
1619 {
1620  GHashTableIter iter;
1621  pcmk_node_t *allowed_node = NULL;
1622  int allowed_nodes = 0;
1623 
1624  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1625  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1626  if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1627  pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1628  return false;
1629  }
1630  }
1631  pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1632  ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1633  return (allowed_nodes == 1);
1634 }
1635 
1654 static void
1655 add_node_scores_matching_attr(GHashTable *nodes,
1656  pcmk_resource_t *source_rsc,
1657  const pcmk_resource_t *target_rsc,
1658  const pcmk__colocation_t *colocation,
1659  float factor, bool only_positive)
1660 {
1661  GHashTableIter iter;
1662  pcmk_node_t *node = NULL;
1663  const char *attr = colocation->node_attribute;
1664 
1665  // Iterate through each node
1666  g_hash_table_iter_init(&iter, nodes);
1667  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1668  float delta_f = 0;
1669  int delta = 0;
1670  int score = 0;
1671  int new_score = 0;
1672  const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1673 
1674  score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1675 
1676  if ((factor < 0) && (score < 0)) {
1677  /* If the dependent is anti-colocated, we generally don't want the
1678  * primary to prefer nodes that the dependent avoids. That could
1679  * lead to unnecessary shuffling of the primary when the dependent
1680  * hits its migration threshold somewhere, for example.
1681  *
1682  * However, there are cases when it is desirable. If the dependent
1683  * can't run anywhere but where the primary is, it would be
1684  * worthwhile to move the primary for the sake of keeping the
1685  * dependent active.
1686  *
1687  * We can't know that exactly at this point since we don't know
1688  * where the primary will be assigned, but we can limit considering
1689  * the preference to when the dependent is allowed only on one node.
1690  * This is less than ideal for multiple reasons:
1691  *
1692  * - the dependent could be allowed on more than one node but have
1693  * anti-colocation primaries on each;
1694  * - the dependent could be a clone or bundle with multiple
1695  * instances, and the dependent as a whole is allowed on multiple
1696  * nodes but some instance still can't run
1697  * - the dependent has considered node-specific criteria such as
1698  * location constraints and stickiness by this point, but might
1699  * have other factors that end up disallowing a node
1700  *
1701  * but the alternative is making the primary move when it doesn't
1702  * need to.
1703  *
1704  * We also consider the primary's stickiness and influence, so the
1705  * user has some say in the matter. (This is the configured primary,
1706  * not a particular instance of the primary, but that doesn't matter
1707  * unless stickiness uses a rule to vary by node, and that seems
1708  * acceptable to ignore.)
1709  */
1710  if ((colocation->primary->stickiness >= -score)
1711  || !pcmk__colocation_has_influence(colocation, NULL)
1712  || !allowed_on_one(colocation->dependent)) {
1713  crm_trace("%s: Filtering %d + %f * %d "
1714  "(double negative disallowed)",
1715  pcmk__node_name(node), node->weight, factor, score);
1716  continue;
1717  }
1718  }
1719 
1720  if (node->weight == INFINITY_HACK) {
1721  crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1722  pcmk__node_name(node), node->weight, factor, score);
1723  continue;
1724  }
1725 
1726  delta_f = factor * score;
1727 
1728  // Round the number; see http://c-faq.com/fp/round.html
1729  delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1730 
1731  /* Small factors can obliterate the small scores that are often actually
1732  * used in configurations. If the score and factor are nonzero, ensure
1733  * that the result is nonzero as well.
1734  */
1735  if ((delta == 0) && (score != 0)) {
1736  if (factor > 0.0) {
1737  delta = 1;
1738  } else if (factor < 0.0) {
1739  delta = -1;
1740  }
1741  }
1742 
1743  new_score = pcmk__add_scores(delta, node->weight);
1744 
1745  if (only_positive && (new_score < 0) && (node->weight > 0)) {
1746  crm_trace("%s: Filtering %d + %f * %d = %d "
1747  "(negative disallowed, marking node unusable)",
1748  pcmk__node_name(node), node->weight, factor, score,
1749  new_score);
1750  node->weight = INFINITY_HACK;
1751  continue;
1752  }
1753 
1754  if (only_positive && (new_score < 0) && (node->weight == 0)) {
1755  crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1756  pcmk__node_name(node), node->weight, factor, score,
1757  new_score);
1758  continue;
1759  }
1760 
1761  crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1762  node->weight, factor, score, new_score);
1763  node->weight = new_score;
1764  }
1765 }
1766 
1797 void
1799  const pcmk_resource_t *target_rsc,
1800  const char *log_id,
1801  GHashTable **nodes,
1802  const pcmk__colocation_t *colocation,
1803  float factor, uint32_t flags)
1804 {
1805  GHashTable *work = NULL;
1806 
1807  pcmk__assert((source_rsc != NULL) && (nodes != NULL)
1808  && ((colocation != NULL)
1809  || ((target_rsc == NULL) && (*nodes == NULL))));
1810 
1811  if (log_id == NULL) {
1812  log_id = source_rsc->id;
1813  }
1814 
1815  // Avoid infinite recursion
1816  if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1817  pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1818  log_id, source_rsc->id);
1819  return;
1820  }
1822 
1823  if (*nodes == NULL) {
1824  work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1825  target_rsc = source_rsc;
1826  } else {
1828 
1829  pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1830  log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1831  work = pcmk__copy_node_table(*nodes);
1832  add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1833  factor, pos);
1834  }
1835 
1836  if (work == NULL) {
1838  return;
1839  }
1840 
1841  if (pcmk__any_node_available(work)) {
1842  GList *colocations = NULL;
1843 
1845  colocations = pcmk__this_with_colocations(source_rsc);
1846  pcmk__rsc_trace(source_rsc,
1847  "Checking additional %d optional '%s with' "
1848  "constraints",
1849  g_list_length(colocations), source_rsc->id);
1850  } else {
1851  colocations = pcmk__with_this_colocations(source_rsc);
1852  pcmk__rsc_trace(source_rsc,
1853  "Checking additional %d optional 'with %s' "
1854  "constraints",
1855  g_list_length(colocations), source_rsc->id);
1856  }
1858 
1859  for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1860  pcmk__colocation_t *constraint = iter->data;
1861 
1862  pcmk_resource_t *other = NULL;
1863  float other_factor = factor * constraint->score
1864  / (float) PCMK_SCORE_INFINITY;
1865 
1867  other = constraint->primary;
1868  } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1869  continue;
1870  } else {
1871  other = constraint->dependent;
1872  }
1873 
1874  pcmk__rsc_trace(source_rsc,
1875  "Optionally merging score of '%s' constraint "
1876  "(%s with %s)",
1877  constraint->id, constraint->dependent->id,
1878  constraint->primary->id);
1879  other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1880  &work, constraint,
1881  other_factor, flags);
1882  pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1883  }
1884  g_list_free(colocations);
1885 
1887  pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1888  log_id, source_rsc->id);
1889  g_hash_table_destroy(work);
1891  return;
1892  }
1893 
1894 
1896  pcmk_node_t *node = NULL;
1897  GHashTableIter iter;
1898 
1899  g_hash_table_iter_init(&iter, work);
1900  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1901  if (node->weight == INFINITY_HACK) {
1902  node->weight = 1;
1903  }
1904  }
1905  }
1906 
1907  if (*nodes != NULL) {
1908  g_hash_table_destroy(*nodes);
1909  }
1910  *nodes = work;
1911 
1913 }
1914 
1922 void
1923 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1924 {
1925  pcmk__colocation_t *colocation = data;
1926  pcmk_resource_t *target_rsc = user_data;
1927 
1928  pcmk_resource_t *source_rsc = colocation->dependent;
1929  const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1930  uint32_t flags = pcmk__coloc_select_active;
1931 
1932  if (!pcmk__colocation_has_influence(colocation, NULL)) {
1933  return;
1934  }
1935  if (pcmk__is_clone(target_rsc)) {
1937  }
1938  pcmk__rsc_trace(target_rsc,
1939  "%s: Incorporating attenuated %s assignment scores due "
1940  "to colocation %s",
1941  target_rsc->id, source_rsc->id, colocation->id);
1942  source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1943  source_rsc->id,
1944  &target_rsc->allowed_nodes,
1945  colocation, factor, flags);
1946 }
1947 
1965 void
1967  const pcmk_resource_t *primary,
1968  const pcmk__colocation_t *colocation,
1969  const GList *primary_nodes, bool merge_scores)
1970 {
1971  GHashTableIter iter;
1972  pcmk_node_t *dependent_node = NULL;
1973 
1974  pcmk__assert((dependent != NULL) && (primary != NULL)
1975  && (colocation != NULL));
1976 
1977  g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1978  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1979  const pcmk_node_t *primary_node = NULL;
1980 
1981  primary_node = pe_find_node_id(primary_nodes,
1982  dependent_node->details->id);
1983  if (primary_node == NULL) {
1984  dependent_node->weight = -PCMK_SCORE_INFINITY;
1985  pcmk__rsc_trace(dependent,
1986  "Banning %s from %s (no primary instance) for %s",
1987  dependent->id, pcmk__node_name(dependent_node),
1988  colocation->id);
1989 
1990  } else if (merge_scores) {
1991  dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1992  primary_node->weight);
1993  pcmk__rsc_trace(dependent,
1994  "Added %s's score %s to %s's score for %s (now %s) "
1995  "for colocation %s",
1996  primary->id, pcmk_readable_score(primary_node->weight),
1997  dependent->id, pcmk__node_name(dependent_node),
1998  pcmk_readable_score(dependent_node->weight),
1999  colocation->id);
2000  }
2001  }
2002 }
2003 
2014 GList *
2016 {
2017  GList *list = NULL;
2018 
2019  rsc->cmds->with_this_colocations(rsc, rsc, &list);
2020  return list;
2021 }
2022 
2033 GList *
2035 {
2036  GList *list = NULL;
2037 
2038  rsc->cmds->this_with_colocations(rsc, rsc, &list);
2039  return list;
2040 }
pcmk_assignment_methods_t * cmds
Definition: resources.h:413
bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
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:974
A dumping ground.
pcmk_scheduler_t * cluster
Definition: resources.h:408
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)
GList * rsc_cons
Definition: resources.h:442
pcmk__coloc_affects
pcmk_resource_t * uber_parent(pcmk_resource_t *rsc)
Definition: complex.c:1046
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:137
pcmk_resource_t rsc2
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
Definition: resources.h:471
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK__XA_WITH_RSC_INSTANCE
#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...)
enum rsc_role_e next_role
Definition: resources.h:465
#define PCMK_XA_WITH_RSC_ROLE
Definition: xml_names.h:453
#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:313
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1604
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:39
#define PCMK_XA_RSC
Definition: xml_names.h:388
#define PCMK__XA_RSC_INSTANCE
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:441
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition: nvpair.c:1020
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
#define PCMK__XA_ORDERING
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:30
void pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
enum rsc_role_e pcmk_parse_role(const char *role)
Parse a resource role from a string role specification.
Definition: roles.c:59
GList * resources
Definition: scheduler.h:231
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)
#define PCMK_ACTION_DEMOTE
Definition: actions.h:49
int weight
Definition: nodes.h:163
pcmk_resource_t * parent
Definition: resources.h:409
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:129
#define pcmk__clear_action_flags(action, flags_to_clear)
Utility functions.
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
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:492
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
#define pcmk__sched_err(fmt...)
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:115
void(* this_with_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
#define CRM_ATTR_UNAME
Definition: crm.h:99
#define crm_trace(fmt, args...)
Definition: logging.h:404
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:94
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2170
#define PCMK_ACTION_START
Definition: actions.h:72
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: nvpair.c:993
int pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation)
unsigned long long flags
Definition: resources.h:428
Unpromoted.
Definition: roles.h:38
Wrappers for and extensions to libxml2.
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:702
void(* apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location)
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:75
GList * actions
Definition: resources.h:444
#define PCMK_XA_INFLUENCE
Definition: xml_names.h:306
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
pcmk_resource_t * rsc
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define PCMK_XA_SCORE
Definition: xml_names.h:396
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:958
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:167
enum pe_obj_types variant
Definition: resources.h:410
xmlNode * input
Definition: scheduler.h:196
#define pcmk__warn_once(wo_flag, fmt...)
uint32_t id
Definition: cpg.c:48
pcmk_resource_t rsc1
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
pcmk_resource_t * primary
#define pcmk__assert(expr)
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:508
#define PCMK_XA_NODE_ATTRIBUTE
Definition: xml_names.h:336
Cluster status and scheduling.
#define PCMK__ROLE_UNKNOWN
#define PCMK_ROLE_STARTED
Definition: roles.h:26
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:1451
pcmk_scheduler_t * scheduler
GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:178
GList * colocation_constraints
Definition: scheduler.h:234
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
Definition: tags.h:29
pcmk_node_t * allocated_to
Definition: resources.h:447
#define PCMK_ACTION_PROMOTE
Definition: actions.h:66
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:176
GList * running_on
Definition: resources.h:456
enum pe_action_flags flags
Definition: actions.h:349
GList * placement_constraints
Definition: scheduler.h:232
const char * node_attribute
#define PCMK_XA_SYMMETRICAL
Definition: xml_names.h:415
#define crm_log_xml_trace(xml, text)
Definition: logging.h:412
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.
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition: clone.c:229
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:297
#define CRM_ATTR_ID
Definition: crm.h:100
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition: xml.c:2130
#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:399
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
uint64_t flags
Definition: remote.c:215
GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:24
GHashTable * allowed_nodes
Definition: resources.h:462