root/lib/pengine/group.c

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

DEFINITIONS

This source file includes following definitions.
  1. inactive_resources
  2. group_header
  3. skip_child_rsc
  4. group_unpack
  5. group_active
  6. group_print_xml
  7. group_print
  8. PCMK__OUTPUT_ARGS
  9. PCMK__OUTPUT_ARGS
  10. group_free
  11. group_resource_state
  12. pe__group_is_filtered

   1 /*
   2  * Copyright 2004-2021 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 <crm/pengine/rules.h>
  13 #include <crm/pengine/status.h>
  14 #include <crm/pengine/internal.h>
  15 #include <crm/msg_xml.h>
  16 #include <crm/common/output.h>
  17 #include <crm/common/strings_internal.h>
  18 #include <crm/common/xml_internal.h>
  19 #include <pe_status_private.h>
  20 
  21 #define VARIANT_GROUP 1
  22 #include "./variant.h"
  23 
  24 static int
  25 inactive_resources(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
  26 {
  27     int retval = 0;
  28 
  29     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
  30         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
  31 
  32         if (!child_rsc->fns->active(child_rsc, TRUE)) {
  33             retval++;
  34         }
  35     }
  36 
  37     return retval;
  38 }
  39 
  40 static void
  41 group_header(pcmk__output_t *out, int *rc, pe_resource_t *rsc, int n_inactive, bool show_inactive)
     /* [previous][next][first][last][top][bottom][index][help] */
  42 {
  43     char *attrs = NULL;
  44     size_t len = 0;
  45 
  46     if (n_inactive > 0 && !show_inactive) {
  47         char *word = crm_strdup_printf("%d member%s inactive", n_inactive, pcmk__plural_s(n_inactive));
  48         pcmk__add_separated_word(&attrs, &len, word, ", ");
  49         free(word);
  50     }
  51 
  52     if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
  53         pcmk__add_separated_word(&attrs, &len, "unmanaged", ", ");
  54     }
  55 
  56     if (pe__resource_is_disabled(rsc)) {
  57         pcmk__add_separated_word(&attrs, &len, "disabled", ", ");
  58     }
  59 
  60     if (attrs) {
  61         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s (%s)",
  62                                  rsc->id, attrs);
  63         free(attrs);
  64     } else {
  65         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Resource Group: %s", rsc->id);
  66     }
  67 }
  68 
  69 static bool
  70 skip_child_rsc(pe_resource_t *rsc, pe_resource_t *child, gboolean parent_passes,
     /* [previous][next][first][last][top][bottom][index][help] */
  71                GList *only_rsc, unsigned int show_opts)
  72 {
  73     bool star_list = pcmk__list_of_1(only_rsc) &&
  74                      pcmk__str_eq("*", g_list_first(only_rsc)->data, pcmk__str_none);
  75     bool child_filtered = child->fns->is_filtered(child, only_rsc, FALSE);
  76     bool child_active = child->fns->active(child, FALSE);
  77     bool show_inactive = pcmk_is_set(show_opts, pcmk_show_inactive_rscs);
  78 
  79     /* If the resource is in only_rsc by name (so, ignoring "*") then allow
  80      * it regardless of if it's active or not.
  81      */
  82     if (!star_list && !child_filtered) {
  83         return false;
  84 
  85     } else if (!child_filtered && (child_active || show_inactive)) {
  86         return false;
  87 
  88     } else if (parent_passes && (child_active || show_inactive)) {
  89         return false;
  90 
  91     }
  92 
  93     return true;
  94 }
  95 
  96 gboolean
  97 group_unpack(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
  98 {
  99     xmlNode *xml_obj = rsc->xml;
 100     xmlNode *xml_native_rsc = NULL;
 101     group_variant_data_t *group_data = NULL;
 102     const char *group_ordered = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED);
 103     const char *group_colocated = g_hash_table_lookup(rsc->meta, "collocated");
 104     const char *clone_id = NULL;
 105 
 106     pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
 107 
 108     group_data = calloc(1, sizeof(group_variant_data_t));
 109     group_data->num_children = 0;
 110     group_data->first_child = NULL;
 111     group_data->last_child = NULL;
 112     rsc->variant_opaque = group_data;
 113 
 114     // We don't actually need the null checks but it speeds up the common case
 115     if ((group_ordered == NULL)
 116         || (crm_str_to_boolean(group_ordered, &(group_data->ordered)) < 0)) {
 117         group_data->ordered = TRUE;
 118     }
 119     if ((group_colocated == NULL)
 120         || (crm_str_to_boolean(group_colocated, &(group_data->colocated)) < 0)) {
 121         group_data->colocated = TRUE;
 122     }
 123 
 124     clone_id = crm_element_value(rsc->xml, XML_RSC_ATTR_INCARNATION);
 125 
 126     for (xml_native_rsc = pcmk__xe_first_child(xml_obj); xml_native_rsc != NULL;
 127          xml_native_rsc = pcmk__xe_next(xml_native_rsc)) {
 128 
 129         if (pcmk__str_eq((const char *)xml_native_rsc->name,
 130                          XML_CIB_TAG_RESOURCE, pcmk__str_none)) {
 131             pe_resource_t *new_rsc = NULL;
 132 
 133             crm_xml_add(xml_native_rsc, XML_RSC_ATTR_INCARNATION, clone_id);
 134             if (common_unpack(xml_native_rsc, &new_rsc, rsc, data_set) == FALSE) {
 135                 pe_err("Failed unpacking resource %s", crm_element_value(xml_obj, XML_ATTR_ID));
 136                 if (new_rsc != NULL && new_rsc->fns != NULL) {
 137                     new_rsc->fns->free(new_rsc);
 138                 }
 139                 continue;
 140             }
 141 
 142             group_data->num_children++;
 143             rsc->children = g_list_append(rsc->children, new_rsc);
 144 
 145             if (group_data->first_child == NULL) {
 146                 group_data->first_child = new_rsc;
 147             }
 148             group_data->last_child = new_rsc;
 149             pe_rsc_trace(rsc, "Added %s member %s", rsc->id, new_rsc->id);
 150         }
 151     }
 152 
 153     if (group_data->num_children == 0) {
 154         pcmk__config_warn("Group %s does not have any children", rsc->id);
 155         return TRUE; // Allow empty groups, children can be added later
 156     }
 157 
 158     pe_rsc_trace(rsc, "Added %d children to resource %s...", group_data->num_children, rsc->id);
 159 
 160     return TRUE;
 161 }
 162 
 163 gboolean
 164 group_active(pe_resource_t * rsc, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166     gboolean c_all = TRUE;
 167     gboolean c_any = FALSE;
 168     GList *gIter = rsc->children;
 169 
 170     for (; gIter != NULL; gIter = gIter->next) {
 171         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 172 
 173         if (child_rsc->fns->active(child_rsc, all)) {
 174             c_any = TRUE;
 175         } else {
 176             c_all = FALSE;
 177         }
 178     }
 179 
 180     if (c_any == FALSE) {
 181         return FALSE;
 182     } else if (all && c_all == FALSE) {
 183         return FALSE;
 184     }
 185     return TRUE;
 186 }
 187 
 188 static void
 189 group_print_xml(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 190 {
 191     GList *gIter = rsc->children;
 192     char *child_text = crm_strdup_printf("%s     ", pre_text);
 193 
 194     status_print("%s<group id=\"%s\" ", pre_text, rsc->id);
 195     status_print("number_resources=\"%d\" ", g_list_length(rsc->children));
 196     status_print(">\n");
 197 
 198     for (; gIter != NULL; gIter = gIter->next) {
 199         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 200 
 201         child_rsc->fns->print(child_rsc, child_text, options, print_data);
 202     }
 203 
 204     status_print("%s</group>\n", pre_text);
 205     free(child_text);
 206 }
 207 
 208 void
 209 group_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211     char *child_text = NULL;
 212     GList *gIter = rsc->children;
 213 
 214     if (pre_text == NULL) {
 215         pre_text = " ";
 216     }
 217 
 218     if (options & pe_print_xml) {
 219         group_print_xml(rsc, pre_text, options, print_data);
 220         return;
 221     }
 222 
 223     child_text = crm_strdup_printf("%s    ", pre_text);
 224 
 225     status_print("%sResource Group: %s", pre_text ? pre_text : "", rsc->id);
 226 
 227     if (options & pe_print_html) {
 228         status_print("\n<ul>\n");
 229 
 230     } else if ((options & pe_print_log) == 0) {
 231         status_print("\n");
 232     }
 233 
 234     if (options & pe_print_brief) {
 235         print_rscs_brief(rsc->children, child_text, options, print_data, TRUE);
 236 
 237     } else {
 238         for (; gIter != NULL; gIter = gIter->next) {
 239             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 240 
 241             if (options & pe_print_html) {
 242                 status_print("<li>\n");
 243             }
 244             child_rsc->fns->print(child_rsc, child_text, options, print_data);
 245             if (options & pe_print_html) {
 246                 status_print("</li>\n");
 247             }
 248         }
 249     }
 250 
 251     if (options & pe_print_html) {
 252         status_print("</ul>\n");
 253     }
 254     free(child_text);
 255 }
 256 
 257 PCMK__OUTPUT_ARGS("group", "unsigned int", "pe_resource_t *", "GList *", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 258 int
 259 pe__group_xml(pcmk__output_t *out, va_list args)
 260 {
 261     unsigned int show_opts = va_arg(args, unsigned int);
 262     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 263     GList *only_node = va_arg(args, GList *);
 264     GList *only_rsc = va_arg(args, GList *);
 265 
 266     GList *gIter = rsc->children;
 267     char *count = pcmk__itoa(g_list_length(gIter));
 268 
 269     int rc = pcmk_rc_no_output;
 270 
 271     gboolean parent_passes = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) ||
 272                              (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none));
 273 
 274     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 275         free(count);
 276         return rc;
 277     }
 278 
 279     for (; gIter != NULL; gIter = gIter->next) {
 280         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 281 
 282         if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
 283             continue;
 284         }
 285 
 286         if (rc == pcmk_rc_no_output) {
 287             rc = pe__name_and_nvpairs_xml(out, true, "group", 4
 288                                           , "id", rsc->id
 289                                           , "number_resources", count
 290                                           , "managed", pe__rsc_bool_str(rsc, pe_rsc_managed)
 291                                           , "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)));
 292             free(count);
 293             CRM_ASSERT(rc == pcmk_rc_ok);
 294         }
 295 
 296         out->message(out, crm_map_element_name(child_rsc->xml), show_opts, child_rsc,
 297                                          only_node, only_rsc);
 298     }
 299 
 300     if (rc == pcmk_rc_ok) {
 301         pcmk__output_xml_pop_parent(out);
 302     }
 303 
 304     return rc;
 305 }
 306 
 307 PCMK__OUTPUT_ARGS("group", "unsigned int", "pe_resource_t *", "GList *", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 308 int
 309 pe__group_default(pcmk__output_t *out, va_list args)
 310 {
 311     unsigned int show_opts = va_arg(args, unsigned int);
 312     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 313     GList *only_node = va_arg(args, GList *);
 314     GList *only_rsc = va_arg(args, GList *);
 315 
 316     int rc = pcmk_rc_no_output;
 317 
 318     gboolean parent_passes = pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none) ||
 319                              (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none));
 320 
 321     gboolean active = rsc->fns->active(rsc, TRUE);
 322     gboolean partially_active = rsc->fns->active(rsc, FALSE);
 323 
 324     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 325         return rc;
 326     }
 327 
 328     if (pcmk_is_set(show_opts, pcmk_show_brief)) {
 329         GList *rscs = pe__filter_rsc_list(rsc->children, only_rsc);
 330 
 331         if (rscs != NULL) {
 332             group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
 333                          pcmk_is_set(show_opts, pcmk_show_inactive_rscs));
 334             pe__rscs_brief_output(out, rscs, show_opts | pcmk_show_inactive_rscs);
 335 
 336             rc = pcmk_rc_ok;
 337             g_list_free(rscs);
 338         }
 339 
 340     } else {
 341         for (GList *gIter = rsc->children; gIter; gIter = gIter->next) {
 342             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 343 
 344             if (skip_child_rsc(rsc, child_rsc, parent_passes, only_rsc, show_opts)) {
 345                 continue;
 346             }
 347 
 348             group_header(out, &rc, rsc, !active && partially_active ? inactive_resources(rsc) : 0,
 349                          pcmk_is_set(show_opts, pcmk_show_inactive_rscs));
 350             out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
 351                          child_rsc, only_node, only_rsc);
 352         }
 353     }
 354 
 355         PCMK__OUTPUT_LIST_FOOTER(out, rc);
 356 
 357     return rc;
 358 }
 359 
 360 void
 361 group_free(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     CRM_CHECK(rsc != NULL, return);
 364 
 365     pe_rsc_trace(rsc, "Freeing %s", rsc->id);
 366 
 367     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 368         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 369 
 370         CRM_ASSERT(child_rsc);
 371         pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
 372         child_rsc->fns->free(child_rsc);
 373     }
 374 
 375     pe_rsc_trace(rsc, "Freeing child list");
 376     g_list_free(rsc->children);
 377 
 378     common_free(rsc);
 379 }
 380 
 381 enum rsc_role_e
 382 group_resource_state(const pe_resource_t * rsc, gboolean current)
     /* [previous][next][first][last][top][bottom][index][help] */
 383 {
 384     enum rsc_role_e group_role = RSC_ROLE_UNKNOWN;
 385     GList *gIter = rsc->children;
 386 
 387     for (; gIter != NULL; gIter = gIter->next) {
 388         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 389         enum rsc_role_e role = child_rsc->fns->state(child_rsc, current);
 390 
 391         if (role > group_role) {
 392             group_role = role;
 393         }
 394     }
 395 
 396     pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(group_role));
 397     return group_role;
 398 }
 399 
 400 gboolean
 401 pe__group_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
     /* [previous][next][first][last][top][bottom][index][help] */
 402 {
 403     gboolean passes = FALSE;
 404 
 405     if (check_parent && pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(rsc)), pcmk__str_none)) {
 406         passes = TRUE;
 407     } else if (pcmk__str_in_list(only_rsc, rsc_printable_id(rsc), pcmk__str_none)) {
 408         passes = TRUE;
 409     } else if (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(only_rsc, rsc->id, pcmk__str_none)) {
 410         passes = TRUE;
 411     } else {
 412         for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 413             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 414 
 415             if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
 416                 passes = TRUE;
 417                 break;
 418             }
 419         }
 420     }
 421 
 422     return !passes;
 423 }

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