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