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