pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
pcmk_sched_ordering.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-2022 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
24};
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(data_set->resources, __name); \
34 if (__rsc == NULL) { \
35 pcmk__config_err("%s: No resource found for %s", __set, __name); \
36 return pcmk_rc_unpack_error; \
37 } \
38 } while (0)
39
40static const char *
41invert_action(const char *action)
42{
43 if (pcmk__str_eq(action, RSC_START, pcmk__str_casei)) {
44 return RSC_STOP;
45
46 } else if (pcmk__str_eq(action, RSC_STOP, pcmk__str_casei)) {
47 return RSC_START;
48
49 } else if (pcmk__str_eq(action, RSC_PROMOTE, pcmk__str_casei)) {
50 return RSC_DEMOTE;
51
52 } else if (pcmk__str_eq(action, RSC_DEMOTE, pcmk__str_casei)) {
53 return RSC_PROMOTE;
54
55 } else if (pcmk__str_eq(action, RSC_PROMOTED, pcmk__str_casei)) {
56 return RSC_DEMOTED;
57
58 } else if (pcmk__str_eq(action, RSC_DEMOTED, pcmk__str_casei)) {
59 return RSC_PROMOTED;
60
61 } else if (pcmk__str_eq(action, RSC_STARTED, pcmk__str_casei)) {
62 return RSC_STOPPED;
63
64 } else if (pcmk__str_eq(action, RSC_STOPPED, pcmk__str_casei)) {
65 return RSC_STARTED;
66 }
67 crm_warn("Unknown action '%s' specified in order constraint", action);
68 return NULL;
69}
70
71static enum pe_order_kind
72get_ordering_type(xmlNode *xml_obj)
73{
75 const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND);
76
77 if (kind == NULL) {
78 const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
79
81
82 if (score) {
83 // @COMPAT deprecated informally since 1.0.7, formally since 2.0.1
84 int score_i = char2score(score);
85
86 if (score_i == 0) {
88 }
90 "Support for 'score' in rsc_order is deprecated "
91 "and will be removed in a future release "
92 "(use 'kind' instead)");
93 }
94
95 } else if (pcmk__str_eq(kind, "Mandatory", pcmk__str_casei)) {
97
98 } else if (pcmk__str_eq(kind, "Optional", pcmk__str_casei)) {
100
101 } else if (pcmk__str_eq(kind, "Serialize", pcmk__str_casei)) {
103
104 } else {
105 pcmk__config_err("Resetting '" XML_ORDER_ATTR_KIND "' for constraint "
106 "%s to 'Mandatory' because '%s' is not valid",
107 pcmk__s(ID(xml_obj), "missing ID"), kind);
108 }
109 return kind_e;
110}
111
123static enum ordering_symmetry
124get_ordering_symmetry(xmlNode *xml_obj, enum pe_order_kind parent_kind,
125 const char *parent_symmetrical_s)
126{
127 int rc = pcmk_rc_ok;
128 bool symmetric = false;
129 enum pe_order_kind kind = parent_kind; // Default to parent's kind
130
131 // Check ordering XML for explicit kind
132 if ((crm_element_value(xml_obj, XML_ORDER_ATTR_KIND) != NULL)
133 || (crm_element_value(xml_obj, XML_RULE_ATTR_SCORE) != NULL)) {
134 kind = get_ordering_type(xml_obj);
135 }
136
137 // Check ordering XML (and parent) for explicit symmetrical setting
138 rc = pcmk__xe_get_bool_attr(xml_obj, XML_CONS_ATTR_SYMMETRICAL, &symmetric);
139
140 if (rc != pcmk_rc_ok && parent_symmetrical_s != NULL) {
141 symmetric = crm_is_true(parent_symmetrical_s);
142 rc = pcmk_rc_ok;
143 }
144
145 if (rc == pcmk_rc_ok) {
146 if (symmetric) {
147 if (kind == pe_order_kind_serialize) {
149 " for '%s' because not valid with "
150 XML_ORDER_ATTR_KIND " of 'Serialize'",
151 ID(xml_obj));
152 } else {
153 return ordering_symmetric;
154 }
155 }
156 return ordering_asymmetric;
157 }
158
159 // Use default symmetry
160 if (kind == pe_order_kind_serialize) {
161 return ordering_asymmetric;
162 }
163 return ordering_symmetric;
164}
165
176static uint32_t
177ordering_flags_for_kind(enum pe_order_kind kind, const char *first,
178 enum ordering_symmetry symmetry)
179{
180 uint32_t flags = pe_order_none; // so we trace-log all flags set
181
183
184 switch (kind) {
186 break;
187
190 break;
191
193 switch (symmetry) {
196 break;
197
201 NULL)) {
203 }
204 break;
205
208 break;
209 }
210 break;
211 }
212 return flags;
213}
214
228static pe_resource_t *
229get_ordering_resource(xmlNode *xml, const char *resource_attr,
230 const char *instance_attr, pe_working_set_t *data_set)
231{
232 // @COMPAT: instance_attr and instance_id variables deprecated since 2.1.5
233 pe_resource_t *rsc = NULL;
234 const char *rsc_id = crm_element_value(xml, resource_attr);
235 const char *instance_id = crm_element_value(xml, instance_attr);
236
237 if (rsc_id == NULL) {
238 pcmk__config_err("Ignoring constraint '%s' without %s",
239 ID(xml), resource_attr);
240 return NULL;
241 }
242
244 if (rsc == NULL) {
245 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
246 "does not exist", ID(xml), rsc_id);
247 return NULL;
248 }
249
250 if (instance_id != NULL) {
252 "Support for " XML_ORDER_ATTR_FIRST_INSTANCE " and "
253 XML_ORDER_ATTR_THEN_INSTANCE " is deprecated and will be "
254 "removed in a future release.");
255
256 if (!pe_rsc_is_clone(rsc)) {
257 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
258 "is not a clone but instance '%s' was requested",
259 ID(xml), rsc_id, instance_id);
260 return NULL;
261 }
262 rsc = find_clone_instance(rsc, instance_id, data_set);
263 if (rsc == NULL) {
264 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
265 "does not have an instance '%s'",
266 "'%s'", ID(xml), rsc_id, instance_id);
267 return NULL;
268 }
269 }
270 return rsc;
271}
272
282static int
283get_minimum_first_instances(pe_resource_t *rsc, xmlNode *xml)
284{
285 const char *clone_min = NULL;
286 bool require_all = false;
287
288 if (!pe_rsc_is_clone(rsc)) {
289 return 0;
290 }
291
292 clone_min = g_hash_table_lookup(rsc->meta,
294 if (clone_min != NULL) {
295 int clone_min_int = 0;
296
297 pcmk__scan_min_int(clone_min, &clone_min_int, 0);
298 return clone_min_int;
299 }
300
301 /* @COMPAT 1.1.13:
302 * require-all=false is deprecated equivalent of clone-min=1
303 */
304 if (pcmk__xe_get_bool_attr(xml, "require-all", &require_all) != ENODATA) {
306 "Support for require-all in ordering constraints "
307 "is deprecated and will be removed in a future release"
308 " (use clone-min clone meta-attribute instead)");
309 if (!require_all) {
310 return 1;
311 }
312 }
313
314 return 0;
315}
316
330static void
331clone_min_ordering(const char *id,
332 pe_resource_t *rsc_first, const char *action_first,
333 pe_resource_t *rsc_then, const char *action_then,
334 uint32_t flags, int clone_min, pe_working_set_t *data_set)
335{
336 // Create a pseudo-action for when the minimum instances are active
337 char *task = crm_strdup_printf(CRM_OP_RELAXED_CLONE ":%s", id);
338 pe_action_t *clone_min_met = get_pseudo_op(task, data_set);
339
340 free(task);
341
342 /* Require the pseudo-action to have the required number of actions to be
343 * considered runnable before allowing the pseudo-action to be runnable.
344 */
345 clone_min_met->required_runnable_before = clone_min;
347
348 // Order the actions for each clone instance before the pseudo-action
349 for (GList *rIter = rsc_first->children; rIter != NULL;
350 rIter = rIter->next) {
351
352 pe_resource_t *child = rIter->data;
353
354 pcmk__new_ordering(child, pcmk__op_key(child->id, action_first, 0),
355 NULL, NULL, NULL, clone_min_met,
357 data_set);
358 }
359
360 // Order "then" action after the pseudo-action (if runnable)
361 pcmk__new_ordering(NULL, NULL, clone_min_met, rsc_then,
362 pcmk__op_key(rsc_then->id, action_then, 0),
364}
365
379#define handle_restart_type(rsc, kind, flag, flags) do { \
380 if (((kind) == pe_order_kind_optional) \
381 && ((rsc)->restart_type == pe_restart_restart)) { \
382 pe__set_order_flags((flags), (flag)); \
383 } \
384 } while (0)
385
398static void
399inverse_ordering(const char *id, enum pe_order_kind kind,
400 pe_resource_t *rsc_first, const char *action_first,
401 pe_resource_t *rsc_then, const char *action_then,
403{
404 action_then = invert_action(action_then);
405 action_first = invert_action(action_first);
406 if ((action_then == NULL) || (action_first == NULL)) {
407 pcmk__config_warn("Cannot invert constraint '%s' "
408 "(please specify inverse manually)", id);
409 } else {
410 uint32_t flags = ordering_flags_for_kind(kind, action_first,
412
414 pcmk__order_resource_actions(rsc_then, action_then, rsc_first,
415 action_first, flags);
416 }
417}
418
419static void
420unpack_simple_rsc_order(xmlNode *xml_obj, pe_working_set_t *data_set)
421{
422 pe_resource_t *rsc_then = NULL;
423 pe_resource_t *rsc_first = NULL;
424 int min_required_before = 0;
426 uint32_t cons_weight = pe_order_none;
427 enum ordering_symmetry symmetry;
428
429 const char *action_then = NULL;
430 const char *action_first = NULL;
431 const char *id = NULL;
432
433 CRM_CHECK(xml_obj != NULL, return);
434
435 id = crm_element_value(xml_obj, XML_ATTR_ID);
436 if (id == NULL) {
437 pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
438 crm_element_name(xml_obj));
439 return;
440 }
441
442 rsc_first = get_ordering_resource(xml_obj, XML_ORDER_ATTR_FIRST,
444 data_set);
445 if (rsc_first == NULL) {
446 return;
447 }
448
449 rsc_then = get_ordering_resource(xml_obj, XML_ORDER_ATTR_THEN,
451 data_set);
452 if (rsc_then == NULL) {
453 return;
454 }
455
456 action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
457 if (action_first == NULL) {
458 action_first = RSC_START;
459 }
460
461 action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
462 if (action_then == NULL) {
463 action_then = action_first;
464 }
465
466 kind = get_ordering_type(xml_obj);
467
468 symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
469 cons_weight = ordering_flags_for_kind(kind, action_first, symmetry);
470
471 handle_restart_type(rsc_then, kind, pe_order_implies_then, cons_weight);
472
473 /* If there is a minimum number of instances that must be runnable before
474 * the 'then' action is runnable, we use a pseudo-action for convenience:
475 * minimum number of clone instances have runnable actions ->
476 * pseudo-action is runnable -> dependency is runnable.
477 */
478 min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
479 if (min_required_before > 0) {
480 clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
481 cons_weight, min_required_before, data_set);
482 } else {
483 pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
484 action_then, cons_weight);
485 }
486
487 if (symmetry == ordering_symmetric) {
488 inverse_ordering(id, kind, rsc_first, action_first,
489 rsc_then, action_then, data_set);
490 }
491}
492
521void
522pcmk__new_ordering(pe_resource_t *first_rsc, char *first_action_task,
523 pe_action_t *first_action, pe_resource_t *then_rsc,
524 char *then_action_task, pe_action_t *then_action,
526{
527 pe__ordering_t *order = NULL;
528
529 // One of action or resource must be specified for each side
530 CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
531 && ((then_action != NULL) || (then_rsc != NULL)),
532 free(first_action_task); free(then_action_task); return);
533
534 if ((first_rsc == NULL) && (first_action != NULL)) {
535 first_rsc = first_action->rsc;
536 }
537 if ((then_rsc == NULL) && (then_action != NULL)) {
538 then_rsc = then_action->rsc;
539 }
540
541 order = calloc(1, sizeof(pe__ordering_t));
542 CRM_ASSERT(order != NULL);
543
544 order->id = data_set->order_id++;
545 order->flags = flags;
546 order->lh_rsc = first_rsc;
547 order->rh_rsc = then_rsc;
548 order->lh_action = first_action;
549 order->rh_action = then_action;
550 order->lh_action_task = first_action_task;
551 order->rh_action_task = then_action_task;
552
553 if ((order->lh_action_task == NULL) && (first_action != NULL)) {
554 order->lh_action_task = strdup(first_action->uuid);
555 }
556
557 if ((order->rh_action_task == NULL) && (then_action != NULL)) {
558 order->rh_action_task = strdup(then_action->uuid);
559 }
560
561 if ((order->lh_rsc == NULL) && (first_action != NULL)) {
562 order->lh_rsc = first_action->rsc;
563 }
564
565 if ((order->rh_rsc == NULL) && (then_action != NULL)) {
566 order->rh_rsc = then_action->rsc;
567 }
568
569 pe_rsc_trace(first_rsc, "Created ordering %d for %s then %s",
570 (data_set->order_id - 1),
571 ((first_action_task == NULL)? "?" : first_action_task),
572 ((then_action_task == NULL)? "?" : then_action_task));
573
575 order);
577}
578
589static int
590unpack_order_set(xmlNode *set, enum pe_order_kind parent_kind,
591 const char *parent_symmetrical_s, pe_working_set_t *data_set)
592{
593 xmlNode *xml_rsc = NULL;
594 GList *set_iter = NULL;
595 GList *resources = NULL;
596
597 pe_resource_t *last = NULL;
598 pe_resource_t *resource = NULL;
599
600 int local_kind = parent_kind;
601 bool sequential = false;
602 uint32_t flags = pe_order_optional;
603 enum ordering_symmetry symmetry;
604
605 char *key = NULL;
606 const char *id = ID(set);
607 const char *action = crm_element_value(set, "action");
608 const char *sequential_s = crm_element_value(set, "sequential");
609 const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND);
610
611 if (action == NULL) {
613 }
614
615 if (kind_s) {
616 local_kind = get_ordering_type(set);
617 }
618 if (sequential_s == NULL) {
619 sequential_s = "1";
620 }
621
622 sequential = crm_is_true(sequential_s);
623
624 symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
625 flags = ordering_flags_for_kind(local_kind, action, symmetry);
626
627 for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
628 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
629
630 EXPAND_CONSTRAINT_IDREF(id, resource, ID(xml_rsc));
631 resources = g_list_append(resources, resource);
632 }
633
634 if (pcmk__list_of_1(resources)) {
635 crm_trace("Single set: %s", id);
636 goto done;
637 }
638
639 set_iter = resources;
640 while (set_iter != NULL) {
641 resource = (pe_resource_t *) set_iter->data;
642 set_iter = set_iter->next;
643
644 key = pcmk__op_key(resource->id, action, 0);
645
646 if (local_kind == pe_order_kind_serialize) {
647 /* Serialize before everything that comes after */
648
649 for (GList *gIter = set_iter; gIter != NULL; gIter = gIter->next) {
650 pe_resource_t *then_rsc = (pe_resource_t *) gIter->data;
651 char *then_key = pcmk__op_key(then_rsc->id, action, 0);
652
653 pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
654 then_key, NULL, flags, data_set);
655 }
656
657 } else if (sequential) {
658 if (last != NULL) {
660 flags);
661 }
662 last = resource;
663 }
664 free(key);
665 }
666
667 if (symmetry == ordering_asymmetric) {
668 goto done;
669 }
670
671 last = NULL;
672 action = invert_action(action);
673
674 flags = ordering_flags_for_kind(local_kind, action,
676
677 set_iter = resources;
678 while (set_iter != NULL) {
679 resource = (pe_resource_t *) set_iter->data;
680 set_iter = set_iter->next;
681
682 if (sequential) {
683 if (last != NULL) {
685 flags);
686 }
687 last = resource;
688 }
689 }
690
691 done:
692 g_list_free(resources);
693 return pcmk_rc_ok;
694}
695
708static int
709order_rsc_sets(const char *id, xmlNode *set1, xmlNode *set2,
711 enum ordering_symmetry symmetry)
712{
713
714 xmlNode *xml_rsc = NULL;
715 xmlNode *xml_rsc_2 = NULL;
716
717 pe_resource_t *rsc_1 = NULL;
718 pe_resource_t *rsc_2 = NULL;
719
720 const char *action_1 = crm_element_value(set1, "action");
721 const char *action_2 = crm_element_value(set2, "action");
722
723 uint32_t flags = pe_order_none;
724
725 bool require_all = true;
726
727 (void) pcmk__xe_get_bool_attr(set1, "require-all", &require_all);
728
729 if (action_1 == NULL) {
730 action_1 = RSC_START;
731 }
732
733 if (action_2 == NULL) {
734 action_2 = RSC_START;
735 }
736
737 if (symmetry == ordering_symmetric_inverse) {
738 action_1 = invert_action(action_1);
739 action_2 = invert_action(action_2);
740 }
741
742 if (pcmk__str_eq(RSC_STOP, action_1, pcmk__str_casei)
743 || pcmk__str_eq(RSC_DEMOTE, action_1, pcmk__str_casei)) {
744 /* Assuming: A -> ( B || C) -> D
745 * The one-or-more logic only applies during the start/promote phase.
746 * During shutdown neither B nor can shutdown until D is down, so simply
747 * turn require_all back on.
748 */
749 require_all = true;
750 }
751
752 // @TODO is action_2 correct here?
753 flags = ordering_flags_for_kind(kind, action_2, symmetry);
754
755 /* If we have an unordered set1, whether it is sequential or not is
756 * irrelevant in regards to set2.
757 */
758 if (!require_all) {
759 char *task = crm_strdup_printf(CRM_OP_RELAXED_SET ":%s", ID(set1));
760 pe_action_t *unordered_action = get_pseudo_op(task, data_set);
761
762 free(task);
764
765 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
766 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
767
768 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
769
770 /* Add an ordering constraint between every element in set1 and the
771 * pseudo action. If any action in set1 is runnable the pseudo
772 * action will be runnable.
773 */
774 pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
775 NULL, NULL, NULL, unordered_action,
777 data_set);
778 }
779 for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
780 xml_rsc_2 != NULL; xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
781
782 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
783
784 /* Add an ordering constraint between the pseudo-action and every
785 * element in set2. If the pseudo-action is runnable, every action
786 * in set2 will be runnable.
787 */
788 pcmk__new_ordering(NULL, NULL, unordered_action,
789 rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
791 }
792
793 return pcmk_rc_ok;
794 }
795
796 if (pcmk__xe_attr_is_true(set1, "sequential")) {
797 if (symmetry == ordering_symmetric_inverse) {
798 // Get the first one
800 if (xml_rsc != NULL) {
801 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
802 }
803
804 } else {
805 // Get the last one
806 const char *rid = NULL;
807
808 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
809 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
810
811 rid = ID(xml_rsc);
812 }
813 EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
814 }
815 }
816
817 if (pcmk__xe_attr_is_true(set2, "sequential")) {
818 if (symmetry == ordering_symmetric_inverse) {
819 // Get the last one
820 const char *rid = NULL;
821
822 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
823 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
824
825 rid = ID(xml_rsc);
826 }
827 EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
828
829 } else {
830 // Get the first one
832 if (xml_rsc != NULL) {
833 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
834 }
835 }
836 }
837
838 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
839 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
840
841 } else if (rsc_1 != NULL) {
842 for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
843 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
844
845 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
846 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
847 flags);
848 }
849
850 } else if (rsc_2 != NULL) {
851 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
852 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
853
854 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
855 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
856 flags);
857 }
858
859 } else {
860 for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
861 xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
862
863 EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
864
865 for (xmlNode *xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
866 xml_rsc_2 != NULL; xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
867
868 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
869 pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
870 action_2, flags);
871 }
872 }
873 }
874
875 return pcmk_rc_ok;
876}
877
889static int
890unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
892{
893 const char *id_first = NULL;
894 const char *id_then = NULL;
895 const char *action_first = NULL;
896 const char *action_then = NULL;
897
898 pe_resource_t *rsc_first = NULL;
899 pe_resource_t *rsc_then = NULL;
900 pe_tag_t *tag_first = NULL;
901 pe_tag_t *tag_then = NULL;
902
903 xmlNode *rsc_set_first = NULL;
904 xmlNode *rsc_set_then = NULL;
905 bool any_sets = false;
906
907 // Check whether there are any resource sets with template or tag references
908 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
909 if (*expanded_xml != NULL) {
910 crm_log_xml_trace(*expanded_xml, "Expanded rsc_order");
911 return pcmk_rc_ok;
912 }
913
914 id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST);
915 id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN);
916 if ((id_first == NULL) || (id_then == NULL)) {
917 return pcmk_rc_ok;
918 }
919
920 if (!pcmk__valid_resource_or_tag(data_set, id_first, &rsc_first,
921 &tag_first)) {
922 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
923 "valid resource or tag", ID(xml_obj), id_first);
925 }
926
927 if (!pcmk__valid_resource_or_tag(data_set, id_then, &rsc_then, &tag_then)) {
928 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
929 "valid resource or tag", ID(xml_obj), id_then);
931 }
932
933 if ((rsc_first != NULL) && (rsc_then != NULL)) {
934 // Neither side references a template or tag
935 return pcmk_rc_ok;
936 }
937
938 action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
939 action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
940
941 *expanded_xml = copy_xml(xml_obj);
942
943 // Convert template/tag reference in "first" into resource_set under constraint
944 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, XML_ORDER_ATTR_FIRST,
945 true, data_set)) {
946 free_xml(*expanded_xml);
947 *expanded_xml = NULL;
949 }
950
951 if (rsc_set_first != NULL) {
952 if (action_first != NULL) {
953 // Move "first-action" into converted resource_set as "action"
954 crm_xml_add(rsc_set_first, "action", action_first);
956 }
957 any_sets = true;
958 }
959
960 // Convert template/tag reference in "then" into resource_set under constraint
961 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, XML_ORDER_ATTR_THEN,
962 true, data_set)) {
963 free_xml(*expanded_xml);
964 *expanded_xml = NULL;
966 }
967
968 if (rsc_set_then != NULL) {
969 if (action_then != NULL) {
970 // Move "then-action" into converted resource_set as "action"
971 crm_xml_add(rsc_set_then, "action", action_then);
973 }
974 any_sets = true;
975 }
976
977 if (any_sets) {
978 crm_log_xml_trace(*expanded_xml, "Expanded rsc_order");
979 } else {
980 free_xml(*expanded_xml);
981 *expanded_xml = NULL;
982 }
983
984 return pcmk_rc_ok;
985}
986
994void
996{
997 xmlNode *set = NULL;
998 xmlNode *last = NULL;
999
1000 xmlNode *orig_xml = NULL;
1001 xmlNode *expanded_xml = NULL;
1002
1003 const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
1004 const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
1005 enum pe_order_kind kind = get_ordering_type(xml_obj);
1006
1007 enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1008 NULL);
1009
1010 // Expand any resource tags in the constraint XML
1011 if (unpack_order_tags(xml_obj, &expanded_xml, data_set) != pcmk_rc_ok) {
1012 return;
1013 }
1014 if (expanded_xml != NULL) {
1015 orig_xml = xml_obj;
1016 xml_obj = expanded_xml;
1017 }
1018
1019 // If the constraint has resource sets, unpack them
1020 for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET);
1021 set != NULL; set = crm_next_same_xml(set)) {
1022
1023 set = expand_idref(set, data_set->input);
1024 if ((set == NULL) // Configuration error, message already logged
1025 || (unpack_order_set(set, kind, invert, data_set) != pcmk_rc_ok)) {
1026
1027 if (expanded_xml != NULL) {
1028 free_xml(expanded_xml);
1029 }
1030 return;
1031 }
1032
1033 if (last != NULL) {
1034
1035 if (order_rsc_sets(id, last, set, kind, data_set,
1036 symmetry) != pcmk_rc_ok) {
1037 if (expanded_xml != NULL) {
1038 free_xml(expanded_xml);
1039 }
1040 return;
1041 }
1042
1043 if ((symmetry == ordering_symmetric)
1044 && (order_rsc_sets(id, set, last, kind, data_set,
1046 if (expanded_xml != NULL) {
1047 free_xml(expanded_xml);
1048 }
1049 return;
1050 }
1051
1052 }
1053 last = set;
1054 }
1055
1056 if (expanded_xml) {
1057 free_xml(expanded_xml);
1058 xml_obj = orig_xml;
1059 }
1060
1061 // If the constraint has no resource sets, unpack it as a simple ordering
1062 if (last == NULL) {
1063 return unpack_simple_rsc_order(xml_obj, data_set);
1064 }
1065}
1066
1067static bool
1068ordering_is_invalid(pe_action_t *action, pe_action_wrapper_t *input)
1069{
1070 /* Prevent user-defined ordering constraints between resources
1071 * running in a guest node and the resource that defines that node.
1072 */
1074 && (input->action->rsc != NULL)
1075 && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1076
1077 crm_warn("Invalid ordering constraint between %s and %s",
1078 input->action->rsc->id, action->rsc->id);
1079 return true;
1080 }
1081
1082 /* If there's an order like
1083 * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
1084 *
1085 * then rscA is being migrated from node1 to node2, while rscB is being
1086 * migrated from node2 to node1. If there would be a graph loop,
1087 * break the order "load_stopped_node2" -> "rscA_migrate_to node1".
1088 */
1089 if ((input->type == pe_order_load) && action->rsc
1090 && pcmk__str_eq(action->task, RSC_MIGRATE, pcmk__str_casei)
1092 return true;
1093 }
1094
1095 return false;
1096}
1097
1098void
1100{
1101 for (GList *iter = data_set->actions; iter != NULL; iter = iter->next) {
1102 pe_action_t *action = (pe_action_t *) iter->data;
1103 pe_action_wrapper_t *input = NULL;
1104
1105 for (GList *input_iter = action->actions_before;
1106 input_iter != NULL; input_iter = input_iter->next) {
1107
1108 input = (pe_action_wrapper_t *) input_iter->data;
1109 if (ordering_is_invalid(action, input)) {
1110 input->type = pe_order_none;
1111 }
1112 }
1113 }
1114}
1115
1123void
1125{
1126 for (GList *iter = node->details->data_set->actions;
1127 iter != NULL; iter = iter->next) {
1128
1129 pe_action_t *action = (pe_action_t *) iter->data;
1130
1131 // Only stops on the node shutting down are relevant
1132 if ((action->rsc == NULL) || (action->node == NULL)
1133 || (action->node->details != node->details)
1134 || !pcmk__str_eq(action->task, RSC_STOP, pcmk__str_casei)) {
1135 continue;
1136 }
1137
1138 // Resources and nodes in maintenance mode won't be touched
1139
1140 if (pcmk_is_set(action->rsc->flags, pe_rsc_maintenance)) {
1141 pe_rsc_trace(action->rsc,
1142 "Not ordering %s before shutdown of %s because "
1143 "resource in maintenance mode",
1144 action->uuid, pe__node_name(node));
1145 continue;
1146
1147 } else if (node->details->maintenance) {
1148 pe_rsc_trace(action->rsc,
1149 "Not ordering %s before shutdown of %s because "
1150 "node in maintenance mode",
1151 action->uuid, pe__node_name(node));
1152 continue;
1153 }
1154
1155 /* Don't touch a resource that is unmanaged or blocked, to avoid
1156 * blocking the shutdown (though if another action depends on this one,
1157 * we may still end up blocking)
1158 */
1159 if (!pcmk_any_flags_set(action->rsc->flags,
1161 pe_rsc_trace(action->rsc,
1162 "Not ordering %s before shutdown of %s because "
1163 "resource is unmanaged or blocked",
1164 action->uuid, pe__node_name(node));
1165 continue;
1166 }
1167
1168 pe_rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1169 action->uuid, pe__node_name(node));
1171 pcmk__new_ordering(action->rsc, NULL, action, NULL,
1172 strdup(CRM_OP_SHUTDOWN), shutdown_op,
1174 node->details->data_set);
1175 }
1176}
1177
1188static GList *
1189find_actions_by_task(const pe_resource_t *rsc, const char *original_key)
1190{
1191 // Search under given task key directly
1192 GList *list = find_actions(rsc->actions, original_key, NULL);
1193
1194 if (list == NULL) {
1195 // Search again using this resource's ID
1196 char *key = NULL;
1197 char *task = NULL;
1198 guint interval_ms = 0;
1199
1200 if (parse_op_key(original_key, NULL, &task, &interval_ms)) {
1201 key = pcmk__op_key(rsc->id, task, interval_ms);
1202 list = find_actions(rsc->actions, key, NULL);
1203 free(key);
1204 free(task);
1205 } else {
1206 crm_err("Invalid operation key (bug?): %s", original_key);
1207 }
1208 }
1209 return list;
1210}
1211
1220static void
1221order_resource_actions_after(pe_action_t *first_action,
1222 const pe_resource_t *rsc, pe__ordering_t *order)
1223{
1224 GList *then_actions = NULL;
1225 uint32_t flags = pe_order_none;
1226
1227 CRM_CHECK((rsc != NULL) && (order != NULL), return);
1228
1229 flags = order->flags;
1230 pe_rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1231 order->id, rsc->id);
1232
1233 if (order->rh_action != NULL) {
1234 then_actions = g_list_prepend(NULL, order->rh_action);
1235
1236 } else {
1237 then_actions = find_actions_by_task(rsc, order->rh_action_task);
1238 }
1239
1240 if (then_actions == NULL) {
1241 pe_rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1242 order->id, order->rh_action_task, rsc->id);
1243 return;
1244 }
1245
1246 if ((first_action != NULL) && (first_action->rsc == rsc)
1247 && pcmk_is_set(first_action->flags, pe_action_dangle)) {
1248
1249 pe_rsc_trace(rsc,
1250 "Detected dangling migration ordering (%s then %s %s)",
1251 first_action->uuid, order->rh_action_task, rsc->id);
1253 }
1254
1255 if ((first_action == NULL) && !pcmk_is_set(flags, pe_order_implies_then)) {
1256 pe_rsc_debug(rsc,
1257 "Ignoring ordering %d for %s: No first action found",
1258 order->id, rsc->id);
1259 g_list_free(then_actions);
1260 return;
1261 }
1262
1263 for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1264 pe_action_t *then_action_iter = (pe_action_t *) iter->data;
1265
1266 if (first_action != NULL) {
1267 order_actions(first_action, then_action_iter, flags);
1268 } else {
1269 pe__clear_action_flags(then_action_iter, pe_action_runnable);
1270 crm_warn("%s of %s is unrunnable because there is no %s of %s "
1271 "to order it after", then_action_iter->task, rsc->id,
1272 order->lh_action_task, order->lh_rsc->id);
1273 }
1274 }
1275
1276 g_list_free(then_actions);
1277}
1278
1279static void
1280rsc_order_first(pe_resource_t *first_rsc, pe__ordering_t *order,
1282{
1283 GList *first_actions = NULL;
1284 pe_action_t *first_action = order->lh_action;
1285 pe_resource_t *then_rsc = order->rh_rsc;
1286
1287 CRM_ASSERT(first_rsc != NULL);
1288 pe_rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1289 order->id, first_rsc->id);
1290
1291 if (first_action != NULL) {
1292 first_actions = g_list_prepend(NULL, first_action);
1293
1294 } else {
1295 first_actions = find_actions_by_task(first_rsc, order->lh_action_task);
1296 }
1297
1298 if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1299 pe_rsc_trace(first_rsc,
1300 "Ignoring constraint %d: first (%s for %s) not found",
1301 order->id, order->lh_action_task, first_rsc->id);
1302
1303 } else if (first_actions == NULL) {
1304 char *key = NULL;
1305 char *op_type = NULL;
1306 guint interval_ms = 0;
1307
1308 parse_op_key(order->lh_action_task, NULL, &op_type, &interval_ms);
1309 key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1310
1311 if ((first_rsc->fns->state(first_rsc, TRUE) == RSC_ROLE_STOPPED)
1312 && pcmk__str_eq(op_type, RSC_STOP, pcmk__str_casei)) {
1313 free(key);
1314 pe_rsc_trace(first_rsc,
1315 "Ignoring constraint %d: first (%s for %s) not found",
1316 order->id, order->lh_action_task, first_rsc->id);
1317
1318 } else if ((first_rsc->fns->state(first_rsc, TRUE) == RSC_ROLE_UNPROMOTED)
1319 && pcmk__str_eq(op_type, RSC_DEMOTE, pcmk__str_casei)) {
1320 free(key);
1321 pe_rsc_trace(first_rsc,
1322 "Ignoring constraint %d: first (%s for %s) not found",
1323 order->id, order->lh_action_task, first_rsc->id);
1324
1325 } else {
1326 pe_rsc_trace(first_rsc,
1327 "Creating first (%s for %s) for constraint %d ",
1328 order->lh_action_task, first_rsc->id, order->id);
1329 first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1330 TRUE, data_set);
1331 first_actions = g_list_prepend(NULL, first_action);
1332 }
1333
1334 free(op_type);
1335 }
1336
1337 if (then_rsc == NULL) {
1338 if (order->rh_action == NULL) {
1339 pe_rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1340 order->id);
1341 return;
1342 }
1343 then_rsc = order->rh_action->rsc;
1344 }
1345 for (GList *gIter = first_actions; gIter != NULL; gIter = gIter->next) {
1346 first_action = (pe_action_t *) gIter->data;
1347
1348 if (then_rsc == NULL) {
1349 order_actions(first_action, order->rh_action, order->flags);
1350
1351 } else {
1352 order_resource_actions_after(first_action, then_rsc, order);
1353 }
1354 }
1355
1356 g_list_free(first_actions);
1357}
1358
1359void
1361{
1362 crm_trace("Applying ordering constraints");
1363
1364 /* Ordering constraints need to be processed in the order they were created.
1365 * rsc_order_first() and order_resource_actions_after() require the relevant
1366 * actions to already exist in some cases, but rsc_order_first() will create
1367 * the 'first' action in certain cases. Thus calling rsc_order_first() can
1368 * change the behavior of later-created orderings.
1369 *
1370 * Also, g_list_append() should be avoided for performance reasons, so we
1371 * prepend orderings when creating them and reverse the list here.
1372 *
1373 * @TODO This is brittle and should be carefully redesigned so that the
1374 * order of creation doesn't matter, and the reverse becomes unneeded.
1375 */
1377
1378 for (GList *gIter = data_set->ordering_constraints;
1379 gIter != NULL; gIter = gIter->next) {
1380
1381 pe__ordering_t *order = gIter->data;
1382 pe_resource_t *rsc = order->lh_rsc;
1383
1384 if (rsc != NULL) {
1385 rsc_order_first(rsc, order, data_set);
1386 continue;
1387 }
1388
1389 rsc = order->rh_rsc;
1390 if (rsc != NULL) {
1391 order_resource_actions_after(order->lh_action, rsc, order);
1392
1393 } else {
1394 crm_trace("Applying ordering constraint %d (non-resource actions)",
1395 order->id);
1396 order_actions(order->lh_action, order->rh_action, order->flags);
1397 }
1398 }
1399
1400 g_list_foreach(data_set->actions, (GFunc) pcmk__block_colocation_dependents,
1401 data_set);
1402
1403 crm_trace("Ordering probes");
1405
1406 crm_trace("Updating %d actions", g_list_length(data_set->actions));
1407 g_list_foreach(data_set->actions,
1409
1411}
1412
1420void
1422{
1423 const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1424
1425 for (GList *iter = list; iter != NULL; iter = iter->next) {
1426 pe_action_t *before = (pe_action_t *) iter->data;
1427 const char *before_desc = before->task? before->task : before->uuid;
1428
1429 crm_debug("Ordering %s on %s before %s on %s",
1430 before_desc, pe__node_name(before->node),
1431 after_desc, pe__node_name(after->node));
1432 order_actions(before, after, pe_order_optional);
1433 }
1434}
1435
1442void
1444{
1445 // Order start and promote after all instances are stopped
1450
1451 // Order stop, start, and promote after all instances are demoted
1458
1459 // Order promote after all instances are started
1462
1463 // Order demote after all instances are demoted
1466}
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: nvpair.c:948
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition: nvpair.c:975
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:45
uint64_t flags
Definition: remote.c:3
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:185
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
gboolean crm_is_true(const char *s)
Definition: strings.c:416
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
@ RSC_ROLE_STOPPED
Definition: common.h:94
@ RSC_ROLE_UNPROMOTED
Definition: common.h:96
A dumping ground.
#define RSC_PROMOTE
Definition: crm.h:205
#define RSC_DEMOTE
Definition: crm.h:207
#define RSC_STARTED
Definition: crm.h:200
#define CRM_OP_RELAXED_SET
Definition: crm.h:155
#define RSC_STOPPED
Definition: crm.h:203
#define CRM_OP_RELAXED_CLONE
Definition: crm.h:156
#define CRM_OP_SHUTDOWN
Definition: crm.h:143
#define RSC_START
Definition: crm.h:199
#define RSC_STOP
Definition: crm.h:202
#define RSC_PROMOTED
Definition: crm.h:206
#define RSC_MIGRATE
Definition: crm.h:196
#define RSC_DEMOTED
Definition: crm.h:208
G_GNUC_INTERNAL xmlNode * pcmk__expand_tags_in_sets(xmlNode *xml_obj, pe_working_set_t *data_set)
void pcmk__order_migration_equivalents(pe__ordering_t *order)
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
G_GNUC_INTERNAL bool pcmk__rsc_corresponds_to_guest(pe_resource_t *rsc, pe_node_t *node)
G_GNUC_INTERNAL bool pcmk__valid_resource_or_tag(pe_working_set_t *data_set, const char *id, pe_resource_t **rsc, pe_tag_t **tag)
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pe_action_t *action, pe_working_set_t *data_set)
G_GNUC_INTERNAL bool pcmk__graph_has_loop(pe_action_t *init_action, pe_action_t *action, pe_action_wrapper_t *input)
G_GNUC_INTERNAL pe_resource_t * pcmk__find_constraint_resource(GList *rsc_list, const char *id)
G_GNUC_INTERNAL bool pcmk__tag_to_set(xmlNode *xml_obj, xmlNode **rsc_set, const char *attr, bool convert_rsc, pe_working_set_t *data_set)
G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pe_action_t *action, pe_working_set_t *data_set)
G_GNUC_INTERNAL void pcmk__order_probes(pe_working_set_t *data_set)
#define crm_warn(fmt, args...)
Definition: logging.h:360
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:227
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
#define crm_log_xml_trace(xml, text)
Definition: logging.h:373
#define crm_trace(fmt, args...)
Definition: logging.h:365
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:216
#define ID(x)
Definition: msg_xml.h:468
#define XML_ORDER_ATTR_THEN_INSTANCE
Definition: msg_xml.h:386
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:336
#define XML_RSC_ATTR_INCARNATION_MIN
Definition: msg_xml.h:230
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:355
#define XML_ATTR_ID
Definition: msg_xml.h:134
#define XML_ORDER_ATTR_THEN_ACTION
Definition: msg_xml.h:379
#define XML_ORDER_ATTR_FIRST_INSTANCE
Definition: msg_xml.h:383
#define XML_ORDER_ATTR_FIRST
Definition: msg_xml.h:376
#define XML_ORDER_ATTR_FIRST_ACTION
Definition: msg_xml.h:378
#define XML_ORDER_ATTR_KIND
Definition: msg_xml.h:380
#define XML_CONS_ATTR_SYMMETRICAL
Definition: msg_xml.h:356
#define XML_ORDER_ATTR_THEN
Definition: msg_xml.h:377
pe_working_set_t * data_set
xmlNode * input
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:517
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:323
const char * action
Definition: pcmk_fence.c:30
void pcmk__apply_orderings(pe_working_set_t *data_set)
void pcmk__promotable_restart_ordering(pe_resource_t *rsc)
void pcmk__unpack_ordering(xmlNode *xml_obj, pe_working_set_t *data_set)
void pcmk__disable_invalid_orderings(pe_working_set_t *data_set)
void pcmk__new_ordering(pe_resource_t *first_rsc, char *first_action_task, pe_action_t *first_action, pe_resource_t *then_rsc, char *then_action_task, pe_action_t *then_action, uint32_t flags, pe_working_set_t *data_set)
void pcmk__order_after_each(pe_action_t *after, GList *list)
#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name)
void pcmk__order_stops_before_shutdown(pe_node_t *node, pe_action_t *shutdown_op)
#define handle_restart_type(rsc, kind, flag, flags)
pe_order_kind
@ pe_order_kind_optional
@ pe_order_kind_mandatory
@ pe_order_kind_serialize
ordering_symmetry
@ ordering_symmetric
@ ordering_symmetric_inverse
@ ordering_asymmetric
#define pe_rsc_block
Definition: pe_types.h:258
#define pe_rsc_managed
Definition: pe_types.h:257
@ pe_order_implies_then
Definition: pe_types.h:485
@ pe_order_one_or_more
Definition: pe_types.h:513
@ pe_order_none
Definition: pe_types.h:480
@ pe_order_serialize_only
Definition: pe_types.h:505
@ pe_order_asymmetrical
Definition: pe_types.h:511
@ pe_order_preserve
Definition: pe_types.h:516
@ pe_order_implies_then_printed
Definition: pe_types.h:509
@ pe_order_optional
Definition: pe_types.h:481
@ pe_order_implies_first
Definition: pe_types.h:484
@ pe_order_runnable_left
Definition: pe_types.h:491
@ pe_order_load
Definition: pe_types.h:512
@ pe_action_optional
Definition: pe_types.h:301
@ pe_action_runnable
Definition: pe_types.h:300
@ pe_action_dangle
Definition: pe_types.h:313
@ pe_action_requires_any
Definition: pe_types.h:318
#define pe_rsc_maintenance
Definition: pe_types.h:290
#define pe_warn_once(pe_wo_bit, fmt...)
Definition: internal.h:172
GList * find_actions(GList *input, const char *key, const pe_node_t *on_node)
Definition: pe_actions.c:1325
pe_action_t * custom_action(pe_resource_t *rsc, char *key, const char *task, const pe_node_t *on_node, gboolean optional, gboolean foo, pe_working_set_t *data_set)
Create or update an action object.
Definition: pe_actions.c:940
pe_action_t * get_pseudo_op(const char *name, pe_working_set_t *data_set)
Definition: pe_actions.c:977
#define pe__clear_order_flags(order_flags, flags_to_clear)
Definition: internal.h:145
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:46
@ pe_wo_order_score
Definition: internal.h:160
@ pe_wo_require_all
Definition: internal.h:159
@ pe_wo_order_inst
Definition: internal.h:164
gboolean order_actions(pe_action_t *lh_action, pe_action_t *rh_action, enum pe_ordering order)
Definition: utils.c:474
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:47
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:167
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:95
#define pe__set_order_flags(order_flags, flags_to_set)
Definition: internal.h:138
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:86
#define ENODATA
Definition: portability.h:145
#define CRM_ASSERT(expr)
Definition: results.h:42
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_unpack_error
Definition: results.h:112
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:928
@ pcmk__str_casei
pe_action_t * rh_action
Definition: internal.h:205
pe_resource_t * rh_rsc
Definition: internal.h:204
pe_resource_t * lh_rsc
Definition: internal.h:199
pe_action_t * lh_action
Definition: internal.h:200
pe_resource_t * rsc
Definition: pe_types.h:406
char * uuid
Definition: pe_types.h:411
char * task
Definition: pe_types.h:410
pe_node_t * node
Definition: pe_types.h:407
enum pe_action_flags flags
Definition: pe_types.h:415
int required_runnable_before
Definition: pe_types.h:441
struct pe_node_shared_s * details
Definition: pe_types.h:252
pe_working_set_t * data_set
Cluster that this node is part of.
Definition: pe_types.h:245
gboolean maintenance
Definition: pe_types.h:229
GList * actions
Definition: pe_types.h:366
GHashTable * meta
Definition: pe_types.h:380
GList * children
Definition: pe_types.h:384
char * id
Definition: pe_types.h:329
resource_object_functions_t * fns
Definition: pe_types.h:340
GList * actions
Definition: pe_types.h:171
xmlNode * input
Definition: pe_types.h:144
GList * resources
Definition: pe_types.h:165
GList * ordering_constraints
Definition: pe_types.h:167
int order_id
Deprecated (will be removed in a future release)
Definition: pe_types.h:179
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:54
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:3002
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2930
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2956
void free_xml(xmlNode *child)
Definition: xml.c:885
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:891
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:2145