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