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