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