root/pengine/constraints.c

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

DEFINITIONS

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

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

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