root/lib/pacemaker/pcmk_sched_promotable.c

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

DEFINITIONS

This source file includes following definitions.
  1. order_instance_promotion
  2. order_instance_demotion
  3. check_for_role_change
  4. apply_promoted_locations
  5. node_to_be_promoted_on
  6. cmp_promotable_instance
  7. add_promotion_priority_to_node_score
  8. apply_coloc_to_primary
  9. set_promotion_priority_to_node_score
  10. sort_promotable_instances
  11. find_active_anon_instance
  12. anonymous_known_on
  13. is_allowed
  14. promotion_score_applies
  15. promotion_attr_value
  16. promotion_score
  17. pcmk__add_promotion_scores
  18. set_current_role_unpromoted
  19. set_next_role_unpromoted
  20. set_next_role_promoted
  21. show_promotion_score
  22. set_instance_priority
  23. set_instance_role
  24. pcmk__set_instance_roles
  25. create_promotable_instance_actions
  26. reset_instance_priorities
  27. pcmk__create_promotable_actions
  28. pcmk__order_promotable_instances
  29. update_dependent_allowed_nodes
  30. pcmk__update_dependent_with_promotable
  31. pcmk__update_promotable_dependent_priority

   1 /*
   2  * Copyright 2004-2024 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 <crm/common/xml.h>
  13 #include <pacemaker-internal.h>
  14 
  15 #include "libpacemaker_private.h"
  16 
  17 /*!
  18  * \internal
  19  * \brief Add implicit promotion ordering for a promotable instance
  20  *
  21  * \param[in,out] clone  Clone resource
  22  * \param[in,out] child  Instance of \p clone being ordered
  23  * \param[in,out] last   Previous instance ordered (NULL if \p child is first)
  24  */
  25 static void
  26 order_instance_promotion(pcmk_resource_t *clone, pcmk_resource_t *child,
     /* [previous][next][first][last][top][bottom][index][help] */
  27                          pcmk_resource_t *last)
  28 {
  29     // "Promote clone" -> promote instance -> "clone promoted"
  30     pcmk__order_resource_actions(clone, PCMK_ACTION_PROMOTE,
  31                                  child, PCMK_ACTION_PROMOTE,
  32                                  pcmk__ar_ordered);
  33     pcmk__order_resource_actions(child, PCMK_ACTION_PROMOTE,
  34                                  clone, PCMK_ACTION_PROMOTED,
  35                                  pcmk__ar_ordered);
  36 
  37     // If clone is ordered, order this instance relative to last
  38     if ((last != NULL) && pe__clone_is_ordered(clone)) {
  39         pcmk__order_resource_actions(last, PCMK_ACTION_PROMOTE,
  40                                      child, PCMK_ACTION_PROMOTE,
  41                                      pcmk__ar_ordered);
  42     }
  43 }
  44 
  45 /*!
  46  * \internal
  47  * \brief Add implicit demotion ordering for a promotable instance
  48  *
  49  * \param[in,out] clone  Clone resource
  50  * \param[in,out] child  Instance of \p clone being ordered
  51  * \param[in]     last   Previous instance ordered (NULL if \p child is first)
  52  */
  53 static void
  54 order_instance_demotion(pcmk_resource_t *clone, pcmk_resource_t *child,
     /* [previous][next][first][last][top][bottom][index][help] */
  55                         pcmk_resource_t *last)
  56 {
  57     // "Demote clone" -> demote instance -> "clone demoted"
  58     pcmk__order_resource_actions(clone, PCMK_ACTION_DEMOTE, child,
  59                                  PCMK_ACTION_DEMOTE,
  60                                  pcmk__ar_then_implies_first_graphed);
  61     pcmk__order_resource_actions(child, PCMK_ACTION_DEMOTE,
  62                                  clone, PCMK_ACTION_DEMOTED,
  63                                  pcmk__ar_first_implies_then_graphed);
  64 
  65     // If clone is ordered, order this instance relative to last
  66     if ((last != NULL) && pe__clone_is_ordered(clone)) {
  67         pcmk__order_resource_actions(child, PCMK_ACTION_DEMOTE, last,
  68                                      PCMK_ACTION_DEMOTE, pcmk__ar_ordered);
  69     }
  70 }
  71 
  72 /*!
  73  * \internal
  74  * \brief Check whether an instance will be promoted or demoted
  75  *
  76  * \param[in]  rsc        Instance to check
  77  * \param[out] demoting   If \p rsc will be demoted, this will be set to true
  78  * \param[out] promoting  If \p rsc will be promoted, this will be set to true
  79  */
  80 static void
  81 check_for_role_change(const pcmk_resource_t *rsc, bool *demoting,
     /* [previous][next][first][last][top][bottom][index][help] */
  82                       bool *promoting)
  83 {
  84     const GList *iter = NULL;
  85 
  86     // If this is a cloned group, check group members recursively
  87     if (rsc->priv->children != NULL) {
  88         for (iter = rsc->priv->children; iter != NULL; iter = iter->next) {
  89             check_for_role_change((const pcmk_resource_t *) iter->data,
  90                                   demoting, promoting);
  91         }
  92         return;
  93     }
  94 
  95     for (iter = rsc->priv->actions; iter != NULL; iter = iter->next) {
  96         const pcmk_action_t *action = (const pcmk_action_t *) iter->data;
  97 
  98         if (*promoting && *demoting) {
  99             return;
 100 
 101         } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
 102             continue;
 103 
 104         } else if (pcmk__str_eq(PCMK_ACTION_DEMOTE, action->task,
 105                                 pcmk__str_none)) {
 106             *demoting = true;
 107 
 108         } else if (pcmk__str_eq(PCMK_ACTION_PROMOTE, action->task,
 109                                 pcmk__str_none)) {
 110             *promoting = true;
 111         }
 112     }
 113 }
 114 
 115 /*!
 116  * \internal
 117  * \brief Add promoted-role location constraint scores to an instance's priority
 118  *
 119  * Adjust a promotable clone instance's promotion priority by the scores of any
 120  * location constraints in a list that are both limited to the promoted role and
 121  * for the node where the instance will be placed.
 122  *
 123  * \param[in,out] child                 Promotable clone instance
 124  * \param[in]     location_constraints  List of location constraints to apply
 125  * \param[in]     chosen                Node where \p child will be placed
 126  */
 127 static void
 128 apply_promoted_locations(pcmk_resource_t *child,
     /* [previous][next][first][last][top][bottom][index][help] */
 129                          const GList *location_constraints,
 130                          const pcmk_node_t *chosen)
 131 {
 132     for (const GList *iter = location_constraints; iter; iter = iter->next) {
 133         const pcmk__location_t *location = iter->data;
 134         const pcmk_node_t *constraint_node = NULL;
 135 
 136         if (location->role_filter == pcmk_role_promoted) {
 137             constraint_node = pe_find_node_id(location->nodes,
 138                                               chosen->priv->id);
 139         }
 140         if (constraint_node != NULL) {
 141             int new_priority = pcmk__add_scores(child->priv->priority,
 142                                                 constraint_node->assign->score);
 143 
 144             pcmk__rsc_trace(child,
 145                             "Applying location %s to %s promotion priority on "
 146                             "%s: %s + %s = %s",
 147                             location->id, child->id,
 148                             pcmk__node_name(constraint_node),
 149                             pcmk_readable_score(child->priv->priority),
 150                             pcmk_readable_score(constraint_node->assign->score),
 151                             pcmk_readable_score(new_priority));
 152             child->priv->priority = new_priority;
 153         }
 154     }
 155 }
 156 
 157 /*!
 158  * \internal
 159  * \brief Get the node that an instance will be promoted on
 160  *
 161  * \param[in] rsc  Promotable clone instance to check
 162  *
 163  * \return Node that \p rsc will be promoted on, or NULL if none
 164  */
 165 static pcmk_node_t *
 166 node_to_be_promoted_on(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168     pcmk_node_t *node = NULL;
 169     pcmk_node_t *local_node = NULL;
 170     const pcmk_resource_t *parent = NULL;
 171 
 172     // If this is a cloned group, bail if any group member can't be promoted
 173     for (GList *iter = rsc->priv->children;
 174          iter != NULL; iter = iter->next) {
 175 
 176         pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 177 
 178         if (node_to_be_promoted_on(child) == NULL) {
 179             pcmk__rsc_trace(rsc,
 180                             "%s can't be promoted because member %s can't",
 181                             rsc->id, child->id);
 182             return NULL;
 183         }
 184     }
 185 
 186     node = rsc->priv->fns->location(rsc, NULL, pcmk__rsc_node_assigned);
 187     if (node == NULL) {
 188         pcmk__rsc_trace(rsc, "%s can't be promoted because it won't be active",
 189                         rsc->id);
 190         return NULL;
 191 
 192     } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 193         if (rsc->priv->fns->state(rsc, TRUE) == pcmk_role_promoted) {
 194             crm_notice("Unmanaged instance %s will be left promoted on %s",
 195                        rsc->id, pcmk__node_name(node));
 196         } else {
 197             pcmk__rsc_trace(rsc, "%s can't be promoted because it is unmanaged",
 198                             rsc->id);
 199             return NULL;
 200         }
 201 
 202     } else if (rsc->priv->priority < 0) {
 203         pcmk__rsc_trace(rsc,
 204                         "%s can't be promoted because its promotion priority "
 205                         "%d is negative",
 206                         rsc->id, rsc->priv->priority);
 207         return NULL;
 208 
 209     } else if (!pcmk__node_available(node, false, true)) {
 210         pcmk__rsc_trace(rsc,
 211                         "%s can't be promoted because %s can't run resources",
 212                         rsc->id, pcmk__node_name(node));
 213         return NULL;
 214     }
 215 
 216     parent = pe__const_top_resource(rsc, false);
 217     local_node = g_hash_table_lookup(parent->priv->allowed_nodes,
 218                                      node->priv->id);
 219 
 220     if (local_node == NULL) {
 221         /* It should not be possible for the scheduler to have assigned the
 222          * instance to a node where its parent is not allowed, but it's good to
 223          * have a fail-safe.
 224          */
 225         if (pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 226             pcmk__sched_err(node->priv->scheduler,
 227                             "%s can't be promoted because %s is not allowed "
 228                             "on %s (scheduler bug?)",
 229                             rsc->id, parent->id, pcmk__node_name(node));
 230         } // else the instance is unmanaged and already promoted
 231         return NULL;
 232 
 233     } else if ((local_node->assign->count >= pe__clone_promoted_node_max(parent))
 234                && pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 235         pcmk__rsc_trace(rsc,
 236                         "%s can't be promoted because %s has "
 237                         "maximum promoted instances already",
 238                         rsc->id, pcmk__node_name(node));
 239         return NULL;
 240     }
 241 
 242     return local_node;
 243 }
 244 
 245 /*!
 246  * \internal
 247  * \brief Compare two promotable clone instances by promotion priority
 248  *
 249  * \param[in] a  First instance to compare
 250  * \param[in] b  Second instance to compare
 251  *
 252  * \return A negative number if \p a has higher promotion priority,
 253  *         a positive number if \p b has higher promotion priority,
 254  *         or 0 if promotion priorities are equal
 255  */
 256 static gint
 257 cmp_promotable_instance(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259     const pcmk_resource_t *rsc1 = (const pcmk_resource_t *) a;
 260     const pcmk_resource_t *rsc2 = (const pcmk_resource_t *) b;
 261 
 262     enum rsc_role_e role1 = pcmk_role_unknown;
 263     enum rsc_role_e role2 = pcmk_role_unknown;
 264 
 265     pcmk__assert((rsc1 != NULL) && (rsc2 != NULL));
 266 
 267     // Check promotion priority set by pcmk__set_instance_roles()
 268     if (rsc1->priv->promotion_priority > rsc2->priv->promotion_priority) {
 269         pcmk__rsc_trace(rsc1,
 270                         "%s has higher promotion priority (%s) than %s (%d)",
 271                         rsc1->id,
 272                         pcmk_readable_score(rsc1->priv->promotion_priority),
 273                         rsc2->id, rsc2->priv->promotion_priority);
 274         return -1;
 275     }
 276 
 277     if (rsc1->priv->promotion_priority < rsc2->priv->promotion_priority) {
 278         pcmk__rsc_trace(rsc1,
 279                         "%s has lower promotion priority (%s) than %s (%d)",
 280                         rsc1->id,
 281                         pcmk_readable_score(rsc1->priv->promotion_priority),
 282                         rsc2->id, rsc2->priv->promotion_priority);
 283         return 1;
 284     }
 285 
 286     // If those are the same, prefer instance whose current role is higher
 287     role1 = rsc1->priv->fns->state(rsc1, TRUE);
 288     role2 = rsc2->priv->fns->state(rsc2, TRUE);
 289     if (role1 > role2) {
 290         pcmk__rsc_trace(rsc1,
 291                         "%s has higher promotion priority than %s "
 292                         "(higher current role)",
 293                         rsc1->id, rsc2->id);
 294         return -1;
 295     } else if (role1 < role2) {
 296         pcmk__rsc_trace(rsc1,
 297                         "%s has lower promotion priority than %s "
 298                         "(lower current role)",
 299                         rsc1->id, rsc2->id);
 300         return 1;
 301     }
 302 
 303     // Finally, do normal clone instance sorting
 304     return pcmk__cmp_instance(a, b);
 305 }
 306 
 307 /*!
 308  * \internal
 309  * \brief Add promotable clone instance's promotion priority to its node's score
 310  *
 311  * Add a promotable clone instance's promotion priority (which sums its
 312  * promotion preferences and scores of relevant location constraints for the
 313  * promoted role) to the node score of the instance's assigned node.
 314  *
 315  * \param[in]     data       Promotable clone instance
 316  * \param[in,out] user_data  Clone parent of \p data
 317  */
 318 static void
 319 add_promotion_priority_to_node_score(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 320 {
 321     const pcmk_resource_t *child = (const pcmk_resource_t *) data;
 322     pcmk_resource_t *clone = (pcmk_resource_t *) user_data;
 323 
 324     pcmk_node_t *node = NULL;
 325     const pcmk_node_t *chosen = NULL;
 326     const int promotion_priority = child->priv->promotion_priority;
 327 
 328     if (promotion_priority < 0) {
 329         pcmk__rsc_trace(clone,
 330                         "Not adding promotion priority of %s: negative (%s)",
 331                         child->id, pcmk_readable_score(promotion_priority));
 332         return;
 333     }
 334 
 335     chosen = child->priv->fns->location(child, NULL, pcmk__rsc_node_assigned);
 336     if (chosen == NULL) {
 337         pcmk__rsc_trace(clone, "Not adding promotion priority of %s: inactive",
 338                         child->id);
 339         return;
 340     }
 341 
 342     node = g_hash_table_lookup(clone->priv->allowed_nodes,
 343                                chosen->priv->id);
 344     pcmk__assert(node != NULL);
 345 
 346     node->assign->score = pcmk__add_scores(promotion_priority,
 347                                            node->assign->score);
 348     pcmk__rsc_trace(clone,
 349                     "Added cumulative priority of %s (%s) to score on %s "
 350                     "(now %d)",
 351                     child->id, pcmk_readable_score(promotion_priority),
 352                     pcmk__node_name(node), node->assign->score);
 353 }
 354 
 355 /*!
 356  * \internal
 357  * \brief Apply colocation to primary's node scores if for promoted role
 358  *
 359  * \param[in,out] data       Colocation constraint to apply
 360  * \param[in,out] user_data  Promotable clone that is constraint's primary
 361  */
 362 static void
 363 apply_coloc_to_primary(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 364 {
 365     pcmk__colocation_t *colocation = data;
 366     pcmk_resource_t *clone = user_data;
 367     pcmk_resource_t *dependent = colocation->dependent;
 368     const float factor = colocation->score / (float) PCMK_SCORE_INFINITY;
 369     const uint32_t flags = pcmk__coloc_select_active
 370                            |pcmk__coloc_select_nonnegative;
 371 
 372     if ((colocation->primary_role != pcmk_role_promoted)
 373          || !pcmk__colocation_has_influence(colocation, NULL)) {
 374         return;
 375     }
 376 
 377     pcmk__rsc_trace(clone, "Applying colocation %s (%s with promoted %s) @%s",
 378                     colocation->id, colocation->dependent->id,
 379                     colocation->primary->id,
 380                     pcmk_readable_score(colocation->score));
 381     dependent->priv->cmds->add_colocated_node_scores(dependent, clone,
 382                                                      clone->id,
 383                                                      &(clone->priv->allowed_nodes),
 384                                                      colocation, factor, flags);
 385 }
 386 
 387 /*!
 388  * \internal
 389  * \brief Set clone instance's promotion priority to its node's score
 390  *
 391  * \param[in,out] data       Promotable clone instance
 392  * \param[in]     user_data  Parent clone of \p data
 393  */
 394 static void
 395 set_promotion_priority_to_node_score(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397     pcmk_resource_t *child = (pcmk_resource_t *) data;
 398     const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
 399 
 400     pcmk_node_t *chosen = child->priv->fns->location(child, NULL,
 401                                                      pcmk__rsc_node_assigned);
 402 
 403     if (!pcmk_is_set(child->flags, pcmk__rsc_managed)
 404         && (child->priv->next_role == pcmk_role_promoted)) {
 405         child->priv->promotion_priority = PCMK_SCORE_INFINITY;
 406         pcmk__rsc_trace(clone,
 407                         "Final promotion priority for %s is %s "
 408                         "(unmanaged promoted)",
 409                         child->id, pcmk_readable_score(PCMK_SCORE_INFINITY));
 410 
 411     } else if (chosen == NULL) {
 412         child->priv->promotion_priority = -PCMK_SCORE_INFINITY;
 413         pcmk__rsc_trace(clone,
 414                         "Final promotion priority for %s is %s "
 415                         "(will not be active)",
 416                         child->id, pcmk_readable_score(-PCMK_SCORE_INFINITY));
 417 
 418     } else if (child->priv->promotion_priority < 0) {
 419         pcmk__rsc_trace(clone,
 420                         "Final promotion priority for %s is %s "
 421                         "(ignoring node score)",
 422                         child->id,
 423                         pcmk_readable_score(child->priv->promotion_priority));
 424 
 425     } else {
 426         const pcmk_node_t *node = NULL;
 427 
 428         node = g_hash_table_lookup(clone->priv->allowed_nodes,
 429                                    chosen->priv->id);
 430 
 431         pcmk__assert(node != NULL);
 432         child->priv->promotion_priority = node->assign->score;
 433         pcmk__rsc_trace(clone,
 434                         "Adding scores for %s: "
 435                         "final promotion priority for %s is %s",
 436                         clone->id, child->id,
 437                         pcmk_readable_score(child->priv->promotion_priority));
 438     }
 439 }
 440 
 441 /*!
 442  * \internal
 443  * \brief Sort a promotable clone's instances by descending promotion priority
 444  *
 445  * \param[in,out] clone  Promotable clone to sort
 446  */
 447 static void
 448 sort_promotable_instances(pcmk_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
 449 {
 450     GList *colocations = NULL;
 451 
 452     if (pe__set_clone_flag(clone, pcmk__clone_promotion_constrained)
 453             == pcmk_rc_already) {
 454         return;
 455     }
 456     pcmk__set_rsc_flags(clone, pcmk__rsc_updating_nodes);
 457 
 458     for (GList *iter = clone->priv->children;
 459          iter != NULL; iter = iter->next) {
 460 
 461         pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 462 
 463         pcmk__rsc_trace(clone,
 464                         "Adding scores for %s: "
 465                         "initial promotion priority for %s is %s",
 466                         clone->id, child->id,
 467                         pcmk_readable_score(child->priv->promotion_priority));
 468     }
 469     pe__show_node_scores(true, clone, "Before", clone->priv->allowed_nodes,
 470                          clone->priv->scheduler);
 471 
 472     g_list_foreach(clone->priv->children,
 473                    add_promotion_priority_to_node_score, clone);
 474 
 475     // "this with" colocations were already applied via set_instance_priority()
 476     colocations = pcmk__with_this_colocations(clone);
 477     g_list_foreach(colocations, apply_coloc_to_primary, clone);
 478     g_list_free(colocations);
 479 
 480     // Ban resource from all nodes if it needs a ticket but doesn't have it
 481     pcmk__require_promotion_tickets(clone);
 482 
 483     pe__show_node_scores(true, clone, "After", clone->priv->allowed_nodes,
 484                          clone->priv->scheduler);
 485 
 486     // Reset promotion priorities to final node scores
 487     g_list_foreach(clone->priv->children,
 488                    set_promotion_priority_to_node_score, clone);
 489 
 490     // Finally, sort instances in descending order of promotion priority
 491     clone->priv->children = g_list_sort(clone->priv->children,
 492                                         cmp_promotable_instance);
 493     pcmk__clear_rsc_flags(clone, pcmk__rsc_updating_nodes);
 494 }
 495 
 496 /*!
 497  * \internal
 498  * \brief Find the active instance (if any) of an anonymous clone on a node
 499  *
 500  * \param[in] clone  Anonymous clone to check
 501  * \param[in] id     Instance ID (without instance number) to check
 502  * \param[in] node   Node to check
 503  *
 504  * \return
 505  */
 506 static pcmk_resource_t *
 507 find_active_anon_instance(const pcmk_resource_t *clone, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 508                           const pcmk_node_t *node)
 509 {
 510     for (GList *iter = clone->priv->children; iter; iter = iter->next) {
 511         pcmk_resource_t *child = iter->data;
 512         pcmk_resource_t *active = NULL;
 513 
 514         // Use ->find_rsc() in case this is a cloned group
 515         active = clone->priv->fns->find_rsc(child, id, node,
 516                                             pcmk_rsc_match_clone_only
 517                                             |pcmk_rsc_match_current_node);
 518         if (active != NULL) {
 519             return active;
 520         }
 521     }
 522     return NULL;
 523 }
 524 
 525 /*
 526  * \brief Check whether an anonymous clone instance is known on a node
 527  *
 528  * \param[in] clone  Anonymous clone to check
 529  * \param[in] id     Instance ID (without instance number) to check
 530  * \param[in] node   Node to check
 531  *
 532  * \return true if \p id instance of \p clone is known on \p node,
 533  *         otherwise false
 534  */
 535 static bool
 536 anonymous_known_on(const pcmk_resource_t *clone, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 537                    const pcmk_node_t *node)
 538 {
 539     for (GList *iter = clone->priv->children; iter; iter = iter->next) {
 540         pcmk_resource_t *child = iter->data;
 541 
 542         /* Use ->find_rsc() because this might be a cloned group, and knowing
 543          * that other members of the group are known here implies nothing.
 544          */
 545         child = clone->priv->fns->find_rsc(child, id, NULL,
 546                                            pcmk_rsc_match_clone_only);
 547         CRM_LOG_ASSERT(child != NULL);
 548         if (child != NULL) {
 549             if (g_hash_table_lookup(child->priv->probed_nodes,
 550                                     node->priv->id)) {
 551                 return true;
 552             }
 553         }
 554     }
 555     return false;
 556 }
 557 
 558 /*!
 559  * \internal
 560  * \brief Check whether a node is allowed to run a resource
 561  *
 562  * \param[in] rsc   Resource to check
 563  * \param[in] node  Node to check
 564  *
 565  * \return true if \p node is allowed to run \p rsc, otherwise false
 566  */
 567 static bool
 568 is_allowed(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     pcmk_node_t *allowed = g_hash_table_lookup(rsc->priv->allowed_nodes,
 571                                                node->priv->id);
 572 
 573     return (allowed != NULL) && (allowed->assign->score >= 0);
 574 }
 575 
 576 /*!
 577  * \brief Check whether a clone instance's promotion score should be considered
 578  *
 579  * \param[in] rsc   Promotable clone instance to check
 580  * \param[in] node  Node where score would be applied
 581  *
 582  * \return true if \p rsc's promotion score should be considered on \p node,
 583  *         otherwise false
 584  */
 585 static bool
 586 promotion_score_applies(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 587 {
 588     char *id = clone_strip(rsc->id);
 589     const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
 590     pcmk_resource_t *active = NULL;
 591     const char *reason = "allowed";
 592 
 593     // Some checks apply only to anonymous clone instances
 594     if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
 595 
 596         // If instance is active on the node, its score definitely applies
 597         active = find_active_anon_instance(parent, id, node);
 598         if (active == rsc) {
 599             reason = "active";
 600             goto check_allowed;
 601         }
 602 
 603         /* If *no* instance is active on this node, this instance's score will
 604          * count if it has been probed on this node.
 605          */
 606         if ((active == NULL) && anonymous_known_on(parent, id, node)) {
 607             reason = "probed";
 608             goto check_allowed;
 609         }
 610     }
 611 
 612     /* If this clone's status is unknown on *all* nodes (e.g. cluster startup),
 613      * take all instances' scores into account, to make sure we use any
 614      * permanent promotion scores.
 615      */
 616     if ((rsc->priv->active_nodes == NULL)
 617         && (g_hash_table_size(rsc->priv->probed_nodes) == 0)) {
 618         reason = "none probed";
 619         goto check_allowed;
 620     }
 621 
 622     /* Otherwise, we've probed and/or started the resource *somewhere*, so
 623      * consider promotion scores on nodes where we know the status.
 624      */
 625     if ((g_hash_table_lookup(rsc->priv->probed_nodes,
 626                              node->priv->id) != NULL)
 627         || (pe_find_node_id(rsc->priv->active_nodes,
 628                             node->priv->id) != NULL)) {
 629         reason = "known";
 630     } else {
 631         pcmk__rsc_trace(rsc,
 632                         "Ignoring %s promotion score (for %s) on %s: "
 633                         "not probed",
 634                         rsc->id, id, pcmk__node_name(node));
 635         free(id);
 636         return false;
 637     }
 638 
 639 check_allowed:
 640     if (is_allowed(rsc, node)) {
 641         pcmk__rsc_trace(rsc, "Counting %s promotion score (for %s) on %s: %s",
 642                         rsc->id, id, pcmk__node_name(node), reason);
 643         free(id);
 644         return true;
 645     }
 646 
 647     pcmk__rsc_trace(rsc,
 648                     "Ignoring %s promotion score (for %s) on %s: not allowed",
 649                     rsc->id, id, pcmk__node_name(node));
 650     free(id);
 651     return false;
 652 }
 653 
 654 /*!
 655  * \internal
 656  * \brief Get the value of a promotion score node attribute
 657  *
 658  * \param[in] rsc   Promotable clone instance to get promotion score for
 659  * \param[in] node  Node to get promotion score for
 660  * \param[in] name  Resource name to use in promotion score attribute name
 661  *
 662  * \return Value of promotion score node attribute for \p rsc on \p node
 663  */
 664 static const char *
 665 promotion_attr_value(const pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 666                      const char *name)
 667 {
 668     char *attr_name = NULL;
 669     const char *attr_value = NULL;
 670     const char *target = NULL;
 671     enum pcmk__rsc_node node_type = pcmk__rsc_node_assigned;
 672 
 673     if (pcmk_is_set(rsc->flags, pcmk__rsc_unassigned)) {
 674         // Not assigned yet
 675         node_type = pcmk__rsc_node_current;
 676     }
 677     target = g_hash_table_lookup(rsc->priv->meta,
 678                                  PCMK_META_CONTAINER_ATTRIBUTE_TARGET);
 679     attr_name = pcmk_promotion_score_name(name);
 680     attr_value = pcmk__node_attr(node, attr_name, target, node_type);
 681     free(attr_name);
 682     return attr_value;
 683 }
 684 
 685 /*!
 686  * \internal
 687  * \brief Get the promotion score for a clone instance on a node
 688  *
 689  * \param[in]  rsc         Promotable clone instance to get score for
 690  * \param[in]  node        Node to get score for
 691  * \param[out] is_default  If non-NULL, will be set true if no score available
 692  *
 693  * \return Promotion score for \p rsc on \p node (or 0 if none)
 694  */
 695 static int
 696 promotion_score(const pcmk_resource_t *rsc, const pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 697                 bool *is_default)
 698 {
 699     int score = 0;
 700     int rc = pcmk_rc_ok;
 701     const char *name = NULL;
 702     const char *attr_value = NULL;
 703 
 704     if (is_default != NULL) {
 705         *is_default = true;
 706     }
 707 
 708     CRM_CHECK((rsc != NULL) && (node != NULL), return 0);
 709 
 710     /* If this is an instance of a cloned group, the promotion score is the sum
 711      * of all members' promotion scores.
 712      */
 713     if (rsc->priv->children != NULL) {
 714         int score = 0;
 715 
 716         for (const GList *iter = rsc->priv->children;
 717              iter != NULL; iter = iter->next) {
 718 
 719             const pcmk_resource_t *child = (const pcmk_resource_t *) iter->data;
 720             bool child_default = false;
 721             int child_score = promotion_score(child, node, &child_default);
 722 
 723             if (!child_default && (is_default != NULL)) {
 724                 *is_default = false;
 725             }
 726             score += child_score;
 727         }
 728         return score;
 729     }
 730 
 731     if (!promotion_score_applies(rsc, node)) {
 732         return 0;
 733     }
 734 
 735     /* For the promotion score attribute name, use the name the resource is
 736      * known as in resource history, since that's what crm_attribute --promotion
 737      * would have used.
 738      */
 739     name = pcmk__s(rsc->priv->history_id, rsc->id);
 740 
 741     attr_value = promotion_attr_value(rsc, node, name);
 742     if (attr_value != NULL) {
 743         pcmk__rsc_trace(rsc, "Promotion score for %s on %s = %s",
 744                         name, pcmk__node_name(node),
 745                         pcmk__s(attr_value, "(unset)"));
 746     } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_unique)) {
 747         /* If we don't have any resource history yet, we won't have history_id.
 748          * In that case, for anonymous clones, try the resource name without
 749          * any instance number.
 750          */
 751         char *rsc_name = clone_strip(rsc->id);
 752 
 753         if (strcmp(rsc->id, rsc_name) != 0) {
 754             attr_value = promotion_attr_value(rsc, node, rsc_name);
 755             pcmk__rsc_trace(rsc, "Promotion score for %s on %s (for %s) = %s",
 756                             rsc_name, pcmk__node_name(node), rsc->id,
 757                             pcmk__s(attr_value, "(unset)"));
 758         }
 759         free(rsc_name);
 760     }
 761 
 762     if (attr_value == NULL) {
 763         return 0;
 764     }
 765 
 766     if (is_default != NULL) {
 767         *is_default = false;
 768     }
 769 
 770     rc = pcmk_parse_score(attr_value, &score, 0);
 771     if (rc != pcmk_rc_ok) {
 772         crm_warn("Using 0 as promotion score for %s on %s "
 773                  "because '%s' is not a valid score",
 774                  rsc->id, pcmk__node_name(node), attr_value);
 775     }
 776     return score;
 777 }
 778 
 779 /*!
 780  * \internal
 781  * \brief Include promotion scores in instances' node scores and priorities
 782  *
 783  * \param[in,out] rsc  Promotable clone resource to update
 784  */
 785 void
 786 pcmk__add_promotion_scores(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 787 {
 788     if (pe__set_clone_flag(rsc,
 789                            pcmk__clone_promotion_added) == pcmk_rc_already) {
 790         return;
 791     }
 792 
 793     for (GList *iter = rsc->priv->children;
 794          iter != NULL; iter = iter->next) {
 795 
 796         pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
 797 
 798         GHashTableIter iter;
 799         pcmk_node_t *node = NULL;
 800         int score, new_score;
 801 
 802         g_hash_table_iter_init(&iter, child_rsc->priv->allowed_nodes);
 803         while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
 804             if (!pcmk__node_available(node, false, false)) {
 805                 /* This node will never be promoted, so don't apply the
 806                  * promotion score, as that may lead to clone shuffling.
 807                  */
 808                 continue;
 809             }
 810 
 811             score = promotion_score(child_rsc, node, NULL);
 812             if (score > 0) {
 813                 new_score = pcmk__add_scores(node->assign->score, score);
 814                 if (new_score != node->assign->score) { // Could remain INFINITY
 815                     node->assign->score = new_score;
 816                     pcmk__rsc_trace(rsc,
 817                                     "Added %s promotion priority (%s) to score "
 818                                     "on %s (now %s)",
 819                                     child_rsc->id, pcmk_readable_score(score),
 820                                     pcmk__node_name(node),
 821                                     pcmk_readable_score(new_score));
 822                 }
 823             }
 824 
 825             if (score > child_rsc->priv->priority) {
 826                 pcmk__rsc_trace(rsc,
 827                                 "Updating %s priority to promotion score "
 828                                 "(%d->%d)",
 829                                 child_rsc->id, child_rsc->priv->priority,
 830                                 score);
 831                 child_rsc->priv->priority = score;
 832             }
 833         }
 834     }
 835 }
 836 
 837 /*!
 838  * \internal
 839  * \brief If a resource's current role is started, change it to unpromoted
 840  *
 841  * \param[in,out] data       Resource to update
 842  * \param[in]     user_data  Ignored
 843  */
 844 static void
 845 set_current_role_unpromoted(void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 846 {
 847     pcmk_resource_t *rsc = (pcmk_resource_t *) data;
 848 
 849     if (rsc->priv->orig_role == pcmk_role_started) {
 850         // Promotable clones should use unpromoted role instead of started
 851         rsc->priv->orig_role = pcmk_role_unpromoted;
 852     }
 853     g_list_foreach(rsc->priv->children, set_current_role_unpromoted, NULL);
 854 }
 855 
 856 /*!
 857  * \internal
 858  * \brief Set a resource's next role to unpromoted (or stopped if unassigned)
 859  *
 860  * \param[in,out] data       Resource to update
 861  * \param[in]     user_data  Ignored
 862  */
 863 static void
 864 set_next_role_unpromoted(void *data, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 865 {
 866     pcmk_resource_t *rsc = (pcmk_resource_t *) data;
 867     GList *assigned = NULL;
 868 
 869     rsc->priv->fns->location(rsc, &assigned, pcmk__rsc_node_assigned);
 870     if (assigned == NULL) {
 871         pe__set_next_role(rsc, pcmk_role_stopped, "stopped instance");
 872     } else {
 873         pe__set_next_role(rsc, pcmk_role_unpromoted, "unpromoted instance");
 874         g_list_free(assigned);
 875     }
 876     g_list_foreach(rsc->priv->children, set_next_role_unpromoted, NULL);
 877 }
 878 
 879 /*!
 880  * \internal
 881  * \brief Set a resource's next role to promoted if not already set
 882  *
 883  * \param[in,out] data       Resource to update
 884  * \param[in]     user_data  Ignored
 885  */
 886 static void
 887 set_next_role_promoted(void *data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 888 {
 889     pcmk_resource_t *rsc = (pcmk_resource_t *) data;
 890 
 891     if (rsc->priv->next_role == pcmk_role_unknown) {
 892         pe__set_next_role(rsc, pcmk_role_promoted, "promoted instance");
 893     }
 894     g_list_foreach(rsc->priv->children, set_next_role_promoted, NULL);
 895 }
 896 
 897 /*!
 898  * \internal
 899  * \brief Show instance's promotion score on node where it will be active
 900  *
 901  * \param[in,out] instance  Promotable clone instance to show
 902  */
 903 static void
 904 show_promotion_score(pcmk_resource_t *instance)
     /* [previous][next][first][last][top][bottom][index][help] */
 905 {
 906     pcmk_node_t *chosen = NULL;
 907     const char *score_s = NULL;
 908 
 909     chosen = instance->priv->fns->location(instance, NULL,
 910                                            pcmk__rsc_node_assigned);
 911     score_s = pcmk_readable_score(instance->priv->promotion_priority);
 912     if (pcmk_is_set(instance->priv->scheduler->flags,
 913                     pcmk__sched_output_scores)
 914         && !pcmk__is_daemon
 915         && (instance->priv->scheduler->priv->out != NULL)) {
 916 
 917         pcmk__output_t *out = instance->priv->scheduler->priv->out;
 918 
 919         out->message(out, "promotion-score", instance, chosen, score_s);
 920 
 921     } else if (chosen == NULL) {
 922         pcmk__rsc_debug(pe__const_top_resource(instance, false),
 923                         "%s promotion score (inactive): %s (priority=%d)",
 924                         instance->id, score_s, instance->priv->priority);
 925 
 926     } else {
 927         pcmk__rsc_debug(pe__const_top_resource(instance, false),
 928                         "%s promotion score on %s: %s (priority=%d)",
 929                         instance->id, pcmk__node_name(chosen),
 930                         score_s, instance->priv->priority);
 931     }
 932 }
 933 
 934 /*!
 935  * \internal
 936  * \brief Set a clone instance's promotion priority
 937  *
 938  * \param[in,out] data       Promotable clone instance to update
 939  * \param[in]     user_data  Instance's parent clone
 940  */
 941 static void
 942 set_instance_priority(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 943 {
 944     pcmk_resource_t *instance = (pcmk_resource_t *) data;
 945     const pcmk_resource_t *clone = (const pcmk_resource_t *) user_data;
 946 
 947     const pcmk_node_t *chosen = NULL;
 948     enum rsc_role_e next_role = pcmk_role_unknown;
 949     GList *list = NULL;
 950 
 951     pcmk__rsc_trace(clone, "Assigning priority for %s: %s", instance->id,
 952                     pcmk_role_text(instance->priv->next_role));
 953 
 954     if (instance->priv->fns->state(instance, TRUE) == pcmk_role_started) {
 955         set_current_role_unpromoted(instance, NULL);
 956     }
 957 
 958     // Only an instance that will be active can be promoted
 959     chosen = instance->priv->fns->location(instance, &list,
 960                                            pcmk__rsc_node_assigned);
 961     if (pcmk__list_of_multiple(list)) {
 962         pcmk__config_err("Cannot promote non-colocated child %s",
 963                          instance->id);
 964     }
 965     g_list_free(list);
 966     if (chosen == NULL) {
 967         return;
 968     }
 969 
 970     next_role = instance->priv->fns->state(instance, FALSE);
 971     switch (next_role) {
 972         case pcmk_role_started:
 973         case pcmk_role_unknown:
 974             // Set instance priority to its promotion score (or -1 if none)
 975             {
 976                 bool is_default = false;
 977 
 978                 instance->priv->priority = promotion_score(instance, chosen,
 979                                                            &is_default);
 980                 if (is_default) {
 981                     /* Default to -1 if no value is set. This allows instances
 982                      * eligible for promotion to be specified based solely on
 983                      * PCMK_XE_RSC_LOCATION constraints, but prevents any
 984                      * instance from being promoted if neither a constraint nor
 985                      * a promotion score is present.
 986                      */
 987                     instance->priv->priority = -1;
 988                 }
 989             }
 990             break;
 991 
 992         case pcmk_role_unpromoted:
 993         case pcmk_role_stopped:
 994             // Instance can't be promoted
 995             instance->priv->priority = -PCMK_SCORE_INFINITY;
 996             break;
 997 
 998         case pcmk_role_promoted:
 999             // Nothing needed (re-creating actions after scheduling fencing)
1000             break;
1001 
1002         default:
1003             CRM_CHECK(FALSE, crm_err("Unknown resource role %d for %s",
1004                                      next_role, instance->id));
1005     }
1006 
1007     // Add relevant location constraint scores for promoted role
1008     apply_promoted_locations(instance, instance->priv->location_constraints,
1009                              chosen);
1010     apply_promoted_locations(instance, clone->priv->location_constraints,
1011                              chosen);
1012 
1013     // Consider instance's role-based colocations with other resources
1014     list = pcmk__this_with_colocations(instance);
1015     for (GList *iter = list; iter != NULL; iter = iter->next) {
1016         pcmk__colocation_t *cons = (pcmk__colocation_t *) iter->data;
1017 
1018         instance->priv->cmds->apply_coloc_score(instance, cons->primary, cons,
1019                                                 true);
1020     }
1021     g_list_free(list);
1022 
1023     instance->priv->promotion_priority = instance->priv->priority;
1024     if (next_role == pcmk_role_promoted) {
1025         instance->priv->promotion_priority = PCMK_SCORE_INFINITY;
1026     }
1027     pcmk__rsc_trace(clone, "Assigning %s priority = %d",
1028                     instance->id, instance->priv->priority);
1029 }
1030 
1031 /*!
1032  * \internal
1033  * \brief Set a promotable clone instance's role
1034  *
1035  * \param[in,out] data       Promotable clone instance to update
1036  * \param[in,out] user_data  Pointer to count of instances chosen for promotion
1037  */
1038 static void
1039 set_instance_role(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1040 {
1041     pcmk_resource_t *instance = (pcmk_resource_t *) data;
1042     int *count = (int *) user_data;
1043 
1044     const pcmk_resource_t *clone = pe__const_top_resource(instance, false);
1045     const pcmk_scheduler_t *scheduler = instance->priv->scheduler;
1046     pcmk_node_t *chosen = NULL;
1047 
1048     show_promotion_score(instance);
1049 
1050     if (instance->priv->promotion_priority < 0) {
1051         pcmk__rsc_trace(clone, "Not supposed to promote instance %s",
1052                         instance->id);
1053 
1054     } else if ((*count < pe__clone_promoted_max(instance))
1055                || !pcmk_is_set(clone->flags, pcmk__rsc_managed)) {
1056         chosen = node_to_be_promoted_on(instance);
1057     }
1058 
1059     if (chosen == NULL) {
1060         set_next_role_unpromoted(instance, NULL);
1061         return;
1062     }
1063 
1064     if ((instance->priv->orig_role < pcmk_role_promoted)
1065         && !pcmk_is_set(scheduler->flags, pcmk__sched_quorate)
1066         && (scheduler->no_quorum_policy == pcmk_no_quorum_freeze)) {
1067         crm_notice("Clone instance %s cannot be promoted without quorum",
1068                    instance->id);
1069         set_next_role_unpromoted(instance, NULL);
1070         return;
1071     }
1072 
1073     chosen->assign->count++;
1074     pcmk__rsc_info(clone, "Choosing %s (%s) on %s for promotion",
1075                    instance->id, pcmk_role_text(instance->priv->orig_role),
1076                    pcmk__node_name(chosen));
1077     set_next_role_promoted(instance, NULL);
1078     (*count)++;
1079 }
1080 
1081 /*!
1082  * \internal
1083  * \brief Set roles for all instances of a promotable clone
1084  *
1085  * \param[in,out] rsc  Promotable clone resource to update
1086  */
1087 void
1088 pcmk__set_instance_roles(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1089 {
1090     int promoted = 0;
1091     GHashTableIter iter;
1092     pcmk_node_t *node = NULL;
1093 
1094     // Repurpose count to track the number of promoted instances assigned
1095     g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
1096     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
1097         node->assign->count = 0;
1098     }
1099 
1100     // Set instances' promotion priorities and sort by highest priority first
1101     g_list_foreach(rsc->priv->children, set_instance_priority, rsc);
1102     sort_promotable_instances(rsc);
1103 
1104     // Choose the first N eligible instances to be promoted
1105     g_list_foreach(rsc->priv->children, set_instance_role, &promoted);
1106     pcmk__rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
1107                    rsc->id, promoted, pe__clone_promoted_max(rsc));
1108 }
1109 
1110 /*!
1111  *
1112  * \internal
1113  * \brief Create actions for promotable clone instances
1114  *
1115  * \param[in,out] clone          Promotable clone to create actions for
1116  * \param[out]    any_promoting  Will be set true if any instance is promoting
1117  * \param[out]    any_demoting   Will be set true if any instance is demoting
1118  */
1119 static void
1120 create_promotable_instance_actions(pcmk_resource_t *clone,
     /* [previous][next][first][last][top][bottom][index][help] */
1121                                    bool *any_promoting, bool *any_demoting)
1122 {
1123     for (GList *iter = clone->priv->children;
1124          iter != NULL; iter = iter->next) {
1125 
1126         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1127 
1128         instance->priv->cmds->create_actions(instance);
1129         check_for_role_change(instance, any_demoting, any_promoting);
1130     }
1131 }
1132 
1133 /*!
1134  * \internal
1135  * \brief Reset each promotable instance's resource priority
1136  *
1137  * Reset the priority of each instance of a promotable clone to the clone's
1138  * priority (after promotion actions are scheduled, when instance priorities
1139  * were repurposed as promotion scores).
1140  *
1141  * \param[in,out] clone  Promotable clone to reset
1142  */
1143 static void
1144 reset_instance_priorities(pcmk_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
1145 {
1146     for (GList *iter = clone->priv->children;
1147          iter != NULL; iter = iter->next) {
1148 
1149         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1150 
1151         instance->priv->priority = clone->priv->priority;
1152     }
1153 }
1154 
1155 /*!
1156  * \internal
1157  * \brief Create actions specific to promotable clones
1158  *
1159  * \param[in,out] clone  Promotable clone to create actions for
1160  */
1161 void
1162 pcmk__create_promotable_actions(pcmk_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
1163 {
1164     bool any_promoting = false;
1165     bool any_demoting = false;
1166 
1167     // Create actions for each clone instance individually
1168     create_promotable_instance_actions(clone, &any_promoting, &any_demoting);
1169 
1170     // Create pseudo-actions for clone as a whole
1171     pe__create_promotable_pseudo_ops(clone, any_promoting, any_demoting);
1172 
1173     // Undo our temporary repurposing of resource priority for instances
1174     reset_instance_priorities(clone);
1175 }
1176 
1177 /*!
1178  * \internal
1179  * \brief Create internal orderings for a promotable clone's instances
1180  *
1181  * \param[in,out] clone  Promotable clone instance to order
1182  */
1183 void
1184 pcmk__order_promotable_instances(pcmk_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
1185 {
1186     pcmk_resource_t *previous = NULL; // Needed for ordered clones
1187 
1188     pcmk__promotable_restart_ordering(clone);
1189 
1190     for (GList *iter = clone->priv->children;
1191          iter != NULL; iter = iter->next) {
1192 
1193         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1194 
1195         // Demote before promote
1196         pcmk__order_resource_actions(instance, PCMK_ACTION_DEMOTE,
1197                                      instance, PCMK_ACTION_PROMOTE,
1198                                      pcmk__ar_ordered);
1199 
1200         order_instance_promotion(clone, instance, previous);
1201         order_instance_demotion(clone, instance, previous);
1202         previous = instance;
1203     }
1204 }
1205 
1206 /*!
1207  * \internal
1208  * \brief Update dependent's allowed nodes for colocation with promotable
1209  *
1210  * \param[in,out] dependent     Dependent resource to update
1211  * \param[in]     primary       Primary resource
1212  * \param[in]     primary_node  Node where an instance of the primary will be
1213  * \param[in]     colocation    Colocation constraint to apply
1214  */
1215 static void
1216 update_dependent_allowed_nodes(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
1217                                const pcmk_resource_t *primary,
1218                                const pcmk_node_t *primary_node,
1219                                const pcmk__colocation_t *colocation)
1220 {
1221     GHashTableIter iter;
1222     pcmk_node_t *node = NULL;
1223     const char *primary_value = NULL;
1224     const char *attr = colocation->node_attribute;
1225 
1226     if (colocation->score >= PCMK_SCORE_INFINITY) {
1227         return; // Colocation is mandatory, so allowed node scores don't matter
1228     }
1229 
1230     primary_value = pcmk__colocation_node_attr(primary_node, attr, primary);
1231 
1232     pcmk__rsc_trace(colocation->primary,
1233                     "Applying %s (%s with %s on %s by %s @%d) to %s",
1234                     colocation->id, colocation->dependent->id,
1235                     colocation->primary->id, pcmk__node_name(primary_node),
1236                     attr, colocation->score, dependent->id);
1237 
1238     g_hash_table_iter_init(&iter, dependent->priv->allowed_nodes);
1239     while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
1240         const char *dependent_value = pcmk__colocation_node_attr(node, attr,
1241                                                                  dependent);
1242 
1243         if (pcmk__str_eq(primary_value, dependent_value, pcmk__str_casei)) {
1244             node->assign->score = pcmk__add_scores(node->assign->score,
1245                                                    colocation->score);
1246             pcmk__rsc_trace(colocation->primary,
1247                             "Added %s score (%s) to %s (now %s)",
1248                             colocation->id,
1249                             pcmk_readable_score(colocation->score),
1250                             pcmk__node_name(node),
1251                             pcmk_readable_score(node->assign->score));
1252         }
1253     }
1254 }
1255 
1256 /*!
1257  * \brief Update dependent for a colocation with a promotable clone
1258  *
1259  * \param[in]     primary     Primary resource in the colocation
1260  * \param[in,out] dependent   Dependent resource in the colocation
1261  * \param[in]     colocation  Colocation constraint to apply
1262  */
1263 void
1264 pcmk__update_dependent_with_promotable(const pcmk_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
1265                                        pcmk_resource_t *dependent,
1266                                        const pcmk__colocation_t *colocation)
1267 {
1268     GList *affected_nodes = NULL;
1269 
1270     /* Build a list of all nodes where an instance of the primary will be, and
1271      * (for optional colocations) update the dependent's allowed node scores for
1272      * each one.
1273      */
1274     for (GList *iter = primary->priv->children;
1275          iter != NULL; iter = iter->next) {
1276 
1277         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
1278         pcmk_node_t *node = NULL;
1279 
1280         node = instance->priv->fns->location(instance, NULL,
1281                                              pcmk__rsc_node_assigned);
1282         if (node == NULL) {
1283             continue;
1284         }
1285         if (instance->priv->fns->state(instance,
1286                                        FALSE) == colocation->primary_role) {
1287             update_dependent_allowed_nodes(dependent, primary, node,
1288                                            colocation);
1289             affected_nodes = g_list_prepend(affected_nodes, node);
1290         }
1291     }
1292 
1293     /* For mandatory colocations, add the primary's node score to the
1294      * dependent's node score for each affected node, and ban the dependent
1295      * from all other nodes.
1296      *
1297      * However, skip this for promoted-with-promoted colocations, otherwise
1298      * inactive dependent instances can't start (in the unpromoted role).
1299      */
1300     if ((colocation->score >= PCMK_SCORE_INFINITY)
1301         && ((colocation->dependent_role != pcmk_role_promoted)
1302             || (colocation->primary_role != pcmk_role_promoted))) {
1303 
1304         pcmk__rsc_trace(colocation->primary,
1305                         "Applying %s (mandatory %s with %s) to %s",
1306                         colocation->id, colocation->dependent->id,
1307                         colocation->primary->id, dependent->id);
1308         pcmk__colocation_intersect_nodes(dependent, primary, colocation,
1309                                          affected_nodes, true);
1310     }
1311     g_list_free(affected_nodes);
1312 }
1313 
1314 /*!
1315  * \internal
1316  * \brief Update dependent priority for colocation with promotable
1317  *
1318  * \param[in]     primary     Primary resource in the colocation
1319  * \param[in,out] dependent   Dependent resource in the colocation
1320  * \param[in]     colocation  Colocation constraint to apply
1321  *
1322  * \return The score added to the dependent's priority
1323  */
1324 int
1325 pcmk__update_promotable_dependent_priority(const pcmk_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
1326                                            pcmk_resource_t *dependent,
1327                                            const pcmk__colocation_t *colocation)
1328 {
1329     pcmk_resource_t *primary_instance = NULL;
1330 
1331     // Look for a primary instance where dependent will be
1332     primary_instance = pcmk__find_compatible_instance(dependent, primary,
1333                                                       colocation->primary_role,
1334                                                       false);
1335 
1336     if (primary_instance != NULL) {
1337         // Add primary instance's priority to dependent's
1338         int new_priority = pcmk__add_scores(dependent->priv->priority,
1339                                             colocation->score);
1340 
1341         pcmk__rsc_trace(colocation->primary,
1342                         "Applying %s (%s with %s) to %s priority "
1343                         "(%s + %s = %s)",
1344                         colocation->id, colocation->dependent->id,
1345                         colocation->primary->id, dependent->id,
1346                         pcmk_readable_score(dependent->priv->priority),
1347                         pcmk_readable_score(colocation->score),
1348                         pcmk_readable_score(new_priority));
1349         dependent->priv->priority = new_priority;
1350         return colocation->score;
1351     }
1352 
1353     if (colocation->score >= PCMK_SCORE_INFINITY) {
1354         // Mandatory colocation, but primary won't be here
1355         pcmk__rsc_trace(colocation->primary,
1356                         "Applying %s (%s with %s) to %s: can't be promoted",
1357                         colocation->id, colocation->dependent->id,
1358                         colocation->primary->id, dependent->id);
1359         dependent->priv->priority = -PCMK_SCORE_INFINITY;
1360         return -PCMK_SCORE_INFINITY;
1361     }
1362     return 0;
1363 }

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