pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_sched_ordering.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#include <crm_internal.h>
11
12#include <inttypes.h> // PRIx32
13#include <stdbool.h>
14#include <glib.h>
15
16#include <crm/crm.h>
17#include <pacemaker-internal.h>
19
25
27 ordering_asymmetric, // the only relation in an asymmetric ordering
28 ordering_symmetric, // the normal relation in a symmetric ordering
29 ordering_symmetric_inverse, // the inverse relation in a symmetric ordering
30};
31
32#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
33 __rsc = pcmk__find_constraint_resource(scheduler->resources, \
34 __name); \
35 if (__rsc == NULL) { \
36 pcmk__config_err("%s: No resource found for %s", __set, __name);\
37 return pcmk_rc_unpack_error; \
38 } \
39 } while (0)
40
41static const char *
42invert_action(const char *action)
43{
44 if (pcmk__str_eq(action, PCMK_ACTION_START, pcmk__str_none)) {
45 return PCMK_ACTION_STOP;
46
47 } else if (pcmk__str_eq(action, PCMK_ACTION_STOP, pcmk__str_none)) {
48 return PCMK_ACTION_START;
49
50 } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
51 return PCMK_ACTION_DEMOTE;
52
53 } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
55
56 } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTED, pcmk__str_none)) {
58
59 } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
61
62 } else if (pcmk__str_eq(action, PCMK_ACTION_RUNNING, pcmk__str_none)) {
64
65 } else if (pcmk__str_eq(action, PCMK_ACTION_STOPPED, pcmk__str_none)) {
67 }
68 pcmk__config_warn("Unknown action '%s' specified in order constraint",
69 action);
70 return NULL;
71}
72
73static enum pe_order_kind
74get_ordering_type(const xmlNode *xml_obj)
75{
77 const char *kind = crm_element_value(xml_obj, PCMK_XA_KIND);
78
79 if (kind == NULL) {
80 const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE);
81
83
84 if (score) {
85 // @COMPAT deprecated informally since 1.0.7, formally since 2.0.1
86 int score_i = char2score(score);
87
88 if (score_i == 0) {
90 }
92 "Support for '" PCMK_XA_SCORE "' in "
93 PCMK_XE_RSC_ORDER " is deprecated and will be "
94 "removed in a future release "
95 "(use '" PCMK_XA_KIND "' instead)");
96 }
97
98 } else if (pcmk__str_eq(kind, PCMK_VALUE_MANDATORY, pcmk__str_none)) {
100
101 } else if (pcmk__str_eq(kind, PCMK_VALUE_OPTIONAL, pcmk__str_none)) {
102 kind_e = pe_order_kind_optional;
103
104 } else if (pcmk__str_eq(kind, PCMK_VALUE_SERIALIZE, pcmk__str_none)) {
106
107 } else {
108 pcmk__config_err("Resetting '" PCMK_XA_KIND "' for constraint %s to "
109 "'" PCMK_VALUE_MANDATORY "' because '%s' is not valid",
110 pcmk__s(pcmk__xe_id(xml_obj), "missing ID"), kind);
111 }
112 return kind_e;
113}
114
127static enum ordering_symmetry
128get_ordering_symmetry(const xmlNode *xml_obj, enum pe_order_kind parent_kind,
129 const char *parent_symmetrical_s)
130{
131 int rc = pcmk_rc_ok;
132 bool symmetric = false;
133 enum pe_order_kind kind = parent_kind; // Default to parent's kind
134
135 // Check ordering XML for explicit kind
136 if ((crm_element_value(xml_obj, PCMK_XA_KIND) != NULL)
137 || (crm_element_value(xml_obj, PCMK_XA_SCORE) != NULL)) {
138 kind = get_ordering_type(xml_obj);
139 }
140
141 // Check ordering XML (and parent) for explicit PCMK_XA_SYMMETRICAL setting
142 rc = pcmk__xe_get_bool_attr(xml_obj, PCMK_XA_SYMMETRICAL, &symmetric);
143
144 if (rc != pcmk_rc_ok && parent_symmetrical_s != NULL) {
145 symmetric = crm_is_true(parent_symmetrical_s);
146 rc = pcmk_rc_ok;
147 }
148
149 if (rc == pcmk_rc_ok) {
150 if (symmetric) {
151 if (kind == pe_order_kind_serialize) {
153 " for '%s' because not valid with "
155 pcmk__xe_id(xml_obj));
156 } else {
157 return ordering_symmetric;
158 }
159 }
160 return ordering_asymmetric;
161 }
162
163 // Use default symmetry
164 if (kind == pe_order_kind_serialize) {
165 return ordering_asymmetric;
166 }
167 return ordering_symmetric;
168}
169
180static uint32_t
181ordering_flags_for_kind(enum pe_order_kind kind, const char *first,
182 enum ordering_symmetry symmetry)
183{
184 uint32_t flags = pcmk__ar_none; // so we trace-log all flags set
185
186 switch (kind) {
189 break;
190
192 /* This flag is not used anywhere directly but means the relation
193 * will not match an equality comparison against pcmk__ar_none or
194 * pcmk__ar_ordered.
195 */
197 break;
198
201 switch (symmetry) {
204 break;
205
210 PCMK_ACTION_PROMOTE, NULL)) {
213 }
214 break;
215
219 break;
220 }
221 break;
222 }
223 return flags;
224}
225
239static pcmk_resource_t *
240get_ordering_resource(const xmlNode *xml, const char *resource_attr,
241 const char *instance_attr,
243{
244 // @COMPAT: instance_attr and instance_id variables deprecated since 2.1.5
245 pcmk_resource_t *rsc = NULL;
246 const char *rsc_id = crm_element_value(xml, resource_attr);
247 const char *instance_id = crm_element_value(xml, instance_attr);
248
249 if (rsc_id == NULL) {
250 pcmk__config_err("Ignoring constraint '%s' without %s",
251 pcmk__xe_id(xml), resource_attr);
252 return NULL;
253 }
254
256 if (rsc == NULL) {
257 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
258 "does not exist", pcmk__xe_id(xml), rsc_id);
259 return NULL;
260 }
261
262 if (instance_id != NULL) {
264 "Support for " PCMK__XA_FIRST_INSTANCE " and "
265 PCMK__XA_THEN_INSTANCE " is deprecated and will be "
266 "removed in a future release.");
267
268 if (!pcmk__is_clone(rsc)) {
269 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
270 "is not a clone but instance '%s' was requested",
271 pcmk__xe_id(xml), rsc_id, instance_id);
272 return NULL;
273 }
274 rsc = find_clone_instance(rsc, instance_id);
275 if (rsc == NULL) {
276 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
277 "does not have an instance '%s'",
278 pcmk__xe_id(xml), rsc_id, instance_id);
279 return NULL;
280 }
281 }
282 return rsc;
283}
284
294static int
295get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml)
296{
297 const char *clone_min = NULL;
298 bool require_all = false;
299
300 if (!pcmk__is_clone(rsc)) {
301 return 0;
302 }
303
304 clone_min = g_hash_table_lookup(rsc->meta, PCMK_META_CLONE_MIN);
305 if (clone_min != NULL) {
306 int clone_min_int = 0;
307
308 pcmk__scan_min_int(clone_min, &clone_min_int, 0);
309 return clone_min_int;
310 }
311
312 /* @COMPAT 1.1.13:
313 * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE is deprecated equivalent of
314 * PCMK_META_CLONE_MIN=1
315 */
317 &require_all) != ENODATA) {
319 "Support for " PCMK_XA_REQUIRE_ALL " in ordering "
320 "constraints is deprecated and will be removed in a "
321 "future release (use " PCMK_META_CLONE_MIN " clone "
322 "meta-attribute instead)");
323 if (!require_all) {
324 return 1;
325 }
326 }
327
328 return 0;
329}
330
343static void
344clone_min_ordering(const char *id,
345 pcmk_resource_t *rsc_first, const char *action_first,
346 pcmk_resource_t *rsc_then, const char *action_then,
347 uint32_t flags, int clone_min)
348{
349 // Create a pseudo-action for when the minimum instances are active
350 char *task = crm_strdup_printf(PCMK_ACTION_CLONE_ONE_OR_MORE ":%s", id);
351 pcmk_action_t *clone_min_met = get_pseudo_op(task, rsc_first->cluster);
352
353 free(task);
354
355 /* Require the pseudo-action to have the required number of actions to be
356 * considered runnable before allowing the pseudo-action to be runnable.
357 */
358 clone_min_met->required_runnable_before = clone_min;
360
361 // Order the actions for each clone instance before the pseudo-action
362 for (GList *iter = rsc_first->children; iter != NULL; iter = iter->next) {
363 pcmk_resource_t *child = iter->data;
364
365 pcmk__new_ordering(child, pcmk__op_key(child->id, action_first, 0),
366 NULL, NULL, NULL, clone_min_met,
369 rsc_first->cluster);
370 }
371
372 // Order "then" action after the pseudo-action (if runnable)
373 pcmk__new_ordering(NULL, NULL, clone_min_met, rsc_then,
374 pcmk__op_key(rsc_then->id, action_then, 0),
376 rsc_first->cluster);
377}
378
392#define handle_restart_type(rsc, kind, flag, flags) do { \
393 if (((kind) == pe_order_kind_optional) \
394 && ((rsc)->restart_type == pe_restart_restart)) { \
395 pcmk__set_relation_flags((flags), (flag)); \
396 } \
397 } while (0)
398
410static void
411inverse_ordering(const char *id, enum pe_order_kind kind,
412 pcmk_resource_t *rsc_first, const char *action_first,
413 pcmk_resource_t *rsc_then, const char *action_then)
414{
415 action_then = invert_action(action_then);
416 action_first = invert_action(action_first);
417 if ((action_then == NULL) || (action_first == NULL)) {
418 pcmk__config_warn("Cannot invert constraint '%s' "
419 "(please specify inverse manually)", id);
420 } else {
421 uint32_t flags = ordering_flags_for_kind(kind, action_first,
423
425 pcmk__order_resource_actions(rsc_then, action_then, rsc_first,
426 action_first, flags);
427 }
428}
429
430static void
431unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
432{
433 pcmk_resource_t *rsc_then = NULL;
434 pcmk_resource_t *rsc_first = NULL;
435 int min_required_before = 0;
437 uint32_t flags = pcmk__ar_none;
438 enum ordering_symmetry symmetry;
439
440 const char *action_then = NULL;
441 const char *action_first = NULL;
442 const char *id = NULL;
443
444 CRM_CHECK(xml_obj != NULL, return);
445
446 id = crm_element_value(xml_obj, PCMK_XA_ID);
447 if (id == NULL) {
448 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
449 xml_obj->name);
450 return;
451 }
452
453 rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST,
455 if (rsc_first == NULL) {
456 return;
457 }
458
459 rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN,
461 if (rsc_then == NULL) {
462 return;
463 }
464
465 action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
466 if (action_first == NULL) {
467 action_first = PCMK_ACTION_START;
468 }
469
470 action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
471 if (action_then == NULL) {
472 action_then = action_first;
473 }
474
475 kind = get_ordering_type(xml_obj);
476
477 symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
478 flags = ordering_flags_for_kind(kind, action_first, symmetry);
479
481
482 /* If there is a minimum number of instances that must be runnable before
483 * the 'then' action is runnable, we use a pseudo-action for convenience:
484 * minimum number of clone instances have runnable actions ->
485 * pseudo-action is runnable -> dependency is runnable.
486 */
487 min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
488 if (min_required_before > 0) {
489 clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
490 flags, min_required_before);
491 } else {
492 pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
493 action_then, flags);
494 }
495
496 if (symmetry == ordering_symmetric) {
497 inverse_ordering(id, kind, rsc_first, action_first,
498 rsc_then, action_then);
499 }
500}
501
530void
531pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task,
532 pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
533 char *then_action_task, pcmk_action_t *then_action,
534 uint32_t flags, pcmk_scheduler_t *sched)
535{
536 pcmk__action_relation_t *order = NULL;
537
538 // One of action or resource must be specified for each side
539 CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
540 && ((then_action != NULL) || (then_rsc != NULL)),
541 free(first_action_task); free(then_action_task); return);
542
543 if ((first_rsc == NULL) && (first_action != NULL)) {
544 first_rsc = first_action->rsc;
545 }
546 if ((then_rsc == NULL) && (then_action != NULL)) {
547 then_rsc = then_action->rsc;
548 }
549
551
552 order->id = sched->order_id++;
553 order->flags = flags;
554 order->rsc1 = first_rsc;
555 order->rsc2 = then_rsc;
556 order->action1 = first_action;
557 order->action2 = then_action;
558 order->task1 = first_action_task;
559 order->task2 = then_action_task;
560
561 if ((order->task1 == NULL) && (first_action != NULL)) {
562 order->task1 = strdup(first_action->uuid);
563 }
564
565 if ((order->task2 == NULL) && (then_action != NULL)) {
566 order->task2 = strdup(then_action->uuid);
567 }
568
569 if ((order->rsc1 == NULL) && (first_action != NULL)) {
570 order->rsc1 = first_action->rsc;
571 }
572
573 if ((order->rsc2 == NULL) && (then_action != NULL)) {
574 order->rsc2 = then_action->rsc;
575 }
576
577 pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s",
578 (sched->order_id - 1),
579 pcmk__s(order->task1, "an underspecified action"),
580 pcmk__s(order->task2, "an underspecified action"));
581
582 sched->ordering_constraints = g_list_prepend(sched->ordering_constraints,
583 order);
585}
586
599static int
600unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind,
601 const char *parent_symmetrical_s, pcmk_scheduler_t *scheduler)
602{
603 GList *set_iter = NULL;
604 GList *resources = NULL;
605
606 pcmk_resource_t *last = NULL;
607 pcmk_resource_t *resource = NULL;
608
609 int local_kind = parent_kind;
610 bool sequential = false;
611 uint32_t flags = pcmk__ar_ordered;
612 enum ordering_symmetry symmetry;
613
614 char *key = NULL;
615 const char *id = pcmk__xe_id(set);
616 const char *action = crm_element_value(set, PCMK_XA_ACTION);
617 const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL);
618 const char *kind_s = crm_element_value(set, PCMK_XA_KIND);
619
620 if (action == NULL) {
622 }
623
624 if (kind_s) {
625 local_kind = get_ordering_type(set);
626 }
627 if (sequential_s == NULL) {
628 sequential_s = "1";
629 }
630
631 sequential = crm_is_true(sequential_s);
632
633 symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
634 flags = ordering_flags_for_kind(local_kind, action, symmetry);
635
636 for (const xmlNode *xml_rsc = pcmk__xe_first_child(set,
638 NULL, NULL);
639 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
640
641 EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc));
642 resources = g_list_append(resources, resource);
643 }
644
645 if (pcmk__list_of_1(resources)) {
646 crm_trace("Single set: %s", id);
647 goto done;
648 }
649
650 set_iter = resources;
651 while (set_iter != NULL) {
652 resource = (pcmk_resource_t *) set_iter->data;
653 set_iter = set_iter->next;
654
655 key = pcmk__op_key(resource->id, action, 0);
656
657 if (local_kind == pe_order_kind_serialize) {
658 /* Serialize before everything that comes after */
659
660 for (GList *iter = set_iter; iter != NULL; iter = iter->next) {
661 pcmk_resource_t *then_rsc = iter->data;
662 char *then_key = pcmk__op_key(then_rsc->id, action, 0);
663
664 pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
665 then_key, NULL, flags, scheduler);
666 }
667
668 } else if (sequential) {
669 if (last != NULL) {
671 flags);
672 }
673 last = resource;
674 }
675 free(key);
676 }
677
678 if (symmetry == ordering_asymmetric) {
679 goto done;
680 }
681
682 last = NULL;
683 action = invert_action(action);
684
685 flags = ordering_flags_for_kind(local_kind, action,
687
688 set_iter = resources;
689 while (set_iter != NULL) {
690 resource = (pcmk_resource_t *) set_iter->data;
691 set_iter = set_iter->next;
692
693 if (sequential) {
694 if (last != NULL) {
696 flags);
697 }
698 last = resource;
699 }
700 }
701
702 done:
703 g_list_free(resources);
704 return pcmk_rc_ok;
705}
706
719static int
720order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
722 enum ordering_symmetry symmetry)
723{
724
725 const xmlNode *xml_rsc = NULL;
726 const xmlNode *xml_rsc_2 = NULL;
727
728 pcmk_resource_t *rsc_1 = NULL;
729 pcmk_resource_t *rsc_2 = NULL;
730
731 const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION);
732 const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION);
733
734 uint32_t flags = pcmk__ar_none;
735
736 bool require_all = true;
737
738 (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all);
739
740 if (action_1 == NULL) {
741 action_1 = PCMK_ACTION_START;
742 }
743
744 if (action_2 == NULL) {
745 action_2 = PCMK_ACTION_START;
746 }
747
748 if (symmetry == ordering_symmetric_inverse) {
749 action_1 = invert_action(action_1);
750 action_2 = invert_action(action_2);
751 }
752
753 if (pcmk__str_eq(PCMK_ACTION_STOP, action_1, pcmk__str_none)
754 || pcmk__str_eq(PCMK_ACTION_DEMOTE, action_1, pcmk__str_none)) {
755 /* Assuming: A -> ( B || C) -> D
756 * The one-or-more logic only applies during the start/promote phase.
757 * During shutdown neither B nor can shutdown until D is down, so simply
758 * turn require_all back on.
759 */
760 require_all = true;
761 }
762
763 flags = ordering_flags_for_kind(kind, action_1, symmetry);
764
765 /* If we have an unordered set1, whether it is sequential or not is
766 * irrelevant in regards to set2.
767 */
768 if (!require_all) {
770 pcmk__xe_id(set1));
771 pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler);
772
773 free(task);
775
776 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
777 NULL);
778 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
779
780 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
781
782 /* Add an ordering constraint between every element in set1 and the
783 * pseudo action. If any action in set1 is runnable the pseudo
784 * action will be runnable.
785 */
786 pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
787 NULL, NULL, NULL, unordered_action,
790 scheduler);
791 }
792 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
793 NULL);
794 xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
795
796 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
797
798 /* Add an ordering constraint between the pseudo-action and every
799 * element in set2. If the pseudo-action is runnable, every action
800 * in set2 will be runnable.
801 */
802 pcmk__new_ordering(NULL, NULL, unordered_action,
803 rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
805 scheduler);
806 }
807
808 return pcmk_rc_ok;
809 }
810
812 if (symmetry == ordering_symmetric_inverse) {
813 // Get the first one
814 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
815 NULL);
816 if (xml_rsc != NULL) {
817 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
818 }
819
820 } else {
821 // Get the last one
822 const char *rid = NULL;
823
824 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF,
825 NULL, NULL);
826 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
827
828 rid = pcmk__xe_id(xml_rsc);
829 }
830 EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
831 }
832 }
833
835 if (symmetry == ordering_symmetric_inverse) {
836 // Get the last one
837 const char *rid = NULL;
838
839 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
840 NULL, NULL);
841 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
842
843 rid = pcmk__xe_id(xml_rsc);
844 }
845 EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
846
847 } else {
848 // Get the first one
849 xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
850 NULL);
851 if (xml_rsc != NULL) {
852 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
853 }
854 }
855 }
856
857 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
858 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
859
860 } else if (rsc_1 != NULL) {
861 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
862 NULL);
863 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
864
865 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
866 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
867 flags);
868 }
869
870 } else if (rsc_2 != NULL) {
871 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
872 NULL);
873 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
874
875 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
876 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
877 flags);
878 }
879
880 } else {
881 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
882 NULL);
883 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
884
885 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
886
887 for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2,
889 NULL, NULL);
890 xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
891
892 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
893 pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
894 action_2, flags);
895 }
896 }
897 }
898
899 return pcmk_rc_ok;
900}
901
913static int
914unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
916{
917 const char *id_first = NULL;
918 const char *id_then = NULL;
919 const char *action_first = NULL;
920 const char *action_then = NULL;
921
922 pcmk_resource_t *rsc_first = NULL;
923 pcmk_resource_t *rsc_then = NULL;
924 pcmk_tag_t *tag_first = NULL;
925 pcmk_tag_t *tag_then = NULL;
926
927 xmlNode *rsc_set_first = NULL;
928 xmlNode *rsc_set_then = NULL;
929 bool any_sets = false;
930
931 // Check whether there are any resource sets with template or tag references
932 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
933 if (*expanded_xml != NULL) {
934 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
935 return pcmk_rc_ok;
936 }
937
938 id_first = crm_element_value(xml_obj, PCMK_XA_FIRST);
939 id_then = crm_element_value(xml_obj, PCMK_XA_THEN);
940 if ((id_first == NULL) || (id_then == NULL)) {
941 return pcmk_rc_ok;
942 }
943
944 if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first,
945 &tag_first)) {
946 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
947 "valid resource or tag",
948 pcmk__xe_id(xml_obj), id_first);
950 }
951
952 if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then,
953 &tag_then)) {
954 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
955 "valid resource or tag",
956 pcmk__xe_id(xml_obj), id_then);
958 }
959
960 if ((rsc_first != NULL) && (rsc_then != NULL)) {
961 // Neither side references a template or tag
962 return pcmk_rc_ok;
963 }
964
965 action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
966 action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
967
968 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
969
970 /* Convert template/tag reference in PCMK_XA_FIRST into constraint
971 * PCMK_XE_RESOURCE_SET
972 */
973 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true,
974 scheduler)) {
975 free_xml(*expanded_xml);
976 *expanded_xml = NULL;
978 }
979
980 if (rsc_set_first != NULL) {
981 if (action_first != NULL) {
982 /* Move PCMK_XA_FIRST_ACTION into converted PCMK_XE_RESOURCE_SET as
983 * PCMK_XA_ACTION
984 */
985 crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first);
987 }
988 any_sets = true;
989 }
990
991 /* Convert template/tag reference in PCMK_XA_THEN into constraint
992 * PCMK_XE_RESOURCE_SET
993 */
994 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true,
995 scheduler)) {
996 free_xml(*expanded_xml);
997 *expanded_xml = NULL;
999 }
1000
1001 if (rsc_set_then != NULL) {
1002 if (action_then != NULL) {
1003 /* Move PCMK_XA_THEN_ACTION into converted PCMK_XE_RESOURCE_SET as
1004 * PCMK_XA_ACTION
1005 */
1006 crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then);
1008 }
1009 any_sets = true;
1010 }
1011
1012 if (any_sets) {
1013 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
1014 } else {
1015 free_xml(*expanded_xml);
1016 *expanded_xml = NULL;
1017 }
1018
1019 return pcmk_rc_ok;
1020}
1021
1029void
1031{
1032 xmlNode *set = NULL;
1033 xmlNode *last = NULL;
1034
1035 xmlNode *orig_xml = NULL;
1036 xmlNode *expanded_xml = NULL;
1037
1038 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
1039 const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL);
1040 enum pe_order_kind kind = get_ordering_type(xml_obj);
1041
1042 enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1043 NULL);
1044
1045 // Expand any resource tags in the constraint XML
1046 if (unpack_order_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
1047 return;
1048 }
1049 if (expanded_xml != NULL) {
1050 orig_xml = xml_obj;
1051 xml_obj = expanded_xml;
1052 }
1053
1054 // If the constraint has resource sets, unpack them
1055 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1056 set != NULL; set = pcmk__xe_next_same(set)) {
1057
1058 set = expand_idref(set, scheduler->input);
1059 if ((set == NULL) // Configuration error, message already logged
1060 || (unpack_order_set(set, kind, invert, scheduler) != pcmk_rc_ok)) {
1061
1062 if (expanded_xml != NULL) {
1063 free_xml(expanded_xml);
1064 }
1065 return;
1066 }
1067
1068 if (last != NULL) {
1069
1070 if (order_rsc_sets(id, last, set, kind, scheduler,
1071 symmetry) != pcmk_rc_ok) {
1072 if (expanded_xml != NULL) {
1073 free_xml(expanded_xml);
1074 }
1075 return;
1076 }
1077
1078 if ((symmetry == ordering_symmetric)
1079 && (order_rsc_sets(id, set, last, kind, scheduler,
1081 if (expanded_xml != NULL) {
1082 free_xml(expanded_xml);
1083 }
1084 return;
1085 }
1086
1087 }
1088 last = set;
1089 }
1090
1091 if (expanded_xml) {
1092 free_xml(expanded_xml);
1093 xml_obj = orig_xml;
1094 }
1095
1096 // If the constraint has no resource sets, unpack it as a simple ordering
1097 if (last == NULL) {
1098 return unpack_simple_rsc_order(xml_obj, scheduler);
1099 }
1100}
1101
1102static bool
1103ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input)
1104{
1105 /* Prevent user-defined ordering constraints between resources
1106 * running in a guest node and the resource that defines that node.
1107 */
1109 && (input->action->rsc != NULL)
1110 && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1111
1112 pcmk__config_warn("Invalid ordering constraint between %s and %s",
1113 input->action->rsc->id, action->rsc->id);
1114 return true;
1115 }
1116
1117 /* If there's an order like
1118 * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
1119 *
1120 * then rscA is being migrated from node1 to node2, while rscB is being
1121 * migrated from node2 to node1. If there would be a graph loop,
1122 * break the order "load_stopped_node2" -> "rscA_migrate_to node1".
1123 */
1124 if (((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target)
1125 && (action->rsc != NULL)
1126 && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)
1128 return true;
1129 }
1130
1131 return false;
1132}
1133
1134void
1136{
1137 for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
1138 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1140
1141 for (GList *input_iter = action->actions_before;
1142 input_iter != NULL; input_iter = input_iter->next) {
1143
1144 input = input_iter->data;
1145 if (ordering_is_invalid(action, input)) {
1147 }
1148 }
1149 }
1150}
1151
1159void
1161{
1162 for (GList *iter = node->details->data_set->actions;
1163 iter != NULL; iter = iter->next) {
1164
1165 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1166
1167 // Only stops on the node shutting down are relevant
1168 if (!pcmk__same_node(action->node, node)
1169 || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1170 continue;
1171 }
1172
1173 // Resources and nodes in maintenance mode won't be touched
1174
1175 if (pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)) {
1177 "Not ordering %s before shutdown of %s because "
1178 "resource in maintenance mode",
1179 action->uuid, pcmk__node_name(node));
1180 continue;
1181
1182 } else if (node->details->maintenance) {
1184 "Not ordering %s before shutdown of %s because "
1185 "node in maintenance mode",
1186 action->uuid, pcmk__node_name(node));
1187 continue;
1188 }
1189
1190 /* Don't touch a resource that is unmanaged or blocked, to avoid
1191 * blocking the shutdown (though if another action depends on this one,
1192 * we may still end up blocking)
1193 */
1194 if (!pcmk_any_flags_set(action->rsc->flags,
1197 "Not ordering %s before shutdown of %s because "
1198 "resource is unmanaged or blocked",
1199 action->uuid, pcmk__node_name(node));
1200 continue;
1201 }
1202
1203 pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1204 action->uuid, pcmk__node_name(node));
1206 pcmk__new_ordering(action->rsc, NULL, action, NULL,
1207 strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op,
1209 node->details->data_set);
1210 }
1211}
1212
1223static GList *
1224find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key)
1225{
1226 // Search under given task key directly
1227 GList *list = find_actions(rsc->actions, original_key, NULL);
1228
1229 if (list == NULL) {
1230 // Search again using this resource's ID
1231 char *key = NULL;
1232 char *task = NULL;
1233 guint interval_ms = 0;
1234
1235 CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms),
1236 return NULL);
1237 key = pcmk__op_key(rsc->id, task, interval_ms);
1238 list = find_actions(rsc->actions, key, NULL);
1239 free(key);
1240 free(task);
1241 }
1242 return list;
1243}
1244
1253static void
1254order_resource_actions_after(pcmk_action_t *first_action,
1255 const pcmk_resource_t *rsc,
1257{
1258 GList *then_actions = NULL;
1259 uint32_t flags = pcmk__ar_none;
1260
1261 CRM_CHECK((rsc != NULL) && (order != NULL), return);
1262
1263 flags = order->flags;
1264 pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1265 order->id, rsc->id);
1266
1267 if (order->action2 != NULL) {
1268 then_actions = g_list_prepend(NULL, order->action2);
1269
1270 } else {
1271 then_actions = find_actions_by_task(rsc, order->task2);
1272 }
1273
1274 if (then_actions == NULL) {
1275 pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1276 order->id, order->task2, rsc->id);
1277 return;
1278 }
1279
1280 if ((first_action != NULL) && (first_action->rsc == rsc)
1281 && pcmk_is_set(first_action->flags, pcmk_action_migration_abort)) {
1282
1283 pcmk__rsc_trace(rsc,
1284 "Detected dangling migration ordering (%s then %s %s)",
1285 first_action->uuid, order->task2, rsc->id);
1287 }
1288
1289 if ((first_action == NULL)
1291
1292 pcmk__rsc_debug(rsc,
1293 "Ignoring ordering %d for %s: No first action found",
1294 order->id, rsc->id);
1295 g_list_free(then_actions);
1296 return;
1297 }
1298
1299 for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1300 pcmk_action_t *then_action_iter = (pcmk_action_t *) iter->data;
1301
1302 if (first_action != NULL) {
1303 order_actions(first_action, then_action_iter, flags);
1304 } else {
1306 crm_warn("%s of %s is unrunnable because there is no %s of %s "
1307 "to order it after", then_action_iter->task, rsc->id,
1308 order->task1, order->rsc1->id);
1309 }
1310 }
1311
1312 g_list_free(then_actions);
1313}
1314
1315static void
1316rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order)
1317{
1318 GList *first_actions = NULL;
1319 pcmk_action_t *first_action = order->action1;
1320 pcmk_resource_t *then_rsc = order->rsc2;
1321
1322 CRM_ASSERT(first_rsc != NULL);
1323 pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1324 order->id, first_rsc->id);
1325
1326 if (first_action != NULL) {
1327 first_actions = g_list_prepend(NULL, first_action);
1328
1329 } else {
1330 first_actions = find_actions_by_task(first_rsc, order->task1);
1331 }
1332
1333 if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1334 pcmk__rsc_trace(first_rsc,
1335 "Ignoring constraint %d: first (%s for %s) not found",
1336 order->id, order->task1, first_rsc->id);
1337
1338 } else if (first_actions == NULL) {
1339 char *key = NULL;
1340 char *op_type = NULL;
1341 guint interval_ms = 0;
1342
1343 parse_op_key(order->task1, NULL, &op_type, &interval_ms);
1344 key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1345
1346 if ((first_rsc->fns->state(first_rsc, TRUE) == pcmk_role_stopped)
1347 && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) {
1348 free(key);
1349 pcmk__rsc_trace(first_rsc,
1350 "Ignoring constraint %d: first (%s for %s) "
1351 "not found",
1352 order->id, order->task1, first_rsc->id);
1353
1354 } else if ((first_rsc->fns->state(first_rsc,
1355 TRUE) == pcmk_role_unpromoted)
1356 && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE,
1357 pcmk__str_none)) {
1358 free(key);
1359 pcmk__rsc_trace(first_rsc,
1360 "Ignoring constraint %d: first (%s for %s) "
1361 "not found",
1362 order->id, order->task1, first_rsc->id);
1363
1364 } else {
1365 pcmk__rsc_trace(first_rsc,
1366 "Creating first (%s for %s) for constraint %d ",
1367 order->task1, first_rsc->id, order->id);
1368 first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1369 first_rsc->cluster);
1370 first_actions = g_list_prepend(NULL, first_action);
1371 }
1372
1373 free(op_type);
1374 }
1375
1376 if (then_rsc == NULL) {
1377 if (order->action2 == NULL) {
1378 pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1379 order->id);
1380 return;
1381 }
1382 then_rsc = order->action2->rsc;
1383 }
1384 for (GList *iter = first_actions; iter != NULL; iter = iter->next) {
1385 first_action = iter->data;
1386
1387 if (then_rsc == NULL) {
1388 order_actions(first_action, order->action2, order->flags);
1389
1390 } else {
1391 order_resource_actions_after(first_action, then_rsc, order);
1392 }
1393 }
1394
1395 g_list_free(first_actions);
1396}
1397
1398// GFunc to call pcmk__block_colocation_dependents()
1399static void
1400block_colocation_dependents(gpointer data, gpointer user_data)
1401{
1403}
1404
1405// GFunc to call pcmk__update_action_for_orderings()
1406static void
1407update_action_for_orderings(gpointer data, gpointer user_data)
1408{
1410 (pcmk_scheduler_t *) user_data);
1411}
1412
1419void
1421{
1422 crm_trace("Applying ordering constraints");
1423
1424 /* Ordering constraints need to be processed in the order they were created.
1425 * rsc_order_first() and order_resource_actions_after() require the relevant
1426 * actions to already exist in some cases, but rsc_order_first() will create
1427 * the 'first' action in certain cases. Thus calling rsc_order_first() can
1428 * change the behavior of later-created orderings.
1429 *
1430 * Also, g_list_append() should be avoided for performance reasons, so we
1431 * prepend orderings when creating them and reverse the list here.
1432 *
1433 * @TODO This is brittle and should be carefully redesigned so that the
1434 * order of creation doesn't matter, and the reverse becomes unneeded.
1435 */
1436 sched->ordering_constraints = g_list_reverse(sched->ordering_constraints);
1437
1438 for (GList *iter = sched->ordering_constraints;
1439 iter != NULL; iter = iter->next) {
1440
1441 pcmk__action_relation_t *order = iter->data;
1442 pcmk_resource_t *rsc = order->rsc1;
1443
1444 if (rsc != NULL) {
1445 rsc_order_first(rsc, order);
1446 continue;
1447 }
1448
1449 rsc = order->rsc2;
1450 if (rsc != NULL) {
1451 order_resource_actions_after(order->action1, rsc, order);
1452
1453 } else {
1454 crm_trace("Applying ordering constraint %d (non-resource actions)",
1455 order->id);
1456 order_actions(order->action1, order->action2, order->flags);
1457 }
1458 }
1459
1460 g_list_foreach(sched->actions, block_colocation_dependents, NULL);
1461
1462 crm_trace("Ordering probes");
1463 pcmk__order_probes(sched);
1464
1465 crm_trace("Updating %d actions", g_list_length(sched->actions));
1466 g_list_foreach(sched->actions, update_action_for_orderings, sched);
1467
1469}
1470
1478void
1480{
1481 const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1482
1483 for (GList *iter = list; iter != NULL; iter = iter->next) {
1484 pcmk_action_t *before = (pcmk_action_t *) iter->data;
1485 const char *before_desc = before->task? before->task : before->uuid;
1486
1487 crm_debug("Ordering %s on %s before %s on %s",
1488 before_desc, pcmk__node_name(before->node),
1489 after_desc, pcmk__node_name(after->node));
1490 order_actions(before, after, pcmk__ar_ordered);
1491 }
1492}
1493
1500void
1502{
1503 // Order start and promote after all instances are stopped
1505 rsc, PCMK_ACTION_START,
1510
1511 // Order stop, start, and promote after all instances are demoted
1513 rsc, PCMK_ACTION_STOP,
1516 rsc, PCMK_ACTION_START,
1521
1522 // Order promote after all instances are started
1526
1527 // Order demote after all instances are demoted
1531}
@ pcmk__ar_first_implies_then
@ pcmk__ar_asymmetric
User-configured asymmetric ordering.
@ pcmk__ar_then_implies_first
@ pcmk__ar_min_runnable
'then' action is runnable if certain number of 'first' instances are
@ pcmk__ar_none
No relation (compare with equality rather than bit set)
@ pcmk__ar_first_implies_then_graphed
If 'first' is required and runnable, 'then' must be in graph.
@ pcmk__ar_unrunnable_first_blocks
'then' is runnable (and migratable) only if 'first' is runnable
@ pcmk__ar_ordered
Actions are ordered (optionally, if no other flags are set)
@ pcmk__ar_if_on_same_node_or_target
Actions are ordered if on same node (or migration target for migrate_to)
@ pcmk__ar_guest_allowed
Ordering applies even if 'first' runs on guest node created by 'then'.
#define pcmk__clear_relation_flags(ar_flags, flags_to_clear)
#define pcmk__set_relation_flags(ar_flags, flags_to_set)
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition actions.c:250
#define PCMK_ACTION_PROMOTED
Definition actions.h:67
#define PCMK_ACTION_STOP
Definition actions.h:75
#define PCMK_ACTION_RUNNING
Definition actions.h:71
pe_ordering
Definition actions.h:283
#define PCMK_ACTION_ONE_OR_MORE
Definition actions.h:65
#define PCMK_ACTION_PROMOTE
Definition actions.h:66
#define PCMK_ACTION_START
Definition actions.h:72
@ pcmk_action_runnable
Definition actions.h:207
@ pcmk_action_optional
Definition actions.h:210
@ pcmk_action_migration_abort
Definition actions.h:225
@ pcmk_action_min_runnable
Definition actions.h:233
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:59
#define PCMK_ACTION_STOPPED
Definition actions.h:76
#define PCMK_ACTION_CLONE_ONE_OR_MORE
Definition actions.h:47
#define PCMK_ACTION_DEMOTED
Definition actions.h:50
#define PCMK_ACTION_DEMOTE
Definition actions.h:49
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:51
#define pcmk__set_action_flags(action, flags_to_set)
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition actions.c:196
#define pcmk__clear_action_flags(action, flags_to_clear)
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition nvpair.c:909
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition nvpair.c:936
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:297
uint64_t flags
Definition remote.c:3
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
gboolean crm_is_true(const char *s)
Definition strings.c:488
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:98
char data[0]
Definition cpg.c:10
A dumping ground.
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk_tag_t **tag)
G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pcmk_action_t *action)
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
G_GNUC_INTERNAL pcmk_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, const pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, const pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL bool pcmk__graph_has_loop(const pcmk_action_t *init_action, const pcmk_action_t *action, pcmk__related_action_t *input)
G_GNUC_INTERNAL bool pcmk__rsc_corresponds_to_guest(const pcmk_resource_t *rsc, const pcmk_node_t *node)
void pcmk__order_migration_equivalents(pcmk__action_relation_t *order)
G_GNUC_INTERNAL void pcmk__order_probes(pcmk_scheduler_t *scheduler)
#define crm_warn(fmt, args...)
Definition logging.h:392
#define CRM_CHECK(expr, failure_action)
Definition logging.h:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define crm_log_xml_trace(xml, text)
Definition logging.h:410
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
@ pcmk__wo_order_inst
@ pcmk__wo_require_all
@ pcmk__wo_order_score
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_scheduler_t * scheduler
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition nvpair.c:301
#define PCMK_VALUE_SERIALIZE
Definition options.h:204
#define PCMK_VALUE_OPTIONAL
Definition options.h:186
#define PCMK_META_CLONE_MIN
Definition options.h:83
#define PCMK_VALUE_MANDATORY
Definition options.h:168
const char * action
Definition pcmk_fence.c:30
void pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op)
void pcmk__apply_orderings(pcmk_scheduler_t *sched)
void pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task, pcmk_action_t *first_action, pcmk_resource_t *then_rsc, char *then_action_task, pcmk_action_t *then_action, uint32_t flags, pcmk_scheduler_t *sched)
#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name)
void pcmk__disable_invalid_orderings(pcmk_scheduler_t *scheduler)
#define handle_restart_type(rsc, kind, flag, flags)
@ pe_order_kind_optional
@ pe_order_kind_mandatory
@ pe_order_kind_serialize
void pcmk__order_after_each(pcmk_action_t *after, GList *list)
ordering_symmetry
@ ordering_symmetric
@ ordering_symmetric_inverse
@ ordering_asymmetric
void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
void pcmk__promotable_restart_ordering(pcmk_resource_t *rsc)
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
GList * find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
gboolean order_actions(pcmk_action_t *lh_action, pcmk_action_t *rh_action, uint32_t flags)
Definition utils.c:457
pcmk_resource_t * find_clone_instance(const pcmk_resource_t *rsc, const char *sub_id)
Definition clone.c:229
pcmk_action_t * custom_action(pcmk_resource_t *rsc, char *key, const char *task, const pcmk_node_t *on_node, gboolean optional, pcmk_scheduler_t *scheduler)
Create or update an action object.
#define ENODATA
@ pcmk_rsc_maintenance
Definition resources.h:166
@ pcmk_rsc_blocked
Definition resources.h:91
@ pcmk_rsc_managed
Definition resources.h:88
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_unpack_error
Definition results.h:125
@ pcmk_role_unpromoted
Unpromoted.
Definition roles.h:38
@ pcmk_role_stopped
Stopped.
Definition roles.h:36
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__rsc_debug(rsc, fmt, args...)
int char2score(const char *score)
Get the integer value of a score string.
Definition scores.c:36
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1026
@ pcmk__str_none
pcmk_node_t * node
Definition actions.h:341
char * uuid
Definition actions.h:344
char * task
Definition actions.h:343
enum pe_action_flags flags
Definition actions.h:349
int required_runnable_before
Definition actions.h:367
pcmk_resource_t * rsc
Definition actions.h:340
enum pe_ordering type
Definition actions.h:317
struct pe_node_shared_s * details
Definition nodes.h:167
pcmk_scheduler_t * data_set
Definition nodes.h:153
gboolean maintenance
Definition nodes.h:104
GList * actions
Definition resources.h:444
GHashTable * meta
Definition resources.h:467
GList * children
Definition resources.h:471
pcmk_scheduler_t * cluster
Definition resources.h:408
pcmk_rsc_methods_t * fns
Definition resources.h:412
xmlNode * input
Definition scheduler.h:196
GList * resources
Definition scheduler.h:231
GList * ordering_constraints
Definition scheduler.h:233
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, gboolean current)
Definition resources.h:316
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2152
void free_xml(xmlNode *child)
Definition xml.c:867
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition xml.c:652
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:883
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
xmlNode * pcmk__xe_next_same(const xmlNode *node)
Definition xml.c:2108
#define PCMK_XA_REQUIRE_ALL
Definition xml_names.h:376
#define PCMK_XA_SCORE
Definition xml_names.h:391
#define PCMK_XA_SYMMETRICAL
Definition xml_names.h:410
#define PCMK_XA_THEN
Definition xml_names.h:423
#define PCMK_XE_RESOURCE_REF
Definition xml_names.h:173
#define PCMK_XE_RSC_ORDER
Definition xml_names.h:185
#define PCMK_XA_ID
Definition xml_names.h:296
#define PCMK_XA_KIND
Definition xml_names.h:307
#define PCMK_XE_RESOURCE_SET
Definition xml_names.h:174
#define PCMK_XA_ACTION
Definition xml_names.h:224
#define PCMK_XA_THEN_ACTION
Definition xml_names.h:424
#define PCMK_XA_SEQUENTIAL
Definition xml_names.h:393
#define PCMK_XA_FIRST_ACTION
Definition xml_names.h:284
#define PCMK_XA_FIRST
Definition xml_names.h:283
#define PCMK__XA_THEN_INSTANCE
#define PCMK__XA_FIRST_INSTANCE