pacemaker  2.1.8-3980678f03
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  CRM_ASSERT(colocation1->primary != NULL);
69  } else {
70  rsc1 = colocation1->primary;
71  rsc2 = colocation2->primary;
72  CRM_ASSERT(colocation1->dependent != NULL);
73  }
74  CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
75 
76  if (rsc1->priority > rsc2->priority) {
77  return -1;
78  }
79  if (rsc1->priority < rsc2->priority) {
80  return 1;
81  }
82 
83  // Process clones before primitives and groups
84  if (rsc1->variant > rsc2->variant) {
85  return -1;
86  }
87  if (rsc1->variant < rsc2->variant) {
88  return 1;
89  }
90 
91  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
92  * clones (probably unnecessary, but avoids having to update regression
93  * tests)
94  */
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  CRM_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  CRM_ASSERT((list != NULL) && (rsc != NULL));
204 
206  {}, // Always add each colocation individually if tracing
207  {
208  if (*list == NULL) {
209  // Trivial case for efficiency if not tracing
210  *list = g_list_copy(addition);
211  return;
212  }
213  }
214  );
215 
216  for (const GList *iter = addition; iter != NULL; iter = iter->next) {
217  pcmk__add_this_with(list, addition->data, rsc);
218  }
219 }
220 
232 void
233 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
234  const pcmk_resource_t *rsc)
235 {
236  CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
237 
238  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  CRM_ASSERT((list != NULL) && (rsc != NULL));
264 
266  {}, // Always add each colocation individually if tracing
267  {
268  if (*list == NULL) {
269  // Trivial case for efficiency if not tracing
270  *list = g_list_copy(addition);
271  return;
272  }
273  }
274  );
275 
276  for (const GList *iter = addition; iter != NULL; iter = iter->next) {
277  pcmk__add_with_this(list, addition->data, rsc);
278  }
279 }
280 
290 static void
291 anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
292  pcmk_resource_t *then_rsc, int then_role)
293 {
294  const char *first_tasks[] = { NULL, NULL };
295  const char *then_tasks[] = { NULL, NULL };
296 
297  /* Actions to make first_rsc lose first_role */
298  if (first_role == pcmk_role_promoted) {
299  first_tasks[0] = PCMK_ACTION_DEMOTE;
300 
301  } else {
302  first_tasks[0] = PCMK_ACTION_STOP;
303 
304  if (first_role == pcmk_role_unpromoted) {
305  first_tasks[1] = PCMK_ACTION_PROMOTE;
306  }
307  }
308 
309  /* Actions to make then_rsc gain then_role */
310  if (then_role == pcmk_role_promoted) {
311  then_tasks[0] = PCMK_ACTION_PROMOTE;
312 
313  } else {
314  then_tasks[0] = PCMK_ACTION_START;
315 
316  if (then_role == pcmk_role_unpromoted) {
317  then_tasks[1] = PCMK_ACTION_DEMOTE;
318  }
319  }
320 
321  for (int first_lpc = 0;
322  (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
323 
324  for (int then_lpc = 0;
325  (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
326 
327  pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
328  then_rsc, then_tasks[then_lpc],
330  }
331  }
332 }
333 
347 void
348 pcmk__new_colocation(const char *id, const char *node_attr, int score,
349  pcmk_resource_t *dependent, pcmk_resource_t *primary,
350  const char *dependent_role, const char *primary_role,
351  uint32_t flags)
352 {
353  pcmk__colocation_t *new_con = NULL;
354 
355  CRM_CHECK(id != NULL, return);
356 
357  if ((dependent == NULL) || (primary == NULL)) {
358  pcmk__config_err("Ignoring colocation '%s' because resource "
359  "does not exist", id);
360  return;
361  }
362 
363  if (score == 0) {
364  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  CRM_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 
1035 static void
1036 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1037  const pcmk_resource_t *reason)
1038 {
1039  GList *iter = NULL;
1040  char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1041 
1042  for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1043  pcmk_action_t *action = iter->data;
1044 
1046  && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1047 
1049  pe_action_set_reason(action, reason_text, false);
1052  }
1053  }
1054 
1055  // If parent resource can't perform an action, neither can any children
1056  for (iter = rsc->children; iter != NULL; iter = iter->next) {
1057  mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1058  }
1059  free(reason_text);
1060 }
1061 
1072 void
1074 {
1075  GList *iter = NULL;
1076  GList *colocations = NULL;
1077  pcmk_resource_t *rsc = NULL;
1078  bool is_start = false;
1079 
1080  if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
1081  return; // Only unrunnable actions block dependents
1082  }
1083 
1084  is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1085  if (!is_start
1086  && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1087  return; // Only unrunnable starts and promotes block dependents
1088  }
1089 
1090  CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions
1091 
1092  /* If this resource is part of a collective resource, dependents are blocked
1093  * only if all instances of the collective are unrunnable, so check the
1094  * collective resource.
1095  */
1096  rsc = uber_parent(action->rsc);
1097  if (rsc->parent != NULL) {
1098  rsc = rsc->parent; // Bundle
1099  }
1100 
1101  // Colocation fails only if entire primary can't reach desired role
1102  for (iter = rsc->children; iter != NULL; iter = iter->next) {
1103  pcmk_resource_t *child = iter->data;
1104  pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1105  action->task, NULL);
1106 
1107  if ((child_action == NULL)
1108  || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1109  crm_trace("Not blocking %s colocation dependents because "
1110  "at least %s has runnable %s",
1111  rsc->id, child->id, action->task);
1112  return; // At least one child can reach desired role
1113  }
1114  }
1115 
1116  crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1117  rsc->id, action->rsc->id, action->task);
1118 
1119  // Check each colocation where this resource is primary
1120  colocations = pcmk__with_this_colocations(rsc);
1121  for (iter = colocations; iter != NULL; iter = iter->next) {
1122  pcmk__colocation_t *colocation = iter->data;
1123 
1124  if (colocation->score < PCMK_SCORE_INFINITY) {
1125  continue; // Only mandatory colocations block dependent
1126  }
1127 
1128  /* If the primary can't start, the dependent can't reach its colocated
1129  * role, regardless of what the primary or dependent colocation role is.
1130  *
1131  * If the primary can't be promoted, the dependent can't reach its
1132  * colocated role if the primary's colocation role is promoted.
1133  */
1134  if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1135  continue;
1136  }
1137 
1138  // Block the dependent from reaching its colocated role
1139  if (colocation->dependent_role == pcmk_role_promoted) {
1140  mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1141  action->rsc);
1142  } else {
1143  mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1144  action->rsc);
1145  }
1146  }
1147  g_list_free(colocations);
1148 }
1149 
1168 static const pcmk_resource_t *
1169 get_resource_for_role(const pcmk_resource_t *rsc)
1170 {
1172  const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1173 
1174  if (child != NULL) {
1175  return child;
1176  }
1177  }
1178  return rsc;
1179 }
1180 
1201  const pcmk_resource_t *primary,
1202  const pcmk__colocation_t *colocation, bool preview)
1203 {
1204  const pcmk_resource_t *dependent_role_rsc = NULL;
1205  const pcmk_resource_t *primary_role_rsc = NULL;
1206 
1207  CRM_ASSERT((dependent != NULL) && (primary != NULL)
1208  && (colocation != NULL));
1209 
1210  if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1211  // Primary resource has not been assigned yet, so we can't do anything
1213  }
1214 
1215  dependent_role_rsc = get_resource_for_role(dependent);
1216  primary_role_rsc = get_resource_for_role(primary);
1217 
1218  if ((colocation->dependent_role >= pcmk_role_unpromoted)
1219  && (dependent_role_rsc->parent != NULL)
1220  && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1221  && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1222 
1223  /* This is a colocation by role, and the dependent is a promotable clone
1224  * that has already been assigned, so the colocation should now affect
1225  * the role.
1226  */
1227  return pcmk__coloc_affects_role;
1228  }
1229 
1230  if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1231  /* The dependent resource has already been through assignment, so the
1232  * constraint no longer has any effect. Log an error if a mandatory
1233  * colocation constraint has been violated.
1234  */
1235 
1236  const pcmk_node_t *primary_node = primary->allocated_to;
1237 
1238  if (dependent->allocated_to == NULL) {
1239  crm_trace("Skipping colocation '%s': %s will not run anywhere",
1240  colocation->id, dependent->id);
1241 
1242  } else if (colocation->score >= PCMK_SCORE_INFINITY) {
1243  // Dependent resource must colocate with primary resource
1244 
1245  if (!pcmk__same_node(primary_node, dependent->allocated_to)) {
1246  pcmk__sched_err("%s must be colocated with %s but is not "
1247  "(%s vs. %s)",
1248  dependent->id, primary->id,
1249  pcmk__node_name(dependent->allocated_to),
1250  pcmk__node_name(primary_node));
1251  }
1252 
1253  } else if (colocation->score <= -PCMK_SCORE_INFINITY) {
1254  // Dependent resource must anti-colocate with primary resource
1255 
1256  if (pcmk__same_node(dependent->allocated_to, primary_node)) {
1257  pcmk__sched_err("%s and %s must be anti-colocated but are "
1258  "assigned to the same node (%s)",
1259  dependent->id, primary->id,
1260  pcmk__node_name(primary_node));
1261  }
1262  }
1264  }
1265 
1266  if ((colocation->dependent_role != pcmk_role_unknown)
1267  && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1268  crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1269 
1270  "but %s next role is %s",
1271  ((colocation->score < 0)? "anti-" : ""),
1272  colocation->id, pcmk_role_text(colocation->dependent_role),
1273  dependent_role_rsc->id,
1274  pcmk_role_text(dependent_role_rsc->next_role));
1276  }
1277 
1278  if ((colocation->primary_role != pcmk_role_unknown)
1279  && (colocation->primary_role != primary_role_rsc->next_role)) {
1280  crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1281  "but %s next role is %s",
1282  ((colocation->score < 0)? "anti-" : ""),
1283  colocation->id, pcmk_role_text(colocation->primary_role),
1284  primary_role_rsc->id,
1285  pcmk_role_text(primary_role_rsc->next_role));
1287  }
1288 
1290 }
1291 
1303 void
1305  const pcmk_resource_t *primary,
1306  const pcmk__colocation_t *colocation)
1307 {
1308  const char *attr = colocation->node_attribute;
1309  const char *value = NULL;
1310  GHashTable *work = NULL;
1311  GHashTableIter iter;
1312  pcmk_node_t *node = NULL;
1313 
1314  if (primary->allocated_to != NULL) {
1315  value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1316  primary);
1317 
1318  } else if (colocation->score < 0) {
1319  // Nothing to do (anti-colocation with something that is not running)
1320  return;
1321  }
1322 
1323  work = pcmk__copy_node_table(dependent->allowed_nodes);
1324 
1325  g_hash_table_iter_init(&iter, work);
1326  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1327  if (primary->allocated_to == NULL) {
1328  node->weight = pcmk__add_scores(-colocation->score, node->weight);
1329  pcmk__rsc_trace(dependent,
1330  "Applied %s to %s score on %s (now %s after "
1331  "subtracting %s because primary %s inactive)",
1332  colocation->id, dependent->id,
1333  pcmk__node_name(node),
1334  pcmk_readable_score(node->weight),
1335  pcmk_readable_score(colocation->score), primary->id);
1336  continue;
1337  }
1338 
1339  if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1340  value, pcmk__str_casei)) {
1341 
1342  /* Add colocation score only if optional (or minus infinity). A
1343  * mandatory colocation is a requirement rather than a preference,
1344  * so we don't need to consider it for relative assignment purposes.
1345  * The resource will simply be forbidden from running on the node if
1346  * the primary isn't active there (via the condition above).
1347  */
1348  if (colocation->score < PCMK_SCORE_INFINITY) {
1349  node->weight = pcmk__add_scores(colocation->score,
1350  node->weight);
1351  pcmk__rsc_trace(dependent,
1352  "Applied %s to %s score on %s (now %s after "
1353  "adding %s)",
1354  colocation->id, dependent->id,
1355  pcmk__node_name(node),
1356  pcmk_readable_score(node->weight),
1357  pcmk_readable_score(colocation->score));
1358  }
1359  continue;
1360  }
1361 
1362  if (colocation->score >= PCMK_SCORE_INFINITY) {
1363  /* Only mandatory colocations are relevant when the colocation
1364  * attribute doesn't match, because an attribute not matching is not
1365  * a negative preference -- the colocation is simply relevant only
1366  * where it matches.
1367  */
1368  node->weight = -PCMK_SCORE_INFINITY;
1369  pcmk__rsc_trace(dependent,
1370  "Banned %s from %s because colocation %s attribute %s "
1371  "does not match",
1372  dependent->id, pcmk__node_name(node),
1373  colocation->id, attr);
1374  }
1375  }
1376 
1377  if ((colocation->score <= -PCMK_SCORE_INFINITY)
1378  || (colocation->score >= PCMK_SCORE_INFINITY)
1379  || pcmk__any_node_available(work)) {
1380 
1381  g_hash_table_destroy(dependent->allowed_nodes);
1382  dependent->allowed_nodes = work;
1383  work = NULL;
1384 
1385  } else {
1386  pcmk__rsc_info(dependent,
1387  "%s: Rolling back scores from %s (no available nodes)",
1388  dependent->id, primary->id);
1389  }
1390 
1391  if (work != NULL) {
1392  g_hash_table_destroy(work);
1393  }
1394 }
1395 
1409 int
1411  const pcmk_resource_t *primary,
1412  const pcmk__colocation_t *colocation)
1413 {
1414  const char *dependent_value = NULL;
1415  const char *primary_value = NULL;
1416  const char *attr = colocation->node_attribute;
1417  int score_multiplier = 1;
1418  int priority_delta = 0;
1419 
1420  CRM_ASSERT((dependent != NULL) && (primary != NULL)
1421  && (colocation != NULL));
1422 
1423  if (dependent->allocated_to == NULL) {
1424  return 0;
1425  }
1426 
1427  if ((primary->allocated_to != NULL)
1428  && (colocation->primary_role != pcmk_role_unknown)) {
1429  /* Colocation applies only if the primary's next role matches.
1430  *
1431  * If primary->allocated_to == NULL, we want to proceed past this block,
1432  * so that dependent->allocated_to is marked ineligible for promotion.
1433  *
1434  * @TODO Why ignore a mandatory colocation in this case when we apply
1435  * its negation in the mismatched value case?
1436  */
1437  const pcmk_resource_t *role_rsc = get_resource_for_role(primary);
1438 
1439  if (colocation->primary_role != role_rsc->next_role) {
1440  return 0;
1441  }
1442  }
1443 
1444  dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1445  dependent);
1446  primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1447  primary);
1448 
1449  if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1450  if ((colocation->score == PCMK_SCORE_INFINITY)
1451  && (colocation->dependent_role == pcmk_role_promoted)) {
1452 
1453  /* For a mandatory promoted-role colocation, mark the dependent node
1454  * ineligible to promote the dependent if its attribute value
1455  * doesn't match the primary node's
1456  */
1457  score_multiplier = -1;
1458 
1459  } else {
1460  // Otherwise, ignore the colocation if attribute values don't match
1461  return 0;
1462  }
1463 
1464  } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1465  /* Node attribute values matched, so we want to avoid promoting the
1466  * dependent on this node
1467  */
1468  score_multiplier = -1;
1469  }
1470 
1471  priority_delta = score_multiplier * colocation->score;
1472  dependent->priority = pcmk__add_scores(priority_delta, dependent->priority);
1473  pcmk__rsc_trace(dependent,
1474  "Applied %s to %s promotion priority (now %s after %s %d)",
1475  colocation->id, dependent->id,
1476  pcmk_readable_score(dependent->priority),
1477  ((score_multiplier == 1)? "adding" : "subtracting"),
1478  colocation->score);
1479 
1480  return priority_delta;
1481 }
1482 
1492 static int
1493 best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1494  pcmk_resource_t *rsc, const char *attr,
1495  const char *value)
1496 {
1497  GHashTable *allowed_nodes_orig = NULL;
1498  GHashTableIter iter;
1499  pcmk_node_t *node = NULL;
1500  int best_score = -PCMK_SCORE_INFINITY;
1501  const char *best_node = NULL;
1502 
1503  if ((colocation != NULL) && (rsc == colocation->dependent)
1504  && pcmk_is_set(colocation->flags, pcmk__coloc_explicit)
1505  && pcmk__is_group(rsc->parent)
1506  && (rsc != rsc->parent->children->data)) {
1507  /* The resource is a user-configured colocation's explicit dependent,
1508  * and a group member other than the first, which means the group's
1509  * location constraint scores were not applied to it (see
1510  * pcmk__group_apply_location()). Explicitly consider those scores now.
1511  *
1512  * @TODO This does leave one suboptimal case: if the group itself or
1513  * another member other than the first is explicitly colocated with
1514  * the same primary, the primary will count the group's location scores
1515  * multiple times. This is much less likely than a single member being
1516  * explicitly colocated, so it's an acceptable tradeoff for now.
1517  */
1518  allowed_nodes_orig = rsc->allowed_nodes;
1519  rsc->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1520  for (GList *loc_iter = rsc->cluster->placement_constraints;
1521  loc_iter != NULL; loc_iter = loc_iter->next) {
1522 
1523  pcmk__location_t *location = loc_iter->data;
1524 
1525  if (location->rsc == rsc->parent) {
1526  rsc->cmds->apply_location(rsc, location);
1527  }
1528  }
1529  }
1530 
1531  // Find best allowed node with matching attribute
1532  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1533  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1534 
1535  if ((node->weight > best_score)
1536  && pcmk__node_available(node, false, false)
1537  && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1538  pcmk__str_casei)) {
1539 
1540  best_score = node->weight;
1541  best_node = node->details->uname;
1542  }
1543  }
1544 
1545  if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1546  if (best_node == NULL) {
1547  crm_info("No allowed node for %s matches node attribute %s=%s",
1548  rsc->id, attr, value);
1549  } else {
1550  crm_info("Allowed node %s for %s had best score (%d) "
1551  "of those matching node attribute %s=%s",
1552  best_node, rsc->id, best_score, attr, value);
1553  }
1554  }
1555 
1556  if (allowed_nodes_orig != NULL) {
1557  g_hash_table_destroy(rsc->allowed_nodes);
1558  rsc->allowed_nodes = allowed_nodes_orig;
1559  }
1560  return best_score;
1561 }
1562 
1571 static bool
1572 allowed_on_one(const pcmk_resource_t *rsc)
1573 {
1574  GHashTableIter iter;
1575  pcmk_node_t *allowed_node = NULL;
1576  int allowed_nodes = 0;
1577 
1578  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1579  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1580  if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1581  pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1582  return false;
1583  }
1584  }
1585  pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1586  ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1587  return (allowed_nodes == 1);
1588 }
1589 
1608 static void
1609 add_node_scores_matching_attr(GHashTable *nodes,
1610  pcmk_resource_t *source_rsc,
1611  const pcmk_resource_t *target_rsc,
1612  const pcmk__colocation_t *colocation,
1613  float factor, bool only_positive)
1614 {
1615  GHashTableIter iter;
1616  pcmk_node_t *node = NULL;
1617  const char *attr = colocation->node_attribute;
1618 
1619  // Iterate through each node
1620  g_hash_table_iter_init(&iter, nodes);
1621  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1622  float delta_f = 0;
1623  int delta = 0;
1624  int score = 0;
1625  int new_score = 0;
1626  const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1627 
1628  score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1629 
1630  if ((factor < 0) && (score < 0)) {
1631  /* If the dependent is anti-colocated, we generally don't want the
1632  * primary to prefer nodes that the dependent avoids. That could
1633  * lead to unnecessary shuffling of the primary when the dependent
1634  * hits its migration threshold somewhere, for example.
1635  *
1636  * However, there are cases when it is desirable. If the dependent
1637  * can't run anywhere but where the primary is, it would be
1638  * worthwhile to move the primary for the sake of keeping the
1639  * dependent active.
1640  *
1641  * We can't know that exactly at this point since we don't know
1642  * where the primary will be assigned, but we can limit considering
1643  * the preference to when the dependent is allowed only on one node.
1644  * This is less than ideal for multiple reasons:
1645  *
1646  * - the dependent could be allowed on more than one node but have
1647  * anti-colocation primaries on each;
1648  * - the dependent could be a clone or bundle with multiple
1649  * instances, and the dependent as a whole is allowed on multiple
1650  * nodes but some instance still can't run
1651  * - the dependent has considered node-specific criteria such as
1652  * location constraints and stickiness by this point, but might
1653  * have other factors that end up disallowing a node
1654  *
1655  * but the alternative is making the primary move when it doesn't
1656  * need to.
1657  *
1658  * We also consider the primary's stickiness and influence, so the
1659  * user has some say in the matter. (This is the configured primary,
1660  * not a particular instance of the primary, but that doesn't matter
1661  * unless stickiness uses a rule to vary by node, and that seems
1662  * acceptable to ignore.)
1663  */
1664  if ((colocation->primary->stickiness >= -score)
1665  || !pcmk__colocation_has_influence(colocation, NULL)
1666  || !allowed_on_one(colocation->dependent)) {
1667  crm_trace("%s: Filtering %d + %f * %d "
1668  "(double negative disallowed)",
1669  pcmk__node_name(node), node->weight, factor, score);
1670  continue;
1671  }
1672  }
1673 
1674  if (node->weight == INFINITY_HACK) {
1675  crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1676  pcmk__node_name(node), node->weight, factor, score);
1677  continue;
1678  }
1679 
1680  delta_f = factor * score;
1681 
1682  // Round the number; see http://c-faq.com/fp/round.html
1683  delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1684 
1685  /* Small factors can obliterate the small scores that are often actually
1686  * used in configurations. If the score and factor are nonzero, ensure
1687  * that the result is nonzero as well.
1688  */
1689  if ((delta == 0) && (score != 0)) {
1690  if (factor > 0.0) {
1691  delta = 1;
1692  } else if (factor < 0.0) {
1693  delta = -1;
1694  }
1695  }
1696 
1697  new_score = pcmk__add_scores(delta, node->weight);
1698 
1699  if (only_positive && (new_score < 0) && (node->weight > 0)) {
1700  crm_trace("%s: Filtering %d + %f * %d = %d "
1701  "(negative disallowed, marking node unusable)",
1702  pcmk__node_name(node), node->weight, factor, score,
1703  new_score);
1704  node->weight = INFINITY_HACK;
1705  continue;
1706  }
1707 
1708  if (only_positive && (new_score < 0) && (node->weight == 0)) {
1709  crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1710  pcmk__node_name(node), node->weight, factor, score,
1711  new_score);
1712  continue;
1713  }
1714 
1715  crm_trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1716  node->weight, factor, score, new_score);
1717  node->weight = new_score;
1718  }
1719 }
1720 
1751 void
1753  const pcmk_resource_t *target_rsc,
1754  const char *log_id,
1755  GHashTable **nodes,
1756  const pcmk__colocation_t *colocation,
1757  float factor, uint32_t flags)
1758 {
1759  GHashTable *work = NULL;
1760 
1761  CRM_ASSERT((source_rsc != NULL) && (nodes != NULL)
1762  && ((colocation != NULL)
1763  || ((target_rsc == NULL) && (*nodes == NULL))));
1764 
1765  if (log_id == NULL) {
1766  log_id = source_rsc->id;
1767  }
1768 
1769  // Avoid infinite recursion
1770  if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1771  pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1772  log_id, source_rsc->id);
1773  return;
1774  }
1776 
1777  if (*nodes == NULL) {
1778  work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1779  target_rsc = source_rsc;
1780  } else {
1782 
1783  pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1784  log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1785  work = pcmk__copy_node_table(*nodes);
1786  add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1787  factor, pos);
1788  }
1789 
1790  if (work == NULL) {
1792  return;
1793  }
1794 
1795  if (pcmk__any_node_available(work)) {
1796  GList *colocations = NULL;
1797 
1799  colocations = pcmk__this_with_colocations(source_rsc);
1800  pcmk__rsc_trace(source_rsc,
1801  "Checking additional %d optional '%s with' "
1802  "constraints",
1803  g_list_length(colocations), source_rsc->id);
1804  } else {
1805  colocations = pcmk__with_this_colocations(source_rsc);
1806  pcmk__rsc_trace(source_rsc,
1807  "Checking additional %d optional 'with %s' "
1808  "constraints",
1809  g_list_length(colocations), source_rsc->id);
1810  }
1812 
1813  for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1814  pcmk__colocation_t *constraint = iter->data;
1815 
1816  pcmk_resource_t *other = NULL;
1817  float other_factor = factor * constraint->score
1818  / (float) PCMK_SCORE_INFINITY;
1819 
1821  other = constraint->primary;
1822  } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1823  continue;
1824  } else {
1825  other = constraint->dependent;
1826  }
1827 
1828  pcmk__rsc_trace(source_rsc,
1829  "Optionally merging score of '%s' constraint "
1830  "(%s with %s)",
1831  constraint->id, constraint->dependent->id,
1832  constraint->primary->id);
1833  other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1834  &work, constraint,
1835  other_factor, flags);
1836  pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1837  }
1838  g_list_free(colocations);
1839 
1841  pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1842  log_id, source_rsc->id);
1843  g_hash_table_destroy(work);
1845  return;
1846  }
1847 
1848 
1850  pcmk_node_t *node = NULL;
1851  GHashTableIter iter;
1852 
1853  g_hash_table_iter_init(&iter, work);
1854  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1855  if (node->weight == INFINITY_HACK) {
1856  node->weight = 1;
1857  }
1858  }
1859  }
1860 
1861  if (*nodes != NULL) {
1862  g_hash_table_destroy(*nodes);
1863  }
1864  *nodes = work;
1865 
1867 }
1868 
1876 void
1877 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1878 {
1879  pcmk__colocation_t *colocation = data;
1880  pcmk_resource_t *target_rsc = user_data;
1881 
1882  pcmk_resource_t *source_rsc = colocation->dependent;
1883  const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1884  uint32_t flags = pcmk__coloc_select_active;
1885 
1886  if (!pcmk__colocation_has_influence(colocation, NULL)) {
1887  return;
1888  }
1889  if (pcmk__is_clone(target_rsc)) {
1891  }
1892  pcmk__rsc_trace(target_rsc,
1893  "%s: Incorporating attenuated %s assignment scores due "
1894  "to colocation %s",
1895  target_rsc->id, source_rsc->id, colocation->id);
1896  source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1897  source_rsc->id,
1898  &target_rsc->allowed_nodes,
1899  colocation, factor, flags);
1900 }
1901 
1919 void
1921  const pcmk_resource_t *primary,
1922  const pcmk__colocation_t *colocation,
1923  const GList *primary_nodes, bool merge_scores)
1924 {
1925  GHashTableIter iter;
1926  pcmk_node_t *dependent_node = NULL;
1927 
1928  CRM_ASSERT((dependent != NULL) && (primary != NULL)
1929  && (colocation != NULL));
1930 
1931  g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1932  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1933  const pcmk_node_t *primary_node = NULL;
1934 
1935  primary_node = pe_find_node_id(primary_nodes,
1936  dependent_node->details->id);
1937  if (primary_node == NULL) {
1938  dependent_node->weight = -PCMK_SCORE_INFINITY;
1939  pcmk__rsc_trace(dependent,
1940  "Banning %s from %s (no primary instance) for %s",
1941  dependent->id, pcmk__node_name(dependent_node),
1942  colocation->id);
1943 
1944  } else if (merge_scores) {
1945  dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1946  primary_node->weight);
1947  pcmk__rsc_trace(dependent,
1948  "Added %s's score %s to %s's score for %s (now %s) "
1949  "for colocation %s",
1950  primary->id, pcmk_readable_score(primary_node->weight),
1951  dependent->id, pcmk__node_name(dependent_node),
1952  pcmk_readable_score(dependent_node->weight),
1953  colocation->id);
1954  }
1955  }
1956 }
1957 
1968 GList *
1970 {
1971  GList *list = NULL;
1972 
1973  rsc->cmds->with_this_colocations(rsc, rsc, &list);
1974  return list;
1975 }
1976 
1987 GList *
1989 {
1990  GList *list = NULL;
1991 
1992  rsc->cmds->this_with_colocations(rsc, rsc, &list);
1993  return list;
1994 }
pcmk_assignment_methods_t * cmds
Definition: resources.h:413
#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:883
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:1007
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:86
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:181
#define PCMK_XE_RESOURCE_REF
Definition: xml_names.h:173
#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:448
#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:301
void pe_action_set_reason(pcmk_action_t *action, const char *reason, bool overwrite)
Definition: pe_actions.c:1602
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:383
#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:936
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:162
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:446
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:487
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
#define pcmk__sched_err(fmt...)
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
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:98
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2152
#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:909
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:652
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:301
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:296
pcmk_resource_t * rsc
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define PCMK_XA_SCORE
Definition: xml_names.h:391
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:867
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:116
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
int crm_str_to_boolean(const char *s, int *ret)
Definition: strings.c:496
#define PCMK_XA_NODE_ATTRIBUTE
Definition: xml_names.h:331
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:1449
pcmk_scheduler_t * scheduler
#define CRM_ASSERT(expr)
Definition: results.h:42
GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:174
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:385
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:176
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:410
#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:447
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:2108
#define PCMK_XA_SEQUENTIAL
Definition: xml_names.h:393
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:382
#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