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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-2024 the Pacemaker project contributors
       3             :  *
       4             :  * The version control history for this file may have further details.
       5             :  *
       6             :  * This source code is licensed under the GNU General Public License version 2
       7             :  * or later (GPLv2+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <crm/common/xml.h>
      13             : #include <pacemaker-internal.h>
      14             : 
      15             : #include "libpacemaker_private.h"
      16             : 
      17             : /*!
      18             :  * \internal
      19             :  * \brief Assign a clone resource's instances to nodes
      20             :  *
      21             :  * \param[in,out] rsc           Clone resource to assign
      22             :  * \param[in]     prefer        Node to prefer, if all else is equal
      23             :  * \param[in]     stop_if_fail  If \c true and a primitive descendant of \p rsc
      24             :  *                              can't be assigned to a node, set the
      25             :  *                              descendant's next role to stopped and update
      26             :  *                              existing actions
      27             :  *
      28             :  * \return NULL (clones are not assigned to a single node)
      29             :  *
      30             :  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
      31             :  *       completely undo the assignment. A successful assignment can be either
      32             :  *       undone or left alone as final. A failed assignment has the same effect
      33             :  *       as calling pcmk__unassign_resource(); there are no side effects on
      34             :  *       roles or actions.
      35             :  */
      36             : pcmk_node_t *
      37           0 : pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
      38             :                    bool stop_if_fail)
      39             : {
      40           0 :     GList *colocations = NULL;
      41             : 
      42           0 :     CRM_ASSERT(pcmk__is_clone(rsc));
      43             : 
      44           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
      45           0 :         return NULL; // Assignment has already been done
      46             :     }
      47             : 
      48             :     // Detect assignment loops
      49           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) {
      50           0 :         pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id);
      51           0 :         return NULL;
      52             :     }
      53           0 :     pcmk__set_rsc_flags(rsc, pcmk_rsc_assigning);
      54             : 
      55             :     // If this clone is promotable, consider nodes' promotion scores
      56           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
      57           0 :         pcmk__add_promotion_scores(rsc);
      58             :     }
      59             : 
      60             :     // If this clone is colocated with any other resources, assign those first
      61           0 :     colocations = pcmk__this_with_colocations(rsc);
      62           0 :     for (GList *iter = colocations; iter != NULL; iter = iter->next) {
      63           0 :         pcmk__colocation_t *constraint = (pcmk__colocation_t *) iter->data;
      64             : 
      65           0 :         pcmk__rsc_trace(rsc, "%s: Assigning colocation %s primary %s first",
      66             :                         rsc->id, constraint->id, constraint->primary->id);
      67           0 :         constraint->primary->cmds->assign(constraint->primary, prefer,
      68             :                                           stop_if_fail);
      69             :     }
      70           0 :     g_list_free(colocations);
      71             : 
      72             :     // If any resources are colocated with this one, consider their preferences
      73           0 :     colocations = pcmk__with_this_colocations(rsc);
      74           0 :     g_list_foreach(colocations, pcmk__add_dependent_scores, rsc);
      75           0 :     g_list_free(colocations);
      76             : 
      77           0 :     pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
      78             :                                       pcmk_sched_output_scores),
      79             :                          rsc, __func__, rsc->allowed_nodes, rsc->cluster);
      80             : 
      81           0 :     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance);
      82           0 :     pcmk__assign_instances(rsc, rsc->children, pe__clone_max(rsc),
      83             :                            pe__clone_node_max(rsc));
      84             : 
      85           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
      86           0 :         pcmk__set_instance_roles(rsc);
      87             :     }
      88             : 
      89           0 :     pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning);
      90           0 :     pcmk__rsc_trace(rsc, "Assigned clone %s", rsc->id);
      91           0 :     return NULL;
      92             : }
      93             : 
      94             : /*!
      95             :  * \internal
      96             :  * \brief Create all actions needed for a given clone resource
      97             :  *
      98             :  * \param[in,out] rsc  Clone resource to create actions for
      99             :  */
     100             : void
     101           0 : pcmk__clone_create_actions(pcmk_resource_t *rsc)
     102             : {
     103           0 :     CRM_ASSERT(pcmk__is_clone(rsc));
     104             : 
     105           0 :     pcmk__rsc_trace(rsc, "Creating actions for clone %s", rsc->id);
     106           0 :     pcmk__create_instance_actions(rsc, rsc->children);
     107           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
     108           0 :         pcmk__create_promotable_actions(rsc);
     109             :     }
     110           0 : }
     111             : 
     112             : /*!
     113             :  * \internal
     114             :  * \brief Create implicit constraints needed for a clone resource
     115             :  *
     116             :  * \param[in,out] rsc  Clone resource to create implicit constraints for
     117             :  */
     118             : void
     119           0 : pcmk__clone_internal_constraints(pcmk_resource_t *rsc)
     120             : {
     121           0 :     bool ordered = false;
     122             : 
     123           0 :     CRM_ASSERT(pcmk__is_clone(rsc));
     124             : 
     125           0 :     pcmk__rsc_trace(rsc, "Creating internal constraints for clone %s", rsc->id);
     126             : 
     127             :     // Restart ordering: Stop -> stopped -> start -> started
     128           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
     129             :                                  rsc, PCMK_ACTION_START,
     130             :                                  pcmk__ar_ordered);
     131           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_START,
     132             :                                  rsc, PCMK_ACTION_RUNNING,
     133             :                                  pcmk__ar_unrunnable_first_blocks);
     134           0 :     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP,
     135             :                                  rsc, PCMK_ACTION_STOPPED,
     136             :                                  pcmk__ar_unrunnable_first_blocks);
     137             : 
     138             :     // Demoted -> stop and started -> promote
     139           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
     140           0 :         pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
     141             :                                      rsc, PCMK_ACTION_STOP,
     142             :                                      pcmk__ar_ordered);
     143           0 :         pcmk__order_resource_actions(rsc, PCMK_ACTION_RUNNING,
     144             :                                      rsc, PCMK_ACTION_PROMOTE,
     145             :                                      pcmk__ar_unrunnable_first_blocks);
     146             :     }
     147             : 
     148           0 :     ordered = pe__clone_is_ordered(rsc);
     149           0 :     if (ordered) {
     150             :         /* Ordered clone instances must start and stop by instance number. The
     151             :          * instances might have been previously shuffled for assignment or
     152             :          * promotion purposes, so re-sort them.
     153             :          */
     154           0 :         rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
     155             :     }
     156           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     157           0 :         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
     158             : 
     159           0 :         instance->cmds->internal_constraints(instance);
     160             : 
     161             :         // Start clone -> start instance -> clone started
     162           0 :         pcmk__order_starts(rsc, instance, pcmk__ar_unrunnable_first_blocks
     163             :                                           |pcmk__ar_then_implies_first_graphed);
     164           0 :         pcmk__order_resource_actions(instance, PCMK_ACTION_START,
     165             :                                      rsc, PCMK_ACTION_RUNNING,
     166             :                                      pcmk__ar_first_implies_then_graphed);
     167             : 
     168             :         // Stop clone -> stop instance -> clone stopped
     169           0 :         pcmk__order_stops(rsc, instance, pcmk__ar_then_implies_first_graphed);
     170           0 :         pcmk__order_resource_actions(instance, PCMK_ACTION_STOP,
     171             :                                      rsc, PCMK_ACTION_STOPPED,
     172             :                                      pcmk__ar_first_implies_then_graphed);
     173             : 
     174             :         /* Instances of ordered clones must be started and stopped by instance
     175             :          * number. Since only some instances may be starting or stopping, order
     176             :          * each instance relative to every later instance.
     177             :          */
     178           0 :         if (ordered) {
     179           0 :             for (GList *later = iter->next;
     180           0 :                  later != NULL; later = later->next) {
     181           0 :                 pcmk__order_starts(instance, (pcmk_resource_t *) later->data,
     182             :                                    pcmk__ar_ordered);
     183           0 :                 pcmk__order_stops((pcmk_resource_t *) later->data, instance,
     184             :                                   pcmk__ar_ordered);
     185             :             }
     186             :         }
     187             :     }
     188           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
     189           0 :         pcmk__order_promotable_instances(rsc);
     190             :     }
     191           0 : }
     192             : 
     193             : /*!
     194             :  * \internal
     195             :  * \brief Check whether colocated resources can be interleaved
     196             :  *
     197             :  * \param[in] colocation  Colocation constraint with clone as primary
     198             :  *
     199             :  * \return true if colocated resources can be interleaved, otherwise false
     200             :  */
     201             : static bool
     202           0 : can_interleave(const pcmk__colocation_t *colocation)
     203             : {
     204           0 :     const pcmk_resource_t *dependent = colocation->dependent;
     205             : 
     206             :     // Only colocations between clone or bundle resources use interleaving
     207           0 :     if (dependent->variant <= pcmk_rsc_variant_group) {
     208           0 :         return false;
     209             :     }
     210             : 
     211             :     // Only the dependent needs to be marked for interleaving
     212           0 :     if (!crm_is_true(g_hash_table_lookup(dependent->meta,
     213             :                                          PCMK_META_INTERLEAVE))) {
     214           0 :         return false;
     215             :     }
     216             : 
     217             :     /* @TODO Do we actually care about multiple primary instances sharing a
     218             :      * dependent instance?
     219             :      */
     220           0 :     if (dependent->fns->max_per_node(dependent)
     221           0 :         != colocation->primary->fns->max_per_node(colocation->primary)) {
     222           0 :         pcmk__config_err("Cannot interleave %s and %s because they do not "
     223             :                          "support the same number of instances per node",
     224             :                          dependent->id, colocation->primary->id);
     225           0 :         return false;
     226             :     }
     227             : 
     228           0 :     return true;
     229             : }
     230             : 
     231             : /*!
     232             :  * \internal
     233             :  * \brief Apply a colocation's score to node scores or resource priority
     234             :  *
     235             :  * Given a colocation constraint, apply its score to the dependent's
     236             :  * allowed node scores (if we are still placing resources) or priority (if
     237             :  * we are choosing promotable clone instance roles).
     238             :  *
     239             :  * \param[in,out] dependent      Dependent resource in colocation
     240             :  * \param[in]     primary        Primary resource in colocation
     241             :  * \param[in]     colocation     Colocation constraint to apply
     242             :  * \param[in]     for_dependent  true if called on behalf of dependent
     243             :  */
     244             : void
     245           0 : pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent,
     246             :                               const pcmk_resource_t *primary,
     247             :                               const pcmk__colocation_t *colocation,
     248             :                               bool for_dependent)
     249             : {
     250           0 :     const GList *iter = NULL;
     251             : 
     252             :     /* This should never be called for the clone itself as a dependent. Instead,
     253             :      * we add its colocation constraints to its instances and call the
     254             :      * apply_coloc_score() method for the instances as dependents.
     255             :      */
     256           0 :     CRM_ASSERT(!for_dependent);
     257             : 
     258           0 :     CRM_ASSERT((colocation != NULL) && pcmk__is_clone(primary)
     259             :                && (dependent != NULL)
     260             :                && (dependent->variant == pcmk_rsc_variant_primitive));
     261             : 
     262           0 :     if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
     263           0 :         pcmk__rsc_trace(primary,
     264             :                         "Delaying processing colocation %s "
     265             :                         "because cloned primary %s is still provisional",
     266             :                         colocation->id, primary->id);
     267           0 :         return;
     268             :     }
     269             : 
     270           0 :     pcmk__rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)",
     271             :                     colocation->id, dependent->id, primary->id,
     272             :                     pcmk_readable_score(colocation->score));
     273             : 
     274             :     // Apply role-specific colocations
     275           0 :     if (pcmk_is_set(primary->flags, pcmk_rsc_promotable)
     276           0 :         && (colocation->primary_role != pcmk_role_unknown)) {
     277             : 
     278           0 :         if (pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
     279             :             // We're assigning the dependent to a node
     280           0 :             pcmk__update_dependent_with_promotable(primary, dependent,
     281             :                                                    colocation);
     282           0 :             return;
     283             :         }
     284             : 
     285           0 :         if (colocation->dependent_role == pcmk_role_promoted) {
     286             :             // We're choosing a role for the dependent
     287           0 :             pcmk__update_promotable_dependent_priority(primary, dependent,
     288             :                                                        colocation);
     289           0 :             return;
     290             :         }
     291             :     }
     292             : 
     293             :     // Apply interleaved colocations
     294           0 :     if (can_interleave(colocation)) {
     295           0 :         const pcmk_resource_t *primary_instance = NULL;
     296             : 
     297           0 :         primary_instance = pcmk__find_compatible_instance(dependent, primary,
     298             :                                                           pcmk_role_unknown,
     299             :                                                           false);
     300           0 :         if (primary_instance != NULL) {
     301           0 :             pcmk__rsc_debug(primary, "Interleaving %s with %s",
     302             :                             dependent->id, primary_instance->id);
     303           0 :             dependent->cmds->apply_coloc_score(dependent, primary_instance,
     304             :                                                colocation, true);
     305             : 
     306           0 :         } else if (colocation->score >= PCMK_SCORE_INFINITY) {
     307           0 :             crm_notice("%s cannot run because it cannot interleave with "
     308             :                        "any instance of %s", dependent->id, primary->id);
     309           0 :             pcmk__assign_resource(dependent, NULL, true, true);
     310             : 
     311             :         } else {
     312           0 :             pcmk__rsc_debug(primary,
     313             :                             "%s will not colocate with %s "
     314             :                             "because no instance can interleave with it",
     315             :                             dependent->id, primary->id);
     316             :         }
     317             : 
     318           0 :         return;
     319             :     }
     320             : 
     321             :     // Apply mandatory colocations
     322           0 :     if (colocation->score >= PCMK_SCORE_INFINITY) {
     323           0 :         GList *primary_nodes = NULL;
     324             : 
     325             :         // Dependent can run only where primary will have unblocked instances
     326           0 :         for (iter = primary->children; iter != NULL; iter = iter->next) {
     327           0 :             const pcmk_resource_t *instance = iter->data;
     328           0 :             pcmk_node_t *chosen = instance->fns->location(instance, NULL, 0);
     329             : 
     330           0 :             if ((chosen != NULL)
     331           0 :                 && !is_set_recursive(instance, pcmk_rsc_blocked, TRUE)) {
     332           0 :                 pcmk__rsc_trace(primary, "Allowing %s: %s %d",
     333             :                                 colocation->id, pcmk__node_name(chosen),
     334             :                                 chosen->weight);
     335           0 :                 primary_nodes = g_list_prepend(primary_nodes, chosen);
     336             :             }
     337             :         }
     338           0 :         pcmk__colocation_intersect_nodes(dependent, primary, colocation,
     339             :                                          primary_nodes, false);
     340           0 :         g_list_free(primary_nodes);
     341           0 :         return;
     342             :     }
     343             : 
     344             :     // Apply optional colocations
     345           0 :     for (iter = primary->children; iter != NULL; iter = iter->next) {
     346           0 :         const pcmk_resource_t *instance = iter->data;
     347             : 
     348           0 :         instance->cmds->apply_coloc_score(dependent, instance, colocation,
     349             :                                           false);
     350             :     }
     351             : }
     352             : 
     353             : // Clone implementation of pcmk_assignment_methods_t:with_this_colocations()
     354             : void
     355           0 : pcmk__with_clone_colocations(const pcmk_resource_t *rsc,
     356             :                              const pcmk_resource_t *orig_rsc, GList **list)
     357             : {
     358           0 :     CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
     359             : 
     360           0 :     pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
     361             : 
     362           0 :     if (rsc->parent != NULL) {
     363           0 :         rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list);
     364             :     }
     365             : }
     366             : 
     367             : // Clone implementation of pcmk_assignment_methods_t:this_with_colocations()
     368             : void
     369           0 : pcmk__clone_with_colocations(const pcmk_resource_t *rsc,
     370             :                              const pcmk_resource_t *orig_rsc, GList **list)
     371             : {
     372           0 :     CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
     373             : 
     374           0 :     pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
     375             : 
     376           0 :     if (rsc->parent != NULL) {
     377           0 :         rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list);
     378             :     }
     379             : }
     380             : 
     381             : /*!
     382             :  * \internal
     383             :  * \brief Return action flags for a given clone resource action
     384             :  *
     385             :  * \param[in,out] action  Action to get flags for
     386             :  * \param[in]     node    If not NULL, limit effects to this node
     387             :  *
     388             :  * \return Flags appropriate to \p action on \p node
     389             :  */
     390             : uint32_t
     391           0 : pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     392             : {
     393           0 :     CRM_ASSERT((action != NULL) && pcmk__is_clone(action->rsc));
     394             : 
     395           0 :     return pcmk__collective_action_flags(action, action->rsc->children, node);
     396             : }
     397             : 
     398             : /*!
     399             :  * \internal
     400             :  * \brief Apply a location constraint to a clone resource's allowed node scores
     401             :  *
     402             :  * \param[in,out] rsc       Clone resource to apply constraint to
     403             :  * \param[in,out] location  Location constraint to apply
     404             :  */
     405             : void
     406           0 : pcmk__clone_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     407             : {
     408           0 :     CRM_CHECK((location != NULL) && pcmk__is_clone(rsc), return);
     409             : 
     410           0 :     pcmk__apply_location(rsc, location);
     411             : 
     412           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     413           0 :         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
     414             : 
     415           0 :         instance->cmds->apply_location(instance, location);
     416             :     }
     417             : }
     418             : 
     419             : // GFunc wrapper for calling the action_flags() resource method
     420             : static void
     421           0 : call_action_flags(gpointer data, gpointer user_data)
     422             : {
     423           0 :     pcmk_resource_t *rsc = user_data;
     424             : 
     425           0 :     rsc->cmds->action_flags((pcmk_action_t *) data, NULL);
     426           0 : }
     427             : 
     428             : /*!
     429             :  * \internal
     430             :  * \brief Add a clone resource's actions to the transition graph
     431             :  *
     432             :  * \param[in,out] rsc  Resource whose actions should be added
     433             :  */
     434             : void
     435           0 : pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc)
     436             : {
     437           0 :     CRM_ASSERT(pcmk__is_clone(rsc));
     438             : 
     439           0 :     g_list_foreach(rsc->actions, call_action_flags, rsc);
     440           0 :     pe__create_clone_notifications(rsc);
     441             : 
     442           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     443           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
     444             : 
     445           0 :         child_rsc->cmds->add_actions_to_graph(child_rsc);
     446             :     }
     447             : 
     448           0 :     pcmk__add_rsc_actions_to_graph(rsc);
     449           0 :     pe__free_clone_notification_data(rsc);
     450           0 : }
     451             : 
     452             : /*!
     453             :  * \internal
     454             :  * \brief Check whether a resource or any children have been probed on a node
     455             :  *
     456             :  * \param[in] rsc   Resource to check
     457             :  * \param[in] node  Node to check
     458             :  *
     459             :  * \return true if \p node is in the known_on table of \p rsc or any of its
     460             :  *         children, otherwise false
     461             :  */
     462             : static bool
     463           0 : rsc_probed_on(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     464             : {
     465           0 :     if (rsc->children != NULL) {
     466           0 :         for (GList *child_iter = rsc->children; child_iter != NULL;
     467           0 :              child_iter = child_iter->next) {
     468             : 
     469           0 :             pcmk_resource_t *child = (pcmk_resource_t *) child_iter->data;
     470             : 
     471           0 :             if (rsc_probed_on(child, node)) {
     472           0 :                 return true;
     473             :             }
     474             :         }
     475           0 :         return false;
     476             :     }
     477             : 
     478           0 :     if (rsc->known_on != NULL) {
     479             :         GHashTableIter iter;
     480           0 :         pcmk_node_t *known_node = NULL;
     481             : 
     482           0 :         g_hash_table_iter_init(&iter, rsc->known_on);
     483           0 :         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) {
     484           0 :             if (pcmk__same_node(node, known_node)) {
     485           0 :                 return true;
     486             :             }
     487             :         }
     488             :     }
     489           0 :     return false;
     490             : }
     491             : 
     492             : /*!
     493             :  * \internal
     494             :  * \brief Find clone instance that has been probed on given node
     495             :  *
     496             :  * \param[in] clone  Clone resource to check
     497             :  * \param[in] node   Node to check
     498             :  *
     499             :  * \return Instance of \p clone that has been probed on \p node if any,
     500             :  *         otherwise NULL
     501             :  */
     502             : static pcmk_resource_t *
     503           0 : find_probed_instance_on(const pcmk_resource_t *clone, const pcmk_node_t *node)
     504             : {
     505           0 :     for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
     506           0 :         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
     507             : 
     508           0 :         if (rsc_probed_on(instance, node)) {
     509           0 :             return instance;
     510             :         }
     511             :     }
     512           0 :     return NULL;
     513             : }
     514             : 
     515             : /*!
     516             :  * \internal
     517             :  * \brief Probe an anonymous clone on a node
     518             :  *
     519             :  * \param[in,out] clone  Anonymous clone to probe
     520             :  * \param[in,out] node   Node to probe \p clone on
     521             :  */
     522             : static bool
     523           0 : probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node)
     524             : {
     525             :     // Check whether we already probed an instance on this node
     526           0 :     pcmk_resource_t *child = find_probed_instance_on(clone, node);
     527             : 
     528             :     // Otherwise, check if we plan to start an instance on this node
     529           0 :     for (GList *iter = clone->children; (iter != NULL) && (child == NULL);
     530           0 :          iter = iter->next) {
     531           0 :         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
     532           0 :         const pcmk_node_t *instance_node = NULL;
     533             : 
     534           0 :         instance_node = instance->fns->location(instance, NULL, 0);
     535           0 :         if (pcmk__same_node(instance_node, node)) {
     536           0 :             child = instance;
     537             :         }
     538             :     }
     539             : 
     540             :     // Otherwise, use the first clone instance
     541           0 :     if (child == NULL) {
     542           0 :         child = clone->children->data;
     543             :     }
     544             : 
     545             :     // Anonymous clones only need to probe a single instance
     546           0 :     return child->cmds->create_probe(child, node);
     547             : }
     548             : 
     549             : /*!
     550             :  * \internal
     551             :  * \brief Schedule any probes needed for a resource on a node
     552             :  *
     553             :  * \param[in,out] rsc   Resource to create probe for
     554             :  * \param[in,out] node  Node to create probe on
     555             :  *
     556             :  * \return true if any probe was created, otherwise false
     557             :  */
     558             : bool
     559           0 : pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
     560             : {
     561           0 :     CRM_ASSERT((node != NULL) && pcmk__is_clone(rsc));
     562             : 
     563           0 :     if (rsc->exclusive_discover) {
     564             :         /* The clone is configured to be probed only where a location constraint
     565             :          * exists with PCMK_XA_RESOURCE_DISCOVERY set to exclusive.
     566             :          *
     567             :          * This check is not strictly necessary here since the instance's
     568             :          * create_probe() method would also check, but doing it here is more
     569             :          * efficient (especially for unique clones with a large number of
     570             :          * instances), and affects the CRM_meta_notify_available_uname variable
     571             :          * passed with notify actions.
     572             :          */
     573           0 :         pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes,
     574           0 :                                                    node->details->id);
     575             : 
     576           0 :         if ((allowed == NULL)
     577           0 :             || (allowed->rsc_discover_mode != pcmk_probe_exclusive)) {
     578             :             /* This node is not marked for resource discovery. Remove it from
     579             :              * allowed_nodes so that notifications contain only nodes that the
     580             :              * clone can possibly run on.
     581             :              */
     582           0 :             pcmk__rsc_trace(rsc,
     583             :                             "Skipping probe for %s on %s because resource has "
     584             :                             "exclusive discovery but is not allowed on node",
     585             :                             rsc->id, pcmk__node_name(node));
     586           0 :             g_hash_table_remove(rsc->allowed_nodes, node->details->id);
     587           0 :             return false;
     588             :         }
     589             :     }
     590             : 
     591           0 :     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
     592           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
     593           0 :         return pcmk__probe_resource_list(rsc->children, node);
     594             :     } else {
     595           0 :         return probe_anonymous_clone(rsc, node);
     596             :     }
     597             : }
     598             : 
     599             : /*!
     600             :  * \internal
     601             :  * \brief Add meta-attributes relevant to transition graph actions to XML
     602             :  *
     603             :  * Add clone-specific meta-attributes needed for transition graph actions.
     604             :  *
     605             :  * \param[in]     rsc  Clone resource whose meta-attributes should be added
     606             :  * \param[in,out] xml  Transition graph action attributes XML to add to
     607             :  */
     608             : void
     609           0 : pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
     610             : {
     611           0 :     char *name = NULL;
     612             : 
     613           0 :     CRM_ASSERT(pcmk__is_clone(rsc) && (xml != NULL));
     614             : 
     615           0 :     name = crm_meta_name(PCMK_META_GLOBALLY_UNIQUE);
     616           0 :     crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
     617           0 :     free(name);
     618             : 
     619           0 :     name = crm_meta_name(PCMK_META_NOTIFY);
     620           0 :     crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_notify));
     621           0 :     free(name);
     622             : 
     623           0 :     name = crm_meta_name(PCMK_META_CLONE_MAX);
     624           0 :     crm_xml_add_int(xml, name, pe__clone_max(rsc));
     625           0 :     free(name);
     626             : 
     627           0 :     name = crm_meta_name(PCMK_META_CLONE_NODE_MAX);
     628           0 :     crm_xml_add_int(xml, name, pe__clone_node_max(rsc));
     629           0 :     free(name);
     630             : 
     631           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
     632           0 :         int promoted_max = pe__clone_promoted_max(rsc);
     633           0 :         int promoted_node_max = pe__clone_promoted_node_max(rsc);
     634             : 
     635           0 :         name = crm_meta_name(PCMK_META_PROMOTED_MAX);
     636           0 :         crm_xml_add_int(xml, name, promoted_max);
     637           0 :         free(name);
     638             : 
     639           0 :         name = crm_meta_name(PCMK_META_PROMOTED_NODE_MAX);
     640           0 :         crm_xml_add_int(xml, name, promoted_node_max);
     641           0 :         free(name);
     642             : 
     643             :         /* @COMPAT Maintain backward compatibility with resource agents that
     644             :          * expect the old names (deprecated since 2.0.0).
     645             :          */
     646           0 :         name = crm_meta_name(PCMK__META_PROMOTED_MAX_LEGACY);
     647           0 :         crm_xml_add_int(xml, name, promoted_max);
     648           0 :         free(name);
     649             : 
     650           0 :         name = crm_meta_name(PCMK__META_PROMOTED_NODE_MAX_LEGACY);
     651           0 :         crm_xml_add_int(xml, name, promoted_node_max);
     652           0 :         free(name);
     653             :     }
     654           0 : }
     655             : 
     656             : // Clone implementation of pcmk_assignment_methods_t:add_utilization()
     657             : void
     658           0 : pcmk__clone_add_utilization(const pcmk_resource_t *rsc,
     659             :                             const pcmk_resource_t *orig_rsc, GList *all_rscs,
     660             :                             GHashTable *utilization)
     661             : {
     662           0 :     bool existing = false;
     663           0 :     pcmk_resource_t *child = NULL;
     664             : 
     665           0 :     CRM_ASSERT(pcmk__is_clone(rsc) && (orig_rsc != NULL)
     666             :                && (utilization != NULL));
     667             : 
     668           0 :     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
     669           0 :         return;
     670             :     }
     671             : 
     672             :     // Look for any child already existing in the list
     673           0 :     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
     674           0 :         child = (pcmk_resource_t *) iter->data;
     675           0 :         if (g_list_find(all_rscs, child)) {
     676           0 :             existing = true; // Keep checking remaining children
     677             :         } else {
     678             :             // If this is a clone of a group, look for group's members
     679           0 :             for (GList *member_iter = child->children; member_iter != NULL;
     680           0 :                  member_iter = member_iter->next) {
     681             : 
     682           0 :                 pcmk_resource_t *member = (pcmk_resource_t *) member_iter->data;
     683             : 
     684           0 :                 if (g_list_find(all_rscs, member) != NULL) {
     685             :                     // Add *child's* utilization, not group member's
     686           0 :                     child->cmds->add_utilization(child, orig_rsc, all_rscs,
     687             :                                                  utilization);
     688           0 :                     existing = true;
     689           0 :                     break;
     690             :                 }
     691             :             }
     692             :         }
     693             :     }
     694             : 
     695           0 :     if (!existing && (rsc->children != NULL)) {
     696             :         // If nothing was found, still add first child's utilization
     697           0 :         child = (pcmk_resource_t *) rsc->children->data;
     698             : 
     699           0 :         child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization);
     700             :     }
     701             : }
     702             : 
     703             : // Clone implementation of pcmk_assignment_methods_t:shutdown_lock()
     704             : void
     705           0 : pcmk__clone_shutdown_lock(pcmk_resource_t *rsc)
     706             : {
     707           0 :     CRM_ASSERT(pcmk__is_clone(rsc));
     708           0 :     return; // Clones currently don't support shutdown locks
     709             : }

Generated by: LCOV version 1.14