pacemaker  2.1.6-802a72226b
Scalable High-Availability cluster resource manager
pcmk_sched_promotable.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU General Public License version 2
7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <crm/msg_xml.h>
13 #include <pacemaker-internal.h>
14 
15 #include "libpacemaker_private.h"
16 
25 static void
26 order_instance_promotion(pe_resource_t *clone, pe_resource_t *child,
27  pe_resource_t *last)
28 {
29  // "Promote clone" -> promote instance -> "clone promoted"
34 
35  // If clone is ordered, order this instance relative to last
36  if ((last != NULL) && pe__clone_is_ordered(clone)) {
39  }
40 }
41 
50 static void
51 order_instance_demotion(pe_resource_t *clone, pe_resource_t *child,
52  pe_resource_t *last)
53 {
54  // "Demote clone" -> demote instance -> "clone demoted"
59 
60  // If clone is ordered, order this instance relative to last
61  if ((last != NULL) && pe__clone_is_ordered(clone)) {
64  }
65 }
66 
75 static void
76 check_for_role_change(const pe_resource_t *rsc, bool *demoting, bool *promoting)
77 {
78  const GList *iter = NULL;
79 
80  // If this is a cloned group, check group members recursively
81  if (rsc->children != NULL) {
82  for (iter = rsc->children; iter != NULL; iter = iter->next) {
83  check_for_role_change((const pe_resource_t *) iter->data,
84  demoting, promoting);
85  }
86  return;
87  }
88 
89  for (iter = rsc->actions; iter != NULL; iter = iter->next) {
90  const pe_action_t *action = (const pe_action_t *) iter->data;
91 
92  if (*promoting && *demoting) {
93  return;
94 
95  } else if (pcmk_is_set(action->flags, pe_action_optional)) {
96  continue;
97 
98  } else if (pcmk__str_eq(RSC_DEMOTE, action->task, pcmk__str_none)) {
99  *demoting = true;
100 
101  } else if (pcmk__str_eq(RSC_PROMOTE, action->task, pcmk__str_none)) {
102  *promoting = true;
103  }
104  }
105 }
106 
119 static void
120 apply_promoted_locations(pe_resource_t *child,
121  const GList *location_constraints,
122  const pe_node_t *chosen)
123 {
124  for (const GList *iter = location_constraints; iter; iter = iter->next) {
125  const pe__location_t *location = iter->data;
126  pe_node_t *weighted_node = NULL;
127 
128  if (location->role_filter == RSC_ROLE_PROMOTED) {
129  weighted_node = pe_find_node_id(location->node_list_rh,
130  chosen->details->id);
131  }
132  if (weighted_node != NULL) {
133  int new_priority = pcmk__add_scores(child->priority,
134  weighted_node->weight);
135 
136  pe_rsc_trace(child,
137  "Applying location %s to %s promotion priority on %s: "
138  "%s + %s = %s",
139  location->id, child->id, pe__node_name(weighted_node),
141  pcmk_readable_score(weighted_node->weight),
142  pcmk_readable_score(new_priority));
143  child->priority = new_priority;
144  }
145  }
146 }
147 
156 static pe_node_t *
157 node_to_be_promoted_on(const pe_resource_t *rsc)
158 {
159  pe_node_t *node = NULL;
160  pe_node_t *local_node = NULL;
161  const pe_resource_t *parent = NULL;
162 
163  // If this is a cloned group, bail if any group member can't be promoted
164  for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
165  pe_resource_t *child = (pe_resource_t *) iter->data;
166 
167  if (node_to_be_promoted_on(child) == NULL) {
168  pe_rsc_trace(rsc,
169  "%s can't be promoted because member %s can't",
170  rsc->id, child->id);
171  return NULL;
172  }
173  }
174 
175  node = rsc->fns->location(rsc, NULL, FALSE);
176  if (node == NULL) {
177  pe_rsc_trace(rsc, "%s can't be promoted because it won't be active",
178  rsc->id);
179  return NULL;
180 
181  } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
182  if (rsc->fns->state(rsc, TRUE) == RSC_ROLE_PROMOTED) {
183  crm_notice("Unmanaged instance %s will be left promoted on %s",
184  rsc->id, pe__node_name(node));
185  } else {
186  pe_rsc_trace(rsc, "%s can't be promoted because it is unmanaged",
187  rsc->id);
188  return NULL;
189  }
190 
191  } else if (rsc->priority < 0) {
192  pe_rsc_trace(rsc,
193  "%s can't be promoted because its promotion priority %d "
194  "is negative",
195  rsc->id, rsc->priority);
196  return NULL;
197 
198  } else if (!pcmk__node_available(node, false, true)) {
199  pe_rsc_trace(rsc, "%s can't be promoted because %s can't run resources",
200  rsc->id, pe__node_name(node));
201  return NULL;
202  }
203 
204  parent = pe__const_top_resource(rsc, false);
205  local_node = pe_hash_table_lookup(parent->allowed_nodes, node->details->id);
206 
207  if (local_node == NULL) {
208  /* It should not be possible for the scheduler to have allocated the
209  * instance to a node where its parent is not allowed, but it's good to
210  * have a fail-safe.
211  */
212  if (pcmk_is_set(rsc->flags, pe_rsc_managed)) {
213  crm_warn("%s can't be promoted because %s is not allowed on %s "
214  "(scheduler bug?)",
215  rsc->id, parent->id, pe__node_name(node));
216  } // else the instance is unmanaged and already promoted
217  return NULL;
218 
219  } else if ((local_node->count >= pe__clone_promoted_node_max(parent))
220  && pcmk_is_set(rsc->flags, pe_rsc_managed)) {
221  pe_rsc_trace(rsc,
222  "%s can't be promoted because %s has "
223  "maximum promoted instances already",
224  rsc->id, pe__node_name(node));
225  return NULL;
226  }
227 
228  return local_node;
229 }
230 
242 static gint
243 cmp_promotable_instance(gconstpointer a, gconstpointer b)
244 {
245  const pe_resource_t *rsc1 = (const pe_resource_t *) a;
246  const pe_resource_t *rsc2 = (const pe_resource_t *) b;
247 
248  enum rsc_role_e role1 = RSC_ROLE_UNKNOWN;
249  enum rsc_role_e role2 = RSC_ROLE_UNKNOWN;
250 
251  CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
252 
253  // Check sort index set by pcmk__set_instance_roles()
254  if (rsc1->sort_index > rsc2->sort_index) {
256  "%s has higher promotion priority than %s "
257  "(sort index %d > %d)",
259  return -1;
260  } else if (rsc1->sort_index < rsc2->sort_index) {
262  "%s has lower promotion priority than %s "
263  "(sort index %d < %d)",
265  return 1;
266  }
267 
268  // If those are the same, prefer instance whose current role is higher
269  role1 = rsc1->fns->state(rsc1, TRUE);
270  role2 = rsc2->fns->state(rsc2, TRUE);
271  if (role1 > role2) {
273  "%s has higher promotion priority than %s "
274  "(higher current role)",
275  rsc1->id, rsc2->id);
276  return -1;
277  } else if (role1 < role2) {
279  "%s has lower promotion priority than %s "
280  "(lower current role)",
281  rsc1->id, rsc2->id);
282  return 1;
283  }
284 
285  // Finally, do normal clone instance sorting
286  return pcmk__cmp_instance(a, b);
287 }
288 
300 static void
301 add_sort_index_to_node_weight(gpointer data, gpointer user_data)
302 {
303  const pe_resource_t *child = (const pe_resource_t *) data;
304  pe_resource_t *clone = (pe_resource_t *) user_data;
305 
306  pe_node_t *node = NULL;
307  const pe_node_t *chosen = NULL;
308 
309  if (child->sort_index < 0) {
310  pe_rsc_trace(clone, "Not adding sort index of %s: negative", child->id);
311  return;
312  }
313 
314  chosen = child->fns->location(child, NULL, FALSE);
315  if (chosen == NULL) {
316  pe_rsc_trace(clone, "Not adding sort index of %s: inactive", child->id);
317  return;
318  }
319 
320  node = (pe_node_t *) pe_hash_table_lookup(clone->allowed_nodes,
321  chosen->details->id);
322  CRM_ASSERT(node != NULL);
323 
324  node->weight = pcmk__add_scores(child->sort_index, node->weight);
325  pe_rsc_trace(clone,
326  "Added cumulative priority of %s (%s) to score on %s (now %s)",
327  child->id, pcmk_readable_score(child->sort_index),
328  pe__node_name(node), pcmk_readable_score(node->weight));
329 }
330 
338 static void
339 apply_coloc_to_dependent(gpointer data, gpointer user_data)
340 {
341  pcmk__colocation_t *constraint = (pcmk__colocation_t *) data;
342  pe_resource_t *clone = (pe_resource_t *) user_data;
343  pe_resource_t *primary = constraint->primary;
345  float factor = constraint->score / (float) INFINITY;
346 
347  if (constraint->dependent_role != RSC_ROLE_PROMOTED) {
348  return;
349  }
350  if (constraint->score < INFINITY) {
352  }
353  pe_rsc_trace(clone, "Applying colocation %s (promoted %s with %s) @%s",
354  constraint->id, constraint->dependent->id,
355  constraint->primary->id,
356  pcmk_readable_score(constraint->score));
357  primary->cmds->add_colocated_node_scores(primary, clone->id,
358  &clone->allowed_nodes,
359  constraint->node_attribute, factor,
360  flags);
361 }
362 
370 static void
371 apply_coloc_to_primary(gpointer data, gpointer user_data)
372 {
373  pcmk__colocation_t *constraint = (pcmk__colocation_t *) data;
374  pe_resource_t *clone = (pe_resource_t *) user_data;
375  pe_resource_t *dependent = constraint->dependent;
376  const float factor = constraint->score / (float) INFINITY;
377  const uint32_t flags = pcmk__coloc_select_active
379 
380  if ((constraint->primary_role != RSC_ROLE_PROMOTED)
381  || !pcmk__colocation_has_influence(constraint, NULL)) {
382  return;
383  }
384 
385  pe_rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s",
386  constraint->id, constraint->dependent->id,
387  constraint->primary->id,
388  pcmk_readable_score(constraint->score));
389  dependent->cmds->add_colocated_node_scores(dependent, clone->id,
390  &clone->allowed_nodes,
391  constraint->node_attribute,
392  factor, flags);
393 }
394 
402 static void
403 set_sort_index_to_node_weight(gpointer data, gpointer user_data)
404 {
405  pe_resource_t *child = (pe_resource_t *) data;
406  const pe_resource_t *clone = (const pe_resource_t *) user_data;
407 
408  pe_node_t *chosen = child->fns->location(child, NULL, FALSE);
409 
410  if (!pcmk_is_set(child->flags, pe_rsc_managed)
411  && (child->next_role == RSC_ROLE_PROMOTED)) {
412  child->sort_index = INFINITY;
413  pe_rsc_trace(clone,
414  "Final sort index for %s is INFINITY (unmanaged promoted)",
415  child->id);
416 
417  } else if ((chosen == NULL) || (child->sort_index < 0)) {
418  pe_rsc_trace(clone,
419  "Final sort index for %s is %d (ignoring node weight)",
420  child->id, child->sort_index);
421 
422  } else {
423  const pe_node_t *node = NULL;
424 
425  node = pe_hash_table_lookup(clone->allowed_nodes, chosen->details->id);
426  CRM_ASSERT(node != NULL);
427 
428  child->sort_index = node->weight;
429  pe_rsc_trace(clone,
430  "Merging weights for %s: final sort index for %s is %d",
431  clone->id, child->id, child->sort_index);
432  }
433 }
434 
441 static void
442 sort_promotable_instances(pe_resource_t *clone)
443 {
445  == pcmk_rc_already) {
446  return;
447  }
449 
450  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
451  pe_resource_t *child = (pe_resource_t *) iter->data;
452 
453  pe_rsc_trace(clone,
454  "Merging weights for %s: initial sort index for %s is %d",
455  clone->id, child->id, child->sort_index);
456  }
457  pe__show_node_weights(true, clone, "Before", clone->allowed_nodes,
458  clone->cluster);
459 
460  /* Because the this_with_colocations() and with_this_colocations() methods
461  * boil down to copies of rsc_cons and rsc_cons_lhs for clones, we can use
462  * those here directly for efficiency.
463  */
464  g_list_foreach(clone->children, add_sort_index_to_node_weight, clone);
465  g_list_foreach(clone->rsc_cons, apply_coloc_to_dependent, clone);
466  g_list_foreach(clone->rsc_cons_lhs, apply_coloc_to_primary, clone);
467 
468  // Ban resource from all nodes if it needs a ticket but doesn't have it
470 
471  pe__show_node_weights(true, clone, "After", clone->allowed_nodes,
472  clone->cluster);
473 
474  // Reset sort indexes to final node weights
475  g_list_foreach(clone->children, set_sort_index_to_node_weight, clone);
476 
477  // Finally, sort instances in descending order of promotion priority
478  clone->children = g_list_sort(clone->children, cmp_promotable_instance);
480 }
481 
492 static pe_resource_t *
493 find_active_anon_instance(const pe_resource_t *clone, const char *id,
494  const pe_node_t *node)
495 {
496  for (GList *iter = clone->children; iter; iter = iter->next) {
497  pe_resource_t *child = iter->data;
498  pe_resource_t *active = NULL;
499 
500  // Use ->find_rsc() in case this is a cloned group
501  active = clone->fns->find_rsc(child, id, node,
503  if (active != NULL) {
504  return active;
505  }
506  }
507  return NULL;
508 }
509 
510 /*
511  * \brief Check whether an anonymous clone instance is known on a node
512  *
513  * \param[in] clone Anonymous clone to check
514  * \param[in] id Instance ID (without instance number) to check
515  * \param[in] node Node to check
516  *
517  * \return true if \p id instance of \p clone is known on \p node,
518  * otherwise false
519  */
520 static bool
521 anonymous_known_on(const pe_resource_t *clone, const char *id,
522  const pe_node_t *node)
523 {
524  for (GList *iter = clone->children; iter; iter = iter->next) {
525  pe_resource_t *child = iter->data;
526 
527  /* Use ->find_rsc() because this might be a cloned group, and knowing
528  * that other members of the group are known here implies nothing.
529  */
530  child = clone->fns->find_rsc(child, id, NULL, pe_find_clone);
531  CRM_LOG_ASSERT(child != NULL);
532  if (child != NULL) {
533  if (g_hash_table_lookup(child->known_on, node->details->id)) {
534  return true;
535  }
536  }
537  }
538  return false;
539 }
540 
550 static bool
551 is_allowed(const pe_resource_t *rsc, const pe_node_t *node)
552 {
553  pe_node_t *allowed = pe_hash_table_lookup(rsc->allowed_nodes,
554  node->details->id);
555 
556  return (allowed != NULL) && (allowed->weight >= 0);
557 }
558 
568 static bool
569 promotion_score_applies(const pe_resource_t *rsc, const pe_node_t *node)
570 {
571  char *id = clone_strip(rsc->id);
572  const pe_resource_t *parent = pe__const_top_resource(rsc, false);
573  pe_resource_t *active = NULL;
574  const char *reason = "allowed";
575 
576  // Some checks apply only to anonymous clone instances
577  if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
578 
579  // If instance is active on the node, its score definitely applies
580  active = find_active_anon_instance(parent, id, node);
581  if (active == rsc) {
582  reason = "active";
583  goto check_allowed;
584  }
585 
586  /* If *no* instance is active on this node, this instance's score will
587  * count if it has been probed on this node.
588  */
589  if ((active == NULL) && anonymous_known_on(parent, id, node)) {
590  reason = "probed";
591  goto check_allowed;
592  }
593  }
594 
595  /* If this clone's status is unknown on *all* nodes (e.g. cluster startup),
596  * take all instances' scores into account, to make sure we use any
597  * permanent promotion scores.
598  */
599  if ((rsc->running_on == NULL) && (g_hash_table_size(rsc->known_on) == 0)) {
600  reason = "none probed";
601  goto check_allowed;
602  }
603 
604  /* Otherwise, we've probed and/or started the resource *somewhere*, so
605  * consider promotion scores on nodes where we know the status.
606  */
607  if ((pe_hash_table_lookup(rsc->known_on, node->details->id) != NULL)
608  || (pe_find_node_id(rsc->running_on, node->details->id) != NULL)) {
609  reason = "known";
610  } else {
611  pe_rsc_trace(rsc,
612  "Ignoring %s promotion score (for %s) on %s: not probed",
613  rsc->id, id, pe__node_name(node));
614  free(id);
615  return false;
616  }
617 
618 check_allowed:
619  if (is_allowed(rsc, node)) {
620  pe_rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s",
621  rsc->id, id, pe__node_name(node), reason);
622  free(id);
623  return true;
624  }
625 
626  pe_rsc_trace(rsc, "Ignoring %s promotion score (for %s) on %s: not allowed",
627  rsc->id, id, pe__node_name(node));
628  free(id);
629  return false;
630 }
631 
642 static const char *
643 promotion_attr_value(const pe_resource_t *rsc, const pe_node_t *node,
644  const char *name)
645 {
646  char *attr_name = NULL;
647  const char *attr_value = NULL;
648 
649  CRM_CHECK((rsc != NULL) && (node != NULL) && (name != NULL), return NULL);
650 
651  attr_name = pcmk_promotion_score_name(name);
652  attr_value = pe_node_attribute_calculated(node, attr_name, rsc);
653  free(attr_name);
654  return attr_value;
655 }
656 
667 static int
668 promotion_score(const pe_resource_t *rsc, const pe_node_t *node,
669  bool *is_default)
670 {
671  char *name = NULL;
672  const char *attr_value = NULL;
673 
674  if (is_default != NULL) {
675  *is_default = true;
676  }
677 
678  CRM_CHECK((rsc != NULL) && (node != NULL), return 0);
679 
680  /* If this is an instance of a cloned group, the promotion score is the sum
681  * of all members' promotion scores.
682  */
683  if (rsc->children != NULL) {
684  int score = 0;
685 
686  for (const GList *iter = rsc->children;
687  iter != NULL; iter = iter->next) {
688 
689  const pe_resource_t *child = (const pe_resource_t *) iter->data;
690  bool child_default = false;
691  int child_score = promotion_score(child, node, &child_default);
692 
693  if (!child_default && (is_default != NULL)) {
694  *is_default = false;
695  }
696  score += child_score;
697  }
698  return score;
699  }
700 
701  if (!promotion_score_applies(rsc, node)) {
702  return 0;
703  }
704 
705  /* For the promotion score attribute name, use the name the resource is
706  * known as in resource history, since that's what crm_attribute --promotion
707  * would have used.
708  */
709  name = (rsc->clone_name == NULL)? rsc->id : rsc->clone_name;
710 
711  attr_value = promotion_attr_value(rsc, node, name);
712  if (attr_value != NULL) {
713  pe_rsc_trace(rsc, "Promotion score for %s on %s = %s",
714  name, pe__node_name(node), pcmk__s(attr_value, "(unset)"));
715  } else if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
716  /* If we don't have any resource history yet, we won't have clone_name.
717  * In that case, for anonymous clones, try the resource name without
718  * any instance number.
719  */
720  name = clone_strip(rsc->id);
721  if (strcmp(rsc->id, name) != 0) {
722  attr_value = promotion_attr_value(rsc, node, name);
723  pe_rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s",
724  name, pe__node_name(node), rsc->id,
725  pcmk__s(attr_value, "(unset)"));
726  }
727  free(name);
728  }
729 
730  if (attr_value == NULL) {
731  return 0;
732  }
733 
734  if (is_default != NULL) {
735  *is_default = false;
736  }
737  return char2score(attr_value);
738 }
739 
746 void
748 {
750  return;
751  }
752 
753  for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
754  pe_resource_t *child_rsc = (pe_resource_t *) iter->data;
755 
756  GHashTableIter iter;
757  pe_node_t *node = NULL;
758  int score, new_score;
759 
760  g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
761  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
762  if (!pcmk__node_available(node, false, false)) {
763  /* This node will never be promoted, so don't apply the
764  * promotion score, as that may lead to clone shuffling.
765  */
766  continue;
767  }
768 
769  score = promotion_score(child_rsc, node, NULL);
770  if (score > 0) {
771  new_score = pcmk__add_scores(node->weight, score);
772  if (new_score != node->weight) { // Could remain INFINITY
773  node->weight = new_score;
774  pe_rsc_trace(rsc,
775  "Added %s promotion priority (%s) to score "
776  "on %s (now %s)",
777  child_rsc->id, pcmk_readable_score(score),
778  pe__node_name(node),
779  pcmk_readable_score(new_score));
780  }
781  }
782 
783  if (score > child_rsc->priority) {
784  pe_rsc_trace(rsc,
785  "Updating %s priority to promotion score (%d->%d)",
786  child_rsc->id, child_rsc->priority, score);
787  child_rsc->priority = score;
788  }
789  }
790  }
791 }
792 
800 static void
801 set_current_role_unpromoted(void *data, void *user_data)
802 {
803  pe_resource_t *rsc = (pe_resource_t *) data;
804 
805  if (rsc->role == RSC_ROLE_STARTED) {
806  // Promotable clones should use unpromoted role instead of started
807  rsc->role = RSC_ROLE_UNPROMOTED;
808  }
809  g_list_foreach(rsc->children, set_current_role_unpromoted, NULL);
810 }
811 
819 static void
820 set_next_role_unpromoted(void *data, void *user_data)
821 {
822  pe_resource_t *rsc = (pe_resource_t *) data;
823  GList *assigned = NULL;
824 
825  rsc->fns->location(rsc, &assigned, FALSE);
826  if (assigned == NULL) {
827  pe__set_next_role(rsc, RSC_ROLE_STOPPED, "stopped instance");
828  } else {
829  pe__set_next_role(rsc, RSC_ROLE_UNPROMOTED, "unpromoted instance");
830  g_list_free(assigned);
831  }
832  g_list_foreach(rsc->children, set_next_role_unpromoted, NULL);
833 }
834 
842 static void
843 set_next_role_promoted(void *data, gpointer user_data)
844 {
845  pe_resource_t *rsc = (pe_resource_t *) data;
846 
847  if (rsc->next_role == RSC_ROLE_UNKNOWN) {
848  pe__set_next_role(rsc, RSC_ROLE_PROMOTED, "promoted instance");
849  }
850  g_list_foreach(rsc->children, set_next_role_promoted, NULL);
851 }
852 
859 static void
860 show_promotion_score(pe_resource_t *instance)
861 {
862  pe_node_t *chosen = instance->fns->location(instance, NULL, FALSE);
863 
865  && !pcmk__is_daemon && (instance->cluster->priv != NULL)) {
866 
867  pcmk__output_t *out = instance->cluster->priv;
868 
869  out->message(out, "promotion-score", instance, chosen,
870  pcmk_readable_score(instance->sort_index));
871  } else {
872  pe_rsc_debug(pe__const_top_resource(instance, false),
873  "%s promotion score on %s: sort=%s priority=%s",
874  instance->id,
875  ((chosen == NULL)? "none" : pe__node_name(chosen)),
876  pcmk_readable_score(instance->sort_index),
877  pcmk_readable_score(instance->priority));
878  }
879 }
880 
888 static void
889 set_instance_priority(gpointer data, gpointer user_data)
890 {
891  pe_resource_t *instance = (pe_resource_t *) data;
892  const pe_resource_t *clone = (const pe_resource_t *) user_data;
893  const pe_node_t *chosen = NULL;
894  enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
895  GList *list = NULL;
896 
897  pe_rsc_trace(clone, "Assigning priority for %s: %s", instance->id,
898  role2text(instance->next_role));
899 
900  if (instance->fns->state(instance, TRUE) == RSC_ROLE_STARTED) {
901  set_current_role_unpromoted(instance, NULL);
902  }
903 
904  // Only an instance that will be active can be promoted
905  chosen = instance->fns->location(instance, &list, FALSE);
906  if (pcmk__list_of_multiple(list)) {
907  pcmk__config_err("Cannot promote non-colocated child %s",
908  instance->id);
909  }
910  g_list_free(list);
911  if (chosen == NULL) {
912  return;
913  }
914 
915  next_role = instance->fns->state(instance, FALSE);
916  switch (next_role) {
917  case RSC_ROLE_STARTED:
918  case RSC_ROLE_UNKNOWN:
919  // Set instance priority to its promotion score (or -1 if none)
920  {
921  bool is_default = false;
922 
923  instance->priority = promotion_score(instance, chosen,
924  &is_default);
925  if (is_default) {
926  /*
927  * Default to -1 if no value is set. This allows
928  * instances eligible for promotion to be specified
929  * based solely on rsc_location constraints, but
930  * prevents any instance from being promoted if neither
931  * a constraint nor a promotion score is present
932  */
933  instance->priority = -1;
934  }
935  }
936  break;
937 
938  case RSC_ROLE_UNPROMOTED:
939  case RSC_ROLE_STOPPED:
940  // Instance can't be promoted
941  instance->priority = -INFINITY;
942  break;
943 
944  case RSC_ROLE_PROMOTED:
945  // Nothing needed (re-creating actions after scheduling fencing)
946  break;
947 
948  default:
949  CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s",
950  next_role, instance->id));
951  }
952 
953  // Add relevant location constraint scores for promoted role
954  apply_promoted_locations(instance, instance->rsc_location, chosen);
955  apply_promoted_locations(instance, clone->rsc_location, chosen);
956 
957  // Consider instance's role-based colocations with other resources
958  list = pcmk__this_with_colocations(instance);
959  for (GList *iter = list; iter != NULL; iter = iter->next) {
960  pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data;
961 
962  instance->cmds->apply_coloc_score(instance, cons->primary, cons, true);
963  }
964  g_list_free(list);
965 
966  instance->sort_index = instance->priority;
967  if (next_role == RSC_ROLE_PROMOTED) {
968  instance->sort_index = INFINITY;
969  }
970  pe_rsc_trace(clone, "Assigning %s priority = %d",
971  instance->id, instance->priority);
972 }
973 
981 static void
982 set_instance_role(gpointer data, gpointer user_data)
983 {
984  pe_resource_t *instance = (pe_resource_t *) data;
985  int *count = (int *) user_data;
986 
987  const pe_resource_t *clone = pe__const_top_resource(instance, false);
988  pe_node_t *chosen = NULL;
989 
990  show_promotion_score(instance);
991 
992  if (instance->sort_index < 0) {
993  pe_rsc_trace(clone, "Not supposed to promote instance %s",
994  instance->id);
995 
996  } else if ((*count < pe__clone_promoted_max(instance))
997  || !pcmk_is_set(clone->flags, pe_rsc_managed)) {
998  chosen = node_to_be_promoted_on(instance);
999  }
1000 
1001  if (chosen == NULL) {
1002  set_next_role_unpromoted(instance, NULL);
1003  return;
1004  }
1005 
1006  if ((instance->role < RSC_ROLE_PROMOTED)
1007  && !pcmk_is_set(instance->cluster->flags, pe_flag_have_quorum)
1008  && (instance->cluster->no_quorum_policy == no_quorum_freeze)) {
1009  crm_notice("Clone instance %s cannot be promoted without quorum",
1010  instance->id);
1011  set_next_role_unpromoted(instance, NULL);
1012  return;
1013  }
1014 
1015  chosen->count++;
1016  pe_rsc_info(clone, "Choosing %s (%s) on %s for promotion",
1017  instance->id, role2text(instance->role),
1018  pe__node_name(chosen));
1019  set_next_role_promoted(instance, NULL);
1020  (*count)++;
1021 }
1022 
1029 void
1031 {
1032  int promoted = 0;
1033  GHashTableIter iter;
1034  pe_node_t *node = NULL;
1035 
1036  // Repurpose count to track the number of promoted instances allocated
1037  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1038  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1039  node->count = 0;
1040  }
1041 
1042  // Set instances' promotion priorities and sort by highest priority first
1043  g_list_foreach(rsc->children, set_instance_priority, rsc);
1044  sort_promotable_instances(rsc);
1045 
1046  // Choose the first N eligible instances to be promoted
1047  g_list_foreach(rsc->children, set_instance_role, &promoted);
1048  pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
1049  rsc->id, promoted, pe__clone_promoted_max(rsc));
1050 }
1051 
1061 static void
1062 create_promotable_instance_actions(pe_resource_t *clone,
1063  bool *any_promoting, bool *any_demoting)
1064 {
1065  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1066  pe_resource_t *instance = (pe_resource_t *) iter->data;
1067 
1068  instance->cmds->create_actions(instance);
1069  check_for_role_change(instance, any_demoting, any_promoting);
1070  }
1071 }
1072 
1083 static void
1084 reset_instance_priorities(pe_resource_t *clone)
1085 {
1086  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1087  pe_resource_t *instance = (pe_resource_t *) iter->data;
1088 
1089  instance->priority = clone->priority;
1090  }
1091 }
1092 
1099 void
1101 {
1102  bool any_promoting = false;
1103  bool any_demoting = false;
1104 
1105  // Create actions for each clone instance individually
1106  create_promotable_instance_actions(clone, &any_promoting, &any_demoting);
1107 
1108  // Create pseudo-actions for clone as a whole
1109  pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting);
1110 
1111  // Undo our temporary repurposing of resource priority for instances
1112  reset_instance_priorities(clone);
1113 }
1114 
1121 void
1123 {
1124  pe_resource_t *previous = NULL; // Needed for ordered clones
1125 
1127 
1128  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1129  pe_resource_t *instance = (pe_resource_t *) iter->data;
1130 
1131  // Demote before promote
1133  instance, RSC_PROMOTE,
1135 
1136  order_instance_promotion(clone, instance, previous);
1137  order_instance_demotion(clone, instance, previous);
1138  previous = instance;
1139  }
1140 }
1141 
1150 static void
1151 update_dependent_allowed_nodes(pe_resource_t *dependent,
1152  const pe_node_t *primary_node,
1153  const pcmk__colocation_t *colocation)
1154 {
1155  GHashTableIter iter;
1156  pe_node_t *node = NULL;
1157  const char *primary_value = NULL;
1158  const char *attr = NULL;
1159 
1160  if (colocation->score >= INFINITY) {
1161  return; // Colocation is mandatory, so allowed node scores don't matter
1162  }
1163 
1164  // Get value of primary's colocation node attribute
1165  attr = colocation->node_attribute;
1166  if (attr == NULL) {
1167  attr = CRM_ATTR_UNAME;
1168  }
1169  primary_value = pe_node_attribute_raw(primary_node, attr);
1170 
1171  pe_rsc_trace(colocation->primary,
1172  "Applying %s (%s with %s on %s by %s @%d) to %s",
1173  colocation->id, colocation->dependent->id,
1174  colocation->primary->id, pe__node_name(primary_node), attr,
1175  colocation->score, dependent->id);
1176 
1177  g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1178  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1179  const char *dependent_value = pe_node_attribute_raw(node, attr);
1180 
1181  if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) {
1182  node->weight = pcmk__add_scores(node->weight, colocation->score);
1183  pe_rsc_trace(colocation->primary,
1184  "Added %s score (%s) to %s (now %s)",
1185  colocation->id, pcmk_readable_score(colocation->score),
1186  pe__node_name(node),
1187  pcmk_readable_score(node->weight));
1188  }
1189  }
1190 }
1191 
1199 void
1201  pe_resource_t *dependent,
1202  const pcmk__colocation_t *colocation)
1203 {
1204  GList *affected_nodes = NULL;
1205 
1206  /* Build a list of all nodes where an instance of the primary will be, and
1207  * (for optional colocations) update the dependent's allowed node scores for
1208  * each one.
1209  */
1210  for (GList *iter = primary->children; iter != NULL; iter = iter->next) {
1211  pe_resource_t *instance = (pe_resource_t *) iter->data;
1212  pe_node_t *node = instance->fns->location(instance, NULL, FALSE);
1213 
1214  if (node == NULL) {
1215  continue;
1216  }
1217  if (instance->fns->state(instance, FALSE) == colocation->primary_role) {
1218  update_dependent_allowed_nodes(dependent, node, colocation);
1219  affected_nodes = g_list_prepend(affected_nodes, node);
1220  }
1221  }
1222 
1223  /* For mandatory colocations, add the primary's node weight to the
1224  * dependent's node weight for each affected node, and ban the dependent
1225  * from all other nodes.
1226  *
1227  * However, skip this for promoted-with-promoted colocations, otherwise
1228  * inactive dependent instances can't start (in the unpromoted role).
1229  */
1230  if ((colocation->score >= INFINITY)
1231  && ((colocation->dependent_role != RSC_ROLE_PROMOTED)
1232  || (colocation->primary_role != RSC_ROLE_PROMOTED))) {
1233 
1234  pe_rsc_trace(colocation->primary,
1235  "Applying %s (mandatory %s with %s) to %s",
1236  colocation->id, colocation->dependent->id,
1237  colocation->primary->id, dependent->id);
1238  node_list_exclude(dependent->allowed_nodes, affected_nodes,
1239  TRUE);
1240  }
1241  g_list_free(affected_nodes);
1242 }
1243 
1252 void
1254  pe_resource_t *dependent,
1255  const pcmk__colocation_t *colocation)
1256 {
1257  pe_resource_t *primary_instance = NULL;
1258 
1259  // Look for a primary instance where dependent will be
1260  primary_instance = pcmk__find_compatible_instance(dependent, primary,
1261  colocation->primary_role,
1262  false);
1263 
1264  if (primary_instance != NULL) {
1265  // Add primary instance's priority to dependent's
1266  int new_priority = pcmk__add_scores(dependent->priority,
1267  colocation->score);
1268 
1269  pe_rsc_trace(colocation->primary,
1270  "Applying %s (%s with %s) to %s priority (%s + %s = %s)",
1271  colocation->id, colocation->dependent->id,
1272  colocation->primary->id, dependent->id,
1273  pcmk_readable_score(dependent->priority),
1274  pcmk_readable_score(colocation->score),
1275  pcmk_readable_score(new_priority));
1276  dependent->priority = new_priority;
1277 
1278  } else if (colocation->score >= INFINITY) {
1279  // Mandatory colocation, but primary won't be here
1280  pe_rsc_trace(colocation->primary,
1281  "Applying %s (%s with %s) to %s: can't be promoted",
1282  colocation->id, colocation->dependent->id,
1283  colocation->primary->id, dependent->id);
1284  dependent->priority = -INFINITY;
1285  }
1286 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:235
enum rsc_role_e role_filter
Definition: internal.h:194
enum pe_quorum_policy no_quorum_policy
Definition: pe_types.h:172
#define crm_notice(fmt, args...)
Definition: logging.h:379
GHashTable * known_on
Definition: pe_types.h:399
const char * pe_node_attribute_calculated(const pe_node_t *node, const char *name, const pe_resource_t *rsc)
Definition: common.c:518
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:49
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
GList * rsc_cons
Definition: pe_types.h:389
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition: internal.h:385
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:86
const char * name
Definition: cib.c:24
int(* message)(pcmk__output_t *out, const char *message_id,...)
G_GNUC_INTERNAL pe_resource_t * pcmk__find_compatible_instance(const pe_resource_t *match_rsc, const pe_resource_t *rsc, enum rsc_role_e role, bool current)
enum rsc_role_e role
Definition: pe_types.h:402
GList * children
Definition: pe_types.h:409
int count
Definition: pe_types.h:267
resource_alloc_functions_t * cmds
Definition: pe_types.h:359
int pe__clone_promoted_max(const pe_resource_t *clone)
Definition: clone.c:96
void pcmk__update_dependent_with_promotable(const pe_resource_t *primary, pe_resource_t *dependent, const pcmk__colocation_t *colocation)
Update dependent for a colocation with a promotable clone.
enum rsc_role_e next_role
Definition: pe_types.h:403
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
#define pcmk__config_err(fmt...)
#define pe_rsc_unique
Definition: pe_types.h:278
int pe__set_clone_flag(pe_resource_t *clone, enum pe__clone_flags flag)
Definition: clone.c:1308
resource_object_functions_t * fns
Definition: pe_types.h:358
void pcmk__update_promotable_dependent_priority(const pe_resource_t *primary, pe_resource_t *dependent, const pcmk__colocation_t *colocation)
pe_resource_t * dependent
void node_list_exclude(GHashTable *list, GList *list2, gboolean merge_scores)
Definition: utils.c:108
const pe_resource_t * pe__const_top_resource(const pe_resource_t *rsc, bool include_bundle)
Definition: complex.c:947
char * pcmk_promotion_score_name(const char *rsc_id)
Return the name of the node attribute used as a promotion score.
Definition: attrs.c:80
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:219
GList * rsc_cons_lhs
Definition: pe_types.h:388
#define pe_flag_have_quorum
Definition: pe_types.h:111
const char * action
Definition: pcmk_fence.c:30
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:77
const char * role2text(enum rsc_role_e role)
Definition: common.c:450
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:55
int weight
Definition: pe_types.h:265
#define pe_rsc_merging
Definition: pe_types.h:284
#define crm_warn(fmt, args...)
Definition: logging.h:378
void pcmk__set_instance_roles(pe_resource_t *rsc)
G_GNUC_INTERNAL bool pcmk__node_available(const pe_node_t *node, bool consider_score, bool consider_guest)
void pcmk__order_promotable_instances(pe_resource_t *clone)
const char * pe_node_attribute_raw(const pe_node_t *node, const char *name)
Definition: common.c:558
char * clone_strip(const char *last_rsc_id)
Definition: unpack.c:1658
pe_resource_t * primary
match only clone instances
Definition: pe_types.h:103
#define CRM_ATTR_UNAME
Definition: crm.h:113
GList * pcmk__this_with_colocations(const pe_resource_t *rsc)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
int pe__clone_promoted_node_max(const pe_resource_t *clone)
Definition: clone.c:113
struct pe_node_shared_s * details
Definition: pe_types.h:268
void pe__create_promotable_pseudo_ops(pe_resource_t *clone, bool any_promoting, bool any_demoting)
Definition: clone.c:1331
G_GNUC_INTERNAL gint pcmk__cmp_instance(gconstpointer a, gconstpointer b)
bool pcmk__is_daemon
Definition: logging.c:47
unsigned long long flags
Definition: pe_types.h:373
char * clone_name
Definition: pe_types.h:348
pe_resource_t rsc2
void(* create_actions)(pe_resource_t *rsc)
GList * actions
Definition: pe_types.h:391
#define RSC_DEMOTED
Definition: crm.h:208
const char * id
Definition: pe_types.h:231
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
void(* apply_coloc_score)(pe_resource_t *dependent, const pe_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent)
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:54
G_GNUC_INTERNAL void pcmk__require_promotion_tickets(pe_resource_t *rsc)
pe_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition: status.c:448
int sort_index
Definition: pe_types.h:367
#define crm_err(fmt, args...)
Definition: logging.h:377
#define CRM_ASSERT(expr)
Definition: results.h:42
bool pe__clone_is_ordered(const pe_resource_t *clone)
Definition: clone.c:1289
#define RSC_PROMOTE
Definition: crm.h:205
void pe__set_next_role(pe_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1166
pe_resource_t rsc1
void pcmk__add_promotion_scores(pe_resource_t *rsc)
This structure contains everything that makes up a single output formatter.
GList * rsc_location
Definition: pe_types.h:390
G_GNUC_INTERNAL void pcmk__promotable_restart_ordering(pe_resource_t *rsc)
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:83
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
GList * running_on
Definition: pe_types.h:398
pe_working_set_t * cluster
Definition: pe_types.h:353
const char * node_attribute
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:116
#define RSC_PROMOTED
Definition: crm.h:206
void pcmk__create_promotable_actions(pe_resource_t *clone)
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:50
unsigned long long flags
Definition: pe_types.h:169
const char * parent
Definition: cib.c:25
pe_resource_t *(* find_rsc)(pe_resource_t *parent, const char *search, const pe_node_t *node, int flags)
Definition: pe_types.h:46
#define pe_flag_show_scores
Definition: pe_types.h:150
#define pe_rsc_managed
Definition: pe_types.h:273
uint64_t flags
Definition: remote.c:215
void(* add_colocated_node_scores)(pe_resource_t *rsc, const char *log_id, GHashTable **nodes, const char *attr, float factor, uint32_t flags)
match resource active on specified node
Definition: pe_types.h:104
#define RSC_DEMOTE
Definition: crm.h:207
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:48
char * id
Definition: pe_types.h:347
GHashTable * allowed_nodes
Definition: pe_types.h:400