LCOV - code coverage report
Current view: top level - pengine - group.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 233 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 16 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 Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <stdint.h>
      13             : 
      14             : #include <crm/pengine/rules.h>
      15             : #include <crm/pengine/status.h>
      16             : #include <crm/pengine/internal.h>
      17             : #include <crm/common/xml.h>
      18             : #include <crm/common/output.h>
      19             : #include <crm/common/strings_internal.h>
      20             : #include <crm/common/xml_internal.h>
      21             : #include <pe_status_private.h>
      22             : 
      23             : typedef struct group_variant_data_s {
      24             :     pcmk_resource_t *last_child;    // Last group member
      25             :     uint32_t flags;                 // Group of enum pcmk__group_flags
      26             : } group_variant_data_t;
      27             : 
      28             : /*!
      29             :  * \internal
      30             :  * \brief Get a group's last member
      31             :  *
      32             :  * \param[in] group  Group resource to check
      33             :  *
      34             :  * \return Last member of \p group if any, otherwise NULL
      35             :  */
      36             : pcmk_resource_t *
      37           0 : pe__last_group_member(const pcmk_resource_t *group)
      38             : {
      39           0 :     if (group != NULL) {
      40           0 :         CRM_CHECK((group->variant == pcmk_rsc_variant_group)
      41             :                   && (group->variant_opaque != NULL), return NULL);
      42           0 :         return ((group_variant_data_t *) group->variant_opaque)->last_child;
      43             :     }
      44           0 :     return NULL;
      45             : }
      46             : 
      47             : /*!
      48             :  * \internal
      49             :  * \brief Check whether a group flag is set
      50             :  *
      51             :  * \param[in] group  Group resource to check
      52             :  * \param[in] flags  Flag or flags to check
      53             :  *
      54             :  * \return true if all \p flags are set for \p group, otherwise false
      55             :  */
      56             : bool
      57           0 : pe__group_flag_is_set(const pcmk_resource_t *group, uint32_t flags)
      58             : {
      59           0 :     group_variant_data_t *group_data = NULL;
      60             : 
      61           0 :     CRM_CHECK((group != NULL) && (group->variant == pcmk_rsc_variant_group)
      62             :               && (group->variant_opaque != NULL), return false);
      63           0 :     group_data = (group_variant_data_t *) group->variant_opaque;
      64           0 :     return pcmk_all_flags_set(group_data->flags, flags);
      65             : }
      66             : 
      67             : /*!
      68             :  * \internal
      69             :  * \brief Set a (deprecated) group flag
      70             :  *
      71             :  * \param[in,out] group   Group resource to check
      72             :  * \param[in]     option  Name of boolean configuration option
      73             :  * \param[in]     flag    Flag to set if \p option is true (which is default)
      74             :  * \param[in]     wo_bit  "Warn once" flag to use for deprecation warning
      75             :  */
      76             : static void
      77           0 : set_group_flag(pcmk_resource_t *group, const char *option, uint32_t flag,
      78             :                uint32_t wo_bit)
      79             : {
      80           0 :     const char *value_s = NULL;
      81           0 :     int value = 0;
      82             : 
      83           0 :     value_s = g_hash_table_lookup(group->meta, option);
      84             : 
      85             :     // We don't actually need the null check but it speeds up the common case
      86           0 :     if ((value_s == NULL) || (crm_str_to_boolean(value_s, &value) < 0)
      87           0 :         || (value != 0)) {
      88             : 
      89           0 :         ((group_variant_data_t *) group->variant_opaque)->flags |= flag;
      90             : 
      91             :     } else {
      92           0 :         pcmk__warn_once(wo_bit,
      93             :                         "Support for the '%s' group meta-attribute is "
      94             :                         "deprecated and will be removed in a future release "
      95             :                         "(use a resource set instead)", option);
      96             :     }
      97           0 : }
      98             : 
      99             : static int
     100           0 : inactive_resources(pcmk_resource_t *rsc)
     101             : {
     102           0 :     int retval = 0;
     103             : 
     104           0 :     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
     105           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     106             : 
     107           0 :         if (!child_rsc->fns->active(child_rsc, TRUE)) {
     108           0 :             retval++;
     109             :         }
     110             :     }
     111             : 
     112           0 :     return retval;
     113             : }
     114             : 
     115             : static void
     116           0 : group_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc,
     117             :              int n_inactive, bool show_inactive, const char *desc)
     118             : {
     119           0 :     GString *attrs = NULL;
     120             : 
     121           0 :     if (n_inactive > 0 && !show_inactive) {
     122           0 :         attrs = g_string_sized_new(64);
     123           0 :         g_string_append_printf(attrs, "%d member%s inactive", n_inactive,
     124             :                                pcmk__plural_s(n_inactive));
     125             :     }
     126             : 
     127           0 :     if (pe__resource_is_disabled(rsc)) {
     128           0 :         pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
     129             :     }
     130             : 
     131           0 :     if (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)) {
     132           0 :         pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
     133             : 
     134           0 :     } else if (!pcmk_is_set(rsc->flags, pcmk_rsc_managed)) {
     135           0 :         pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
     136             :     }
     137             : 
     138           0 :     if (attrs != NULL) {
     139           0 :         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s (%s)%s%s%s",
     140             :                                  rsc->id,
     141             :                                  (const char *) attrs->str, desc ? " (" : "",
     142             :                                  desc ? desc : "", desc ? ")" : "");
     143           0 :         g_string_free(attrs, TRUE);
     144             :     } else {
     145           0 :         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s%s%s%s",
     146             :                                  rsc->id,
     147             :                                  desc ? " (" : "", desc ? desc : "",
     148             :                                  desc ? ")" : "");
     149             :     }
     150           0 : }
     151             : 
     152             : static bool
     153           0 : skip_child_rsc(pcmk_resource_t *rsc, pcmk_resource_t *child,
     154             :                gboolean parent_passes, GList *only_rsc, uint32_t show_opts)
     155             : {
     156           0 :     bool star_list = pcmk__list_of_1(only_rsc) &&
     157           0 :                      pcmk__str_eq("*", g_list_first(only_rsc)->data, pcmk__str_none);
     158           0 :     bool child_filtered = child->fns->is_filtered(child, only_rsc, FALSE);
     159           0 :     bool child_active = child->fns->active(child, FALSE);
     160           0 :     bool show_inactive = pcmk_is_set(show_opts, pcmk_show_inactive_rscs);
     161             : 
     162             :     /* If the resource is in only_rsc by name (so, ignoring "*") then allow
     163             :      * it regardless of if it's active or not.
     164             :      */
     165           0 :     if (!star_list && !child_filtered) {
     166           0 :         return false;
     167             : 
     168           0 :     } else if (!child_filtered && (child_active || show_inactive)) {
     169           0 :         return false;
     170             : 
     171           0 :     } else if (parent_passes && (child_active || show_inactive)) {
     172           0 :         return false;
     173             : 
     174             :     }
     175             : 
     176           0 :     return true;
     177             : }
     178             : 
     179             : gboolean
     180           0 : group_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
     181             : {
     182           0 :     xmlNode *xml_obj = rsc->xml;
     183           0 :     xmlNode *xml_native_rsc = NULL;
     184           0 :     group_variant_data_t *group_data = NULL;
     185           0 :     const char *clone_id = NULL;
     186             : 
     187           0 :     pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
     188             : 
     189           0 :     group_data = pcmk__assert_alloc(1, sizeof(group_variant_data_t));
     190           0 :     group_data->last_child = NULL;
     191           0 :     rsc->variant_opaque = group_data;
     192             : 
     193             :     // @COMPAT These are deprecated since 2.1.5
     194           0 :     set_group_flag(rsc, PCMK_META_ORDERED, pcmk__group_ordered,
     195             :                    pcmk__wo_group_order);
     196           0 :     set_group_flag(rsc, "collocated", pcmk__group_colocated,
     197             :                    pcmk__wo_group_coloc);
     198             : 
     199           0 :     clone_id = crm_element_value(rsc->xml, PCMK__META_CLONE);
     200             : 
     201           0 :     for (xml_native_rsc = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
     202           0 :          xml_native_rsc != NULL;
     203           0 :          xml_native_rsc = pcmk__xe_next(xml_native_rsc)) {
     204             : 
     205           0 :         if (pcmk__xe_is(xml_native_rsc, PCMK_XE_PRIMITIVE)) {
     206           0 :             pcmk_resource_t *new_rsc = NULL;
     207             : 
     208           0 :             crm_xml_add(xml_native_rsc, PCMK__META_CLONE, clone_id);
     209           0 :             if (pe__unpack_resource(xml_native_rsc, &new_rsc, rsc,
     210             :                                     scheduler) != pcmk_rc_ok) {
     211           0 :                 continue;
     212             :             }
     213             : 
     214           0 :             rsc->children = g_list_append(rsc->children, new_rsc);
     215           0 :             group_data->last_child = new_rsc;
     216           0 :             pcmk__rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id);
     217             :         }
     218             :     }
     219             : 
     220           0 :     if (rsc->children == NULL) {
     221             :         /* The schema does not allow empty groups, but if validation is
     222             :          * disabled, we allow them (members can be added later).
     223             :          *
     224             :          * @COMPAT At a major release bump, we should consider this a failure so
     225             :          *         that group methods can assume children is not NULL, and there
     226             :          *         are no strange effects from phantom groups due to their
     227             :          *         presence or meta-attributes.
     228             :          */
     229           0 :         pcmk__config_warn("Group %s will be ignored because it does not have "
     230             :                           "any members", rsc->id);
     231             :     }
     232           0 :     return TRUE;
     233             : }
     234             : 
     235             : gboolean
     236           0 : group_active(pcmk_resource_t *rsc, gboolean all)
     237             : {
     238           0 :     gboolean c_all = TRUE;
     239           0 :     gboolean c_any = FALSE;
     240           0 :     GList *gIter = rsc->children;
     241             : 
     242           0 :     for (; gIter != NULL; gIter = gIter->next) {
     243           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     244             : 
     245           0 :         if (child_rsc->fns->active(child_rsc, all)) {
     246           0 :             c_any = TRUE;
     247             :         } else {
     248           0 :             c_all = FALSE;
     249             :         }
     250             :     }
     251             : 
     252           0 :     if (c_any == FALSE) {
     253           0 :         return FALSE;
     254           0 :     } else if (all && c_all == FALSE) {
     255           0 :         return FALSE;
     256             :     }
     257           0 :     return TRUE;
     258             : }
     259             : 
     260             : /*!
     261             :  * \internal
     262             :  * \deprecated This function will be removed in a future release
     263             :  */
     264             : static void
     265           0 : group_print_xml(pcmk_resource_t *rsc, const char *pre_text, long options,
     266             :                 void *print_data)
     267             : {
     268           0 :     GList *gIter = rsc->children;
     269           0 :     char *child_text = crm_strdup_printf("%s     ", pre_text);
     270             : 
     271           0 :     status_print("%s<group " PCMK_XA_ID "=\"%s\" ", pre_text, rsc->id);
     272           0 :     status_print("number_resources=\"%d\" ", g_list_length(rsc->children));
     273           0 :     status_print(">\n");
     274             : 
     275           0 :     for (; gIter != NULL; gIter = gIter->next) {
     276           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     277             : 
     278           0 :         child_rsc->fns->print(child_rsc, child_text, options, print_data);
     279             :     }
     280             : 
     281           0 :     status_print("%s</group>\n", pre_text);
     282           0 :     free(child_text);
     283           0 : }
     284             : 
     285             : /*!
     286             :  * \internal
     287             :  * \deprecated This function will be removed in a future release
     288             :  */
     289             : void
     290           0 : group_print(pcmk_resource_t *rsc, const char *pre_text, long options,
     291             :             void *print_data)
     292             : {
     293           0 :     char *child_text = NULL;
     294           0 :     GList *gIter = rsc->children;
     295             : 
     296           0 :     if (pre_text == NULL) {
     297           0 :         pre_text = " ";
     298             :     }
     299             : 
     300           0 :     if (options & pe_print_xml) {
     301           0 :         group_print_xml(rsc, pre_text, options, print_data);
     302           0 :         return;
     303             :     }
     304             : 
     305           0 :     child_text = crm_strdup_printf("%s    ", pre_text);
     306             : 
     307           0 :     status_print("%sResource Group: %s", pre_text ? pre_text : "", rsc->id);
     308             : 
     309           0 :     if (options & pe_print_html) {
     310           0 :         status_print("\n<ul>\n");
     311             : 
     312           0 :     } else if ((options & pe_print_log) == 0) {
     313           0 :         status_print("\n");
     314             :     }
     315             : 
     316           0 :     if (options & pe_print_brief) {
     317           0 :         print_rscs_brief(rsc->children, child_text, options, print_data, TRUE);
     318             : 
     319             :     } else {
     320           0 :         for (; gIter != NULL; gIter = gIter->next) {
     321           0 :             pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     322             : 
     323           0 :             if (options & pe_print_html) {
     324           0 :                 status_print("<li>\n");
     325             :             }
     326           0 :             child_rsc->fns->print(child_rsc, child_text, options, print_data);
     327           0 :             if (options & pe_print_html) {
     328           0 :                 status_print("</li>\n");
     329             :             }
     330             :         }
     331             :     }
     332             : 
     333           0 :     if (options & pe_print_html) {
     334           0 :         status_print("</ul>\n");
     335             :     }
     336           0 :     free(child_text);
     337             : }
     338             : 
     339             : PCMK__OUTPUT_ARGS("group", "uint32_t", "pcmk_resource_t *", "GList *",
     340             :                   "GList *")
     341             : int
     342           0 : pe__group_xml(pcmk__output_t *out, va_list args)
     343             : {
     344           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     345           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
     346           0 :     GList *only_node = va_arg(args, GList *);
     347           0 :     GList *only_rsc = va_arg(args, GList *);
     348             :     
     349           0 :     const char *desc = NULL;
     350           0 :     GList *gIter = rsc->children;
     351             : 
     352           0 :     int rc = pcmk_rc_no_output;
     353             : 
     354           0 :     gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
     355           0 :                              (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
     356             : 
     357           0 :     desc = pe__resource_description(rsc, show_opts);
     358             : 
     359           0 :     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
     360           0 :         return rc;
     361             :     }
     362             : 
     363           0 :     for (; gIter != NULL; gIter = gIter->next) {
     364           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     365             : 
     366           0 :         if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
     367           0 :             continue;
     368             :         }
     369             : 
     370           0 :         if (rc == pcmk_rc_no_output) {
     371           0 :             char *count = pcmk__itoa(g_list_length(gIter));
     372           0 :             const char *maintenance = pcmk__flag_text(rsc->flags,
     373             :                                                       pcmk_rsc_maintenance);
     374           0 :             const char *managed = pcmk__flag_text(rsc->flags, pcmk_rsc_managed);
     375           0 :             const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
     376             : 
     377           0 :             rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_GROUP,
     378             :                                           PCMK_XA_ID, rsc->id,
     379             :                                           PCMK_XA_NUMBER_RESOURCES, count,
     380             :                                           PCMK_XA_MAINTENANCE, maintenance,
     381             :                                           PCMK_XA_MANAGED, managed,
     382             :                                           PCMK_XA_DISABLED, disabled,
     383             :                                           PCMK_XA_DESCRIPTION, desc,
     384             :                                           NULL);
     385           0 :             free(count);
     386           0 :             CRM_ASSERT(rc == pcmk_rc_ok);
     387             :         }
     388             : 
     389           0 :         out->message(out, (const char *) child_rsc->xml->name, show_opts,
     390             :                      child_rsc, only_node, only_rsc);
     391             :     }
     392             : 
     393           0 :     if (rc == pcmk_rc_ok) {
     394           0 :         pcmk__output_xml_pop_parent(out);
     395             :     }
     396             : 
     397           0 :     return rc;
     398             : }
     399             : 
     400             : PCMK__OUTPUT_ARGS("group", "uint32_t", "pcmk_resource_t *", "GList *",
     401             :                   "GList *")
     402             : int
     403           0 : pe__group_default(pcmk__output_t *out, va_list args)
     404             : {
     405           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     406           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
     407           0 :     GList *only_node = va_arg(args, GList *);
     408           0 :     GList *only_rsc = va_arg(args, GList *);
     409             : 
     410           0 :     const char *desc = NULL;
     411           0 :     int rc = pcmk_rc_no_output;
     412             : 
     413           0 :     gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
     414           0 :                              (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
     415             : 
     416           0 :     gboolean active = rsc->fns->active(rsc, TRUE);
     417           0 :     gboolean partially_active = rsc->fns->active(rsc, FALSE);
     418             : 
     419           0 :     desc = pe__resource_description(rsc, show_opts);
     420             : 
     421           0 :     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
     422           0 :         return rc;
     423             :     }
     424             : 
     425           0 :     if (pcmk_is_set(show_opts, pcmk_show_brief)) {
     426           0 :         GList *rscs = pe__filter_rsc_list(rsc->children, only_rsc);
     427             : 
     428           0 :         if (rscs != NULL) {
     429           0 :             group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
     430           0 :                          pcmk_is_set(show_opts, pcmk_show_inactive_rscs), desc);
     431           0 :             pe__rscs_brief_output(out, rscs, show_opts | pcmk_show_inactive_rscs);
     432             : 
     433           0 :             rc = pcmk_rc_ok;
     434           0 :             g_list_free(rscs);
     435             :         }
     436             : 
     437             :     } else {
     438           0 :         for (GList *gIter = rsc->children; gIter; gIter = gIter->next) {
     439           0 :             pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     440             : 
     441           0 :             if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
     442           0 :                 continue;
     443             :             }
     444             : 
     445           0 :             group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
     446           0 :                          pcmk_is_set(show_opts, pcmk_show_inactive_rscs), desc);
     447           0 :             out->message(out, (const char *) child_rsc->xml->name, show_opts,
     448             :                          child_rsc, only_node, only_rsc);
     449             :         }
     450             :     }
     451             : 
     452           0 :         PCMK__OUTPUT_LIST_FOOTER(out, rc);
     453             : 
     454           0 :     return rc;
     455             : }
     456             : 
     457             : void
     458           0 : group_free(pcmk_resource_t * rsc)
     459             : {
     460           0 :     CRM_CHECK(rsc != NULL, return);
     461             : 
     462           0 :     pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
     463             : 
     464           0 :     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
     465           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     466             : 
     467           0 :         CRM_ASSERT(child_rsc);
     468           0 :         pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
     469           0 :         child_rsc->fns->free(child_rsc);
     470             :     }
     471             : 
     472           0 :     pcmk__rsc_trace(rsc, "Freeing child list");
     473           0 :     g_list_free(rsc->children);
     474             : 
     475           0 :     common_free(rsc);
     476             : }
     477             : 
     478             : enum rsc_role_e
     479           0 : group_resource_state(const pcmk_resource_t * rsc, gboolean current)
     480             : {
     481           0 :     enum rsc_role_e group_role = pcmk_role_unknown;
     482           0 :     GList *gIter = rsc->children;
     483             : 
     484           0 :     for (; gIter != NULL; gIter = gIter->next) {
     485           0 :         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
     486           0 :         enum rsc_role_e role = child_rsc->fns->state(child_rsc, current);
     487             : 
     488           0 :         if (role > group_role) {
     489           0 :             group_role = role;
     490             :         }
     491             :     }
     492             : 
     493           0 :     pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(group_role));
     494           0 :     return group_role;
     495             : }
     496             : 
     497             : gboolean
     498           0 : pe__group_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
     499             :                       gboolean check_parent)
     500             : {
     501           0 :     gboolean passes = FALSE;
     502             : 
     503           0 :     if (check_parent
     504           0 :         && pcmk__str_in_list(rsc_printable_id(pe__const_top_resource(rsc,
     505             :                                                                      false)),
     506             :                              only_rsc, pcmk__str_star_matches)) {
     507           0 :         passes = TRUE;
     508           0 :     } else if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
     509           0 :         passes = TRUE;
     510           0 :     } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
     511           0 :         passes = TRUE;
     512             :     } else {
     513           0 :         for (const GList *iter = rsc->children;
     514           0 :              iter != NULL; iter = iter->next) {
     515             : 
     516           0 :             const pcmk_resource_t *child_rsc = iter->data;
     517             : 
     518           0 :             if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
     519           0 :                 passes = TRUE;
     520           0 :                 break;
     521             :             }
     522             :         }
     523             :     }
     524             : 
     525           0 :     return !passes;
     526             : }
     527             : 
     528             : /*!
     529             :  * \internal
     530             :  * \brief Get maximum group resource instances per node
     531             :  *
     532             :  * \param[in] rsc  Group resource to check
     533             :  *
     534             :  * \return Maximum number of \p rsc instances that can be active on one node
     535             :  */
     536             : unsigned int
     537           0 : pe__group_max_per_node(const pcmk_resource_t *rsc)
     538             : {
     539           0 :     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
     540           0 :     return 1U;
     541             : }

Generated by: LCOV version 1.14