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. pcmk__new_colocation
  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_influence
  34. unpack_colocation_set
  35. colocate_rsc_sets
  36. unpack_simple_colocation
  37. unpack_colocation_tags
  38. unpack_rsc_colocation
  39. rsc_ticket_new
  40. unpack_rsc_ticket_set
  41. unpack_simple_rsc_ticket
  42. unpack_rsc_ticket_tags
  43. unpack_rsc_ticket

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

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