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