Line data Source code
1 : /*
2 : * Copyright 2004-2024 the Pacemaker project contributors
3 : *
4 : * The version control history for this file may have further details.
5 : *
6 : * This source code is licensed under the GNU General Public License version 2
7 : * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 : */
9 :
10 : #include <crm_internal.h>
11 :
12 : #include <crm/common/xml.h>
13 : #include <pacemaker-internal.h>
14 :
15 : #include "libpacemaker_private.h"
16 :
17 : /*!
18 : * \internal
19 : * \brief Add implicit promotion ordering for a promotable instance
20 : *
21 : * \param[in,out] clone Clone resource
22 : * \param[in,out] child Instance of \p clone being ordered
23 : * \param[in,out] last Previous instance ordered (NULL if \p child is first)
24 : */
25 : static void
26 0 : order_instance_promotion(pcmk_resource_t *clone, pcmk_resource_t *child,
27 : pcmk_resource_t *last)
28 : {
29 : // "Promote clone" -> promote instance -> "clone promoted"
30 0 : pcmk__order_resource_actions(clone, PCMK_ACTION_PROMOTE,
31 : child, PCMK_ACTION_PROMOTE,
32 : pcmk__ar_ordered);
33 0 : pcmk__order_resource_actions(child, PCMK_ACTION_PROMOTE,
34 : clone, PCMK_ACTION_PROMOTED,
35 : pcmk__ar_ordered);
36 :
37 : // If clone is ordered, order this instance relative to last
38 0 : if ((last != NULL) && pe__clone_is_ordered(clone)) {
39 0 : pcmk__order_resource_actions(last, PCMK_ACTION_PROMOTE,
40 : child, PCMK_ACTION_PROMOTE,
41 : pcmk__ar_ordered);
42 : }
43 0 : }
44 :
45 : /*!
46 : * \internal
47 : * \brief Add implicit demotion ordering for a promotable instance
48 : *
49 : * \param[in,out] clone Clone resource
50 : * \param[in,out] child Instance of \p clone being ordered
51 : * \param[in] last Previous instance ordered (NULL if \p child is first)
52 : */
53 : static void
54 0 : order_instance_demotion(pcmk_resource_t *clone, pcmk_resource_t *child,
55 : pcmk_resource_t *last)
56 : {
57 : // "Demote clone" -> demote instance -> "clone demoted"
58 0 : pcmk__order_resource_actions(clone, PCMK_ACTION_DEMOTE, child,
59 : PCMK_ACTION_DEMOTE,
60 : pcmk__ar_then_implies_first_graphed);
61 0 : pcmk__order_resource_actions(child, PCMK_ACTION_DEMOTE,
62 : clone, PCMK_ACTION_DEMOTED,
63 : pcmk__ar_first_implies_then_graphed);
64 :
65 : // If clone is ordered, order this instance relative to last
66 0 : if ((last != NULL) && pe__clone_is_ordered(clone)) {
67 0 : pcmk__order_resource_actions(child, PCMK_ACTION_DEMOTE, last,
68 : PCMK_ACTION_DEMOTE, pcmk__ar_ordered);
69 : }
70 0 : }
71 :
72 : /*!
73 : * \internal
74 : * \brief Check whether an instance will be promoted or demoted
75 : *
76 : * \param[in] rsc Instance to check
77 : * \param[out] demoting If \p rsc will be demoted, this will be set to true
78 : * \param[out] promoting If \p rsc will be promoted, this will be set to true
79 : */
80 : static void
81 0 : check_for_role_change(const pcmk_resource_t *rsc, bool *demoting,
82 : bool *promoting)
83 : {
84 0 : const GList *iter = NULL;
85 :
86 : // If this is a cloned group, check group members recursively
87 0 : if (rsc->children != NULL) {
88 0 : for (iter = rsc->children; iter != NULL; iter = iter->next) {
89 0 : check_for_role_change((const pcmk_resource_t *) iter->data,
90 : demoting, promoting);
91 : }
92 0 : return;
93 : }
94 :
95 0 : for (iter = rsc->actions; iter != NULL; iter = iter->next) {
96 0 : const pcmk_action_t *action = (const pcmk_action_t *) iter->data;
97 :
98 0 : if (*promoting && *demoting) {
99 0 : return;
100 :
101 0 : } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
102 0 : continue;
103 :
104 0 : } else if (pcmk__str_eq(PCMK_ACTION_DEMOTE, action->task,
105 : pcmk__str_none)) {
106 0 : *demoting = true;
107 :
108 0 : } else if (pcmk__str_eq(PCMK_ACTION_PROMOTE, action->task,
109 : pcmk__str_none)) {
110 0 : *promoting = true;
111 : }
112 : }
113 : }
114 :
115 : /*!
116 : * \internal
117 : * \brief Add promoted-role location constraint scores to an instance's priority
118 : *
119 : * Adjust a promotable clone instance's promotion priority by the scores of any
120 : * location constraints in a list that are both limited to the promoted role and
121 : * for the node where the instance will be placed.
122 : *
123 : * \param[in,out] child Promotable clone instance
124 : * \param[in] location_constraints List of location constraints to apply
125 : * \param[in] chosen Node where \p child will be placed
126 : */
127 : static void
128 0 : apply_promoted_locations(pcmk_resource_t *child,
129 : const GList *location_constraints,
130 : const pcmk_node_t *chosen)
131 : {
132 0 : for (const GList *iter = location_constraints; iter; iter = iter->next) {
133 0 : const pcmk__location_t *location = iter->data;
134 0 : const pcmk_node_t *constraint_node = NULL;
135 :
136 0 : if (location->role_filter == pcmk_role_promoted) {
137 0 : constraint_node = pe_find_node_id(location->nodes,
138 0 : chosen->details->id);
139 : }
140 0 : if (constraint_node != NULL) {
141 0 : int new_priority = pcmk__add_scores(child->priority,
142 0 : constraint_node->weight);
143 :
144 0 : 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),
149 : pcmk_readable_score(child->priority),
150 : pcmk_readable_score(constraint_node->weight),
151 : pcmk_readable_score(new_priority));
152 0 : child->priority = new_priority;
153 : }
154 : }
155 0 : }
156 :
157 : /*!
158 : * \internal
159 : * \brief Get the node that an instance will be promoted on
160 : *
161 : * \param[in] rsc Promotable clone instance to check
162 : *
163 : * \return Node that \p rsc will be promoted on, or NULL if none
164 : */
165 : static pcmk_node_t *
166 0 : node_to_be_promoted_on(const pcmk_resource_t *rsc)
167 : {
168 0 : pcmk_node_t *node = NULL;
169 0 : pcmk_node_t *local_node = NULL;
170 0 : const pcmk_resource_t *parent = NULL;
171 :
172 : // If this is a cloned group, bail if any group member can't be promoted
173 0 : for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
174 0 : pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
175 :
176 0 : if (node_to_be_promoted_on(child) == NULL) {
177 0 : pcmk__rsc_trace(rsc,
178 : "%s can't be promoted because member %s can't",
179 : rsc->id, child->id);
180 0 : return NULL;
181 : }
182 : }
183 :
184 0 : node = rsc->fns->location(rsc, NULL, FALSE);
185 0 : if (node == NULL) {
186 0 : pcmk__rsc_trace(rsc, "%s can't be promoted because it won't be active",
187 : rsc->id);
188 0 : return NULL;
189 :
190 0 : } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
191 0 : if (rsc->fns->state(rsc, TRUE) == pcmk_role_promoted) {
192 0 : crm_notice("Unmanaged instance %s will be left promoted on %s",
193 : rsc->id, pcmk__node_name(node));
194 : } else {
195 0 : pcmk__rsc_trace(rsc, "%s can't be promoted because it is unmanaged",
196 : rsc->id);
197 0 : return NULL;
198 : }
199 :
200 0 : } else if (rsc->priority < 0) {
201 0 : pcmk__rsc_trace(rsc,
202 : "%s can't be promoted because its promotion priority "
203 : "%d is negative",
204 : rsc->id, rsc->priority);
205 0 : return NULL;
206 :
207 0 : } else if (!pcmk__node_available(node, false, true)) {
208 0 : pcmk__rsc_trace(rsc,
209 : "%s can't be promoted because %s can't run resources",
210 : rsc->id, pcmk__node_name(node));
211 0 : return NULL;
212 : }
213 :
214 0 : parent = pe__const_top_resource(rsc, false);
215 0 : local_node = g_hash_table_lookup(parent->allowed_nodes, node->details->id);
216 :
217 0 : 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 : */
222 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
223 0 : 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 0 : return NULL;
228 :
229 0 : } else if ((local_node->count >= pe__clone_promoted_node_max(parent))
230 0 : && pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
231 0 : 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 0 : return NULL;
236 : }
237 :
238 0 : return local_node;
239 : }
240 :
241 : /*!
242 : * \internal
243 : * \brief Compare two promotable clone instances by promotion priority
244 : *
245 : * \param[in] a First instance to compare
246 : * \param[in] b Second instance to compare
247 : *
248 : * \return A negative number if \p a has higher promotion priority,
249 : * a positive number if \p b has higher promotion priority,
250 : * or 0 if promotion priorities are equal
251 : */
252 : static gint
253 0 : cmp_promotable_instance(gconstpointer a, gconstpointer b)
254 : {
255 0 : const pcmk_resource_t *rsc1 = (const pcmk_resource_t *) a;
256 0 : const pcmk_resource_t *rsc2 = (const pcmk_resource_t *) b;
257 :
258 0 : enum rsc_role_e role1 = pcmk_role_unknown;
259 0 : enum rsc_role_e role2 = pcmk_role_unknown;
260 :
261 0 : CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
262 :
263 : // Check sort index set by pcmk__set_instance_roles()
264 0 : if (rsc1->sort_index > rsc2->sort_index) {
265 0 : pcmk__rsc_trace(rsc1,
266 : "%s has higher promotion priority than %s "
267 : "(sort index %d > %d)",
268 : rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index);
269 0 : return -1;
270 0 : } else if (rsc1->sort_index < rsc2->sort_index) {
271 0 : pcmk__rsc_trace(rsc1,
272 : "%s has lower promotion priority than %s "
273 : "(sort index %d < %d)",
274 : rsc1->id, rsc2->id, rsc1->sort_index, rsc2->sort_index);
275 0 : return 1;
276 : }
277 :
278 : // If those are the same, prefer instance whose current role is higher
279 0 : role1 = rsc1->fns->state(rsc1, TRUE);
280 0 : role2 = rsc2->fns->state(rsc2, TRUE);
281 0 : if (role1 > role2) {
282 0 : pcmk__rsc_trace(rsc1,
283 : "%s has higher promotion priority than %s "
284 : "(higher current role)",
285 : rsc1->id, rsc2->id);
286 0 : return -1;
287 0 : } else if (role1 < role2) {
288 0 : pcmk__rsc_trace(rsc1,
289 : "%s has lower promotion priority than %s "
290 : "(lower current role)",
291 : rsc1->id, rsc2->id);
292 0 : return 1;
293 : }
294 :
295 : // Finally, do normal clone instance sorting
296 0 : return pcmk__cmp_instance(a, b);
297 : }
298 :
299 : /*!
300 : * \internal
301 : * \brief Add a promotable clone instance's sort index to its node's score
302 : *
303 : * Add a promotable clone instance's sort index (which sums its promotion
304 : * preferences and scores of relevant location constraints for the promoted
305 : * role) to the node score of the instance's assigned node.
306 : *
307 : * \param[in] data Promotable clone instance
308 : * \param[in,out] user_data Clone parent of \p data
309 : */
310 : static void
311 0 : add_sort_index_to_node_score(gpointer data, gpointer user_data)
312 : {
313 0 : const pcmk_resource_t *child = (const pcmk_resource_t *) data;
314 0 : pcmk_resource_t *clone = (pcmk_resource_t *) user_data;
315 :
316 0 : pcmk_node_t *node = NULL;
317 0 : const pcmk_node_t *chosen = NULL;
318 :
319 0 : if (child->sort_index < 0) {
320 0 : pcmk__rsc_trace(clone, "Not adding sort index of %s: negative",
321 : child->id);
322 0 : return;
323 : }
324 :
325 0 : chosen = child->fns->location(child, NULL, FALSE);
326 0 : if (chosen == NULL) {
327 0 : pcmk__rsc_trace(clone, "Not adding sort index of %s: inactive",
328 : child->id);
329 0 : return;
330 : }
331 :
332 0 : node = g_hash_table_lookup(clone->allowed_nodes, chosen->details->id);
333 0 : CRM_ASSERT(node != NULL);
334 :
335 0 : node->weight = pcmk__add_scores(child->sort_index, node->weight);
336 0 : 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 :
343 : /*!
344 : * \internal
345 : * \brief Apply colocation to dependent's node scores if for promoted role
346 : *
347 : * \param[in,out] data Colocation constraint to apply
348 : * \param[in,out] user_data Promotable clone that is constraint's dependent
349 : */
350 : static void
351 0 : apply_coloc_to_dependent(gpointer data, gpointer user_data)
352 : {
353 0 : pcmk__colocation_t *colocation = data;
354 0 : pcmk_resource_t *clone = user_data;
355 0 : pcmk_resource_t *primary = colocation->primary;
356 0 : uint32_t flags = pcmk__coloc_select_default;
357 0 : float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
358 :
359 0 : if (colocation->dependent_role != pcmk_role_promoted) {
360 0 : return;
361 : }
362 0 : if (colocation->score < PCMK_SCORE_INFINITY) {
363 0 : flags = pcmk__coloc_select_active;
364 : }
365 0 : 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 0 : primary->cmds->add_colocated_node_scores(primary, clone, clone->id,
370 : &clone->allowed_nodes, colocation,
371 : factor, flags);
372 : }
373 :
374 : /*!
375 : * \internal
376 : * \brief Apply colocation to primary's node scores if for promoted role
377 : *
378 : * \param[in,out] data Colocation constraint to apply
379 : * \param[in,out] user_data Promotable clone that is constraint's primary
380 : */
381 : static void
382 0 : apply_coloc_to_primary(gpointer data, gpointer user_data)
383 : {
384 0 : pcmk__colocation_t *colocation = data;
385 0 : pcmk_resource_t *clone = user_data;
386 0 : pcmk_resource_t *dependent = colocation->dependent;
387 0 : const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
388 0 : const uint32_t flags = pcmk__coloc_select_active
389 : |pcmk__coloc_select_nonnegative;
390 :
391 0 : if ((colocation->primary_role != pcmk_role_promoted)
392 0 : || !pcmk__colocation_has_influence(colocation, NULL)) {
393 0 : return;
394 : }
395 :
396 0 : 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 0 : dependent->cmds->add_colocated_node_scores(dependent, clone, clone->id,
401 : &clone->allowed_nodes,
402 : colocation, factor, flags);
403 : }
404 :
405 : /*!
406 : * \internal
407 : * \brief Set clone instance's sort index to its node's score
408 : *
409 : * \param[in,out] data Promotable clone instance
410 : * \param[in] user_data Parent clone of \p data
411 : */
412 : static void
413 0 : set_sort_index_to_node_score(gpointer data, gpointer user_data)
414 : {
415 0 : pcmk_resource_t *child = (pcmk_resource_t *) data;
416 0 : const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
417 :
418 0 : pcmk_node_t *chosen = child->fns->location(child, NULL, FALSE);
419 :
420 0 : if (!pcmk_is_set(child->flags, pcmk_rsc_managed)
421 0 : && (child->next_role == pcmk_role_promoted)) {
422 0 : child->sort_index = PCMK_SCORE_INFINITY;
423 0 : pcmk__rsc_trace(clone,
424 : "Final sort index for %s is INFINITY "
425 : "(unmanaged promoted)",
426 : child->id);
427 :
428 0 : } else if ((chosen == NULL) || (child->sort_index < 0)) {
429 0 : pcmk__rsc_trace(clone,
430 : "Final sort index for %s is %d (ignoring node score)",
431 : child->id, child->sort_index);
432 :
433 : } else {
434 0 : const pcmk_node_t *node = g_hash_table_lookup(clone->allowed_nodes,
435 0 : chosen->details->id);
436 :
437 0 : CRM_ASSERT(node != NULL);
438 0 : child->sort_index = node->weight;
439 0 : pcmk__rsc_trace(clone,
440 : "Adding scores for %s: final sort index for %s is %d",
441 : clone->id, child->id, child->sort_index);
442 : }
443 0 : }
444 :
445 : /*!
446 : * \internal
447 : * \brief Sort a promotable clone's instances by descending promotion priority
448 : *
449 : * \param[in,out] clone Promotable clone to sort
450 : */
451 : static void
452 0 : sort_promotable_instances(pcmk_resource_t *clone)
453 : {
454 0 : GList *colocations = NULL;
455 :
456 0 : if (pe__set_clone_flag(clone, pcmk__clone_promotion_constrained)
457 : == pcmk_rc_already) {
458 0 : return;
459 : }
460 0 : pcmk__set_rsc_flags(clone, pcmk_rsc_updating_nodes);
461 :
462 0 : for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
463 0 : pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
464 :
465 0 : pcmk__rsc_trace(clone,
466 : "Adding scores for %s: initial sort index for %s is %d",
467 : clone->id, child->id, child->sort_index);
468 : }
469 0 : pe__show_node_scores(true, clone, "Before", clone->allowed_nodes,
470 : clone->cluster);
471 :
472 0 : g_list_foreach(clone->children, add_sort_index_to_node_score, clone);
473 :
474 0 : colocations = pcmk__this_with_colocations(clone);
475 0 : g_list_foreach(colocations, apply_coloc_to_dependent, clone);
476 0 : g_list_free(colocations);
477 :
478 0 : colocations = pcmk__with_this_colocations(clone);
479 0 : g_list_foreach(colocations, apply_coloc_to_primary, clone);
480 0 : g_list_free(colocations);
481 :
482 : // Ban resource from all nodes if it needs a ticket but doesn't have it
483 0 : pcmk__require_promotion_tickets(clone);
484 :
485 0 : pe__show_node_scores(true, clone, "After", clone->allowed_nodes,
486 : clone->cluster);
487 :
488 : // Reset sort indexes to final node scores
489 0 : g_list_foreach(clone->children, set_sort_index_to_node_score, clone);
490 :
491 : // Finally, sort instances in descending order of promotion priority
492 0 : clone->children = g_list_sort(clone->children, cmp_promotable_instance);
493 0 : pcmk__clear_rsc_flags(clone, pcmk_rsc_updating_nodes);
494 : }
495 :
496 : /*!
497 : * \internal
498 : * \brief Find the active instance (if any) of an anonymous clone on a node
499 : *
500 : * \param[in] clone Anonymous clone to check
501 : * \param[in] id Instance ID (without instance number) to check
502 : * \param[in] node Node to check
503 : *
504 : * \return
505 : */
506 : static pcmk_resource_t *
507 0 : find_active_anon_instance(const pcmk_resource_t *clone, const char *id,
508 : const pcmk_node_t *node)
509 : {
510 0 : for (GList *iter = clone->children; iter; iter = iter->next) {
511 0 : pcmk_resource_t *child = iter->data;
512 0 : pcmk_resource_t *active = NULL;
513 :
514 : // Use ->find_rsc() in case this is a cloned group
515 0 : active = clone->fns->find_rsc(child, id, node,
516 : pcmk_rsc_match_clone_only
517 : |pcmk_rsc_match_current_node);
518 0 : if (active != NULL) {
519 0 : return active;
520 : }
521 : }
522 0 : return NULL;
523 : }
524 :
525 : /*
526 : * \brief Check whether an anonymous clone instance is known on a node
527 : *
528 : * \param[in] clone Anonymous clone to check
529 : * \param[in] id Instance ID (without instance number) to check
530 : * \param[in] node Node to check
531 : *
532 : * \return true if \p id instance of \p clone is known on \p node,
533 : * otherwise false
534 : */
535 : static bool
536 0 : anonymous_known_on(const pcmk_resource_t *clone, const char *id,
537 : const pcmk_node_t *node)
538 : {
539 0 : for (GList *iter = clone->children; iter; iter = iter->next) {
540 0 : pcmk_resource_t *child = iter->data;
541 :
542 : /* Use ->find_rsc() because this might be a cloned group, and knowing
543 : * that other members of the group are known here implies nothing.
544 : */
545 0 : child = clone->fns->find_rsc(child, id, NULL,
546 : pcmk_rsc_match_clone_only);
547 0 : CRM_LOG_ASSERT(child != NULL);
548 0 : if (child != NULL) {
549 0 : if (g_hash_table_lookup(child->known_on, node->details->id)) {
550 0 : return true;
551 : }
552 : }
553 : }
554 0 : return false;
555 : }
556 :
557 : /*!
558 : * \internal
559 : * \brief Check whether a node is allowed to run a resource
560 : *
561 : * \param[in] rsc Resource to check
562 : * \param[in] node Node to check
563 : *
564 : * \return true if \p node is allowed to run \p rsc, otherwise false
565 : */
566 : static bool
567 0 : is_allowed(const pcmk_resource_t *rsc, const pcmk_node_t *node)
568 : {
569 0 : pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes,
570 0 : node->details->id);
571 :
572 0 : return (allowed != NULL) && (allowed->weight >= 0);
573 : }
574 :
575 : /*!
576 : * \brief Check whether a clone instance's promotion score should be considered
577 : *
578 : * \param[in] rsc Promotable clone instance to check
579 : * \param[in] node Node where score would be applied
580 : *
581 : * \return true if \p rsc's promotion score should be considered on \p node,
582 : * otherwise false
583 : */
584 : static bool
585 0 : promotion_score_applies(const pcmk_resource_t *rsc, const pcmk_node_t *node)
586 : {
587 0 : char *id = clone_strip(rsc->id);
588 0 : const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
589 0 : pcmk_resource_t *active = NULL;
590 0 : const char *reason = "allowed";
591 :
592 : // Some checks apply only to anonymous clone instances
593 0 : if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
594 :
595 : // If instance is active on the node, its score definitely applies
596 0 : active = find_active_anon_instance(parent, id, node);
597 0 : if (active == rsc) {
598 0 : reason = "active";
599 0 : goto check_allowed;
600 : }
601 :
602 : /* If *no* instance is active on this node, this instance's score will
603 : * count if it has been probed on this node.
604 : */
605 0 : if ((active == NULL) && anonymous_known_on(parent, id, node)) {
606 0 : reason = "probed";
607 0 : goto check_allowed;
608 : }
609 : }
610 :
611 : /* If this clone's status is unknown on *all* nodes (e.g. cluster startup),
612 : * take all instances' scores into account, to make sure we use any
613 : * permanent promotion scores.
614 : */
615 0 : if ((rsc->running_on == NULL) && (g_hash_table_size(rsc->known_on) == 0)) {
616 0 : reason = "none probed";
617 0 : goto check_allowed;
618 : }
619 :
620 : /* Otherwise, we've probed and/or started the resource *somewhere*, so
621 : * consider promotion scores on nodes where we know the status.
622 : */
623 0 : if ((g_hash_table_lookup(rsc->known_on, node->details->id) != NULL)
624 0 : || (pe_find_node_id(rsc->running_on, node->details->id) != NULL)) {
625 0 : reason = "known";
626 : } else {
627 0 : pcmk__rsc_trace(rsc,
628 : "Ignoring %s promotion score (for %s) on %s: "
629 : "not probed",
630 : rsc->id, id, pcmk__node_name(node));
631 0 : free(id);
632 0 : return false;
633 : }
634 :
635 0 : check_allowed:
636 0 : if (is_allowed(rsc, node)) {
637 0 : pcmk__rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s",
638 : rsc->id, id, pcmk__node_name(node), reason);
639 0 : free(id);
640 0 : return true;
641 : }
642 :
643 0 : pcmk__rsc_trace(rsc,
644 : "Ignoring %s promotion score (for %s) on %s: not allowed",
645 : rsc->id, id, pcmk__node_name(node));
646 0 : free(id);
647 0 : return false;
648 : }
649 :
650 : /*!
651 : * \internal
652 : * \brief Get the value of a promotion score node attribute
653 : *
654 : * \param[in] rsc Promotable clone instance to get promotion score for
655 : * \param[in] node Node to get promotion score for
656 : * \param[in] name Resource name to use in promotion score attribute name
657 : *
658 : * \return Value of promotion score node attribute for \p rsc on \p node
659 : */
660 : static const char *
661 0 : promotion_attr_value(const pcmk_resource_t *rsc, const pcmk_node_t *node,
662 : const char *name)
663 : {
664 0 : char *attr_name = NULL;
665 0 : const char *attr_value = NULL;
666 0 : const char *target = NULL;
667 0 : enum pcmk__rsc_node node_type = pcmk__rsc_node_assigned;
668 :
669 0 : if (pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
670 : // Not assigned yet
671 0 : node_type = pcmk__rsc_node_current;
672 : }
673 0 : target = g_hash_table_lookup(rsc->meta,
674 : PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
675 0 : attr_name = pcmk_promotion_score_name(name);
676 0 : attr_value = pcmk__node_attr(node, attr_name, target, node_type);
677 0 : free(attr_name);
678 0 : return attr_value;
679 : }
680 :
681 : /*!
682 : * \internal
683 : * \brief Get the promotion score for a clone instance on a node
684 : *
685 : * \param[in] rsc Promotable clone instance to get score for
686 : * \param[in] node Node to get score for
687 : * \param[out] is_default If non-NULL, will be set true if no score available
688 : *
689 : * \return Promotion score for \p rsc on \p node (or 0 if none)
690 : */
691 : static int
692 0 : promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node,
693 : bool *is_default)
694 : {
695 0 : char *name = NULL;
696 0 : const char *attr_value = NULL;
697 :
698 0 : if (is_default != NULL) {
699 0 : *is_default = true;
700 : }
701 :
702 0 : CRM_CHECK((rsc != NULL) && (node != NULL), return 0);
703 :
704 : /* If this is an instance of a cloned group, the promotion score is the sum
705 : * of all members' promotion scores.
706 : */
707 0 : if (rsc->children != NULL) {
708 0 : int score = 0;
709 :
710 0 : for (const GList *iter = rsc->children;
711 0 : iter != NULL; iter = iter->next) {
712 :
713 0 : const pcmk_resource_t *child = (const pcmk_resource_t *) iter->data;
714 0 : bool child_default = false;
715 0 : int child_score = promotion_score(child, node, &child_default);
716 :
717 0 : if (!child_default && (is_default != NULL)) {
718 0 : *is_default = false;
719 : }
720 0 : score += child_score;
721 : }
722 0 : return score;
723 : }
724 :
725 0 : if (!promotion_score_applies(rsc, node)) {
726 0 : return 0;
727 : }
728 :
729 : /* For the promotion score attribute name, use the name the resource is
730 : * known as in resource history, since that's what crm_attribute --promotion
731 : * would have used.
732 : */
733 0 : name = (rsc->clone_name == NULL)? rsc->id : rsc->clone_name;
734 :
735 0 : attr_value = promotion_attr_value(rsc, node, name);
736 0 : if (attr_value != NULL) {
737 0 : pcmk__rsc_trace(rsc, "Promotion score for %s on %s = %s",
738 : name, pcmk__node_name(node),
739 : pcmk__s(attr_value, "(unset)"));
740 0 : } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
741 : /* If we don't have any resource history yet, we won't have clone_name.
742 : * In that case, for anonymous clones, try the resource name without
743 : * any instance number.
744 : */
745 0 : name = clone_strip(rsc->id);
746 0 : if (strcmp(rsc->id, name) != 0) {
747 0 : attr_value = promotion_attr_value(rsc, node, name);
748 0 : pcmk__rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s",
749 : name, pcmk__node_name(node), rsc->id,
750 : pcmk__s(attr_value, "(unset)"));
751 : }
752 0 : free(name);
753 : }
754 :
755 0 : if (attr_value == NULL) {
756 0 : return 0;
757 : }
758 :
759 0 : if (is_default != NULL) {
760 0 : *is_default = false;
761 : }
762 0 : return char2score(attr_value);
763 : }
764 :
765 : /*!
766 : * \internal
767 : * \brief Include promotion scores in instances' node scores and priorities
768 : *
769 : * \param[in,out] rsc Promotable clone resource to update
770 : */
771 : void
772 0 : pcmk__add_promotion_scores(pcmk_resource_t *rsc)
773 : {
774 0 : if (pe__set_clone_flag(rsc,
775 : pcmk__clone_promotion_added) == pcmk_rc_already) {
776 0 : return;
777 : }
778 :
779 0 : for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
780 0 : pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
781 :
782 : GHashTableIter iter;
783 0 : pcmk_node_t *node = NULL;
784 : int score, new_score;
785 :
786 0 : g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
787 0 : while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
788 0 : if (!pcmk__node_available(node, false, false)) {
789 : /* This node will never be promoted, so don't apply the
790 : * promotion score, as that may lead to clone shuffling.
791 : */
792 0 : continue;
793 : }
794 :
795 0 : score = promotion_score(child_rsc, node, NULL);
796 0 : if (score > 0) {
797 0 : new_score = pcmk__add_scores(node->weight, score);
798 0 : if (new_score != node->weight) { // Could remain INFINITY
799 0 : node->weight = new_score;
800 0 : pcmk__rsc_trace(rsc,
801 : "Added %s promotion priority (%s) to score "
802 : "on %s (now %s)",
803 : child_rsc->id, pcmk_readable_score(score),
804 : pcmk__node_name(node),
805 : pcmk_readable_score(new_score));
806 : }
807 : }
808 :
809 0 : if (score > child_rsc->priority) {
810 0 : pcmk__rsc_trace(rsc,
811 : "Updating %s priority to promotion score "
812 : "(%d->%d)",
813 : child_rsc->id, child_rsc->priority, score);
814 0 : child_rsc->priority = score;
815 : }
816 : }
817 : }
818 : }
819 :
820 : /*!
821 : * \internal
822 : * \brief If a resource's current role is started, change it to unpromoted
823 : *
824 : * \param[in,out] data Resource to update
825 : * \param[in] user_data Ignored
826 : */
827 : static void
828 0 : set_current_role_unpromoted(void *data, void *user_data)
829 : {
830 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) data;
831 :
832 0 : if (rsc->role == pcmk_role_started) {
833 : // Promotable clones should use unpromoted role instead of started
834 0 : rsc->role = pcmk_role_unpromoted;
835 : }
836 0 : g_list_foreach(rsc->children, set_current_role_unpromoted, NULL);
837 0 : }
838 :
839 : /*!
840 : * \internal
841 : * \brief Set a resource's next role to unpromoted (or stopped if unassigned)
842 : *
843 : * \param[in,out] data Resource to update
844 : * \param[in] user_data Ignored
845 : */
846 : static void
847 0 : set_next_role_unpromoted(void *data, void *user_data)
848 : {
849 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) data;
850 0 : GList *assigned = NULL;
851 :
852 0 : rsc->fns->location(rsc, &assigned, FALSE);
853 0 : if (assigned == NULL) {
854 0 : pe__set_next_role(rsc, pcmk_role_stopped, "stopped instance");
855 : } else {
856 0 : pe__set_next_role(rsc, pcmk_role_unpromoted, "unpromoted instance");
857 0 : g_list_free(assigned);
858 : }
859 0 : g_list_foreach(rsc->children, set_next_role_unpromoted, NULL);
860 0 : }
861 :
862 : /*!
863 : * \internal
864 : * \brief Set a resource's next role to promoted if not already set
865 : *
866 : * \param[in,out] data Resource to update
867 : * \param[in] user_data Ignored
868 : */
869 : static void
870 0 : set_next_role_promoted(void *data, gpointer user_data)
871 : {
872 0 : pcmk_resource_t *rsc = (pcmk_resource_t *) data;
873 :
874 0 : if (rsc->next_role == pcmk_role_unknown) {
875 0 : pe__set_next_role(rsc, pcmk_role_promoted, "promoted instance");
876 : }
877 0 : g_list_foreach(rsc->children, set_next_role_promoted, NULL);
878 0 : }
879 :
880 : /*!
881 : * \internal
882 : * \brief Show instance's promotion score on node where it will be active
883 : *
884 : * \param[in,out] instance Promotable clone instance to show
885 : */
886 : static void
887 0 : show_promotion_score(pcmk_resource_t *instance)
888 : {
889 0 : pcmk_node_t *chosen = instance->fns->location(instance, NULL, FALSE);
890 :
891 0 : if (pcmk_is_set(instance->cluster->flags, pcmk_sched_output_scores)
892 0 : && !pcmk__is_daemon && (instance->cluster->priv != NULL)) {
893 :
894 0 : pcmk__output_t *out = instance->cluster->priv;
895 :
896 0 : out->message(out, "promotion-score", instance, chosen,
897 : pcmk_readable_score(instance->sort_index));
898 : } else {
899 0 : pcmk__rsc_debug(pe__const_top_resource(instance, false),
900 : "%s promotion score on %s: sort=%s priority=%s",
901 : instance->id,
902 : ((chosen == NULL)? "none" : pcmk__node_name(chosen)),
903 : pcmk_readable_score(instance->sort_index),
904 : pcmk_readable_score(instance->priority));
905 : }
906 0 : }
907 :
908 : /*!
909 : * \internal
910 : * \brief Set a clone instance's promotion priority
911 : *
912 : * \param[in,out] data Promotable clone instance to update
913 : * \param[in] user_data Instance's parent clone
914 : */
915 : static void
916 0 : set_instance_priority(gpointer data, gpointer user_data)
917 : {
918 0 : pcmk_resource_t *instance = (pcmk_resource_t *) data;
919 0 : const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
920 0 : const pcmk_node_t *chosen = NULL;
921 0 : enum rsc_role_e next_role = pcmk_role_unknown;
922 0 : GList *list = NULL;
923 :
924 0 : pcmk__rsc_trace(clone, "Assigning priority for %s: %s", instance->id,
925 : pcmk_role_text(instance->next_role));
926 :
927 0 : if (instance->fns->state(instance, TRUE) == pcmk_role_started) {
928 0 : set_current_role_unpromoted(instance, NULL);
929 : }
930 :
931 : // Only an instance that will be active can be promoted
932 0 : chosen = instance->fns->location(instance, &list, FALSE);
933 0 : if (pcmk__list_of_multiple(list)) {
934 0 : pcmk__config_err("Cannot promote non-colocated child %s",
935 : instance->id);
936 : }
937 0 : g_list_free(list);
938 0 : if (chosen == NULL) {
939 0 : return;
940 : }
941 :
942 0 : next_role = instance->fns->state(instance, FALSE);
943 0 : switch (next_role) {
944 0 : case pcmk_role_started:
945 : case pcmk_role_unknown:
946 : // Set instance priority to its promotion score (or -1 if none)
947 : {
948 0 : bool is_default = false;
949 :
950 0 : instance->priority = promotion_score(instance, chosen,
951 : &is_default);
952 0 : if (is_default) {
953 : /* Default to -1 if no value is set. This allows instances
954 : * eligible for promotion to be specified based solely on
955 : * PCMK_XE_RSC_LOCATION constraints, but prevents any
956 : * instance from being promoted if neither a constraint nor
957 : * a promotion score is present.
958 : */
959 0 : instance->priority = -1;
960 : }
961 : }
962 0 : break;
963 :
964 0 : case pcmk_role_unpromoted:
965 : case pcmk_role_stopped:
966 : // Instance can't be promoted
967 0 : instance->priority = -PCMK_SCORE_INFINITY;
968 0 : break;
969 :
970 0 : case pcmk_role_promoted:
971 : // Nothing needed (re-creating actions after scheduling fencing)
972 0 : break;
973 :
974 0 : default:
975 0 : CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s",
976 : next_role, instance->id));
977 : }
978 :
979 : // Add relevant location constraint scores for promoted role
980 0 : apply_promoted_locations(instance, instance->rsc_location, chosen);
981 0 : apply_promoted_locations(instance, clone->rsc_location, chosen);
982 :
983 : // Consider instance's role-based colocations with other resources
984 0 : list = pcmk__this_with_colocations(instance);
985 0 : for (GList *iter = list; iter != NULL; iter = iter->next) {
986 0 : pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data;
987 :
988 0 : instance->cmds->apply_coloc_score(instance, cons->primary, cons, true);
989 : }
990 0 : g_list_free(list);
991 :
992 0 : instance->sort_index = instance->priority;
993 0 : if (next_role == pcmk_role_promoted) {
994 0 : instance->sort_index = PCMK_SCORE_INFINITY;
995 : }
996 0 : pcmk__rsc_trace(clone, "Assigning %s priority = %d",
997 : instance->id, instance->priority);
998 : }
999 :
1000 : /*!
1001 : * \internal
1002 : * \brief Set a promotable clone instance's role
1003 : *
1004 : * \param[in,out] data Promotable clone instance to update
1005 : * \param[in,out] user_data Pointer to count of instances chosen for promotion
1006 : */
1007 : static void
1008 0 : set_instance_role(gpointer data, gpointer user_data)
1009 : {
1010 0 : pcmk_resource_t *instance = (pcmk_resource_t *) data;
1011 0 : int *count = (int *) user_data;
1012 :
1013 0 : const pcmk_resource_t *clone = pe__const_top_resource(instance, false);
1014 0 : pcmk_node_t *chosen = NULL;
1015 :
1016 0 : show_promotion_score(instance);
1017 :
1018 0 : if (instance->sort_index < 0) {
1019 0 : pcmk__rsc_trace(clone, "Not supposed to promote instance %s",
1020 : instance->id);
1021 :
1022 0 : } else if ((*count < pe__clone_promoted_max(instance))
1023 0 : || !pcmk_is_set(clone->flags, pcmk_rsc_managed)) {
1024 0 : chosen = node_to_be_promoted_on(instance);
1025 : }
1026 :
1027 0 : if (chosen == NULL) {
1028 0 : set_next_role_unpromoted(instance, NULL);
1029 0 : return;
1030 : }
1031 :
1032 0 : if ((instance->role < pcmk_role_promoted)
1033 0 : && !pcmk_is_set(instance->cluster->flags, pcmk_sched_quorate)
1034 0 : && (instance->cluster->no_quorum_policy == pcmk_no_quorum_freeze)) {
1035 0 : crm_notice("Clone instance %s cannot be promoted without quorum",
1036 : instance->id);
1037 0 : set_next_role_unpromoted(instance, NULL);
1038 0 : return;
1039 : }
1040 :
1041 0 : chosen->count++;
1042 0 : pcmk__rsc_info(clone, "Choosing %s (%s) on %s for promotion",
1043 : instance->id, pcmk_role_text(instance->role),
1044 : pcmk__node_name(chosen));
1045 0 : set_next_role_promoted(instance, NULL);
1046 0 : (*count)++;
1047 : }
1048 :
1049 : /*!
1050 : * \internal
1051 : * \brief Set roles for all instances of a promotable clone
1052 : *
1053 : * \param[in,out] rsc Promotable clone resource to update
1054 : */
1055 : void
1056 0 : pcmk__set_instance_roles(pcmk_resource_t *rsc)
1057 : {
1058 0 : int promoted = 0;
1059 : GHashTableIter iter;
1060 0 : pcmk_node_t *node = NULL;
1061 :
1062 : // Repurpose count to track the number of promoted instances assigned
1063 0 : g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1064 0 : while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1065 0 : node->count = 0;
1066 : }
1067 :
1068 : // Set instances' promotion priorities and sort by highest priority first
1069 0 : g_list_foreach(rsc->children, set_instance_priority, rsc);
1070 0 : sort_promotable_instances(rsc);
1071 :
1072 : // Choose the first N eligible instances to be promoted
1073 0 : g_list_foreach(rsc->children, set_instance_role, &promoted);
1074 0 : pcmk__rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
1075 : rsc->id, promoted, pe__clone_promoted_max(rsc));
1076 0 : }
1077 :
1078 : /*!
1079 : *
1080 : * \internal
1081 : * \brief Create actions for promotable clone instances
1082 : *
1083 : * \param[in,out] clone Promotable clone to create actions for
1084 : * \param[out] any_promoting Will be set true if any instance is promoting
1085 : * \param[out] any_demoting Will be set true if any instance is demoting
1086 : */
1087 : static void
1088 0 : create_promotable_instance_actions(pcmk_resource_t *clone,
1089 : bool *any_promoting, bool *any_demoting)
1090 : {
1091 0 : for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1092 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1093 :
1094 0 : instance->cmds->create_actions(instance);
1095 0 : check_for_role_change(instance, any_demoting, any_promoting);
1096 : }
1097 0 : }
1098 :
1099 : /*!
1100 : * \internal
1101 : * \brief Reset each promotable instance's resource priority
1102 : *
1103 : * Reset the priority of each instance of a promotable clone to the clone's
1104 : * priority (after promotion actions are scheduled, when instance priorities
1105 : * were repurposed as promotion scores).
1106 : *
1107 : * \param[in,out] clone Promotable clone to reset
1108 : */
1109 : static void
1110 0 : reset_instance_priorities(pcmk_resource_t *clone)
1111 : {
1112 0 : for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1113 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1114 :
1115 0 : instance->priority = clone->priority;
1116 : }
1117 0 : }
1118 :
1119 : /*!
1120 : * \internal
1121 : * \brief Create actions specific to promotable clones
1122 : *
1123 : * \param[in,out] clone Promotable clone to create actions for
1124 : */
1125 : void
1126 0 : pcmk__create_promotable_actions(pcmk_resource_t *clone)
1127 : {
1128 0 : bool any_promoting = false;
1129 0 : bool any_demoting = false;
1130 :
1131 : // Create actions for each clone instance individually
1132 0 : create_promotable_instance_actions(clone, &any_promoting, &any_demoting);
1133 :
1134 : // Create pseudo-actions for clone as a whole
1135 0 : pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting);
1136 :
1137 : // Undo our temporary repurposing of resource priority for instances
1138 0 : reset_instance_priorities(clone);
1139 0 : }
1140 :
1141 : /*!
1142 : * \internal
1143 : * \brief Create internal orderings for a promotable clone's instances
1144 : *
1145 : * \param[in,out] clone Promotable clone instance to order
1146 : */
1147 : void
1148 0 : pcmk__order_promotable_instances(pcmk_resource_t *clone)
1149 : {
1150 0 : pcmk_resource_t *previous = NULL; // Needed for ordered clones
1151 :
1152 0 : pcmk__promotable_restart_ordering(clone);
1153 :
1154 0 : for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
1155 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1156 :
1157 : // Demote before promote
1158 0 : pcmk__order_resource_actions(instance, PCMK_ACTION_DEMOTE,
1159 : instance, PCMK_ACTION_PROMOTE,
1160 : pcmk__ar_ordered);
1161 :
1162 0 : order_instance_promotion(clone, instance, previous);
1163 0 : order_instance_demotion(clone, instance, previous);
1164 0 : previous = instance;
1165 : }
1166 0 : }
1167 :
1168 : /*!
1169 : * \internal
1170 : * \brief Update dependent's allowed nodes for colocation with promotable
1171 : *
1172 : * \param[in,out] dependent Dependent resource to update
1173 : * \param[in] primary Primary resource
1174 : * \param[in] primary_node Node where an instance of the primary will be
1175 : * \param[in] colocation Colocation constraint to apply
1176 : */
1177 : static void
1178 0 : update_dependent_allowed_nodes(pcmk_resource_t *dependent,
1179 : const pcmk_resource_t *primary,
1180 : const pcmk_node_t *primary_node,
1181 : const pcmk__colocation_t *colocation)
1182 : {
1183 : GHashTableIter iter;
1184 0 : pcmk_node_t *node = NULL;
1185 0 : const char *primary_value = NULL;
1186 0 : const char *attr = colocation->node_attribute;
1187 :
1188 0 : if (colocation->score >= PCMK_SCORE_INFINITY) {
1189 0 : return; // Colocation is mandatory, so allowed node scores don't matter
1190 : }
1191 :
1192 0 : primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1193 :
1194 0 : pcmk__rsc_trace(colocation->primary,
1195 : "Applying %s (%s with %s on %s by %s @%d) to %s",
1196 : colocation->id, colocation->dependent->id,
1197 : colocation->primary->id, pcmk__node_name(primary_node),
1198 : attr, colocation->score, dependent->id);
1199 :
1200 0 : g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1201 0 : while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1202 0 : const char *dependent_value = pcmk__colocation_node_attr(node, attr,
1203 : dependent);
1204 :
1205 0 : if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) {
1206 0 : node->weight = pcmk__add_scores(node->weight, colocation->score);
1207 0 : pcmk__rsc_trace(colocation->primary,
1208 : "Added %s score (%s) to %s (now %s)",
1209 : colocation->id,
1210 : pcmk_readable_score(colocation->score),
1211 : pcmk__node_name(node),
1212 : pcmk_readable_score(node->weight));
1213 : }
1214 : }
1215 : }
1216 :
1217 : /*!
1218 : * \brief Update dependent for a colocation with a promotable clone
1219 : *
1220 : * \param[in] primary Primary resource in the colocation
1221 : * \param[in,out] dependent Dependent resource in the colocation
1222 : * \param[in] colocation Colocation constraint to apply
1223 : */
1224 : void
1225 0 : pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary,
1226 : pcmk_resource_t *dependent,
1227 : const pcmk__colocation_t *colocation)
1228 : {
1229 0 : GList *affected_nodes = NULL;
1230 :
1231 : /* Build a list of all nodes where an instance of the primary will be, and
1232 : * (for optional colocations) update the dependent's allowed node scores for
1233 : * each one.
1234 : */
1235 0 : for (GList *iter = primary->children; iter != NULL; iter = iter->next) {
1236 0 : pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1237 0 : pcmk_node_t *node = instance->fns->location(instance, NULL, FALSE);
1238 :
1239 0 : if (node == NULL) {
1240 0 : continue;
1241 : }
1242 0 : if (instance->fns->state(instance, FALSE) == colocation->primary_role) {
1243 0 : update_dependent_allowed_nodes(dependent, primary, node,
1244 : colocation);
1245 0 : affected_nodes = g_list_prepend(affected_nodes, node);
1246 : }
1247 : }
1248 :
1249 : /* For mandatory colocations, add the primary's node score to the
1250 : * dependent's node score for each affected node, and ban the dependent
1251 : * from all other nodes.
1252 : *
1253 : * However, skip this for promoted-with-promoted colocations, otherwise
1254 : * inactive dependent instances can't start (in the unpromoted role).
1255 : */
1256 0 : if ((colocation->score >= PCMK_SCORE_INFINITY)
1257 0 : && ((colocation->dependent_role != pcmk_role_promoted)
1258 0 : || (colocation->primary_role != pcmk_role_promoted))) {
1259 :
1260 0 : pcmk__rsc_trace(colocation->primary,
1261 : "Applying %s (mandatory %s with %s) to %s",
1262 : colocation->id, colocation->dependent->id,
1263 : colocation->primary->id, dependent->id);
1264 0 : pcmk__colocation_intersect_nodes(dependent, primary, colocation,
1265 : affected_nodes, true);
1266 : }
1267 0 : g_list_free(affected_nodes);
1268 0 : }
1269 :
1270 : /*!
1271 : * \internal
1272 : * \brief Update dependent priority for colocation with promotable
1273 : *
1274 : * \param[in] primary Primary resource in the colocation
1275 : * \param[in,out] dependent Dependent resource in the colocation
1276 : * \param[in] colocation Colocation constraint to apply
1277 : */
1278 : void
1279 0 : pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary,
1280 : pcmk_resource_t *dependent,
1281 : const pcmk__colocation_t *colocation)
1282 : {
1283 0 : pcmk_resource_t *primary_instance = NULL;
1284 :
1285 : // Look for a primary instance where dependent will be
1286 0 : primary_instance = pcmk__find_compatible_instance(dependent, primary,
1287 0 : colocation->primary_role,
1288 : false);
1289 :
1290 0 : if (primary_instance != NULL) {
1291 : // Add primary instance's priority to dependent's
1292 0 : int new_priority = pcmk__add_scores(dependent->priority,
1293 0 : colocation->score);
1294 :
1295 0 : pcmk__rsc_trace(colocation->primary,
1296 : "Applying %s (%s with %s) to %s priority "
1297 : "(%s + %s = %s)",
1298 : colocation->id, colocation->dependent->id,
1299 : colocation->primary->id, dependent->id,
1300 : pcmk_readable_score(dependent->priority),
1301 : pcmk_readable_score(colocation->score),
1302 : pcmk_readable_score(new_priority));
1303 0 : dependent->priority = new_priority;
1304 :
1305 0 : } else if (colocation->score >= PCMK_SCORE_INFINITY) {
1306 : // Mandatory colocation, but primary won't be here
1307 0 : pcmk__rsc_trace(colocation->primary,
1308 : "Applying %s (%s with %s) to %s: can't be promoted",
1309 : colocation->id, colocation->dependent->id,
1310 : colocation->primary->id, dependent->id);
1311 0 : dependent->priority = -PCMK_SCORE_INFINITY;
1312 : }
1313 0 : }
|