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. unpack_meta_int
  13. clone_unpack
  14. clone_active
  15. short_print
  16. configured_role_str
  17. configured_role
  18. clone_print_xml
  19. is_set_recursive
  20. clone_print
  21. PCMK__OUTPUT_ARGS
  22. PCMK__OUTPUT_ARGS
  23. clone_free
  24. clone_resource_state
  25. pe__is_universal_clone
  26. pe__clone_is_filtered
  27. pe__clone_child_id
  28. pe__clone_is_ordered
  29. pe__set_clone_flag
  30. pe__clone_flag_is_set
  31. pe__create_promotable_pseudo_ops
  32. pe__create_clone_notifications
  33. pe__free_clone_notification_data
  34. pe__create_clone_notif_pseudo_ops
  35. pe__clone_max_per_node

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

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