root/lib/pengine/clone.c

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

DEFINITIONS

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

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

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