root/lib/pacemaker/pcmk_sched_colocation.c

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

DEFINITIONS

This source file includes following definitions.
  1. cmp_colocation_priority
  2. cmp_dependent_priority
  3. cmp_primary_priority
  4. pcmk__add_this_with
  5. pcmk__add_this_with_list
  6. pcmk__add_with_this
  7. pcmk__add_with_this_list
  8. anti_colocation_order
  9. pcmk__new_colocation
  10. unpack_influence
  11. unpack_colocation_set
  12. colocate_rsc_sets
  13. unpack_simple_colocation
  14. unpack_colocation_tags
  15. pcmk__unpack_colocation
  16. mark_action_blocked
  17. pcmk__block_colocation_dependents
  18. get_resource_for_role
  19. pcmk__colocation_affects
  20. pcmk__apply_coloc_to_scores
  21. pcmk__apply_coloc_to_priority
  22. best_node_score_matching_attr
  23. allowed_on_one
  24. add_node_scores_matching_attr
  25. pcmk__add_colocated_node_scores
  26. pcmk__add_dependent_scores
  27. pcmk__colocation_intersect_nodes
  28. pcmk__with_this_colocations
  29. 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/common/scheduler_internal.h>
  17 #include <crm/pengine/status.h>
  18 #include <pacemaker-internal.h>
  19 
  20 #include "crm/common/util.h"
  21 #include "crm/common/xml_internal.h"
  22 #include "crm/msg_xml.h"
  23 #include "libpacemaker_private.h"
  24 
  25 // Used to temporarily mark a node as unusable
  26 #define INFINITY_HACK   (INFINITY * -100)
  27 
  28 /*!
  29  * \internal
  30  * \brief Compare two colocations according to priority
  31  *
  32  * Compare two colocations according to the order in which they should be
  33  * considered, based on either their dependent resources or their primary
  34  * resources -- preferring (in order):
  35  *  * Colocation that is not \c NULL
  36  *  * Colocation whose resource has higher priority
  37  *  * Colocation whose resource is of a higher-level variant
  38  *    (bundle > clone > group > primitive)
  39  *  * Colocation whose resource is promotable, if both are clones
  40  *  * Colocation whose resource has lower ID in lexicographic order
  41  *
  42  * \param[in] colocation1  First colocation to compare
  43  * \param[in] colocation2  Second colocation to compare
  44  * \param[in] dependent    If \c true, compare colocations by dependent
  45  *                         priority; otherwise compare them by primary priority
  46  *
  47  * \return A negative number if \p colocation1 should be considered first,
  48  *         a positive number if \p colocation2 should be considered first,
  49  *         or 0 if order doesn't matter
  50  */
  51 static gint
  52 cmp_colocation_priority(const pcmk__colocation_t *colocation1,
     /* [previous][next][first][last][top][bottom][index][help] */
  53                         const pcmk__colocation_t *colocation2, bool dependent)
  54 {
  55     const pcmk_resource_t *rsc1 = NULL;
  56     const pcmk_resource_t *rsc2 = NULL;
  57 
  58     if (colocation1 == NULL) {
  59         return 1;
  60     }
  61     if (colocation2 == NULL) {
  62         return -1;
  63     }
  64 
  65     if (dependent) {
  66         rsc1 = colocation1->dependent;
  67         rsc2 = colocation2->dependent;
  68         CRM_ASSERT(colocation1->primary != NULL);
  69     } else {
  70         rsc1 = colocation1->primary;
  71         rsc2 = colocation2->primary;
  72         CRM_ASSERT(colocation1->dependent != NULL);
  73     }
  74     CRM_ASSERT((rsc1 != NULL) && (rsc2 != NULL));
  75 
  76     if (rsc1->priority > rsc2->priority) {
  77         return -1;
  78     }
  79     if (rsc1->priority < rsc2->priority) {
  80         return 1;
  81     }
  82 
  83     // Process clones before primitives and groups
  84     if (rsc1->variant > rsc2->variant) {
  85         return -1;
  86     }
  87     if (rsc1->variant < rsc2->variant) {
  88         return 1;
  89     }
  90 
  91     /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
  92      * clones (probably unnecessary, but avoids having to update regression
  93      * tests)
  94      */
  95     if (rsc1->variant == pcmk_rsc_variant_clone) {
  96         if (pcmk_is_set(rsc1->flags, pcmk_rsc_promotable)
  97             && !pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) {
  98             return -1;
  99         }
 100         if (!pcmk_is_set(rsc1->flags, pcmk_rsc_promotable)
 101             && pcmk_is_set(rsc2->flags, pcmk_rsc_promotable)) {
 102             return 1;
 103         }
 104     }
 105 
 106     return strcmp(rsc1->id, rsc2->id);
 107 }
 108 
 109 /*!
 110  * \internal
 111  * \brief Compare two colocations according to priority based on dependents
 112  *
 113  * Compare two colocations according to the order in which they should be
 114  * considered, based on their dependent resources -- preferring (in order):
 115  *  * Colocation that is not \c NULL
 116  *  * Colocation whose resource has higher priority
 117  *  * Colocation whose resource is of a higher-level variant
 118  *    (bundle > clone > group > primitive)
 119  *  * Colocation whose resource is promotable, if both are clones
 120  *  * Colocation whose resource has lower ID in lexicographic order
 121  *
 122  * \param[in] a  First colocation to compare
 123  * \param[in] b  Second colocation to compare
 124  *
 125  * \return A negative number if \p a should be considered first,
 126  *         a positive number if \p b should be considered first,
 127  *         or 0 if order doesn't matter
 128  */
 129 static gint
 130 cmp_dependent_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132     return cmp_colocation_priority(a, b, true);
 133 }
 134 
 135 /*!
 136  * \internal
 137  * \brief Compare two colocations according to priority based on primaries
 138  *
 139  * Compare two colocations according to the order in which they should be
 140  * considered, based on their primary resources -- preferring (in order):
 141  *  * Colocation that is not \c NULL
 142  *  * Colocation whose primary has higher priority
 143  *  * Colocation whose primary is of a higher-level variant
 144  *    (bundle > clone > group > primitive)
 145  *  * Colocation whose primary is promotable, if both are clones
 146  *  * Colocation whose primary has lower ID in lexicographic order
 147  *
 148  * \param[in] a  First colocation to compare
 149  * \param[in] b  Second colocation to compare
 150  *
 151  * \return A negative number if \p a should be considered first,
 152  *         a positive number if \p b should be considered first,
 153  *         or 0 if order doesn't matter
 154  */
 155 static gint
 156 cmp_primary_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158     return cmp_colocation_priority(a, b, false);
 159 }
 160 
 161 /*!
 162  * \internal
 163  * \brief Add a "this with" colocation constraint to a sorted list
 164  *
 165  * \param[in,out] list        List of constraints to add \p colocation to
 166  * \param[in]     colocation  Colocation constraint to add to \p list
 167  * \param[in]     rsc         Resource whose colocations we're getting (for
 168  *                            logging only)
 169  *
 170  * \note The list will be sorted using cmp_primary_priority().
 171  */
 172 void
 173 pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation,
     /* [previous][next][first][last][top][bottom][index][help] */
 174                     const pcmk_resource_t *rsc)
 175 {
 176     CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
 177 
 178     pe_rsc_trace(rsc,
 179                  "Adding colocation %s (%s with %s using %s @%s) to "
 180                  "'this with' list for %s",
 181                  colocation->id, colocation->dependent->id,
 182                  colocation->primary->id, colocation->node_attribute,
 183                  pcmk_readable_score(colocation->score), rsc->id);
 184     *list = g_list_insert_sorted(*list, (gpointer) colocation,
 185                                  cmp_primary_priority);
 186 }
 187 
 188 /*!
 189  * \internal
 190  * \brief Add a list of "this with" colocation constraints to a list
 191  *
 192  * \param[in,out] list      List of constraints to add \p addition to
 193  * \param[in]     addition  List of colocation constraints to add to \p list
 194  * \param[in]     rsc       Resource whose colocations we're getting (for
 195  *                          logging only)
 196  *
 197  * \note The lists must be pre-sorted by cmp_primary_priority().
 198  */
 199 void
 200 pcmk__add_this_with_list(GList **list, GList *addition,
     /* [previous][next][first][last][top][bottom][index][help] */
 201                          const pcmk_resource_t *rsc)
 202 {
 203     CRM_ASSERT((list != NULL) && (rsc != NULL));
 204 
 205     pcmk__if_tracing(
 206         {}, // Always add each colocation individually if tracing
 207         {
 208             if (*list == NULL) {
 209                 // Trivial case for efficiency if not tracing
 210                 *list = g_list_copy(addition);
 211                 return;
 212             }
 213         }
 214     );
 215 
 216     for (const GList *iter = addition; iter != NULL; iter = iter->next) {
 217         pcmk__add_this_with(list, addition->data, rsc);
 218     }
 219 }
 220 
 221 /*!
 222  * \internal
 223  * \brief Add a "with this" colocation constraint to a sorted list
 224  *
 225  * \param[in,out] list        List of constraints to add \p colocation to
 226  * \param[in]     colocation  Colocation constraint to add to \p list
 227  * \param[in]     rsc         Resource whose colocations we're getting (for
 228  *                            logging only)
 229  *
 230  * \note The list will be sorted using cmp_dependent_priority().
 231  */
 232 void
 233 pcmk__add_with_this(GList **list, const pcmk__colocation_t *colocation,
     /* [previous][next][first][last][top][bottom][index][help] */
 234                     const pcmk_resource_t *rsc)
 235 {
 236     CRM_ASSERT((list != NULL) && (colocation != NULL) && (rsc != NULL));
 237 
 238     pe_rsc_trace(rsc,
 239                  "Adding colocation %s (%s with %s using %s @%s) to "
 240                  "'with this' list for %s",
 241                  colocation->id, colocation->dependent->id,
 242                  colocation->primary->id, colocation->node_attribute,
 243                  pcmk_readable_score(colocation->score), rsc->id);
 244     *list = g_list_insert_sorted(*list, (gpointer) colocation,
 245                                  cmp_dependent_priority);
 246 }
 247 
 248 /*!
 249  * \internal
 250  * \brief Add a list of "with this" colocation constraints to a list
 251  *
 252  * \param[in,out] list      List of constraints to add \p addition to
 253  * \param[in]     addition  List of colocation constraints to add to \p list
 254  * \param[in]     rsc       Resource whose colocations we're getting (for
 255  *                          logging only)
 256  *
 257  * \note The lists must be pre-sorted by cmp_dependent_priority().
 258  */
 259 void
 260 pcmk__add_with_this_list(GList **list, GList *addition,
     /* [previous][next][first][last][top][bottom][index][help] */
 261                          const pcmk_resource_t *rsc)
 262 {
 263     CRM_ASSERT((list != NULL) && (rsc != NULL));
 264 
 265     pcmk__if_tracing(
 266         {}, // Always add each colocation individually if tracing
 267         {
 268             if (*list == NULL) {
 269                 // Trivial case for efficiency if not tracing
 270                 *list = g_list_copy(addition);
 271                 return;
 272             }
 273         }
 274     );
 275 
 276     for (const GList *iter = addition; iter != NULL; iter = iter->next) {
 277         pcmk__add_with_this(list, addition->data, rsc);
 278     }
 279 }
 280 
 281 /*!
 282  * \internal
 283  * \brief Add orderings necessary for an anti-colocation constraint
 284  *
 285  * \param[in,out] first_rsc   One resource in an anti-colocation
 286  * \param[in]     first_role  Anti-colocation role of \p first_rsc
 287  * \param[in]     then_rsc    Other resource in the anti-colocation
 288  * \param[in]     then_role   Anti-colocation role of \p then_rsc
 289  */
 290 static void
 291 anti_colocation_order(pcmk_resource_t *first_rsc, int first_role,
     /* [previous][next][first][last][top][bottom][index][help] */
 292                       pcmk_resource_t *then_rsc, int then_role)
 293 {
 294     const char *first_tasks[] = { NULL, NULL };
 295     const char *then_tasks[] = { NULL, NULL };
 296 
 297     /* Actions to make first_rsc lose first_role */
 298     if (first_role == pcmk_role_promoted) {
 299         first_tasks[0] = PCMK_ACTION_DEMOTE;
 300 
 301     } else {
 302         first_tasks[0] = PCMK_ACTION_STOP;
 303 
 304         if (first_role == pcmk_role_unpromoted) {
 305             first_tasks[1] = PCMK_ACTION_PROMOTE;
 306         }
 307     }
 308 
 309     /* Actions to make then_rsc gain then_role */
 310     if (then_role == pcmk_role_promoted) {
 311         then_tasks[0] = PCMK_ACTION_PROMOTE;
 312 
 313     } else {
 314         then_tasks[0] = PCMK_ACTION_START;
 315 
 316         if (then_role == pcmk_role_unpromoted) {
 317             then_tasks[1] = PCMK_ACTION_DEMOTE;
 318         }
 319     }
 320 
 321     for (int first_lpc = 0;
 322          (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
 323 
 324         for (int then_lpc = 0;
 325              (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
 326 
 327             pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
 328                                          then_rsc, then_tasks[then_lpc],
 329                                          pcmk__ar_if_required_on_same_node);
 330         }
 331     }
 332 }
 333 
 334 /*!
 335  * \internal
 336  * \brief Add a new colocation constraint to scheduler data
 337  *
 338  * \param[in]     id              XML ID for this constraint
 339  * \param[in]     node_attr       Colocate by this attribute (NULL for #uname)
 340  * \param[in]     score           Constraint score
 341  * \param[in,out] dependent       Resource to be colocated
 342  * \param[in,out] primary         Resource to colocate \p dependent with
 343  * \param[in]     dependent_role  Current role of \p dependent
 344  * \param[in]     primary_role    Current role of \p primary
 345  * \param[in]     flags           Group of enum pcmk__coloc_flags
 346  */
 347 void
 348 pcmk__new_colocation(const char *id, const char *node_attr, int score,
     /* [previous][next][first][last][top][bottom][index][help] */
 349                      pcmk_resource_t *dependent, pcmk_resource_t *primary,
 350                      const char *dependent_role, const char *primary_role,
 351                      uint32_t flags)
 352 {
 353     pcmk__colocation_t *new_con = NULL;
 354 
 355     CRM_CHECK(id != NULL, return);
 356 
 357     if ((dependent == NULL) || (primary == NULL)) {
 358         pcmk__config_err("Ignoring colocation '%s' because resource "
 359                          "does not exist", id);
 360         return;
 361     }
 362 
 363     if (score == 0) {
 364         pe_rsc_trace(dependent,
 365                      "Ignoring colocation '%s' (%s with %s) because score is 0",
 366                      id, dependent->id, primary->id);
 367         return;
 368     }
 369 
 370     new_con = calloc(1, sizeof(pcmk__colocation_t));
 371     CRM_ASSERT(new_con != NULL);
 372 
 373     if (pcmk__str_eq(dependent_role, PCMK__ROLE_STARTED,
 374                      pcmk__str_null_matches|pcmk__str_casei)) {
 375         dependent_role = PCMK__ROLE_UNKNOWN;
 376     }
 377 
 378     if (pcmk__str_eq(primary_role, PCMK__ROLE_STARTED,
 379                      pcmk__str_null_matches|pcmk__str_casei)) {
 380         primary_role = PCMK__ROLE_UNKNOWN;
 381     }
 382 
 383     new_con->id = id;
 384     new_con->dependent = dependent;
 385     new_con->primary = primary;
 386     new_con->score = score;
 387     new_con->dependent_role = text2role(dependent_role);
 388     new_con->primary_role = text2role(primary_role);
 389     new_con->node_attribute = pcmk__s(node_attr, CRM_ATTR_UNAME);
 390     new_con->flags = flags;
 391 
 392     pcmk__add_this_with(&(dependent->rsc_cons), new_con, dependent);
 393     pcmk__add_with_this(&(primary->rsc_cons_lhs), new_con, primary);
 394 
 395     dependent->cluster->colocation_constraints = g_list_prepend(
 396         dependent->cluster->colocation_constraints, new_con);
 397 
 398     if (score <= -INFINITY) {
 399         anti_colocation_order(dependent, new_con->dependent_role, primary,
 400                               new_con->primary_role);
 401         anti_colocation_order(primary, new_con->primary_role, dependent,
 402                               new_con->dependent_role);
 403     }
 404 }
 405 
 406 /*!
 407  * \internal
 408  * \brief Return the boolean influence corresponding to configuration
 409  *
 410  * \param[in] coloc_id     Colocation XML ID (for error logging)
 411  * \param[in] rsc          Resource involved in constraint (for default)
 412  * \param[in] influence_s  String value of influence option
 413  *
 414  * \return pcmk__coloc_influence if string evaluates true, or string is NULL or
 415  *         invalid and resource's critical option evaluates true, otherwise
 416  *         pcmk__coloc_none
 417  */
 418 static uint32_t
 419 unpack_influence(const char *coloc_id, const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 420                  const char *influence_s)
 421 {
 422     if (influence_s != NULL) {
 423         int influence_i = 0;
 424 
 425         if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
 426             pcmk__config_err("Constraint '%s' has invalid value for "
 427                              XML_COLOC_ATTR_INFLUENCE " (using default)",
 428                              coloc_id);
 429         } else {
 430             return (influence_i == 0)? pcmk__coloc_none : pcmk__coloc_influence;
 431         }
 432     }
 433     if (pcmk_is_set(rsc->flags, pcmk_rsc_critical)) {
 434         return pcmk__coloc_influence;
 435     }
 436     return pcmk__coloc_none;
 437 }
 438 
 439 static void
 440 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 441                       const char *influence_s, pcmk_scheduler_t *scheduler)
 442 {
 443     xmlNode *xml_rsc = NULL;
 444     pcmk_resource_t *other = NULL;
 445     pcmk_resource_t *resource = NULL;
 446     const char *set_id = ID(set);
 447     const char *role = crm_element_value(set, "role");
 448     bool with_previous = false;
 449     int local_score = score;
 450     bool sequential = false;
 451     uint32_t flags = pcmk__coloc_none;
 452     const char *xml_rsc_id = NULL;
 453     const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
 454 
 455     if (score_s) {
 456         local_score = char2score(score_s);
 457     }
 458     if (local_score == 0) {
 459         crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
 460                   coloc_id, set_id);
 461         return;
 462     }
 463 
 464     /* @COMPAT The deprecated "ordering" attribute specifies whether resources
 465      * in a positive-score set are colocated with the previous or next resource.
 466      */
 467     if (pcmk__str_eq(crm_element_value(set, "ordering"), "group",
 468                      pcmk__str_null_matches|pcmk__str_casei)) {
 469         with_previous = true;
 470     } else {
 471         pe_warn_once(pcmk__wo_set_ordering,
 472                      "Support for 'ordering' other than 'group' in "
 473                      XML_CONS_TAG_RSC_SET " (such as %s) is deprecated and "
 474                      "will be removed in a future release", set_id);
 475     }
 476 
 477     if ((pcmk__xe_get_bool_attr(set, "sequential", &sequential) == pcmk_rc_ok)
 478         && !sequential) {
 479         return;
 480     }
 481 
 482     if (local_score > 0) {
 483         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 484              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 485 
 486             xml_rsc_id = ID(xml_rsc);
 487             resource = pcmk__find_constraint_resource(scheduler->resources,
 488                                                       xml_rsc_id);
 489             if (resource == NULL) {
 490                 // Should be possible only with validation disabled
 491                 pcmk__config_err("Ignoring %s and later resources in set %s: "
 492                                  "No such resource", xml_rsc_id, set_id);
 493                 return;
 494             }
 495             if (other != NULL) {
 496                 flags = pcmk__coloc_explicit
 497                         | unpack_influence(coloc_id, resource, influence_s);
 498                 if (with_previous) {
 499                     pe_rsc_trace(resource, "Colocating %s with %s in set %s",
 500                                  resource->id, other->id, set_id);
 501                     pcmk__new_colocation(set_id, NULL, local_score, resource,
 502                                          other, role, role, flags);
 503                 } else {
 504                     pe_rsc_trace(resource, "Colocating %s with %s in set %s",
 505                                  other->id, resource->id, set_id);
 506                     pcmk__new_colocation(set_id, NULL, local_score, other,
 507                                          resource, role, role, flags);
 508                 }
 509             }
 510             other = resource;
 511         }
 512 
 513     } else {
 514         /* Anti-colocating with every prior resource is
 515          * the only way to ensure the intuitive result
 516          * (i.e. that no one in the set can run with anyone else in the set)
 517          */
 518 
 519         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 520              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 521 
 522             xmlNode *xml_rsc_with = NULL;
 523 
 524             xml_rsc_id = ID(xml_rsc);
 525             resource = pcmk__find_constraint_resource(scheduler->resources,
 526                                                       xml_rsc_id);
 527             if (resource == NULL) {
 528                 // Should be possible only with validation disabled
 529                 pcmk__config_err("Ignoring %s and later resources in set %s: "
 530                                  "No such resource", xml_rsc_id, set_id);
 531                 return;
 532             }
 533             flags = pcmk__coloc_explicit
 534                     | unpack_influence(coloc_id, resource, influence_s);
 535             for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
 536                  xml_rsc_with != NULL;
 537                  xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
 538 
 539                 xml_rsc_id = ID(xml_rsc_with);
 540                 if (pcmk__str_eq(resource->id, xml_rsc_id, pcmk__str_none)) {
 541                     break;
 542                 }
 543                 other = pcmk__find_constraint_resource(scheduler->resources,
 544                                                        xml_rsc_id);
 545                 CRM_ASSERT(other != NULL); // We already processed it
 546                 pcmk__new_colocation(set_id, NULL, local_score,
 547                                      resource, other, role, role, flags);
 548             }
 549         }
 550     }
 551 }
 552 
 553 /*!
 554  * \internal
 555  * \brief Colocate two resource sets relative to each other
 556  *
 557  * \param[in]     id           Colocation XML ID
 558  * \param[in]     set1         Dependent set
 559  * \param[in]     set2         Primary set
 560  * \param[in]     score        Colocation score
 561  * \param[in]     influence_s  Value of colocation's "influence" attribute
 562  * \param[in,out] scheduler    Scheduler data
 563  */
 564 static void
 565 colocate_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
     /* [previous][next][first][last][top][bottom][index][help] */
 566                   int score, const char *influence_s,
 567                   pcmk_scheduler_t *scheduler)
 568 {
 569     xmlNode *xml_rsc = NULL;
 570     pcmk_resource_t *rsc_1 = NULL;
 571     pcmk_resource_t *rsc_2 = NULL;
 572 
 573     const char *xml_rsc_id = NULL;
 574     const char *role_1 = crm_element_value(set1, "role");
 575     const char *role_2 = crm_element_value(set2, "role");
 576 
 577     int rc = pcmk_rc_ok;
 578     bool sequential = false;
 579     uint32_t flags = pcmk__coloc_none;
 580 
 581     if (score == 0) {
 582         crm_trace("Ignoring colocation '%s' between sets %s and %s "
 583                   "because score is 0", id, ID(set1), ID(set2));
 584         return;
 585     }
 586 
 587     rc = pcmk__xe_get_bool_attr(set1, "sequential", &sequential);
 588     if ((rc != pcmk_rc_ok) || sequential) {
 589         // Get the first one
 590         xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 591         if (xml_rsc != NULL) {
 592             xml_rsc_id = ID(xml_rsc);
 593             rsc_1 = pcmk__find_constraint_resource(scheduler->resources,
 594                                                    xml_rsc_id);
 595             if (rsc_1 == NULL) {
 596                 // Should be possible only with validation disabled
 597                 pcmk__config_err("Ignoring colocation of set %s with set %s "
 598                                  "because first resource %s not found",
 599                                  ID(set1), ID(set2), xml_rsc_id);
 600                 return;
 601             }
 602         }
 603     }
 604 
 605     rc = pcmk__xe_get_bool_attr(set2, "sequential", &sequential);
 606     if ((rc != pcmk_rc_ok) || sequential) {
 607         // Get the last one
 608         for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
 609              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 610 
 611             xml_rsc_id = ID(xml_rsc);
 612         }
 613         rsc_2 = pcmk__find_constraint_resource(scheduler->resources,
 614                                                xml_rsc_id);
 615         if (rsc_2 == NULL) {
 616             // Should be possible only with validation disabled
 617             pcmk__config_err("Ignoring colocation of set %s with set %s "
 618                              "because last resource %s not found",
 619                              ID(set1), ID(set2), xml_rsc_id);
 620             return;
 621         }
 622     }
 623 
 624     if ((rsc_1 != NULL) && (rsc_2 != NULL)) { // Both sets are sequential
 625         flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
 626         pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
 627                              flags);
 628 
 629     } else if (rsc_1 != NULL) { // Only set1 is sequential
 630         flags = pcmk__coloc_explicit | unpack_influence(id, rsc_1, influence_s);
 631         for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
 632              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 633 
 634             xml_rsc_id = ID(xml_rsc);
 635             rsc_2 = pcmk__find_constraint_resource(scheduler->resources,
 636                                                    xml_rsc_id);
 637             if (rsc_2 == NULL) {
 638                 // Should be possible only with validation disabled
 639                 pcmk__config_err("Ignoring set %s colocation with resource %s "
 640                                  "in set %s: No such resource",
 641                                  ID(set1), xml_rsc_id, ID(set2));
 642                 continue;
 643             }
 644             pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
 645                                  role_2, flags);
 646         }
 647 
 648     } else if (rsc_2 != NULL) { // Only set2 is sequential
 649         for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 650              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 651 
 652             xml_rsc_id = ID(xml_rsc);
 653             rsc_1 = pcmk__find_constraint_resource(scheduler->resources,
 654                                                    xml_rsc_id);
 655             if (rsc_1 == NULL) {
 656                 // Should be possible only with validation disabled
 657                 pcmk__config_err("Ignoring colocation of set %s resource %s "
 658                                  "with set %s: No such resource",
 659                                  ID(set1), xml_rsc_id, ID(set2));
 660                 continue;
 661             }
 662             flags = pcmk__coloc_explicit
 663                     | unpack_influence(id, rsc_1, influence_s);
 664             pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
 665                                  role_2, flags);
 666         }
 667 
 668     } else { // Neither set is sequential
 669         for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 670              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 671 
 672             xmlNode *xml_rsc_2 = NULL;
 673 
 674             xml_rsc_id = ID(xml_rsc);
 675             rsc_1 = pcmk__find_constraint_resource(scheduler->resources,
 676                                                    xml_rsc_id);
 677             if (rsc_1 == NULL) {
 678                 // Should be possible only with validation disabled
 679                 pcmk__config_err("Ignoring colocation of set %s resource %s "
 680                                  "with set %s: No such resource",
 681                                  ID(set1), xml_rsc_id, ID(set2));
 682                 continue;
 683             }
 684 
 685             flags = pcmk__coloc_explicit
 686                     | unpack_influence(id, rsc_1, influence_s);
 687             for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
 688                  xml_rsc_2 != NULL;
 689                  xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
 690 
 691                 xml_rsc_id = ID(xml_rsc_2);
 692                 rsc_2 = pcmk__find_constraint_resource(scheduler->resources,
 693                                                        xml_rsc_id);
 694                 if (rsc_2 == NULL) {
 695                     // Should be possible only with validation disabled
 696                     pcmk__config_err("Ignoring colocation of set %s resource "
 697                                      "%s with set %s resource %s: No such "
 698                                      "resource", ID(set1), ID(xml_rsc),
 699                                      ID(set2), xml_rsc_id);
 700                     continue;
 701                 }
 702                 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
 703                                      role_1, role_2, flags);
 704             }
 705         }
 706     }
 707 }
 708 
 709 static void
 710 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 711                          const char *influence_s, pcmk_scheduler_t *scheduler)
 712 {
 713     int score_i = 0;
 714     uint32_t flags = pcmk__coloc_none;
 715 
 716     const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 717     const char *dependent_id = crm_element_value(xml_obj,
 718                                                  XML_COLOC_ATTR_SOURCE);
 719     const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
 720     const char *dependent_role = crm_element_value(xml_obj,
 721                                                    XML_COLOC_ATTR_SOURCE_ROLE);
 722     const char *primary_role = crm_element_value(xml_obj,
 723                                                  XML_COLOC_ATTR_TARGET_ROLE);
 724     const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
 725 
 726     const char *primary_instance = NULL;
 727     const char *dependent_instance = NULL;
 728     pcmk_resource_t *primary = NULL;
 729     pcmk_resource_t *dependent = NULL;
 730 
 731     primary = pcmk__find_constraint_resource(scheduler->resources, primary_id);
 732     dependent = pcmk__find_constraint_resource(scheduler->resources,
 733                                                dependent_id);
 734 
 735     // @COMPAT: Deprecated since 2.1.5
 736     primary_instance = crm_element_value(xml_obj,
 737                                          XML_COLOC_ATTR_TARGET_INSTANCE);
 738     dependent_instance = crm_element_value(xml_obj,
 739                                            XML_COLOC_ATTR_SOURCE_INSTANCE);
 740     if (dependent_instance != NULL) {
 741         pe_warn_once(pcmk__wo_coloc_inst,
 742                      "Support for " XML_COLOC_ATTR_SOURCE_INSTANCE " is "
 743                      "deprecated and will be removed in a future release.");
 744     }
 745     if (primary_instance != NULL) {
 746         pe_warn_once(pcmk__wo_coloc_inst,
 747                      "Support for " XML_COLOC_ATTR_TARGET_INSTANCE " is "
 748                      "deprecated and will be removed in a future release.");
 749     }
 750 
 751     if (dependent == NULL) {
 752         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 753                          "does not exist", id, dependent_id);
 754         return;
 755 
 756     } else if (primary == NULL) {
 757         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 758                          "does not exist", id, primary_id);
 759         return;
 760 
 761     } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
 762         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 763                          "is not a clone but instance '%s' was requested",
 764                          id, dependent_id, dependent_instance);
 765         return;
 766 
 767     } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
 768         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 769                          "is not a clone but instance '%s' was requested",
 770                          id, primary_id, primary_instance);
 771         return;
 772     }
 773 
 774     if (dependent_instance != NULL) {
 775         dependent = find_clone_instance(dependent, dependent_instance);
 776         if (dependent == NULL) {
 777             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 778                               "does not have an instance '%s'",
 779                               id, dependent_id, dependent_instance);
 780             return;
 781         }
 782     }
 783 
 784     if (primary_instance != NULL) {
 785         primary = find_clone_instance(primary, primary_instance);
 786         if (primary == NULL) {
 787             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 788                               "does not have an instance '%s'",
 789                               "'%s'", id, primary_id, primary_instance);
 790             return;
 791         }
 792     }
 793 
 794     if (pcmk__xe_attr_is_true(xml_obj, XML_CONS_ATTR_SYMMETRICAL)) {
 795         pcmk__config_warn("The colocation constraint '"
 796                           XML_CONS_ATTR_SYMMETRICAL
 797                           "' attribute has been removed");
 798     }
 799 
 800     if (score) {
 801         score_i = char2score(score);
 802     }
 803 
 804     flags = pcmk__coloc_explicit | unpack_influence(id, dependent, influence_s);
 805     pcmk__new_colocation(id, attr, score_i, dependent, primary,
 806                          dependent_role, primary_role, flags);
 807 }
 808 
 809 // \return Standard Pacemaker return code
 810 static int
 811 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 812                        pcmk_scheduler_t *scheduler)
 813 {
 814     const char *id = NULL;
 815     const char *dependent_id = NULL;
 816     const char *primary_id = NULL;
 817     const char *dependent_role = NULL;
 818     const char *primary_role = NULL;
 819 
 820     pcmk_resource_t *dependent = NULL;
 821     pcmk_resource_t *primary = NULL;
 822 
 823     pcmk_tag_t *dependent_tag = NULL;
 824     pcmk_tag_t *primary_tag = NULL;
 825 
 826     xmlNode *dependent_set = NULL;
 827     xmlNode *primary_set = NULL;
 828     bool any_sets = false;
 829 
 830     *expanded_xml = NULL;
 831 
 832     CRM_CHECK(xml_obj != NULL, return EINVAL);
 833 
 834     id = ID(xml_obj);
 835     if (id == NULL) {
 836         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 837                          xml_obj->name);
 838         return pcmk_rc_unpack_error;
 839     }
 840 
 841     // Check whether there are any resource sets with template or tag references
 842     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
 843     if (*expanded_xml != NULL) {
 844         crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
 845         return pcmk_rc_ok;
 846     }
 847 
 848     dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
 849     primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
 850     if ((dependent_id == NULL) || (primary_id == NULL)) {
 851         return pcmk_rc_ok;
 852     }
 853 
 854     if (!pcmk__valid_resource_or_tag(scheduler, dependent_id, &dependent,
 855                                      &dependent_tag)) {
 856         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 857                          "valid resource or tag", id, dependent_id);
 858         return pcmk_rc_unpack_error;
 859     }
 860 
 861     if (!pcmk__valid_resource_or_tag(scheduler, primary_id, &primary,
 862                                      &primary_tag)) {
 863         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 864                          "valid resource or tag", id, primary_id);
 865         return pcmk_rc_unpack_error;
 866     }
 867 
 868     if ((dependent != NULL) && (primary != NULL)) {
 869         /* Neither side references any template/tag. */
 870         return pcmk_rc_ok;
 871     }
 872 
 873     if ((dependent_tag != NULL) && (primary_tag != NULL)) {
 874         // A colocation constraint between two templates/tags makes no sense
 875         pcmk__config_err("Ignoring constraint '%s' because two templates or "
 876                          "tags cannot be colocated", id);
 877         return pcmk_rc_unpack_error;
 878     }
 879 
 880     dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
 881     primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
 882 
 883     *expanded_xml = copy_xml(xml_obj);
 884 
 885     // Convert dependent's template/tag reference into constraint resource_set
 886     if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
 887                           true, scheduler)) {
 888         free_xml(*expanded_xml);
 889         *expanded_xml = NULL;
 890         return pcmk_rc_unpack_error;
 891     }
 892 
 893     if (dependent_set != NULL) {
 894         if (dependent_role != NULL) {
 895             // Move "rsc-role" into converted resource_set as "role"
 896             crm_xml_add(dependent_set, "role", dependent_role);
 897             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
 898         }
 899         any_sets = true;
 900     }
 901 
 902     // Convert primary's template/tag reference into constraint resource_set
 903     if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
 904                           true, scheduler)) {
 905         free_xml(*expanded_xml);
 906         *expanded_xml = NULL;
 907         return pcmk_rc_unpack_error;
 908     }
 909 
 910     if (primary_set != NULL) {
 911         if (primary_role != NULL) {
 912             // Move "with-rsc-role" into converted resource_set as "role"
 913             crm_xml_add(primary_set, "role", primary_role);
 914             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_TARGET_ROLE);
 915         }
 916         any_sets = true;
 917     }
 918 
 919     if (any_sets) {
 920         crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
 921     } else {
 922         free_xml(*expanded_xml);
 923         *expanded_xml = NULL;
 924     }
 925 
 926     return pcmk_rc_ok;
 927 }
 928 
 929 /*!
 930  * \internal
 931  * \brief Parse a colocation constraint from XML into scheduler data
 932  *
 933  * \param[in,out] xml_obj    Colocation constraint XML to unpack
 934  * \param[in,out] scheduler  Scheduler data to add constraint to
 935  */
 936 void
 937 pcmk__unpack_colocation(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 938 {
 939     int score_i = 0;
 940     xmlNode *set = NULL;
 941     xmlNode *last = NULL;
 942 
 943     xmlNode *orig_xml = NULL;
 944     xmlNode *expanded_xml = NULL;
 945 
 946     const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 947     const char *score = NULL;
 948     const char *influence_s = NULL;
 949 
 950     if (pcmk__str_empty(id)) {
 951         pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_DEPEND
 952                          " without " CRM_ATTR_ID);
 953         return;
 954     }
 955 
 956     if (unpack_colocation_tags(xml_obj, &expanded_xml,
 957                                scheduler) != pcmk_rc_ok) {
 958         return;
 959     }
 960     if (expanded_xml != NULL) {
 961         orig_xml = xml_obj;
 962         xml_obj = expanded_xml;
 963     }
 964 
 965     score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 966     if (score != NULL) {
 967         score_i = char2score(score);
 968     }
 969     influence_s = crm_element_value(xml_obj, XML_COLOC_ATTR_INFLUENCE);
 970 
 971     for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
 972          set = crm_next_same_xml(set)) {
 973 
 974         set = expand_idref(set, scheduler->input);
 975         if (set == NULL) { // Configuration error, message already logged
 976             if (expanded_xml != NULL) {
 977                 free_xml(expanded_xml);
 978             }
 979             return;
 980         }
 981 
 982         if (pcmk__str_empty(ID(set))) {
 983             pcmk__config_err("Ignoring " XML_CONS_TAG_RSC_SET
 984                              " without " CRM_ATTR_ID);
 985             continue;
 986         }
 987         unpack_colocation_set(set, score_i, id, influence_s, scheduler);
 988 
 989         if (last != NULL) {
 990             colocate_rsc_sets(id, last, set, score_i, influence_s, scheduler);
 991         }
 992         last = set;
 993     }
 994 
 995     if (expanded_xml) {
 996         free_xml(expanded_xml);
 997         xml_obj = orig_xml;
 998     }
 999 
