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