LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_group.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 312 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 17 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 <stdbool.h>
      13             : 
      14             : #include <crm/common/xml.h>
      15             : 
      16             : #include <pacemaker-internal.h>
      17             : #include "libpacemaker_private.h"
      18             : 
      19             : /*!
      20             :  * \internal
      21             :  * \brief Assign a group resource to a node
      22             :  *
      23             :  * \param[in,out] rsc           Group resource to assign to a node
      24             :  * \param[in]     prefer        Node to prefer, if all else is equal
      25             :  * \param[in]     stop_if_fail  If \c true and a child of \p rsc can't be
      26             :  *                              assigned to a node, set the child's next role to
      27             :  *                              stopped and update existing actions
      28             :  *
      29             :  * \return Node that \p rsc is assigned to, if assigned entirely to one node
      30             :  *
      31             :  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
      32             :  *       completely undo the assignment. A successful assignment can be either
      33             :  *       undone or left alone as final. A failed assignment has the same effect
      34             :  *       as calling pcmk__unassign_resource(); there are no side effects on
      35             :  *       roles or actions.
      36             :  */
      37             : pcmk_node_t *
      38           0 : pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
      39             :                    bool stop_if_fail)
      40             : {
      41           0 :     pcmk_node_t *first_assigned_node = NULL;
      42           0 :     pcmk_resource_t *first_member = NULL;
      43             : 
      44           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
      45             : 
      46           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
      47           0 :         return rsc->allocated_to; // Assignment already done
      48             :     }
      49           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) {
      50           0 :         pcmk__rsc_debug(rsc, "Assignment dependency loop detected involving %s",
      51             :                         rsc->id);
      52           0 :         return NULL;
      53             :     }
      54             : 
      55           0 :     if (rsc->children == NULL) {
      56             :         // No members to assign
      57           0 :         pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned);
      58           0 :         return NULL;
      59             :     }
      60             : 
      61           0 :     pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning);
      62           0 :     first_member = (pcmk_resource_t *) rsc->children->data;
      63           0 :     rsc->role = first_member->role;
      64             : 
      65           0 :     pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
      66             :                                       pcmk_sched_output_scores),
      67             :                          rsc, __func__, rsc->allowed_nodes, rsc->cluster);
      68             : 
      69           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
      70           0 :         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
      71           0 :         pcmk_node_t *node = NULL;
      72             : 
      73           0 :         pcmk__rsc_trace(rsc, "Assigning group %s member %s",
      74             :                         rsc->id, member->id);
      75           0 :         node = member->cmds->assign(member, prefer, stop_if_fail);
      76           0 :         if (first_assigned_node == NULL) {
      77           0 :             first_assigned_node = node;
      78             :         }
      79             :     }
      80             : 
      81           0 :     pe__set_next_role(rsc, first_member->next_role, "first group member");
      82           0 :     pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned);
      83             : 
      84           0 :     if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) {
      85           0 :         return NULL;
      86             :     }
      87           0 :     return first_assigned_node;
      88             : }
      89             : 
      90             : /*!
      91             :  * \internal
      92             :  * \brief Create a pseudo-operation for a group as an ordering point
      93             :  *
      94             :  * \param[in,out] group   Group resource to create action for
      95             :  * \param[in]     action  Action name
      96             :  *
      97             :  * \return Newly created pseudo-operation
      98             :  */
      99             : static pcmk_action_t *
     100           0 : create_group_pseudo_op(pcmk_resource_t *group, const char *action)
     101             : {
     102           0 :     pcmk_action_t *op = custom_action(group, pcmk__op_key(group->id, action, 0),
     103             :                                       action, NULL, TRUE, group->cluster);
     104             : 
     105           0 :     pcmk__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable);
     106           0 :     return op;
     107             : }
     108             : 
     109             : /*!
     110             :  * \internal
     111             :  * \brief Create all actions needed for a given group resource
     112             :  *
     113             :  * \param[in,out] rsc  Group resource to create actions for
     114             :  */
     115             : void
     116           0 : pcmk__group_create_actions(pcmk_resource_t *rsc)
     117             : {
     118           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
     119             : 
     120           0 :     pcmk__rsc_trace(rsc, "Creating actions for group %s", rsc->id);
     121             : 
     122             :     // Create actions for individual group members
     123           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     124           0 :         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
     125             : 
     126           0 :         member->cmds->create_actions(member);
     127             :     }
     128             : 
     129             :     // Create pseudo-actions for group itself to serve as ordering points
     130           0 :     create_group_pseudo_op(rsc, PCMK_ACTION_START);
     131           0 :     create_group_pseudo_op(rsc, PCMK_ACTION_RUNNING);
     132           0 :     create_group_pseudo_op(rsc, PCMK_ACTION_STOP);
     133           0 :     create_group_pseudo_op(rsc, PCMK_ACTION_STOPPED);
     134           0 :     if (crm_is_true(g_hash_table_lookup(rsc->meta, PCMK_META_PROMOTABLE))) {
     135           0 :         create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTE);
     136           0 :         create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTED);
     137           0 :         create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTE);
     138           0 :         create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTED);
     139             :     }
     140           0 : }
     141             : 
     142             : // User data for member_internal_constraints()
     143             : struct member_data {
     144             :     // These could be derived from member but this avoids some function calls
     145             :     bool ordered;
     146             :     bool colocated;
     147             :     bool promotable;
     148             : 
     149             :     pcmk_resource_t *last_active;
     150             :     pcmk_resource_t *previous_member;
     151             : };
     152             : 
     153             : /*!
     154             :  * \internal
     155             :  * \brief Create implicit constraints needed for a group member
     156             :  *
     157             :  * \param[in,out] data       Group member to create implicit constraints for
     158             :  * \param[in,out] user_data  Member data (struct member_data *)
     159             :  */
     160             : static void
     161           0 : member_internal_constraints(gpointer data, gpointer user_data)
     162             : {
     163           0 :     pcmk_resource_t *member = (pcmk_resource_t *) data;
     164           0 :     struct member_data *member_data = (struct member_data *) user_data;
     165             : 
     166             :     // For ordering demote vs demote or stop vs stop
     167           0 :     uint32_t down_flags = pcmk__ar_then_implies_first_graphed;
     168             : 
     169             :     // For ordering demote vs demoted or stop vs stopped
     170           0 :     uint32_t post_down_flags = pcmk__ar_first_implies_then_graphed;
     171             : 
     172             :     // Create the individual member's implicit constraints
     173           0 :     member->cmds->internal_constraints(member);
     174             : 
     175           0 :     if (member_data->previous_member == NULL) {
     176             :         // This is first member
     177           0 :         if (member_data->ordered) {
     178           0 :             pcmk__set_relation_flags(down_flags, pcmk__ar_ordered);
     179           0 :             post_down_flags = pcmk__ar_first_implies_then;
     180             :         }
     181             : 
     182           0 :     } else if (member_data->colocated) {
     183           0 :         uint32_t flags = pcmk__coloc_none;
     184             : 
     185           0 :         if (pcmk_is_set(member->flags, pcmk_rsc_critical)) {
     186           0 :             flags |= pcmk__coloc_influence;
     187             :         }
     188             : 
     189             :         // Colocate this member with the previous one
     190           0 :         pcmk__new_colocation("#group-members", NULL, PCMK_SCORE_INFINITY,
     191             :                              member, member_data->previous_member, NULL, NULL,
     192             :                              flags);
     193             :     }
     194             : 
     195           0 :     if (member_data->promotable) {
     196             :         // Demote group -> demote member -> group is demoted
     197           0 :         pcmk__order_resource_actions(member->parent, PCMK_ACTION_DEMOTE,
     198             :                                      member, PCMK_ACTION_DEMOTE, down_flags);
     199           0 :         pcmk__order_resource_actions(member, PCMK_ACTION_DEMOTE,
     200             :                                      member->parent, PCMK_ACTION_DEMOTED,
     201             :                                      post_down_flags);
     202             : 
     203             :         // Promote group -> promote member -> group is promoted
     204           0 :         pcmk__order_resource_actions(member, PCMK_ACTION_PROMOTE,
     205             :                                      member->parent, PCMK_ACTION_PROMOTED,
     206             :                                      pcmk__ar_unrunnable_first_blocks
     207             :                                      |pcmk__ar_first_implies_then
     208             :                                      |pcmk__ar_first_implies_then_graphed);
     209           0 :         pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE,
     210             :                                      member, PCMK_ACTION_PROMOTE,
     211             :                                      pcmk__ar_then_implies_first_graphed);
     212             :     }
     213             : 
     214             :     // Stop group -> stop member -> group is stopped
     215           0 :     pcmk__order_stops(member->parent, member, down_flags);
     216           0 :     pcmk__order_resource_actions(member, PCMK_ACTION_STOP,
     217             :                                  member->parent, PCMK_ACTION_STOPPED,
     218             :                                  post_down_flags);
     219             : 
     220             :     // Start group -> start member -> group is started
     221           0 :     pcmk__order_starts(member->parent, member,
     222             :                        pcmk__ar_then_implies_first_graphed);
     223           0 :     pcmk__order_resource_actions(member, PCMK_ACTION_START,
     224             :                                  member->parent, PCMK_ACTION_RUNNING,
     225             :                                  pcmk__ar_unrunnable_first_blocks
     226             :                                  |pcmk__ar_first_implies_then
     227             :                                  |pcmk__ar_first_implies_then_graphed);
     228             : 
     229           0 :     if (!member_data->ordered) {
     230           0 :         pcmk__order_starts(member->parent, member,
     231             :                            pcmk__ar_first_implies_then
     232             :                            |pcmk__ar_unrunnable_first_blocks
     233             :                            |pcmk__ar_then_implies_first_graphed);
     234           0 :         if (member_data->promotable) {
     235           0 :             pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE,
     236             :                                          member, PCMK_ACTION_PROMOTE,
     237             :                                          pcmk__ar_first_implies_then
     238             :                                          |pcmk__ar_unrunnable_first_blocks
     239             :                                          |pcmk__ar_then_implies_first_graphed);
     240             :         }
     241             : 
     242           0 :     } else if (member_data->previous_member == NULL) {
     243           0 :         pcmk__order_starts(member->parent, member, pcmk__ar_none);
     244           0 :         if (member_data->promotable) {
     245           0 :             pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE,
     246             :                                          member, PCMK_ACTION_PROMOTE,
     247             :                                          pcmk__ar_none);
     248             :         }
     249             : 
     250             :     } else {
     251             :         // Order this member relative to the previous one
     252             : 
     253           0 :         pcmk__order_starts(member_data->previous_member, member,
     254             :                            pcmk__ar_first_implies_then
     255             :                            |pcmk__ar_unrunnable_first_blocks);
     256           0 :         pcmk__order_stops(member, member_data->previous_member,
     257             :                           pcmk__ar_ordered|pcmk__ar_intermediate_stop);
     258             : 
     259             :         /* In unusual circumstances (such as adding a new member to the middle
     260             :          * of a group with unmanaged later members), this member may be active
     261             :          * while the previous (new) member is inactive. In this situation, the
     262             :          * usual restart orderings will be irrelevant, so we need to order this
     263             :          * member's stop before the previous member's start.
     264             :          */
     265           0 :         if ((member->running_on != NULL)
     266           0 :             && (member_data->previous_member->running_on == NULL)) {
     267           0 :             pcmk__order_resource_actions(member, PCMK_ACTION_STOP,
     268             :                                          member_data->previous_member,
     269             :                                          PCMK_ACTION_START,
     270             :                                          pcmk__ar_then_implies_first
     271             :                                          |pcmk__ar_unrunnable_first_blocks);
     272             :         }
     273             : 
     274           0 :         if (member_data->promotable) {
     275           0 :             pcmk__order_resource_actions(member_data->previous_member,
     276             :                                          PCMK_ACTION_PROMOTE, member,
     277             :                                          PCMK_ACTION_PROMOTE,
     278             :                                          pcmk__ar_first_implies_then
     279             :                                          |pcmk__ar_unrunnable_first_blocks);
     280           0 :             pcmk__order_resource_actions(member, PCMK_ACTION_DEMOTE,
     281             :                                          member_data->previous_member,
     282             :                                          PCMK_ACTION_DEMOTE, pcmk__ar_ordered);
     283             :         }
     284             :     }
     285             : 
     286             :     // Make sure partially active groups shut down in sequence
     287           0 :     if (member->running_on != NULL) {
     288           0 :         if (member_data->ordered && (member_data->previous_member != NULL)
     289           0 :             && (member_data->previous_member->running_on == NULL)
     290           0 :             && (member_data->last_active != NULL)
     291           0 :             && (member_data->last_active->running_on != NULL)) {
     292           0 :             pcmk__order_stops(member, member_data->last_active,
     293             :                               pcmk__ar_ordered);
     294             :         }
     295           0 :         member_data->last_active = member;
     296             :     }
     297             : 
     298           0 :     member_data->previous_member = member;
     299           0 : }
     300             : 
     301             : /*!
     302             :  * \internal
     303             :  * \brief Create implicit constraints needed for a group resource
     304             :  *
     305             :  * \param[in,out] rsc  Group resource to create implicit constraints for
     306             :  */
     307             : void
     308           0 : pcmk__group_internal_constraints(pcmk_resource_t *rsc)
     309             : {
     310           0 :     struct member_data member_data = { false, };
     311           0 :     const pcmk_resource_t *top = NULL;
     312             : 
     313           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
     314             : 
     315             :     /* Order group pseudo-actions relative to each other for restarting:
     316             :      * stop group -> group is stopped -> start group -> group is started
     317             :      */
     318           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP,
     319             :                                  rsc, PCMK_ACTION_STOPPED,
     320             :                                  pcmk__ar_unrunnable_first_blocks);
     321           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
     322             :                                  rsc, PCMK_ACTION_START,
     323             :                                  pcmk__ar_ordered);
     324           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_START,
     325             :                                  rsc, PCMK_ACTION_RUNNING,
     326             :                                  pcmk__ar_unrunnable_first_blocks);
     327             : 
     328           0 :     top = pe__const_top_resource(rsc, false);
     329             : 
     330           0 :     member_data.ordered = pe__group_flag_is_set(rsc, pcmk__group_ordered);
     331           0 :     member_data.colocated = pe__group_flag_is_set(rsc, pcmk__group_colocated);
     332           0 :     member_data.promotable = pcmk_is_set(top->flags, pcmk_rsc_promotable);
     333           0 :     g_list_foreach(rsc->children, member_internal_constraints, &member_data);
     334           0 : }
     335             : 
     336             : /*!
     337             :  * \internal
     338             :  * \brief Apply a colocation's score to node scores or resource priority
     339             :  *
     340             :  * Given a colocation constraint for a group with some other resource, apply the
     341             :  * score to the dependent's allowed node scores (if we are still placing
     342             :  * resources) or priority (if we are choosing promotable clone instance roles).
     343             :  *
     344             :  * \param[in,out] dependent      Dependent group resource in colocation
     345             :  * \param[in]     primary        Primary resource in colocation
     346             :  * \param[in]     colocation     Colocation constraint to apply
     347             :  */
     348             : static void
     349           0 : colocate_group_with(pcmk_resource_t *dependent, const pcmk_resource_t *primary,
     350             :                     const pcmk__colocation_t *colocation)
     351             : {
     352           0 :     pcmk_resource_t *member = NULL;
     353             : 
     354           0 :     if (dependent->children == NULL) {
     355           0 :         return;
     356             :     }
     357             : 
     358           0 :     pcmk__rsc_trace(primary, "Processing %s (group %s with %s) for dependent",
     359             :                     colocation->id, dependent->id, primary->id);
     360             : 
     361           0 :     if (pe__group_flag_is_set(dependent, pcmk__group_colocated)) {
     362             :         // Colocate first member (internal colocations will handle the rest)
     363           0 :         member = (pcmk_resource_t *) dependent->children->data;
     364           0 :         member->cmds->apply_coloc_score(member, primary, colocation, true);
     365           0 :         return;
     366             :     }
     367             : 
     368           0 :     if (colocation->score >= PCMK_SCORE_INFINITY) {
     369           0 :         pcmk__config_err("%s: Cannot perform mandatory colocation between "
     370             :                          "non-colocated group and %s",
     371             :                          dependent->id, primary->id);
     372           0 :         return;
     373             :     }
     374             : 
     375             :     // Colocate each member individually
     376           0 :     for (GList *iter = dependent->children; iter != NULL; iter = iter->next) {
     377           0 :         member = (pcmk_resource_t *) iter->data;
     378           0 :         member->cmds->apply_coloc_score(member, primary, colocation, true);
     379             :     }
     380             : }
     381             : 
     382             : /*!
     383             :  * \internal
     384             :  * \brief Apply a colocation's score to node scores or resource priority
     385             :  *
     386             :  * Given a colocation constraint for some other resource with a group, apply the
     387             :  * score to the dependent's allowed node scores (if we are still placing
     388             :  * resources) or priority (if we are choosing promotable clone instance roles).
     389             :  *
     390             :  * \param[in,out] dependent      Dependent resource in colocation
     391             :  * \param[in]     primary        Primary group resource in colocation
     392             :  * \param[in]     colocation     Colocation constraint to apply
     393             :  */
     394             : static void
     395           0 : colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary,
     396             :                     const pcmk__colocation_t *colocation)
     397             : {
     398           0 :     const pcmk_resource_t *member = NULL;
     399             : 
     400           0 :     pcmk__rsc_trace(primary,
     401             :                     "Processing colocation %s (%s with group %s) for primary",
     402             :                     colocation->id, dependent->id, primary->id);
     403             : 
     404           0 :     if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
     405           0 :         return;
     406             :     }
     407             : 
     408           0 :     if (pe__group_flag_is_set(primary, pcmk__group_colocated)) {
     409             : 
     410           0 :         if (colocation->score >= PCMK_SCORE_INFINITY) {
     411             :             /* For mandatory colocations, the entire group must be assignable
     412             :              * (and in the specified role if any), so apply the colocation based
     413             :              * on the last member.
     414             :              */
     415           0 :             member = pe__last_group_member(primary);
     416           0 :         } else if (primary->children != NULL) {
     417             :             /* For optional colocations, whether the group is partially or fully
     418             :              * up doesn't matter, so apply the colocation based on the first
     419             :              * member.
     420             :              */
     421           0 :             member = (pcmk_resource_t *) primary->children->data;
     422             :         }
     423           0 :         if (member == NULL) {
     424           0 :             return; // Nothing to colocate with
     425             :         }
     426             : 
     427           0 :         member->cmds->apply_coloc_score(dependent, member, colocation, false);
     428           0 :         return;
     429             :     }
     430             : 
     431           0 :     if (colocation->score >= PCMK_SCORE_INFINITY) {
     432           0 :         pcmk__config_err("%s: Cannot perform mandatory colocation with"
     433             :                          " non-colocated group %s",
     434             :                          dependent->id, primary->id);
     435           0 :         return;
     436             :     }
     437             : 
     438             :     // Colocate dependent with each member individually
     439           0 :     for (const GList *iter = primary->children; iter != NULL;
     440           0 :          iter = iter->next) {
     441           0 :         member = iter->data;
     442           0 :         member->cmds->apply_coloc_score(dependent, member, colocation, false);
     443             :     }
     444             : }
     445             : 
     446             : /*!
     447             :  * \internal
     448             :  * \brief Apply a colocation's score to node scores or resource priority
     449             :  *
     450             :  * Given a colocation constraint, apply its score to the dependent's
     451             :  * allowed node scores (if we are still placing resources) or priority (if
     452             :  * we are choosing promotable clone instance roles).
     453             :  *
     454             :  * \param[in,out] dependent      Dependent resource in colocation
     455             :  * \param[in]     primary        Primary resource in colocation
     456             :  * \param[in]     colocation     Colocation constraint to apply
     457             :  * \param[in]     for_dependent  true if called on behalf of dependent
     458             :  */
     459             : void
     460           0 : pcmk__group_apply_coloc_score(pcmk_resource_t *dependent,
     461             :                               const pcmk_resource_t *primary,
     462             :                               const pcmk__colocation_t *colocation,
     463             :                               bool for_dependent)
     464             : {
     465           0 :     CRM_ASSERT((dependent != NULL) && (primary != NULL)
     466             :                && (colocation != NULL));
     467             : 
     468           0 :     if (for_dependent) {
     469           0 :         colocate_group_with(dependent, primary, colocation);
     470             : 
     471             :     } else {
     472             :         // Method should only be called for primitive dependents
     473           0 :         CRM_ASSERT(dependent->variant == pcmk_rsc_variant_primitive);
     474             : 
     475           0 :         colocate_with_group(dependent, primary, colocation);
     476             :     }
     477           0 : }
     478             : 
     479             : /*!
     480             :  * \internal
     481             :  * \brief Return action flags for a given group resource action
     482             :  *
     483             :  * \param[in,out] action  Group action to get flags for
     484             :  * \param[in]     node    If not NULL, limit effects to this node
     485             :  *
     486             :  * \return Flags appropriate to \p action on \p node
     487             :  */
     488             : uint32_t
     489           0 : pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     490             : {
     491             :     // Default flags for a group action
     492           0 :     uint32_t flags = pcmk_action_optional
     493             :                      |pcmk_action_runnable
     494             :                      |pcmk_action_pseudo;
     495             : 
     496           0 :     CRM_ASSERT(action != NULL);
     497             : 
     498             :     // Update flags considering each member's own flags for same action
     499           0 :     for (GList *iter = action->rsc->children; iter != NULL; iter = iter->next) {
     500           0 :         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
     501             : 
     502             :         // Check whether member has the same action
     503           0 :         enum action_tasks task = get_complex_task(member, action->task);
     504           0 :         const char *task_s = pcmk_action_text(task);
     505           0 :         pcmk_action_t *member_action = find_first_action(member->actions, NULL,
     506             :                                                          task_s, node);
     507             : 
     508           0 :         if (member_action != NULL) {
     509           0 :             uint32_t member_flags = member->cmds->action_flags(member_action,
     510             :                                                                node);
     511             : 
     512             :             // Group action is mandatory if any member action is
     513           0 :             if (pcmk_is_set(flags, pcmk_action_optional)
     514           0 :                 && !pcmk_is_set(member_flags, pcmk_action_optional)) {
     515           0 :                 pcmk__rsc_trace(action->rsc, "%s is mandatory because %s is",
     516             :                                 action->uuid, member_action->uuid);
     517           0 :                 pcmk__clear_raw_action_flags(flags, "group action",
     518             :                                              pcmk_action_optional);
     519           0 :                 pcmk__clear_action_flags(action, pcmk_action_optional);
     520             :             }
     521             : 
     522             :             // Group action is unrunnable if any member action is
     523           0 :             if (!pcmk__str_eq(task_s, action->task, pcmk__str_none)
     524           0 :                 && pcmk_is_set(flags, pcmk_action_runnable)
     525           0 :                 && !pcmk_is_set(member_flags, pcmk_action_runnable)) {
     526             : 
     527           0 :                 pcmk__rsc_trace(action->rsc, "%s is unrunnable because %s is",
     528             :                                 action->uuid, member_action->uuid);
     529           0 :                 pcmk__clear_raw_action_flags(flags, "group action",
     530             :                                              pcmk_action_runnable);
     531           0 :                 pcmk__clear_action_flags(action, pcmk_action_runnable);
     532             :             }
     533             : 
     534             :         /* Group (pseudo-)actions other than stop or demote are unrunnable
     535             :          * unless every member will do it.
     536             :          */
     537           0 :         } else if ((task != pcmk_action_stop) && (task != pcmk_action_demote)) {
     538           0 :             pcmk__rsc_trace(action->rsc,
     539             :                             "%s is not runnable because %s will not %s",
     540             :                             action->uuid, member->id, task_s);
     541           0 :             pcmk__clear_raw_action_flags(flags, "group action",
     542             :                                          pcmk_action_runnable);
     543             :         }
     544             :     }
     545             : 
     546           0 :     return flags;
     547             : }
     548             : 
     549             : /*!
     550             :  * \internal
     551             :  * \brief Update two actions according to an ordering between them
     552             :  *
     553             :  * Given information about an ordering of two actions, update the actions' flags
     554             :  * (and runnable_before members if appropriate) as appropriate for the ordering.
     555             :  * Effects may cascade to other orderings involving the actions as well.
     556             :  *
     557             :  * \param[in,out] first      'First' action in an ordering
     558             :  * \param[in,out] then       'Then' action in an ordering
     559             :  * \param[in]     node       If not NULL, limit scope of ordering to this node
     560             :  *                           (only used when interleaving instances)
     561             :  * \param[in]     flags      Action flags for \p first for ordering purposes
     562             :  * \param[in]     filter     Action flags to limit scope of certain updates (may
     563             :  *                           include pcmk_action_optional to affect only
     564             :  *                           mandatory actions, and pcmk_action_runnable to
     565             :  *                           affect only runnable actions)
     566             :  * \param[in]     type       Group of enum pcmk__action_relation_flags to apply
     567             :  * \param[in,out] scheduler  Scheduler data
     568             :  *
     569             :  * \return Group of enum pcmk__updated flags indicating what was updated
     570             :  */
     571             : uint32_t
     572           0 : pcmk__group_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then,
     573             :                                    const pcmk_node_t *node, uint32_t flags,
     574             :                                    uint32_t filter, uint32_t type,
     575             :                                    pcmk_scheduler_t *scheduler)
     576             : {
     577           0 :     uint32_t changed = pcmk__updated_none;
     578             : 
     579             :     // Group method can be called only on behalf of "then" action
     580           0 :     CRM_ASSERT((first != NULL) && (then != NULL) && (then->rsc != NULL)
     581             :                && (scheduler != NULL));
     582             : 
     583             :     // Update the actions for the group itself
     584           0 :     changed |= pcmk__update_ordered_actions(first, then, node, flags, filter,
     585             :                                             type, scheduler);
     586             : 
     587             :     // Update the actions for each group member
     588           0 :     for (GList *iter = then->rsc->children; iter != NULL; iter = iter->next) {
     589           0 :         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
     590             : 
     591           0 :         pcmk_action_t *member_action = find_first_action(member->actions, NULL,
     592           0 :                                                          then->task, node);
     593             : 
     594           0 :         if (member_action != NULL) {
     595           0 :             changed |= member->cmds->update_ordered_actions(first,
     596             :                                                             member_action, node,
     597             :                                                             flags, filter, type,
     598             :                                                             scheduler);
     599             :         }
     600             :     }
     601           0 :     return changed;
     602             : }
     603             : 
     604             : /*!
     605             :  * \internal
     606             :  * \brief Apply a location constraint to a group's allowed node scores
     607             :  *
     608             :  * \param[in,out] rsc       Group resource to apply constraint to
     609             :  * \param[in,out] location  Location constraint to apply
     610             :  */
     611             : void
     612           0 : pcmk__group_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     613             : {
     614           0 :     GList *node_list_orig = NULL;
     615           0 :     GList *node_list_copy = NULL;
     616           0 :     bool reset_scores = true;
     617             : 
     618           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
     619             :                && (location != NULL));
     620             : 
     621           0 :     node_list_orig = location->nodes;
     622           0 :     node_list_copy = pcmk__copy_node_list(node_list_orig, true);
     623           0 :     reset_scores = pe__group_flag_is_set(rsc, pcmk__group_colocated);
     624             : 
     625             :     // Apply the constraint for the group itself (updates node scores)
     626           0 :     pcmk__apply_location(rsc, location);
     627             : 
     628             :     // Apply the constraint for each member
     629           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     630           0 :         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
     631             : 
     632           0 :         member->cmds->apply_location(member, location);
     633             : 
     634           0 :         if (reset_scores) {
     635             :             /* The first member of colocated groups needs to use the original
     636             :              * node scores, but subsequent members should work on a copy, since
     637             :              * the first member's scores already incorporate theirs.
     638             :              */
     639           0 :             reset_scores = false;
     640           0 :             location->nodes = node_list_copy;
     641             :         }
     642             :     }
     643             : 
     644           0 :     location->nodes = node_list_orig;
     645           0 :     g_list_free_full(node_list_copy, free);
     646           0 : }
     647             : 
     648             : // Group implementation of pcmk_assignment_methods_t:colocated_resources()
     649             : GList *
     650           0 : pcmk__group_colocated_resources(const pcmk_resource_t *rsc,
     651             :                                 const pcmk_resource_t *orig_rsc,
     652             :                                 GList *colocated_rscs)
     653             : {
     654           0 :     const pcmk_resource_t *member = NULL;
     655             : 
     656           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
     657             : 
     658           0 :     if (orig_rsc == NULL) {
     659           0 :         orig_rsc = rsc;
     660             :     }
     661             : 
     662           0 :     if (pe__group_flag_is_set(rsc, pcmk__group_colocated)
     663           0 :         || pcmk__is_clone(rsc->parent)) {
     664             :         /* This group has colocated members and/or is cloned -- either way,
     665             :          * add every child's colocated resources to the list. The first and last
     666             :          * members will include the group's own colocations.
     667             :          */
     668           0 :         colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
     669           0 :         for (const GList *iter = rsc->children;
     670           0 :              iter != NULL; iter = iter->next) {
     671             : 
     672           0 :             member = (const pcmk_resource_t *) iter->data;
     673           0 :             colocated_rscs = member->cmds->colocated_resources(member, orig_rsc,
     674             :                                                                colocated_rscs);
     675             :         }
     676             : 
     677           0 :     } else if (rsc->children != NULL) {
     678             :         /* This group's members are not colocated, and the group is not cloned,
     679             :          * so just add the group's own colocations to the list.
     680             :          */
     681           0 :         colocated_rscs = pcmk__colocated_resources(rsc, orig_rsc,
     682             :                                                    colocated_rscs);
     683             :     }
     684             : 
     685           0 :     return colocated_rscs;
     686             : }
     687             : 
     688             : // Group implementation of pcmk_assignment_methods_t:with_this_colocations()
     689             : void
     690           0 : pcmk__with_group_colocations(const pcmk_resource_t *rsc,
     691             :                              const pcmk_resource_t *orig_rsc, GList **list)
     692             : 
     693             : {
     694           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
     695             :                && (orig_rsc != NULL) && (list != NULL));
     696             : 
     697             :     // Ignore empty groups
     698           0 :     if (rsc->children == NULL) {
     699           0 :         return;
     700             :     }
     701             : 
     702             :     /* "With this" colocations are needed only for the group itself and for its
     703             :      * last member. (Previous members will chain via the group internal
     704             :      * colocations.)
     705             :      */
     706           0 :     if ((orig_rsc != rsc) && (orig_rsc != pe__last_group_member(rsc))) {
     707           0 :         return;
     708             :     }
     709             : 
     710           0 :     pcmk__rsc_trace(rsc, "Adding 'with %s' colocations to list for %s",
     711             :                     rsc->id, orig_rsc->id);
     712             : 
     713             :     // Add the group's own colocations
     714           0 :     pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
     715             : 
     716             :     // If cloned, add any relevant colocations with the clone
     717           0 :     if (rsc->parent != NULL) {
     718           0 :         rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc,
     719             :                                                  list);
     720             :     }
     721             : 
     722           0 :     if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) {
     723             :         // @COMPAT Non-colocated groups are deprecated
     724           0 :         return;
     725             :     }
     726             : 
     727             :     // Add explicit colocations with the group's (other) children
     728           0 :     for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     729           0 :         const pcmk_resource_t *member = iter->data;
     730             : 
     731           0 :         if (member != orig_rsc) {
     732           0 :             member->cmds->with_this_colocations(member, orig_rsc, list);
     733             :         }
     734             :     }
     735             : }
     736             : 
     737             : // Group implementation of pcmk_assignment_methods_t:this_with_colocations()
     738             : void
     739           0 : pcmk__group_with_colocations(const pcmk_resource_t *rsc,
     740             :                              const pcmk_resource_t *orig_rsc, GList **list)
     741             : {
     742           0 :     const pcmk_resource_t *member = NULL;
     743             : 
     744           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
     745             :                && (orig_rsc != NULL) && (list != NULL));
     746             : 
     747             :     // Ignore empty groups
     748           0 :     if (rsc->children == NULL) {
     749           0 :         return;
     750             :     }
     751             : 
     752             :     /* "This with" colocations are normally needed only for the group itself and
     753             :      * for its first member.
     754             :      */
     755           0 :     if ((rsc == orig_rsc)
     756           0 :         || (orig_rsc == (const pcmk_resource_t *) rsc->children->data)) {
     757           0 :         pcmk__rsc_trace(rsc, "Adding '%s with' colocations to list for %s",
     758             :                         rsc->id, orig_rsc->id);
     759             : 
     760             :         // Add the group's own colocations
     761           0 :         pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
     762             : 
     763             :         // If cloned, add any relevant colocations involving the clone
     764           0 :         if (rsc->parent != NULL) {
     765           0 :             rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc,
     766             :                                                      list);
     767             :         }
     768             : 
     769           0 :         if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) {
     770             :             // @COMPAT Non-colocated groups are deprecated
     771           0 :             return;
     772             :         }
     773             : 
     774             :         // Add explicit colocations involving the group's (other) children
     775           0 :         for (const GList *iter = rsc->children;
     776           0 :              iter != NULL; iter = iter->next) {
     777           0 :             member = iter->data;
     778           0 :             if (member != orig_rsc) {
     779           0 :                 member->cmds->this_with_colocations(member, orig_rsc, list);
     780             :             }
     781             :         }
     782           0 :         return;
     783             :     }
     784             : 
     785             :     /* Later group members honor the group's colocations indirectly, due to the
     786             :      * internal group colocations that chain everything from the first member.
     787             :      * However, if an earlier group member is unmanaged, this chaining will not
     788             :      * happen, so the group's mandatory colocations must be explicitly added.
     789             :      */
     790           0 :     for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     791           0 :         member = iter->data;
     792           0 :         if (orig_rsc == member) {
     793           0 :             break; // We've seen all earlier members, and none are unmanaged
     794             :         }
     795             : 
     796           0 :         if (!pcmk_is_set(member->flags, pcmk_rsc_managed)) {
     797           0 :             crm_trace("Adding mandatory '%s with' colocations to list for "
     798             :                       "member %s because earlier member %s is unmanaged",
     799             :                       rsc->id, orig_rsc->id, member->id);
     800           0 :             for (const GList *cons_iter = rsc->rsc_cons; cons_iter != NULL;
     801           0 :                  cons_iter = cons_iter->next) {
     802           0 :                 const pcmk__colocation_t *colocation = NULL;
     803             : 
     804           0 :                 colocation = (const pcmk__colocation_t *) cons_iter->data;
     805           0 :                 if (colocation->score == PCMK_SCORE_INFINITY) {
     806           0 :                     pcmk__add_this_with(list, colocation, orig_rsc);
     807             :                 }
     808             :             }
     809             :             // @TODO Add mandatory (or all?) clone constraints if cloned
     810           0 :             break;
     811             :         }
     812             :     }
     813             : }
     814             : 
     815             : /*!
     816             :  * \internal
     817             :  * \brief Update nodes with scores of colocated resources' nodes
     818             :  *
     819             :  * Given a table of nodes and a resource, update the nodes' scores with the
     820             :  * scores of the best nodes matching the attribute used for each of the
     821             :  * resource's relevant colocations.
     822             :  *
     823             :  * \param[in,out] source_rsc  Group resource whose node scores to add
     824             :  * \param[in]     target_rsc  Resource on whose behalf to update \p *nodes
     825             :  * \param[in]     log_id      Resource ID for logs (if \c NULL, use
     826             :  *                            \p source_rsc ID)
     827             :  * \param[in,out] nodes       Nodes to update (set initial contents to \c NULL
     828             :  *                            to copy allowed nodes from \p source_rsc)
     829             :  * \param[in]     colocation  Original colocation constraint (used to get
     830             :  *                            configured primary resource's stickiness, and
     831             :  *                            to get colocation node attribute; if \c NULL,
     832             :  *                            <tt>source_rsc</tt>'s own matching node scores will
     833             :  *                            not be added, and \p *nodes must be \c NULL as
     834             :  *                            well)
     835             :  * \param[in]     factor      Incorporate scores multiplied by this factor
     836             :  * \param[in]     flags       Bitmask of enum pcmk__coloc_select values
     837             :  *
     838             :  * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, and
     839             :  *       the \c pcmk__coloc_select_this_with flag are used together (and only by
     840             :  *       \c cmp_resources()).
     841             :  * \note The caller remains responsible for freeing \p *nodes.
     842             :  * \note This is the group implementation of
     843             :  *       \c pcmk_assignment_methods_t:add_colocated_node_scores().
     844             :  */
     845             : void
     846           0 : pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc,
     847             :                                       const pcmk_resource_t *target_rsc,
     848             :                                       const char *log_id, GHashTable **nodes,
     849             :                                       const pcmk__colocation_t *colocation,
     850             :                                       float factor, uint32_t flags)
     851             : {
     852           0 :     pcmk_resource_t *member = NULL;
     853             : 
     854           0 :     CRM_ASSERT((source_rsc != NULL)
     855             :                && (source_rsc->variant == pcmk_rsc_variant_group)
     856             :                && (nodes != NULL)
     857             :                && ((colocation != NULL)
     858             :                    || ((target_rsc == NULL) && (*nodes == NULL))));
     859             : 
     860           0 :     if (log_id == NULL) {
     861           0 :         log_id = source_rsc->id;
     862             :     }
     863             : 
     864             :     // Avoid infinite recursion
     865           0 :     if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
     866           0 :         pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
     867             :                        log_id, source_rsc->id);
     868           0 :         return;
     869             :     }
     870           0 :     pcmk__set_rsc_flags(source_rsc, pcmk_rsc_updating_nodes);
     871             : 
     872             :     // Ignore empty groups (only possible with schema validation disabled)
     873           0 :     if (source_rsc->children == NULL) {
     874           0 :         return;
     875             :     }
     876             : 
     877             :     /* Refer the operation to the first or last member as appropriate.
     878             :      *
     879             :      * cmp_resources() is the only caller that passes a NULL nodes table,
     880             :      * and is also the only caller using pcmk__coloc_select_this_with.
     881             :      * For "this with" colocations, the last member will recursively incorporate
     882             :      * all the other members' "this with" colocations via the internal group
     883             :      * colocations (and via the first member, the group's own colocations).
     884             :      *
     885             :      * For "with this" colocations, the first member works similarly.
     886             :      */
     887           0 :     if (*nodes == NULL) {
     888           0 :         member = pe__last_group_member(source_rsc);
     889             :     } else {
     890           0 :         member = source_rsc->children->data;
     891             :     }
     892           0 :     pcmk__rsc_trace(source_rsc, "%s: Merging scores from group %s using member %s "
     893             :                     "(at %.6f)", log_id, source_rsc->id, member->id, factor);
     894           0 :     member->cmds->add_colocated_node_scores(member, target_rsc, log_id, nodes,
     895             :                                             colocation, factor, flags);
     896           0 :     pcmk__clear_rsc_flags(source_rsc, pcmk_rsc_updating_nodes);
     897             : }
     898             : 
     899             : // Group implementation of pcmk_assignment_methods_t:add_utilization()
     900             : void
     901           0 : pcmk__group_add_utilization(const pcmk_resource_t *rsc,
     902             :                             const pcmk_resource_t *orig_rsc, GList *all_rscs,
     903             :                             GHashTable *utilization)
     904             : {
     905           0 :     pcmk_resource_t *member = NULL;
     906             : 
     907           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
     908             :                && (orig_rsc != NULL) && (utilization != NULL));
     909             : 
     910           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
     911           0 :         return;
     912             :     }
     913             : 
     914           0 :     pcmk__rsc_trace(orig_rsc, "%s: Adding group %s as colocated utilization",
     915             :                     orig_rsc->id, rsc->id);
     916           0 :     if (pe__group_flag_is_set(rsc, pcmk__group_colocated)
     917           0 :         || pcmk__is_clone(rsc->parent)) {
     918             :         // Every group member will be on same node, so sum all members
     919           0 :         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     920           0 :             member = (pcmk_resource_t *) iter->data;
     921             : 
     922           0 :             if (pcmk_is_set(member->flags, pcmk_rsc_unassigned)
     923           0 :                 && (g_list_find(all_rscs, member) == NULL)) {
     924           0 :                 member->cmds->add_utilization(member, orig_rsc, all_rscs,
     925             :                                               utilization);
     926             :             }
     927             :         }
     928             : 
     929           0 :     } else if (rsc->children != NULL) {
     930             :         // Just add first member's utilization
     931           0 :         member = (pcmk_resource_t *) rsc->children->data;
     932           0 :         if ((member != NULL)
     933           0 :             && pcmk_is_set(member->flags, pcmk_rsc_unassigned)
     934           0 :             && (g_list_find(all_rscs, member) == NULL)) {
     935             : 
     936           0 :             member->cmds->add_utilization(member, orig_rsc, all_rscs,
     937             :                                           utilization);
     938             :         }
     939             :     }
     940             : }
     941             : 
     942             : void
     943           0 : pcmk__group_shutdown_lock(pcmk_resource_t *rsc)
     944             : {
     945           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
     946             : 
     947           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     948           0 :         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
     949             : 
     950           0 :         member->cmds->shutdown_lock(member);
     951             :     }
     952           0 : }

Generated by: LCOV version 1.14