pacemaker 3.0.1-16e74fc4da
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-2025 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> // bool, true, false
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// @TODO de-functionize this for readability and possibly better log messages
33#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
34 __rsc = pcmk__find_constraint_resource(scheduler->priv->resources, \
35 __name); \
36 if (__rsc == NULL) { \
37 pcmk__config_err("%s: No resource found for %s", __set, __name);\
38 return pcmk_rc_unpack_error; \
39 } \
40 } while (0)
41
42static const char *
43invert_action(const char *action)
44{
45 if (pcmk__str_eq(action, PCMK_ACTION_START, pcmk__str_none)) {
46 return PCMK_ACTION_STOP;
47
48 } else if (pcmk__str_eq(action, PCMK_ACTION_STOP, pcmk__str_none)) {
49 return PCMK_ACTION_START;
50
51 } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
52 return PCMK_ACTION_DEMOTE;
53
54 } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
56
57 } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTED, pcmk__str_none)) {
59
60 } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
62
63 } else if (pcmk__str_eq(action, PCMK_ACTION_RUNNING, pcmk__str_none)) {
65
66 } else if (pcmk__str_eq(action, PCMK_ACTION_STOPPED, pcmk__str_none)) {
68 }
69 pcmk__config_warn("Unknown action '%s' specified in order constraint",
70 action);
71 return NULL;
72}
73
74static enum pe_order_kind
75get_ordering_type(const xmlNode *xml_obj)
76{
78 const char *kind = crm_element_value(xml_obj, PCMK_XA_KIND);
79
80 if (kind == NULL) {
81 const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE);
82
84
85 if (score) {
86 // @COMPAT deprecated informally since 1.0.7, formally since 2.0.1
87 int score_i = 0;
88
89 (void) pcmk_parse_score(score, &score_i, 0);
90 if (score_i == 0) {
92 }
94 "Support for '" PCMK_XA_SCORE "' in "
95 PCMK_XE_RSC_ORDER " is deprecated and will be "
96 "removed in a future release "
97 "(use '" PCMK_XA_KIND "' instead)");
98 }
99
100 } else if (pcmk__str_eq(kind, PCMK_VALUE_MANDATORY, pcmk__str_none)) {
102
103 } else if (pcmk__str_eq(kind, PCMK_VALUE_OPTIONAL, pcmk__str_none)) {
104 kind_e = pe_order_kind_optional;
105
106 } else if (pcmk__str_eq(kind, PCMK_VALUE_SERIALIZE, pcmk__str_none)) {
108
109 } else {
110 pcmk__config_err("Resetting '" PCMK_XA_KIND "' for constraint %s to "
111 "'" PCMK_VALUE_MANDATORY "' because '%s' is not valid",
112 pcmk__s(pcmk__xe_id(xml_obj), "missing ID"), kind);
113 }
114 return kind_e;
115}
116
129static enum ordering_symmetry
130get_ordering_symmetry(const xmlNode *xml_obj, enum pe_order_kind parent_kind,
131 const char *parent_symmetrical_s)
132{
133 int rc = pcmk_rc_ok;
134 bool symmetric = false;
135 enum pe_order_kind kind = parent_kind; // Default to parent's kind
136
137 // Check ordering XML for explicit kind
138 if ((crm_element_value(xml_obj, PCMK_XA_KIND) != NULL)
139 || (crm_element_value(xml_obj, PCMK_XA_SCORE) != NULL)) {
140 kind = get_ordering_type(xml_obj);
141 }
142
143 // Check ordering XML (and parent) for explicit PCMK_XA_SYMMETRICAL setting
144 rc = pcmk__xe_get_bool_attr(xml_obj, PCMK_XA_SYMMETRICAL, &symmetric);
145
146 if (rc != pcmk_rc_ok && parent_symmetrical_s != NULL) {
147 symmetric = crm_is_true(parent_symmetrical_s);
148 rc = pcmk_rc_ok;
149 }
150
151 if (rc == pcmk_rc_ok) {
152 if (symmetric) {
153 if (kind == pe_order_kind_serialize) {
155 " for '%s' because not valid with "
157 pcmk__xe_id(xml_obj));
158 } else {
159 return ordering_symmetric;
160 }
161 }
162 return ordering_asymmetric;
163 }
164
165 // Use default symmetry
166 if (kind == pe_order_kind_serialize) {
167 return ordering_asymmetric;
168 }
169 return ordering_symmetric;
170}
171
182static uint32_t
183ordering_flags_for_kind(enum pe_order_kind kind, const char *first,
184 enum ordering_symmetry symmetry)
185{
186 uint32_t flags = pcmk__ar_none; // so we trace-log all flags set
187
188 switch (kind) {
191 break;
192
194 /* This flag is not used anywhere directly but means the relation
195 * will not match an equality comparison against pcmk__ar_none or
196 * pcmk__ar_ordered.
197 */
199 break;
200
203 switch (symmetry) {
206 break;
207
211 if (pcmk__is_up_action(first)) {
214 }
215 break;
216
220 break;
221 }
222 break;
223 }
224 return flags;
225}
226
237static pcmk_resource_t *
238get_ordering_resource(const xmlNode *xml, const char *resource_attr,
240{
241 pcmk_resource_t *rsc = NULL;
242 const char *rsc_id = crm_element_value(xml, resource_attr);
243
244 if (rsc_id == NULL) {
245 pcmk__config_err("Ignoring constraint '%s' without %s",
246 pcmk__xe_id(xml), resource_attr);
247 return NULL;
248 }
249
251 if (rsc == NULL) {
252 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
253 "does not exist", pcmk__xe_id(xml), rsc_id);
254 return NULL;
255 }
256
257 return rsc;
258}
259
269static int
270get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml)
271{
272 const char *clone_min = NULL;
273 bool require_all = false;
274
275 if (!pcmk__is_clone(rsc)) {
276 return 0;
277 }
278
279 clone_min = g_hash_table_lookup(rsc->priv->meta, PCMK_META_CLONE_MIN);
280 if (clone_min != NULL) {
281 int clone_min_int = 0;
282
283 pcmk__scan_min_int(clone_min, &clone_min_int, 0);
284 return clone_min_int;
285 }
286
287 /* @COMPAT 1.1.13:
288 * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE is deprecated equivalent of
289 * PCMK_META_CLONE_MIN=1
290 */
292 &require_all) != ENODATA) {
294 "Support for " PCMK_XA_REQUIRE_ALL " in ordering "
295 "constraints is deprecated and will be removed in a "
296 "future release (use " PCMK_META_CLONE_MIN " clone "
297 "meta-attribute instead)");
298 if (!require_all) {
299 return 1;
300 }
301 }
302
303 return 0;
304}
305
318static void
319clone_min_ordering(const char *id,
320 pcmk_resource_t *rsc_first, const char *action_first,
321 pcmk_resource_t *rsc_then, const char *action_then,
322 uint32_t flags, int clone_min)
323{
324 // Create a pseudo-action for when the minimum instances are active
325 char *task = crm_strdup_printf(PCMK_ACTION_CLONE_ONE_OR_MORE ":%s", id);
326 pcmk_action_t *clone_min_met = get_pseudo_op(task,
327 rsc_first->priv->scheduler);
328
329 free(task);
330
331 /* Require the pseudo-action to have the required number of actions to be
332 * considered runnable before allowing the pseudo-action to be runnable.
333 */
334 clone_min_met->required_runnable_before = clone_min;
335
336 // Order the actions for each clone instance before the pseudo-action
337 for (GList *iter = rsc_first->priv->children;
338 iter != NULL; iter = iter->next) {
339
340 pcmk_resource_t *child = iter->data;
341
342 pcmk__new_ordering(child, pcmk__op_key(child->id, action_first, 0),
343 NULL, NULL, NULL, clone_min_met,
346 rsc_first->priv->scheduler);
347 }
348
349 // Order "then" action after the pseudo-action (if runnable)
350 pcmk__new_ordering(NULL, NULL, clone_min_met, rsc_then,
351 pcmk__op_key(rsc_then->id, action_then, 0),
353 rsc_first->priv->scheduler);
354}
355
367static void
368inverse_ordering(const char *id, enum pe_order_kind kind,
369 pcmk_resource_t *rsc_first, const char *action_first,
370 pcmk_resource_t *rsc_then, const char *action_then)
371{
372 uint32_t flags;
373 const char *inverted_first = invert_action(action_first);
374 const char *inverted_then = invert_action(action_then);
375
376 if ((inverted_then == NULL) || (inverted_first == NULL)) {
377 pcmk__config_warn("Cannot invert constraint '%s' "
378 "(please specify inverse manually)", id);
379 return;
380 }
381
382 // Order inverted actions
383 flags = ordering_flags_for_kind(kind, inverted_first,
385 pcmk__order_resource_actions(rsc_then, inverted_then,
386 rsc_first, inverted_first, flags);
387}
388
389static void
390unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
391{
392 pcmk_resource_t *rsc_then = NULL;
393 pcmk_resource_t *rsc_first = NULL;
394 int min_required_before = 0;
396 uint32_t flags = pcmk__ar_none;
397 enum ordering_symmetry symmetry;
398
399 const char *action_then = NULL;
400 const char *action_first = NULL;
401 const char *id = NULL;
402
403 CRM_CHECK(xml_obj != NULL, return);
404
405 id = crm_element_value(xml_obj, PCMK_XA_ID);
406 if (id == NULL) {
407 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
408 xml_obj->name);
409 return;
410 }
411
412 rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST, scheduler);
413 if (rsc_first == NULL) {
414 return;
415 }
416
417 rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN, scheduler);
418 if (rsc_then == NULL) {
419 return;
420 }
421
422 action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
423 if (action_first == NULL) {
424 action_first = PCMK_ACTION_START;
425 }
426
427 action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
428 if (action_then == NULL) {
429 action_then = action_first;
430 }
431
432 kind = get_ordering_type(xml_obj);
433
434 symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
435 flags = ordering_flags_for_kind(kind, action_first, symmetry);
436
437 /* If there is a minimum number of instances that must be runnable before
438 * the 'then' action is runnable, we use a pseudo-action for convenience:
439 * minimum number of clone instances have runnable actions ->
440 * pseudo-action is runnable -> dependency is runnable.
441 */
442 min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
443 if (min_required_before > 0) {
444 clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
445 flags, min_required_before);
446 } else {
447 pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
448 action_then, flags);
449 }
450
451 if (symmetry == ordering_symmetric) {
452 inverse_ordering(id, kind, rsc_first, action_first,
453 rsc_then, action_then);
454 }
455}
456
485void
486pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task,
487 pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
488 char *then_action_task, pcmk_action_t *then_action,
489 uint32_t flags, pcmk_scheduler_t *sched)
490{
491 pcmk__action_relation_t *order = NULL;
492
493 // One of action or resource must be specified for each side
494 CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
495 && ((then_action != NULL) || (then_rsc != NULL)),
496 free(first_action_task); free(then_action_task); return);
497
498 if ((first_rsc == NULL) && (first_action != NULL)) {
499 first_rsc = first_action->rsc;
500 }
501 if ((then_rsc == NULL) && (then_action != NULL)) {
502 then_rsc = then_action->rsc;
503 }
504
506
507 order->id = sched->priv->next_ordering_id++;
508 order->flags = flags;
509 order->rsc1 = first_rsc;
510 order->rsc2 = then_rsc;
511 order->action1 = first_action;
512 order->action2 = then_action;
513 order->task1 = first_action_task;
514 order->task2 = then_action_task;
515
516 if ((order->task1 == NULL) && (first_action != NULL)) {
517 order->task1 = strdup(first_action->uuid);
518 }
519
520 if ((order->task2 == NULL) && (then_action != NULL)) {
521 order->task2 = strdup(then_action->uuid);
522 }
523
524 if ((order->rsc1 == NULL) && (first_action != NULL)) {
525 order->rsc1 = first_action->rsc;
526 }
527
528 if ((order->rsc2 == NULL) && (then_action != NULL)) {
529 order->rsc2 = then_action->rsc;
530 }
531
532 pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s",
533 (sched->priv->next_ordering_id - 1),
534 pcmk__s(order->task1, "an underspecified action"),
535 pcmk__s(order->task2, "an underspecified action"));
536
537 sched->priv->ordering_constraints =
538 g_list_prepend(sched->priv->ordering_constraints, order);
540}
541
554static int
555unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind,
556 const char *parent_symmetrical_s, pcmk_scheduler_t *scheduler)
557{
558 GList *set_iter = NULL;
559 GList *resources = NULL;
560
561 pcmk_resource_t *last = NULL;
562 pcmk_resource_t *resource = NULL;
563
564 int local_kind = parent_kind;
565 bool sequential = false;
566 uint32_t flags = pcmk__ar_ordered;
567 enum ordering_symmetry symmetry;
568
569 char *key = NULL;
570 const char *id = pcmk__xe_id(set);
571 const char *action = crm_element_value(set, PCMK_XA_ACTION);
572 const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL);
573 const char *kind_s = crm_element_value(set, PCMK_XA_KIND);
574
575 if (action == NULL) {
577 }
578
579 if (kind_s) {
580 local_kind = get_ordering_type(set);
581 }
582 if (sequential_s == NULL) {
583 sequential_s = "1";
584 }
585
586 sequential = crm_is_true(sequential_s);
587
588 symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
589 flags = ordering_flags_for_kind(local_kind, action, symmetry);
590
591 for (const xmlNode *xml_rsc = pcmk__xe_first_child(set,
593 NULL, NULL);
594 xml_rsc != NULL;
595 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
596
597 EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc));
598 resources = g_list_append(resources, resource);
599 }
600
601 if (pcmk__list_of_1(resources)) {
602 crm_trace("Single set: %s", id);
603 goto done;
604 }
605
606 set_iter = resources;
607 while (set_iter != NULL) {
608 resource = (pcmk_resource_t *) set_iter->data;
609 set_iter = set_iter->next;
610
611 key = pcmk__op_key(resource->id, action, 0);
612
613 if (local_kind == pe_order_kind_serialize) {
614 /* Serialize before everything that comes after */
615
616 for (GList *iter = set_iter; iter != NULL; iter = iter->next) {
617 pcmk_resource_t *then_rsc = iter->data;
618 char *then_key = pcmk__op_key(then_rsc->id, action, 0);
619
620 pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
621 then_key, NULL, flags, scheduler);
622 }
623
624 } else if (sequential) {
625 if (last != NULL) {
627 flags);
628 }
629 last = resource;
630 }
631 free(key);
632 }
633
634 if (symmetry == ordering_asymmetric) {
635 goto done;
636 }
637
638 last = NULL;
639 action = invert_action(action);
640
641 flags = ordering_flags_for_kind(local_kind, action,
643
644 set_iter = resources;
645 while (set_iter != NULL) {
646 resource = (pcmk_resource_t *) set_iter->data;
647 set_iter = set_iter->next;
648
649 if (sequential) {
650 if (last != NULL) {
652 flags);
653 }
654 last = resource;
655 }
656 }
657
658 done:
659 g_list_free(resources);
660 return pcmk_rc_ok;
661}
662
675static int
676order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
678 enum ordering_symmetry symmetry)
679{
680
681 const xmlNode *xml_rsc = NULL;
682 const xmlNode *xml_rsc_2 = NULL;
683
684 pcmk_resource_t *rsc_1 = NULL;
685 pcmk_resource_t *rsc_2 = NULL;
686
687 const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION);
688 const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION);
689
690 uint32_t flags = pcmk__ar_none;
691
692 bool require_all = true;
693
694 (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all);
695
696 if (action_1 == NULL) {
697 action_1 = PCMK_ACTION_START;
698 }
699
700 if (action_2 == NULL) {
701 action_2 = PCMK_ACTION_START;
702 }
703
704 if (symmetry == ordering_symmetric_inverse) {
705 action_1 = invert_action(action_1);
706 action_2 = invert_action(action_2);
707 }
708
709 if (pcmk__str_eq(PCMK_ACTION_STOP, action_1, pcmk__str_none)
710 || pcmk__str_eq(PCMK_ACTION_DEMOTE, action_1, pcmk__str_none)) {
711 /* Assuming: A -> ( B || C) -> D
712 * The one-or-more logic only applies during the start/promote phase.
713 * During shutdown neither B nor can shutdown until D is down, so simply
714 * turn require_all back on.
715 */
716 require_all = true;
717 }
718
719 flags = ordering_flags_for_kind(kind, action_1, symmetry);
720
721 /* If we have an unordered set1, whether it is sequential or not is
722 * irrelevant in regards to set2.
723 */
724 if (!require_all) {
726 pcmk__xe_id(set1));
727 pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler);
728
729 free(task);
730 unordered_action->required_runnable_before = 1;
731
732 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
733 NULL);
734 xml_rsc != NULL;
735 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
736
737 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
738
739 /* Add an ordering constraint between every element in set1 and the
740 * pseudo action. If any action in set1 is runnable the pseudo
741 * action will be runnable.
742 */
743 pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
744 NULL, NULL, NULL, unordered_action,
747 scheduler);
748 }
749 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
750 NULL);
751 xml_rsc_2 != NULL;
752 xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
753
754 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
755
756 /* Add an ordering constraint between the pseudo-action and every
757 * element in set2. If the pseudo-action is runnable, every action
758 * in set2 will be runnable.
759 */
760 pcmk__new_ordering(NULL, NULL, unordered_action,
761 rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
763 scheduler);
764 }
765
766 return pcmk_rc_ok;
767 }
768
770 if (symmetry == ordering_symmetric_inverse) {
771 // Get the first one
772 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
773 NULL);
774 if (xml_rsc != NULL) {
775 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
776 }
777
778 } else {
779 // Get the last one
780 const char *rid = NULL;
781
782 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF,
783 NULL, NULL);
784 xml_rsc != NULL;
785 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
786
787 rid = pcmk__xe_id(xml_rsc);
788 }
789 EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
790 }
791 }
792
794 if (symmetry == ordering_symmetric_inverse) {
795 // Get the last one
796 const char *rid = NULL;
797
798 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
799 NULL, NULL);
800 xml_rsc != NULL;
801 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
802
803 rid = pcmk__xe_id(xml_rsc);
804 }
805 EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
806
807 } else {
808 // Get the first one
809 xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
810 NULL);
811 if (xml_rsc != NULL) {
812 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
813 }
814 }
815 }
816
817 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
818 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
819
820 } else if (rsc_1 != NULL) {
821 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
822 NULL);
823 xml_rsc != NULL;
824 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
825
826 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
827 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
828 flags);
829 }
830
831 } else if (rsc_2 != NULL) {
832 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
833 NULL);
834 xml_rsc != NULL;
835 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
836
837 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
838 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
839 flags);
840 }
841
842 } else {
843 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
844 NULL);
845 xml_rsc != NULL;
846 xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
847
848 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
849
850 for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2,
852 NULL, NULL);
853 xml_rsc_2 != NULL;
854 xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
855
856 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
857 pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
858 action_2, flags);
859 }
860 }
861 }
862
863 return pcmk_rc_ok;
864}
865
877static int
878unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
880{
881 const char *id_first = NULL;
882 const char *id_then = NULL;
883 const char *action_first = NULL;
884 const char *action_then = NULL;
885
886 pcmk_resource_t *rsc_first = NULL;
887 pcmk_resource_t *rsc_then = NULL;
888 pcmk__idref_t *tag_first = NULL;
889 pcmk__idref_t *tag_then = NULL;
890
891 xmlNode *rsc_set_first = NULL;
892 xmlNode *rsc_set_then = NULL;
893 bool any_sets = false;
894
895 // Check whether there are any resource sets with template or tag references
896 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
897 if (*expanded_xml != NULL) {
898 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
899 return pcmk_rc_ok;
900 }
901
902 id_first = crm_element_value(xml_obj, PCMK_XA_FIRST);
903 id_then = crm_element_value(xml_obj, PCMK_XA_THEN);
904 if ((id_first == NULL) || (id_then == NULL)) {
905 return pcmk_rc_ok;
906 }
907
908 if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first,
909 &tag_first)) {
910 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
911 "valid resource or tag",
912 pcmk__xe_id(xml_obj), id_first);
914 }
915
916 if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then,
917 &tag_then)) {
918 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
919 "valid resource or tag",
920 pcmk__xe_id(xml_obj), id_then);
922 }
923
924 if ((rsc_first != NULL) && (rsc_then != NULL)) {
925 // Neither side references a template or tag
926 return pcmk_rc_ok;
927 }
928
929 action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
930 action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
931
932 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
933
934 /* Convert template/tag reference in PCMK_XA_FIRST into constraint
935 * PCMK_XE_RESOURCE_SET
936 */
937 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true,
938 scheduler)) {
939 pcmk__xml_free(*expanded_xml);
940 *expanded_xml = NULL;
942 }
943
944 if (rsc_set_first != NULL) {
945 if (action_first != NULL) {
946 /* Move PCMK_XA_FIRST_ACTION into converted PCMK_XE_RESOURCE_SET as
947 * PCMK_XA_ACTION
948 */
949 crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first);
951 }
952 any_sets = true;
953 }
954
955 /* Convert template/tag reference in PCMK_XA_THEN into constraint
956 * PCMK_XE_RESOURCE_SET
957 */
958 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true,
959 scheduler)) {
960 pcmk__xml_free(*expanded_xml);
961 *expanded_xml = NULL;
963 }
964
965 if (rsc_set_then != NULL) {
966 if (action_then != NULL) {
967 /* Move PCMK_XA_THEN_ACTION into converted PCMK_XE_RESOURCE_SET as
968 * PCMK_XA_ACTION
969 */
970 crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then);
972 }
973 any_sets = true;
974 }
975
976 if (any_sets) {
977 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
978 } else {
979 pcmk__xml_free(*expanded_xml);
980 *expanded_xml = NULL;
981 }
982
983 return pcmk_rc_ok;
984}
985
993void
995{
996 xmlNode *set = NULL;
997 xmlNode *last = NULL;
998
999 xmlNode *orig_xml = NULL;
1000 xmlNode *expanded_xml = NULL;
1001
1002 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
1003 const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL);
1004 enum pe_order_kind kind = get_ordering_type(xml_obj);
1005
1006 enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1007 NULL);
1008
1009 // Expand any resource tags in the constraint XML
1010 if (unpack_order_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
1011 return;
1012 }
1013 if (expanded_xml != NULL) {
1014 orig_xml = xml_obj;
1015 xml_obj = expanded_xml;
1016 }
1017
1018 // If the constraint has resource sets, unpack them
1019 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1020 set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
1021
1023 if ((set == NULL) // Configuration error, message already logged
1024 || (unpack_order_set(set, kind, invert, scheduler) != pcmk_rc_ok)) {
1025
1026 if (expanded_xml != NULL) {
1027 pcmk__xml_free(expanded_xml);
1028 }
1029 return;
1030 }
1031
1032 if (last != NULL) {
1033
1034 if (order_rsc_sets(id, last, set, kind, scheduler,
1035 symmetry) != pcmk_rc_ok) {
1036 if (expanded_xml != NULL) {
1037 pcmk__xml_free(expanded_xml);
1038 }
1039 return;
1040 }
1041
1042 if ((symmetry == ordering_symmetric)
1043 && (order_rsc_sets(id, set, last, kind, scheduler,
1045 if (expanded_xml != NULL) {
1046 pcmk__xml_free(expanded_xml);
1047 }
1048 return;
1049 }
1050
1051 }
1052 last = set;
1053 }
1054
1055 if (expanded_xml) {
1056 pcmk__xml_free(expanded_xml);
1057 xml_obj = orig_xml;
1058 }
1059
1060 // If the constraint has no resource sets, unpack it as a simple ordering
1061 if (last == NULL) {
1062 return unpack_simple_rsc_order(xml_obj, scheduler);
1063 }
1064}
1065
1066static bool
1067ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input)
1068{
1069 /* Prevent user-defined ordering constraints between resources
1070 * running in a guest node and the resource that defines that node.
1071 */
1073 && (input->action->rsc != NULL)
1074 && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1075
1076 pcmk__config_warn("Invalid ordering constraint between %s and %s",
1077 input->action->rsc->id, action->rsc->id);
1078 return true;
1079 }
1080
1081 /* If there's an order like
1082 * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
1083 *
1084 * then rscA is being migrated from node1 to node2, while rscB is being
1085 * migrated from node2 to node1. If there would be a graph loop,
1086 * break the order "load_stopped_node2" -> "rscA_migrate_to node1".
1087 */
1089 && (action->rsc != NULL)
1090 && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)
1092 return true;
1093 }
1094
1095 return false;
1096}
1097
1098void
1100{
1101 for (GList *iter = scheduler->priv->actions;
1102 iter != NULL; iter = iter->next) {
1103
1104 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1106
1107 for (GList *input_iter = action->actions_before;
1108 input_iter != NULL; input_iter = input_iter->next) {
1109
1110 input = input_iter->data;
1111 if (ordering_is_invalid(action, input)) {
1112 input->flags = pcmk__ar_none;
1113 }
1114 }
1115 }
1116}
1117
1125void
1127{
1128 for (GList *iter = node->priv->scheduler->priv->actions;
1129 iter != NULL; iter = iter->next) {
1130
1131 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1132
1133 // Only stops on the node shutting down are relevant
1134 if (!pcmk__same_node(action->node, node)
1135 || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1136 continue;
1137 }
1138
1139 // Resources and nodes in maintenance mode won't be touched
1140
1141 if (pcmk_is_set(action->rsc->flags, pcmk__rsc_maintenance)) {
1143 "Not ordering %s before shutdown of %s because "
1144 "resource in maintenance mode",
1145 action->uuid, pcmk__node_name(node));
1146 continue;
1147
1148 } else if (node->details->maintenance) {
1150 "Not ordering %s before shutdown of %s because "
1151 "node in maintenance mode",
1152 action->uuid, pcmk__node_name(node));
1153 continue;
1154 }
1155
1156 /* Don't touch a resource that is unmanaged or blocked, to avoid
1157 * blocking the shutdown (though if another action depends on this one,
1158 * we may still end up blocking)
1159 *
1160 * @TODO This "if" looks wrong, create a regression test for these cases
1161 */
1162 if (!pcmk_any_flags_set(action->rsc->flags,
1165 "Not ordering %s before shutdown of %s because "
1166 "resource is unmanaged or blocked",
1167 action->uuid, pcmk__node_name(node));
1168 continue;
1169 }
1170
1171 pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1172 action->uuid, pcmk__node_name(node));
1174 pcmk__new_ordering(action->rsc, NULL, action, NULL,
1175 strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op,
1177 node->priv->scheduler);
1178 }
1179}
1180
1191static GList *
1192find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key)
1193{
1194 // Search under given task key directly
1195 GList *list = find_actions(rsc->priv->actions, original_key, NULL);
1196
1197 if (list == NULL) {
1198 // Search again using this resource's ID
1199 char *key = NULL;
1200 char *task = NULL;
1201 guint interval_ms = 0;
1202
1203 CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms),
1204 return NULL);
1205 key = pcmk__op_key(rsc->id, task, interval_ms);
1206 list = find_actions(rsc->priv->actions, key, NULL);
1207 free(key);
1208 free(task);
1209 }
1210 return list;
1211}
1212
1221static void
1222order_resource_actions_after(pcmk_action_t *first_action,
1223 const pcmk_resource_t *rsc,
1225{
1226 GList *then_actions = NULL;
1227 uint32_t flags = pcmk__ar_none;
1228
1229 CRM_CHECK((rsc != NULL) && (order != NULL), return);
1230
1231 flags = order->flags;
1232 pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1233 order->id, rsc->id);
1234
1235 if (order->action2 != NULL) {
1236 then_actions = g_list_prepend(NULL, order->action2);
1237
1238 } else {
1239 then_actions = find_actions_by_task(rsc, order->task2);
1240 }
1241
1242 if (then_actions == NULL) {
1243 pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1244 order->id, order->task2, rsc->id);
1245 return;
1246 }
1247
1248 if ((first_action != NULL) && (first_action->rsc == rsc)
1249 && pcmk_is_set(first_action->flags, pcmk__action_migration_abort)) {
1250
1251 pcmk__rsc_trace(rsc,
1252 "Detected dangling migration ordering (%s then %s %s)",
1253 first_action->uuid, order->task2, rsc->id);
1255 }
1256
1257 if ((first_action == NULL)
1259
1260 pcmk__rsc_debug(rsc,
1261 "Ignoring ordering %d for %s: No first action found",
1262 order->id, rsc->id);
1263 g_list_free(then_actions);
1264 return;
1265 }
1266
1267 for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1268 pcmk_action_t *then_action_iter = (pcmk_action_t *) iter->data;
1269
1270 if (first_action != NULL) {
1271 order_actions(first_action, then_action_iter, flags);
1272 } else {
1274 // coverity[null_field] order->rsc1 can't be NULL here
1275 crm_warn("%s of %s is unrunnable because there is no %s of %s "
1276 "to order it after", then_action_iter->task, rsc->id,
1277 order->task1, order->rsc1->id);
1278 }
1279 }
1280
1281 g_list_free(then_actions);
1282}
1283
1284static void
1285rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order)
1286{
1287 GList *first_actions = NULL;
1288 pcmk_action_t *first_action = order->action1;
1289 pcmk_resource_t *then_rsc = order->rsc2;
1290
1291 pcmk__assert(first_rsc != NULL);
1292 pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1293 order->id, first_rsc->id);
1294
1295 if (first_action != NULL) {
1296 first_actions = g_list_prepend(NULL, first_action);
1297
1298 } else {
1299 first_actions = find_actions_by_task(first_rsc, order->task1);
1300 }
1301
1302 if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1303 pcmk__rsc_trace(first_rsc,
1304 "Ignoring constraint %d: first (%s for %s) not found",
1305 order->id, order->task1, first_rsc->id);
1306
1307 } else if (first_actions == NULL) {
1308 char *key = NULL;
1309 char *op_type = NULL;
1310 guint interval_ms = 0;
1311 enum rsc_role_e first_role;
1312
1313 parse_op_key(order->task1, NULL, &op_type, &interval_ms);
1314 key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1315
1316 first_role = first_rsc->priv->fns->state(first_rsc, true);
1317 if ((first_role == pcmk_role_stopped)
1318 && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) {
1319 free(key);
1320 pcmk__rsc_trace(first_rsc,
1321 "Ignoring constraint %d: first (%s for %s) "
1322 "not found",
1323 order->id, order->task1, first_rsc->id);
1324
1325 } else if ((first_role == pcmk_role_unpromoted)
1326 && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE,
1327 pcmk__str_none)) {
1328 free(key);
1329 pcmk__rsc_trace(first_rsc,
1330 "Ignoring constraint %d: first (%s for %s) "
1331 "not found",
1332 order->id, order->task1, first_rsc->id);
1333
1334 } else {
1335 pcmk__rsc_trace(first_rsc,
1336 "Creating first (%s for %s) for constraint %d ",
1337 order->task1, first_rsc->id, order->id);
1338 first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1339 first_rsc->priv->scheduler);
1340 first_actions = g_list_prepend(NULL, first_action);
1341 }
1342
1343 free(op_type);
1344 }
1345
1346 if (then_rsc == NULL) {
1347 if (order->action2 == NULL) {
1348 pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1349 order->id);
1350 return;
1351 }
1352 then_rsc = order->action2->rsc;
1353 }
1354 for (GList *iter = first_actions; iter != NULL; iter = iter->next) {
1355 first_action = iter->data;
1356
1357 if (then_rsc == NULL) {
1358 order_actions(first_action, order->action2, order->flags);
1359
1360 } else {
1361 order_resource_actions_after(first_action, then_rsc, order);
1362 }
1363 }
1364
1365 g_list_free(first_actions);
1366}
1367
1368// GFunc to call pcmk__block_colocation_dependents()
1369static void
1370block_colocation_dependents(gpointer data, gpointer user_data)
1371{
1373}
1374
1375// GFunc to call pcmk__update_action_for_orderings()
1376static void
1377update_action_for_orderings(gpointer data, gpointer user_data)
1378{
1380 (pcmk_scheduler_t *) user_data);
1381}
1382
1389void
1391{
1392 crm_trace("Applying ordering constraints");
1393
1394 /* Ordering constraints need to be processed in the order they were created.
1395 * rsc_order_first() and order_resource_actions_after() require the relevant
1396 * actions to already exist in some cases, but rsc_order_first() will create
1397 * the 'first' action in certain cases. Thus calling rsc_order_first() can
1398 * change the behavior of later-created orderings.
1399 *
1400 * Also, g_list_append() should be avoided for performance reasons, so we
1401 * prepend orderings when creating them and reverse the list here.
1402 *
1403 * @TODO This is brittle and should be carefully redesigned so that the
1404 * order of creation doesn't matter, and the reverse becomes unneeded.
1405 */
1406 sched->priv->ordering_constraints =
1407 g_list_reverse(sched->priv->ordering_constraints);
1408
1409 for (GList *iter = sched->priv->ordering_constraints;
1410 iter != NULL; iter = iter->next) {
1411
1412 pcmk__action_relation_t *order = iter->data;
1413 pcmk_resource_t *rsc = order->rsc1;
1414
1415 if (rsc != NULL) {
1416 rsc_order_first(rsc, order);
1417 continue;
1418 }
1419
1420 rsc = order->rsc2;
1421 if (rsc != NULL) {
1422 order_resource_actions_after(order->action1, rsc, order);
1423
1424 } else {
1425 crm_trace("Applying ordering constraint %d (non-resource actions)",
1426 order->id);
1427 order_actions(order->action1, order->action2, order->flags);
1428 }
1429 }
1430
1431 g_list_foreach(sched->priv->actions, block_colocation_dependents, NULL);
1432
1433 crm_trace("Ordering probes");
1434 pcmk__order_probes(sched);
1435
1436 crm_trace("Updating %d actions", g_list_length(sched->priv->actions));
1437 g_list_foreach(sched->priv->actions, update_action_for_orderings, sched);
1438
1440}
1441
1449void
1451{
1452 const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1453
1454 for (GList *iter = list; iter != NULL; iter = iter->next) {
1455 pcmk_action_t *before = (pcmk_action_t *) iter->data;
1456 const char *before_desc = before->task? before->task : before->uuid;
1457
1458 crm_debug("Ordering %s on %s before %s on %s",
1459 before_desc, pcmk__node_name(before->node),
1460 after_desc, pcmk__node_name(after->node));
1461 order_actions(before, after, pcmk__ar_ordered);
1462 }
1463}
1464
1471void
1473{
1474 // Order start and promote after all instances are stopped
1476 rsc, PCMK_ACTION_START,
1481
1482 // Order stop, start, and promote after all instances are demoted
1484 rsc, PCMK_ACTION_STOP,
1487 rsc, PCMK_ACTION_START,
1492
1493 // Order promote after all instances are started
1497
1498 // Order demote after all instances are demoted
1502}
@ 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: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_ONE_OR_MORE
Definition actions.h:56
#define PCMK_ACTION_PROMOTE
Definition actions.h:57
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:50
#define PCMK_ACTION_STOPPED
Definition actions.h:67
#define PCMK_ACTION_CLONE_ONE_OR_MORE
Definition actions.h:38
#define PCMK_ACTION_DEMOTED
Definition actions.h:41
#define PCMK_ACTION_DEMOTE
Definition actions.h:40
#define PCMK_ACTION_DO_SHUTDOWN
Definition actions.h:42
@ pcmk__action_migration_abort
@ pcmk__action_runnable
@ pcmk__action_optional
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:225
#define pcmk__clear_action_flags(action, flags_to_clear)
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
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
char data[0]
Definition cpg.c:10
A dumping ground.
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__valid_resource_or_tag(const pcmk_scheduler_t *scheduler, const char *id, pcmk_resource_t **rsc, pcmk__idref_t **tag)
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:360
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_log_xml_trace(xml, text)
Definition logging.h:378
#define crm_trace(fmt, args...)
Definition logging.h:370
#define pcmk__config_warn(fmt...)
#define pcmk__config_err(fmt...)
@ pcmk__wo_require_all
@ pcmk__wo_order_score
#define pcmk__warn_once(wo_flag, fmt...)
pcmk_scheduler_t * scheduler
xmlNode * input
#define PCMK_VALUE_SERIALIZE
Definition options.h:208
#define PCMK_VALUE_OPTIONAL
Definition options.h:189
#define PCMK_META_CLONE_MIN
Definition options.h:84
#define PCMK_VALUE_MANDATORY
Definition options.h:170
const char * action
Definition pcmk_fence.c:32
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)
@ 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 *first, pcmk_action_t *then, uint32_t flags)
Definition utils.c:483
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
Definition portability.h:61
@ pcmk__rsc_managed
@ pcmk__rsc_maintenance
@ pcmk__rsc_blocked
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_unpack_error
Definition results.h:122
#define pcmk__assert(expr)
rsc_role_e
Definition roles.h:34
@ 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 pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition scores.c:34
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
gboolean crm_is_true(const char *s)
Definition strings.c:490
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:116
@ pcmk__str_none
pcmk_node_t * node
pcmk_resource_t * rsc
gboolean maintenance
Definition nodes.h:66
pcmk_scheduler_t * scheduler
pcmk_scheduler_t * scheduler
const pcmk__rsc_methods_t * fns
pcmk__resource_private_t * priv
Definition resources.h:61
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, bool current)
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
xmlNode * input
Definition scheduler.h:81
pcmk__node_private_t * priv
Definition nodes.h:85
struct pcmk__node_details * details
Definition nodes.h:82
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
xmlNode * pcmk__xe_resolve_idref(xmlNode *xml, xmlNode *search)
Definition xml_idref.c:85
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
#define PCMK_XA_REQUIRE_ALL
Definition xml_names.h:381
#define PCMK_XA_SCORE
Definition xml_names.h:396
#define PCMK_XA_SYMMETRICAL
Definition xml_names.h:415
#define PCMK_XA_THEN
Definition xml_names.h:428
#define PCMK_XE_RESOURCE_REF
Definition xml_names.h:177
#define PCMK_XE_RSC_ORDER
Definition xml_names.h:189
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_KIND
Definition xml_names.h:312
#define PCMK_XE_RESOURCE_SET
Definition xml_names.h:178
#define PCMK_XA_ACTION
Definition xml_names.h:229
#define PCMK_XA_THEN_ACTION
Definition xml_names.h:429
#define PCMK_XA_SEQUENTIAL
Definition xml_names.h:398
#define PCMK_XA_FIRST_ACTION
Definition xml_names.h:289
#define PCMK_XA_FIRST
Definition xml_names.h:288