1 /*
2 * Copyright 2014-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 <stdlib.h>
14 #include <string.h>
15 #include <crm/common/xml.h>
16 #include <pacemaker-internal.h>
17
18 #include "libpacemaker_private.h"
19
20 // Resource assignment methods by resource variant
21 static pcmk__assignment_methods_t assignment_methods[] = {
22 {
23 pcmk__primitive_assign,
24 pcmk__primitive_create_actions,
25 pcmk__probe_rsc_on_node,
26 pcmk__primitive_internal_constraints,
27 pcmk__primitive_apply_coloc_score,
28 pcmk__colocated_resources,
29 pcmk__with_primitive_colocations,
30 pcmk__primitive_with_colocations,
31 pcmk__add_colocated_node_scores,
32 pcmk__apply_location,
33 pcmk__primitive_action_flags,
34 pcmk__update_ordered_actions,
35 pcmk__output_resource_actions,
36 pcmk__add_rsc_actions_to_graph,
37 pcmk__primitive_add_graph_meta,
38 pcmk__primitive_add_utilization,
39 pcmk__primitive_shutdown_lock,
40 },
41 {
42 pcmk__group_assign,
43 pcmk__group_create_actions,
44 pcmk__probe_rsc_on_node,
45 pcmk__group_internal_constraints,
46 pcmk__group_apply_coloc_score,
47 pcmk__group_colocated_resources,
48 pcmk__with_group_colocations,
49 pcmk__group_with_colocations,
50 pcmk__group_add_colocated_node_scores,
51 pcmk__group_apply_location,
52 pcmk__group_action_flags,
53 pcmk__group_update_ordered_actions,
54 pcmk__output_resource_actions,
55 pcmk__add_rsc_actions_to_graph,
56 pcmk__noop_add_graph_meta,
57 pcmk__group_add_utilization,
58 pcmk__group_shutdown_lock,
59 },
60 {
61 pcmk__clone_assign,
62 pcmk__clone_create_actions,
63 pcmk__clone_create_probe,
64 pcmk__clone_internal_constraints,
65 pcmk__clone_apply_coloc_score,
66 pcmk__colocated_resources,
67 pcmk__with_clone_colocations,
68 pcmk__clone_with_colocations,
69 pcmk__add_colocated_node_scores,
70 pcmk__clone_apply_location,
71 pcmk__clone_action_flags,
72 pcmk__instance_update_ordered_actions,
73 pcmk__output_resource_actions,
74 pcmk__clone_add_actions_to_graph,
75 pcmk__clone_add_graph_meta,
76 pcmk__clone_add_utilization,
77 pcmk__clone_shutdown_lock,
78 },
79 {
80 pcmk__bundle_assign,
81 pcmk__bundle_create_actions,
82 pcmk__bundle_create_probe,
83 pcmk__bundle_internal_constraints,
84 pcmk__bundle_apply_coloc_score,
85 pcmk__colocated_resources,
86 pcmk__with_bundle_colocations,
87 pcmk__bundle_with_colocations,
88 pcmk__add_colocated_node_scores,
89 pcmk__bundle_apply_location,
90 pcmk__bundle_action_flags,
91 pcmk__instance_update_ordered_actions,
92 pcmk__output_bundle_actions,
93 pcmk__bundle_add_actions_to_graph,
94 pcmk__noop_add_graph_meta,
95 pcmk__bundle_add_utilization,
96 pcmk__bundle_shutdown_lock,
97 }
98 };
99
100 /*!
101 * \internal
102 * \brief Check whether a resource's agent standard, provider, or type changed
103 *
104 * \param[in,out] rsc Resource to check
105 * \param[in,out] node Node needing unfencing if agent changed
106 * \param[in] rsc_entry XML with previously known agent information
107 * \param[in] active_on_node Whether \p rsc is active on \p node
108 *
109 * \return true if agent for \p rsc changed, otherwise false
110 */
111 bool
112 pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node,
113 const xmlNode *rsc_entry, bool active_on_node)
114 {
115 bool changed = false;
116 const char *attr_list[] = {
117 PCMK_XA_TYPE,
118 PCMK_XA_CLASS,
119 PCMK_XA_PROVIDER,
120 };
121
122 for (int i = 0; i < PCMK__NELEM(attr_list); i++) {
123 const char *value = pcmk__xe_get(rsc->priv->xml, attr_list[i]);
124 const char *old_value = pcmk__xe_get(rsc_entry, attr_list[i]);
125
126 if (!pcmk__str_eq(value, old_value, pcmk__str_none)) {
127 changed = true;
128 trigger_unfencing(rsc, node, "Device definition changed", NULL,
129 rsc->priv->scheduler);
130 if (active_on_node) {
131 pcmk__notice("Forcing restart of %s on %s because %s changed "
132 "from '%s' to '%s'",
133 rsc->id, pcmk__node_name(node), attr_list[i],
134 pcmk__s(old_value, ""), pcmk__s(value, ""));
135 }
136 }
137 }
138 if (changed && active_on_node) {
139 // Make sure the resource is restarted
140 custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
141 rsc->priv->scheduler);
142 pcmk__set_rsc_flags(rsc, pcmk__rsc_start_pending);
143 }
144 return changed;
145 }
146
147 /*!
148 * \internal
149 * \brief Add resource (and any matching children) to list if it matches ID
150 *
151 * \param[in] result List to add resource to
152 * \param[in] rsc Resource to check
153 * \param[in] id ID to match
154 *
155 * \return (Possibly new) head of list
156 */
157 static GList *
158 add_rsc_if_matching(GList *result, pcmk_resource_t *rsc, const char *id)
159 {
160 if (pcmk__str_eq(id, rsc->id, pcmk__str_none)
161 || pcmk__str_eq(id, rsc->priv->history_id, pcmk__str_none)) {
162 result = g_list_prepend(result, rsc);
163 }
164
165 for (GList *iter = rsc->priv->children;
166 iter != NULL; iter = iter->next) {
167
168 pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
169
170 result = add_rsc_if_matching(result, child, id);
171 }
172 return result;
173 }
174
175 /*!
176 * \internal
177 * \brief Find all resources matching a given ID by either ID or clone name
178 *
179 * \param[in] id Resource ID to check
180 * \param[in] scheduler Scheduler data
181 *
182 * \return List of all resources that match \p id
183 * \note The caller is responsible for freeing the return value with
184 * g_list_free().
185 */
186 GList *
187 pcmk__rscs_matching_id(const char *id, const pcmk_scheduler_t *scheduler)
188 {
189 GList *result = NULL;
190
191 CRM_CHECK((id != NULL) && (scheduler != NULL), return NULL);
192
193 for (GList *iter = scheduler->priv->resources;
194 iter != NULL; iter = iter->next) {
195
196 result = add_rsc_if_matching(result, (pcmk_resource_t *) iter->data,
197 id);
198 }
199 return result;
200 }
201
202 /*!
203 * \internal
204 * \brief Set the variant-appropriate assignment methods for a resource
205 *
206 * \param[in,out] data Resource to set assignment methods for
207 * \param[in] user_data Ignored
208 */
209 static void
210 set_assignment_methods_for_rsc(gpointer data, gpointer user_data)
211 {
212 pcmk_resource_t *rsc = data;
213
214 rsc->priv->cmds = &assignment_methods[rsc->priv->variant];
215 g_list_foreach(rsc->priv->children, set_assignment_methods_for_rsc,
216 NULL);
217 }
218
219 /*!
220 * \internal
221 * \brief Set the variant-appropriate assignment methods for all resources
222 *
223 * \param[in,out] scheduler Scheduler data
224 */
225 void
226 pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler)
227 {
228 g_list_foreach(scheduler->priv->resources, set_assignment_methods_for_rsc,
229 NULL);
230 }
231
232 /*!
233 * \internal
234 * \brief Wrapper for colocated_resources() method for readability
235 *
236 * \param[in] rsc Resource to add to colocated list
237 * \param[in] orig_rsc Resource originally requested
238 * \param[in,out] list Pointer to list to add to
239 *
240 * \return (Possibly new) head of list
241 */
242 static inline void
243 add_colocated_resources(const pcmk_resource_t *rsc,
244 const pcmk_resource_t *orig_rsc, GList **list)
245 {
246 *list = rsc->priv->cmds->colocated_resources(rsc, orig_rsc, *list);
247 }
248
249 // Shared implementation of pcmk__assignment_methods_t:colocated_resources()
250 GList *
251 pcmk__colocated_resources(const pcmk_resource_t *rsc,
252 const pcmk_resource_t *orig_rsc,
253 GList *colocated_rscs)
254 {
255 const GList *iter = NULL;
256 GList *colocations = NULL;
257
258 if (orig_rsc == NULL) {
259 orig_rsc = rsc;
260 }
261
262 if ((rsc == NULL) || (g_list_find(colocated_rscs, rsc) != NULL)) {
263 return colocated_rscs;
264 }
265
266 pcmk__rsc_trace(orig_rsc, "%s is in colocation chain with %s",
267 rsc->id, orig_rsc->id);
268 colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
269
270 // Follow colocations where this resource is the dependent resource
271 colocations = pcmk__this_with_colocations(rsc);
272 for (iter = colocations; iter != NULL; iter = iter->next) {
273 const pcmk__colocation_t *constraint = iter->data;
274 const pcmk_resource_t *primary = constraint->primary;
275
276 if (primary == orig_rsc) {
277 continue; // Break colocation loop
278 }
279
280 if ((constraint->score == PCMK_SCORE_INFINITY) &&
281 (pcmk__colocation_affects(rsc, primary, constraint,
282 true) == pcmk__coloc_affects_location)) {
283 add_colocated_resources(primary, orig_rsc, &colocated_rscs);
284 }
285 }
286 g_list_free(colocations);
287
288 // Follow colocations where this resource is the primary resource
289 colocations = pcmk__with_this_colocations(rsc);
290 for (iter = colocations; iter != NULL; iter = iter->next) {
291 const pcmk__colocation_t *constraint = iter->data;
292 const pcmk_resource_t *dependent = constraint->dependent;
293
294 if (dependent == orig_rsc) {
295 continue; // Break colocation loop
296 }
297
298 if (pcmk__is_clone(rsc) && !pcmk__is_clone(dependent)) {
299 continue; // We can't be sure whether dependent will be colocated
300 }
301
302 if ((constraint->score == PCMK_SCORE_INFINITY) &&
303 (pcmk__colocation_affects(dependent, rsc, constraint,
304 true) == pcmk__coloc_affects_location)) {
305 add_colocated_resources(dependent, orig_rsc, &colocated_rscs);
306 }
307 }
308 g_list_free(colocations);
309
310 return colocated_rscs;
311 }
312
313 // No-op function for variants that don't need to implement add_graph_meta()
314 void
315 pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
316 {
317 }
318
319 /*!
320 * \internal
321 * \brief Output a summary of scheduled actions for a resource
322 *
323 * \param[in,out] rsc Resource to output actions for
324 */
325 void
326 pcmk__output_resource_actions(pcmk_resource_t *rsc)
327 {
328 pcmk_node_t *next = NULL;
329 pcmk_node_t *current = NULL;
330 pcmk__output_t *out = NULL;
331
332 pcmk__assert(rsc != NULL);
333
334 out = rsc->priv->scheduler->priv->out;
335 if (rsc->priv->children != NULL) {
336
337 for (GList *iter = rsc->priv->children;
338 iter != NULL; iter = iter->next) {
339
340 pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
341
342 child->priv->cmds->output_actions(child);
343 }
344 return;
345 }
346
347 next = rsc->priv->assigned_node;
348 if (rsc->priv->active_nodes != NULL) {
349 current = pcmk__current_node(rsc);
350 if (rsc->priv->orig_role == pcmk_role_stopped) {
351 /* This can occur when resources are being recovered because
352 * the current role can change in pcmk__primitive_create_actions()
353 */
354 rsc->priv->orig_role = pcmk_role_started;
355 }
356 }
357
358 if ((current == NULL) && pcmk__is_set(rsc->flags, pcmk__rsc_removed)) {
359 // Don't log stopped removed resources
360 return;
361 }
362
363 out->message(out, "rsc-action", rsc, current, next);
364 }
365
366 /*!
367 * \internal
368 * \brief Add a resource to a node's list of assigned resources
369 *
370 * \param[in,out] node Node to add resource to
371 * \param[in] rsc Resource to add
372 */
373 static inline void
374 add_assigned_resource(pcmk_node_t *node, pcmk_resource_t *rsc)
375 {
376 node->priv->assigned_resources =
377 g_list_prepend(node->priv->assigned_resources, rsc);
378 }
379
380 /*!
381 * \internal
382 * \brief Assign a specified resource (of any variant) to a node
383 *
384 * Assign a specified resource and its children (if any) to a specified node, if
385 * the node can run the resource (or unconditionally, if \p force is true). Mark
386 * the resources as no longer provisional.
387 *
388 * If a resource can't be assigned (or \p node is \c NULL), unassign any
389 * previous assignment. If \p stop_if_fail is \c true, set next role to stopped
390 * and update any existing actions scheduled for the resource.
391 *
392 * \param[in,out] rsc Resource to assign
393 * \param[in,out] node Node to assign \p rsc to
394 * \param[in] force If true, assign to \p node even if unavailable
395 * \param[in] stop_if_fail If \c true and either \p rsc can't be assigned
396 * or \p chosen is \c NULL, set next role to
397 * stopped and update existing actions (if \p rsc
398 * is not a primitive, this applies to its
399 * primitive descendants instead)
400 *
401 * \return \c true if the assignment of \p rsc changed, or \c false otherwise
402 *
403 * \note Assigning a resource to the NULL node using this function is different
404 * from calling pcmk__unassign_resource(), in that it may also update any
405 * actions created for the resource.
406 * \note The \c pcmk__assignment_methods_t:assign() method is preferred, unless
407 * a resource should be assigned to the \c NULL node or every resource in
408 * a tree should be assigned to the same node.
409 * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
410 * completely undo the assignment. A successful assignment can be either
411 * undone or left alone as final. A failed assignment has the same effect
412 * as calling pcmk__unassign_resource(); there are no side effects on
413 * roles or actions.
414 */
415 bool
416 pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force,
417 bool stop_if_fail)
418 {
419 bool changed = false;
420 pcmk_scheduler_t *scheduler = NULL;
421
422 pcmk__assert(rsc != NULL);
423 scheduler = rsc->priv->scheduler;
424
425 if (rsc->priv->children != NULL) {
426
427 for (GList *iter = rsc->priv->children;
428 iter != NULL; iter = iter->next) {
429
430 pcmk_resource_t *child_rsc = iter->data;
431
432 changed |= pcmk__assign_resource(child_rsc, node, force,
433 stop_if_fail);
434 }
435 return changed;
436 }
437
438 // Assigning a primitive
439
440 if (!force && (node != NULL)
441 && ((node->assign->score < 0)
442 // Allow graph to assume that guest node connections will come up
443 || (!pcmk__node_available(node, true, false)
444 && !pcmk__is_guest_or_bundle_node(node)))) {
445
446 pcmk__rsc_debug(rsc,
447 "All nodes for resource %s are unavailable, unclean or "
448 "shutting down (%s can%s run resources, with score %s)",
449 rsc->id, pcmk__node_name(node),
450 (pcmk__node_available(node, true, false)? "" : "not"),
451 pcmk_readable_score(node->assign->score));
452
453 if (stop_if_fail) {
454 pe__set_next_role(rsc, pcmk_role_stopped, "node availability");
455 }
456 node = NULL;
457 }
458
459 if (rsc->priv->assigned_node != NULL) {
460 changed = !pcmk__same_node(rsc->priv->assigned_node, node);
461 } else {
462 changed = (node != NULL);
463 }
464 pcmk__unassign_resource(rsc);
465 pcmk__clear_rsc_flags(rsc, pcmk__rsc_unassigned);
466
467 if (node == NULL) {
468 char *rc_stopped = NULL;
469
470 pcmk__rsc_debug(rsc, "Could not assign %s to a node", rsc->id);
471
472 if (!stop_if_fail) {
473 return changed;
474 }
475 pe__set_next_role(rsc, pcmk_role_stopped, "unable to assign");
476
477 for (GList *iter = rsc->priv->actions;
478 iter != NULL; iter = iter->next) {
479
480 pcmk_action_t *op = (pcmk_action_t *) iter->data;
481
482 pcmk__rsc_debug(rsc, "Updating %s for %s assignment failure",
483 op->uuid, rsc->id);
484
485 if (pcmk__str_eq(op->task, PCMK_ACTION_STOP, pcmk__str_none)) {
486 pcmk__clear_action_flags(op, pcmk__action_optional);
487
488 } else if (pcmk__str_eq(op->task, PCMK_ACTION_START,
489 pcmk__str_none)) {
490 pcmk__clear_action_flags(op, pcmk__action_runnable);
491
492 } else {
493 // Cancel recurring actions, unless for stopped state
494 const char *interval_ms_s = NULL;
495 const char *target_rc_s = NULL;
496
497 interval_ms_s = g_hash_table_lookup(op->meta,
498 PCMK_META_INTERVAL);
499 target_rc_s = g_hash_table_lookup(op->meta,
500 PCMK__META_OP_TARGET_RC);
501 if (rc_stopped == NULL) {
502 rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
503 }
504
505 if (!pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)
506 && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) {
507
508 pcmk__clear_action_flags(op, pcmk__action_runnable);
509 }
510 }
511 }
512 free(rc_stopped);
513 return changed;
514 }
515
516 pcmk__rsc_debug(rsc, "Assigning %s to %s", rsc->id, pcmk__node_name(node));
517 rsc->priv->assigned_node = pe__copy_node(node);
518
519 add_assigned_resource(node, rsc);
520 node->priv->num_resources++;
521 node->assign->count++;
522 pcmk__consume_node_capacity(node->priv->utilization, rsc);
523
524 if (pcmk__is_set(scheduler->flags, pcmk__sched_show_utilization)) {
525 pcmk__output_t *out = scheduler->priv->out;
526
527 out->message(out, "resource-util", rsc, node, __func__);
528 }
529 return changed;
530 }
531
532 /*!
533 * \internal
534 * \brief Remove any node assignment from a specified resource and its children
535 *
536 * If a specified resource has been assigned to a node, remove that assignment
537 * and mark the resource as provisional again.
538 *
539 * \param[in,out] rsc Resource to unassign
540 *
541 * \note This function is called recursively on \p rsc and its children.
542 */
543 void
544 pcmk__unassign_resource(pcmk_resource_t *rsc)
545 {
546 pcmk_node_t *old = rsc->priv->assigned_node;
547
548 if (old == NULL) {
549 pcmk__info("Unassigning %s", rsc->id);
550 } else {
551 pcmk__info("Unassigning %s from %s", rsc->id, pcmk__node_name(old));
552 }
553
554 pcmk__set_rsc_flags(rsc, pcmk__rsc_unassigned);
555
556 if (rsc->priv->children == NULL) {
557 if (old == NULL) {
558 return;
559 }
560 rsc->priv->assigned_node = NULL;
561
562 /* We're going to free the pcmk_node_t copy, but its priv member is
563 * shared and will remain, so update that appropriately first.
564 */
565 old->priv->assigned_resources =
566 g_list_remove(old->priv->assigned_resources, rsc);
567 old->priv->num_resources--;
568 pcmk__release_node_capacity(old->priv->utilization, rsc);
569 pcmk__free_node_copy(old);
570 return;
571 }
572
573 for (GList *iter = rsc->priv->children;
574 iter != NULL; iter = iter->next) {
575
576 pcmk__unassign_resource((pcmk_resource_t *) iter->data);
577 }
578 }
579
580 /*!
581 * \internal
582 * \brief Check whether a resource has reached its migration threshold on a node
583 *
584 * \param[in,out] rsc Resource to check
585 * \param[in] node Node to check
586 * \param[out] failed If threshold has been reached, this will be set to
587 * resource that failed (possibly a parent of \p rsc)
588 *
589 * \return true if the migration threshold has been reached, false otherwise
590 */
591 bool
592 pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node,
593 pcmk_resource_t **failed)
594 {
595 int fail_count, remaining_tries;
596 pcmk_resource_t *rsc_to_ban = rsc;
597
598 // Migration threshold of 0 means never force away
599 if (rsc->priv->ban_after_failures == 0) {
600 return false;
601 }
602
603 // If we're ignoring failures, also ignore the migration threshold
604 if (pcmk__is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
605 return false;
606 }
607
608 // If there are no failures, there's no need to force away
609 fail_count = pe_get_failcount(node, rsc, NULL,
610 pcmk__fc_effective|pcmk__fc_launched, NULL);
611 if (fail_count <= 0) {
612 return false;
613 }
614
615 // If failed resource is anonymous clone instance, we'll force clone away
616 if (!pcmk__is_set(rsc->flags, pcmk__rsc_unique)) {
617 rsc_to_ban = uber_parent(rsc);
618 }
619
620 // How many more times recovery will be tried on this node
621 remaining_tries = rsc->priv->ban_after_failures - fail_count;
622
623 if (remaining_tries <= 0) {
624 pcmk__sched_warn(rsc->priv->scheduler,
625 "%s cannot run on %s due to reaching migration "
626 "threshold (clean up resource to allow again) "
627 QB_XS " failures=%d "
628 PCMK_META_MIGRATION_THRESHOLD "=%d",
629 rsc_to_ban->id, pcmk__node_name(node), fail_count,
630 rsc->priv->ban_after_failures);
631 if (failed != NULL) {
632 *failed = rsc_to_ban;
633 }
634 return true;
635 }
636
637 pcmk__info("%s can fail %d more time%s on %s before reaching migration "
638 "threshold (%d)",
639 rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries),
640 pcmk__node_name(node), rsc->priv->ban_after_failures);
641 return false;
642 }
643
644 /*!
645 * \internal
646 * \brief Get a node's score
647 *
648 * \param[in] node Node with ID to check
649 * \param[in] nodes List of nodes to look for \p node score in
650 *
651 * \return Node's score, or -INFINITY if not found
652 */
653 static int
654 get_node_score(const pcmk_node_t *node, GHashTable *nodes)
655 {
656 pcmk_node_t *found_node = NULL;
657
658 if ((node != NULL) && (nodes != NULL)) {
659 found_node = g_hash_table_lookup(nodes, node->priv->id);
660 }
661 if (found_node == NULL) {
662 return -PCMK_SCORE_INFINITY;
663 }
664 return found_node->assign->score;
665 }
666
667 /*!
668 * \internal
669 * \brief Compare two resources according to which should be assigned first
670 *
671 * \param[in] a First resource to compare
672 * \param[in] b Second resource to compare
673 * \param[in] data Sorted list of all nodes in cluster
674 *
675 * \return -1 if \p a should be assigned before \b, 0 if they are equal,
676 * or +1 if \p a should be assigned after \b
677 */
678 static gint
679 cmp_resources(gconstpointer a, gconstpointer b, gpointer data)
680 {
681 /* GLib insists that this function require gconstpointer arguments, but we
682 * make a small, temporary change to each argument (setting the
683 * pe_rsc_merging flag) during comparison
684 */
685 pcmk_resource_t *resource1 = (pcmk_resource_t *) a;
686 pcmk_resource_t *resource2 = (pcmk_resource_t *) b;
687 const GList *nodes = data;
688
689 int rc = 0;
690 int r1_score = -PCMK_SCORE_INFINITY;
691 int r2_score = -PCMK_SCORE_INFINITY;
692 pcmk_node_t *r1_node = NULL;
693 pcmk_node_t *r2_node = NULL;
694 GHashTable *r1_nodes = NULL;
695 GHashTable *r2_nodes = NULL;
696 const char *reason = NULL;
697
698 // Resources with highest priority should be assigned first
699 reason = "priority";
700 r1_score = resource1->priv->priority;
701 r2_score = resource2->priv->priority;
|
(1) Event path: |
Condition "r1_score > r2_score", taking true branch. |
702 if (r1_score > r2_score) {
703 rc = -1;
|
(2) Event path: |
Jumping to label "done". |
704 goto done;
705 }
706 if (r1_score < r2_score) {
707 rc = 1;
708 goto done;
709 }
710
711 // We need nodes to make any other useful comparisons
712 reason = "no node list";
713 if (nodes == NULL) {
714 goto done;
715 }
716
717 // Calculate and log node scores
718 resource1->priv->cmds->add_colocated_node_scores(resource1, NULL,
719 resource1->id,
720 &r1_nodes, NULL, 1,
721 pcmk__coloc_select_this_with);
722 resource2->priv->cmds->add_colocated_node_scores(resource2, NULL,
723 resource2->id,
724 &r2_nodes, NULL, 1,
725 pcmk__coloc_select_this_with);
726 pe__show_node_scores(true, NULL, resource1->id, r1_nodes,
727 resource1->priv->scheduler);
728 pe__show_node_scores(true, NULL, resource2->id, r2_nodes,
729 resource2->priv->scheduler);
730
731 // The resource with highest score on its current node goes first
732 reason = "current location";
733 if (resource1->priv->active_nodes != NULL) {
734 r1_node = pcmk__current_node(resource1);
735 }
736 if (resource2->priv->active_nodes != NULL) {
737 r2_node = pcmk__current_node(resource2);
738 }
739 r1_score = get_node_score(r1_node, r1_nodes);
740 r2_score = get_node_score(r2_node, r2_nodes);
741 if (r1_score > r2_score) {
742 rc = -1;
743 goto done;
744 }
745 if (r1_score < r2_score) {
746 rc = 1;
747 goto done;
748 }
749
750 // Otherwise a higher score on any node will do
751 reason = "score";
752 for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
753 const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
754
755 r1_score = get_node_score(node, r1_nodes);
756 r2_score = get_node_score(node, r2_nodes);
757 if (r1_score > r2_score) {
758 rc = -1;
759 goto done;
760 }
761 if (r1_score < r2_score) {
762 rc = 1;
763 goto done;
764 }
765 }
766
767 done:
|
(3) Event path: |
Switch case default. |
|
(4) Event path: |
Condition "trace_cs == NULL", taking true branch. |
|
(5) Event path: |
Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch. |
|
(6) Event path: |
Breaking from switch. |
768 pcmk__trace("%s (%d)%s%s %c %s (%d)%s%s: %s",
769 resource1->id, r1_score,
770 ((r1_node == NULL)? "" : " on "),
771 ((r1_node == NULL)? "" : r1_node->priv->id),
772 ((rc < 0)? '>' : ((rc > 0)? '<' : '=')),
773 resource2->id, r2_score,
774 ((r2_node == NULL)? "" : " on "),
775 ((r2_node == NULL)? "" : r2_node->priv->id),
776 reason);
777
|
(7) Event path: |
Condition "_p", taking true branch. |
778 g_clear_pointer(&r1_nodes, g_hash_table_destroy);
|
CID (unavailable; MK=d3a8f7b05320d43983ee8df22f460c27) (#2 of 2): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(8) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(9) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
779 g_clear_pointer(&r2_nodes, g_hash_table_destroy);
780 return rc;
781 }
782
783 /*!
784 * \internal
785 * \brief Sort resources in the order they should be assigned to nodes
786 *
787 * \param[in,out] scheduler Scheduler data
788 */
789 void
790 pcmk__sort_resources(pcmk_scheduler_t *scheduler)
791 {
792 GList *nodes = g_list_copy(scheduler->nodes);
793
794 nodes = pcmk__sort_nodes(nodes, NULL);
795 scheduler->priv->resources =
796 g_list_sort_with_data(scheduler->priv->resources, cmp_resources, nodes);
797 g_list_free(nodes);
798 }
799