root/lib/pacemaker/pcmk_sched_constraints.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. evaluate_lifetime
  2. unpack_constraints
  3. invert_action
  4. get_ordering_type
  5. pe_find_constraint_resource
  6. pe_find_constraint_tag
  7. valid_resource_or_tag
  8. order_is_symmetrical
  9. unpack_simple_rsc_order
  10. expand_tags_in_sets
  11. tag_to_set
  12. unpack_simple_location
  13. unpack_rsc_location
  14. unpack_location_tags
  15. unpack_location_set
  16. unpack_location
  17. get_node_score
  18. generate_location_rule
  19. sort_cons_priority_lh
  20. sort_cons_priority_rh
  21. anti_colocation_order
  22. rsc_colocation_new
  23. new_rsc_order
  24. task_from_action_or_key
  25. handle_migration_ordering
  26. custom_action_order
  27. get_asymmetrical_flags
  28. get_flags
  29. unpack_order_set
  30. order_rsc_sets
  31. unpack_order_tags
  32. unpack_rsc_order
  33. unpack_colocation_set
  34. colocate_rsc_sets
  35. unpack_simple_colocation
  36. unpack_colocation_tags
  37. unpack_rsc_colocation
  38. rsc_ticket_new
  39. unpack_rsc_ticket_set
  40. unpack_simple_rsc_ticket
  41. unpack_rsc_ticket_tags
  42. unpack_rsc_ticket

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

/* [previous][next][first][last][top][bottom][index][help] */