pacemaker  2.1.9-49aab99839
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 = 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 
240 static pcmk_resource_t *
241 get_ordering_resource(const xmlNode *xml, const char *resource_attr,
242  const char *instance_attr,
244 {
245  // @COMPAT: instance_attr and instance_id variables deprecated since 2.1.5
246  pcmk_resource_t *rsc = NULL;
247  const char *rsc_id = crm_element_value(xml, resource_attr);
248  const char *instance_id = crm_element_value(xml, instance_attr);
249 
250  if (rsc_id == NULL) {
251  pcmk__config_err("Ignoring constraint '%s' without %s",
252  pcmk__xe_id(xml), resource_attr);
253  return NULL;
254  }
255 
257  if (rsc == NULL) {
258  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
259  "does not exist", pcmk__xe_id(xml), rsc_id);
260  return NULL;
261  }
262 
263  if (instance_id != NULL) {
265  "Support for " PCMK__XA_FIRST_INSTANCE " and "
266  PCMK__XA_THEN_INSTANCE " is deprecated and will be "
267  "removed in a future release.");
268 
269  if (!pcmk__is_clone(rsc)) {
270  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
271  "is not a clone but instance '%s' was requested",
272  pcmk__xe_id(xml), rsc_id, instance_id);
273  return NULL;
274  }
275  rsc = find_clone_instance(rsc, instance_id);
276  if (rsc == NULL) {
277  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
278  "does not have an instance '%s'",
279  pcmk__xe_id(xml), rsc_id, instance_id);
280  return NULL;
281  }
282  }
283  return rsc;
284 }
285 
295 static int
296 get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml)
297 {
298  const char *clone_min = NULL;
299  bool require_all = false;
300 
301  if (!pcmk__is_clone(rsc)) {
302  return 0;
303  }
304 
305  clone_min = g_hash_table_lookup(rsc->meta, PCMK_META_CLONE_MIN);
306  if (clone_min != NULL) {
307  int clone_min_int = 0;
308 
309  pcmk__scan_min_int(clone_min, &clone_min_int, 0);
310  return clone_min_int;
311  }
312 
313  /* @COMPAT 1.1.13:
314  * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE is deprecated equivalent of
315  * PCMK_META_CLONE_MIN=1
316  */
318  &require_all) != ENODATA) {
320  "Support for " PCMK_XA_REQUIRE_ALL " in ordering "
321  "constraints is deprecated and will be removed in a "
322  "future release (use " PCMK_META_CLONE_MIN " clone "
323  "meta-attribute instead)");
324  if (!require_all) {
325  return 1;
326  }
327  }
328 
329  return 0;
330 }
331 
344 static void
345 clone_min_ordering(const char *id,
346  pcmk_resource_t *rsc_first, const char *action_first,
347  pcmk_resource_t *rsc_then, const char *action_then,
348  uint32_t flags, int clone_min)
349 {
350  // Create a pseudo-action for when the minimum instances are active
351  char *task = crm_strdup_printf(PCMK_ACTION_CLONE_ONE_OR_MORE ":%s", id);
352  pcmk_action_t *clone_min_met = get_pseudo_op(task, rsc_first->cluster);
353 
354  free(task);
355 
356  /* Require the pseudo-action to have the required number of actions to be
357  * considered runnable before allowing the pseudo-action to be runnable.
358  */
359  clone_min_met->required_runnable_before = clone_min;
361 
362  // Order the actions for each clone instance before the pseudo-action
363  for (GList *iter = rsc_first->children; iter != NULL; iter = iter->next) {
364  pcmk_resource_t *child = iter->data;
365 
366  pcmk__new_ordering(child, pcmk__op_key(child->id, action_first, 0),
367  NULL, NULL, NULL, clone_min_met,
370  rsc_first->cluster);
371  }
372 
373  // Order "then" action after the pseudo-action (if runnable)
374  pcmk__new_ordering(NULL, NULL, clone_min_met, rsc_then,
375  pcmk__op_key(rsc_then->id, action_then, 0),
377  rsc_first->cluster);
378 }
379 
393 #define handle_restart_type(rsc, kind, flag, flags) do { \
394  if (((kind) == pe_order_kind_optional) \
395  && ((rsc)->restart_type == pe_restart_restart)) { \
396  pcmk__set_relation_flags((flags), (flag)); \
397  } \
398  } while (0)
399 
411 static void
412 inverse_ordering(const char *id, enum pe_order_kind kind,
413  pcmk_resource_t *rsc_first, const char *action_first,
414  pcmk_resource_t *rsc_then, const char *action_then)
415 {
416  action_then = invert_action(action_then);
417  action_first = invert_action(action_first);
418  if ((action_then == NULL) || (action_first == NULL)) {
419  pcmk__config_warn("Cannot invert constraint '%s' "
420  "(please specify inverse manually)", id);
421  } else {
422  uint32_t flags = ordering_flags_for_kind(kind, action_first,
424 
426  pcmk__order_resource_actions(rsc_then, action_then, rsc_first,
427  action_first, flags);
428  }
429 }
430 
431 static void
432 unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
433 {
434  pcmk_resource_t *rsc_then = NULL;
435  pcmk_resource_t *rsc_first = NULL;
436  int min_required_before = 0;
438  uint32_t flags = pcmk__ar_none;
439  enum ordering_symmetry symmetry;
440 
441  const char *action_then = NULL;
442  const char *action_first = NULL;
443  const char *id = NULL;
444 
445  CRM_CHECK(xml_obj != NULL, return);
446 
447  id = crm_element_value(xml_obj, PCMK_XA_ID);
448  if (id == NULL) {
449  pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
450  xml_obj->name);
451  return;
452  }
453 
454  rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST,
456  if (rsc_first == NULL) {
457  return;
458  }
459 
460  rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN,
462  if (rsc_then == NULL) {
463  return;
464  }
465 
466  action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
467  if (action_first == NULL) {
468  action_first = PCMK_ACTION_START;
469  }
470 
471  action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
472  if (action_then == NULL) {
473  action_then = action_first;
474  }
475 
476  kind = get_ordering_type(xml_obj);
477 
478  symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
479  flags = ordering_flags_for_kind(kind, action_first, symmetry);
480 
482 
483  /* If there is a minimum number of instances that must be runnable before
484  * the 'then' action is runnable, we use a pseudo-action for convenience:
485  * minimum number of clone instances have runnable actions ->
486  * pseudo-action is runnable -> dependency is runnable.
487  */
488  min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
489  if (min_required_before > 0) {
490  clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
491  flags, min_required_before);
492  } else {
493  pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
494  action_then, flags);
495  }
496 
497  if (symmetry == ordering_symmetric) {
498  inverse_ordering(id, kind, rsc_first, action_first,
499  rsc_then, action_then);
500  }
501 }
502 
531 void
532 pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task,
533  pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
534  char *then_action_task, pcmk_action_t *then_action,
535  uint32_t flags, pcmk_scheduler_t *sched)
536 {
537  pcmk__action_relation_t *order = NULL;
538 
539  // One of action or resource must be specified for each side
540  CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
541  && ((then_action != NULL) || (then_rsc != NULL)),
542  free(first_action_task); free(then_action_task); return);
543 
544  if ((first_rsc == NULL) && (first_action != NULL)) {
545  first_rsc = first_action->rsc;
546  }
547  if ((then_rsc == NULL) && (then_action != NULL)) {
548  then_rsc = then_action->rsc;
549  }
550 
551  order = pcmk__assert_alloc(1, sizeof(pcmk__action_relation_t));
552 
553  order->id = sched->order_id++;
554  order->flags = flags;
555  order->rsc1 = first_rsc;
556  order->rsc2 = then_rsc;
557  order->action1 = first_action;
558  order->action2 = then_action;
559  order->task1 = first_action_task;
560  order->task2 = then_action_task;
561 
562  if ((order->task1 == NULL) && (first_action != NULL)) {
563  order->task1 = strdup(first_action->uuid);
564  }
565 
566  if ((order->task2 == NULL) && (then_action != NULL)) {
567  order->task2 = strdup(then_action->uuid);
568  }
569 
570  if ((order->rsc1 == NULL) && (first_action != NULL)) {
571  order->rsc1 = first_action->rsc;
572  }
573 
574  if ((order->rsc2 == NULL) && (then_action != NULL)) {
575  order->rsc2 = then_action->rsc;
576  }
577 
578  pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s",
579  (sched->order_id - 1),
580  pcmk__s(order->task1, "an underspecified action"),
581  pcmk__s(order->task2, "an underspecified action"));
582 
583  sched->ordering_constraints = g_list_prepend(sched->ordering_constraints,
584  order);
586 }
587 
600 static int
601 unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind,
602  const char *parent_symmetrical_s, pcmk_scheduler_t *scheduler)
603 {
604  GList *set_iter = NULL;
605  GList *resources = NULL;
606 
607  pcmk_resource_t *last = NULL;
608  pcmk_resource_t *resource = NULL;
609 
610  int local_kind = parent_kind;
611  bool sequential = false;
612  uint32_t flags = pcmk__ar_ordered;
613  enum ordering_symmetry symmetry;
614 
615  char *key = NULL;
616  const char *id = pcmk__xe_id(set);
617  const char *action = crm_element_value(set, PCMK_XA_ACTION);
618  const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL);
619  const char *kind_s = crm_element_value(set, PCMK_XA_KIND);
620 
621  if (action == NULL) {
623  }
624 
625  if (kind_s) {
626  local_kind = get_ordering_type(set);
627  }
628  if (sequential_s == NULL) {
629  sequential_s = "1";
630  }
631 
632  sequential = crm_is_true(sequential_s);
633 
634  symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
635  flags = ordering_flags_for_kind(local_kind, action, symmetry);
636 
637  for (const xmlNode *xml_rsc = pcmk__xe_first_child(set,
639  NULL, NULL);
640  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
641 
642  EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc));
643  resources = g_list_append(resources, resource);
644  }
645 
646  if (pcmk__list_of_1(resources)) {
647  crm_trace("Single set: %s", id);
648  goto done;
649  }
650 
651  set_iter = resources;
652  while (set_iter != NULL) {
653  resource = (pcmk_resource_t *) set_iter->data;
654  set_iter = set_iter->next;
655 
656  key = pcmk__op_key(resource->id, action, 0);
657 
658  if (local_kind == pe_order_kind_serialize) {
659  /* Serialize before everything that comes after */
660 
661  for (GList *iter = set_iter; iter != NULL; iter = iter->next) {
662  pcmk_resource_t *then_rsc = iter->data;
663  char *then_key = pcmk__op_key(then_rsc->id, action, 0);
664 
665  pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
666  then_key, NULL, flags, scheduler);
667  }
668 
669  } else if (sequential) {
670  if (last != NULL) {
671  pcmk__order_resource_actions(last, action, resource, action,
672  flags);
673  }
674  last = resource;
675  }
676  free(key);
677  }
678 
679  if (symmetry == ordering_asymmetric) {
680  goto done;
681  }
682 
683  last = NULL;
684  action = invert_action(action);
685 
686  flags = ordering_flags_for_kind(local_kind, action,
688 
689  set_iter = resources;
690  while (set_iter != NULL) {
691  resource = (pcmk_resource_t *) set_iter->data;
692  set_iter = set_iter->next;
693 
694  if (sequential) {
695  if (last != NULL) {
696  pcmk__order_resource_actions(resource, action, last, action,
697  flags);
698  }
699  last = resource;
700  }
701  }
702 
703  done:
704  g_list_free(resources);
705  return pcmk_rc_ok;
706 }
707 
720 static int
721 order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
723  enum ordering_symmetry symmetry)
724 {
725 
726  const xmlNode *xml_rsc = NULL;
727  const xmlNode *xml_rsc_2 = NULL;
728 
729  pcmk_resource_t *rsc_1 = NULL;
730  pcmk_resource_t *rsc_2 = NULL;
731 
732  const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION);
733  const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION);
734 
735  uint32_t flags = pcmk__ar_none;
736 
737  bool require_all = true;
738 
739  (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all);
740 
741  if (action_1 == NULL) {
742  action_1 = PCMK_ACTION_START;
743  }
744 
745  if (action_2 == NULL) {
746  action_2 = PCMK_ACTION_START;
747  }
748 
749  if (symmetry == ordering_symmetric_inverse) {
750  action_1 = invert_action(action_1);
751  action_2 = invert_action(action_2);
752  }
753 
754  if (pcmk__str_eq(PCMK_ACTION_STOP, action_1, pcmk__str_none)
755  || pcmk__str_eq(PCMK_ACTION_DEMOTE, action_1, pcmk__str_none)) {
756  /* Assuming: A -> ( B || C) -> D
757  * The one-or-more logic only applies during the start/promote phase.
758  * During shutdown neither B nor can shutdown until D is down, so simply
759  * turn require_all back on.
760  */
761  require_all = true;
762  }
763 
764  flags = ordering_flags_for_kind(kind, action_1, symmetry);
765 
766  /* If we have an unordered set1, whether it is sequential or not is
767  * irrelevant in regards to set2.
768  */
769  if (!require_all) {
770  char *task = crm_strdup_printf(PCMK_ACTION_ONE_OR_MORE ":%s",
771  pcmk__xe_id(set1));
772  pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler);
773 
774  free(task);
776 
777  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
778  NULL);
779  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
780 
781  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
782 
783  /* Add an ordering constraint between every element in set1 and the
784  * pseudo action. If any action in set1 is runnable the pseudo
785  * action will be runnable.
786  */
787  pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
788  NULL, NULL, NULL, unordered_action,
791  scheduler);
792  }
793  for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
794  NULL);
795  xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
796 
797  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
798 
799  /* Add an ordering constraint between the pseudo-action and every
800  * element in set2. If the pseudo-action is runnable, every action
801  * in set2 will be runnable.
802  */
803  pcmk__new_ordering(NULL, NULL, unordered_action,
804  rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
806  scheduler);
807  }
808 
809  return pcmk_rc_ok;
810  }
811 
813  if (symmetry == ordering_symmetric_inverse) {
814  // Get the first one
815  xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
816  NULL);
817  if (xml_rsc != NULL) {
818  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
819  }
820 
821  } else {
822  // Get the last one
823  const char *rid = NULL;
824 
825  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF,
826  NULL, NULL);
827  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
828 
829  rid = pcmk__xe_id(xml_rsc);
830  }
831  EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
832  }
833  }
834 
836  if (symmetry == ordering_symmetric_inverse) {
837  // Get the last one
838  const char *rid = NULL;
839 
840  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
841  NULL, NULL);
842  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
843 
844  rid = pcmk__xe_id(xml_rsc);
845  }
846  EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
847 
848  } else {
849  // Get the first one
850  xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
851  NULL);
852  if (xml_rsc != NULL) {
853  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
854  }
855  }
856  }
857 
858  if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
859  pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
860 
861  } else if (rsc_1 != NULL) {
862  for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
863  NULL);
864  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
865 
866  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
867  pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
868  flags);
869  }
870 
871  } else if (rsc_2 != NULL) {
872  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
873  NULL);
874  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
875 
876  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
877  pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
878  flags);
879  }
880 
881  } else {
882  for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
883  NULL);
884  xml_rsc != NULL; xml_rsc = pcmk__xe_next_same(xml_rsc)) {
885 
886  EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
887 
888  for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2,
890  NULL, NULL);
891  xml_rsc_2 != NULL; xml_rsc_2 = pcmk__xe_next_same(xml_rsc_2)) {
892 
893  EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
894  pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
895  action_2, flags);
896  }
897  }
898  }
899 
900  return pcmk_rc_ok;
901 }
902 
914 static int
915 unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
917 {
918  const char *id_first = NULL;
919  const char *id_then = NULL;
920  const char *action_first = NULL;
921  const char *action_then = NULL;
922 
923  pcmk_resource_t *rsc_first = NULL;
924  pcmk_resource_t *rsc_then = NULL;
925  pcmk_tag_t *tag_first = NULL;
926  pcmk_tag_t *tag_then = NULL;
927 
928  xmlNode *rsc_set_first = NULL;
929  xmlNode *rsc_set_then = NULL;
930  bool any_sets = false;
931 
932  // Check whether there are any resource sets with template or tag references
933  *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
934  if (*expanded_xml != NULL) {
935  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
936  return pcmk_rc_ok;
937  }
938 
939  id_first = crm_element_value(xml_obj, PCMK_XA_FIRST);
940  id_then = crm_element_value(xml_obj, PCMK_XA_THEN);
941  if ((id_first == NULL) || (id_then == NULL)) {
942  return pcmk_rc_ok;
943  }
944 
945  if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first,
946  &tag_first)) {
947  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
948  "valid resource or tag",
949  pcmk__xe_id(xml_obj), id_first);
950  return pcmk_rc_unpack_error;
951  }
952 
953  if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then,
954  &tag_then)) {
955  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
956  "valid resource or tag",
957  pcmk__xe_id(xml_obj), id_then);
958  return pcmk_rc_unpack_error;
959  }
960 
961  if ((rsc_first != NULL) && (rsc_then != NULL)) {
962  // Neither side references a template or tag
963  return pcmk_rc_ok;
964  }
965 
966  action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
967  action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
968 
969  *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
970 
971  /* Convert template/tag reference in PCMK_XA_FIRST into constraint
972  * PCMK_XE_RESOURCE_SET
973  */
974  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true,
975  scheduler)) {
976  free_xml(*expanded_xml);
977  *expanded_xml = NULL;
978  return pcmk_rc_unpack_error;
979  }
980 
981  if (rsc_set_first != NULL) {
982  if (action_first != NULL) {
983  /* Move PCMK_XA_FIRST_ACTION into converted PCMK_XE_RESOURCE_SET as
984  * PCMK_XA_ACTION
985  */
986  crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first);
988  }
989  any_sets = true;
990  }
991 
992  /* Convert template/tag reference in PCMK_XA_THEN into constraint
993  * PCMK_XE_RESOURCE_SET
994  */
995  if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true,
996  scheduler)) {
997  free_xml(*expanded_xml);
998  *expanded_xml = NULL;
999  return pcmk_rc_unpack_error;
1000  }
1001 
1002  if (rsc_set_then != NULL) {
1003  if (action_then != NULL) {
1004  /* Move PCMK_XA_THEN_ACTION into converted PCMK_XE_RESOURCE_SET as
1005  * PCMK_XA_ACTION
1006  */
1007  crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then);
1008  pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_THEN_ACTION);
1009  }
1010  any_sets = true;
1011  }
1012 
1013  if (any_sets) {
1014  crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
1015  } else {
1016  free_xml(*expanded_xml);
1017  *expanded_xml = NULL;
1018  }
1019 
1020  return pcmk_rc_ok;
1021 }
1022 
1030 void
1032 {
1033  xmlNode *set = NULL;
1034  xmlNode *last = NULL;
1035 
1036  xmlNode *orig_xml = NULL;
1037  xmlNode *expanded_xml = NULL;
1038 
1039  const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
1040  const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL);
1041  enum pe_order_kind kind = get_ordering_type(xml_obj);
1042 
1043  enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1044  NULL);
1045 
1046  // Expand any resource tags in the constraint XML
1047  if (unpack_order_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
1048  return;
1049  }
1050  if (expanded_xml != NULL) {
1051  orig_xml = xml_obj;
1052  xml_obj = expanded_xml;
1053  }
1054 
1055  // If the constraint has resource sets, unpack them
1056  for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1057  set != NULL; set = pcmk__xe_next_same(set)) {
1058 
1059  set = expand_idref(set, scheduler->input);
1060  if ((set == NULL) // Configuration error, message already logged
1061  || (unpack_order_set(set, kind, invert, scheduler) != pcmk_rc_ok)) {
1062 
1063  if (expanded_xml != NULL) {
1064  free_xml(expanded_xml);
1065  }
1066  return;
1067  }
1068 
1069  if (last != NULL) {
1070 
1071  if (order_rsc_sets(id, last, set, kind, scheduler,
1072  symmetry) != pcmk_rc_ok) {
1073  if (expanded_xml != NULL) {
1074  free_xml(expanded_xml);
1075  }
1076  return;
1077  }
1078 
1079  if ((symmetry == ordering_symmetric)
1080  && (order_rsc_sets(id, set, last, kind, scheduler,
1082  if (expanded_xml != NULL) {
1083  free_xml(expanded_xml);
1084  }
1085  return;
1086  }
1087 
1088  }
1089  last = set;
1090  }
1091 
1092  if (expanded_xml) {
1093  free_xml(expanded_xml);
1094  xml_obj = orig_xml;
1095  }
1096 
1097  // If the constraint has no resource sets, unpack it as a simple ordering
1098  if (last == NULL) {
1099  return unpack_simple_rsc_order(xml_obj, scheduler);
1100  }
1101 }
1102 
1103 static bool
1104 ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input)
1105 {
1106  /* Prevent user-defined ordering constraints between resources
1107  * running in a guest node and the resource that defines that node.
1108  */
1110  && (input->action->rsc != NULL)
1111  && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1112 
1113  pcmk__config_warn("Invalid ordering constraint between %s and %s",
1114  input->action->rsc->id, action->rsc->id);
1115  return true;
1116  }
1117 
1118  /* If there's an order like
1119  * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
1120  *
1121  * then rscA is being migrated from node1 to node2, while rscB is being
1122  * migrated from node2 to node1. If there would be a graph loop,
1123  * break the order "load_stopped_node2" -> "rscA_migrate_to node1".
1124  */
1125  if (((uint32_t) input->type == pcmk__ar_if_on_same_node_or_target)
1126  && (action->rsc != NULL)
1127  && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)
1129  return true;
1130  }
1131 
1132  return false;
1133 }
1134 
1135 void
1137 {
1138  for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
1139  pcmk_action_t *action = (pcmk_action_t *) iter->data;
1140  pcmk__related_action_t *input = NULL;
1141 
1142  for (GList *input_iter = action->actions_before;
1143  input_iter != NULL; input_iter = input_iter->next) {
1144 
1145  input = input_iter->data;
1146  if (ordering_is_invalid(action, input)) {
1147  input->type = (enum pe_ordering) pcmk__ar_none;
1148  }
1149  }
1150  }
1151 }
1152 
1160 void
1162 {
1163  for (GList *iter = node->details->data_set->actions;
1164  iter != NULL; iter = iter->next) {
1165 
1166  pcmk_action_t *action = (pcmk_action_t *) iter->data;
1167 
1168  // Only stops on the node shutting down are relevant
1169  if (!pcmk__same_node(action->node, node)
1170  || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1171  continue;
1172  }
1173 
1174  // Resources and nodes in maintenance mode won't be touched
1175 
1176  if (pcmk_is_set(action->rsc->flags, pcmk_rsc_maintenance)) {
1177  pcmk__rsc_trace(action->rsc,
1178  "Not ordering %s before shutdown of %s because "
1179  "resource in maintenance mode",
1180  action->uuid, pcmk__node_name(node));
1181  continue;
1182 
1183  } else if (node->details->maintenance) {
1184  pcmk__rsc_trace(action->rsc,
1185  "Not ordering %s before shutdown of %s because "
1186  "node in maintenance mode",
1187  action->uuid, pcmk__node_name(node));
1188  continue;
1189  }
1190 
1191  /* Don't touch a resource that is unmanaged or blocked, to avoid
1192  * blocking the shutdown (though if another action depends on this one,
1193  * we may still end up blocking)
1194  */
1195  if (!pcmk_any_flags_set(action->rsc->flags,
1197  pcmk__rsc_trace(action->rsc,
1198  "Not ordering %s before shutdown of %s because "
1199  "resource is unmanaged or blocked",
1200  action->uuid, pcmk__node_name(node));
1201  continue;
1202  }
1203 
1204  pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1205  action->uuid, pcmk__node_name(node));
1207  pcmk__new_ordering(action->rsc, NULL, action, NULL,
1208  strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op,
1210  node->details->data_set);
1211  }
1212 }
1213 
1224 static GList *
1225 find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key)
1226 {
1227  // Search under given task key directly
1228  GList *list = find_actions(rsc->actions, original_key, NULL);
1229 
1230  if (list == NULL) {
1231  // Search again using this resource's ID
1232  char *key = NULL;
1233  char *task = NULL;
1234  guint interval_ms = 0;
1235 
1236  CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms),
1237  return NULL);
1238  key = pcmk__op_key(rsc->id, task, interval_ms);
1239  list = find_actions(rsc->actions, key, NULL);
1240  free(key);
1241  free(task);
1242  }
1243  return list;
1244 }
1245 
1254 static void
1255 order_resource_actions_after(pcmk_action_t *first_action,
1256  const pcmk_resource_t *rsc,
1257  pcmk__action_relation_t *order)
1258 {
1259  GList *then_actions = NULL;
1260  uint32_t flags = pcmk__ar_none;
1261 
1262  CRM_CHECK((rsc != NULL) && (order != NULL), return);
1263 
1264  flags = order->flags;
1265  pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1266  order->id, rsc->id);
1267 
1268  if (order->action2 != NULL) {
1269  then_actions = g_list_prepend(NULL, order->action2);
1270 
1271  } else {
1272  then_actions = find_actions_by_task(rsc, order->task2);
1273  }
1274 
1275  if (then_actions == NULL) {
1276  pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1277  order->id, order->task2, rsc->id);
1278  return;
1279  }
1280 
1281  if ((first_action != NULL) && (first_action->rsc == rsc)
1282  && pcmk_is_set(first_action->flags, pcmk_action_migration_abort)) {
1283 
1284  pcmk__rsc_trace(rsc,
1285  "Detected dangling migration ordering (%s then %s %s)",
1286  first_action->uuid, order->task2, rsc->id);
1288  }
1289 
1290  if ((first_action == NULL)
1292 
1293  pcmk__rsc_debug(rsc,
1294  "Ignoring ordering %d for %s: No first action found",
1295  order->id, rsc->id);
1296  g_list_free(then_actions);
1297  return;
1298  }
1299 
1300  for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1301  pcmk_action_t *then_action_iter = (pcmk_action_t *) iter->data;
1302 
1303  if (first_action != NULL) {
1304  order_actions(first_action, then_action_iter, flags);
1305  } else {
1307  crm_warn("%s of %s is unrunnable because there is no %s of %s "
1308  "to order it after", then_action_iter->task, rsc->id,
1309  order->task1, order->rsc1->id);
1310  }
1311  }
1312 
1313  g_list_free(then_actions);
1314 }
1315 
1316 static void
1317 rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order)
1318 {
1319  GList *first_actions = NULL;
1320  pcmk_action_t *first_action = order->action1;
1321  pcmk_resource_t *then_rsc = order->rsc2;
1322 
1323  pcmk__assert(first_rsc != NULL);
1324  pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1325  order->id, first_rsc->id);
1326 
1327  if (first_action != NULL) {
1328  first_actions = g_list_prepend(NULL, first_action);
1329 
1330  } else {
1331  first_actions = find_actions_by_task(first_rsc, order->task1);
1332  }
1333 
1334  if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1335  pcmk__rsc_trace(first_rsc,
1336  "Ignoring constraint %d: first (%s for %s) not found",
1337  order->id, order->task1, first_rsc->id);
1338 
1339  } else if (first_actions == NULL) {
1340  char *key = NULL;
1341  char *op_type = NULL;
1342  guint interval_ms = 0;
1343 
1344  parse_op_key(order->task1, NULL, &op_type, &interval_ms);
1345  key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1346 
1347  if ((first_rsc->fns->state(first_rsc, TRUE) == pcmk_role_stopped)
1348  && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) {
1349  free(key);
1350  pcmk__rsc_trace(first_rsc,
1351  "Ignoring constraint %d: first (%s for %s) "
1352  "not found",
1353  order->id, order->task1, first_rsc->id);
1354 
1355  } else if ((first_rsc->fns->state(first_rsc,
1356  TRUE) == pcmk_role_unpromoted)
1357  && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE,
1358  pcmk__str_none)) {
1359  free(key);
1360  pcmk__rsc_trace(first_rsc,
1361  "Ignoring constraint %d: first (%s for %s) "
1362  "not found",
1363  order->id, order->task1, first_rsc->id);
1364 
1365  } else {
1366  pcmk__rsc_trace(first_rsc,
1367  "Creating first (%s for %s) for constraint %d ",
1368  order->task1, first_rsc->id, order->id);
1369  first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1370  first_rsc->cluster);
1371  first_actions = g_list_prepend(NULL, first_action);
1372  }
1373 
1374  free(op_type);
1375  }
1376 
1377  if (then_rsc == NULL) {
1378  if (order->action2 == NULL) {
1379  pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1380  order->id);
1381  return;
1382  }
1383  then_rsc = order->action2->rsc;
1384  }
1385  for (GList *iter = first_actions; iter != NULL; iter = iter->next) {
1386  first_action = iter->data;
1387 
1388  if (then_rsc == NULL) {
1389  order_actions(first_action, order->action2, order->flags);
1390 
1391  } else {
1392  order_resource_actions_after(first_action, then_rsc, order);
1393  }
1394  }
1395 
1396  g_list_free(first_actions);
1397 }
1398 
1399 // GFunc to call pcmk__block_colocation_dependents()
1400 static void
1401 block_colocation_dependents(gpointer data, gpointer user_data)
1402 {
1404 }
1405 
1406 // GFunc to call pcmk__update_action_for_orderings()
1407 static void
1408 update_action_for_orderings(gpointer data, gpointer user_data)
1409 {
1411  (pcmk_scheduler_t *) user_data);
1412 }
1413 
1420 void
1422 {
1423  crm_trace("Applying ordering constraints");
1424 
1425  /* Ordering constraints need to be processed in the order they were created.
1426  * rsc_order_first() and order_resource_actions_after() require the relevant
1427  * actions to already exist in some cases, but rsc_order_first() will create
1428  * the 'first' action in certain cases. Thus calling rsc_order_first() can
1429  * change the behavior of later-created orderings.
1430  *
1431  * Also, g_list_append() should be avoided for performance reasons, so we
1432  * prepend orderings when creating them and reverse the list here.
1433  *
1434  * @TODO This is brittle and should be carefully redesigned so that the
1435  * order of creation doesn't matter, and the reverse becomes unneeded.
1436  */
1437  sched->ordering_constraints = g_list_reverse(sched->ordering_constraints);
1438 
1439  for (GList *iter = sched->ordering_constraints;
1440  iter != NULL; iter = iter->next) {
1441 
1442  pcmk__action_relation_t *order = iter->data;
1443  pcmk_resource_t *rsc = order->rsc1;
1444 
1445  if (rsc != NULL) {
1446  rsc_order_first(rsc, order);
1447  continue;
1448  }
1449 
1450  rsc = order->rsc2;
1451  if (rsc != NULL) {
1452  order_resource_actions_after(order->action1, rsc, order);
1453 
1454  } else {
1455  crm_trace("Applying ordering constraint %d (non-resource actions)",
1456  order->id);
1457  order_actions(order->action1, order->action2, order->flags);
1458  }
1459  }
1460 
1461  g_list_foreach(sched->actions, block_colocation_dependents, NULL);
1462 
1463  crm_trace("Ordering probes");
1464  pcmk__order_probes(sched);
1465 
1466  crm_trace("Updating %d actions", g_list_length(sched->actions));
1467  g_list_foreach(sched->actions, update_action_for_orderings, sched);
1468 
1470 }
1471 
1479 void
1481 {
1482  const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1483 
1484  for (GList *iter = list; iter != NULL; iter = iter->next) {
1485  pcmk_action_t *before = (pcmk_action_t *) iter->data;
1486  const char *before_desc = before->task? before->task : before->uuid;
1487 
1488  crm_debug("Ordering %s on %s before %s on %s",
1489  before_desc, pcmk__node_name(before->node),
1490  after_desc, pcmk__node_name(after->node));
1491  order_actions(before, after, pcmk__ar_ordered);
1492  }
1493 }
1494 
1501 void
1503 {
1504  // Order start and promote after all instances are stopped
1506  rsc, PCMK_ACTION_START,
1509  rsc, PCMK_ACTION_PROMOTE,
1511 
1512  // Order stop, start, and promote after all instances are demoted
1514  rsc, PCMK_ACTION_STOP,
1517  rsc, PCMK_ACTION_START,
1520  rsc, PCMK_ACTION_PROMOTE,
1522 
1523  // Order promote after all instances are started
1525  rsc, PCMK_ACTION_PROMOTE,
1527 
1528  // Order demote after all instances are demoted
1530  rsc, PCMK_ACTION_DEMOTED,
1532 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:974
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:189
User-configured asymmetric ordering.
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:126
Stopped.
Definition: roles.h:36
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1038
#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...)
#define PCMK_XE_RESOURCE_REF
Definition: xml_names.h:177
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:313
#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:1020
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:154
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:458
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
#define PCMK_XA_THEN_ACTION
Definition: xml_names.h:429
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:94
struct pe_node_shared_s * details
Definition: nodes.h:168
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:2170
#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:993
Unpromoted.
Definition: roles.h:38
void pcmk__xe_remove_attr(xmlNode *element, const char *name)
Definition: xml.c:702
#define PCMK_ACTION_STOP
Definition: actions.h:75
GList * actions
Definition: resources.h:444
#define PCMK_XA_ID
Definition: xml_names.h:301
char * uuid
Definition: actions.h:344
#define PCMK_XA_SCORE
Definition: xml_names.h:396
#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:958
xmlNode * input
Definition: scheduler.h:196
#define pcmk__warn_once(wo_flag, fmt...)
gboolean order_actions(pcmk_action_t *first, pcmk_action_t *then, uint32_t flags)
Definition: utils.c:457
#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:1129
#define pcmk__clear_relation_flags(ar_flags, flags_to_clear)
#define PCMK_XA_THEN
Definition: xml_names.h:428
#define pcmk__assert(expr)
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:1480
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:76
xmlNode * input
pe_order_kind
#define PCMK_XE_RESOURCE_SET
Definition: xml_names.h:178
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: actions.c:249
Definition: tags.h:29
#define PCMK_ACTION_PROMOTE
Definition: actions.h:66
int pcmk_parse_score(const char *score_s, int *score, int default_score)
Parse an integer score from a string.
Definition: scores.c:39
void pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
#define PCMK_XA_FIRST_ACTION
Definition: xml_names.h:289
enum pe_action_flags flags
Definition: actions.h:349
gboolean maintenance
Definition: nodes.h:105
#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:415
#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:500
pcmk_resource_t * rsc
Definition: actions.h:340
#define PCMK_XA_KIND
Definition: xml_names.h:312
#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:2130
&#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: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: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