root/lib/pengine/clone.c

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

DEFINITIONS

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

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