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