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

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