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

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