pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_sched_promotable.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 <crm/common/xml.h>
13 #include <pacemaker-internal.h>
14 
15 #include "libpacemaker_private.h"
16 
25 static void
26 order_instance_promotion(pcmk_resource_t *clone, pcmk_resource_t *child,
27  pcmk_resource_t *last)
28 {
29  // "Promote clone" -> promote instance -> "clone promoted"
31  child, PCMK_ACTION_PROMOTE,
34  clone, PCMK_ACTION_PROMOTED,
36 
37  // If clone is ordered, order this instance relative to last
38  if ((last != NULL) && pe__clone_is_ordered(clone)) {
40  child, PCMK_ACTION_PROMOTE,
42  }
43 }
44 
53 static void
54 order_instance_demotion(pcmk_resource_t *clone, pcmk_resource_t *child,
55  pcmk_resource_t *last)
56 {
57  // "Demote clone" -> demote instance -> "clone demoted"
62  clone, PCMK_ACTION_DEMOTED,
64 
65  // If clone is ordered, order this instance relative to last
66  if ((last != NULL) && pe__clone_is_ordered(clone)) {
69  }
70 }
71 
80 static void
81 check_for_role_change(const pcmk_resource_t *rsc, bool *demoting,
82  bool *promoting)
83 {
84  const GList *iter = NULL;
85 
86  // If this is a cloned group, check group members recursively
87  if (rsc->priv->children != NULL) {
88  for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
89  check_for_role_change((const pcmk_resource_t *) iter->data,
90  demoting, promoting);
91  }
92  return;
93  }
94 
95  for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
96  const pcmk_action_t *action = (const pcmk_action_t *) iter->data;
97 
98  if (*promoting && *demoting) {
99  return;
100 
101  } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
102  continue;
103 
104  } else if (pcmk__str_eq(PCMK_ACTION_DEMOTE, action->task,
105  pcmk__str_none)) {
106  *demoting = true;
107 
108  } else if (pcmk__str_eq(PCMK_ACTION_PROMOTE, action->task,
109  pcmk__str_none)) {
110  *promoting = true;
111  }
112  }
113 }
114 
127 static void
128 apply_promoted_locations(pcmk_resource_t *child,
129  const GList *location_constraints,
130  const pcmk_node_t *chosen)
131 {
132  for (const GList *iter = location_constraints; iter; iter = iter->next) {
133  const pcmk__location_t *location = iter->data;
134  const pcmk_node_t *constraint_node = NULL;
135 
136  if (location->role_filter == pcmk_role_promoted) {
137  constraint_node = pe_find_node_id(location->nodes,
138  chosen->priv->id);
139  }
140  if (constraint_node != NULL) {
141  int new_priority = pcmk__add_scores(child->priv->priority,
142  constraint_node->assign->score);
143 
144  pcmk__rsc_trace(child,
145  "Applying location %s to %s promotion priority on "
146  "%s: %s + %s = %s",
147  location->id, child->id,
148  pcmk__node_name(constraint_node),
150  pcmk_readable_score(constraint_node->assign->score),
151  pcmk_readable_score(new_priority));
152  child->priv->priority = new_priority;
153  }
154  }
155 }
156 
165 static pcmk_node_t *
166 node_to_be_promoted_on(const pcmk_resource_t *rsc)
167 {
168  pcmk_node_t *node = NULL;
169  pcmk_node_t *local_node = NULL;
170  const pcmk_resource_t *parent = NULL;
171 
172  // If this is a cloned group, bail if any group member can't be promoted
173  for (GList *iter = rsc->priv->children;
174  iter != NULL; iter = iter->next) {
175 
176  pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
177 
178  if (node_to_be_promoted_on(child) == NULL) {
179  pcmk__rsc_trace(rsc,
180  "%s can't be promoted because member %s can't",
181  rsc->id, child->id);
182  return NULL;
183  }
184  }
185 
186  node = rsc->priv->fns->location(rsc, NULL, pcmk__rsc_node_assigned);
187  if (node == NULL) {
188  pcmk__rsc_trace(rsc, "%s can't be promoted because it won't be active",
189  rsc->id);
190  return NULL;
191 
192  } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
193  if (rsc->priv->fns->state(rsc, TRUE) == pcmk_role_promoted) {
194  crm_notice("Unmanaged instance %s will be left promoted on %s",
195  rsc->id, pcmk__node_name(node));
196  } else {
197  pcmk__rsc_trace(rsc, "%s can't be promoted because it is unmanaged",
198  rsc->id);
199  return NULL;
200  }
201 
202  } else if (rsc->priv->priority < 0) {
203  pcmk__rsc_trace(rsc,
204  "%s can't be promoted because its promotion priority "
205  "%d is negative",
206  rsc->id, rsc->priv->priority);
207  return NULL;
208 
209  } else if (!pcmk__node_available(node, false, true)) {
210  pcmk__rsc_trace(rsc,
211  "%s can't be promoted because %s can't run resources",
212  rsc->id, pcmk__node_name(node));
213  return NULL;
214  }
215 
216  parent = pe__const_top_resource(rsc, false);
217  local_node = g_hash_table_lookup(parent->priv->allowed_nodes,
218  node->priv->id);
219 
220  if (local_node == NULL) {
221  /* It should not be possible for the scheduler to have assigned the
222  * instance to a node where its parent is not allowed, but it's good to
223  * have a fail-safe.
224  */
225  if (pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
227  "%s can't be promoted because %s is not allowed "
228  "on %s (scheduler bug?)",
229  rsc->id, parent->id, pcmk__node_name(node));
230  } // else the instance is unmanaged and already promoted
231  return NULL;
232 
233  } else if ((local_node->assign->count >= pe__clone_promoted_node_max(parent))
234  && pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
235  pcmk__rsc_trace(rsc,
236  "%s can't be promoted because %s has "
237  "maximum promoted instances already",
238  rsc->id, pcmk__node_name(node));
239  return NULL;
240  }
241 
242  return local_node;
243 }
244 
256 static gint
257 cmp_promotable_instance(gconstpointer a, gconstpointer b)
258 {
259  const pcmk_resource_t *rsc1 = (const pcmk_resource_t *) a;
260  const pcmk_resource_t *rsc2 = (const pcmk_resource_t *) b;
261 
262  enum rsc_role_e role1 = pcmk_role_unknown;
263  enum rsc_role_e role2 = pcmk_role_unknown;
264 
265  pcmk__assert((rsc1 != NULL) && (rsc2 != NULL));
266 
267  // Check promotion priority set by pcmk__set_instance_roles()
268  if (rsc1->priv->promotion_priority > rsc2->priv->promotion_priority) {
269  pcmk__rsc_trace(rsc1,
270  "%s has higher promotion priority (%s) than %s (%d)",
271  rsc1->id,
273  rsc2->id, rsc2->priv->promotion_priority);
274  return -1;
275  }
276 
277  if (rsc1->priv->promotion_priority < rsc2->priv->promotion_priority) {
278  pcmk__rsc_trace(rsc1,
279  "%s has lower promotion priority (%s) than %s (%d)",
280  rsc1->id,
282  rsc2->id, rsc2->priv->promotion_priority);
283  return 1;
284  }
285 
286  // If those are the same, prefer instance whose current role is higher
287  role1 = rsc1->priv->fns->state(rsc1, TRUE);
288  role2 = rsc2->priv->fns->state(rsc2, TRUE);
289  if (role1 > role2) {
290  pcmk__rsc_trace(rsc1,
291  "%s has higher promotion priority than %s "
292  "(higher current role)",
293  rsc1->id, rsc2->id);
294  return -1;
295  } else if (role1 < role2) {
296  pcmk__rsc_trace(rsc1,
297  "%s has lower promotion priority than %s "
298  "(lower current role)",
299  rsc1->id, rsc2->id);
300  return 1;
301  }
302 
303  // Finally, do normal clone instance sorting
304  return pcmk__cmp_instance(a, b);
305 }
306 
318 static void
319 add_promotion_priority_to_node_score(gpointer data, gpointer user_data)
320 {
321  const pcmk_resource_t *child = (const pcmk_resource_t *) data;
322  pcmk_resource_t *clone = (pcmk_resource_t *) user_data;
323 
324  pcmk_node_t *node = NULL;
325  const pcmk_node_t *chosen = NULL;
326  const int promotion_priority = child->priv->promotion_priority;
327 
328  if (promotion_priority < 0) {
329  pcmk__rsc_trace(clone,
330  "Not adding promotion priority of %s: negative (%s)",
331  child->id, pcmk_readable_score(promotion_priority));
332  return;
333  }
334 
335  chosen = child->priv->fns->location(child, NULL, pcmk__rsc_node_assigned);
336  if (chosen == NULL) {
337  pcmk__rsc_trace(clone, "Not adding promotion priority of %s: inactive",
338  child->id);
339  return;
340  }
341 
342  node = g_hash_table_lookup(clone->priv->allowed_nodes,
343  chosen->priv->id);
344  pcmk__assert(node != NULL);
345 
346  node->assign->score = pcmk__add_scores(promotion_priority,
347  node->assign->score);
348  pcmk__rsc_trace(clone,
349  "Added cumulative priority of %s (%s) to score on %s "
350  "(now %d)",
351  child->id, pcmk_readable_score(promotion_priority),
352  pcmk__node_name(node), node->assign->score);
353 }
354 
362 static void
363 apply_coloc_to_primary(gpointer data, gpointer user_data)
364 {
365  pcmk__colocation_t *colocation = data;
366  pcmk_resource_t *clone = user_data;
367  pcmk_resource_t *dependent = colocation->dependent;
368  const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
369  const uint32_t flags = pcmk__coloc_select_active
371 
372  if ((colocation->primary_role != pcmk_role_promoted)
373  || !pcmk__colocation_has_influence(colocation, NULL)) {
374  return;
375  }
376 
377  pcmk__rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s",
378  colocation->id, colocation->dependent->id,
379  colocation->primary->id,
380  pcmk_readable_score(colocation->score));
381  dependent->priv->cmds->add_colocated_node_scores(dependent, clone,
382  clone->id,
383  &(clone->priv->allowed_nodes),
384  colocation, factor, flags);
385 }
386 
394 static void
395 set_promotion_priority_to_node_score(gpointer data, gpointer user_data)
396 {
397  pcmk_resource_t *child = (pcmk_resource_t *) data;
398  const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
399 
400  pcmk_node_t *chosen = child->priv->fns->location(child, NULL,
402 
403  if (!pcmk_is_set(child->flags, pcmk__rsc_managed)
404  && (child->priv->next_role == pcmk_role_promoted)) {
406  pcmk__rsc_trace(clone,
407  "Final promotion priority for %s is %s "
408  "(unmanaged promoted)",
410 
411  } else if (chosen == NULL) {
413  pcmk__rsc_trace(clone,
414  "Final promotion priority for %s is %s "
415  "(will not be active)",
417 
418  } else if (child->priv->promotion_priority < 0) {
419  pcmk__rsc_trace(clone,
420  "Final promotion priority for %s is %s "
421  "(ignoring node score)",
422  child->id,
424 
425  } else {
426  const pcmk_node_t *node = NULL;
427 
428  node = g_hash_table_lookup(clone->priv->allowed_nodes,
429  chosen->priv->id);
430 
431  pcmk__assert(node != NULL);
432  child->priv->promotion_priority = node->assign->score;
433  pcmk__rsc_trace(clone,
434  "Adding scores for %s: "
435  "final promotion priority for %s is %s",
436  clone->id, child->id,
438  }
439 }
440 
447 static void
448 sort_promotable_instances(pcmk_resource_t *clone)
449 {
450  GList *colocations = NULL;
451 
453  == pcmk_rc_already) {
454  return;
455  }
457 
458  for (GList *iter = clone->priv->children;
459  iter != NULL; iter = iter->next) {
460 
461  pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
462 
463  pcmk__rsc_trace(clone,
464  "Adding scores for %s: "
465  "initial promotion priority for %s is %s",
466  clone->id, child->id,
468  }
469  pe__show_node_scores(true, clone, "Before", clone->priv->allowed_nodes,
470  clone->priv->scheduler);
471 
472  g_list_foreach(clone->priv->children,
473  add_promotion_priority_to_node_score, clone);
474 
475  // "this with" colocations were already applied via set_instance_priority()
476  colocations = pcmk__with_this_colocations(clone);
477  g_list_foreach(colocations, apply_coloc_to_primary, clone);
478  g_list_free(colocations);
479 
480  // Ban resource from all nodes if it needs a ticket but doesn't have it
482 
483  pe__show_node_scores(true, clone, "After", clone->priv->allowed_nodes,
484  clone->priv->scheduler);
485 
486  // Reset promotion priorities to final node scores
487  g_list_foreach(clone->priv->children,
488  set_promotion_priority_to_node_score, clone);
489 
490  // Finally, sort instances in descending order of promotion priority
491  clone->priv->children = g_list_sort(clone->priv->children,
492  cmp_promotable_instance);
494 }
495 
506 static pcmk_resource_t *
507 find_active_anon_instance(const pcmk_resource_t *clone, const char *id,
508  const pcmk_node_t *node)
509 {
510  for (GList *iter = clone->priv->children; iter; iter = iter->next) {
511  pcmk_resource_t *child = iter->data;
512  pcmk_resource_t *active = NULL;
513 
514  // Use ->find_rsc() in case this is a cloned group
515  active = clone->priv->fns->find_rsc(child, id, node,
518  if (active != NULL) {
519  return active;
520  }
521  }
522  return NULL;
523 }
524 
525 /*
526  * \brief Check whether an anonymous clone instance is known on a node
527  *
528  * \param[in] clone Anonymous clone to check
529  * \param[in] id Instance ID (without instance number) to check
530  * \param[in] node Node to check
531  *
532  * \return true if \p id instance of \p clone is known on \p node,
533  * otherwise false
534  */
535 static bool
536 anonymous_known_on(const pcmk_resource_t *clone, const char *id,
537  const pcmk_node_t *node)
538 {
539  for (GList *iter = clone->priv->children; iter; iter = iter->next) {
540  pcmk_resource_t *child = iter->data;
541 
542  /* Use ->find_rsc() because this might be a cloned group, and knowing
543  * that other members of the group are known here implies nothing.
544  */
545  child = clone->priv->fns->find_rsc(child, id, NULL,
547  CRM_LOG_ASSERT(child != NULL);
548  if (child != NULL) {
549  if (g_hash_table_lookup(child->priv->probed_nodes,
550  node->priv->id)) {
551  return true;
552  }
553  }
554  }
555  return false;
556 }
557 
567 static bool
568 is_allowed(const pcmk_resource_t *rsc, const pcmk_node_t *node)
569 {
570  pcmk_node_t *allowed = g_hash_table_lookup(rsc->priv->allowed_nodes,
571  node->priv->id);
572 
573  return (allowed != NULL) && (allowed->assign->score >= 0);
574 }
575 
585 static bool
586 promotion_score_applies(const pcmk_resource_t *rsc, const pcmk_node_t *node)
587 {
588  char *id = clone_strip(rsc->id);
589  const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
590  pcmk_resource_t *active = NULL;
591  const char *reason = "allowed";
592 
593  // Some checks apply only to anonymous clone instances
594  if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
595 
596  // If instance is active on the node, its score definitely applies
597  active = find_active_anon_instance(parent, id, node);
598  if (active == rsc) {
599  reason = "active";
600  goto check_allowed;
601  }
602 
603  /* If *no* instance is active on this node, this instance's score will
604  * count if it has been probed on this node.
605  */
606  if ((active == NULL) && anonymous_known_on(parent, id, node)) {
607  reason = "probed";
608  goto check_allowed;
609  }
610  }
611 
612  /* If this clone's status is unknown on *all* nodes (e.g. cluster startup),
613  * take all instances' scores into account, to make sure we use any
614  * permanent promotion scores.
615  */
616  if ((rsc->priv->active_nodes == NULL)
617  && (g_hash_table_size(rsc->priv->probed_nodes) == 0)) {
618  reason = "none probed";
619  goto check_allowed;
620  }
621 
622  /* Otherwise, we've probed and/or started the resource *somewhere*, so
623  * consider promotion scores on nodes where we know the status.
624  */
625  if ((g_hash_table_lookup(rsc->priv->probed_nodes,
626  node->priv->id) != NULL)
628  node->priv->id) != NULL)) {
629  reason = "known";
630  } else {
631  pcmk__rsc_trace(rsc,
632  "Ignoring %s promotion score (for %s) on %s: "
633  "not probed",
634  rsc->id, id, pcmk__node_name(node));
635  free(id);
636  return false;
637  }
638 
639 check_allowed:
640  if (is_allowed(rsc, node)) {
641  pcmk__rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s",
642  rsc->id, id, pcmk__node_name(node), reason);
643  free(id);
644  return true;
645  }
646 
647  pcmk__rsc_trace(rsc,
648  "Ignoring %s promotion score (for %s) on %s: not allowed",
649  rsc->id, id, pcmk__node_name(node));
650  free(id);
651  return false;
652 }
653 
664 static const char *
665 promotion_attr_value(const pcmk_resource_t *rsc, const pcmk_node_t *node,
666  const char *name)
667 {
668  char *attr_name = NULL;
669  const char *attr_value = NULL;
670  const char *target = NULL;
671  enum pcmk__rsc_node node_type = pcmk__rsc_node_assigned;
672 
674  // Not assigned yet
675  node_type = pcmk__rsc_node_current;
676  }
677  target = g_hash_table_lookup(rsc->priv->meta,
679  attr_name = pcmk_promotion_score_name(name);
680  attr_value = pcmk__node_attr(node, attr_name, target, node_type);
681  free(attr_name);
682  return attr_value;
683 }
684 
695 static int
696 promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node,
697  bool *is_default)
698 {
699  int score = 0;
700  int rc = pcmk_rc_ok;
701  const char *name = NULL;
702  const char *attr_value = NULL;
703 
704  if (is_default != NULL) {
705  *is_default = true;
706  }
707 
708  CRM_CHECK((rsc != NULL) && (node != NULL), return 0);
709 
710  /* If this is an instance of a cloned group, the promotion score is the sum
711  * of all members' promotion scores.
712  */
713  if (rsc->priv->children != NULL) {
714  int score = 0;
715 
716  for (const GList *iter = rsc->priv->children;
717  iter != NULL; iter = iter->next) {
718 
719  const pcmk_resource_t *child = (const pcmk_resource_t *) iter->data;
720  bool child_default = false;
721  int child_score = promotion_score(child, node, &child_default);
722 
723  if (!child_default && (is_default != NULL)) {
724  *is_default = false;
725  }
726  score += child_score;
727  }
728  return score;
729  }
730 
731  if (!promotion_score_applies(rsc, node)) {
732  return 0;
733  }
734 
735  /* For the promotion score attribute name, use the name the resource is
736  * known as in resource history, since that's what crm_attribute --promotion
737  * would have used.
738  */
739  name = pcmk__s(rsc->priv->history_id, rsc->id);
740 
741  attr_value = promotion_attr_value(rsc, node, name);
742  if (attr_value != NULL) {
743  pcmk__rsc_trace(rsc, "Promotion score for %s on %s = %s",
744  name, pcmk__node_name(node),
745  pcmk__s(attr_value, "(unset)"));
746  } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
747  /* If we don't have any resource history yet, we won't have history_id.
748  * In that case, for anonymous clones, try the resource name without
749  * any instance number.
750  */
751  char *rsc_name = clone_strip(rsc->id);
752 
753  if (strcmp(rsc->id, rsc_name) != 0) {
754  attr_value = promotion_attr_value(rsc, node, rsc_name);
755  pcmk__rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s",
756  rsc_name, pcmk__node_name(node), rsc->id,
757  pcmk__s(attr_value, "(unset)"));
758  }
759  free(rsc_name);
760  }
761 
762  if (attr_value == NULL) {
763  return 0;
764  }
765 
766  if (is_default != NULL) {
767  *is_default = false;
768  }
769 
770  rc = pcmk_parse_score(attr_value, &score, 0);
771  if (rc != pcmk_rc_ok) {
772  crm_warn("Using 0 as promotion score for %s on %s "
773  "because '%s' is not a valid score",
774  rsc->id, pcmk__node_name(node), attr_value);
775  }
776  return score;
777 }
778 
785 void
787 {
788  if (pe__set_clone_flag(rsc,
790  return;
791  }
792 
793  for (GList *iter = rsc->priv->children;
794  iter != NULL; iter = iter->next) {
795 
796  pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
797 
798  GHashTableIter iter;
799  pcmk_node_t *node = NULL;
800  int score, new_score;
801 
802  g_hash_table_iter_init(&iter, child_rsc->priv->allowed_nodes);
803  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
804  if (!pcmk__node_available(node, false, false)) {
805  /* This node will never be promoted, so don't apply the
806  * promotion score, as that may lead to clone shuffling.
807  */
808  continue;
809  }
810 
811  score = promotion_score(child_rsc, node, NULL);
812  if (score > 0) {
813  new_score = pcmk__add_scores(node->assign->score, score);
814  if (new_score != node->assign->score) { // Could remain INFINITY
815  node->assign->score = new_score;
816  pcmk__rsc_trace(rsc,
817  "Added %s promotion priority (%s) to score "
818  "on %s (now %s)",
819  child_rsc->id, pcmk_readable_score(score),
820  pcmk__node_name(node),
821  pcmk_readable_score(new_score));
822  }
823  }
824 
825  if (score > child_rsc->priv->priority) {
826  pcmk__rsc_trace(rsc,
827  "Updating %s priority to promotion score "
828  "(%d->%d)",
829  child_rsc->id, child_rsc->priv->priority,
830  score);
831  child_rsc->priv->priority = score;
832  }
833  }
834  }
835 }
836 
844 static void
845 set_current_role_unpromoted(void *data, void *user_data)
846 {
848 
849  if (rsc->priv->orig_role == pcmk_role_started) {
850  // Promotable clones should use unpromoted role instead of started
852  }
853  g_list_foreach(rsc->priv->children, set_current_role_unpromoted, NULL);
854 }
855 
863 static void
864 set_next_role_unpromoted(void *data, void *user_data)
865 {
867  GList *assigned = NULL;
868 
869  rsc->priv->fns->location(rsc, &assigned, pcmk__rsc_node_assigned);
870  if (assigned == NULL) {
871  pe__set_next_role(rsc, pcmk_role_stopped, "stopped instance");
872  } else {
873  pe__set_next_role(rsc, pcmk_role_unpromoted, "unpromoted instance");
874  g_list_free(assigned);
875  }
876  g_list_foreach(rsc->priv->children, set_next_role_unpromoted, NULL);
877 }
878 
886 static void
887 set_next_role_promoted(void *data, gpointer user_data)
888 {
890 
891  if (rsc->priv->next_role == pcmk_role_unknown) {
892  pe__set_next_role(rsc, pcmk_role_promoted, "promoted instance");
893  }
894  g_list_foreach(rsc->priv->children, set_next_role_promoted, NULL);
895 }
896 
903 static void
904 show_promotion_score(pcmk_resource_t *instance)
905 {
906  pcmk_node_t *chosen = NULL;
907  const char *score_s = NULL;
908 
909  chosen = instance->priv->fns->location(instance, NULL,
911  score_s = pcmk_readable_score(instance->priv->promotion_priority);
912  if (pcmk_is_set(instance->priv->scheduler->flags,
914  && !pcmk__is_daemon
915  && (instance->priv->scheduler->priv->out != NULL)) {
916 
917  pcmk__output_t *out = instance->priv->scheduler->priv->out;
918 
919  out->message(out, "promotion-score", instance, chosen, score_s);
920 
921  } else if (chosen == NULL) {
922  pcmk__rsc_debug(pe__const_top_resource(instance, false),
923  "%s promotion score (inactive): %s (priority=%d)",
924  instance->id, score_s, instance->priv->priority);
925 
926  } else {
927  pcmk__rsc_debug(pe__const_top_resource(instance, false),
928  "%s promotion score on %s: %s (priority=%d)",
929  instance->id, pcmk__node_name(chosen),
930  score_s, instance->priv->priority);
931  }
932 }
933 
941 static void
942 set_instance_priority(gpointer data, gpointer user_data)
943 {
944  pcmk_resource_t *instance = (pcmk_resource_t *) data;
945  const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
946 
947  const pcmk_node_t *chosen = NULL;
948  enum rsc_role_e next_role = pcmk_role_unknown;
949  GList *list = NULL;
950 
951  pcmk__rsc_trace(clone, "Assigning priority for %s: %s", instance->id,
952  pcmk_role_text(instance->priv->next_role));
953 
954  if (instance->priv->fns->state(instance, TRUE) == pcmk_role_started) {
955  set_current_role_unpromoted(instance, NULL);
956  }
957 
958  // Only an instance that will be active can be promoted
959  chosen = instance->priv->fns->location(instance, &list,
961  if (pcmk__list_of_multiple(list)) {
962  pcmk__config_err("Cannot promote non-colocated child %s",
963  instance->id);
964  }
965  g_list_free(list);
966  if (chosen == NULL) {
967  return;
968  }
969 
970  next_role = instance->priv->fns->state(instance, FALSE);
971  switch (next_role) {
972  case pcmk_role_started:
973  case pcmk_role_unknown:
974  // Set instance priority to its promotion score (or -1 if none)
975  {
976  bool is_default = false;
977 
978  instance->priv->priority = promotion_score(instance, chosen,
979  &is_default);
980  if (is_default) {
981  /* Default to -1 if no value is set. This allows instances
982  * eligible for promotion to be specified based solely on
983  * PCMK_XE_RSC_LOCATION constraints, but prevents any
984  * instance from being promoted if neither a constraint nor
985  * a promotion score is present.
986  */
987  instance->priv->priority = -1;
988  }
989  }
990  break;
991 
993  case pcmk_role_stopped:
994  // Instance can't be promoted
995  instance->priv->priority = -PCMK_SCORE_INFINITY;
996  break;
997 
998  case pcmk_role_promoted:
999  // Nothing needed (re-creating actions after scheduling fencing)
1000  break;
1001 
1002  default:
1003  CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s",
1004  next_role, instance->id));
1005  }
1006 
1007  // Add relevant location constraint scores for promoted role
1008  apply_promoted_locations(instance, instance->priv->location_constraints,
1009  chosen);
1010  apply_promoted_locations(instance, clone->priv->location_constraints,
1011  chosen);
1012 
1013  // Consider instance's role-based colocations with other resources
1014  list = pcmk__this_with_colocations(instance);
1015  for (GList *iter = list; iter != NULL; iter = iter->next) {
1016  pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data;
1017 
1018  instance->priv->cmds->apply_coloc_score(instance, cons->primary, cons,
1019  true);
1020  }
1021  g_list_free(list);
1022 
1023  instance->priv->promotion_priority = instance->priv->priority;
1024  if (next_role == pcmk_role_promoted) {
1026  }
1027  pcmk__rsc_trace(clone, "Assigning %s priority = %d",
1028  instance->id, instance->priv->priority);
1029 }
1030 
1038 static void
1039 set_instance_role(gpointer data, gpointer user_data)
1040 {
1041  pcmk_resource_t *instance = (pcmk_resource_t *) data;
1042  int *count = (int *) user_data;
1043 
1044  const pcmk_resource_t *clone = pe__const_top_resource(instance, false);
1045  const pcmk_scheduler_t *scheduler = instance->priv->scheduler;
1046  pcmk_node_t *chosen = NULL;
1047 
1048  show_promotion_score(instance);
1049 
1050  if (instance->priv->promotion_priority < 0) {
1051  pcmk__rsc_trace(clone, "Not supposed to promote instance %s",
1052  instance->id);
1053 
1054  } else if ((*count < pe__clone_promoted_max(instance))
1055  || !pcmk_is_set(clone->flags, pcmk__rsc_managed)) {
1056  chosen = node_to_be_promoted_on(instance);
1057  }
1058 
1059  if (chosen == NULL) {
1060  set_next_role_unpromoted(instance, NULL);
1061  return;
1062  }
1063 
1064  if ((instance->priv->orig_role < pcmk_role_promoted)
1067  crm_notice("Clone instance %s cannot be promoted without quorum",
1068  instance->id);
1069  set_next_role_unpromoted(instance, NULL);
1070  return;
1071  }
1072 
1073  chosen->assign->count++;
1074  pcmk__rsc_info(clone, "Choosing %s (%s) on %s for promotion",
1075  instance->id, pcmk_role_text(instance->priv->orig_role),
1076  pcmk__node_name(chosen));
1077  set_next_role_promoted(instance, NULL);
1078  (*count)++;
1079 }
1080 
1087 void
1089 {
1090  int promoted = 0;
1091  GHashTableIter iter;
1092  pcmk_node_t *node = NULL;
1093 
1094  // Repurpose count to track the number of promoted instances assigned
1095  g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1096  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1097  node->assign->count = 0;
1098  }
1099 
1100  // Set instances' promotion priorities and sort by highest priority first
1101  g_list_foreach(rsc->priv->children, set_instance_priority, rsc);
1102  sort_promotable_instances(rsc);
1103 
1104  // Choose the first N eligible instances to be promoted
1105  g_list_foreach(rsc->priv->children, set_instance_role, &promoted);
1106  pcmk__rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
1107  rsc->id, promoted, pe__clone_promoted_max(rsc));
1108 }
1109 
1119 static void
1120 create_promotable_instance_actions(pcmk_resource_t *clone,
1121  bool *any_promoting, bool *any_demoting)
1122 {
1123  for (GList *iter = clone->priv->children;
1124  iter != NULL; iter = iter->next) {
1125 
1126  pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1127 
1128  instance->priv->cmds->create_actions(instance);
1129  check_for_role_change(instance, any_demoting, any_promoting);
1130  }
1131 }
1132 
1143 static void
1144 reset_instance_priorities(pcmk_resource_t *clone)
1145 {
1146  for (GList *iter = clone->priv->children;
1147  iter != NULL; iter = iter->next) {
1148 
1149  pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1150 
1151  instance->priv->priority = clone->priv->priority;
1152  }
1153 }
1154 
1161 void
1163 {
1164  bool any_promoting = false;
1165  bool any_demoting = false;
1166 
1167  // Create actions for each clone instance individually
1168  create_promotable_instance_actions(clone, &any_promoting, &any_demoting);
1169 
1170  // Create pseudo-actions for clone as a whole
1171  pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting);
1172 
1173  // Undo our temporary repurposing of resource priority for instances
1174  reset_instance_priorities(clone);
1175 }
1176 
1183 void
1185 {
1186  pcmk_resource_t *previous = NULL; // Needed for ordered clones
1187 
1189 
1190  for (GList *iter = clone->priv->children;
1191  iter != NULL; iter = iter->next) {
1192 
1193  pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1194 
1195  // Demote before promote
1197  instance, PCMK_ACTION_PROMOTE,
1199 
1200  order_instance_promotion(clone, instance, previous);
1201  order_instance_demotion(clone, instance, previous);
1202  previous = instance;
1203  }
1204 }
1205 
1215 static void
1216 update_dependent_allowed_nodes(pcmk_resource_t *dependent,
1217  const pcmk_resource_t *primary,
1218  const pcmk_node_t *primary_node,
1219  const pcmk__colocation_t *colocation)
1220 {
1221  GHashTableIter iter;
1222  pcmk_node_t *node = NULL;
1223  const char *primary_value = NULL;
1224  const char *attr = colocation->node_attribute;
1225 
1226  if (colocation->score >= PCMK_SCORE_INFINITY) {
1227  return; // Colocation is mandatory, so allowed node scores don't matter
1228  }
1229 
1230  primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1231 
1232  pcmk__rsc_trace(colocation->primary,
1233  "Applying %s (%s with %s on %s by %s @%d) to %s",
1234  colocation->id, colocation->dependent->id,
1235  colocation->primary->id, pcmk__node_name(primary_node),
1236  attr, colocation->score, dependent->id);
1237 
1238  g_hash_table_iter_init(&iter, dependent->priv->allowed_nodes);
1239  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1240  const char *dependent_value = pcmk__colocation_node_attr(node, attr,
1241  dependent);
1242 
1243  if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) {
1244  node->assign->score = pcmk__add_scores(node->assign->score,
1245  colocation->score);
1246  pcmk__rsc_trace(colocation->primary,
1247  "Added %s score (%s) to %s (now %s)",
1248  colocation->id,
1249  pcmk_readable_score(colocation->score),
1250  pcmk__node_name(node),
1251  pcmk_readable_score(node->assign->score));
1252  }
1253  }
1254 }
1255 
1263 void
1265  pcmk_resource_t *dependent,
1266  const pcmk__colocation_t *colocation)
1267 {
1268  GList *affected_nodes = NULL;
1269 
1270  /* Build a list of all nodes where an instance of the primary will be, and
1271  * (for optional colocations) update the dependent's allowed node scores for
1272  * each one.
1273  */
1274  for (GList *iter = primary->priv->children;
1275  iter != NULL; iter = iter->next) {
1276 
1277  pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1278  pcmk_node_t *node = NULL;
1279 
1280  node = instance->priv->fns->location(instance, NULL,
1282  if (node == NULL) {
1283  continue;
1284  }
1285  if (instance->priv->fns->state(instance,
1286  FALSE) == colocation->primary_role) {
1287  update_dependent_allowed_nodes(dependent, primary, node,
1288  colocation);
1289  affected_nodes = g_list_prepend(affected_nodes, node);
1290  }
1291  }
1292 
1293  /* For mandatory colocations, add the primary's node score to the
1294  * dependent's node score for each affected node, and ban the dependent
1295  * from all other nodes.
1296  *
1297  * However, skip this for promoted-with-promoted colocations, otherwise
1298  * inactive dependent instances can't start (in the unpromoted role).
1299  */
1300  if ((colocation->score >= PCMK_SCORE_INFINITY)
1301  && ((colocation->dependent_role != pcmk_role_promoted)
1302  || (colocation->primary_role != pcmk_role_promoted))) {
1303 
1304  pcmk__rsc_trace(colocation->primary,
1305  "Applying %s (mandatory %s with %s) to %s",
1306  colocation->id, colocation->dependent->id,
1307  colocation->primary->id, dependent->id);
1308  pcmk__colocation_intersect_nodes(dependent, primary, colocation,
1309  affected_nodes, true);
1310  }
1311  g_list_free(affected_nodes);
1312 }
1313 
1324 int
1326  pcmk_resource_t *dependent,
1327  const pcmk__colocation_t *colocation)
1328 {
1329  pcmk_resource_t *primary_instance = NULL;
1330 
1331  // Look for a primary instance where dependent will be
1332  primary_instance = pcmk__find_compatible_instance(dependent, primary,
1333  colocation->primary_role,
1334  false);
1335 
1336  if (primary_instance != NULL) {
1337  // Add primary instance's priority to dependent's
1338  int new_priority = pcmk__add_scores(dependent->priv->priority,
1339  colocation->score);
1340 
1341  pcmk__rsc_trace(colocation->primary,
1342  "Applying %s (%s with %s) to %s priority "
1343  "(%s + %s = %s)",
1344  colocation->id, colocation->dependent->id,
1345  colocation->primary->id, dependent->id,
1346  pcmk_readable_score(dependent->priv->priority),
1347  pcmk_readable_score(colocation->score),
1348  pcmk_readable_score(new_priority));
1349  dependent->priv->priority = new_priority;
1350  return colocation->score;
1351  }
1352 
1353  if (colocation->score >= PCMK_SCORE_INFINITY) {
1354  // Mandatory colocation, but primary won't be here
1355  pcmk__rsc_trace(colocation->primary,
1356  "Applying %s (%s with %s) to %s: can't be promoted",
1357  colocation->id, colocation->dependent->id,
1358  colocation->primary->id, dependent->id);
1359  dependent->priv->priority = -PCMK_SCORE_INFINITY;
1360  return -PCMK_SCORE_INFINITY;
1361  }
1362  return 0;
1363 }
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition: complex.c:1043
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
#define crm_notice(fmt, args...)
Definition: logging.h:365
#define pcmk__sched_err(scheduler, fmt...)
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition: roles.c:23
Do not recover resources from outside partition.
Definition: scheduler.h:39
char data[0]
Definition: cpg.c:58
void(* create_actions)(pcmk_resource_t *rsc)
Stopped.
Definition: roles.h:36
const char * name
Definition: cib.c:26
int(* message)(pcmk__output_t *out, const char *message_id,...)
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:102
G_GNUC_INTERNAL void pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET
Definition: options.h:85
Match only clones and their instances, by either clone or instance ID.
Definition: resources.h:40
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__config_err(fmt...)
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_compatible_instance(const pcmk_resource_t *match_rsc, const pcmk_resource_t *rsc, enum rsc_role_e role, bool current)
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
pcmk_node_t *(* location)(const pcmk_resource_t *rsc, GList **list, uint32_t target)
Promoted.
Definition: roles.h:39
uint64_t flags
Definition: scheduler.h:89
char * pcmk_promotion_score_name(const char *rsc_id)
Return the name of the node attribute used as a promotion score.
Definition: attrs.c:88
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:196
const pcmk__rsc_methods_t * fns
int pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
Definition: clone.c:1060
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
const char * action
Definition: pcmk_fence.c:32
enum pe_quorum_policy no_quorum_policy
Definition: scheduler.h:93
G_GNUC_INTERNAL const char * pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr, const pcmk_resource_t *rsc)
#define pcmk__rsc_debug(rsc, fmt, args...)
pcmk__node_private_t * priv
Definition: nodes.h:85
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
#define crm_warn(fmt, args...)
Definition: logging.h:362
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1265
void pcmk__order_promotable_instances(pcmk_resource_t *clone)
pcmk_scheduler_t * scheduler
Actions are ordered (optionally, if no other flags are set)
pcmk_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition: status.c:510
char * clone_strip(const char *last_rsc_id)
Definition: unpack.c:1945
int pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary, pcmk_resource_t *dependent, const pcmk__colocation_t *colocation)
void(* add_colocated_node_scores)(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
void pcmk__create_promotable_actions(pcmk_resource_t *clone)
void pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting, bool any_demoting)
Definition: clone.c:1103
pcmk__resource_private_t * priv
Definition: resources.h:61
G_GNUC_INTERNAL gint pcmk__cmp_instance(gconstpointer a, gconstpointer b)
bool pcmk__is_daemon
Definition: logging.c:47
Unpromoted.
Definition: roles.h:38
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, gboolean current)
pcmk_resource_t *(* find_rsc)(pcmk_resource_t *rsc, const char *search, const pcmk_node_t *node, int flags)
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
pcmk_scheduler_t * scheduler
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:159
void pcmk__set_instance_roles(pcmk_resource_t *rsc)
int pe__clone_promoted_node_max(const pcmk_resource_t *clone)
Definition: clone.c:107
int pe__clone_promoted_max(const pcmk_resource_t *clone)
Definition: clone.c:90
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
pcmk_resource_t * primary
#define pcmk__assert(expr)
const char * target
Definition: pcmk_fence.c:31
pcmk__rsc_node
If &#39;then&#39; is required, &#39;first&#39; must be added to the transition graph.
G_GNUC_INTERNAL GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
#define crm_err(fmt, args...)
Definition: logging.h:359
pcmk_scheduler_t * scheduler
If matching by node, compare current node instead of assigned node.
Definition: resources.h:43
If &#39;first&#39; is required and runnable, &#39;then&#39; must be in graph.
G_GNUC_INTERNAL GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
const char * pcmk__node_attr(const pcmk_node_t *node, const char *name, const char *target, enum pcmk__rsc_node node_type)
Definition: attrs.c:114
Started.
Definition: roles.h:37
This structure contains everything that makes up a single output formatter.
enum rsc_role_e role_filter
void pcmk__add_promotion_scores(pcmk_resource_t *rsc)
G_GNUC_INTERNAL void pcmk__promotable_restart_ordering(pcmk_resource_t *rsc)
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition: scores.c:34
pcmk_resource_t * dependent
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:164
unsigned long long flags
Definition: resources.h:69
const char * node_attribute
#define PCMK_ACTION_PROMOTED
Definition: actions.h:58
Resource role is unknown.
Definition: roles.h:35
Location constraint object.
bool pe__clone_is_ordered(const pcmk_resource_t *clone)
Definition: clone.c:1041
const char * parent
Definition: cib.c:27
#define PCMK_ACTION_DEMOTED
Definition: actions.h:41
int(* apply_coloc_score)(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent)
G_GNUC_INTERNAL 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)
uint64_t flags
Definition: remote.c:211
const pcmk__assignment_methods_t * cmds
G_GNUC_INTERNAL bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
void pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary, pcmk_resource_t *dependent, const pcmk__colocation_t *colocation)
Update dependent for a colocation with a promotable clone.
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:26
struct pcmk__node_assignment * assign
Definition: nodes.h:79