1 /*
2 * Copyright 2004-2026 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 <stdbool.h>
13 #include <glib.h>
14
15 #include <crm/crm.h>
16 #include <crm/common/scheduler.h>
17 #include <crm/common/util.h>
18 #include <crm/common/xml.h>
19 #include <crm/pengine/status.h>
20 #include <pacemaker-internal.h>
21
22 #include "libpacemaker_private.h"
23
24 // Used to temporarily mark a node as unusable
25 #define INFINITY_HACK (PCMK_SCORE_INFINITY * -100)
26
27 /*!
28 * \internal
29 * \brief Get the value of a colocation's node attribute
30 *
31 * \param[in] node Node on which to look up the attribute
32 * \param[in] attr Name of attribute to look up
33 * \param[in] rsc Resource on whose behalf to look up the attribute
34 *
35 * \return Value of \p attr on \p node or on the host of \p node, as appropriate
36 */
37 const char *
38 pcmk__colocation_node_attr(const pcmk_node_t *node, const char *attr,
39 const pcmk_resource_t *rsc)
40 {
41 const char *target = NULL;
42
43 /* A resource colocated with a bundle or its primitive can't run on the
44 * bundle node itself (where only the primitive, if any, can run). Instead,
45 * we treat it as a colocation with the bundle's containers, so always look
46 * up colocation node attributes on the container host.
47 */
48 if (pcmk__is_bundle_node(node) && pcmk__is_bundled(rsc)
49 && (pe__const_top_resource(rsc, false) == pe__bundled_resource(rsc))) {
50 target = PCMK_VALUE_HOST;
51
52 } else if (rsc != NULL) {
53 target = g_hash_table_lookup(rsc->priv->meta,
54 PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
55 }
56
57 return pcmk__node_attr(node, attr, target, pcmk__rsc_node_assigned);
58 }
59
60 /*!
61 * \internal
62 * \brief Compare two colocations according to priority
63 *
64 * Compare two colocations according to the order in which they should be
65 * considered, based on either their dependent resources or their primary
66 * resources -- preferring (in order):
67 * * Colocation that is not \c NULL
68 * * Colocation whose resource has higher priority
69 * * Colocation whose resource is of a higher-level variant
70 * (bundle > clone > group > primitive)
71 * * Colocation whose resource is promotable, if both are clones
72 * * Colocation whose resource has lower ID in lexicographic order
73 *
74 * \param[in] colocation1 First colocation to compare
75 * \param[in] colocation2 Second colocation to compare
76 * \param[in] dependent If \c true, compare colocations by dependent
77 * priority; otherwise compare them by primary priority
78 *
79 * \return A negative number if \p colocation1 should be considered first,
80 * a positive number if \p colocation2 should be considered first,
81 * or 0 if order doesn't matter
82 */
83 static gint
84 cmp_colocation_priority(const pcmk__colocation_t *colocation1,
85 const pcmk__colocation_t *colocation2, bool dependent)
86 {
87 const pcmk_resource_t *rsc1 = NULL;
88 const pcmk_resource_t *rsc2 = NULL;
89
90 if (colocation1 == NULL) {
91 return 1;
92 }
93 if (colocation2 == NULL) {
94 return -1;
95 }
96
97 if (dependent) {
98 rsc1 = colocation1->dependent;
99 rsc2 = colocation2->dependent;
100 pcmk__assert(colocation1->primary != NULL);
101 } else {
102 rsc1 = colocation1->primary;
103 rsc2 = colocation2->primary;
104 pcmk__assert(colocation1->dependent != NULL);
105 }
106 pcmk__assert((rsc1 != NULL) && (rsc2 != NULL));
107
108 if (rsc1->priv->priority > rsc2->priv->priority) {
109 return -1;
110 }
111 if (rsc1->priv->priority < rsc2->priv->priority) {
112 return 1;
113 }
114
115 // Process clones before primitives and groups
116 if (rsc1->priv->variant > rsc2->priv->variant) {
117 return -1;
118 }
119 if (rsc1->priv->variant < rsc2->priv->variant) {
120 return 1;
121 }
122
123 /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
124 * clones (probably unnecessary, but avoids having to update regression
125 * tests)
126 */
127 if (pcmk__is_clone(rsc1)) {
128 if (pcmk__is_set(rsc1->flags, pcmk__rsc_promotable)
129 && !pcmk__is_set(rsc2->flags, pcmk__rsc_promotable)) {
130 return -1;
131 }
132 if (!pcmk__is_set(rsc1->flags, pcmk__rsc_promotable)
133 && pcmk__is_set(rsc2->flags, pcmk__rsc_promotable)) {
134 return 1;
135 }
136 }
137
138 return strcmp(rsc1->id, rsc2->id);
139 }
140
141 /*!
142 * \internal
143 * \brief Compare two colocations according to priority based on dependents
144 *
145 * Compare two colocations according to the order in which they should be
146 * considered, based on their dependent resources -- preferring (in order):
147 * * Colocation that is not \c NULL
148 * * Colocation whose resource has higher priority
149 * * Colocation whose resource is of a higher-level variant
150 * (bundle > clone > group > primitive)
151 * * Colocation whose resource is promotable, if both are clones
152 * * Colocation whose resource has lower ID in lexicographic order
153 *
154 * \param[in] a First colocation to compare
155 * \param[in] b Second colocation to compare
156 *
157 * \return A negative number if \p a should be considered first,
158 * a positive number if \p b should be considered first,
159 * or 0 if order doesn't matter
160 */
161 static gint
162 cmp_dependent_priority(gconstpointer a, gconstpointer b)
163 {
164 return cmp_colocation_priority(a, b, true);
165 }
166
167 /*!
168 * \internal
169 * \brief Compare two colocations according to priority based on primaries
170 *
171 * Compare two colocations according to the order in which they should be
172 * considered, based on their primary resources -- preferring (in order):
173 * * Colocation that is not \c NULL
174 * * Colocation whose primary has higher priority
175 * * Colocation whose primary is of a higher-level variant
176 * (bundle > clone > group > primitive)
177 * * Colocation whose primary is promotable, if both are clones
178 * * Colocation whose primary has lower ID in lexicographic order
179 *
180 * \param[in] a First colocation to compare
181 * \param[in] b Second colocation to compare
182 *
183 * \return A negative number if \p a should be considered first,
184 * a positive number if \p b should be considered first,
185 * or 0 if order doesn't matter
186 */
187 static gint
188 cmp_primary_priority(gconstpointer a, gconstpointer b)
189 {
190 return cmp_colocation_priority(a, b, false);
191 }
192
193 /*!
194 * \internal
195 * \brief Add a "this with" colocation constraint to a sorted list
196 *
197 * \param[in,out] list List of constraints to add \p colocation to
198 * \param[in] colocation Colocation constraint to add to \p list
199 * \param[in] rsc Resource whose colocations we're getting (for
200 * logging only)
201 *
202 * \note The list will be sorted using cmp_primary_priority().
203 */
204 void
205 pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
206 const pcmk_resource_t *rsc)
207 {
208 pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
209
210 pcmk__rsc_trace(rsc,
211 "Adding colocation %s (%s with %s using %s @%s) to "
212 "'this with' list for %s",
213 colocation->id, colocation->dependent->id,
214 colocation->primary->id, colocation->node_attribute,
215 pcmk_readable_score(colocation->score), rsc->id);
216 *list = g_list_insert_sorted(*list, (gpointer) colocation,
217 cmp_primary_priority);
218 }
219
220 /*!
221 * \internal
222 * \brief Add a list of "this with" colocation constraints to a list
223 *
224 * \param[in,out] list List of constraints to add \p addition to
225 * \param[in] addition List of colocation constraints to add to \p list
226 * \param[in] rsc Resource whose colocations we're getting (for
227 * logging only)
228 *
229 * \note The lists must be pre-sorted by cmp_primary_priority().
230 */
231 void
232 pcmk__add_this_with_list(GList **list, GList *addition,
233 const pcmk_resource_t *rsc)
234 {
235 pcmk__assert((list != NULL) && (rsc != NULL));
236
237 pcmk__if_tracing(
238 {}, // Always add each colocation individually if tracing
239 {
240 if (*list == NULL) {
241 // Trivial case for efficiency if not tracing
242 *list = g_list_copy(addition);
243 return;
244 }
245 }
246 );
247
248 for (const GList *iter = addition; iter != NULL; iter = iter->next) {
249 pcmk__add_this_with(list, addition->data, rsc);
250 }
251 }
252
253 /*!
254 * \internal
255 * \brief Add a "with this" colocation constraint to a sorted list
256 *
257 * \param[in,out] list List of constraints to add \p colocation to
258 * \param[in] colocation Colocation constraint to add to \p list
259 * \param[in] rsc Resource whose colocations we're getting (for
260 * logging only)
261 *
262 * \note The list will be sorted using cmp_dependent_priority().
263 */
264 void
265 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
266 const pcmk_resource_t *rsc)
267 {
268 pcmk__assert((list != NULL) && (colocation != NULL) && (rsc != NULL));
269
270 pcmk__rsc_trace(rsc,
271 "Adding colocation %s (%s with %s using %s @%s) to "
272 "'with this' list for %s",
273 colocation->id, colocation->dependent->id,
274 colocation->primary->id, colocation->node_attribute,
275 pcmk_readable_score(colocation->score), rsc->id);
276 *list = g_list_insert_sorted(*list, (gpointer) colocation,
277 cmp_dependent_priority);
278 }
279
280 /*!
281 * \internal
282 * \brief Add a list of "with this" colocation constraints to a list
283 *
284 * \param[in,out] list List of constraints to add \p addition to
285 * \param[in] addition List of colocation constraints to add to \p list
286 * \param[in] rsc Resource whose colocations we're getting (for
287 * logging only)
288 *
289 * \note The lists must be pre-sorted by cmp_dependent_priority().
290 */
291 void
292 pcmk__add_with_this_list(GList **list, GList *addition,
293 const pcmk_resource_t *rsc)
294 {
295 pcmk__assert((list != NULL) && (rsc != NULL));
296
297 pcmk__if_tracing(
298 {}, // Always add each colocation individually if tracing
299 {
300 if (*list == NULL) {
301 // Trivial case for efficiency if not tracing
302 *list = g_list_copy(addition);
303 return;
304 }
305 }
306 );
307
308 for (const GList *iter = addition; iter != NULL; iter = iter->next) {
309 pcmk__add_with_this(list, addition->data, rsc);
310 }
311 }
312
313 /*!
314 * \internal
315 * \brief Add orderings necessary for an anti-colocation constraint
316 *
317 * \param[in,out] first_rsc One resource in an anti-colocation
318 * \param[in] first_role Anti-colocation role of \p first_rsc
319 * \param[in] then_rsc Other resource in the anti-colocation
320 * \param[in] then_role Anti-colocation role of \p then_rsc
321 */
322 static void
323 anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
324 pcmk_resource_t *then_rsc, int then_role)
325 {
326 const char *first_tasks[] = { NULL, NULL };
327 const char *then_tasks[] = { NULL, NULL };
328
329 /* Actions to make first_rsc lose first_role */
330 if (first_role == pcmk_role_promoted) {
331 first_tasks[0] = PCMK_ACTION_DEMOTE;
332
333 } else {
334 first_tasks[0] = PCMK_ACTION_STOP;
335
336 if (first_role == pcmk_role_unpromoted) {
337 first_tasks[1] = PCMK_ACTION_PROMOTE;
338 }
339 }
340
341 /* Actions to make then_rsc gain then_role */
342 if (then_role == pcmk_role_promoted) {
343 then_tasks[0] = PCMK_ACTION_PROMOTE;
344
345 } else {
346 then_tasks[0] = PCMK_ACTION_START;
347
348 if (then_role == pcmk_role_unpromoted) {
349 then_tasks[1] = PCMK_ACTION_DEMOTE;
350 }
351 }
352
353 for (int first_lpc = 0;
354 (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
355
356 for (int then_lpc = 0;
357 (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
358
359 pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
360 then_rsc, then_tasks[then_lpc],
361 pcmk__ar_if_required_on_same_node);
362 }
363 }
364 }
365
366 /*!
367 * \internal
368 * \brief Add a new colocation constraint to scheduler data
369 *
370 * \param[in] id XML ID for this constraint
371 * \param[in] node_attr Colocate by this attribute (NULL for #uname)
372 * \param[in] score Constraint score
373 * \param[in,out] dependent Resource to be colocated
374 * \param[in,out] primary Resource to colocate \p dependent with
375 * \param[in] dependent_role_spec If not NULL, only \p dependent instances
376 * with this role should be colocated
377 * \param[in] primary_role_spec If not NULL, only \p primary instances
378 * with this role should be colocated
379 * \param[in] flags Group of enum pcmk__coloc_flags
380 */
381 void
382 pcmk__new_colocation(const char *id, const char *node_attr, int score,
383 pcmk_resource_t *dependent, pcmk_resource_t *primary,
384 const char *dependent_role_spec,
385 const char *primary_role_spec, uint32_t flags)
386 {
387 pcmk__colocation_t *new_con = NULL;
388 enum rsc_role_e dependent_role = pcmk_role_unknown;
389 enum rsc_role_e primary_role = pcmk_role_unknown;
390
391 CRM_CHECK(id != NULL, return);
392
393 if ((dependent == NULL) || (primary == NULL)) {
394 pcmk__config_err("Ignoring colocation '%s' because resource "
395 "does not exist", id);
396 return;
397 }
398 if ((pcmk__parse_constraint_role(id, dependent_role_spec,
399 &dependent_role) != pcmk_rc_ok)
400 || (pcmk__parse_constraint_role(id, primary_role_spec,
401 &primary_role) != pcmk_rc_ok)) {
402 // Not possible with schema validation enabled (error already logged)
403 return;
404 }
405
406 if (score == 0) {
407 pcmk__rsc_trace(dependent,
408 "Ignoring colocation '%s' (%s with %s) because score is 0",
409 id, dependent->id, primary->id);
410 return;
411 }
412
413 new_con = pcmk__assert_alloc(1, sizeof(pcmk__colocation_t));
414 new_con->id = id;
415 new_con->dependent = dependent;
416 new_con->primary = primary;
417 new_con->score = score;
418 new_con->dependent_role = dependent_role;
419 new_con->primary_role = primary_role;
420
421 new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
422 new_con->flags = flags;
423
424 pcmk__add_this_with(&(dependent->priv->this_with_colocations), new_con,
425 dependent);
426 pcmk__add_with_this(&(primary->priv->with_this_colocations), new_con,
427 primary);
428
429 dependent->priv->scheduler->priv->colocation_constraints =
430 g_list_prepend(dependent->priv->scheduler->priv->colocation_constraints,
431 new_con);
432
433 if (score <= -PCMK_SCORE_INFINITY) {
434 anti_colocation_order(dependent, new_con->dependent_role, primary,
435 new_con->primary_role);
436 anti_colocation_order(primary, new_con->primary_role, dependent,
437 new_con->dependent_role);
438 }
439 }
440
441 /*!
442 * \internal
443 * \brief Return the boolean influence corresponding to configuration
444 *
445 * \param[in] coloc_id Colocation XML ID (for error logging)
446 * \param[in] rsc Resource involved in constraint (for default)
447 * \param[in] influence_s String value of \c PCMK_XA_INFLUENCE option
448 *
449 * \return \c pcmk__coloc_influence if string evaluates true, or string is
450 * \c NULL or invalid and resource's \c PCMK_META_CRITICAL option
451 * evaluates true, otherwise \c pcmk__coloc_none
452 */
453 static uint32_t
454 unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
455 const char *influence_s)
456 {
457 if (influence_s != NULL) {
458 bool influence = false;
459
460 if (pcmk__parse_bool(influence_s, &influence) == pcmk_rc_ok) {
461 return (influence? pcmk__coloc_influence : pcmk__coloc_none);
462 }
463 pcmk__config_err("Constraint '%s' has invalid value for "
464 PCMK_XA_INFLUENCE " (using default)",
465 coloc_id);
466 }
467 if (pcmk__is_set(rsc->flags, pcmk__rsc_critical)) {
468 return pcmk__coloc_influence;
469 }
470 return pcmk__coloc_none;
471 }
472
473 static void
474 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
475 const char *influence_s, pcmk_scheduler_t *scheduler)
476 {
477 xmlNode *xml_rsc = NULL;
478 pcmk_resource_t *other = NULL;
479 pcmk_resource_t *resource = NULL;
480 const char *set_id = pcmk__xe_id(set);
481 const char *role = pcmk__xe_get(set, PCMK_XA_ROLE);
482 bool with_previous = false;
483 int local_score = score;
484 bool sequential = false;
485 uint32_t flags = pcmk__coloc_none;
486 const char *xml_rsc_id = NULL;
487 const char *score_s = pcmk__xe_get(set, PCMK_XA_SCORE);
488
489 if (score_s != NULL) {
490 int rc = pcmk_parse_score(score_s, &local_score, 0);
491
492 if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
493 pcmk__config_err("Ignoring colocation '%s' for set '%s' "
494 "because '%s' is not a valid score",
495 coloc_id, set_id, score_s);
496 return;
497 }
498 }
499 if (local_score == 0) {
500 pcmk__trace("Ignoring colocation '%s' for set '%s' because score is 0",
501 coloc_id, set_id);
502 return;
503 }
504
505 /* @COMPAT The deprecated PCMK__XA_ORDERING attribute specifies whether
506 * resources in a positive-score set are colocated with the previous or next
507 * resource.
508 */
509 if (pcmk__str_eq(pcmk__xe_get(set, PCMK__XA_ORDERING), PCMK__VALUE_GROUP,
510 pcmk__str_null_matches|pcmk__str_casei)) {
511 with_previous = true;
512 } else {
513 pcmk__warn_once(pcmk__wo_set_ordering,
514 "Support for '" PCMK__XA_ORDERING "' other than"
515 " '" PCMK__VALUE_GROUP "' in " PCMK_XE_RESOURCE_SET
516 " (such as %s) is deprecated and will be removed in a"
517 " future release",
518 set_id);
519 }
520
521 if ((pcmk__xe_get_bool(set, PCMK_XA_SEQUENTIAL, &sequential) == pcmk_rc_ok)
522 && !sequential) {
523 return;
524 }
525
526 if (local_score > 0) {
527 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
528 NULL);
529 xml_rsc != NULL;
530 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
531
532 xml_rsc_id = pcmk__xe_id(xml_rsc);
533 resource =
534 pcmk__find_constraint_resource(scheduler->priv->resources,
535 xml_rsc_id);
536 if (resource == NULL) {
537 // Should be possible only with validation disabled
538 pcmk__config_err("Ignoring %s and later resources in set %s: "
539 "No such resource", xml_rsc_id, set_id);
540 return;
541 }
542 if (other != NULL) {
543 flags = pcmk__coloc_explicit
544 | unpack_influence(coloc_id, resource, influence_s);
545 if (with_previous) {
546 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
547 resource->id, other->id, set_id);
548 pcmk__new_colocation(set_id, NULL, local_score, resource,
549 other, role, role, flags);
550 } else {
551 pcmk__rsc_trace(resource, "Colocating %s with %s in set %s",
552 other->id, resource->id, set_id);
553 pcmk__new_colocation(set_id, NULL, local_score, other,
554 resource, role, role, flags);
555 }
556 }
557 other = resource;
558 }
559
560 } else {
561 /* Anti-colocating with every prior resource is
562 * the only way to ensure the intuitive result
563 * (i.e. that no one in the set can run with anyone else in the set)
564 */
565
566 for (xml_rsc = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF, NULL,
567 NULL);
568 xml_rsc != NULL;
569 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
570
571 xmlNode *xml_rsc_with = NULL;
572
573 xml_rsc_id = pcmk__xe_id(xml_rsc);
574 resource =
575 pcmk__find_constraint_resource(scheduler->priv->resources,
576 xml_rsc_id);
577 if (resource == NULL) {
578 // Should be possible only with validation disabled
579 pcmk__config_err("Ignoring %s and later resources in set %s: "
580 "No such resource", xml_rsc_id, set_id);
581 return;
582 }
583 flags = pcmk__coloc_explicit
584 | unpack_influence(coloc_id, resource, influence_s);
585 for (xml_rsc_with = pcmk__xe_first_child(set, PCMK_XE_RESOURCE_REF,
586 NULL, NULL);
587 xml_rsc_with != NULL;
588 xml_rsc_with = pcmk__xe_next(xml_rsc_with,
589 PCMK_XE_RESOURCE_REF)) {
590
591 xml_rsc_id = pcmk__xe_id(xml_rsc_with);
592 if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
593 break;
594 }
595 other =
596 pcmk__find_constraint_resource(scheduler->priv->resources,
597 xml_rsc_id);
598 pcmk__assert(other != NULL); // We already processed it
599 pcmk__new_colocation(set_id, NULL, local_score,
600 resource, other, role, role, flags);
601 }
602 }
603 }
604 }
605
606 /*!
607 * \internal
608 * \brief Colocate two resource sets relative to each other
609 *
610 * \param[in] id Colocation XML ID
611 * \param[in] set1 Dependent set
612 * \param[in] set2 Primary set
613 * \param[in] score Colocation score
614 * \param[in] influence_s Value of colocation's \c PCMK_XA_INFLUENCE
615 * attribute
616 * \param[in,out] scheduler Scheduler data
617 */
618 static void
619 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
620 int score, const char *influence_s,
621 pcmk_scheduler_t *scheduler)
622 {
623 xmlNode *xml_rsc = NULL;
624 pcmk_resource_t *rsc_1 = NULL;
625 pcmk_resource_t *rsc_2 = NULL;
626
627 const char *xml_rsc_id = NULL;
628 const char *role_1 = pcmk__xe_get(set1, PCMK_XA_ROLE);
629 const char *role_2 = pcmk__xe_get(set2, PCMK_XA_ROLE);
630
631 int rc = pcmk_rc_ok;
632 bool sequential = false;
633 uint32_t flags = pcmk__coloc_none;
634
635 if (score == 0) {
636 pcmk__trace("Ignoring colocation '%s' between sets %s and %s because "
637 "score is 0",
638 id, pcmk__xe_id(set1), pcmk__xe_id(set2));
639 return;
640 }
641
642 rc = pcmk__xe_get_bool(set1, PCMK_XA_SEQUENTIAL, &sequential);
643 if ((rc != pcmk_rc_ok) || sequential) {
644 // Get the first one
645 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL, NULL);
646 if (xml_rsc != NULL) {
647 xml_rsc_id = pcmk__xe_id(xml_rsc);
648 rsc_1 = pcmk__find_constraint_resource(scheduler->priv->resources,
649 xml_rsc_id);
650 if (rsc_1 == NULL) {
651 // Should be possible only with validation disabled
652 pcmk__config_err("Ignoring colocation of set %s with set %s "
653 "because first resource %s not found",
654 pcmk__xe_id(set1), pcmk__xe_id(set2),
655 xml_rsc_id);
656 return;
657 }
658 }
659 }
660
661 rc = pcmk__xe_get_bool(set2, PCMK_XA_SEQUENTIAL, &sequential);
662 if ((rc != pcmk_rc_ok) || sequential) {
663 // Get the last one
664 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
665 NULL);
666 xml_rsc != NULL;
667 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
668
669 xml_rsc_id = pcmk__xe_id(xml_rsc);
670 }
671 rsc_2 = pcmk__find_constraint_resource(scheduler->priv->resources,
672 xml_rsc_id);
673 if (rsc_2 == NULL) {
674 // Should be possible only with validation disabled
675 pcmk__config_err("Ignoring colocation of set %s with set %s "
676 "because last resource %s not found",
677 pcmk__xe_id(set1), pcmk__xe_id(set2), xml_rsc_id);
678 return;
679 }
680 }
681
682 if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
683 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
684 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
685 flags);
686
687 } else if (rsc_1 != NULL) { // Only set1 is sequential
688 flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
689 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
690 NULL);
691 xml_rsc != NULL;
692 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
693
694 xml_rsc_id = pcmk__xe_id(xml_rsc);
695 rsc_2 = pcmk__find_constraint_resource(scheduler->priv->resources,
696 xml_rsc_id);
697 if (rsc_2 == NULL) {
698 // Should be possible only with validation disabled
699 pcmk__config_err("Ignoring set %s colocation with resource %s "
700 "in set %s: No such resource",
701 pcmk__xe_id(set1), xml_rsc_id,
702 pcmk__xe_id(set2));
703 continue;
704 }
705 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
706 role_2, flags);
707 }
708
709 } else if (rsc_2 != NULL) { // Only set2 is sequential
710 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
711 NULL);
712 xml_rsc != NULL;
713 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
714
715 xml_rsc_id = pcmk__xe_id(xml_rsc);
716 rsc_1 = pcmk__find_constraint_resource(scheduler->priv->resources,
717 xml_rsc_id);
718 if (rsc_1 == NULL) {
719 // Should be possible only with validation disabled
720 pcmk__config_err("Ignoring colocation of set %s resource %s "
721 "with set %s: No such resource",
722 pcmk__xe_id(set1), xml_rsc_id,
723 pcmk__xe_id(set2));
724 continue;
725 }
726 flags = pcmk__coloc_explicit
727 | unpack_influence(id, rsc_1, influence_s);
728 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
729 role_2, flags);
730 }
731
732 } else { // Neither set is sequential
733 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
734 NULL);
735 xml_rsc != NULL;
736 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
737
738 xmlNode *xml_rsc_2 = NULL;
739
740 xml_rsc_id = pcmk__xe_id(xml_rsc);
741 rsc_1 = pcmk__find_constraint_resource(scheduler->priv->resources,
742 xml_rsc_id);
743 if (rsc_1 == NULL) {
744 // Should be possible only with validation disabled
745 pcmk__config_err("Ignoring colocation of set %s resource %s "
746 "with set %s: No such resource",
747 pcmk__xe_id(set1), xml_rsc_id,
748 pcmk__xe_id(set2));
749 continue;
750 }
751
752 flags = pcmk__coloc_explicit
753 | unpack_influence(id, rsc_1, influence_s);
754 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
755 NULL, NULL);
756 xml_rsc_2 != NULL;
757 xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
758
759 xml_rsc_id = pcmk__xe_id(xml_rsc_2);
760 rsc_2 =
761 pcmk__find_constraint_resource(scheduler->priv->resources,
762 xml_rsc_id);
763 if (rsc_2 == NULL) {
764 // Should be possible only with validation disabled
765 pcmk__config_err("Ignoring colocation of set %s resource "
766 "%s with set %s resource %s: No such "
767 "resource",
768 pcmk__xe_id(set1), pcmk__xe_id(xml_rsc),
769 pcmk__xe_id(set2), xml_rsc_id);
770 continue;
771 }
772 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
773 role_1, role_2, flags);
774 }
775 }
776 }
777 }
778
779 /*!
780 * \internal
781 * \brief Unpack a colocation constraint that contains no resource sets
782 *
783 * \param[in] xml_obj Colocation constraint XML
784 * \param[in] id Colocation constraint XML ID (non-NULL)
785 * \param[in] score Integer score parsed from score attribute
786 * \param[in] influence_s Colocation constraint's influence attribute value
787 * \param[in,out] scheduler Scheduler data
788 */
789 static void
790 unpack_simple_colocation(const xmlNode *xml_obj, const char *id, int score,
791 const char *influence_s, pcmk_scheduler_t *scheduler)
792 {
793 uint32_t flags = pcmk__coloc_none;
794
795 const char *dependent_id = pcmk__xe_get(xml_obj, PCMK_XA_RSC);
796 const char *primary_id = pcmk__xe_get(xml_obj, PCMK_XA_WITH_RSC);
797 const char *dependent_role = pcmk__xe_get(xml_obj, PCMK_XA_RSC_ROLE);
798 const char *primary_role = pcmk__xe_get(xml_obj, PCMK_XA_WITH_RSC_ROLE);
799 const char *attr = pcmk__xe_get(xml_obj, PCMK_XA_NODE_ATTRIBUTE);
800
801 pcmk_resource_t *primary = NULL;
802 pcmk_resource_t *dependent = NULL;
803
804 primary = pcmk__find_constraint_resource(scheduler->priv->resources,
805 primary_id);
806 dependent = pcmk__find_constraint_resource(scheduler->priv->resources,
807 dependent_id);
808
809 if (dependent == NULL) {
810 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
811 "does not exist", id, dependent_id);
812 return;
813
814 } else if (primary == NULL) {
815 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
816 "does not exist", id, primary_id);
817 return;
818 }
819
820 if (pcmk__xe_attr_is_true(xml_obj, PCMK_XA_SYMMETRICAL)) {
821 pcmk__config_warn("The colocation constraint "
822 "'" PCMK_XA_SYMMETRICAL "' attribute has been "
823 "removed");
824 }
825
826 flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
827 pcmk__new_colocation(id, attr, score, dependent, primary,
828 dependent_role, primary_role, flags);
829 }
830
831 // \return Standard Pacemaker return code
832 static int
833 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
834 pcmk_scheduler_t *scheduler)
835 {
836 const char *id = NULL;
837 const char *dependent_id = NULL;
838 const char *primary_id = NULL;
839 const char *dependent_role = NULL;
840 const char *primary_role = NULL;
841
842 pcmk_resource_t *dependent = NULL;
843 pcmk_resource_t *primary = NULL;
844
845 pcmk__idref_t *dependent_tag = NULL;
846 pcmk__idref_t *primary_tag = NULL;
847
848 xmlNode *dependent_set = NULL;
849 xmlNode *primary_set = NULL;
850 bool any_sets = false;
851
852 *expanded_xml = NULL;
853
854 CRM_CHECK(xml_obj != NULL, return EINVAL);
855
856 id = pcmk__xe_id(xml_obj);
857 if (id == NULL) {
858 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
859 xml_obj->name);
860 return pcmk_rc_unpack_error;
861 }
862
863 // Check whether there are any resource sets with template or tag references
864 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
865 if (*expanded_xml != NULL) {
866 pcmk__log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
867 return pcmk_rc_ok;
868 }
869
870 dependent_id = pcmk__xe_get(xml_obj, PCMK_XA_RSC);
871 primary_id = pcmk__xe_get(xml_obj, PCMK_XA_WITH_RSC);
872 if ((dependent_id == NULL) || (primary_id == NULL)) {
873 return pcmk_rc_ok;
874 }
875
876 if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
877 &dependent_tag)) {
878 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
879 "valid resource or tag", id, dependent_id);
880 return pcmk_rc_unpack_error;
881 }
882
883 if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
884 &primary_tag)) {
885 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
886 "valid resource or tag", id, primary_id);
887 return pcmk_rc_unpack_error;
888 }
889
890 if ((dependent != NULL) && (primary != NULL)) {
891 /* Neither side references any template/tag. */
892 return pcmk_rc_ok;
893 }
894
895 if ((dependent_tag != NULL) && (primary_tag != NULL)) {
896 // A colocation constraint between two templates/tags makes no sense
897 pcmk__config_err("Ignoring constraint '%s' because two templates or "
898 "tags cannot be colocated", id);
899 return pcmk_rc_unpack_error;
900 }
901
902 dependent_role = pcmk__xe_get(xml_obj, PCMK_XA_RSC_ROLE);
903 primary_role = pcmk__xe_get(xml_obj, PCMK_XA_WITH_RSC_ROLE);
904
905 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
906
907 /* Convert dependent's template/tag reference into constraint
908 * PCMK_XE_RESOURCE_SET
909 */
910 if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, PCMK_XA_RSC, true,
911 scheduler)) {
912
913 g_clear_pointer(expanded_xml, pcmk__xml_free);
914 return pcmk_rc_unpack_error;
915 }
916
917 if (dependent_set != NULL) {
918 if (dependent_role != NULL) {
919 /* Move PCMK_XA_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
920 * PCMK_XA_ROLE
921 */
922 pcmk__xe_set(dependent_set, PCMK_XA_ROLE, dependent_role);
923 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_RSC_ROLE);
924 }
925 any_sets = true;
926 }
927
928 /* Convert primary's template/tag reference into constraint
929 * PCMK_XE_RESOURCE_SET
930 */
931 if (!pcmk__tag_to_set(*expanded_xml, &primary_set, PCMK_XA_WITH_RSC, true,
932 scheduler)) {
933
934 g_clear_pointer(expanded_xml, pcmk__xml_free);
935 return pcmk_rc_unpack_error;
936 }
937
938 if (primary_set != NULL) {
939 if (primary_role != NULL) {
940 /* Move PCMK_XA_WITH_RSC_ROLE into converted PCMK_XE_RESOURCE_SET as
941 * PCMK_XA_ROLE
942 */
943 pcmk__xe_set(primary_set, PCMK_XA_ROLE, primary_role);
944 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_WITH_RSC_ROLE);
945 }
946 any_sets = true;
947 }
948
949 if (any_sets) {
950 pcmk__log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_COLOCATION);
951 } else {
952 g_clear_pointer(expanded_xml, pcmk__xml_free);
953 }
954
955 return pcmk_rc_ok;
956 }
957
958 /*!
959 * \internal
960 * \brief Parse a colocation constraint from XML into scheduler data
961 *
962 * \param[in,out] xml_obj Colocation constraint XML to unpack
963 * \param[in,out] scheduler Scheduler data to add constraint to
964 */
965 void
966 pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
967 {
968 int score_i = 0;
969 xmlNode *set = NULL;
970 xmlNode *last = NULL;
971
972 xmlNode *orig_xml = NULL;
973 xmlNode *expanded_xml = NULL;
974
975 const char *id = pcmk__xe_get(xml_obj, PCMK_XA_ID);
976 const char *score = NULL;
977 const char *influence_s = NULL;
978
979 if (pcmk__str_empty(id)) {
980 pcmk__config_err("Ignoring " PCMK_XE_RSC_COLOCATION
981 " without " CRM_ATTR_ID);
982 return;
983 }
984
985 if (unpack_colocation_tags(xml_obj, &expanded_xml,
986 scheduler) != pcmk_rc_ok) {
987 return;
988 }
989 if (expanded_xml != NULL) {
990 orig_xml = xml_obj;
991 xml_obj = expanded_xml;
992 }
993
994 score = pcmk__xe_get(xml_obj, PCMK_XA_SCORE);
995 if (score != NULL) {
996 int rc = pcmk_parse_score(score, &score_i, 0);
997
998 if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
999 pcmk__config_err("Ignoring colocation %s because '%s' "
1000 "is not a valid score", id, score);
1001 return;
1002 }
1003 }
1004 influence_s = pcmk__xe_get(xml_obj, PCMK_XA_INFLUENCE);
1005
1006 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1007 set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
1008
1009 set = pcmk__xe_resolve_idref(set, scheduler->input);
1010 if (set == NULL) { // Configuration error, message already logged
1011 if (expanded_xml != NULL) {
1012 pcmk__xml_free(expanded_xml);
1013 }
1014 return;
1015 }
1016
1017 if (pcmk__str_empty(pcmk__xe_id(set))) {
1018 pcmk__config_err("Ignoring " PCMK_XE_RESOURCE_SET
1019 " without " CRM_ATTR_ID);
1020 continue;
1021 }
1022 unpack_colocation_set(set, score_i, id, influence_s, scheduler);
1023
1024 if (last != NULL) {
1025 colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
1026 }
1027 last = set;
1028 }
1029
1030 if (expanded_xml) {
1031 pcmk__xml_free(expanded_xml);
1032 xml_obj = orig_xml;
1033 }
1034
1035 if (last == NULL) {
1036 unpack_simple_colocation(xml_obj, id, score_i, influence_s, scheduler);
1037 }
1038 }
1039
1040 /*!
1041 * \internal
1042 * \brief Check whether colocation's dependent preferences should be considered
1043 *
1044 * \param[in] colocation Colocation constraint
1045 * \param[in] rsc Primary instance (normally this will be
1046 * colocation->primary, which NULL will be treated as,
1047 * but for clones or bundles with multiple instances
1048 * this can be a particular instance)
1049 *
1050 * \return true if colocation influence should be effective, otherwise false
1051 */
1052 bool
1053 pcmk__colocation_has_influence(const pcmk__colocation_t *colocation,
1054 const pcmk_resource_t *rsc)
1055 {
1056 if (rsc == NULL) {
1057 rsc = colocation->primary;
1058 }
1059
1060 /* A bundle replica colocates its remote connection with its container,
1061 * using a finite score so that the container can run on Pacemaker Remote
1062 * nodes.
1063 *
1064 * Moving a connection is lightweight and does not interrupt the service,
1065 * while moving a container is heavyweight and does interrupt the service,
1066 * so don't move a clean, active container based solely on the preferences
1067 * of its connection.
1068 *
1069 * This also avoids problematic scenarios where two containers want to
1070 * perpetually swap places.
1071 */
1072 if (pcmk__is_set(colocation->dependent->flags,
1073 pcmk__rsc_remote_nesting_allowed)
1074 && !pcmk__is_set(rsc->flags, pcmk__rsc_failed)
1075 && pcmk__list_of_1(rsc->priv->active_nodes)) {
1076 return false;
1077 }
1078
1079 /* The dependent in a colocation influences the primary's location
1080 * if the PCMK_XA_INFLUENCE option is true or the primary is not yet active.
1081 */
1082 return pcmk__is_set(colocation->flags, pcmk__coloc_influence)
1083 || (rsc->priv->active_nodes == NULL);
1084 }
1085
1086 /*!
1087 * \internal
1088 * \brief Make actions of a given type unrunnable for a given resource
1089 *
1090 * \param[in,out] rsc Resource whose actions should be blocked
1091 * \param[in] task Name of action to block
1092 * \param[in] reason Unrunnable start action causing the block
1093 */
1094 static void
1095 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
1096 const pcmk_resource_t *reason)
1097 {
1098 GList *iter = NULL;
1099 char *reason_text = pcmk__assert_asprintf("colocation with %s", reason->id);
1100
1101 for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
1102 pcmk_action_t *action = iter->data;
1103
1104 if (pcmk__is_set(action->flags, pcmk__action_runnable)
1105 && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1106
1107 pcmk__clear_action_flags(action, pcmk__action_runnable);
1108 pe_action_set_reason(action, reason_text, false);
1109 pcmk__block_colocation_dependents(action);
1110 pcmk__update_action_for_orderings(action, rsc->priv->scheduler);
1111 }
1112 }
1113
1114 // If parent resource can't perform an action, neither can any children
1115 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
1116 mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1117 }
1118 free(reason_text);
1119 }
1120
1121 /*!
1122 * \internal
1123 * \brief If an action is unrunnable, block any relevant dependent actions
1124 *
1125 * If a given action is an unrunnable start or promote, block the start or
1126 * promote actions of resources colocated with it, as appropriate to the
1127 * colocations' configured roles.
1128 *
1129 * \param[in,out] action Action to check
1130 */
1131 void
1132 pcmk__block_colocation_dependents(pcmk_action_t *action)
1133 {
1134 GList *iter = NULL;
1135 GList *colocations = NULL;
1136 pcmk_resource_t *rsc = NULL;
1137 bool is_start = false;
1138
1139 if (pcmk__is_set(action->flags, pcmk__action_runnable)) {
1140 return; // Only unrunnable actions block dependents
1141 }
1142
1143 is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1144 if (!is_start
1145 && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1146 return; // Only unrunnable starts and promotes block dependents
1147 }
1148
1149 pcmk__assert(action->rsc != NULL); // Start and promote are resource actions
1150
1151 /* If this resource is part of a collective resource, dependents are blocked
1152 * only if all instances of the collective are unrunnable, so check the
1153 * collective resource.
1154 */
1155 rsc = uber_parent(action->rsc);
1156 if (rsc->priv->parent != NULL) {
1157 rsc = rsc->priv->parent; // Bundle
1158 }
1159
1160 // Colocation fails only if entire primary can't reach desired role
1161 for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
1162 pcmk_resource_t *child = iter->data;
1163 pcmk_action_t *child_action = NULL;
1164
1165 child_action = find_first_action(child->priv->actions, NULL,
1166 action->task, NULL);
1167 if ((child_action == NULL)
1168 || pcmk__is_set(child_action->flags, pcmk__action_runnable)) {
1169 pcmk__trace("Not blocking %s colocation dependents because at "
1170 "least %s has runnable %s",
1171 rsc->id, child->id, action->task);
1172 return; // At least one child can reach desired role
1173 }
1174 }
1175
1176 pcmk__trace("Blocking %s colocation dependents due to unrunnable %s %s",
1177 rsc->id, action->rsc->id, action->task);
1178
1179 // Check each colocation where this resource is primary
1180 colocations = pcmk__with_this_colocations(rsc);
1181 for (iter = colocations; iter != NULL; iter = iter->next) {
1182 pcmk__colocation_t *colocation = iter->data;
1183
1184 if (colocation->score < PCMK_SCORE_INFINITY) {
1185 continue; // Only mandatory colocations block dependent
1186 }
1187
1188 /* If the primary can't start, the dependent can't reach its colocated
1189 * role, regardless of what the primary or dependent colocation role is.
1190 *
1191 * If the primary can't be promoted, the dependent can't reach its
1192 * colocated role if the primary's colocation role is promoted.
1193 */
1194 if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1195 continue;
1196 }
1197
1198 // Block the dependent from reaching its colocated role
1199 if (colocation->dependent_role == pcmk_role_promoted) {
1200 mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1201 action->rsc);
1202 } else {
1203 mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1204 action->rsc);
1205 }
1206 }
1207 g_list_free(colocations);
1208 }
1209
1210 /*!
1211 * \internal
1212 * \brief Get the resource to use for role comparisons
1213 *
1214 * A bundle replica includes a container and possibly an instance of the bundled
1215 * resource. The dependent in a "with bundle" colocation is colocated with a
1216 * particular bundle container. However, if the colocation includes a role, then
1217 * the role must be checked on the bundled resource instance inside the
1218 * container. The container itself will never be promoted; the bundled resource
1219 * may be.
1220 *
1221 * If the given resource is a bundle replica container, return the resource
1222 * inside it, if any. Otherwise, return the resource itself.
1223 *
1224 * \param[in] rsc Resource to check
1225 *
1226 * \return Resource to use for role comparisons
1227 */
1228 static const pcmk_resource_t *
1229 get_resource_for_role(const pcmk_resource_t *rsc)
1230 {
1231 if (pcmk__is_set(rsc->flags, pcmk__rsc_replica_container)) {
1232 const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1233
1234 if (child != NULL) {
1235 return child;
1236 }
1237 }
1238 return rsc;
1239 }
1240
1241 /*!
1242 * \internal
1243 * \brief Determine how a colocation constraint should affect a resource
1244 *
1245 * Colocation constraints have different effects at different points in the
1246 * scheduler sequence. Initially, they affect a resource's location; once that
1247 * is determined, then for promotable clones they can affect a resource
1248 * instance's role; after both are determined, the constraints no longer matter.
1249 * Given a specific colocation constraint, check what has been done so far to
1250 * determine what should be affected at the current point in the scheduler.
1251 *
1252 * \param[in] dependent Dependent resource in colocation
1253 * \param[in] primary Primary resource in colocation
1254 * \param[in] colocation Colocation constraint
1255 * \param[in] preview If true, pretend resources have already been assigned
1256 *
1257 * \return How colocation constraint should be applied at this point
1258 */
1259 enum pcmk__coloc_affects
1260 pcmk__colocation_affects(const pcmk_resource_t *dependent,
1261 const pcmk_resource_t *primary,
1262 const pcmk__colocation_t *colocation, bool preview)
1263 {
1264 const pcmk_resource_t *dependent_role_rsc = NULL;
1265 const pcmk_resource_t *primary_role_rsc = NULL;
1266
1267 pcmk__assert((dependent != NULL) && (primary != NULL)
1268 && (colocation != NULL));
1269
1270 if (!preview && pcmk__is_set(primary->flags, pcmk__rsc_unassigned)) {
1271 // Primary resource has not been assigned yet, so we can't do anything
1272 return pcmk__coloc_affects_nothing;
1273 }
1274
1275 dependent_role_rsc = get_resource_for_role(dependent);
1276
1277 primary_role_rsc = get_resource_for_role(primary);
1278
1279 if ((colocation->dependent_role >= pcmk_role_unpromoted)
1280 && (dependent_role_rsc->priv->parent != NULL)
1281 && pcmk__is_set(dependent_role_rsc->priv->parent->flags,
1282 pcmk__rsc_promotable)
1283 && !pcmk__is_set(dependent_role_rsc->flags, pcmk__rsc_unassigned)) {
1284
1285 /* This is a colocation by role, and the dependent is a promotable clone
1286 * that has already been assigned, so the colocation should now affect
1287 * the role.
1288 */
1289 return pcmk__coloc_affects_role;
1290 }
1291
1292 if (!preview && !pcmk__is_set(dependent->flags, pcmk__rsc_unassigned)) {
1293 /* The dependent resource has already been through assignment, so the
1294 * constraint no longer matters.
1295 */
1296 return pcmk__coloc_affects_nothing;
1297 }
1298
1299 if ((colocation->dependent_role != pcmk_role_unknown)
1300 && (colocation->dependent_role != dependent_role_rsc->priv->next_role)) {
1301 pcmk__trace("Skipping %scolocation '%s': dependent limited to %s role "
1302 "but %s next role is %s",
1303 ((colocation->score < 0)? "anti-" : ""),
1304 colocation->id, pcmk_role_text(colocation->dependent_role),
1305 dependent_role_rsc->id,
1306 pcmk_role_text(dependent_role_rsc->priv->next_role));
1307 return pcmk__coloc_affects_nothing;
1308 }
1309
1310 if ((colocation->primary_role != pcmk_role_unknown)
1311 && (colocation->primary_role != primary_role_rsc->priv->next_role)) {
1312 pcmk__trace("Skipping %scolocation '%s': primary limited to %s role "
1313 "but %s next role is %s",
1314 ((colocation->score < 0)? "anti-" : ""),
1315 colocation->id, pcmk_role_text(colocation->primary_role),
1316 primary_role_rsc->id,
1317 pcmk_role_text(primary_role_rsc->priv->next_role));
1318 return pcmk__coloc_affects_nothing;
1319 }
1320
1321 return pcmk__coloc_affects_location;
1322 }
1323
1324 /*!
1325 * \internal
1326 * \brief Apply colocation to dependent for assignment purposes
1327 *
1328 * Update the allowed node scores of the dependent resource in a colocation,
1329 * for the purposes of assigning it to a node.
1330 *
1331 * \param[in,out] dependent Dependent resource in colocation
1332 * \param[in] primary Primary resource in colocation
1333 * \param[in] colocation Colocation constraint
1334 */
1335 void
1336 pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent,
1337 const pcmk_resource_t *primary,
1338 const pcmk__colocation_t *colocation)
1339 {
1340 const char *attr = colocation->node_attribute;
1341 const char *value = NULL;
1342 GHashTable *work = NULL;
1343 GHashTableIter iter;
1344 pcmk_node_t *node = NULL;
1345
1346 if (primary->priv->assigned_node != NULL) {
1347 value = pcmk__colocation_node_attr(primary->priv->assigned_node,
1348 attr, primary);
1349
1350 } else if (colocation->score < 0) {
1351 // Nothing to do (anti-colocation with something that is not running)
1352 return;
1353 }
1354
1355 work = pcmk__copy_node_table(dependent->priv->allowed_nodes);
1356
1357 g_hash_table_iter_init(&iter, work);
1358 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1359 if (primary->priv->assigned_node == NULL) {
1360 node->assign->score = pcmk__add_scores(-colocation->score,
1361 node->assign->score);
1362 pcmk__rsc_trace(dependent,
1363 "Applied %s to %s score on %s (now %s after "
1364 "subtracting %s because primary %s inactive)",
1365 colocation->id, dependent->id,
1366 pcmk__node_name(node),
1367 pcmk_readable_score(node->assign->score),
1368 pcmk_readable_score(colocation->score), primary->id);
1369 continue;
1370 }
1371
1372 if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1373 value, pcmk__str_casei)) {
1374
1375 /* Add colocation score only if optional (or minus infinity). A
1376 * mandatory colocation is a requirement rather than a preference,
1377 * so we don't need to consider it for relative assignment purposes.
1378 * The resource will simply be forbidden from running on the node if
1379 * the primary isn't active there (via the condition above).
1380 */
1381 if (colocation->score < PCMK_SCORE_INFINITY) {
1382 node->assign->score = pcmk__add_scores(colocation->score,
1383 node->assign->score);
1384 pcmk__rsc_trace(dependent,
1385 "Applied %s to %s score on %s (now %s after "
1386 "adding %s)",
1387 colocation->id, dependent->id,
1388 pcmk__node_name(node),
1389 pcmk_readable_score(node->assign->score),
1390 pcmk_readable_score(colocation->score));
1391 }
1392 continue;
1393 }
1394
1395 if (colocation->score >= PCMK_SCORE_INFINITY) {
1396 /* Only mandatory colocations are relevant when the colocation
1397 * attribute doesn't match, because an attribute not matching is not
1398 * a negative preference -- the colocation is simply relevant only
1399 * where it matches.
1400 */
1401 node->assign->score = -PCMK_SCORE_INFINITY;
1402 pcmk__rsc_trace(dependent,
1403 "Banned %s from %s because colocation %s attribute %s "
1404 "does not match",
1405 dependent->id, pcmk__node_name(node),
1406 colocation->id, attr);
1407 }
1408 }
1409
1410 if ((colocation->score <= -PCMK_SCORE_INFINITY)
1411 || (colocation->score >= PCMK_SCORE_INFINITY)
1412 || pcmk__any_node_available(work)) {
1413
1414 g_hash_table_destroy(dependent->priv->allowed_nodes);
1415 dependent->priv->allowed_nodes = work;
1416 work = NULL;
1417
1418 } else {
1419 pcmk__rsc_info(dependent,
1420 "%s: Rolling back scores from %s (no available nodes)",
1421 dependent->id, primary->id);
1422 }
1423
1424 g_clear_pointer(&work, g_hash_table_destroy);
1425 }
1426
1427 /*!
1428 * \internal
1429 * \brief Apply colocation to dependent for role purposes
1430 *
1431 * Update the priority of the dependent resource in a colocation, for the
1432 * purposes of selecting its role
1433 *
1434 * \param[in,out] dependent Dependent resource in colocation
1435 * \param[in] primary Primary resource in colocation
1436 * \param[in] colocation Colocation constraint
1437 *
1438 * \return The score added to the dependent's priority
1439 */
1440 int
1441 pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent,
1442 const pcmk_resource_t *primary,
1443 const pcmk__colocation_t *colocation)
1444 {
1445 const char *dependent_value = NULL;
1446 const char *primary_value = NULL;
1447 const char *attr = colocation->node_attribute;
1448 int score_multiplier = 1;
1449 int priority_delta = 0;
1450 const pcmk_node_t *primary_node = NULL;
1451 const pcmk_node_t *dependent_node = NULL;
1452
1453 pcmk__assert((dependent != NULL) && (primary != NULL)
1454 && (colocation != NULL));
1455
1456 primary_node = primary->priv->assigned_node;
1457 dependent_node = dependent->priv->assigned_node;
1458
1459 if (dependent_node == NULL) {
1460 return 0;
1461 }
1462
1463 if ((primary_node != NULL)
1464 && (colocation->primary_role != pcmk_role_unknown)) {
1465 /* Colocation applies only if the primary's next role matches.
1466 *
1467 * If primary_node == NULL, we want to proceed past this block, so that
1468 * dependent_node is marked ineligible for promotion.
1469 *
1470 * @TODO Why ignore a mandatory colocation in this case when we apply
1471 * its negation in the mismatched value case?
1472 */
1473 const pcmk_resource_t *role_rsc = get_resource_for_role(primary);
1474
1475 if (colocation->primary_role != role_rsc->priv->next_role) {
1476 return 0;
1477 }
1478 }
1479
1480 dependent_value = pcmk__colocation_node_attr(dependent_node, attr,
1481 dependent);
1482 primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1483
1484 if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1485 if ((colocation->score == PCMK_SCORE_INFINITY)
1486 && (colocation->dependent_role == pcmk_role_promoted)) {
1487 /* For a mandatory promoted-role colocation, mark the dependent node
1488 * ineligible to promote the dependent if its attribute value
1489 * doesn't match the primary node's
1490 */
1491 score_multiplier = -1;
1492
1493 } else {
1494 // Otherwise, ignore the colocation if attribute values don't match
1495 return 0;
1496 }
1497
1498 } else if (colocation->dependent_role == pcmk_role_unpromoted) {
1499 /* Node attribute values matched, so we want to avoid promoting the
1500 * dependent on this node
1501 */
1502 score_multiplier = -1;
1503 }
1504
1505 priority_delta = score_multiplier * colocation->score;
1506 dependent->priv->priority = pcmk__add_scores(priority_delta,
1507 dependent->priv->priority);
1508 pcmk__rsc_trace(dependent,
1509 "Applied %s to %s promotion priority (now %s after %s %d)",
1510 colocation->id, dependent->id,
1511 pcmk_readable_score(dependent->priv->priority),
1512 ((score_multiplier == 1)? "adding" : "subtracting"),
1513 colocation->score);
1514
1515 return priority_delta;
1516 }
1517
1518 /*!
1519 * \internal
1520 * \brief Find score of highest-scored node that matches colocation attribute
1521 *
1522 * \param[in] colocation Colocation constraint being applied
1523 * \param[in,out] rsc Resource whose allowed nodes should be searched
1524 * \param[in] attr Colocation attribute name (must not be NULL)
1525 * \param[in] value Colocation attribute value to require
1526 */
1527 static int
1528 best_node_score_matching_attr(const pcmk__colocation_t *colocation,
1529 pcmk_resource_t *rsc, const char *attr,
1530 const char *value)
1531 {
1532 GHashTable *allowed_nodes_orig = NULL;
1533 GHashTableIter iter;
1534 pcmk_node_t *node = NULL;
1535 int best_score = -PCMK_SCORE_INFINITY;
1536 const char *best_node = NULL;
1537
1538 if ((colocation != NULL) && (rsc == colocation->dependent)
1539 && pcmk__is_set(colocation->flags, pcmk__coloc_explicit)
1540 && pcmk__is_group(rsc->priv->parent)
1541 && (rsc != rsc->priv->parent->priv->children->data)) {
1542 /* The resource is a user-configured colocation's explicit dependent,
1543 * and a group member other than the first, which means the group's
1544 * location constraint scores were not applied to it (see
1545 * pcmk__group_apply_location()). Explicitly consider those scores now.
1546 *
1547 * @TODO This does leave one suboptimal case: if the group itself or
1548 * another member other than the first is explicitly colocated with
1549 * the same primary, the primary will count the group's location scores
1550 * multiple times. This is much less likely than a single member being
1551 * explicitly colocated, so it's an acceptable tradeoff for now.
1552 */
1553 allowed_nodes_orig = rsc->priv->allowed_nodes;
1554 rsc->priv->allowed_nodes = pcmk__copy_node_table(allowed_nodes_orig);
1555 for (GList *loc_iter = rsc->priv->scheduler->priv->location_constraints;
1556 loc_iter != NULL; loc_iter = loc_iter->next) {
1557
1558 pcmk__location_t *location = loc_iter->data;
1559
1560 if (location->rsc == rsc->priv->parent) {
1561 rsc->priv->cmds->apply_location(rsc, location);
1562 }
1563 }
1564 }
1565
1566 // Find best allowed node with matching attribute
1567 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1568 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1569
1570 if ((node->assign->score > best_score)
1571 && pcmk__node_available(node, false, false)
1572 && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1573 pcmk__str_casei)) {
1574
1575 best_score = node->assign->score;
1576 best_node = node->priv->name;
1577 }
1578 }
1579
1580 if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1581 if (best_node == NULL) {
1582 pcmk__info("No allowed node for %s matches node attribute %s=%s",
1583 rsc->id, attr, value);
1584 } else {
1585 pcmk__info("Allowed node %s for %s had best score (%d) of those "
1586 "matching node attribute %s=%s",
1587 best_node, rsc->id, best_score, attr, value);
1588 }
1589 }
1590
1591 if (allowed_nodes_orig != NULL) {
1592 g_hash_table_destroy(rsc->priv->allowed_nodes);
1593 rsc->priv->allowed_nodes = allowed_nodes_orig;
1594 }
1595 return best_score;
1596 }
1597
1598 /*!
1599 * \internal
1600 * \brief Check whether a resource is allowed only on a single node
1601 *
1602 * \param[in] rsc Resource to check
1603 *
1604 * \return \c true if \p rsc is allowed only on one node, otherwise \c false
1605 */
1606 static bool
1607 allowed_on_one(const pcmk_resource_t *rsc)
1608 {
1609 GHashTableIter iter;
1610 pcmk_node_t *allowed_node = NULL;
1611 int allowed_nodes = 0;
1612
1613 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1614 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1615 if ((allowed_node->assign->score >= 0) && (++allowed_nodes > 1)) {
1616 pcmk__rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1617 return false;
1618 }
1619 }
1620 pcmk__rsc_trace(rsc, "%s is allowed %s", rsc->id,
1621 ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1622 return (allowed_nodes == 1);
1623 }
1624
1625 /*!
1626 * \internal
1627 * \brief Add resource's colocation matches to current node assignment scores
1628 *
1629 * For each node in a given table, if any of a given resource's allowed nodes
1630 * have a matching value for the colocation attribute, add the highest of those
1631 * nodes' scores to the node's score.
1632 *
1633 * \param[in,out] nodes Table of nodes with assignment scores so far
1634 * \param[in,out] source_rsc Resource whose node scores to add
1635 * \param[in] target_rsc Resource on whose behalf to update \p nodes
1636 * \param[in] colocation Original colocation constraint (used to get
1637 * configured primary resource's stickiness, and
1638 * to get colocation node attribute; pass NULL to
1639 * ignore stickiness and use default attribute)
1640 * \param[in] factor Factor by which to multiply scores being added
1641 * \param[in] only_positive Whether to add only positive scores
1642 */
1643 static void
1644 add_node_scores_matching_attr(GHashTable *nodes,
1645 pcmk_resource_t *source_rsc,
1646 const pcmk_resource_t *target_rsc,
1647 const pcmk__colocation_t *colocation,
1648 float factor, bool only_positive)
1649 {
1650 GHashTableIter iter;
1651 pcmk_node_t *node = NULL;
1652 const char *attr = colocation->node_attribute;
1653
1654 // Iterate through each node
1655 g_hash_table_iter_init(&iter, nodes);
1656 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1657 float delta_f = 0;
1658 int delta = 0;
1659 int score = 0;
1660 int new_score = 0;
1661 const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1662
1663 score = best_node_score_matching_attr(colocation, source_rsc, attr, value);
1664
1665 if ((factor < 0) && (score < 0)) {
1666 /* If the dependent is anti-colocated, we generally don't want the
1667 * primary to prefer nodes that the dependent avoids. That could
1668 * lead to unnecessary shuffling of the primary when the dependent
1669 * hits its migration threshold somewhere, for example.
1670 *
1671 * However, there are cases when it is desirable. If the dependent
1672 * can't run anywhere but where the primary is, it would be
1673 * worthwhile to move the primary for the sake of keeping the
1674 * dependent active.
1675 *
1676 * We can't know that exactly at this point since we don't know
1677 * where the primary will be assigned, but we can limit considering
1678 * the preference to when the dependent is allowed only on one node.
1679 * This is less than ideal for multiple reasons:
1680 *
1681 * - the dependent could be allowed on more than one node but have
1682 * anti-colocation primaries on each;
1683 * - the dependent could be a clone or bundle with multiple
1684 * instances, and the dependent as a whole is allowed on multiple
1685 * nodes but some instance still can't run
1686 * - the dependent has considered node-specific criteria such as
1687 * location constraints and stickiness by this point, but might
1688 * have other factors that end up disallowing a node
1689 *
1690 * but the alternative is making the primary move when it doesn't
1691 * need to.
1692 *
1693 * We also consider the primary's stickiness and influence, so the
1694 * user has some say in the matter. (This is the configured primary,
1695 * not a particular instance of the primary, but that doesn't matter
1696 * unless stickiness uses a rule to vary by node, and that seems
1697 * acceptable to ignore.)
1698 */
1699 if ((colocation->primary->priv->stickiness >= -score)
1700 || !pcmk__colocation_has_influence(colocation, NULL)
1701 || !allowed_on_one(colocation->dependent)) {
1702 pcmk__trace("%s: Filtering %d + %f * %d "
1703 "(double negative disallowed)",
1704 pcmk__node_name(node), node->assign->score, factor,
1705 score);
1706 continue;
1707 }
1708 }
1709
1710 if (node->assign->score == INFINITY_HACK) {
1711 pcmk__trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1712 pcmk__node_name(node), node->assign->score, factor,
1713 score);
1714 continue;
1715 }
1716
1717 delta_f = factor * score;
1718
1719 // Round the number; see http://c-faq.com/fp/round.html
1720 delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1721
1722 /* Small factors can obliterate the small scores that are often actually
1723 * used in configurations. If the score and factor are nonzero, ensure
1724 * that the result is nonzero as well.
1725 */
1726 if ((delta == 0) && (score != 0)) {
1727 if (factor > 0.0) {
1728 delta = 1;
1729 } else if (factor < 0.0) {
1730 delta = -1;
1731 }
1732 }
1733
1734 new_score = pcmk__add_scores(delta, node->assign->score);
1735
1736 if (only_positive && (new_score < 0) && (node->assign->score > 0)) {
1737 pcmk__trace("%s: Filtering %d + %f * %d = %d "
1738 "(negative disallowed, marking node unusable)",
1739 pcmk__node_name(node), node->assign->score, factor,
1740 score, new_score);
1741 node->assign->score = INFINITY_HACK;
1742 continue;
1743 }
1744
1745 if (only_positive && (new_score < 0) && (node->assign->score == 0)) {
1746 pcmk__trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1747 pcmk__node_name(node), node->assign->score, factor,
1748 score, new_score);
1749 continue;
1750 }
1751
1752 pcmk__trace("%s: %d + %f * %d = %d", pcmk__node_name(node),
1753 node->assign->score, factor, score, new_score);
1754 node->assign->score = new_score;
1755 }
1756 }
1757
1758 /*!
1759 * \internal
1760 * \brief Update nodes with scores of colocated resources' nodes
1761 *
1762 * Given a table of nodes and a resource, update the nodes' scores with the
1763 * scores of the best nodes matching the attribute used for each of the
1764 * resource's relevant colocations.
1765 *
1766 * \param[in,out] source_rsc Resource whose node scores to add
1767 * \param[in] target_rsc Resource on whose behalf to update \p *nodes
1768 * \param[in] log_id Resource ID for logs (if \c NULL, use
1769 * \p source_rsc ID)
1770 * \param[in,out] nodes Nodes to update (set initial contents to \c NULL
1771 * to copy allowed nodes from \p source_rsc)
1772 * \param[in] colocation Original colocation constraint (used to get
1773 * configured primary resource's stickiness, and
1774 * to get colocation node attribute; if \c NULL,
1775 * <tt>source_rsc</tt>'s own matching node scores
1776 * will not be added, and \p *nodes must be \c NULL
1777 * as well)
1778 * \param[in] factor Incorporate scores multiplied by this factor
1779 * \param[in] flags Bitmask of enum pcmk__coloc_select values
1780 *
1781 * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, and
1782 * the \c pcmk__coloc_select_this_with flag are used together (and only by
1783 * \c cmp_resources()).
1784 * \note The caller remains responsible for freeing \p *nodes.
1785 * \note This is the shared implementation of
1786 * \c pcmk__assignment_methods_t:add_colocated_node_scores().
1787 */
1788 void
1789 pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc,
1790 const pcmk_resource_t *target_rsc,
1791 const char *log_id,
1792 GHashTable **nodes,
1793 const pcmk__colocation_t *colocation,
1794 float factor, uint32_t flags)
1795 {
1796 GHashTable *work = NULL;
1797
|
(1) Event path: |
Condition "source_rsc != NULL", taking true branch. |
|
(2) Event path: |
Condition "nodes != NULL", taking true branch. |
|
(3) Event path: |
Condition "colocation != NULL", taking false branch. |
|
(4) Event path: |
Condition "target_rsc == NULL", taking true branch. |
|
(5) Event path: |
Condition "*nodes == NULL", taking true branch. |
1798 pcmk__assert((source_rsc != NULL) && (nodes != NULL)
1799 && ((colocation != NULL)
1800 || ((target_rsc == NULL) && (*nodes == NULL))));
1801
|
(6) Event path: |
Condition "log_id == NULL", taking true branch. |
1802 if (log_id == NULL) {
1803 log_id = source_rsc->id;
1804 }
1805
1806 // Avoid infinite recursion
|
(7) Event path: |
Condition "pcmk__is_set(source_rsc->flags, pcmk__rsc_updating_nodes)", taking false branch. |
1807 if (pcmk__is_set(source_rsc->flags, pcmk__rsc_updating_nodes)) {
1808 pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1809 log_id, source_rsc->id);
1810 return;
1811 }
1812 pcmk__set_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1813
|
(8) Event path: |
Condition "*nodes == NULL", taking true branch. |
1814 if (*nodes == NULL) {
1815 work = pcmk__copy_node_table(source_rsc->priv->allowed_nodes);
1816 target_rsc = source_rsc;
|
(9) Event path: |
Falling through to end of if statement. |
1817 } else {
1818 const bool pos = pcmk__is_set(flags, pcmk__coloc_select_nonnegative);
1819
1820 pcmk__rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1821 log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1822 work = pcmk__copy_node_table(*nodes);
1823 add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1824 factor, pos);
1825 }
1826
|
(10) Event path: |
Condition "work == NULL", taking false branch. |
1827 if (work == NULL) {
1828 pcmk__clear_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1829 return;
1830 }
1831
|
(11) Event path: |
Condition "pcmk__any_node_available(work)", taking true branch. |
1832 if (pcmk__any_node_available(work)) {
1833 GList *colocations = NULL;
1834
|
(12) Event path: |
Condition "pcmk__is_set(flags, pcmk__coloc_select_this_with)", taking true branch. |
1835 if (pcmk__is_set(flags, pcmk__coloc_select_this_with)) {
1836 colocations = pcmk__this_with_colocations(source_rsc);
|
(13) Event path: |
Switch case default. |
|
(14) Event path: |
Condition "trace_tag_cs == NULL", taking true branch. |
|
(15) Event path: |
Condition "crm_is_callsite_active(trace_tag_cs, _level, converted_tag)", taking false branch. |
1837 pcmk__rsc_trace(source_rsc,
1838 "Checking additional %d optional '%s with' "
1839 "constraints",
1840 g_list_length(colocations), source_rsc->id);
|
(16) Event path: |
Falling through to end of if statement. |
1841 } else {
1842 colocations = pcmk__with_this_colocations(source_rsc);
1843 pcmk__rsc_trace(source_rsc,
1844 "Checking additional %d optional 'with %s' "
1845 "constraints",
1846 g_list_length(colocations), source_rsc->id);
1847 }
1848 flags |= pcmk__coloc_select_active;
1849
|
(17) Event path: |
Condition "iter != NULL", taking true branch. |
|
(24) Event path: |
Condition "iter != NULL", taking false branch. |
1850 for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1851 pcmk__colocation_t *constraint = iter->data;
1852
1853 pcmk_resource_t *other = NULL;
1854 float other_factor = factor * constraint->score
1855 / (float) PCMK_SCORE_INFINITY;
1856
|
(18) Event path: |
Condition "pcmk__is_set(flags, pcmk__coloc_select_this_with)", taking true branch. |
1857 if (pcmk__is_set(flags, pcmk__coloc_select_this_with)) {
1858 other = constraint->primary;
|
(19) Event path: |
Falling through to end of if statement. |
1859 } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1860 continue;
1861 } else {
1862 other = constraint->dependent;
1863 }
1864
|
(20) Event path: |
Switch case default. |
|
(21) Event path: |
Condition "trace_tag_cs == NULL", taking true branch. |
|
(22) Event path: |
Condition "crm_is_callsite_active(trace_tag_cs, _level, converted_tag)", taking false branch. |
1865 pcmk__rsc_trace(source_rsc,
1866 "Optionally merging score of '%s' constraint "
1867 "(%s with %s)",
1868 constraint->id, constraint->dependent->id,
1869 constraint->primary->id);
1870 other->priv->cmds->add_colocated_node_scores(other, target_rsc,
1871 log_id, &work,
1872 constraint,
1873 other_factor, flags);
1874 pe__show_node_scores(true, NULL, log_id, work,
1875 source_rsc->priv->scheduler);
|
(23) Event path: |
Jumping back to the beginning of the loop. |
1876 }
1877 g_list_free(colocations);
1878
|
(25) Event path: |
Falling through to end of if statement. |
1879 } else if (pcmk__is_set(flags, pcmk__coloc_select_active)) {
1880 pcmk__rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1881 log_id, source_rsc->id);
1882 g_hash_table_destroy(work);
1883 pcmk__clear_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1884 return;
1885 }
1886
1887
|
(26) Event path: |
Condition "pcmk__is_set(flags, pcmk__coloc_select_nonnegative)", taking true branch. |
1888 if (pcmk__is_set(flags, pcmk__coloc_select_nonnegative)) {
1889 pcmk_node_t *node = NULL;
1890 GHashTableIter iter;
1891
1892 g_hash_table_iter_init(&iter, work);
|
(27) Event path: |
Condition "g_hash_table_iter_next(&iter, NULL, (void **)&node)", taking true branch. |
|
(30) Event path: |
Condition "g_hash_table_iter_next(&iter, NULL, (void **)&node)", taking false branch. |
1893 while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
|
(28) Event path: |
Condition "node->assign->score == -100000000 /* 1000000 * -100 */", taking true branch. |
1894 if (node->assign->score == INFINITY_HACK) {
1895 node->assign->score = 1;
1896 }
|
(29) Event path: |
Jumping back to the beginning of the loop. |
1897 }
1898 }
1899
|
CID (unavailable; MK=08825f3bb08c5728117ad1a1c2dcffc8) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(31) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(32) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
1900 g_clear_pointer(nodes, g_hash_table_destroy);
1901 *nodes = work;
1902
1903 pcmk__clear_rsc_flags(source_rsc, pcmk__rsc_updating_nodes);
1904 }
1905
1906 /*!
1907 * \internal
1908 * \brief Apply a "with this" colocation to a resource's allowed node scores
1909 *
1910 * \param[in,out] data Colocation to apply
1911 * \param[in,out] user_data Resource being assigned
1912 */
1913 void
1914 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
1915 {
1916 pcmk__colocation_t *colocation = data;
1917 pcmk_resource_t *primary = user_data;
1918
1919 pcmk_resource_t *dependent = colocation->dependent;
1920 const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
1921 uint32_t flags = pcmk__coloc_select_active;
1922
1923 if (!pcmk__colocation_has_influence(colocation, NULL)) {
1924 return;
1925 }
1926 if (pcmk__is_clone(primary)) {
1927 flags |= pcmk__coloc_select_nonnegative;
1928 }
1929 pcmk__rsc_trace(primary,
1930 "%s: Incorporating attenuated %s assignment scores due "
1931 "to colocation %s",
1932 primary->id, dependent->id, colocation->id);
1933 dependent->priv->cmds->add_colocated_node_scores(dependent, primary,
1934 dependent->id,
1935 &(primary->priv->allowed_nodes),
1936 colocation, factor, flags);
1937 }
1938
1939 /*!
1940 * \internal
1941 * \brief Exclude nodes from a dependent's node table if not in a given list
1942 *
1943 * Given a dependent resource in a colocation and a list of nodes where the
1944 * primary resource will run, set a node's score to \c -INFINITY in the
1945 * dependent's node table if not found in the primary nodes list.
1946 *
1947 * \param[in,out] dependent Dependent resource
1948 * \param[in] primary Primary resource (for logging only)
1949 * \param[in] colocation Colocation constraint (for logging only)
1950 * \param[in] primary_nodes List of nodes where the primary will have
1951 * unblocked instances in a suitable role
1952 * \param[in] merge_scores If \c true and a node is found in both \p table
1953 * and \p list, add the node's score in \p list to
1954 * the node's score in \p table
1955 */
1956 void
1957 pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent,
1958 const pcmk_resource_t *primary,
1959 const pcmk__colocation_t *colocation,
1960 const GList *primary_nodes, bool merge_scores)
1961 {
1962 GHashTableIter iter;
1963 pcmk_node_t *dependent_node = NULL;
1964
1965 pcmk__assert((dependent != NULL) && (primary != NULL)
1966 && (colocation != NULL));
1967
1968 g_hash_table_iter_init(&iter, dependent->priv->allowed_nodes);
1969 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1970 const pcmk_node_t *primary_node = NULL;
1971
1972 primary_node = pe_find_node_id(primary_nodes,
1973 dependent_node->priv->id);
1974 if (primary_node == NULL) {
1975 dependent_node->assign->score = -PCMK_SCORE_INFINITY;
1976 pcmk__rsc_trace(dependent,
1977 "Banning %s from %s (no primary instance) for %s",
1978 dependent->id, pcmk__node_name(dependent_node),
1979 colocation->id);
1980
1981 } else if (merge_scores) {
1982 dependent_node->assign->score =
1983 pcmk__add_scores(dependent_node->assign->score,
1984 primary_node->assign->score);
1985 pcmk__rsc_trace(dependent,
1986 "Added %s's score %s to %s's score for %s (now %d) "
1987 "for colocation %s",
1988 primary->id,
1989 pcmk_readable_score(primary_node->assign->score),
1990 dependent->id, pcmk__node_name(dependent_node),
1991 dependent_node->assign->score, colocation->id);
1992 }
1993 }
1994 }
1995
1996 /*!
1997 * \internal
1998 * \brief Get all colocations affecting a resource as the primary
1999 *
2000 * \param[in] rsc Resource to get colocations for
2001 *
2002 * \return Newly allocated list of colocations affecting \p rsc as primary
2003 *
2004 * \note This is a convenience wrapper for the with_this_colocations() method.
2005 */
2006 GList *
2007 pcmk__with_this_colocations(const pcmk_resource_t *rsc)
2008 {
2009 GList *list = NULL;
2010
2011 rsc->priv->cmds->with_this_colocations(rsc, rsc, &list);
2012 return list;
2013 }
2014
2015 /*!
2016 * \internal
2017 * \brief Get all colocations affecting a resource as the dependent
2018 *
2019 * \param[in] rsc Resource to get colocations for
2020 *
2021 * \return Newly allocated list of colocations affecting \p rsc as dependent
2022 *
2023 * \note This is a convenience wrapper for the this_with_colocations() method.
2024 */
2025 GList *
2026 pcmk__this_with_colocations(const pcmk_resource_t *rsc)
2027 {
2028 GList *list = NULL;
2029
2030 rsc->priv->cmds->this_with_colocations(rsc, rsc, &list);
2031 return list;
2032 }
2033