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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2019-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/common/xml_internal.h>
      15             : #include <crm/common/output.h>
      16             : #include <crm/common/scheduler_internal.h>
      17             : #include <crm/cib/util.h>
      18             : #include <crm/common/xml.h>
      19             : #include <crm/pengine/internal.h>
      20             : 
      21             : const char *
      22           0 : pe__resource_description(const pcmk_resource_t *rsc, uint32_t show_opts)
      23             : {
      24           0 :     const char * desc = NULL;
      25             :     // User-supplied description
      26           0 :     if (pcmk_any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)) {
      27           0 :         desc = crm_element_value(rsc->xml, PCMK_XA_DESCRIPTION);
      28             :     }
      29           0 :     return desc;
      30             : }
      31             : 
      32             : /* Never display node attributes whose name starts with one of these prefixes */
      33             : #define FILTER_STR { PCMK__FAIL_COUNT_PREFIX, PCMK__LAST_FAILURE_PREFIX,    \
      34             :                      PCMK__NODE_ATTR_SHUTDOWN, PCMK_NODE_ATTR_TERMINATE,    \
      35             :                      PCMK_NODE_ATTR_STANDBY, "#", NULL }
      36             : 
      37             : static int
      38           0 : compare_attribute(gconstpointer a, gconstpointer b)
      39             : {
      40             :     int rc;
      41             : 
      42           0 :     rc = strcmp((const char *)a, (const char *)b);
      43             : 
      44           0 :     return rc;
      45             : }
      46             : 
      47             : /*!
      48             :  * \internal
      49             :  * \brief Determine whether extended information about an attribute should be added.
      50             :  *
      51             :  * \param[in]     node            Node that ran this resource
      52             :  * \param[in,out] rsc_list        List of resources for this node
      53             :  * \param[in,out] scheduler       Scheduler data
      54             :  * \param[in]     attrname        Attribute to find
      55             :  * \param[out]    expected_score  Expected value for this attribute
      56             :  *
      57             :  * \return true if extended information should be printed, false otherwise
      58             :  * \note Currently, extended information is only supported for ping/pingd
      59             :  *       resources, for which a message will be printed if connectivity is lost
      60             :  *       or degraded.
      61             :  */
      62             : static bool
      63           0 : add_extra_info(const pcmk_node_t *node, GList *rsc_list,
      64             :                pcmk_scheduler_t *scheduler, const char *attrname,
      65             :                int *expected_score)
      66             : {
      67           0 :     GList *gIter = NULL;
      68             : 
      69           0 :     for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
      70           0 :         pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
      71           0 :         const char *type = g_hash_table_lookup(rsc->meta, PCMK_XA_TYPE);
      72           0 :         const char *name = NULL;
      73           0 :         GHashTable *params = NULL;
      74             : 
      75           0 :         if (rsc->children != NULL) {
      76           0 :             if (add_extra_info(node, rsc->children, scheduler, attrname,
      77             :                                expected_score)) {
      78           0 :                 return true;
      79             :             }
      80             :         }
      81             : 
      82           0 :         if (!pcmk__strcase_any_of(type, "ping", "pingd", NULL)) {
      83           0 :             continue;
      84             :         }
      85             : 
      86           0 :         params = pe_rsc_params(rsc, node, scheduler);
      87           0 :         name = g_hash_table_lookup(params, PCMK_XA_NAME);
      88             : 
      89           0 :         if (name == NULL) {
      90           0 :             name = "pingd";
      91             :         }
      92             : 
      93             :         /* To identify the resource with the attribute name. */
      94           0 :         if (pcmk__str_eq(name, attrname, pcmk__str_casei)) {
      95           0 :             int host_list_num = 0;
      96           0 :             const char *hosts = g_hash_table_lookup(params, "host_list");
      97           0 :             const char *multiplier = g_hash_table_lookup(params, "multiplier");
      98             :             int multiplier_i;
      99             : 
     100           0 :             if (hosts) {
     101           0 :                 char **host_list = g_strsplit(hosts, " ", 0);
     102           0 :                 host_list_num = g_strv_length(host_list);
     103           0 :                 g_strfreev(host_list);
     104             :             }
     105             : 
     106           0 :             if ((multiplier == NULL)
     107           0 :                 || (pcmk__scan_min_int(multiplier, &multiplier_i,
     108             :                                        INT_MIN) != pcmk_rc_ok)) {
     109             :                 /* The ocf:pacemaker:ping resource agent defaults multiplier to
     110             :                  * 1. The agent currently does not handle invalid text, but it
     111             :                  * should, and this would be a reasonable choice ...
     112             :                  */
     113           0 :                 multiplier_i = 1;
     114             :             }
     115           0 :             *expected_score = host_list_num * multiplier_i;
     116             : 
     117           0 :             return true;
     118             :         }
     119             :     }
     120           0 :     return false;
     121             : }
     122             : 
     123             : static GList *
     124           0 : filter_attr_list(GList *attr_list, char *name)
     125             : {
     126             :     int i;
     127           0 :     const char *filt_str[] = FILTER_STR;
     128             : 
     129           0 :     CRM_CHECK(name != NULL, return attr_list);
     130             : 
     131             :     /* filtering automatic attributes */
     132           0 :     for (i = 0; filt_str[i] != NULL; i++) {
     133           0 :         if (g_str_has_prefix(name, filt_str[i])) {
     134           0 :             return attr_list;
     135             :         }
     136             :     }
     137             : 
     138           0 :     return g_list_insert_sorted(attr_list, name, compare_attribute);
     139             : }
     140             : 
     141             : static GList *
     142           0 : get_operation_list(xmlNode *rsc_entry) {
     143           0 :     GList *op_list = NULL;
     144           0 :     xmlNode *rsc_op = NULL;
     145             : 
     146           0 :     for (rsc_op = pcmk__xe_first_child(rsc_entry, NULL, NULL, NULL);
     147           0 :          rsc_op != NULL; rsc_op = pcmk__xe_next(rsc_op)) {
     148             : 
     149           0 :         const char *task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
     150           0 :         const char *interval_ms_s = crm_element_value(rsc_op,
     151             :                                                       PCMK_META_INTERVAL);
     152           0 :         const char *op_rc = crm_element_value(rsc_op, PCMK__XA_RC_CODE);
     153             :         int op_rc_i;
     154             : 
     155           0 :         pcmk__scan_min_int(op_rc, &op_rc_i, 0);
     156             : 
     157             :         /* Display 0-interval monitors as "probe" */
     158           0 :         if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)
     159           0 :             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
     160           0 :             task = "probe";
     161             :         }
     162             : 
     163             :         /* Ignore notifies and some probes */
     164           0 :         if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)
     165           0 :             || (pcmk__str_eq(task, "probe", pcmk__str_none)
     166           0 :                 && (op_rc_i == CRM_EX_NOT_RUNNING))) {
     167           0 :             continue;
     168             :         }
     169             : 
     170           0 :         if (pcmk__xe_is(rsc_op, PCMK__XE_LRM_RSC_OP)) {
     171           0 :             op_list = g_list_append(op_list, rsc_op);
     172             :         }
     173             :     }
     174             : 
     175           0 :     op_list = g_list_sort(op_list, sort_op_by_callid);
     176           0 :     return op_list;
     177             : }
     178             : 
     179             : static void
     180           0 : add_dump_node(gpointer key, gpointer value, gpointer user_data)
     181             : {
     182           0 :     xmlNodePtr node = user_data;
     183             : 
     184           0 :     node = pcmk__xe_create(node, (const char *) key);
     185           0 :     pcmk__xe_set_content(node, "%s", (const char *) value);
     186           0 : }
     187             : 
     188             : static void
     189           0 : append_dump_text(gpointer key, gpointer value, gpointer user_data)
     190             : {
     191           0 :     char **dump_text = user_data;
     192           0 :     char *new_text = crm_strdup_printf("%s %s=%s",
     193             :                                        *dump_text, (char *)key, (char *)value);
     194             : 
     195           0 :     free(*dump_text);
     196           0 :     *dump_text = new_text;
     197           0 : }
     198             : 
     199             : #define XPATH_STACK "//" PCMK_XE_NVPAIR     \
     200             :                     "[@" PCMK_XA_NAME "='"  \
     201             :                         PCMK_OPT_CLUSTER_INFRASTRUCTURE "']"
     202             : 
     203             : static const char *
     204           0 : get_cluster_stack(pcmk_scheduler_t *scheduler)
     205             : {
     206           0 :     xmlNode *stack = get_xpath_object(XPATH_STACK, scheduler->input, LOG_DEBUG);
     207             : 
     208           0 :     if (stack != NULL) {
     209           0 :         return crm_element_value(stack, PCMK_XA_VALUE);
     210             :     }
     211           0 :     return PCMK_VALUE_UNKNOWN;
     212             : }
     213             : 
     214             : static char *
     215           0 : last_changed_string(const char *last_written, const char *user,
     216             :                     const char *client, const char *origin) {
     217           0 :     if (last_written != NULL || user != NULL || client != NULL || origin != NULL) {
     218           0 :         return crm_strdup_printf("%s%s%s%s%s%s%s",
     219             :                                  last_written ? last_written : "",
     220             :                                  user ? " by " : "",
     221             :                                  user ? user : "",
     222             :                                  client ? " via " : "",
     223             :                                  client ? client : "",
     224             :                                  origin ? " on " : "",
     225             :                                  origin ? origin : "");
     226             :     } else {
     227           0 :         return strdup("");
     228             :     }
     229             : }
     230             : 
     231             : static char *
     232           0 : op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s,
     233             :                   int rc, bool print_timing) {
     234           0 :     const char *call = crm_element_value(xml_op, PCMK__XA_CALL_ID);
     235           0 :     char *interval_str = NULL;
     236           0 :     char *buf = NULL;
     237             : 
     238           0 :     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
     239           0 :         char *pair = pcmk__format_nvpair(PCMK_XA_INTERVAL, interval_ms_s, "ms");
     240           0 :         interval_str = crm_strdup_printf(" %s", pair);
     241           0 :         free(pair);
     242             :     }
     243             : 
     244           0 :     if (print_timing) {
     245           0 :         char *last_change_str = NULL;
     246           0 :         char *exec_str = NULL;
     247           0 :         char *queue_str = NULL;
     248             : 
     249           0 :         const char *value = NULL;
     250             : 
     251           0 :         time_t epoch = 0;
     252             : 
     253           0 :         if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
     254             :                                      &epoch) == pcmk_ok)
     255           0 :             && (epoch > 0)) {
     256           0 :             char *epoch_str = pcmk__epoch2str(&epoch, 0);
     257             : 
     258           0 :             last_change_str = crm_strdup_printf(" %s=\"%s\"",
     259             :                                                 PCMK_XA_LAST_RC_CHANGE,
     260             :                                                 pcmk__s(epoch_str, ""));
     261           0 :             free(epoch_str);
     262             :         }
     263             : 
     264           0 :         value = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
     265           0 :         if (value) {
     266           0 :             char *pair = pcmk__format_nvpair(PCMK_XA_EXEC_TIME, value, "ms");
     267           0 :             exec_str = crm_strdup_printf(" %s", pair);
     268           0 :             free(pair);
     269             :         }
     270             : 
     271           0 :         value = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
     272           0 :         if (value) {
     273           0 :             char *pair = pcmk__format_nvpair(PCMK_XA_QUEUE_TIME, value, "ms");
     274           0 :             queue_str = crm_strdup_printf(" %s", pair);
     275           0 :             free(pair);
     276             :         }
     277             : 
     278           0 :         buf = crm_strdup_printf("(%s) %s:%s%s%s%s rc=%d (%s)", call, task,
     279             :                                 interval_str ? interval_str : "",
     280             :                                 last_change_str ? last_change_str : "",
     281             :                                 exec_str ? exec_str : "",
     282             :                                 queue_str ? queue_str : "",
     283             :                                 rc, services_ocf_exitcode_str(rc));
     284             : 
     285           0 :         if (last_change_str) {
     286           0 :             free(last_change_str);
     287             :         }
     288             : 
     289           0 :         if (exec_str) {
     290           0 :             free(exec_str);
     291             :         }
     292             : 
     293           0 :         if (queue_str) {
     294           0 :             free(queue_str);
     295             :         }
     296             :     } else {
     297           0 :         buf = crm_strdup_printf("(%s) %s%s%s", call, task,
     298             :                                 interval_str ? ":" : "",
     299             :                                 interval_str ? interval_str : "");
     300             :     }
     301             : 
     302           0 :     if (interval_str) {
     303           0 :         free(interval_str);
     304             :     }
     305             : 
     306           0 :     return buf;
     307             : }
     308             : 
     309             : static char *
     310           0 : resource_history_string(pcmk_resource_t *rsc, const char *rsc_id, bool all,
     311             :                         int failcount, time_t last_failure) {
     312           0 :     char *buf = NULL;
     313             : 
     314           0 :     if (rsc == NULL) {
     315           0 :         buf = crm_strdup_printf("%s: orphan", rsc_id);
     316           0 :     } else if (all || failcount || last_failure > 0) {
     317           0 :         char *failcount_s = NULL;
     318           0 :         char *lastfail_s = NULL;
     319             : 
     320           0 :         if (failcount > 0) {
     321           0 :             failcount_s = crm_strdup_printf(" %s=%d",
     322             :                                             PCMK_XA_FAIL_COUNT, failcount);
     323             :         } else {
     324           0 :             failcount_s = strdup("");
     325             :         }
     326           0 :         if (last_failure > 0) {
     327           0 :             buf = pcmk__epoch2str(&last_failure, 0);
     328           0 :             lastfail_s = crm_strdup_printf(" %s='%s'",
     329             :                                            PCMK_XA_LAST_FAILURE, buf);
     330           0 :             free(buf);
     331             :         }
     332             : 
     333           0 :         buf = crm_strdup_printf("%s: " PCMK_META_MIGRATION_THRESHOLD "=%d%s%s",
     334             :                                 rsc_id, rsc->migration_threshold, failcount_s,
     335             :                                 lastfail_s? lastfail_s : "");
     336           0 :         free(failcount_s);
     337           0 :         free(lastfail_s);
     338             :     } else {
     339           0 :         buf = crm_strdup_printf("%s:", rsc_id);
     340             :     }
     341             : 
     342           0 :     return buf;
     343             : }
     344             : 
     345             : /*!
     346             :  * \internal
     347             :  * \brief Get a node's feature set for status display purposes
     348             :  *
     349             :  * \param[in] node  Node to check
     350             :  *
     351             :  * \return String representation of feature set if the node is fully up (using
     352             :  *         "<3.15.1" for older nodes that don't set the #feature-set attribute),
     353             :  *         otherwise NULL
     354             :  */
     355             : static const char *
     356           0 : get_node_feature_set(const pcmk_node_t *node)
     357             : {
     358           0 :     if (node->details->online && node->details->expected_up
     359           0 :         && !pcmk__is_pacemaker_remote_node(node)) {
     360             : 
     361           0 :         const char *feature_set = g_hash_table_lookup(node->details->attrs,
     362             :                                                       CRM_ATTR_FEATURE_SET);
     363             : 
     364             :         /* The feature set attribute is present since 3.15.1. If it is missing,
     365             :          * then the node must be running an earlier version.
     366             :          */
     367           0 :         return pcmk__s(feature_set, "<3.15.1");
     368             :     }
     369           0 :     return NULL;
     370             : }
     371             : 
     372             : static bool
     373           0 : is_mixed_version(pcmk_scheduler_t *scheduler)
     374             : {
     375           0 :     const char *feature_set = NULL;
     376           0 :     for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
     377           0 :         pcmk_node_t *node = gIter->data;
     378           0 :         const char *node_feature_set = get_node_feature_set(node);
     379           0 :         if (node_feature_set != NULL) {
     380           0 :             if (feature_set == NULL) {
     381           0 :                 feature_set = node_feature_set;
     382           0 :             } else if (strcmp(feature_set, node_feature_set) != 0) {
     383           0 :                 return true;
     384             :             }
     385             :         }
     386             :     }
     387           0 :     return false;
     388             : }
     389             : 
     390             : static void
     391           0 : formatted_xml_buf(const pcmk_resource_t *rsc, GString *xml_buf, bool raw)
     392             : {
     393           0 :     if (raw && (rsc->orig_xml != NULL)) {
     394           0 :         pcmk__xml_string(rsc->orig_xml, pcmk__xml_fmt_pretty, xml_buf, 0);
     395             :     } else {
     396           0 :         pcmk__xml_string(rsc->xml, pcmk__xml_fmt_pretty, xml_buf, 0);
     397             :     }
     398           0 : }
     399             : 
     400             : #define XPATH_DC_VERSION "//" PCMK_XE_NVPAIR    \
     401             :                          "[@" PCMK_XA_NAME "='" PCMK_OPT_DC_VERSION "']"
     402             : 
     403             : PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *",
     404             :                   "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
     405             : static int
     406           0 : cluster_summary(pcmk__output_t *out, va_list args) {
     407           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
     408           0 :     enum pcmk_pacemakerd_state pcmkd_state =
     409             :         (enum pcmk_pacemakerd_state) va_arg(args, int);
     410           0 :     uint32_t section_opts = va_arg(args, uint32_t);
     411           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     412             : 
     413           0 :     int rc = pcmk_rc_no_output;
     414           0 :     const char *stack_s = get_cluster_stack(scheduler);
     415             : 
     416           0 :     if (pcmk_is_set(section_opts, pcmk_section_stack)) {
     417           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     418           0 :         out->message(out, "cluster-stack", stack_s, pcmkd_state);
     419             :     }
     420             : 
     421           0 :     if (pcmk_is_set(section_opts, pcmk_section_dc)) {
     422           0 :         xmlNode *dc_version = get_xpath_object(XPATH_DC_VERSION,
     423             :                                                scheduler->input, LOG_DEBUG);
     424           0 :         const char *dc_version_s = dc_version?
     425           0 :                                    crm_element_value(dc_version, PCMK_XA_VALUE)
     426           0 :                                    : NULL;
     427           0 :         const char *quorum = crm_element_value(scheduler->input,
     428             :                                                PCMK_XA_HAVE_QUORUM);
     429           0 :         char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
     430           0 :         bool mixed_version = is_mixed_version(scheduler);
     431             : 
     432           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     433           0 :         out->message(out, "cluster-dc", scheduler->dc_node, quorum,
     434             :                      dc_version_s, dc_name, mixed_version);
     435           0 :         free(dc_name);
     436             :     }
     437             : 
     438           0 :     if (pcmk_is_set(section_opts, pcmk_section_times)) {
     439           0 :         const char *last_written = crm_element_value(scheduler->input,
     440             :                                                      PCMK_XA_CIB_LAST_WRITTEN);
     441           0 :         const char *user = crm_element_value(scheduler->input,
     442             :                                              PCMK_XA_UPDATE_USER);
     443           0 :         const char *client = crm_element_value(scheduler->input,
     444             :                                                PCMK_XA_UPDATE_CLIENT);
     445           0 :         const char *origin = crm_element_value(scheduler->input,
     446             :                                                PCMK_XA_UPDATE_ORIGIN);
     447             : 
     448           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     449           0 :         out->message(out, "cluster-times",
     450             :                      scheduler->localhost, last_written, user, client, origin);
     451             :     }
     452             : 
     453           0 :     if (pcmk_is_set(section_opts, pcmk_section_counts)) {
     454           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     455           0 :         out->message(out, "cluster-counts", g_list_length(scheduler->nodes),
     456             :                      scheduler->ninstances, scheduler->disabled_resources,
     457             :                      scheduler->blocked_resources);
     458             :     }
     459             : 
     460           0 :     if (pcmk_is_set(section_opts, pcmk_section_options)) {
     461           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     462           0 :         out->message(out, "cluster-options", scheduler);
     463             :     }
     464             : 
     465           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
     466             : 
     467           0 :     if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
     468           0 :         if (out->message(out, "maint-mode", scheduler->flags) == pcmk_rc_ok) {
     469           0 :             rc = pcmk_rc_ok;
     470             :         }
     471             :     }
     472             : 
     473           0 :     return rc;
     474             : }
     475             : 
     476             : PCMK__OUTPUT_ARGS("cluster-summary", "pcmk_scheduler_t *",
     477             :                   "enum pcmk_pacemakerd_state", "uint32_t", "uint32_t")
     478             : static int
     479           0 : cluster_summary_html(pcmk__output_t *out, va_list args) {
     480           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
     481           0 :     enum pcmk_pacemakerd_state pcmkd_state =
     482             :         (enum pcmk_pacemakerd_state) va_arg(args, int);
     483           0 :     uint32_t section_opts = va_arg(args, uint32_t);
     484           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     485             : 
     486           0 :     int rc = pcmk_rc_no_output;
     487           0 :     const char *stack_s = get_cluster_stack(scheduler);
     488             : 
     489           0 :     if (pcmk_is_set(section_opts, pcmk_section_stack)) {
     490           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     491           0 :         out->message(out, "cluster-stack", stack_s, pcmkd_state);
     492             :     }
     493             : 
     494             :     /* Always print DC if none, even if not requested */
     495           0 :     if ((scheduler->dc_node == NULL)
     496           0 :         || pcmk_is_set(section_opts, pcmk_section_dc)) {
     497           0 :         xmlNode *dc_version = get_xpath_object(XPATH_DC_VERSION,
     498             :                                                scheduler->input, LOG_DEBUG);
     499           0 :         const char *dc_version_s = dc_version?
     500           0 :                                    crm_element_value(dc_version, PCMK_XA_VALUE)
     501           0 :                                    : NULL;
     502           0 :         const char *quorum = crm_element_value(scheduler->input,
     503             :                                                PCMK_XA_HAVE_QUORUM);
     504           0 :         char *dc_name = scheduler->dc_node? pe__node_display_name(scheduler->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
     505           0 :         bool mixed_version = is_mixed_version(scheduler);
     506             : 
     507           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     508           0 :         out->message(out, "cluster-dc", scheduler->dc_node, quorum,
     509             :                      dc_version_s, dc_name, mixed_version);
     510           0 :         free(dc_name);
     511             :     }
     512             : 
     513           0 :     if (pcmk_is_set(section_opts, pcmk_section_times)) {
     514           0 :         const char *last_written = crm_element_value(scheduler->input,
     515             :                                                      PCMK_XA_CIB_LAST_WRITTEN);
     516           0 :         const char *user = crm_element_value(scheduler->input,
     517             :                                              PCMK_XA_UPDATE_USER);
     518           0 :         const char *client = crm_element_value(scheduler->input,
     519             :                                                PCMK_XA_UPDATE_CLIENT);
     520           0 :         const char *origin = crm_element_value(scheduler->input,
     521             :                                                PCMK_XA_UPDATE_ORIGIN);
     522             : 
     523           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     524           0 :         out->message(out, "cluster-times",
     525             :                      scheduler->localhost, last_written, user, client, origin);
     526             :     }
     527             : 
     528           0 :     if (pcmk_is_set(section_opts, pcmk_section_counts)) {
     529           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Cluster Summary");
     530           0 :         out->message(out, "cluster-counts", g_list_length(scheduler->nodes),
     531             :                      scheduler->ninstances, scheduler->disabled_resources,
     532             :                      scheduler->blocked_resources);
     533             :     }
     534             : 
     535           0 :     if (pcmk_is_set(section_opts, pcmk_section_options)) {
     536             :         /* Kind of a hack - close the list we may have opened earlier in this
     537             :          * function so we can put all the options into their own list.  We
     538             :          * only want to do this on HTML output, though.
     539             :          */
     540           0 :         PCMK__OUTPUT_LIST_FOOTER(out, rc);
     541             : 
     542           0 :         out->begin_list(out, NULL, NULL, "Config Options");
     543           0 :         out->message(out, "cluster-options", scheduler);
     544             :     }
     545             : 
     546           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
     547             : 
     548           0 :     if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
     549           0 :         if (out->message(out, "maint-mode", scheduler->flags) == pcmk_rc_ok) {
     550           0 :             rc = pcmk_rc_ok;
     551             :         }
     552             :     }
     553             : 
     554           0 :     return rc;
     555             : }
     556             : 
     557             : char *
     558           0 : pe__node_display_name(pcmk_node_t *node, bool print_detail)
     559             : {
     560             :     char *node_name;
     561           0 :     const char *node_host = NULL;
     562           0 :     const char *node_id = NULL;
     563             :     int name_len;
     564             : 
     565           0 :     CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
     566             : 
     567             :     /* Host is displayed only if this is a guest node and detail is requested */
     568           0 :     if (print_detail && pcmk__is_guest_or_bundle_node(node)) {
     569           0 :         const pcmk_resource_t *container = node->details->remote_rsc->container;
     570           0 :         const pcmk_node_t *host_node = pcmk__current_node(container);
     571             : 
     572           0 :         if (host_node && host_node->details) {
     573           0 :             node_host = host_node->details->uname;
     574             :         }
     575           0 :         if (node_host == NULL) {
     576           0 :             node_host = ""; /* so we at least get "uname@" to indicate guest */
     577             :         }
     578             :     }
     579             : 
     580             :     /* Node ID is displayed if different from uname and detail is requested */
     581           0 :     if (print_detail && !pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
     582           0 :         node_id = node->details->id;
     583             :     }
     584             : 
     585             :     /* Determine name length */
     586           0 :     name_len = strlen(node->details->uname) + 1;
     587           0 :     if (node_host) {
     588           0 :         name_len += strlen(node_host) + 1; /* "@node_host" */
     589             :     }
     590           0 :     if (node_id) {
     591           0 :         name_len += strlen(node_id) + 3; /* + " (node_id)" */
     592             :     }
     593             : 
     594             :     /* Allocate and populate display name */
     595           0 :     node_name = pcmk__assert_alloc(name_len, sizeof(char));
     596           0 :     strcpy(node_name, node->details->uname);
     597           0 :     if (node_host) {
     598           0 :         strcat(node_name, "@");
     599           0 :         strcat(node_name, node_host);
     600             :     }
     601           0 :     if (node_id) {
     602           0 :         strcat(node_name, " (");
     603           0 :         strcat(node_name, node_id);
     604           0 :         strcat(node_name, ")");
     605             :     }
     606           0 :     return node_name;
     607             : }
     608             : 
     609             : int
     610           0 : pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name,
     611             :                          ...)
     612             : {
     613           0 :     xmlNodePtr xml_node = NULL;
     614             :     va_list pairs;
     615             : 
     616           0 :     CRM_ASSERT(tag_name != NULL);
     617             : 
     618           0 :     xml_node = pcmk__output_xml_peek_parent(out);
     619           0 :     CRM_ASSERT(xml_node != NULL);
     620           0 :     xml_node = pcmk__xe_create(xml_node, tag_name);
     621             : 
     622           0 :     va_start(pairs, tag_name);
     623           0 :     pcmk__xe_set_propv(xml_node, pairs);
     624           0 :     va_end(pairs);
     625             : 
     626           0 :     if (is_list) {
     627           0 :         pcmk__output_xml_push_parent(out, xml_node);
     628             :     }
     629           0 :     return pcmk_rc_ok;
     630             : }
     631             : 
     632             : static const char *
     633           0 : role_desc(enum rsc_role_e role)
     634             : {
     635           0 :     if (role == pcmk_role_promoted) {
     636             : #ifdef PCMK__COMPAT_2_0
     637             :         return "as " PCMK__ROLE_PROMOTED_LEGACY " ";
     638             : #else
     639           0 :         return "in " PCMK_ROLE_PROMOTED " role ";
     640             : #endif
     641             :     }
     642           0 :     return "";
     643             : }
     644             : 
     645             : PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t")
     646             : static int
     647           0 : ban_html(pcmk__output_t *out, va_list args) {
     648           0 :     pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
     649           0 :     pcmk__location_t *location = va_arg(args, pcmk__location_t *);
     650           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     651             : 
     652           0 :     char *node_name = pe__node_display_name(pe_node,
     653           0 :                                             pcmk_is_set(show_opts, pcmk_show_node_id));
     654           0 :     char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s",
     655           0 :                                   location->id, location->rsc->id,
     656             :                                   role_desc(location->role_filter), node_name);
     657             : 
     658           0 :     pcmk__output_create_html_node(out, "li", NULL, NULL, buf);
     659             : 
     660           0 :     free(node_name);
     661           0 :     free(buf);
     662           0 :     return pcmk_rc_ok;
     663             : }
     664             : 
     665             : PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t")
     666             : static int
     667           0 : ban_text(pcmk__output_t *out, va_list args) {
     668           0 :     pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
     669           0 :     pcmk__location_t *location = va_arg(args, pcmk__location_t *);
     670           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     671             : 
     672           0 :     char *node_name = pe__node_display_name(pe_node,
     673           0 :                                             pcmk_is_set(show_opts, pcmk_show_node_id));
     674           0 :     out->list_item(out, NULL, "%s\tprevents %s from running %son %s",
     675           0 :                    location->id, location->rsc->id,
     676             :                    role_desc(location->role_filter), node_name);
     677             : 
     678           0 :     free(node_name);
     679           0 :     return pcmk_rc_ok;
     680             : }
     681             : 
     682             : PCMK__OUTPUT_ARGS("ban", "pcmk_node_t *", "pcmk__location_t *", "uint32_t")
     683             : static int
     684           0 : ban_xml(pcmk__output_t *out, va_list args) {
     685           0 :     pcmk_node_t *pe_node = va_arg(args, pcmk_node_t *);
     686           0 :     pcmk__location_t *location = va_arg(args, pcmk__location_t *);
     687           0 :     uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
     688             : 
     689           0 :     const char *promoted_only = pcmk__btoa(location->role_filter == pcmk_role_promoted);
     690           0 :     char *weight_s = pcmk__itoa(pe_node->weight);
     691             : 
     692           0 :     pcmk__output_create_xml_node(out, PCMK_XE_BAN,
     693             :                                  PCMK_XA_ID, location->id,
     694           0 :                                  PCMK_XA_RESOURCE, location->rsc->id,
     695           0 :                                  PCMK_XA_NODE, pe_node->details->uname,
     696             :                                  PCMK_XA_WEIGHT, weight_s,
     697             :                                  PCMK_XA_PROMOTED_ONLY, promoted_only,
     698             :                                  /* This is a deprecated alias for
     699             :                                   * promoted_only. Removing it will break
     700             :                                   * backward compatibility of the API schema,
     701             :                                   * which will require an API schema major
     702             :                                   * version bump.
     703             :                                   */
     704             :                                  PCMK__XA_PROMOTED_ONLY_LEGACY, promoted_only,
     705             :                                  NULL);
     706             : 
     707           0 :     free(weight_s);
     708           0 :     return pcmk_rc_ok;
     709             : }
     710             : 
     711             : PCMK__OUTPUT_ARGS("ban-list", "pcmk_scheduler_t *", "const char *", "GList *",
     712             :                   "uint32_t", "bool")
     713             : static int
     714           0 : ban_list(pcmk__output_t *out, va_list args) {
     715           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
     716           0 :     const char *prefix = va_arg(args, const char *);
     717           0 :     GList *only_rsc = va_arg(args, GList *);
     718           0 :     uint32_t show_opts = va_arg(args, uint32_t);
     719           0 :     bool print_spacer = va_arg(args, int);
     720             : 
     721             :     GList *gIter, *gIter2;
     722           0 :     int rc = pcmk_rc_no_output;
     723             : 
     724             :     /* Print each ban */
     725           0 :     for (gIter = scheduler->placement_constraints;
     726           0 :          gIter != NULL; gIter = gIter->next) {
     727           0 :         pcmk__location_t *location = gIter->data;
     728           0 :         const pcmk_resource_t *rsc = location->rsc;
     729             : 
     730           0 :         if (prefix != NULL && !g_str_has_prefix(location->id, prefix)) {
     731           0 :             continue;
     732             :         }
     733             : 
     734           0 :         if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
     735             :                                pcmk__str_star_matches)
     736           0 :             && !pcmk__str_in_list(rsc_printable_id(pe__const_top_resource(rsc, false)),
     737             :                                   only_rsc, pcmk__str_star_matches)) {
     738           0 :             continue;
     739             :         }
     740             : 
     741           0 :         for (gIter2 = location->nodes; gIter2 != NULL; gIter2 = gIter2->next) {
     742           0 :             pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
     743             : 
     744           0 :             if (node->weight < 0) {
     745           0 :                 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Negative Location Constraints");
     746           0 :                 out->message(out, "ban", node, location, show_opts);
     747             :             }
     748             :         }
     749             :     }
     750             : 
     751           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
     752           0 :     return rc;
     753             : }
     754             : 
     755             : PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     756             : static int
     757           0 : cluster_counts_html(pcmk__output_t *out, va_list args) {
     758           0 :     unsigned int nnodes = va_arg(args, unsigned int);
     759           0 :     int nresources = va_arg(args, int);
     760           0 :     int ndisabled = va_arg(args, int);
     761           0 :     int nblocked = va_arg(args, int);
     762             : 
     763           0 :     xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li", NULL);
     764           0 :     xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li", NULL);
     765           0 :     xmlNode *child = NULL;
     766             : 
     767           0 :     child = pcmk__html_create(nodes_node, PCMK__XE_SPAN, NULL, NULL);
     768           0 :     pcmk__xe_set_content(child, "%d node%s configured",
     769             :                          nnodes, pcmk__plural_s(nnodes));
     770             : 
     771           0 :     if (ndisabled && nblocked) {
     772           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     773           0 :         pcmk__xe_set_content(child, "%d resource instance%s configured (%d ",
     774             :                              nresources, pcmk__plural_s(nresources), ndisabled);
     775             : 
     776           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
     777             :                                   PCMK__VALUE_BOLD);
     778           0 :         pcmk__xe_set_content(child, "DISABLED");
     779             : 
     780           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     781           0 :         pcmk__xe_set_content(child, ", %d ", nblocked);
     782             : 
     783           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
     784             :                                   PCMK__VALUE_BOLD);
     785           0 :         pcmk__xe_set_content(child, "BLOCKED");
     786             : 
     787           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     788           0 :         pcmk__xe_set_content(child, " from further action due to failure)");
     789             : 
     790           0 :     } else if (ndisabled && !nblocked) {
     791           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     792           0 :         pcmk__xe_set_content(child, "%d resource instance%s configured (%d ",
     793             :                              nresources, pcmk__plural_s(nresources),
     794             :                              ndisabled);
     795             : 
     796           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
     797             :                                   PCMK__VALUE_BOLD);
     798           0 :         pcmk__xe_set_content(child, "DISABLED");
     799             : 
     800           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     801           0 :         pcmk__xe_set_content(child, ")");
     802             : 
     803           0 :     } else if (!ndisabled && nblocked) {
     804           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     805           0 :         pcmk__xe_set_content(child, "%d resource instance%s configured (%d ",
     806             :                              nresources, pcmk__plural_s(nresources),
     807             :                              nblocked);
     808             : 
     809           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL,
     810             :                                   PCMK__VALUE_BOLD);
     811           0 :         pcmk__xe_set_content(child, "BLOCKED");
     812             : 
     813           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     814           0 :         pcmk__xe_set_content(child, " from further action due to failure)");
     815             : 
     816             :     } else {
     817           0 :         child = pcmk__html_create(resources_node, PCMK__XE_SPAN, NULL, NULL);
     818           0 :         pcmk__xe_set_content(child, "%d resource instance%s configured",
     819             :                              nresources, pcmk__plural_s(nresources));
     820             :     }
     821             : 
     822           0 :     return pcmk_rc_ok;
     823             : }
     824             : 
     825             : PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     826             : static int
     827           0 : cluster_counts_text(pcmk__output_t *out, va_list args) {
     828           0 :     unsigned int nnodes = va_arg(args, unsigned int);
     829           0 :     int nresources = va_arg(args, int);
     830           0 :     int ndisabled = va_arg(args, int);
     831           0 :     int nblocked = va_arg(args, int);
     832             : 
     833           0 :     out->list_item(out, NULL, "%d node%s configured",
     834             :                    nnodes, pcmk__plural_s(nnodes));
     835             : 
     836           0 :     if (ndisabled && nblocked) {
     837           0 :         out->list_item(out, NULL, "%d resource instance%s configured "
     838             :                                   "(%d DISABLED, %d BLOCKED from "
     839             :                                   "further action due to failure)",
     840             :                        nresources, pcmk__plural_s(nresources), ndisabled,
     841             :                        nblocked);
     842           0 :     } else if (ndisabled && !nblocked) {
     843           0 :         out->list_item(out, NULL, "%d resource instance%s configured "
     844             :                                   "(%d DISABLED)",
     845             :                        nresources, pcmk__plural_s(nresources), ndisabled);
     846           0 :     } else if (!ndisabled && nblocked) {
     847           0 :         out->list_item(out, NULL, "%d resource instance%s configured "
     848             :                                   "(%d BLOCKED from further action "
     849             :                                   "due to failure)",
     850             :                        nresources, pcmk__plural_s(nresources), nblocked);
     851             :     } else {
     852           0 :         out->list_item(out, NULL, "%d resource instance%s configured",
     853             :                        nresources, pcmk__plural_s(nresources));
     854             :     }
     855             : 
     856           0 :     return pcmk_rc_ok;
     857             : }
     858             : 
     859             : PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     860             : static int
     861           0 : cluster_counts_xml(pcmk__output_t *out, va_list args) {
     862           0 :     unsigned int nnodes = va_arg(args, unsigned int);
     863           0 :     int nresources = va_arg(args, int);
     864           0 :     int ndisabled = va_arg(args, int);
     865           0 :     int nblocked = va_arg(args, int);
     866             : 
     867           0 :     xmlNodePtr nodes_node = NULL;
     868           0 :     xmlNodePtr resources_node = NULL;
     869           0 :     char *s = NULL;
     870             : 
     871           0 :     nodes_node = pcmk__output_create_xml_node(out, PCMK_XE_NODES_CONFIGURED,
     872             :                                               NULL);
     873           0 :     resources_node = pcmk__output_create_xml_node(out,
     874             :                                                   PCMK_XE_RESOURCES_CONFIGURED,
     875             :                                                   NULL);
     876             : 
     877           0 :     s = pcmk__itoa(nnodes);
     878           0 :     crm_xml_add(nodes_node, PCMK_XA_NUMBER, s);
     879           0 :     free(s);
     880             : 
     881           0 :     s = pcmk__itoa(nresources);
     882           0 :     crm_xml_add(resources_node, PCMK_XA_NUMBER, s);
     883           0 :     free(s);
     884             : 
     885           0 :     s = pcmk__itoa(ndisabled);
     886           0 :     crm_xml_add(resources_node, PCMK_XA_DISABLED, s);
     887           0 :     free(s);
     888             : 
     889           0 :     s = pcmk__itoa(nblocked);
     890           0 :     crm_xml_add(resources_node, PCMK_XA_BLOCKED, s);
     891           0 :     free(s);
     892             : 
     893           0 :     return pcmk_rc_ok;
     894             : }
     895             : 
     896             : PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
     897             :                   "char *", "int")
     898             : static int
     899           0 : cluster_dc_html(pcmk__output_t *out, va_list args) {
     900           0 :     pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
     901           0 :     const char *quorum = va_arg(args, const char *);
     902           0 :     const char *dc_version_s = va_arg(args, const char *);
     903           0 :     char *dc_name = va_arg(args, char *);
     904           0 :     bool mixed_version = va_arg(args, int);
     905             : 
     906           0 :     xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
     907           0 :     xmlNode *child = NULL;
     908             : 
     909           0 :     child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
     910           0 :     pcmk__xe_set_content(child, "Current DC: ");
     911             : 
     912           0 :     if (dc) {
     913           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
     914           0 :         pcmk__xe_set_content(child, "%s (version %s) -",
     915             :                              dc_name, pcmk__s(dc_version_s, "unknown"));
     916             : 
     917           0 :         if (mixed_version) {
     918           0 :             child = pcmk__html_create(node, PCMK__XE_SPAN, NULL,
     919             :                                       PCMK__VALUE_WARNING);
     920           0 :             pcmk__xe_set_content(child, " MIXED-VERSION");
     921             :         }
     922             : 
     923           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
     924           0 :         pcmk__xe_set_content(child, " partition");
     925             : 
     926           0 :         if (crm_is_true(quorum)) {
     927           0 :             child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
     928           0 :             pcmk__xe_set_content(child, " with");
     929             : 
     930             :         } else {
     931           0 :             child = pcmk__html_create(node, PCMK__XE_SPAN, NULL,
     932             :                                       PCMK__VALUE_WARNING);
     933           0 :             pcmk__xe_set_content(child, " WITHOUT");
     934             :         }
     935             : 
     936           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
     937           0 :         pcmk__xe_set_content(child, " quorum");
     938             : 
     939             :     } else {
     940           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL,
     941             :                                   PCMK__VALUE_WARNING);
     942           0 :         pcmk__xe_set_content(child, "NONE");
     943             :     }
     944             : 
     945           0 :     return pcmk_rc_ok;
     946             : }
     947             : 
     948             : PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
     949             :                   "char *", "int")
     950             : static int
     951           0 : cluster_dc_text(pcmk__output_t *out, va_list args) {
     952           0 :     pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
     953           0 :     const char *quorum = va_arg(args, const char *);
     954           0 :     const char *dc_version_s = va_arg(args, const char *);
     955           0 :     char *dc_name = va_arg(args, char *);
     956           0 :     bool mixed_version = va_arg(args, int);
     957             : 
     958           0 :     if (dc) {
     959           0 :         out->list_item(out, "Current DC",
     960             :                        "%s (version %s) - %spartition %s quorum",
     961             :                        dc_name, dc_version_s ? dc_version_s : "unknown",
     962             :                        mixed_version ? "MIXED-VERSION " : "",
     963           0 :                        crm_is_true(quorum) ? "with" : "WITHOUT");
     964             :     } else {
     965           0 :         out->list_item(out, "Current DC", "NONE");
     966             :     }
     967             : 
     968           0 :     return pcmk_rc_ok;
     969             : }
     970             : 
     971             : PCMK__OUTPUT_ARGS("cluster-dc", "pcmk_node_t *", "const char *", "const char *",
     972             :                   "char *", "int")
     973             : static int
     974           0 : cluster_dc_xml(pcmk__output_t *out, va_list args) {
     975           0 :     pcmk_node_t *dc = va_arg(args, pcmk_node_t *);
     976           0 :     const char *quorum = va_arg(args, const char *);
     977           0 :     const char *dc_version_s = va_arg(args, const char *);
     978           0 :     char *dc_name G_GNUC_UNUSED = va_arg(args, char *);
     979           0 :     bool mixed_version = va_arg(args, int);
     980             : 
     981           0 :     if (dc) {
     982           0 :         const char *with_quorum = pcmk__btoa(crm_is_true(quorum));
     983           0 :         const char *mixed_version_s = pcmk__btoa(mixed_version);
     984             : 
     985           0 :         pcmk__output_create_xml_node(out, PCMK_XE_CURRENT_DC,
     986             :                                      PCMK_XA_PRESENT, PCMK_VALUE_TRUE,
     987             :                                      PCMK_XA_VERSION, pcmk__s(dc_version_s, ""),
     988           0 :                                      PCMK_XA_NAME, dc->details->uname,
     989           0 :                                      PCMK_XA_ID, dc->details->id,
     990             :                                      PCMK_XA_WITH_QUORUM, with_quorum,
     991             :                                      PCMK_XA_MIXED_VERSION, mixed_version_s,
     992             :                                      NULL);
     993             :     } else {
     994           0 :         pcmk__output_create_xml_node(out, PCMK_XE_CURRENT_DC,
     995             :                                      PCMK_XA_PRESENT, PCMK_VALUE_FALSE,
     996             :                                      NULL);
     997             :     }
     998             : 
     999           0 :     return pcmk_rc_ok;
    1000             : }
    1001             : 
    1002             : PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long int")
    1003             : static int
    1004           0 : cluster_maint_mode_text(pcmk__output_t *out, va_list args) {
    1005           0 :     unsigned long long flags = va_arg(args, unsigned long long);
    1006             : 
    1007           0 :     if (pcmk_is_set(flags, pcmk_sched_in_maintenance)) {
    1008           0 :         pcmk__formatted_printf(out, "\n              *** Resource management is DISABLED ***\n");
    1009           0 :         pcmk__formatted_printf(out, "  The cluster will not attempt to start, stop or recover services\n");
    1010           0 :         return pcmk_rc_ok;
    1011           0 :     } else if (pcmk_is_set(flags, pcmk_sched_stop_all)) {
    1012           0 :         pcmk__formatted_printf(out, "\n    *** Resource management is DISABLED ***\n");
    1013           0 :         pcmk__formatted_printf(out, "  The cluster will keep all resources stopped\n");
    1014           0 :         return pcmk_rc_ok;
    1015             :     } else {
    1016           0 :         return pcmk_rc_no_output;
    1017             :     }
    1018             : }
    1019             : 
    1020             : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
    1021             : static int
    1022           0 : cluster_options_html(pcmk__output_t *out, va_list args) {
    1023           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    1024             : 
    1025           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) {
    1026           0 :         out->list_item(out, NULL, "STONITH of failed nodes enabled");
    1027             :     } else {
    1028           0 :         out->list_item(out, NULL, "STONITH of failed nodes disabled");
    1029             :     }
    1030             : 
    1031           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) {
    1032           0 :         out->list_item(out, NULL, "Cluster is symmetric");
    1033             :     } else {
    1034           0 :         out->list_item(out, NULL, "Cluster is asymmetric");
    1035             :     }
    1036             : 
    1037           0 :     switch (scheduler->no_quorum_policy) {
    1038           0 :         case pcmk_no_quorum_freeze:
    1039           0 :             out->list_item(out, NULL, "No quorum policy: Freeze resources");
    1040           0 :             break;
    1041             : 
    1042           0 :         case pcmk_no_quorum_stop:
    1043           0 :             out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
    1044           0 :             break;
    1045             : 
    1046           0 :         case pcmk_no_quorum_demote:
    1047           0 :             out->list_item(out, NULL, "No quorum policy: Demote promotable "
    1048             :                            "resources and stop all other resources");
    1049           0 :             break;
    1050             : 
    1051           0 :         case pcmk_no_quorum_ignore:
    1052           0 :             out->list_item(out, NULL, "No quorum policy: Ignore");
    1053           0 :             break;
    1054             : 
    1055           0 :         case pcmk_no_quorum_fence:
    1056           0 :             out->list_item(out, NULL, "No quorum policy: Suicide");
    1057           0 :             break;
    1058             :     }
    1059             : 
    1060           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
    1061           0 :         xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
    1062           0 :         xmlNode *child = NULL;
    1063             : 
    1064           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1065           0 :         pcmk__xe_set_content(child, "Resource management: ");
    1066             : 
    1067           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
    1068           0 :         pcmk__xe_set_content(child, "DISABLED");
    1069             : 
    1070           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1071           0 :         pcmk__xe_set_content(child,
    1072             :                              " (the cluster will not attempt to start, stop,"
    1073             :                              " or recover services)");
    1074             : 
    1075           0 :     } else if (pcmk_is_set(scheduler->flags, pcmk_sched_stop_all)) {
    1076           0 :         xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
    1077           0 :         xmlNode *child = NULL;
    1078             : 
    1079           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1080           0 :         pcmk__xe_set_content(child, "Resource management: ");
    1081             : 
    1082           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
    1083           0 :         pcmk__xe_set_content(child, "STOPPED");
    1084             : 
    1085           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1086           0 :         pcmk__xe_set_content(child,
    1087             :                              " (the cluster will keep all resources stopped)");
    1088             : 
    1089             :     } else {
    1090           0 :         out->list_item(out, NULL, "Resource management: enabled");
    1091             :     }
    1092             : 
    1093           0 :     return pcmk_rc_ok;
    1094             : }
    1095             : 
    1096             : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
    1097             : static int
    1098           0 : cluster_options_log(pcmk__output_t *out, va_list args) {
    1099           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    1100             : 
    1101           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
    1102           0 :         return out->info(out, "Resource management is DISABLED.  The cluster will not attempt to start, stop or recover services.");
    1103           0 :     } else if (pcmk_is_set(scheduler->flags, pcmk_sched_stop_all)) {
    1104           0 :         return out->info(out, "Resource management is DISABLED.  The cluster has stopped all resources.");
    1105             :     } else {
    1106           0 :         return pcmk_rc_no_output;
    1107             :     }
    1108             : }
    1109             : 
    1110             : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
    1111             : static int
    1112           0 : cluster_options_text(pcmk__output_t *out, va_list args) {
    1113           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    1114             : 
    1115           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_fencing_enabled)) {
    1116           0 :         out->list_item(out, NULL, "STONITH of failed nodes enabled");
    1117             :     } else {
    1118           0 :         out->list_item(out, NULL, "STONITH of failed nodes disabled");
    1119             :     }
    1120             : 
    1121           0 :     if (pcmk_is_set(scheduler->flags, pcmk_sched_symmetric_cluster)) {
    1122           0 :         out->list_item(out, NULL, "Cluster is symmetric");
    1123             :     } else {
    1124           0 :         out->list_item(out, NULL, "Cluster is asymmetric");
    1125             :     }
    1126             : 
    1127           0 :     switch (scheduler->no_quorum_policy) {
    1128           0 :         case pcmk_no_quorum_freeze:
    1129           0 :             out->list_item(out, NULL, "No quorum policy: Freeze resources");
    1130           0 :             break;
    1131             : 
    1132           0 :         case pcmk_no_quorum_stop:
    1133           0 :             out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
    1134           0 :             break;
    1135             : 
    1136           0 :         case pcmk_no_quorum_demote:
    1137           0 :             out->list_item(out, NULL, "No quorum policy: Demote promotable "
    1138             :                            "resources and stop all other resources");
    1139           0 :             break;
    1140             : 
    1141           0 :         case pcmk_no_quorum_ignore:
    1142           0 :             out->list_item(out, NULL, "No quorum policy: Ignore");
    1143           0 :             break;
    1144             : 
    1145           0 :         case pcmk_no_quorum_fence:
    1146           0 :             out->list_item(out, NULL, "No quorum policy: Suicide");
    1147           0 :             break;
    1148             :     }
    1149             : 
    1150           0 :     return pcmk_rc_ok;
    1151             : }
    1152             : 
    1153             : /*!
    1154             :  * \internal
    1155             :  * \brief Get readable string representation of a no-quorum policy
    1156             :  *
    1157             :  * \param[in] policy  No-quorum policy
    1158             :  *
    1159             :  * \return String representation of \p policy
    1160             :  */
    1161             : static const char *
    1162           0 : no_quorum_policy_text(enum pe_quorum_policy policy)
    1163             : {
    1164           0 :     switch (policy) {
    1165           0 :         case pcmk_no_quorum_freeze:
    1166           0 :             return PCMK_VALUE_FREEZE;
    1167             : 
    1168           0 :         case pcmk_no_quorum_stop:
    1169           0 :             return PCMK_VALUE_STOP;
    1170             : 
    1171           0 :         case pcmk_no_quorum_demote:
    1172           0 :             return PCMK_VALUE_DEMOTE;
    1173             : 
    1174           0 :         case pcmk_no_quorum_ignore:
    1175           0 :             return PCMK_VALUE_IGNORE;
    1176             : 
    1177           0 :         case pcmk_no_quorum_fence:
    1178           0 :             return PCMK_VALUE_FENCE_LEGACY;
    1179             : 
    1180           0 :         default:
    1181           0 :             return PCMK_VALUE_UNKNOWN;
    1182             :     }
    1183             : }
    1184             : 
    1185             : PCMK__OUTPUT_ARGS("cluster-options", "pcmk_scheduler_t *")
    1186             : static int
    1187           0 : cluster_options_xml(pcmk__output_t *out, va_list args) {
    1188           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    1189             : 
    1190           0 :     const char *stonith_enabled = pcmk__flag_text(scheduler->flags,
    1191             :                                                   pcmk_sched_fencing_enabled);
    1192             :     const char *symmetric_cluster =
    1193           0 :         pcmk__flag_text(scheduler->flags, pcmk_sched_symmetric_cluster);
    1194             :     const char *no_quorum_policy =
    1195           0 :         no_quorum_policy_text(scheduler->no_quorum_policy);
    1196           0 :     const char *maintenance_mode = pcmk__flag_text(scheduler->flags,
    1197             :                                                    pcmk_sched_in_maintenance);
    1198           0 :     const char *stop_all_resources = pcmk__flag_text(scheduler->flags,
    1199             :                                                      pcmk_sched_stop_all);
    1200           0 :     char *stonith_timeout_ms_s = pcmk__itoa(scheduler->stonith_timeout);
    1201             :     char *priority_fencing_delay_ms_s =
    1202           0 :         pcmk__itoa(scheduler->priority_fencing_delay * 1000);
    1203             : 
    1204           0 :     pcmk__output_create_xml_node(out, PCMK_XE_CLUSTER_OPTIONS,
    1205             :                                  PCMK_XA_STONITH_ENABLED, stonith_enabled,
    1206             :                                  PCMK_XA_SYMMETRIC_CLUSTER, symmetric_cluster,
    1207             :                                  PCMK_XA_NO_QUORUM_POLICY, no_quorum_policy,
    1208             :                                  PCMK_XA_MAINTENANCE_MODE, maintenance_mode,
    1209             :                                  PCMK_XA_STOP_ALL_RESOURCES, stop_all_resources,
    1210             :                                  PCMK_XA_STONITH_TIMEOUT_MS,
    1211             :                                      stonith_timeout_ms_s,
    1212             :                                  PCMK_XA_PRIORITY_FENCING_DELAY_MS,
    1213             :                                      priority_fencing_delay_ms_s,
    1214             :                                  NULL);
    1215           0 :     free(stonith_timeout_ms_s);
    1216           0 :     free(priority_fencing_delay_ms_s);
    1217             : 
    1218           0 :     return pcmk_rc_ok;
    1219             : }
    1220             : 
    1221             : PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
    1222             : static int
    1223           0 : cluster_stack_html(pcmk__output_t *out, va_list args) {
    1224           0 :     const char *stack_s = va_arg(args, const char *);
    1225           0 :     enum pcmk_pacemakerd_state pcmkd_state =
    1226             :         (enum pcmk_pacemakerd_state) va_arg(args, int);
    1227             : 
    1228           0 :     xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
    1229           0 :     xmlNode *child = NULL;
    1230             : 
    1231           0 :     child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, PCMK__VALUE_BOLD);
    1232           0 :     pcmk__xe_set_content(child, "Stack: ");
    1233             : 
    1234           0 :     child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1235           0 :     pcmk__xe_set_content(child, "%s", stack_s);
    1236             : 
    1237           0 :     if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
    1238           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1239           0 :         pcmk__xe_set_content(child, " (");
    1240             : 
    1241           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1242           0 :         pcmk__xe_set_content(child, "%s",
    1243             :                              pcmk__pcmkd_state_enum2friendly(pcmkd_state));
    1244             : 
    1245           0 :         child = pcmk__html_create(node, PCMK__XE_SPAN, NULL, NULL);
    1246           0 :         pcmk__xe_set_content(child, ")");
    1247             :     }
    1248           0 :     return pcmk_rc_ok;
    1249             : }
    1250             : 
    1251             : PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
    1252             : static int
    1253           0 : cluster_stack_text(pcmk__output_t *out, va_list args) {
    1254           0 :     const char *stack_s = va_arg(args, const char *);
    1255           0 :     enum pcmk_pacemakerd_state pcmkd_state =
    1256             :         (enum pcmk_pacemakerd_state) va_arg(args, int);
    1257             : 
    1258           0 :     if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
    1259           0 :         out->list_item(out, "Stack", "%s (%s)",
    1260             :                        stack_s, pcmk__pcmkd_state_enum2friendly(pcmkd_state));
    1261             :     } else {
    1262           0 :         out->list_item(out, "Stack", "%s", stack_s);
    1263             :     }
    1264             : 
    1265           0 :     return pcmk_rc_ok;
    1266             : }
    1267             : 
    1268             : PCMK__OUTPUT_ARGS("cluster-stack", "const char *", "enum pcmk_pacemakerd_state")
    1269             : static int
    1270           0 : cluster_stack_xml(pcmk__output_t *out, va_list args) {
    1271           0 :     const char *stack_s = va_arg(args, const char *);
    1272           0 :     enum pcmk_pacemakerd_state pcmkd_state =
    1273             :         (enum pcmk_pacemakerd_state) va_arg(args, int);
    1274             : 
    1275           0 :     const char *state_s = NULL;
    1276             : 
    1277           0 :     if (pcmkd_state != pcmk_pacemakerd_state_invalid) {
    1278           0 :         state_s = pcmk_pacemakerd_api_daemon_state_enum2text(pcmkd_state);
    1279             :     }
    1280             : 
    1281           0 :     pcmk__output_create_xml_node(out, PCMK_XE_STACK,
    1282             :                                  PCMK_XA_TYPE, stack_s,
    1283             :                                  PCMK_XA_PACEMAKERD_STATE, state_s,
    1284             :                                  NULL);
    1285             : 
    1286           0 :     return pcmk_rc_ok;
    1287             : }
    1288             : 
    1289             : PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
    1290             :                   "const char *", "const char *", "const char *")
    1291             : static int
    1292           0 : cluster_times_html(pcmk__output_t *out, va_list args) {
    1293           0 :     const char *our_nodename = va_arg(args, const char *);
    1294           0 :     const char *last_written = va_arg(args, const char *);
    1295           0 :     const char *user = va_arg(args, const char *);
    1296           0 :     const char *client = va_arg(args, const char *);
    1297           0 :     const char *origin = va_arg(args, const char *);
    1298             : 
    1299           0 :     xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li", NULL);
    1300           0 :     xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li", NULL);
    1301           0 :     xmlNode *child = NULL;
    1302             : 
    1303           0 :     char *time_s = NULL;
    1304             : 
    1305           0 :     child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL,
    1306             :                               PCMK__VALUE_BOLD);
    1307           0 :     pcmk__xe_set_content(child, "Last updated: ");
    1308             : 
    1309           0 :     child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL);
    1310           0 :     time_s = pcmk__epoch2str(NULL, 0);
    1311           0 :     pcmk__xe_set_content(child, "%s", time_s);
    1312           0 :     free(time_s);
    1313             : 
    1314           0 :     if (our_nodename != NULL) {
    1315           0 :         child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL);
    1316           0 :         pcmk__xe_set_content(child, " on ");
    1317             : 
    1318           0 :         child = pcmk__html_create(updated_node, PCMK__XE_SPAN, NULL, NULL);
    1319           0 :         pcmk__xe_set_content(child, "%s", our_nodename);
    1320             :     }
    1321             : 
    1322           0 :     child = pcmk__html_create(changed_node, PCMK__XE_SPAN, NULL,
    1323             :                               PCMK__VALUE_BOLD);
    1324           0 :     pcmk__xe_set_content(child, "Last change: ");
    1325             : 
    1326           0 :     child = pcmk__html_create(changed_node, PCMK__XE_SPAN, NULL, NULL);
    1327           0 :     time_s = last_changed_string(last_written, user, client, origin);
    1328           0 :     pcmk__xe_set_content(child, "%s", time_s);
    1329           0 :     free(time_s);
    1330             : 
    1331           0 :     return pcmk_rc_ok;
    1332             : }
    1333             : 
    1334             : PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
    1335             :                   "const char *", "const char *", "const char *")
    1336             : static int
    1337           0 : cluster_times_xml(pcmk__output_t *out, va_list args) {
    1338           0 :     const char *our_nodename = va_arg(args, const char *);
    1339           0 :     const char *last_written = va_arg(args, const char *);
    1340           0 :     const char *user = va_arg(args, const char *);
    1341           0 :     const char *client = va_arg(args, const char *);
    1342           0 :     const char *origin = va_arg(args, const char *);
    1343             : 
    1344           0 :     char *time_s = pcmk__epoch2str(NULL, 0);
    1345             : 
    1346           0 :     pcmk__output_create_xml_node(out, PCMK_XE_LAST_UPDATE,
    1347             :                                  PCMK_XA_TIME, time_s,
    1348             :                                  PCMK_XA_ORIGIN, our_nodename,
    1349             :                                  NULL);
    1350             : 
    1351           0 :     pcmk__output_create_xml_node(out, PCMK_XE_LAST_CHANGE,
    1352             :                                  PCMK_XA_TIME, pcmk__s(last_written, ""),
    1353             :                                  PCMK_XA_USER, pcmk__s(user, ""),
    1354             :                                  PCMK_XA_CLIENT, pcmk__s(client, ""),
    1355             :                                  PCMK_XA_ORIGIN, pcmk__s(origin, ""),
    1356             :                                  NULL);
    1357             : 
    1358           0 :     free(time_s);
    1359           0 :     return pcmk_rc_ok;
    1360             : }
    1361             : 
    1362             : PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *",
    1363             :                   "const char *", "const char *", "const char *")
    1364             : static int
    1365           0 : cluster_times_text(pcmk__output_t *out, va_list args) {
    1366           0 :     const char *our_nodename = va_arg(args, const char *);
    1367           0 :     const char *last_written = va_arg(args, const char *);
    1368           0 :     const char *user = va_arg(args, const char *);
    1369           0 :     const char *client = va_arg(args, const char *);
    1370           0 :     const char *origin = va_arg(args, const char *);
    1371             : 
    1372           0 :     char *time_s = pcmk__epoch2str(NULL, 0);
    1373             : 
    1374           0 :     out->list_item(out, "Last updated", "%s%s%s",
    1375             :                    time_s, (our_nodename != NULL)? " on " : "",
    1376             :                    pcmk__s(our_nodename, ""));
    1377             : 
    1378           0 :     free(time_s);
    1379           0 :     time_s = last_changed_string(last_written, user, client, origin);
    1380             : 
    1381           0 :     out->list_item(out, "Last change", " %s", time_s);
    1382             : 
    1383           0 :     free(time_s);
    1384           0 :     return pcmk_rc_ok;
    1385             : }
    1386             : 
    1387             : /*!
    1388             :  * \internal
    1389             :  * \brief Display a failed action in less-technical natural language
    1390             :  *
    1391             :  * \param[in,out] out          Output object to use for display
    1392             :  * \param[in]     xml_op       XML containing failed action
    1393             :  * \param[in]     op_key       Operation key of failed action
    1394             :  * \param[in]     node_name    Where failed action occurred
    1395             :  * \param[in]     rc           OCF exit code of failed action
    1396             :  * \param[in]     status       Execution status of failed action
    1397             :  * \param[in]     exit_reason  Exit reason given for failed action
    1398             :  * \param[in]     exec_time    String containing execution time in milliseconds
    1399             :  */
    1400             : static void
    1401           0 : failed_action_friendly(pcmk__output_t *out, const xmlNode *xml_op,
    1402             :                        const char *op_key, const char *node_name, int rc,
    1403             :                        int status, const char *exit_reason,
    1404             :                        const char *exec_time)
    1405             : {
    1406           0 :     char *rsc_id = NULL;
    1407           0 :     char *task = NULL;
    1408           0 :     guint interval_ms = 0;
    1409           0 :     time_t last_change_epoch = 0;
    1410           0 :     GString *str = NULL;
    1411             : 
    1412           0 :     if (pcmk__str_empty(op_key)
    1413           0 :         || !parse_op_key(op_key, &rsc_id, &task, &interval_ms)) {
    1414             : 
    1415           0 :         pcmk__str_update(&rsc_id, "unknown resource");
    1416           0 :         pcmk__str_update(&task, "unknown action");
    1417           0 :         interval_ms = 0;
    1418             :     }
    1419           0 :     CRM_ASSERT((rsc_id != NULL) && (task != NULL));
    1420             : 
    1421           0 :     str = g_string_sized_new(256); // Should be sufficient for most messages
    1422             : 
    1423           0 :     pcmk__g_strcat(str, rsc_id, " ", NULL);
    1424             : 
    1425           0 :     if (interval_ms != 0) {
    1426           0 :         pcmk__g_strcat(str, pcmk__readable_interval(interval_ms), "-interval ",
    1427             :                        NULL);
    1428             :     }
    1429           0 :     pcmk__g_strcat(str, pcmk__readable_action(task, interval_ms), " on ",
    1430             :                    node_name, NULL);
    1431             : 
    1432           0 :     if (status == PCMK_EXEC_DONE) {
    1433           0 :         pcmk__g_strcat(str, " returned '", services_ocf_exitcode_str(rc), "'",
    1434             :                        NULL);
    1435           0 :         if (!pcmk__str_empty(exit_reason)) {
    1436           0 :             pcmk__g_strcat(str, " (", exit_reason, ")", NULL);
    1437             :         }
    1438             : 
    1439             :     } else {
    1440           0 :         pcmk__g_strcat(str, " could not be executed (",
    1441             :                        pcmk_exec_status_str(status), NULL);
    1442           0 :         if (!pcmk__str_empty(exit_reason)) {
    1443           0 :             pcmk__g_strcat(str, ": ", exit_reason, NULL);
    1444             :         }
    1445             :         g_string_append_c(str, ')');
    1446             :     }
    1447             : 
    1448             : 
    1449           0 :     if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
    1450             :                                 &last_change_epoch) == pcmk_ok) {
    1451           0 :         char *s = pcmk__epoch2str(&last_change_epoch, 0);
    1452             : 
    1453           0 :         pcmk__g_strcat(str, " at ", s, NULL);
    1454           0 :         free(s);
    1455             :     }
    1456           0 :     if (!pcmk__str_empty(exec_time)) {
    1457           0 :         int exec_time_ms = 0;
    1458             : 
    1459           0 :         if ((pcmk__scan_min_int(exec_time, &exec_time_ms, 0) == pcmk_rc_ok)
    1460           0 :             && (exec_time_ms > 0)) {
    1461             : 
    1462           0 :             pcmk__g_strcat(str, " after ",
    1463             :                            pcmk__readable_interval(exec_time_ms), NULL);
    1464             :         }
    1465             :     }
    1466             : 
    1467           0 :     out->list_item(out, NULL, "%s", str->str);
    1468           0 :     g_string_free(str, TRUE);
    1469           0 :     free(rsc_id);
    1470           0 :     free(task);
    1471           0 : }
    1472             : 
    1473             : /*!
    1474             :  * \internal
    1475             :  * \brief Display a failed action with technical details
    1476             :  *
    1477             :  * \param[in,out] out          Output object to use for display
    1478             :  * \param[in]     xml_op       XML containing failed action
    1479             :  * \param[in]     op_key       Operation key of failed action
    1480             :  * \param[in]     node_name    Where failed action occurred
    1481             :  * \param[in]     rc           OCF exit code of failed action
    1482             :  * \param[in]     status       Execution status of failed action
    1483             :  * \param[in]     exit_reason  Exit reason given for failed action
    1484             :  * \param[in]     exec_time    String containing execution time in milliseconds
    1485             :  */
    1486             : static void
    1487           0 : failed_action_technical(pcmk__output_t *out, const xmlNode *xml_op,
    1488             :                         const char *op_key, const char *node_name, int rc,
    1489             :                         int status, const char *exit_reason,
    1490             :                         const char *exec_time)
    1491             : {
    1492           0 :     const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
    1493           0 :     const char *queue_time = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
    1494           0 :     const char *exit_status = services_ocf_exitcode_str(rc);
    1495           0 :     const char *lrm_status = pcmk_exec_status_str(status);
    1496           0 :     time_t last_change_epoch = 0;
    1497           0 :     GString *str = NULL;
    1498             : 
    1499           0 :     if (pcmk__str_empty(op_key)) {
    1500           0 :         op_key = "unknown operation";
    1501             :     }
    1502           0 :     if (pcmk__str_empty(exit_status)) {
    1503           0 :         exit_status = "unknown exit status";
    1504             :     }
    1505           0 :     if (pcmk__str_empty(call_id)) {
    1506           0 :         call_id = "unknown";
    1507             :     }
    1508             : 
    1509           0 :     str = g_string_sized_new(256);
    1510             : 
    1511           0 :     g_string_append_printf(str, "%s on %s '%s' (%d): call=%s, status='%s'",
    1512             :                            op_key, node_name, exit_status, rc, call_id,
    1513             :                            lrm_status);
    1514             : 
    1515           0 :     if (!pcmk__str_empty(exit_reason)) {
    1516           0 :         pcmk__g_strcat(str, ", exitreason='", exit_reason, "'", NULL);
    1517             :     }
    1518             : 
    1519           0 :     if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
    1520             :                                 &last_change_epoch) == pcmk_ok) {
    1521           0 :         char *last_change_str = pcmk__epoch2str(&last_change_epoch, 0);
    1522             : 
    1523           0 :         pcmk__g_strcat(str,
    1524             :                        ", " PCMK_XA_LAST_RC_CHANGE "="
    1525             :                        "'", last_change_str, "'", NULL);
    1526           0 :         free(last_change_str);
    1527             :     }
    1528           0 :     if (!pcmk__str_empty(queue_time)) {
    1529           0 :         pcmk__g_strcat(str, ", queued=", queue_time, "ms", NULL);
    1530             :     }
    1531           0 :     if (!pcmk__str_empty(exec_time)) {
    1532           0 :         pcmk__g_strcat(str, ", exec=", exec_time, "ms", NULL);
    1533             :     }
    1534             : 
    1535           0 :     out->list_item(out, NULL, "%s", str->str);
    1536           0 :     g_string_free(str, TRUE);
    1537           0 : }
    1538             : 
    1539             : PCMK__OUTPUT_ARGS("failed-action", "xmlNode *", "uint32_t")
    1540             : static int
    1541           0 : failed_action_default(pcmk__output_t *out, va_list args)
    1542             : {
    1543           0 :     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
    1544           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1545             : 
    1546           0 :     const char *op_key = pcmk__xe_history_key(xml_op);
    1547           0 :     const char *node_name = crm_element_value(xml_op, PCMK_XA_UNAME);
    1548           0 :     const char *exit_reason = crm_element_value(xml_op, PCMK_XA_EXIT_REASON);
    1549           0 :     const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
    1550             : 
    1551             :     int rc;
    1552             :     int status;
    1553             : 
    1554           0 :     pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_RC_CODE), &rc, 0);
    1555             : 
    1556           0 :     pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status,
    1557             :                        0);
    1558             : 
    1559           0 :     if (pcmk__str_empty(node_name)) {
    1560           0 :         node_name = "unknown node";
    1561             :     }
    1562             : 
    1563           0 :     if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
    1564           0 :         failed_action_technical(out, xml_op, op_key, node_name, rc, status,
    1565             :                                 exit_reason, exec_time);
    1566             :     } else {
    1567           0 :         failed_action_friendly(out, xml_op, op_key, node_name, rc, status,
    1568             :                                exit_reason, exec_time);
    1569             :     }
    1570           0 :     return pcmk_rc_ok;
    1571             : }
    1572             : 
    1573             : PCMK__OUTPUT_ARGS("failed-action", "xmlNode *", "uint32_t")
    1574             : static int
    1575           0 : failed_action_xml(pcmk__output_t *out, va_list args) {
    1576           0 :     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
    1577           0 :     uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
    1578             : 
    1579           0 :     const char *op_key = pcmk__xe_history_key(xml_op);
    1580           0 :     const char *op_key_name = PCMK_XA_OP_KEY;
    1581             :     int rc;
    1582             :     int status;
    1583           0 :     const char *uname = crm_element_value(xml_op, PCMK_XA_UNAME);
    1584           0 :     const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
    1585           0 :     const char *exitstatus = NULL;
    1586           0 :     const char *exit_reason = pcmk__s(crm_element_value(xml_op,
    1587             :                                                         PCMK_XA_EXIT_REASON),
    1588             :                                       "none");
    1589           0 :     const char *status_s = NULL;
    1590             : 
    1591           0 :     time_t epoch = 0;
    1592           0 :     gchar *exit_reason_esc = NULL;
    1593           0 :     char *rc_s = NULL;
    1594           0 :     xmlNodePtr node = NULL;
    1595             : 
    1596           0 :     if (pcmk__xml_needs_escape(exit_reason, pcmk__xml_escape_attr)) {
    1597           0 :         exit_reason_esc = pcmk__xml_escape(exit_reason, pcmk__xml_escape_attr);
    1598           0 :         exit_reason = exit_reason_esc;
    1599             :     }
    1600           0 :     pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_RC_CODE), &rc, 0);
    1601           0 :     pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status,
    1602             :                        0);
    1603             : 
    1604           0 :     if (crm_element_value(xml_op, PCMK__XA_OPERATION_KEY) == NULL) {
    1605           0 :         op_key_name = PCMK_XA_ID;
    1606             :     }
    1607           0 :     exitstatus = services_ocf_exitcode_str(rc);
    1608           0 :     rc_s = pcmk__itoa(rc);
    1609           0 :     status_s = pcmk_exec_status_str(status);
    1610           0 :     node = pcmk__output_create_xml_node(out, PCMK_XE_FAILURE,
    1611             :                                         op_key_name, op_key,
    1612             :                                         PCMK_XA_NODE, uname,
    1613             :                                         PCMK_XA_EXITSTATUS, exitstatus,
    1614             :                                         PCMK_XA_EXITREASON, exit_reason,
    1615             :                                         PCMK_XA_EXITCODE, rc_s,
    1616             :                                         PCMK_XA_CALL, call_id,
    1617             :                                         PCMK_XA_STATUS, status_s,
    1618             :                                         NULL);
    1619           0 :     free(rc_s);
    1620             : 
    1621           0 :     if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
    1622           0 :                                  &epoch) == pcmk_ok) && (epoch > 0)) {
    1623             : 
    1624           0 :         const char *queue_time = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
    1625           0 :         const char *exec = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
    1626           0 :         const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
    1627           0 :         guint interval_ms = 0;
    1628           0 :         char *interval_ms_s = NULL;
    1629           0 :         char *rc_change = pcmk__epoch2str(&epoch,
    1630             :                                           crm_time_log_date
    1631             :                                           |crm_time_log_timeofday
    1632             :                                           |crm_time_log_with_timezone);
    1633             : 
    1634           0 :         crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
    1635           0 :         interval_ms_s = crm_strdup_printf("%u", interval_ms);
    1636             : 
    1637           0 :         pcmk__xe_set_props(node,
    1638             :                            PCMK_XA_LAST_RC_CHANGE, rc_change,
    1639             :                            PCMK_XA_QUEUED, queue_time,
    1640             :                            PCMK_XA_EXEC, exec,
    1641             :                            PCMK_XA_INTERVAL, interval_ms_s,
    1642             :                            PCMK_XA_TASK, task,
    1643             :                            NULL);
    1644             : 
    1645           0 :         free(interval_ms_s);
    1646           0 :         free(rc_change);
    1647             :     }
    1648             : 
    1649           0 :     g_free(exit_reason_esc);
    1650           0 :     return pcmk_rc_ok;
    1651             : }
    1652             : 
    1653             : PCMK__OUTPUT_ARGS("failed-action-list", "pcmk_scheduler_t *", "GList *",
    1654             :                   "GList *", "uint32_t", "bool")
    1655             : static int
    1656           0 : failed_action_list(pcmk__output_t *out, va_list args) {
    1657           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    1658           0 :     GList *only_node = va_arg(args, GList *);
    1659           0 :     GList *only_rsc = va_arg(args, GList *);
    1660           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1661           0 :     bool print_spacer = va_arg(args, int);
    1662             : 
    1663           0 :     xmlNode *xml_op = NULL;
    1664           0 :     int rc = pcmk_rc_no_output;
    1665             : 
    1666           0 :     if (xmlChildElementCount(scheduler->failed) == 0) {
    1667           0 :         return rc;
    1668             :     }
    1669             : 
    1670           0 :     for (xml_op = pcmk__xe_first_child(scheduler->failed, NULL, NULL, NULL);
    1671           0 :          xml_op != NULL; xml_op = pcmk__xe_next(xml_op)) {
    1672             : 
    1673           0 :         char *rsc = NULL;
    1674             : 
    1675           0 :         if (!pcmk__str_in_list(crm_element_value(xml_op, PCMK_XA_UNAME),
    1676             :                                only_node,
    1677             :                                pcmk__str_star_matches|pcmk__str_casei)) {
    1678           0 :             continue;
    1679             :         }
    1680             : 
    1681           0 :         if (pcmk_xe_mask_probe_failure(xml_op)) {
    1682           0 :             continue;
    1683             :         }
    1684             : 
    1685           0 :         if (!parse_op_key(pcmk__xe_history_key(xml_op), &rsc, NULL, NULL)) {
    1686           0 :             continue;
    1687             :         }
    1688             : 
    1689           0 :         if (!pcmk__str_in_list(rsc, only_rsc, pcmk__str_star_matches)) {
    1690           0 :             free(rsc);
    1691           0 :             continue;
    1692             :         }
    1693             : 
    1694           0 :         free(rsc);
    1695             : 
    1696           0 :         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Resource Actions");
    1697           0 :         out->message(out, "failed-action", xml_op, show_opts);
    1698             :     }
    1699             : 
    1700           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    1701           0 :     return rc;
    1702             : }
    1703             : 
    1704             : static void
    1705           0 : status_node(pcmk_node_t *node, xmlNodePtr parent, uint32_t show_opts)
    1706             : {
    1707           0 :     int health = pe__node_health(node);
    1708           0 :     xmlNode *child = NULL;
    1709             : 
    1710             :     // Cluster membership
    1711           0 :     if (node->details->online) {
    1712           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1713             :                                   PCMK_VALUE_ONLINE);
    1714           0 :         pcmk__xe_set_content(child, " online");
    1715             : 
    1716             :     } else {
    1717           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1718             :                                   PCMK_VALUE_OFFLINE);
    1719           0 :         pcmk__xe_set_content(child, " OFFLINE");
    1720             :     }
    1721             : 
    1722             :     // Standby mode
    1723           0 :     if (node->details->standby_onfail && (node->details->running_rsc != NULL)) {
    1724           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1725             :                                   PCMK_VALUE_STANDBY);
    1726           0 :         pcmk__xe_set_content(child,
    1727             :                              " (in standby due to " PCMK_META_ON_FAIL ","
    1728             :                              " with active resources)");
    1729             : 
    1730           0 :     } else if (node->details->standby_onfail) {
    1731           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1732             :                                   PCMK_VALUE_STANDBY);
    1733           0 :         pcmk__xe_set_content(child,
    1734             :                              " (in standby due to " PCMK_META_ON_FAIL ")");
    1735             : 
    1736           0 :     } else if (node->details->standby && (node->details->running_rsc != NULL)) {
    1737           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1738             :                                   PCMK_VALUE_STANDBY);
    1739           0 :         pcmk__xe_set_content(child,
    1740             :                              " (in standby, with active resources)");
    1741             : 
    1742           0 :     } else if (node->details->standby) {
    1743           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1744             :                                   PCMK_VALUE_STANDBY);
    1745           0 :         pcmk__xe_set_content(child, " (in standby)");
    1746             :     }
    1747             : 
    1748             :     // Maintenance mode
    1749           0 :     if (node->details->maintenance) {
    1750           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1751             :                                   PCMK__VALUE_MAINT);
    1752           0 :         pcmk__xe_set_content(child, " (in maintenance mode)");
    1753             :     }
    1754             : 
    1755             :     // Node health
    1756           0 :     if (health < 0) {
    1757           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1758             :                                   PCMK__VALUE_HEALTH_RED);
    1759           0 :         pcmk__xe_set_content(child, " (health is RED)");
    1760             : 
    1761           0 :     } else if (health == 0) {
    1762           0 :         child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL,
    1763             :                                   PCMK__VALUE_HEALTH_YELLOW);
    1764           0 :         pcmk__xe_set_content(child, " (health is YELLOW)");
    1765             :     }
    1766             : 
    1767             :     // Feature set
    1768           0 :     if (pcmk_is_set(show_opts, pcmk_show_feature_set)) {
    1769           0 :         const char *feature_set = get_node_feature_set(node);
    1770           0 :         if (feature_set != NULL) {
    1771           0 :             child = pcmk__html_create(parent, PCMK__XE_SPAN, NULL, NULL);
    1772           0 :             pcmk__xe_set_content(child, ", feature set %s", feature_set);
    1773             :         }
    1774             :     }
    1775           0 : }
    1776             : 
    1777             : PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool",
    1778             :                   "GList *", "GList *")
    1779             : static int
    1780           0 : node_html(pcmk__output_t *out, va_list args) {
    1781           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    1782           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1783           0 :     bool full = va_arg(args, int);
    1784           0 :     GList *only_node = va_arg(args, GList *);
    1785           0 :     GList *only_rsc = va_arg(args, GList *);
    1786             : 
    1787           0 :     char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
    1788             : 
    1789           0 :     if (full) {
    1790           0 :         xmlNode *item_node = NULL;
    1791           0 :         xmlNode *child = NULL;
    1792             : 
    1793           0 :         if (pcmk_all_flags_set(show_opts, pcmk_show_brief | pcmk_show_rscs_by_node)) {
    1794           0 :             GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
    1795             : 
    1796           0 :             out->begin_list(out, NULL, NULL, "%s:", node_name);
    1797           0 :             item_node = pcmk__output_xml_create_parent(out, "li", NULL);
    1798           0 :             child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL);
    1799           0 :             pcmk__xe_set_content(child, "Status:");
    1800           0 :             status_node(node, item_node, show_opts);
    1801             : 
    1802           0 :             if (rscs != NULL) {
    1803           0 :                 uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
    1804           0 :                 out->begin_list(out, NULL, NULL, "Resources");
    1805           0 :                 pe__rscs_brief_output(out, rscs, new_show_opts);
    1806           0 :                 out->end_list(out);
    1807             :             }
    1808             : 
    1809           0 :             pcmk__output_xml_pop_parent(out);
    1810           0 :             out->end_list(out);
    1811             : 
    1812           0 :         } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    1813           0 :             GList *lpc2 = NULL;
    1814           0 :             int rc = pcmk_rc_no_output;
    1815             : 
    1816           0 :             out->begin_list(out, NULL, NULL, "%s:", node_name);
    1817           0 :             item_node = pcmk__output_xml_create_parent(out, "li", NULL);
    1818           0 :             child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL);
    1819           0 :             pcmk__xe_set_content(child, "Status:");
    1820           0 :             status_node(node, item_node, show_opts);
    1821             : 
    1822           0 :             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
    1823           0 :                 pcmk_resource_t *rsc = (pcmk_resource_t *) lpc2->data;
    1824           0 :                 PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Resources");
    1825             : 
    1826           0 :                 show_opts |= pcmk_show_rsc_only;
    1827           0 :                 out->message(out, pcmk__map_element_name(rsc->xml), show_opts,
    1828             :                              rsc, only_node, only_rsc);
    1829             :             }
    1830             : 
    1831           0 :             PCMK__OUTPUT_LIST_FOOTER(out, rc);
    1832           0 :             pcmk__output_xml_pop_parent(out);
    1833           0 :             out->end_list(out);
    1834             : 
    1835             :         } else {
    1836           0 :             item_node = pcmk__output_create_xml_node(out, "li", NULL);
    1837           0 :             child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL,
    1838             :                                       PCMK__VALUE_BOLD);
    1839           0 :             pcmk__xe_set_content(child, "%s:", node_name);
    1840           0 :             status_node(node, item_node, show_opts);
    1841             :         }
    1842             :     } else {
    1843           0 :         out->begin_list(out, NULL, NULL, "%s:", node_name);
    1844             :     }
    1845             : 
    1846           0 :     free(node_name);
    1847           0 :     return pcmk_rc_ok;
    1848             : }
    1849             : 
    1850             : /*!
    1851             :  * \internal
    1852             :  * \brief Get a human-friendly textual description of a node's status
    1853             :  *
    1854             :  * \param[in] node  Node to check
    1855             :  *
    1856             :  * \return String representation of node's status
    1857             :  */
    1858             : static const char *
    1859           0 : node_text_status(const pcmk_node_t *node)
    1860             : {
    1861           0 :     if (node->details->unclean) {
    1862           0 :         if (node->details->online) {
    1863           0 :             return "UNCLEAN (online)";
    1864             : 
    1865           0 :         } else if (node->details->pending) {
    1866           0 :             return "UNCLEAN (pending)";
    1867             : 
    1868             :         } else {
    1869           0 :             return "UNCLEAN (offline)";
    1870             :         }
    1871             : 
    1872           0 :     } else if (node->details->pending) {
    1873           0 :         return "pending";
    1874             : 
    1875           0 :     } else if (node->details->standby_onfail && node->details->online) {
    1876           0 :         return "standby (" PCMK_META_ON_FAIL ")";
    1877             : 
    1878           0 :     } else if (node->details->standby) {
    1879           0 :         if (node->details->online) {
    1880           0 :             if (node->details->running_rsc) {
    1881           0 :                 return "standby (with active resources)";
    1882             :             } else {
    1883           0 :                 return "standby";
    1884             :             }
    1885             :         } else {
    1886           0 :             return "OFFLINE (standby)";
    1887             :         }
    1888             : 
    1889           0 :     } else if (node->details->maintenance) {
    1890           0 :         if (node->details->online) {
    1891           0 :             return "maintenance";
    1892             :         } else {
    1893           0 :             return "OFFLINE (maintenance)";
    1894             :         }
    1895             : 
    1896           0 :     } else if (node->details->online) {
    1897           0 :         return "online";
    1898             :     }
    1899             : 
    1900           0 :     return "OFFLINE";
    1901             : }
    1902             : 
    1903             : PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *",
    1904             :                   "GList *")
    1905             : static int
    1906           0 : node_text(pcmk__output_t *out, va_list args) {
    1907           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    1908           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    1909           0 :     bool full = va_arg(args, int);
    1910           0 :     GList *only_node = va_arg(args, GList *);
    1911           0 :     GList *only_rsc = va_arg(args, GList *);
    1912             : 
    1913           0 :     if (full) {
    1914           0 :         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
    1915           0 :         GString *str = g_string_sized_new(64);
    1916           0 :         int health = pe__node_health(node);
    1917             : 
    1918             :         // Create a summary line with node type, name, and status
    1919           0 :         if (pcmk__is_guest_or_bundle_node(node)) {
    1920           0 :             g_string_append(str, "GuestNode");
    1921           0 :         } else if (pcmk__is_remote_node(node)) {
    1922           0 :             g_string_append(str, "RemoteNode");
    1923             :         } else {
    1924           0 :             g_string_append(str, "Node");
    1925             :         }
    1926           0 :         pcmk__g_strcat(str, " ", node_name, ": ", node_text_status(node), NULL);
    1927             : 
    1928           0 :         if (health < 0) {
    1929           0 :             g_string_append(str, " (health is RED)");
    1930           0 :         } else if (health == 0) {
    1931           0 :             g_string_append(str, " (health is YELLOW)");
    1932             :         }
    1933           0 :         if (pcmk_is_set(show_opts, pcmk_show_feature_set)) {
    1934           0 :             const char *feature_set = get_node_feature_set(node);
    1935           0 :             if (feature_set != NULL) {
    1936           0 :                 pcmk__g_strcat(str, ", feature set ", feature_set, NULL);
    1937             :             }
    1938             :         }
    1939             : 
    1940             :         /* If we're grouping by node, print its resources */
    1941           0 :         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    1942           0 :             if (pcmk_is_set(show_opts, pcmk_show_brief)) {
    1943           0 :                 GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
    1944             : 
    1945           0 :                 if (rscs != NULL) {
    1946           0 :                     uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
    1947           0 :                     out->begin_list(out, NULL, NULL, "%s", str->str);
    1948           0 :                     out->begin_list(out, NULL, NULL, "Resources");
    1949             : 
    1950           0 :                     pe__rscs_brief_output(out, rscs, new_show_opts);
    1951             : 
    1952           0 :                     out->end_list(out);
    1953           0 :                     out->end_list(out);
    1954             : 
    1955           0 :                     g_list_free(rscs);
    1956             :                 }
    1957             : 
    1958             :             } else {
    1959           0 :                 GList *gIter2 = NULL;
    1960             : 
    1961           0 :                 out->begin_list(out, NULL, NULL, "%s", str->str);
    1962           0 :                 out->begin_list(out, NULL, NULL, "Resources");
    1963             : 
    1964           0 :                 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
    1965           0 :                     pcmk_resource_t *rsc = (pcmk_resource_t *) gIter2->data;
    1966             : 
    1967           0 :                     show_opts |= pcmk_show_rsc_only;
    1968           0 :                     out->message(out, pcmk__map_element_name(rsc->xml),
    1969             :                                  show_opts, rsc, only_node, only_rsc);
    1970             :                 }
    1971             : 
    1972           0 :                 out->end_list(out);
    1973           0 :                 out->end_list(out);
    1974             :             }
    1975             :         } else {
    1976           0 :             out->list_item(out, NULL, "%s", str->str);
    1977             :         }
    1978             : 
    1979           0 :         g_string_free(str, TRUE);
    1980           0 :         free(node_name);
    1981             :     } else {
    1982           0 :         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
    1983           0 :         out->begin_list(out, NULL, NULL, "Node: %s", node_name);
    1984           0 :         free(node_name);
    1985             :     }
    1986             : 
    1987           0 :     return pcmk_rc_ok;
    1988             : }
    1989             : 
    1990             : /*!
    1991             :  * \internal
    1992             :  * \brief Convert an integer health value to a string representation
    1993             :  *
    1994             :  * \param[in] health  Integer health value
    1995             :  *
    1996             :  * \retval \c PCMK_VALUE_RED if \p health is less than 0
    1997             :  * \retval \c PCMK_VALUE_YELLOW if \p health is equal to 0
    1998             :  * \retval \c PCMK_VALUE_GREEN if \p health is greater than 0
    1999             :  */
    2000             : static const char *
    2001           0 : health_text(int health)
    2002             : {
    2003           0 :     if (health < 0) {
    2004           0 :         return PCMK_VALUE_RED;
    2005           0 :     } else if (health == 0) {
    2006           0 :         return PCMK_VALUE_YELLOW;
    2007             :     } else {
    2008           0 :         return PCMK_VALUE_GREEN;
    2009             :     }
    2010             : }
    2011             : 
    2012             : /*!
    2013             :  * \internal
    2014             :  * \brief Convert a node type to a string representation
    2015             :  *
    2016             :  * \param[in] type  Node type
    2017             :  *
    2018             :  * \retval \c PCMK_VALUE_MEMBER if \p node_type is \c pcmk_node_variant_cluster
    2019             :  * \retval \c PCMK_VALUE_REMOTE if \p node_type is \c pcmk_node_variant_remote
    2020             :  * \retval \c PCMK__VALUE_PING if \p node_type is \c node_ping
    2021             :  * \retval \c PCMK_VALUE_UNKNOWN otherwise
    2022             :  */
    2023             : static const char *
    2024           0 : node_type_str(enum node_type type)
    2025             : {
    2026           0 :     switch (type) {
    2027           0 :         case pcmk_node_variant_cluster:
    2028           0 :             return PCMK_VALUE_MEMBER;
    2029           0 :         case pcmk_node_variant_remote:
    2030           0 :             return PCMK_VALUE_REMOTE;
    2031           0 :         case node_ping:
    2032           0 :             return PCMK__VALUE_PING;
    2033           0 :         default:
    2034           0 :             return PCMK_VALUE_UNKNOWN;
    2035             :     }
    2036             : }
    2037             : 
    2038             : PCMK__OUTPUT_ARGS("node", "pcmk_node_t *", "uint32_t", "bool", "GList *",
    2039             :                   "GList *")
    2040             : static int
    2041           0 : node_xml(pcmk__output_t *out, va_list args) {
    2042           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    2043           0 :     uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
    2044           0 :     bool full = va_arg(args, int);
    2045           0 :     GList *only_node = va_arg(args, GList *);
    2046           0 :     GList *only_rsc = va_arg(args, GList *);
    2047             : 
    2048           0 :     if (full) {
    2049           0 :         const char *online = pcmk__btoa(node->details->online);
    2050           0 :         const char *standby = pcmk__btoa(node->details->standby);
    2051           0 :         const char *standby_onfail = pcmk__btoa(node->details->standby_onfail);
    2052           0 :         const char *maintenance = pcmk__btoa(node->details->maintenance);
    2053           0 :         const char *pending = pcmk__btoa(node->details->pending);
    2054           0 :         const char *unclean = pcmk__btoa(node->details->unclean);
    2055           0 :         const char *health = health_text(pe__node_health(node));
    2056           0 :         const char *feature_set = get_node_feature_set(node);
    2057           0 :         const char *shutdown = pcmk__btoa(node->details->shutdown);
    2058           0 :         const char *expected_up = pcmk__btoa(node->details->expected_up);
    2059           0 :         const char *is_dc = pcmk__btoa(node->details->is_dc);
    2060           0 :         int length = g_list_length(node->details->running_rsc);
    2061           0 :         char *resources_running = pcmk__itoa(length);
    2062           0 :         const char *node_type = node_type_str(node->details->type);
    2063             : 
    2064           0 :         int rc = pcmk_rc_ok;
    2065             : 
    2066           0 :         rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_NODE,
    2067           0 :                                       PCMK_XA_NAME, node->details->uname,
    2068           0 :                                       PCMK_XA_ID, node->details->id,
    2069             :                                       PCMK_XA_ONLINE, online,
    2070             :                                       PCMK_XA_STANDBY, standby,
    2071             :                                       PCMK_XA_STANDBY_ONFAIL, standby_onfail,
    2072             :                                       PCMK_XA_MAINTENANCE, maintenance,
    2073             :                                       PCMK_XA_PENDING, pending,
    2074             :                                       PCMK_XA_UNCLEAN, unclean,
    2075             :                                       PCMK_XA_HEALTH, health,
    2076             :                                       PCMK_XA_FEATURE_SET, feature_set,
    2077             :                                       PCMK_XA_SHUTDOWN, shutdown,
    2078             :                                       PCMK_XA_EXPECTED_UP, expected_up,
    2079             :                                       PCMK_XA_IS_DC, is_dc,
    2080             :                                       PCMK_XA_RESOURCES_RUNNING, resources_running,
    2081             :                                       PCMK_XA_TYPE, node_type,
    2082             :                                       NULL);
    2083             : 
    2084           0 :         free(resources_running);
    2085           0 :         CRM_ASSERT(rc == pcmk_rc_ok);
    2086             : 
    2087           0 :         if (pcmk__is_guest_or_bundle_node(node)) {
    2088           0 :             xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
    2089           0 :             crm_xml_add(xml_node, PCMK_XA_ID_AS_RESOURCE,
    2090           0 :                         node->details->remote_rsc->container->id);
    2091             :         }
    2092             : 
    2093           0 :         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    2094           0 :             GList *lpc = NULL;
    2095             : 
    2096           0 :             for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
    2097           0 :                 pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
    2098             : 
    2099           0 :                 show_opts |= pcmk_show_rsc_only;
    2100           0 :                 out->message(out, pcmk__map_element_name(rsc->xml), show_opts,
    2101             :                              rsc, only_node, only_rsc);
    2102             :             }
    2103             :         }
    2104             : 
    2105           0 :         out->end_list(out);
    2106             :     } else {
    2107           0 :         pcmk__output_xml_create_parent(out, PCMK_XE_NODE,
    2108           0 :                                        PCMK_XA_NAME, node->details->uname,
    2109             :                                        NULL);
    2110             :     }
    2111             : 
    2112           0 :     return pcmk_rc_ok;
    2113             : }
    2114             : 
    2115             : PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
    2116             : static int
    2117           0 : node_attribute_text(pcmk__output_t *out, va_list args) {
    2118           0 :     const char *name = va_arg(args, const char *);
    2119           0 :     const char *value = va_arg(args, const char *);
    2120           0 :     bool add_extra = va_arg(args, int);
    2121           0 :     int expected_score = va_arg(args, int);
    2122             : 
    2123           0 :     if (add_extra) {
    2124             :         int v;
    2125             : 
    2126           0 :         if (value == NULL) {
    2127           0 :             v = 0;
    2128             :         } else {
    2129           0 :             pcmk__scan_min_int(value, &v, INT_MIN);
    2130             :         }
    2131           0 :         if (v <= 0) {
    2132           0 :             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value);
    2133           0 :         } else if (v < expected_score) {
    2134           0 :             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score);
    2135             :         } else {
    2136           0 :             out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
    2137             :         }
    2138             :     } else {
    2139           0 :         out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
    2140             :     }
    2141             : 
    2142           0 :     return pcmk_rc_ok;
    2143             : }
    2144             : 
    2145             : PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
    2146             : static int
    2147           0 : node_attribute_html(pcmk__output_t *out, va_list args) {
    2148           0 :     const char *name = va_arg(args, const char *);
    2149           0 :     const char *value = va_arg(args, const char *);
    2150           0 :     bool add_extra = va_arg(args, int);
    2151           0 :     int expected_score = va_arg(args, int);
    2152             : 
    2153           0 :     if (add_extra) {
    2154           0 :         int v = 0;
    2155           0 :         xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li", NULL);
    2156           0 :         xmlNode *child = NULL;
    2157             : 
    2158           0 :         if (value != NULL) {
    2159           0 :             pcmk__scan_min_int(value, &v, INT_MIN);
    2160             :         }
    2161             : 
    2162           0 :         child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL, NULL);
    2163           0 :         pcmk__xe_set_content(child, "%s: %s", name, value);
    2164             : 
    2165           0 :         if (v <= 0) {
    2166           0 :             child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL,
    2167             :                                       PCMK__VALUE_BOLD);
    2168           0 :             pcmk__xe_set_content(child, "(connectivity is lost)");
    2169             : 
    2170           0 :         } else if (v < expected_score) {
    2171           0 :             child = pcmk__html_create(item_node, PCMK__XE_SPAN, NULL,
    2172             :                                       PCMK__VALUE_BOLD);
    2173           0 :             pcmk__xe_set_content(child,
    2174             :                                  "(connectivity is degraded -- expected %d)",
    2175             :                                  expected_score);
    2176             :         }
    2177             :     } else {
    2178           0 :         out->list_item(out, NULL, "%s: %s", name, value);
    2179             :     }
    2180             : 
    2181           0 :     return pcmk_rc_ok;
    2182             : }
    2183             : 
    2184             : PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNode *")
    2185             : static int
    2186           0 : node_and_op(pcmk__output_t *out, va_list args) {
    2187           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    2188           0 :     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
    2189             : 
    2190           0 :     pcmk_resource_t *rsc = NULL;
    2191           0 :     gchar *node_str = NULL;
    2192           0 :     char *last_change_str = NULL;
    2193             : 
    2194           0 :     const char *op_rsc = crm_element_value(xml_op, PCMK_XA_RESOURCE);
    2195             :     int status;
    2196           0 :     time_t last_change = 0;
    2197             : 
    2198           0 :     pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS), &status,
    2199             :                        PCMK_EXEC_UNKNOWN);
    2200             : 
    2201           0 :     rsc = pe_find_resource(scheduler->resources, op_rsc);
    2202             : 
    2203           0 :     if (rsc) {
    2204           0 :         const pcmk_node_t *node = pcmk__current_node(rsc);
    2205           0 :         const char *target_role = g_hash_table_lookup(rsc->meta,
    2206             :                                                       PCMK_META_TARGET_ROLE);
    2207           0 :         uint32_t show_opts = pcmk_show_rsc_only | pcmk_show_pending;
    2208             : 
    2209           0 :         if (node == NULL) {
    2210           0 :             node = rsc->pending_node;
    2211             :         }
    2212             : 
    2213           0 :         node_str = pcmk__native_output_string(rsc, rsc_printable_id(rsc), node,
    2214             :                                               show_opts, target_role, false);
    2215             :     } else {
    2216           0 :         node_str = crm_strdup_printf("Unknown resource %s", op_rsc);
    2217             :     }
    2218             : 
    2219           0 :     if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
    2220             :                                 &last_change) == pcmk_ok) {
    2221           0 :         const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
    2222             : 
    2223           0 :         last_change_str = crm_strdup_printf(", %s='%s', exec=%sms",
    2224             :                                             PCMK_XA_LAST_RC_CHANGE,
    2225             :                                             pcmk__trim(ctime(&last_change)),
    2226             :                                             exec_time);
    2227             :     }
    2228             : 
    2229           0 :     out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s",
    2230             :                    node_str, pcmk__xe_history_key(xml_op),
    2231             :                    crm_element_value(xml_op, PCMK_XA_UNAME),
    2232             :                    crm_element_value(xml_op, PCMK__XA_CALL_ID),
    2233             :                    crm_element_value(xml_op, PCMK__XA_RC_CODE),
    2234             :                    last_change_str ? last_change_str : "",
    2235             :                    pcmk_exec_status_str(status));
    2236             : 
    2237           0 :     g_free(node_str);
    2238           0 :     free(last_change_str);
    2239           0 :     return pcmk_rc_ok;
    2240             : }
    2241             : 
    2242             : PCMK__OUTPUT_ARGS("node-and-op", "pcmk_scheduler_t *", "xmlNode *")
    2243             : static int
    2244           0 : node_and_op_xml(pcmk__output_t *out, va_list args) {
    2245           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    2246           0 :     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
    2247             : 
    2248           0 :     pcmk_resource_t *rsc = NULL;
    2249           0 :     const char *uname = crm_element_value(xml_op, PCMK_XA_UNAME);
    2250           0 :     const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
    2251           0 :     const char *rc_s = crm_element_value(xml_op, PCMK__XA_RC_CODE);
    2252           0 :     const char *status_s = NULL;
    2253           0 :     const char *op_rsc = crm_element_value(xml_op, PCMK_XA_RESOURCE);
    2254             :     int status;
    2255           0 :     time_t last_change = 0;
    2256           0 :     xmlNode *node = NULL;
    2257             : 
    2258           0 :     pcmk__scan_min_int(crm_element_value(xml_op, PCMK__XA_OP_STATUS),
    2259             :                        &status, PCMK_EXEC_UNKNOWN);
    2260           0 :     status_s = pcmk_exec_status_str(status);
    2261             : 
    2262           0 :     node = pcmk__output_create_xml_node(out, PCMK_XE_OPERATION,
    2263             :                                         PCMK_XA_OP, pcmk__xe_history_key(xml_op),
    2264             :                                         PCMK_XA_NODE, uname,
    2265             :                                         PCMK_XA_CALL, call_id,
    2266             :                                         PCMK_XA_RC, rc_s,
    2267             :                                         PCMK_XA_STATUS, status_s,
    2268             :                                         NULL);
    2269             : 
    2270           0 :     rsc = pe_find_resource(scheduler->resources, op_rsc);
    2271             : 
    2272           0 :     if (rsc) {
    2273           0 :         const char *class = crm_element_value(rsc->xml, PCMK_XA_CLASS);
    2274           0 :         const char *provider = crm_element_value(rsc->xml, PCMK_XA_PROVIDER);
    2275           0 :         const char *kind = crm_element_value(rsc->xml, PCMK_XA_TYPE);
    2276           0 :         bool has_provider = pcmk_is_set(pcmk_get_ra_caps(class),
    2277             :                                         pcmk_ra_cap_provider);
    2278             : 
    2279           0 :         char *agent_tuple = crm_strdup_printf("%s:%s:%s",
    2280             :                                               class,
    2281             :                                               (has_provider? provider : ""),
    2282             :                                               kind);
    2283             : 
    2284           0 :         pcmk__xe_set_props(node,
    2285             :                            PCMK_XA_RSC, rsc_printable_id(rsc),
    2286             :                            PCMK_XA_AGENT, agent_tuple,
    2287             :                            NULL);
    2288           0 :         free(agent_tuple);
    2289             :     }
    2290             : 
    2291           0 :     if (crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
    2292             :                                 &last_change) == pcmk_ok) {
    2293           0 :         const char *last_rc_change = pcmk__trim(ctime(&last_change));
    2294           0 :         const char *exec_time = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
    2295             : 
    2296           0 :         pcmk__xe_set_props(node,
    2297             :                            PCMK_XA_LAST_RC_CHANGE, last_rc_change,
    2298             :                            PCMK_XA_EXEC_TIME, exec_time,
    2299             :                            NULL);
    2300             :     }
    2301             : 
    2302           0 :     return pcmk_rc_ok;
    2303             : }
    2304             : 
    2305             : PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "bool", "int")
    2306             : static int
    2307           0 : node_attribute_xml(pcmk__output_t *out, va_list args) {
    2308           0 :     const char *name = va_arg(args, const char *);
    2309           0 :     const char *value = va_arg(args, const char *);
    2310           0 :     bool add_extra = va_arg(args, int);
    2311           0 :     int expected_score = va_arg(args, int);
    2312             : 
    2313           0 :     xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_ATTRIBUTE,
    2314             :                                                    PCMK_XA_NAME, name,
    2315             :                                                    PCMK_XA_VALUE, value,
    2316             :                                                    NULL);
    2317             : 
    2318           0 :     if (add_extra) {
    2319           0 :         char *buf = pcmk__itoa(expected_score);
    2320           0 :         crm_xml_add(node, PCMK_XA_EXPECTED, buf);
    2321           0 :         free(buf);
    2322             :     }
    2323             : 
    2324           0 :     return pcmk_rc_ok;
    2325             : }
    2326             : 
    2327             : PCMK__OUTPUT_ARGS("node-attribute-list", "pcmk_scheduler_t *", "uint32_t",
    2328             :                   "bool", "GList *", "GList *")
    2329             : static int
    2330           0 : node_attribute_list(pcmk__output_t *out, va_list args) {
    2331           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    2332           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2333           0 :     bool print_spacer = va_arg(args, int);
    2334           0 :     GList *only_node = va_arg(args, GList *);
    2335           0 :     GList *only_rsc = va_arg(args, GList *);
    2336             : 
    2337           0 :     int rc = pcmk_rc_no_output;
    2338             : 
    2339             :     /* Display each node's attributes */
    2340           0 :     for (GList *gIter = scheduler->nodes; gIter != NULL; gIter = gIter->next) {
    2341           0 :         pcmk_node_t *node = gIter->data;
    2342             : 
    2343           0 :         GList *attr_list = NULL;
    2344             :         GHashTableIter iter;
    2345             :         gpointer key;
    2346             : 
    2347           0 :         if (!node || !node->details || !node->details->online) {
    2348           0 :             continue;
    2349             :         }
    2350             : 
    2351           0 :         g_hash_table_iter_init(&iter, node->details->attrs);
    2352           0 :         while (g_hash_table_iter_next (&iter, &key, NULL)) {
    2353           0 :             attr_list = filter_attr_list(attr_list, key);
    2354             :         }
    2355             : 
    2356           0 :         if (attr_list == NULL) {
    2357           0 :             continue;
    2358             :         }
    2359             : 
    2360           0 :         if (!pcmk__str_in_list(node->details->uname, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
    2361           0 :             g_list_free(attr_list);
    2362           0 :             continue;
    2363             :         }
    2364             : 
    2365           0 :         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node Attributes");
    2366             : 
    2367           0 :         out->message(out, "node", node, show_opts, false, only_node, only_rsc);
    2368             : 
    2369           0 :         for (GList *aIter = attr_list; aIter != NULL; aIter = aIter->next) {
    2370           0 :             const char *name = aIter->data;
    2371           0 :             const char *value = NULL;
    2372           0 :             int expected_score = 0;
    2373           0 :             bool add_extra = false;
    2374             : 
    2375           0 :             value = pcmk__node_attr(node, name, NULL, pcmk__rsc_node_current);
    2376             : 
    2377           0 :             add_extra = add_extra_info(node, node->details->running_rsc,
    2378             :                                        scheduler, name, &expected_score);
    2379             : 
    2380             :             /* Print attribute name and value */
    2381           0 :             out->message(out, "node-attribute", name, value, add_extra,
    2382             :                          expected_score);
    2383             :         }
    2384             : 
    2385           0 :         g_list_free(attr_list);
    2386           0 :         out->end_list(out);
    2387             :     }
    2388             : 
    2389           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    2390           0 :     return rc;
    2391             : }
    2392             : 
    2393             : PCMK__OUTPUT_ARGS("node-capacity", "const pcmk_node_t *", "const char *")
    2394             : static int
    2395           0 : node_capacity(pcmk__output_t *out, va_list args)
    2396             : {
    2397           0 :     const pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    2398           0 :     const char *comment = va_arg(args, const char *);
    2399             : 
    2400           0 :     char *dump_text = crm_strdup_printf("%s: %s capacity:",
    2401             :                                         comment, pcmk__node_name(node));
    2402             : 
    2403           0 :     g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
    2404           0 :     out->list_item(out, NULL, "%s", dump_text);
    2405           0 :     free(dump_text);
    2406             : 
    2407           0 :     return pcmk_rc_ok;
    2408             : }
    2409             : 
    2410             : PCMK__OUTPUT_ARGS("node-capacity", "const pcmk_node_t *", "const char *")
    2411             : static int
    2412           0 : node_capacity_xml(pcmk__output_t *out, va_list args)
    2413             : {
    2414           0 :     const pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    2415           0 :     const char *uname = node->details->uname;
    2416           0 :     const char *comment = va_arg(args, const char *);
    2417             : 
    2418           0 :     xmlNodePtr xml_node = pcmk__output_create_xml_node(out, PCMK_XE_CAPACITY,
    2419             :                                                        PCMK_XA_NODE, uname,
    2420             :                                                        PCMK_XA_COMMENT, comment,
    2421             :                                                        NULL);
    2422           0 :     g_hash_table_foreach(node->details->utilization, add_dump_node, xml_node);
    2423             : 
    2424           0 :     return pcmk_rc_ok;
    2425             : }
    2426             : 
    2427             : PCMK__OUTPUT_ARGS("node-history-list", "pcmk_scheduler_t *", "pcmk_node_t *",
    2428             :                   "xmlNode *", "GList *", "GList *", "uint32_t", "uint32_t")
    2429             : static int
    2430           0 : node_history_list(pcmk__output_t *out, va_list args) {
    2431           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    2432           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    2433           0 :     xmlNode *node_state = va_arg(args, xmlNode *);
    2434           0 :     GList *only_node = va_arg(args, GList *);
    2435           0 :     GList *only_rsc = va_arg(args, GList *);
    2436           0 :     uint32_t section_opts = va_arg(args, uint32_t);
    2437           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2438             : 
    2439           0 :     xmlNode *lrm_rsc = NULL;
    2440           0 :     xmlNode *rsc_entry = NULL;
    2441           0 :     int rc = pcmk_rc_no_output;
    2442             : 
    2443           0 :     lrm_rsc = pcmk__xe_first_child(node_state, PCMK__XE_LRM, NULL, NULL);
    2444           0 :     lrm_rsc = pcmk__xe_first_child(lrm_rsc, PCMK__XE_LRM_RESOURCES, NULL, NULL);
    2445             : 
    2446             :     /* Print history of each of the node's resources */
    2447           0 :     for (rsc_entry = pcmk__xe_first_child(lrm_rsc, PCMK__XE_LRM_RESOURCE, NULL,
    2448             :                                           NULL);
    2449           0 :          rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) {
    2450             : 
    2451           0 :         const char *rsc_id = crm_element_value(rsc_entry, PCMK_XA_ID);
    2452           0 :         pcmk_resource_t *rsc = pe_find_resource(scheduler->resources, rsc_id);
    2453           0 :         const pcmk_resource_t *parent = pe__const_top_resource(rsc, false);
    2454             : 
    2455             :         /* We can't use is_filtered here to filter group resources.  For is_filtered,
    2456             :          * we have to decide whether to check the parent or not.  If we check the
    2457             :          * parent, all elements of a group will always be printed because that's how
    2458             :          * is_filtered works for groups.  If we do not check the parent, sometimes
    2459             :          * this will filter everything out.
    2460             :          *
    2461             :          * For other resource types, is_filtered is okay.
    2462             :          */
    2463           0 :         if (parent->variant == pcmk_rsc_variant_group) {
    2464           0 :             if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
    2465             :                                    pcmk__str_star_matches)
    2466           0 :                 && !pcmk__str_in_list(rsc_printable_id(parent), only_rsc,
    2467             :                                       pcmk__str_star_matches)) {
    2468           0 :                 continue;
    2469             :             }
    2470             :         } else {
    2471           0 :             if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
    2472           0 :                 continue;
    2473             :             }
    2474             :         }
    2475             : 
    2476           0 :         if (!pcmk_is_set(section_opts, pcmk_section_operations)) {
    2477           0 :             time_t last_failure = 0;
    2478           0 :             int failcount = pe_get_failcount(node, rsc, &last_failure,
    2479             :                                              pcmk__fc_default, NULL);
    2480             : 
    2481           0 :             if (failcount <= 0) {
    2482           0 :                 continue;
    2483             :             }
    2484             : 
    2485           0 :             if (rc == pcmk_rc_no_output) {
    2486           0 :                 rc = pcmk_rc_ok;
    2487           0 :                 out->message(out, "node", node, show_opts, false, only_node,
    2488             :                              only_rsc);
    2489             :             }
    2490             : 
    2491           0 :             out->message(out, "resource-history", rsc, rsc_id, false,
    2492             :                          failcount, last_failure, false);
    2493             :         } else {
    2494           0 :             GList *op_list = get_operation_list(rsc_entry);
    2495           0 :             pcmk_resource_t *rsc = NULL;
    2496             : 
    2497           0 :             if (op_list == NULL) {
    2498           0 :                 continue;
    2499             :             }
    2500             : 
    2501           0 :             rsc = pe_find_resource(scheduler->resources,
    2502             :                                    crm_element_value(rsc_entry, PCMK_XA_ID));
    2503             : 
    2504           0 :             if (rc == pcmk_rc_no_output) {
    2505           0 :                 rc = pcmk_rc_ok;
    2506           0 :                 out->message(out, "node", node, show_opts, false, only_node,
    2507             :                              only_rsc);
    2508             :             }
    2509             : 
    2510           0 :             out->message(out, "resource-operation-list", scheduler, rsc, node,
    2511             :                          op_list, show_opts);
    2512             :         }
    2513             :     }
    2514             : 
    2515           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    2516           0 :     return rc;
    2517             : }
    2518             : 
    2519             : PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
    2520             : static int
    2521           0 : node_list_html(pcmk__output_t *out, va_list args) {
    2522           0 :     GList *nodes = va_arg(args, GList *);
    2523           0 :     GList *only_node = va_arg(args, GList *);
    2524           0 :     GList *only_rsc = va_arg(args, GList *);
    2525           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2526           0 :     bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
    2527             : 
    2528           0 :     int rc = pcmk_rc_no_output;
    2529             : 
    2530           0 :     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
    2531           0 :         pcmk_node_t *node = (pcmk_node_t *) gIter->data;
    2532             : 
    2533           0 :         if (!pcmk__str_in_list(node->details->uname, only_node,
    2534             :                                pcmk__str_star_matches|pcmk__str_casei)) {
    2535           0 :             continue;
    2536             :         }
    2537             : 
    2538           0 :         PCMK__OUTPUT_LIST_HEADER(out, false, rc, "Node List");
    2539             : 
    2540           0 :         out->message(out, "node", node, show_opts, true, only_node, only_rsc);
    2541             :     }
    2542             : 
    2543           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    2544           0 :     return rc;
    2545             : }
    2546             : 
    2547             : PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
    2548             : static int
    2549           0 : node_list_text(pcmk__output_t *out, va_list args) {
    2550           0 :     GList *nodes = va_arg(args, GList *);
    2551           0 :     GList *only_node = va_arg(args, GList *);
    2552           0 :     GList *only_rsc = va_arg(args, GList *);
    2553           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2554           0 :     bool print_spacer = va_arg(args, int);
    2555             : 
    2556             :     /* space-separated lists of node names */
    2557           0 :     GString *online_nodes = NULL;
    2558           0 :     GString *online_remote_nodes = NULL;
    2559           0 :     GString *online_guest_nodes = NULL;
    2560           0 :     GString *offline_nodes = NULL;
    2561           0 :     GString *offline_remote_nodes = NULL;
    2562             : 
    2563           0 :     int rc = pcmk_rc_no_output;
    2564             : 
    2565           0 :     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
    2566           0 :         pcmk_node_t *node = (pcmk_node_t *) gIter->data;
    2567           0 :         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
    2568             : 
    2569           0 :         if (!pcmk__str_in_list(node->details->uname, only_node,
    2570             :                                pcmk__str_star_matches|pcmk__str_casei)) {
    2571           0 :             free(node_name);
    2572           0 :             continue;
    2573             :         }
    2574             : 
    2575           0 :         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node List");
    2576             : 
    2577             :         // Determine whether to display node individually or in a list
    2578           0 :         if (node->details->unclean || node->details->pending
    2579           0 :             || (node->details->standby_onfail && node->details->online)
    2580           0 :             || node->details->standby || node->details->maintenance
    2581           0 :             || pcmk_is_set(show_opts, pcmk_show_rscs_by_node)
    2582           0 :             || pcmk_is_set(show_opts, pcmk_show_feature_set)
    2583           0 :             || (pe__node_health(node) <= 0)) {
    2584             :             // Display node individually
    2585             : 
    2586           0 :         } else if (node->details->online) {
    2587             :             // Display online node in a list
    2588           0 :             if (pcmk__is_guest_or_bundle_node(node)) {
    2589           0 :                 pcmk__add_word(&online_guest_nodes, 1024, node_name);
    2590             : 
    2591           0 :             } else if (pcmk__is_remote_node(node)) {
    2592           0 :                 pcmk__add_word(&online_remote_nodes, 1024, node_name);
    2593             : 
    2594             :             } else {
    2595           0 :                 pcmk__add_word(&online_nodes, 1024, node_name);
    2596             :             }
    2597           0 :             free(node_name);
    2598           0 :             continue;
    2599             : 
    2600             :         } else {
    2601             :             // Display offline node in a list
    2602           0 :             if (pcmk__is_remote_node(node)) {
    2603           0 :                 pcmk__add_word(&offline_remote_nodes, 1024, node_name);
    2604             : 
    2605           0 :             } else if (pcmk__is_guest_or_bundle_node(node)) {
    2606             :                 /* ignore offline guest nodes */
    2607             : 
    2608             :             } else {
    2609           0 :                 pcmk__add_word(&offline_nodes, 1024, node_name);
    2610             :             }
    2611           0 :             free(node_name);
    2612           0 :             continue;
    2613             :         }
    2614             : 
    2615             :         /* If we get here, node is in bad state, or we're grouping by node */
    2616           0 :         out->message(out, "node", node, show_opts, true, only_node, only_rsc);
    2617           0 :         free(node_name);
    2618             :     }
    2619             : 
    2620             :     /* If we're not grouping by node, summarize nodes by status */
    2621           0 :     if (online_nodes != NULL) {
    2622           0 :         out->list_item(out, "Online", "[ %s ]",
    2623           0 :                        (const char *) online_nodes->str);
    2624           0 :         g_string_free(online_nodes, TRUE);
    2625             :     }
    2626           0 :     if (offline_nodes != NULL) {
    2627           0 :         out->list_item(out, "OFFLINE", "[ %s ]",
    2628           0 :                        (const char *) offline_nodes->str);
    2629           0 :         g_string_free(offline_nodes, TRUE);
    2630             :     }
    2631           0 :     if (online_remote_nodes) {
    2632           0 :         out->list_item(out, "RemoteOnline", "[ %s ]",
    2633           0 :                        (const char *) online_remote_nodes->str);
    2634           0 :         g_string_free(online_remote_nodes, TRUE);
    2635             :     }
    2636           0 :     if (offline_remote_nodes) {
    2637           0 :         out->list_item(out, "RemoteOFFLINE", "[ %s ]",
    2638           0 :                        (const char *) offline_remote_nodes->str);
    2639           0 :         g_string_free(offline_remote_nodes, TRUE);
    2640             :     }
    2641           0 :     if (online_guest_nodes != NULL) {
    2642           0 :         out->list_item(out, "GuestOnline", "[ %s ]",
    2643           0 :                        (const char *) online_guest_nodes->str);
    2644           0 :         g_string_free(online_guest_nodes, TRUE);
    2645             :     }
    2646             : 
    2647           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    2648           0 :     return rc;
    2649             : }
    2650             : 
    2651             : PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "bool")
    2652             : static int
    2653           0 : node_list_xml(pcmk__output_t *out, va_list args) {
    2654           0 :     GList *nodes = va_arg(args, GList *);
    2655           0 :     GList *only_node = va_arg(args, GList *);
    2656           0 :     GList *only_rsc = va_arg(args, GList *);
    2657           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2658           0 :     bool print_spacer G_GNUC_UNUSED = va_arg(args, int);
    2659             : 
    2660             :     /* PCMK_XE_NODES acts as the list's element name for CLI tools that use
    2661             :      * pcmk__output_enable_list_element.  Otherwise PCMK_XE_NODES is the
    2662             :      * value of the list's PCMK_XA_NAME attribute.
    2663             :      */
    2664           0 :     out->begin_list(out, NULL, NULL, PCMK_XE_NODES);
    2665           0 :     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
    2666           0 :         pcmk_node_t *node = (pcmk_node_t *) gIter->data;
    2667             : 
    2668           0 :         if (!pcmk__str_in_list(node->details->uname, only_node,
    2669             :                                pcmk__str_star_matches|pcmk__str_casei)) {
    2670           0 :             continue;
    2671             :         }
    2672             : 
    2673           0 :         out->message(out, "node", node, show_opts, true, only_node, only_rsc);
    2674             :     }
    2675           0 :     out->end_list(out);
    2676             : 
    2677           0 :     return pcmk_rc_ok;
    2678             : }
    2679             : 
    2680             : PCMK__OUTPUT_ARGS("node-summary", "pcmk_scheduler_t *", "GList *", "GList *",
    2681             :                   "uint32_t", "uint32_t", "bool")
    2682             : static int
    2683           0 : node_summary(pcmk__output_t *out, va_list args) {
    2684           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    2685           0 :     GList *only_node = va_arg(args, GList *);
    2686           0 :     GList *only_rsc = va_arg(args, GList *);
    2687           0 :     uint32_t section_opts = va_arg(args, uint32_t);
    2688           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2689           0 :     bool print_spacer = va_arg(args, int);
    2690             : 
    2691           0 :     xmlNode *node_state = NULL;
    2692           0 :     xmlNode *cib_status = pcmk_find_cib_element(scheduler->input,
    2693             :                                                 PCMK_XE_STATUS);
    2694           0 :     int rc = pcmk_rc_no_output;
    2695             : 
    2696           0 :     if (xmlChildElementCount(cib_status) == 0) {
    2697           0 :         return rc;
    2698             :     }
    2699             : 
    2700           0 :     for (node_state = pcmk__xe_first_child(cib_status, PCMK__XE_NODE_STATE,
    2701             :                                            NULL, NULL);
    2702           0 :          node_state != NULL; node_state = pcmk__xe_next_same(node_state)) {
    2703             : 
    2704           0 :         pcmk_node_t *node = pe_find_node_id(scheduler->nodes,
    2705             :                                             pcmk__xe_id(node_state));
    2706             : 
    2707           0 :         if (!node || !node->details || !node->details->online) {
    2708           0 :             continue;
    2709             :         }
    2710             : 
    2711           0 :         if (!pcmk__str_in_list(node->details->uname, only_node,
    2712             :                                pcmk__str_star_matches|pcmk__str_casei)) {
    2713           0 :             continue;
    2714             :         }
    2715             : 
    2716           0 :         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc,
    2717             :                                  pcmk_is_set(section_opts, pcmk_section_operations) ? "Operations" : "Migration Summary");
    2718             : 
    2719           0 :         out->message(out, "node-history-list", scheduler, node, node_state,
    2720             :                      only_node, only_rsc, section_opts, show_opts);
    2721             :     }
    2722             : 
    2723           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    2724           0 :     return rc;
    2725             : }
    2726             : 
    2727             : PCMK__OUTPUT_ARGS("node-weight", "const pcmk_resource_t *", "const char *",
    2728             :                   "const char *", "const char *")
    2729             : static int
    2730           0 : node_weight(pcmk__output_t *out, va_list args)
    2731             : {
    2732           0 :     const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
    2733           0 :     const char *prefix = va_arg(args, const char *);
    2734           0 :     const char *uname = va_arg(args, const char *);
    2735           0 :     const char *score = va_arg(args, const char *);
    2736             : 
    2737           0 :     if (rsc) {
    2738           0 :         out->list_item(out, NULL, "%s: %s allocation score on %s: %s",
    2739           0 :                        prefix, rsc->id, uname, score);
    2740             :     } else {
    2741           0 :         out->list_item(out, NULL, "%s: %s = %s", prefix, uname, score);
    2742             :     }
    2743             : 
    2744           0 :     return pcmk_rc_ok;
    2745             : }
    2746             : 
    2747             : PCMK__OUTPUT_ARGS("node-weight", "const pcmk_resource_t *", "const char *",
    2748             :                   "const char *", "const char *")
    2749             : static int
    2750           0 : node_weight_xml(pcmk__output_t *out, va_list args)
    2751             : {
    2752           0 :     const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
    2753           0 :     const char *prefix = va_arg(args, const char *);
    2754           0 :     const char *uname = va_arg(args, const char *);
    2755           0 :     const char *score = va_arg(args, const char *);
    2756             : 
    2757           0 :     xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_NODE_WEIGHT,
    2758             :                                                    PCMK_XA_FUNCTION, prefix,
    2759             :                                                    PCMK_XA_NODE, uname,
    2760             :                                                    PCMK_XA_SCORE, score,
    2761             :                                                    NULL);
    2762             : 
    2763           0 :     if (rsc) {
    2764           0 :         crm_xml_add(node, PCMK_XA_ID, rsc->id);
    2765             :     }
    2766             : 
    2767           0 :     return pcmk_rc_ok;
    2768             : }
    2769             : 
    2770             : PCMK__OUTPUT_ARGS("op-history", "xmlNode *", "const char *", "const char *", "int", "uint32_t")
    2771             : static int
    2772           0 : op_history_text(pcmk__output_t *out, va_list args) {
    2773           0 :     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
    2774           0 :     const char *task = va_arg(args, const char *);
    2775           0 :     const char *interval_ms_s = va_arg(args, const char *);
    2776           0 :     int rc = va_arg(args, int);
    2777           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2778             : 
    2779           0 :     char *buf = op_history_string(xml_op, task, interval_ms_s, rc,
    2780           0 :                                   pcmk_is_set(show_opts, pcmk_show_timing));
    2781             : 
    2782           0 :     out->list_item(out, NULL, "%s", buf);
    2783             : 
    2784           0 :     free(buf);
    2785           0 :     return pcmk_rc_ok;
    2786             : }
    2787             : 
    2788             : PCMK__OUTPUT_ARGS("op-history", "xmlNode *", "const char *", "const char *", "int", "uint32_t")
    2789             : static int
    2790           0 : op_history_xml(pcmk__output_t *out, va_list args) {
    2791           0 :     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
    2792           0 :     const char *task = va_arg(args, const char *);
    2793           0 :     const char *interval_ms_s = va_arg(args, const char *);
    2794           0 :     int rc = va_arg(args, int);
    2795           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2796             : 
    2797           0 :     const char *call_id = crm_element_value(xml_op, PCMK__XA_CALL_ID);
    2798           0 :     char *rc_s = pcmk__itoa(rc);
    2799           0 :     const char *rc_text = services_ocf_exitcode_str(rc);
    2800           0 :     xmlNodePtr node = NULL;
    2801             : 
    2802           0 :     node = pcmk__output_create_xml_node(out, PCMK_XE_OPERATION_HISTORY,
    2803             :                                         PCMK_XA_CALL, call_id,
    2804             :                                         PCMK_XA_TASK, task,
    2805             :                                         PCMK_XA_RC, rc_s,
    2806             :                                         PCMK_XA_RC_TEXT, rc_text,
    2807             :                                         NULL);
    2808           0 :     free(rc_s);
    2809             : 
    2810           0 :     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
    2811           0 :         char *s = crm_strdup_printf("%sms", interval_ms_s);
    2812           0 :         crm_xml_add(node, PCMK_XA_INTERVAL, s);
    2813           0 :         free(s);
    2814             :     }
    2815             : 
    2816           0 :     if (pcmk_is_set(show_opts, pcmk_show_timing)) {
    2817           0 :         const char *value = NULL;
    2818           0 :         time_t epoch = 0;
    2819             : 
    2820           0 :         if ((crm_element_value_epoch(xml_op, PCMK_XA_LAST_RC_CHANGE,
    2821           0 :                                      &epoch) == pcmk_ok) && (epoch > 0)) {
    2822           0 :             char *s = pcmk__epoch2str(&epoch, 0);
    2823           0 :             crm_xml_add(node, PCMK_XA_LAST_RC_CHANGE, s);
    2824           0 :             free(s);
    2825             :         }
    2826             : 
    2827           0 :         value = crm_element_value(xml_op, PCMK_XA_EXEC_TIME);
    2828           0 :         if (value) {
    2829           0 :             char *s = crm_strdup_printf("%sms", value);
    2830           0 :             crm_xml_add(node, PCMK_XA_EXEC_TIME, s);
    2831           0 :             free(s);
    2832             :         }
    2833           0 :         value = crm_element_value(xml_op, PCMK_XA_QUEUE_TIME);
    2834           0 :         if (value) {
    2835           0 :             char *s = crm_strdup_printf("%sms", value);
    2836           0 :             crm_xml_add(node, PCMK_XA_QUEUE_TIME, s);
    2837           0 :             free(s);
    2838             :         }
    2839             :     }
    2840             : 
    2841           0 :     return pcmk_rc_ok;
    2842             : }
    2843             : 
    2844             : PCMK__OUTPUT_ARGS("promotion-score", "pcmk_resource_t *", "pcmk_node_t *",
    2845             :                   "const char *")
    2846             : static int
    2847           0 : promotion_score(pcmk__output_t *out, va_list args)
    2848             : {
    2849           0 :     pcmk_resource_t *child_rsc = va_arg(args, pcmk_resource_t *);
    2850           0 :     pcmk_node_t *chosen = va_arg(args, pcmk_node_t *);
    2851           0 :     const char *score = va_arg(args, const char *);
    2852             : 
    2853           0 :     out->list_item(out, NULL, "%s promotion score on %s: %s",
    2854             :                    child_rsc->id,
    2855           0 :                    chosen? chosen->details->uname : "none",
    2856             :                    score);
    2857           0 :     return pcmk_rc_ok;
    2858             : }
    2859             : 
    2860             : PCMK__OUTPUT_ARGS("promotion-score", "pcmk_resource_t *", "pcmk_node_t *",
    2861             :                   "const char *")
    2862             : static int
    2863           0 : promotion_score_xml(pcmk__output_t *out, va_list args)
    2864             : {
    2865           0 :     pcmk_resource_t *child_rsc = va_arg(args, pcmk_resource_t *);
    2866           0 :     pcmk_node_t *chosen = va_arg(args, pcmk_node_t *);
    2867           0 :     const char *score = va_arg(args, const char *);
    2868             : 
    2869           0 :     xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_PROMOTION_SCORE,
    2870             :                                                    PCMK_XA_ID, child_rsc->id,
    2871             :                                                    PCMK_XA_SCORE, score,
    2872             :                                                    NULL);
    2873             : 
    2874           0 :     if (chosen) {
    2875           0 :         crm_xml_add(node, PCMK_XA_NODE, chosen->details->uname);
    2876             :     }
    2877             : 
    2878           0 :     return pcmk_rc_ok;
    2879             : }
    2880             : 
    2881             : PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool")
    2882             : static int
    2883           0 : resource_config(pcmk__output_t *out, va_list args) {
    2884           0 :     const pcmk_resource_t *rsc = va_arg(args, const pcmk_resource_t *);
    2885           0 :     GString *xml_buf = g_string_sized_new(1024);
    2886           0 :     bool raw = va_arg(args, int);
    2887             : 
    2888           0 :     formatted_xml_buf(rsc, xml_buf, raw);
    2889             : 
    2890           0 :     out->output_xml(out, PCMK_XE_XML, xml_buf->str);
    2891             : 
    2892           0 :     g_string_free(xml_buf, TRUE);
    2893           0 :     return pcmk_rc_ok;
    2894             : }
    2895             : 
    2896             : PCMK__OUTPUT_ARGS("resource-config", "const pcmk_resource_t *", "bool")
    2897             : static int
    2898           0 : resource_config_text(pcmk__output_t *out, va_list args) {
    2899           0 :     pcmk__formatted_printf(out, "Resource XML:\n");
    2900           0 :     return resource_config(out, args);
    2901             : }
    2902             : 
    2903             : PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *",
    2904             :                   "bool", "int", "time_t", "bool")
    2905             : static int
    2906           0 : resource_history_text(pcmk__output_t *out, va_list args) {
    2907           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    2908           0 :     const char *rsc_id = va_arg(args, const char *);
    2909           0 :     bool all = va_arg(args, int);
    2910           0 :     int failcount = va_arg(args, int);
    2911           0 :     time_t last_failure = va_arg(args, time_t);
    2912           0 :     bool as_header = va_arg(args, int);
    2913             : 
    2914           0 :     char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure);
    2915             : 
    2916           0 :     if (as_header) {
    2917           0 :         out->begin_list(out, NULL, NULL, "%s", buf);
    2918             :     } else {
    2919           0 :         out->list_item(out, NULL, "%s", buf);
    2920             :     }
    2921             : 
    2922           0 :     free(buf);
    2923           0 :     return pcmk_rc_ok;
    2924             : }
    2925             : 
    2926             : PCMK__OUTPUT_ARGS("resource-history", "pcmk_resource_t *", "const char *",
    2927             :                   "bool", "int", "time_t", "bool")
    2928             : static int
    2929           0 : resource_history_xml(pcmk__output_t *out, va_list args) {
    2930           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    2931           0 :     const char *rsc_id = va_arg(args, const char *);
    2932           0 :     bool all = va_arg(args, int);
    2933           0 :     int failcount = va_arg(args, int);
    2934           0 :     time_t last_failure = va_arg(args, time_t);
    2935           0 :     bool as_header = va_arg(args, int);
    2936             : 
    2937           0 :     xmlNodePtr node = pcmk__output_xml_create_parent(out,
    2938             :                                                      PCMK_XE_RESOURCE_HISTORY,
    2939             :                                                      PCMK_XA_ID, rsc_id,
    2940             :                                                      NULL);
    2941             : 
    2942           0 :     if (rsc == NULL) {
    2943           0 :         pcmk__xe_set_bool_attr(node, PCMK_XA_ORPHAN, true);
    2944           0 :     } else if (all || failcount || last_failure > 0) {
    2945           0 :         char *migration_s = pcmk__itoa(rsc->migration_threshold);
    2946             : 
    2947           0 :         pcmk__xe_set_props(node,
    2948             :                            PCMK_XA_ORPHAN, PCMK_VALUE_FALSE,
    2949             :                            PCMK_META_MIGRATION_THRESHOLD, migration_s,
    2950             :                            NULL);
    2951           0 :         free(migration_s);
    2952             : 
    2953           0 :         if (failcount > 0) {
    2954           0 :             char *s = pcmk__itoa(failcount);
    2955             : 
    2956           0 :             crm_xml_add(node, PCMK_XA_FAIL_COUNT, s);
    2957           0 :             free(s);
    2958             :         }
    2959             : 
    2960           0 :         if (last_failure > 0) {
    2961           0 :             char *s = pcmk__epoch2str(&last_failure, 0);
    2962             : 
    2963           0 :             crm_xml_add(node, PCMK_XA_LAST_FAILURE, s);
    2964           0 :             free(s);
    2965             :         }
    2966             :     }
    2967             : 
    2968           0 :     if (!as_header) {
    2969           0 :         pcmk__output_xml_pop_parent(out);
    2970             :     }
    2971             : 
    2972           0 :     return pcmk_rc_ok;
    2973             : }
    2974             : 
    2975             : static void
    2976           0 : print_resource_header(pcmk__output_t *out, uint32_t show_opts)
    2977             : {
    2978           0 :     if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    2979             :         /* Active resources have already been printed by node */
    2980           0 :         out->begin_list(out, NULL, NULL, "Inactive Resources");
    2981           0 :     } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
    2982           0 :         out->begin_list(out, NULL, NULL, "Full List of Resources");
    2983             :     } else {
    2984           0 :         out->begin_list(out, NULL, NULL, "Active Resources");
    2985             :     }
    2986           0 : }
    2987             : 
    2988             : 
    2989             : PCMK__OUTPUT_ARGS("resource-list", "pcmk_scheduler_t *", "uint32_t", "bool",
    2990             :                   "GList *", "GList *", "bool")
    2991             : static int
    2992           0 : resource_list(pcmk__output_t *out, va_list args)
    2993             : {
    2994           0 :     pcmk_scheduler_t *scheduler = va_arg(args, pcmk_scheduler_t *);
    2995           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    2996           0 :     bool print_summary = va_arg(args, int);
    2997           0 :     GList *only_node = va_arg(args, GList *);
    2998           0 :     GList *only_rsc = va_arg(args, GList *);
    2999           0 :     bool print_spacer = va_arg(args, int);
    3000             : 
    3001             :     GList *rsc_iter;
    3002           0 :     int rc = pcmk_rc_no_output;
    3003           0 :     bool printed_header = false;
    3004             : 
    3005             :     /* If we already showed active resources by node, and
    3006             :      * we're not showing inactive resources, we have nothing to do
    3007             :      */
    3008           0 :     if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node) &&
    3009           0 :         !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
    3010           0 :         return rc;
    3011             :     }
    3012             : 
    3013             :     /* If we haven't already printed resources grouped by node,
    3014             :      * and brief output was requested, print resource summary */
    3015           0 :     if (pcmk_is_set(show_opts, pcmk_show_brief)
    3016           0 :         && !pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    3017           0 :         GList *rscs = pe__filter_rsc_list(scheduler->resources, only_rsc);
    3018             : 
    3019           0 :         PCMK__OUTPUT_SPACER_IF(out, print_spacer);
    3020           0 :         print_resource_header(out, show_opts);
    3021           0 :         printed_header = true;
    3022             : 
    3023           0 :         rc = pe__rscs_brief_output(out, rscs, show_opts);
    3024           0 :         g_list_free(rscs);
    3025             :     }
    3026             : 
    3027             :     /* For each resource, display it if appropriate */
    3028           0 :     for (rsc_iter = scheduler->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
    3029           0 :         pcmk_resource_t *rsc = (pcmk_resource_t *) rsc_iter->data;
    3030             :         int x;
    3031             : 
    3032             :         /* Complex resources may have some sub-resources active and some inactive */
    3033           0 :         gboolean is_active = rsc->fns->active(rsc, TRUE);
    3034           0 :         gboolean partially_active = rsc->fns->active(rsc, FALSE);
    3035             : 
    3036             :         /* Skip inactive orphans (deleted but still in CIB) */
    3037           0 :         if (pcmk_is_set(rsc->flags, pcmk_rsc_removed) && !is_active) {
    3038           0 :             continue;
    3039             : 
    3040             :         /* Skip active resources if we already displayed them by node */
    3041           0 :         } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    3042           0 :             if (is_active) {
    3043           0 :                 continue;
    3044             :             }
    3045             : 
    3046             :         /* Skip primitives already counted in a brief summary */
    3047           0 :         } else if (pcmk_is_set(show_opts, pcmk_show_brief)
    3048           0 :                    && (rsc->variant == pcmk_rsc_variant_primitive)) {
    3049           0 :             continue;
    3050             : 
    3051             :         /* Skip resources that aren't at least partially active,
    3052             :          * unless we're displaying inactive resources
    3053             :          */
    3054           0 :         } else if (!partially_active && !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
    3055           0 :             continue;
    3056             : 
    3057           0 :         } else if (partially_active && !pe__rsc_running_on_any(rsc, only_node)) {
    3058           0 :             continue;
    3059             :         }
    3060             : 
    3061           0 :         if (!printed_header) {
    3062           0 :             PCMK__OUTPUT_SPACER_IF(out, print_spacer);
    3063           0 :             print_resource_header(out, show_opts);
    3064           0 :             printed_header = true;
    3065             :         }
    3066             : 
    3067             :         /* Print this resource */
    3068           0 :         x = out->message(out, pcmk__map_element_name(rsc->xml), show_opts, rsc,
    3069             :                          only_node, only_rsc);
    3070           0 :         if (x == pcmk_rc_ok) {
    3071           0 :             rc = pcmk_rc_ok;
    3072             :         }
    3073             :     }
    3074             : 
    3075           0 :     if (print_summary && rc != pcmk_rc_ok) {
    3076           0 :         if (!printed_header) {
    3077           0 :             PCMK__OUTPUT_SPACER_IF(out, print_spacer);
    3078           0 :             print_resource_header(out, show_opts);
    3079           0 :             printed_header = true;
    3080             :         }
    3081             : 
    3082           0 :         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
    3083           0 :             out->list_item(out, NULL, "No inactive resources");
    3084           0 :         } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
    3085           0 :             out->list_item(out, NULL, "No resources");
    3086             :         } else {
    3087           0 :             out->list_item(out, NULL, "No active resources");
    3088             :         }
    3089             :     }
    3090             : 
    3091           0 :     if (printed_header) {
    3092           0 :         out->end_list(out);
    3093             :     }
    3094             : 
    3095           0 :     return rc;
    3096             : }
    3097             : 
    3098             : PCMK__OUTPUT_ARGS("resource-operation-list", "pcmk_scheduler_t *",
    3099             :                   "pcmk_resource_t *", "pcmk_node_t *", "GList *", "uint32_t")
    3100             : static int
    3101           0 : resource_operation_list(pcmk__output_t *out, va_list args)
    3102             : {
    3103           0 :     pcmk_scheduler_t *scheduler G_GNUC_UNUSED = va_arg(args,
    3104             :                                                        pcmk_scheduler_t *);
    3105           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    3106           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    3107           0 :     GList *op_list = va_arg(args, GList *);
    3108           0 :     uint32_t show_opts = va_arg(args, uint32_t);
    3109             : 
    3110           0 :     GList *gIter = NULL;
    3111           0 :     int rc = pcmk_rc_no_output;
    3112             : 
    3113             :     /* Print each operation */
    3114           0 :     for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
    3115           0 :         xmlNode *xml_op = (xmlNode *) gIter->data;
    3116           0 :         const char *task = crm_element_value(xml_op, PCMK_XA_OPERATION);
    3117           0 :         const char *interval_ms_s = crm_element_value(xml_op,
    3118             :                                                       PCMK_META_INTERVAL);
    3119           0 :         const char *op_rc = crm_element_value(xml_op, PCMK__XA_RC_CODE);
    3120             :         int op_rc_i;
    3121             : 
    3122           0 :         pcmk__scan_min_int(op_rc, &op_rc_i, 0);
    3123             : 
    3124             :         /* Display 0-interval monitors as "probe" */
    3125           0 :         if (pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_casei)
    3126           0 :             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
    3127           0 :             task = "probe";
    3128             :         }
    3129             : 
    3130             :         /* If this is the first printed operation, print heading for resource */
    3131           0 :         if (rc == pcmk_rc_no_output) {
    3132           0 :             time_t last_failure = 0;
    3133           0 :             int failcount = pe_get_failcount(node, rsc, &last_failure,
    3134             :                                              pcmk__fc_default, NULL);
    3135             : 
    3136           0 :             out->message(out, "resource-history", rsc, rsc_printable_id(rsc), true,
    3137             :                          failcount, last_failure, true);
    3138           0 :             rc = pcmk_rc_ok;
    3139             :         }
    3140             : 
    3141             :         /* Print the operation */
    3142           0 :         out->message(out, "op-history", xml_op, task, interval_ms_s,
    3143             :                      op_rc_i, show_opts);
    3144             :     }
    3145             : 
    3146             :     /* Free the list we created (no need to free the individual items) */
    3147           0 :     g_list_free(op_list);
    3148             : 
    3149           0 :     PCMK__OUTPUT_LIST_FOOTER(out, rc);
    3150           0 :     return rc;
    3151             : }
    3152             : 
    3153             : PCMK__OUTPUT_ARGS("resource-util", "pcmk_resource_t *", "pcmk_node_t *",
    3154             :                   "const char *")
    3155             : static int
    3156           0 : resource_util(pcmk__output_t *out, va_list args)
    3157             : {
    3158           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    3159           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    3160           0 :     const char *fn = va_arg(args, const char *);
    3161             : 
    3162           0 :     char *dump_text = crm_strdup_printf("%s: %s utilization on %s:",
    3163             :                                         fn, rsc->id, pcmk__node_name(node));
    3164             : 
    3165           0 :     g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
    3166           0 :     out->list_item(out, NULL, "%s", dump_text);
    3167           0 :     free(dump_text);
    3168             : 
    3169           0 :     return pcmk_rc_ok;
    3170             : }
    3171             : 
    3172             : PCMK__OUTPUT_ARGS("resource-util", "pcmk_resource_t *", "pcmk_node_t *",
    3173             :                   "const char *")
    3174             : static int
    3175           0 : resource_util_xml(pcmk__output_t *out, va_list args)
    3176             : {
    3177           0 :     pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
    3178           0 :     pcmk_node_t *node = va_arg(args, pcmk_node_t *);
    3179           0 :     const char *uname = node->details->uname;
    3180           0 :     const char *fn = va_arg(args, const char *);
    3181             : 
    3182           0 :     xmlNodePtr xml_node = NULL;
    3183             : 
    3184           0 :     xml_node = pcmk__output_create_xml_node(out, PCMK_XE_UTILIZATION,
    3185             :                                             PCMK_XA_RESOURCE, rsc->id,
    3186             :                                             PCMK_XA_NODE, uname,
    3187             :                                             PCMK_XA_FUNCTION, fn,
    3188             :                                             NULL);
    3189           0 :     g_hash_table_foreach(rsc->utilization, add_dump_node, xml_node);
    3190             : 
    3191           0 :     return pcmk_rc_ok;
    3192             : }
    3193             : 
    3194             : PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *", "bool", "bool")
    3195             : static int
    3196           0 : ticket_default(pcmk__output_t *out, va_list args) {
    3197           0 :     pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
    3198           0 :     bool raw = va_arg(args, int);
    3199           0 :     bool details = va_arg(args, int);
    3200             : 
    3201           0 :     GString *detail_str = NULL;
    3202             : 
    3203           0 :     if (raw) {
    3204           0 :         out->list_item(out, ticket->id, "%s", ticket->id);
    3205           0 :         return pcmk_rc_ok;
    3206             :     }
    3207             : 
    3208           0 :     if (details && g_hash_table_size(ticket->state) > 0) {
    3209             :         GHashTableIter iter;
    3210           0 :         const char *name = NULL;
    3211           0 :         const char *value = NULL;
    3212           0 :         bool already_added = false;
    3213             : 
    3214           0 :         detail_str = g_string_sized_new(100);
    3215           0 :         pcmk__g_strcat(detail_str, "\t(", NULL);
    3216             : 
    3217           0 :         g_hash_table_iter_init(&iter, ticket->state);
    3218           0 :         while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &value)) {
    3219           0 :             if (already_added) {
    3220           0 :                 g_string_append_printf(detail_str, ", %s=", name);
    3221             :             } else {
    3222           0 :                 g_string_append_printf(detail_str, "%s=", name);
    3223           0 :                 already_added = true;
    3224             :             }
    3225             : 
    3226           0 :             if (pcmk__str_any_of(name, PCMK_XA_LAST_GRANTED, "expires", NULL)) {
    3227           0 :                 char *epoch_str = NULL;
    3228             :                 long long time_ll;
    3229             : 
    3230           0 :                 pcmk__scan_ll(value, &time_ll, 0);
    3231           0 :                 epoch_str = pcmk__epoch2str((const time_t *) &time_ll, 0);
    3232           0 :                 pcmk__g_strcat(detail_str, epoch_str, NULL);
    3233           0 :                 free(epoch_str);
    3234             :             } else {
    3235           0 :                 pcmk__g_strcat(detail_str, value, NULL);
    3236             :             }
    3237             :         }
    3238             : 
    3239           0 :         pcmk__g_strcat(detail_str, ")", NULL);
    3240             :     }
    3241             : 
    3242           0 :     if (ticket->last_granted > -1) {
    3243             :         /* Prior to the introduction of the details & raw arguments to this
    3244             :          * function, last-granted would always be added in this block.  We need
    3245             :          * to preserve that behavior.  At the same time, we also need to preserve
    3246             :          * the existing behavior from crm_ticket, which would include last-granted
    3247             :          * as part of the (...) detail string.
    3248             :          *
    3249             :          * Luckily we can check detail_str - if it's NULL, either there were no
    3250             :          * details, or we are preserving the previous behavior of this function.
    3251             :          * If it's not NULL, we are either preserving the previous behavior of
    3252             :          * crm_ticket or we were given details=true as an argument.
    3253             :          */
    3254           0 :         if (detail_str == NULL) {
    3255           0 :             char *epoch_str = pcmk__epoch2str(&(ticket->last_granted), 0);
    3256             : 
    3257           0 :             out->list_item(out, NULL, "%s\t%s%s last-granted=\"%s\"",
    3258             :                            ticket->id,
    3259           0 :                            (ticket->granted? "granted" : "revoked"),
    3260           0 :                            (ticket->standby? " [standby]" : ""),
    3261             :                            pcmk__s(epoch_str, ""));
    3262           0 :             free(epoch_str);
    3263             :         } else {
    3264           0 :             out->list_item(out, NULL, "%s\t%s%s %s",
    3265             :                            ticket->id,
    3266           0 :                            (ticket->granted? "granted" : "revoked"),
    3267           0 :                            (ticket->standby? " [standby]" : ""),
    3268             :                            detail_str->str);
    3269             :         }
    3270             :     } else {
    3271           0 :         out->list_item(out, NULL, "%s\t%s%s%s", ticket->id,
    3272           0 :                        ticket->granted ? "granted" : "revoked",
    3273           0 :                        ticket->standby ? " [standby]" : "",
    3274             :                        detail_str != NULL ? detail_str->str : "");
    3275             :     }
    3276             : 
    3277           0 :     if (detail_str != NULL) {
    3278           0 :         g_string_free(detail_str, TRUE);
    3279             :     }
    3280             : 
    3281           0 :     return pcmk_rc_ok;
    3282             : }
    3283             : 
    3284             : PCMK__OUTPUT_ARGS("ticket", "pcmk_ticket_t *", "bool", "bool")
    3285             : static int
    3286           0 : ticket_xml(pcmk__output_t *out, va_list args) {
    3287           0 :     pcmk_ticket_t *ticket = va_arg(args, pcmk_ticket_t *);
    3288           0 :     bool raw G_GNUC_UNUSED = va_arg(args, int);
    3289           0 :     bool details G_GNUC_UNUSED = va_arg(args, int);
    3290             : 
    3291           0 :     const char *status = NULL;
    3292           0 :     const char *standby = pcmk__btoa(ticket->standby);
    3293             : 
    3294           0 :     xmlNodePtr node = NULL;
    3295             :     GHashTableIter iter;
    3296           0 :     const char *name = NULL;
    3297           0 :     const char *value = NULL;
    3298             : 
    3299           0 :     status = ticket->granted? PCMK_VALUE_GRANTED : PCMK_VALUE_REVOKED;
    3300             : 
    3301           0 :     node = pcmk__output_create_xml_node(out, PCMK_XE_TICKET,
    3302             :                                         PCMK_XA_ID, ticket->id,
    3303             :                                         PCMK_XA_STATUS, status,
    3304             :                                         PCMK_XA_STANDBY, standby,
    3305             :                                         NULL);
    3306             : 
    3307           0 :     if (ticket->last_granted > -1) {
    3308           0 :         char *buf = pcmk__epoch2str(&ticket->last_granted, 0);
    3309             : 
    3310           0 :         crm_xml_add(node, PCMK_XA_LAST_GRANTED, buf);
    3311           0 :         free(buf);
    3312             :     }
    3313             : 
    3314           0 :     g_hash_table_iter_init(&iter, ticket->state);
    3315           0 :     while (g_hash_table_iter_next(&iter, (void **) &name, (void **) &value)) {
    3316             :         /* PCMK_XA_LAST_GRANTED and "expires" are already added by the check
    3317             :          * for ticket->last_granted above.
    3318             :          */
    3319           0 :         if (pcmk__str_any_of(name, PCMK_XA_LAST_GRANTED, PCMK_XA_EXPIRES,
    3320             :                              NULL)) {
    3321           0 :             continue;
    3322             :         }
    3323             : 
    3324           0 :         crm_xml_add(node, name, value);
    3325             :     }
    3326             : 
    3327           0 :     return pcmk_rc_ok;
    3328             : }
    3329             : 
    3330             : PCMK__OUTPUT_ARGS("ticket-list", "GHashTable *", "bool", "bool", "bool")
    3331             : static int
    3332           0 : ticket_list(pcmk__output_t *out, va_list args) {
    3333           0 :     GHashTable *tickets = va_arg(args, GHashTable *);
    3334           0 :     bool print_spacer = va_arg(args, int);
    3335           0 :     bool raw = va_arg(args, int);
    3336           0 :     bool details = va_arg(args, int);
    3337             : 
    3338             :     GHashTableIter iter;
    3339             :     gpointer value;
    3340             : 
    3341           0 :     if (g_hash_table_size(tickets) == 0) {
    3342           0 :         return pcmk_rc_no_output;
    3343             :     }
    3344             : 
    3345           0 :     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
    3346             : 
    3347             :     /* Print section heading */
    3348           0 :     out->begin_list(out, NULL, NULL, "Tickets");
    3349             : 
    3350             :     /* Print each ticket */
    3351           0 :     g_hash_table_iter_init(&iter, tickets);
    3352           0 :     while (g_hash_table_iter_next(&iter, NULL, &value)) {
    3353           0 :         pcmk_ticket_t *ticket = (pcmk_ticket_t *) value;
    3354           0 :         out->message(out, "ticket", ticket, raw, details);
    3355             :     }
    3356             : 
    3357             :     /* Close section */
    3358           0 :     out->end_list(out);
    3359           0 :     return pcmk_rc_ok;
    3360             : }
    3361             : 
    3362             : static pcmk__message_entry_t fmt_functions[] = {
    3363             :     { "ban", "default", ban_text },
    3364             :     { "ban", "html", ban_html },
    3365             :     { "ban", "xml", ban_xml },
    3366             :     { "ban-list", "default", ban_list },
    3367             :     { "bundle", "default", pe__bundle_text },
    3368             :     { "bundle", "xml",  pe__bundle_xml },
    3369             :     { "bundle", "html",  pe__bundle_html },
    3370             :     { "clone", "default", pe__clone_default },
    3371             :     { "clone", "xml",  pe__clone_xml },
    3372             :     { "cluster-counts", "default", cluster_counts_text },
    3373             :     { "cluster-counts", "html", cluster_counts_html },
    3374             :     { "cluster-counts", "xml", cluster_counts_xml },
    3375             :     { "cluster-dc", "default", cluster_dc_text },
    3376             :     { "cluster-dc", "html", cluster_dc_html },
    3377             :     { "cluster-dc", "xml", cluster_dc_xml },
    3378             :     { "cluster-options", "default", cluster_options_text },
    3379             :     { "cluster-options", "html", cluster_options_html },
    3380             :     { "cluster-options", "log", cluster_options_log },
    3381             :     { "cluster-options", "xml", cluster_options_xml },
    3382             :     { "cluster-summary", "default", cluster_summary },
    3383             :     { "cluster-summary", "html", cluster_summary_html },
    3384             :     { "cluster-stack", "default", cluster_stack_text },
    3385             :     { "cluster-stack", "html", cluster_stack_html },
    3386             :     { "cluster-stack", "xml", cluster_stack_xml },
    3387             :     { "cluster-times", "default", cluster_times_text },
    3388             :     { "cluster-times", "html", cluster_times_html },
    3389             :     { "cluster-times", "xml", cluster_times_xml },
    3390             :     { "failed-action", "default", failed_action_default },
    3391             :     { "failed-action", "xml", failed_action_xml },
    3392             :     { "failed-action-list", "default", failed_action_list },
    3393             :     { "group", "default",  pe__group_default},
    3394             :     { "group", "xml",  pe__group_xml },
    3395             :     { "maint-mode", "text", cluster_maint_mode_text },
    3396             :     { "node", "default", node_text },
    3397             :     { "node", "html", node_html },
    3398             :     { "node", "xml", node_xml },
    3399             :     { "node-and-op", "default", node_and_op },
    3400             :     { "node-and-op", "xml", node_and_op_xml },
    3401             :     { "node-capacity", "default", node_capacity },
    3402             :     { "node-capacity", "xml", node_capacity_xml },
    3403             :     { "node-history-list", "default", node_history_list },
    3404             :     { "node-list", "default", node_list_text },
    3405             :     { "node-list", "html", node_list_html },
    3406             :     { "node-list", "xml", node_list_xml },
    3407             :     { "node-weight", "default", node_weight },
    3408             :     { "node-weight", "xml", node_weight_xml },
    3409             :     { "node-attribute", "default", node_attribute_text },
    3410             :     { "node-attribute", "html", node_attribute_html },
    3411             :     { "node-attribute", "xml", node_attribute_xml },
    3412             :     { "node-attribute-list", "default", node_attribute_list },
    3413             :     { "node-summary", "default", node_summary },
    3414             :     { "op-history", "default", op_history_text },
    3415             :     { "op-history", "xml", op_history_xml },
    3416             :     { "primitive", "default",  pe__resource_text },
    3417             :     { "primitive", "xml",  pe__resource_xml },
    3418             :     { "primitive", "html",  pe__resource_html },
    3419             :     { "promotion-score", "default", promotion_score },
    3420             :     { "promotion-score", "xml", promotion_score_xml },
    3421             :     { "resource-config", "default", resource_config },
    3422             :     { "resource-config", "text", resource_config_text },
    3423             :     { "resource-history", "default", resource_history_text },
    3424             :     { "resource-history", "xml", resource_history_xml },
    3425             :     { "resource-list", "default", resource_list },
    3426             :     { "resource-operation-list", "default", resource_operation_list },
    3427             :     { "resource-util", "default", resource_util },
    3428             :     { "resource-util", "xml", resource_util_xml },
    3429             :     { "ticket", "default", ticket_default },
    3430             :     { "ticket", "xml", ticket_xml },
    3431             :     { "ticket-list", "default", ticket_list },
    3432             : 
    3433             :     { NULL, NULL, NULL }
    3434             : };
    3435             : 
    3436             : void
    3437           0 : pe__register_messages(pcmk__output_t *out) {
    3438           0 :     pcmk__register_messages(out, fmt_functions);
    3439           0 : }

Generated by: LCOV version 1.14