pacemaker 2.1.8-2.1.8~rc4
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
16
25static void
26order_instance_promotion(pcmk_resource_t *clone, pcmk_resource_t *child,
27 pcmk_resource_t *last)
28{
29 // "Promote clone" -> promote instance -> "clone promoted"
36
37 // If clone is ordered, order this instance relative to last
38 if ((last != NULL) && pe__clone_is_ordered(clone)) {
42 }
43}
44
53static void
54order_instance_demotion(pcmk_resource_t *clone, pcmk_resource_t *child,
55 pcmk_resource_t *last)
56{
57 // "Demote clone" -> demote instance -> "clone 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
80static void
81check_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->children != NULL) {
88 for (iter = rsc->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->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,
106 *demoting = true;
107
108 } else if (pcmk__str_eq(PCMK_ACTION_PROMOTE, action->task,
110 *promoting = true;
111 }
112 }
113}
114
127static void
128apply_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->details->id);
139 }
140 if (constraint_node != NULL) {
141 int new_priority = pcmk__add_scores(child->priority,
142 constraint_node->weight);
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->weight),
151 pcmk_readable_score(new_priority));
152 child->priority = new_priority;
153 }
154 }
155}
156
165static pcmk_node_t *
166node_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->children; iter != NULL; iter = iter->next) {
174 pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
175
176 if (node_to_be_promoted_on(child) == NULL) {
177 pcmk__rsc_trace(rsc,
178 "%s can't be promoted because member %s can't",
179 rsc->id, child->id);
180 return NULL;
181 }
182 }
183
184 node = rsc->fns->location(rsc, NULL, FALSE);
185 if (node == NULL) {
186 pcmk__rsc_trace(rsc, "%s can't be promoted because it won't be active",
187 rsc->id);
188 return NULL;
189
190 } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
191 if (rsc->fns->state(rsc, TRUE) == pcmk_role_promoted) {
192 crm_notice("Unmanaged instance %s will be left promoted on %s",
193 rsc->id, pcmk__node_name(node));
194 } else {
195 pcmk__rsc_trace(rsc, "%s can't be promoted because it is unmanaged",
196 rsc->id);
197 return NULL;
198 }
199
200 } else if (rsc->priority < 0) {
201 pcmk__rsc_trace(rsc,
202 "%s can't be promoted because its promotion priority "
203 "%d is negative",
204 rsc->id, rsc->priority);
205 return NULL;
206
207 } else if (!pcmk__node_available(node, false, true)) {
208 pcmk__rsc_trace(rsc,
209 "%s can't be promoted because %s can't run resources",
210 rsc->id, pcmk__node_name(node));
211 return NULL;
212 }
213
214 parent = pe__const_top_resource(rsc, false);
215 local_node = g_hash_table_lookup(parent->allowed_nodes, node->details->id);
216
217 if (local_node == NULL) {
218 /* It should not be possible for the scheduler to have assigned the
219 * instance to a node where its parent is not allowed, but it's good to
220 * have a fail-safe.
221 */
223 pcmk__sched_err("%s can't be promoted because %s is not allowed "
224 "on %s (scheduler bug?)",
225 rsc->id, parent->id, pcmk__node_name(node));
226 } // else the instance is unmanaged and already promoted
227 return NULL;
228
229 } else if ((local_node->count >= pe__clone_promoted_node_max(parent))
231 pcmk__rsc_trace(rsc,
232 "%s can't be promoted because %s has "
233 "maximum promoted instances already",
234 rsc->id, pcmk__node_name(node));
235 return NULL;
236 }
237
238 return local_node;
239}
240
252static gint
253cmp_promotable_instance(gconstpointer a, gconstpointer b)
254{
255 const pcmk_resource_t *rsc1 = (const pcmk_resource_t *) a;
256 const pcmk_resource_t *rsc2 = (const pcmk_resource_t *) b;
257
258 enum rsc_role_e role1 = pcmk_role_unknown;
259 enum rsc_role_e role2 = pcmk_role_unknown;
260
261 CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
262
263 // Check sort index set by pcmk__set_instance_roles()
264 if (rsc1->sort_index > rsc2->sort_index) {
265 pcmk__rsc_trace(rsc1,
266 "%s has higher promotion priority than %s "
267 "(sort index %d > %d)",
269 return -1;
270 } else if (rsc1->sort_index < rsc2->sort_index) {
271 pcmk__rsc_trace(rsc1,
272 "%s has lower promotion priority than %s "
273 "(sort index %d < %d)",
275 return 1;
276 }
277
278 // If those are the same, prefer instance whose current role is higher
279 role1 = rsc1->fns->state(rsc1, TRUE);
280 role2 = rsc2->fns->state(rsc2, TRUE);
281 if (role1 > role2) {
282 pcmk__rsc_trace(rsc1,
283 "%s has higher promotion priority than %s "
284 "(higher current role)",
285 rsc1->id, rsc2->id);
286 return -1;
287 } else if (role1 < role2) {
288 pcmk__rsc_trace(rsc1,
289 "%s has lower promotion priority than %s "
290 "(lower current role)",
291 rsc1->id, rsc2->id);
292 return 1;
293 }
294
295 // Finally, do normal clone instance sorting
296 return pcmk__cmp_instance(a, b);
297}
298
310static void
311add_sort_index_to_node_score(gpointer data, gpointer user_data)
312{
313 const pcmk_resource_t *child = (const pcmk_resource_t *) data;
314 pcmk_resource_t *clone = (pcmk_resource_t *) user_data;
315
316 pcmk_node_t *node = NULL;
317 const pcmk_node_t *chosen = NULL;
318
319 if (child->sort_index < 0) {
320 pcmk__rsc_trace(clone, "Not adding sort index of %s: negative",
321 child->id);
322 return;
323 }
324
325 chosen = child->fns->location(child, NULL, FALSE);
326 if (chosen == NULL) {
327 pcmk__rsc_trace(clone, "Not adding sort index of %s: inactive",
328 child->id);
329 return;
330 }
331
332 node = g_hash_table_lookup(clone->allowed_nodes, chosen->details->id);
333 CRM_ASSERT(node != NULL);
334
335 node->weight = pcmk__add_scores(child->sort_index, node->weight);
336 pcmk__rsc_trace(clone,
337 "Added cumulative priority of %s (%s) to score on %s "
338 "(now %s)",
339 child->id, pcmk_readable_score(child->sort_index),
340 pcmk__node_name(node), pcmk_readable_score(node->weight));
341}
342
350static void
351apply_coloc_to_dependent(gpointer data, gpointer user_data)
352{
353 pcmk__colocation_t *colocation = data;
354 pcmk_resource_t *clone = user_data;
355 pcmk_resource_t *primary = colocation->primary;
357 float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
358
359 if (colocation->dependent_role != pcmk_role_promoted) {
360 return;
361 }
362 if (colocation->score < PCMK_SCORE_INFINITY) {
364 }
365 pcmk__rsc_trace(clone, "Applying colocation %s (promoted %s with %s) @%s",
366 colocation->id, colocation->dependent->id,
367 colocation->primary->id,
368 pcmk_readable_score(colocation->score));
369 primary->cmds->add_colocated_node_scores(primary, clone, clone->id,
370 &clone->allowed_nodes, colocation,
371 factor, flags);
372}
373
381static void
382apply_coloc_to_primary(gpointer data, gpointer user_data)
383{
384 pcmk__colocation_t *colocation = data;
385 pcmk_resource_t *clone = user_data;
386 pcmk_resource_t *dependent = colocation->dependent;
387 const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
388 const uint32_t flags = pcmk__coloc_select_active
390
391 if ((colocation->primary_role != pcmk_role_promoted)
392 || !pcmk__colocation_has_influence(colocation, NULL)) {
393 return;
394 }
395
396 pcmk__rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s",
397 colocation->id, colocation->dependent->id,
398 colocation->primary->id,
399 pcmk_readable_score(colocation->score));
400 dependent->cmds->add_colocated_node_scores(dependent, clone, clone->id,
401 &clone->allowed_nodes,
402 colocation, factor, flags);
403}
404
412static void
413set_sort_index_to_node_score(gpointer data, gpointer user_data)
414{
416 const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
417
418 pcmk_node_t *chosen = child->fns->location(child, NULL, FALSE);
419
421 && (child->next_role == pcmk_role_promoted)) {
423 pcmk__rsc_trace(clone,
424 "Final sort index for %s is INFINITY "
425 "(unmanaged promoted)",
426 child->id);
427
428 } else if (chosen == NULL) {
430 pcmk__rsc_trace(clone,
431 "Final promotion priority for %s is %s "
432 "(will not be active)",
434
435 } else if (child->sort_index < 0) {
436 pcmk__rsc_trace(clone,
437 "Final sort index for %s is %d (ignoring node score)",
438 child->id, child->sort_index);
439
440 } else {
441 const pcmk_node_t *node = g_hash_table_lookup(clone->allowed_nodes,
442 chosen->details->id);
443
444 CRM_ASSERT(node != NULL);
445 child->sort_index = node->weight;
446 pcmk__rsc_trace(clone,
447 "Adding scores for %s: final sort index for %s is %d",
448 clone->id, child->id, child->sort_index);
449 }
450}
451
458static void
459sort_promotable_instances(pcmk_resource_t *clone)
460{
461 GList *colocations = NULL;
462
464 == pcmk_rc_already) {
465 return;
466 }
468
469 for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
470 pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
471
472 pcmk__rsc_trace(clone,
473 "Adding scores for %s: initial sort index for %s is %d",
474 clone->id, child->id, child->sort_index);
475 }
476 pe__show_node_scores(true, clone, "Before", clone->allowed_nodes,
477 clone->cluster);
478
479 g_list_foreach(clone->children, add_sort_index_to_node_score, clone);
480
481 colocations = pcmk__this_with_colocations(clone);
482 g_list_foreach(colocations, apply_coloc_to_dependent, clone);
483 g_list_free(colocations);
484
485 colocations = pcmk__with_this_colocations(clone);
486 g_list_foreach(colocations, apply_coloc_to_primary, clone);
487 g_list_free(colocations);
488
489 // Ban resource from all nodes if it needs a ticket but doesn't have it
491
492 pe__show_node_scores(true, clone, "After", clone->allowed_nodes,
493 clone->cluster);
494
495 // Reset sort indexes to final node scores
496 g_list_foreach(clone->children, set_sort_index_to_node_score, clone);
497
498 // Finally, sort instances in descending order of promotion priority
499 clone->children = g_list_sort(clone->children, cmp_promotable_instance);
501}
502
513static pcmk_resource_t *
514find_active_anon_instance(const pcmk_resource_t *clone, const char *id,
515 const pcmk_node_t *node)
516{
517 for (GList *iter = clone->children; iter; iter = iter->next) {
518 pcmk_resource_t *child = iter->data;
519 pcmk_resource_t *active = NULL;
520
521 // Use ->find_rsc() in case this is a cloned group
522 active = clone->fns->find_rsc(child, id, node,
525 if (active != NULL) {
526 return active;
527 }
528 }
529 return NULL;
530}
531
532/*
533 * \brief Check whether an anonymous clone instance is known on a node
534 *
535 * \param[in] clone Anonymous clone to check
536 * \param[in] id Instance ID (without instance number) to check
537 * \param[in] node Node to check
538 *
539 * \return true if \p id instance of \p clone is known on \p node,
540 * otherwise false
541 */
542static bool
543anonymous_known_on(const pcmk_resource_t *clone, const char *id,
544 const pcmk_node_t *node)
545{
546 for (GList *iter = clone->children; iter; iter = iter->next) {
547 pcmk_resource_t *child = iter->data;
548
549 /* Use ->find_rsc() because this might be a cloned group, and knowing
550 * that other members of the group are known here implies nothing.
551 */
552 child = clone->fns->find_rsc(child, id, NULL,
554 CRM_LOG_ASSERT(child != NULL);
555 if (child != NULL) {
556 if (g_hash_table_lookup(child->known_on, node->details->id)) {
557 return true;
558 }
559 }
560 }
561 return false;
562}
563
573static bool
574is_allowed(const pcmk_resource_t *rsc, const pcmk_node_t *node)
575{
576 pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes,
577 node->details->id);
578
579 return (allowed != NULL) && (allowed->weight >= 0);
580}
581
591static bool
592promotion_score_applies(const pcmk_resource_t *rsc, const pcmk_node_t *node)
593{
594 char *id = clone_strip(rsc->id);
595 const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
596 pcmk_resource_t *active = NULL;
597 const char *reason = "allowed";
598
599 // Some checks apply only to anonymous clone instances
600 if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
601
602 // If instance is active on the node, its score definitely applies
603 active = find_active_anon_instance(parent, id, node);
604 if (active == rsc) {
605 reason = "active";
606 goto check_allowed;
607 }
608
609 /* If *no* instance is active on this node, this instance's score will
610 * count if it has been probed on this node.
611 */
612 if ((active == NULL) && anonymous_known_on(parent, id, node)) {
613 reason = "probed";
614 goto check_allowed;
615 }
616 }
617
618 /* If this clone's status is unknown on *all* nodes (e.g. cluster startup),
619 * take all instances' scores into account, to make sure we use any
620 * permanent promotion scores.
621 */
622 if ((rsc->running_on == NULL) && (g_hash_table_size(rsc->known_on) == 0)) {
623 reason = "none probed";
624 goto check_allowed;
625 }
626
627 /* Otherwise, we've probed and/or started the resource *somewhere*, so
628 * consider promotion scores on nodes where we know the status.
629 */
630 if ((g_hash_table_lookup(rsc->known_on, node->details->id) != NULL)
631 || (pe_find_node_id(rsc->running_on, node->details->id) != NULL)) {
632 reason = "known";
633 } else {
634 pcmk__rsc_trace(rsc,
635 "Ignoring %s promotion score (for %s) on %s: "
636 "not probed",
637 rsc->id, id, pcmk__node_name(node));
638 free(id);
639 return false;
640 }
641
642check_allowed:
643 if (is_allowed(rsc, node)) {
644 pcmk__rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s",
645 rsc->id, id, pcmk__node_name(node), reason);
646 free(id);
647 return true;
648 }
649
650 pcmk__rsc_trace(rsc,
651 "Ignoring %s promotion score (for %s) on %s: not allowed",
652 rsc->id, id, pcmk__node_name(node));
653 free(id);
654 return false;
655}
656
667static const char *
668promotion_attr_value(const pcmk_resource_t *rsc, const pcmk_node_t *node,
669 const char *name)
670{
671 char *attr_name = NULL;
672 const char *attr_value = NULL;
673 const char *target = NULL;
675
677 // Not assigned yet
679 }
680 target = g_hash_table_lookup(rsc->meta,
682 attr_name = pcmk_promotion_score_name(name);
683 attr_value = pcmk__node_attr(node, attr_name, target, node_type);
684 free(attr_name);
685 return attr_value;
686}
687
698static int
699promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node,
700 bool *is_default)
701{
702 char *name = NULL;
703 const char *attr_value = NULL;
704
705 if (is_default != NULL) {
706 *is_default = true;
707 }
708
709 CRM_CHECK((rsc != NULL) && (node != NULL), return 0);
710
711 /* If this is an instance of a cloned group, the promotion score is the sum
712 * of all members' promotion scores.
713 */
714 if (rsc->children != NULL) {
715 int score = 0;
716
717 for (const GList *iter = rsc->children;
718 iter != NULL; iter = iter->next) {
719
720 const pcmk_resource_t *child = (const pcmk_resource_t *) iter->data;
721 bool child_default = false;
722 int child_score = promotion_score(child, node, &child_default);
723
724 if (!child_default && (is_default != NULL)) {
725 *is_default = false;
726 }
727 score += child_score;
728 }
729 return score;
730 }
731
732 if (!promotion_score_applies(rsc, node)) {
733 return 0;
734 }
735
736 /* For the promotion score attribute name, use the name the resource is
737 * known as in resource history, since that's what crm_attribute --promotion
738 * would have used.
739 */
740 name = (rsc->clone_name == NULL)? rsc->id : rsc->clone_name;
741
742 attr_value = promotion_attr_value(rsc, node, name);
743 if (attr_value != NULL) {
744 pcmk__rsc_trace(rsc, "Promotion score for %s on %s = %s",
745 name, pcmk__node_name(node),
746 pcmk__s(attr_value, "(unset)"));
747 } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
748 /* If we don't have any resource history yet, we won't have clone_name.
749 * In that case, for anonymous clones, try the resource name without
750 * any instance number.
751 */
752 name = clone_strip(rsc->id);
753 if (strcmp(rsc->id, name) != 0) {
754 attr_value = promotion_attr_value(rsc, node, name);
755 pcmk__rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s",
756 name, pcmk__node_name(node), rsc->id,
757 pcmk__s(attr_value, "(unset)"));
758 }
759 free(name);
760 }
761
762 if (attr_value == NULL) {
763 return 0;
764 }
765
766 if (is_default != NULL) {
767 *is_default = false;
768 }
769 return char2score(attr_value);
770}
771
778void
780{
781 if (pe__set_clone_flag(rsc,
783 return;
784 }
785
786 for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
787 pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
788
789 GHashTableIter iter;
790 pcmk_node_t *node = NULL;
791 int score, new_score;
792
793 g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
794 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
795 if (!pcmk__node_available(node, false, false)) {
796 /* This node will never be promoted, so don't apply the
797 * promotion score, as that may lead to clone shuffling.
798 */
799 continue;
800 }
801
802 score = promotion_score(child_rsc, node, NULL);
803 if (score > 0) {
804 new_score = pcmk__add_scores(node->weight, score);
805 if (new_score != node->weight) { // Could remain INFINITY
806 node->weight = new_score;
807 pcmk__rsc_trace(rsc,
808 "Added %s promotion priority (%s) to score "
809 "on %s (now %s)",
810 child_rsc->id, pcmk_readable_score(score),
811 pcmk__node_name(node),
812 pcmk_readable_score(new_score));
813 }
814 }
815
816 if (score > child_rsc->priority) {
817 pcmk__rsc_trace(rsc,
818 "Updating %s priority to promotion score "
819 "(%d->%d)",
820 child_rsc->id, child_rsc->priority, score);
821 child_rsc->priority = score;
822 }
823 }
824 }
825}
826
834static void
835set_current_role_unpromoted(void *data, void *user_data)
836{
838
839 if (rsc->role == pcmk_role_started) {
840 // Promotable clones should use unpromoted role instead of started
842 }
843 g_list_foreach(rsc->children, set_current_role_unpromoted, NULL);
844}
845
853static void
854set_next_role_unpromoted(void *data, void *user_data)
855{
857 GList *assigned = NULL;
858
859 rsc->fns->location(rsc, &assigned, FALSE);
860 if (assigned == NULL) {
861 pe__set_next_role(rsc, pcmk_role_stopped, "stopped instance");
862 } else {
863 pe__set_next_role(rsc, pcmk_role_unpromoted, "unpromoted instance");
864 g_list_free(assigned);
865 }
866 g_list_foreach(rsc->children, set_next_role_unpromoted, NULL);
867}
868
876static void
877set_next_role_promoted(void *data, gpointer user_data)
878{
880
881 if (rsc->next_role == pcmk_role_unknown) {
882 pe__set_next_role(rsc, pcmk_role_promoted, "promoted instance");
883 }
884 g_list_foreach(rsc->children, set_next_role_promoted, NULL);
885}
886
893static void
894show_promotion_score(pcmk_resource_t *instance)
895{
896 pcmk_node_t *chosen = instance->fns->location(instance, NULL, FALSE);
897
899 && !pcmk__is_daemon && (instance->cluster->priv != NULL)) {
900
901 pcmk__output_t *out = instance->cluster->priv;
902
903 out->message(out, "promotion-score", instance, chosen,
905
906 } else if (chosen == NULL) {
908 "%s promotion score (inactive): %s (priority=%d)",
909 instance->id, pcmk_readable_score(instance->sort_index), instance->priority);
910
911 } else {
913 "%s promotion score on %s: %s (priority=%d)",
914 instance->id, pcmk__node_name(chosen),
916 instance->priority);
917 }
918}
919
927static void
928set_instance_priority(gpointer data, gpointer user_data)
929{
930 pcmk_resource_t *instance = (pcmk_resource_t *) data;
931 const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
932 const pcmk_node_t *chosen = NULL;
933 enum rsc_role_e next_role = pcmk_role_unknown;
934 GList *list = NULL;
935
936 pcmk__rsc_trace(clone, "Assigning priority for %s: %s", instance->id,
937 pcmk_role_text(instance->next_role));
938
939 if (instance->fns->state(instance, TRUE) == pcmk_role_started) {
940 set_current_role_unpromoted(instance, NULL);
941 }
942
943 // Only an instance that will be active can be promoted
944 chosen = instance->fns->location(instance, &list, FALSE);
945 if (pcmk__list_of_multiple(list)) {
946 pcmk__config_err("Cannot promote non-colocated child %s",
947 instance->id);
948 }
949 g_list_free(list);
950 if (chosen == NULL) {
951 return;
952 }
953
954 next_role = instance->fns->state(instance, FALSE);
955 switch (next_role) {
958 // Set instance priority to its promotion score (or -1 if none)
959 {
960 bool is_default = false;
961
962 instance->priority = promotion_score(instance, chosen,
963 &is_default);
964 if (is_default) {
965 /* Default to -1 if no value is set. This allows instances
966 * eligible for promotion to be specified based solely on
967 * PCMK_XE_RSC_LOCATION constraints, but prevents any
968 * instance from being promoted if neither a constraint nor
969 * a promotion score is present.
970 */
971 instance->priority = -1;
972 }
973 }
974 break;
975
978 // Instance can't be promoted
979 instance->priority = -PCMK_SCORE_INFINITY;
980 break;
981
983 // Nothing needed (re-creating actions after scheduling fencing)
984 break;
985
986 default:
987 CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s",
988 next_role, instance->id));
989 }
990
991 // Add relevant location constraint scores for promoted role
992 apply_promoted_locations(instance, instance->rsc_location, chosen);
993 apply_promoted_locations(instance, clone->rsc_location, chosen);
994
995 // Consider instance's role-based colocations with other resources
996 list = pcmk__this_with_colocations(instance);
997 for (GList *iter = list; iter != NULL; iter = iter->next) {
998 pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data;
999
1000 instance->cmds->apply_coloc_score(instance, cons->primary, cons, true);
1001 }
1002 g_list_free(list);
1003
1004 instance->sort_index = instance->priority;
1005 if (next_role == pcmk_role_promoted) {
1006 instance->sort_index = PCMK_SCORE_INFINITY;
1007 }
1008 pcmk__rsc_trace(clone, "Assigning %s priority = %d",
1009 instance->id, instance->priority);
1010}
1011
1019static void
1020set_instance_role(gpointer data, gpointer user_data)
1021{
1022 pcmk_resource_t *instance = (pcmk_resource_t *) data;
1023 int *count = (int *) user_data;
1024
1025 const pcmk_resource_t *clone = pe__const_top_resource(instance, false);
1026 pcmk_node_t *chosen = NULL;
1027
1028 show_promotion_score(instance);
1029
1030 if (instance->sort_index < 0) {
1031 pcmk__rsc_trace(clone, "Not supposed to promote instance %s",
1032 instance->id);
1033
1034 } else if ((*count < pe__clone_promoted_max(instance))
1035 || !pcmk_is_set(clone->flags, pcmk_rsc_managed)) {
1036 chosen = node_to_be_promoted_on(instance);
1037 }
1038
1039 if (chosen == NULL) {
1040 set_next_role_unpromoted(instance, NULL);
1041 return;
1042 }
1043
1044 if ((instance->role < pcmk_role_promoted)
1046 && (instance->cluster->no_quorum_policy == pcmk_no_quorum_freeze)) {
1047 crm_notice("Clone instance %s cannot be promoted without quorum",
1048 instance->id);
1049 set_next_role_unpromoted(instance, NULL);
1050 return;
1051 }
1052
1053 chosen->count++;
1054 pcmk__rsc_info(clone, "Choosing %s (%s) on %s for promotion",
1055 instance->id, pcmk_role_text(instance->role),
1056 pcmk__node_name(chosen));
1057 set_next_role_promoted(instance, NULL);
1058 (*count)++;
1059}
1060
1067void
1069{
1070 int promoted = 0;
1071 GHashTableIter iter;
1072 pcmk_node_t *node = NULL;
1073
1074 // Repurpose count to track the number of promoted instances assigned
1075 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1076 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1077 node->count = 0;
1078 }
1079
1080 // Set instances' promotion priorities and sort by highest priority first
1081 g_list_foreach(rsc->children, set_instance_priority, rsc);
1082 sort_promotable_instances(rsc);
1083
1084 // Choose the first N eligible instances to be promoted
1085 g_list_foreach(rsc->children, set_instance_role, &promoted);
1086 pcmk__rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
1087 rsc->id, promoted, pe__clone_promoted_max(rsc));
1088}
1089
1099static void
1100create_promotable_instance_actions(pcmk_resource_t *clone,
1101 bool *any_promoting, bool *any_demoting)
1102{
1103 for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1104 pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1105
1106 instance->cmds->create_actions(instance);
1107 check_for_role_change(instance, any_demoting, any_promoting);
1108 }
1109}
1110
1121static void
1122reset_instance_priorities(pcmk_resource_t *clone)
1123{
1124 for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1125 pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1126
1127 instance->priority = clone->priority;
1128 }
1129}
1130
1137void
1139{
1140 bool any_promoting = false;
1141 bool any_demoting = false;
1142
1143 // Create actions for each clone instance individually
1144 create_promotable_instance_actions(clone, &any_promoting, &any_demoting);
1145
1146 // Create pseudo-actions for clone as a whole
1147 pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting);
1148
1149 // Undo our temporary repurposing of resource priority for instances
1150 reset_instance_priorities(clone);
1151}
1152
1159void
1161{
1162 pcmk_resource_t *previous = NULL; // Needed for ordered clones
1163
1165
1166 for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1167 pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1168
1169 // Demote before promote
1171 instance, PCMK_ACTION_PROMOTE,
1173
1174 order_instance_promotion(clone, instance, previous);
1175 order_instance_demotion(clone, instance, previous);
1176 previous = instance;
1177 }
1178}
1179
1189static void
1190update_dependent_allowed_nodes(pcmk_resource_t *dependent,
1191 const pcmk_resource_t *primary,
1192 const pcmk_node_t *primary_node,
1193 const pcmk__colocation_t *colocation)
1194{
1195 GHashTableIter iter;
1196 pcmk_node_t *node = NULL;
1197 const char *primary_value = NULL;
1198 const char *attr = colocation->node_attribute;
1199
1200 if (colocation->score >= PCMK_SCORE_INFINITY) {
1201 return; // Colocation is mandatory, so allowed node scores don't matter
1202 }
1203
1204 primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1205
1206 pcmk__rsc_trace(colocation->primary,
1207 "Applying %s (%s with %s on %s by %s @%d) to %s",
1208 colocation->id, colocation->dependent->id,
1209 colocation->primary->id, pcmk__node_name(primary_node),
1210 attr, colocation->score, dependent->id);
1211
1212 g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1213 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1214 const char *dependent_value = pcmk__colocation_node_attr(node, attr,
1215 dependent);
1216
1217 if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) {
1218 node->weight = pcmk__add_scores(node->weight, colocation->score);
1219 pcmk__rsc_trace(colocation->primary,
1220 "Added %s score (%s) to %s (now %s)",
1221 colocation->id,
1222 pcmk_readable_score(colocation->score),
1223 pcmk__node_name(node),
1224 pcmk_readable_score(node->weight));
1225 }
1226 }
1227}
1228
1236void
1238 pcmk_resource_t *dependent,
1239 const pcmk__colocation_t *colocation)
1240{
1241 GList *affected_nodes = NULL;
1242
1243 /* Build a list of all nodes where an instance of the primary will be, and
1244 * (for optional colocations) update the dependent's allowed node scores for
1245 * each one.
1246 */
1247 for (GList *iter = primary->children; iter != NULL; iter = iter->next) {
1248 pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1249 pcmk_node_t *node = instance->fns->location(instance, NULL, FALSE);
1250
1251 if (node == NULL) {
1252 continue;
1253 }
1254 if (instance->fns->state(instance, FALSE) == colocation->primary_role) {
1255 update_dependent_allowed_nodes(dependent, primary, node,
1256 colocation);
1257 affected_nodes = g_list_prepend(affected_nodes, node);
1258 }
1259 }
1260
1261 /* For mandatory colocations, add the primary's node score to the
1262 * dependent's node score for each affected node, and ban the dependent
1263 * from all other nodes.
1264 *
1265 * However, skip this for promoted-with-promoted colocations, otherwise
1266 * inactive dependent instances can't start (in the unpromoted role).
1267 */
1268 if ((colocation->score >= PCMK_SCORE_INFINITY)
1269 && ((colocation->dependent_role != pcmk_role_promoted)
1270 || (colocation->primary_role != pcmk_role_promoted))) {
1271
1272 pcmk__rsc_trace(colocation->primary,
1273 "Applying %s (mandatory %s with %s) to %s",
1274 colocation->id, colocation->dependent->id,
1275 colocation->primary->id, dependent->id);
1276 pcmk__colocation_intersect_nodes(dependent, primary, colocation,
1277 affected_nodes, true);
1278 }
1279 g_list_free(affected_nodes);
1280}
1281
1290void
1292 pcmk_resource_t *dependent,
1293 const pcmk__colocation_t *colocation)
1294{
1295 pcmk_resource_t *primary_instance = NULL;
1296
1297 // Look for a primary instance where dependent will be
1298 primary_instance = pcmk__find_compatible_instance(dependent, primary,
1299 colocation->primary_role,
1300 false);
1301
1302 if (primary_instance != NULL) {
1303 // Add primary instance's priority to dependent's
1304 int new_priority = pcmk__add_scores(dependent->priority,
1305 colocation->score);
1306
1307 pcmk__rsc_trace(colocation->primary,
1308 "Applying %s (%s with %s) to %s priority "
1309 "(%s + %s = %s)",
1310 colocation->id, colocation->dependent->id,
1311 colocation->primary->id, dependent->id,
1312 pcmk_readable_score(dependent->priority),
1313 pcmk_readable_score(colocation->score),
1314 pcmk_readable_score(new_priority));
1315 dependent->priority = new_priority;
1316
1317 } else if (colocation->score >= PCMK_SCORE_INFINITY) {
1318 // Mandatory colocation, but primary won't be here
1319 pcmk__rsc_trace(colocation->primary,
1320 "Applying %s (%s with %s) to %s: can't be promoted",
1321 colocation->id, colocation->dependent->id,
1322 colocation->primary->id, dependent->id);
1323 dependent->priority = -PCMK_SCORE_INFINITY;
1324 }
1325}
@ pcmk__ar_then_implies_first_graphed
If 'then' is required, 'first' must be added to the transition graph.
@ pcmk__ar_first_implies_then_graphed
If 'first' is required and runnable, 'then' must be in graph.
@ pcmk__ar_ordered
Actions are ordered (optionally, if no other flags are set)
#define PCMK_ACTION_PROMOTED
Definition actions.h:67
#define PCMK_ACTION_PROMOTE
Definition actions.h:66
@ pcmk_action_optional
Definition actions.h:210
#define PCMK_ACTION_DEMOTED
Definition actions.h:50
#define PCMK_ACTION_DEMOTE
Definition actions.h:49
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:118
const char * parent
Definition cib.c:27
const char * name
Definition cib.c:26
@ pcmk__clone_promotion_added
@ pcmk__clone_promotion_constrained
bool pcmk__is_daemon
Definition logging.c:47
uint64_t flags
Definition remote.c:3
char * pcmk_promotion_score_name(const char *rsc_id)
Return the name of the node attribute used as a promotion score.
Definition attrs.c:92
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:100
char data[0]
Definition cpg.c:10
@ pcmk__rsc_node_current
Where resource is running.
@ pcmk__rsc_node_assigned
Where resource is assigned.
@ pcmk__coloc_select_active
@ pcmk__coloc_select_nonnegative
@ pcmk__coloc_select_default
G_GNUC_INTERNAL void pcmk__require_promotion_tickets(pcmk_resource_t *rsc)
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)
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
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)
G_GNUC_INTERNAL GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
G_GNUC_INTERNAL gint pcmk__cmp_instance(gconstpointer a, gconstpointer b)
G_GNUC_INTERNAL GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
G_GNUC_INTERNAL void pcmk__promotable_restart_ordering(pcmk_resource_t *rsc)
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
#define CRM_LOG_ASSERT(expr)
Definition logging.h:228
#define crm_notice(fmt, args...)
Definition logging.h:395
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_err(fmt, args...)
Definition logging.h:389
#define pcmk__config_err(fmt...)
node_type
Definition nodes.h:38
#define PCMK_META_CONTAINER_ATTRIBUTE_TARGET
Definition options.h:85
const char * action
Definition pcmk_fence.c:30
const char * target
Definition pcmk_fence.c:29
void pcmk__add_promotion_scores(pcmk_resource_t *rsc)
void pcmk__order_promotable_instances(pcmk_resource_t *clone)
void pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary, pcmk_resource_t *dependent, const pcmk__colocation_t *colocation)
void pcmk__create_promotable_actions(pcmk_resource_t *clone)
void pcmk__set_instance_roles(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.
pcmk_resource_t rsc2
pcmk_resource_t rsc1
void pe__create_promotable_pseudo_ops(pcmk_resource_t *clone, bool any_promoting, bool any_demoting)
Definition clone.c:1406
int pe__clone_promoted_node_max(const pcmk_resource_t *clone)
Definition clone.c:114
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition complex.c:1032
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition internal.h:176
bool pe__clone_is_ordered(const pcmk_resource_t *clone)
Definition clone.c:1344
int pe__set_clone_flag(pcmk_resource_t *clone, enum pcmk__clone_flags flag)
Definition clone.c:1363
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition complex.c:1253
char * clone_strip(const char *last_rsc_id)
Definition unpack.c:1955
int pe__clone_promoted_max(const pcmk_resource_t *clone)
Definition clone.c:97
@ pcmk_rsc_match_clone_only
Match only clones and their instances, by either clone or instance ID.
Definition resources.h:191
@ pcmk_rsc_match_current_node
If matching by node, compare current node instead of assigned node.
Definition resources.h:194
@ pcmk_rsc_unassigned
Definition resources.h:109
@ pcmk_rsc_unique
Definition resources.h:100
@ pcmk_rsc_updating_nodes
Definition resources.h:115
@ pcmk_rsc_managed
Definition resources.h:88
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_already
Definition results.h:153
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
rsc_role_e
Definition roles.h:34
@ pcmk_role_started
Started.
Definition roles.h:37
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
@ pcmk_role_unpromoted
Unpromoted.
Definition roles.h:38
@ pcmk_role_promoted
Promoted.
Definition roles.h:39
@ pcmk_role_stopped
Stopped.
Definition roles.h:36
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
@ pcmk_no_quorum_freeze
Definition scheduler.h:41
@ pcmk_sched_quorate
Definition scheduler.h:80
@ pcmk_sched_output_scores
Definition scheduler.h:173
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__rsc_debug(rsc, fmt, args...)
#define pcmk__sched_err(fmt...)
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition scores.c:86
int char2score(const char *score)
Get the integer value of a score string.
Definition scores.c:36
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:24
int pcmk__add_scores(int score1, int score2)
Definition scores.c:116
pcmk_node_t * pe_find_node_id(const GList *node_list, const char *id)
Find a node by ID in a list of nodes.
Definition status.c:487
@ pcmk__str_none
@ pcmk__str_casei
const char * node_attribute
pcmk_resource_t * primary
pcmk_resource_t * dependent
Location constraint object.
enum rsc_role_e role_filter
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
int weight
Definition nodes.h:162
int count
Definition nodes.h:164
struct pe_node_shared_s * details
Definition nodes.h:167
const char * id
Definition nodes.h:72
pcmk_assignment_methods_t * cmds
Definition resources.h:413
GList * running_on
Definition resources.h:456
GList * actions
Definition resources.h:444
GList * rsc_location
Definition resources.h:443
GHashTable * meta
Definition resources.h:467
GList * children
Definition resources.h:471
pcmk_scheduler_t * cluster
Definition resources.h:408
pcmk_rsc_methods_t * fns
Definition resources.h:412
GHashTable * known_on
Definition resources.h:459
char * clone_name
Definition resources.h:397
GHashTable * allowed_nodes
Definition resources.h:462
unsigned long long flags
Definition resources.h:428
enum rsc_role_e next_role
Definition resources.h:465
enum rsc_role_e role
Definition resources.h:464
unsigned long long flags
Definition scheduler.h:211
enum pe_quorum_policy no_quorum_policy
Definition scheduler.h:217
void(* create_actions)(pcmk_resource_t *rsc)
void(* apply_coloc_score)(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent)
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)
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, gboolean current)
Definition resources.h:316
pcmk_resource_t *(* find_rsc)(pcmk_resource_t *rsc, const char *search, const pcmk_node_t *node, int flags)
Definition resources.h:276
pcmk_node_t *(* location)(const pcmk_resource_t *rsc, GList **list, int current)
Definition resources.h:328
Wrappers for and extensions to libxml2.