1000     if (last == NULL) {
1001         unpack_simple_colocation(xml_obj, id, influence_s, scheduler);
1002     }
1003 }
1004 
1005 /*!
1006  * \internal
1007  * \brief Make actions of a given type unrunnable for a given resource
1008  *
1009  * \param[in,out] rsc     Resource whose actions should be blocked
1010  * \param[in]     task    Name of action to block
1011  * \param[in]     reason  Unrunnable start action causing the block
1012  */
1013 static void
1014 mark_action_blocked(pcmk_resource_t *rsc, const char *task,
     /* [previous][next][first][last][top][bottom][index][help] */
1015                     const pcmk_resource_t *reason)
1016 {
1017     GList *iter = NULL;
1018     char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
1019 
1020     for (iter = rsc->actions; iter != NULL; iter = iter->next) {
1021         pcmk_action_t *action = iter->data;
1022 
1023         if (pcmk_is_set(action->flags, pcmk_action_runnable)
1024             && pcmk__str_eq(action->task, task, pcmk__str_none)) {
1025 
1026             pe__clear_action_flags(action, pcmk_action_runnable);
1027             pe_action_set_reason(action, reason_text, false);
1028             pcmk__block_colocation_dependents(action);
1029             pcmk__update_action_for_orderings(action, rsc->cluster);
1030         }
1031     }
1032 
1033     // If parent resource can't perform an action, neither can any children
1034     for (iter = rsc->children; iter != NULL; iter = iter->next) {
1035         mark_action_blocked((pcmk_resource_t *) (iter->data), task, reason);
1036     }
1037     free(reason_text);
1038 }
1039 
1040 /*!
1041  * \internal
1042  * \brief If an action is unrunnable, block any relevant dependent actions
1043  *
1044  * If a given action is an unrunnable start or promote, block the start or
1045  * promote actions of resources colocated with it, as appropriate to the
1046  * colocations' configured roles.
1047  *
1048  * \param[in,out] action  Action to check
1049  */
1050 void
1051 pcmk__block_colocation_dependents(pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1052 {
1053     GList *iter = NULL;
1054     GList *colocations = NULL;
1055     pcmk_resource_t *rsc = NULL;
1056     bool is_start = false;
1057 
1058     if (pcmk_is_set(action->flags, pcmk_action_runnable)) {
1059         return; // Only unrunnable actions block dependents
1060     }
1061 
1062     is_start = pcmk__str_eq(action->task, PCMK_ACTION_START, pcmk__str_none);
1063     if (!is_start
1064         && !pcmk__str_eq(action->task, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
1065         return; // Only unrunnable starts and promotes block dependents
1066     }
1067 
1068     CRM_ASSERT(action->rsc != NULL); // Start and promote are resource actions
1069 
1070     /* If this resource is part of a collective resource, dependents are blocked
1071      * only if all instances of the collective are unrunnable, so check the
1072      * collective resource.
1073      */
1074     rsc = uber_parent(action->rsc);
1075     if (rsc->parent != NULL) {
1076         rsc = rsc->parent; // Bundle
1077     }
1078 
1079     // Colocation fails only if entire primary can't reach desired role
1080     for (iter = rsc->children; iter != NULL; iter = iter->next) {
1081         pcmk_resource_t *child = iter->data;
1082         pcmk_action_t *child_action = find_first_action(child->actions, NULL,
1083                                                         action->task, NULL);
1084 
1085         if ((child_action == NULL)
1086             || pcmk_is_set(child_action->flags, pcmk_action_runnable)) {
1087             crm_trace("Not blocking %s colocation dependents because "
1088                       "at least %s has runnable %s",
1089                       rsc->id, child->id, action->task);
1090             return; // At least one child can reach desired role
1091         }
1092     }
1093 
1094     crm_trace("Blocking %s colocation dependents due to unrunnable %s %s",
1095               rsc->id, action->rsc->id, action->task);
1096 
1097     // Check each colocation where this resource is primary
1098     colocations = pcmk__with_this_colocations(rsc);
1099     for (iter = colocations; iter != NULL; iter = iter->next) {
1100         pcmk__colocation_t *colocation = iter->data;
1101 
1102         if (colocation->score < INFINITY) {
1103             continue; // Only mandatory colocations block dependent
1104         }
1105 
1106         /* If the primary can't start, the dependent can't reach its colocated
1107          * role, regardless of what the primary or dependent colocation role is.
1108          *
1109          * If the primary can't be promoted, the dependent can't reach its
1110          * colocated role if the primary's colocation role is promoted.
1111          */
1112         if (!is_start && (colocation->primary_role != pcmk_role_promoted)) {
1113             continue;
1114         }
1115 
1116         // Block the dependent from reaching its colocated role
1117         if (colocation->dependent_role == pcmk_role_promoted) {
1118             mark_action_blocked(colocation->dependent, PCMK_ACTION_PROMOTE,
1119                                 action->rsc);
1120         } else {
1121             mark_action_blocked(colocation->dependent, PCMK_ACTION_START,
1122                                 action->rsc);
1123         }
1124     }
1125     g_list_free(colocations);
1126 }
1127 
1128 /*!
1129  * \internal
1130  * \brief Get the resource to use for role comparisons
1131  *
1132  * A bundle replica includes a container and possibly an instance of the bundled
1133  * resource. The dependent in a "with bundle" colocation is colocated with a
1134  * particular bundle container. However, if the colocation includes a role, then
1135  * the role must be checked on the bundled resource instance inside the
1136  * container. The container itself will never be promoted; the bundled resource
1137  * may be.
1138  *
1139  * If the given resource is a bundle replica container, return the resource
1140  * inside it, if any. Otherwise, return the resource itself.
1141  *
1142  * \param[in] rsc  Resource to check
1143  *
1144  * \return Resource to use for role comparisons
1145  */
1146 static const pcmk_resource_t *
1147 get_resource_for_role(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1148 {
1149     if (pcmk_is_set(rsc->flags, pcmk_rsc_replica_container)) {
1150         const pcmk_resource_t *child = pe__get_rsc_in_container(rsc);
1151 
1152         if (child != NULL) {
1153             return child;
1154         }
1155     }
1156     return rsc;
1157 }
1158 
1159 /*!
1160  * \internal
1161  * \brief Determine how a colocation constraint should affect a resource
1162  *
1163  * Colocation constraints have different effects at different points in the
1164  * scheduler sequence. Initially, they affect a resource's location; once that
1165  * is determined, then for promotable clones they can affect a resource
1166  * instance's role; after both are determined, the constraints no longer matter.
1167  * Given a specific colocation constraint, check what has been done so far to
1168  * determine what should be affected at the current point in the scheduler.
1169  *
1170  * \param[in] dependent   Dependent resource in colocation
1171  * \param[in] primary     Primary resource in colocation
1172  * \param[in] colocation  Colocation constraint
1173  * \param[in] preview     If true, pretend resources have already been assigned
1174  *
1175  * \return How colocation constraint should be applied at this point
1176  */
1177 enum pcmk__coloc_affects
1178 pcmk__colocation_affects(const pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1179                          const pcmk_resource_t *primary,
1180                          const pcmk__colocation_t *colocation, bool preview)
1181 {
1182     const pcmk_resource_t *dependent_role_rsc = NULL;
1183     const pcmk_resource_t *primary_role_rsc = NULL;
1184 
1185     CRM_ASSERT((dependent != NULL) && (primary != NULL)
1186                && (colocation != NULL));
1187 
1188     if (!preview && pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
1189         // Primary resource has not been assigned yet, so we can't do anything
1190         return pcmk__coloc_affects_nothing;
1191     }
1192 
1193     dependent_role_rsc = get_resource_for_role(dependent);
1194     primary_role_rsc = get_resource_for_role(primary);
1195 
1196     if ((colocation->dependent_role >= pcmk_role_unpromoted)
1197         && (dependent_role_rsc->parent != NULL)
1198         && pcmk_is_set(dependent_role_rsc->parent->flags, pcmk_rsc_promotable)
1199         && !pcmk_is_set(dependent_role_rsc->flags, pcmk_rsc_unassigned)) {
1200 
1201         /* This is a colocation by role, and the dependent is a promotable clone
1202          * that has already been assigned, so the colocation should now affect
1203          * the role.
1204          */
1205         return pcmk__coloc_affects_role;
1206     }
1207 
1208     if (!preview && !pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
1209         /* The dependent resource has already been through assignment, so the
1210          * constraint no longer has any effect. Log an error if a mandatory
1211          * colocation constraint has been violated.
1212          */
1213 
1214         const pcmk_node_t *primary_node = primary->allocated_to;
1215 
1216         if (dependent->allocated_to == NULL) {
1217             crm_trace("Skipping colocation '%s': %s will not run anywhere",
1218                       colocation->id, dependent->id);
1219 
1220         } else if (colocation->score >= INFINITY) {
1221             // Dependent resource must colocate with primary resource
1222 
1223             if (!pe__same_node(primary_node, dependent->allocated_to)) {
1224                 crm_err("%s must be colocated with %s but is not (%s vs. %s)",
1225                         dependent->id, primary->id,
1226                         pe__node_name(dependent->allocated_to),
1227                         pe__node_name(primary_node));
1228             }
1229 
1230         } else if (colocation->score <= -CRM_SCORE_INFINITY) {
1231             // Dependent resource must anti-colocate with primary resource
1232 
1233             if (pe__same_node(dependent->allocated_to, primary_node)) {
1234                 crm_err("%s and %s must be anti-colocated but are assigned "
1235                         "to the same node (%s)",
1236                         dependent->id, primary->id,
1237                         pe__node_name(primary_node));
1238             }
1239         }
1240         return pcmk__coloc_affects_nothing;
1241     }
1242 
1243     if ((colocation->dependent_role != pcmk_role_unknown)
1244         && (colocation->dependent_role != dependent_role_rsc->next_role)) {
1245         crm_trace("Skipping %scolocation '%s': dependent limited to %s role "
1246 
1247                   "but %s next role is %s",
1248                   ((colocation->score < 0)? "anti-" : ""),
1249                   colocation->id, role2text(colocation->dependent_role),
1250                   dependent_role_rsc->id,
1251                   role2text(dependent_role_rsc->next_role));
1252         return pcmk__coloc_affects_nothing;
1253     }
1254 
1255     if ((colocation->primary_role != pcmk_role_unknown)
1256         && (colocation->primary_role != primary_role_rsc->next_role)) {
1257         crm_trace("Skipping %scolocation '%s': primary limited to %s role "
1258                   "but %s next role is %s",
1259                   ((colocation->score < 0)? "anti-" : ""),
1260                   colocation->id, role2text(colocation->primary_role),
1261                   primary_role_rsc->id, role2text(primary_role_rsc->next_role));
1262         return pcmk__coloc_affects_nothing;
1263     }
1264 
1265     return pcmk__coloc_affects_location;
1266 }
1267 
1268 /*!
1269  * \internal
1270  * \brief Apply colocation to dependent for assignment purposes
1271  *
1272  * Update the allowed node scores of the dependent resource in a colocation,
1273  * for the purposes of assigning it to a node.
1274  *
1275  * \param[in,out] dependent   Dependent resource in colocation
1276  * \param[in]     primary     Primary resource in colocation
1277  * \param[in]     colocation  Colocation constraint
1278  */
1279 void
1280 pcmk__apply_coloc_to_scores(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1281                             const pcmk_resource_t *primary,
1282                             const pcmk__colocation_t *colocation)
1283 {
1284     const char *attr = colocation->node_attribute;
1285     const char *value = NULL;
1286     GHashTable *work = NULL;
1287     GHashTableIter iter;
1288     pcmk_node_t *node = NULL;
1289 
1290     if (primary->allocated_to != NULL) {
1291         value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1292                                            primary);
1293 
1294     } else if (colocation->score < 0) {
1295         // Nothing to do (anti-colocation with something that is not running)
1296         return;
1297     }
1298 
1299     work = pcmk__copy_node_table(dependent->allowed_nodes);
1300 
1301     g_hash_table_iter_init(&iter, work);
1302     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1303         if (primary->allocated_to == NULL) {
1304             node->weight = pcmk__add_scores(-colocation->score, node->weight);
1305             pe_rsc_trace(dependent,
1306                          "Applied %s to %s score on %s (now %s after "
1307                          "subtracting %s because primary %s inactive)",
1308                          colocation->id, dependent->id, pe__node_name(node),
1309                          pcmk_readable_score(node->weight),
1310                          pcmk_readable_score(colocation->score), primary->id);
1311             continue;
1312         }
1313 
1314         if (pcmk__str_eq(pcmk__colocation_node_attr(node, attr, dependent),
1315                          value, pcmk__str_casei)) {
1316 
1317             /* Add colocation score only if optional (or minus infinity). A
1318              * mandatory colocation is a requirement rather than a preference,
1319              * so we don't need to consider it for relative assignment purposes.
1320              * The resource will simply be forbidden from running on the node if
1321              * the primary isn't active there (via the condition above).
1322              */
1323             if (colocation->score < CRM_SCORE_INFINITY) {
1324                 node->weight = pcmk__add_scores(colocation->score,
1325                                                 node->weight);
1326                 pe_rsc_trace(dependent,
1327                              "Applied %s to %s score on %s (now %s after "
1328                              "adding %s)",
1329                              colocation->id, dependent->id, pe__node_name(node),
1330                              pcmk_readable_score(node->weight),
1331                              pcmk_readable_score(colocation->score));
1332             }
1333             continue;
1334         }
1335 
1336         if (colocation->score >= CRM_SCORE_INFINITY) {
1337             /* Only mandatory colocations are relevant when the colocation
1338              * attribute doesn't match, because an attribute not matching is not
1339              * a negative preference -- the colocation is simply relevant only
1340              * where it matches.
1341              */
1342             node->weight = -CRM_SCORE_INFINITY;
1343             pe_rsc_trace(dependent,
1344                          "Banned %s from %s because colocation %s attribute %s "
1345                          "does not match",
1346                          dependent->id, pe__node_name(node), colocation->id,
1347                          attr);
1348         }
1349     }
1350 
1351     if ((colocation->score <= -INFINITY) || (colocation->score >= INFINITY)
1352         || pcmk__any_node_available(work)) {
1353 
1354         g_hash_table_destroy(dependent->allowed_nodes);
1355         dependent->allowed_nodes = work;
1356         work = NULL;
1357 
1358     } else {
1359         pe_rsc_info(dependent,
1360                     "%s: Rolling back scores from %s (no available nodes)",
1361                     dependent->id, primary->id);
1362     }
1363 
1364     if (work != NULL) {
1365         g_hash_table_destroy(work);
1366     }
1367 }
1368 
1369 /*!
1370  * \internal
1371  * \brief Apply colocation to dependent for role purposes
1372  *
1373  * Update the priority of the dependent resource in a colocation, for the
1374  * purposes of selecting its role
1375  *
1376  * \param[in,out] dependent   Dependent resource in colocation
1377  * \param[in]     primary     Primary resource in colocation
1378  * \param[in]     colocation  Colocation constraint
1379  */
1380 void
1381 pcmk__apply_coloc_to_priority(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1382                               const pcmk_resource_t *primary,
1383                               const pcmk__colocation_t *colocation)
1384 {
1385     const char *dependent_value = NULL;
1386     const char *primary_value = NULL;
1387     const char *attr = colocation->node_attribute;
1388     int score_multiplier = 1;
1389 
1390     const pcmk_resource_t *primary_role_rsc = NULL;
1391 
1392     CRM_ASSERT((dependent != NULL) && (primary != NULL) &&
1393                (colocation != NULL));
1394 
1395     if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1396         return;
1397     }
1398 
1399     dependent_value = pcmk__colocation_node_attr(dependent->allocated_to, attr,
1400                                                  dependent);
1401     primary_value = pcmk__colocation_node_attr(primary->allocated_to, attr,
1402                                                primary);
1403 
1404     primary_role_rsc = get_resource_for_role(primary);
1405 
1406     if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1407         if ((colocation->score == INFINITY)
1408             && (colocation->dependent_role == pcmk_role_promoted)) {
1409             dependent->priority = -INFINITY;
1410         }
1411         return;
1412     }
1413 
1414     if ((colocation->primary_role != pcmk_role_unknown)
1415         && (colocation->primary_role != primary_role_rsc->next_role)) {
1416         return;
1417     }
1418 
1419     if (colocation->dependent_role == pcmk_role_unpromoted) {
1420         score_multiplier = -1;
1421     }
1422 
1423     dependent->priority = pcmk__add_scores(score_multiplier * colocation->score,
1424                                            dependent->priority);
1425     pe_rsc_trace(dependent,
1426                  "Applied %s to %s promotion priority (now %s after %s %s)",
1427                  colocation->id, dependent->id,
1428                  pcmk_readable_score(dependent->priority),
1429                  ((score_multiplier == 1)? "adding" : "subtracting"),
1430                  pcmk_readable_score(colocation->score));
1431 }
1432 
1433 /*!
1434  * \internal
1435  * \brief Find score of highest-scored node that matches colocation attribute
1436  *
1437  * \param[in] rsc    Resource whose allowed nodes should be searched
1438  * \param[in] attr   Colocation attribute name (must not be NULL)
1439  * \param[in] value  Colocation attribute value to require
1440  */
1441 static int
1442 best_node_score_matching_attr(const pcmk_resource_t *rsc, const char *attr,
     /* [previous][next][first][last][top][bottom][index][help] */
1443                               const char *value)
1444 {
1445     GHashTableIter iter;
1446     pcmk_node_t *node = NULL;
1447     int best_score = -INFINITY;
1448     const char *best_node = NULL;
1449 
1450     // Find best allowed node with matching attribute
1451     g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1452     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1453 
1454         if ((node->weight > best_score)
1455             && pcmk__node_available(node, false, false)
1456             && pcmk__str_eq(value, pcmk__colocation_node_attr(node, attr, rsc),
1457                             pcmk__str_casei)) {
1458 
1459             best_score = node->weight;
1460             best_node = node->details->uname;
1461         }
1462     }
1463 
1464     if (!pcmk__str_eq(attr, CRM_ATTR_UNAME, pcmk__str_none)) {
1465         if (best_node == NULL) {
1466             crm_info("No allowed node for %s matches node attribute %s=%s",
1467                      rsc->id, attr, value);
1468         } else {
1469             crm_info("Allowed node %s for %s had best score (%d) "
1470                      "of those matching node attribute %s=%s",
1471                      best_node, rsc->id, best_score, attr, value);
1472         }
1473     }
1474     return best_score;
1475 }
1476 
1477 /*!
1478  * \internal
1479  * \brief Check whether a resource is allowed only on a single node
1480  *
1481  * \param[in] rsc   Resource to check
1482  *
1483  * \return \c true if \p rsc is allowed only on one node, otherwise \c false
1484  */
1485 static bool
1486 allowed_on_one(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1487 {
1488     GHashTableIter iter;
1489     pcmk_node_t *allowed_node = NULL;
1490     int allowed_nodes = 0;
1491 
1492     g_hash_table_iter_init(&iter, rsc->allowed_nodes);
1493     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &allowed_node)) {
1494         if ((allowed_node->weight >= 0) && (++allowed_nodes > 1)) {
1495             pe_rsc_trace(rsc, "%s is allowed on multiple nodes", rsc->id);
1496             return false;
1497         }
1498     }
1499     pe_rsc_trace(rsc, "%s is allowed %s", rsc->id,
1500                  ((allowed_nodes == 1)? "on a single node" : "nowhere"));
1501     return (allowed_nodes == 1);
1502 }
1503 
1504 /*!
1505  * \internal
1506  * \brief Add resource's colocation matches to current node assignment scores
1507  *
1508  * For each node in a given table, if any of a given resource's allowed nodes
1509  * have a matching value for the colocation attribute, add the highest of those
1510  * nodes' scores to the node's score.
1511  *
1512  * \param[in,out] nodes          Table of nodes with assignment scores so far
1513  * \param[in]     source_rsc     Resource whose node scores to add
1514  * \param[in]     target_rsc     Resource on whose behalf to update \p nodes
1515  * \param[in]     colocation     Original colocation constraint (used to get
1516  *                               configured primary resource's stickiness, and
1517  *                               to get colocation node attribute; pass NULL to
1518  *                               ignore stickiness and use default attribute)
1519  * \param[in]     factor         Factor by which to multiply scores being added
1520  * \param[in]     only_positive  Whether to add only positive scores
1521  */
1522 static void
1523 add_node_scores_matching_attr(GHashTable *nodes,
     /* [previous][next][first][last][top][bottom][index][help] */
1524                               const pcmk_resource_t *source_rsc,
1525                               const pcmk_resource_t *target_rsc,
1526                               const pcmk__colocation_t *colocation,
1527                               float factor, bool only_positive)
1528 {
1529     GHashTableIter iter;
1530     pcmk_node_t *node = NULL;
1531     const char *attr = colocation->node_attribute;
1532 
1533     // Iterate through each node
1534     g_hash_table_iter_init(&iter, nodes);
1535     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1536         float delta_f = 0;
1537         int delta = 0;
1538         int score = 0;
1539         int new_score = 0;
1540         const char *value = pcmk__colocation_node_attr(node, attr, target_rsc);
1541 
1542         score = best_node_score_matching_attr(source_rsc, attr, value);
1543 
1544         if ((factor < 0) && (score < 0)) {
1545             /* If the dependent is anti-colocated, we generally don't want the
1546              * primary to prefer nodes that the dependent avoids. That could
1547              * lead to unnecessary shuffling of the primary when the dependent
1548              * hits its migration threshold somewhere, for example.
1549              *
1550              * However, there are cases when it is desirable. If the dependent
1551              * can't run anywhere but where the primary is, it would be
1552              * worthwhile to move the primary for the sake of keeping the
1553              * dependent active.
1554              *
1555              * We can't know that exactly at this point since we don't know
1556              * where the primary will be assigned, but we can limit considering
1557              * the preference to when the dependent is allowed only on one node.
1558              * This is less than ideal for multiple reasons:
1559              *
1560              * - the dependent could be allowed on more than one node but have
1561              *   anti-colocation primaries on each;
1562              * - the dependent could be a clone or bundle with multiple
1563              *   instances, and the dependent as a whole is allowed on multiple
1564              *   nodes but some instance still can't run
1565              * - the dependent has considered node-specific criteria such as
1566              *   location constraints and stickiness by this point, but might
1567              *   have other factors that end up disallowing a node
1568              *
1569              * but the alternative is making the primary move when it doesn't
1570              * need to.
1571              *
1572              * We also consider the primary's stickiness and influence, so the
1573              * user has some say in the matter. (This is the configured primary,
1574              * not a particular instance of the primary, but that doesn't matter
1575              * unless stickiness uses a rule to vary by node, and that seems
1576              * acceptable to ignore.)
1577              */
1578             if ((colocation->primary->stickiness >= -score)
1579                 || !pcmk__colocation_has_influence(colocation, NULL)
1580                 || !allowed_on_one(colocation->dependent)) {
1581                 crm_trace("%s: Filtering %d + %f * %d "
1582                           "(double negative disallowed)",
1583                           pe__node_name(node), node->weight, factor, score);
1584                 continue;
1585             }
1586         }
1587 
1588         if (node->weight == INFINITY_HACK) {
1589             crm_trace("%s: Filtering %d + %f * %d (node was marked unusable)",
1590                       pe__node_name(node), node->weight, factor, score);
1591             continue;
1592         }
1593 
1594         delta_f = factor * score;
1595 
1596         // Round the number; see http://c-faq.com/fp/round.html
1597         delta = (int) ((delta_f < 0)? (delta_f - 0.5) : (delta_f + 0.5));
1598 
1599         /* Small factors can obliterate the small scores that are often actually
1600          * used in configurations. If the score and factor are nonzero, ensure
1601          * that the result is nonzero as well.
1602          */
1603         if ((delta == 0) && (score != 0)) {
1604             if (factor > 0.0) {
1605                 delta = 1;
1606             } else if (factor < 0.0) {
1607                 delta = -1;
1608             }
1609         }
1610 
1611         new_score = pcmk__add_scores(delta, node->weight);
1612 
1613         if (only_positive && (new_score < 0) && (node->weight > 0)) {
1614             crm_trace("%s: Filtering %d + %f * %d = %d "
1615                       "(negative disallowed, marking node unusable)",
1616                       pe__node_name(node), node->weight, factor, score,
1617                       new_score);
1618             node->weight = INFINITY_HACK;
1619             continue;
1620         }
1621 
1622         if (only_positive && (new_score < 0) && (node->weight == 0)) {
1623             crm_trace("%s: Filtering %d + %f * %d = %d (negative disallowed)",
1624                       pe__node_name(node), node->weight, factor, score,
1625                       new_score);
1626             continue;
1627         }
1628 
1629         crm_trace("%s: %d + %f * %d = %d", pe__node_name(node),
1630                   node->weight, factor, score, new_score);
1631         node->weight = new_score;
1632     }
1633 }
1634 
1635 /*!
1636  * \internal
1637  * \brief Update nodes with scores of colocated resources' nodes
1638  *
1639  * Given a table of nodes and a resource, update the nodes' scores with the
1640  * scores of the best nodes matching the attribute used for each of the
1641  * resource's relevant colocations.
1642  *
1643  * \param[in,out] source_rsc  Resource whose node scores to add
1644  * \param[in]     target_rsc  Resource on whose behalf to update \p *nodes
1645  * \param[in]     log_id      Resource ID for logs (if \c NULL, use
1646  *                            \p source_rsc ID)
1647  * \param[in,out] nodes       Nodes to update (set initial contents to \c NULL
1648  *                            to copy allowed nodes from \p source_rsc)
1649  * \param[in]     colocation  Original colocation constraint (used to get
1650  *                            configured primary resource's stickiness, and
1651  *                            to get colocation node attribute; if \c NULL,
1652  *                            <tt>source_rsc</tt>'s own matching node scores
1653  *                            will not be added, and \p *nodes must be \c NULL
1654  *                            as well)
1655  * \param[in]     factor      Incorporate scores multiplied by this factor
1656  * \param[in]     flags       Bitmask of enum pcmk__coloc_select values
1657  *
1658  * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, and
1659  *       the \c pcmk__coloc_select_this_with flag are used together (and only by
1660  *       \c cmp_resources()).
1661  * \note The caller remains responsible for freeing \p *nodes.
1662  * \note This is the shared implementation of
1663  *       \c pcmk_assignment_methods_t:add_colocated_node_scores().
1664  */
1665 void
1666 pcmk__add_colocated_node_scores(pcmk_resource_t *source_rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1667                                 const pcmk_resource_t *target_rsc,
1668                                 const char *log_id,
1669                                 GHashTable **nodes,
1670                                 const pcmk__colocation_t *colocation,
1671                                 float factor, uint32_t flags)
1672 {
1673     GHashTable *work = NULL;
1674 
1675     CRM_ASSERT((source_rsc != NULL) && (nodes != NULL)
1676                && ((colocation != NULL)
1677                    || ((target_rsc == NULL) && (*nodes == NULL))));
1678 
1679     if (log_id == NULL) {
1680         log_id = source_rsc->id;
1681     }
1682 
1683     // Avoid infinite recursion
1684     if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
1685         pe_rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
1686                     log_id, source_rsc->id);
1687         return;
1688     }
1689     pe__set_resource_flags(source_rsc, pcmk_rsc_updating_nodes);
1690 
1691     if (*nodes == NULL) {
1692         work = pcmk__copy_node_table(source_rsc->allowed_nodes);
1693         target_rsc = source_rsc;
1694     } else {
1695         const bool pos = pcmk_is_set(flags, pcmk__coloc_select_nonnegative);
1696 
1697         pe_rsc_trace(source_rsc, "%s: Merging %s scores from %s (at %.6f)",
1698                      log_id, (pos? "positive" : "all"), source_rsc->id, factor);
1699         work = pcmk__copy_node_table(*nodes);
1700         add_node_scores_matching_attr(work, source_rsc, target_rsc, colocation,
1701                                       factor, pos);
1702     }
1703 
1704     if (work == NULL) {
1705         pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes);
1706         return;
1707     }
1708 
1709     if (pcmk__any_node_available(work)) {
1710         GList *colocations = NULL;
1711 
1712         if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1713             colocations = pcmk__this_with_colocations(source_rsc);
1714             pe_rsc_trace(source_rsc,
1715                          "Checking additional %d optional '%s with' "
1716                          "constraints",
1717                          g_list_length(colocations), source_rsc->id);
1718         } else {
1719             colocations = pcmk__with_this_colocations(source_rsc);
1720             pe_rsc_trace(source_rsc,
1721                          "Checking additional %d optional 'with %s' "
1722                          "constraints",
1723                          g_list_length(colocations), source_rsc->id);
1724         }
1725         flags |= pcmk__coloc_select_active;
1726 
1727         for (GList *iter = colocations; iter != NULL; iter = iter->next) {
1728             pcmk__colocation_t *constraint = iter->data;
1729 
1730             pcmk_resource_t *other = NULL;
1731             float other_factor = factor * constraint->score / (float) INFINITY;
1732 
1733             if (pcmk_is_set(flags, pcmk__coloc_select_this_with)) {
1734                 other = constraint->primary;
1735             } else if (!pcmk__colocation_has_influence(constraint, NULL)) {
1736                 continue;
1737             } else {
1738                 other = constraint->dependent;
1739             }
1740 
1741             pe_rsc_trace(source_rsc,
1742                          "Optionally merging score of '%s' constraint "
1743                          "(%s with %s)",
1744                          constraint->id, constraint->dependent->id,
1745                          constraint->primary->id);
1746             other->cmds->add_colocated_node_scores(other, target_rsc, log_id,
1747                                                    &work, constraint,
1748                                                    other_factor, flags);
1749             pe__show_node_scores(true, NULL, log_id, work, source_rsc->cluster);
1750         }
1751         g_list_free(colocations);
1752 
1753     } else if (pcmk_is_set(flags, pcmk__coloc_select_active)) {
1754         pe_rsc_info(source_rsc, "%s: Rolling back optional scores from %s",
1755                     log_id, source_rsc->id);
1756         g_hash_table_destroy(work);
1757         pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes);
1758         return;
1759     }
1760 
1761 
1762     if (pcmk_is_set(flags, pcmk__coloc_select_nonnegative)) {
1763         pcmk_node_t *node = NULL;
1764         GHashTableIter iter;
1765 
1766         g_hash_table_iter_init(&iter, work);
1767         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1768             if (node->weight == INFINITY_HACK) {
1769                 node->weight = 1;
1770             }
1771         }
1772     }
1773 
1774     if (*nodes != NULL) {
1775        g_hash_table_destroy(*nodes);
1776     }
1777     *nodes = work;
1778 
1779     pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes);
1780 }
1781 
1782 /*!
1783  * \internal
1784  * \brief Apply a "with this" colocation to a resource's allowed node scores
1785  *
1786  * \param[in,out] data       Colocation to apply
1787  * \param[in,out] user_data  Resource being assigned
1788  */
1789 void
1790 pcmk__add_dependent_scores(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1791 {
1792     pcmk__colocation_t *colocation = data;
1793     pcmk_resource_t *target_rsc = user_data;
1794 
1795     pcmk_resource_t *source_rsc = colocation->dependent;
1796     const float factor = colocation->score / (float) INFINITY;
1797     uint32_t flags = pcmk__coloc_select_active;
1798 
1799     if (!pcmk__colocation_has_influence(colocation, NULL)) {
1800         return;
1801     }
1802     if (target_rsc->variant == pcmk_rsc_variant_clone) {
1803         flags |= pcmk__coloc_select_nonnegative;
1804     }
1805     pe_rsc_trace(target_rsc,
1806                  "%s: Incorporating attenuated %s assignment scores due "
1807                  "to colocation %s",
1808                  target_rsc->id, source_rsc->id, colocation->id);
1809     source_rsc->cmds->add_colocated_node_scores(source_rsc, target_rsc,
1810                                                 source_rsc->id,
1811                                                 &target_rsc->allowed_nodes,
1812                                                 colocation, factor, flags);
1813 }
1814 
1815 /*!
1816  * \internal
1817  * \brief Exclude nodes from a dependent's node table if not in a given list
1818  *
1819  * Given a dependent resource in a colocation and a list of nodes where the
1820  * primary resource will run, set a node's score to \c -INFINITY in the
1821  * dependent's node table if not found in the primary nodes list.
1822  *
1823  * \param[in,out] dependent      Dependent resource
1824  * \param[in]     primary        Primary resource (for logging only)
1825  * \param[in]     colocation     Colocation constraint (for logging only)
1826  * \param[in]     primary_nodes  List of nodes where the primary will have
1827  *                               unblocked instances in a suitable role
1828  * \param[in]     merge_scores   If \c true and a node is found in both \p table
1829  *                               and \p list, add the node's score in \p list to
1830  *                               the node's score in \p table
1831  */
1832 void
1833 pcmk__colocation_intersect_nodes(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1834                                  const pcmk_resource_t *primary,
1835                                  const pcmk__colocation_t *colocation,
1836                                  const GList *primary_nodes, bool merge_scores)
1837 {
1838     GHashTableIter iter;
1839     pcmk_node_t *dependent_node = NULL;
1840 
1841     CRM_ASSERT((dependent != NULL) && (primary != NULL)
1842                && (colocation != NULL));
1843 
1844     g_hash_table_iter_init(&iter, dependent->allowed_nodes);
1845     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &dependent_node)) {
1846         const pcmk_node_t *primary_node = NULL;
1847 
1848         primary_node = pe_find_node_id(primary_nodes,
1849                                        dependent_node->details->id);
1850         if (primary_node == NULL) {
1851             dependent_node->weight = -INFINITY;
1852             pe_rsc_trace(dependent,
1853                          "Banning %s from %s (no primary instance) for %s",
1854                          dependent->id, pe__node_name(dependent_node),
1855                          colocation->id);
1856 
1857         } else if (merge_scores) {
1858             dependent_node->weight = pcmk__add_scores(dependent_node->weight,
1859                                                       primary_node->weight);
1860             pe_rsc_trace(dependent,
1861                          "Added %s's score %s to %s's score for %s (now %s) "
1862                          "for colocation %s",
1863                          primary->id, pcmk_readable_score(primary_node->weight),
1864                          dependent->id, pe__node_name(dependent_node),
1865                          pcmk_readable_score(dependent_node->weight),
1866                          colocation->id);
1867         }
1868     }
1869 }
1870 
1871 /*!
1872  * \internal
1873  * \brief Get all colocations affecting a resource as the primary
1874  *
1875  * \param[in] rsc  Resource to get colocations for
1876  *
1877  * \return Newly allocated list of colocations affecting \p rsc as primary
1878  *
1879  * \note This is a convenience wrapper for the with_this_colocations() method.
1880  */
1881 GList *
1882 pcmk__with_this_colocations(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1883 {
1884     GList *list = NULL;
1885 
1886     rsc->cmds->with_this_colocations(rsc, rsc, &list);
1887     return list;
1888 }
1889 
1890 /*!
1891  * \internal
1892  * \brief Get all colocations affecting a resource as the dependent
1893  *
1894  * \param[in] rsc  Resource to get colocations for
1895  *
1896  * \return Newly allocated list of colocations affecting \p rsc as dependent
1897  *
1898  * \note This is a convenience wrapper for the this_with_colocations() method.
1899  */
1900 GList *
1901 pcmk__this_with_colocations(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1902 {
1903     GList *list = NULL;
1904 
1905     rsc->cmds->this_with_colocations(rsc, rsc, &list);
1906     return list;
1907 }

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