root/lib/pengine/clone.c

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

DEFINITIONS

This source file includes following definitions.
  1. pe__clone_max
  2. pe__clone_node_max
  3. pe__clone_promoted_max
  4. pe__clone_promoted_node_max
  5. sorted_hash_table_values
  6. nodes_with_status
  7. node_list_to_str
  8. clone_header
  9. pe__force_anon
  10. find_clone_instance
  11. pe__create_clone_child
  12. clone_unpack
  13. clone_active
  14. short_print
  15. configured_role_str
  16. configured_role
  17. clone_print_xml
  18. is_set_recursive
  19. clone_print
  20. PCMK__OUTPUT_ARGS
  21. PCMK__OUTPUT_ARGS
  22. clone_free
  23. clone_resource_state
  24. pe__is_universal_clone
  25. pe__clone_is_filtered
  26. pe__clone_child_id
  27. pe__clone_is_ordered
  28. pe__set_clone_flag
  29. pe__create_promotable_pseudo_ops
  30. pe__create_clone_notifications
  31. pe__free_clone_notification_data
  32. pe__create_clone_notif_pseudo_ops

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdint.h>
  13 
  14 #include <crm/pengine/rules.h>
  15 #include <crm/pengine/status.h>
  16 #include <crm/pengine/internal.h>
  17 #include <pe_status_private.h>
  18 #include <crm/msg_xml.h>
  19 #include <crm/common/output.h>
  20 #include <crm/common/xml_internal.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 typedef struct clone_variant_data_s {
  31     int clone_max;
  32     int clone_node_max;
  33 
  34     int promoted_max;
  35     int promoted_node_max;
  36 
  37     int total_clones;
  38 
  39     uint32_t flags; // Group of enum pe__clone_flags
  40 
  41     notify_data_t *stop_notify;
  42     notify_data_t *start_notify;
  43     notify_data_t *demote_notify;
  44     notify_data_t *promote_notify;
  45 
  46     xmlNode *xml_obj_child;
  47 } clone_variant_data_t;
  48 
  49 #define get_clone_variant_data(data, rsc)                       \
  50     CRM_ASSERT((rsc != NULL) && (rsc->variant == pe_clone));    \
  51     data = (clone_variant_data_t *) rsc->variant_opaque;
  52 
  53 /*!
  54  * \internal
  55  * \brief Return the maximum number of clone instances allowed to be run
  56  *
  57  * \param[in] clone  Clone or clone instance to check
  58  *
  59  * \return Maximum instances for \p clone
  60  */
  61 int
  62 pe__clone_max(const pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     const clone_variant_data_t *clone_data = NULL;
  65 
  66     get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
  67     return clone_data->clone_max;
  68 }
  69 
  70 /*!
  71  * \internal
  72  * \brief Return the maximum number of clone instances allowed per node
  73  *
  74  * \param[in] clone  Promotable clone or clone instance to check
  75  *
  76  * \return Maximum allowed instances per node for \p clone
  77  */
  78 int
  79 pe__clone_node_max(const pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
  80 {
  81     const clone_variant_data_t *clone_data = NULL;
  82 
  83     get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
  84     return clone_data->clone_node_max;
  85 }
  86 
  87 /*!
  88  * \internal
  89  * \brief Return the maximum number of clone instances allowed to be promoted
  90  *
  91  * \param[in] clone  Promotable clone or clone instance to check
  92  *
  93  * \return Maximum promoted instances for \p clone
  94  */
  95 int
  96 pe__clone_promoted_max(const pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
  97 {
  98     clone_variant_data_t *clone_data = NULL;
  99 
 100     get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
 101     return clone_data->promoted_max;
 102 }
 103 
 104 /*!
 105  * \internal
 106  * \brief Return the maximum number of clone instances allowed to be promoted
 107  *
 108  * \param[in] clone  Promotable clone or clone instance to check
 109  *
 110  * \return Maximum promoted instances for \p clone
 111  */
 112 int
 113 pe__clone_promoted_node_max(const pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
 114 {
 115     clone_variant_data_t *clone_data = NULL;
 116 
 117     get_clone_variant_data(clone_data, pe__const_top_resource(clone, false));
 118     return clone_data->promoted_node_max;
 119 }
 120 
 121 static GList *
 122 sorted_hash_table_values(GHashTable *table)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     GList *retval = NULL;
 125     GHashTableIter iter;
 126     gpointer key, value;
 127 
 128     g_hash_table_iter_init(&iter, table);
 129     while (g_hash_table_iter_next(&iter, &key, &value)) {
 130         if (!g_list_find_custom(retval, value, (GCompareFunc) strcmp)) {
 131             retval = g_list_prepend(retval, (char *) value);
 132         }
 133     }
 134 
 135     retval = g_list_sort(retval, (GCompareFunc) strcmp);
 136     return retval;
 137 }
 138 
 139 static GList *
 140 nodes_with_status(GHashTable *table, const char *status)
     /* [previous][next][first][last][top][bottom][index][help] */
 141 {
 142     GList *retval = NULL;
 143     GHashTableIter iter;
 144     gpointer key, value;
 145 
 146     g_hash_table_iter_init(&iter, table);
 147     while (g_hash_table_iter_next(&iter, &key, &value)) {
 148         if (!strcmp((char *) value, status)) {
 149             retval = g_list_prepend(retval, key);
 150         }
 151     }
 152 
 153     retval = g_list_sort(retval, (GCompareFunc) pcmk__numeric_strcasecmp);
 154     return retval;
 155 }
 156 
 157 static GString *
 158 node_list_to_str(const GList *list)
     /* [previous][next][first][last][top][bottom][index][help] */
 159 {
 160     GString *retval = NULL;
 161 
 162     for (const GList *iter = list; iter != NULL; iter = iter->next) {
 163         pcmk__add_word(&retval, 1024, (const char *) iter->data);
 164     }
 165 
 166     return retval;
 167 }
 168 
 169 static void
 170 clone_header(pcmk__output_t *out, int *rc, const pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 171              clone_variant_data_t *clone_data, const char *desc)
 172 {
 173     GString *attrs = NULL;
 174 
 175     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 176         pcmk__add_separated_word(&attrs, 64, "promotable", ", ");
 177     }
 178 
 179     if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 180         pcmk__add_separated_word(&attrs, 64, "unique", ", ");
 181     }
 182 
 183     if (pe__resource_is_disabled(rsc)) {
 184         pcmk__add_separated_word(&attrs, 64, "disabled", ", ");
 185     }
 186 
 187     if (pcmk_is_set(rsc->flags, pe_rsc_maintenance)) {
 188         pcmk__add_separated_word(&attrs, 64, "maintenance", ", ");
 189 
 190     } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 191         pcmk__add_separated_word(&attrs, 64, "unmanaged", ", ");
 192     }
 193 
 194     if (attrs != NULL) {
 195         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s] (%s)%s%s%s",
 196                                  rsc->id, ID(clone_data->xml_obj_child),
 197                                  (const char *) attrs->str, desc ? " (" : "",
 198                                  desc ? desc : "", desc ? ")" : "");
 199         g_string_free(attrs, TRUE);
 200     } else {
 201         PCMK__OUTPUT_LIST_HEADER(out, FALSE, *rc, "Clone Set: %s [%s]%s%s%s",
 202                                  rsc->id, ID(clone_data->xml_obj_child),
 203                                  desc ? " (" : "", desc ? desc : "",
 204                                  desc ? ")" : "");
 205     }
 206 }
 207 
 208 void
 209 pe__force_anon(const char *standard, pe_resource_t *rsc, const char *rid,
     /* [previous][next][first][last][top][bottom][index][help] */
 210                pe_working_set_t *data_set)
 211 {
 212     if (pe_rsc_is_clone(rsc)) {
 213         clone_variant_data_t *clone_data = rsc->variant_opaque;
 214 
 215         pe_warn("Ignoring " XML_RSC_ATTR_UNIQUE " for %s because %s resources "
 216                 "such as %s can be used only as anonymous clones",
 217                 rsc->id, standard, rid);
 218 
 219         clone_data->clone_node_max = 1;
 220         clone_data->clone_max = QB_MIN(clone_data->clone_max,
 221                                        g_list_length(data_set->nodes));
 222     }
 223 }
 224 
 225 pe_resource_t *
 226 find_clone_instance(const pe_resource_t *rsc, const char *sub_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228     char *child_id = NULL;
 229     pe_resource_t *child = NULL;
 230     const char *child_base = NULL;
 231     clone_variant_data_t *clone_data = NULL;
 232 
 233     get_clone_variant_data(clone_data, rsc);
 234 
 235     child_base = ID(clone_data->xml_obj_child);
 236     child_id = crm_strdup_printf("%s:%s", child_base, sub_id);
 237     child = pe_find_resource(rsc->children, child_id);
 238 
 239     free(child_id);
 240     return child;
 241 }
 242 
 243 pe_resource_t *
 244 pe__create_clone_child(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 245 {
 246     gboolean as_orphan = FALSE;
 247     char *inc_num = NULL;
 248     char *inc_max = NULL;
 249     pe_resource_t *child_rsc = NULL;
 250     xmlNode *child_copy = NULL;
 251     clone_variant_data_t *clone_data = NULL;
 252 
 253     get_clone_variant_data(clone_data, rsc);
 254 
 255     CRM_CHECK(clone_data->xml_obj_child != NULL, return FALSE);
 256 
 257     if (clone_data->total_clones >= clone_data->clone_max) {
 258         // If we've already used all available instances, this is an orphan
 259         as_orphan = TRUE;
 260     }
 261 
 262     // Allocate instance numbers in numerical order (starting at 0)
 263     inc_num = pcmk__itoa(clone_data->total_clones);
 264     inc_max = pcmk__itoa(clone_data->clone_max);
 265 
 266     child_copy = copy_xml(clone_data->xml_obj_child);
 267 
 268     crm_xml_add(child_copy, XML_RSC_ATTR_INCARNATION, inc_num);
 269 
 270     if (pe__unpack_resource(child_copy, &child_rsc, rsc,
 271                             data_set) != pcmk_rc_ok) {
 272         goto bail;
 273     }
 274 /*  child_rsc->globally_unique = rsc->globally_unique; */
 275 
 276     CRM_ASSERT(child_rsc);
 277     clone_data->total_clones += 1;
 278     pe_rsc_trace(child_rsc, "Setting clone attributes for: %s", child_rsc->id);
 279     rsc->children = g_list_append(rsc->children, child_rsc);
 280     if (as_orphan) {
 281         pe__set_resource_flags_recursive(child_rsc, pe_rsc_orphan);
 282     }
 283 
 284     add_hash_param(child_rsc->meta, XML_RSC_ATTR_INCARNATION_MAX, inc_max);
 285     pe_rsc_trace(rsc, "Added %s instance %s", rsc->id, child_rsc->id);
 286 
 287   bail:
 288     free(inc_num);
 289     free(inc_max);
 290 
 291     return child_rsc;
 292 }
 293 
 294 gboolean
 295 clone_unpack(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 296 {
 297     int lpc = 0;
 298     xmlNode *a_child = NULL;
 299     xmlNode *xml_obj = rsc->xml;
 300     clone_variant_data_t *clone_data = NULL;
 301 
 302     const char *max_clones = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_MAX);
 303     const char *max_clones_node = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION_NODEMAX);
 304 
 305     pe_rsc_trace(rsc, "Processing resource %s...", rsc->id);
 306 
 307     clone_data = calloc(1, sizeof(clone_variant_data_t));
 308     rsc->variant_opaque = clone_data;
 309 
 310     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 311         const char *promoted_max = NULL;
 312         const char *promoted_node_max = NULL;
 313 
 314         promoted_max = g_hash_table_lookup(rsc->meta,
 315                                            XML_RSC_ATTR_PROMOTED_MAX);
 316         if (promoted_max == NULL) {
 317             // @COMPAT deprecated since 2.0.0
 318             promoted_max = g_hash_table_lookup(rsc->meta,
 319                                                PCMK_XA_PROMOTED_MAX_LEGACY);
 320         }
 321 
 322         promoted_node_max = g_hash_table_lookup(rsc->meta,
 323                                                 XML_RSC_ATTR_PROMOTED_NODEMAX);
 324         if (promoted_node_max == NULL) {
 325             // @COMPAT deprecated since 2.0.0
 326             promoted_node_max =
 327                 g_hash_table_lookup(rsc->meta,
 328                                     PCMK_XA_PROMOTED_NODE_MAX_LEGACY);
 329         }
 330 
 331         // Use 1 as default but 0 for minimum and invalid
 332         if (promoted_max == NULL) {
 333             clone_data->promoted_max = 1;
 334         } else {
 335             pcmk__scan_min_int(promoted_max, &(clone_data->promoted_max), 0);
 336         }
 337 
 338         // Use 1 as default but 0 for minimum and invalid
 339         if (promoted_node_max == NULL) {
 340             clone_data->promoted_node_max = 1;
 341         } else {
 342             pcmk__scan_min_int(promoted_node_max,
 343                                &(clone_data->promoted_node_max), 0);
 344         }
 345     }
 346 
 347     // Implied by calloc()
 348     /* clone_data->xml_obj_child = NULL; */
 349 
 350     // Use 1 as default but 0 for minimum and invalid
 351     if (max_clones_node == NULL) {
 352         clone_data->clone_node_max = 1;
 353     } else {
 354         pcmk__scan_min_int(max_clones_node, &(clone_data->clone_node_max), 0);
 355     }
 356 
 357     /* Use number of nodes (but always at least 1, which is handy for crm_verify
 358      * for a CIB without nodes) as default, but 0 for minimum and invalid
 359      */
 360     if (max_clones == NULL) {
 361         clone_data->clone_max = QB_MAX(1, g_list_length(data_set->nodes));
 362     } else {
 363         pcmk__scan_min_int(max_clones, &(clone_data->clone_max), 0);
 364     }
 365 
 366     if (crm_is_true(g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_ORDERED))) {
 367         clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
 368                                                "Clone", rsc->id,
 369                                                clone_data->flags,
 370                                                pe__clone_ordered,
 371                                                "pe__clone_ordered");
 372     }
 373 
 374     if ((rsc->flags & pe_rsc_unique) == 0 && clone_data->clone_node_max > 1) {
 375         pcmk__config_err("Ignoring " XML_RSC_ATTR_PROMOTED_MAX " for %s "
 376                          "because anonymous clones support only one instance "
 377                          "per node", rsc->id);
 378         clone_data->clone_node_max = 1;
 379     }
 380 
 381     pe_rsc_trace(rsc, "Options for %s", rsc->id);
 382     pe_rsc_trace(rsc, "\tClone max: %d", clone_data->clone_max);
 383     pe_rsc_trace(rsc, "\tClone node max: %d", clone_data->clone_node_max);
 384     pe_rsc_trace(rsc, "\tClone is unique: %s",
 385                  pe__rsc_bool_str(rsc, pe_rsc_unique));
 386     pe_rsc_trace(rsc, "\tClone is promotable: %s",
 387                  pe__rsc_bool_str(rsc, pe_rsc_promotable));
 388 
 389     // Clones may contain a single group or primitive
 390     for (a_child = pcmk__xe_first_child(xml_obj); a_child != NULL;
 391          a_child = pcmk__xe_next(a_child)) {
 392 
 393         if (pcmk__str_any_of((const char *)a_child->name, XML_CIB_TAG_RESOURCE, XML_CIB_TAG_GROUP, NULL)) {
 394             clone_data->xml_obj_child = a_child;
 395             break;
 396         }
 397     }
 398 
 399     if (clone_data->xml_obj_child == NULL) {
 400         pcmk__config_err("%s has nothing to clone", rsc->id);
 401         return FALSE;
 402     }
 403 
 404     /*
 405      * Make clones ever so slightly sticky by default
 406      *
 407      * This helps ensure clone instances are not shuffled around the cluster
 408      * for no benefit in situations when pre-allocation is not appropriate
 409      */
 410     if (g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_STICKINESS) == NULL) {
 411         add_hash_param(rsc->meta, XML_RSC_ATTR_STICKINESS, "1");
 412     }
 413 
 414     /* This ensures that the globally-unique value always exists for children to
 415      * inherit when being unpacked, as well as in resource agents' environment.
 416      */
 417     add_hash_param(rsc->meta, XML_RSC_ATTR_UNIQUE,
 418                    pe__rsc_bool_str(rsc, pe_rsc_unique));
 419 
 420     if (clone_data->clone_max <= 0) {
 421         /* Create one child instance so that unpack_find_resource() will hook up
 422          * any orphans up to the parent correctly.
 423          */
 424         if (pe__create_clone_child(rsc, data_set) == NULL) {
 425             return FALSE;
 426         }
 427 
 428     } else {
 429         // Create a child instance for each available instance number
 430         for (lpc = 0; lpc < clone_data->clone_max; lpc++) {
 431             if (pe__create_clone_child(rsc, data_set) == NULL) {
 432                 return FALSE;
 433             }
 434         }
 435     }
 436 
 437     pe_rsc_trace(rsc, "Added %d children to resource %s...", clone_data->clone_max, rsc->id);
 438     return TRUE;
 439 }
 440 
 441 gboolean
 442 clone_active(pe_resource_t * rsc, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 443 {
 444     GList *gIter = rsc->children;
 445 
 446     for (; gIter != NULL; gIter = gIter->next) {
 447         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 448         gboolean child_active = child_rsc->fns->active(child_rsc, all);
 449 
 450         if (all == FALSE && child_active) {
 451             return TRUE;
 452         } else if (all && child_active == FALSE) {
 453             return FALSE;
 454         }
 455     }
 456 
 457     if (all) {
 458         return TRUE;
 459     } else {
 460         return FALSE;
 461     }
 462 }
 463 
 464 /*!
 465  * \internal
 466  * \deprecated This function will be removed in a future release
 467  */
 468 static void
 469 short_print(const char *list, const char *prefix, const char *type,
     /* [previous][next][first][last][top][bottom][index][help] */
 470             const char *suffix, long options, void *print_data)
 471 {
 472     if(suffix == NULL) {
 473         suffix = "";
 474     }
 475 
 476     if (!pcmk__str_empty(list)) {
 477         if (options & pe_print_html) {
 478             status_print("<li>");
 479         }
 480         status_print("%s%s: [ %s ]%s", prefix, type, list, suffix);
 481 
 482         if (options & pe_print_html) {
 483             status_print("</li>\n");
 484 
 485         } else if (options & pe_print_suppres_nl) {
 486             /* nothing */
 487         } else if ((options & pe_print_printf) || (options & pe_print_ncurses)) {
 488             status_print("\n");
 489         }
 490 
 491     }
 492 }
 493 
 494 static const char *
 495 configured_role_str(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 496 {
 497     const char *target_role = g_hash_table_lookup(rsc->meta,
 498                                                   XML_RSC_ATTR_TARGET_ROLE);
 499 
 500     if ((target_role == NULL) && rsc->children && rsc->children->data) {
 501         target_role = g_hash_table_lookup(((pe_resource_t*)rsc->children->data)->meta,
 502                                           XML_RSC_ATTR_TARGET_ROLE);
 503     }
 504     return target_role;
 505 }
 506 
 507 static enum rsc_role_e
 508 configured_role(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 509 {
 510     const char *target_role = configured_role_str(rsc);
 511 
 512     if (target_role) {
 513         return text2role(target_role);
 514     }
 515     return RSC_ROLE_UNKNOWN;
 516 }
 517 
 518 /*!
 519  * \internal
 520  * \deprecated This function will be removed in a future release
 521  */
 522 static void
 523 clone_print_xml(pe_resource_t *rsc, const char *pre_text, long options,
     /* [previous][next][first][last][top][bottom][index][help] */
 524                 void *print_data)
 525 {
 526     char *child_text = crm_strdup_printf("%s    ", pre_text);
 527     const char *target_role = configured_role_str(rsc);
 528     GList *gIter = rsc->children;
 529 
 530     status_print("%s<clone ", pre_text);
 531     status_print(XML_ATTR_ID "=\"%s\" ", rsc->id);
 532     status_print("multi_state=\"%s\" ",
 533                  pe__rsc_bool_str(rsc, pe_rsc_promotable));
 534     status_print("unique=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_unique));
 535     status_print("managed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_managed));
 536     status_print("failed=\"%s\" ", pe__rsc_bool_str(rsc, pe_rsc_failed));
 537     status_print("failure_ignored=\"%s\" ",
 538                  pe__rsc_bool_str(rsc, pe_rsc_failure_ignored));
 539     if (target_role) {
 540         status_print("target_role=\"%s\" ", target_role);
 541     }
 542     status_print(">\n");
 543 
 544     for (; gIter != NULL; gIter = gIter->next) {
 545         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 546 
 547         child_rsc->fns->print(child_rsc, child_text, options, print_data);
 548     }
 549 
 550     status_print("%s</clone>\n", pre_text);
 551     free(child_text);
 552 }
 553 
 554 bool
 555 is_set_recursive(const pe_resource_t *rsc, long long flag, bool any)
     /* [previous][next][first][last][top][bottom][index][help] */
 556 {
 557     GList *gIter;
 558     bool all = !any;
 559 
 560     if (pcmk_is_set(rsc->flags, flag)) {
 561         if(any) {
 562             return TRUE;
 563         }
 564     } else if(all) {
 565         return FALSE;
 566     }
 567 
 568     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 569         if(is_set_recursive(gIter->data, flag, any)) {
 570             if(any) {
 571                 return TRUE;
 572             }
 573 
 574         } else if(all) {
 575             return FALSE;
 576         }
 577     }
 578 
 579     if(all) {
 580         return TRUE;
 581     }
 582     return FALSE;
 583 }
 584 
 585 /*!
 586  * \internal
 587  * \deprecated This function will be removed in a future release
 588  */
 589 void
 590 clone_print(pe_resource_t *rsc, const char *pre_text, long options,
     /* [previous][next][first][last][top][bottom][index][help] */
 591             void *print_data)
 592 {
 593     GString *list_text = NULL;
 594     char *child_text = NULL;
 595     GString *stopped_list = NULL;
 596 
 597     GList *promoted_list = NULL;
 598     GList *started_list = NULL;
 599     GList *gIter = rsc->children;
 600 
 601     clone_variant_data_t *clone_data = NULL;
 602     int active_instances = 0;
 603 
 604     if (pre_text == NULL) {
 605         pre_text = " ";
 606     }
 607 
 608     if (options & pe_print_xml) {
 609         clone_print_xml(rsc, pre_text, options, print_data);
 610         return;
 611     }
 612 
 613     get_clone_variant_data(clone_data, rsc);
 614 
 615     child_text = crm_strdup_printf("%s    ", pre_text);
 616 
 617     status_print("%sClone Set: %s [%s]%s%s%s",
 618                  pre_text ? pre_text : "", rsc->id, ID(clone_data->xml_obj_child),
 619                  pcmk_is_set(rsc->flags, pe_rsc_promotable)? " (promotable)" : "",
 620                  pcmk_is_set(rsc->flags, pe_rsc_unique)? " (unique)" : "",
 621                  pcmk_is_set(rsc->flags, pe_rsc_managed)? "" : " (unmanaged)");
 622 
 623     if (options & pe_print_html) {
 624         status_print("\n<ul>\n");
 625 
 626     } else if ((options & pe_print_log) == 0) {
 627         status_print("\n");
 628     }
 629 
 630     for (; gIter != NULL; gIter = gIter->next) {
 631         gboolean print_full = FALSE;
 632         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 633         gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
 634 
 635         if (options & pe_print_clone_details) {
 636             print_full = TRUE;
 637         }
 638 
 639         if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 640             // Print individual instance when unique (except stopped orphans)
 641             if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 642                 print_full = TRUE;
 643             }
 644 
 645         // Everything else in this block is for anonymous clones
 646 
 647         } else if (pcmk_is_set(options, pe_print_pending)
 648                    && (child_rsc->pending_task != NULL)
 649                    && strcmp(child_rsc->pending_task, "probe")) {
 650             // Print individual instance when non-probe action is pending
 651             print_full = TRUE;
 652 
 653         } else if (partially_active == FALSE) {
 654             // List stopped instances when requested (except orphans)
 655             if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
 656                 && !pcmk_is_set(options, pe_print_clone_active)) {
 657 
 658                 pcmk__add_word(&stopped_list, 1024, child_rsc->id);
 659             }
 660 
 661         } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
 662                    || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
 663                    || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
 664 
 665             // Print individual instance when active orphaned/unmanaged/failed
 666             print_full = TRUE;
 667 
 668         } else if (child_rsc->fns->active(child_rsc, TRUE)) {
 669             // Instance of fully active anonymous clone
 670 
 671             pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
 672 
 673             if (location) {
 674                 // Instance is active on a single node
 675 
 676                 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
 677 
 678                 if (location->details->online == FALSE && location->details->unclean) {
 679                     print_full = TRUE;
 680 
 681                 } else if (a_role > RSC_ROLE_UNPROMOTED) {
 682                     promoted_list = g_list_append(promoted_list, location);
 683 
 684                 } else {
 685                     started_list = g_list_append(started_list, location);
 686                 }
 687 
 688             } else {
 689                 /* uncolocated group - bleh */
 690                 print_full = TRUE;
 691             }
 692 
 693         } else {
 694             // Instance of partially active anonymous clone
 695             print_full = TRUE;
 696         }
 697 
 698         if (print_full) {
 699             if (options & pe_print_html) {
 700                 status_print("<li>\n");
 701             }
 702             child_rsc->fns->print(child_rsc, child_text, options, print_data);
 703             if (options & pe_print_html) {
 704                 status_print("</li>\n");
 705             }
 706         }
 707     }
 708 
 709     /* Promoted */
 710     promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
 711     for (gIter = promoted_list; gIter; gIter = gIter->next) {
 712         pe_node_t *host = gIter->data;
 713 
 714         pcmk__add_word(&list_text, 1024, host->details->uname);
 715         active_instances++;
 716     }
 717 
 718     if (list_text != NULL) {
 719         short_print((const char *) list_text->str, child_text,
 720                     PROMOTED_INSTANCES, NULL, options, print_data);
 721         g_string_truncate(list_text, 0);
 722     }
 723     g_list_free(promoted_list);
 724 
 725     /* Started/Unpromoted */
 726     started_list = g_list_sort(started_list, pe__cmp_node_name);
 727     for (gIter = started_list; gIter; gIter = gIter->next) {
 728         pe_node_t *host = gIter->data;
 729 
 730         pcmk__add_word(&list_text, 1024, host->details->uname);
 731         active_instances++;
 732     }
 733 
 734     if (list_text != NULL) {
 735         if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 736             enum rsc_role_e role = configured_role(rsc);
 737 
 738             if (role == RSC_ROLE_UNPROMOTED) {
 739                 short_print((const char *) list_text->str, child_text,
 740                             UNPROMOTED_INSTANCES " (target-role)", NULL,
 741                             options, print_data);
 742             } else {
 743                 short_print((const char *) list_text->str, child_text,
 744                             UNPROMOTED_INSTANCES, NULL, options, print_data);
 745             }
 746 
 747         } else {
 748             short_print((const char *) list_text->str, child_text, "Started",
 749                         NULL, options, print_data);
 750         }
 751     }
 752 
 753     g_list_free(started_list);
 754 
 755     if (!pcmk_is_set(options, pe_print_clone_active)) {
 756         const char *state = "Stopped";
 757         enum rsc_role_e role = configured_role(rsc);
 758 
 759         if (role == RSC_ROLE_STOPPED) {
 760             state = "Stopped (disabled)";
 761         }
 762 
 763         if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
 764             && (clone_data->clone_max > active_instances)) {
 765 
 766             GList *nIter;
 767             GList *list = g_hash_table_get_values(rsc->allowed_nodes);
 768 
 769             /* Custom stopped list for non-unique clones */
 770             if (stopped_list != NULL) {
 771                 g_string_truncate(stopped_list, 0);
 772             }
 773 
 774             if (list == NULL) {
 775                 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
 776                  * If we've not probed for them yet, the Stopped list will be empty
 777                  */
 778                 list = g_hash_table_get_values(rsc->known_on);
 779             }
 780 
 781             list = g_list_sort(list, pe__cmp_node_name);
 782             for (nIter = list; nIter != NULL; nIter = nIter->next) {
 783                 pe_node_t *node = (pe_node_t *)nIter->data;
 784 
 785                 if (pe_find_node(rsc->running_on, node->details->uname) == NULL) {
 786                     pcmk__add_word(&stopped_list, 1024, node->details->uname);
 787                 }
 788             }
 789             g_list_free(list);
 790         }
 791 
 792         if (stopped_list != NULL) {
 793             short_print((const char *) stopped_list->str, child_text, state,
 794                         NULL, options, print_data);
 795         }
 796     }
 797 
 798     if (options & pe_print_html) {
 799         status_print("</ul>\n");
 800     }
 801 
 802     if (list_text != NULL) {
 803         g_string_free(list_text, TRUE);
 804     }
 805 
 806     if (stopped_list != NULL) {
 807         g_string_free(stopped_list, TRUE);
 808     }
 809     free(child_text);
 810 }
 811 
 812 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 813 int
 814 pe__clone_xml(pcmk__output_t *out, va_list args)
 815 {
 816     uint32_t show_opts = va_arg(args, uint32_t);
 817     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 818     GList *only_node = va_arg(args, GList *);
 819     GList *only_rsc = va_arg(args, GList *);
 820 
 821 
 822     const char *desc = NULL;
 823     GList *gIter = rsc->children;
 824     GList *all = NULL;
 825     int rc = pcmk_rc_no_output;
 826     gboolean printed_header = FALSE;
 827     gboolean print_everything = TRUE;
 828 
 829     
 830 
 831     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 832         return rc;
 833     }
 834 
 835     print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
 836                        (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
 837 
 838     all = g_list_prepend(all, (gpointer) "*");
 839 
 840     for (; gIter != NULL; gIter = gIter->next) {
 841         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 842 
 843         if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
 844             continue;
 845         }
 846 
 847         if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
 848             continue;
 849         }
 850 
 851         if (!printed_header) {
 852             printed_header = TRUE;
 853 
 854             desc = pe__resource_description(rsc, show_opts);
 855             
 856             rc = pe__name_and_nvpairs_xml(out, true, "clone", 10,
 857                     "id", rsc->id,
 858                     "multi_state", pe__rsc_bool_str(rsc, pe_rsc_promotable),
 859                     "unique", pe__rsc_bool_str(rsc, pe_rsc_unique),
 860                     "maintenance", pe__rsc_bool_str(rsc, pe_rsc_maintenance),
 861                     "managed", pe__rsc_bool_str(rsc, pe_rsc_managed),
 862                     "disabled", pcmk__btoa(pe__resource_is_disabled(rsc)),
 863                     "failed", pe__rsc_bool_str(rsc, pe_rsc_failed),
 864                     "failure_ignored", pe__rsc_bool_str(rsc, pe_rsc_failure_ignored),
 865                     "target_role", configured_role_str(rsc),
 866                     "description", desc);
 867             CRM_ASSERT(rc == pcmk_rc_ok);
 868         }
 869 
 870         out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
 871                      child_rsc, only_node, all);
 872     }
 873 
 874     if (printed_header) {
 875         pcmk__output_xml_pop_parent(out);
 876     }
 877 
 878     g_list_free(all);
 879     return rc;
 880 }
 881 
 882 PCMK__OUTPUT_ARGS("clone", "uint32_t", "pe_resource_t *", "GList *", "GList *")
     /* [previous][next][first][last][top][bottom][index][help] */
 883 int
 884 pe__clone_default(pcmk__output_t *out, va_list args)
 885 {
 886     uint32_t show_opts = va_arg(args, uint32_t);
 887     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
 888     GList *only_node = va_arg(args, GList *);
 889     GList *only_rsc = va_arg(args, GList *);
 890 
 891     GHashTable *stopped = NULL;
 892 
 893     GString *list_text = NULL;
 894 
 895     GList *promoted_list = NULL;
 896     GList *started_list = NULL;
 897     GList *gIter = rsc->children;
 898 
 899     const char *desc = NULL;
 900 
 901     clone_variant_data_t *clone_data = NULL;
 902     int active_instances = 0;
 903     int rc = pcmk_rc_no_output;
 904     gboolean print_everything = TRUE;
 905 
 906     desc = pe__resource_description(rsc, show_opts);
 907 
 908     get_clone_variant_data(clone_data, rsc);
 909 
 910     if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 911         return rc;
 912     }
 913 
 914     print_everything = pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) ||
 915                        (strstr(rsc->id, ":") != NULL && pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches));
 916 
 917     for (; gIter != NULL; gIter = gIter->next) {
 918         gboolean print_full = FALSE;
 919         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 920         gboolean partially_active = child_rsc->fns->active(child_rsc, FALSE);
 921 
 922         if (pcmk__rsc_filtered_by_node(child_rsc, only_node)) {
 923             continue;
 924         }
 925 
 926         if (child_rsc->fns->is_filtered(child_rsc, only_rsc, print_everything)) {
 927             continue;
 928         }
 929 
 930         if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
 931             print_full = TRUE;
 932         }
 933 
 934         if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 935             // Print individual instance when unique (except stopped orphans)
 936             if (partially_active || !pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
 937                 print_full = TRUE;
 938             }
 939 
 940         // Everything else in this block is for anonymous clones
 941 
 942         } else if (pcmk_is_set(show_opts, pcmk_show_pending)
 943                    && (child_rsc->pending_task != NULL)
 944                    && strcmp(child_rsc->pending_task, "probe")) {
 945             // Print individual instance when non-probe action is pending
 946             print_full = TRUE;
 947 
 948         } else if (partially_active == FALSE) {
 949             // List stopped instances when requested (except orphans)
 950             if (!pcmk_is_set(child_rsc->flags, pe_rsc_orphan)
 951                 && !pcmk_is_set(show_opts, pcmk_show_clone_detail)
 952                 && pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
 953                 if (stopped == NULL) {
 954                     stopped = pcmk__strkey_table(free, free);
 955                 }
 956                 g_hash_table_insert(stopped, strdup(child_rsc->id), strdup("Stopped"));
 957             }
 958 
 959         } else if (is_set_recursive(child_rsc, pe_rsc_orphan, TRUE)
 960                    || is_set_recursive(child_rsc, pe_rsc_managed, FALSE) == FALSE
 961                    || is_set_recursive(child_rsc, pe_rsc_failed, TRUE)) {
 962 
 963             // Print individual instance when active orphaned/unmanaged/failed
 964             print_full = TRUE;
 965 
 966         } else if (child_rsc->fns->active(child_rsc, TRUE)) {
 967             // Instance of fully active anonymous clone
 968 
 969             pe_node_t *location = child_rsc->fns->location(child_rsc, NULL, TRUE);
 970 
 971             if (location) {
 972                 // Instance is active on a single node
 973 
 974                 enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, TRUE);
 975 
 976                 if (location->details->online == FALSE && location->details->unclean) {
 977                     print_full = TRUE;
 978 
 979                 } else if (a_role > RSC_ROLE_UNPROMOTED) {
 980                     promoted_list = g_list_append(promoted_list, location);
 981 
 982                 } else {
 983                     started_list = g_list_append(started_list, location);
 984                 }
 985 
 986             } else {
 987                 /* uncolocated group - bleh */
 988                 print_full = TRUE;
 989             }
 990 
 991         } else {
 992             // Instance of partially active anonymous clone
 993             print_full = TRUE;
 994         }
 995 
 996         if (print_full) {
 997             GList *all = NULL;
 998 
 999             clone_header(out, &rc, rsc, clone_data, desc);
1000 
1001             /* Print every resource that's a child of this clone. */
1002             all = g_list_prepend(all, (gpointer) "*");
1003             out->message(out, crm_map_element_name(child_rsc->xml), show_opts,
1004                          child_rsc, only_node, all);
1005             g_list_free(all);
1006         }
1007     }
1008 
1009     if (pcmk_is_set(show_opts, pcmk_show_clone_detail)) {
1010         PCMK__OUTPUT_LIST_FOOTER(out, rc);
1011         return pcmk_rc_ok;
1012     }
1013 
1014     /* Promoted */
1015     promoted_list = g_list_sort(promoted_list, pe__cmp_node_name);
1016     for (gIter = promoted_list; gIter; gIter = gIter->next) {
1017         pe_node_t *host = gIter->data;
1018 
1019         if (!pcmk__str_in_list(host->details->uname, only_node,
1020                                pcmk__str_star_matches|pcmk__str_casei)) {
1021             continue;
1022         }
1023 
1024         pcmk__add_word(&list_text, 1024, host->details->uname);
1025         active_instances++;
1026     }
1027     g_list_free(promoted_list);
1028 
1029     if ((list_text != NULL) && (list_text->len > 0)) {
1030         clone_header(out, &rc, rsc, clone_data, desc);
1031 
1032         out->list_item(out, NULL, PROMOTED_INSTANCES ": [ %s ]",
1033                        (const char *) list_text->str);
1034         g_string_truncate(list_text, 0);
1035     }
1036 
1037     /* Started/Unpromoted */
1038     started_list = g_list_sort(started_list, pe__cmp_node_name);
1039     for (gIter = started_list; gIter; gIter = gIter->next) {
1040         pe_node_t *host = gIter->data;
1041 
1042         if (!pcmk__str_in_list(host->details->uname, only_node,
1043                                pcmk__str_star_matches|pcmk__str_casei)) {
1044             continue;
1045         }
1046 
1047         pcmk__add_word(&list_text, 1024, host->details->uname);
1048         active_instances++;
1049     }
1050     g_list_free(started_list);
1051 
1052     if ((list_text != NULL) && (list_text->len > 0)) {
1053         clone_header(out, &rc, rsc, clone_data, desc);
1054 
1055         if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
1056             enum rsc_role_e role = configured_role(rsc);
1057 
1058             if (role == RSC_ROLE_UNPROMOTED) {
1059                 out->list_item(out, NULL,
1060                                UNPROMOTED_INSTANCES " (target-role): [ %s ]",
1061                                (const char *) list_text->str);
1062             } else {
1063                 out->list_item(out, NULL, UNPROMOTED_INSTANCES ": [ %s ]",
1064                                (const char *) list_text->str);
1065             }
1066 
1067         } else {
1068             out->list_item(out, NULL, "Started: [ %s ]",
1069                            (const char *) list_text->str);
1070         }
1071     }
1072 
1073     if (list_text != NULL) {
1074         g_string_free(list_text, TRUE);
1075     }
1076 
1077     if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
1078         if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
1079             && (clone_data->clone_max > active_instances)) {
1080 
1081             GList *nIter;
1082             GList *list = g_hash_table_get_values(rsc->allowed_nodes);
1083 
1084             /* Custom stopped table for non-unique clones */
1085             if (stopped != NULL) {
1086                 g_hash_table_destroy(stopped);
1087                 stopped = NULL;
1088             }
1089 
1090             if (list == NULL) {
1091                 /* Clusters with symmetrical=false haven't calculated allowed_nodes yet
1092                  * If we've not probed for them yet, the Stopped list will be empty
1093                  */
1094                 list = g_hash_table_get_values(rsc->known_on);
1095             }
1096 
1097             list = g_list_sort(list, pe__cmp_node_name);
1098             for (nIter = list; nIter != NULL; nIter = nIter->next) {
1099                 pe_node_t *node = (pe_node_t *)nIter->data;
1100 
1101                 if (pe_find_node(rsc->running_on, node->details->uname) == NULL &&
1102                     pcmk__str_in_list(node->details->uname, only_node,
1103                                       pcmk__str_star_matches|pcmk__str_casei)) {
1104                     xmlNode *probe_op = pe__failed_probe_for_rsc(rsc, node->details->uname);
1105                     const char *state = "Stopped";
1106 
1107                     if (configured_role(rsc) == RSC_ROLE_STOPPED) {
1108                         state = "Stopped (disabled)";
1109                     }
1110 
1111                     if (stopped == NULL) {
1112                         stopped = pcmk__strkey_table(free, free);
1113                     }
1114                     if (probe_op != NULL) {
1115                         int rc;
1116 
1117                         pcmk__scan_min_int(crm_element_value(probe_op, XML_LRM_ATTR_RC), &rc, 0);
1118                         g_hash_table_insert(stopped, strdup(node->details->uname),
1119                                             crm_strdup_printf("Stopped (%s)", services_ocf_exitcode_str(rc)));
1120                     } else {
1121                         g_hash_table_insert(stopped, strdup(node->details->uname),
1122                                             strdup(state));
1123                     }
1124                 }
1125             }
1126             g_list_free(list);
1127         }
1128 
1129         if (stopped != NULL) {
1130             GList *list = sorted_hash_table_values(stopped);
1131 
1132             clone_header(out, &rc, rsc, clone_data, desc);
1133 
1134             for (GList *status_iter = list; status_iter != NULL; status_iter = status_iter->next) {
1135                 const char *status = status_iter->data;
1136                 GList *nodes = nodes_with_status(stopped, status);
1137                 GString *nodes_str = node_list_to_str(nodes);
1138 
1139                 if (nodes_str != NULL) {
1140                     if (nodes_str->len > 0) {
1141                         out->list_item(out, NULL, "%s: [ %s ]", status,
1142                                        (const char *) nodes_str->str);
1143                     }
1144                     g_string_free(nodes_str, TRUE);
1145                 }
1146 
1147                 g_list_free(nodes);
1148             }
1149 
1150             g_list_free(list);
1151             g_hash_table_destroy(stopped);
1152 
1153         /* If there are no instances of this clone (perhaps because there are no
1154          * nodes configured), simply output the clone header by itself.  This can
1155          * come up in PCS testing.
1156          */
1157         } else if (active_instances == 0) {
1158             clone_header(out, &rc, rsc, clone_data, desc);
1159             PCMK__OUTPUT_LIST_FOOTER(out, rc);
1160             return rc;
1161         }
1162     }
1163 
1164     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1165     return rc;
1166 }
1167 
1168 void
1169 clone_free(pe_resource_t * rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1170 {
1171     clone_variant_data_t *clone_data = NULL;
1172 
1173     get_clone_variant_data(clone_data, rsc);
1174 
1175     pe_rsc_trace(rsc, "Freeing %s", rsc->id);
1176 
1177     for (GList *gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
1178         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1179 
1180         CRM_ASSERT(child_rsc);
1181         pe_rsc_trace(child_rsc, "Freeing child %s", child_rsc->id);
1182         free_xml(child_rsc->xml);
1183         child_rsc->xml = NULL;
1184         /* There could be a saved unexpanded xml */
1185         free_xml(child_rsc->orig_xml);
1186         child_rsc->orig_xml = NULL;
1187         child_rsc->fns->free(child_rsc);
1188     }
1189 
1190     g_list_free(rsc->children);
1191 
1192     if (clone_data) {
1193         CRM_ASSERT(clone_data->demote_notify == NULL);
1194         CRM_ASSERT(clone_data->stop_notify == NULL);
1195         CRM_ASSERT(clone_data->start_notify == NULL);
1196         CRM_ASSERT(clone_data->promote_notify == NULL);
1197     }
1198 
1199     common_free(rsc);
1200 }
1201 
1202 enum rsc_role_e
1203 clone_resource_state(const pe_resource_t * rsc, gboolean current)
     /* [previous][next][first][last][top][bottom][index][help] */
1204 {
1205     enum rsc_role_e clone_role = RSC_ROLE_UNKNOWN;
1206     GList *gIter = rsc->children;
1207 
1208     for (; gIter != NULL; gIter = gIter->next) {
1209         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1210         enum rsc_role_e a_role = child_rsc->fns->state(child_rsc, current);
1211 
1212         if (a_role > clone_role) {
1213             clone_role = a_role;
1214         }
1215     }
1216 
1217     pe_rsc_trace(rsc, "%s role: %s", rsc->id, role2text(clone_role));
1218     return clone_role;
1219 }
1220 
1221 /*!
1222  * \internal
1223  * \brief Check whether a clone has an instance for every node
1224  *
1225  * \param[in] rsc       Clone to check
1226  * \param[in] data_set  Cluster state
1227  */
1228 bool
1229 pe__is_universal_clone(const pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1230                        const pe_working_set_t *data_set)
1231 {
1232     if (pe_rsc_is_clone(rsc)) {
1233         clone_variant_data_t *clone_data = rsc->variant_opaque;
1234 
1235         if (clone_data->clone_max == g_list_length(data_set->nodes)) {
1236             return TRUE;
1237         }
1238     }
1239     return FALSE;
1240 }
1241 
1242 gboolean
1243 pe__clone_is_filtered(const pe_resource_t *rsc, GList *only_rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1244                       gboolean check_parent)
1245 {
1246     gboolean passes = FALSE;
1247     clone_variant_data_t *clone_data = NULL;
1248 
1249     if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches)) {
1250         passes = TRUE;
1251     } else {
1252         get_clone_variant_data(clone_data, rsc);
1253         passes = pcmk__str_in_list(ID(clone_data->xml_obj_child), only_rsc, pcmk__str_star_matches);
1254 
1255         if (!passes) {
1256             for (const GList *iter = rsc->children;
1257                  iter != NULL; iter = iter->next) {
1258 
1259                 const pe_resource_t *child_rsc = NULL;
1260 
1261                 child_rsc = (const pe_resource_t *) iter->data;
1262                 if (!child_rsc->fns->is_filtered(child_rsc, only_rsc, FALSE)) {
1263                     passes = TRUE;
1264                     break;
1265                 }
1266             }
1267         }
1268     }
1269     return !passes;
1270 }
1271 
1272 const char *
1273 pe__clone_child_id(const pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1274 {
1275     clone_variant_data_t *clone_data = NULL;
1276     get_clone_variant_data(clone_data, rsc);
1277     return ID(clone_data->xml_obj_child);
1278 }
1279 
1280 /*!
1281  * \internal
1282  * \brief Check whether a clone is ordered
1283  *
1284  * \param[in] clone  Clone resource to check
1285  *
1286  * \return true if clone is ordered, otherwise false
1287  */
1288 bool
1289 pe__clone_is_ordered(const pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
1290 {
1291     clone_variant_data_t *clone_data = NULL;
1292 
1293     get_clone_variant_data(clone_data, clone);
1294     return pcmk_is_set(clone_data->flags, pe__clone_ordered);
1295 }
1296 
1297 /*!
1298  * \internal
1299  * \brief Set a clone flag
1300  *
1301  * \param[in,out] clone  Clone resource to set flag for
1302  * \param[in]     flag   Clone flag to set
1303  *
1304  * \return Standard Pacemaker return code (either pcmk_rc_ok if flag was not
1305  *         already set or pcmk_rc_already if it was)
1306  */
1307 int
1308 pe__set_clone_flag(pe_resource_t *clone, enum pe__clone_flags flag)
     /* [previous][next][first][last][top][bottom][index][help] */
1309 {
1310     clone_variant_data_t *clone_data = NULL;
1311 
1312     get_clone_variant_data(clone_data, clone);
1313     if (pcmk_is_set(clone_data->flags, flag)) {
1314         return pcmk_rc_already;
1315     }
1316     clone_data->flags = pcmk__set_flags_as(__func__, __LINE__, LOG_TRACE,
1317                                            "Clone", clone->id,
1318                                            clone_data->flags, flag, "flag");
1319     return pcmk_rc_ok;
1320 }
1321 
1322 /*!
1323  * \internal
1324  * \brief Create pseudo-actions needed for promotable clones
1325  *
1326  * \param[in,out] clone          Promotable clone to create actions for
1327  * \param[in]     any_promoting  Whether any instances will be promoted
1328  * \param[in]     any_demoting   Whether any instance will be demoted
1329  */
1330 void
1331 pe__create_promotable_pseudo_ops(pe_resource_t *clone, bool any_promoting,
     /* [previous][next][first][last][top][bottom][index][help] */
1332                                  bool any_demoting)
1333 {
1334     pe_action_t *action = NULL;
1335     pe_action_t *action_complete = NULL;
1336     clone_variant_data_t *clone_data = NULL;
1337 
1338     get_clone_variant_data(clone_data, clone);
1339 
1340     // Create a "promote" action for the clone itself
1341     action = pe__new_rsc_pseudo_action(clone, RSC_PROMOTE, !any_promoting,
1342                                        true);
1343 
1344     // Create a "promoted" action for when all promotions are done
1345     action_complete = pe__new_rsc_pseudo_action(clone, RSC_PROMOTED,
1346                                                 !any_promoting, true);
1347     action_complete->priority = INFINITY;
1348 
1349     // Create notification pseudo-actions for promotion
1350     if (clone_data->promote_notify == NULL) {
1351         clone_data->promote_notify = pe__action_notif_pseudo_ops(clone,
1352                                                                  RSC_PROMOTE,
1353                                                                  action,
1354                                                                  action_complete);
1355     }
1356 
1357     // Create a "demote" action for the clone itself
1358     action = pe__new_rsc_pseudo_action(clone, RSC_DEMOTE, !any_demoting, true);
1359 
1360     // Create a "demoted" action for when all demotions are done
1361     action_complete = pe__new_rsc_pseudo_action(clone, RSC_DEMOTED,
1362                                                 !any_demoting, true);
1363     action_complete->priority = INFINITY;
1364 
1365     // Create notification pseudo-actions for demotion
1366     if (clone_data->demote_notify == NULL) {
1367         clone_data->demote_notify = pe__action_notif_pseudo_ops(clone,
1368                                                                 RSC_DEMOTE,
1369                                                                 action,
1370                                                                 action_complete);
1371 
1372         if (clone_data->promote_notify != NULL) {
1373             order_actions(clone_data->stop_notify->post_done,
1374                           clone_data->promote_notify->pre,
1375                           pe_order_optional);
1376             order_actions(clone_data->start_notify->post_done,
1377                           clone_data->promote_notify->pre,
1378                           pe_order_optional);
1379             order_actions(clone_data->demote_notify->post_done,
1380                           clone_data->promote_notify->pre,
1381                           pe_order_optional);
1382             order_actions(clone_data->demote_notify->post_done,
1383                           clone_data->start_notify->pre,
1384                           pe_order_optional);
1385             order_actions(clone_data->demote_notify->post_done,
1386                           clone_data->stop_notify->pre,
1387                           pe_order_optional);
1388         }
1389     }
1390 }
1391 
1392 /*!
1393  * \internal
1394  * \brief Create all notification data and actions for a clone
1395  *
1396  * \param[in,out] clone  Clone to create notifications for
1397  */
1398 void
1399 pe__create_clone_notifications(pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
1400 {
1401     clone_variant_data_t *clone_data = NULL;
1402 
1403     get_clone_variant_data(clone_data, clone);
1404 
1405     pe__create_action_notifications(clone, clone_data->start_notify);
1406     pe__create_action_notifications(clone, clone_data->stop_notify);
1407     pe__create_action_notifications(clone, clone_data->promote_notify);
1408     pe__create_action_notifications(clone, clone_data->demote_notify);
1409 }
1410 
1411 /*!
1412  * \internal
1413  * \brief Free all notification data for a clone
1414  *
1415  * \param[in,out] clone  Clone to free notification data for
1416  */
1417 void
1418 pe__free_clone_notification_data(pe_resource_t *clone)
     /* [previous][next][first][last][top][bottom][index][help] */
1419 {
1420     clone_variant_data_t *clone_data = NULL;
1421 
1422     get_clone_variant_data(clone_data, clone);
1423 
1424     pe__free_action_notification_data(clone_data->demote_notify);
1425     clone_data->demote_notify = NULL;
1426 
1427     pe__free_action_notification_data(clone_data->stop_notify);
1428     clone_data->stop_notify = NULL;
1429 
1430     pe__free_action_notification_data(clone_data->start_notify);
1431     clone_data->start_notify = NULL;
1432 
1433     pe__free_action_notification_data(clone_data->promote_notify);
1434     clone_data->promote_notify = NULL;
1435 }
1436 
1437 /*!
1438  * \internal
1439  * \brief Create pseudo-actions for clone start/stop notifications
1440  *
1441  * \param[in,out] clone    Clone to create pseudo-actions for
1442  * \param[in,out] start    Start action for \p clone
1443  * \param[in,out] stop     Stop action for \p clone
1444  * \param[in,out] started  Started action for \p clone
1445  * \param[in,out] stopped  Stopped action for \p clone
1446  */
1447 void
1448 pe__create_clone_notif_pseudo_ops(pe_resource_t *clone,
     /* [previous][next][first][last][top][bottom][index][help] */
1449                                   pe_action_t *start, pe_action_t *started,
1450                                   pe_action_t *stop, pe_action_t *stopped)
1451 {
1452     clone_variant_data_t *clone_data = NULL;
1453 
1454     get_clone_variant_data(clone_data, clone);
1455 
1456     if (clone_data->start_notify == NULL) {
1457         clone_data->start_notify = pe__action_notif_pseudo_ops(clone, RSC_START,
1458                                                                start, started);
1459     }
1460 
1461     if (clone_data->stop_notify == NULL) {
1462         clone_data->stop_notify = pe__action_notif_pseudo_ops(clone, RSC_STOP,
1463                                                               stop, stopped);
1464         if ((clone_data->start_notify != NULL)
1465             && (clone_data->stop_notify != NULL)) {
1466             order_actions(clone_data->stop_notify->post_done,
1467                           clone_data->start_notify->pre, pe_order_optional);
1468         }
1469     }
1470 }

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