LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_promotable.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 467 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 32 0.0 %

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

Generated by: LCOV version 1.14