root/lib/pengine/clone.c

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

DEFINITIONS

This source file includes following definitions.
  1. clone_header
  2. pe__force_anon
  3. find_clone_instance
  4. pe__create_clone_child
  5. clone_unpack
  6. clone_active
  7. short_print
  8. configured_role_str
  9. configured_role
  10. clone_print_xml
  11. is_set_recursive
  12. clone_print
  13. PCMK__OUTPUT_ARGS
  14. PCMK__OUTPUT_ARGS
  15. clone_free
  16. clone_resource_state
  17. pe__is_universal_clone
  18. pe__clone_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 <pe_status_private.h>
  16 #include <crm/msg_xml.h>
  17 #include <crm/common/output.h>
  18 #include <crm/common/xml_internal.h>
  19 
  20 #define VARIANT_CLONE 1
  21 #include "./variant.h"
  22 
  23 #ifdef PCMK__COMPAT_2_0
  24 #define PROMOTED_INSTANCES   RSC_ROLE_PROMOTED_LEGACY_S "s"
  25 #define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_LEGACY_S "s"
  26 #else
  27 #define PROMOTED_INSTANCES   RSC_ROLE_PROMOTED_S
  28 #define UNPROMOTED_INSTANCES RSC_ROLE_UNPROMOTED_S
  29 #endif
  30 
  31 static void
  32 clone_header(pcmk__output_t *out, int *rc, pe_resource_t *rsc, clone_variant_data_t *clone_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34     char *attrs = NULL;
  35     size_t len = 0;
  36 
  37     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
  38         pcmk__add_separated_word(&attrs, &len, "promotable", ", ");
  39     }
  40 
  41     if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
  42         pcmk__add_separated_word(&attrs, &len, "unique", ", ");
  43     }
  44 
  45     if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
  46         pcmk__add_separated_word(&attrs, &len, "unmanaged", ", ");
  47     }
  48 
  49     if (pe__resource_is_disabled(rsc)) {
  50         pcmk__add_separated_word(&attrs, &len, "disabled", ", ");
  51     }
  52 
  53     if (attrs) {
  54         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)",
  55                              rsc->id, ID(clone_data->xml_obj_child),
  56                                  attrs);
  57         free(attrs);
  58     } else {
  59         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]",
  60                                  rsc->id, ID(clone_data->xml_obj_child))
  61     }
  62 }
  63 
  64 void
  65 pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid,
     /* [previous][next][first][last][top][bottom][index][help] */
  66                pe_working_set_t *data_set)
  67 {
  68     if (pe_rsc_is_clone(rsc)) {
  69         clone_variant_data_t *clone_data = NULL;
  70 
  71         get_clone_variant_data(clone_data, rsc);
  72 
  73         pe_warn("Ignoring " XML_RSC_ATTR_UNIQUE " for %s because %s resources "
  74                 "such as %s can be used only as anonymous clones",
  75                 rsc->id, standard, rid);
  76 
  77         clone_data->clone_node_max = 1;
  78         clone_data->clone_max = QB_MIN(clone_data->clone_max,
  79                                        g_list_length(data_set->nodes));
  80     }
  81 }
  82 
  83 pe_resource_t *
  84 find_clone_instance(pe_resource_t * rsc, const char *sub_id, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
  85 {
  86     char *child_id = NULL;
  87     pe_resource_t *child = NULL;
  88     const char *child_base = NULL;
  89     clone_variant_data_t *clone_data = NULL;
  90 
  91     get_clone_variant_data(clone_data, rsc);
  92 
  93     child_base = ID(clone_data->xml_obj_child);
  94     child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
  95     child = pe_find_resource(rsc->children, child_id);
  96 
  97     free(child_id);
  98     return child;
  99 }
 100 
 101 pe_resource_t *
 102 pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 103 {
 104     gboolean as_orphan = FALSE;
 105     char *inc_num = NULL;
 106     char *inc_max = NULL;
 107     pe_resource_t *child_rsc = NULL;
 108     xmlNode *child_copy = NULL;
 109     clone_variant_data_t *clone_data = NULL;
 110 
 111     get_clone_variant_data(clone_data, rsc);
 112 
 113     CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
 114 
 115     if (clone_data->total_clones >= clone_data->clone_max) {
 116         // If we've already used all available instances, this is an orphan
 117         as_orphan = TRUE;
 118     }
 119 
 120     // Allocate instance numbers in numerical order (starting at 0)
 121     inc_num = pcmk__itoa(clone_data->total_clones);
 122     inc_max = pcmk__itoa(clone_data->clone_max);
 123 
 124     child_copy = copy_xml(clone_data->xml_obj_child);
 125 
 126     crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num);
 127 
 128     if (common_unpack(child_copy, &child_rsc, rsc, data_set) == FALSE) {
 129         pe_err("Failed unpacking resource %s", crm_element_value(child_copy, XML_ATTR_ID));
 130         child_rsc = NULL;
 131         goto bail;
 132     }
 133 /*  child_rsc->globally_unique = rsc->globally_unique; */
 134 
 135     CRM_ASSERT(child_rsc);
 136     clone_data->total_clones += 1;
 137     pe_rsc_trace(child_rsc, "Setting clone attributes for: %s", child_rsc->id);
 138     rsc->children = g_list_append(rsc->children, child_rsc);
 139     if (as_orphan) {
 140         pe__set_resource_flags_recursive(child_rsc, pe_rsc_orphan);
 141     }
 142 
 143     add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max);
 144     pe_rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
 145 
 146   bail:
 147     free(inc_num);
 148     free(inc_max);
 149 
 150     return child_rsc;
 151 }
 152 
 153 gboolean
 154 clone_unpack(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156     int lpc = 0;
 157     xmlNode *a_child = NULL;
 158     xmlNode *xml_obj = rsc->xml;
 159     clone_variant_data_t *clone_data = NULL;
 160 
 161     const char *ordered = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED);
 162     const char *max_clones = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_MAX);
 163     const char *max_clones_node = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
 164 
 165     pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
 166 
 167     clone_data = calloc(1, sizeof(clone_variant_data_t));
 168     rsc->variant_opaque = clone_data;
 169 
 170     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 171         const char *promoted_max = NULL;
 172         const char *promoted_node_max = NULL;
 173 
 174         promoted_max = g_hash_table_lookup(rsc->meta,
 175                                            XML_RSC_ATTR_PROMOTED_MAX);
 176         if (promoted_max == NULL) {
 177             // @COMPAT deprecated since 2.0.0
 178             promoted_max = g_hash_table_lookup(rsc->meta,
 179                                                PCMK_XE_PROMOTED_MAX_LEGACY);
 180         }
 181 
 182         promoted_node_max = g_hash_table_lookup(rsc->meta,
 183                                                 XML_RSC_ATTR_PROMOTED_NODEMAX);
 184         if (promoted_node_max == NULL) {
 185             // @COMPAT deprecated since 2.0.0
 186             promoted_node_max = g_hash_table_lookup(rsc->meta,
 187                                                     PCMK_XE_PROMOTED_NODE_MAX_LEGACY);
 188         }
 189 
 190         // Use 1 as default but 0 for minimum and invalid
 191         if (promoted_max == NULL) {
 192             clone_data->promoted_max = 1;
 193         } else {
 194             pcmk__scan_min_int(promoted_max, &(clone_data->promoted_max), 0);
 195         }
 196 
 197         // Use 1 as default but 0 for minimum and invalid
 198         if (promoted_node_max == NULL) {
 199             clone_data->promoted_node_max = 1;
 200         } else {
 201             pcmk__scan_min_int(promoted_node_max,
 202                                &(clone_data->promoted_node_max), 0);
 203         }
 204     }
 205 
 206     // Implied by calloc()
 207     /* clone_data->xml_obj_child = NULL; */
 208 
 209     // Use 1 as default but 0 for minimum and invalid
 210     if (max_clones_node == NULL) {
 211         clone_data->clone_node_max = 1;
 212     } else {
 213         pcmk__scan_min_int(max_clones_node, &(clone_data->clone_node_max), 0);
 214     }
 215 
 216     /* Use number of nodes (but always at least 1, which is handy for crm_verify
 217      * for a CIB without nodes) as default, but 0 for minimum and invalid
 218      */
 219     if (max_clones == NULL) {
 220         clone_data->clone_max = QB_MAX(1, g_list_length(data_set->nodes));
 221     } else {
 222         pcmk__scan_min_int(max_clones, &(clone_data->clone_max), 0);
 223     }
 224 
 225     clone_data->ordered = crm_is_true(ordered);
 226 
 227     if ((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) {
 228         pcmk__config_err("Ignoring " XML_RSC_ATTR_PROMOTED_MAX " for %s "
 229                          "because anonymous clones support only one instance "
 230                          "per node", rsc->id);
 231         clone_data->clone_node_max = 1;
 232     }
 233 
 234     pe_rsc_trace(rsc, "Options for %s", rsc->id);
 235     pe_rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
 236     pe_rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
 237     pe_rsc_trace(rsc, "\tClone is unique: %s",
 238                  pe__rsc_bool_str(rsc, pe_rsc_unique));
 239     pe_rsc_trace(rsc, "\tClone is promotable: %s",
 240                  pe__rsc_bool_str(rsc, pe_rsc_promotable));
 241 
 242     // Clones may contain a single group or primitive
 243     for (a_child = pcmk__xe_first_child(xml_obj); a_child != NULL;
 244          a_child = pcmk__xe_next(a_child)) {
 245 
 246         if (pcmk__str_any_of((const char *)a_child->name, XML_CIB_TAG_RESOURCE, XML_CIB_TAG_GROUP, NULL)) {
 247             clone_data->xml_obj_child = a_child;
 248             break;
 249         }
 250     }
 251 
 252     if (clone_data->xml_obj_child == NULL) {
 253         pcmk__config_err("%s has nothing to clone", rsc->id);
 254         return FALSE;
 255     }
 256 
 257     /*
 258      * Make clones ever so slightly sticky by default
 259      *
 260      * This helps ensure clone instances are not shuffled around the cluster
 261      * for no benefit in situations when pre-allocation is not appropriate
 262      */
 263     if (g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_STICKINESS) == NULL) {
 264         add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1");
 265     }
 266 
 267     /* This ensures that the globally-unique value always exists for children to
 268      * inherit when being unpacked, as well as in resource agents' environment.
 269      */
 270     add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE,
 271                    pe__rsc_bool_str(rsc, pe_rsc_unique));
 272 
 273     if (clone_data->clone_max <= 0) {
 274         /* Create one child instance so that unpack_find_resource() will hook up
 275          * any orphans up to the parent correctly.
 276          */
 277         if (pe__create_clone_child(rsc, data_set) == NULL) {
 278             return FALSE;
 279         }
 280 
 281     } else {
 282         // Create a child instance for each available instance number
 283         for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
 284             if (pe__create_clone_child(rsc, data_set) == NULL) {
 285                 return FALSE;
 286             }
 287         }
 288     }
 289 
 290     pe_rsc_trace(rsc, "Added %d children to resource %s...", clone_data->clone_max, rsc->id);
 291     return TRUE;
 292 }
 293 
 294 gboolean
 295 clone_active(pe_resource_t * rsc, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 296 {
 297     GList *gIter = rsc->children;
 298 
 299     for (; gIter != NULL; gIter = gIter->next) {
 300         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 301         gboolean child_active = child_rsc->fns->active(child_rsc, all);
 302 
 303         if (all == FALSE && child_active) {
 304             return TRUE;
 305         } else if (all && child_active == FALSE) {
 306             return FALSE;
 307         }
 308     }
 309 
 310     if (all) {
 311         return TRUE;
 312     } else {
 313         return FALSE;
 314     }
 315 }
 316 
 317 static void
 318 short_print(char *list, const char *prefix, const char *type, const char *suffix, long options, void *print_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 319 {
 320     if(suffix == NULL) {
 321         suffix = "";
 322     }
 323 
 324     if (list) {
 325         if (options & pe_print_html) {
 326             status_print("<li>");
 327         }
 328         status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
 329 
 330         if (options & pe_print_html) {
 331             status_print("</li>\n");
 332 
 333         } else if (options & pe_print_suppres_nl) {
 334             /* nothing */
 335         } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
 336             status_print("\n");
 337         }
 338 
 339     }
 340 }
 341 
 342 static const char *
 343 configured_role_str(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345     const char *target_role = g_hash_table_lookup(rsc->meta,
 346                                                   XML_RSC_ATTR_TARGET_ROLE);
 347 
 348     if ((target_role == NULL) && rsc->children && rsc->children->data) {
 349         target_role = g_hash_table_lookup(((pe_resource_t*)rsc->children->data)->meta,
 350                                           XML_RSC_ATTR_TARGET_ROLE);
 351     }
 352     return target_role;
 353 }
 354 
 355 static enum rsc_role_e
 356 configured_role(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358     const char *target_role = configured_role_str(rsc);
 359 
 360     if (target_role) {
 361         return text2role(target_role);
 362     }
 363     return RSC_ROLE_UNKNOWN;
 364 }
 365 
 366 static void
 367 clone_print_xml(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 368 {
 369     char *child_text = crm_strdup_printf("%s    ", pre_text);
 370     const char *target_role = configured_role_str(rsc);
 371     GList *gIter = rsc->children;
 372 
 373     status_print("%s<clone ", pre_text);
 374     status_print("id=\"%s\" ", rsc->id);
 375     status_print("multi_state=\"%s\" ",
 376                  pe__rsc_bool_str(rsc, pe_rsc_promotable));
 377     status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_unique));
 378     status_print("managed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_managed));
 379     status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_failed));
 380     status_print("failure_ignored=\"%s\" ",
 381                  pe__rsc_bool_str(rsc, pe_rsc_failure_ignored));
 382     if (target_role) {
 383         status_print("target_role=\"%s\" ", target_role);
 384     }
 385     status_print(">\n");
 386 
 387     for (; gIter != NULL; gIter = gIter->next) {
 388         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 389 
 390         child_rsc->fns->print(child_rsc, child_text, options, print_data);
 391     }
 392 
 393     status_print("%s</clone>\n", pre_text);
 394     free(child_text);
 395 }
 396 
 397 bool is_set_recursive(pe_resource_t * rsc, long long flag, bool any)
     /* [previous][next][first][last][top][bottom][index][help] */
 398 {
 399     GList *gIter;
 400     bool all = !any;
 401 
 402     if (pcmk_is_set(rsc->flags, flag)) {
 403         if(any) {
 404             return TRUE;
 405         }
 406     } else if(all) {
 407         return FALSE;
 408     }
 409 
 410     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 411         if(is_set_recursive(gIter->data, flag, any)) {
 412             if(any) {
 413                 return TRUE;
 414             }
 415 
 416         } else if(all) {
 417             return FALSE;
 418         }
 419     }
 420 
 421     if(all) {
 422         return TRUE;
 423     }
 424     return FALSE;
 425 }
 426 
 427 void
 428 clone_print(pe_resource_t * rsc, const char *pre_text, long options, void *print_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 429 {
 430     char *list_text = NULL;
 431     char *child_text = NULL;
 432     char *stopped_list = NULL;
 433     size_t list_text_len = 0;
 434     size_t stopped_list_len = 0;
 435 
 436     GList *promoted_list = NULL;
 437     GList *started_list = NULL;
 438     GList *gIter = rsc->children;
 439 
 440     clone_variant_data_t *clone_data = NULL;
 441     int active_instances = 0;
 442 
 443     if (pre_text == NULL) {
 444         pre_text = " ";
 445     }
 446 
 447     if (options & pe_print_xml) {
 448         clone_print_xml(rsc, pre_text, options, print_data);
 449         return;
 450     }
 451 
 452     get_clone_variant_data(clone_data, rsc);
 453 
 454     child_text = crm_strdup_printf("%s    ", pre_text);
 455 
 456     status_print("%sClone Set: %s [%s]%s%s%s",
 457                  pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child),
 458                  pcmk_is_set(rsc->flags, pe_rsc_promotable)? " (promotable)" : "",
 459                  pcmk_is_set(rsc->flags, pe_rsc_unique)? " (unique)" : "",
 460                  pcmk_is_set(rsc->flags, pe_rsc_managed)? "" : " (unmanaged)");
 461 
 462     if (options & pe_print_html) {
 463         status_print("\n<ul>\n");
 464 
 465     } else if ((options & pe_print_log) == 0) {
 466         status_print("\n");
 467     }
 468 
 469     for (; gIter != NULL; gIter = gIter->next) {
 470         gboolean print_full = FALSE;
 471         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 472         gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
 473 
 474         if (options & pe_print_clone_details) {
 475             print_full = TRUE;
 476         }
 477 
 478         if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 479             // Print individual instance when unique (except stopped orphans)
 480             if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 481                 print_full = TRUE;
 482             }
 483 
 484         // Everything else in this block is for anonymous clones
 485 
 486         } else if (pcmk_is_set(options, pe_print_pending)
 487                    && (child_rsc->pending_task != NULL)
 488                    && strcmp(child_rsc->pending_task, "probe")) {
 489             // Print individual instance when non-probe action is pending
 490             print_full = TRUE;
 491 
 492         } else if (partially_active == FALSE) {
 493             // List stopped instances when requested (except orphans)
 494             if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
 495                 && !pcmk_is_set(options, pe_print_clone_active)) {
 496                 pcmk__add_word(&stopped_list, &stopped_list_len, child_rsc->id);
 497             }
 498 
 499         } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
 500                    || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
 501                    || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
 502 
 503             // Print individual instance when active orphaned/unmanaged/failed
 504             print_full = TRUE;
 505 
 506         } else if (child_rsc->fns->active(child_rsc, TRUE)) {
 507             // Instance of fully active anonymous clone
 508 
 509             pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
 510 
 511             if (location) {
 512                 // Instance is active on a single node
 513 
 514                 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
 515 
 516                 if (location->details->online == FALSE && location->details->unclean) {
 517                     print_full = TRUE;
 518 
 519                 } else if (a_role > RSC_ROLE_UNPROMOTED) {
 520                     promoted_list = g_list_append(promoted_list, location);
 521 
 522                 } else {
 523                     started_list = g_list_append(started_list, location);
 524                 }
 525 
 526             } else {
 527                 /* uncolocated group - bleh */
 528                 print_full = TRUE;
 529             }
 530 
 531         } else {
 532             // Instance of partially active anonymous clone
 533             print_full = TRUE;
 534         }
 535 
 536         if (print_full) {
 537             if (options & pe_print_html) {
 538                 status_print("<li>\n");
 539             }
 540             child_rsc->fns->print(child_rsc, child_text, options, print_data);
 541             if (options & pe_print_html) {
 542                 status_print("</li>\n");
 543             }
 544         }
 545     }
 546 
 547     /* Promoted */
 548     promoted_list = g_list_sort(promoted_list, sort_node_uname);
 549     for (gIter = promoted_list; gIter; gIter = gIter->next) {
 550         pe_node_t *host = gIter->data;
 551 
 552         pcmk__add_word(&list_text, &list_text_len, host->details->uname);
 553         active_instances++;
 554     }
 555 
 556     short_print(list_text, child_text, PROMOTED_INSTANCES, NULL, options,
 557                 print_data);
 558     g_list_free(promoted_list);
 559     free(list_text);
 560     list_text = NULL;
 561     list_text_len = 0;
 562 
 563     /* Started/Unpromoted */
 564     started_list = g_list_sort(started_list, sort_node_uname);
 565     for (gIter = started_list; gIter; gIter = gIter->next) {
 566         pe_node_t *host = gIter->data;
 567 
 568         pcmk__add_word(&list_text, &list_text_len, host->details->uname);
 569         active_instances++;
 570     }
 571 
 572     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 573         enum rsc_role_e role = configured_role(rsc);
 574 
 575         if (role == RSC_ROLE_UNPROMOTED) {
 576             short_print(list_text, child_text,
 577                         UNPROMOTED_INSTANCES " (target-role)", NULL, options,
 578                         print_data);
 579         } else {
 580             short_print(list_text, child_text, UNPROMOTED_INSTANCES, NULL,
 581                         options, print_data);
 582         }
 583 
 584     } else {
 585         short_print(list_text, child_text, "Started", NULL, options, print_data);
 586     }
 587 
 588     g_list_free(started_list);
 589     free(list_text);
 590     list_text = NULL;
 591     list_text_len = 0;
 592 
 593     if (!pcmk_is_set(options, pe_print_clone_active)) {
 594         const char *state = "Stopped";
 595         enum rsc_role_e role = configured_role(rsc);
 596 
 597         if (role == RSC_ROLE_STOPPED) {
 598             state = "Stopped (disabled)";
 599         }
 600 
 601         if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
 602             && (clone_data->clone_max > active_instances)) {
 603 
 604             GList *nIter;
 605             GList *list = g_hash_table_get_values(rsc->allowed_nodes);
 606 
 607             /* Custom stopped list for non-unique clones */
 608             free(stopped_list);
 609             stopped_list = NULL;
 610             stopped_list_len = 0;
 611 
 612             if (list == NULL) {
 613                 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
 614                  * If we've not probed for them yet, the Stopped list will be empty
 615                  */
 616                 list = g_hash_table_get_values(rsc->known_on);
 617             }
 618 
 619             list = g_list_sort(list, sort_node_uname);
 620             for (nIter = list; nIter != NULL; nIter = nIter->next) {
 621                 pe_node_t *node = (pe_node_t *)nIter->data;
 622 
 623                 if (pe_find_node(rsc->running_on, node->details->uname) == NULL) {
 624                     pcmk__add_word(&stopped_list, &stopped_list_len,
 625                                    node->details->uname);
 626                 }
 627             }
 628             g_list_free(list);
 629         }
 630 
 631         short_print(stopped_list, child_text, state, NULL, options, print_data);
 632         free(stopped_list);
 633     }
 634 
 635     if (options & pe_print_html) {
 636         status_print("</ul>\n");
 637     }
 638 
 639     free(child_text);
 640 }
 641 
 642 PCMK__OUTPUT_ARGS("clone", "unsigned int", "pe_resource_t *", "GList *", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 643 int
 644 pe__clone_xml(pcmk__output_t *out, va_list args)
 645 {
 646     unsigned int show_opts = va_arg(args, unsigned int);
 647     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 648     GList *only_node = va_arg(args, GList *);
 649     GList *only_rsc = va_arg(args, GList *);
 650 
 651     GList *gIter = rsc->children;
 652     GList *all = NULL;
 653     int rc = pcmk_rc_no_output;
 654     gboolean printed_header = FALSE;
 655     gboolean print_everything = TRUE;
 656 
 657     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 658         return rc;
 659     }
 660 
 661     print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
 662                        (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
 663 
 664     all = g_list_prepend(all, (gpointer) "*");
 665 
 666     for (; gIter != NULL; gIter = gIter->next) {
 667         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 668 
 669         if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
 670             continue;
 671         }
 672 
 673         if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
 674             continue;
 675         }
 676 
 677         if (!printed_header) {
 678             printed_header = TRUE;
 679 
 680             rc = pe__name_and_nvpairs_xml(out, true, "clone", 8,
 681                     "id", rsc->id,
 682                     "multi_state", pe__rsc_bool_str(rsc, pe_rsc_promotable),
 683                     "unique", pe__rsc_bool_str(rsc, pe_rsc_unique),
 684                     "managed", pe__rsc_bool_str(rsc, pe_rsc_managed),
 685                     "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)),
 686                     "failed", pe__rsc_bool_str(rsc, pe_rsc_failed),
 687                     "failure_ignored", pe__rsc_bool_str(rsc, pe_rsc_failure_ignored),
 688                     "target_role", configured_role_str(rsc));
 689             CRM_ASSERT(rc == pcmk_rc_ok);
 690         }
 691 
 692         out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
 693                      child_rsc, only_node, all);
 694     }
 695 
 696     if (printed_header) {
 697         pcmk__output_xml_pop_parent(out);
 698     }
 699 
 700     g_list_free(all);
 701     return rc;
 702 }
 703 
 704 PCMK__OUTPUT_ARGS("clone", "unsigned int", "pe_resource_t *", "GList *", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 705 int
 706 pe__clone_default(pcmk__output_t *out, va_list args)
 707 {
 708     unsigned int show_opts = va_arg(args, unsigned int);
 709     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 710     GList *only_node = va_arg(args, GList *);
 711     GList *only_rsc = va_arg(args, GList *);
 712 
 713     char *list_text = NULL;
 714     char *stopped_list = NULL;
 715     size_t list_text_len = 0;
 716     size_t stopped_list_len = 0;
 717 
 718     GList *promoted_list = NULL;
 719     GList *started_list = NULL;
 720     GList *gIter = rsc->children;
 721 
 722     clone_variant_data_t *clone_data = NULL;
 723     int active_instances = 0;
 724     int rc = pcmk_rc_no_output;
 725     gboolean print_everything = TRUE;
 726 
 727     get_clone_variant_data(clone_data, rsc);
 728 
 729     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 730         return rc;
 731     }
 732 
 733     print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
 734                        (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
 735 
 736     for (; gIter != NULL; gIter = gIter->next) {
 737         gboolean print_full = FALSE;
 738         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 739         gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
 740 
 741         if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
 742             continue;
 743         }
 744 
 745         if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
 746             continue;
 747         }
 748 
 749         if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
 750             print_full = TRUE;
 751         }
 752 
 753         if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 754             // Print individual instance when unique (except stopped orphans)
 755             if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 756                 print_full = TRUE;
 757             }
 758 
 759         // Everything else in this block is for anonymous clones
 760 
 761         } else if (pcmk_is_set(show_opts, pcmk_show_pending)
 762                    && (child_rsc->pending_task != NULL)
 763                    && strcmp(child_rsc->pending_task, "probe")) {
 764             // Print individual instance when non-probe action is pending
 765             print_full = TRUE;
 766 
 767         } else if (partially_active == FALSE) {
 768             // List stopped instances when requested (except orphans)
 769             if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
 770                 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
 771                 pcmk__add_word(&stopped_list, &stopped_list_len, child_rsc->id);
 772             }
 773 
 774         } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
 775                    || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
 776                    || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
 777 
 778             // Print individual instance when active orphaned/unmanaged/failed
 779             print_full = TRUE;
 780 
 781         } else if (child_rsc->fns->active(child_rsc, TRUE)) {
 782             // Instance of fully active anonymous clone
 783 
 784             pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
 785 
 786             if (location) {
 787                 // Instance is active on a single node
 788 
 789                 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
 790 
 791                 if (location->details->online == FALSE && location->details->unclean) {
 792                     print_full = TRUE;
 793 
 794                 } else if (a_role > RSC_ROLE_UNPROMOTED) {
 795                     promoted_list = g_list_append(promoted_list, location);
 796 
 797                 } else {
 798                     started_list = g_list_append(started_list, location);
 799                 }
 800 
 801             } else {
 802                 /* uncolocated group - bleh */
 803                 print_full = TRUE;
 804             }
 805 
 806         } else {
 807             // Instance of partially active anonymous clone
 808             print_full = TRUE;
 809         }
 810 
 811         if (print_full) {
 812             GList *all = NULL;
 813 
 814             clone_header(out, &rc, rsc, clone_data);
 815 
 816             /* Print every resource that's a child of this clone. */
 817             all = g_list_prepend(all, (gpointer) "*");
 818             out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
 819                          child_rsc, only_node, all);
 820             g_list_free(all);
 821         }
 822     }
 823 
 824     if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
 825         free(stopped_list);
 826         PCMK__OUTPUT_LIST_FOOTER(out, rc);
 827         return pcmk_rc_ok;
 828     }
 829 
 830     /* Promoted */
 831     promoted_list = g_list_sort(promoted_list, sort_node_uname);
 832     for (gIter = promoted_list; gIter; gIter = gIter->next) {
 833         pe_node_t *host = gIter->data;
 834 
 835         if (!pcmk__str_in_list(host->details->uname, only_node,
 836                                pcmk__str_star_matches|pcmk__str_casei)) {
 837             continue;
 838         }
 839 
 840         pcmk__add_word(&list_text, &list_text_len, host->details->uname);
 841         active_instances++;
 842     }
 843     g_list_free(promoted_list);
 844 
 845     if (list_text != NULL) {
 846         clone_header(out, &rc, rsc, clone_data);
 847 
 848         out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]", list_text);
 849         free(list_text);
 850         list_text = NULL;
 851         list_text_len = 0;
 852     }
 853 
 854     /* Started/Unpromoted */
 855     started_list = g_list_sort(started_list, sort_node_uname);
 856     for (gIter = started_list; gIter; gIter = gIter->next) {
 857         pe_node_t *host = gIter->data;
 858 
 859         if (!pcmk__str_in_list(host->details->uname, only_node,
 860                                pcmk__str_star_matches|pcmk__str_casei)) {
 861             continue;
 862         }
 863 
 864         pcmk__add_word(&list_text, &list_text_len, host->details->uname);
 865         active_instances++;
 866     }
 867     g_list_free(started_list);
 868 
 869     if (list_text != NULL) {
 870         clone_header(out, &rc, rsc, clone_data);
 871 
 872         if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 873             enum rsc_role_e role = configured_role(rsc);
 874 
 875             if (role == RSC_ROLE_UNPROMOTED) {
 876                 out->list_item(out, NULL,
 877                                UNPROMOTED_INSTANCES " (target-role): [ %s ]",
 878                                list_text);
 879             } else {
 880                 out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
 881                                list_text);
 882             }
 883 
 884         } else {
 885             out->list_item(out, NULL, "Started: [ %s ]", list_text);
 886         }
 887         free(list_text);
 888         list_text = NULL;
 889         list_text_len = 0;
 890     }
 891 
 892     if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
 893         const char *state = "Stopped";
 894         enum rsc_role_e role = configured_role(rsc);
 895 
 896         if (role == RSC_ROLE_STOPPED) {
 897             state = "Stopped (disabled)";
 898         }
 899 
 900         if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
 901             && (clone_data->clone_max > active_instances)) {
 902 
 903             GList *nIter;
 904             GList *list = g_hash_table_get_values(rsc->allowed_nodes);
 905 
 906             /* Custom stopped list for non-unique clones */
 907             free(stopped_list);
 908             stopped_list = NULL;
 909             stopped_list_len = 0;
 910 
 911             if (list == NULL) {
 912                 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
 913                  * If we've not probed for them yet, the Stopped list will be empty
 914                  */
 915                 list = g_hash_table_get_values(rsc->known_on);
 916             }
 917 
 918             list = g_list_sort(list, sort_node_uname);
 919             for (nIter = list; nIter != NULL; nIter = nIter->next) {
 920                 pe_node_t *node = (pe_node_t *)nIter->data;
 921 
 922                 if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
 923                     pcmk__str_in_list(node->details->uname, only_node,
 924                                       pcmk__str_star_matches|pcmk__str_casei)) {
 925                     pcmk__add_word(&stopped_list, &stopped_list_len,
 926                                    node->details->uname);
 927                 }
 928             }
 929             g_list_free(list);
 930         }
 931 
 932         if (stopped_list != NULL) {
 933             clone_header(out, &rc, rsc, clone_data);
 934 
 935             out->list_item(out, NULL, "%s: [ %s ]", state, stopped_list);
 936             free(stopped_list);
 937             stopped_list_len = 0;
 938 
 939         /* If there are no instances of this clone (perhaps because there are no
 940          * nodes configured), simply output the clone header by itself.  This can
 941          * come up in PCS testing.
 942          */
 943         } else if (active_instances == 0) {
 944             clone_header(out, &rc, rsc, clone_data);
 945             PCMK__OUTPUT_LIST_FOOTER(out, rc);
 946             return rc;
 947         }
 948     }
 949 
 950     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 951     return rc;
 952 }
 953 
 954 void
 955 clone_free(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 956 {
 957     clone_variant_data_t *clone_data = NULL;
 958 
 959     get_clone_variant_data(clone_data, rsc);
 960 
 961     pe_rsc_trace(rsc, "Freeing %s", rsc->id);
 962 
 963     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 964         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 965 
 966         CRM_ASSERT(child_rsc);
 967         pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
 968         free_xml(child_rsc->xml);
 969         child_rsc->xml = NULL;
 970         /* There could be a saved unexpanded xml */
 971         free_xml(child_rsc->orig_xml);
 972         child_rsc->orig_xml = NULL;
 973         child_rsc->fns->free(child_rsc);
 974     }
 975 
 976     g_list_free(rsc->children);
 977 
 978     if (clone_data) {
 979         CRM_ASSERT(clone_data->demote_notify == NULL);
 980         CRM_ASSERT(clone_data->stop_notify == NULL);
 981         CRM_ASSERT(clone_data->start_notify == NULL);
 982         CRM_ASSERT(clone_data->promote_notify == NULL);
 983     }
 984 
 985     common_free(rsc);
 986 }
 987 
 988 enum rsc_role_e
 989 clone_resource_state(const pe_resource_t * rsc, gboolean current)
     /* [previous][next][first][last][top][bottom][index][help] */
 990 {
 991     enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
 992     GList *gIter = rsc->children;
 993 
 994     for (; gIter != NULL; gIter = gIter->next) {
 995         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 996         enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
 997 
 998         if (a_role > clone_role) {
 999             clone_role = a_role;
1000         }
1001     }
1002 
1003     pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role));
1004     return clone_role;
1005 }
1006 
1007 /*!
1008  * \internal
1009  * \brief Check whether a clone has an instance for every node
1010  *
1011  * \param[in] rsc       Clone to check
1012  * \param[in] data_set  Cluster state
1013  */
1014 bool
1015 pe__is_universal_clone(pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1016                        pe_working_set_t *data_set)
1017 {
1018     if (pe_rsc_is_clone(rsc)) {
1019         clone_variant_data_t *clone_data = NULL;
1020 
1021         get_clone_variant_data(clone_data, rsc);
1022         if (clone_data->clone_max == g_list_length(data_set->nodes)) {
1023             return TRUE;
1024         }
1025     }
1026     return FALSE;
1027 }
1028 
1029 gboolean
1030 pe__clone_is_filtered(pe_resource_t *rsc, GList *only_rsc, gboolean check_parent)
     /* [previous][next][first][last][top][bottom][index][help] */
1031 {
1032     gboolean passes = FALSE;
1033     clone_variant_data_t *clone_data = NULL;
1034 
1035     if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1036         passes = TRUE;
1037     } else {
1038         get_clone_variant_data(clone_data, rsc);
1039         passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches);
1040 
1041         if (!passes) {
1042             for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1043                 pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1044 
1045                 if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1046                     passes = TRUE;
1047                     break;
1048                 }
1049             }
1050         }
1051     }
1052 
1053     return !passes;
1054 }

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