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_sort_index_to_node_score
  8. apply_coloc_to_dependent
  9. apply_coloc_to_primary
  10. set_sort_index_to_node_score
  11. sort_promotable_instances
  12. find_active_anon_instance
  13. anonymous_known_on
  14. is_allowed
  15. promotion_score_applies
  16. promotion_attr_value
  17. promotion_score
  18. pcmk__add_promotion_scores
  19. set_current_role_unpromoted
  20. set_next_role_unpromoted
  21. set_next_role_promoted
  22. show_promotion_score
  23. set_instance_priority
  24. set_instance_role
  25. pcmk__set_instance_roles
  26. create_promotable_instance_actions
  27. reset_instance_priorities
  28. pcmk__create_promotable_actions
  29. pcmk__order_promotable_instances
  30. update_dependent_allowed_nodes
  31. pcmk__update_dependent_with_promotable
  32. pcmk__update_promotable_dependent_priority

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

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