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