LCOV - code coverage report
Current view: top level - pengine - clone.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 682 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 35 0.0 %

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

Generated by: LCOV version 1.14