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