pacemaker  3.0.0-d8340737c4
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->priv->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 = 0;
87 
88  (void) pcmk_parse_score(score, &score_i, 0);
89  if (score_i == 0) {
90  kind_e = pe_order_kind_optional;
91  }
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 
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; // Default to parent's kind
135 
136  // Check ordering XML for explicit kind
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  // Check ordering XML (and parent) for explicit PCMK_XA_SYMMETRICAL setting
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) {
154  " for '%s' because not valid with "
156  pcmk__xe_id(xml_obj));
157  } else {
158  return ordering_symmetric;
159  }
160  }
161  return ordering_asymmetric;
162  }
163 
164  // Use default symmetry
165  if (kind == pe_order_kind_serialize) {
166  return ordering_asymmetric;
167  }
168  return ordering_symmetric;
169 }
170 
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; // so we trace-log all flags set
186 
187  switch (kind) {
190  break;
191 
193  /* This flag is not used anywhere directly but means the relation
194  * will not match an equality comparison against pcmk__ar_none or
195  * pcmk__ar_ordered.
196  */
198  break;
199 
202  switch (symmetry) {
203  case ordering_asymmetric:
205  break;
206 
207  case ordering_symmetric:
211  PCMK_ACTION_PROMOTE, NULL)) {
214  }
215  break;
216 
220  break;
221  }
222  break;
223  }
224  return flags;
225 }
226 
237 static pcmk_resource_t *
238 get_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 
269 static int
270 get_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 
318 static void
319 clone_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 
367 static void
368 inverse_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  action_then = invert_action(action_then);
373  action_first = invert_action(action_first);
374  if ((action_then == NULL) || (action_first == NULL)) {
375  pcmk__config_warn("Cannot invert constraint '%s' "
376  "(please specify inverse manually)", id);
377  } else {
378  uint32_t flags = ordering_flags_for_kind(kind, action_first,
380 
381  pcmk__order_resource_actions(rsc_then, action_then, rsc_first,
382  action_first, flags);
383  }
384 }
385 
386 static void
387 unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
388 {
389  pcmk_resource_t *rsc_then = NULL;
390  pcmk_resource_t *rsc_first = NULL;
391  int min_required_before = 0;
393  uint32_t flags = pcmk__ar_none;
394  enum ordering_symmetry symmetry;
395 
396  const char *action_then = NULL;
397  const char *action_first = NULL;
398  const char *id = NULL;
399 
400  CRM_CHECK(xml_obj != NULL, return);
401 
402  id = crm_element_value(xml_obj, PCMK_XA_ID);
403  if (id == NULL) {
404  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
405  xml_obj->name);
406  return;
407  }
408 
409  rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST, scheduler);
410  if (rsc_first == NULL) {
411  return;
412  }
413 
414  rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN, scheduler);
415  if (rsc_then == NULL) {
416  return;
417  }
418 
419  action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
420  if (action_first == NULL) {
421  action_first = PCMK_ACTION_START;
422  }
423 
424  action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
425  if (action_then == NULL) {
426  action_then = action_first;
427  }
428 
429  kind = get_ordering_type(xml_obj);
430 
431  symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
432  flags = ordering_flags_for_kind(kind, action_first, symmetry);
433 
434  /* If there is a minimum number of instances that must be runnable before
435  * the 'then' action is runnable, we use a pseudo-action for convenience:
436  * minimum number of clone instances have runnable actions ->
437  * pseudo-action is runnable -> dependency is runnable.
438  */
439  min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
440  if (min_required_before > 0) {
441  clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
442  flags, min_required_before);
443  } else {
444  pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
445  action_then, flags);
446  }
447 
448  if (symmetry == ordering_symmetric) {
449  inverse_ordering(id, kind, rsc_first, action_first,
450  rsc_then, action_then);
451  }
452 }
453 
482 void
483 pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task,
484  pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
485  char *then_action_task, pcmk_action_t *then_action,
486  uint32_t flags, pcmk_scheduler_t *sched)
487 {
488  pcmk__action_relation_t *order = NULL;
489 
490  // One of action or resource must be specified for each side
491  CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
492  && ((then_action != NULL) || (then_rsc != NULL)),
493  free(first_action_task); free(then_action_task); return);
494 
495  if ((first_rsc == NULL) && (first_action != NULL)) {
496  first_rsc = first_action->rsc;
497  }
498  if ((then_rsc == NULL) && (then_action != NULL)) {
499  then_rsc = then_action->rsc;
500  }
501 
502  order = pcmk__assert_alloc(1, sizeof(pcmk__action_relation_t));
503 
504  order->id = sched->priv->next_ordering_id++;
505  order->flags = flags;
506  order->rsc1 = first_rsc;
507  order->rsc2 = then_rsc;
508  order->action1 = first_action;
509  order->action2 = then_action;
510  order->task1 = first_action_task;
511  order->task2 = then_action_task;
512 
513  if ((order->task1 == NULL) && (first_action != NULL)) {
514  order->task1 = strdup(first_action->uuid);
515  }
516 
517  if ((order->task2 == NULL) && (then_action != NULL)) {
518  order->task2 = strdup(then_action->uuid);
519  }
520 
521  if ((order->rsc1 == NULL) && (first_action != NULL)) {
522  order->rsc1 = first_action->rsc;
523  }
524 
525  if ((order->rsc2 == NULL) && (then_action != NULL)) {
526  order->rsc2 = then_action->rsc;
527  }
528 
529  pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s",
530  (sched->priv->next_ordering_id - 1),
531  pcmk__s(order->task1, "an underspecified action"),
532  pcmk__s(order->task2, "an underspecified action"));
533 
534  sched->priv->ordering_constraints =
535  g_list_prepend(sched->priv->ordering_constraints, order);
537 }
538 
551 static int
552 unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind,
553  const char *parent_symmetrical_s, pcmk_scheduler_t *scheduler)
554 {
555  GList *set_iter = NULL;
556  GList *resources = NULL;
557 
558  pcmk_resource_t *last = NULL;
559  pcmk_resource_t *resource = NULL;
560 
561  int local_kind = parent_kind;
562  bool sequential = false;
563  uint32_t flags = pcmk__ar_ordered;
564  enum ordering_symmetry symmetry;
565 
566  char *key = NULL;
567  const char *id = pcmk__xe_id(set);
568  const char *action = crm_element_value(set, PCMK_XA_ACTION);
569  const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL);
570  const char *kind_s = crm_element_value(set, PCMK_XA_KIND);
571 
572  if (action == NULL) {
574  }
575 
576  if (kind_s) {
577  local_kind = get_ordering_type(set);
578  }
579  if (sequential_s == NULL) {
580  sequential_s = "1";
581  }
582 
583  sequential = crm_is_true(sequential_s);
584 
585  symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
586  flags = ordering_flags_for_kind(local_kind, action, symmetry);
587 
588  for (const xmlNode *xml_rsc = pcmk__xe_first_child(set,
590  NULL, NULL);
591  xml_rsc != NULL;
592  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
593 
594  EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc));
595  resources = g_list_append(resources, resource);
596  }
597 
598  if (pcmk__list_of_1(resources)) {
599  crm_trace("Single set: %s", id);
600  goto done;
601  }
602 
603  set_iter = resources;
604  while (set_iter != NULL) {
605  resource = (pcmk_resource_t *) set_iter->data;
606  set_iter = set_iter->next;
607 
608  key = pcmk__op_key(resource->id, action, 0);
609 
610  if (local_kind == pe_order_kind_serialize) {
611  /* Serialize before everything that comes after */
612 
613  for (GList *iter = set_iter; iter != NULL; iter = iter->next) {
614  pcmk_resource_t *then_rsc = iter->data;
615  char *then_key = pcmk__op_key(then_rsc->id, action, 0);
616 
617  pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
618  then_key, NULL, flags, scheduler);
619  }
620 
621  } else if (sequential) {
622  if (last != NULL) {
623  pcmk__order_resource_actions(last, action, resource, action,
624  flags);
625  }
626  last = resource;
627  }
628  free(key);
629  }
630 
631  if (symmetry == ordering_asymmetric) {
632  goto done;
633  }
634 
635  last = NULL;
636  action = invert_action(action);
637 
638  flags = ordering_flags_for_kind(local_kind, action,
640 
641  set_iter = resources;
642  while (set_iter != NULL) {
643  resource = (pcmk_resource_t *) set_iter->data;
644  set_iter = set_iter->next;
645 
646  if (sequential) {
647  if (last != NULL) {
648  pcmk__order_resource_actions(resource, action, last, action,
649  flags);
650  }
651  last = resource;
652  }
653  }
654 
655  done:
656  g_list_free(resources);
657  return pcmk_rc_ok;
658 }
659 
672 static int
673 order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
675  enum ordering_symmetry symmetry)
676 {
677 
678  const xmlNode *xml_rsc = NULL;
679  const xmlNode *xml_rsc_2 = NULL;
680 
681  pcmk_resource_t *rsc_1 = NULL;
682  pcmk_resource_t *rsc_2 = NULL;
683 
684  const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION);
685  const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION);
686 
687  uint32_t flags = pcmk__ar_none;
688 
689  bool require_all = true;
690 
691  (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all);
692 
693  if (action_1 == NULL) {
694  action_1 = PCMK_ACTION_START;
695  }
696 
697  if (action_2 == NULL) {
698  action_2 = PCMK_ACTION_START;
699  }
700 
701  if (symmetry == ordering_symmetric_inverse) {
702  action_1 = invert_action(action_1);
703  action_2 = invert_action(action_2);
704  }
705 
706  if (pcmk__str_eq(PCMK_ACTION_STOP, action_1, pcmk__str_none)
707  || pcmk__str_eq(PCMK_ACTION_DEMOTE, action_1, pcmk__str_none)) {
708  /* Assuming: A -> ( B || C) -> D
709  * The one-or-more logic only applies during the start/promote phase.
710  * During shutdown neither B nor can shutdown until D is down, so simply
711  * turn require_all back on.
712  */
713  require_all = true;
714  }
715 
716  flags = ordering_flags_for_kind(kind, action_1, symmetry);
717 
718  /* If we have an unordered set1, whether it is sequential or not is
719  * irrelevant in regards to set2.
720  */
721  if (!require_all) {
722  char *task = crm_strdup_printf(PCMK_ACTION_ONE_OR_MORE ":%s",
723  pcmk__xe_id(set1));
724  pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler);
725 
726  free(task);
727  unordered_action->required_runnable_before = 1;
728 
729  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
730  NULL);
731  xml_rsc != NULL;
732  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
733 
734  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
735 
736  /* Add an ordering constraint between every element in set1 and the
737  * pseudo action. If any action in set1 is runnable the pseudo
738  * action will be runnable.
739  */
740  pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
741  NULL, NULL, NULL, unordered_action,
744  scheduler);
745  }
746  for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
747  NULL);
748  xml_rsc_2 != NULL;
749  xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
750 
751  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
752 
753  /* Add an ordering constraint between the pseudo-action and every
754  * element in set2. If the pseudo-action is runnable, every action
755  * in set2 will be runnable.
756  */
757  pcmk__new_ordering(NULL, NULL, unordered_action,
758  rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
760  scheduler);
761  }
762 
763  return pcmk_rc_ok;
764  }
765 
767  if (symmetry == ordering_symmetric_inverse) {
768  // Get the first one
769  xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
770  NULL);
771  if (xml_rsc != NULL) {
772  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
773  }
774 
775  } else {
776  // Get the last one
777  const char *rid = NULL;
778 
779  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF,
780  NULL, NULL);
781  xml_rsc != NULL;
782  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
783 
784  rid = pcmk__xe_id(xml_rsc);
785  }
786  EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
787  }
788  }
789 
791  if (symmetry == ordering_symmetric_inverse) {
792  // Get the last one
793  const char *rid = NULL;
794 
795  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
796  NULL, NULL);
797  xml_rsc != NULL;
798  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
799 
800  rid = pcmk__xe_id(xml_rsc);
801  }
802  EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
803 
804  } else {
805  // Get the first one
806  xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
807  NULL);
808  if (xml_rsc != NULL) {
809  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
810  }
811  }
812  }
813 
814  if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
815  pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
816 
817  } else if (rsc_1 != NULL) {
818  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
819  NULL);
820  xml_rsc != NULL;
821  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
822 
823  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
824  pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
825  flags);
826  }
827 
828  } else if (rsc_2 != NULL) {
829  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
830  NULL);
831  xml_rsc != NULL;
832  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
833 
834  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
835  pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
836  flags);
837  }
838 
839  } else {
840  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
841  NULL);
842  xml_rsc != NULL;
843  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
844 
845  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
846 
847  for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2,
849  NULL, NULL);
850  xml_rsc_2 != NULL;
851  xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
852 
853  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
854  pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
855  action_2, flags);
856  }
857  }
858  }
859 
860  return pcmk_rc_ok;
861 }
862 
874 static int
875 unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
877 {
878  const char *id_first = NULL;
879  const char *id_then = NULL;
880  const char *action_first = NULL;
881  const char *action_then = NULL;
882 
883  pcmk_resource_t *rsc_first = NULL;
884  pcmk_resource_t *rsc_then = NULL;
885  pcmk__idref_t *tag_first = NULL;
886  pcmk__idref_t *tag_then = NULL;
887 
888  xmlNode *rsc_set_first = NULL;
889  xmlNode *rsc_set_then = NULL;
890  bool any_sets = false;
891 
892  // Check whether there are any resource sets with template or tag references
893  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
894  if (*expanded_xml != NULL) {
895  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
896  return pcmk_rc_ok;
897  }
898 
899  id_first = crm_element_value(xml_obj, PCMK_XA_FIRST);
900  id_then = crm_element_value(xml_obj, PCMK_XA_THEN);
901  if ((id_first == NULL) || (id_then == NULL)) {
902  return pcmk_rc_ok;
903  }
904 
905  if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first,
906  &tag_first)) {
907  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
908  "valid resource or tag",
909  pcmk__xe_id(xml_obj), id_first);
910  return pcmk_rc_unpack_error;
911  }
912 
913  if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then,
914  &tag_then)) {
915  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
916  "valid resource or tag",
917  pcmk__xe_id(xml_obj), id_then);
918  return pcmk_rc_unpack_error;
919  }
920 
921  if ((rsc_first != NULL) && (rsc_then != NULL)) {
922  // Neither side references a template or tag
923  return pcmk_rc_ok;
924  }
925 
926  action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
927  action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
928 
929  *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
930 
931  /* Convert template/tag reference in PCMK_XA_FIRST into constraint
932  * PCMK_XE_RESOURCE_SET
933  */
934  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true,
935  scheduler)) {
936  pcmk__xml_free(*expanded_xml);
937  *expanded_xml = NULL;
938  return pcmk_rc_unpack_error;
939  }
940 
941  if (rsc_set_first != NULL) {
942  if (action_first != NULL) {
943  /* Move PCMK_XA_FIRST_ACTION into converted PCMK_XE_RESOURCE_SET as
944  * PCMK_XA_ACTION
945  */
946  crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first);
948  }
949  any_sets = true;
950  }
951 
952  /* Convert template/tag reference in PCMK_XA_THEN into constraint
953  * PCMK_XE_RESOURCE_SET
954  */
955  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true,
956  scheduler)) {
957  pcmk__xml_free(*expanded_xml);
958  *expanded_xml = NULL;
959  return pcmk_rc_unpack_error;
960  }
961 
962  if (rsc_set_then != NULL) {
963  if (action_then != NULL) {
964  /* Move PCMK_XA_THEN_ACTION into converted PCMK_XE_RESOURCE_SET as
965  * PCMK_XA_ACTION
966  */
967  crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then);
969  }
970  any_sets = true;
971  }
972 
973  if (any_sets) {
974  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
975  } else {
976  pcmk__xml_free(*expanded_xml);
977  *expanded_xml = NULL;
978  }
979 
980  return pcmk_rc_ok;
981 }
982 
990 void
992 {
993  xmlNode *set = NULL;
994  xmlNode *last = NULL;
995 
996  xmlNode *orig_xml = NULL;
997  xmlNode *expanded_xml = NULL;
998 
999  const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
1000  const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL);
1001  enum pe_order_kind kind = get_ordering_type(xml_obj);
1002 
1003  enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1004  NULL);
1005 
1006  // Expand any resource tags in the constraint XML
1007  if (unpack_order_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
1008  return;
1009  }
1010  if (expanded_xml != NULL) {
1011  orig_xml = xml_obj;
1012  xml_obj = expanded_xml;
1013  }
1014 
1015  // If the constraint has resource sets, unpack them
1016  for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1017  set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
1018 
1019  set = pcmk__xe_resolve_idref(set, scheduler->input);
1020  if ((set == NULL) // Configuration error, message already logged
1021  || (unpack_order_set(set, kind, invert, scheduler) != pcmk_rc_ok)) {
1022 
1023  if (expanded_xml != NULL) {
1024  pcmk__xml_free(expanded_xml);
1025  }
1026  return;
1027  }
1028 
1029  if (last != NULL) {
1030 
1031  if (order_rsc_sets(id, last, set, kind, scheduler,
1032  symmetry) != pcmk_rc_ok) {
1033  if (expanded_xml != NULL) {
1034  pcmk__xml_free(expanded_xml);
1035  }
1036  return;
1037  }
1038 
1039  if ((symmetry == ordering_symmetric)
1040  && (order_rsc_sets(id, set, last, kind, scheduler,
1042  if (expanded_xml != NULL) {
1043  pcmk__xml_free(expanded_xml);
1044  }
1045  return;
1046  }
1047 
1048  }
1049  last = set;
1050  }
1051 
1052  if (expanded_xml) {
1053  pcmk__xml_free(expanded_xml);
1054  xml_obj = orig_xml;
1055  }
1056 
1057  // If the constraint has no resource sets, unpack it as a simple ordering
1058  if (last == NULL) {
1059  return unpack_simple_rsc_order(xml_obj, scheduler);
1060  }
1061 }
1062 
1063 static bool
1064 ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input)
1065 {
1066  /* Prevent user-defined ordering constraints between resources
1067  * running in a guest node and the resource that defines that node.
1068  */
1070  && (input->action->rsc != NULL)
1071  && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1072 
1073  pcmk__config_warn("Invalid ordering constraint between %s and %s",
1074  input->action->rsc->id, action->rsc->id);
1075  return true;
1076  }
1077 
1078  /* If there's an order like
1079  * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
1080  *
1081  * then rscA is being migrated from node1 to node2, while rscB is being
1082  * migrated from node2 to node1. If there would be a graph loop,
1083  * break the order "load_stopped_node2" -> "rscA_migrate_to node1".
1084  */
1086  && (action->rsc != NULL)
1087  && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)
1089  return true;
1090  }
1091 
1092  return false;
1093 }
1094 
1095 void
1097 {
1098  for (GList *iter = scheduler->priv->actions;
1099  iter != NULL; iter = iter->next) {
1100 
1101  pcmk_action_t *action = (pcmk_action_t *) iter->data;
1102  pcmk__related_action_t *input = NULL;
1103 
1104  for (GList *input_iter = action->actions_before;
1105  input_iter != NULL; input_iter = input_iter->next) {
1106 
1107  input = input_iter->data;
1108  if (ordering_is_invalid(action, input)) {
1109  input->flags = pcmk__ar_none;
1110  }
1111  }
1112  }
1113 }
1114 
1122 void
1124 {
1125  for (GList *iter = node->priv->scheduler->priv->actions;
1126  iter != NULL; iter = iter->next) {
1127 
1128  pcmk_action_t *action = (pcmk_action_t *) iter->data;
1129 
1130  // Only stops on the node shutting down are relevant
1131  if (!pcmk__same_node(action->node, node)
1132  || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1133  continue;
1134  }
1135 
1136  // Resources and nodes in maintenance mode won't be touched
1137 
1138  if (pcmk_is_set(action->rsc->flags, pcmk__rsc_maintenance)) {
1139  pcmk__rsc_trace(action->rsc,
1140  "Not ordering %s before shutdown of %s because "
1141  "resource in maintenance mode",
1142  action->uuid, pcmk__node_name(node));
1143  continue;
1144 
1145  } else if (node->details->maintenance) {
1146  pcmk__rsc_trace(action->rsc,
1147  "Not ordering %s before shutdown of %s because "
1148  "node in maintenance mode",
1149  action->uuid, pcmk__node_name(node));
1150  continue;
1151  }
1152 
1153  /* Don't touch a resource that is unmanaged or blocked, to avoid
1154  * blocking the shutdown (though if another action depends on this one,
1155  * we may still end up blocking)
1156  */
1157  if (!pcmk_any_flags_set(action->rsc->flags,
1159  pcmk__rsc_trace(action->rsc,
1160  "Not ordering %s before shutdown of %s because "
1161  "resource is unmanaged or blocked",
1162  action->uuid, pcmk__node_name(node));
1163  continue;
1164  }
1165 
1166  pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1167  action->uuid, pcmk__node_name(node));
1169  pcmk__new_ordering(action->rsc, NULL, action, NULL,
1170  strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op,
1172  node->priv->scheduler);
1173  }
1174 }
1175 
1186 static GList *
1187 find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key)
1188 {
1189  // Search under given task key directly
1190  GList *list = find_actions(rsc->priv->actions, original_key, NULL);
1191 
1192  if (list == NULL) {
1193  // Search again using this resource's ID
1194  char *key = NULL;
1195  char *task = NULL;
1196  guint interval_ms = 0;
1197 
1198  CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms),
1199  return NULL);
1200  key = pcmk__op_key(rsc->id, task, interval_ms);
1201  list = find_actions(rsc->priv->actions, key, NULL);
1202  free(key);
1203  free(task);
1204  }
1205  return list;
1206 }
1207 
1216 static void
1217 order_resource_actions_after(pcmk_action_t *first_action,
1218  const pcmk_resource_t *rsc,
1219  pcmk__action_relation_t *order)
1220 {
1221  GList *then_actions = NULL;
1222  uint32_t flags = pcmk__ar_none;
1223 
1224  CRM_CHECK((rsc != NULL) && (order != NULL), return);
1225 
1226  flags = order->flags;
1227  pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1228  order->id, rsc->id);
1229 
1230  if (order->action2 != NULL) {
1231  then_actions = g_list_prepend(NULL, order->action2);
1232 
1233  } else {
1234  then_actions = find_actions_by_task(rsc, order->task2);
1235  }
1236 
1237  if (then_actions == NULL) {
1238  pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1239  order->id, order->task2, rsc->id);
1240  return;
1241  }
1242 
1243  if ((first_action != NULL) && (first_action->rsc == rsc)
1244  && pcmk_is_set(first_action->flags, pcmk__action_migration_abort)) {
1245 
1246  pcmk__rsc_trace(rsc,
1247  "Detected dangling migration ordering (%s then %s %s)",
1248  first_action->uuid, order->task2, rsc->id);
1250  }
1251 
1252  if ((first_action == NULL)
1254 
1255  pcmk__rsc_debug(rsc,
1256  "Ignoring ordering %d for %s: No first action found",
1257  order->id, rsc->id);
1258  g_list_free(then_actions);
1259  return;
1260  }
1261 
1262  for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1263  pcmk_action_t *then_action_iter = (pcmk_action_t *) iter->data;
1264 
1265  if (first_action != NULL) {
1266  order_actions(first_action, then_action_iter, flags);
1267  } else {
1269  crm_warn("%s of %s is unrunnable because there is no %s of %s "
1270  "to order it after", then_action_iter->task, rsc->id,
1271  order->task1, order->rsc1->id);
1272  }
1273  }
1274 
1275  g_list_free(then_actions);
1276 }
1277 
1278 static void
1279 rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order)
1280 {
1281  GList *first_actions = NULL;
1282  pcmk_action_t *first_action = order->action1;
1283  pcmk_resource_t *then_rsc = order->rsc2;
1284 
1285  pcmk__assert(first_rsc != NULL);
1286  pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1287  order->id, first_rsc->id);
1288 
1289  if (first_action != NULL) {
1290  first_actions = g_list_prepend(NULL, first_action);
1291 
1292  } else {
1293  first_actions = find_actions_by_task(first_rsc, order->task1);
1294  }
1295 
1296  if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1297  pcmk__rsc_trace(first_rsc,
1298  "Ignoring constraint %d: first (%s for %s) not found",
1299  order->id, order->task1, first_rsc->id);
1300 
1301  } else if (first_actions == NULL) {
1302  char *key = NULL;
1303  char *op_type = NULL;
1304  guint interval_ms = 0;
1305  enum rsc_role_e first_role;
1306 
1307  parse_op_key(order->task1, NULL, &op_type, &interval_ms);
1308  key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1309 
1310  first_role = first_rsc->priv->fns->state(first_rsc, TRUE);
1311  if ((first_role == pcmk_role_stopped)
1312  && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) {
1313  free(key);
1314  pcmk__rsc_trace(first_rsc,
1315  "Ignoring constraint %d: first (%s for %s) "
1316  "not found",
1317  order->id, order->task1, first_rsc->id);
1318 
1319  } else if ((first_role == pcmk_role_unpromoted)
1320  && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE,
1321  pcmk__str_none)) {
1322  free(key);
1323  pcmk__rsc_trace(first_rsc,
1324  "Ignoring constraint %d: first (%s for %s) "
1325  "not found",
1326  order->id, order->task1, first_rsc->id);
1327 
1328  } else {
1329  pcmk__rsc_trace(first_rsc,
1330  "Creating first (%s for %s) for constraint %d ",
1331  order->task1, first_rsc->id, order->id);
1332  first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1333  first_rsc->priv->scheduler);
1334  first_actions = g_list_prepend(NULL, first_action);
1335  }
1336 
1337  free(op_type);
1338  }
1339 
1340  if (then_rsc == NULL) {
1341  if (order->action2 == NULL) {
1342  pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1343  order->id);
1344  return;
1345  }
1346  then_rsc = order->action2->rsc;
1347  }
1348  for (GList *iter = first_actions; iter != NULL; iter = iter->next) {
1349  first_action = iter->data;
1350 
1351  if (then_rsc == NULL) {
1352  order_actions(first_action, order->action2, order->flags);
1353 
1354  } else {
1355  order_resource_actions_after(first_action, then_rsc, order);
1356  }
1357  }
1358 
1359  g_list_free(first_actions);
1360 }
1361 
1362 // GFunc to call pcmk__block_colocation_dependents()
1363 static void
1364 block_colocation_dependents(gpointer data, gpointer user_data)
1365 {
1367 }
1368 
1369 // GFunc to call pcmk__update_action_for_orderings()
1370 static void
1371 update_action_for_orderings(gpointer data, gpointer user_data)
1372 {
1374  (pcmk_scheduler_t *) user_data);
1375 }
1376 
1383 void
1385 {
1386  crm_trace("Applying ordering constraints");
1387 
1388  /* Ordering constraints need to be processed in the order they were created.
1389  * rsc_order_first() and order_resource_actions_after() require the relevant
1390  * actions to already exist in some cases, but rsc_order_first() will create
1391  * the 'first' action in certain cases. Thus calling rsc_order_first() can
1392  * change the behavior of later-created orderings.
1393  *
1394  * Also, g_list_append() should be avoided for performance reasons, so we
1395  * prepend orderings when creating them and reverse the list here.
1396  *
1397  * @TODO This is brittle and should be carefully redesigned so that the
1398  * order of creation doesn't matter, and the reverse becomes unneeded.
1399  */
1400  sched->priv->ordering_constraints =
1401  g_list_reverse(sched->priv->ordering_constraints);
1402 
1403  for (GList *iter = sched->priv->ordering_constraints;
1404  iter != NULL; iter = iter->next) {
1405 
1406  pcmk__action_relation_t *order = iter->data;
1407  pcmk_resource_t *rsc = order->rsc1;
1408 
1409  if (rsc != NULL) {
1410  rsc_order_first(rsc, order);
1411  continue;
1412  }
1413 
1414  rsc = order->rsc2;
1415  if (rsc != NULL) {
1416  order_resource_actions_after(order->action1, rsc, order);
1417 
1418  } else {
1419  crm_trace("Applying ordering constraint %d (non-resource actions)",
1420  order->id);
1421  order_actions(order->action1, order->action2, order->flags);
1422  }
1423  }
1424 
1425  g_list_foreach(sched->priv->actions, block_colocation_dependents, NULL);
1426 
1427  crm_trace("Ordering probes");
1428  pcmk__order_probes(sched);
1429 
1430  crm_trace("Updating %d actions", g_list_length(sched->priv->actions));
1431  g_list_foreach(sched->priv->actions, update_action_for_orderings, sched);
1432 
1434 }
1435 
1443 void
1445 {
1446  const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1447 
1448  for (GList *iter = list; iter != NULL; iter = iter->next) {
1449  pcmk_action_t *before = (pcmk_action_t *) iter->data;
1450  const char *before_desc = before->task? before->task : before->uuid;
1451 
1452  crm_debug("Ordering %s on %s before %s on %s",
1453  before_desc, pcmk__node_name(before->node),
1454  after_desc, pcmk__node_name(after->node));
1455  order_actions(before, after, pcmk__ar_ordered);
1456  }
1457 }
1458 
1465 void
1467 {
1468  // Order start and promote after all instances are stopped
1470  rsc, PCMK_ACTION_START,
1473  rsc, PCMK_ACTION_PROMOTE,
1475 
1476  // Order stop, start, and promote after all instances are demoted
1478  rsc, PCMK_ACTION_STOP,
1481  rsc, PCMK_ACTION_START,
1484  rsc, PCMK_ACTION_PROMOTE,
1486 
1487  // Order promote after all instances are started
1489  rsc, PCMK_ACTION_PROMOTE,
1491 
1492  // Order demote after all instances are demoted
1494  rsc, PCMK_ACTION_DEMOTED,
1496 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:213
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:805
A dumping ground.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
&#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)
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:1140
#define PCMK_XE_RSC_ORDER
Definition: xml_names.h:189
User-configured asymmetric ordering.
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:116
Stopped.
Definition: roles.h:36
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1027
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...)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define PCMK_XE_RESOURCE_REF
Definition: xml_names.h:177
#define pcmk__config_err(fmt...)
G_GNUC_INTERNAL void pcmk__block_colocation_dependents(pcmk_action_t *action)
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:50
#define PCMK_ACTION_DO_SHUTDOWN
Definition: actions.h:42
G_GNUC_INTERNAL void pcmk__update_action_for_orderings(pcmk_action_t *action, pcmk_scheduler_t *scheduler)
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml_element.c:339
const pcmk__rsc_methods_t * fns
bool pcmk__xe_attr_is_true(const xmlNode *node, const char *name)
Definition: xml_element.c:1538
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:32
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
#define pcmk__rsc_debug(rsc, fmt, args...)
pcmk__node_private_t * priv
Definition: nodes.h:85
int pcmk__xe_get_bool_attr(const xmlNode *node, const char *name, bool *value)
Definition: xml_element.c:1501
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
#define crm_warn(fmt, args...)
Definition: logging.h:362
#define PCMK_ACTION_CLONE_ONE_OR_MORE
Definition: actions.h:38
#define crm_debug(fmt, args...)
Definition: logging.h:370
pcmk_scheduler_t * scheduler
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)
#define PCMK_XA_THEN_ACTION
Definition: xml_names.h:429
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
#define crm_trace(fmt, args...)
Definition: logging.h:372
gboolean maintenance
Definition: nodes.h:66
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:80
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)
#define PCMK_ACTION_START
Definition: actions.h:63
pcmk__resource_private_t * priv
Definition: resources.h:61
Unpromoted.
Definition: roles.h:38
rsc_role_e
Definition: roles.h:34
enum rsc_role_e(* state)(const pcmk_resource_t *rsc, gboolean current)
#define PCMK_ACTION_STOP
Definition: actions.h:66
#define PCMK_XA_ID
Definition: xml_names.h:301
pcmk_scheduler_t * scheduler
#define PCMK_XA_SCORE
Definition: xml_names.h:396
#define PCMK_META_CLONE_MIN
Definition: options.h:83
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:195
#define pcmk__warn_once(wo_flag, fmt...)
gboolean order_actions(pcmk_action_t *first, pcmk_action_t *then, uint32_t flags)
Definition: utils.c:465
#define PCMK_XA_REQUIRE_ALL
Definition: xml_names.h:381
#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:1093
#define pcmk__clear_relation_flags(ar_flags, flags_to_clear)
xmlNode * pcmk__xe_next(const xmlNode *node, const char *element_name)
Definition: xml_element.c:106
#define PCMK_XA_THEN
Definition: xml_names.h:428
#define pcmk__assert(expr)
ordering_symmetry
#define ENODATA
Definition: portability.h:61
pcmk_node_t * node
int required_runnable_before
#define PCMK_VALUE_SERIALIZE
Definition: options.h:207
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_resource_t * rsc
pcmk_scheduler_t * scheduler
GList * find_actions(GList *input, const char *key, const pcmk_node_t *on_node)
Definition: pe_actions.c:1445
If &#39;first&#39; is required and runnable, &#39;then&#39; must be in graph.
#define PCMK_XA_FIRST
Definition: xml_names.h:288
#define PCMK_ACTION_STOPPED
Definition: actions.h:67
xmlNode * input
pe_order_kind
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)
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:178
xmlNode * input
Definition: scheduler.h:81
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:248
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition: scores.c:34
gboolean crm_is_true(const char *s)
Definition: strings.c:490
void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define PCMK_XA_FIRST_ACTION
Definition: xml_names.h:289
void pcmk__order_migration_equivalents(pcmk__action_relation_t *order)
#define PCMK_XA_SYMMETRICAL
Definition: xml_names.h:415
#define crm_log_xml_trace(xml, text)
Definition: logging.h:380
#define PCMK_ACTION_PROMOTED
Definition: actions.h:58
#define PCMK_XA_KIND
Definition: xml_names.h:312
#define PCMK_ACTION_RUNNING
Definition: actions.h:62
struct pcmk__node_details * details
Definition: nodes.h:82
#define PCMK_ACTION_DEMOTED
Definition: actions.h:41
#define pcmk__assert_alloc(nmemb, size)
Definition: internal.h:257
&#39;then&#39; action is runnable if certain number of &#39;first&#39; instances are
#define PCMK_ACTION_ONE_OR_MORE
Definition: actions.h:56
#define PCMK_XA_ACTION
Definition: xml_names.h:229
#define PCMK_XA_SEQUENTIAL
Definition: xml_names.h:398
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:211
#define PCMK_VALUE_MANDATORY
Definition: options.h:169
No relation (compare with equality rather than bit set)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define PCMK_VALUE_OPTIONAL
Definition: options.h:188