pacemaker 2.1.5-a3f44794f94
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
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(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
119static void
120apply_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
153static pe_node_t *
154node_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
238static gint
239cmp_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
296static void
297add_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
333static void
334apply_coloc_to_dependent(gpointer data, gpointer user_data)
335{
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
363static void
364apply_coloc_to_primary(gpointer data, gpointer user_data)
365{
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
393static void
394set_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
433static void
434sort_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
480static pe_resource_t *
481find_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 */
508static bool
509anonymous_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
538static bool
539is_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
556static bool
557promotion_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
606check_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
630static const char *
631promotion_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
655static int
656promotion_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
731void
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
784static void
785set_current_role_unpromoted(void *data, void *user_data)
786{
788
789 if (rsc->role == RSC_ROLE_STARTED) {
790 // Promotable clones should use unpromoted role instead of started
792 }
793 g_list_foreach(rsc->children, set_current_role_unpromoted, NULL);
794}
795
803static void
804set_next_role_unpromoted(void *data, void *user_data)
805{
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
826static void
827set_next_role_promoted(void *data, gpointer user_data)
828{
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
843static void
844show_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,
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)),
861 pcmk_readable_score(instance->priority));
862 }
863}
864
872static void
873set_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
923 case RSC_ROLE_STOPPED:
924 // Instance can't be promoted
925 instance->priority = -INFINITY;
926 break;
927
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
963static void
964set_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
1011void
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
1043static void
1044create_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
1065static void
1066reset_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
1081void
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
1103void
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
1132static void
1133update_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
1178void
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
1231void
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}
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:113
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:454
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
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:912
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 void pcmk__add_colocated_node_scores(pe_resource_t *rsc, const char *log_id, GHashTable **nodes, const char *attr, float factor, uint32_t flags)
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:360
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
#define crm_notice(fmt, args...)
Definition: logging.h:361
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_err(fmt, args...)
Definition: logging.h:359
#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.
pe_resource_t * find_compatible_child(const pe_resource_t *local_child, const pe_resource_t *rsc, enum rsc_role_e filter, gboolean current)
pe_resource_t rsc2
pe_resource_t rsc1
@ no_quorum_freeze
Definition: pe_types.h:64
#define pe_flag_have_quorum
Definition: pe_types.h:95
#define pe_rsc_managed
Definition: pe_types.h:257
@ pe_order_implies_first_printed
Definition: pe_types.h:508
@ pe_order_implies_then_printed
Definition: pe_types.h:509
@ pe_order_optional
Definition: pe_types.h:481
#define pe_rsc_unique
Definition: pe_types.h:262
#define pe_flag_show_scores
Definition: pe_types.h:134
@ pe_action_optional
Definition: pe_types.h:301
@ pe_find_clone
match only clone instances
Definition: pe_types.h:87
@ pe_find_current
match resource active on specified node
Definition: pe_types.h:88
#define pe_rsc_merging
Definition: pe_types.h:268
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition: internal.h:394
void node_list_exclude(GHashTable *list, GList *list2, gboolean merge_scores)
Definition: utils.c:108
const char * pe_node_attribute_raw(const pe_node_t *node, const char *name)
Definition: common.c:562
void pe__create_promotable_pseudo_ops(pe_resource_t *clone, bool any_promoting, bool any_demoting)
Definition: clone.c:1258
void pe__set_next_role(pe_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1120
bool pe__clone_is_ordered(pe_resource_t *clone)
Definition: clone.c:1216
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:80
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:46
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:47
@ pe__clone_promotion_added
Definition: internal.h:26
@ pe__clone_promotion_constrained
Definition: internal.h:29
int pe__clone_promoted_max(pe_resource_t *clone)
Definition: clone.c:42
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:74
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:45
int pe__set_clone_flag(pe_resource_t *clone, enum pe__clone_flags flag)
Definition: clone.c:1235
const char * pe_node_attribute_calculated(const pe_node_t *node, const char *name, const pe_resource_t *rsc)
Definition: common.c:522
char * clone_strip(const char *last_rsc_id)
Definition: unpack.c:1647
int pe__clone_promoted_node_max(pe_resource_t *clone)
Definition: clone.c:59
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_already
Definition: results.h:140
pe_node_t * pe_find_node_id(GList *node_list, const char *id)
Definition: status.c:427
@ 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:189
int weight
Definition: pe_types.h:249
int count
Definition: pe_types.h:251
struct pe_node_shared_s * details
Definition: pe_types.h:252
const char * id
Definition: pe_types.h:215
GList * running_on
Definition: pe_types.h:373
GList * actions
Definition: pe_types.h:366
GList * rsc_location
Definition: pe_types.h:365
GList * rsc_cons
Definition: pe_types.h:364
GList * rsc_cons_lhs
Definition: pe_types.h:363
GList * children
Definition: pe_types.h:384
GHashTable * known_on
Definition: pe_types.h:374
pe_working_set_t * cluster
Definition: pe_types.h:335
char * clone_name
Definition: pe_types.h:330
char * id
Definition: pe_types.h:329
GHashTable * allowed_nodes
Definition: pe_types.h:375
int sort_index
Definition: pe_types.h:349
unsigned long long flags
Definition: pe_types.h:355
resource_alloc_functions_t * cmds
Definition: pe_types.h:341
enum rsc_role_e next_role
Definition: pe_types.h:378
enum rsc_role_e role
Definition: pe_types.h:377
resource_object_functions_t * fns
Definition: pe_types.h:340
unsigned long long flags
Definition: pe_types.h:153
enum pe_quorum_policy no_quorum_policy
Definition: pe_types.h:156
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)
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