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_with_this
  5. anti_colocation_order
  6. pcmk__new_colocation
  7. unpack_influence
  8. unpack_colocation_set
  9. colocate_rsc_sets
  10. unpack_simple_colocation
  11. unpack_colocation_tags
  12. pcmk__unpack_colocation
  13. mark_action_blocked
  14. pcmk__block_colocation_dependents
  15. pcmk__colocation_affects
  16. pcmk__apply_coloc_to_weights
  17. pcmk__apply_coloc_to_priority
  18. best_node_score_matching_attr
  19. add_node_scores_matching_attr
  20. init_group_colocated_nodes
  21. init_nongroup_colocated_nodes
  22. pcmk__add_colocated_node_scores

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

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