root/lib/pengine/group.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pe__last_group_member
  2. pe__group_flag_is_set
  3. set_group_flag
  4. inactive_resources
  5. group_header
  6. skip_child_rsc
  7. group_unpack
  8. group_active
  9. PCMK__OUTPUT_ARGS
  10. PCMK__OUTPUT_ARGS
  11. group_free
  12. group_resource_state
  13. pe__group_is_filtered
  14. pe__group_max_per_node

   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 pe__last_group_member(const pcmk_resource_t *group)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39     if (group != NULL) {
  40         const group_variant_data_t *group_data = NULL;
  41 
  42         CRM_CHECK(pcmk__is_group(group), return NULL);
  43         group_data = group->priv->variant_opaque;
  44         return group_data->last_child;
  45     }
  46     return NULL;
  47 }
  48 
  49 /*!
  50  * \internal
  51  * \brief Check whether a group flag is set
  52  *
  53  * \param[in] group  Group resource to check
  54  * \param[in] flags  Flag or flags to check
  55  *
  56  * \return true if all \p flags are set for \p group, otherwise false
  57  */
  58 bool
  59 pe__group_flag_is_set(const pcmk_resource_t *group, uint32_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61     const group_variant_data_t *group_data = NULL;
  62 
  63     CRM_CHECK(pcmk__is_group(group), return false);
  64     group_data = group->priv->variant_opaque;
  65     return pcmk_all_flags_set(group_data->flags, flags);
  66 }
  67 
  68 /*!
  69  * \internal
  70  * \brief Set a (deprecated) group flag
  71  *
  72  * \param[in,out] group   Group resource to check
  73  * \param[in]     option  Name of boolean configuration option
  74  * \param[in]     flag    Flag to set if \p option is true (which is default)
  75  * \param[in]     wo_bit  "Warn once" flag to use for deprecation warning
  76  */
  77 static void
  78 set_group_flag(pcmk_resource_t *group, const char *option, uint32_t flag,
     /* [previous][next][first][last][top][bottom][index][help] */
  79                uint32_t wo_bit)
  80 {
  81     const char *value_s = NULL;
  82     int value = 0;
  83 
  84     value_s = g_hash_table_lookup(group->priv->meta, option);
  85 
  86     // We don't actually need the null check but it speeds up the common case
  87     if ((value_s == NULL) || (crm_str_to_boolean(value_s, &value) < 0)
  88         || (value != 0)) {
  89         group_variant_data_t *group_data = group->priv->variant_opaque;
  90 
  91         group_data->flags |= flag;
  92 
  93     } else {
  94         pcmk__warn_once(wo_bit,
  95                         "Support for the '%s' group meta-attribute is "
  96                         "deprecated and will be removed in a future release "
  97                         "(use a resource set instead)", option);
  98     }
  99 }
 100 
 101 static int
 102 inactive_resources(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 103 {
 104     int retval = 0;
 105 
 106     for (GList *gIter = rsc->priv->children;
 107          gIter != NULL; gIter = gIter->next) {
 108 
 109         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
 110 
 111         if (!child_rsc->priv->fns->active(child_rsc, TRUE)) {
 112             retval++;
 113         }
 114     }
 115 
 116     return retval;
 117 }
 118 
 119 static void
 120 group_header(pcmk__output_t *out, int *rc, const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 121              int n_inactive, bool show_inactive, const char *desc)
 122 {
 123     GString *attrs = NULL;
 124 
 125     if (n_inactive > 0 && !show_inactive) {
 126         attrs = g_string_sized_new(64);
 127         g_string_append_printf(attrs, "%d member%s inactive", n_inactive,
 128                                pcmk__plural_s(n_inactive));
 129     }
 130 
 131     if (pe__resource_is_disabled(rsc)) {
 132         pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
 133     }
 134 
 135     if (pcmk_is_set(rsc->flags, pcmk__rsc_maintenance)) {
 136         pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
 137 
 138     } else if (!pcmk_is_set(rsc->flags, pcmk__rsc_managed)) {
 139         pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
 140     }
 141 
 142     if (attrs != NULL) {
 143         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s (%s)%s%s%s",
 144                                  rsc->id,
 145                                  (const char *) attrs->str, desc ? " (" : "",
 146                                  desc ? desc : "", desc ? ")" : "");
 147         g_string_free(attrs, TRUE);
 148     } else {
 149         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s%s%s%s",
 150                                  rsc->id,
 151                                  desc ? " (" : "", desc ? desc : "",
 152                                  desc ? ")" : "");
 153     }
 154 }
 155 
 156 static bool
 157 skip_child_rsc(pcmk_resource_t *rsc, pcmk_resource_t *child,
     /* [previous][next][first][last][top][bottom][index][help] */
 158                gboolean parent_passes, GList *only_rsc, uint32_t show_opts)
 159 {
 160     bool star_list = pcmk__list_of_1(only_rsc) &&
 161                      pcmk__str_eq("*", g_list_first(only_rsc)->data, pcmk__str_none);
 162     bool child_filtered = child->priv->fns->is_filtered(child, only_rsc, FALSE);
 163     bool child_active = child->priv->fns->active(child, FALSE);
 164     bool show_inactive = pcmk_is_set(show_opts, pcmk_show_inactive_rscs);
 165 
 166     /* If the resource is in only_rsc by name (so, ignoring "*") then allow
 167      * it regardless of if it's active or not.
 168      */
 169     if (!star_list && !child_filtered) {
 170         return false;
 171 
 172     } else if (!child_filtered && (child_active || show_inactive)) {
 173         return false;
 174 
 175     } else if (parent_passes && (child_active || show_inactive)) {
 176         return false;
 177 
 178     }
 179 
 180     return true;
 181 }
 182 
 183 gboolean
 184 group_unpack(pcmk_resource_t *rsc, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     xmlNode *xml_obj = rsc->priv->xml;
 187     xmlNode *xml_native_rsc = NULL;
 188     group_variant_data_t *group_data = NULL;
 189     const char *clone_id = NULL;
 190 
 191     pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
 192 
 193     group_data = pcmk__assert_alloc(1, sizeof(group_variant_data_t));
 194     group_data->last_child = NULL;
 195     rsc->priv->variant_opaque = group_data;
 196 
 197     // @COMPAT These are deprecated since 2.1.5
 198     set_group_flag(rsc, PCMK_META_ORDERED, pcmk__group_ordered,
 199                    pcmk__wo_group_order);
 200     set_group_flag(rsc, "collocated", pcmk__group_colocated,
 201                    pcmk__wo_group_coloc);
 202 
 203     clone_id = crm_element_value(rsc->priv->xml, PCMK__META_CLONE);
 204 
 205     for (xml_native_rsc = pcmk__xe_first_child(xml_obj, PCMK_XE_PRIMITIVE,
 206                                                NULL, NULL);
 207          xml_native_rsc != NULL;
 208          xml_native_rsc = pcmk__xe_next(xml_native_rsc, PCMK_XE_PRIMITIVE)) {
 209 
 210         pcmk_resource_t *new_rsc = NULL;
 211 
 212         crm_xml_add(xml_native_rsc, PCMK__META_CLONE, clone_id);
 213         if (pe__unpack_resource(xml_native_rsc, &new_rsc, rsc,
 214                                 scheduler) != pcmk_rc_ok) {
 215             continue;
 216         }
 217 
 218         rsc->priv->children = g_list_append(rsc->priv->children, new_rsc);
 219         group_data->last_child = new_rsc;
 220         pcmk__rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id);
 221     }
 222 
 223     if (rsc->priv->children == NULL) {
 224         // Not possible with schema validation enabled
 225         free(group_data);
 226         rsc->priv->variant_opaque = NULL;
 227         pcmk__config_err("Group %s has no members", rsc->id);
 228         return FALSE;
 229     }
 230 
 231     return TRUE;
 232 }
 233 
 234 gboolean
 235 group_active(pcmk_resource_t *rsc, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 236 {
 237     gboolean c_all = TRUE;
 238     gboolean c_any = FALSE;
 239 
 240     for (GList *gIter = rsc->priv->children;
 241          gIter != NULL; gIter = gIter->next) {
 242 
 243         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
 244 
 245         if (child_rsc->priv->fns->active(child_rsc, all)) {
 246             c_any = TRUE;
 247         } else {
 248             c_all = FALSE;
 249         }
 250     }
 251 
 252     if (c_any == FALSE) {
 253         return FALSE;
 254     } else if (all && c_all == FALSE) {
 255         return FALSE;
 256     }
 257     return TRUE;
 258 }
 259 
 260 PCMK__OUTPUT_ARGS("group", "uint32_t", "pcmk_resource_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
 261                   "GList *")
 262 int
 263 pe__group_xml(pcmk__output_t *out, va_list args)
 264 {
 265     uint32_t show_opts = va_arg(args, uint32_t);
 266     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 267     GList *only_node = va_arg(args, GList *);
 268     GList *only_rsc = va_arg(args, GList *);
 269 
 270     const char *desc = NULL;
 271 
 272     int rc = pcmk_rc_no_output;
 273 
 274     gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
 275                              (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
 276 
 277     desc = pe__resource_description(rsc, show_opts);
 278 
 279     if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) {
 280         return rc;
 281     }
 282 
 283     for (GList *gIter = rsc->priv->children;
 284          gIter != NULL; gIter = gIter->next) {
 285 
 286         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
 287 
 288         if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
 289             continue;
 290         }
 291 
 292         if (rc == pcmk_rc_no_output) {
 293             char *count = pcmk__itoa(g_list_length(gIter));
 294             const char *maintenance = pcmk__flag_text(rsc->flags,
 295                                                       pcmk__rsc_maintenance);
 296             const char *managed = pcmk__flag_text(rsc->flags,
 297                                                   pcmk__rsc_managed);
 298             const char *disabled = pcmk__btoa(pe__resource_is_disabled(rsc));
 299 
 300             rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_GROUP,
 301                                           PCMK_XA_ID, rsc->id,
 302                                           PCMK_XA_NUMBER_RESOURCES, count,
 303                                           PCMK_XA_MAINTENANCE, maintenance,
 304                                           PCMK_XA_MANAGED, managed,
 305                                           PCMK_XA_DISABLED, disabled,
 306                                           PCMK_XA_DESCRIPTION, desc,
 307                                           NULL);
 308             free(count);
 309             pcmk__assert(rc == pcmk_rc_ok);
 310         }
 311 
 312         out->message(out, (const char *) child_rsc->priv->xml->name,
 313                      show_opts, child_rsc, only_node, only_rsc);
 314     }
 315 
 316     if (rc == pcmk_rc_ok) {
 317         pcmk__output_xml_pop_parent(out);
 318     }
 319 
 320     return rc;
 321 }
 322 
 323 PCMK__OUTPUT_ARGS("group", "uint32_t", "pcmk_resource_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
 324                   "GList *")
 325 int
 326 pe__group_default(pcmk__output_t *out, va_list args)
 327 {
 328     uint32_t show_opts = va_arg(args, uint32_t);
 329     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
 330     GList *only_node = va_arg(args, GList *);
 331     GList *only_rsc = va_arg(args, GList *);
 332 
 333     const char *desc = NULL;
 334     int rc = pcmk_rc_no_output;
 335 
 336     gboolean parent_passes = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
 337                              (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
 338 
 339     gboolean active = rsc->priv->fns->active(rsc, TRUE);
 340     gboolean partially_active = rsc->priv->fns->active(rsc, FALSE);
 341 
 342     desc = pe__resource_description(rsc, show_opts);
 343 
 344     if (rsc->priv->fns->is_filtered(rsc, only_rsc, TRUE)) {
 345         return rc;
 346     }
 347 
 348     if (pcmk_is_set(show_opts, pcmk_show_brief)) {
 349         GList *rscs = pe__filter_rsc_list(rsc->priv->children, only_rsc);
 350 
 351         if (rscs != NULL) {
 352             group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
 353                          pcmk_is_set(show_opts, pcmk_show_inactive_rscs), desc);
 354             pe__rscs_brief_output(out, rscs, show_opts | pcmk_show_inactive_rscs);
 355 
 356             rc = pcmk_rc_ok;
 357             g_list_free(rscs);
 358         }
 359 
 360     } else {
 361         for (GList *gIter = rsc->priv->children;
 362              gIter != NULL; gIter = gIter->next) {
 363             pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
 364 
 365             if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
 366                 continue;
 367             }
 368 
 369             group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
 370                          pcmk_is_set(show_opts, pcmk_show_inactive_rscs), desc);
 371             out->message(out, (const char *) child_rsc->priv->xml->name,
 372                          show_opts, child_rsc, only_node, only_rsc);
 373         }
 374     }
 375 
 376         PCMK__OUTPUT_LIST_FOOTER(out, rc);
 377 
 378     return rc;
 379 }
 380 
 381 void
 382 group_free(pcmk_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 383 {
 384     CRM_CHECK(rsc != NULL, return);
 385 
 386     pcmk__rsc_trace(rsc, "Freeing %s", rsc->id);
 387 
 388     for (GList *gIter = rsc->priv->children;
 389          gIter != NULL; gIter = gIter->next) {
 390 
 391         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
 392 
 393         pcmk__assert(child_rsc != NULL);
 394         pcmk__rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
 395         child_rsc->priv->fns->free(child_rsc);
 396     }
 397 
 398     pcmk__rsc_trace(rsc, "Freeing child list");
 399     g_list_free(rsc->priv->children);
 400 
 401     common_free(rsc);
 402 }
 403 
 404 enum rsc_role_e
 405 group_resource_state(const pcmk_resource_t * rsc, gboolean current)
     /* [previous][next][first][last][top][bottom][index][help] */
 406 {
 407     enum rsc_role_e group_role = pcmk_role_unknown;
 408 
 409     for (GList *gIter = rsc->priv->children;
 410          gIter != NULL; gIter = gIter->next) {
 411 
 412         pcmk_resource_t *child_rsc = (pcmk_resource_t *) gIter->data;
 413         enum rsc_role_e role = child_rsc->priv->fns->state(child_rsc,
 414                                                            current);
 415 
 416         if (role > group_role) {
 417             group_role = role;
 418         }
 419     }
 420 
 421     pcmk__rsc_trace(rsc, "%s role: %s", rsc->id, pcmk_role_text(group_role));
 422     return group_role;
 423 }
 424 
 425 gboolean
 426 pe__group_is_filtered(const pcmk_resource_t *rsc, GList *only_rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 427                       gboolean check_parent)
 428 {
 429     gboolean passes = FALSE;
 430 
 431     if (check_parent
 432         && pcmk__str_in_list(rsc_printable_id(pe__const_top_resource(rsc,
 433                                                                      false)),
 434                              only_rsc, pcmk__str_star_matches)) {
 435         passes = TRUE;
 436     } else if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
 437         passes = TRUE;
 438     } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
 439         passes = TRUE;
 440     } else {
 441         for (const GList *iter = rsc->priv->children;
 442              iter != NULL; iter = iter->next) {
 443 
 444             const pcmk_resource_t *child_rsc = iter->data;
 445 
 446             if (!child_rsc->priv->fns->is_filtered(child_rsc, only_rsc,
 447                                                    FALSE)) {
 448                 passes = TRUE;
 449                 break;
 450             }
 451         }
 452     }
 453 
 454     return !passes;
 455 }
 456 
 457 /*!
 458  * \internal
 459  * \brief Get maximum group resource instances per node
 460  *
 461  * \param[in] rsc  Group resource to check
 462  *
 463  * \return Maximum number of \p rsc instances that can be active on one node
 464  */
 465 unsigned int
 466 pe__group_max_per_node(const pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 467 {
 468     pcmk__assert(pcmk__is_group(rsc));
 469     return 1U;
 470 }

/* [previous][next][first][last][top][bottom][index][help] */