LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_bundle.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 354 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 25 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             : #include <pacemaker-internal.h>
      16             : 
      17             : #include "libpacemaker_private.h"
      18             : 
      19             : struct assign_data {
      20             :     const pcmk_node_t *prefer;
      21             :     bool stop_if_fail;
      22             : };
      23             : 
      24             : /*!
      25             :  * \internal
      26             :  * \brief Assign a single bundle replica's resources (other than container)
      27             :  *
      28             :  * \param[in,out] replica    Replica to assign
      29             :  * \param[in]     user_data  Preferred node, if any
      30             :  *
      31             :  * \return true (to indicate that any further replicas should be processed)
      32             :  */
      33             : static bool
      34           0 : assign_replica(pcmk__bundle_replica_t *replica, void *user_data)
      35             : {
      36           0 :     pcmk_node_t *container_host = NULL;
      37             : 
      38           0 :     struct assign_data *assign_data = user_data;
      39           0 :     const pcmk_node_t *prefer = assign_data->prefer;
      40           0 :     bool stop_if_fail = assign_data->stop_if_fail;
      41             : 
      42           0 :     const pcmk_resource_t *bundle = pe__const_top_resource(replica->container,
      43             :                                                            true);
      44             : 
      45           0 :     if (replica->ip != NULL) {
      46           0 :         pcmk__rsc_trace(bundle, "Assigning bundle %s IP %s",
      47             :                         bundle->id, replica->ip->id);
      48           0 :         replica->ip->cmds->assign(replica->ip, prefer, stop_if_fail);
      49             :     }
      50             : 
      51           0 :     container_host = replica->container->allocated_to;
      52           0 :     if (replica->remote != NULL) {
      53           0 :         if (pcmk__is_pacemaker_remote_node(container_host)) {
      54             :             /* REMOTE_CONTAINER_HACK: "Nested" connection resources must be on
      55             :              * the same host because Pacemaker Remote only supports a single
      56             :              * active connection.
      57             :              */
      58           0 :             pcmk__new_colocation("#replica-remote-with-host-remote", NULL,
      59             :                                  PCMK_SCORE_INFINITY, replica->remote,
      60           0 :                                  container_host->details->remote_rsc, NULL,
      61             :                                  NULL, pcmk__coloc_influence);
      62             :         }
      63           0 :         pcmk__rsc_trace(bundle, "Assigning bundle %s connection %s",
      64             :                         bundle->id, replica->remote->id);
      65           0 :         replica->remote->cmds->assign(replica->remote, prefer, stop_if_fail);
      66             :     }
      67             : 
      68           0 :     if (replica->child != NULL) {
      69           0 :         pcmk_node_t *node = NULL;
      70             :         GHashTableIter iter;
      71             : 
      72           0 :         g_hash_table_iter_init(&iter, replica->child->allowed_nodes);
      73           0 :         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
      74           0 :             if (!pcmk__same_node(node, replica->node)) {
      75           0 :                 node->weight = -PCMK_SCORE_INFINITY;
      76           0 :             } else if (!pcmk__threshold_reached(replica->child, node, NULL)) {
      77           0 :                 node->weight = PCMK_SCORE_INFINITY;
      78             :             }
      79             :         }
      80             : 
      81           0 :         pcmk__set_rsc_flags(replica->child->parent, pcmk_rsc_assigning);
      82           0 :         pcmk__rsc_trace(bundle, "Assigning bundle %s replica child %s",
      83             :                         bundle->id, replica->child->id);
      84           0 :         replica->child->cmds->assign(replica->child, replica->node,
      85             :                                      stop_if_fail);
      86           0 :         pcmk__clear_rsc_flags(replica->child->parent, pcmk_rsc_assigning);
      87             :     }
      88           0 :     return true;
      89             : }
      90             : 
      91             : /*!
      92             :  * \internal
      93             :  * \brief Assign a bundle resource to a node
      94             :  *
      95             :  * \param[in,out] rsc           Resource to assign to a node
      96             :  * \param[in]     prefer        Node to prefer, if all else is equal
      97             :  * \param[in]     stop_if_fail  If \c true and a primitive descendant of \p rsc
      98             :  *                              can't be assigned to a node, set the
      99             :  *                              descendant's next role to stopped and update
     100             :  *                              existing actions
     101             :  *
     102             :  * \return Node that \p rsc is assigned to, if assigned entirely to one node
     103             :  *
     104             :  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
     105             :  *       completely undo the assignment. A successful assignment can be either
     106             :  *       undone or left alone as final. A failed assignment has the same effect
     107             :  *       as calling pcmk__unassign_resource(); there are no side effects on
     108             :  *       roles or actions.
     109             :  */
     110             : pcmk_node_t *
     111           0 : pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
     112             :                     bool stop_if_fail)
     113             : {
     114           0 :     GList *containers = NULL;
     115           0 :     pcmk_resource_t *bundled_resource = NULL;
     116           0 :     struct assign_data assign_data = { prefer, stop_if_fail };
     117             : 
     118           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
     119             : 
     120           0 :     pcmk__rsc_trace(rsc, "Assigning bundle %s", rsc->id);
     121           0 :     pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning);
     122             : 
     123           0 :     pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
     124             :                                       pcmk_sched_output_scores),
     125             :                          rsc, __func__, rsc->allowed_nodes, rsc->cluster);
     126             : 
     127             :     // Assign all containers first, so we know what nodes the bundle will be on
     128           0 :     containers = g_list_sort(pe__bundle_containers(rsc), pcmk__cmp_instance);
     129           0 :     pcmk__assign_instances(rsc, containers, pe__bundle_max(rsc),
     130           0 :                            rsc->fns->max_per_node(rsc));
     131           0 :     g_list_free(containers);
     132             : 
     133             :     // Then assign remaining replica resources
     134           0 :     pe__foreach_bundle_replica(rsc, assign_replica, (void *) &assign_data);
     135             : 
     136             :     // Finally, assign the bundled resources to each bundle node
     137           0 :     bundled_resource = pe__bundled_resource(rsc);
     138           0 :     if (bundled_resource != NULL) {
     139           0 :         pcmk_node_t *node = NULL;
     140             :         GHashTableIter iter;
     141             : 
     142           0 :         g_hash_table_iter_init(&iter, bundled_resource->allowed_nodes);
     143           0 :         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
     144           0 :             if (pe__node_is_bundle_instance(rsc, node)) {
     145           0 :                 node->weight = 0;
     146             :             } else {
     147           0 :                 node->weight = -PCMK_SCORE_INFINITY;
     148             :             }
     149             :         }
     150           0 :         bundled_resource->cmds->assign(bundled_resource, prefer, stop_if_fail);
     151             :     }
     152             : 
     153           0 :     pcmk__clear_rsc_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned);
     154           0 :     return NULL;
     155             : }
     156             : 
     157             : /*!
     158             :  * \internal
     159             :  * \brief Create actions for a bundle replica's resources (other than child)
     160             :  *
     161             :  * \param[in,out] replica    Replica to create actions for
     162             :  * \param[in]     user_data  Unused
     163             :  *
     164             :  * \return true (to indicate that any further replicas should be processed)
     165             :  */
     166             : static bool
     167           0 : create_replica_actions(pcmk__bundle_replica_t *replica, void *user_data)
     168             : {
     169           0 :     if (replica->ip != NULL) {
     170           0 :         replica->ip->cmds->create_actions(replica->ip);
     171             :     }
     172           0 :     if (replica->container != NULL) {
     173           0 :         replica->container->cmds->create_actions(replica->container);
     174             :     }
     175           0 :     if (replica->remote != NULL) {
     176           0 :         replica->remote->cmds->create_actions(replica->remote);
     177             :     }
     178           0 :     return true;
     179             : }
     180             : 
     181             : /*!
     182             :  * \internal
     183             :  * \brief Create all actions needed for a given bundle resource
     184             :  *
     185             :  * \param[in,out] rsc  Bundle resource to create actions for
     186             :  */
     187             : void
     188           0 : pcmk__bundle_create_actions(pcmk_resource_t *rsc)
     189             : {
     190           0 :     pcmk_action_t *action = NULL;
     191           0 :     GList *containers = NULL;
     192           0 :     pcmk_resource_t *bundled_resource = NULL;
     193             : 
     194           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
     195             : 
     196           0 :     pe__foreach_bundle_replica(rsc, create_replica_actions, NULL);
     197             : 
     198           0 :     containers = pe__bundle_containers(rsc);
     199           0 :     pcmk__create_instance_actions(rsc, containers);
     200           0 :     g_list_free(containers);
     201             : 
     202           0 :     bundled_resource = pe__bundled_resource(rsc);
     203           0 :     if (bundled_resource != NULL) {
     204           0 :         bundled_resource->cmds->create_actions(bundled_resource);
     205             : 
     206           0 :         if (pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) {
     207           0 :             pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTE, true, true);
     208           0 :             action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTED,
     209             :                                                true, true);
     210           0 :             action->priority = PCMK_SCORE_INFINITY;
     211             : 
     212           0 :             pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTE, true, true);
     213           0 :             action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTED,
     214             :                                                true, true);
     215           0 :             action->priority = PCMK_SCORE_INFINITY;
     216             :         }
     217             :     }
     218           0 : }
     219             : 
     220             : /*!
     221             :  * \internal
     222             :  * \brief Create internal constraints for a bundle replica's resources
     223             :  *
     224             :  * \param[in,out] replica    Replica to create internal constraints for
     225             :  * \param[in,out] user_data  Replica's parent bundle
     226             :  *
     227             :  * \return true (to indicate that any further replicas should be processed)
     228             :  */
     229             : static bool
     230           0 : replica_internal_constraints(pcmk__bundle_replica_t *replica, void *user_data)
     231             : {
     232           0 :     pcmk_resource_t *bundle = user_data;
     233             : 
     234           0 :     replica->container->cmds->internal_constraints(replica->container);
     235             : 
     236             :     // Start bundle -> start replica container
     237           0 :     pcmk__order_starts(bundle, replica->container,
     238             :                        pcmk__ar_unrunnable_first_blocks
     239             :                        |pcmk__ar_then_implies_first_graphed);
     240             : 
     241             :     // Stop bundle -> stop replica child and container
     242           0 :     if (replica->child != NULL) {
     243           0 :         pcmk__order_stops(bundle, replica->child,
     244             :                           pcmk__ar_then_implies_first_graphed);
     245             :     }
     246           0 :     pcmk__order_stops(bundle, replica->container,
     247             :                       pcmk__ar_then_implies_first_graphed);
     248             : 
     249             :     // Start replica container -> bundle is started
     250           0 :     pcmk__order_resource_actions(replica->container, PCMK_ACTION_START, bundle,
     251             :                                  PCMK_ACTION_RUNNING,
     252             :                                  pcmk__ar_first_implies_then_graphed);
     253             : 
     254             :     // Stop replica container -> bundle is stopped
     255           0 :     pcmk__order_resource_actions(replica->container, PCMK_ACTION_STOP, bundle,
     256             :                                  PCMK_ACTION_STOPPED,
     257             :                                  pcmk__ar_first_implies_then_graphed);
     258             : 
     259           0 :     if (replica->ip != NULL) {
     260           0 :         replica->ip->cmds->internal_constraints(replica->ip);
     261             : 
     262             :         // Replica IP address -> replica container (symmetric)
     263           0 :         pcmk__order_starts(replica->ip, replica->container,
     264             :                            pcmk__ar_unrunnable_first_blocks
     265             :                            |pcmk__ar_guest_allowed);
     266           0 :         pcmk__order_stops(replica->container, replica->ip,
     267             :                           pcmk__ar_then_implies_first|pcmk__ar_guest_allowed);
     268             : 
     269           0 :         pcmk__new_colocation("#ip-with-container", NULL, PCMK_SCORE_INFINITY,
     270             :                              replica->ip, replica->container, NULL, NULL,
     271             :                              pcmk__coloc_influence);
     272             :     }
     273             : 
     274           0 :     if (replica->remote != NULL) {
     275             :         /* This handles ordering and colocating remote relative to container
     276             :          * (via "#resource-with-container"). Since IP is also ordered and
     277             :          * colocated relative to the container, we don't need to do anything
     278             :          * explicit here with IP.
     279             :          */
     280           0 :         replica->remote->cmds->internal_constraints(replica->remote);
     281             :     }
     282             : 
     283           0 :     if (replica->child != NULL) {
     284           0 :         CRM_ASSERT(replica->remote != NULL);
     285             :         // "Start remote then child" is implicit in scheduler's remote logic
     286             :     }
     287           0 :     return true;
     288             : }
     289             : 
     290             : /*!
     291             :  * \internal
     292             :  * \brief Create implicit constraints needed for a bundle resource
     293             :  *
     294             :  * \param[in,out] rsc  Bundle resource to create implicit constraints for
     295             :  */
     296             : void
     297           0 : pcmk__bundle_internal_constraints(pcmk_resource_t *rsc)
     298             : {
     299           0 :     pcmk_resource_t *bundled_resource = NULL;
     300             : 
     301           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
     302             : 
     303           0 :     pe__foreach_bundle_replica(rsc, replica_internal_constraints, rsc);
     304             : 
     305           0 :     bundled_resource = pe__bundled_resource(rsc);
     306           0 :     if (bundled_resource == NULL) {
     307           0 :         return;
     308             :     }
     309             : 
     310             :     // Start bundle -> start bundled clone
     311           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_START, bundled_resource,
     312             :                                  PCMK_ACTION_START,
     313             :                                  pcmk__ar_then_implies_first_graphed);
     314             : 
     315             :     // Bundled clone is started -> bundle is started
     316           0 :     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_RUNNING,
     317             :                                  rsc, PCMK_ACTION_RUNNING,
     318             :                                  pcmk__ar_first_implies_then_graphed);
     319             : 
     320             :     // Stop bundle -> stop bundled clone
     321           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, bundled_resource,
     322             :                                  PCMK_ACTION_STOP,
     323             :                                  pcmk__ar_then_implies_first_graphed);
     324             : 
     325             :     // Bundled clone is stopped -> bundle is stopped
     326           0 :     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_STOPPED,
     327             :                                  rsc, PCMK_ACTION_STOPPED,
     328             :                                  pcmk__ar_first_implies_then_graphed);
     329             : 
     330           0 :     bundled_resource->cmds->internal_constraints(bundled_resource);
     331             : 
     332           0 :     if (!pcmk_is_set(bundled_resource->flags, pcmk_rsc_promotable)) {
     333           0 :         return;
     334             :     }
     335           0 :     pcmk__promotable_restart_ordering(rsc);
     336             : 
     337             :     // Demote bundle -> demote bundled clone
     338           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTE, bundled_resource,
     339             :                                  PCMK_ACTION_DEMOTE,
     340             :                                  pcmk__ar_then_implies_first_graphed);
     341             : 
     342             :     // Bundled clone is demoted -> bundle is demoted
     343           0 :     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_DEMOTED,
     344             :                                  rsc, PCMK_ACTION_DEMOTED,
     345             :                                  pcmk__ar_first_implies_then_graphed);
     346             : 
     347             :     // Promote bundle -> promote bundled clone
     348           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_PROMOTE,
     349             :                                  bundled_resource, PCMK_ACTION_PROMOTE,
     350             :                                  pcmk__ar_then_implies_first_graphed);
     351             : 
     352             :     // Bundled clone is promoted -> bundle is promoted
     353           0 :     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_PROMOTED,
     354             :                                  rsc, PCMK_ACTION_PROMOTED,
     355             :                                  pcmk__ar_first_implies_then_graphed);
     356             : }
     357             : 
     358             : struct match_data {
     359             :     const pcmk_node_t *node;    // Node to compare against replica
     360             :     pcmk_resource_t *container; // Replica container corresponding to node
     361             : };
     362             : 
     363             : /*!
     364             :  * \internal
     365             :  * \brief Check whether a replica container is assigned to a given node
     366             :  *
     367             :  * \param[in]     replica    Replica to check
     368             :  * \param[in,out] user_data  struct match_data with node to compare against
     369             :  *
     370             :  * \return true if the replica does not match (to indicate further replicas
     371             :  *         should be processed), otherwise false
     372             :  */
     373             : static bool
     374           0 : match_replica_container(const pcmk__bundle_replica_t *replica, void *user_data)
     375             : {
     376           0 :     struct match_data *match_data = user_data;
     377             : 
     378           0 :     if (pcmk__instance_matches(replica->container, match_data->node,
     379             :                                pcmk_role_unknown, false)) {
     380           0 :         match_data->container = replica->container;
     381           0 :         return false; // Match found, don't bother searching further replicas
     382             :     }
     383           0 :     return true; // No match, keep searching
     384             : }
     385             : 
     386             : /*!
     387             :  * \internal
     388             :  * \brief Get the host to which a bundle node is assigned
     389             :  *
     390             :  * \param[in] node  Possible bundle node to check
     391             :  *
     392             :  * \return Node to which the container for \p node is assigned if \p node is a
     393             :  *         bundle node, otherwise \p node itself
     394             :  */
     395             : static const pcmk_node_t *
     396           0 : get_bundle_node_host(const pcmk_node_t *node)
     397             : {
     398           0 :     if (pcmk__is_bundle_node(node)) {
     399           0 :         const pcmk_resource_t *container = node->details->remote_rsc->container;
     400             : 
     401           0 :         return container->fns->location(container, NULL, 0);
     402             :     }
     403           0 :     return node;
     404             : }
     405             : 
     406             : /*!
     407             :  * \internal
     408             :  * \brief Find a bundle container compatible with a dependent resource
     409             :  *
     410             :  * \param[in] dependent  Dependent resource in colocation with bundle
     411             :  * \param[in] bundle     Bundle that \p dependent is colocated with
     412             :  *
     413             :  * \return A container from \p bundle assigned to the same node as \p dependent
     414             :  *         if assigned, otherwise assigned to any of dependent's allowed nodes,
     415             :  *         otherwise NULL.
     416             :  */
     417             : static pcmk_resource_t *
     418           0 : compatible_container(const pcmk_resource_t *dependent,
     419             :                      const pcmk_resource_t *bundle)
     420             : {
     421           0 :     GList *scratch = NULL;
     422           0 :     struct match_data match_data = { NULL, NULL };
     423             : 
     424             :     // If dependent is assigned, only check there
     425           0 :     match_data.node = dependent->fns->location(dependent, NULL, 0);
     426           0 :     match_data.node = get_bundle_node_host(match_data.node);
     427           0 :     if (match_data.node != NULL) {
     428           0 :         pe__foreach_const_bundle_replica(bundle, match_replica_container,
     429             :                                          &match_data);
     430           0 :         return match_data.container;
     431             :     }
     432             : 
     433             :     // Otherwise, check for any of the dependent's allowed nodes
     434           0 :     scratch = g_hash_table_get_values(dependent->allowed_nodes);
     435           0 :     scratch = pcmk__sort_nodes(scratch, NULL);
     436           0 :     for (const GList *iter = scratch; iter != NULL; iter = iter->next) {
     437           0 :         match_data.node = iter->data;
     438           0 :         match_data.node = get_bundle_node_host(match_data.node);
     439           0 :         if (match_data.node == NULL) {
     440           0 :             continue;
     441             :         }
     442             : 
     443           0 :         pe__foreach_const_bundle_replica(bundle, match_replica_container,
     444             :                                          &match_data);
     445           0 :         if (match_data.container != NULL) {
     446           0 :             break;
     447             :         }
     448             :     }
     449           0 :     g_list_free(scratch);
     450           0 :     return match_data.container;
     451             : }
     452             : 
     453             : struct coloc_data {
     454             :     const pcmk__colocation_t *colocation;
     455             :     pcmk_resource_t *dependent;
     456             :     GList *container_hosts;
     457             : };
     458             : 
     459             : /*!
     460             :  * \internal
     461             :  * \brief Apply a colocation score to replica node scores or resource priority
     462             :  *
     463             :  * \param[in]     replica    Replica of primary bundle resource in colocation
     464             :  * \param[in,out] user_data  struct coloc_data for colocation being applied
     465             :  *
     466             :  * \return true (to indicate that any further replicas should be processed)
     467             :  */
     468             : static bool
     469           0 : replica_apply_coloc_score(const pcmk__bundle_replica_t *replica,
     470             :                           void *user_data)
     471             : {
     472           0 :     struct coloc_data *coloc_data = user_data;
     473           0 :     pcmk_node_t *chosen = NULL;
     474             : 
     475           0 :     if (coloc_data->colocation->score < PCMK_SCORE_INFINITY) {
     476           0 :         replica->container->cmds->apply_coloc_score(coloc_data->dependent,
     477           0 :                                                     replica->container,
     478             :                                                     coloc_data->colocation,
     479             :                                                     false);
     480           0 :         return true;
     481             :     }
     482             : 
     483           0 :     chosen = replica->container->fns->location(replica->container, NULL, 0);
     484           0 :     if ((chosen == NULL)
     485           0 :         || is_set_recursive(replica->container, pcmk_rsc_blocked, true)) {
     486           0 :         return true;
     487             :     }
     488             : 
     489           0 :     if ((coloc_data->colocation->primary_role >= pcmk_role_promoted)
     490           0 :         && ((replica->child == NULL)
     491           0 :             || (replica->child->next_role < pcmk_role_promoted))) {
     492           0 :         return true;
     493             :     }
     494             : 
     495           0 :     pcmk__rsc_trace(pe__const_top_resource(replica->container, true),
     496             :                     "Allowing mandatory colocation %s using %s @%d",
     497             :                     coloc_data->colocation->id, pcmk__node_name(chosen),
     498             :                     chosen->weight);
     499           0 :     coloc_data->container_hosts = g_list_prepend(coloc_data->container_hosts,
     500             :                                                  chosen);
     501           0 :     return true;
     502             : }
     503             : 
     504             : /*!
     505             :  * \internal
     506             :  * \brief Apply a colocation's score to node scores or resource priority
     507             :  *
     508             :  * Given a colocation constraint, apply its score to the dependent's
     509             :  * allowed node scores (if we are still placing resources) or priority (if
     510             :  * we are choosing promotable clone instance roles).
     511             :  *
     512             :  * \param[in,out] dependent      Dependent resource in colocation
     513             :  * \param[in]     primary        Primary resource in colocation
     514             :  * \param[in]     colocation     Colocation constraint to apply
     515             :  * \param[in]     for_dependent  true if called on behalf of dependent
     516             :  */
     517             : void
     518           0 : pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent,
     519             :                                const pcmk_resource_t *primary,
     520             :                                const pcmk__colocation_t *colocation,
     521             :                                bool for_dependent)
     522             : {
     523           0 :     struct coloc_data coloc_data = { colocation, dependent, NULL };
     524             : 
     525             :     /* This should never be called for the bundle itself as a dependent.
     526             :      * Instead, we add its colocation constraints to its containers and bundled
     527             :      * primitive and call the apply_coloc_score() method for them as dependents.
     528             :      */
     529           0 :     CRM_ASSERT((primary != NULL)
     530             :                && (primary->variant == pcmk_rsc_variant_bundle)
     531             :                && (dependent != NULL)
     532             :                && (dependent->variant == pcmk_rsc_variant_primitive)
     533             :                && (colocation != NULL) && !for_dependent);
     534             : 
     535           0 :     if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
     536           0 :         pcmk__rsc_trace(primary,
     537             :                         "Skipping applying colocation %s "
     538             :                         "because %s is still provisional",
     539             :                         colocation->id, primary->id);
     540           0 :         return;
     541             :     }
     542           0 :     pcmk__rsc_trace(primary, "Applying colocation %s (%s with %s at %s)",
     543             :                     colocation->id, dependent->id, primary->id,
     544             :                     pcmk_readable_score(colocation->score));
     545             : 
     546             :     /* If the constraint dependent is a clone or bundle, "dependent" here is one
     547             :      * of its instances. Look for a compatible instance of this bundle.
     548             :      */
     549           0 :     if (colocation->dependent->variant > pcmk_rsc_variant_group) {
     550           0 :         const pcmk_resource_t *primary_container = NULL;
     551             : 
     552           0 :         primary_container = compatible_container(dependent, primary);
     553           0 :         if (primary_container != NULL) { // Success, we found one
     554           0 :             pcmk__rsc_debug(primary, "Pairing %s with %s",
     555             :                             dependent->id, primary_container->id);
     556           0 :             dependent->cmds->apply_coloc_score(dependent, primary_container,
     557             :                                                colocation, true);
     558             : 
     559           0 :         } else if (colocation->score >= PCMK_SCORE_INFINITY) {
     560             :             // Failure, and it's fatal
     561           0 :             crm_notice("%s cannot run because there is no compatible "
     562             :                        "instance of %s to colocate with",
     563             :                        dependent->id, primary->id);
     564           0 :             pcmk__assign_resource(dependent, NULL, true, true);
     565             : 
     566             :         } else { // Failure, but we can ignore it
     567           0 :             pcmk__rsc_debug(primary,
     568             :                             "%s cannot be colocated with any instance of %s",
     569             :                             dependent->id, primary->id);
     570             :         }
     571           0 :         return;
     572             :     }
     573             : 
     574           0 :     pe__foreach_const_bundle_replica(primary, replica_apply_coloc_score,
     575             :                                      &coloc_data);
     576             : 
     577           0 :     if (colocation->score >= PCMK_SCORE_INFINITY) {
     578           0 :         pcmk__colocation_intersect_nodes(dependent, primary, colocation,
     579           0 :                                          coloc_data.container_hosts, false);
     580             :     }
     581           0 :     g_list_free(coloc_data.container_hosts);
     582             : }
     583             : 
     584             : // Bundle implementation of pcmk_assignment_methods_t:with_this_colocations()
     585             : void
     586           0 : pcmk__with_bundle_colocations(const pcmk_resource_t *rsc,
     587             :                               const pcmk_resource_t *orig_rsc, GList **list)
     588             : {
     589           0 :     const pcmk_resource_t *bundled_rsc = NULL;
     590             : 
     591           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
     592             :                && (orig_rsc != NULL) && (list != NULL));
     593             : 
     594             :     // The bundle itself and its containers always get its colocations
     595           0 :     if ((orig_rsc == rsc)
     596           0 :         || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) {
     597             : 
     598           0 :         pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
     599           0 :         return;
     600             :     }
     601             : 
     602             :     /* The bundled resource gets the colocations if it's promotable and we've
     603             :      * begun choosing roles
     604             :      */
     605           0 :     bundled_rsc = pe__bundled_resource(rsc);
     606           0 :     if ((bundled_rsc == NULL)
     607           0 :         || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable)
     608           0 :         || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
     609           0 :         return;
     610             :     }
     611             : 
     612           0 :     if (orig_rsc == bundled_rsc) {
     613           0 :         if (pe__clone_flag_is_set(orig_rsc,
     614             :                                   pcmk__clone_promotion_constrained)) {
     615             :             /* orig_rsc is the clone and we're setting roles (or have already
     616             :              * done so)
     617             :              */
     618           0 :             pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
     619             :         }
     620             : 
     621           0 :     } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) {
     622             :         /* orig_rsc is an instance and is already assigned. If something
     623             :          * requests colocations for orig_rsc now, it's for setting roles.
     624             :          */
     625           0 :         pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
     626             :     }
     627             : }
     628             : 
     629             : // Bundle implementation of pcmk_assignment_methods_t:this_with_colocations()
     630             : void
     631           0 : pcmk__bundle_with_colocations(const pcmk_resource_t *rsc,
     632             :                               const pcmk_resource_t *orig_rsc, GList **list)
     633             : {
     634           0 :     const pcmk_resource_t *bundled_rsc = NULL;
     635             : 
     636           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
     637             :                && (orig_rsc != NULL) && (list != NULL));
     638             : 
     639             :     // The bundle itself and its containers always get its colocations
     640           0 :     if ((orig_rsc == rsc)
     641           0 :         || pcmk_is_set(orig_rsc->flags, pcmk_rsc_replica_container)) {
     642             : 
     643           0 :         pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
     644           0 :         return;
     645             :     }
     646             : 
     647             :     /* The bundled resource gets the colocations if it's promotable and we've
     648             :      * begun choosing roles
     649             :      */
     650           0 :     bundled_rsc = pe__bundled_resource(rsc);
     651           0 :     if ((bundled_rsc == NULL)
     652           0 :         || !pcmk_is_set(bundled_rsc->flags, pcmk_rsc_promotable)
     653           0 :         || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
     654           0 :         return;
     655             :     }
     656             : 
     657           0 :     if (orig_rsc == bundled_rsc) {
     658           0 :         if (pe__clone_flag_is_set(orig_rsc,
     659             :                                   pcmk__clone_promotion_constrained)) {
     660             :             /* orig_rsc is the clone and we're setting roles (or have already
     661             :              * done so)
     662             :              */
     663           0 :             pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
     664             :         }
     665             : 
     666           0 :     } else if (!pcmk_is_set(orig_rsc->flags, pcmk_rsc_unassigned)) {
     667             :         /* orig_rsc is an instance and is already assigned. If something
     668             :          * requests colocations for orig_rsc now, it's for setting roles.
     669             :          */
     670           0 :         pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
     671             :     }
     672             : }
     673             : 
     674             : /*!
     675             :  * \internal
     676             :  * \brief Return action flags for a given bundle resource action
     677             :  *
     678             :  * \param[in,out] action  Bundle resource action to get flags for
     679             :  * \param[in]     node    If not NULL, limit effects to this node
     680             :  *
     681             :  * \return Flags appropriate to \p action on \p node
     682             :  */
     683             : uint32_t
     684           0 : pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     685             : {
     686           0 :     GList *containers = NULL;
     687           0 :     uint32_t flags = 0;
     688           0 :     pcmk_resource_t *bundled_resource = NULL;
     689             : 
     690           0 :     CRM_ASSERT((action != NULL) && (action->rsc != NULL)
     691             :                && (action->rsc->variant == pcmk_rsc_variant_bundle));
     692             : 
     693           0 :     bundled_resource = pe__bundled_resource(action->rsc);
     694           0 :     if (bundled_resource != NULL) {
     695             :         // Clone actions are done on the bundled clone resource, not container
     696           0 :         switch (get_complex_task(bundled_resource, action->task)) {
     697           0 :             case pcmk_action_unspecified:
     698             :             case pcmk_action_notify:
     699             :             case pcmk_action_notified:
     700             :             case pcmk_action_promote:
     701             :             case pcmk_action_promoted:
     702             :             case pcmk_action_demote:
     703             :             case pcmk_action_demoted:
     704           0 :                 return pcmk__collective_action_flags(action,
     705           0 :                                                      bundled_resource->children,
     706             :                                                      node);
     707           0 :             default:
     708           0 :                 break;
     709             :         }
     710             :     }
     711             : 
     712           0 :     containers = pe__bundle_containers(action->rsc);
     713           0 :     flags = pcmk__collective_action_flags(action, containers, node);
     714           0 :     g_list_free(containers);
     715           0 :     return flags;
     716             : }
     717             : 
     718             : /*!
     719             :  * \internal
     720             :  * \brief Apply a location constraint to a bundle replica
     721             :  *
     722             :  * \param[in,out] replica    Replica to apply constraint to
     723             :  * \param[in,out] user_data  Location constraint to apply
     724             :  *
     725             :  * \return true (to indicate that any further replicas should be processed)
     726             :  */
     727             : static bool
     728           0 : apply_location_to_replica(pcmk__bundle_replica_t *replica, void *user_data)
     729             : {
     730           0 :     pcmk__location_t *location = user_data;
     731             : 
     732           0 :     if (replica->container != NULL) {
     733           0 :         replica->container->cmds->apply_location(replica->container, location);
     734             :     }
     735           0 :     if (replica->ip != NULL) {
     736           0 :         replica->ip->cmds->apply_location(replica->ip, location);
     737             :     }
     738           0 :     return true;
     739             : }
     740             : 
     741             : /*!
     742             :  * \internal
     743             :  * \brief Apply a location constraint to a bundle resource's allowed node scores
     744             :  *
     745             :  * \param[in,out] rsc       Bundle resource to apply constraint to
     746             :  * \param[in,out] location  Location constraint to apply
     747             :  */
     748             : void
     749           0 : pcmk__bundle_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     750             : {
     751           0 :     pcmk_resource_t *bundled_resource = NULL;
     752             : 
     753           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle)
     754             :                && (location != NULL));
     755             : 
     756           0 :     pcmk__apply_location(rsc, location);
     757           0 :     pe__foreach_bundle_replica(rsc, apply_location_to_replica, location);
     758             : 
     759           0 :     bundled_resource = pe__bundled_resource(rsc);
     760           0 :     if ((bundled_resource != NULL)
     761           0 :         && ((location->role_filter == pcmk_role_unpromoted)
     762           0 :             || (location->role_filter == pcmk_role_promoted))) {
     763           0 :         bundled_resource->cmds->apply_location(bundled_resource, location);
     764           0 :         bundled_resource->rsc_location = g_list_prepend(
     765             :             bundled_resource->rsc_location, location);
     766             :     }
     767           0 : }
     768             : 
     769             : #define XPATH_REMOTE "//nvpair[@name='" PCMK_REMOTE_RA_ADDR "']"
     770             : 
     771             : /*!
     772             :  * \internal
     773             :  * \brief Add a bundle replica's actions to transition graph
     774             :  *
     775             :  * \param[in,out] replica    Replica to add to graph
     776             :  * \param[in]     user_data  Bundle that replica belongs to (for logging only)
     777             :  *
     778             :  * \return true (to indicate that any further replicas should be processed)
     779             :  */
     780             : static bool
     781           0 : add_replica_actions_to_graph(pcmk__bundle_replica_t *replica, void *user_data)
     782             : {
     783           0 :     if ((replica->remote != NULL) && (replica->container != NULL)
     784           0 :         && pe__bundle_needs_remote_name(replica->remote)) {
     785             : 
     786             :         /* REMOTE_CONTAINER_HACK: Allow remote nodes to run containers that
     787             :          * run pacemaker-remoted inside, without needing a separate IP for
     788             :          * the container. This is done by configuring the inner remote's
     789             :          * connection host as the magic string "#uname", then
     790             :          * replacing it with the underlying host when needed.
     791             :          */
     792           0 :         xmlNode *nvpair = get_xpath_object(XPATH_REMOTE, replica->remote->xml,
     793             :                                            LOG_ERR);
     794           0 :         const char *calculated_addr = NULL;
     795             : 
     796             :         // Replace the value in replica->remote->xml (if appropriate)
     797           0 :         calculated_addr = pe__add_bundle_remote_name(replica->remote, nvpair,
     798             :                                                      PCMK_XA_VALUE);
     799           0 :         if (calculated_addr != NULL) {
     800             :             /* Since this is for the bundle as a resource, and not any
     801             :              * particular action, replace the value in the default
     802             :              * parameters (not evaluated for node). create_graph_action()
     803             :              * will grab it from there to replace it in node-evaluated
     804             :              * parameters.
     805             :              */
     806           0 :             GHashTable *params = pe_rsc_params(replica->remote,
     807           0 :                                                NULL, replica->remote->cluster);
     808             : 
     809           0 :             pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, calculated_addr);
     810             :         } else {
     811           0 :             pcmk_resource_t *bundle = user_data;
     812             : 
     813             :             /* The only way to get here is if the remote connection is
     814             :              * neither currently running nor scheduled to run. That means we
     815             :              * won't be doing any operations that require addr (only start
     816             :              * requires it; we additionally use it to compare digests when
     817             :              * unpacking status, promote, and migrate_from history, but
     818             :              * that's already happened by this point).
     819             :              */
     820           0 :             pcmk__rsc_info(bundle,
     821             :                            "Unable to determine address for bundle %s "
     822             :                            "remote connection", bundle->id);
     823             :         }
     824             :     }
     825           0 :     if (replica->ip != NULL) {
     826           0 :         replica->ip->cmds->add_actions_to_graph(replica->ip);
     827             :     }
     828           0 :     if (replica->container != NULL) {
     829           0 :         replica->container->cmds->add_actions_to_graph(replica->container);
     830             :     }
     831           0 :     if (replica->remote != NULL) {
     832           0 :         replica->remote->cmds->add_actions_to_graph(replica->remote);
     833             :     }
     834           0 :     return true;
     835             : }
     836             : 
     837             : /*!
     838             :  * \internal
     839             :  * \brief Add a bundle resource's actions to the transition graph
     840             :  *
     841             :  * \param[in,out] rsc  Bundle resource whose actions should be added
     842             :  */
     843             : void
     844           0 : pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc)
     845             : {
     846           0 :     pcmk_resource_t *bundled_resource = NULL;
     847             : 
     848           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
     849             : 
     850           0 :     bundled_resource = pe__bundled_resource(rsc);
     851           0 :     if (bundled_resource != NULL) {
     852           0 :         bundled_resource->cmds->add_actions_to_graph(bundled_resource);
     853             :     }
     854           0 :     pe__foreach_bundle_replica(rsc, add_replica_actions_to_graph, rsc);
     855           0 : }
     856             : 
     857             : struct probe_data {
     858             :     pcmk_resource_t *bundle;    // Bundle being probed
     859             :     pcmk_node_t *node;          // Node to create probes on
     860             :     bool any_created;           // Whether any probes have been created
     861             : };
     862             : 
     863             : /*!
     864             :  * \internal
     865             :  * \brief Order a bundle replica's start after another replica's probe
     866             :  *
     867             :  * \param[in,out] replica    Replica to order start for
     868             :  * \param[in,out] user_data  Replica with probe to order after
     869             :  *
     870             :  * \return true (to indicate that any further replicas should be processed)
     871             :  */
     872             : static bool
     873           0 : order_replica_start_after(pcmk__bundle_replica_t *replica, void *user_data)
     874             : {
     875           0 :     pcmk__bundle_replica_t *probed_replica = user_data;
     876             : 
     877           0 :     if ((replica == probed_replica) || (replica->container == NULL)) {
     878           0 :         return true;
     879             :     }
     880           0 :     pcmk__new_ordering(probed_replica->container,
     881           0 :                        pcmk__op_key(probed_replica->container->id,
     882             :                                     PCMK_ACTION_MONITOR, 0),
     883             :                        NULL, replica->container,
     884           0 :                        pcmk__op_key(replica->container->id, PCMK_ACTION_START,
     885             :                                     0),
     886             :                        NULL, pcmk__ar_ordered|pcmk__ar_if_on_same_node,
     887           0 :                        replica->container->cluster);
     888           0 :     return true;
     889             : }
     890             : 
     891             : /*!
     892             :  * \internal
     893             :  * \brief Create probes for a bundle replica's resources
     894             :  *
     895             :  * \param[in,out] replica    Replica to create probes for
     896             :  * \param[in,out] user_data  struct probe_data
     897             :  *
     898             :  * \return true (to indicate that any further replicas should be processed)
     899             :  */
     900             : static bool
     901           0 : create_replica_probes(pcmk__bundle_replica_t *replica, void *user_data)
     902             : {
     903           0 :     struct probe_data *probe_data = user_data;
     904             : 
     905           0 :     if ((replica->ip != NULL)
     906           0 :         && replica->ip->cmds->create_probe(replica->ip, probe_data->node)) {
     907           0 :         probe_data->any_created = true;
     908             :     }
     909           0 :     if ((replica->child != NULL)
     910           0 :         && pcmk__same_node(probe_data->node, replica->node)
     911           0 :         && replica->child->cmds->create_probe(replica->child,
     912             :                                               probe_data->node)) {
     913           0 :         probe_data->any_created = true;
     914             :     }
     915           0 :     if ((replica->container != NULL)
     916           0 :         && replica->container->cmds->create_probe(replica->container,
     917             :                                                   probe_data->node)) {
     918           0 :         probe_data->any_created = true;
     919             : 
     920             :         /* If we're limited to one replica per host (due to
     921             :          * the lack of an IP range probably), then we don't
     922             :          * want any of our peer containers starting until
     923             :          * we've established that no other copies are already
     924             :          * running.
     925             :          *
     926             :          * Partly this is to ensure that the maximum replicas per host is
     927             :          * observed, but also to ensure that the containers
     928             :          * don't fail to start because the necessary port
     929             :          * mappings (which won't include an IP for uniqueness)
     930             :          * are already taken
     931             :          */
     932           0 :         if (probe_data->bundle->fns->max_per_node(probe_data->bundle) == 1) {
     933           0 :             pe__foreach_bundle_replica(probe_data->bundle,
     934             :                                        order_replica_start_after, replica);
     935             :         }
     936             :     }
     937           0 :     if ((replica->container != NULL) && (replica->remote != NULL)
     938           0 :         && replica->remote->cmds->create_probe(replica->remote,
     939             :                                                probe_data->node)) {
     940             :         /* Do not probe the remote resource until we know where the container is
     941             :          * running. This is required for REMOTE_CONTAINER_HACK to correctly
     942             :          * probe remote resources.
     943             :          */
     944           0 :         char *probe_uuid = pcmk__op_key(replica->remote->id,
     945             :                                         PCMK_ACTION_MONITOR, 0);
     946           0 :         pcmk_action_t *probe = find_first_action(replica->remote->actions,
     947             :                                                  probe_uuid, NULL,
     948           0 :                                                  probe_data->node);
     949             : 
     950           0 :         free(probe_uuid);
     951           0 :         if (probe != NULL) {
     952           0 :             probe_data->any_created = true;
     953           0 :             pcmk__rsc_trace(probe_data->bundle, "Ordering %s probe on %s",
     954             :                             replica->remote->id,
     955             :                             pcmk__node_name(probe_data->node));
     956           0 :             pcmk__new_ordering(replica->container,
     957           0 :                                pcmk__op_key(replica->container->id,
     958             :                                             PCMK_ACTION_START, 0),
     959             :                                NULL, replica->remote, NULL, probe,
     960             :                                pcmk__ar_nested_remote_probe,
     961           0 :                                probe_data->bundle->cluster);
     962             :         }
     963             :     }
     964           0 :     return true;
     965             : }
     966             : 
     967             : /*!
     968             :  * \internal
     969             :  *
     970             :  * \brief Schedule any probes needed for a bundle resource on a node
     971             :  *
     972             :  * \param[in,out] rsc   Bundle resource to create probes for
     973             :  * \param[in,out] node  Node to create probe on
     974             :  *
     975             :  * \return true if any probe was created, otherwise false
     976             :  */
     977             : bool
     978           0 : pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
     979             : {
     980           0 :     struct probe_data probe_data = { rsc, node, false };
     981             : 
     982           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
     983           0 :     pe__foreach_bundle_replica(rsc, create_replica_probes, &probe_data);
     984           0 :     return probe_data.any_created;
     985             : }
     986             : 
     987             : /*!
     988             :  * \internal
     989             :  * \brief Output actions for one bundle replica
     990             :  *
     991             :  * \param[in,out] replica    Replica to output actions for
     992             :  * \param[in]     user_data  Unused
     993             :  *
     994             :  * \return true (to indicate that any further replicas should be processed)
     995             :  */
     996             : static bool
     997           0 : output_replica_actions(pcmk__bundle_replica_t *replica, void *user_data)
     998             : {
     999           0 :     if (replica->ip != NULL) {
    1000           0 :         replica->ip->cmds->output_actions(replica->ip);
    1001             :     }
    1002           0 :     if (replica->container != NULL) {
    1003           0 :         replica->container->cmds->output_actions(replica->container);
    1004             :     }
    1005           0 :     if (replica->remote != NULL) {
    1006           0 :         replica->remote->cmds->output_actions(replica->remote);
    1007             :     }
    1008           0 :     if (replica->child != NULL) {
    1009           0 :         replica->child->cmds->output_actions(replica->child);
    1010             :     }
    1011           0 :     return true;
    1012             : }
    1013             : 
    1014             : /*!
    1015             :  * \internal
    1016             :  * \brief Output a summary of scheduled actions for a bundle resource
    1017             :  *
    1018             :  * \param[in,out] rsc  Bundle resource to output actions for
    1019             :  */
    1020             : void
    1021           0 : pcmk__output_bundle_actions(pcmk_resource_t *rsc)
    1022             : {
    1023           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
    1024           0 :     pe__foreach_bundle_replica(rsc, output_replica_actions, NULL);
    1025           0 : }
    1026             : 
    1027             : // Bundle implementation of pcmk_assignment_methods_t:add_utilization()
    1028             : void
    1029           0 : pcmk__bundle_add_utilization(const pcmk_resource_t *rsc,
    1030             :                              const pcmk_resource_t *orig_rsc, GList *all_rscs,
    1031             :                              GHashTable *utilization)
    1032             : {
    1033           0 :     pcmk_resource_t *container = NULL;
    1034             : 
    1035           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
    1036             : 
    1037           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
    1038           0 :         return;
    1039             :     }
    1040             : 
    1041             :     /* All bundle replicas are identical, so using the utilization of the first
    1042             :      * is sufficient for any. Only the implicit container resource can have
    1043             :      * utilization values.
    1044             :      */
    1045           0 :     container = pe__first_container(rsc);
    1046           0 :     if (container != NULL) {
    1047           0 :         container->cmds->add_utilization(container, orig_rsc, all_rscs,
    1048             :                                          utilization);
    1049             :     }
    1050             : }
    1051             : 
    1052             : // Bundle implementation of pcmk_assignment_methods_t:shutdown_lock()
    1053             : void
    1054           0 : pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc)
    1055             : {
    1056           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_bundle));
    1057             :     // Bundles currently don't support shutdown locks
    1058           0 : }

Generated by: LCOV version 1.14