pacemaker  2.1.5-b7adf64e51
Scalable High-Availability cluster resource manager
pcmk_sched_promotable.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2022 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(pe_resource_t *rsc, bool *demoting, bool *promoting)
77 {
78  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((pe_resource_t *) iter->data,
84  demoting, promoting);
85  }
86  return;
87  }
88 
89  for (iter = rsc->actions; iter != NULL; iter = iter->next) {
90  pe_action_t *action = (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, GList *location_constraints,
121  pe_node_t *chosen)
122 {
123  for (GList *iter = location_constraints; iter; iter = iter->next) {
124  pe__location_t *location = iter->data;
125  pe_node_t *weighted_node = NULL;
126 
127  if (location->role_filter == RSC_ROLE_PROMOTED) {
128  weighted_node = pe_find_node_id(location->node_list_rh,
129  chosen->details->id);
130  }
131  if (weighted_node != NULL) {
132  int new_priority = pcmk__add_scores(child->priority,
133  weighted_node->weight);
134 
135  pe_rsc_trace(child,
136  "Applying location %s to %s promotion priority on %s: "
137  "%d + %d = %d",
138  location->id, child->id, pe__node_name(weighted_node),
139  child->priority, weighted_node->weight, new_priority);
140  child->priority = new_priority;
141  }
142  }
143 }
144 
153 static pe_node_t *
154 node_to_be_promoted_on(pe_resource_t *rsc)
155 {
156  pe_node_t *node = NULL;
157  pe_node_t *local_node = NULL;
159 
160  // If this is a cloned group, bail if any group member can't be promoted
161  for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
162  pe_resource_t *child = (pe_resource_t *) iter->data;
163 
164  if (node_to_be_promoted_on(child) == NULL) {
165  pe_rsc_trace(rsc,
166  "%s can't be promoted because member %s can't",
167  rsc->id, child->id);
168  return NULL;
169  }
170  }
171 
172  node = rsc->fns->location(rsc, NULL, FALSE);
173  if (node == NULL) {
174  pe_rsc_trace(rsc, "%s can't be promoted because it won't be active",
175  rsc->id);
176  return NULL;
177 
178  } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
179  if (rsc->fns->state(rsc, TRUE) == RSC_ROLE_PROMOTED) {
180  crm_notice("Unmanaged instance %s will be left promoted on %s",
181  rsc->id, pe__node_name(node));
182  } else {
183  pe_rsc_trace(rsc, "%s can't be promoted because it is unmanaged",
184  rsc->id);
185  return NULL;
186  }
187 
188  } else if (rsc->priority < 0) {
189  pe_rsc_trace(rsc,
190  "%s can't be promoted because its promotion priority %d "
191  "is negative",
192  rsc->id, rsc->priority);
193  return NULL;
194 
195  } else if (!pcmk__node_available(node, false, true)) {
196  pe_rsc_trace(rsc, "%s can't be promoted because %s can't run resources",
197  rsc->id, pe__node_name(node));
198  return NULL;
199  }
200 
201  local_node = pe_hash_table_lookup(parent->allowed_nodes, node->details->id);
202 
203  if (local_node == NULL) {
204  /* It should not be possible for the scheduler to have allocated the
205  * instance to a node where its parent is not allowed, but it's good to
206  * have a fail-safe.
207  */
208  if (pcmk_is_set(rsc->flags, pe_rsc_managed)) {
209  crm_warn("%s can't be promoted because %s is not allowed on %s "
210  "(scheduler bug?)",
211  rsc->id, parent->id, pe__node_name(node));
212  } // else the instance is unmanaged and already promoted
213  return NULL;
214 
215  } else if ((local_node->count >= pe__clone_promoted_node_max(parent))
216  && pcmk_is_set(rsc->flags, pe_rsc_managed)) {
217  pe_rsc_trace(rsc,
218  "%s can't be promoted because %s has "
219  "maximum promoted instances already",
220  rsc->id, pe__node_name(node));
221  return NULL;
222  }
223 
224  return local_node;
225 }
226 
238 static gint
239 cmp_promotable_instance(gconstpointer a, gconstpointer b)
240 {
241  const pe_resource_t *rsc1 = (const pe_resource_t *) a;
242  const pe_resource_t *rsc2 = (const pe_resource_t *) b;
243 
244  enum rsc_role_e role1 = RSC_ROLE_UNKNOWN;
245  enum rsc_role_e role2 = RSC_ROLE_UNKNOWN;
246 
247  CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
248 
249  // Check sort index set by pcmk__set_instance_roles()
250  if (rsc1->sort_index > rsc2->sort_index) {
252  "%s has higher promotion priority than %s "
253  "(sort index %d > %d)",
255  return -1;
256  } else if (rsc1->sort_index < rsc2->sort_index) {
258  "%s has lower promotion priority than %s "
259  "(sort index %d < %d)",
261  return 1;
262  }
263 
264  // If those are the same, prefer instance whose current role is higher
265  role1 = rsc1->fns->state(rsc1, TRUE);
266  role2 = rsc2->fns->state(rsc2, TRUE);
267  if (role1 > role2) {
269  "%s has higher promotion priority than %s "
270  "(higher current role)",
271  rsc1->id, rsc2->id);
272  return -1;
273  } else if (role1 < role2) {
275  "%s has lower promotion priority than %s "
276  "(lower current role)",
277  rsc1->id, rsc2->id);
278  return 1;
279  }
280 
281  // Finally, do normal clone instance sorting
282  return pcmk__cmp_instance(a, b);
283 }
284 
296 static void
297 add_sort_index_to_node_weight(gpointer data, gpointer user_data)
298 {
299  pe_resource_t *child = (pe_resource_t *) data;
300  pe_resource_t *clone = (pe_resource_t *) user_data;
301 
302  pe_node_t *node = NULL;
303  pe_node_t *chosen = NULL;
304 
305  if (child->sort_index < 0) {
306  pe_rsc_trace(clone, "Not adding sort index of %s: negative", child->id);
307  return;
308  }
309 
310  chosen = child->fns->location(child, NULL, FALSE);
311  if (chosen == NULL) {
312  pe_rsc_trace(clone, "Not adding sort index of %s: inactive", child->id);
313  return;
314  }
315 
316  node = (pe_node_t *) pe_hash_table_lookup(clone->allowed_nodes,
317  chosen->details->id);
318  CRM_ASSERT(node != NULL);
319 
320  pe_rsc_trace(clone, "Adding sort index %s of %s to weight for %s",
321  pcmk_readable_score(child->sort_index), child->id,
322  pe__node_name(node));
323  node->weight = pcmk__add_scores(child->sort_index, node->weight);
324 }
325 
333 static void
334 apply_coloc_to_dependent(gpointer data, gpointer user_data)
335 {
336  pcmk__colocation_t *constraint = (pcmk__colocation_t *) data;
337  pe_resource_t *clone = (pe_resource_t *) user_data;
338  pe_resource_t *primary = constraint->primary;
340  float factor = constraint->score / (float) INFINITY;
341 
342  if (constraint->dependent_role != RSC_ROLE_PROMOTED) {
343  return;
344  }
345  if (constraint->score < INFINITY) {
347  }
348  pe_rsc_trace(clone, "Applying colocation %s (promoted %s with %s) @%s",
349  constraint->id, constraint->dependent->id,
350  constraint->primary->id,
351  pcmk_readable_score(constraint->score));
352  pcmk__add_colocated_node_scores(primary, clone->id, &clone->allowed_nodes,
353  constraint->node_attribute, factor, flags);
354 }
355 
363 static void
364 apply_coloc_to_primary(gpointer data, gpointer user_data)
365 {
366  pcmk__colocation_t *constraint = (pcmk__colocation_t *) data;
367  pe_resource_t *clone = (pe_resource_t *) user_data;
368  pe_resource_t *dependent = constraint->dependent;
369  const float factor = constraint->score / (float) INFINITY;
370  const uint32_t flags = pcmk__coloc_select_active
372 
373  if ((constraint->primary_role != RSC_ROLE_PROMOTED)
374  || !pcmk__colocation_has_influence(constraint, NULL)) {
375  return;
376  }
377 
378  pe_rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s",
379  constraint->id, constraint->dependent->id,
380  constraint->primary->id,
381  pcmk_readable_score(constraint->score));
382  pcmk__add_colocated_node_scores(dependent, clone->id, &clone->allowed_nodes,
383  constraint->node_attribute, factor, flags);
384 }
385 
393 static void
394 set_sort_index_to_node_weight(gpointer data, gpointer user_data)
395 {
396  pe_resource_t *child = (pe_resource_t *) data;
397  pe_resource_t *clone = (pe_resource_t *) user_data;
398 
399  pe_node_t *chosen = child->fns->location(child, NULL, FALSE);
400 
401  if (!pcmk_is_set(child->flags, pe_rsc_managed)
402  && (child->next_role == RSC_ROLE_PROMOTED)) {
403  child->sort_index = INFINITY;
404  pe_rsc_trace(clone,
405  "Final sort index for %s is INFINITY (unmanaged promoted)",
406  child->id);
407 
408  } else if ((chosen == NULL) || (child->sort_index < 0)) {
409  pe_rsc_trace(clone,
410  "Final sort index for %s is %d (ignoring node weight)",
411  child->id, child->sort_index);
412 
413  } else {
414  pe_node_t *node = NULL;
415 
416  node = (pe_node_t *) pe_hash_table_lookup(clone->allowed_nodes,
417  chosen->details->id);
418  CRM_ASSERT(node != NULL);
419 
420  child->sort_index = node->weight;
421  pe_rsc_trace(clone,
422  "Merging weights for %s: final sort index for %s is %d",
423  clone->id, child->id, child->sort_index);
424  }
425 }
426 
433 static void
434 sort_promotable_instances(pe_resource_t *clone)
435 {
437  == pcmk_rc_already) {
438  return;
439  }
441 
442  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
443  pe_resource_t *child = (pe_resource_t *) iter->data;
444 
445  pe_rsc_trace(clone,
446  "Merging weights for %s: initial sort index for %s is %d",
447  clone->id, child->id, child->sort_index);
448  }
449  pe__show_node_weights(true, clone, "Before", clone->allowed_nodes,
450  clone->cluster);
451 
452  g_list_foreach(clone->children, add_sort_index_to_node_weight, clone);
453  g_list_foreach(clone->rsc_cons, apply_coloc_to_dependent, clone);
454  g_list_foreach(clone->rsc_cons_lhs, apply_coloc_to_primary, clone);
455 
456  // Ban resource from all nodes if it needs a ticket but doesn't have it
458 
459  pe__show_node_weights(true, clone, "After", clone->allowed_nodes,
460  clone->cluster);
461 
462  // Reset sort indexes to final node weights
463  g_list_foreach(clone->children, set_sort_index_to_node_weight, clone);
464 
465  // Finally, sort instances in descending order of promotion priority
466  clone->children = g_list_sort(clone->children, cmp_promotable_instance);
468 }
469 
480 static pe_resource_t *
481 find_active_anon_instance(pe_resource_t *clone, const char *id,
482  const pe_node_t *node)
483 {
484  for (GList *iter = clone->children; iter; iter = iter->next) {
485  pe_resource_t *child = iter->data;
486  pe_resource_t *active = NULL;
487 
488  // Use ->find_rsc() in case this is a cloned group
489  active = clone->fns->find_rsc(child, id, node,
491  if (active != NULL) {
492  return active;
493  }
494  }
495  return NULL;
496 }
497 
498 /*
499  * \brief Check whether an anonymous clone instance is known on a node
500  *
501  * \param[in] clone Anonymous clone to check
502  * \param[in] id Instance ID (without instance number) to check
503  * \param[in] node Node to check
504  *
505  * \return true if \p id instance of \p clone is known on \p node,
506  * otherwise false
507  */
508 static bool
509 anonymous_known_on(const pe_resource_t *clone, const char *id,
510  const pe_node_t *node)
511 {
512  for (GList *iter = clone->children; iter; iter = iter->next) {
513  pe_resource_t *child = iter->data;
514 
515  /* Use ->find_rsc() because this might be a cloned group, and knowing
516  * that other members of the group are known here implies nothing.
517  */
518  child = clone->fns->find_rsc(child, id, NULL, pe_find_clone);
519  CRM_LOG_ASSERT(child != NULL);
520  if (child != NULL) {
521  if (g_hash_table_lookup(child->known_on, node->details->id)) {
522  return true;
523  }
524  }
525  }
526  return false;
527 }
528 
538 static bool
539 is_allowed(const pe_resource_t *rsc, const pe_node_t *node)
540 {
541  pe_node_t *allowed = pe_hash_table_lookup(rsc->allowed_nodes,
542  node->details->id);
543 
544  return (allowed != NULL) && (allowed->weight >= 0);
545 }
546 
556 static bool
557 promotion_score_applies(pe_resource_t *rsc, const pe_node_t *node)
558 {
559  char *id = clone_strip(rsc->id);
561  pe_resource_t *active = NULL;
562  const char *reason = "allowed";
563 
564  // Some checks apply only to anonymous clone instances
565  if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
566 
567  // If instance is active on the node, its score definitely applies
568  active = find_active_anon_instance(parent, id, node);
569  if (active == rsc) {
570  reason = "active";
571  goto check_allowed;
572  }
573 
574  /* If *no* instance is active on this node, this instance's score will
575  * count if it has been probed on this node.
576  */
577  if ((active == NULL) && anonymous_known_on(parent, id, node)) {
578  reason = "probed";
579  goto check_allowed;
580  }
581  }
582 
583  /* If this clone's status is unknown on *all* nodes (e.g. cluster startup),
584  * take all instances' scores into account, to make sure we use any
585  * permanent promotion scores.
586  */
587  if ((rsc->running_on == NULL) && (g_hash_table_size(rsc->known_on) == 0)) {
588  reason = "none probed";
589  goto check_allowed;
590  }
591 
592  /* Otherwise, we've probed and/or started the resource *somewhere*, so
593  * consider promotion scores on nodes where we know the status.
594  */
595  if ((pe_hash_table_lookup(rsc->known_on, node->details->id) != NULL)
596  || (pe_find_node_id(rsc->running_on, node->details->id) != NULL)) {
597  reason = "known";
598  } else {
599  pe_rsc_trace(rsc,
600  "Ignoring %s promotion score (for %s) on %s: not probed",
601  rsc->id, id, pe__node_name(node));
602  free(id);
603  return false;
604  }
605 
606 check_allowed:
607  if (is_allowed(rsc, node)) {
608  pe_rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s",
609  rsc->id, id, pe__node_name(node), reason);
610  free(id);
611  return true;
612  }
613 
614  pe_rsc_trace(rsc, "Ignoring %s promotion score (for %s) on %s: not allowed",
615  rsc->id, id, pe__node_name(node));
616  free(id);
617  return false;
618 }
619 
630 static const char *
631 promotion_attr_value(pe_resource_t *rsc, const pe_node_t *node,
632  const char *name)
633 {
634  char *attr_name = NULL;
635  const char *attr_value = NULL;
636 
637  CRM_CHECK((rsc != NULL) && (node != NULL) && (name != NULL), return NULL);
638 
639  attr_name = pcmk_promotion_score_name(name);
640  attr_value = pe_node_attribute_calculated(node, attr_name, rsc);
641  free(attr_name);
642  return attr_value;
643 }
644 
655 static int
656 promotion_score(pe_resource_t *rsc, const pe_node_t *node, bool *is_default)
657 {
658  char *name = NULL;
659  const char *attr_value = NULL;
660 
661  if (is_default != NULL) {
662  *is_default = true;
663  }
664 
665  CRM_CHECK((rsc != NULL) && (node != NULL), return 0);
666 
667  /* If this is an instance of a cloned group, the promotion score is the sum
668  * of all members' promotion scores.
669  */
670  if (rsc->children != NULL) {
671  int score = 0;
672 
673  for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
674  pe_resource_t *child = (pe_resource_t *) iter->data;
675  bool child_default = false;
676  int child_score = promotion_score(child, node, &child_default);
677 
678  if (!child_default && (is_default != NULL)) {
679  *is_default = false;
680  }
681  score += child_score;
682  }
683  return score;
684  }
685 
686  if (!promotion_score_applies(rsc, node)) {
687  return 0;
688  }
689 
690  /* For the promotion score attribute name, use the name the resource is
691  * known as in resource history, since that's what crm_attribute --promotion
692  * would have used.
693  */
694  name = (rsc->clone_name == NULL)? rsc->id : rsc->clone_name;
695 
696  attr_value = promotion_attr_value(rsc, node, name);
697  if (attr_value != NULL) {
698  pe_rsc_trace(rsc, "Promotion score for %s on %s = %s",
699  name, pe__node_name(node), pcmk__s(attr_value, "(unset)"));
700  } else if (!pcmk_is_set(rsc->flags, pe_rsc_unique)) {
701  /* If we don't have any resource history yet, we won't have clone_name.
702  * In that case, for anonymous clones, try the resource name without
703  * any instance number.
704  */
705  name = clone_strip(rsc->id);
706  if (strcmp(rsc->id, name) != 0) {
707  attr_value = promotion_attr_value(rsc, node, name);
708  pe_rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s",
709  name, pe__node_name(node), rsc->id,
710  pcmk__s(attr_value, "(unset)"));
711  }
712  free(name);
713  }
714 
715  if (attr_value == NULL) {
716  return 0;
717  }
718 
719  if (is_default != NULL) {
720  *is_default = false;
721  }
722  return char2score(attr_value);
723 }
724 
731 void
733 {
735  return;
736  }
737 
738  for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
739  pe_resource_t *child_rsc = (pe_resource_t *) iter->data;
740 
741  GHashTableIter iter;
742  pe_node_t *node = NULL;
743  int score, new_score;
744 
745  g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
746  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
747  if (!pcmk__node_available(node, false, false)) {
748  /* This node will never be promoted, so don't apply the
749  * promotion score, as that may lead to clone shuffling.
750  */
751  continue;
752  }
753 
754  score = promotion_score(child_rsc, node, NULL);
755  if (score > 0) {
756  new_score = pcmk__add_scores(node->weight, score);
757  if (new_score != node->weight) {
758  pe_rsc_trace(rsc,
759  "Adding promotion score to preference "
760  "for %s on %s (%d->%d)",
761  child_rsc->id, pe__node_name(node),
762  node->weight, new_score);
763  node->weight = new_score;
764  }
765  }
766 
767  if (score > child_rsc->priority) {
768  pe_rsc_trace(rsc,
769  "Updating %s priority to promotion score (%d->%d)",
770  child_rsc->id, child_rsc->priority, score);
771  child_rsc->priority = score;
772  }
773  }
774  }
775 }
776 
784 static void
785 set_current_role_unpromoted(void *data, void *user_data)
786 {
787  pe_resource_t *rsc = (pe_resource_t *) data;
788 
789  if (rsc->role == RSC_ROLE_STARTED) {
790  // Promotable clones should use unpromoted role instead of started
791  rsc->role = RSC_ROLE_UNPROMOTED;
792  }
793  g_list_foreach(rsc->children, set_current_role_unpromoted, NULL);
794 }
795 
803 static void
804 set_next_role_unpromoted(void *data, void *user_data)
805 {
806  pe_resource_t *rsc = (pe_resource_t *) data;
807  GList *assigned = NULL;
808 
809  rsc->fns->location(rsc, &assigned, FALSE);
810  if (assigned == NULL) {
811  pe__set_next_role(rsc, RSC_ROLE_STOPPED, "stopped instance");
812  } else {
813  pe__set_next_role(rsc, RSC_ROLE_UNPROMOTED, "unpromoted instance");
814  g_list_free(assigned);
815  }
816  g_list_foreach(rsc->children, set_next_role_unpromoted, NULL);
817 }
818 
826 static void
827 set_next_role_promoted(void *data, gpointer user_data)
828 {
829  pe_resource_t *rsc = (pe_resource_t *) data;
830 
831  if (rsc->next_role == RSC_ROLE_UNKNOWN) {
832  pe__set_next_role(rsc, RSC_ROLE_PROMOTED, "promoted instance");
833  }
834  g_list_foreach(rsc->children, set_next_role_promoted, NULL);
835 }
836 
843 static void
844 show_promotion_score(pe_resource_t *instance)
845 {
846  pe_node_t *chosen = instance->fns->location(instance, NULL, FALSE);
847 
849  && !pcmk__is_daemon && (instance->cluster->priv != NULL)) {
850 
851  pcmk__output_t *out = instance->cluster->priv;
852 
853  out->message(out, "promotion-score", instance, chosen,
854  pcmk_readable_score(instance->sort_index));
855  } else {
856  pe_rsc_debug(uber_parent(instance),
857  "%s promotion score on %s: sort=%s priority=%s",
858  instance->id,
859  ((chosen == NULL)? "none" : pe__node_name(chosen)),
860  pcmk_readable_score(instance->sort_index),
861  pcmk_readable_score(instance->priority));
862  }
863 }
864 
872 static void
873 set_instance_priority(gpointer data, gpointer user_data)
874 {
875  pe_resource_t *instance = (pe_resource_t *) data;
876  pe_resource_t *clone = (pe_resource_t *) user_data;
877  pe_node_t *chosen = NULL;
878  enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
879  GList *list = NULL;
880 
881  pe_rsc_trace(clone, "Assigning priority for %s: %s", instance->id,
882  role2text(instance->next_role));
883 
884  if (instance->fns->state(instance, TRUE) == RSC_ROLE_STARTED) {
885  set_current_role_unpromoted(instance, NULL);
886  }
887 
888  // Only an instance that will be active can be promoted
889  chosen = instance->fns->location(instance, &list, FALSE);
890  if (pcmk__list_of_multiple(list)) {
891  pcmk__config_err("Cannot promote non-colocated child %s",
892  instance->id);
893  }
894  g_list_free(list);
895  if (chosen == NULL) {
896  return;
897  }
898 
899  next_role = instance->fns->state(instance, FALSE);
900  switch (next_role) {
901  case RSC_ROLE_STARTED:
902  case RSC_ROLE_UNKNOWN:
903  // Set instance priority to its promotion score (or -1 if none)
904  {
905  bool is_default = false;
906 
907  instance->priority = promotion_score(instance, chosen,
908  &is_default);
909  if (is_default) {
910  /*
911  * Default to -1 if no value is set. This allows
912  * instances eligible for promotion to be specified
913  * based solely on rsc_location constraints, but
914  * prevents any instance from being promoted if neither
915  * a constraint nor a promotion score is present
916  */
917  instance->priority = -1;
918  }
919  }
920  break;
921 
922  case RSC_ROLE_UNPROMOTED:
923  case RSC_ROLE_STOPPED:
924  // Instance can't be promoted
925  instance->priority = -INFINITY;
926  break;
927 
928  case RSC_ROLE_PROMOTED:
929  // Nothing needed (re-creating actions after scheduling fencing)
930  break;
931 
932  default:
933  CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s",
934  next_role, instance->id));
935  }
936 
937  // Add relevant location constraint scores for promoted role
938  apply_promoted_locations(instance, instance->rsc_location, chosen);
939  apply_promoted_locations(instance, clone->rsc_location, chosen);
940 
941  // Apply relevant colocations with promoted role
942  for (GList *iter = instance->rsc_cons; iter != NULL; iter = iter->next) {
943  pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data;
944 
945  instance->cmds->apply_coloc_score(instance, cons->primary, cons, true);
946  }
947 
948  instance->sort_index = instance->priority;
949  if (next_role == RSC_ROLE_PROMOTED) {
950  instance->sort_index = INFINITY;
951  }
952  pe_rsc_trace(clone, "Assigning %s priority = %d",
953  instance->id, instance->priority);
954 }
955 
963 static void
964 set_instance_role(gpointer data, gpointer user_data)
965 {
966  pe_resource_t *instance = (pe_resource_t *) data;
967  int *count = (int *) user_data;
968 
969  pe_resource_t *clone = uber_parent(instance);
970  pe_node_t *chosen = NULL;
971 
972  show_promotion_score(instance);
973 
974  if (instance->sort_index < 0) {
975  pe_rsc_trace(clone, "Not supposed to promote instance %s",
976  instance->id);
977 
978  } else if ((*count < pe__clone_promoted_max(instance))
979  || !pcmk_is_set(clone->flags, pe_rsc_managed)) {
980  chosen = node_to_be_promoted_on(instance);
981  }
982 
983  if (chosen == NULL) {
984  set_next_role_unpromoted(instance, NULL);
985  return;
986  }
987 
988  if ((instance->role < RSC_ROLE_PROMOTED)
990  && (instance->cluster->no_quorum_policy == no_quorum_freeze)) {
991  crm_notice("Clone instance %s cannot be promoted without quorum",
992  instance->id);
993  set_next_role_unpromoted(instance, NULL);
994  return;
995  }
996 
997  chosen->count++;
998  pe_rsc_info(clone, "Choosing %s (%s) on %s for promotion",
999  instance->id, role2text(instance->role),
1000  pe__node_name(chosen));
1001  set_next_role_promoted(instance, NULL);
1002  (*count)++;
1003 }
1004 
1011 void
1013 {
1014  int promoted = 0;
1015  GHashTableIter iter;
1016  pe_node_t *node = NULL;
1017 
1018  // Repurpose count to track the number of promoted instances allocated
1019  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1020  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1021  node->count = 0;
1022  }
1023 
1024  // Set instances' promotion priorities and sort by highest priority first
1025  g_list_foreach(rsc->children, set_instance_priority, rsc);
1026  sort_promotable_instances(rsc);
1027 
1028  // Choose the first N eligible instances to be promoted
1029  g_list_foreach(rsc->children, set_instance_role, &promoted);
1030  pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
1031  rsc->id, promoted, pe__clone_promoted_max(rsc));
1032 }
1033 
1043 static void
1044 create_promotable_instance_actions(pe_resource_t *clone,
1045  bool *any_promoting, bool *any_demoting)
1046 {
1047  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1048  pe_resource_t *instance = (pe_resource_t *) iter->data;
1049 
1050  instance->cmds->create_actions(instance);
1051  check_for_role_change(instance, any_demoting, any_promoting);
1052  }
1053 }
1054 
1065 static void
1066 reset_instance_priorities(pe_resource_t *clone)
1067 {
1068  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1069  pe_resource_t *instance = (pe_resource_t *) iter->data;
1070 
1071  instance->priority = clone->priority;
1072  }
1073 }
1074 
1081 void
1083 {
1084  bool any_promoting = false;
1085  bool any_demoting = false;
1086 
1087  // Create actions for each clone instance individually
1088  create_promotable_instance_actions(clone, &any_promoting, &any_demoting);
1089 
1090  // Create pseudo-actions for clone as a whole
1091  pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting);
1092 
1093  // Undo our temporary repurposing of resource priority for instances
1094  reset_instance_priorities(clone);
1095 }
1096 
1103 void
1105 {
1106  pe_resource_t *previous = NULL; // Needed for ordered clones
1107 
1109 
1110  for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1111  pe_resource_t *instance = (pe_resource_t *) iter->data;
1112 
1113  // Demote before promote
1115  instance, RSC_PROMOTE,
1117 
1118  order_instance_promotion(clone, instance, previous);
1119  order_instance_demotion(clone, instance, previous);
1120  previous = instance;
1121  }
1122 }
1123 
1132 static void
1133 update_dependent_allowed_nodes(pe_resource_t *dependent,
1134  const pe_node_t *primary_node,
1135  const pcmk__colocation_t *colocation)
1136 {
1137  GHashTableIter iter;
1138  pe_node_t *node = NULL;
1139  const char *primary_value = NULL;
1140  const char *attr = NULL;
1141 
1142  if (colocation->score >= INFINITY) {
1143  return; // Colocation is mandatory, so allowed node scores don't matter
1144  }
1145 
1146  // Get value of primary's colocation node attribute
1147  attr = colocation->node_attribute;
1148  if (attr == NULL) {
1149  attr = CRM_ATTR_UNAME;
1150  }
1151  primary_value = pe_node_attribute_raw(primary_node, attr);
1152 
1153  pe_rsc_trace(colocation->primary,
1154  "Applying %s (%s with %s on %s by %s @%d) to %s",
1155  colocation->id, colocation->dependent->id,
1156  colocation->primary->id, pe__node_name(primary_node), attr,
1157  colocation->score, dependent->id);
1158 
1159  g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1160  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1161  const char *dependent_value = pe_node_attribute_raw(node, attr);
1162 
1163  if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) {
1164  pe_rsc_trace(colocation->primary, "%s: %d + %d",
1165  pe__node_name(node), node->weight, colocation->score);
1166  node->weight = pcmk__add_scores(node->weight, colocation->score);
1167  }
1168  }
1169 }
1170 
1178 void
1180  pe_resource_t *dependent,
1181  const pcmk__colocation_t *colocation)
1182 {
1183  GList *affected_nodes = NULL;
1184 
1185  /* Build a list of all nodes where an instance of the primary will be, and
1186  * (for optional colocations) update the dependent's allowed node scores for
1187  * each one.
1188  */
1189  for (GList *iter = primary->children; iter != NULL; iter = iter->next) {
1190  pe_resource_t *instance = (pe_resource_t *) iter->data;
1191  pe_node_t *node = instance->fns->location(instance, NULL, FALSE);
1192 
1193  if (node == NULL) {
1194  continue;
1195  }
1196  if (instance->fns->state(instance, FALSE) == colocation->primary_role) {
1197  update_dependent_allowed_nodes(dependent, node, colocation);
1198  affected_nodes = g_list_prepend(affected_nodes, node);
1199  }
1200  }
1201 
1202  /* For mandatory colocations, add the primary's node weight to the
1203  * dependent's node weight for each affected node, and ban the dependent
1204  * from all other nodes.
1205  *
1206  * However, skip this for promoted-with-promoted colocations, otherwise
1207  * inactive dependent instances can't start (in the unpromoted role).
1208  */
1209  if ((colocation->score >= INFINITY)
1210  && ((colocation->dependent_role != RSC_ROLE_PROMOTED)
1211  || (colocation->primary_role != RSC_ROLE_PROMOTED))) {
1212 
1213  pe_rsc_trace(colocation->primary,
1214  "Applying %s (mandatory %s with %s) to %s",
1215  colocation->id, colocation->dependent->id,
1216  colocation->primary->id, dependent->id);
1217  node_list_exclude(dependent->allowed_nodes, affected_nodes,
1218  TRUE);
1219  }
1220  g_list_free(affected_nodes);
1221 }
1222 
1231 void
1233  pe_resource_t *dependent,
1234  const pcmk__colocation_t *colocation)
1235 {
1236  pe_resource_t *primary_instance = NULL;
1237 
1238  // Look for a primary instance where dependent will be
1239  primary_instance = find_compatible_child(dependent, primary,
1240  colocation->primary_role, FALSE);
1241 
1242  if (primary_instance != NULL) {
1243  // Add primary instance's priority to dependent's
1244  int new_priority = pcmk__add_scores(dependent->priority,
1245  colocation->score);
1246 
1247  pe_rsc_trace(colocation->primary,
1248  "Applying %s (%s with %s) to %s priority (%s + %s = %s)",
1249  colocation->id, colocation->dependent->id,
1250  colocation->primary->id, dependent->id,
1251  pcmk_readable_score(dependent->priority),
1252  pcmk_readable_score(colocation->score),
1253  pcmk_readable_score(new_priority));
1254  dependent->priority = new_priority;
1255 
1256  } else if (colocation->score >= INFINITY) {
1257  // Mandatory colocation, but primary won't be here
1258  pe_rsc_trace(colocation->primary,
1259  "Applying %s (%s with %s) to %s: can't be promoted",
1260  colocation->id, colocation->dependent->id,
1261  colocation->primary->id, dependent->id);
1262  dependent->priority = -INFINITY;
1263  }
1264 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
enum rsc_role_e role_filter
Definition: internal.h:189
enum pe_quorum_policy no_quorum_policy
Definition: pe_types.h:156
#define crm_notice(fmt, args...)
Definition: logging.h:361
GHashTable * known_on
Definition: pe_types.h:374
const char * pe_node_attribute_calculated(const pe_node_t *node, const char *name, const pe_resource_t *rsc)
Definition: common.c:522
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:46
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
GList * rsc_cons
Definition: pe_types.h:364
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition: internal.h:394
int pe__clone_promoted_node_max(pe_resource_t *clone)
Definition: clone.c:59
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,...)
enum rsc_role_e role
Definition: pe_types.h:377
GList * children
Definition: pe_types.h:384
int count
Definition: pe_types.h:251
resource_alloc_functions_t * cmds
Definition: pe_types.h:341
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:378
int pe__clone_promoted_max(pe_resource_t *clone)
Definition: clone.c:42
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:262
int pe__set_clone_flag(pe_resource_t *clone, enum pe__clone_flags flag)
Definition: clone.c:1235
resource_object_functions_t * fns
Definition: pe_types.h:340
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
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:211
GList * rsc_cons_lhs
Definition: pe_types.h:363
G_GNUC_INTERNAL void pcmk__add_colocated_node_scores(pe_resource_t *rsc, const char *log_id, GHashTable **nodes, const char *attr, float factor, uint32_t flags)
#define pe_flag_have_quorum
Definition: pe_types.h:95
const char * action
Definition: pcmk_fence.c:30
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:74
const char * role2text(enum rsc_role_e role)
Definition: common.c:454
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:55
int weight
Definition: pe_types.h:249
#define pe_rsc_merging
Definition: pe_types.h:268
#define crm_warn(fmt, args...)
Definition: logging.h:360
void pcmk__set_instance_roles(pe_resource_t *rsc)
pe_resource_t * find_compatible_child(const pe_resource_t *local_child, const pe_resource_t *rsc, enum rsc_role_e filter, gboolean current)
G_GNUC_INTERNAL bool pcmk__node_available(const pe_node_t *node, bool consider_score, bool consider_guest)
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:912
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:562
char * clone_strip(const char *last_rsc_id)
Definition: unpack.c:1647
pe_resource_t * primary
match only clone instances
Definition: pe_types.h:87
#define CRM_ATTR_UNAME
Definition: crm.h:113
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
struct pe_node_shared_s * details
Definition: pe_types.h:252
void pe__create_promotable_pseudo_ops(pe_resource_t *clone, bool any_promoting, bool any_demoting)
Definition: clone.c:1258
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:355
char * clone_name
Definition: pe_types.h:330
pe_resource_t rsc2
void(* create_actions)(pe_resource_t *rsc)
GList * actions
Definition: pe_types.h:366
bool pe__clone_is_ordered(pe_resource_t *clone)
Definition: clone.c:1216
#define RSC_DEMOTED
Definition: crm.h:208
const char * id
Definition: pe_types.h:215
#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)
pe_node_t * pe_find_node_id(GList *node_list, const char *id)
Definition: status.c:427
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)
int sort_index
Definition: pe_types.h:349
#define crm_err(fmt, args...)
Definition: logging.h:359
#define CRM_ASSERT(expr)
Definition: results.h:42
#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:1120
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:365
G_GNUC_INTERNAL void pcmk__promotable_restart_ordering(pe_resource_t *rsc)
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:80
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
GList * running_on
Definition: pe_types.h:373
pe_working_set_t * cluster
Definition: pe_types.h:335
const char * node_attribute
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:113
#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:47
unsigned long long flags
Definition: pe_types.h:153
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:134
#define pe_rsc_managed
Definition: pe_types.h:257
uint64_t flags
Definition: remote.c:215
match resource active on specified node
Definition: pe_types.h:88
#define RSC_DEMOTE
Definition: crm.h:207
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:45
char * id
Definition: pe_types.h:329
GHashTable * allowed_nodes
Definition: pe_types.h:375