root/lib/pacemaker/pcmk_sched_colocation.c

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

DEFINITIONS

This source file includes following definitions.
  1. cmp_dependent_priority
  2. cmp_primary_priority
  3. pcmk__add_this_with
  4. pcmk__add_this_with_list
  5. pcmk__add_with_this
  6. pcmk__add_with_this_list
  7. anti_colocation_order
  8. pcmk__new_colocation
  9. unpack_influence
  10. unpack_colocation_set
  11. colocate_rsc_sets
  12. unpack_simple_colocation
  13. unpack_colocation_tags
  14. pcmk__unpack_colocation
  15. mark_action_blocked
  16. pcmk__block_colocation_dependents
  17. pcmk__colocation_affects
  18. pcmk__apply_coloc_to_weights
  19. pcmk__apply_coloc_to_priority
  20. best_node_score_matching_attr
  21. add_node_scores_matching_attr
  22. pcmk__add_colocated_node_scores
  23. pcmk__add_dependent_scores
  24. pcmk__with_this_colocations
  25. pcmk__this_with_colocations

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdbool.h>
  13 #include <glib.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/pengine/status.h>
  17 #include <pacemaker-internal.h>
  18 
  19 #include "crm/common/util.h"
  20 #include "crm/common/xml_internal.h"
  21 #include "crm/msg_xml.h"
  22 #include "libpacemaker_private.h"
  23 
  24 #define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do {                      \
  25         __rsc = pcmk__find_constraint_resource(data_set->resources, __name);    \
  26         if (__rsc == NULL) {                                                    \
  27             pcmk__config_err("%s: No resource found for %s", __set, __name);    \
  28             return;                                                             \
  29         }                                                                       \
  30     } while(0)
  31 
  32 // Used to temporarily mark a node as unusable
  33 #define INFINITY_HACK   (INFINITY * -100)
  34 
  35 static gint
  36 cmp_dependent_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
  39     const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
  40 
  41     if (a == NULL) {
  42         return 1;
  43     }
  44     if (b == NULL) {
  45         return -1;
  46     }
  47 
  48     CRM_ASSERT(rsc_constraint1->dependent != NULL);
  49     CRM_ASSERT(rsc_constraint1->primary != NULL);
  50 
  51     if (rsc_constraint1->dependent->priority > rsc_constraint2->dependent->priority) {
  52         return -1;
  53     }
  54 
  55     if (rsc_constraint1->dependent->priority < rsc_constraint2->dependent->priority) {
  56         return 1;
  57     }
  58 
  59     /* Process clones before primitives and groups */
  60     if (rsc_constraint1->dependent->variant > rsc_constraint2->dependent->variant) {
  61         return -1;
  62     }
  63     if (rsc_constraint1->dependent->variant < rsc_constraint2->dependent->variant) {
  64         return 1;
  65     }
  66 
  67     /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
  68      * clones (probably unnecessary, but avoids having to update regression
  69      * tests)
  70      */
  71     if (rsc_constraint1->dependent->variant == pe_clone) {
  72         if (pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
  73             && !pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
  74             return -1;
  75         } else if (!pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
  76             && pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
  77             return 1;
  78         }
  79     }
  80 
  81     return strcmp(rsc_constraint1->dependent->id,
  82                   rsc_constraint2->dependent->id);
  83 }
  84 
  85 static gint
  86 cmp_primary_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  87 {
  88     const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
  89     const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
  90 
  91     if (a == NULL) {
  92         return 1;
  93     }
  94     if (b == NULL) {
  95         return -1;
  96     }
  97 
  98     CRM_ASSERT(rsc_constraint1->dependent != NULL);
  99     CRM_ASSERT(rsc_constraint1->primary != NULL);
 100 
 101     if (rsc_constraint1->primary->priority > rsc_constraint2->primary->priority) {
 102         return -1;
 103     }
 104 
 105     if (rsc_constraint1->primary->priority < rsc_constraint2->primary->priority) {
 106         return 1;
 107     }
 108 
 109     /* Process clones before primitives and groups */
 110     if (rsc_constraint1->primary->variant > rsc_constraint2->primary->variant) {
 111         return -1;
 112     } else if (rsc_constraint1->primary->variant < rsc_constraint2->primary->variant) {
 113         return 1;
 114     }
 115 
 116     /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
 117      * clones (probably unnecessary, but avoids having to update regression
 118      * tests)
 119      */
 120     if (rsc_constraint1->primary->variant == pe_clone) {
 121         if (pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
 122             && !pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
 123             return -1;
 124         } else if (!pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
 125             && pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
 126             return 1;
 127         }
 128     }
 129 
 130     return strcmp(rsc_constraint1->primary->id, rsc_constraint2->primary->id);
 131 }
 132 
 133 /*!
 134  * \internal
 135  * \brief Add a "this with" colocation constraint to a sorted list
 136  *
 137  * \param[in,out] list        List of constraints to add \p colocation to
 138  * \param[in]     colocation  Colocation constraint to add to \p list
 139  *
 140  * \note The list will be sorted using cmp_primary_priority().
 141  */
 142 void
 143 pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145     CRM_ASSERT((list != NULL) && (colocation != NULL));
 146 
 147     crm_trace("Adding colocation %s (%s with %s%s%s @%d) "
 148               "to 'this with' list",
 149               colocation->id, colocation->dependent->id,
 150               colocation->primary->id,
 151               (colocation->node_attribute == NULL)? "" : " using ",
 152               pcmk__s(colocation->node_attribute, ""),
 153               colocation->score);
 154     *list = g_list_insert_sorted(*list, (gpointer) colocation,
 155                                  cmp_primary_priority);
 156 }
 157 
 158 /*!
 159  * \internal
 160  * \brief Add a list of "this with" colocation constraints to a list
 161  *
 162  * \param[in,out] list      List of constraints to add \p addition to
 163  * \param[in]     addition  List of colocation constraints to add to \p list
 164  *
 165  * \note The lists must be pre-sorted by cmp_primary_priority().
 166  */
 167 void
 168 pcmk__add_this_with_list(GList **list, GList *addition)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170     CRM_CHECK((list != NULL), return);
 171 
 172     if (*list == NULL) { // Trivial case for efficiency
 173         crm_trace("Copying %u 'this with' colocations to new list",
 174                   g_list_length(addition));
 175         *list = g_list_copy(addition);
 176     } else {
 177         while (addition != NULL) {
 178             pcmk__add_this_with(list, addition->data);
 179             addition = addition->next;
 180         }
 181     }
 182 }
 183 
 184 /*!
 185  * \internal
 186  * \brief Add a "with this" colocation constraint to a sorted list
 187  *
 188  * \param[in,out] list        List of constraints to add \p colocation to
 189  * \param[in]     colocation  Colocation constraint to add to \p list
 190  *
 191  * \note The list will be sorted using cmp_dependent_priority().
 192  */
 193 void
 194 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation)
     /* [previous][next][first][last][top][bottom][index][help] */
 195 {
 196     CRM_ASSERT((list != NULL) && (colocation != NULL));
 197 
 198     crm_trace("Adding colocation %s (%s with %s%s%s @%d) "
 199               "to 'with this' list",
 200               colocation->id, colocation->dependent->id,
 201               colocation->primary->id,
 202               (colocation->node_attribute == NULL)? "" : " using ",
 203               pcmk__s(colocation->node_attribute, ""),
 204               colocation->score);
 205     *list = g_list_insert_sorted(*list, (gpointer) colocation,
 206                                  cmp_dependent_priority);
 207 }
 208 
 209 /*!
 210  * \internal
 211  * \brief Add a list of "with this" colocation constraints to a list
 212  *
 213  * \param[in,out] list      List of constraints to add \p addition to
 214  * \param[in]     addition  List of colocation constraints to add to \p list
 215  *
 216  * \note The lists must be pre-sorted by cmp_dependent_priority().
 217  */
 218 void
 219 pcmk__add_with_this_list(GList **list, GList *addition)
     /* [previous][next][first][last][top][bottom][index][help] */
 220 {
 221     CRM_CHECK((list != NULL), return);
 222 
 223     if (*list == NULL) { // Trivial case for efficiency
 224         crm_trace("Copying %u 'with this' colocations to new list",
 225                   g_list_length(addition));
 226         *list = g_list_copy(addition);
 227     } else {
 228         while (addition != NULL) {
 229             pcmk__add_with_this(list, addition->data);
 230             addition = addition->next;
 231         }
 232     }
 233 }
 234 
 235 /*!
 236  * \internal
 237  * \brief Add orderings necessary for an anti-colocation constraint
 238  *
 239  * \param[in,out] first_rsc   One resource in an anti-colocation
 240  * \param[in]     first_role  Anti-colocation role of \p first_rsc
 241  * \param[in]     then_rsc    Other resource in the anti-colocation
 242  * \param[in]     then_role   Anti-colocation role of \p then_rsc
 243  */
 244 static void
 245 anti_colocation_order(pe_resource_t *first_rsc, int first_role,
     /* [previous][next][first][last][top][bottom][index][help] */
 246                       pe_resource_t *then_rsc, int then_role)
 247 {
 248     const char *first_tasks[] = { NULL, NULL };
 249     const char *then_tasks[] = { NULL, NULL };
 250 
 251     /* Actions to make first_rsc lose first_role */
 252     if (first_role == RSC_ROLE_PROMOTED) {
 253         first_tasks[0] = CRMD_ACTION_DEMOTE;
 254 
 255     } else {
 256         first_tasks[0] = CRMD_ACTION_STOP;
 257 
 258         if (first_role == RSC_ROLE_UNPROMOTED) {
 259             first_tasks[1] = CRMD_ACTION_PROMOTE;
 260         }
 261     }
 262 
 263     /* Actions to make then_rsc gain then_role */
 264     if (then_role == RSC_ROLE_PROMOTED) {
 265         then_tasks[0] = CRMD_ACTION_PROMOTE;
 266 
 267     } else {
 268         then_tasks[0] = CRMD_ACTION_START;
 269 
 270         if (then_role == RSC_ROLE_UNPROMOTED) {
 271             then_tasks[1] = CRMD_ACTION_DEMOTE;
 272         }
 273     }
 274 
 275     for (int first_lpc = 0;
 276          (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
 277 
 278         for (int then_lpc = 0;
 279              (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
 280 
 281             pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
 282                                          then_rsc, then_tasks[then_lpc],
 283                                          pe_order_anti_colocation);
 284         }
 285     }
 286 }
 287 
 288 /*!
 289  * \internal
 290  * \brief Add a new colocation constraint to a cluster working set
 291  *
 292  * \param[in]     id              XML ID for this constraint
 293  * \param[in]     node_attr       Colocate by this attribute (NULL for #uname)
 294  * \param[in]     score           Constraint score
 295  * \param[in,out] dependent       Resource to be colocated
 296  * \param[in,out] primary         Resource to colocate \p dependent with
 297  * \param[in]     dependent_role  Current role of \p dependent
 298  * \param[in]     primary_role    Current role of \p primary
 299  * \param[in]     influence       Whether colocation constraint has influence
 300  * \param[in,out] data_set        Cluster working set to add constraint to
 301  */
 302 void
 303 pcmk__new_colocation(const char *id, const char *node_attr, int score,
     /* [previous][next][first][last][top][bottom][index][help] */
 304                      pe_resource_t *dependent, pe_resource_t *primary,
 305                      const char *dependent_role, const char *primary_role,
 306                      bool influence, pe_working_set_t *data_set)
 307 {
 308     pcmk__colocation_t *new_con = NULL;
 309 
 310     if (score == 0) {
 311         crm_trace("Ignoring colocation '%s' because score is 0", id);
 312         return;
 313     }
 314     if ((dependent == NULL) || (primary == NULL)) {
 315         pcmk__config_err("Ignoring colocation '%s' because resource "
 316                          "does not exist", id);
 317         return;
 318     }
 319 
 320     new_con = calloc(1, sizeof(pcmk__colocation_t));
 321     if (new_con == NULL) {
 322         return;
 323     }
 324 
 325     if (pcmk__str_eq(dependent_role, RSC_ROLE_STARTED_S,
 326                      pcmk__str_null_matches|pcmk__str_casei)) {
 327         dependent_role = RSC_ROLE_UNKNOWN_S;
 328     }
 329 
 330     if (pcmk__str_eq(primary_role, RSC_ROLE_STARTED_S,
 331                      pcmk__str_null_matches|pcmk__str_casei)) {
 332         primary_role = RSC_ROLE_UNKNOWN_S;
 333     }
 334 
 335     new_con->id = id;
 336     new_con->dependent = dependent;
 337     new_con->primary = primary;
 338     new_con->score = score;
 339     new_con->dependent_role = text2role(dependent_role);
 340     new_con->primary_role = text2role(primary_role);
 341     new_con->node_attribute = node_attr;
 342     new_con->influence = influence;
 343 
 344     if (node_attr == NULL) {
 345         node_attr = CRM_ATTR_UNAME;
 346     }
 347 
 348     pe_rsc_trace(dependent, "%s ==> %s (%s %d)",
 349                  dependent->id, primary->id, node_attr, score);
 350 
 351     pcmk__add_this_with(&(dependent->rsc_cons), new_con);
 352     pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con);
 353 
 354     data_set->colocation_constraints = g_list_append(data_set->colocation_constraints,
 355                                                      new_con);
 356 
 357     if (score <= -INFINITY) {
 358         anti_colocation_order(dependent, new_con->dependent_role, primary,
 359                               new_con->primary_role);
 360         anti_colocation_order(primary, new_con->primary_role, dependent,
 361                               new_con->dependent_role);
 362     }
 363 }
 364 
 365 /*!
 366  * \internal
 367  * \brief Return the boolean influence corresponding to configuration
 368  *
 369  * \param[in] coloc_id     Colocation XML ID (for error logging)
 370  * \param[in] rsc          Resource involved in constraint (for default)
 371  * \param[in] influence_s  String value of influence option
 372  *
 373  * \return true if string evaluates true, false if string evaluates false,
 374  *         or value of resource's critical option if string is NULL or invalid
 375  */
 376 static bool
 377 unpack_influence(const char *coloc_id, const pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 378                  const char *influence_s)
 379 {
 380     if (influence_s != NULL) {
 381         int influence_i = 0;
 382 
 383         if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
 384             pcmk__config_err("Constraint '%s' has invalid value for "
 385                              XML_COLOC_ATTR_INFLUENCE " (using default)",
 386                              coloc_id);
 387         } else {
 388             return (influence_i != 0);
 389         }
 390     }
 391     return pcmk_is_set(rsc->flags, pe_rsc_critical);
 392 }
 393 
 394 static void
 395 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 396                       const char *influence_s, pe_working_set_t *data_set)
 397 {
 398     xmlNode *xml_rsc = NULL;
 399     pe_resource_t *with = NULL;
 400     pe_resource_t *resource = NULL;
 401     const char *set_id = ID(set);
 402     const char *role = crm_element_value(set, "role");
 403     const char *ordering = crm_element_value(set, "ordering");
 404     int local_score = score;
 405     bool sequential = false;
 406 
 407     const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
 408 
 409     if (score_s) {
 410         local_score = char2score(score_s);
 411     }
 412     if (local_score == 0) {
 413         crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
 414                   coloc_id, set_id);
 415         return;
 416     }
 417 
 418     if (ordering == NULL) {
 419         ordering = "group";
 420     }
 421 
 422     if (pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok && !sequential) {
 423         return;
 424 
 425     } else if ((local_score > 0)
 426                && pcmk__str_eq(ordering, "group", pcmk__str_casei)) {
 427         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 428              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 429 
 430             EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
 431             if (with != NULL) {
 432                 pe_rsc_trace(resource, "Colocating %s with %s", resource->id, with->id);
 433                 pcmk__new_colocation(set_id, NULL, local_score, resource,
 434                                      with, role, role,
 435                                      unpack_influence(coloc_id, resource,
 436                                                       influence_s), data_set);
 437             }
 438             with = resource;
 439         }
 440 
 441     } else if (local_score > 0) {
 442         pe_resource_t *last = NULL;
 443 
 444         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 445              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 446 
 447             EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
 448             if (last != NULL) {
 449                 pe_rsc_trace(resource, "Colocating %s with %s",
 450                              last->id, resource->id);
 451                 pcmk__new_colocation(set_id, NULL, local_score, last,
 452                                      resource, role, role,
 453                                      unpack_influence(coloc_id, last,
 454                                                       influence_s), data_set);
 455             }
 456 
 457             last = resource;
 458         }
 459 
 460     } else {
 461         /* Anti-colocating with every prior resource is
 462          * the only way to ensure the intuitive result
 463          * (i.e. that no one in the set can run with anyone else in the set)
 464          */
 465 
 466         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 467              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 468 
 469             xmlNode *xml_rsc_with = NULL;
 470             bool influence = true;
 471 
 472             EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
 473             influence = unpack_influence(coloc_id, resource, influence_s);
 474 
 475             for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
 476                  xml_rsc_with != NULL;
 477                  xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
 478 
 479                 if (pcmk__str_eq(resource->id, ID(xml_rsc_with),
 480                                  pcmk__str_casei)) {
 481                     break;
 482                 }
 483                 EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
 484                 pe_rsc_trace(resource, "Anti-Colocating %s with %s", resource->id,
 485                              with->id);
 486                 pcmk__new_colocation(set_id, NULL, local_score,
 487                                      resource, with, role, role,
 488                                      influence, data_set);
 489             }
 490         }
 491     }
 492 }
 493 
 494 static void
 495 colocate_rsc_sets(const char *id, xmlNode *set1, xmlNode *set2, int score,
     /* [previous][next][first][last][top][bottom][index][help] */
 496                   const char *influence_s, pe_working_set_t *data_set)
 497 {
 498     xmlNode *xml_rsc = NULL;
 499     pe_resource_t *rsc_1 = NULL;
 500     pe_resource_t *rsc_2 = NULL;
 501 
 502     const char *role_1 = crm_element_value(set1, "role");
 503     const char *role_2 = crm_element_value(set2, "role");
 504 
 505     int rc = pcmk_rc_ok;
 506     bool sequential = false;
 507 
 508     if (score == 0) {
 509         crm_trace("Ignoring colocation '%s' between sets because score is 0",
 510                   id);
 511         return;
 512     }
 513 
 514     rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential);
 515     if (rc != pcmk_rc_ok || sequential) {
 516         // Get the first one
 517         xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 518         if (xml_rsc != NULL) {
 519             EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
 520         }
 521     }
 522 
 523     rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential);
 524     if (rc != pcmk_rc_ok || sequential) {
 525         // Get the last one
 526         const char *rid = NULL;
 527 
 528         for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
 529              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 530 
 531             rid = ID(xml_rsc);
 532         }
 533         EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
 534     }
 535 
 536     if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
 537         pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
 538                              unpack_influence(id, rsc_1, influence_s),
 539                              data_set);
 540 
 541     } else if (rsc_1 != NULL) {
 542         bool influence = unpack_influence(id, rsc_1, influence_s);
 543 
 544         for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
 545              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 546 
 547             EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
 548             pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
 549                                  role_2, influence, data_set);
 550         }
 551 
 552     } else if (rsc_2 != NULL) {
 553         for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 554              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 555 
 556             EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
 557             pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
 558                                  role_2,
 559                                  unpack_influence(id, rsc_1, influence_s),
 560                                  data_set);
 561         }
 562 
 563     } else {
 564         for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 565              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 566 
 567             xmlNode *xml_rsc_2 = NULL;
 568             bool influence = true;
 569 
 570             EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
 571             influence = unpack_influence(id, rsc_1, influence_s);
 572 
 573             for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
 574                  xml_rsc_2 != NULL;
 575                  xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
 576 
 577                 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
 578                 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
 579                                      role_1, role_2, influence,
 580                                      data_set);
 581             }
 582         }
 583     }
 584 }
 585 
 586 static void
 587 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 588                          const char *influence_s, pe_working_set_t *data_set)
 589 {
 590     int score_i = 0;
 591 
 592     const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 593     const char *dependent_id = crm_element_value(xml_obj,
 594                                                  XML_COLOC_ATTR_SOURCE);
 595     const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
 596     const char *dependent_role = crm_element_value(xml_obj,
 597                                                    XML_COLOC_ATTR_SOURCE_ROLE);
 598     const char *primary_role = crm_element_value(xml_obj,
 599                                                  XML_COLOC_ATTR_TARGET_ROLE);
 600     const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
 601 
 602     // @COMPAT: Deprecated since 2.1.5
 603     const char *dependent_instance = crm_element_value(xml_obj,
 604                                                        XML_COLOC_ATTR_SOURCE_INSTANCE);
 605     // @COMPAT: Deprecated since 2.1.5
 606     const char *primary_instance = crm_element_value(xml_obj,
 607                                                      XML_COLOC_ATTR_TARGET_INSTANCE);
 608 
 609     pe_resource_t *dependent = pcmk__find_constraint_resource(data_set->resources,
 610                                                               dependent_id);
 611     pe_resource_t *primary = pcmk__find_constraint_resource(data_set->resources,
 612                                                             primary_id);
 613 
 614     if (dependent_instance != NULL) {
 615         pe_warn_once(pe_wo_coloc_inst,
 616                      "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
 617                      "deprecated and will be removed in a future release.");
 618     }
 619 
 620     if (primary_instance != NULL) {
 621         pe_warn_once(pe_wo_coloc_inst,
 622                      "Support for " XML_COLOC_ATTR_TARGET_INSTANCE " is "
 623                      "deprecated and will be removed in a future release.");
 624     }
 625 
 626     if (dependent == NULL) {
 627         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 628                          "does not exist", id, dependent_id);
 629         return;
 630 
 631     } else if (primary == NULL) {
 632         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 633                          "does not exist", id, primary_id);
 634         return;
 635 
 636     } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
 637         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 638                          "is not a clone but instance '%s' was requested",
 639                          id, dependent_id, dependent_instance);
 640         return;
 641 
 642     } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
 643         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 644                          "is not a clone but instance '%s' was requested",
 645                          id, primary_id, primary_instance);
 646         return;
 647     }
 648 
 649     if (dependent_instance != NULL) {
 650         dependent = find_clone_instance(dependent, dependent_instance);
 651         if (dependent == NULL) {
 652             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 653                               "does not have an instance '%s'",
 654                               id, dependent_id, dependent_instance);
 655             return;
 656         }
 657     }
 658 
 659     if (primary_instance != NULL) {
 660         primary = find_clone_instance(primary, primary_instance);
 661         if (primary == NULL) {
 662             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 663                               "does not have an instance '%s'",
 664                               "'%s'", id, primary_id, primary_instance);
 665             return;
 666         }
 667     }
 668 
 669     if (pcmk__xe_attr_is_true(xml_obj, XML_CONS_ATTR_SYMMETRICAL)) {
 670         pcmk__config_warn("The colocation constraint '"
 671                           XML_CONS_ATTR_SYMMETRICAL
 672                           "' attribute has been removed");
 673     }
 674 
 675     if (score) {
 676         score_i = char2score(score);
 677     }
 678 
 679     pcmk__new_colocation(id, attr, score_i, dependent, primary,
 680                          dependent_role, primary_role,
 681                          unpack_influence(id, dependent, influence_s), data_set);
 682 }
 683 
 684 // \return Standard Pacemaker return code
 685 static int
 686 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 687                        pe_working_set_t *data_set)
 688 {
 689     const char *id = NULL;
 690     const char *dependent_id = NULL;
 691     const char *primary_id = NULL;
 692     const char *dependent_role = NULL;
 693     const char *primary_role = NULL;
 694 
 695     pe_resource_t *dependent = NULL;
 696     pe_resource_t *primary = NULL;
 697 
 698     pe_tag_t *dependent_tag = NULL;
 699     pe_tag_t *primary_tag = NULL;
 700 
 701     xmlNode *dependent_set = NULL;
 702     xmlNode *primary_set = NULL;
 703     bool any_sets = false;
 704 
 705     *expanded_xml = NULL;
 706 
 707     CRM_CHECK(xml_obj != NULL, return EINVAL);
 708 
 709     id = ID(xml_obj);
 710     if (id == NULL) {
 711         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 712                          crm_element_name(xml_obj));
 713         return pcmk_rc_unpack_error;
 714     }
 715 
 716     // Check whether there are any resource sets with template or tag references
 717     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
 718     if (*expanded_xml != NULL) {
 719         crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
 720         return pcmk_rc_ok;
 721     }
 722 
 723     dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
 724     primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
 725     if ((dependent_id == NULL) || (primary_id == NULL)) {
 726         return pcmk_rc_ok;
 727     }
 728 
 729     if (!pcmk__valid_resource_or_tag(data_set, dependent_id, &dependent,
 730                                      &dependent_tag)) {
 731         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 732                          "valid resource or tag", id, dependent_id);
 733         return pcmk_rc_unpack_error;
 734     }
 735 
 736     if (!pcmk__valid_resource_or_tag(data_set, primary_id, &primary,
 737                                      &primary_tag)) {
 738         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 739                          "valid resource or tag", id, primary_id);
 740         return pcmk_rc_unpack_error;
 741     }
 742 
 743     if ((dependent != NULL) && (primary != NULL)) {
 744         /* Neither side references any template/tag. */
 745         return pcmk_rc_ok;
 746     }
 747 
 748     if ((dependent_tag != NULL) && (primary_tag != NULL)) {
 749         // A colocation constraint between two templates/tags makes no sense
 750         pcmk__config_err("Ignoring constraint '%s' because two templates or "
 751                          "tags cannot be colocated", id);
 752         return pcmk_rc_unpack_error;
 753     }
 754 
 755     dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
 756     primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
 757 
 758     *expanded_xml = copy_xml(xml_obj);
 759 
 760     // Convert template/tag reference in "rsc" into resource_set under constraint
 761     if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
 762                           true, data_set)) {
 763         free_xml(*expanded_xml);
 764         *expanded_xml = NULL;
 765         return pcmk_rc_unpack_error;
 766     }
 767 
 768     if (dependent_set != NULL) {
 769         if (dependent_role != NULL) {
 770             // Move "rsc-role" into converted resource_set as "role"
 771             crm_xml_add(dependent_set, "role", dependent_role);
 772             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
 773         }
 774         any_sets = true;
 775     }
 776 
 777     // Convert template/tag reference in "with-rsc" into resource_set under constraint
 778     if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
 779                           true, data_set)) {
 780         free_xml(*expanded_xml);
 781         *expanded_xml = NULL;
 782         return pcmk_rc_unpack_error;
 783     }
 784 
 785     if (primary_set != NULL) {
 786         if (primary_role != NULL) {
 787             // Move "with-rsc-role" into converted resource_set as "role"
 788             crm_xml_add(primary_set, "role", primary_role);
 789             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_TARGET_ROLE);
 790         }
 791         any_sets = true;
 792     }
 793 
 794     if (any_sets) {
 795         crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
 796     } else {
 797         free_xml(*expanded_xml);
 798         *expanded_xml = NULL;
 799     }
 800 
 801     return pcmk_rc_ok;
 802 }
 803 
 804 /*!
 805  * \internal
 806  * \brief Parse a colocation constraint from XML into a cluster working set
 807  *
 808  * \param[in,out] xml_obj   Colocation constraint XML to unpack
 809  * \param[in,out] data_set  Cluster working set to add constraint to
 810  */
 811 void
 812 pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 813 {
 814     int score_i = 0;
 815     xmlNode *set = NULL;
 816     xmlNode *last = NULL;
 817 
 818     xmlNode *orig_xml = NULL;
 819     xmlNode *expanded_xml = NULL;
 820 
 821     const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 822     const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 823     const char *influence_s = crm_element_value(xml_obj,
 824                                                 XML_COLOC_ATTR_INFLUENCE);
 825 
 826     if (score) {
 827         score_i = char2score(score);
 828     }
 829 
 830     if (unpack_colocation_tags(xml_obj, &expanded_xml,
 831                                data_set) != pcmk_rc_ok) {
 832         return;
 833     }
 834     if (expanded_xml) {
 835         orig_xml = xml_obj;
 836         xml_obj = expanded_xml;
 837     }
 838 
 839     for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
 840          set = crm_next_same_xml(set)) {
 841 
 842         set = expand_idref(set, data_set->input);
 843         if (set == NULL) { // Configuration error, message already logged
 844             if (expanded_xml != NULL) {
 845                 free_xml(expanded_xml);
 846             }
 847             return;
 848         }
 849 
 850         unpack_colocation_set(set, score_i, id, influence_s, data_set);
 851 
 852         if (last != NULL) {
 853             colocate_rsc_sets(id, last, set, score_i, influence_s, data_set);
 854         }
 855         last = set;
 856     }
 857 
 858     if (expanded_xml) {
 859         free_xml(expanded_xml);
 860         xml_obj = orig_xml;
 861     }
 862 
 863     if (last == NULL) {
 864         unpack_simple_colocation(xml_obj, id, influence_s, data_set);
 865     }
 866 }
 867 
 868 /*!
 869  * \internal
 870  * \brief Make actions of a given type unrunnable for a given resource
 871  *
 872  * \param[in,out] rsc     Resource whose actions should be blocked
 873  * \param[in]     task    Name of action to block
 874  * \param[in]     reason  Unrunnable start action causing the block
 875  */
 876 static void
 877 mark_action_blocked(pe_resource_t *rsc, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
 878                     const pe_resource_t *reason)
 879 {
 880     char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
 881 
 882     for (GList *gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
 883         pe_action_t *action = (pe_action_t *) gIter->data;
 884 
 885         if (pcmk_is_set(action->flags, pe_action_runnable)
 886             && pcmk__str_eq(action->task, task, pcmk__str_casei)) {
 887 
 888             pe__clear_action_flags(action, pe_action_runnable);
 889             pe_action_set_reason(action, reason_text, false);
 890             pcmk__block_colocation_dependents(action, rsc->cluster);
 891             pcmk__update_action_for_orderings(action, rsc->cluster);
 892         }
 893     }
 894 
 895     // If parent resource can't perform an action, neither can any children
 896     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 897         mark_action_blocked((pe_resource_t *) (iter->data), task, reason);
 898     }
 899     free(reason_text);
 900 }
 901 
 902 /*!
 903  * \internal
 904  * \brief If an action is unrunnable, block any relevant dependent actions
 905  *
 906  * If a given action is an unrunnable start or promote, block the start or
 907  * promote actions of resources colocated with it, as appropriate to the
 908  * colocations' configured roles.
 909  *
 910  * \param[in,out] action    Action to check
 911  * \param[in]     data_set  Cluster working set (ignored)
 912  */
 913 void
 914 pcmk__block_colocation_dependents(pe_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 915                                   pe_working_set_t *data_set)
 916 {
 917     GList *gIter = NULL;
 918     GList *colocations = NULL;
 919     pe_resource_t *rsc = NULL;
 920     bool is_start = false;
 921 
 922     if (pcmk_is_set(action->flags, pe_action_runnable)) {
 923         return; // Only unrunnable actions block dependents
 924     }
 925 
 926     is_start = pcmk__str_eq(action->task, RSC_START, pcmk__str_none);
 927     if (!is_start && !pcmk__str_eq(action->task, RSC_PROMOTE, pcmk__str_none)) {
 928         return; // Only unrunnable starts and promotes block dependents
 929     }
 930 
 931     CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions
 932 
 933     /* If this resource is part of a collective resource, dependents are blocked
 934      * only if all instances of the collective are unrunnable, so check the
 935      * collective resource.
 936      */
 937     rsc = uber_parent(action->rsc);
 938     if (rsc->parent != NULL) {
 939         rsc = rsc->parent; // Bundle
 940     }
 941 
 942     // Colocation fails only if entire primary can't reach desired role
 943     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 944         pe_resource_t *child = (pe_resource_t *) gIter->data;
 945         pe_action_t *child_action = find_first_action(child->actions, NULL,
 946                                                       action->task, NULL);
 947 
 948         if ((child_action == NULL)
 949             || pcmk_is_set(child_action->flags, pe_action_runnable)) {
 950             crm_trace("Not blocking %s colocation dependents because "
 951                       "at least %s has runnable %s",
 952                       rsc->id, child->id, action->task);
 953             return; // At least one child can reach desired role
 954         }
 955     }
 956 
 957     crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
 958               rsc->id, action->rsc->id, action->task);
 959 
 960     // Check each colocation where this resource is primary
 961     colocations = pcmk__with_this_colocations(rsc);
 962     for (gIter = colocations; gIter != NULL; gIter = gIter->next) {
 963         pcmk__colocation_t *colocation = (pcmk__colocation_t *) gIter->data;
 964 
 965         if (colocation->score < INFINITY) {
 966             continue; // Only mandatory colocations block dependent
 967         }
 968 
 969         /* If the primary can't start, the dependent can't reach its colocated
 970          * role, regardless of what the primary or dependent colocation role is.
 971          *
 972          * If the primary can't be promoted, the dependent can't reach its
 973          * colocated role if the primary's colocation role is promoted.
 974          */
 975         if (!is_start && (colocation->primary_role != RSC_ROLE_PROMOTED)) {
 976             continue;
 977         }
 978 
 979         // Block the dependent from reaching its colocated role
 980         if (colocation->dependent_role == RSC_ROLE_PROMOTED) {
 981             mark_action_blocked(colocation->dependent, RSC_PROMOTE,
 982                                 action->rsc);
 983         } else {
 984             mark_action_blocked(colocation->dependent, RSC_START, action->rsc);
 985         }
 986     }
 987     g_list_free(colocations);
 988 }
 989 
 990 /*!
 991  * \internal
 992  * \brief Determine how a colocation constraint should affect a resource
 993  *
 994  * Colocation constraints have different effects at different points in the
 995  * scheduler sequence. Initially, they affect a resource's location; once that
 996  * is determined, then for promotable clones they can affect a resource
 997  * instance's role; after both are determined, the constraints no longer matter.
 998  * Given a specific colocation constraint, check what has been done so far to
 999  * determine what should be affected at the current point in the scheduler.
1000  *
1001  * \param[in] dependent   Dependent resource in colocation
1002  * \param[in] primary     Primary resource in colocation
1003  * \param[in] colocation  Colocation constraint
1004  * \param[in] preview     If true, pretend resources have already been allocated
1005  *
1006  * \return How colocation constraint should be applied at this point
1007  */
1008 enum pcmk__coloc_affects
1009 pcmk__colocation_affects(const pe_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1010                          const pe_resource_t *primary,
1011                          const pcmk__colocation_t *colocation, bool preview)
1012 {
1013     if (!preview && pcmk_is_set(primary->flags, pe_rsc_provisional)) {
1014         // Primary resource has not been allocated yet, so we can't do anything
1015         return pcmk__coloc_affects_nothing;
1016     }
1017 
1018     if ((colocation->dependent_role >= RSC_ROLE_UNPROMOTED)
1019         && (dependent->parent != NULL)
1020         && pcmk_is_set(dependent->parent->flags, pe_rsc_promotable)
1021         && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
1022 
1023         /* This is a colocation by role, and the dependent is a promotable clone
1024          * that has already been allocated, so the colocation should now affect
1025          * the role.
1026          */
1027         return pcmk__coloc_affects_role;
1028     }
1029 
1030     if (!preview && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
1031         /* The dependent resource has already been through allocation, so the
1032          * constraint no longer has any effect. Log an error if a mandatory
1033          * colocation constraint has been violated.
1034          */
1035 
1036         const pe_node_t *primary_node = primary->allocated_to;
1037 
1038         if (dependent->allocated_to == NULL) {
1039             crm_trace("Skipping colocation '%s': %s will not run anywhere",
1040                       colocation->id, dependent->id);
1041 
1042         } else if (colocation->score >= INFINITY) {
1043             // Dependent resource must colocate with primary resource
1044 
1045             if ((primary_node == NULL) ||
1046                 (primary_node->details != dependent->allocated_to->details)) {
1047                 crm_err("%s must be colocated with %s but is not (%s vs. %s)",
1048                         dependent->id, primary->id,
1049                         pe__node_name(dependent->allocated_to),
1050                         pe__node_name(primary_node));
1051             }
1052 
1053         } else if (colocation->score <= -CRM_SCORE_INFINITY) {
1054             // Dependent resource must anti-colocate with primary resource
1055 
1056             if ((primary_node != NULL) &&
1057                 (dependent->allocated_to->details == primary_node->details)) {
1058                 crm_err("%s and %s must be anti-colocated but are allocated "
1059                         "to the same node (%s)",
1060                         dependent->id, primary->id, pe__node_name(primary_node));
1061             }
1062         }
1063         return pcmk__coloc_affects_nothing;
1064     }
1065 
1066     if ((colocation->score > 0)
1067         && (colocation->dependent_role != RSC_ROLE_UNKNOWN)
1068         && (colocation->dependent_role != dependent->next_role)) {
1069 
1070         crm_trace("Skipping colocation '%s': dependent limited to %s role "
1071                   "but %s next role is %s",
1072                   colocation->id, role2text(colocation->dependent_role),
1073                   dependent->id, role2text(dependent->next_role));
1074         return pcmk__coloc_affects_nothing;
1075     }
1076 
1077     if ((colocation->score > 0)
1078         && (colocation->primary_role != RSC_ROLE_UNKNOWN)
1079         && (colocation->primary_role != primary->next_role)) {
1080 
1081         crm_trace("Skipping colocation '%s': primary limited to %s role "
1082                   "but %s next role is %s",
1083                   colocation->id, role2text(colocation->primary_role),
1084                   primary->id, role2text(primary->next_role));
1085         return pcmk__coloc_affects_nothing;
1086     }
1087 
1088     if ((colocation->score < 0)
1089         && (colocation->dependent_role != RSC_ROLE_UNKNOWN)
1090         && (colocation->dependent_role == dependent->next_role)) {
1091         crm_trace("Skipping anti-colocation '%s': dependent role %s matches",
1092                   colocation->id, role2text(colocation->dependent_role));
1093         return pcmk__coloc_affects_nothing;
1094     }
1095 
1096     if ((colocation->score < 0)
1097         && (colocation->primary_role != RSC_ROLE_UNKNOWN)
1098         && (colocation->primary_role == primary->next_role)) {
1099         crm_trace("Skipping anti-colocation '%s': primary role %s matches",
1100                   colocation->id, role2text(colocation->primary_role));
1101         return pcmk__coloc_affects_nothing;
1102     }
1103 
1104     return pcmk__coloc_affects_location;
1105 }
1106 
1107 /*!
1108  * \internal
1109  * \brief Apply colocation to dependent for allocation purposes
1110  *
1111  * Update the allowed node weights of the dependent resource in a colocation,
1112  * for the purposes of allocating it to a node
1113  *
1114  * \param[in,out] dependent   Dependent resource in colocation
1115  * \param[in]     primary     Primary resource in colocation
1116  * \param[in]     colocation  Colocation constraint
1117  */
1118 void
1119 pcmk__apply_coloc_to_weights(pe_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1120                              const pe_resource_t *primary,
1121                              const pcmk__colocation_t *colocation)
1122 {
1123     const char *attribute = CRM_ATTR_ID;
1124     const char *value = NULL;
1125     GHashTable *work = NULL;
1126     GHashTableIter iter;
1127     pe_node_t *node = NULL;
1128 
1129     if (colocation->node_attribute != NULL) {
1130         attribute = colocation->node_attribute;
1131     }
1132 
1133     if (primary->allocated_to != NULL) {
1134         value = pe_node_attribute_raw(primary->allocated_to, attribute);
1135 
1136     } else if (colocation->score < 0) {
1137         // Nothing to do (anti-colocation with something that is not running)
1138         return;
1139     }
1140 
1141     work = pcmk__copy_node_table(dependent->allowed_nodes);
1142 
1143     g_hash_table_iter_init(&iter, work);
1144     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1145         if (primary->allocated_to == NULL) {
1146             node->weight = pcmk__add_scores(-colocation->score, node->weight);
1147             pe_rsc_trace(dependent,
1148                          "Applied %s to %s score on %s (now %s after "
1149                          "subtracting %s because primary %s inactive)",
1150                          colocation->id, dependent->id, pe__node_name(node),
1151                          pcmk_readable_score(node->weight),
1152                          pcmk_readable_score(colocation->score), primary->id);
1153 
1154         } else if (pcmk__str_eq(pe_node_attribute_raw(node, attribute), value,
1155                                 pcmk__str_casei)) {
1156             /* Add colocation score only if optional (or minus infinity). A
1157              * mandatory colocation is a requirement rather than a preference,
1158              * so we don't need to consider it for relative assignment purposes.
1159              * The resource will simply be forbidden from running on the node if
1160              * the primary isn't active there (via the condition above).
1161              */
1162             if (colocation->score < CRM_SCORE_INFINITY) {
1163                 node->weight = pcmk__add_scores(colocation->score,
1164                                                 node->weight);
1165                 pe_rsc_trace(dependent,
1166                              "Applied %s to %s score on %s (now %s after "
1167                              "adding %s)",
1168                              colocation->id, dependent->id, pe__node_name(node),
1169                              pcmk_readable_score(node->weight),
1170                              pcmk_readable_score(colocation->score));
1171             }
1172 
1173         } else if (colocation->score >= CRM_SCORE_INFINITY) {
1174             /* Only mandatory colocations are relevant when the colocation
1175              * attribute doesn't match, because an attribute not matching is not
1176              * a negative preference -- the colocation is simply relevant only
1177              * where it matches.
1178              */
1179             node->weight = -CRM_SCORE_INFINITY;
1180             pe_rsc_trace(dependent,
1181                          "Banned %s from %s because colocation %s attribute %s "
1182                          "does not match",
1183                          dependent->id, pe__node_name(node), colocation->id,
1184                          attribute);
1185         }
1186     }
1187 
1188     if ((colocation->score <= -INFINITY) || (colocation->score >= INFINITY)
1189         || pcmk__any_node_available(work)) {
1190 
1191         g_hash_table_destroy(dependent->allowed_nodes);
1192         dependent->allowed_nodes = work;
1193         work = NULL;
1194 
1195     } else {
1196         pe_rsc_info(dependent,
1197                     "%s: Rolling back scores from %s (no available nodes)",
1198                     dependent->id, primary->id);
1199     }
1200 
1201     if (work != NULL) {
1202         g_hash_table_destroy(work);
1203     }
1204 }
1205 
1206 /*!
1207  * \internal
1208  * \brief Apply colocation to dependent for role purposes
1209  *
1210  * Update the priority of the dependent resource in a colocation, for the
1211  * purposes of selecting its role
1212  *
1213  * \param[in,out] dependent   Dependent resource in colocation
1214  * \param[in]     primary     Primary resource in colocation
1215  * \param[in]     colocation  Colocation constraint
1216  */
1217 void
1218 pcmk__apply_coloc_to_priority(pe_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1219                               const pe_resource_t *primary,
1220                               const pcmk__colocation_t *colocation)
1221 {
1222     const char *dependent_value = NULL;
1223     const char *primary_value = NULL;
1224     const char *attribute = CRM_ATTR_ID;
1225     int score_multiplier = 1;
1226 
1227     if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1228         return;
1229     }
1230 
1231     if (colocation->node_attribute != NULL) {
1232         attribute = colocation->node_attribute;
1233     }
1234 
1235     dependent_value = pe_node_attribute_raw(dependent->allocated_to, attribute);
1236     primary_value = pe_node_attribute_raw(primary->allocated_to, attribute);
1237 
1238     if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1239         if ((colocation->score == INFINITY)
1240             && (colocation->dependent_role == RSC_ROLE_PROMOTED)) {
1241             dependent->priority = -INFINITY;
1242         }
1243         return;
1244     }
1245 
1246     if ((colocation->primary_role != RSC_ROLE_UNKNOWN)
1247         && (colocation->primary_role != primary->next_role)) {
1248         return;
1249     }
1250 
1251     if (colocation->dependent_role == RSC_ROLE_UNPROMOTED) {
1252         score_multiplier = -1;
1253     }
1254 
1255     dependent->priority = pcmk__add_scores(score_multiplier * colocation->score,
1256                                            dependent->priority);
1257     pe_rsc_trace(dependent,
1258                  "Applied %s to %s promotion priority (now %s after %s %s)",
1259                  colocation->id, dependent->id,
1260                  pcmk_readable_score(dependent->priority),
1261                  ((score_multiplier == 1)? "adding" : "subtracting"),
1262                  pcmk_readable_score(colocation->score));
1263 }
1264 
1265 /*!
1266  * \internal
1267  * \brief Find score of highest-scored node that matches colocation attribute
1268  *
1269  * \param[in] rsc    Resource whose allowed nodes should be searched
1270  * \param[in] attr   Colocation attribute name (must not be NULL)
1271  * \param[in] value  Colocation attribute value to require
1272  */
1273 static int
1274 best_node_score_matching_attr(const pe_resource_t *rsc, const char *attr,
     /* [previous][next][first][last][top][bottom][index][help] */
1275                               const char *value)
1276 {
1277     GHashTableIter iter;
1278     pe_node_t *node = NULL;
1279     int best_score = -INFINITY;
1280     const char *best_node = NULL;
1281 
1282     // Find best allowed node with matching attribute
1283     g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1284     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1285 
1286         if ((node->weight > best_score) && pcmk__node_available(node, false, false)
1287             && pcmk__str_eq(value, pe_node_attribute_raw(node, attr), pcmk__str_casei)) {
1288 
1289             best_score = node->weight;
1290             best_node = node->details->uname;
1291         }
1292     }
1293 
1294     if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_casei)) {
1295         if (best_node == NULL) {
1296             crm_info("No allowed node for %s matches node attribute %s=%s",
1297                      rsc->id, attr, value);
1298         } else {
1299             crm_info("Allowed node %s for %s had best score (%d) "
1300                      "of those matching node attribute %s=%s",
1301                      best_node, rsc->id, best_score, attr, value);
1302         }
1303     }
1304     return best_score;
1305 }
1306 
1307 /*!
1308  * \internal
1309  * \brief Add resource's colocation matches to current node allocation scores
1310  *
1311  * For each node in a given table, if any of a given resource's allowed nodes
1312  * have a matching value for the colocation attribute, add the highest of those
1313  * nodes' scores to the node's score.
1314  *
1315  * \param[in,out] nodes  Hash table of nodes with allocation scores so far
1316  * \param[in]     rsc    Resource whose allowed nodes should be compared
1317  * \param[in]     attr   Colocation attribute that must match (NULL for default)
1318  * \param[in]     factor Factor by which to multiply scores being added
1319  * \param[in]     only_positive  Whether to add only positive scores
1320  */
1321 static void
1322 add_node_scores_matching_attr(GHashTable *nodes, const pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1323                               const char *attr, float factor,
1324                               bool only_positive)
1325 {
1326     GHashTableIter iter;
1327     pe_node_t *node = NULL;
1328 
1329     if (attr == NULL) {
1330         attr = CRM_ATTR_UNAME;
1331     }
1332 
1333     // Iterate through each node
1334     g_hash_table_iter_init(&iter, nodes);
1335     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1336         float weight_f = 0;
1337         int weight = 0;
1338         int score = 0;
1339         int new_score = 0;
1340 
1341         score = best_node_score_matching_attr(rsc, attr,
1342                                               pe_node_attribute_raw(node, attr));
1343 
1344         if ((factor < 0) && (score < 0)) {
1345             /* Negative preference for a node with a negative score
1346              * should not become a positive preference.
1347              *
1348              * @TODO Consider filtering only if weight is -INFINITY
1349              */
1350             crm_trace("%s: Filtering %d + %f * %d (double negative disallowed)",
1351                       pe__node_name(node), node->weight, factor, score);
1352             continue;
1353         }
1354 
1355         if (node->weight == INFINITY_HACK) {
1356             crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1357                       pe__node_name(node), node->weight, factor, score);
1358             continue;
1359         }
1360 
1361         weight_f = factor * score;
1362 
1363         // Round the number; see http://c-faq.com/fp/round.html
1364         weight = (int) ((weight_f < 0)? (weight_f - 0.5) : (weight_f + 0.5));
1365 
1366         /* Small factors can obliterate the small scores that are often actually
1367          * used in configurations. If the score and factor are nonzero, ensure
1368          * that the result is nonzero as well.
1369          */
1370         if ((weight == 0) && (score != 0)) {
1371             if (factor > 0.0) {
1372                 weight = 1;
1373             } else if (factor < 0.0) {
1374                 weight = -1;
1375             }
1376         }
1377 
1378         new_score = pcmk__add_scores(weight, node->weight);
1379 
1380         if (only_positive && (new_score < 0) && (node->weight > 0)) {
1381             crm_trace("%s: Filtering %d + %f * %d = %d "
1382                       "(negative disallowed, marking node unusable)",
1383                       pe__node_name(node), node->weight, factor, score,
1384                       new_score);
1385             node->weight = INFINITY_HACK;
1386             continue;
1387         }
1388 
1389         if (only_positive && (new_score < 0) && (node->weight == 0)) {
1390             crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1391                       pe__node_name(node), node->weight, factor, score,
1392                       new_score);
1393             continue;
1394         }
1395 
1396         crm_trace("%s: %d + %f * %d = %d", pe__node_name(node),
1397                   node->weight, factor, score, new_score);
1398         node->weight = new_score;
1399     }
1400 }
1401 
1402 /*!
1403  * \internal
1404  * \brief Update nodes with scores of colocated resources' nodes
1405  *
1406  * Given a table of nodes and a resource, update the nodes' scores with the
1407  * scores of the best nodes matching the attribute used for each of the
1408  * resource's relevant colocations.
1409  *
1410  * \param[in,out] rsc      Resource to check colocations for
1411  * \param[in]     log_id   Resource ID to use in logs (if NULL, use \p rsc ID)
1412  * \param[in,out] nodes    Nodes to update
1413  * \param[in]     attr     Colocation attribute (NULL to use default)
1414  * \param[in]     factor   Incorporate scores multiplied by this factor
1415  * \param[in]     flags    Bitmask of enum pcmk__coloc_select values
1416  *
1417  * \note The caller remains responsible for freeing \p *nodes.
1418  */
1419 void
1420 pcmk__add_colocated_node_scores(pe_resource_t *rsc, const char *log_id,
     /* [previous][next][first][last][top][bottom][index][help] */
1421                                 GHashTable **nodes, const char *attr,
1422                                 float factor, uint32_t flags)
1423 {
1424     GHashTable *work = NULL;
1425 
1426     CRM_CHECK((rsc != NULL) && (nodes != NULL), return);
1427 
1428     if (log_id == NULL) {
1429         log_id = rsc->id;
1430     }
1431 
1432     // Avoid infinite recursion
1433     if (pcmk_is_set(rsc->flags, pe_rsc_merging)) {
1434         pe_rsc_info(rsc, "%s: Breaking dependency loop at %s",
1435                     log_id, rsc->id);
1436         return;
1437     }
1438     pe__set_resource_flags(rsc, pe_rsc_merging);
1439 
1440     if (*nodes == NULL) {
1441         /* Only cmp_resources() passes a NULL nodes table, which indicates we
1442          * should initialize it with the resource's allowed node scores.
1443          */
1444         work = pcmk__copy_node_table(rsc->allowed_nodes);
1445     } else {
1446         pe_rsc_trace(rsc, "%s: Merging scores from %s (at %.6f)",
1447                      log_id, rsc->id, factor);
1448         work = pcmk__copy_node_table(*nodes);
1449         add_node_scores_matching_attr(work, rsc, attr, factor,
1450                                       pcmk_is_set(flags,
1451                                                   pcmk__coloc_select_nonnegative));
1452     }
1453 
1454     if (work == NULL) {
1455         pe__clear_resource_flags(rsc, pe_rsc_merging);
1456         return;
1457     }
1458 
1459     if (pcmk__any_node_available(work)) {
1460         GList *colocations = NULL;
1461 
1462         if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1463             colocations = pcmk__this_with_colocations(rsc);
1464             pe_rsc_trace(rsc,
1465                          "Checking additional %d optional '%s with' constraints",
1466                          g_list_length(colocations), rsc->id);
1467         } else {
1468             colocations = pcmk__with_this_colocations(rsc);
1469             pe_rsc_trace(rsc,
1470                          "Checking additional %d optional 'with %s' constraints",
1471                          g_list_length(colocations), rsc->id);
1472         }
1473         flags |= pcmk__coloc_select_active;
1474 
1475         for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1476             pcmk__colocation_t *constraint = (pcmk__colocation_t *) iter->data;
1477 
1478             pe_resource_t *other = NULL;
1479             float other_factor = factor * constraint->score / (float) INFINITY;
1480 
1481             if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1482                 other = constraint->primary;
1483             } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1484                 continue;
1485             } else {
1486                 other = constraint->dependent;
1487             }
1488 
1489             pe_rsc_trace(rsc, "Optionally merging score of '%s' constraint (%s with %s)",
1490                          constraint->id, constraint->dependent->id,
1491                          constraint->primary->id);
1492             other->cmds->add_colocated_node_scores(other, log_id, &work,
1493                                                    constraint->node_attribute,
1494                                                    other_factor, flags);
1495             pe__show_node_weights(true, NULL, log_id, work, rsc->cluster);
1496         }
1497         g_list_free(colocations);
1498 
1499     } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) {
1500         pe_rsc_info(rsc, "%s: Rolling back optional scores from %s",
1501                     log_id, rsc->id);
1502         g_hash_table_destroy(work);
1503         pe__clear_resource_flags(rsc, pe_rsc_merging);
1504         return;
1505     }
1506 
1507 
1508     if (pcmk_is_set(flags, pcmk__coloc_select_nonnegative)) {
1509         pe_node_t *node = NULL;
1510         GHashTableIter iter;
1511 
1512         g_hash_table_iter_init(&iter, work);
1513         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1514             if (node->weight == INFINITY_HACK) {
1515                 node->weight = 1;
1516             }
1517         }
1518     }
1519 
1520     if (*nodes != NULL) {
1521        g_hash_table_destroy(*nodes);
1522     }
1523     *nodes = work;
1524 
1525     pe__clear_resource_flags(rsc, pe_rsc_merging);
1526 }
1527 
1528 /*!
1529  * \internal
1530  * \brief Apply a "with this" colocation to a resource's allowed node scores
1531  *
1532  * \param[in,out] data       Colocation to apply
1533  * \param[in,out] user_data  Resource being assigned
1534  */
1535 void
1536 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1537 {
1538     pcmk__colocation_t *colocation = (pcmk__colocation_t *) data;
1539     pe_resource_t *rsc = (pe_resource_t *) user_data;
1540 
1541     pe_resource_t *other = colocation->dependent;
1542     const float factor = colocation->score / (float) INFINITY;
1543     uint32_t flags = pcmk__coloc_select_active;
1544 
1545     if (!pcmk__colocation_has_influence(colocation, NULL)) {
1546         return;
1547     }
1548     if (rsc->variant == pe_clone) {
1549         flags |= pcmk__coloc_select_nonnegative;
1550     }
1551     pe_rsc_trace(rsc,
1552                  "%s: Incorporating attenuated %s assignment scores due "
1553                  "to colocation %s", rsc->id, other->id, colocation->id);
1554     other->cmds->add_colocated_node_scores(other, rsc->id, &rsc->allowed_nodes,
1555                                            colocation->node_attribute, factor,
1556                                            flags);
1557 }
1558 
1559 /*!
1560  * \internal
1561  * \brief Get all colocations affecting a resource as the primary
1562  *
1563  * \param[in] rsc  Resource to get colocations for
1564  *
1565  * \return Newly allocated list of colocations affecting \p rsc as primary
1566  *
1567  * \note This is a convenience wrapper for the with_this_colocations() method.
1568  */
1569 GList *
1570 pcmk__with_this_colocations(const pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1571 {
1572     GList *list = NULL;
1573 
1574     rsc->cmds->with_this_colocations(rsc, rsc, &list);
1575     return list;
1576 }
1577 
1578 /*!
1579  * \internal
1580  * \brief Get all colocations affecting a resource as the dependent
1581  *
1582  * \param[in] rsc  Resource to get colocations for
1583  *
1584  * \return Newly allocated list of colocations affecting \p rsc as dependent
1585  *
1586  * \note This is a convenience wrapper for the this_with_colocations() method.
1587  */
1588 GList *
1589 pcmk__this_with_colocations(const pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1590 {
1591     GList *list = NULL;
1592 
1593     rsc->cmds->this_with_colocations(rsc, rsc, &list);
1594     return list;
1595 }

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