pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_sched_instances.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10/* This file is intended for code usable with both clone instances and bundle
11 * replica containers.
12 */
13
14#include <crm_internal.h>
15#include <crm/common/xml.h>
16#include <pacemaker-internal.h>
18
29static bool
30can_run_instance(const pcmk_resource_t *instance, const pcmk_node_t *node,
31 int max_per_node)
32{
33 pcmk_node_t *allowed_node = NULL;
34
35 if (pcmk_is_set(instance->flags, pcmk__rsc_removed)) {
36 pcmk__rsc_trace(instance, "%s cannot run on %s: orphaned",
37 instance->id, pcmk__node_name(node));
38 return false;
39 }
40
41 if (!pcmk__node_available(node, false, false)) {
42 pcmk__rsc_trace(instance,
43 "%s cannot run on %s: node cannot run resources",
44 instance->id, pcmk__node_name(node));
45 return false;
46 }
47
48 allowed_node = pcmk__top_allowed_node(instance, node);
49 if (allowed_node == NULL) {
50 crm_warn("%s cannot run on %s: node not allowed",
51 instance->id, pcmk__node_name(node));
52 return false;
53 }
54
55 if (allowed_node->assign->score < 0) {
56 pcmk__rsc_trace(instance,
57 "%s cannot run on %s: parent score is %s there",
58 instance->id, pcmk__node_name(node),
59 pcmk_readable_score(allowed_node->assign->score));
60 return false;
61 }
62
63 if (allowed_node->assign->count >= max_per_node) {
64 pcmk__rsc_trace(instance,
65 "%s cannot run on %s: node already has %d instance%s",
66 instance->id, pcmk__node_name(node), max_per_node,
67 pcmk__plural_s(max_per_node));
68 return false;
69 }
70
71 pcmk__rsc_trace(instance, "%s can run on %s (%d already running)",
72 instance->id, pcmk__node_name(node),
73 allowed_node->assign->count);
74 return true;
75}
76
84static void
85ban_unavailable_allowed_nodes(pcmk_resource_t *instance, int max_per_node)
86{
87 if (instance->priv->allowed_nodes != NULL) {
88 GHashTableIter iter;
89 pcmk_node_t *node = NULL;
90
91 g_hash_table_iter_init(&iter, instance->priv->allowed_nodes);
92 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
93 if (!can_run_instance(instance, node, max_per_node)) {
94 pcmk__rsc_trace(instance, "Banning %s from unavailable node %s",
95 instance->id, pcmk__node_name(node));
96 node->assign->score = -PCMK_SCORE_INFINITY;
97
98 for (GList *child_iter = instance->priv->children;
99 child_iter != NULL; child_iter = child_iter->next) {
100
101 pcmk_resource_t *child = child_iter->data;
102 pcmk_node_t *child_node = NULL;
103
104 child_node =
105 g_hash_table_lookup(child->priv->allowed_nodes,
106 node->priv->id);
107 if (child_node != NULL) {
108 pcmk__rsc_trace(instance,
109 "Banning %s child %s "
110 "from unavailable node %s",
111 instance->id, child->id,
112 pcmk__node_name(node));
113 child_node->assign->score = -PCMK_SCORE_INFINITY;
114 }
115 }
116 }
117 }
118 }
119}
120
131static GHashTable *
132new_node_table(pcmk_node_t *node)
133{
134 GHashTable *table = pcmk__strkey_table(NULL, pcmk__free_node_copy);
135
136 node = pe__copy_node(node);
137 g_hash_table_insert(table, (gpointer) node->priv->id, node);
138 return table;
139}
140
148static void
149apply_parent_colocations(const pcmk_resource_t *rsc, GHashTable **nodes)
150{
151 GList *colocations = pcmk__this_with_colocations(rsc);
152
153 for (const GList *iter = colocations; iter != NULL; iter = iter->next) {
154 const pcmk__colocation_t *colocation = iter->data;
155 pcmk_resource_t *other = colocation->primary;
156 float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
157
158 other->priv->cmds->add_colocated_node_scores(other, rsc, rsc->id,
159 nodes, colocation, factor,
161 }
162 g_list_free(colocations);
163 colocations = pcmk__with_this_colocations(rsc);
164
165 for (const GList *iter = colocations; iter != NULL; iter = iter->next) {
166 const pcmk__colocation_t *colocation = iter->data;
167 pcmk_resource_t *other = colocation->dependent;
168 float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
169
170 if (!pcmk__colocation_has_influence(colocation, rsc)) {
171 continue;
172 }
173 other->priv->cmds->add_colocated_node_scores(other, rsc, rsc->id,
174 nodes, colocation, factor,
176 }
177 g_list_free(colocations);
178}
179
195static int
196cmp_instance_by_colocation(const pcmk_resource_t *instance1,
197 const pcmk_resource_t *instance2)
198{
199 int rc = 0;
200 pcmk_node_t *node1 = NULL;
201 pcmk_node_t *node2 = NULL;
202 pcmk_node_t *current_node1 = pcmk__current_node(instance1);
203 pcmk_node_t *current_node2 = pcmk__current_node(instance2);
204 GHashTable *colocated_scores1 = NULL;
205 GHashTable *colocated_scores2 = NULL;
206
207 pcmk__assert((instance1 != NULL) && (instance1->priv->parent != NULL)
208 && (instance2 != NULL) && (instance2->priv->parent != NULL)
209 && (current_node1 != NULL) && (current_node2 != NULL));
210
211 // Create node tables initialized with each node
212 colocated_scores1 = new_node_table(current_node1);
213 colocated_scores2 = new_node_table(current_node2);
214
215 // Apply parental colocations
216 apply_parent_colocations(instance1, &colocated_scores1);
217 apply_parent_colocations(instance2, &colocated_scores2);
218
219 // Find original nodes again, with scores updated for colocations
220 node1 = g_hash_table_lookup(colocated_scores1, current_node1->priv->id);
221 node2 = g_hash_table_lookup(colocated_scores2, current_node2->priv->id);
222
223 // Compare nodes by updated scores
224 if (node1->assign->score < node2->assign->score) {
225 crm_trace("Assign %s (%d on %s) after %s (%d on %s)",
226 instance1->id, node1->assign->score, pcmk__node_name(node1),
227 instance2->id, node2->assign->score, pcmk__node_name(node2));
228 rc = 1;
229
230 } else if (node1->assign->score > node2->assign->score) {
231 crm_trace("Assign %s (%d on %s) before %s (%d on %s)",
232 instance1->id, node1->assign->score, pcmk__node_name(node1),
233 instance2->id, node2->assign->score, pcmk__node_name(node2));
234 rc = -1;
235 }
236
237 g_hash_table_destroy(colocated_scores1);
238 g_hash_table_destroy(colocated_scores2);
239 return rc;
240}
241
250static bool
251did_fail(const pcmk_resource_t *rsc)
252{
254 return true;
255 }
256
257 for (GList *iter = rsc->priv->children;
258 iter != NULL; iter = iter->next) {
259
260 if (did_fail((const pcmk_resource_t *) iter->data)) {
261 return true;
262 }
263 }
264 return false;
265}
266
276static bool
277node_is_allowed(const pcmk_resource_t *rsc, pcmk_node_t **node)
278{
279 if (*node != NULL) {
280 pcmk_node_t *allowed = g_hash_table_lookup(rsc->priv->allowed_nodes,
281 (*node)->priv->id);
282
283 if ((allowed == NULL) || (allowed->assign->score < 0)) {
284 pcmk__rsc_trace(rsc, "%s: current location (%s) is unavailable",
285 rsc->id, pcmk__node_name(*node));
286 *node = NULL;
287 return false;
288 }
289 }
290 return true;
291}
292
304gint
305pcmk__cmp_instance_number(gconstpointer a, gconstpointer b)
306{
307 const pcmk_resource_t *instance1 = (const pcmk_resource_t *) a;
308 const pcmk_resource_t *instance2 = (const pcmk_resource_t *) b;
309 char *div1 = NULL;
310 char *div2 = NULL;
311
312 pcmk__assert((instance1 != NULL) && (instance2 != NULL));
313
314 // Clone numbers are after a colon, bundle numbers after a dash
315 div1 = strrchr(instance1->id, ':');
316 if (div1 == NULL) {
317 div1 = strrchr(instance1->id, '-');
318 }
319 div2 = strrchr(instance2->id, ':');
320 if (div2 == NULL) {
321 div2 = strrchr(instance2->id, '-');
322 }
323 pcmk__assert((div1 != NULL) && (div2 != NULL));
324
325 return (gint) (strtol(div1 + 1, NULL, 10) - strtol(div2 + 1, NULL, 10));
326}
327
353gint
354pcmk__cmp_instance(gconstpointer a, gconstpointer b)
355{
356 int rc = 0;
357 pcmk_node_t *node1 = NULL;
358 pcmk_node_t *node2 = NULL;
359 unsigned int nnodes1 = 0;
360 unsigned int nnodes2 = 0;
361
362 bool can1 = true;
363 bool can2 = true;
364
365 const pcmk_resource_t *instance1 = (const pcmk_resource_t *) a;
366 const pcmk_resource_t *instance2 = (const pcmk_resource_t *) b;
367
368 pcmk__assert((instance1 != NULL) && (instance2 != NULL));
369
370 node1 = instance1->priv->fns->active_node(instance1, &nnodes1, NULL);
371 node2 = instance2->priv->fns->active_node(instance2, &nnodes2, NULL);
372
373 /* If both instances are running and at least one is multiply
374 * active, prefer instance that's running on fewer nodes.
375 */
376 if ((nnodes1 > 0) && (nnodes2 > 0)) {
377 if (nnodes1 < nnodes2) {
378 crm_trace("Assign %s (active on %d) before %s (active on %d): "
379 "less multiply active",
380 instance1->id, nnodes1, instance2->id, nnodes2);
381 return -1;
382
383 } else if (nnodes1 > nnodes2) {
384 crm_trace("Assign %s (active on %d) after %s (active on %d): "
385 "more multiply active",
386 instance1->id, nnodes1, instance2->id, nnodes2);
387 return 1;
388 }
389 }
390
391 /* An instance that is either inactive or active on an allowed node is
392 * preferred over an instance that is active on a no-longer-allowed node.
393 */
394 can1 = node_is_allowed(instance1, &node1);
395 can2 = node_is_allowed(instance2, &node2);
396 if (can1 && !can2) {
397 crm_trace("Assign %s before %s: not active on a disallowed node",
398 instance1->id, instance2->id);
399 return -1;
400
401 } else if (!can1 && can2) {
402 crm_trace("Assign %s after %s: active on a disallowed node",
403 instance1->id, instance2->id);
404 return 1;
405 }
406
407 // Prefer instance with higher configured priority
408 if (instance1->priv->priority > instance2->priv->priority) {
409 crm_trace("Assign %s before %s: priority (%d > %d)",
410 instance1->id, instance2->id,
411 instance1->priv->priority, instance2->priv->priority);
412 return -1;
413
414 } else if (instance1->priv->priority < instance2->priv->priority) {
415 crm_trace("Assign %s after %s: priority (%d < %d)",
416 instance1->id, instance2->id,
417 instance1->priv->priority, instance2->priv->priority);
418 return 1;
419 }
420
421 // Prefer active instance
422 if ((node1 == NULL) && (node2 == NULL)) {
423 crm_trace("No assignment preference for %s vs. %s: inactive",
424 instance1->id, instance2->id);
425 return 0;
426
427 } else if (node1 == NULL) {
428 crm_trace("Assign %s after %s: active", instance1->id, instance2->id);
429 return 1;
430
431 } else if (node2 == NULL) {
432 crm_trace("Assign %s before %s: active", instance1->id, instance2->id);
433 return -1;
434 }
435
436 // Prefer instance whose current node can run resources
437 can1 = pcmk__node_available(node1, false, false);
438 can2 = pcmk__node_available(node2, false, false);
439 if (can1 && !can2) {
440 crm_trace("Assign %s before %s: current node can run resources",
441 instance1->id, instance2->id);
442 return -1;
443
444 } else if (!can1 && can2) {
445 crm_trace("Assign %s after %s: current node can't run resources",
446 instance1->id, instance2->id);
447 return 1;
448 }
449
450 // Prefer instance whose parent is allowed to run on instance's current node
451 node1 = pcmk__top_allowed_node(instance1, node1);
452 node2 = pcmk__top_allowed_node(instance2, node2);
453 if ((node1 == NULL) && (node2 == NULL)) {
454 crm_trace("No assignment preference for %s vs. %s: "
455 "parent not allowed on either instance's current node",
456 instance1->id, instance2->id);
457 return 0;
458
459 } else if (node1 == NULL) {
460 crm_trace("Assign %s after %s: parent not allowed on current node",
461 instance1->id, instance2->id);
462 return 1;
463
464 } else if (node2 == NULL) {
465 crm_trace("Assign %s before %s: parent allowed on current node",
466 instance1->id, instance2->id);
467 return -1;
468 }
469
470 // Prefer instance whose current node is running fewer other instances
471 if (node1->assign->count < node2->assign->count) {
472 crm_trace("Assign %s before %s: fewer active instances on current node",
473 instance1->id, instance2->id);
474 return -1;
475
476 } else if (node1->assign->count > node2->assign->count) {
477 crm_trace("Assign %s after %s: more active instances on current node",
478 instance1->id, instance2->id);
479 return 1;
480 }
481
482 // Prefer instance that isn't failed
483 can1 = did_fail(instance1);
484 can2 = did_fail(instance2);
485 if (!can1 && can2) {
486 crm_trace("Assign %s before %s: not failed",
487 instance1->id, instance2->id);
488 return -1;
489 } else if (can1 && !can2) {
490 crm_trace("Assign %s after %s: failed",
491 instance1->id, instance2->id);
492 return 1;
493 }
494
495 // Prefer instance with higher cumulative colocation score on current node
496 rc = cmp_instance_by_colocation(instance1, instance2);
497 if (rc != 0) {
498 return rc;
499 }
500
501 // Prefer instance with lower instance number
502 rc = pcmk__cmp_instance_number(instance1, instance2);
503 if (rc < 0) {
504 crm_trace("Assign %s before %s: instance number",
505 instance1->id, instance2->id);
506 } else if (rc > 0) {
507 crm_trace("Assign %s after %s: instance number",
508 instance1->id, instance2->id);
509 } else {
510 crm_trace("No assignment preference for %s vs. %s",
511 instance1->id, instance2->id);
512 }
513 return rc;
514}
515
527static void
528increment_parent_count(pcmk_resource_t *instance,
529 const pcmk_node_t *assigned_to)
530{
531 pcmk_node_t *allowed = NULL;
532
533 if (assigned_to == NULL) {
534 return;
535 }
536 allowed = pcmk__top_allowed_node(instance, assigned_to);
537
538 if (allowed == NULL) {
539 /* The instance is allowed on the node, but its parent isn't. This
540 * shouldn't be possible if the resource is managed, and we won't be
541 * able to limit the number of instances assigned to the node.
542 */
544
545 } else {
546 allowed->assign->count++;
547 }
548}
549
562static const pcmk_node_t *
563assign_instance(pcmk_resource_t *instance, const pcmk_node_t *prefer,
564 int max_per_node)
565{
566 pcmk_node_t *chosen = NULL;
567
568 pcmk__rsc_trace(instance, "Assigning %s (preferring %s)", instance->id,
569 ((prefer == NULL)? "no node" : prefer->priv->name));
570
571 if (pcmk_is_set(instance->flags, pcmk__rsc_assigning)) {
572 pcmk__rsc_debug(instance,
573 "Assignment loop detected involving %s colocations",
574 instance->id);
575 return NULL;
576 }
577 ban_unavailable_allowed_nodes(instance, max_per_node);
578
579 // Failed early assignments are reversible (stop_if_fail=false)
580 chosen = instance->priv->cmds->assign(instance, prefer, (prefer == NULL));
581 increment_parent_count(instance, chosen);
582 return chosen;
583}
584
598static bool
599assign_instance_early(const pcmk_resource_t *rsc, pcmk_resource_t *instance,
600 const pcmk_node_t *current, int max_per_node,
601 int available)
602{
603 const pcmk_node_t *chosen = NULL;
604 int reserved = 0;
605
606 pcmk_resource_t *parent = instance->priv->parent;
607 GHashTable *allowed_orig = NULL;
608 GHashTable *allowed_orig_parent = parent->priv->allowed_nodes;
609 const pcmk_node_t *allowed_node = NULL;
610
611 pcmk__rsc_trace(instance, "Trying to assign %s to its current node %s",
612 instance->id, pcmk__node_name(current));
613
614 allowed_node = g_hash_table_lookup(instance->priv->allowed_nodes,
615 current->priv->id);
616 if (!pcmk__node_available(allowed_node, true, false)) {
617 pcmk__rsc_info(instance,
618 "Not assigning %s to current node %s: unavailable",
619 instance->id, pcmk__node_name(current));
620 return false;
621 }
622
623 /* On each iteration, if instance gets assigned to a node other than its
624 * current one, we reserve one instance for the chosen node, unassign
625 * instance, restore instance's original node tables, and try again. This
626 * way, instances are proportionally assigned to nodes based on preferences,
627 * but shuffling of specific instances is minimized. If a node will be
628 * assigned instances at all, it preferentially receives instances that are
629 * currently active there.
630 *
631 * parent->private->allowed_nodes tracks the number of instances assigned to
632 * each node. If a node already has max_per_node instances assigned,
633 * ban_unavailable_allowed_nodes() marks it as unavailable.
634 *
635 * In the end, we restore the original parent->private->allowed_nodes to
636 * undo the changes to counts during tentative assignments. If we
637 * successfully assigned an instance to its current node, we increment that
638 * node's counter.
639 */
640
641 // Back up the allowed node tables of instance and its children recursively
642 pcmk__copy_node_tables(instance, &allowed_orig);
643
644 // Update instances-per-node counts in a scratch table
645 parent->priv->allowed_nodes = pcmk__copy_node_table(allowed_orig_parent);
646
647 while (reserved < available) {
648 chosen = assign_instance(instance, current, max_per_node);
649
650 if (pcmk__same_node(chosen, current)) {
651 // Successfully assigned to current node
652 break;
653 }
654
655 // Assignment updates scores, so restore to original state
656 pcmk__rsc_debug(instance, "Rolling back node scores for %s",
657 instance->id);
658 pcmk__restore_node_tables(instance, allowed_orig);
659
660 if (chosen == NULL) {
661 // Assignment failed, so give up
662 pcmk__rsc_info(instance,
663 "Not assigning %s to current node %s: unavailable",
664 instance->id, pcmk__node_name(current));
666 break;
667 }
668
669 // We prefer more strongly to assign an instance to the chosen node
670 pcmk__rsc_debug(instance,
671 "Not assigning %s to current node %s: %s is better",
672 instance->id, pcmk__node_name(current),
673 pcmk__node_name(chosen));
674
675 // Reserve one instance for the chosen node and try again
676 if (++reserved >= available) {
677 pcmk__rsc_info(instance,
678 "Not assigning %s to current node %s: "
679 "other assignments are more important",
680 instance->id, pcmk__node_name(current));
681
682 } else {
683 pcmk__rsc_debug(instance,
684 "Reserved an instance of %s for %s. Retrying "
685 "assignment of %s to %s",
686 rsc->id, pcmk__node_name(chosen), instance->id,
687 pcmk__node_name(current));
688 }
689
690 // Clear this assignment (frees chosen); leave instance counts in parent
691 pcmk__unassign_resource(instance);
692 chosen = NULL;
693 }
694
695 g_hash_table_destroy(allowed_orig);
696
697 // Restore original instances-per-node counts
698 g_hash_table_destroy(parent->priv->allowed_nodes);
699 parent->priv->allowed_nodes = allowed_orig_parent;
700
701 if (chosen == NULL) {
702 // Couldn't assign instance to current node
703 return false;
704 }
705 pcmk__rsc_trace(instance, "Assigned %s to current node %s",
706 instance->id, pcmk__node_name(current));
707 increment_parent_count(instance, chosen);
708 return true;
709}
710
719static unsigned int
720reset_allowed_node_counts(pcmk_resource_t *rsc)
721{
722 unsigned int available_nodes = 0;
723 pcmk_node_t *node = NULL;
724 GHashTableIter iter;
725
726 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
727 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
728 node->assign->count = 0;
729 if (pcmk__node_available(node, false, false)) {
730 available_nodes++;
731 }
732 }
733 return available_nodes;
734}
735
745static const pcmk_node_t *
746preferred_node(const pcmk_resource_t *instance, int optimal_per_node)
747{
748 const pcmk_node_t *node = NULL;
749 const pcmk_node_t *parent_node = NULL;
750
751 // Check whether instance is active, healthy, and not yet assigned
752 if ((instance->priv->active_nodes == NULL)
754 || pcmk_is_set(instance->flags, pcmk__rsc_failed)) {
755 return NULL;
756 }
757
758 // Check whether instance's current node can run resources
759 node = pcmk__current_node(instance);
760 if (!pcmk__node_available(node, true, false)) {
761 pcmk__rsc_trace(instance, "Not assigning %s to %s early (unavailable)",
762 instance->id, pcmk__node_name(node));
763 return NULL;
764 }
765
766 // Check whether node already has optimal number of instances assigned
767 parent_node = pcmk__top_allowed_node(instance, node);
768 if ((parent_node != NULL)
769 && (parent_node->assign->count >= optimal_per_node)) {
770 pcmk__rsc_trace(instance,
771 "Not assigning %s to %s early "
772 "(optimal instances already assigned)",
773 instance->id, pcmk__node_name(node));
774 return NULL;
775 }
776
777 return node;
778}
779
789void
790pcmk__assign_instances(pcmk_resource_t *collective, GList *instances,
791 int max_total, int max_per_node)
792{
793 // Reuse node count to track number of assigned instances
794 unsigned int available_nodes = reset_allowed_node_counts(collective);
795
796 int optimal_per_node = 0;
797 int assigned = 0;
798 GList *iter = NULL;
799 pcmk_resource_t *instance = NULL;
800 const pcmk_node_t *current = NULL;
801
802 if (available_nodes > 0) {
803 optimal_per_node = max_total / available_nodes;
804 }
805 if (optimal_per_node < 1) {
806 optimal_per_node = 1;
807 }
808
809 pcmk__rsc_debug(collective,
810 "Assigning up to %d %s instance%s to up to %u node%s "
811 "(at most %d per host, %d optimal)",
812 max_total, collective->id, pcmk__plural_s(max_total),
813 available_nodes, pcmk__plural_s(available_nodes),
814 max_per_node, optimal_per_node);
815
816 // Assign as many instances as possible to their current location
817 for (iter = instances; (iter != NULL) && (assigned < max_total);
818 iter = iter->next) {
819 int available = max_total - assigned;
820
821 instance = iter->data;
822 if (!pcmk_is_set(instance->flags, pcmk__rsc_unassigned)) {
823 continue; // Already assigned
824 }
825
826 current = preferred_node(instance, optimal_per_node);
827 if ((current != NULL)
828 && assign_instance_early(collective, instance, current,
829 max_per_node, available)) {
830 assigned++;
831 }
832 }
833
834 pcmk__rsc_trace(collective, "Assigned %d of %d instance%s to current node",
835 assigned, max_total, pcmk__plural_s(max_total));
836
837 for (iter = instances; iter != NULL; iter = iter->next) {
838 instance = (pcmk_resource_t *) iter->data;
839
840 if (!pcmk_is_set(instance->flags, pcmk__rsc_unassigned)) {
841 continue; // Already assigned
842 }
843
844 if (instance->priv->active_nodes != NULL) {
845 current = pcmk__current_node(instance);
846 if (pcmk__top_allowed_node(instance, current) == NULL) {
847 const char *unmanaged = "";
848
849 if (!pcmk_is_set(instance->flags, pcmk__rsc_managed)) {
850 unmanaged = "Unmanaged resource ";
851 }
852 crm_notice("%s%s is running on %s which is no longer allowed",
853 unmanaged, instance->id, pcmk__node_name(current));
854 }
855 }
856
857 if (assigned >= max_total) {
858 pcmk__rsc_debug(collective,
859 "Not assigning %s because maximum %d instances "
860 "already assigned",
861 instance->id, max_total);
862 resource_location(instance, NULL, -PCMK_SCORE_INFINITY,
863 "collective_limit_reached",
864 collective->priv->scheduler);
865
866 } else if (assign_instance(instance, NULL, max_per_node) != NULL) {
867 assigned++;
868 }
869 }
870
871 pcmk__rsc_debug(collective, "Assigned %d of %d possible instance%s of %s",
872 assigned, max_total, pcmk__plural_s(max_total),
873 collective->id);
874}
875
879
880 /* This indicates that some instance is restarting. It's not the same as
881 * instance_starting|instance_stopping, which would indicate that some
882 * instance is starting, and some instance (not necessarily the same one) is
883 * stopping.
884 */
886
887 instance_active = (1 << 3),
888
891};
892
900static void
901check_instance_state(const pcmk_resource_t *instance, uint32_t *state)
902{
903 const GList *iter = NULL;
904 uint32_t instance_state = 0; // State of just this instance
905
906 // No need to check further if all conditions have already been detected
907 if (pcmk_all_flags_set(*state, instance_all)) {
908 return;
909 }
910
911 // If instance is a collective (a cloned group), check its children instead
912 if (instance->priv->variant > pcmk__rsc_variant_primitive) {
913 for (iter = instance->priv->children;
914 (iter != NULL) && !pcmk_all_flags_set(*state, instance_all);
915 iter = iter->next) {
916 check_instance_state((const pcmk_resource_t *) iter->data, state);
917 }
918 return;
919 }
920
921 // If we get here, instance is a primitive
922
923 if (instance->priv->active_nodes != NULL) {
925 }
926
927 // Check each of the instance's actions for runnable start or stop
928 for (iter = instance->priv->actions;
929 (iter != NULL) && !pcmk_all_flags_set(instance_state,
932 iter = iter->next) {
933
934 const pcmk_action_t *action = (const pcmk_action_t *) iter->data;
935 const bool optional = pcmk_is_set(action->flags, pcmk__action_optional);
936
937 if (pcmk__str_eq(PCMK_ACTION_START, action->task, pcmk__str_none)) {
938 if (!optional
940
941 pcmk__rsc_trace(instance, "Instance is starting due to %s",
942 action->uuid);
944 } else {
945 pcmk__rsc_trace(instance, "%s doesn't affect %s state (%s)",
946 action->uuid, instance->id,
947 (optional? "optional" : "unrunnable"));
948 }
949
950 } else if (pcmk__str_eq(PCMK_ACTION_STOP, action->task,
952 /* Only stop actions can be pseudo-actions for primitives. That
953 * indicates that the node they are on is being fenced, so the stop
954 * is implied rather than actually executed.
955 */
956 if (!optional
957 && pcmk_any_flags_set(action->flags, pcmk__action_pseudo
959 pcmk__rsc_trace(instance, "Instance is stopping due to %s",
960 action->uuid);
962 } else {
963 pcmk__rsc_trace(instance, "%s doesn't affect %s state (%s)",
964 action->uuid, instance->id,
965 (optional? "optional" : "unrunnable"));
966 }
967 }
968 }
969
970 if (pcmk_all_flags_set(instance_state,
973 }
974 *state |= instance_state;
975}
976
984void
985pcmk__create_instance_actions(pcmk_resource_t *collective, GList *instances)
986{
987 uint32_t state = 0;
988
989 pcmk_action_t *stop = NULL;
990 pcmk_action_t *stopped = NULL;
991
992 pcmk_action_t *start = NULL;
993 pcmk_action_t *started = NULL;
994
995 pcmk__rsc_trace(collective, "Creating collective instance actions for %s",
996 collective->id);
997
998 // Create actions for each instance appropriate to its variant
999 for (GList *iter = instances; iter != NULL; iter = iter->next) {
1000 pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1001
1002 instance->priv->cmds->create_actions(instance);
1003 check_instance_state(instance, &state);
1004 }
1005
1006 // Create pseudo-actions for rsc start and started
1007 start = pe__new_rsc_pseudo_action(collective, PCMK_ACTION_START,
1009 true);
1010 started = pe__new_rsc_pseudo_action(collective, PCMK_ACTION_RUNNING,
1012 false);
1013 started->priority = PCMK_SCORE_INFINITY;
1014 if (pcmk_any_flags_set(state, instance_active|instance_starting)) {
1016 }
1017
1018 // Create pseudo-actions for rsc stop and stopped
1021 true);
1022 stopped = pe__new_rsc_pseudo_action(collective, PCMK_ACTION_STOPPED,
1024 true);
1025 stopped->priority = PCMK_SCORE_INFINITY;
1026 if (!pcmk_is_set(state, instance_restarting)) {
1028 }
1029
1030 if (pcmk__is_clone(collective)) {
1031 pe__create_clone_notif_pseudo_ops(collective, start, started, stop,
1032 stopped);
1033 }
1034}
1035
1047static inline GList *
1048get_instance_list(const pcmk_resource_t *rsc)
1049{
1050 if (pcmk__is_bundle(rsc)) {
1051 return pe__bundle_containers(rsc);
1052 } else {
1053 return rsc->priv->children;
1054 }
1055}
1056
1064static inline void
1065free_instance_list(const pcmk_resource_t *rsc, GList *list)
1066{
1067 if (list != rsc->priv->children) {
1068 g_list_free(list);
1069 }
1070}
1071
1085bool
1087 enum rsc_role_e role, bool current)
1088{
1089 pcmk_node_t *instance_node = NULL;
1090
1091 CRM_CHECK((instance != NULL) && (node != NULL), return false);
1092
1093 if ((role != pcmk_role_unknown)
1094 && (role != instance->priv->fns->state(instance, current))) {
1095 pcmk__rsc_trace(instance,
1096 "%s is not a compatible instance (role is not %s)",
1097 instance->id, pcmk_role_text(role));
1098 return false;
1099 }
1100
1101 if (!is_set_recursive(instance, pcmk__rsc_blocked, true)) {
1103
1104 if (current) {
1106 }
1107
1108 // We only want instances that haven't failed
1109 instance_node = instance->priv->fns->location(instance, NULL, target);
1110 }
1111
1112 if (instance_node == NULL) {
1113 pcmk__rsc_trace(instance,
1114 "%s is not a compatible instance "
1115 "(not assigned to a node)",
1116 instance->id);
1117 return false;
1118 }
1119
1120 if (!pcmk__same_node(instance_node, node)) {
1121 pcmk__rsc_trace(instance,
1122 "%s is not a compatible instance "
1123 "(assigned to %s not %s)",
1124 instance->id, pcmk__node_name(instance_node),
1125 pcmk__node_name(node));
1126 return false;
1127 }
1128
1129 return true;
1130}
1131
1132#define display_role(r) \
1133 (((r) == pcmk_role_unknown)? "matching" : pcmk_role_text(r))
1134
1148static pcmk_resource_t *
1149find_compatible_instance_on_node(const pcmk_resource_t *match_rsc,
1150 const pcmk_resource_t *rsc,
1151 const pcmk_node_t *node, enum rsc_role_e role,
1152 bool current)
1153{
1154 GList *instances = NULL;
1155
1156 instances = get_instance_list(rsc);
1157 for (GList *iter = instances; iter != NULL; iter = iter->next) {
1158 pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1159
1160 if (pcmk__instance_matches(instance, node, role, current)) {
1161 pcmk__rsc_trace(match_rsc,
1162 "Found %s %s instance %s compatible with %s on %s",
1163 display_role(role), rsc->id, instance->id,
1164 match_rsc->id, pcmk__node_name(node));
1165 free_instance_list(rsc, instances); // Only frees list, not contents
1166 return instance;
1167 }
1168 }
1169 free_instance_list(rsc, instances);
1170
1171 pcmk__rsc_trace(match_rsc,
1172 "No %s %s instance found compatible with %s on %s",
1173 display_role(role), rsc->id, match_rsc->id,
1174 pcmk__node_name(node));
1175 return NULL;
1176}
1177
1193 const pcmk_resource_t *rsc, enum rsc_role_e role,
1194 bool current)
1195{
1196 pcmk_resource_t *instance = NULL;
1197 GList *nodes = NULL;
1198 const pcmk_node_t *node = NULL;
1199 GHashTable *allowed_nodes = match_rsc->priv->allowed_nodes;
1201
1202 if (current) {
1204 }
1205
1206 // If match_rsc has a node, check only that node
1207 node = match_rsc->priv->fns->location(match_rsc, NULL, target);
1208 if (node != NULL) {
1209 return find_compatible_instance_on_node(match_rsc, rsc, node, role,
1210 current);
1211 }
1212
1213 // Otherwise check for an instance matching any of match_rsc's allowed nodes
1214 nodes = pcmk__sort_nodes(g_hash_table_get_values(allowed_nodes), NULL);
1215 for (GList *iter = nodes; (iter != NULL) && (instance == NULL);
1216 iter = iter->next) {
1217 instance = find_compatible_instance_on_node(match_rsc, rsc,
1218 (pcmk_node_t *) iter->data,
1219 role, current);
1220 }
1221
1222 if (instance == NULL) {
1223 pcmk__rsc_debug(rsc, "No %s instance found compatible with %s",
1224 rsc->id, match_rsc->id);
1225 }
1226 g_list_free(nodes);
1227 return instance;
1228}
1229
1242static bool
1243unassign_if_mandatory(const pcmk_action_t *first, const pcmk_action_t *then,
1244 pcmk_resource_t *then_instance, uint32_t type,
1245 bool current)
1246{
1247 // Allow "then" instance to go down even without an interleave match
1248 if (current) {
1249 pcmk__rsc_trace(then->rsc,
1250 "%s has no instance to order before stopping "
1251 "or demoting %s",
1252 first->rsc->id, then_instance->id);
1253
1254 /* If the "first" action must be runnable, but there is no "first"
1255 * instance, the "then" instance must not be allowed to come up.
1256 */
1257 } else if (pcmk_any_flags_set(type, pcmk__ar_unrunnable_first_blocks
1259 pcmk__rsc_info(then->rsc,
1260 "Inhibiting %s from being active "
1261 "because there is no %s instance to interleave",
1262 then_instance->id, first->rsc->id);
1263 return pcmk__assign_resource(then_instance, NULL, true, true);
1264 }
1265 return false;
1266}
1267
1283static pcmk_action_t *
1284find_instance_action(const pcmk_action_t *action, const pcmk_resource_t *instance,
1285 const char *action_name, const pcmk_node_t *node,
1286 bool for_first)
1287{
1288 const pcmk_resource_t *rsc = NULL;
1289 pcmk_action_t *matching_action = NULL;
1290
1291 /* If instance is a bundle container, sometimes we should interleave the
1292 * action for the container itself, and sometimes for the containerized
1293 * resource.
1294 *
1295 * For example, given "start bundle A then bundle B", B likely requires the
1296 * service inside A's container to be active, rather than just the
1297 * container, so we should interleave the action for A's containerized
1298 * resource. On the other hand, it's possible B's container itself requires
1299 * something from A, so we should interleave the action for B's container.
1300 *
1301 * Essentially, for 'first', we should use the containerized resource for
1302 * everything except stop, and for 'then', we should use the container for
1303 * everything except promote and demote (which can only be performed on the
1304 * containerized resource).
1305 */
1306 if ((for_first && !pcmk__str_any_of(action->task, PCMK_ACTION_STOP,
1307 PCMK_ACTION_STOPPED, NULL))
1308
1309 || (!for_first && pcmk__str_any_of(action->task, PCMK_ACTION_PROMOTE,
1312 PCMK_ACTION_DEMOTED, NULL))) {
1313
1314 rsc = pe__get_rsc_in_container(instance);
1315 }
1316 if (rsc == NULL) {
1317 rsc = instance; // No containerized resource, use instance itself
1318 } else {
1319 node = NULL; // Containerized actions are on bundle-created guest
1320 }
1321
1322 matching_action = find_first_action(rsc->priv->actions, NULL,
1323 action_name, node);
1324 if (matching_action != NULL) {
1325 return matching_action;
1326 }
1327
1328 if (pcmk_is_set(instance->flags, pcmk__rsc_removed)
1329 || pcmk__is_down_action(action_name)) {
1330 crm_trace("No %s action found for %s%s",
1331 action_name,
1332 pcmk_is_set(instance->flags, pcmk__rsc_removed)? "orphan " : "",
1333 instance->id);
1334 } else {
1335 crm_err("No %s action found for %s to interleave (bug?)",
1336 action_name, instance->id);
1337 }
1338 return NULL;
1339}
1340
1354static const char *
1355orig_action_name(const pcmk_action_t *action)
1356{
1357 // Any instance will do
1358 const pcmk_resource_t *instance = action->rsc->priv->children->data;
1359
1360 char *action_type = NULL;
1361 const char *action_name = action->task;
1363
1365 PCMK_ACTION_NOTIFIED, NULL)) {
1366 // action->uuid is RSC_(confirmed-){pre,post}_notify_ACTION_INTERVAL
1367 CRM_CHECK(parse_op_key(action->uuid, NULL, &action_type, NULL),
1369 action_name = strstr(action_type, "_notify_");
1370 CRM_CHECK(action_name != NULL,
1372 action_name += strlen("_notify_");
1373 }
1374 orig_task = get_complex_task(instance, action_name);
1375 free(action_type);
1376 return pcmk__action_text(orig_task);
1377}
1378
1399static uint32_t
1400update_interleaved_actions(pcmk_action_t *first, pcmk_action_t *then,
1401 const pcmk_node_t *node, uint32_t filter,
1402 uint32_t type)
1403{
1404 GList *instances = NULL;
1405 uint32_t changed = pcmk__updated_none;
1406 const char *orig_first_task = orig_action_name(first);
1407
1408 // Stops and demotes must be interleaved with instance on current node
1409 bool current = pcmk__ends_with(first->uuid, "_" PCMK_ACTION_STOPPED "_0")
1410 || pcmk__ends_with(first->uuid,
1411 "_" PCMK_ACTION_DEMOTED "_0");
1412
1413 // Update the specified actions for each "then" instance individually
1414 instances = get_instance_list(then->rsc);
1415 for (GList *iter = instances; iter != NULL; iter = iter->next) {
1416 pcmk_resource_t *first_instance = NULL;
1417 pcmk_resource_t *then_instance = iter->data;
1418
1419 pcmk_action_t *first_action = NULL;
1420 pcmk_action_t *then_action = NULL;
1421
1422 // Find a "first" instance to interleave with this "then" instance
1423 first_instance = pcmk__find_compatible_instance(then_instance,
1424 first->rsc,
1426 current);
1427
1428 if (first_instance == NULL) { // No instance can be interleaved
1429 if (unassign_if_mandatory(first, then, then_instance, type,
1430 current)) {
1432 }
1433 continue;
1434 }
1435
1436 first_action = find_instance_action(first, first_instance,
1437 orig_first_task, node, true);
1438 if (first_action == NULL) {
1439 continue;
1440 }
1441
1442 then_action = find_instance_action(then, then_instance, then->task,
1443 node, false);
1444 if (then_action == NULL) {
1445 continue;
1446 }
1447
1448 if (order_actions(first_action, then_action, type)) {
1449 pcmk__set_updated_flags(changed, first,
1451 }
1452
1453 changed |= then_instance->priv->cmds->update_ordered_actions(
1454 first_action, then_action, node,
1455 first_instance->priv->cmds->action_flags(first_action, node),
1456 filter, type, then->rsc->priv->scheduler);
1457 }
1458 free_instance_list(then->rsc, instances);
1459 return changed;
1460}
1461
1471static bool
1472can_interleave_actions(const pcmk_action_t *first, const pcmk_action_t *then)
1473{
1474 bool interleave = false;
1475 pcmk_resource_t *rsc = NULL;
1476
1477 if ((first->rsc == NULL) || (then->rsc == NULL)) {
1478 crm_trace("Not interleaving %s with %s: not resource actions",
1479 first->uuid, then->uuid);
1480 return false;
1481 }
1482
1483 if (first->rsc == then->rsc) {
1484 crm_trace("Not interleaving %s with %s: same resource",
1485 first->uuid, then->uuid);
1486 return false;
1487 }
1488
1489 if ((first->rsc->priv->variant < pcmk__rsc_variant_clone)
1490 || (then->rsc->priv->variant < pcmk__rsc_variant_clone)) {
1491 crm_trace("Not interleaving %s with %s: not clones or bundles",
1492 first->uuid, then->uuid);
1493 return false;
1494 }
1495
1496 if (pcmk__ends_with(then->uuid, "_stop_0")
1497 || pcmk__ends_with(then->uuid, "_demote_0")) {
1498 rsc = first->rsc;
1499 } else {
1500 rsc = then->rsc;
1501 }
1502
1503 interleave = crm_is_true(g_hash_table_lookup(rsc->priv->meta,
1505 pcmk__rsc_trace(rsc, "'%s then %s' will %sbe interleaved (based on %s)",
1506 first->uuid, then->uuid, (interleave? "" : "not "),
1507 rsc->id);
1508 return interleave;
1509}
1510
1533static uint32_t
1534update_noninterleaved_actions(pcmk_resource_t *instance, pcmk_action_t *first,
1535 const pcmk_action_t *then, const pcmk_node_t *node,
1536 uint32_t flags, uint32_t filter, uint32_t type)
1537{
1538 pcmk_action_t *instance_action = NULL;
1540 uint32_t instance_flags = 0;
1541 uint32_t changed = pcmk__updated_none;
1542
1543 // Check whether instance has an equivalent of "then" action
1544 instance_action = find_first_action(instance->priv->actions, NULL,
1545 then->task, node);
1546 if (instance_action == NULL) {
1547 return changed;
1548 }
1549
1550 // Check whether action is runnable
1551 instance_flags = instance->priv->cmds->action_flags(instance_action, node);
1552 if (!pcmk_is_set(instance_flags, pcmk__action_runnable)) {
1553 return changed;
1554 }
1555
1556 // If so, update actions for the instance
1557 changed = instance->priv->cmds->update_ordered_actions(first,
1558 instance_action,
1559 node, flags, filter,
1560 type, scheduler);
1561
1562 // Propagate any changes to later actions
1563 if (pcmk_is_set(changed, pcmk__updated_then)) {
1564 for (GList *after_iter = instance_action->actions_after;
1565 after_iter != NULL; after_iter = after_iter->next) {
1566 pcmk__related_action_t *after = after_iter->data;
1567
1569 }
1570 }
1571
1572 return changed;
1573}
1574
1598uint32_t
1600 const pcmk_node_t *node, uint32_t flags,
1601 uint32_t filter, uint32_t type,
1603{
1604 pcmk__assert((first != NULL) && (then != NULL) && (scheduler != NULL));
1605
1606 if (then->rsc == NULL) {
1607 return pcmk__updated_none;
1608
1609 } else if (can_interleave_actions(first, then)) {
1610 return update_interleaved_actions(first, then, node, filter, type);
1611
1612 } else {
1613 uint32_t changed = pcmk__updated_none;
1614 GList *instances = get_instance_list(then->rsc);
1615
1616 // Update actions for the clone or bundle resource itself
1617 changed |= pcmk__update_ordered_actions(first, then, node, flags,
1618 filter, type, scheduler);
1619
1620 // Update the 'then' clone instances or bundle containers individually
1621 for (GList *iter = instances; iter != NULL; iter = iter->next) {
1622 pcmk_resource_t *instance = iter->data;
1623
1624 changed |= update_noninterleaved_actions(instance, first, then,
1625 node, flags, filter, type);
1626 }
1627 free_instance_list(then->rsc, instances);
1628 return changed;
1629 }
1630}
1631
1632#define pe__clear_action_summary_flags(flags, action, flag) do { \
1633 flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE, \
1634 "Action summary", action->rsc->id, \
1635 flags, flag, #flag); \
1636 } while (0)
1637
1648uint32_t
1650 const pcmk_node_t *node)
1651{
1652 bool any_runnable = false;
1653 const char *action_name = orig_action_name(action);
1654
1655 // Set original assumptions (optional and runnable may be cleared below)
1656 uint32_t flags = pcmk__action_optional
1659
1660 for (const GList *iter = instances; iter != NULL; iter = iter->next) {
1661 const pcmk_resource_t *instance = iter->data;
1662 const pcmk_node_t *instance_node = NULL;
1663 pcmk_action_t *instance_action = NULL;
1664 uint32_t instance_flags;
1665
1666 // Node is relevant only to primitive instances
1667 if (pcmk__is_primitive(instance)) {
1668 instance_node = node;
1669 }
1670
1671 instance_action = find_first_action(instance->priv->actions, NULL,
1672 action_name, instance_node);
1673 if (instance_action == NULL) {
1674 pcmk__rsc_trace(action->rsc, "%s has no %s action on %s",
1675 instance->id, action_name, pcmk__node_name(node));
1676 continue;
1677 }
1678
1679 pcmk__rsc_trace(action->rsc, "%s has %s for %s on %s",
1680 instance->id, instance_action->uuid, action_name,
1681 pcmk__node_name(node));
1682
1683 instance_flags = instance->priv->cmds->action_flags(instance_action,
1684 node);
1685
1686 // If any instance action is mandatory, so is the collective action
1688 && !pcmk_is_set(instance_flags, pcmk__action_optional)) {
1689 pcmk__rsc_trace(instance, "%s is mandatory because %s is",
1690 action->uuid, instance_action->uuid);
1694 }
1695
1696 // If any instance action is runnable, so is the collective action
1697 if (pcmk_is_set(instance_flags, pcmk__action_runnable)) {
1698 any_runnable = true;
1699 }
1700 }
1701
1702 if (!any_runnable) {
1704 "%s is not runnable because no instance can run %s",
1705 action->uuid, action_name);
1707 if (node == NULL) {
1709 }
1710 }
1711
1712 return flags;
1713}
@ pcmk__ar_first_implies_then
@ pcmk__ar_unrunnable_first_blocks
'then' is runnable (and migratable) only if 'first' is runnable
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition actions.c:278
#define PCMK_ACTION_PROMOTED
Definition actions.h:58
#define PCMK_ACTION_STOP
Definition actions.h:66
#define PCMK_ACTION_RUNNING
Definition actions.h:62
#define PCMK_ACTION_PROMOTE
Definition actions.h:57
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_NOTIFIED
Definition actions.h:52
#define PCMK_ACTION_STOPPED
Definition actions.h:67
#define PCMK_ACTION_DEMOTED
Definition actions.h:41
#define PCMK_ACTION_DEMOTE
Definition actions.h:40
#define PCMK_ACTION_NOTIFY
Definition actions.h:53
@ pcmk__action_runnable
@ pcmk__action_migratable
@ pcmk__action_optional
@ pcmk__action_pseudo
#define pcmk__set_action_flags(action, flags_to_set)
const char * pcmk__action_text(enum pcmk__action_type action)
Definition actions.c:34
#define pcmk__clear_action_flags(action, flags_to_clear)
pcmk__action_type
@ pcmk__action_unspecified
const char * parent
Definition cib.c:27
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
enum pcmk_ipc_server type
Definition cpg.c:3
@ pcmk__coloc_select_nonnegative
@ pcmk__coloc_select_default
G_GNUC_INTERNAL void pcmk__unassign_resource(pcmk_resource_t *rsc)
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL uint32_t pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force, bool stop_if_fail)
#define pcmk__set_updated_flags(au_flags, action, flags_to_set)
G_GNUC_INTERNAL GList * pcmk__with_this_colocations(const pcmk_resource_t *rsc)
G_GNUC_INTERNAL bool pcmk__colocation_has_influence(const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
G_GNUC_INTERNAL GList * pcmk__sort_nodes(GList *nodes, pcmk_node_t *active_node)
G_GNUC_INTERNAL void pcmk__restore_node_tables(pcmk_resource_t *rsc, GHashTable *backup)
G_GNUC_INTERNAL GList * pcmk__this_with_colocations(const pcmk_resource_t *rsc)
G_GNUC_INTERNAL void pcmk__copy_node_tables(const pcmk_resource_t *rsc, GHashTable **copy)
@ pcmk__updated_none
@ pcmk__updated_first
@ pcmk__updated_then
G_GNUC_INTERNAL pcmk_node_t * pcmk__top_allowed_node(const pcmk_resource_t *rsc, const pcmk_node_t *node)
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
G_GNUC_INTERNAL GHashTable * pcmk__copy_node_table(GHashTable *nodes)
#define crm_warn(fmt, args...)
Definition logging.h:360
#define CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define crm_notice(fmt, args...)
Definition logging.h:363
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
pcmk_scheduler_t * scheduler
void pcmk__free_node_copy(void *data)
Definition nodes.c:64
#define PCMK_META_INTERLEAVE
Definition options.h:91
const char * action
Definition pcmk_fence.c:32
const char * target
Definition pcmk_fence.c:31
void pcmk__create_instance_actions(pcmk_resource_t *collective, GList *instances)
#define pe__clear_action_summary_flags(flags, action, flag)
bool pcmk__instance_matches(const pcmk_resource_t *instance, const pcmk_node_t *node, enum rsc_role_e role, bool current)
gint pcmk__cmp_instance_number(gconstpointer a, gconstpointer b)
uint32_t pcmk__instance_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
void pcmk__assign_instances(pcmk_resource_t *collective, GList *instances, int max_total, int max_per_node)
pcmk_resource_t * pcmk__find_compatible_instance(const pcmk_resource_t *match_rsc, const pcmk_resource_t *rsc, enum rsc_role_e role, bool current)
#define display_role(r)
@ instance_starting
@ instance_restarting
@ instance_active
@ instance_stopping
uint32_t pcmk__collective_action_flags(pcmk_action_t *action, const GList *instances, const pcmk_node_t *node)
gint pcmk__cmp_instance(gconstpointer a, gconstpointer b)
pcmk_node_t node2
pcmk_node_t node1
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition utils.c:124
bool is_set_recursive(const pcmk_resource_t *rsc, long long flag, bool any)
Definition clone.c:500
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition utils.c:398
pcmk_action_t * pe__new_rsc_pseudo_action(pcmk_resource_t *rsc, const char *task, bool optional, bool runnable)
const pcmk_resource_t * pe__get_rsc_in_container(const pcmk_resource_t *instance)
Definition bundle.c:131
gboolean order_actions(pcmk_action_t *first, pcmk_action_t *then, uint32_t flags)
Definition utils.c:483
void pe__create_clone_notif_pseudo_ops(pcmk_resource_t *clone, pcmk_action_t *start, pcmk_action_t *started, pcmk_action_t *stop, pcmk_action_t *stopped)
Definition clone.c:1218
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
GList * pe__bundle_containers(const pcmk_resource_t *bundle)
Definition bundle.c:1989
enum pcmk__action_type get_complex_task(const pcmk_resource_t *rsc, const char *name)
@ pcmk__rsc_managed
@ pcmk__rsc_assigning
@ pcmk__rsc_unassigned
@ pcmk__rsc_blocked
@ pcmk__rsc_removed
@ pcmk__rsc_failed
#define pcmk__set_rsc_flags(resource, flags_to_set)
@ pcmk__rsc_variant_clone
Clone resource.
@ pcmk__rsc_variant_primitive
Primitive resource.
@ pcmk__rsc_node_current
@ pcmk__rsc_node_assigned
#define pcmk__assert(expr)
const char * pcmk_role_text(enum rsc_role_e role)
Get readable description of a resource role.
Definition roles.c:23
rsc_role_e
Definition roles.h:34
@ pcmk_role_unknown
Resource role is unknown.
Definition roles.h:35
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__rsc_debug(rsc, fmt, args...)
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition scores.c:102
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:26
gboolean crm_is_true(const char *s)
Definition strings.c:490
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
#define pcmk__plural_s(i)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1029
@ pcmk__str_none
bool pcmk__ends_with(const char *s, const char *match)
Definition strings.c:610
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
pcmk_resource_t * rsc
uint32_t(* action_flags)(pcmk_action_t *action, const pcmk_node_t *node)
pcmk_node_t *(* assign)(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail)
uint32_t(* update_ordered_actions)(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
void(* create_actions)(pcmk_resource_t *rsc)
void(* add_colocated_node_scores)(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
pcmk_resource_t * primary
pcmk_resource_t * dependent
pcmk_scheduler_t * scheduler
enum pcmk__rsc_variant variant
const pcmk__assignment_methods_t * cmds
const pcmk__rsc_methods_t * fns
unsigned long long flags
Definition resources.h:69
pcmk__resource_private_t * priv
Definition resources.h:61
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, bool current)
pcmk_node_t *(* location)(const pcmk_resource_t *rsc, GList **list, uint32_t target)
pcmk_node_t *(* active_node)(const pcmk_resource_t *rsc, unsigned int *count_all, unsigned int *count_clean)
pcmk__node_private_t * priv
Definition nodes.h:85
struct pcmk__node_assignment * assign
Definition nodes.h:79
Wrappers for and extensions to libxml2.