pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pcmk_sched_constraints.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2020 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 <sys/param.h>
13 #include <sys/types.h>
14 #include <regex.h>
15 #include <glib.h>
16 
17 #include <crm/crm.h>
18 #include <crm/cib.h>
19 #include <crm/msg_xml.h>
20 #include <crm/common/xml.h>
21 #include <crm/common/iso8601.h>
22 #include <crm/pengine/status.h>
23 #include <crm/pengine/internal.h>
24 #include <crm/pengine/rules.h>
25 #include <pacemaker-internal.h>
26 
31 };
32 
33 #define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do { \
34  __rsc = pe_find_constraint_resource(data_set->resources, __name); \
35  if(__rsc == NULL) { \
36  pcmk__config_err("%s: No resource found for %s", __set, __name); \
37  return FALSE; \
38  } \
39  } while(0)
40 
41 enum pe_ordering get_flags(const char *id, enum pe_order_kind kind,
42  const char *action_first, const char *action_then, gboolean invert);
44 static pe__location_t *generate_location_rule(pe_resource_t *rsc,
45  xmlNode *rule_xml,
46  const char *discovery,
47  crm_time_t *next_change,
48  pe_working_set_t *data_set,
49  pe_match_data_t *match_data);
50 
51 static bool
52 evaluate_lifetime(xmlNode *lifetime, pe_working_set_t *data_set)
53 {
54  bool result = FALSE;
55  crm_time_t *next_change = crm_time_new_undefined();
56 
57  result = pe_evaluate_rules(lifetime, NULL, data_set->now, next_change);
58  if (crm_time_is_defined(next_change)) {
59  time_t recheck = (time_t) crm_time_get_seconds_since_epoch(next_change);
60 
61  pe__update_recheck_time(recheck, data_set);
62  }
63  crm_time_free(next_change);
64  return result;
65 }
66 
67 gboolean
68 unpack_constraints(xmlNode * xml_constraints, pe_working_set_t * data_set)
69 {
70  xmlNode *xml_obj = NULL;
71  xmlNode *lifetime = NULL;
72 
73  for (xml_obj = __xml_first_child_element(xml_constraints); xml_obj != NULL;
74  xml_obj = __xml_next_element(xml_obj)) {
75  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
76  const char *tag = crm_element_name(xml_obj);
77 
78  if (id == NULL) {
79  pcmk__config_err("Ignoring <%s> constraint without "
80  XML_ATTR_ID, tag);
81  continue;
82  }
83 
84  crm_trace("Processing constraint %s %s", tag, id);
85 
86  lifetime = first_named_child(xml_obj, "lifetime");
87  if (lifetime) {
88  pcmk__config_warn("Support for 'lifetime' attribute (in %s) is "
89  "deprecated (the rules it contains should "
90  "instead be direct descendents of the "
91  "constraint object)", id);
92  }
93 
94  if (lifetime && !evaluate_lifetime(lifetime, data_set)) {
95  crm_info("Constraint %s %s is not active", tag, id);
96 
97  } else if (safe_str_eq(XML_CONS_TAG_RSC_ORDER, tag)) {
98  unpack_rsc_order(xml_obj, data_set);
99 
100  } else if (safe_str_eq(XML_CONS_TAG_RSC_DEPEND, tag)) {
101  unpack_rsc_colocation(xml_obj, data_set);
102 
103  } else if (safe_str_eq(XML_CONS_TAG_RSC_LOCATION, tag)) {
104  unpack_location(xml_obj, data_set);
105 
106  } else if (safe_str_eq(XML_CONS_TAG_RSC_TICKET, tag)) {
107  unpack_rsc_ticket(xml_obj, data_set);
108 
109  } else {
110  pe_err("Unsupported constraint type: %s", tag);
111  }
112  }
113 
114  return TRUE;
115 }
116 
117 static const char *
118 invert_action(const char *action)
119 {
120  if (safe_str_eq(action, RSC_START)) {
121  return RSC_STOP;
122 
123  } else if (safe_str_eq(action, RSC_STOP)) {
124  return RSC_START;
125 
126  } else if (safe_str_eq(action, RSC_PROMOTE)) {
127  return RSC_DEMOTE;
128 
129  } else if (safe_str_eq(action, RSC_DEMOTE)) {
130  return RSC_PROMOTE;
131 
132  } else if (safe_str_eq(action, RSC_PROMOTED)) {
133  return RSC_DEMOTED;
134 
135  } else if (safe_str_eq(action, RSC_DEMOTED)) {
136  return RSC_PROMOTED;
137 
138  } else if (safe_str_eq(action, RSC_STARTED)) {
139  return RSC_STOPPED;
140 
141  } else if (safe_str_eq(action, RSC_STOPPED)) {
142  return RSC_STARTED;
143  }
144  crm_warn("Unknown action '%s' specified in order constraint", action);
145  return NULL;
146 }
147 
148 static enum pe_order_kind
149 get_ordering_type(xmlNode * xml_obj)
150 {
152  const char *kind = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND);
153 
154  if (kind == NULL) {
155  const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
156 
157  kind_e = pe_order_kind_mandatory;
158 
159  if (score) {
160  // @COMPAT deprecated informally since 1.0.7, formally since 2.0.1
161  int score_i = char2score(score);
162 
163  if (score_i == 0) {
164  kind_e = pe_order_kind_optional;
165  }
167  "Support for 'score' in rsc_order is deprecated "
168  "and will be removed in a future release (use 'kind' instead)");
169  }
170 
171  } else if (safe_str_eq(kind, "Mandatory")) {
172  kind_e = pe_order_kind_mandatory;
173 
174  } else if (safe_str_eq(kind, "Optional")) {
175  kind_e = pe_order_kind_optional;
176 
177  } else if (safe_str_eq(kind, "Serialize")) {
178  kind_e = pe_order_kind_serialize;
179 
180  } else {
181  pcmk__config_err("Resetting '" XML_ORDER_ATTR_KIND "' for constraint "
182  "'%s' to Mandatory because '%s' is not valid",
183  crm_str(ID(xml_obj)), kind);
184  }
185  return kind_e;
186 }
187 
188 static pe_resource_t *
189 pe_find_constraint_resource(GListPtr rsc_list, const char *id)
190 {
191  GListPtr rIter = NULL;
192 
193  for (rIter = rsc_list; id && rIter; rIter = rIter->next) {
194  pe_resource_t *parent = rIter->data;
195  pe_resource_t *match = parent->fns->find_rsc(parent, id, NULL,
197 
198  if (match != NULL) {
199  if(safe_str_neq(match->id, id)) {
200  /* We found an instance of a clone instead */
201  match = uber_parent(match);
202  crm_debug("Found %s for %s", match->id, id);
203  }
204  return match;
205  }
206  }
207  crm_trace("No match for %s", id);
208  return NULL;
209 }
210 
211 static gboolean
212 pe_find_constraint_tag(pe_working_set_t * data_set, const char * id, pe_tag_t ** tag)
213 {
214  gboolean rc = FALSE;
215 
216  *tag = NULL;
217  rc = g_hash_table_lookup_extended(data_set->template_rsc_sets, id,
218  NULL, (gpointer*) tag);
219 
220  if (rc == FALSE) {
221  rc = g_hash_table_lookup_extended(data_set->tags, id,
222  NULL, (gpointer*) tag);
223 
224  if (rc == FALSE) {
225  crm_warn("No template or tag named '%s'", id);
226  return FALSE;
227 
228  } else if (*tag == NULL) {
229  crm_warn("No resource is tagged with '%s'", id);
230  return FALSE;
231  }
232 
233  } else if (*tag == NULL) {
234  crm_warn("No resource is derived from template '%s'", id);
235  return FALSE;
236  }
237 
238  return rc;
239 }
240 
241 static gboolean
242 valid_resource_or_tag(pe_working_set_t * data_set, const char * id,
243  pe_resource_t ** rsc, pe_tag_t ** tag)
244 {
245  gboolean rc = FALSE;
246 
247  if (rsc) {
248  *rsc = NULL;
249  *rsc = pe_find_constraint_resource(data_set->resources, id);
250  if (*rsc) {
251  return TRUE;
252  }
253  }
254 
255  if (tag) {
256  *tag = NULL;
257  rc = pe_find_constraint_tag(data_set, id, tag);
258  }
259 
260  return rc;
261 }
262 
263 static gboolean
264 order_is_symmetrical(xmlNode * xml_obj,
265  enum pe_order_kind parent_kind, const char * parent_symmetrical_s)
266 {
267  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
268  const char *kind_s = crm_element_value(xml_obj, XML_ORDER_ATTR_KIND);
269  const char *score_s = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
270  const char *symmetrical_s = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
271  enum pe_order_kind kind = parent_kind;
272 
273  if (kind_s || score_s) {
274  kind = get_ordering_type(xml_obj);
275  }
276 
277  if (symmetrical_s == NULL) {
278  symmetrical_s = parent_symmetrical_s;
279  }
280 
281  if (symmetrical_s) {
282  gboolean symmetrical = crm_is_true(symmetrical_s);
283 
284  if (symmetrical && kind == pe_order_kind_serialize) {
286  " for '%s' because not valid with "
287  XML_ORDER_ATTR_KIND " of 'Serialize'", id);
288  return FALSE;
289  }
290 
291  return symmetrical;
292 
293  } else {
294  if (kind == pe_order_kind_serialize) {
295  return FALSE;
296 
297  } else {
298  return TRUE;
299  }
300  }
301 }
302 
303 static gboolean
304 unpack_simple_rsc_order(xmlNode * xml_obj, pe_working_set_t * data_set)
305 {
306  int order_id = 0;
307  pe_resource_t *rsc_then = NULL;
308  pe_resource_t *rsc_first = NULL;
309  gboolean invert_bool = TRUE;
310  int min_required_before = 0;
312  enum pe_ordering cons_weight = pe_order_optional;
313 
314  const char *id_first = NULL;
315  const char *id_then = NULL;
316  const char *action_then = NULL;
317  const char *action_first = NULL;
318  const char *instance_then = NULL;
319  const char *instance_first = NULL;
320 
321  const char *id = NULL;
322 
323  CRM_CHECK(xml_obj != NULL, return FALSE);
324 
325  id = crm_element_value(xml_obj, XML_ATTR_ID);
326  if (id == NULL) {
327  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
328  crm_element_name(xml_obj));
329  return FALSE;
330  }
331 
332  invert_bool = order_is_symmetrical(xml_obj, kind, NULL);
333 
334  id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN);
335  id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST);
336 
337  action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
338  action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
339 
340  instance_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_INSTANCE);
341  instance_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_INSTANCE);
342 
343  if (action_first == NULL) {
344  action_first = RSC_START;
345  }
346  if (action_then == NULL) {
347  action_then = action_first;
348  }
349 
350  if (id_first == NULL) {
351  pcmk__config_err("Ignoring constraint '%s' without "
353  return FALSE;
354  }
355  if (id_then == NULL) {
356  pcmk__config_err("Ignoring constraint '%s' without "
357  XML_ORDER_ATTR_THEN, id);
358  return FALSE;
359  }
360 
361  rsc_then = pe_find_constraint_resource(data_set->resources, id_then);
362  rsc_first = pe_find_constraint_resource(data_set->resources, id_first);
363 
364  if (rsc_then == NULL) {
365  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
366  "does not exist", id, id_then);
367  return FALSE;
368 
369  } else if (rsc_first == NULL) {
370  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
371  "does not exist", id, id_first);
372  return FALSE;
373 
374  } else if (instance_then && pe_rsc_is_clone(rsc_then) == FALSE) {
375  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
376  "is not a clone but instance '%s' was requested",
377  id, id_then, instance_then);
378  return FALSE;
379 
380  } else if (instance_first && pe_rsc_is_clone(rsc_first) == FALSE) {
381  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
382  "is not a clone but instance '%s' was requested",
383  id, id_first, instance_first);
384  return FALSE;
385  }
386 
387  if (instance_then) {
388  rsc_then = find_clone_instance(rsc_then, instance_then, data_set);
389  if (rsc_then == NULL) {
390  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
391  "does not have an instance '%s'",
392  id, id_then, instance_then);
393  return FALSE;
394  }
395  }
396 
397  if (instance_first) {
398  rsc_first = find_clone_instance(rsc_first, instance_first, data_set);
399  if (rsc_first == NULL) {
400  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
401  "does not have an instance '%s'",
402  "'%s'", id, id_first, instance_first);
403  return FALSE;
404  }
405  }
406 
407  cons_weight = pe_order_optional;
408  kind = get_ordering_type(xml_obj);
409 
410  if (kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
411  crm_trace("Upgrade : recovery - implies right");
412  cons_weight |= pe_order_implies_then;
413  }
414 
415  if (invert_bool == FALSE) {
416  cons_weight |= get_asymmetrical_flags(kind);
417  } else {
418  cons_weight |= get_flags(id, kind, action_first, action_then, FALSE);
419  }
420 
421  if (pe_rsc_is_clone(rsc_first)) {
422  /* If clone-min is set, require at least that number of instances to be
423  * runnable before allowing dependencies to be runnable.
424  */
425  const char *min_clones_s = g_hash_table_lookup(rsc_first->meta,
427 
428  // @COMPAT 1.1.13: deprecated
429  const char *require_all_s = crm_element_value(xml_obj, "require-all");
430 
431  if (min_clones_s) {
432  min_required_before = crm_parse_int(min_clones_s, "0");
433 
434  } else if (require_all_s) {
436  "Support for require-all in ordering constraints "
437  "is deprecated and will be removed in a future release"
438  " (use clone-min clone meta-attribute instead)");
439  if (crm_is_true(require_all_s) == FALSE) {
440  // require-all=false is deprecated equivalent of clone-min=1
441  min_required_before = 1;
442  }
443  }
444  }
445 
446  /* If there is a minimum number of instances that must be runnable before
447  * the 'then' action is runnable, we use a pseudo action as an intermediate step
448  * start min number of clones -> pseudo action is runnable -> dependency runnable. */
449  if (min_required_before) {
450  GListPtr rIter = NULL;
451  char *task = crm_strdup_printf(CRM_OP_RELAXED_CLONE ":%s", id);
452  pe_action_t *unordered_action = get_pseudo_op(task, data_set);
453  free(task);
454 
455  /* require the pseudo action to have "min_required_before" number of
456  * actions to be considered runnable before allowing the pseudo action
457  * to be runnable. */
458  unordered_action->required_runnable_before = min_required_before;
459  update_action_flags(unordered_action, pe_action_requires_any, __FUNCTION__, __LINE__);
460 
461  for (rIter = rsc_first->children; id && rIter; rIter = rIter->next) {
462  pe_resource_t *child = rIter->data;
463  /* order each clone instance before the pseudo action */
464  custom_action_order(child, pcmk__op_key(child->id, action_first, 0),
465  NULL, NULL, NULL, unordered_action,
467  data_set);
468  }
469 
470  /* order the "then" dependency to occur after the pseudo action only if
471  * the pseudo action is runnable */
472  order_id = custom_action_order(NULL, NULL, unordered_action, rsc_then,
473  pcmk__op_key(rsc_then->id, action_then, 0),
474  NULL, cons_weight|pe_order_runnable_left,
475  data_set);
476  } else {
477  order_id = new_rsc_order(rsc_first, action_first, rsc_then, action_then, cons_weight, data_set);
478  }
479 
480  pe_rsc_trace(rsc_first, "order-%d (%s): %s_%s before %s_%s flags=0x%.6x",
481  order_id, id, rsc_first->id, action_first, rsc_then->id, action_then, cons_weight);
482 
483  if (invert_bool == FALSE) {
484  return TRUE;
485  }
486 
487  action_then = invert_action(action_then);
488  action_first = invert_action(action_first);
489  if (action_then == NULL || action_first == NULL) {
490  pcmk__config_err("Cannot invert constraint '%s' "
491  "(please specify inverse manually)", id);
492  return TRUE;
493  }
494 
495  cons_weight = pe_order_optional;
496  if (kind == pe_order_kind_optional && rsc_then->restart_type == pe_restart_restart) {
497  crm_trace("Upgrade : recovery - implies left");
498  cons_weight |= pe_order_implies_first;
499  }
500 
501  cons_weight |= get_flags(id, kind, action_first, action_then, TRUE);
502 
503  order_id = new_rsc_order(rsc_then, action_then, rsc_first, action_first, cons_weight, data_set);
504 
505  pe_rsc_trace(rsc_then, "order-%d (%s): %s_%s before %s_%s flags=0x%.6x",
506  order_id, id, rsc_then->id, action_then, rsc_first->id, action_first, cons_weight);
507 
508  return TRUE;
509 }
510 
511 static gboolean
512 expand_tags_in_sets(xmlNode * xml_obj, xmlNode ** expanded_xml, pe_working_set_t * data_set)
513 {
514  xmlNode *new_xml = NULL;
515  xmlNode *set = NULL;
516  gboolean any_refs = FALSE;
517  const char *cons_id = NULL;
518 
519  *expanded_xml = NULL;
520 
521  CRM_CHECK(xml_obj != NULL, return FALSE);
522 
523  new_xml = copy_xml(xml_obj);
524  cons_id = ID(new_xml);
525 
526  for (set = __xml_first_child_element(new_xml); set != NULL;
527  set = __xml_next_element(set)) {
528 
529  xmlNode *xml_rsc = NULL;
530  GListPtr tag_refs = NULL;
531  GListPtr gIter = NULL;
532 
533  if (safe_str_neq((const char *)set->name, XML_CONS_TAG_RSC_SET)) {
534  continue;
535  }
536 
537  for (xml_rsc = __xml_first_child_element(set); xml_rsc != NULL;
538  xml_rsc = __xml_next_element(xml_rsc)) {
539 
540  pe_resource_t *rsc = NULL;
541  pe_tag_t *tag = NULL;
542  const char *id = ID(xml_rsc);
543 
544  if (safe_str_neq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF)) {
545  continue;
546  }
547 
548  if (valid_resource_or_tag(data_set, id, &rsc, &tag) == FALSE) {
549  pcmk__config_err("Ignoring resource sets for constraint '%s' "
550  "because '%s' is not a valid resource or tag",
551  cons_id, id);
552  free_xml(new_xml);
553  return FALSE;
554 
555  } else if (rsc) {
556  continue;
557 
558  } else if (tag) {
559  /* The resource_ref under the resource_set references a template/tag */
560  xmlNode *last_ref = xml_rsc;
561 
562  /* A sample:
563 
564  Original XML:
565 
566  <resource_set id="tag1-colocation-0" sequential="true">
567  <resource_ref id="rsc1"/>
568  <resource_ref id="tag1"/>
569  <resource_ref id="rsc4"/>
570  </resource_set>
571 
572  Now we are appending rsc2 and rsc3 which are tagged with tag1 right after it:
573 
574  <resource_set id="tag1-colocation-0" sequential="true">
575  <resource_ref id="rsc1"/>
576  <resource_ref id="tag1"/>
577  <resource_ref id="rsc2"/>
578  <resource_ref id="rsc3"/>
579  <resource_ref id="rsc4"/>
580  </resource_set>
581 
582  */
583 
584  for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
585  const char *obj_ref = (const char *) gIter->data;
586  xmlNode *new_rsc_ref = NULL;
587 
588  new_rsc_ref = xmlNewDocRawNode(getDocPtr(set), NULL,
590  crm_xml_add(new_rsc_ref, XML_ATTR_ID, obj_ref);
591  xmlAddNextSibling(last_ref, new_rsc_ref);
592 
593  last_ref = new_rsc_ref;
594  }
595 
596  any_refs = TRUE;
597 
598  /* Do not directly free '<resource_ref id="tag1"/>'.
599  That would break the further __xml_next_element(xml_rsc)) and cause "Invalid read" seen by valgrind.
600  So just record it into a hash table for freeing it later.
601  */
602  tag_refs = g_list_append(tag_refs, xml_rsc);
603  }
604  }
605 
606  /* Now free '<resource_ref id="tag1"/>', and finally get:
607 
608  <resource_set id="tag1-colocation-0" sequential="true">
609  <resource_ref id="rsc1"/>
610  <resource_ref id="rsc2"/>
611  <resource_ref id="rsc3"/>
612  <resource_ref id="rsc4"/>
613  </resource_set>
614 
615  */
616  for (gIter = tag_refs; gIter != NULL; gIter = gIter->next) {
617  xmlNode *tag_ref = gIter->data;
618 
619  free_xml(tag_ref);
620  }
621  g_list_free(tag_refs);
622  }
623 
624  if (any_refs) {
625  *expanded_xml = new_xml;
626  } else {
627  free_xml(new_xml);
628  }
629 
630  return TRUE;
631 }
632 
633 static gboolean
634 tag_to_set(xmlNode * xml_obj, xmlNode ** rsc_set, const char * attr,
635  gboolean convert_rsc, pe_working_set_t * data_set)
636 {
637  const char *cons_id = NULL;
638  const char *id = NULL;
639 
640  pe_resource_t *rsc = NULL;
641  pe_tag_t *tag = NULL;
642 
643  *rsc_set = NULL;
644 
645  CRM_CHECK((xml_obj != NULL) && (attr != NULL), return FALSE);
646 
647  cons_id = ID(xml_obj);
648  if (cons_id == NULL) {
649  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
650  crm_element_name(xml_obj));
651  return FALSE;
652  }
653 
654  id = crm_element_value(xml_obj, attr);
655  if (id == NULL) {
656  return TRUE;
657  }
658 
659  if (valid_resource_or_tag(data_set, id, &rsc, &tag) == FALSE) {
660  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
661  "valid resource or tag", cons_id, id);
662  return FALSE;
663 
664  } else if (tag) {
665  GListPtr gIter = NULL;
666 
667  /* A template/tag is referenced by the "attr" attribute (first, then, rsc or with-rsc).
668  Add the template/tag's corresponding "resource_set" which contains the resources derived
669  from it or tagged with it under the constraint. */
670  *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
671  crm_xml_add(*rsc_set, XML_ATTR_ID, id);
672 
673  for (gIter = tag->refs; gIter != NULL; gIter = gIter->next) {
674  const char *obj_ref = (const char *) gIter->data;
675  xmlNode *rsc_ref = NULL;
676 
677  rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
678  crm_xml_add(rsc_ref, XML_ATTR_ID, obj_ref);
679  }
680 
681  /* Set sequential="false" for the resource_set */
682  crm_xml_add(*rsc_set, "sequential", XML_BOOLEAN_FALSE);
683 
684  } else if (rsc && convert_rsc) {
685  /* Even a regular resource is referenced by "attr", convert it into a resource_set.
686  Because the other side of the constraint could be a template/tag reference. */
687  xmlNode *rsc_ref = NULL;
688 
689  *rsc_set = create_xml_node(xml_obj, XML_CONS_TAG_RSC_SET);
690  crm_xml_add(*rsc_set, XML_ATTR_ID, id);
691 
692  rsc_ref = create_xml_node(*rsc_set, XML_TAG_RESOURCE_REF);
693  crm_xml_add(rsc_ref, XML_ATTR_ID, id);
694 
695  } else {
696  return TRUE;
697  }
698 
699  /* Remove the "attr" attribute referencing the template/tag */
700  if (*rsc_set) {
701  xml_remove_prop(xml_obj, attr);
702  }
703 
704  return TRUE;
705 }
706 
707 static gboolean unpack_rsc_location(xmlNode * xml_obj, pe_resource_t * rsc_lh, const char * role,
708  const char * score, pe_working_set_t * data_set, pe_match_data_t * match_data);
709 
710 static gboolean
711 unpack_simple_location(xmlNode * xml_obj, pe_working_set_t * data_set)
712 {
713  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
714  const char *value = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
715 
716  if(value) {
717  pe_resource_t *rsc_lh = pe_find_constraint_resource(data_set->resources, value);
718 
719  return unpack_rsc_location(xml_obj, rsc_lh, NULL, NULL, data_set, NULL);
720  }
721 
723  if(value) {
724  regex_t *r_patt = calloc(1, sizeof(regex_t));
725  bool invert = FALSE;
726  GListPtr rIter = NULL;
727 
728  if(value[0] == '!') {
729  value++;
730  invert = TRUE;
731  }
732 
733  if (regcomp(r_patt, value, REG_EXTENDED)) {
734  pcmk__config_err("Ignoring constraint '%s' because "
736  " has invalid value '%s'", id, value);
737  regfree(r_patt);
738  free(r_patt);
739  return FALSE;
740  }
741 
742  for (rIter = data_set->resources; rIter; rIter = rIter->next) {
743  pe_resource_t *r = rIter->data;
744  int nregs = 0;
745  regmatch_t *pmatch = NULL;
746  int status;
747 
748  if(r_patt->re_nsub > 0) {
749  nregs = r_patt->re_nsub + 1;
750  } else {
751  nregs = 1;
752  }
753  pmatch = calloc(nregs, sizeof(regmatch_t));
754 
755  status = regexec(r_patt, r->id, nregs, pmatch, 0);
756 
757  if(invert == FALSE && status == 0) {
758  pe_re_match_data_t re_match_data = {
759  .string = r->id,
760  .nregs = nregs,
761  .pmatch = pmatch
762  };
763  pe_match_data_t match_data = {
764  .re = &re_match_data,
765  .params = r->parameters,
766  .meta = r->meta,
767  };
768  crm_debug("'%s' matched '%s' for %s", r->id, value, id);
769  unpack_rsc_location(xml_obj, r, NULL, NULL, data_set, &match_data);
770 
771  } else if (invert && (status != 0)) {
772  crm_debug("'%s' is an inverted match of '%s' for %s", r->id, value, id);
773  unpack_rsc_location(xml_obj, r, NULL, NULL, data_set, NULL);
774 
775  } else {
776  crm_trace("'%s' does not match '%s' for %s", r->id, value, id);
777  }
778 
779  free(pmatch);
780  }
781 
782  regfree(r_patt);
783  free(r_patt);
784  }
785 
786  return FALSE;
787 }
788 
789 static gboolean
790 unpack_rsc_location(xmlNode * xml_obj, pe_resource_t * rsc_lh, const char * role,
791  const char * score, pe_working_set_t * data_set, pe_match_data_t * match_data)
792 {
793  pe__location_t *location = NULL;
794  const char *id_lh = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
795  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
796  const char *node = crm_element_value(xml_obj, XML_CIB_TAG_NODE);
797  const char *discovery = crm_element_value(xml_obj, XML_LOCATION_ATTR_DISCOVERY);
798 
799  if (rsc_lh == NULL) {
800  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
801  "does not exist", id, id_lh);
802  return FALSE;
803  }
804 
805  if (score == NULL) {
806  score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
807  }
808 
809  if (node != NULL && score != NULL) {
810  int score_i = char2score(score);
811  pe_node_t *match = pe_find_node(data_set->nodes, node);
812 
813  if (!match) {
814  return FALSE;
815  }
816  location = rsc2node_new(id, rsc_lh, score_i, discovery, match, data_set);
817 
818  } else {
819  bool empty = TRUE;
820  crm_time_t *next_change = crm_time_new_undefined();
821 
822  /* This loop is logically parallel to pe_evaluate_rules(), except
823  * instead of checking whether any rule is active, we set up location
824  * constraints for each active rule.
825  */
826  for (xmlNode *rule_xml = first_named_child(xml_obj, XML_TAG_RULE);
827  rule_xml != NULL; rule_xml = crm_next_same_xml(rule_xml)) {
828  empty = FALSE;
829  crm_trace("Unpacking %s/%s", id, ID(rule_xml));
830  generate_location_rule(rsc_lh, rule_xml, discovery, next_change,
831  data_set, match_data);
832  }
833 
834  if (empty) {
835  pcmk__config_err("Ignoring constraint '%s' because it contains "
836  "no rules", id);
837  }
838 
839  /* If there is a point in the future when the evaluation of a rule will
840  * change, make sure the scheduler is re-run by that time.
841  */
842  if (crm_time_is_defined(next_change)) {
843  time_t t = (time_t) crm_time_get_seconds_since_epoch(next_change);
844 
845  pe__update_recheck_time(t, data_set);
846  }
847  crm_time_free(next_change);
848  return TRUE;
849  }
850 
851  if (role == NULL) {
852  role = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
853  }
854 
855  if (location && role) {
856  if (text2role(role) == RSC_ROLE_UNKNOWN) {
857  pe_err("Invalid constraint %s: Bad role %s", id, role);
858  return FALSE;
859 
860  } else {
861  enum rsc_role_e r = text2role(role);
862  switch(r) {
863  case RSC_ROLE_UNKNOWN:
864  case RSC_ROLE_STARTED:
865  case RSC_ROLE_SLAVE:
866  /* Applies to all */
867  location->role_filter = RSC_ROLE_UNKNOWN;
868  break;
869  default:
870  location->role_filter = r;
871  break;
872  }
873  }
874  }
875 
876  return TRUE;
877 }
878 
879 static gboolean
880 unpack_location_tags(xmlNode * xml_obj, xmlNode ** expanded_xml, pe_working_set_t * data_set)
881 {
882  const char *id = NULL;
883  const char *id_lh = NULL;
884  const char *state_lh = NULL;
885 
886  pe_resource_t *rsc_lh = NULL;
887 
888  pe_tag_t *tag_lh = NULL;
889 
890  xmlNode *new_xml = NULL;
891  xmlNode *rsc_set_lh = NULL;
892 
893  *expanded_xml = NULL;
894 
895  CRM_CHECK(xml_obj != NULL, return FALSE);
896 
897  id = ID(xml_obj);
898  if (id == NULL) {
899  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
900  crm_element_name(xml_obj));
901  return FALSE;
902  }
903 
904  /* Attempt to expand any template/tag references in possible resource sets. */
905  expand_tags_in_sets(xml_obj, &new_xml, data_set);
906  if (new_xml) {
907  /* There are resource sets referencing templates. Return with the expanded XML. */
908  crm_log_xml_trace(new_xml, "Expanded rsc_location...");
909  *expanded_xml = new_xml;
910  return TRUE;
911  }
912 
913  id_lh = crm_element_value(xml_obj, XML_LOC_ATTR_SOURCE);
914  if (id_lh == NULL) {
915  return TRUE;
916  }
917 
918  if (valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh) == FALSE) {
919  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
920  "valid resource or tag", id, id_lh);
921  return FALSE;
922 
923  } else if (rsc_lh) {
924  /* No template is referenced. */
925  return TRUE;
926  }
927 
928  state_lh = crm_element_value(xml_obj, XML_RULE_ATTR_ROLE);
929 
930  new_xml = copy_xml(xml_obj);
931 
932  /* Convert the template/tag reference in "rsc" into a resource_set under the rsc_location constraint. */
933  if (tag_to_set(new_xml, &rsc_set_lh, XML_LOC_ATTR_SOURCE, FALSE, data_set) == FALSE) {
934  free_xml(new_xml);
935  return FALSE;
936  }
937 
938  if (rsc_set_lh) {
939  if (state_lh) {
940  /* A "rsc-role" is specified.
941  Move it into the converted resource_set as a "role"" attribute. */
942  crm_xml_add(rsc_set_lh, "role", state_lh);
944  }
945  crm_log_xml_trace(new_xml, "Expanded rsc_location...");
946  *expanded_xml = new_xml;
947 
948  } else {
949  /* No sets */
950  free_xml(new_xml);
951  }
952 
953  return TRUE;
954 }
955 
956 static gboolean
957 unpack_location_set(xmlNode * location, xmlNode * set, pe_working_set_t * data_set)
958 {
959  xmlNode *xml_rsc = NULL;
960  pe_resource_t *resource = NULL;
961  const char *set_id;
962  const char *role;
963  const char *local_score;
964 
965  CRM_CHECK(set != NULL, return FALSE);
966 
967  set_id = ID(set);
968  if (set_id == NULL) {
969  pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET " without "
970  XML_ATTR_ID " in constraint '%s'",
971  crm_str(ID(location)));
972  return FALSE;
973  }
974 
975  role = crm_element_value(set, "role");
976  local_score = crm_element_value(set, XML_RULE_ATTR_SCORE);
977 
978  for (xml_rsc = __xml_first_child_element(set); xml_rsc != NULL;
979  xml_rsc = __xml_next_element(xml_rsc)) {
980 
981  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
982  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
983  unpack_rsc_location(location, resource, role, local_score, data_set, NULL);
984  }
985  }
986 
987  return TRUE;
988 }
989 
990 gboolean
991 unpack_location(xmlNode * xml_obj, pe_working_set_t * data_set)
992 {
993  xmlNode *set = NULL;
994  gboolean any_sets = FALSE;
995 
996  xmlNode *orig_xml = NULL;
997  xmlNode *expanded_xml = NULL;
998 
999  if (unpack_location_tags(xml_obj, &expanded_xml, data_set) == FALSE) {
1000  return FALSE;
1001  }
1002 
1003  if (expanded_xml) {
1004  orig_xml = xml_obj;
1005  xml_obj = expanded_xml;
1006  }
1007 
1008  for (set = __xml_first_child_element(xml_obj); set != NULL;
1009  set = __xml_next_element(set)) {
1010 
1011  if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
1012  any_sets = TRUE;
1013  set = expand_idref(set, data_set->input);
1014  if (unpack_location_set(xml_obj, set, data_set) == FALSE) {
1015  if (expanded_xml) {
1016  free_xml(expanded_xml);
1017  }
1018  return FALSE;
1019  }
1020  }
1021  }
1022 
1023  if (expanded_xml) {
1024  free_xml(expanded_xml);
1025  xml_obj = orig_xml;
1026  }
1027 
1028  if (any_sets == FALSE) {
1029  return unpack_simple_location(xml_obj, data_set);
1030  }
1031 
1032  return TRUE;
1033 }
1034 
1035 static int
1036 get_node_score(const char *rule, const char *score, gboolean raw, pe_node_t * node, pe_resource_t *rsc)
1037 {
1038  int score_f = 0;
1039 
1040  if (score == NULL) {
1041  pe_err("Rule %s: no score specified. Assuming 0.", rule);
1042 
1043  } else if (raw) {
1044  score_f = char2score(score);
1045 
1046  } else {
1047  const char *attr_score = pe_node_attribute_calculated(node, score, rsc);
1048 
1049  if (attr_score == NULL) {
1050  crm_debug("Rule %s: node %s did not have a value for %s",
1051  rule, node->details->uname, score);
1052  score_f = -INFINITY;
1053 
1054  } else {
1055  crm_debug("Rule %s: node %s had value %s for %s",
1056  rule, node->details->uname, attr_score, score);
1057  score_f = char2score(attr_score);
1058  }
1059  }
1060  return score_f;
1061 }
1062 
1063 static pe__location_t *
1064 generate_location_rule(pe_resource_t *rsc, xmlNode *rule_xml,
1065  const char *discovery, crm_time_t *next_change,
1066  pe_working_set_t *data_set, pe_match_data_t *match_data)
1067 {
1068  const char *rule_id = NULL;
1069  const char *score = NULL;
1070  const char *boolean = NULL;
1071  const char *role = NULL;
1072 
1073  GListPtr gIter = NULL;
1074  GListPtr match_L = NULL;
1075 
1076  gboolean do_and = TRUE;
1077  gboolean accept = TRUE;
1078  gboolean raw_score = TRUE;
1079  gboolean score_allocated = FALSE;
1080 
1081  pe__location_t *location_rule = NULL;
1082 
1083  rule_xml = expand_idref(rule_xml, data_set->input);
1084  rule_id = crm_element_value(rule_xml, XML_ATTR_ID);
1085  boolean = crm_element_value(rule_xml, XML_RULE_ATTR_BOOLEAN_OP);
1086  role = crm_element_value(rule_xml, XML_RULE_ATTR_ROLE);
1087 
1088  crm_trace("Processing rule: %s", rule_id);
1089 
1090  if (role != NULL && text2role(role) == RSC_ROLE_UNKNOWN) {
1091  pe_err("Bad role specified for %s: %s", rule_id, role);
1092  return NULL;
1093  }
1094 
1095  score = crm_element_value(rule_xml, XML_RULE_ATTR_SCORE);
1096  if (score == NULL) {
1098  if (score != NULL) {
1099  raw_score = FALSE;
1100  }
1101  }
1102  if (safe_str_eq(boolean, "or")) {
1103  do_and = FALSE;
1104  }
1105 
1106  location_rule = rsc2node_new(rule_id, rsc, 0, discovery, NULL, data_set);
1107 
1108  if (location_rule == NULL) {
1109  return NULL;
1110  }
1111 
1112  if (match_data && match_data->re && match_data->re->nregs > 0 && match_data->re->pmatch[0].rm_so != -1) {
1113  if (raw_score == FALSE) {
1114  char *result = pe_expand_re_matches(score, match_data->re);
1115 
1116  if (result) {
1117  score = (const char *) result;
1118  score_allocated = TRUE;
1119  }
1120  }
1121  }
1122 
1123  if (role != NULL) {
1124  crm_trace("Setting role filter: %s", role);
1125  location_rule->role_filter = text2role(role);
1126  if (location_rule->role_filter == RSC_ROLE_SLAVE) {
1127  /* Any promotable clone cannot be promoted without being a slave first
1128  * Ergo, any constraint for the slave role applies to every role
1129  */
1130  location_rule->role_filter = RSC_ROLE_UNKNOWN;
1131  }
1132  }
1133  if (do_and) {
1134  GListPtr gIter = NULL;
1135 
1136  match_L = pcmk__copy_node_list(data_set->nodes, true);
1137  for (gIter = match_L; gIter != NULL; gIter = gIter->next) {
1138  pe_node_t *node = (pe_node_t *) gIter->data;
1139 
1140  node->weight = get_node_score(rule_id, score, raw_score, node, rsc);
1141  }
1142  }
1143 
1144  for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1145  int score_f = 0;
1146  pe_node_t *node = (pe_node_t *) gIter->data;
1147 
1148  accept = pe_test_rule(rule_xml, node->details->attrs, RSC_ROLE_UNKNOWN,
1149  data_set->now, next_change, match_data);
1150 
1151  crm_trace("Rule %s %s on %s", ID(rule_xml), accept ? "passed" : "failed",
1152  node->details->uname);
1153 
1154  score_f = get_node_score(rule_id, score, raw_score, node, rsc);
1155 /* if(accept && score_f == -INFINITY) { */
1156 /* accept = FALSE; */
1157 /* } */
1158 
1159  if (accept) {
1160  pe_node_t *local = pe_find_node_id(match_L, node->details->id);
1161 
1162  if (local == NULL && do_and) {
1163  continue;
1164 
1165  } else if (local == NULL) {
1166  local = pe__copy_node(node);
1167  match_L = g_list_append(match_L, local);
1168  }
1169 
1170  if (do_and == FALSE) {
1171  local->weight = pe__add_scores(local->weight, score_f);
1172  }
1173  crm_trace("node %s now has weight %d", node->details->uname, local->weight);
1174 
1175  } else if (do_and && !accept) {
1176  /* remove it */
1177  pe_node_t *delete = pe_find_node_id(match_L, node->details->id);
1178 
1179  if (delete != NULL) {
1180  match_L = g_list_remove(match_L, delete);
1181  crm_trace("node %s did not match", node->details->uname);
1182  }
1183  free(delete);
1184  }
1185  }
1186 
1187  if (score_allocated == TRUE) {
1188  free((char *)score);
1189  }
1190 
1191  location_rule->node_list_rh = match_L;
1192  if (location_rule->node_list_rh == NULL) {
1193  crm_trace("No matching nodes for rule %s", rule_id);
1194  return NULL;
1195  }
1196 
1197  crm_trace("%s: %d nodes matched", rule_id, g_list_length(location_rule->node_list_rh));
1198  return location_rule;
1199 }
1200 
1201 static gint
1202 sort_cons_priority_lh(gconstpointer a, gconstpointer b)
1203 {
1204  const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t *)a;
1205  const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t *)b;
1206 
1207  if (a == NULL) {
1208  return 1;
1209  }
1210  if (b == NULL) {
1211  return -1;
1212  }
1213 
1214  CRM_ASSERT(rsc_constraint1->rsc_lh != NULL);
1215  CRM_ASSERT(rsc_constraint1->rsc_rh != NULL);
1216 
1217  if (rsc_constraint1->rsc_lh->priority > rsc_constraint2->rsc_lh->priority) {
1218  return -1;
1219  }
1220 
1221  if (rsc_constraint1->rsc_lh->priority < rsc_constraint2->rsc_lh->priority) {
1222  return 1;
1223  }
1224 
1225  /* Process clones before primitives and groups */
1226  if (rsc_constraint1->rsc_lh->variant > rsc_constraint2->rsc_lh->variant) {
1227  return -1;
1228  } else if (rsc_constraint1->rsc_lh->variant < rsc_constraint2->rsc_lh->variant) {
1229  return 1;
1230  }
1231 
1232  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
1233  * clones (probably unnecessary, but avoids having to update regression
1234  * tests)
1235  */
1236  if (rsc_constraint1->rsc_lh->variant == pe_clone) {
1237  if (is_set(rsc_constraint1->rsc_lh->flags, pe_rsc_promotable)
1238  && is_not_set(rsc_constraint2->rsc_lh->flags, pe_rsc_promotable)) {
1239  return -1;
1240  } else if (is_not_set(rsc_constraint1->rsc_lh->flags, pe_rsc_promotable)
1241  && is_set(rsc_constraint2->rsc_lh->flags, pe_rsc_promotable)) {
1242  return 1;
1243  }
1244  }
1245 
1246  return strcmp(rsc_constraint1->rsc_lh->id, rsc_constraint2->rsc_lh->id);
1247 }
1248 
1249 static gint
1250 sort_cons_priority_rh(gconstpointer a, gconstpointer b)
1251 {
1252  const rsc_colocation_t *rsc_constraint1 = (const rsc_colocation_t *)a;
1253  const rsc_colocation_t *rsc_constraint2 = (const rsc_colocation_t *)b;
1254 
1255  if (a == NULL) {
1256  return 1;
1257  }
1258  if (b == NULL) {
1259  return -1;
1260  }
1261 
1262  CRM_ASSERT(rsc_constraint1->rsc_lh != NULL);
1263  CRM_ASSERT(rsc_constraint1->rsc_rh != NULL);
1264 
1265  if (rsc_constraint1->rsc_rh->priority > rsc_constraint2->rsc_rh->priority) {
1266  return -1;
1267  }
1268 
1269  if (rsc_constraint1->rsc_rh->priority < rsc_constraint2->rsc_rh->priority) {
1270  return 1;
1271  }
1272 
1273  /* Process clones before primitives and groups */
1274  if (rsc_constraint1->rsc_rh->variant > rsc_constraint2->rsc_rh->variant) {
1275  return -1;
1276  } else if (rsc_constraint1->rsc_rh->variant < rsc_constraint2->rsc_rh->variant) {
1277  return 1;
1278  }
1279 
1280  /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
1281  * clones (probably unnecessary, but avoids having to update regression
1282  * tests)
1283  */
1284  if (rsc_constraint1->rsc_rh->variant == pe_clone) {
1285  if (is_set(rsc_constraint1->rsc_rh->flags, pe_rsc_promotable)
1286  && is_not_set(rsc_constraint2->rsc_rh->flags, pe_rsc_promotable)) {
1287  return -1;
1288  } else if (is_not_set(rsc_constraint1->rsc_rh->flags, pe_rsc_promotable)
1289  && is_set(rsc_constraint2->rsc_rh->flags, pe_rsc_promotable)) {
1290  return 1;
1291  }
1292  }
1293 
1294  return strcmp(rsc_constraint1->rsc_rh->id, rsc_constraint2->rsc_rh->id);
1295 }
1296 
1297 static void
1298 anti_colocation_order(pe_resource_t * first_rsc, int first_role,
1299  pe_resource_t * then_rsc, int then_role,
1300  pe_working_set_t * data_set)
1301 {
1302  const char *first_tasks[] = { NULL, NULL };
1303  const char *then_tasks[] = { NULL, NULL };
1304  int first_lpc = 0;
1305  int then_lpc = 0;
1306 
1307  /* Actions to make first_rsc lose first_role */
1308  if (first_role == RSC_ROLE_MASTER) {
1309  first_tasks[0] = CRMD_ACTION_DEMOTE;
1310 
1311  } else {
1312  first_tasks[0] = CRMD_ACTION_STOP;
1313 
1314  if (first_role == RSC_ROLE_SLAVE) {
1315  first_tasks[1] = CRMD_ACTION_PROMOTE;
1316  }
1317  }
1318 
1319  /* Actions to make then_rsc gain then_role */
1320  if (then_role == RSC_ROLE_MASTER) {
1321  then_tasks[0] = CRMD_ACTION_PROMOTE;
1322 
1323  } else {
1324  then_tasks[0] = CRMD_ACTION_START;
1325 
1326  if (then_role == RSC_ROLE_SLAVE) {
1327  then_tasks[1] = CRMD_ACTION_DEMOTE;
1328  }
1329  }
1330 
1331  for (first_lpc = 0; first_lpc <= 1 && first_tasks[first_lpc] != NULL; first_lpc++) {
1332  for (then_lpc = 0; then_lpc <= 1 && then_tasks[then_lpc] != NULL; then_lpc++) {
1333  new_rsc_order(first_rsc, first_tasks[first_lpc], then_rsc, then_tasks[then_lpc],
1334  pe_order_anti_colocation, data_set);
1335  }
1336  }
1337 }
1338 
1339 gboolean
1340 rsc_colocation_new(const char *id, const char *node_attr, int score,
1341  pe_resource_t * rsc_lh, pe_resource_t * rsc_rh,
1342  const char *state_lh, const char *state_rh, pe_working_set_t * data_set)
1343 {
1344  rsc_colocation_t *new_con = NULL;
1345 
1346  if ((rsc_lh == NULL) || (rsc_rh == NULL)) {
1347  pcmk__config_err("Ignoring colocation '%s' because resource "
1348  "does not exist", id);
1349  return FALSE;
1350  }
1351 
1352  new_con = calloc(1, sizeof(rsc_colocation_t));
1353  if (new_con == NULL) {
1354  return FALSE;
1355  }
1356 
1357  if (state_lh == NULL || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) {
1358  state_lh = RSC_ROLE_UNKNOWN_S;
1359  }
1360 
1361  if (state_rh == NULL || safe_str_eq(state_rh, RSC_ROLE_STARTED_S)) {
1362  state_rh = RSC_ROLE_UNKNOWN_S;
1363  }
1364 
1365  new_con->id = id;
1366  new_con->rsc_lh = rsc_lh;
1367  new_con->rsc_rh = rsc_rh;
1368  new_con->score = score;
1369  new_con->role_lh = text2role(state_lh);
1370  new_con->role_rh = text2role(state_rh);
1371  new_con->node_attribute = node_attr;
1372 
1373  if (node_attr == NULL) {
1374  node_attr = CRM_ATTR_UNAME;
1375  }
1376 
1377  pe_rsc_trace(rsc_lh, "%s ==> %s (%s %d)", rsc_lh->id, rsc_rh->id, node_attr, score);
1378 
1379  rsc_lh->rsc_cons = g_list_insert_sorted(rsc_lh->rsc_cons, new_con, sort_cons_priority_rh);
1380 
1381  rsc_rh->rsc_cons_lhs =
1382  g_list_insert_sorted(rsc_rh->rsc_cons_lhs, new_con, sort_cons_priority_lh);
1383 
1384  data_set->colocation_constraints = g_list_append(data_set->colocation_constraints, new_con);
1385 
1386  if (score <= -INFINITY) {
1387  anti_colocation_order(rsc_lh, new_con->role_lh, rsc_rh, new_con->role_rh, data_set);
1388  anti_colocation_order(rsc_rh, new_con->role_rh, rsc_lh, new_con->role_lh, data_set);
1389  }
1390 
1391  return TRUE;
1392 }
1393 
1394 /* LHS before RHS */
1395 int
1396 new_rsc_order(pe_resource_t * lh_rsc, const char *lh_task,
1397  pe_resource_t * rh_rsc, const char *rh_task,
1398  enum pe_ordering type, pe_working_set_t * data_set)
1399 {
1400  char *lh_key = NULL;
1401  char *rh_key = NULL;
1402 
1403  CRM_CHECK(lh_rsc != NULL, return -1);
1404  CRM_CHECK(lh_task != NULL, return -1);
1405  CRM_CHECK(rh_rsc != NULL, return -1);
1406  CRM_CHECK(rh_task != NULL, return -1);
1407 
1408 #if 0
1409  /* We do not need to test if these reference stonith resources
1410  * because the fencer has access to them even when they're not "running"
1411  */
1412  if (validate_order_resources(lh_rsc, lh_task, rh_rsc, rh_task)) {
1413  return -1;
1414  }
1415 #endif
1416 
1417  lh_key = pcmk__op_key(lh_rsc->id, lh_task, 0);
1418  rh_key = pcmk__op_key(rh_rsc->id, rh_task, 0);
1419 
1420  return custom_action_order(lh_rsc, lh_key, NULL, rh_rsc, rh_key, NULL, type, data_set);
1421 }
1422 
1423 static char *
1424 task_from_action_or_key(pe_action_t *action, const char *key)
1425 {
1426  char *res = NULL;
1427 
1428  if (action) {
1429  res = strdup(action->task);
1430  } else if (key) {
1431  parse_op_key(key, NULL, &res, NULL);
1432  }
1433  return res;
1434 }
1435 
1436 /* when order constraints are made between two resources start and stop actions
1437  * those constraints have to be mirrored against the corresponding
1438  * migration actions to ensure start/stop ordering is preserved during
1439  * a migration */
1440 static void
1441 handle_migration_ordering(pe__ordering_t *order, pe_working_set_t *data_set)
1442 {
1443  char *lh_task = NULL;
1444  char *rh_task = NULL;
1445  gboolean rh_migratable;
1446  gboolean lh_migratable;
1447 
1448  if (order->lh_rsc == NULL || order->rh_rsc == NULL) {
1449  return;
1450  } else if (order->lh_rsc == order->rh_rsc) {
1451  return;
1452  /* don't mess with those constraints built between parent
1453  * resources and the children */
1454  } else if (is_parent(order->lh_rsc, order->rh_rsc)) {
1455  return;
1456  } else if (is_parent(order->rh_rsc, order->lh_rsc)) {
1457  return;
1458  }
1459 
1460  lh_migratable = is_set(order->lh_rsc->flags, pe_rsc_allow_migrate);
1461  rh_migratable = is_set(order->rh_rsc->flags, pe_rsc_allow_migrate);
1462 
1463  /* one of them has to be migratable for
1464  * the migrate ordering logic to be applied */
1465  if (lh_migratable == FALSE && rh_migratable == FALSE) {
1466  return;
1467  }
1468 
1469  /* at this point we have two resources which allow migrations that have an
1470  * order dependency set between them. If those order dependencies involve
1471  * start/stop actions, we need to mirror the corresponding migrate actions
1472  * so order will be preserved. */
1473  lh_task = task_from_action_or_key(order->lh_action, order->lh_action_task);
1474  rh_task = task_from_action_or_key(order->rh_action, order->rh_action_task);
1475  if (lh_task == NULL || rh_task == NULL) {
1476  goto cleanup_order;
1477  }
1478 
1479  if (safe_str_eq(lh_task, RSC_START) && safe_str_eq(rh_task, RSC_START)) {
1480  int flags = pe_order_optional;
1481 
1482  if (lh_migratable && rh_migratable) {
1483  /* A start then B start
1484  * A migrate_from then B migrate_to */
1485  custom_action_order(order->lh_rsc,
1486  pcmk__op_key(order->lh_rsc->id, RSC_MIGRATED, 0),
1487  NULL, order->rh_rsc,
1488  pcmk__op_key(order->rh_rsc->id, RSC_MIGRATE, 0),
1489  NULL, flags, data_set);
1490  }
1491 
1492  if (rh_migratable) {
1493  if (lh_migratable) {
1495  }
1496 
1497  /* A start then B start
1498  * A start then B migrate_to... only if A start is not a part of a migration*/
1499  custom_action_order(order->lh_rsc,
1500  pcmk__op_key(order->lh_rsc->id, RSC_START, 0),
1501  NULL, order->rh_rsc,
1502  pcmk__op_key(order->rh_rsc->id, RSC_MIGRATE, 0),
1503  NULL, flags, data_set);
1504  }
1505 
1506  } else if (rh_migratable == TRUE && safe_str_eq(lh_task, RSC_STOP) && safe_str_eq(rh_task, RSC_STOP)) {
1507  int flags = pe_order_optional;
1508 
1509  if (lh_migratable) {
1511  }
1512 
1513  /* rh side is at the bottom of the stack during a stop. If we have a constraint
1514  * stop B then stop A, if B is migrating via stop/start, and A is migrating using migration actions,
1515  * we need to enforce that A's migrate_to action occurs after B's stop action. */
1516  custom_action_order(order->lh_rsc,
1517  pcmk__op_key(order->lh_rsc->id, RSC_STOP, 0), NULL,
1518  order->rh_rsc,
1519  pcmk__op_key(order->rh_rsc->id, RSC_MIGRATE, 0),
1520  NULL, flags, data_set);
1521 
1522  /* We need to build the stop constraint against migrate_from as well
1523  * to account for partial migrations. */
1524  if (order->rh_rsc->partial_migration_target) {
1525  custom_action_order(order->lh_rsc,
1526  pcmk__op_key(order->lh_rsc->id, RSC_STOP, 0),
1527  NULL, order->rh_rsc,
1528  pcmk__op_key(order->rh_rsc->id, RSC_MIGRATED, 0),
1529  NULL, flags, data_set);
1530  }
1531 
1532  } else if (safe_str_eq(lh_task, RSC_PROMOTE) && safe_str_eq(rh_task, RSC_START)) {
1533  int flags = pe_order_optional;
1534 
1535  if (rh_migratable) {
1536  /* A promote then B start
1537  * A promote then B migrate_to */
1538  custom_action_order(order->lh_rsc,
1539  pcmk__op_key(order->lh_rsc->id, RSC_PROMOTE, 0),
1540  NULL, order->rh_rsc,
1541  pcmk__op_key(order->rh_rsc->id, RSC_MIGRATE, 0),
1542  NULL, flags, data_set);
1543  }
1544 
1545  } else if (safe_str_eq(lh_task, RSC_DEMOTE) && safe_str_eq(rh_task, RSC_STOP)) {
1546  int flags = pe_order_optional;
1547 
1548  if (rh_migratable) {
1549  /* A demote then B stop
1550  * A demote then B migrate_to */
1551  custom_action_order(order->lh_rsc, pcmk__op_key(order->lh_rsc->id, RSC_DEMOTE, 0), NULL,
1552  order->rh_rsc, pcmk__op_key(order->rh_rsc->id, RSC_MIGRATE, 0), NULL,
1553  flags, data_set);
1554 
1555  /* We need to build the demote constraint against migrate_from as well
1556  * to account for partial migrations. */
1557  if (order->rh_rsc->partial_migration_target) {
1558  custom_action_order(order->lh_rsc,
1559  pcmk__op_key(order->lh_rsc->id, RSC_DEMOTE, 0),
1560  NULL, order->rh_rsc,
1561  pcmk__op_key(order->rh_rsc->id, RSC_MIGRATED, 0),
1562  NULL, flags, data_set);
1563  }
1564  }
1565  }
1566 
1567 cleanup_order:
1568  free(lh_task);
1569  free(rh_task);
1570 }
1571 
1572 /* LHS before RHS */
1573 int
1574 custom_action_order(pe_resource_t * lh_rsc, char *lh_action_task, pe_action_t * lh_action,
1575  pe_resource_t * rh_rsc, char *rh_action_task, pe_action_t * rh_action,
1576  enum pe_ordering type, pe_working_set_t * data_set)
1577 {
1578  pe__ordering_t *order = NULL;
1579 
1580  if (lh_rsc == NULL && lh_action) {
1581  lh_rsc = lh_action->rsc;
1582  }
1583  if (rh_rsc == NULL && rh_action) {
1584  rh_rsc = rh_action->rsc;
1585  }
1586 
1587  if ((lh_action == NULL && lh_rsc == NULL)
1588  || (rh_action == NULL && rh_rsc == NULL)) {
1589  crm_err("Invalid ordering (bug?)");
1590  free(lh_action_task);
1591  free(rh_action_task);
1592  return -1;
1593  }
1594 
1595  order = calloc(1, sizeof(pe__ordering_t));
1596 
1597  crm_trace("Creating[%d] %s %s %s - %s %s %s", data_set->order_id,
1598  lh_rsc?lh_rsc->id:"NA", lh_action_task, lh_action?lh_action->uuid:"NA",
1599  rh_rsc?rh_rsc->id:"NA", rh_action_task, rh_action?rh_action->uuid:"NA");
1600 
1601  /* CRM_ASSERT(data_set->order_id != 291); */
1602 
1603  order->id = data_set->order_id++;
1604  order->type = type;
1605  order->lh_rsc = lh_rsc;
1606  order->rh_rsc = rh_rsc;
1607  order->lh_action = lh_action;
1608  order->rh_action = rh_action;
1609  order->lh_action_task = lh_action_task;
1610  order->rh_action_task = rh_action_task;
1611 
1612  if (order->lh_action_task == NULL && lh_action) {
1613  order->lh_action_task = strdup(lh_action->uuid);
1614  }
1615 
1616  if (order->rh_action_task == NULL && rh_action) {
1617  order->rh_action_task = strdup(rh_action->uuid);
1618  }
1619 
1620  if (order->lh_rsc == NULL && lh_action) {
1621  order->lh_rsc = lh_action->rsc;
1622  }
1623 
1624  if (order->rh_rsc == NULL && rh_action) {
1625  order->rh_rsc = rh_action->rsc;
1626  }
1627 
1628  data_set->ordering_constraints = g_list_prepend(data_set->ordering_constraints, order);
1629  handle_migration_ordering(order, data_set);
1630 
1631  return order->id;
1632 }
1633 
1634 enum pe_ordering
1636 {
1637  enum pe_ordering flags = pe_order_optional;
1638 
1639  if (kind == pe_order_kind_mandatory) {
1640  flags |= pe_order_asymmetrical;
1641  } else if (kind == pe_order_kind_serialize) {
1642  flags |= pe_order_serialize_only;
1643  }
1644  return flags;
1645 }
1646 
1647 enum pe_ordering
1648 get_flags(const char *id, enum pe_order_kind kind,
1649  const char *action_first, const char *action_then, gboolean invert)
1650 {
1651  enum pe_ordering flags = pe_order_optional;
1652 
1653  if (invert && kind == pe_order_kind_mandatory) {
1654  crm_trace("Upgrade %s: implies left", id);
1655  flags |= pe_order_implies_first;
1656 
1657  } else if (kind == pe_order_kind_mandatory) {
1658  crm_trace("Upgrade %s: implies right", id);
1659  flags |= pe_order_implies_then;
1660  if (safe_str_eq(action_first, RSC_START)
1661  || safe_str_eq(action_first, RSC_PROMOTE)) {
1662  crm_trace("Upgrade %s: runnable", id);
1663  flags |= pe_order_runnable_left;
1664  }
1665 
1666  } else if (kind == pe_order_kind_serialize) {
1667  flags |= pe_order_serialize_only;
1668  }
1669 
1670  return flags;
1671 }
1672 
1673 static gboolean
1674 unpack_order_set(xmlNode * set, enum pe_order_kind parent_kind, pe_resource_t ** rsc,
1675  pe_action_t ** begin, pe_action_t ** end, pe_action_t ** inv_begin,
1676  pe_action_t ** inv_end, const char *parent_symmetrical_s,
1677  pe_working_set_t * data_set)
1678 {
1679  xmlNode *xml_rsc = NULL;
1680  GListPtr set_iter = NULL;
1681  GListPtr resources = NULL;
1682 
1683  pe_resource_t *last = NULL;
1684  pe_resource_t *resource = NULL;
1685 
1686  int local_kind = parent_kind;
1687  gboolean sequential = FALSE;
1688  enum pe_ordering flags = pe_order_optional;
1689  gboolean symmetrical = TRUE;
1690 
1691  char *key = NULL;
1692  const char *id = ID(set);
1693  const char *action = crm_element_value(set, "action");
1694  const char *sequential_s = crm_element_value(set, "sequential");
1695  const char *kind_s = crm_element_value(set, XML_ORDER_ATTR_KIND);
1696 
1697  /*
1698  char *pseudo_id = NULL;
1699  char *end_id = NULL;
1700  char *begin_id = NULL;
1701  */
1702 
1703  if (action == NULL) {
1704  action = RSC_START;
1705  }
1706 
1707  if (kind_s) {
1708  local_kind = get_ordering_type(set);
1709  }
1710  if (sequential_s == NULL) {
1711  sequential_s = "1";
1712  }
1713 
1714  sequential = crm_is_true(sequential_s);
1715 
1716  symmetrical = order_is_symmetrical(set, parent_kind, parent_symmetrical_s);
1717  if (symmetrical) {
1718  flags = get_flags(id, local_kind, action, action, FALSE);
1719  } else {
1720  flags = get_asymmetrical_flags(local_kind);
1721  }
1722 
1723  for (xml_rsc = __xml_first_child_element(set); xml_rsc != NULL;
1724  xml_rsc = __xml_next_element(xml_rsc)) {
1725 
1726  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1727  EXPAND_CONSTRAINT_IDREF(id, resource, ID(xml_rsc));
1728  resources = g_list_append(resources, resource);
1729  }
1730  }
1731 
1732  if (pcmk__list_of_1(resources)) {
1733  crm_trace("Single set: %s", id);
1734  *rsc = resource;
1735  *end = NULL;
1736  *begin = NULL;
1737  *inv_end = NULL;
1738  *inv_begin = NULL;
1739  goto done;
1740  }
1741 
1742  /*
1743  pseudo_id = crm_strdup_printf("%s-%s", id, action);
1744  end_id = crm_strdup_printf("%s-%s", pseudo_id, "end");
1745  begin_id = crm_strdup_printf("%s-%s", pseudo_id, "begin");
1746  */
1747 
1748  *rsc = NULL;
1749  /*
1750  *end = get_pseudo_op(end_id, data_set);
1751  *begin = get_pseudo_op(begin_id, data_set);
1752 
1753  free(pseudo_id);
1754  free(begin_id);
1755  free(end_id);
1756  */
1757 
1758  set_iter = resources;
1759  while (set_iter != NULL) {
1760  resource = (pe_resource_t *) set_iter->data;
1761  set_iter = set_iter->next;
1762 
1763  key = pcmk__op_key(resource->id, action, 0);
1764 
1765  /*
1766  custom_action_order(NULL, NULL, *begin, resource, strdup(key), NULL,
1767  flags|pe_order_implies_first_printed, data_set);
1768 
1769  custom_action_order(resource, strdup(key), NULL, NULL, NULL, *end,
1770  flags|pe_order_implies_then_printed, data_set);
1771  */
1772 
1773  if (local_kind == pe_order_kind_serialize) {
1774  /* Serialize before everything that comes after */
1775 
1776  GListPtr gIter = NULL;
1777 
1778  for (gIter = set_iter; gIter != NULL; gIter = gIter->next) {
1779  pe_resource_t *then_rsc = (pe_resource_t *) gIter->data;
1780  char *then_key = pcmk__op_key(then_rsc->id, action, 0);
1781 
1782  custom_action_order(resource, strdup(key), NULL, then_rsc, then_key, NULL,
1783  flags, data_set);
1784  }
1785 
1786  } else if (sequential) {
1787  if (last != NULL) {
1788  new_rsc_order(last, action, resource, action, flags, data_set);
1789  }
1790  last = resource;
1791  }
1792  free(key);
1793  }
1794 
1795  if (symmetrical == FALSE) {
1796  goto done;
1797  }
1798 
1799  last = NULL;
1800  action = invert_action(action);
1801 
1802  /*
1803  pseudo_id = crm_strdup_printf("%s-%s", id, action);
1804  end_id = crm_strdup_printf("%s-%s", pseudo_id, "end");
1805  begin_id = crm_strdup_printf("%s-%s", pseudo_id, "begin");
1806 
1807  *inv_end = get_pseudo_op(end_id, data_set);
1808  *inv_begin = get_pseudo_op(begin_id, data_set);
1809 
1810  free(pseudo_id);
1811  free(begin_id);
1812  free(end_id);
1813  */
1814 
1815  flags = get_flags(id, local_kind, action, action, TRUE);
1816 
1817  set_iter = resources;
1818  while (set_iter != NULL) {
1819  resource = (pe_resource_t *) set_iter->data;
1820  set_iter = set_iter->next;
1821 
1822  /*
1823  key = pcmk__op_key(resource->id, action, 0);
1824 
1825  custom_action_order(NULL, NULL, *inv_begin, resource, strdup(key), NULL,
1826  flags|pe_order_implies_first_printed, data_set);
1827 
1828  custom_action_order(resource, key, NULL, NULL, NULL, *inv_end,
1829  flags|pe_order_implies_then_printed, data_set);
1830  */
1831 
1832  if (sequential) {
1833  if (last != NULL) {
1834  new_rsc_order(resource, action, last, action, flags, data_set);
1835  }
1836  last = resource;
1837  }
1838  }
1839 
1840  done:
1841  g_list_free(resources);
1842  return TRUE;
1843 }
1844 
1845 static gboolean
1846 order_rsc_sets(const char *id, xmlNode * set1, xmlNode * set2, enum pe_order_kind kind,
1847  pe_working_set_t * data_set, gboolean invert, gboolean symmetrical)
1848 {
1849 
1850  xmlNode *xml_rsc = NULL;
1851  xmlNode *xml_rsc_2 = NULL;
1852 
1853  pe_resource_t *rsc_1 = NULL;
1854  pe_resource_t *rsc_2 = NULL;
1855 
1856  const char *action_1 = crm_element_value(set1, "action");
1857  const char *action_2 = crm_element_value(set2, "action");
1858 
1859  const char *sequential_1 = crm_element_value(set1, "sequential");
1860  const char *sequential_2 = crm_element_value(set2, "sequential");
1861 
1862  const char *require_all_s = crm_element_value(set1, "require-all");
1863  gboolean require_all = require_all_s ? crm_is_true(require_all_s) : TRUE;
1864 
1865  enum pe_ordering flags = pe_order_none;
1866 
1867  if (action_1 == NULL) {
1868  action_1 = RSC_START;
1869  };
1870 
1871  if (action_2 == NULL) {
1872  action_2 = RSC_START;
1873  };
1874 
1875  if (invert) {
1876  action_1 = invert_action(action_1);
1877  action_2 = invert_action(action_2);
1878  }
1879 
1880  if(safe_str_eq(RSC_STOP, action_1) || safe_str_eq(RSC_DEMOTE, action_1)) {
1881  /* Assuming: A -> ( B || C) -> D
1882  * The one-or-more logic only applies during the start/promote phase
1883  * During shutdown neither B nor can shutdown until D is down, so simply turn require_all back on.
1884  */
1885  require_all = TRUE;
1886  }
1887 
1888  if (symmetrical == FALSE) {
1889  flags = get_asymmetrical_flags(kind);
1890  } else {
1891  flags = get_flags(id, kind, action_2, action_1, invert);
1892  }
1893 
1894  /* If we have an un-ordered set1, whether it is sequential or not is irrelevant in regards to set2. */
1895  if (!require_all) {
1896  char *task = crm_strdup_printf(CRM_OP_RELAXED_SET ":%s", ID(set1));
1897  pe_action_t *unordered_action = get_pseudo_op(task, data_set);
1898 
1899  free(task);
1900  update_action_flags(unordered_action, pe_action_requires_any, __FUNCTION__, __LINE__);
1901 
1902  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
1903  xml_rsc = __xml_next_element(xml_rsc)) {
1904 
1905  if (!crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1906  continue;
1907  }
1908 
1909  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
1910 
1911  /* Add an ordering constraint between every element in set1 and the pseudo action.
1912  * If any action in set1 is runnable the pseudo action will be runnable. */
1913  custom_action_order(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
1914  NULL, NULL, NULL, unordered_action,
1916  data_set);
1917  }
1918  for (xml_rsc_2 = __xml_first_child_element(set2); xml_rsc_2 != NULL;
1919  xml_rsc_2 = __xml_next_element(xml_rsc_2)) {
1920 
1921  if (!crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) {
1922  continue;
1923  }
1924 
1925  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
1926 
1927  /* Add an ordering constraint between the pseudo action and every element in set2.
1928  * If the pseudo action is runnable, every action in set2 will be runnable */
1929  custom_action_order(NULL, NULL, unordered_action,
1930  rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
1931  NULL, flags|pe_order_runnable_left, data_set);
1932  }
1933 
1934  return TRUE;
1935  }
1936 
1937  if (crm_is_true(sequential_1)) {
1938  if (invert == FALSE) {
1939  /* get the last one */
1940  const char *rid = NULL;
1941 
1942  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
1943  xml_rsc = __xml_next_element(xml_rsc)) {
1944 
1945  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1946  rid = ID(xml_rsc);
1947  }
1948  }
1949  EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
1950 
1951  } else {
1952  /* get the first one */
1953  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
1954  xml_rsc = __xml_next_element(xml_rsc)) {
1955 
1956  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1957  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
1958  break;
1959  }
1960  }
1961  }
1962  }
1963 
1964  if (crm_is_true(sequential_2)) {
1965  if (invert == FALSE) {
1966  /* get the first one */
1967  for (xml_rsc = __xml_first_child_element(set2); xml_rsc != NULL;
1968  xml_rsc = __xml_next_element(xml_rsc)) {
1969 
1970  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1971  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
1972  break;
1973  }
1974  }
1975 
1976  } else {
1977  /* get the last one */
1978  const char *rid = NULL;
1979 
1980  for (xml_rsc = __xml_first_child_element(set2); xml_rsc != NULL;
1981  xml_rsc = __xml_next_element(xml_rsc)) {
1982 
1983  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1984  rid = ID(xml_rsc);
1985  }
1986  }
1987  EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
1988  }
1989  }
1990 
1991  if (rsc_1 != NULL && rsc_2 != NULL) {
1992  new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
1993 
1994  } else if (rsc_1 != NULL) {
1995  for (xml_rsc = __xml_first_child_element(set2); xml_rsc != NULL;
1996  xml_rsc = __xml_next_element(xml_rsc)) {
1997 
1998  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
1999  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
2000  new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
2001  }
2002  }
2003 
2004  } else if (rsc_2 != NULL) {
2005  xmlNode *xml_rsc = NULL;
2006 
2007  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
2008  xml_rsc = __xml_next_element(xml_rsc)) {
2009 
2010  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2011  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
2012  new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
2013  }
2014  }
2015 
2016  } else {
2017  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
2018  xml_rsc = __xml_next_element(xml_rsc)) {
2019 
2020  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2021  xmlNode *xml_rsc_2 = NULL;
2022 
2023  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
2024 
2025  for (xml_rsc_2 = __xml_first_child_element(set2);
2026  xml_rsc_2 != NULL;
2027  xml_rsc_2 = __xml_next_element(xml_rsc_2)) {
2028 
2029  if (crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) {
2030  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
2031  new_rsc_order(rsc_1, action_1, rsc_2, action_2, flags, data_set);
2032  }
2033  }
2034  }
2035  }
2036  }
2037 
2038  return TRUE;
2039 }
2040 
2041 static gboolean
2042 unpack_order_tags(xmlNode * xml_obj, xmlNode ** expanded_xml, pe_working_set_t * data_set)
2043 {
2044  const char *id = NULL;
2045  const char *id_first = NULL;
2046  const char *id_then = NULL;
2047  const char *action_first = NULL;
2048  const char *action_then = NULL;
2049 
2050  pe_resource_t *rsc_first = NULL;
2051  pe_resource_t *rsc_then = NULL;
2052  pe_tag_t *tag_first = NULL;
2053  pe_tag_t *tag_then = NULL;
2054 
2055  xmlNode *new_xml = NULL;
2056  xmlNode *rsc_set_first = NULL;
2057  xmlNode *rsc_set_then = NULL;
2058  gboolean any_sets = FALSE;
2059 
2060  *expanded_xml = NULL;
2061 
2062  CRM_CHECK(xml_obj != NULL, return FALSE);
2063 
2064  id = ID(xml_obj);
2065  if (id == NULL) {
2066  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
2067  crm_element_name(xml_obj));
2068  return FALSE;
2069  }
2070 
2071  /* Attempt to expand any template/tag references in possible resource sets. */
2072  expand_tags_in_sets(xml_obj, &new_xml, data_set);
2073  if (new_xml) {
2074  /* There are resource sets referencing templates/tags. Return with the expanded XML. */
2075  crm_log_xml_trace(new_xml, "Expanded rsc_order...");
2076  *expanded_xml = new_xml;
2077  return TRUE;
2078  }
2079 
2080  id_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST);
2081  id_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN);
2082  if (id_first == NULL || id_then == NULL) {
2083  return TRUE;
2084  }
2085 
2086  if (valid_resource_or_tag(data_set, id_first, &rsc_first, &tag_first) == FALSE) {
2087  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
2088  "valid resource or tag", id, id_first);
2089  return FALSE;
2090  }
2091 
2092  if (valid_resource_or_tag(data_set, id_then, &rsc_then, &tag_then) == FALSE) {
2093  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
2094  "valid resource or tag", id, id_then);
2095  return FALSE;
2096  }
2097 
2098  if (rsc_first && rsc_then) {
2099  /* Neither side references any template/tag. */
2100  return TRUE;
2101  }
2102 
2103  action_first = crm_element_value(xml_obj, XML_ORDER_ATTR_FIRST_ACTION);
2104  action_then = crm_element_value(xml_obj, XML_ORDER_ATTR_THEN_ACTION);
2105 
2106  new_xml = copy_xml(xml_obj);
2107 
2108  /* Convert the template/tag reference in "first" into a resource_set under the order constraint. */
2109  if (tag_to_set(new_xml, &rsc_set_first, XML_ORDER_ATTR_FIRST, TRUE, data_set) == FALSE) {
2110  free_xml(new_xml);
2111  return FALSE;
2112  }
2113 
2114  if (rsc_set_first) {
2115  if (action_first) {
2116  /* A "first-action" is specified.
2117  Move it into the converted resource_set as an "action" attribute. */
2118  crm_xml_add(rsc_set_first, "action", action_first);
2120  }
2121  any_sets = TRUE;
2122  }
2123 
2124  /* Convert the template/tag reference in "then" into a resource_set under the order constraint. */
2125  if (tag_to_set(new_xml, &rsc_set_then, XML_ORDER_ATTR_THEN, TRUE, data_set) == FALSE) {
2126  free_xml(new_xml);
2127  return FALSE;
2128  }
2129 
2130  if (rsc_set_then) {
2131  if (action_then) {
2132  /* A "then-action" is specified.
2133  Move it into the converted resource_set as an "action" attribute. */
2134  crm_xml_add(rsc_set_then, "action", action_then);
2136  }
2137  any_sets = TRUE;
2138  }
2139 
2140  if (any_sets) {
2141  crm_log_xml_trace(new_xml, "Expanded rsc_order...");
2142  *expanded_xml = new_xml;
2143  } else {
2144  free_xml(new_xml);
2145  }
2146 
2147  return TRUE;
2148 }
2149 
2150 gboolean
2151 unpack_rsc_order(xmlNode * xml_obj, pe_working_set_t * data_set)
2152 {
2153  gboolean any_sets = FALSE;
2154 
2155  pe_resource_t *rsc = NULL;
2156 
2157  /*
2158  pe_resource_t *last_rsc = NULL;
2159  */
2160 
2161  pe_action_t *set_end = NULL;
2162  pe_action_t *set_begin = NULL;
2163 
2164  pe_action_t *set_inv_end = NULL;
2165  pe_action_t *set_inv_begin = NULL;
2166 
2167  xmlNode *set = NULL;
2168  xmlNode *last = NULL;
2169 
2170  xmlNode *orig_xml = NULL;
2171  xmlNode *expanded_xml = NULL;
2172 
2173  /*
2174  pe_action_t *last_end = NULL;
2175  pe_action_t *last_begin = NULL;
2176  pe_action_t *last_inv_end = NULL;
2177  pe_action_t *last_inv_begin = NULL;
2178  */
2179 
2180  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
2181  const char *invert = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
2182  enum pe_order_kind kind = get_ordering_type(xml_obj);
2183 
2184  gboolean invert_bool = order_is_symmetrical(xml_obj, kind, NULL);
2185  gboolean rc = TRUE;
2186 
2187  rc = unpack_order_tags(xml_obj, &expanded_xml, data_set);
2188  if (expanded_xml) {
2189  orig_xml = xml_obj;
2190  xml_obj = expanded_xml;
2191 
2192  } else if (rc == FALSE) {
2193  return FALSE;
2194  }
2195 
2196  for (set = __xml_first_child_element(xml_obj); set != NULL;
2197  set = __xml_next_element(set)) {
2198 
2199  if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
2200  any_sets = TRUE;
2201  set = expand_idref(set, data_set->input);
2202  if (unpack_order_set(set, kind, &rsc, &set_begin, &set_end,
2203  &set_inv_begin, &set_inv_end, invert, data_set) == FALSE) {
2204  return FALSE;
2205 
2206  /* Expand orders in order_rsc_sets() instead of via pseudo actions. */
2207  /*
2208  } else if(last) {
2209  const char *set_action = crm_element_value(set, "action");
2210  const char *last_action = crm_element_value(last, "action");
2211  enum pe_ordering flags = get_flags(id, kind, last_action, set_action, FALSE);
2212 
2213  if(!set_action) { set_action = RSC_START; }
2214  if(!last_action) { last_action = RSC_START; }
2215 
2216  if(rsc == NULL && last_rsc == NULL) {
2217  order_actions(last_end, set_begin, flags);
2218  } else {
2219  custom_action_order(
2220  last_rsc, null_or_opkey(last_rsc, last_action), last_end,
2221  rsc, null_or_opkey(rsc, set_action), set_begin,
2222  flags, data_set);
2223  }
2224 
2225  if(crm_is_true(invert)) {
2226  set_action = invert_action(set_action);
2227  last_action = invert_action(last_action);
2228 
2229  flags = get_flags(id, kind, last_action, set_action, TRUE);
2230  if(rsc == NULL && last_rsc == NULL) {
2231  order_actions(last_inv_begin, set_inv_end, flags);
2232 
2233  } else {
2234  custom_action_order(
2235  last_rsc, null_or_opkey(last_rsc, last_action), last_inv_begin,
2236  rsc, null_or_opkey(rsc, set_action), set_inv_end,
2237  flags, data_set);
2238  }
2239  }
2240  */
2241 
2242  } else if ( /* never called -- Now call it for supporting clones in resource sets */
2243  last) {
2244  if (order_rsc_sets(id, last, set, kind, data_set, FALSE, invert_bool) == FALSE) {
2245  return FALSE;
2246  }
2247 
2248  if (invert_bool
2249  && order_rsc_sets(id, set, last, kind, data_set, TRUE, invert_bool) == FALSE) {
2250  return FALSE;
2251  }
2252 
2253  }
2254  last = set;
2255  /*
2256  last_rsc = rsc;
2257  last_end = set_end;
2258  last_begin = set_begin;
2259  last_inv_end = set_inv_end;
2260  last_inv_begin = set_inv_begin;
2261  */
2262  }
2263  }
2264 
2265  if (expanded_xml) {
2266  free_xml(expanded_xml);
2267  xml_obj = orig_xml;
2268  }
2269 
2270  if (any_sets == FALSE) {
2271  return unpack_simple_rsc_order(xml_obj, data_set);
2272  }
2273 
2274  return TRUE;
2275 }
2276 
2277 static gboolean
2278 unpack_colocation_set(xmlNode * set, int score, pe_working_set_t * data_set)
2279 {
2280  xmlNode *xml_rsc = NULL;
2281  pe_resource_t *with = NULL;
2282  pe_resource_t *resource = NULL;
2283  const char *set_id = ID(set);
2284  const char *role = crm_element_value(set, "role");
2285  const char *sequential = crm_element_value(set, "sequential");
2286  const char *ordering = crm_element_value(set, "ordering");
2287  int local_score = score;
2288 
2289  const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
2290 
2291  if (score_s) {
2292  local_score = char2score(score_s);
2293  }
2294 
2295  if(ordering == NULL) {
2296  ordering = "group";
2297  }
2298 
2299  if (sequential != NULL && crm_is_true(sequential) == FALSE) {
2300  return TRUE;
2301 
2302  } else if (local_score >= 0 && safe_str_eq(ordering, "group")) {
2303  for (xml_rsc = __xml_first_child_element(set); xml_rsc != NULL;
2304  xml_rsc = __xml_next_element(xml_rsc)) {
2305 
2306  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2307  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
2308  if (with != NULL) {
2309  pe_rsc_trace(resource, "Colocating %s with %s", resource->id, with->id);
2310  rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role,
2311  data_set);
2312  }
2313 
2314  with = resource;
2315  }
2316  }
2317  } else if (local_score >= 0) {
2318  pe_resource_t *last = NULL;
2319  for (xml_rsc = __xml_first_child_element(set); xml_rsc != NULL;
2320  xml_rsc = __xml_next_element(xml_rsc)) {
2321 
2322  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2323  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
2324  if (last != NULL) {
2325  pe_rsc_trace(resource, "Colocating %s with %s", last->id, resource->id);
2326  rsc_colocation_new(set_id, NULL, local_score, last, resource, role, role,
2327  data_set);
2328  }
2329 
2330  last = resource;
2331  }
2332  }
2333 
2334  } else {
2335  /* Anti-colocating with every prior resource is
2336  * the only way to ensure the intuitive result
2337  * (i.e. that no one in the set can run with anyone else in the set)
2338  */
2339 
2340  for (xml_rsc = __xml_first_child_element(set); xml_rsc != NULL;
2341  xml_rsc = __xml_next_element(xml_rsc)) {
2342 
2343  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2344  xmlNode *xml_rsc_with = NULL;
2345 
2346  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
2347 
2348  for (xml_rsc_with = __xml_first_child_element(set);
2349  xml_rsc_with != NULL;
2350  xml_rsc_with = __xml_next_element(xml_rsc_with)) {
2351 
2352  if (crm_str_eq((const char *)xml_rsc_with->name, XML_TAG_RESOURCE_REF, TRUE)) {
2353  if (safe_str_eq(resource->id, ID(xml_rsc_with))) {
2354  break;
2355  }
2356  EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
2357  pe_rsc_trace(resource, "Anti-Colocating %s with %s", resource->id,
2358  with->id);
2359  rsc_colocation_new(set_id, NULL, local_score, resource, with, role, role,
2360  data_set);
2361  }
2362  }
2363  }
2364  }
2365  }
2366 
2367  return TRUE;
2368 }
2369 
2370 static gboolean
2371 colocate_rsc_sets(const char *id, xmlNode * set1, xmlNode * set2, int score,
2372  pe_working_set_t * data_set)
2373 {
2374  xmlNode *xml_rsc = NULL;
2375  pe_resource_t *rsc_1 = NULL;
2376  pe_resource_t *rsc_2 = NULL;
2377 
2378  const char *role_1 = crm_element_value(set1, "role");
2379  const char *role_2 = crm_element_value(set2, "role");
2380 
2381  const char *sequential_1 = crm_element_value(set1, "sequential");
2382  const char *sequential_2 = crm_element_value(set2, "sequential");
2383 
2384  if (sequential_1 == NULL || crm_is_true(sequential_1)) {
2385  /* get the first one */
2386  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
2387  xml_rsc = __xml_next_element(xml_rsc)) {
2388 
2389  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2390  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
2391  break;
2392  }
2393  }
2394  }
2395 
2396  if (sequential_2 == NULL || crm_is_true(sequential_2)) {
2397  /* get the last one */
2398  const char *rid = NULL;
2399 
2400  for (xml_rsc = __xml_first_child_element(set2); xml_rsc != NULL;
2401  xml_rsc = __xml_next_element(xml_rsc)) {
2402 
2403  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2404  rid = ID(xml_rsc);
2405  }
2406  }
2407  EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
2408  }
2409 
2410  if (rsc_1 != NULL && rsc_2 != NULL) {
2411  rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
2412 
2413  } else if (rsc_1 != NULL) {
2414  for (xml_rsc = __xml_first_child_element(set2); xml_rsc != NULL;
2415  xml_rsc = __xml_next_element(xml_rsc)) {
2416 
2417  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2418  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
2419  rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
2420  }
2421  }
2422 
2423  } else if (rsc_2 != NULL) {
2424  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
2425  xml_rsc = __xml_next_element(xml_rsc)) {
2426 
2427  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2428  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
2429  rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
2430  }
2431  }
2432 
2433  } else {
2434  for (xml_rsc = __xml_first_child_element(set1); xml_rsc != NULL;
2435  xml_rsc = __xml_next_element(xml_rsc)) {
2436 
2437  if (crm_str_eq((const char *)xml_rsc->name, XML_TAG_RESOURCE_REF, TRUE)) {
2438  xmlNode *xml_rsc_2 = NULL;
2439 
2440  EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
2441 
2442  for (xml_rsc_2 = __xml_first_child_element(set2);
2443  xml_rsc_2 != NULL;
2444  xml_rsc_2 = __xml_next_element(xml_rsc_2)) {
2445 
2446  if (crm_str_eq((const char *)xml_rsc_2->name, XML_TAG_RESOURCE_REF, TRUE)) {
2447  EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
2448  rsc_colocation_new(id, NULL, score, rsc_1, rsc_2, role_1, role_2, data_set);
2449  }
2450  }
2451  }
2452  }
2453  }
2454 
2455  return TRUE;
2456 }
2457 
2458 static gboolean
2459 unpack_simple_colocation(xmlNode * xml_obj, pe_working_set_t * data_set)
2460 {
2461  int score_i = 0;
2462 
2463  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
2464  const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
2465 
2466  const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
2467  const char *id_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
2468  const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
2469  const char *state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
2470  const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
2471  const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
2472 
2473  // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
2474  const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
2475  const char *instance_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_INSTANCE);
2476 
2477  pe_resource_t *rsc_lh = pe_find_constraint_resource(data_set->resources, id_lh);
2478  pe_resource_t *rsc_rh = pe_find_constraint_resource(data_set->resources, id_rh);
2479 
2480  if (rsc_lh == NULL) {
2481  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
2482  "does not exist", id, id_lh);
2483  return FALSE;
2484 
2485  } else if (rsc_rh == NULL) {
2486  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
2487  "does not exist", id, id_rh);
2488  return FALSE;
2489 
2490  } else if (instance_lh && pe_rsc_is_clone(rsc_lh) == FALSE) {
2491  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
2492  "is not a clone but instance '%s' was requested",
2493  id, id_lh, instance_lh);
2494  return FALSE;
2495 
2496  } else if (instance_rh && pe_rsc_is_clone(rsc_rh) == FALSE) {
2497  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
2498  "is not a clone but instance '%s' was requested",
2499  id, id_rh, instance_rh);
2500  return FALSE;
2501  }
2502 
2503  if (instance_lh) {
2504  rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
2505  if (rsc_lh == NULL) {
2506  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
2507  "does not have an instance '%s'",
2508  id, id_lh, instance_lh);
2509  return FALSE;
2510  }
2511  }
2512 
2513  if (instance_rh) {
2514  rsc_rh = find_clone_instance(rsc_rh, instance_rh, data_set);
2515  if (rsc_rh == NULL) {
2516  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
2517  "does not have an instance '%s'",
2518  "'%s'", id, id_rh, instance_rh);
2519  return FALSE;
2520  }
2521  }
2522 
2523  if (crm_is_true(symmetrical)) {
2524  pcmk__config_warn("The colocation constraint '"
2526  "' attribute has been removed");
2527  }
2528 
2529  if (score) {
2530  score_i = char2score(score);
2531  }
2532 
2533  rsc_colocation_new(id, attr, score_i, rsc_lh, rsc_rh, state_lh, state_rh, data_set);
2534  return TRUE;
2535 }
2536 
2537 static gboolean
2538 unpack_colocation_tags(xmlNode * xml_obj, xmlNode ** expanded_xml, pe_working_set_t * data_set)
2539 {
2540  const char *id = NULL;
2541  const char *id_lh = NULL;
2542  const char *id_rh = NULL;
2543  const char *state_lh = NULL;
2544  const char *state_rh = NULL;
2545 
2546  pe_resource_t *rsc_lh = NULL;
2547  pe_resource_t *rsc_rh = NULL;
2548 
2549  pe_tag_t *tag_lh = NULL;
2550  pe_tag_t *tag_rh = NULL;
2551 
2552  xmlNode *new_xml = NULL;
2553  xmlNode *rsc_set_lh = NULL;
2554  xmlNode *rsc_set_rh = NULL;
2555  gboolean any_sets = FALSE;
2556 
2557  *expanded_xml = NULL;
2558 
2559  CRM_CHECK(xml_obj != NULL, return FALSE);
2560 
2561  id = ID(xml_obj);
2562  if (id == NULL) {
2563  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
2564  crm_element_name(xml_obj));
2565  return FALSE;
2566  }
2567 
2568  /* Attempt to expand any template/tag references in possible resource sets. */
2569  expand_tags_in_sets(xml_obj, &new_xml, data_set);
2570  if (new_xml) {
2571  /* There are resource sets referencing templates/tags. Return with the expanded XML. */
2572  crm_log_xml_trace(new_xml, "Expanded rsc_colocation...");
2573  *expanded_xml = new_xml;
2574  return TRUE;
2575  }
2576 
2577  id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
2578  id_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
2579  if (id_lh == NULL || id_rh == NULL) {
2580  return TRUE;
2581  }
2582 
2583  if (valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh) == FALSE) {
2584  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
2585  "valid resource or tag", id, id_lh);
2586  return FALSE;
2587  }
2588 
2589  if (valid_resource_or_tag(data_set, id_rh, &rsc_rh, &tag_rh) == FALSE) {
2590  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
2591  "valid resource or tag", id, id_rh);
2592  return FALSE;
2593  }
2594 
2595  if (rsc_lh && rsc_rh) {
2596  /* Neither side references any template/tag. */
2597  return TRUE;
2598  }
2599 
2600  if (tag_lh && tag_rh) {
2601  /* A colocation constraint between two templates/tags makes no sense. */
2602  pcmk__config_err("Ignoring constraint '%s' because two templates or "
2603  "tags cannot be colocated", id);
2604  return FALSE;
2605  }
2606 
2607  state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
2608  state_rh = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
2609 
2610  new_xml = copy_xml(xml_obj);
2611 
2612  /* Convert the template/tag reference in "rsc" into a resource_set under the colocation constraint. */
2613  if (tag_to_set(new_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE, TRUE, data_set) == FALSE) {
2614  free_xml(new_xml);
2615  return FALSE;
2616  }
2617 
2618  if (rsc_set_lh) {
2619  if (state_lh) {
2620  /* A "rsc-role" is specified.
2621  Move it into the converted resource_set as a "role"" attribute. */
2622  crm_xml_add(rsc_set_lh, "role", state_lh);
2624  }
2625  any_sets = TRUE;
2626  }
2627 
2628  /* Convert the template/tag reference in "with-rsc" into a resource_set under the colocation constraint. */
2629  if (tag_to_set(new_xml, &rsc_set_rh, XML_COLOC_ATTR_TARGET, TRUE, data_set) == FALSE) {
2630  free_xml(new_xml);
2631  return FALSE;
2632  }
2633 
2634  if (rsc_set_rh) {
2635  if (state_rh) {
2636  /* A "with-rsc-role" is specified.
2637  Move it into the converted resource_set as a "role"" attribute. */
2638  crm_xml_add(rsc_set_rh, "role", state_rh);
2640  }
2641  any_sets = TRUE;
2642  }
2643 
2644  if (any_sets) {
2645  crm_log_xml_trace(new_xml, "Expanded rsc_colocation...");
2646  *expanded_xml = new_xml;
2647  } else {
2648  free_xml(new_xml);
2649  }
2650 
2651  return TRUE;
2652 }
2653 
2654 gboolean
2655 unpack_rsc_colocation(xmlNode * xml_obj, pe_working_set_t * data_set)
2656 {
2657  int score_i = 0;
2658  xmlNode *set = NULL;
2659  xmlNode *last = NULL;
2660  gboolean any_sets = FALSE;
2661 
2662  xmlNode *orig_xml = NULL;
2663  xmlNode *expanded_xml = NULL;
2664 
2665  const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
2666  const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
2667 
2668  gboolean rc = TRUE;
2669 
2670  if (score) {
2671  score_i = char2score(score);
2672  }
2673 
2674  rc = unpack_colocation_tags(xml_obj, &expanded_xml, data_set);
2675  if (expanded_xml) {
2676  orig_xml = xml_obj;
2677  xml_obj = expanded_xml;
2678 
2679  } else if (rc == FALSE) {
2680  return FALSE;
2681  }
2682 
2683  for (set = __xml_first_child_element(xml_obj); set != NULL;
2684  set = __xml_next_element(set)) {
2685 
2686  if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
2687  any_sets = TRUE;
2688  set = expand_idref(set, data_set->input);
2689  if (unpack_colocation_set(set, score_i, data_set) == FALSE) {
2690  return FALSE;
2691 
2692  } else if (last && colocate_rsc_sets(id, last, set, score_i, data_set) == FALSE) {
2693  return FALSE;
2694  }
2695  last = set;
2696  }
2697  }
2698 
2699  if (expanded_xml) {
2700  free_xml(expanded_xml);
2701  xml_obj = orig_xml;
2702  }
2703 
2704  if (any_sets == FALSE) {
2705  return unpack_simple_colocation(xml_obj, data_set);
2706  }
2707 
2708  return TRUE;
2709 }
2710 
2711 gboolean
2712 rsc_ticket_new(const char *id, pe_resource_t * rsc_lh, pe_ticket_t * ticket,
2713  const char *state_lh, const char *loss_policy, pe_working_set_t * data_set)
2714 {
2715  rsc_ticket_t *new_rsc_ticket = NULL;
2716 
2717  if (rsc_lh == NULL) {
2718  pcmk__config_err("Ignoring ticket '%s' because resource "
2719  "does not exist", id);
2720  return FALSE;
2721  }
2722 
2723  new_rsc_ticket = calloc(1, sizeof(rsc_ticket_t));
2724  if (new_rsc_ticket == NULL) {
2725  return FALSE;
2726  }
2727 
2728  if (state_lh == NULL || safe_str_eq(state_lh, RSC_ROLE_STARTED_S)) {
2729  state_lh = RSC_ROLE_UNKNOWN_S;
2730  }
2731 
2732  new_rsc_ticket->id = id;
2733  new_rsc_ticket->ticket = ticket;
2734  new_rsc_ticket->rsc_lh = rsc_lh;
2735  new_rsc_ticket->role_lh = text2role(state_lh);
2736 
2737  if (safe_str_eq(loss_policy, "fence")) {
2738  if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
2739  new_rsc_ticket->loss_policy = loss_ticket_fence;
2740  } else {
2742  "' for ticket '%s' to 'stop' "
2743  "because fencing is not configured", ticket->id);
2744  loss_policy = "stop";
2745  }
2746  }
2747 
2748  if (new_rsc_ticket->loss_policy == loss_ticket_fence) {
2749  crm_debug("On loss of ticket '%s': Fence the nodes running %s (%s)",
2750  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
2751  role2text(new_rsc_ticket->role_lh));
2752 
2753  } else if (safe_str_eq(loss_policy, "freeze")) {
2754  crm_debug("On loss of ticket '%s': Freeze %s (%s)",
2755  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
2756  role2text(new_rsc_ticket->role_lh));
2757  new_rsc_ticket->loss_policy = loss_ticket_freeze;
2758 
2759  } else if (safe_str_eq(loss_policy, "demote")) {
2760  crm_debug("On loss of ticket '%s': Demote %s (%s)",
2761  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
2762  role2text(new_rsc_ticket->role_lh));
2763  new_rsc_ticket->loss_policy = loss_ticket_demote;
2764 
2765  } else if (safe_str_eq(loss_policy, "stop")) {
2766  crm_debug("On loss of ticket '%s': Stop %s (%s)",
2767  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
2768  role2text(new_rsc_ticket->role_lh));
2769  new_rsc_ticket->loss_policy = loss_ticket_stop;
2770 
2771  } else {
2772  if (new_rsc_ticket->role_lh == RSC_ROLE_MASTER) {
2773  crm_debug("On loss of ticket '%s': Default to demote %s (%s)",
2774  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
2775  role2text(new_rsc_ticket->role_lh));
2776  new_rsc_ticket->loss_policy = loss_ticket_demote;
2777 
2778  } else {
2779  crm_debug("On loss of ticket '%s': Default to stop %s (%s)",
2780  new_rsc_ticket->ticket->id, new_rsc_ticket->rsc_lh->id,
2781  role2text(new_rsc_ticket->role_lh));
2782  new_rsc_ticket->loss_policy = loss_ticket_stop;
2783  }
2784  }
2785 
2786  pe_rsc_trace(rsc_lh, "%s (%s) ==> %s", rsc_lh->id, role2text(new_rsc_ticket->role_lh),
2787  ticket->id);
2788 
2789  rsc_lh->rsc_tickets = g_list_append(rsc_lh->rsc_tickets, new_rsc_ticket);
2790 
2791  data_set->ticket_constraints = g_list_append(data_set->ticket_constraints, new_rsc_ticket);
2792 
2793  if (new_rsc_ticket->ticket->granted == FALSE || new_rsc_ticket->ticket->standby) {
2794  rsc_ticket_constraint(rsc_lh, new_rsc_ticket, data_set);
2795  }
2796 
2797  return TRUE;
2798 }
2799 
2800 static gboolean
2801 unpack_rsc_ticket_set(xmlNode * set, pe_ticket_t * ticket, const char *loss_policy,
2802  pe_working_set_t * data_set)
2803 {
2804  xmlNode *xml_rsc = NULL;
2805  pe_resource_t *resource = NULL;
2806  const char *set_id = NULL;
2807  const char *role = NULL;
2808 
2809  CRM_CHECK(set != NULL, return FALSE);
2810  CRM_CHECK(ticket != NULL, return FALSE);
2811 
2812  set_id = ID(set);
2813  if (set_id == NULL) {
2814  pcmk__config_err("Ignoring <" XML_CONS_TAG_RSC_SET "> without "
2815  XML_ATTR_ID);
2816  return FALSE;
2817  }
2818 
2819  role = crm_element_value(set, "role");
2820 
2821  for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
2822  xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
2823 
2824  EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
2825  pe_rsc_trace(resource, "Resource '%s' depends on ticket '%s'",
2826  resource->id, ticket->id);
2827  rsc_ticket_new(set_id, resource, ticket, role, loss_policy, data_set);
2828  }
2829 
2830  return TRUE;
2831 }
2832 
2833 static gboolean
2834 unpack_simple_rsc_ticket(xmlNode * xml_obj, pe_working_set_t * data_set)
2835 {
2836  const char *id = NULL;
2837  const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
2838  const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
2839 
2840  pe_ticket_t *ticket = NULL;
2841 
2842  const char *id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
2843  const char *state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
2844 
2845  // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
2846  const char *instance_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_INSTANCE);
2847 
2848  pe_resource_t *rsc_lh = NULL;
2849 
2850  CRM_CHECK(xml_obj != NULL, return FALSE);
2851 
2852  id = ID(xml_obj);
2853  if (id == NULL) {
2854  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
2855  crm_element_name(xml_obj));
2856  return FALSE;
2857  }
2858 
2859  if (ticket_str == NULL) {
2860  pcmk__config_err("Ignoring constraint '%s' without ticket specified",
2861  id);
2862  return FALSE;
2863  } else {
2864  ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
2865  }
2866 
2867  if (ticket == NULL) {
2868  pcmk__config_err("Ignoring constraint '%s' because ticket '%s' "
2869  "does not exist", id, ticket_str);
2870  return FALSE;
2871  }
2872 
2873  if (id_lh == NULL) {
2874  pcmk__config_err("Ignoring constraint '%s' without resource", id);
2875  return FALSE;
2876  } else {
2877  rsc_lh = pe_find_constraint_resource(data_set->resources, id_lh);
2878  }
2879 
2880  if (rsc_lh == NULL) {
2881  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
2882  "does not exist", id, id_lh);
2883  return FALSE;
2884 
2885  } else if (instance_lh && pe_rsc_is_clone(rsc_lh) == FALSE) {
2886  pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
2887  "is not a clone but instance '%s' was requested",
2888  id, id_lh, instance_lh);
2889  return FALSE;
2890  }
2891 
2892  if (instance_lh) {
2893  rsc_lh = find_clone_instance(rsc_lh, instance_lh, data_set);
2894  if (rsc_lh == NULL) {
2895  pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
2896  "does not have an instance '%s'",
2897  "'%s'", id, id_lh, instance_lh);
2898  return FALSE;
2899  }
2900  }
2901 
2902  rsc_ticket_new(id, rsc_lh, ticket, state_lh, loss_policy, data_set);
2903  return TRUE;
2904 }
2905 
2906 static gboolean
2907 unpack_rsc_ticket_tags(xmlNode * xml_obj, xmlNode ** expanded_xml, pe_working_set_t * data_set)
2908 {
2909  const char *id = NULL;
2910  const char *id_lh = NULL;
2911  const char *state_lh = NULL;
2912 
2913  pe_resource_t *rsc_lh = NULL;
2914  pe_tag_t *tag_lh = NULL;
2915 
2916  xmlNode *new_xml = NULL;
2917  xmlNode *rsc_set_lh = NULL;
2918  gboolean any_sets = FALSE;
2919 
2920  *expanded_xml = NULL;
2921 
2922  CRM_CHECK(xml_obj != NULL, return FALSE);
2923 
2924  id = ID(xml_obj);
2925  if (id == NULL) {
2926  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
2927  crm_element_name(xml_obj));
2928  return FALSE;
2929  }
2930 
2931  /* Attempt to expand any template/tag references in possible resource sets. */
2932  expand_tags_in_sets(xml_obj, &new_xml, data_set);
2933  if (new_xml) {
2934  /* There are resource sets referencing templates/tags. Return with the expanded XML. */
2935  crm_log_xml_trace(new_xml, "Expanded rsc_ticket...");
2936  *expanded_xml = new_xml;
2937  return TRUE;
2938  }
2939 
2940  id_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
2941  if (id_lh == NULL) {
2942  return TRUE;
2943  }
2944 
2945  if (valid_resource_or_tag(data_set, id_lh, &rsc_lh, &tag_lh) == FALSE) {
2946  pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
2947  "valid resource or tag", id, id_lh);
2948  return FALSE;
2949 
2950  } else if (rsc_lh) {
2951  /* No template/tag is referenced. */
2952  return TRUE;
2953  }
2954 
2955  state_lh = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
2956 
2957  new_xml = copy_xml(xml_obj);
2958 
2959  /* Convert the template/tag reference in "rsc" into a resource_set under the rsc_ticket constraint. */
2960  if (tag_to_set(new_xml, &rsc_set_lh, XML_COLOC_ATTR_SOURCE, FALSE, data_set) == FALSE) {
2961  free_xml(new_xml);
2962  return FALSE;
2963  }
2964 
2965  if (rsc_set_lh) {
2966  if (state_lh) {
2967  /* A "rsc-role" is specified.
2968  Move it into the converted resource_set as a "role"" attribute. */
2969  crm_xml_add(rsc_set_lh, "role", state_lh);
2971  }
2972  any_sets = TRUE;
2973  }
2974 
2975  if (any_sets) {
2976  crm_log_xml_trace(new_xml, "Expanded rsc_ticket...");
2977  *expanded_xml = new_xml;
2978  } else {
2979  free_xml(new_xml);
2980  }
2981 
2982  return TRUE;
2983 }
2984 
2985 gboolean
2986 unpack_rsc_ticket(xmlNode * xml_obj, pe_working_set_t * data_set)
2987 {
2988  xmlNode *set = NULL;
2989  gboolean any_sets = FALSE;
2990 
2991  const char *id = NULL;
2992  const char *ticket_str = crm_element_value(xml_obj, XML_TICKET_ATTR_TICKET);
2993  const char *loss_policy = crm_element_value(xml_obj, XML_TICKET_ATTR_LOSS_POLICY);
2994 
2995  pe_ticket_t *ticket = NULL;
2996 
2997  xmlNode *orig_xml = NULL;
2998  xmlNode *expanded_xml = NULL;
2999 
3000  gboolean rc = TRUE;
3001 
3002  CRM_CHECK(xml_obj != NULL, return FALSE);
3003 
3004  id = ID(xml_obj);
3005  if (id == NULL) {
3006  pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
3007  crm_element_name(xml_obj));
3008  return FALSE;
3009  }
3010 
3011  if (data_set->tickets == NULL) {
3012  data_set->tickets =
3013  g_hash_table_new_full(crm_str_hash, g_str_equal, free, destroy_ticket);
3014  }
3015 
3016  if (ticket_str == NULL) {
3017  pcmk__config_err("Ignoring constraint '%s' without ticket", id);
3018  return FALSE;
3019  } else {
3020  ticket = g_hash_table_lookup(data_set->tickets, ticket_str);
3021  }
3022 
3023  if (ticket == NULL) {
3024  ticket = ticket_new(ticket_str, data_set);
3025  if (ticket == NULL) {
3026  return FALSE;
3027  }
3028  }
3029 
3030  rc = unpack_rsc_ticket_tags(xml_obj, &expanded_xml, data_set);
3031  if (expanded_xml) {
3032  orig_xml = xml_obj;
3033  xml_obj = expanded_xml;
3034 
3035  } else if (rc == FALSE) {
3036  return FALSE;
3037  }
3038 
3039  for (set = __xml_first_child_element(xml_obj); set != NULL;
3040  set = __xml_next_element(set)) {
3041 
3042  if (crm_str_eq((const char *)set->name, XML_CONS_TAG_RSC_SET, TRUE)) {
3043  any_sets = TRUE;
3044  set = expand_idref(set, data_set->input);
3045  if (unpack_rsc_ticket_set(set, ticket, loss_policy, data_set) == FALSE) {
3046  return FALSE;
3047  }
3048  }
3049  }
3050 
3051  if (expanded_xml) {
3052  free_xml(expanded_xml);
3053  xml_obj = orig_xml;
3054  }
3055 
3056  if (any_sets == FALSE) {
3057  return unpack_simple_rsc_ticket(xml_obj, data_set);
3058  }
3059 
3060  return TRUE;
3061 }
GList * pcmk__copy_node_list(const GList *list, bool reset)
crm_time_t * crm_time_new_undefined(void)
Allocate memory for an uninitialized time object.
Definition: iso8601.c:117
GHashTable * tags
Definition: pe_types.h:169
pe_resource_t * rsc_lh
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
GListPtr nodes
Definition: pe_types.h:146
gboolean rsc_colocation_new(const char *id, const char *node_attr, int score, pe_resource_t *rsc_lh, pe_resource_t *rsc_rh, const char *state_lh, const char *state_rh, pe_working_set_t *data_set)
pe_action_t * lh_action
Definition: internal.h:68
enum rsc_role_e role_filter
Definition: internal.h:57
#define RSC_STOP
Definition: crm.h:199
A dumping ground.
gboolean parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
Definition: operations.c:48
GHashTable * attrs
Definition: pe_types.h:221
const char * pe_node_attribute_calculated(const pe_node_t *node, const char *name, const pe_resource_t *rsc)
Definition: common.c:590
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:263
#define INFINITY
Definition: crm.h:95
#define XML_ORDER_ATTR_THEN_ACTION
Definition: msg_xml.h:331
gboolean is_parent(pe_resource_t *child, pe_resource_t *rsc)
Definition: complex.c:745
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:133
#define XML_ORDER_ATTR_THEN
Definition: msg_xml.h:329
#define XML_COLOC_ATTR_TARGET_INSTANCE
Definition: msg_xml.h:323
struct crm_time_s crm_time_t
Definition: iso8601.h:32
void pe__update_recheck_time(time_t recheck, pe_working_set_t *data_set)
Definition: utils.c:2681
#define CRM_OP_RELAXED_SET
Definition: crm.h:153
#define RSC_ROLE_STARTED_S
Definition: common.h:88
gboolean unpack_rsc_order(xmlNode *xml_obj, pe_working_set_t *data_set)
gboolean pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Definition: rules.c:63
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:296
gboolean unpack_rsc_ticket(xmlNode *xml_obj, pe_working_set_t *data_set)
#define XML_BOOLEAN_FALSE
Definition: msg_xml.h:108
gboolean standby
Definition: pe_types.h:438
void rsc_ticket_constraint(pe_resource_t *lh_rsc, rsc_ticket_t *rsc_ticket, pe_working_set_t *data_set)
#define pcmk__config_err(fmt...)
Definition: internal.h:95
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:4399
GListPtr rsc_tickets
Definition: pe_types.h:344
pe_resource_t * rsc
Definition: pe_types.h:388
#define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name)
int char2score(const char *score)
Definition: utils.c:59
GHashTable * meta
Definition: pe_types.h:357
#define XML_LOCATION_ATTR_DISCOVERY
Definition: msg_xml.h:315
pe_re_match_data_t * re
Definition: rules.h:41
resource_object_functions_t * fns
Definition: pe_types.h:316
GHashTable * parameters
Definition: pe_types.h:358
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:316
pe_node_t * pe__copy_node(const pe_node_t *this_node)
Definition: utils.c:139
#define CRMD_ACTION_PROMOTE
Definition: crm.h:177
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:126
GListPtr resources
Definition: pe_types.h:147
gboolean unpack_constraints(xmlNode *xml_constraints, pe_working_set_t *data_set)
#define XML_CONS_TAG_RSC_DEPEND
Definition: msg_xml.h:308
pe_node_t * pe_find_node(GListPtr node_list, const char *uname)
Definition: status.c:427
pe_action_t * rh_action
Definition: internal.h:73
int order_id
Deprecated (will be removed in a future release)
Definition: pe_types.h:161
pe_ticket_t * ticket
#define XML_CONS_TAG_RSC_TICKET
Definition: msg_xml.h:311
GListPtr rsc_cons
Definition: pe_types.h:341
pe_node_t * partial_migration_target
Definition: pe_types.h:348
#define RSC_START
Definition: crm.h:196
GHashTable * tickets
Definition: pe_types.h:141
#define XML_COLOC_ATTR_TARGET_ROLE
Definition: msg_xml.h:320
#define XML_ORDER_ATTR_FIRST_INSTANCE
Definition: msg_xml.h:332
gboolean unpack_rsc_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
#define XML_LOC_ATTR_SOURCE
Definition: msg_xml.h:325
#define XML_CONS_TAG_RSC_SET
Definition: msg_xml.h:312
pe_node_t * pe_find_node_id(GListPtr node_list, const char *id)
Definition: status.c:411
#define RSC_MIGRATE
Definition: crm.h:193
const char * action
Definition: pcmk_fence.c:29
xmlDoc * getDocPtr(xmlNode *node)
Definition: xml.c:1938
#define CRMD_ACTION_START
Definition: crm.h:171
uint32_t id
Definition: internal.h:80
#define XML_RSC_ATTR_INCARNATION_MIN
Definition: msg_xml.h:188
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2142
const char * role2text(enum rsc_role_e role)
Definition: common.c:463
pe__location_t * rsc2node_new(const char *id, pe_resource_t *rsc, int weight, const char *discovery_mode, pe_node_t *node, pe_working_set_t *data_set)
#define CRMD_ACTION_STOP
Definition: crm.h:174
#define XML_ORDER_ATTR_FIRST
Definition: msg_xml.h:328
int weight
Definition: pe_types.h:228
#define crm_warn(fmt, args...)
Definition: logging.h:364
#define CRMD_ACTION_DEMOTE
Definition: crm.h:179
#define XML_COLOC_ATTR_TARGET
Definition: msg_xml.h:319
#define pe_rsc_allow_migrate
Definition: pe_types.h:258
int rc
Definition: pcmk_fence.c:34
pe_action_t * get_pseudo_op(const char *name, pe_working_set_t *data_set)
Definition: utils.c:1875
#define crm_debug(fmt, args...)
Definition: logging.h:368
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:762
#define XML_ATTR_ID
Definition: msg_xml.h:96
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:522
#define XML_LOC_ATTR_SOURCE_PATTERN
Definition: msg_xml.h:326
const char * node_attribute
#define XML_CONS_TAG_RSC_LOCATION
Definition: msg_xml.h:310
pe_resource_t *(* find_rsc)(pe_resource_t *parent, const char *search, const pe_node_t *node, int flags)
Definition: pe_types.h:45
#define pe_warn_once(pe_wo_bit, fmt...)
Definition: internal.h:42
pe_ticket_t * ticket_new(const char *ticket_id, pe_working_set_t *data_set)
Definition: utils.c:1904
char * task
Definition: pe_types.h:392
#define CRM_ATTR_UNAME
Definition: crm.h:110
int custom_action_order(pe_resource_t *lh_rsc, char *lh_task, pe_action_t *lh_action, pe_resource_t *rh_rsc, char *rh_task, pe_action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set)
GListPtr refs
Definition: pe_types.h:444
#define crm_trace(fmt, args...)
Definition: logging.h:369
struct pe_node_shared_s * details
Definition: pe_types.h:231
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:4472
pe_resource_t * find_clone_instance(pe_resource_t *rsc, const char *sub_id, pe_working_set_t *data_set)
Definition: clone.c:41
enum loss_ticket_policy_e loss_policy
unsigned long long flags
Definition: pe_types.h:332
const char * uname
Definition: pe_types.h:196
#define pe_rsc_promotable
Definition: pe_types.h:243
Wrappers for and extensions to libxml2.
pe_resource_t * lh_rsc
Definition: internal.h:67
ISO_8601 Date handling.
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:1976
long long int crm_time_get_seconds_since_epoch(crm_time_t *dt)
Definition: iso8601.c:351
enum pe_restart restart_type
Definition: pe_types.h:322
#define pe_flag_stonith_enabled
Definition: pe_types.h:95
#define XML_COLOC_ATTR_SOURCE_INSTANCE
Definition: msg_xml.h:322
#define XML_TAG_RESOURCE_REF
Definition: msg_xml.h:173
GListPtr rsc_cons_lhs
Definition: pe_types.h:340
enum pe_ordering type
Definition: internal.h:64
char * uuid
Definition: pe_types.h:393
gboolean update_action_flags(pe_action_t *action, enum pe_action_flags flags, const char *source, int line)
char * pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
Definition: rules.c:1027
pe_resource_t * rsc_lh
void free_xml(xmlNode *child)
Definition: xml.c:2136
enum rsc_role_e text2role(const char *role)
Definition: common.c:484
enum pe_obj_types variant
Definition: pe_types.h:314
xmlNode * input
Definition: pe_types.h:126
gboolean granted
Definition: pe_types.h:436
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:326
#define XML_CIB_TAG_NODE
Definition: msg_xml.h:159
int new_rsc_order(pe_resource_t *lh_rsc, const char *lh_task, pe_resource_t *rh_rsc, const char *rh_task, enum pe_ordering type, pe_working_set_t *data_set)
#define RSC_DEMOTED
Definition: crm.h:205
const char * id
Definition: pe_types.h:195
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:40
#define XML_COLOC_ATTR_SOURCE_ROLE
Definition: msg_xml.h:318
char * id
Definition: pe_types.h:435
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:299
GListPtr ordering_constraints
Definition: pe_types.h:149
const xmlChar * pcmkXmlStr
Definition: xml.h:51
GListPtr colocation_constraints
Definition: pe_types.h:150
match resource ID or LRM history ID
Definition: pe_types.h:81
#define XML_ORDER_ATTR_THEN_INSTANCE
Definition: msg_xml.h:333
#define XML_COLOC_ATTR_NODE_ATTR
Definition: msg_xml.h:321
#define RSC_STARTED
Definition: crm.h:197
Cluster status and scheduling.
GListPtr children
Definition: pe_types.h:361
#define XML_ORDER_ATTR_KIND
Definition: msg_xml.h:334
int pe__add_scores(int score1, int score2)
Definition: common.c:510
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
GListPtr ticket_constraints
Definition: pe_types.h:151
Cluster Configuration.
#define RSC_PROMOTE
Definition: crm.h:202
regmatch_t * pmatch
Definition: rules.h:37
void xml_remove_prop(xmlNode *obj, const char *name)
Definition: xml.c:3340
#define CRM_OP_RELAXED_CLONE
Definition: crm.h:154
#define crm_str_hash
Definition: util.h:66
#define crm_str(x)
Definition: logging.h:389
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Evaluate any rules contained by given XML element.
Definition: rules.c:38
rsc_role_e
Definition: common.h:76
#define XML_CONS_TAG_RSC_ORDER
Definition: msg_xml.h:309
#define RSC_STOPPED
Definition: crm.h:200
const char * id
void destroy_ticket(gpointer data)
Definition: utils.c:1892
#define RSC_PROMOTED
Definition: crm.h:203
pe_resource_t * rh_rsc
Definition: internal.h:72
#define RSC_ROLE_UNKNOWN_S
Definition: common.h:86
#define crm_log_xml_trace(xml, text)
Definition: logging.h:377
#define XML_RULE_ATTR_SCORE_ATTRIBUTE
Definition: msg_xml.h:297
enum pe_ordering get_flags(const char *id, enum pe_order_kind kind, const char *action_first, const char *action_then, gboolean invert)
gboolean crm_is_true(const char *s)
Definition: strings.c:278
const char * id
enum pe_ordering get_asymmetrical_flags(enum pe_order_kind kind)
#define XML_TICKET_ATTR_TICKET
Definition: msg_xml.h:336
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:19
unsigned long long flags
Definition: pe_types.h:135
#define ID(x)
Definition: msg_xml.h:418
#define pe_err(fmt...)
Definition: internal.h:21
#define safe_str_eq(a, b)
Definition: util.h:65
#define XML_TAG_RULE
Definition: msg_xml.h:295
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define XML_CONS_ATTR_SYMMETRICAL
Definition: msg_xml.h:313
GList * GListPtr
Definition: crm.h:214
crm_time_t * now
Definition: pe_types.h:127
#define crm_info(fmt, args...)
Definition: logging.h:366
gboolean rsc_ticket_new(const char *id, pe_resource_t *rsc_lh, pe_ticket_t *ticket, const char *state_lh, const char *loss_policy, pe_working_set_t *data_set)
gboolean unpack_location(xmlNode *xml_obj, pe_working_set_t *data_set)
GHashTable * template_rsc_sets
Definition: pe_types.h:167
#define XML_COLOC_ATTR_SOURCE
Definition: msg_xml.h:317
#define pcmk__config_warn(fmt...)
Definition: internal.h:100
pe_ordering
Definition: pe_types.h:461
char * string
Definition: rules.h:35
#define XML_ORDER_ATTR_FIRST_ACTION
Definition: msg_xml.h:330
uint64_t flags
Definition: remote.c:149
int required_runnable_before
Definition: pe_types.h:423
enum crm_ais_msg_types type
Definition: internal.h:83
#define RSC_DEMOTE
Definition: crm.h:204
pe_resource_t * rsc_rh
char * id
Definition: pe_types.h:305
#define RSC_MIGRATED
Definition: crm.h:194
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:4425
#define XML_RULE_ATTR_ROLE
Definition: msg_xml.h:298
#define XML_TICKET_ATTR_LOSS_POLICY
Definition: msg_xml.h:337
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:141
gboolean local
Definition: internal.h:82