This source file includes following definitions.
- invert_action
- get_ordering_type
- get_ordering_symmetry
- ordering_flags_for_kind
- get_ordering_resource
- get_minimum_first_instances
- clone_min_ordering
- inverse_ordering
- unpack_simple_rsc_order
- pcmk__new_ordering
- unpack_order_set
- order_rsc_sets
- unpack_order_tags
- pcmk__unpack_ordering
- ordering_is_invalid
- pcmk__disable_invalid_orderings
- pcmk__order_stops_before_shutdown
- find_actions_by_task
- order_resource_actions_after
- rsc_order_first
- block_colocation_dependents
- update_action_for_orderings
- pcmk__apply_orderings
- pcmk__order_after_each
- pcmk__promotable_restart_ordering
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <inttypes.h>
13 #include <stdbool.h>
14 #include <glib.h>
15
16 #include <crm/crm.h>
17 #include <pacemaker-internal.h>
18 #include "libpacemaker_private.h"
19
20 enum pe_order_kind {
21 pe_order_kind_optional,
22 pe_order_kind_mandatory,
23 pe_order_kind_serialize,
24 };
25
26 enum ordering_symmetry {
27 ordering_asymmetric,
28 ordering_symmetric,
29 ordering_symmetric_inverse,
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
41 static const char *
42 invert_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)) {
54 return PCMK_ACTION_PROMOTE;
55
56 } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTED, pcmk__str_none)) {
57 return PCMK_ACTION_DEMOTED;
58
59 } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
60 return PCMK_ACTION_PROMOTED;
61
62 } else if (pcmk__str_eq(action, PCMK_ACTION_RUNNING, pcmk__str_none)) {
63 return PCMK_ACTION_STOPPED;
64
65 } else if (pcmk__str_eq(action, PCMK_ACTION_STOPPED, pcmk__str_none)) {
66 return PCMK_ACTION_RUNNING;
67 }
68 pcmk__config_warn("Unknown action '%s' specified in order constraint",
69 action);
70 return NULL;
71 }
72
73 static enum pe_order_kind
74 get_ordering_type(const xmlNode *xml_obj)
75 {
76 enum pe_order_kind kind_e = pe_order_kind_mandatory;
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
82 kind_e = pe_order_kind_mandatory;
83
84 if (score) {
85
86 int score_i = 0;
87
88 (void) pcmk_parse_score(score, &score_i, 0);
89 if (score_i == 0) {
90 kind_e = pe_order_kind_optional;
91 }
92 pcmk__warn_once(pcmk__wo_order_score,
93 "Support for '" PCMK_XA_SCORE "' in "
94 PCMK_XE_RSC_ORDER " is deprecated and will be "
95 "removed in a future release "
96 "(use '" PCMK_XA_KIND "' instead)");
97 }
98
99 } else if (pcmk__str_eq(kind, PCMK_VALUE_MANDATORY, pcmk__str_none)) {
100 kind_e = pe_order_kind_mandatory;
101
102 } else if (pcmk__str_eq(kind, PCMK_VALUE_OPTIONAL, pcmk__str_none)) {
103 kind_e = pe_order_kind_optional;
104
105 } else if (pcmk__str_eq(kind, PCMK_VALUE_SERIALIZE, pcmk__str_none)) {
106 kind_e = pe_order_kind_serialize;
107
108 } else {
109 pcmk__config_err("Resetting '" PCMK_XA_KIND "' for constraint %s to "
110 "'" PCMK_VALUE_MANDATORY "' because '%s' is not valid",
111 pcmk__s(pcmk__xe_id(xml_obj), "missing ID"), kind);
112 }
113 return kind_e;
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127
128 static enum ordering_symmetry
129 get_ordering_symmetry(const xmlNode *xml_obj, enum pe_order_kind parent_kind,
130 const char *parent_symmetrical_s)
131 {
132 int rc = pcmk_rc_ok;
133 bool symmetric = false;
134 enum pe_order_kind kind = parent_kind;
135
136
137 if ((crm_element_value(xml_obj, PCMK_XA_KIND) != NULL)
138 || (crm_element_value(xml_obj, PCMK_XA_SCORE) != NULL)) {
139 kind = get_ordering_type(xml_obj);
140 }
141
142
143 rc = pcmk__xe_get_bool_attr(xml_obj, PCMK_XA_SYMMETRICAL, &symmetric);
144
145 if (rc != pcmk_rc_ok && parent_symmetrical_s != NULL) {
146 symmetric = crm_is_true(parent_symmetrical_s);
147 rc = pcmk_rc_ok;
148 }
149
150 if (rc == pcmk_rc_ok) {
151 if (symmetric) {
152 if (kind == pe_order_kind_serialize) {
153 pcmk__config_warn("Ignoring " PCMK_XA_SYMMETRICAL
154 " for '%s' because not valid with "
155 PCMK_XA_KIND " of '" PCMK_VALUE_SERIALIZE "'",
156 pcmk__xe_id(xml_obj));
157 } else {
158 return ordering_symmetric;
159 }
160 }
161 return ordering_asymmetric;
162 }
163
164
165 if (kind == pe_order_kind_serialize) {
166 return ordering_asymmetric;
167 }
168 return ordering_symmetric;
169 }
170
171
172
173
174
175
176
177
178
179
180
181 static uint32_t
182 ordering_flags_for_kind(enum pe_order_kind kind, const char *first,
183 enum ordering_symmetry symmetry)
184 {
185 uint32_t flags = pcmk__ar_none;
186
187 switch (kind) {
188 case pe_order_kind_optional:
189 pcmk__set_relation_flags(flags, pcmk__ar_ordered);
190 break;
191
192 case pe_order_kind_serialize:
193
194
195
196
197 pcmk__set_relation_flags(flags, pcmk__ar_serialize);
198 break;
199
200 case pe_order_kind_mandatory:
201 pcmk__set_relation_flags(flags, pcmk__ar_ordered);
202 switch (symmetry) {
203 case ordering_asymmetric:
204 pcmk__set_relation_flags(flags, pcmk__ar_asymmetric);
205 break;
206
207 case ordering_symmetric:
208 pcmk__set_relation_flags(flags,
209 pcmk__ar_first_implies_then);
210 if (pcmk__strcase_any_of(first, PCMK_ACTION_START,
211 PCMK_ACTION_PROMOTE, NULL)) {
212 pcmk__set_relation_flags(flags,
213 pcmk__ar_unrunnable_first_blocks);
214 }
215 break;
216
217 case ordering_symmetric_inverse:
218 pcmk__set_relation_flags(flags,
219 pcmk__ar_then_implies_first);
220 break;
221 }
222 break;
223 }
224 return flags;
225 }
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 static pcmk_resource_t *
241 get_ordering_resource(const xmlNode *xml, const char *resource_attr,
242 const char *instance_attr,
243 const pcmk_scheduler_t *scheduler)
244 {
245
246 pcmk_resource_t *rsc = NULL;
247 const char *rsc_id = crm_element_value(xml, resource_attr);
248 const char *instance_id = crm_element_value(xml, instance_attr);
249
250 if (rsc_id == NULL) {
251 pcmk__config_err("Ignoring constraint '%s' without %s",
252 pcmk__xe_id(xml), resource_attr);
253 return NULL;
254 }
255
256 rsc = pcmk__find_constraint_resource(scheduler->resources, rsc_id);
257 if (rsc == NULL) {
258 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
259 "does not exist", pcmk__xe_id(xml), rsc_id);
260 return NULL;
261 }
262
263 if (instance_id != NULL) {
264 pcmk__warn_once(pcmk__wo_order_inst,
265 "Support for " PCMK__XA_FIRST_INSTANCE " and "
266 PCMK__XA_THEN_INSTANCE " is deprecated and will be "
267 "removed in a future release.");
268
269 if (!pcmk__is_clone(rsc)) {
270 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
271 "is not a clone but instance '%s' was requested",
272 pcmk__xe_id(xml), rsc_id, instance_id);
273 return NULL;
274 }
275 rsc = find_clone_instance(rsc, instance_id);
276 if (rsc == NULL) {
277 pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
278 "does not have an instance '%s'",
279 pcmk__xe_id(xml), rsc_id, instance_id);
280 return NULL;
281 }
282 }
283 return rsc;
284 }
285
286
287
288
289
290
291
292
293
294
295 static int
296 get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml)
297 {
298 const char *clone_min = NULL;
299 bool require_all = false;
300
301 if (!pcmk__is_clone(rsc)) {
302 return 0;
303 }
304
305 clone_min = g_hash_table_lookup(rsc->meta, PCMK_META_CLONE_MIN);
306 if (clone_min != NULL) {
307 int clone_min_int = 0;
308
309 pcmk__scan_min_int(clone_min, &clone_min_int, 0);
310 return clone_min_int;
311 }
312
313
314
315
316
317 if (pcmk__xe_get_bool_attr(xml, PCMK_XA_REQUIRE_ALL,
318 &require_all) != ENODATA) {
319 pcmk__warn_once(pcmk__wo_require_all,
320 "Support for " PCMK_XA_REQUIRE_ALL " in ordering "
321 "constraints is deprecated and will be removed in a "
322 "future release (use " PCMK_META_CLONE_MIN " clone "
323 "meta-attribute instead)");
324 if (!require_all) {
325 return 1;
326 }
327 }
328
329 return 0;
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343
344 static void
345 clone_min_ordering(const char *id,
346 pcmk_resource_t *rsc_first, const char *action_first,
347 pcmk_resource_t *rsc_then, const char *action_then,
348 uint32_t flags, int clone_min)
349 {
350
351 char *task = crm_strdup_printf(PCMK_ACTION_CLONE_ONE_OR_MORE ":%s", id);
352 pcmk_action_t *clone_min_met = get_pseudo_op(task, rsc_first->cluster);
353
354 free(task);
355
356
357
358
359 clone_min_met->required_runnable_before = clone_min;
360 pcmk__set_action_flags(clone_min_met, pcmk_action_min_runnable);
361
362
363 for (GList *iter = rsc_first->children; iter != NULL; iter = iter->next) {
364 pcmk_resource_t *child = iter->data;
365
366 pcmk__new_ordering(child, pcmk__op_key(child->id, action_first, 0),
367 NULL, NULL, NULL, clone_min_met,
368 pcmk__ar_min_runnable
369 |pcmk__ar_first_implies_then_graphed,
370 rsc_first->cluster);
371 }
372
373
374 pcmk__new_ordering(NULL, NULL, clone_min_met, rsc_then,
375 pcmk__op_key(rsc_then->id, action_then, 0),
376 NULL, flags|pcmk__ar_unrunnable_first_blocks,
377 rsc_first->cluster);
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393 #define handle_restart_type(rsc, kind, flag, flags) do { \
394 if (((kind) == pe_order_kind_optional) \
395 && ((rsc)->restart_type == pe_restart_restart)) { \
396 pcmk__set_relation_flags((flags), (flag)); \
397 } \
398 } while (0)
399
400
401
402
403
404
405
406
407
408
409
410
411 static void
412 inverse_ordering(const char *id, enum pe_order_kind kind,
413 pcmk_resource_t *rsc_first, const char *action_first,
414 pcmk_resource_t *rsc_then, const char *action_then)
415 {
416 action_then = invert_action(action_then);
417 action_first = invert_action(action_first);
418 if ((action_then == NULL) || (action_first == NULL)) {
419 pcmk__config_warn("Cannot invert constraint '%s' "
420 "(please specify inverse manually)", id);
421 } else {
422 uint32_t flags = ordering_flags_for_kind(kind, action_first,
423 ordering_symmetric_inverse);
424
425 handle_restart_type(rsc_then, kind, pcmk__ar_then_implies_first, flags);
426 pcmk__order_resource_actions(rsc_then, action_then, rsc_first,
427 action_first, flags);
428 }
429 }
430
431 static void
432 unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
433 {
434 pcmk_resource_t *rsc_then = NULL;
435 pcmk_resource_t *rsc_first = NULL;
436 int min_required_before = 0;
437 enum pe_order_kind kind = pe_order_kind_mandatory;
438 uint32_t flags = pcmk__ar_none;
439 enum ordering_symmetry symmetry;
440
441 const char *action_then = NULL;
442 const char *action_first = NULL;
443 const char *id = NULL;
444
445 CRM_CHECK(xml_obj != NULL, return);
446
447 id = crm_element_value(xml_obj, PCMK_XA_ID);
448 if (id == NULL) {
449 pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
450 xml_obj->name);
451 return;
452 }
453
454 rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST,
455 PCMK__XA_FIRST_INSTANCE, scheduler);
456 if (rsc_first == NULL) {
457 return;
458 }
459
460 rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN,
461 PCMK__XA_THEN_INSTANCE, scheduler);
462 if (rsc_then == NULL) {
463 return;
464 }
465
466 action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
467 if (action_first == NULL) {
468 action_first = PCMK_ACTION_START;
469 }
470
471 action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
472 if (action_then == NULL) {
473 action_then = action_first;
474 }
475
476 kind = get_ordering_type(xml_obj);
477
478 symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
479 flags = ordering_flags_for_kind(kind, action_first, symmetry);
480
481 handle_restart_type(rsc_then, kind, pcmk__ar_first_implies_then, flags);
482
483
484
485
486
487
488 min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
489 if (min_required_before > 0) {
490 clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
491 flags, min_required_before);
492 } else {
493 pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
494 action_then, flags);
495 }
496
497 if (symmetry == ordering_symmetric) {
498 inverse_ordering(id, kind, rsc_first, action_first,
499 rsc_then, action_then);
500 }
501 }
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531 void
532 pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task,
533 pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
534 char *then_action_task, pcmk_action_t *then_action,
535 uint32_t flags, pcmk_scheduler_t *sched)
536 {
537 pcmk__action_relation_t *order = NULL;
538
539
540 CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
541 && ((then_action != NULL) || (then_rsc != NULL)),
542 free(first_action_task); free(then_action_task); return);
543
544 if ((first_rsc == NULL) && (first_action != NULL)) {
545 first_rsc = first_action->rsc;
546 }
547 if ((then_rsc == NULL) && (then_action != NULL)) {
548 then_rsc = then_action->rsc;
549 }
550
551 order = pcmk__assert_alloc(1, sizeof(pcmk__action_relation_t));
552
553 order->id = sched->order_id++;
554 order->flags = flags;
555 order->rsc1 = first_rsc;
556 order->rsc2 = then_rsc;
557 order->action1 = first_action;
558 order->action2 = then_action;
559 order->task1 = first_action_task;
560 order->task2 = then_action_task;
561
562 if ((order->task1 == NULL) && (first_action != NULL)) {
563 order->task1 = strdup(first_action->uuid);
564 }
565
566 if ((order->task2 == NULL) && (then_action != NULL)) {
567 order->task2 = strdup(then_action->uuid);
568 }
569
570 if ((order->rsc1 == NULL) && (first_action != NULL)) {
571 order->rsc1 = first_action->rsc;
572 }
573
574 if ((order->rsc2 == NULL) && (then_action != NULL)) {
575 order->rsc2 = then_action->rsc;
576 }
577
578 pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s",
579 (sched->order_id - 1),
580 pcmk__s(order->task1, "an underspecified action"),
581 pcmk__s(order->task2, "an underspecified action"));
582
583 sched->ordering_constraints = g_list_prepend(sched->ordering_constraints,
584 order);
585 pcmk__order_migration_equivalents(order);
586 }
587
588
589
590
591
592
593
594
595
596
597
598
599
600 static int
601 unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind,
602 const char *parent_symmetrical_s, pcmk_scheduler_t *scheduler)
603 {
604 GList *set_iter = NULL;
605 GList *resources = NULL;
606
607 pcmk_resource_t *last = NULL;
608 pcmk_resource_t *resource = NULL;
609
610 int local_kind = parent_kind;
611 bool sequential = false;
612 uint32_t flags = pcmk__ar_ordered;
613 enum ordering_symmetry symmetry;
614
615 char *key = NULL;
616 const char *id = pcmk__xe_id(set);
617 const char *action = crm_element_value(set, PCMK_XA_ACTION);
618 const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL);
619 const char *kind_s = crm_element_value(set, PCMK_XA_KIND);
620
621 if (action == NULL) {
622 action = PCMK_ACTION_START;
623 }
624
625 if (kind_s) {
626 local_kind = get_ordering_type(set);
627 }
628 if (sequential_s == NULL) {
629 sequential_s = "1";
630 }
631
632 sequential = crm_is_true(sequential_s);
633
634 symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
635 flags = ordering_flags_for_kind(local_kind, action, symmetry);
636
637 for (const xmlNode *xml_rsc = pcmk__xe_first_child(set,
638 PCMK_XE_RESOURCE_REF,
639 NULL, NULL);
640 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
641
642 EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc));
643 resources = g_list_append(resources, resource);
644 }
645
646 if (pcmk__list_of_1(resources)) {
647 crm_trace("Single set: %s", id);
648 goto done;
649 }
650
651 set_iter = resources;
652 while (set_iter != NULL) {
653 resource = (pcmk_resource_t *) set_iter->data;
654 set_iter = set_iter->next;
655
656 key = pcmk__op_key(resource->id, action, 0);
657
658 if (local_kind == pe_order_kind_serialize) {
659
660
661 for (GList *iter = set_iter; iter != NULL; iter = iter->next) {
662 pcmk_resource_t *then_rsc = iter->data;
663 char *then_key = pcmk__op_key(then_rsc->id, action, 0);
664
665 pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
666 then_key, NULL, flags, scheduler);
667 }
668
669 } else if (sequential) {
670 if (last != NULL) {
671 pcmk__order_resource_actions(last, action, resource, action,
672 flags);
673 }
674 last = resource;
675 }
676 free(key);
677 }
678
679 if (symmetry == ordering_asymmetric) {
680 goto done;
681 }
682
683 last = NULL;
684 action = invert_action(action);
685
686 flags = ordering_flags_for_kind(local_kind, action,
687 ordering_symmetric_inverse);
688
689 set_iter = resources;
690 while (set_iter != NULL) {
691 resource = (pcmk_resource_t *) set_iter->data;
692 set_iter = set_iter->next;
693
694 if (sequential) {
695 if (last != NULL) {
696 pcmk__order_resource_actions(resource, action, last, action,
697 flags);
698 }
699 last = resource;
700 }
701 }
702
703 done:
704 g_list_free(resources);
705 return pcmk_rc_ok;
706 }
707
708
709
710
711
712
713
714
715
716
717
718
719
720 static int
721 order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
722 enum pe_order_kind kind, pcmk_scheduler_t *scheduler,
723 enum ordering_symmetry symmetry)
724 {
725
726 const xmlNode *xml_rsc = NULL;
727 const xmlNode *xml_rsc_2 = NULL;
728
729 pcmk_resource_t *rsc_1 = NULL;
730 pcmk_resource_t *rsc_2 = NULL;
731
732 const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION);
733 const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION);
734
735 uint32_t flags = pcmk__ar_none;
736
737 bool require_all = true;
738
739 (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all);
740
741 if (action_1 == NULL) {
742 action_1 = PCMK_ACTION_START;
743 }
744
745 if (action_2 == NULL) {
746 action_2 = PCMK_ACTION_START;
747 }
748
749 if (symmetry == ordering_symmetric_inverse) {
750 action_1 = invert_action(action_1);
751 action_2 = invert_action(action_2);
752 }
753
754 if (pcmk__str_eq(PCMK_ACTION_STOP, action_1, pcmk__str_none)
755 || pcmk__str_eq(PCMK_ACTION_DEMOTE, action_1, pcmk__str_none)) {
756
757
758
759
760
761 require_all = true;
762 }
763
764 flags = ordering_flags_for_kind(kind, action_1, symmetry);
765
766
767
768
769 if (!require_all) {
770 char *task = crm_strdup_printf(PCMK_ACTION_ONE_OR_MORE ":%s",
771 pcmk__xe_id(set1));
772 pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler);
773
774 free(task);
775 pcmk__set_action_flags(unordered_action, pcmk_action_min_runnable);
776
777 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
778 NULL);
779 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
780
781 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
782
783
784
785
786
787 pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
788 NULL, NULL, NULL, unordered_action,
789 pcmk__ar_min_runnable
790 |pcmk__ar_first_implies_then_graphed,
791 scheduler);
792 }
793 for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
794 NULL);
795 xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
796
797 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
798
799
800
801
802
803 pcmk__new_ordering(NULL, NULL, unordered_action,
804 rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
805 NULL, flags|pcmk__ar_unrunnable_first_blocks,
806 scheduler);
807 }
808
809 return pcmk_rc_ok;
810 }
811
812 if (pcmk__xe_attr_is_true(set1, PCMK_XA_SEQUENTIAL)) {
813 if (symmetry == ordering_symmetric_inverse) {
814
815 xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
816 NULL);
817 if (xml_rsc != NULL) {
818 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
819 }
820
821 } else {
822
823 const char *rid = NULL;
824
825 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF,
826 NULL, NULL);
827 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
828
829 rid = pcmk__xe_id(xml_rsc);
830 }
831 EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
832 }
833 }
834
835 if (pcmk__xe_attr_is_true(set2, PCMK_XA_SEQUENTIAL)) {
836 if (symmetry == ordering_symmetric_inverse) {
837
838 const char *rid = NULL;
839
840 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
841 NULL, NULL);
842 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
843
844 rid = pcmk__xe_id(xml_rsc);
845 }
846 EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
847
848 } else {
849
850 xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
851 NULL);
852 if (xml_rsc != NULL) {
853 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
854 }
855 }
856 }
857
858 if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
859 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
860
861 } else if (rsc_1 != NULL) {
862 for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
863 NULL);
864 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
865
866 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
867 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
868 flags);
869 }
870
871 } else if (rsc_2 != NULL) {
872 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
873 NULL);
874 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
875
876 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
877 pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
878 flags);
879 }
880
881 } else {
882 for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
883 NULL);
884 xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
885
886 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
887
888 for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2,
889 PCMK_XE_RESOURCE_REF,
890 NULL, NULL);
891 xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
892
893 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
894 pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
895 action_2, flags);
896 }
897 }
898 }
899
900 return pcmk_rc_ok;
901 }
902
903
904
905
906
907
908
909
910
911
912
913
914 static int
915 unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
916 const pcmk_scheduler_t *scheduler)
917 {
918 const char *id_first = NULL;
919 const char *id_then = NULL;
920 const char *action_first = NULL;
921 const char *action_then = NULL;
922
923 pcmk_resource_t *rsc_first = NULL;
924 pcmk_resource_t *rsc_then = NULL;
925 pcmk_tag_t *tag_first = NULL;
926 pcmk_tag_t *tag_then = NULL;
927
928 xmlNode *rsc_set_first = NULL;
929 xmlNode *rsc_set_then = NULL;
930 bool any_sets = false;
931
932
933 *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
934 if (*expanded_xml != NULL) {
935 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
936 return pcmk_rc_ok;
937 }
938
939 id_first = crm_element_value(xml_obj, PCMK_XA_FIRST);
940 id_then = crm_element_value(xml_obj, PCMK_XA_THEN);
941 if ((id_first == NULL) || (id_then == NULL)) {
942 return pcmk_rc_ok;
943 }
944
945 if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first,
946 &tag_first)) {
947 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
948 "valid resource or tag",
949 pcmk__xe_id(xml_obj), id_first);
950 return pcmk_rc_unpack_error;
951 }
952
953 if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then,
954 &tag_then)) {
955 pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
956 "valid resource or tag",
957 pcmk__xe_id(xml_obj), id_then);
958 return pcmk_rc_unpack_error;
959 }
960
961 if ((rsc_first != NULL) && (rsc_then != NULL)) {
962
963 return pcmk_rc_ok;
964 }
965
966 action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
967 action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
968
969 *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
970
971
972
973
974 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true,
975 scheduler)) {
976 free_xml(*expanded_xml);
977 *expanded_xml = NULL;
978 return pcmk_rc_unpack_error;
979 }
980
981 if (rsc_set_first != NULL) {
982 if (action_first != NULL) {
983
984
985
986 crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first);
987 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_FIRST_ACTION);
988 }
989 any_sets = true;
990 }
991
992
993
994
995 if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true,
996 scheduler)) {
997 free_xml(*expanded_xml);
998 *expanded_xml = NULL;
999 return pcmk_rc_unpack_error;
1000 }
1001
1002 if (rsc_set_then != NULL) {
1003 if (action_then != NULL) {
1004
1005
1006
1007 crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then);
1008 pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_THEN_ACTION);
1009 }
1010 any_sets = true;
1011 }
1012
1013 if (any_sets) {
1014 crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
1015 } else {
1016 free_xml(*expanded_xml);
1017 *expanded_xml = NULL;
1018 }
1019
1020 return pcmk_rc_ok;
1021 }
1022
1023
1024
1025
1026
1027
1028
1029
1030 void
1031 pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
1032 {
1033 xmlNode *set = NULL;
1034 xmlNode *last = NULL;
1035
1036 xmlNode *orig_xml = NULL;
1037 xmlNode *expanded_xml = NULL;
1038
1039 const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
1040 const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL);
1041 enum pe_order_kind kind = get_ordering_type(xml_obj);
1042
1043 enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1044 NULL);
1045
1046
1047 if (unpack_order_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
1048 return;
1049 }
1050 if (expanded_xml != NULL) {
1051 orig_xml = xml_obj;
1052 xml_obj = expanded_xml;
1053 }
1054
1055
1056 for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1057 set != NULL; set = pcmk__xe_next_same(set)) {
1058
1059 set = expand_idref(set, scheduler->input);
1060 if ((set == NULL)
1061 || (unpack_order_set(set, kind, invert, scheduler) != pcmk_rc_ok)) {
1062
1063 if (expanded_xml != NULL) {
1064 free_xml(expanded_xml);
1065 }
1066 return;
1067 }
1068
1069 if (last != NULL) {
1070
1071 if (order_rsc_sets(id, last, set, kind, scheduler,
1072 symmetry) != pcmk_rc_ok) {
1073 if (expanded_xml != NULL) {
1074 free_xml(expanded_xml);
1075 }
1076 return;
1077 }
1078
1079 if ((symmetry == ordering_symmetric)
1080 && (order_rsc_sets(id, set, last, kind, scheduler,
1081 ordering_symmetric_inverse) != pcmk_rc_ok)) {
1082 if (expanded_xml != NULL) {
1083 free_xml(expanded_xml);
1084 }
1085 return;
1086 }
1087
1088 }
1089 last = set;
1090 }
1091
1092 if (expanded_xml) {
1093 free_xml(expanded_xml);
1094 xml_obj = orig_xml;
1095 }
1096
1097
1098 if (last == NULL) {
1099 return unpack_simple_rsc_order(xml_obj, scheduler);
1100 }
1101 }
1102
1103 static bool
1104 ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input)
1105 {
1106
1107
1108
1109 if (!pcmk_is_set(input->type, pcmk__ar_guest_allowed)
1110 && (input->action->rsc != NULL)
1111 && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1112
1113 pcmk__config_warn("Invalid ordering constraint between %s and %s",
1114 input->action->rsc->id, action->rsc->id);
1115 return true;
1116 }
1117
1118
1119
1120
1121
1122
1123
1124
1125 if (((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target)
1126 && (action->rsc != NULL)
1127 && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)
1128 && pcmk__graph_has_loop(action, action, input)) {
1129 return true;
1130 }
1131
1132 return false;
1133 }
1134
1135 void
1136 pcmk__disable_invalid_orderings(pcmk_scheduler_t *scheduler)
1137 {
1138 for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
1139 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1140 pcmk__related_action_t *input = NULL;
1141
1142 for (GList *input_iter = action->actions_before;
1143 input_iter != NULL; input_iter = input_iter->next) {
1144
1145 input = input_iter->data;
1146 if (ordering_is_invalid(action, input)) {
1147 input->type = (enum pe_ordering) pcmk__ar_none;
1148 }
1149 }
1150 }
1151 }
1152
1153
1154
1155
1156
1157
1158
1159
1160 void
1161 pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op)
1162 {
1163 for (GList *iter = node->details->data_set->actions;
1164 iter != NULL; iter = iter->next) {
1165
1166 pcmk_action_t *action = (pcmk_action_t *) iter->data;
1167
1168
1169 if (!pcmk__same_node(action->node, node)
1170 || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1171 continue;
1172 }
1173
1174
1175
1176 if (pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)) {
1177 pcmk__rsc_trace(action->rsc,
1178 "Not ordering %s before shutdown of %s because "
1179 "resource in maintenance mode",
1180 action->uuid, pcmk__node_name(node));
1181 continue;
1182
1183 } else if (node->details->maintenance) {
1184 pcmk__rsc_trace(action->rsc,
1185 "Not ordering %s before shutdown of %s because "
1186 "node in maintenance mode",
1187 action->uuid, pcmk__node_name(node));
1188 continue;
1189 }
1190
1191
1192
1193
1194
1195 if (!pcmk_any_flags_set(action->rsc->flags,
1196 pcmk_rsc_managed|pcmk_rsc_blocked)) {
1197 pcmk__rsc_trace(action->rsc,
1198 "Not ordering %s before shutdown of %s because "
1199 "resource is unmanaged or blocked",
1200 action->uuid, pcmk__node_name(node));
1201 continue;
1202 }
1203
1204 pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1205 action->uuid, pcmk__node_name(node));
1206 pcmk__clear_action_flags(action, pcmk_action_optional);
1207 pcmk__new_ordering(action->rsc, NULL, action, NULL,
1208 strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op,
1209 pcmk__ar_ordered|pcmk__ar_unrunnable_first_blocks,
1210 node->details->data_set);
1211 }
1212 }
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224 static GList *
1225 find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key)
1226 {
1227
1228 GList *list = find_actions(rsc->actions, original_key, NULL);
1229
1230 if (list == NULL) {
1231
1232 char *key = NULL;
1233 char *task = NULL;
1234 guint interval_ms = 0;
1235
1236 CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms),
1237 return NULL);
1238 key = pcmk__op_key(rsc->id, task, interval_ms);
1239 list = find_actions(rsc->actions, key, NULL);
1240 free(key);
1241 free(task);
1242 }
1243 return list;
1244 }
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254 static void
1255 order_resource_actions_after(pcmk_action_t *first_action,
1256 const pcmk_resource_t *rsc,
1257 pcmk__action_relation_t *order)
1258 {
1259 GList *then_actions = NULL;
1260 uint32_t flags = pcmk__ar_none;
1261
1262 CRM_CHECK((rsc != NULL) && (order != NULL), return);
1263
1264 flags = order->flags;
1265 pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1266 order->id, rsc->id);
1267
1268 if (order->action2 != NULL) {
1269 then_actions = g_list_prepend(NULL, order->action2);
1270
1271 } else {
1272 then_actions = find_actions_by_task(rsc, order->task2);
1273 }
1274
1275 if (then_actions == NULL) {
1276 pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1277 order->id, order->task2, rsc->id);
1278 return;
1279 }
1280
1281 if ((first_action != NULL) && (first_action->rsc == rsc)
1282 && pcmk_is_set(first_action->flags, pcmk_action_migration_abort)) {
1283
1284 pcmk__rsc_trace(rsc,
1285 "Detected dangling migration ordering (%s then %s %s)",
1286 first_action->uuid, order->task2, rsc->id);
1287 pcmk__clear_relation_flags(flags, pcmk__ar_first_implies_then);
1288 }
1289
1290 if ((first_action == NULL)
1291 && !pcmk_is_set(flags, pcmk__ar_first_implies_then)) {
1292
1293 pcmk__rsc_debug(rsc,
1294 "Ignoring ordering %d for %s: No first action found",
1295 order->id, rsc->id);
1296 g_list_free(then_actions);
1297 return;
1298 }
1299
1300 for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1301 pcmk_action_t *then_action_iter = (pcmk_action_t *) iter->data;
1302
1303 if (first_action != NULL) {
1304 order_actions(first_action, then_action_iter, flags);
1305 } else {
1306 pcmk__clear_action_flags(then_action_iter, pcmk_action_runnable);
1307 crm_warn("%s of %s is unrunnable because there is no %s of %s "
1308 "to order it after", then_action_iter->task, rsc->id,
1309 order->task1, order->rsc1->id);
1310 }
1311 }
1312
1313 g_list_free(then_actions);
1314 }
1315
1316 static void
1317 rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order)
1318 {
1319 GList *first_actions = NULL;
1320 pcmk_action_t *first_action = order->action1;
1321 pcmk_resource_t *then_rsc = order->rsc2;
1322
1323 pcmk__assert(first_rsc != NULL);
1324 pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1325 order->id, first_rsc->id);
1326
1327 if (first_action != NULL) {
1328 first_actions = g_list_prepend(NULL, first_action);
1329
1330 } else {
1331 first_actions = find_actions_by_task(first_rsc, order->task1);
1332 }
1333
1334 if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1335 pcmk__rsc_trace(first_rsc,
1336 "Ignoring constraint %d: first (%s for %s) not found",
1337 order->id, order->task1, first_rsc->id);
1338
1339 } else if (first_actions == NULL) {
1340 char *key = NULL;
1341 char *op_type = NULL;
1342 guint interval_ms = 0;
1343
1344 parse_op_key(order->task1, NULL, &op_type, &interval_ms);
1345 key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1346
1347 if ((first_rsc->fns->state(first_rsc, TRUE) == pcmk_role_stopped)
1348 && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) {
1349 free(key);
1350 pcmk__rsc_trace(first_rsc,
1351 "Ignoring constraint %d: first (%s for %s) "
1352 "not found",
1353 order->id, order->task1, first_rsc->id);
1354
1355 } else if ((first_rsc->fns->state(first_rsc,
1356 TRUE) == pcmk_role_unpromoted)
1357 && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE,
1358 pcmk__str_none)) {
1359 free(key);
1360 pcmk__rsc_trace(first_rsc,
1361 "Ignoring constraint %d: first (%s for %s) "
1362 "not found",
1363 order->id, order->task1, first_rsc->id);
1364
1365 } else {
1366 pcmk__rsc_trace(first_rsc,
1367 "Creating first (%s for %s) for constraint %d ",
1368 order->task1, first_rsc->id, order->id);
1369 first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1370 first_rsc->cluster);
1371 first_actions = g_list_prepend(NULL, first_action);
1372 }
1373
1374 free(op_type);
1375 }
1376
1377 if (then_rsc == NULL) {
1378 if (order->action2 == NULL) {
1379 pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1380 order->id);
1381 return;
1382 }
1383 then_rsc = order->action2->rsc;
1384 }
1385 for (GList *iter = first_actions; iter != NULL; iter = iter->next) {
1386 first_action = iter->data;
1387
1388 if (then_rsc == NULL) {
1389 order_actions(first_action, order->action2, order->flags);
1390
1391 } else {
1392 order_resource_actions_after(first_action, then_rsc, order);
1393 }
1394 }
1395
1396 g_list_free(first_actions);
1397 }
1398
1399
1400 static void
1401 block_colocation_dependents(gpointer data, gpointer user_data)
1402 {
1403 pcmk__block_colocation_dependents(data);
1404 }
1405
1406
1407 static void
1408 update_action_for_orderings(gpointer data, gpointer user_data)
1409 {
1410 pcmk__update_action_for_orderings((pcmk_action_t *) data,
1411 (pcmk_scheduler_t *) user_data);
1412 }
1413
1414
1415
1416
1417
1418
1419
1420 void
1421 pcmk__apply_orderings(pcmk_scheduler_t *sched)
1422 {
1423 crm_trace("Applying ordering constraints");
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437 sched->ordering_constraints = g_list_reverse(sched->ordering_constraints);
1438
1439 for (GList *iter = sched->ordering_constraints;
1440 iter != NULL; iter = iter->next) {
1441
1442 pcmk__action_relation_t *order = iter->data;
1443 pcmk_resource_t *rsc = order->rsc1;
1444
1445 if (rsc != NULL) {
1446 rsc_order_first(rsc, order);
1447 continue;
1448 }
1449
1450 rsc = order->rsc2;
1451 if (rsc != NULL) {
1452 order_resource_actions_after(order->action1, rsc, order);
1453
1454 } else {
1455 crm_trace("Applying ordering constraint %d (non-resource actions)",
1456 order->id);
1457 order_actions(order->action1, order->action2, order->flags);
1458 }
1459 }
1460
1461 g_list_foreach(sched->actions, block_colocation_dependents, NULL);
1462
1463 crm_trace("Ordering probes");
1464 pcmk__order_probes(sched);
1465
1466 crm_trace("Updating %d actions", g_list_length(sched->actions));
1467 g_list_foreach(sched->actions, update_action_for_orderings, sched);
1468
1469 pcmk__disable_invalid_orderings(sched);
1470 }
1471
1472
1473
1474
1475
1476
1477
1478
1479 void
1480 pcmk__order_after_each(pcmk_action_t *after, GList *list)
1481 {
1482 const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1483
1484 for (GList *iter = list; iter != NULL; iter = iter->next) {
1485 pcmk_action_t *before = (pcmk_action_t *) iter->data;
1486 const char *before_desc = before->task? before->task : before->uuid;
1487
1488 crm_debug("Ordering %s on %s before %s on %s",
1489 before_desc, pcmk__node_name(before->node),
1490 after_desc, pcmk__node_name(after->node));
1491 order_actions(before, after, pcmk__ar_ordered);
1492 }
1493 }
1494
1495
1496
1497
1498
1499
1500
1501 void
1502 pcmk__promotable_restart_ordering(pcmk_resource_t *rsc)
1503 {
1504
1505 pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
1506 rsc, PCMK_ACTION_START,
1507 pcmk__ar_ordered);
1508 pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
1509 rsc, PCMK_ACTION_PROMOTE,
1510 pcmk__ar_ordered);
1511
1512
1513 pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
1514 rsc, PCMK_ACTION_STOP,
1515 pcmk__ar_ordered);
1516 pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
1517 rsc, PCMK_ACTION_START,
1518 pcmk__ar_ordered);
1519 pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
1520 rsc, PCMK_ACTION_PROMOTE,
1521 pcmk__ar_ordered);
1522
1523
1524 pcmk__order_resource_actions(rsc, PCMK_ACTION_RUNNING,
1525 rsc, PCMK_ACTION_PROMOTE,
1526 pcmk__ar_ordered);
1527
1528
1529 pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTE,
1530 rsc, PCMK_ACTION_DEMOTED,
1531 pcmk__ar_ordered);
1532 }