root/lib/pengine/pe_output.c

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

DEFINITIONS

This source file includes following definitions.
  1. compare_attribute
  2. add_extra_info
  3. filter_attr_list
  4. get_operation_list
  5. add_dump_node
  6. append_dump_text
  7. get_cluster_stack
  8. last_changed_string
  9. op_history_string
  10. resource_history_string
  11. PCMK__OUTPUT_ARGS
  12. PCMK__OUTPUT_ARGS
  13. pe__node_display_name
  14. pe__name_and_nvpairs_xml
  15. role_desc
  16. PCMK__OUTPUT_ARGS
  17. PCMK__OUTPUT_ARGS
  18. PCMK__OUTPUT_ARGS
  19. PCMK__OUTPUT_ARGS
  20. PCMK__OUTPUT_ARGS
  21. PCMK__OUTPUT_ARGS
  22. PCMK__OUTPUT_ARGS
  23. PCMK__OUTPUT_ARGS
  24. PCMK__OUTPUT_ARGS
  25. PCMK__OUTPUT_ARGS
  26. PCMK__OUTPUT_ARGS
  27. PCMK__OUTPUT_ARGS
  28. PCMK__OUTPUT_ARGS
  29. PCMK__OUTPUT_ARGS
  30. PCMK__OUTPUT_ARGS
  31. PCMK__OUTPUT_ARGS
  32. PCMK__OUTPUT_ARGS
  33. PCMK__OUTPUT_ARGS
  34. PCMK__OUTPUT_ARGS
  35. PCMK__OUTPUT_ARGS
  36. PCMK__OUTPUT_ARGS
  37. failed_action_friendly
  38. failed_action_technical
  39. PCMK__OUTPUT_ARGS
  40. PCMK__OUTPUT_ARGS
  41. PCMK__OUTPUT_ARGS
  42. status_node
  43. PCMK__OUTPUT_ARGS
  44. PCMK__OUTPUT_ARGS
  45. PCMK__OUTPUT_ARGS
  46. PCMK__OUTPUT_ARGS
  47. PCMK__OUTPUT_ARGS
  48. PCMK__OUTPUT_ARGS
  49. PCMK__OUTPUT_ARGS
  50. PCMK__OUTPUT_ARGS
  51. PCMK__OUTPUT_ARGS
  52. PCMK__OUTPUT_ARGS
  53. PCMK__OUTPUT_ARGS
  54. PCMK__OUTPUT_ARGS
  55. PCMK__OUTPUT_ARGS
  56. PCMK__OUTPUT_ARGS
  57. PCMK__OUTPUT_ARGS
  58. PCMK__OUTPUT_ARGS
  59. PCMK__OUTPUT_ARGS
  60. PCMK__OUTPUT_ARGS
  61. PCMK__OUTPUT_ARGS
  62. PCMK__OUTPUT_ARGS
  63. PCMK__OUTPUT_ARGS
  64. PCMK__OUTPUT_ARGS
  65. PCMK__OUTPUT_ARGS
  66. PCMK__OUTPUT_ARGS
  67. PCMK__OUTPUT_ARGS
  68. print_resource_header
  69. PCMK__OUTPUT_ARGS
  70. PCMK__OUTPUT_ARGS
  71. PCMK__OUTPUT_ARGS
  72. PCMK__OUTPUT_ARGS
  73. PCMK__OUTPUT_ARGS
  74. PCMK__OUTPUT_ARGS
  75. PCMK__OUTPUT_ARGS
  76. PCMK__OUTPUT_ARGS
  77. pe__register_messages
  78. pe__output_node

   1 /*
   2  * Copyright 2019-2021 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 #include <crm/common/xml_internal.h>
  12 #include <crm/common/output.h>
  13 #include <crm/cib/util.h>
  14 #include <crm/msg_xml.h>
  15 #include <crm/pengine/internal.h>
  16 
  17 /* Never display node attributes whose name starts with one of these prefixes */
  18 #define FILTER_STR { PCMK__FAIL_COUNT_PREFIX, PCMK__LAST_FAILURE_PREFIX,   \
  19                      "shutdown", "terminate", "standby", "probe_complete", \
  20                      "#", NULL }
  21 
  22 static int
  23 compare_attribute(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  24 {
  25     int rc;
  26 
  27     rc = strcmp((const char *)a, (const char *)b);
  28 
  29     return rc;
  30 }
  31 
  32 /*!
  33  * \internal
  34  * \brief Determine whether extended information about an attribute should be added.
  35  *
  36  * \param[in]  node           Node that ran this resource.
  37  * \param[in]  rsc_list       The list of resources for this node.
  38  * \param[in]  attrname       The attribute to find.
  39  * \param[out] expected_score The expected value for this attribute.
  40  *
  41  * \return TRUE if extended information should be printed, FALSE otherwise
  42  * \note Currently, extended information is only supported for ping/pingd
  43  *       resources, for which a message will be printed if connectivity is lost
  44  *       or degraded.
  45  */
  46 static gboolean
  47 add_extra_info(pe_node_t *node, GList *rsc_list, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
  48                const char *attrname, int *expected_score)
  49 {
  50     GList *gIter = NULL;
  51 
  52     for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
  53         pe_resource_t *rsc = (pe_resource_t *) gIter->data;
  54         const char *type = g_hash_table_lookup(rsc->meta, "type");
  55         const char *name = NULL;
  56         GHashTable *params = NULL;
  57 
  58         if (rsc->children != NULL) {
  59             if (add_extra_info(node, rsc->children, data_set, attrname,
  60                                expected_score)) {
  61                 return TRUE;
  62             }
  63         }
  64 
  65         if (!pcmk__strcase_any_of(type, "ping", "pingd", NULL)) {
  66             continue;
  67         }
  68 
  69         params = pe_rsc_params(rsc, node, data_set);
  70         name = g_hash_table_lookup(params, "name");
  71 
  72         if (name == NULL) {
  73             name = "pingd";
  74         }
  75 
  76         /* To identify the resource with the attribute name. */
  77         if (pcmk__str_eq(name, attrname, pcmk__str_casei)) {
  78             int host_list_num = 0;
  79             const char *hosts = g_hash_table_lookup(params, "host_list");
  80             const char *multiplier = g_hash_table_lookup(params, "multiplier");
  81             int multiplier_i;
  82 
  83             if (hosts) {
  84                 char **host_list = g_strsplit(hosts, " ", 0);
  85                 host_list_num = g_strv_length(host_list);
  86                 g_strfreev(host_list);
  87             }
  88 
  89             if ((multiplier == NULL)
  90                 || (pcmk__scan_min_int(multiplier, &multiplier_i,
  91                                        INT_MIN) != pcmk_rc_ok)) {
  92                 /* The ocf:pacemaker:ping resource agent defaults multiplier to
  93                  * 1. The agent currently does not handle invalid text, but it
  94                  * should, and this would be a reasonable choice ...
  95                  */
  96                 multiplier_i = 1;
  97             }
  98             *expected_score = host_list_num * multiplier_i;
  99 
 100             return TRUE;
 101         }
 102     }
 103     return FALSE;
 104 }
 105 
 106 static GList *
 107 filter_attr_list(GList *attr_list, char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 108 {
 109     int i;
 110     const char *filt_str[] = FILTER_STR;
 111 
 112     CRM_CHECK(name != NULL, return attr_list);
 113 
 114     /* filtering automatic attributes */
 115     for (i = 0; filt_str[i] != NULL; i++) {
 116         if (g_str_has_prefix(name, filt_str[i])) {
 117             return attr_list;
 118         }
 119     }
 120 
 121     return g_list_insert_sorted(attr_list, name, compare_attribute);
 122 }
 123 
 124 static GList *
 125 get_operation_list(xmlNode *rsc_entry) {
     /* [previous][next][first][last][top][bottom][index][help] */
 126     GList *op_list = NULL;
 127     xmlNode *rsc_op = NULL;
 128 
 129     for (rsc_op = pcmk__xe_first_child(rsc_entry); rsc_op != NULL;
 130          rsc_op = pcmk__xe_next(rsc_op)) {
 131         const char *task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
 132         const char *interval_ms_s = crm_element_value(rsc_op,
 133                                                       XML_LRM_ATTR_INTERVAL_MS);
 134         const char *op_rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC);
 135         int op_rc_i;
 136 
 137         pcmk__scan_min_int(op_rc, &op_rc_i, 0);
 138 
 139         /* Display 0-interval monitors as "probe" */
 140         if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)
 141             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
 142             task = "probe";
 143         }
 144 
 145         /* Ignore notifies and some probes */
 146         if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_casei) || (pcmk__str_eq(task, "probe", pcmk__str_casei) && (op_rc_i == 7))) {
 147             continue;
 148         }
 149 
 150         if (pcmk__str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, pcmk__str_none)) {
 151             op_list = g_list_append(op_list, rsc_op);
 152         }
 153     }
 154 
 155     op_list = g_list_sort(op_list, sort_op_by_callid);
 156     return op_list;
 157 }
 158 
 159 static void
 160 add_dump_node(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162     xmlNodePtr node = user_data;
 163     pcmk_create_xml_text_node(node, (const char *) key, (const char *) value);
 164 }
 165 
 166 static void
 167 append_dump_text(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 {
 169     char **dump_text = user_data;
 170     char *new_text = crm_strdup_printf("%s %s=%s",
 171                                        *dump_text, (char *)key, (char *)value);
 172 
 173     free(*dump_text);
 174     *dump_text = new_text;
 175 }
 176 
 177 static const char *
 178 get_cluster_stack(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']",
 181                                       data_set->input, LOG_DEBUG);
 182     return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown";
 183 }
 184 
 185 static char *
 186 last_changed_string(const char *last_written, const char *user,
     /* [previous][next][first][last][top][bottom][index][help] */
 187                     const char *client, const char *origin) {
 188     if (last_written != NULL || user != NULL || client != NULL || origin != NULL) {
 189         return crm_strdup_printf("%s%s%s%s%s%s%s",
 190                                  last_written ? last_written : "",
 191                                  user ? " by " : "",
 192                                  user ? user : "",
 193                                  client ? " via " : "",
 194                                  client ? client : "",
 195                                  origin ? " on " : "",
 196                                  origin ? origin : "");
 197     } else {
 198         return strdup("");
 199     }
 200 }
 201 
 202 static char *
 203 op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s,
     /* [previous][next][first][last][top][bottom][index][help] */
 204                   int rc, gboolean print_timing) {
 205     const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
 206     char *interval_str = NULL;
 207     char *buf = NULL;
 208 
 209     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
 210         char *pair = pcmk__format_nvpair("interval", interval_ms_s, "ms");
 211         interval_str = crm_strdup_printf(" %s", pair);
 212         free(pair);
 213     }
 214 
 215     if (print_timing) {
 216         char *last_change_str = NULL;
 217         char *exec_str = NULL;
 218         char *queue_str = NULL;
 219 
 220         const char *value = NULL;
 221 
 222         time_t epoch = 0;
 223 
 224         if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &epoch) == pcmk_ok)
 225             && (epoch > 0)) {
 226             char *time = pcmk__format_named_time(XML_RSC_OP_LAST_CHANGE, epoch);
 227 
 228             last_change_str = crm_strdup_printf(" %s", time);
 229             free(time);
 230         }
 231 
 232         value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
 233         if (value) {
 234             char *pair = pcmk__format_nvpair(XML_RSC_OP_T_EXEC, value, "ms");
 235             exec_str = crm_strdup_printf(" %s", pair);
 236             free(pair);
 237         }
 238 
 239         value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
 240         if (value) {
 241             char *pair = pcmk__format_nvpair(XML_RSC_OP_T_QUEUE, value, "ms");
 242             queue_str = crm_strdup_printf(" %s", pair);
 243             free(pair);
 244         }
 245 
 246         buf = crm_strdup_printf("(%s) %s:%s%s%s%s rc=%d (%s)", call, task,
 247                                 interval_str ? interval_str : "",
 248                                 last_change_str ? last_change_str : "",
 249                                 exec_str ? exec_str : "",
 250                                 queue_str ? queue_str : "",
 251                                 rc, services_ocf_exitcode_str(rc));
 252 
 253         if (last_change_str) {
 254             free(last_change_str);
 255         }
 256 
 257         if (exec_str) {
 258             free(exec_str);
 259         }
 260 
 261         if (queue_str) {
 262             free(queue_str);
 263         }
 264     } else {
 265         buf = crm_strdup_printf("(%s) %s%s%s", call, task,
 266                                 interval_str ? ":" : "",
 267                                 interval_str ? interval_str : "");
 268     }
 269 
 270     if (interval_str) {
 271         free(interval_str);
 272     }
 273 
 274     return buf;
 275 }
 276 
 277 static char *
 278 resource_history_string(pe_resource_t *rsc, const char *rsc_id, gboolean all,
     /* [previous][next][first][last][top][bottom][index][help] */
 279                         int failcount, time_t last_failure) {
 280     char *buf = NULL;
 281 
 282     if (rsc == NULL) {
 283         buf = crm_strdup_printf("%s: orphan", rsc_id);
 284     } else if (all || failcount || last_failure > 0) {
 285         char *failcount_s = NULL;
 286         char *lastfail_s = NULL;
 287 
 288         if (failcount > 0) {
 289             failcount_s = crm_strdup_printf(" %s=%d", PCMK__FAIL_COUNT_PREFIX,
 290                                             failcount);
 291         } else {
 292             failcount_s = strdup("");
 293         }
 294         if (last_failure > 0) {
 295             lastfail_s = crm_strdup_printf(" %s='%s'",
 296                                            PCMK__LAST_FAILURE_PREFIX,
 297                                            pcmk__epoch2str(&last_failure));
 298         }
 299 
 300         buf = crm_strdup_printf("%s: migration-threshold=%d%s%s",
 301                                 rsc_id, rsc->migration_threshold, failcount_s,
 302                                 lastfail_s? lastfail_s : "");
 303         free(failcount_s);
 304         free(lastfail_s);
 305     } else {
 306         buf = crm_strdup_printf("%s:", rsc_id);
 307     }
 308 
 309     return buf;
 310 }
 311 
 312 PCMK__OUTPUT_ARGS("cluster-summary", "pe_working_set_t *", "unsigned int", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
 313 static int
 314 cluster_summary(pcmk__output_t *out, va_list args) {
 315     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 316     unsigned int section_opts = va_arg(args, unsigned int);
 317     unsigned int show_opts = va_arg(args, unsigned int);
 318 
 319     int rc = pcmk_rc_no_output;
 320     const char *stack_s = get_cluster_stack(data_set);
 321 
 322     if (pcmk_is_set(section_opts, pcmk_section_stack)) {
 323         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 324         out->message(out, "cluster-stack", stack_s);
 325     }
 326 
 327     if (pcmk_is_set(section_opts, pcmk_section_dc)) {
 328         xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
 329                                                data_set->input, LOG_DEBUG);
 330         const char *dc_version_s = dc_version?
 331                                    crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
 332                                    : NULL;
 333         const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
 334         char *dc_name = data_set->dc_node ? pe__node_display_name(data_set->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
 335 
 336         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 337         out->message(out, "cluster-dc", data_set->dc_node, quorum, dc_version_s, dc_name);
 338         free(dc_name);
 339     }
 340 
 341     if (pcmk_is_set(section_opts, pcmk_section_times)) {
 342         const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
 343         const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
 344         const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
 345         const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
 346 
 347         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 348         out->message(out, "cluster-times", last_written, user, client, origin);
 349     }
 350 
 351     if (pcmk_is_set(section_opts, pcmk_section_counts)) {
 352         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 353         out->message(out, "cluster-counts", g_list_length(data_set->nodes),
 354                      data_set->ninstances, data_set->disabled_resources,
 355                      data_set->blocked_resources);
 356     }
 357 
 358     if (pcmk_is_set(section_opts, pcmk_section_options)) {
 359         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 360         out->message(out, "cluster-options", data_set);
 361     }
 362 
 363     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 364 
 365     if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
 366         if (out->message(out, "maint-mode", data_set->flags) == pcmk_rc_ok) {
 367             rc = pcmk_rc_ok;
 368         }
 369     }
 370 
 371     return rc;
 372 }
 373 
 374 PCMK__OUTPUT_ARGS("cluster-summary", "pe_working_set_t *", "unsigned int", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
 375 static int
 376 cluster_summary_html(pcmk__output_t *out, va_list args) {
 377     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 378     unsigned int section_opts = va_arg(args, unsigned int);
 379     unsigned int show_opts = va_arg(args, unsigned int);
 380 
 381     int rc = pcmk_rc_no_output;
 382     const char *stack_s = get_cluster_stack(data_set);
 383 
 384     if (pcmk_is_set(section_opts, pcmk_section_stack)) {
 385         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 386         out->message(out, "cluster-stack", stack_s);
 387     }
 388 
 389     /* Always print DC if none, even if not requested */
 390     if (data_set->dc_node == NULL || pcmk_is_set(section_opts, pcmk_section_dc)) {
 391         xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
 392                                                data_set->input, LOG_DEBUG);
 393         const char *dc_version_s = dc_version?
 394                                    crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
 395                                    : NULL;
 396         const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
 397         char *dc_name = data_set->dc_node ? pe__node_display_name(data_set->dc_node, pcmk_is_set(show_opts, pcmk_show_node_id)) : NULL;
 398 
 399         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 400         out->message(out, "cluster-dc", data_set->dc_node, quorum, dc_version_s, dc_name);
 401         free(dc_name);
 402     }
 403 
 404     if (pcmk_is_set(section_opts, pcmk_section_times)) {
 405         const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
 406         const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
 407         const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
 408         const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
 409 
 410         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 411         out->message(out, "cluster-times", last_written, user, client, origin);
 412     }
 413 
 414     if (pcmk_is_set(section_opts, pcmk_section_counts)) {
 415         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 416         out->message(out, "cluster-counts", g_list_length(data_set->nodes),
 417                      data_set->ninstances, data_set->disabled_resources,
 418                      data_set->blocked_resources);
 419     }
 420 
 421     if (pcmk_is_set(section_opts, pcmk_section_options)) {
 422         /* Kind of a hack - close the list we may have opened earlier in this
 423          * function so we can put all the options into their own list.  We
 424          * only want to do this on HTML output, though.
 425          */
 426         PCMK__OUTPUT_LIST_FOOTER(out, rc);
 427 
 428         out->begin_list(out, NULL, NULL, "Config Options");
 429         out->message(out, "cluster-options", data_set);
 430     }
 431 
 432     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 433 
 434     if (pcmk_is_set(section_opts, pcmk_section_maint_mode)) {
 435         if (out->message(out, "maint-mode", data_set->flags) == pcmk_rc_ok) {
 436             rc = pcmk_rc_ok;
 437         }
 438     }
 439 
 440     return rc;
 441 }
 442 
 443 char *
 444 pe__node_display_name(pe_node_t *node, bool print_detail)
     /* [previous][next][first][last][top][bottom][index][help] */
 445 {
 446     char *node_name;
 447     const char *node_host = NULL;
 448     const char *node_id = NULL;
 449     int name_len;
 450 
 451     CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
 452 
 453     /* Host is displayed only if this is a guest node */
 454     if (pe__is_guest_node(node)) {
 455         pe_node_t *host_node = pe__current_node(node->details->remote_rsc);
 456 
 457         if (host_node && host_node->details) {
 458             node_host = host_node->details->uname;
 459         }
 460         if (node_host == NULL) {
 461             node_host = ""; /* so we at least get "uname@" to indicate guest */
 462         }
 463     }
 464 
 465     /* Node ID is displayed if different from uname and detail is requested */
 466     if (print_detail && !pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
 467         node_id = node->details->id;
 468     }
 469 
 470     /* Determine name length */
 471     name_len = strlen(node->details->uname) + 1;
 472     if (node_host) {
 473         name_len += strlen(node_host) + 1; /* "@node_host" */
 474     }
 475     if (node_id) {
 476         name_len += strlen(node_id) + 3; /* + " (node_id)" */
 477     }
 478 
 479     /* Allocate and populate display name */
 480     node_name = malloc(name_len);
 481     CRM_ASSERT(node_name != NULL);
 482     strcpy(node_name, node->details->uname);
 483     if (node_host) {
 484         strcat(node_name, "@");
 485         strcat(node_name, node_host);
 486     }
 487     if (node_id) {
 488         strcat(node_name, " (");
 489         strcat(node_name, node_id);
 490         strcat(node_name, ")");
 491     }
 492     return node_name;
 493 }
 494 
 495 int
 496 pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name
     /* [previous][next][first][last][top][bottom][index][help] */
 497                          , size_t pairs_count, ...)
 498 {
 499     xmlNodePtr xml_node = NULL;
 500     va_list args;
 501 
 502     CRM_ASSERT(tag_name != NULL);
 503 
 504     xml_node = pcmk__output_xml_peek_parent(out);
 505     CRM_ASSERT(xml_node != NULL);
 506     xml_node = is_list
 507         ? create_xml_node(xml_node, tag_name)
 508         : xmlNewChild(xml_node, NULL, (pcmkXmlStr) tag_name, NULL);
 509 
 510     va_start(args, pairs_count);
 511     while(pairs_count--) {
 512         const char *param_name = va_arg(args, const char *);
 513         const char *param_value = va_arg(args, const char *);
 514         if (param_name && param_value) {
 515             crm_xml_add(xml_node, param_name, param_value);
 516         }
 517     };
 518     va_end(args);
 519 
 520     if (is_list) {
 521         pcmk__output_xml_push_parent(out, xml_node);
 522     }
 523     return pcmk_rc_ok;
 524 }
 525 
 526 static const char *
 527 role_desc(enum rsc_role_e role)
     /* [previous][next][first][last][top][bottom][index][help] */
 528 {
 529     if (role == RSC_ROLE_PROMOTED) {
 530 #ifdef PCMK__COMPAT_2_0
 531         return "as " RSC_ROLE_PROMOTED_LEGACY_S " ";
 532 #else
 533         return "in " RSC_ROLE_PROMOTED_S " role ";
 534 #endif
 535     }
 536     return "";
 537 }
 538 
 539 PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
 540 static int
 541 ban_html(pcmk__output_t *out, va_list args) {
 542     pe_node_t *pe_node = va_arg(args, pe_node_t *);
 543     pe__location_t *location = va_arg(args, pe__location_t *);
 544     unsigned int show_opts = va_arg(args, unsigned int);
 545 
 546     char *node_name = pe__node_display_name(pe_node,
 547                                             pcmk_is_set(show_opts, pcmk_show_node_id));
 548     char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s",
 549                                   location->id, location->rsc_lh->id,
 550                                   role_desc(location->role_filter), node_name);
 551 
 552     pcmk__output_create_html_node(out, "li", NULL, NULL, buf);
 553 
 554     free(node_name);
 555     free(buf);
 556     return pcmk_rc_ok;
 557 }
 558 
 559 PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
 560 static int
 561 ban_text(pcmk__output_t *out, va_list args) {
 562     pe_node_t *pe_node = va_arg(args, pe_node_t *);
 563     pe__location_t *location = va_arg(args, pe__location_t *);
 564     unsigned int show_opts = va_arg(args, unsigned int);
 565 
 566     char *node_name = pe__node_display_name(pe_node,
 567                                             pcmk_is_set(show_opts, pcmk_show_node_id));
 568     out->list_item(out, NULL, "%s\tprevents %s from running %son %s",
 569                    location->id, location->rsc_lh->id,
 570                    role_desc(location->role_filter), node_name);
 571 
 572     free(node_name);
 573     return pcmk_rc_ok;
 574 }
 575 
 576 PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
 577 static int
 578 ban_xml(pcmk__output_t *out, va_list args) {
 579     pe_node_t *pe_node = va_arg(args, pe_node_t *);
 580     pe__location_t *location = va_arg(args, pe__location_t *);
 581     unsigned int show_opts G_GNUC_UNUSED = va_arg(args, unsigned int);
 582 
 583     const char *promoted_only = pcmk__btoa(location->role_filter == RSC_ROLE_PROMOTED);
 584     char *weight_s = pcmk__itoa(pe_node->weight);
 585 
 586     pcmk__output_create_xml_node(out, "ban",
 587                                  "id", location->id,
 588                                  "resource", location->rsc_lh->id,
 589                                  "node", pe_node->details->uname,
 590                                  "weight", weight_s,
 591                                  "promoted-only", promoted_only,
 592                                  /* This is a deprecated alias for
 593                                   * promoted_only. Removing it will break
 594                                   * backward compatibility of the API schema,
 595                                   * which will require an API schema major
 596                                   * version bump.
 597                                   */
 598                                  "master_only", promoted_only,
 599                                  NULL);
 600 
 601     free(weight_s);
 602     return pcmk_rc_ok;
 603 }
 604 
 605 PCMK__OUTPUT_ARGS("ban-list", "pe_working_set_t *", "const char *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
 606                   "unsigned int", "gboolean")
 607 static int
 608 ban_list(pcmk__output_t *out, va_list args) {
 609     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 610     const char *prefix = va_arg(args, const char *);
 611     GList *only_rsc = va_arg(args, GList *);
 612     unsigned int show_opts = va_arg(args, unsigned int);
 613     gboolean print_spacer = va_arg(args, gboolean);
 614 
 615     GList *gIter, *gIter2;
 616     int rc = pcmk_rc_no_output;
 617 
 618     /* Print each ban */
 619     for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
 620         pe__location_t *location = gIter->data;
 621 
 622         if (prefix != NULL && !g_str_has_prefix(location->id, prefix)) {
 623             continue;
 624         }
 625 
 626         if (!pcmk__str_in_list(rsc_printable_id(location->rsc_lh), only_rsc, pcmk__str_star_matches) &&
 627             !pcmk__str_in_list(rsc_printable_id(uber_parent(location->rsc_lh)), only_rsc, pcmk__str_star_matches)) {
 628             continue;
 629         }
 630 
 631         for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) {
 632             pe_node_t *node = (pe_node_t *) gIter2->data;
 633 
 634             if (node->weight < 0) {
 635                 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Negative Location Constraints");
 636                 out->message(out, "ban", node, location, show_opts);
 637             }
 638         }
 639     }
 640 
 641     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 642     return rc;
 643 }
 644 
 645 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 646 static int
 647 cluster_counts_html(pcmk__output_t *out, va_list args) {
 648     unsigned int nnodes = va_arg(args, unsigned int);
 649     int nresources = va_arg(args, int);
 650     int ndisabled = va_arg(args, int);
 651     int nblocked = va_arg(args, int);
 652 
 653     xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li", NULL);
 654     xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li", NULL);
 655 
 656     char *nnodes_str = crm_strdup_printf("%d node%s configured",
 657                                          nnodes, pcmk__plural_s(nnodes));
 658 
 659     pcmk_create_html_node(nodes_node, "span", NULL, NULL, nnodes_str);
 660     free(nnodes_str);
 661 
 662     if (ndisabled && nblocked) {
 663         char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
 664                                     nresources, pcmk__plural_s(nresources),
 665                                     ndisabled);
 666         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 667         free(s);
 668 
 669         pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED");
 670 
 671         s = crm_strdup_printf(", %d ", nblocked);
 672         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 673         free(s);
 674 
 675         pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED");
 676         pcmk_create_html_node(resources_node, "span", NULL, NULL,
 677                               " from further action due to failure)");
 678     } else if (ndisabled && !nblocked) {
 679         char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
 680                                     nresources, pcmk__plural_s(nresources),
 681                                     ndisabled);
 682         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 683         free(s);
 684 
 685         pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED");
 686         pcmk_create_html_node(resources_node, "span", NULL, NULL, ")");
 687     } else if (!ndisabled && nblocked) {
 688         char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
 689                                     nresources, pcmk__plural_s(nresources),
 690                                     nblocked);
 691         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 692         free(s);
 693 
 694         pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED");
 695         pcmk_create_html_node(resources_node, "span", NULL, NULL,
 696                               " from further action due to failure)");
 697     } else {
 698         char *s = crm_strdup_printf("%d resource instance%s configured",
 699                                     nresources, pcmk__plural_s(nresources));
 700         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 701         free(s);
 702     }
 703 
 704     return pcmk_rc_ok;
 705 }
 706 
 707 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 708 static int
 709 cluster_counts_text(pcmk__output_t *out, va_list args) {
 710     unsigned int nnodes = va_arg(args, unsigned int);
 711     int nresources = va_arg(args, int);
 712     int ndisabled = va_arg(args, int);
 713     int nblocked = va_arg(args, int);
 714 
 715     out->list_item(out, NULL, "%d node%s configured",
 716                    nnodes, pcmk__plural_s(nnodes));
 717 
 718     if (ndisabled && nblocked) {
 719         out->list_item(out, NULL, "%d resource instance%s configured "
 720                                   "(%d DISABLED, %d BLOCKED from "
 721                                   "further action due to failure)",
 722                        nresources, pcmk__plural_s(nresources), ndisabled,
 723                        nblocked);
 724     } else if (ndisabled && !nblocked) {
 725         out->list_item(out, NULL, "%d resource instance%s configured "
 726                                   "(%d DISABLED)",
 727                        nresources, pcmk__plural_s(nresources), ndisabled);
 728     } else if (!ndisabled && nblocked) {
 729         out->list_item(out, NULL, "%d resource instance%s configured "
 730                                   "(%d BLOCKED from further action "
 731                                   "due to failure)",
 732                        nresources, pcmk__plural_s(nresources), nblocked);
 733     } else {
 734         out->list_item(out, NULL, "%d resource instance%s configured",
 735                        nresources, pcmk__plural_s(nresources));
 736     }
 737 
 738     return pcmk_rc_ok;
 739 }
 740 
 741 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 742 static int
 743 cluster_counts_xml(pcmk__output_t *out, va_list args) {
 744     unsigned int nnodes = va_arg(args, unsigned int);
 745     int nresources = va_arg(args, int);
 746     int ndisabled = va_arg(args, int);
 747     int nblocked = va_arg(args, int);
 748 
 749     xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured", NULL);
 750     xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured", NULL);
 751 
 752     char *s = pcmk__itoa(nnodes);
 753     crm_xml_add(nodes_node, "number", s);
 754     free(s);
 755 
 756     s = pcmk__itoa(nresources);
 757     crm_xml_add(resources_node, "number", s);
 758     free(s);
 759 
 760     s = pcmk__itoa(ndisabled);
 761     crm_xml_add(resources_node, "disabled", s);
 762     free(s);
 763 
 764     s = pcmk__itoa(nblocked);
 765     crm_xml_add(resources_node, "blocked", s);
 766     free(s);
 767 
 768     return pcmk_rc_ok;
 769 }
 770 
 771 PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 772 static int
 773 cluster_dc_html(pcmk__output_t *out, va_list args) {
 774     pe_node_t *dc = va_arg(args, pe_node_t *);
 775     const char *quorum = va_arg(args, const char *);
 776     const char *dc_version_s = va_arg(args, const char *);
 777     char *dc_name = va_arg(args, char *);
 778 
 779     xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
 780 
 781     pcmk_create_html_node(node, "span", NULL, "bold", "Current DC: ");
 782 
 783     if (dc) {
 784         if (crm_is_true(quorum)) {
 785             char *buf = crm_strdup_printf("%s (version %s) - partition with quorum",
 786                                           dc_name, dc_version_s ? dc_version_s : "unknown");
 787             pcmk_create_html_node(node, "span", NULL, NULL, buf);
 788             free(buf);
 789         } else {
 790             char *buf = crm_strdup_printf("%s (version %s) - partition",
 791                                           dc_name, dc_version_s ? dc_version_s : "unknown");
 792             pcmk_create_html_node(node, "span", NULL, NULL, buf);
 793             free(buf);
 794 
 795             pcmk_create_html_node(node, "span", NULL, "warning", "WITHOUT");
 796             pcmk_create_html_node(node, "span", NULL, NULL, "quorum");
 797         }
 798     } else {
 799         pcmk_create_html_node(node ,"span", NULL, "warning", "NONE");
 800     }
 801 
 802     return pcmk_rc_ok;
 803 }
 804 
 805 PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 806 static int
 807 cluster_dc_text(pcmk__output_t *out, va_list args) {
 808     pe_node_t *dc = va_arg(args, pe_node_t *);
 809     const char *quorum = va_arg(args, const char *);
 810     const char *dc_version_s = va_arg(args, const char *);
 811     char *dc_name = va_arg(args, char *);
 812 
 813     if (dc) {
 814         out->list_item(out, "Current DC", "%s (version %s) - partition %s quorum",
 815                        dc_name, dc_version_s ? dc_version_s : "unknown",
 816                        crm_is_true(quorum) ? "with" : "WITHOUT");
 817     } else {
 818         out->list_item(out, "Current DC", "NONE");
 819     }
 820 
 821     return pcmk_rc_ok;
 822 }
 823 
 824 PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 825 static int
 826 cluster_dc_xml(pcmk__output_t *out, va_list args) {
 827     pe_node_t *dc = va_arg(args, pe_node_t *);
 828     const char *quorum = va_arg(args, const char *);
 829     const char *dc_version_s = va_arg(args, const char *);
 830     char *dc_name G_GNUC_UNUSED = va_arg(args, char *);
 831 
 832     if (dc) {
 833         pcmk__output_create_xml_node(out, "current_dc",
 834                                      "present", "true",
 835                                      "version", dc_version_s ? dc_version_s : "",
 836                                      "name", dc->details->uname,
 837                                      "id", dc->details->id,
 838                                      "with_quorum", pcmk__btoa(crm_is_true(quorum)),
 839                                      NULL);
 840     } else {
 841         pcmk__output_create_xml_node(out, "current_dc",
 842                                      "present", "false",
 843                                      NULL);
 844     }
 845 
 846     return pcmk_rc_ok;
 847 }
 848 
 849 PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long int")
     /* [previous][next][first][last][top][bottom][index][help] */
 850 static int
 851 cluster_maint_mode_text(pcmk__output_t *out, va_list args) {
 852     unsigned long long flags = va_arg(args, unsigned long long);
 853 
 854     if (pcmk_is_set(flags, pe_flag_maintenance_mode)) {
 855         pcmk__formatted_printf(out, "\n              *** Resource management is DISABLED ***\n");
 856         pcmk__formatted_printf(out, "  The cluster will not attempt to start, stop or recover services\n");
 857         return pcmk_rc_ok;
 858     } else if (pcmk_is_set(flags, pe_flag_stop_everything)) {
 859         pcmk__formatted_printf(out, "\n    *** Resource management is DISABLED ***\n");
 860         pcmk__formatted_printf(out, "  The cluster will keep all resources stopped\n");
 861         return pcmk_rc_ok;
 862     } else {
 863         return pcmk_rc_no_output;
 864     }
 865 }
 866 
 867 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 868 static int
 869 cluster_options_html(pcmk__output_t *out, va_list args) {
 870     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 871 
 872     out->list_item(out, NULL, "STONITH of failed nodes %s",
 873                    pcmk_is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
 874 
 875     out->list_item(out, NULL, "Cluster is %s",
 876                    pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric");
 877 
 878     switch (data_set->no_quorum_policy) {
 879         case no_quorum_freeze:
 880             out->list_item(out, NULL, "No quorum policy: Freeze resources");
 881             break;
 882 
 883         case no_quorum_stop:
 884             out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
 885             break;
 886 
 887         case no_quorum_demote:
 888             out->list_item(out, NULL, "No quorum policy: Demote promotable "
 889                            "resources and stop all other resources");
 890             break;
 891 
 892         case no_quorum_ignore:
 893             out->list_item(out, NULL, "No quorum policy: Ignore");
 894             break;
 895 
 896         case no_quorum_suicide:
 897             out->list_item(out, NULL, "No quorum policy: Suicide");
 898             break;
 899     }
 900 
 901     if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 902         xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
 903 
 904         pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
 905         pcmk_create_html_node(node, "span", NULL, "bold", "DISABLED");
 906         pcmk_create_html_node(node, "span", NULL, NULL,
 907                               " (the cluster will not attempt to start, stop, or recover services)");
 908     } else if (pcmk_is_set(data_set->flags, pe_flag_stop_everything)) {
 909         xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
 910 
 911         pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
 912         pcmk_create_html_node(node, "span", NULL, "bold", "STOPPED");
 913         pcmk_create_html_node(node, "span", NULL, NULL,
 914                               " (the cluster will keep all resources stopped)");
 915     } else {
 916         out->list_item(out, NULL, "Resource management: enabled");
 917     }
 918 
 919     return pcmk_rc_ok;
 920 }
 921 
 922 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 923 static int
 924 cluster_options_log(pcmk__output_t *out, va_list args) {
 925     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 926 
 927     if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 928         return out->info(out, "Resource management is DISABLED.  The cluster will not attempt to start, stop or recover services.");
 929     } else if (pcmk_is_set(data_set->flags, pe_flag_stop_everything)) {
 930         return out->info(out, "Resource management is DISABLED.  The cluster has stopped all resources.");
 931     } else {
 932         return pcmk_rc_no_output;
 933     }
 934 }
 935 
 936 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 937 static int
 938 cluster_options_text(pcmk__output_t *out, va_list args) {
 939     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 940 
 941     out->list_item(out, NULL, "STONITH of failed nodes %s",
 942                    pcmk_is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
 943 
 944     out->list_item(out, NULL, "Cluster is %s",
 945                    pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric");
 946 
 947     switch (data_set->no_quorum_policy) {
 948         case no_quorum_freeze:
 949             out->list_item(out, NULL, "No quorum policy: Freeze resources");
 950             break;
 951 
 952         case no_quorum_stop:
 953             out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
 954             break;
 955 
 956         case no_quorum_demote:
 957             out->list_item(out, NULL, "No quorum policy: Demote promotable "
 958                            "resources and stop all other resources");
 959             break;
 960 
 961         case no_quorum_ignore:
 962             out->list_item(out, NULL, "No quorum policy: Ignore");
 963             break;
 964 
 965         case no_quorum_suicide:
 966             out->list_item(out, NULL, "No quorum policy: Suicide");
 967             break;
 968     }
 969 
 970     return pcmk_rc_ok;
 971 }
 972 
 973 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 974 static int
 975 cluster_options_xml(pcmk__output_t *out, va_list args) {
 976     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 977 
 978     const char *no_quorum_policy = NULL;
 979     char *stonith_timeout_str = pcmk__itoa(data_set->stonith_timeout);
 980     char *priority_fencing_delay_str = pcmk__itoa(data_set->priority_fencing_delay * 1000);
 981 
 982     switch (data_set->no_quorum_policy) {
 983         case no_quorum_freeze:
 984             no_quorum_policy = "freeze";
 985             break;
 986 
 987         case no_quorum_stop:
 988             no_quorum_policy = "stop";
 989             break;
 990 
 991         case no_quorum_demote:
 992             no_quorum_policy = "demote";
 993             break;
 994 
 995         case no_quorum_ignore:
 996             no_quorum_policy = "ignore";
 997             break;
 998 
 999         case no_quorum_suicide:
1000             no_quorum_policy = "suicide";
1001             break;
1002     }
1003 
1004     pcmk__output_create_xml_node(out, "cluster_options",
1005                                  "stonith-enabled", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)),
1006                                  "symmetric-cluster", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster)),
1007                                  "no-quorum-policy", no_quorum_policy,
1008                                  "maintenance-mode", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)),
1009                                  "stop-all-resources", pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_stop_everything)),
1010                                  "stonith-timeout-ms", stonith_timeout_str,
1011                                  "priority-fencing-delay-ms", priority_fencing_delay_str,
1012                                  NULL);
1013     free(stonith_timeout_str);
1014     free(priority_fencing_delay_str);
1015 
1016     return pcmk_rc_ok;
1017 }
1018 
1019 PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1020 static int
1021 cluster_stack_html(pcmk__output_t *out, va_list args) {
1022     const char *stack_s = va_arg(args, const char *);
1023 
1024     xmlNodePtr node = pcmk__output_create_xml_node(out, "li", NULL);
1025 
1026     pcmk_create_html_node(node, "span", NULL, "bold", "Stack: ");
1027     pcmk_create_html_node(node, "span", NULL, NULL, stack_s);
1028 
1029     return pcmk_rc_ok;
1030 }
1031 
1032 PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1033 static int
1034 cluster_stack_text(pcmk__output_t *out, va_list args) {
1035     const char *stack_s = va_arg(args, const char *);
1036 
1037     out->list_item(out, "Stack", "%s", stack_s);
1038     return pcmk_rc_ok;
1039 }
1040 
1041 PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1042 static int
1043 cluster_stack_xml(pcmk__output_t *out, va_list args) {
1044     const char *stack_s = va_arg(args, const char *);
1045 
1046     pcmk__output_create_xml_node(out, "stack",
1047                                  "type", stack_s,
1048                                  NULL);
1049 
1050     return pcmk_rc_ok;
1051 }
1052 
1053 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1054 static int
1055 cluster_times_html(pcmk__output_t *out, va_list args) {
1056     const char *last_written = va_arg(args, const char *);
1057     const char *user = va_arg(args, const char *);
1058     const char *client = va_arg(args, const char *);
1059     const char *origin = va_arg(args, const char *);
1060 
1061     xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li", NULL);
1062     xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li", NULL);
1063 
1064     char *buf = last_changed_string(last_written, user, client, origin);
1065 
1066     pcmk_create_html_node(updated_node, "span", NULL, "bold", "Last updated: ");
1067     pcmk_create_html_node(updated_node, "span", NULL, NULL,
1068                           pcmk__epoch2str(NULL));
1069 
1070     pcmk_create_html_node(changed_node, "span", NULL, "bold", "Last change: ");
1071     pcmk_create_html_node(changed_node, "span", NULL, NULL, buf);
1072 
1073     free(buf);
1074     return pcmk_rc_ok;
1075 }
1076 
1077 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1078 static int
1079 cluster_times_xml(pcmk__output_t *out, va_list args) {
1080     const char *last_written = va_arg(args, const char *);
1081     const char *user = va_arg(args, const char *);
1082     const char *client = va_arg(args, const char *);
1083     const char *origin = va_arg(args, const char *);
1084 
1085     pcmk__output_create_xml_node(out, "last_update",
1086                                  "time", pcmk__epoch2str(NULL),
1087                                  NULL);
1088     pcmk__output_create_xml_node(out, "last_change",
1089                                  "time", last_written ? last_written : "",
1090                                  "user", user ? user : "",
1091                                  "client", client ? client : "",
1092                                  "origin", origin ? origin : "",
1093                                  NULL);
1094 
1095     return pcmk_rc_ok;
1096 }
1097 
1098 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1099 static int
1100 cluster_times_text(pcmk__output_t *out, va_list args) {
1101     const char *last_written = va_arg(args, const char *);
1102     const char *user = va_arg(args, const char *);
1103     const char *client = va_arg(args, const char *);
1104     const char *origin = va_arg(args, const char *);
1105 
1106     char *buf = last_changed_string(last_written, user, client, origin);
1107 
1108     out->list_item(out, "Last updated", "%s", pcmk__epoch2str(NULL));
1109     out->list_item(out, "Last change", " %s", buf);
1110 
1111     free(buf);
1112     return pcmk_rc_ok;
1113 }
1114 
1115 /*!
1116  * \internal
1117  * \brief Display a failed action in less-technical natural language
1118  */
1119 static void
1120 failed_action_friendly(pcmk__output_t *out, xmlNodePtr xml_op,
     /* [previous][next][first][last][top][bottom][index][help] */
1121                        const char *op_key, const char *node_name, int rc,
1122                        int status, const char *exit_reason,
1123                        const char *exec_time)
1124 {
1125     char *rsc_id = NULL;
1126     char *task = NULL;
1127     guint interval_ms = 0;
1128     const char *last_change_str = NULL;
1129     time_t last_change_epoch = 0;
1130     GString *str = NULL;
1131 
1132     if (pcmk__str_empty(op_key)
1133         || !parse_op_key(op_key, &rsc_id, &task, &interval_ms)) {
1134         rsc_id = strdup("unknown resource");
1135         task = strdup("unknown action");
1136         interval_ms = 0;
1137     }
1138 
1139     str = g_string_sized_new(strlen(rsc_id) + strlen(task) + strlen(node_name)
1140                              + 100); // reasonable starting size
1141 
1142     g_string_printf(str, "%s ", rsc_id);
1143 
1144     if (interval_ms != 0) {
1145         g_string_append_printf(str, "%s-interval ",
1146                                pcmk__readable_interval(interval_ms));
1147     }
1148     g_string_append_printf(str, "%s on %s",
1149                            crm_action_str(task, interval_ms), node_name);
1150 
1151     if (status == PCMK_EXEC_DONE) {
1152         g_string_append_printf(str, " returned '%s'",
1153                                services_ocf_exitcode_str(rc));
1154     } else {
1155         g_string_append_printf(str, " could not be executed (%s)",
1156                                pcmk_exec_status_str(status));
1157     }
1158 
1159     if (!pcmk__str_empty(exit_reason)) {
1160         g_string_append_printf(str, " because '%s'", exit_reason);
1161     }
1162 
1163     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1164                                 &last_change_epoch) == pcmk_ok) {
1165         last_change_str = pcmk__epoch2str(&last_change_epoch);
1166         if (last_change_str != NULL) {
1167             g_string_append_printf(str, " at %s", last_change_str);
1168         }
1169     }
1170     if (!pcmk__str_empty(exec_time)) {
1171         int exec_time_ms = 0;
1172 
1173         if ((pcmk__scan_min_int(exec_time, &exec_time_ms, 0) == pcmk_rc_ok)
1174             && (exec_time_ms > 0)) {
1175             g_string_append_printf(str, " after %s",
1176                                    pcmk__readable_interval(exec_time_ms));
1177         }
1178     }
1179 
1180     out->list_item(out, NULL, "%s", str->str);
1181     g_string_free(str, TRUE);
1182     free(rsc_id);
1183     free(task);
1184 }
1185 
1186 /*!
1187  * \internal
1188  * \brief Display a failed action with technical details
1189  */
1190 static void
1191 failed_action_technical(pcmk__output_t *out, xmlNodePtr xml_op,
     /* [previous][next][first][last][top][bottom][index][help] */
1192                         const char *op_key, const char *node_name, int rc,
1193                         int status, const char *exit_reason,
1194                         const char *exec_time)
1195 {
1196     const char *call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
1197     const char *queue_time = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
1198     const char *exit_status = services_ocf_exitcode_str(rc);
1199     const char *lrm_status = pcmk_exec_status_str(status);
1200     const char *last_change_str = NULL;
1201     time_t last_change_epoch = 0;
1202     GString *str = NULL;
1203 
1204     if (pcmk__str_empty(op_key)) {
1205         op_key = "unknown operation";
1206     }
1207     if (pcmk__str_empty(exit_status)) {
1208         exit_status = "unknown exit status";
1209     }
1210     if (pcmk__str_empty(call_id)) {
1211         call_id = "unknown";
1212     }
1213 
1214     str = g_string_sized_new(strlen(op_key) + strlen(node_name)
1215                              + strlen(exit_status) + strlen(call_id)
1216                              + strlen(lrm_status) + 50); // rough estimate
1217 
1218     g_string_printf(str, "%s on %s '%s' (%d): call=%s, status='%s'",
1219                     op_key, node_name, exit_status, rc, call_id, lrm_status);
1220 
1221     if (!pcmk__str_empty(exit_reason)) {
1222         g_string_append_printf(str, ", exitreason='%s'", exit_reason);
1223     }
1224 
1225     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1226                                 &last_change_epoch) == pcmk_ok) {
1227         last_change_str = pcmk__epoch2str(&last_change_epoch);
1228         if (last_change_str != NULL) {
1229             g_string_append_printf(str, ", " XML_RSC_OP_LAST_CHANGE "='%s'",
1230                                    last_change_str);
1231         }
1232     }
1233     if (!pcmk__str_empty(queue_time)) {
1234         g_string_append_printf(str, ", queued=%sms", queue_time);
1235     }
1236     if (!pcmk__str_empty(exec_time)) {
1237         g_string_append_printf(str, ", exec=%sms", exec_time);
1238     }
1239 
1240     out->list_item(out, NULL, "%s", str->str);
1241     g_string_free(str, TRUE);
1242 }
1243 
1244 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
1245 static int
1246 failed_action_default(pcmk__output_t *out, va_list args)
1247 {
1248     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1249     unsigned int show_opts = va_arg(args, unsigned int);
1250 
1251     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1252     const char *node_name = crm_element_value(xml_op, XML_ATTR_UNAME);
1253     const char *exit_reason = crm_element_value(xml_op,
1254                                                 XML_LRM_ATTR_EXIT_REASON);
1255     const char *exec_time = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
1256 
1257     int rc;
1258     int status;
1259 
1260     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), &rc, 0);
1261 
1262     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1263                        &status, 0);
1264 
1265     if (pcmk__str_empty(op_key)) {
1266         op_key = ID(xml_op);
1267     }
1268     if (pcmk__str_empty(node_name)) {
1269         node_name = "unknown node";
1270     }
1271 
1272     if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
1273         failed_action_technical(out, xml_op, op_key, node_name, rc, status,
1274                                 exit_reason, exec_time);
1275     } else {
1276         failed_action_friendly(out, xml_op, op_key, node_name, rc, status,
1277                                exit_reason, exec_time);
1278     }
1279     return pcmk_rc_ok;
1280 }
1281 
1282 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "unsigned int")
     /* [previous][next][first][last][top][bottom][index][help] */
1283 static int
1284 failed_action_xml(pcmk__output_t *out, va_list args) {
1285     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1286     unsigned int show_opts G_GNUC_UNUSED = va_arg(args, unsigned int);
1287 
1288     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1289     int rc;
1290     int status;
1291     const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
1292 
1293     time_t epoch = 0;
1294     char *rc_s = NULL;
1295     char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none");
1296     xmlNodePtr node = NULL;
1297 
1298     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), &rc, 0);
1299     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1300                        &status, 0);
1301 
1302     rc_s = pcmk__itoa(rc);
1303     node = pcmk__output_create_xml_node(out, "failure",
1304                                         (op_key == NULL)? "id" : "op_key",
1305                                         (op_key == NULL)? ID(xml_op) : op_key,
1306                                         "node", crm_element_value(xml_op, XML_ATTR_UNAME),
1307                                         "exitstatus", services_ocf_exitcode_str(rc),
1308                                         "exitreason", crm_str(reason_s),
1309                                         "exitcode", rc_s,
1310                                         "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1311                                         "status", pcmk_exec_status_str(status),
1312                                         NULL);
1313     free(rc_s);
1314 
1315     if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1316                                  &epoch) == pcmk_ok) && (epoch > 0)) {
1317         guint interval_ms = 0;
1318         char *s = NULL;
1319         crm_time_t *crm_when = crm_time_new_undefined();
1320         char *rc_change = NULL;
1321 
1322         crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
1323         s = pcmk__itoa(interval_ms);
1324 
1325         crm_time_set_timet(crm_when, &epoch);
1326         rc_change = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1327 
1328         pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, rc_change,
1329                            "queued", crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
1330                            "exec", crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
1331                            "interval", s,
1332                            "task", crm_element_value(xml_op, XML_LRM_ATTR_TASK),
1333                            NULL);
1334 
1335         free(s);
1336         free(rc_change);
1337         crm_time_free(crm_when);
1338     }
1339 
1340     free(reason_s);
1341     return pcmk_rc_ok;
1342 }
1343 
1344 PCMK__OUTPUT_ARGS("failed-action-list", "pe_working_set_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
1345                   "GList *", "unsigned int", "gboolean")
1346 static int
1347 failed_action_list(pcmk__output_t *out, va_list args) {
1348     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1349     GList *only_node = va_arg(args, GList *);
1350     GList *only_rsc = va_arg(args, GList *);
1351     unsigned int show_opts = va_arg(args, gboolean);
1352     gboolean print_spacer = va_arg(args, gboolean);
1353 
1354     xmlNode *xml_op = NULL;
1355     int rc = pcmk_rc_no_output;
1356 
1357     const char *id = NULL;
1358 
1359     if (xmlChildElementCount(data_set->failed) == 0) {
1360         return rc;
1361     }
1362 
1363     for (xml_op = pcmk__xml_first_child(data_set->failed); xml_op != NULL;
1364          xml_op = pcmk__xml_next(xml_op)) {
1365         char *rsc = NULL;
1366 
1367         if (!pcmk__str_in_list(crm_element_value(xml_op, XML_ATTR_UNAME), only_node,
1368                                pcmk__str_star_matches|pcmk__str_casei)) {
1369             continue;
1370         }
1371 
1372         id = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1373         if (parse_op_key(id ? id : ID(xml_op), &rsc, NULL, NULL) == FALSE) {
1374             continue;
1375         }
1376 
1377         if (!pcmk__str_in_list(rsc, only_rsc, pcmk__str_star_matches)) {
1378             free(rsc);
1379             continue;
1380         }
1381 
1382         free(rsc);
1383 
1384         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Resource Actions");
1385         out->message(out, "failed-action", xml_op, show_opts);
1386     }
1387 
1388     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1389     return rc;
1390 }
1391 
1392 static void
1393 status_node(pe_node_t *node, xmlNodePtr parent)
     /* [previous][next][first][last][top][bottom][index][help] */
1394 {
1395     if (node->details->standby_onfail && node->details->online) {
1396         pcmk_create_html_node(parent, "span", NULL, "standby", " standby (on-fail)");
1397     } else if (node->details->standby && node->details->online) {
1398         char *s = crm_strdup_printf(" standby%s", node->details->running_rsc ? " (with active resources)" : "");
1399         pcmk_create_html_node(parent, "span", NULL, " standby", s);
1400         free(s);
1401     } else if (node->details->standby) {
1402         pcmk_create_html_node(parent, "span", NULL, "offline", " OFFLINE (standby)");
1403     } else if (node->details->maintenance && node->details->online) {
1404         pcmk_create_html_node(parent, "span", NULL, "maint", " maintenance");
1405     } else if (node->details->maintenance) {
1406         pcmk_create_html_node(parent, "span", NULL, "offline", " OFFLINE (maintenance)");
1407     } else if (node->details->online) {
1408         pcmk_create_html_node(parent, "span", NULL, "online", " online");
1409     } else {
1410         pcmk_create_html_node(parent, "span", NULL, "offline", " OFFLINE");
1411     }
1412 }
1413 
1414 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1415                   "GList *", "GList *")
1416 static int
1417 node_html(pcmk__output_t *out, va_list args) {
1418     pe_node_t *node = va_arg(args, pe_node_t *);
1419     unsigned int show_opts = va_arg(args, unsigned int);
1420     gboolean full = va_arg(args, gboolean);
1421     const char *node_mode G_GNUC_UNUSED = va_arg(args, const char *);
1422     GList *only_node = va_arg(args, GList *);
1423     GList *only_rsc = va_arg(args, GList *);
1424 
1425     char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1426 
1427     if (full) {
1428         xmlNodePtr item_node;
1429 
1430         if (pcmk_all_flags_set(show_opts, pcmk_show_brief | pcmk_show_rscs_by_node)) {
1431             GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1432 
1433             out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1434             item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1435             pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:");
1436             status_node(node, item_node);
1437 
1438             if (rscs != NULL) {
1439                 unsigned int new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1440                 out->begin_list(out, NULL, NULL, "Resources");
1441                 pe__rscs_brief_output(out, rscs, new_show_opts);
1442                 out->end_list(out);
1443             }
1444 
1445             pcmk__output_xml_pop_parent(out);
1446             out->end_list(out);
1447 
1448         } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1449             GList *lpc2 = NULL;
1450             int rc = pcmk_rc_no_output;
1451 
1452             out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1453             item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1454             pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:");
1455             status_node(node, item_node);
1456 
1457             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
1458                 pe_resource_t *rsc = (pe_resource_t *) lpc2->data;
1459                 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources");
1460 
1461                 out->message(out, crm_map_element_name(rsc->xml), show_opts | pcmk_show_rsc_only,
1462                              rsc, only_node, only_rsc);
1463             }
1464 
1465             PCMK__OUTPUT_LIST_FOOTER(out, rc);
1466             pcmk__output_xml_pop_parent(out);
1467             out->end_list(out);
1468 
1469         } else {
1470             char *buf = crm_strdup_printf("Node: %s", node_name);
1471 
1472             item_node = pcmk__output_create_xml_node(out, "li", NULL);
1473             pcmk_create_html_node(item_node, "span", NULL, NULL, buf);
1474             status_node(node, item_node);
1475 
1476             free(buf);
1477         }
1478     } else {
1479         out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1480     }
1481 
1482     free(node_name);
1483     return pcmk_rc_ok;
1484 }
1485 
1486 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1487                   "GList *", "GList *")
1488 static int
1489 node_text(pcmk__output_t *out, va_list args) {
1490     pe_node_t *node = va_arg(args, pe_node_t *);
1491     unsigned int show_opts = va_arg(args, unsigned int);
1492     gboolean full = va_arg(args, gboolean);
1493     const char *node_mode = va_arg(args, const char *);
1494     GList *only_node = va_arg(args, GList *);
1495     GList *only_rsc = va_arg(args, GList *);
1496 
1497     if (full) {
1498         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1499         char *buf = NULL;
1500 
1501         /* Print the node name and status */
1502         if (pe__is_guest_node(node)) {
1503             buf = crm_strdup_printf("GuestNode %s: %s", node_name, node_mode);
1504         } else if (pe__is_remote_node(node)) {
1505             buf = crm_strdup_printf("RemoteNode %s: %s", node_name, node_mode);
1506         } else {
1507             buf = crm_strdup_printf("Node %s: %s", node_name, node_mode);
1508         }
1509 
1510         /* If we're grouping by node, print its resources */
1511         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1512             if (pcmk_is_set(show_opts, pcmk_show_brief)) {
1513                 GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1514 
1515                 if (rscs != NULL) {
1516                     unsigned int new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1517                     out->begin_list(out, NULL, NULL, "%s", buf);
1518                     out->begin_list(out, NULL, NULL, "Resources");
1519 
1520                     pe__rscs_brief_output(out, rscs, new_show_opts);
1521 
1522                     out->end_list(out);
1523                     out->end_list(out);
1524 
1525                     g_list_free(rscs);
1526                 }
1527 
1528             } else {
1529                 GList *gIter2 = NULL;
1530 
1531                 out->begin_list(out, NULL, NULL, "%s", buf);
1532                 out->begin_list(out, NULL, NULL, "Resources");
1533 
1534                 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
1535                     pe_resource_t *rsc = (pe_resource_t *) gIter2->data;
1536                     out->message(out, crm_map_element_name(rsc->xml), show_opts | pcmk_show_rsc_only,
1537                                  rsc, only_node, only_rsc);
1538                 }
1539 
1540                 out->end_list(out);
1541                 out->end_list(out);
1542             }
1543         } else {
1544             out->list_item(out, NULL, "%s", buf);
1545         }
1546 
1547         free(buf);
1548         free(node_name);
1549     } else {
1550         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1551         out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1552         free(node_name);
1553     }
1554 
1555     return pcmk_rc_ok;
1556 }
1557 
1558 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1559                   "GList *", "GList *")
1560 static int
1561 node_xml(pcmk__output_t *out, va_list args) {
1562     pe_node_t *node = va_arg(args, pe_node_t *);
1563     unsigned int show_opts G_GNUC_UNUSED = va_arg(args, unsigned int);
1564     gboolean full = va_arg(args, gboolean);
1565     const char *node_mode G_GNUC_UNUSED = va_arg(args, const char *);
1566     GList *only_node = va_arg(args, GList *);
1567     GList *only_rsc = va_arg(args, GList *);
1568 
1569     if (full) {
1570         const char *node_type = "unknown";
1571         char *length_s = pcmk__itoa(g_list_length(node->details->running_rsc));
1572 
1573         switch (node->details->type) {
1574             case node_member:
1575                 node_type = "member";
1576                 break;
1577             case node_remote:
1578                 node_type = "remote";
1579                 break;
1580             case node_ping:
1581                 node_type = "ping";
1582                 break;
1583         }
1584         pe__name_and_nvpairs_xml(out, true, "node", 13,
1585                                  "name", node->details->uname,
1586                                  "id", node->details->id,
1587                                  "online", pcmk__btoa(node->details->online),
1588                                  "standby", pcmk__btoa(node->details->standby),
1589                                  "standby_onfail", pcmk__btoa(node->details->standby_onfail),
1590                                  "maintenance", pcmk__btoa(node->details->maintenance),
1591                                  "pending", pcmk__btoa(node->details->pending),
1592                                  "unclean", pcmk__btoa(node->details->unclean),
1593                                  "shutdown", pcmk__btoa(node->details->shutdown),
1594                                  "expected_up", pcmk__btoa(node->details->expected_up),
1595                                  "is_dc", pcmk__btoa(node->details->is_dc),
1596                                  "resources_running", length_s,
1597                                  "type", node_type);
1598 
1599         if (pe__is_guest_node(node)) {
1600             xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
1601             crm_xml_add(xml_node, "id_as_resource", node->details->remote_rsc->container->id);
1602         }
1603 
1604         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1605             GList *lpc = NULL;
1606 
1607             for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
1608                 pe_resource_t *rsc = (pe_resource_t *) lpc->data;
1609                 out->message(out, crm_map_element_name(rsc->xml), show_opts | pcmk_show_rsc_only,
1610                              rsc, only_node, only_rsc);
1611             }
1612         }
1613 
1614         free(length_s);
1615 
1616         out->end_list(out);
1617     } else {
1618         pcmk__output_xml_create_parent(out, "node",
1619                                        "name", node->details->uname,
1620                                        NULL);
1621     }
1622 
1623     return pcmk_rc_ok;
1624 }
1625 
1626 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1627 static int
1628 node_attribute_text(pcmk__output_t *out, va_list args) {
1629     const char *name = va_arg(args, const char *);
1630     const char *value = va_arg(args, const char *);
1631     gboolean add_extra = va_arg(args, gboolean);
1632     int expected_score = va_arg(args, int);
1633 
1634     if (add_extra) {
1635         int v;
1636 
1637         if (value == NULL) {
1638             v = 0;
1639         } else {
1640             pcmk__scan_min_int(value, &v, INT_MIN);
1641         }
1642         if (v <= 0) {
1643             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value);
1644         } else if (v < expected_score) {
1645             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score);
1646         } else {
1647             out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1648         }
1649     } else {
1650         out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1651     }
1652 
1653     return pcmk_rc_ok;
1654 }
1655 
1656 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1657 static int
1658 node_attribute_html(pcmk__output_t *out, va_list args) {
1659     const char *name = va_arg(args, const char *);
1660     const char *value = va_arg(args, const char *);
1661     gboolean add_extra = va_arg(args, gboolean);
1662     int expected_score = va_arg(args, int);
1663 
1664     if (add_extra) {
1665         int v;
1666         char *s = crm_strdup_printf("%s: %s", name, value);
1667         xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li", NULL);
1668 
1669         if (value == NULL) {
1670             v = 0;
1671         } else {
1672             pcmk__scan_min_int(value, &v, INT_MIN);
1673         }
1674 
1675         pcmk_create_html_node(item_node, "span", NULL, NULL, s);
1676         free(s);
1677 
1678         if (v <= 0) {
1679             pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)");
1680         } else if (v < expected_score) {
1681             char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score);
1682             pcmk_create_html_node(item_node, "span", NULL, "bold", buf);
1683             free(buf);
1684         }
1685     } else {
1686         out->list_item(out, NULL, "%s: %s", name, value);
1687     }
1688 
1689     return pcmk_rc_ok;
1690 }
1691 
1692 PCMK__OUTPUT_ARGS("node-and-op", "pe_working_set_t *", "xmlNodePtr")
     /* [previous][next][first][last][top][bottom][index][help] */
1693 static int
1694 node_and_op(pcmk__output_t *out, va_list args) {
1695     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1696     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1697 
1698     pe_resource_t *rsc = NULL;
1699     gchar *node_str = NULL;
1700     char *last_change_str = NULL;
1701 
1702     const char *op_rsc = crm_element_value(xml_op, "resource");
1703     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1704     int status;
1705     time_t last_change = 0;
1706 
1707     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1708                        &status, 0);
1709 
1710     rsc = pe_find_resource(data_set->resources, op_rsc);
1711 
1712     if (rsc) {
1713         pe_node_t *node = pe__current_node(rsc);
1714         const char *target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
1715         unsigned int show_opts = pcmk_show_rsc_only | pcmk_show_pending;
1716 
1717         if (node == NULL) {
1718             node = rsc->pending_node;
1719         }
1720 
1721         node_str = pcmk__native_output_string(rsc, rsc_printable_id(rsc), node,
1722                                               show_opts, target_role, false);
1723     } else {
1724         node_str = crm_strdup_printf("Unknown resource %s", op_rsc);
1725     }
1726 
1727     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1728                                 &last_change) == pcmk_ok) {
1729         last_change_str = crm_strdup_printf(", %s=%s, exec=%sms",
1730                                             XML_RSC_OP_LAST_CHANGE,
1731                                             pcmk__trim(ctime(&last_change)),
1732                                             crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
1733     }
1734 
1735     out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s",
1736                    node_str, op_key ? op_key : ID(xml_op),
1737                    crm_element_value(xml_op, XML_ATTR_UNAME),
1738                    crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1739                    crm_element_value(xml_op, XML_LRM_ATTR_RC),
1740                    last_change_str ? last_change_str : "",
1741                    pcmk_exec_status_str(status));
1742 
1743     g_free(node_str);
1744     free(last_change_str);
1745     return pcmk_rc_ok;
1746 }
1747 
1748 PCMK__OUTPUT_ARGS("node-and-op", "pe_working_set_t *", "xmlNodePtr")
     /* [previous][next][first][last][top][bottom][index][help] */
1749 static int
1750 node_and_op_xml(pcmk__output_t *out, va_list args) {
1751     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1752     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1753 
1754     pe_resource_t *rsc = NULL;
1755     const char *op_rsc = crm_element_value(xml_op, "resource");
1756     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1757     int status;
1758     time_t last_change = 0;
1759     xmlNode *node = NULL;
1760 
1761     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1762                        &status, 0);
1763     node = pcmk__output_create_xml_node(out, "operation",
1764                                         "op", op_key ? op_key : ID(xml_op),
1765                                         "node", crm_element_value(xml_op, XML_ATTR_UNAME),
1766                                         "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1767                                         "rc", crm_element_value(xml_op, XML_LRM_ATTR_RC),
1768                                         "status", pcmk_exec_status_str(status),
1769                                         NULL);
1770 
1771     rsc = pe_find_resource(data_set->resources, op_rsc);
1772 
1773     if (rsc) {
1774         const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
1775         const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE);
1776         char *agent_tuple = NULL;
1777 
1778         agent_tuple = crm_strdup_printf("%s:%s:%s", class,
1779                                         pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider) ? crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER) : "",
1780                                         kind);
1781 
1782         pcmk__xe_set_props(node, "rsc", rsc_printable_id(rsc),
1783                            "agent", agent_tuple,
1784                            NULL);
1785         free(agent_tuple);
1786     }
1787 
1788     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1789                                 &last_change) == pcmk_ok) {
1790         pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE,
1791                            pcmk__trim(ctime(&last_change)),
1792                            XML_RSC_OP_T_EXEC, crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
1793                            NULL);
1794     }
1795 
1796     return pcmk_rc_ok;
1797 }
1798 
1799 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1800 static int
1801 node_attribute_xml(pcmk__output_t *out, va_list args) {
1802     const char *name = va_arg(args, const char *);
1803     const char *value = va_arg(args, const char *);
1804     gboolean add_extra = va_arg(args, gboolean);
1805     int expected_score = va_arg(args, int);
1806 
1807     xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute",
1808                                                    "name", name,
1809                                                    "value", value,
1810                                                    NULL);
1811 
1812     if (add_extra) {
1813         char *buf = pcmk__itoa(expected_score);
1814         crm_xml_add(node, "expected", buf);
1815         free(buf);
1816     }
1817 
1818     return pcmk_rc_ok;
1819 }
1820 
1821 PCMK__OUTPUT_ARGS("node-attribute-list", "pe_working_set_t *", "unsigned int",
     /* [previous][next][first][last][top][bottom][index][help] */
1822                   "gboolean", "GList *", "GList *")
1823 static int
1824 node_attribute_list(pcmk__output_t *out, va_list args) {
1825     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1826     unsigned int show_opts = va_arg(args, unsigned int);
1827     gboolean print_spacer = va_arg(args, gboolean);
1828     GList *only_node = va_arg(args, GList *);
1829     GList *only_rsc = va_arg(args, GList *);
1830 
1831     int rc = pcmk_rc_no_output;
1832 
1833     /* Display each node's attributes */
1834     for (GList *gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1835         pe_node_t *node = gIter->data;
1836 
1837         GList *attr_list = NULL;
1838         GHashTableIter iter;
1839         gpointer key;
1840 
1841         if (!node || !node->details || !node->details->online) {
1842             continue;
1843         }
1844 
1845         g_hash_table_iter_init(&iter, node->details->attrs);
1846         while (g_hash_table_iter_next (&iter, &key, NULL)) {
1847             attr_list = filter_attr_list(attr_list, key);
1848         }
1849 
1850         if (attr_list == NULL) {
1851             continue;
1852         }
1853 
1854         if (!pcmk__str_in_list(node->details->uname, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
1855             g_list_free(attr_list);
1856             continue;
1857         }
1858 
1859         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node Attributes");
1860 
1861         out->message(out, "node", node, show_opts, FALSE, NULL, only_node, only_rsc);
1862 
1863         for (GList *aIter = attr_list; aIter != NULL; aIter = aIter->next) {
1864             const char *name = aIter->data;
1865             const char *value = NULL;
1866             int expected_score = 0;
1867             gboolean add_extra = FALSE;
1868 
1869             value = pe_node_attribute_raw(node, name);
1870 
1871             add_extra = add_extra_info(node, node->details->running_rsc,
1872                                        data_set, name, &expected_score);
1873 
1874             /* Print attribute name and value */
1875             out->message(out, "node-attribute", name, value, add_extra,
1876                          expected_score);
1877         }
1878 
1879         g_list_free(attr_list);
1880         out->end_list(out);
1881     }
1882 
1883     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1884     return rc;
1885 }
1886 
1887 PCMK__OUTPUT_ARGS("node-capacity", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1888 static int
1889 node_capacity(pcmk__output_t *out, va_list args)
1890 {
1891     pe_node_t *node = va_arg(args, pe_node_t *);
1892     const char *comment = va_arg(args, const char *);
1893 
1894     char *dump_text = crm_strdup_printf("%s: %s capacity:",
1895                                         comment, node->details->uname);
1896 
1897     g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
1898     out->list_item(out, NULL, "%s", dump_text);
1899     free(dump_text);
1900 
1901     return pcmk_rc_ok;
1902 }
1903 
1904 PCMK__OUTPUT_ARGS("node-capacity", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1905 static int
1906 node_capacity_xml(pcmk__output_t *out, va_list args)
1907 {
1908     pe_node_t *node = va_arg(args, pe_node_t *);
1909     const char *comment = va_arg(args, const char *);
1910 
1911     xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "capacity",
1912                                                        "node", node->details->uname,
1913                                                        "comment", comment,
1914                                                        NULL);
1915     g_hash_table_foreach(node->details->utilization, add_dump_node, xml_node);
1916 
1917     return pcmk_rc_ok;
1918 }
1919 
1920 PCMK__OUTPUT_ARGS("node-history-list", "pe_working_set_t *", "pe_node_t *", "xmlNodePtr",
     /* [previous][next][first][last][top][bottom][index][help] */
1921                   "GList *", "GList *", "unsigned int", "unsigned int")
1922 static int
1923 node_history_list(pcmk__output_t *out, va_list args) {
1924     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1925     pe_node_t *node = va_arg(args, pe_node_t *);
1926     xmlNode *node_state = va_arg(args, xmlNode *);
1927     GList *only_node = va_arg(args, GList *);
1928     GList *only_rsc = va_arg(args, GList *);
1929     unsigned int section_opts = va_arg(args, unsigned int);
1930     unsigned int show_opts = va_arg(args, unsigned int);
1931 
1932     xmlNode *lrm_rsc = NULL;
1933     xmlNode *rsc_entry = NULL;
1934     int rc = pcmk_rc_no_output;
1935 
1936     lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
1937     lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
1938 
1939     /* Print history of each of the node's resources */
1940     for (rsc_entry = first_named_child(lrm_rsc, XML_LRM_TAG_RESOURCE);
1941          rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) {
1942         const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
1943         pe_resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
1944 
1945         /* We can't use is_filtered here to filter group resources.  For is_filtered,
1946          * we have to decide whether to check the parent or not.  If we check the
1947          * parent, all elements of a group will always be printed because that's how
1948          * is_filtered works for groups.  If we do not check the parent, sometimes
1949          * this will filter everything out.
1950          *
1951          * For other resource types, is_filtered is okay.
1952          */
1953         if (uber_parent(rsc)->variant == pe_group) {
1954             if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) &&
1955                 !pcmk__str_in_list(rsc_printable_id(uber_parent(rsc)), only_rsc, pcmk__str_star_matches)) {
1956                 continue;
1957             }
1958         } else {
1959             if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
1960                 continue;
1961             }
1962         }
1963 
1964         if (!pcmk_is_set(section_opts, pcmk_section_operations)) {
1965             time_t last_failure = 0;
1966             int failcount = pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
1967                                              NULL, data_set);
1968 
1969             if (failcount <= 0) {
1970                 continue;
1971             }
1972 
1973             if (rc == pcmk_rc_no_output) {
1974                 rc = pcmk_rc_ok;
1975                 out->message(out, "node", node, show_opts, FALSE, NULL, only_node, only_rsc);
1976             }
1977 
1978             out->message(out, "resource-history", rsc, rsc_id, FALSE,
1979                          failcount, last_failure, FALSE);
1980         } else {
1981             GList *op_list = get_operation_list(rsc_entry);
1982             pe_resource_t *rsc = pe_find_resource(data_set->resources,
1983                                                   crm_element_value(rsc_entry, XML_ATTR_ID));
1984 
1985             if (op_list == NULL) {
1986                 continue;
1987             }
1988 
1989             if (rc == pcmk_rc_no_output) {
1990                 rc = pcmk_rc_ok;
1991                 out->message(out, "node", node, show_opts, FALSE, NULL, only_node, only_rsc);
1992             }
1993 
1994             out->message(out, "resource-operation-list", data_set, rsc, node,
1995                          op_list, show_opts);
1996         }
1997     }
1998 
1999     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2000     return rc;
2001 }
2002 
2003 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "unsigned int", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2004 static int
2005 node_list_html(pcmk__output_t *out, va_list args) {
2006     GList *nodes = va_arg(args, GList *);
2007     GList *only_node = va_arg(args, GList *);
2008     GList *only_rsc = va_arg(args, GList *);
2009     unsigned int show_opts = va_arg(args, unsigned int);
2010     gboolean print_spacer G_GNUC_UNUSED = va_arg(args, gboolean);
2011 
2012     int rc = pcmk_rc_no_output;
2013 
2014     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2015         pe_node_t *node = (pe_node_t *) gIter->data;
2016 
2017         if (!pcmk__str_in_list(node->details->uname, only_node,
2018                                pcmk__str_star_matches|pcmk__str_casei)) {
2019             continue;
2020         }
2021 
2022         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Node List");
2023 
2024         out->message(out, "node", node, show_opts, TRUE, NULL, only_node, only_rsc);
2025     }
2026 
2027     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2028     return rc;
2029 }
2030 
2031 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "unsigned int", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2032 static int
2033 node_list_text(pcmk__output_t *out, va_list args) {
2034     GList *nodes = va_arg(args, GList *);
2035     GList *only_node = va_arg(args, GList *);
2036     GList *only_rsc = va_arg(args, GList *);
2037     unsigned int show_opts = va_arg(args, unsigned int);
2038     gboolean print_spacer = va_arg(args, gboolean);
2039 
2040     /* space-separated lists of node names */
2041     char *online_nodes = NULL;
2042     char *online_remote_nodes = NULL;
2043     char *online_guest_nodes = NULL;
2044     char *offline_nodes = NULL;
2045     char *offline_remote_nodes = NULL;
2046 
2047     size_t online_nodes_len = 0;
2048     size_t online_remote_nodes_len = 0;
2049     size_t online_guest_nodes_len = 0;
2050     size_t offline_nodes_len = 0;
2051     size_t offline_remote_nodes_len = 0;
2052 
2053     int rc = pcmk_rc_no_output;
2054 
2055     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2056         pe_node_t *node = (pe_node_t *) gIter->data;
2057         const char *node_mode = NULL;
2058         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
2059 
2060         if (!pcmk__str_in_list(node->details->uname, only_node,
2061                                pcmk__str_star_matches|pcmk__str_casei)) {
2062             free(node_name);
2063             continue;
2064         }
2065 
2066         PCMK__OUTPUT_LIST_HEADER(out, print_spacer == TRUE, rc, "Node List");
2067 
2068         /* Get node mode */
2069         if (node->details->unclean) {
2070             if (node->details->online) {
2071                 node_mode = "UNCLEAN (online)";
2072 
2073             } else if (node->details->pending) {
2074                 node_mode = "UNCLEAN (pending)";
2075 
2076             } else {
2077                 node_mode = "UNCLEAN (offline)";
2078             }
2079 
2080         } else if (node->details->pending) {
2081             node_mode = "pending";
2082 
2083         } else if (node->details->standby_onfail && node->details->online) {
2084             node_mode = "standby (on-fail)";
2085 
2086         } else if (node->details->standby) {
2087             if (node->details->online) {
2088                 if (node->details->running_rsc) {
2089                     node_mode = "standby (with active resources)";
2090                 } else {
2091                     node_mode = "standby";
2092                 }
2093             } else {
2094                 node_mode = "OFFLINE (standby)";
2095             }
2096 
2097         } else if (node->details->maintenance) {
2098             if (node->details->online) {
2099                 node_mode = "maintenance";
2100             } else {
2101                 node_mode = "OFFLINE (maintenance)";
2102             }
2103 
2104         } else if (node->details->online) {
2105             node_mode = "online";
2106             if (!pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2107                 if (pe__is_guest_node(node)) {
2108                     pcmk__add_word(&online_guest_nodes,
2109                                    &online_guest_nodes_len, node_name);
2110                 } else if (pe__is_remote_node(node)) {
2111                     pcmk__add_word(&online_remote_nodes,
2112                                    &online_remote_nodes_len, node_name);
2113                 } else {
2114                     pcmk__add_word(&online_nodes, &online_nodes_len, node_name);
2115                 }
2116                 free(node_name);
2117                 continue;
2118             }
2119 
2120         } else {
2121             node_mode = "OFFLINE";
2122             if (!pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2123                 if (pe__is_remote_node(node)) {
2124                     pcmk__add_word(&offline_remote_nodes,
2125                                    &offline_remote_nodes_len, node_name);
2126                 } else if (pe__is_guest_node(node)) {
2127                     /* ignore offline guest nodes */
2128                 } else {
2129                     pcmk__add_word(&offline_nodes,
2130                                    &offline_nodes_len, node_name);
2131                 }
2132                 free(node_name);
2133                 continue;
2134             }
2135         }
2136 
2137         /* If we get here, node is in bad state, or we're grouping by node */
2138         out->message(out, "node", node, show_opts, TRUE, node_mode, only_node, only_rsc);
2139         free(node_name);
2140     }
2141 
2142     /* If we're not grouping by node, summarize nodes by status */
2143     if (online_nodes) {
2144         out->list_item(out, "Online", "[ %s ]", online_nodes);
2145         free(online_nodes);
2146     }
2147     if (offline_nodes) {
2148         out->list_item(out, "OFFLINE", "[ %s ]", offline_nodes);
2149         free(offline_nodes);
2150     }
2151     if (online_remote_nodes) {
2152         out->list_item(out, "RemoteOnline", "[ %s ]", online_remote_nodes);
2153         free(online_remote_nodes);
2154     }
2155     if (offline_remote_nodes) {
2156         out->list_item(out, "RemoteOFFLINE", "[ %s ]", offline_remote_nodes);
2157         free(offline_remote_nodes);
2158     }
2159     if (online_guest_nodes) {
2160         out->list_item(out, "GuestOnline", "[ %s ]", online_guest_nodes);
2161         free(online_guest_nodes);
2162     }
2163 
2164     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2165     return rc;
2166 }
2167 
2168 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "unsigned int", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2169 static int
2170 node_list_xml(pcmk__output_t *out, va_list args) {
2171     GList *nodes = va_arg(args, GList *);
2172     GList *only_node = va_arg(args, GList *);
2173     GList *only_rsc = va_arg(args, GList *);
2174     unsigned int show_opts = va_arg(args, unsigned int);
2175     gboolean print_spacer G_GNUC_UNUSED = va_arg(args, gboolean);
2176 
2177     out->begin_list(out, NULL, NULL, "nodes");
2178     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2179         pe_node_t *node = (pe_node_t *) gIter->data;
2180 
2181         if (!pcmk__str_in_list(node->details->uname, only_node,
2182                                pcmk__str_star_matches|pcmk__str_casei)) {
2183             continue;
2184         }
2185 
2186         out->message(out, "node", node, show_opts, TRUE, NULL, only_node, only_rsc);
2187     }
2188     out->end_list(out);
2189 
2190     return pcmk_rc_ok;
2191 }
2192 
2193 PCMK__OUTPUT_ARGS("node-summary", "pe_working_set_t *", "GList *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
2194                   "unsigned int", "unsigned int", "gboolean")
2195 static int
2196 node_summary(pcmk__output_t *out, va_list args) {
2197     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2198     GList *only_node = va_arg(args, GList *);
2199     GList *only_rsc = va_arg(args, GList *);
2200     unsigned int section_opts = va_arg(args, unsigned int);
2201     unsigned int show_opts = va_arg(args, unsigned int);
2202     gboolean print_spacer = va_arg(args, gboolean);
2203 
2204     xmlNode *node_state = NULL;
2205     xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
2206     int rc = pcmk_rc_no_output;
2207 
2208     if (xmlChildElementCount(cib_status) == 0) {
2209         return rc;
2210     }
2211 
2212     for (node_state = first_named_child(cib_status, XML_CIB_TAG_STATE);
2213          node_state != NULL; node_state = crm_next_same_xml(node_state)) {
2214         pe_node_t *node = pe_find_node_id(data_set->nodes, ID(node_state));
2215 
2216         if (!node || !node->details || !node->details->online) {
2217             continue;
2218         }
2219 
2220         if (!pcmk__str_in_list(node->details->uname, only_node,
2221                                pcmk__str_star_matches|pcmk__str_casei)) {
2222             continue;
2223         }
2224 
2225         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc,
2226                                  pcmk_is_set(section_opts, pcmk_section_operations) ? "Operations" : "Migration Summary");
2227 
2228         out->message(out, "node-history-list", data_set, node, node_state,
2229                      only_node, only_rsc, section_opts, show_opts);
2230     }
2231 
2232     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2233     return rc;
2234 }
2235 
2236 PCMK__OUTPUT_ARGS("node-weight", "pe_resource_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2237 static int
2238 node_weight(pcmk__output_t *out, va_list args)
2239 {
2240     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2241     const char *prefix = va_arg(args, const char *);
2242     const char *uname = va_arg(args, const char *);
2243     char *score = va_arg(args, char *);
2244 
2245     if (rsc) {
2246         out->list_item(out, NULL, "%s: %s allocation score on %s: %s",
2247                        prefix, rsc->id, uname, score);
2248     } else {
2249         out->list_item(out, NULL, "%s: %s = %s", prefix, uname, score);
2250     }
2251 
2252     return pcmk_rc_ok;
2253 }
2254 
2255 PCMK__OUTPUT_ARGS("node-weight", "pe_resource_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2256 static int
2257 node_weight_xml(pcmk__output_t *out, va_list args)
2258 {
2259     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2260     const char *prefix = va_arg(args, const char *);
2261     const char *uname = va_arg(args, const char *);
2262     char *score = va_arg(args, char *);
2263 
2264     xmlNodePtr node = pcmk__output_create_xml_node(out, "node_weight",
2265                                                    "function", prefix,
2266                                                    "node", uname,
2267                                                    "score", score,
2268                                                    NULL);
2269 
2270     if (rsc) {
2271         crm_xml_add(node, "id", rsc->id);
2272     }
2273 
2274     return pcmk_rc_ok;
2275 }
2276 
2277 PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int",
     /* [previous][next][first][last][top][bottom][index][help] */
2278                   "unsigned int")
2279 static int
2280 op_history_text(pcmk__output_t *out, va_list args) {
2281     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2282     const char *task = va_arg(args, const char *);
2283     const char *interval_ms_s = va_arg(args, const char *);
2284     int rc = va_arg(args, int);
2285     unsigned int show_opts = va_arg(args, unsigned int);
2286 
2287     char *buf = op_history_string(xml_op, task, interval_ms_s, rc,
2288                                   pcmk_is_set(show_opts, pcmk_show_timing));
2289 
2290     out->list_item(out, NULL, "%s", buf);
2291 
2292     free(buf);
2293     return pcmk_rc_ok;
2294 }
2295 
2296 PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int",
     /* [previous][next][first][last][top][bottom][index][help] */
2297                   "unsigned int")
2298 static int
2299 op_history_xml(pcmk__output_t *out, va_list args) {
2300     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2301     const char *task = va_arg(args, const char *);
2302     const char *interval_ms_s = va_arg(args, const char *);
2303     int rc = va_arg(args, int);
2304     unsigned int show_opts = va_arg(args, unsigned int);
2305 
2306     char *rc_s = pcmk__itoa(rc);
2307     xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history",
2308                                                    "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
2309                                                    "task", task,
2310                                                    "rc", rc_s,
2311                                                    "rc_text", services_ocf_exitcode_str(rc),
2312                                                    NULL);
2313     free(rc_s);
2314 
2315     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
2316         char *s = crm_strdup_printf("%sms", interval_ms_s);
2317         crm_xml_add(node, "interval", s);
2318         free(s);
2319     }
2320 
2321     if (pcmk_is_set(show_opts, pcmk_show_timing)) {
2322         const char *value = NULL;
2323         time_t epoch = 0;
2324 
2325         if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
2326                                      &epoch) == pcmk_ok) && (epoch > 0)) {
2327             crm_xml_add(node, XML_RSC_OP_LAST_CHANGE, pcmk__epoch2str(&epoch));
2328         }
2329 
2330         value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
2331         if (value) {
2332             char *s = crm_strdup_printf("%sms", value);
2333             crm_xml_add(node, XML_RSC_OP_T_EXEC, s);
2334             free(s);
2335         }
2336         value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
2337         if (value) {
2338             char *s = crm_strdup_printf("%sms", value);
2339             crm_xml_add(node, XML_RSC_OP_T_QUEUE, s);
2340             free(s);
2341         }
2342     }
2343 
2344     return pcmk_rc_ok;
2345 }
2346 
2347 PCMK__OUTPUT_ARGS("promotion-score", "pe_resource_t *", "pe_node_t *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2348 static int
2349 promotion_score(pcmk__output_t *out, va_list args)
2350 {
2351     pe_resource_t *child_rsc = va_arg(args, pe_resource_t *);
2352     pe_node_t *chosen = va_arg(args, pe_node_t *);
2353     char *score = va_arg(args, char *);
2354 
2355     out->list_item(out, NULL, "%s promotion score on %s: %s",
2356                    child_rsc->id,
2357                    chosen? chosen->details->uname : "none",
2358                    score);
2359     return pcmk_rc_ok;
2360 }
2361 
2362 PCMK__OUTPUT_ARGS("promotion-score", "pe_resource_t *", "pe_node_t *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2363 static int
2364 promotion_score_xml(pcmk__output_t *out, va_list args)
2365 {
2366     pe_resource_t *child_rsc = va_arg(args, pe_resource_t *);
2367     pe_node_t *chosen = va_arg(args, pe_node_t *);
2368     char *score = va_arg(args, char *);
2369 
2370     xmlNodePtr node = pcmk__output_create_xml_node(out, "promotion_score",
2371                                                    "id", child_rsc->id,
2372                                                    "score", score,
2373                                                    NULL);
2374 
2375     if (chosen) {
2376         crm_xml_add(node, "node", chosen->details->uname);
2377     }
2378 
2379     return pcmk_rc_ok;
2380 }
2381 
2382 PCMK__OUTPUT_ARGS("resource-config", "pe_resource_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2383 static int
2384 resource_config(pcmk__output_t *out, va_list args) {
2385     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2386     gboolean raw = va_arg(args, gboolean);
2387 
2388     char *rsc_xml = NULL;
2389 
2390     if (raw) {
2391         rsc_xml = dump_xml_formatted(rsc->orig_xml ? rsc->orig_xml : rsc->xml);
2392     } else {
2393         rsc_xml = dump_xml_formatted(rsc->xml);
2394     }
2395 
2396     pcmk__formatted_printf(out, "Resource XML:\n");
2397     out->output_xml(out, "xml", rsc_xml);
2398 
2399     free(rsc_xml);
2400     return pcmk_rc_ok;
2401 }
2402 
2403 PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2404 static int
2405 resource_history_text(pcmk__output_t *out, va_list args) {
2406     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2407     const char *rsc_id = va_arg(args, const char *);
2408     gboolean all = va_arg(args, gboolean);
2409     int failcount = va_arg(args, int);
2410     time_t last_failure = va_arg(args, int);
2411     gboolean as_header = va_arg(args, gboolean);
2412 
2413     char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure);
2414 
2415     if (as_header) {
2416         out->begin_list(out, NULL, NULL, "%s", buf);
2417     } else {
2418         out->list_item(out, NULL, "%s", buf);
2419     }
2420 
2421     free(buf);
2422     return pcmk_rc_ok;
2423 }
2424 
2425 PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2426 static int
2427 resource_history_xml(pcmk__output_t *out, va_list args) {
2428     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2429     const char *rsc_id = va_arg(args, const char *);
2430     gboolean all = va_arg(args, gboolean);
2431     int failcount = va_arg(args, int);
2432     time_t last_failure = va_arg(args, int);
2433     gboolean as_header = va_arg(args, gboolean);
2434 
2435     xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource_history",
2436                                                      "id", rsc_id,
2437                                                      NULL);
2438 
2439     if (rsc == NULL) {
2440         crm_xml_add(node, "orphan", "true");
2441     } else if (all || failcount || last_failure > 0) {
2442         char *migration_s = pcmk__itoa(rsc->migration_threshold);
2443 
2444         pcmk__xe_set_props(node, "orphan", "false",
2445                            "migration-threshold", migration_s,
2446                            NULL);
2447         free(migration_s);
2448 
2449         if (failcount > 0) {
2450             char *s = pcmk__itoa(failcount);
2451 
2452             crm_xml_add(node, PCMK__FAIL_COUNT_PREFIX, s);
2453             free(s);
2454         }
2455 
2456         if (last_failure > 0) {
2457             crm_xml_add(node, PCMK__LAST_FAILURE_PREFIX, pcmk__epoch2str(&last_failure));
2458         }
2459     }
2460 
2461     if (as_header == FALSE) {
2462         pcmk__output_xml_pop_parent(out);
2463     }
2464 
2465     return pcmk_rc_ok;
2466 }
2467 
2468 static void
2469 print_resource_header(pcmk__output_t *out, unsigned int show_opts)
     /* [previous][next][first][last][top][bottom][index][help] */
2470 {
2471     if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2472         /* Active resources have already been printed by node */
2473         out->begin_list(out, NULL, NULL, "Inactive Resources");
2474     } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2475         out->begin_list(out, NULL, NULL, "Full List of Resources");
2476     } else {
2477         out->begin_list(out, NULL, NULL, "Active Resources");
2478     }
2479 }
2480 
2481 
2482 PCMK__OUTPUT_ARGS("resource-list", "pe_working_set_t *", "unsigned int",
     /* [previous][next][first][last][top][bottom][index][help] */
2483                   "gboolean", "GList *", "GList *", "gboolean")
2484 static int
2485 resource_list(pcmk__output_t *out, va_list args)
2486 {
2487     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2488     unsigned int show_opts = va_arg(args, unsigned int);
2489     gboolean print_summary = va_arg(args, gboolean);
2490     GList *only_node = va_arg(args, GList *);
2491     GList *only_rsc = va_arg(args, GList *);
2492     gboolean print_spacer = va_arg(args, gboolean);
2493 
2494     GList *rsc_iter;
2495     int rc = pcmk_rc_no_output;
2496     bool printed_header = false;
2497 
2498     /* If we already showed active resources by node, and
2499      * we're not showing inactive resources, we have nothing to do
2500      */
2501     if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node) &&
2502         !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2503         return rc;
2504     }
2505 
2506     /* If we haven't already printed resources grouped by node,
2507      * and brief output was requested, print resource summary */
2508     if (pcmk_is_set(show_opts, pcmk_show_brief) && !pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2509         GList *rscs = pe__filter_rsc_list(data_set->resources, only_rsc);
2510 
2511         PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2512         print_resource_header(out, show_opts);
2513         printed_header = true;
2514 
2515         rc = pe__rscs_brief_output(out, rscs, show_opts);
2516         g_list_free(rscs);
2517     }
2518 
2519     /* For each resource, display it if appropriate */
2520     for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
2521         pe_resource_t *rsc = (pe_resource_t *) rsc_iter->data;
2522         int x;
2523 
2524         /* Complex resources may have some sub-resources active and some inactive */
2525         gboolean is_active = rsc->fns->active(rsc, TRUE);
2526         gboolean partially_active = rsc->fns->active(rsc, FALSE);
2527 
2528         /* Skip inactive orphans (deleted but still in CIB) */
2529         if (pcmk_is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
2530             continue;
2531 
2532         /* Skip active resources if we already displayed them by node */
2533         } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2534             if (is_active) {
2535                 continue;
2536             }
2537 
2538         /* Skip primitives already counted in a brief summary */
2539         } else if (pcmk_is_set(show_opts, pcmk_show_brief) && (rsc->variant == pe_native)) {
2540             continue;
2541 
2542         /* Skip resources that aren't at least partially active,
2543          * unless we're displaying inactive resources
2544          */
2545         } else if (!partially_active && !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2546             continue;
2547 
2548         } else if (partially_active && !pe__rsc_running_on_any(rsc, only_node)) {
2549             continue;
2550         }
2551 
2552         if (!printed_header) {
2553             PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2554             print_resource_header(out, show_opts);
2555             printed_header = true;
2556         }
2557 
2558         /* Print this resource */
2559         x = out->message(out, crm_map_element_name(rsc->xml), show_opts, rsc,
2560                          only_node, only_rsc);
2561         if (x == pcmk_rc_ok) {
2562             rc = pcmk_rc_ok;
2563         }
2564     }
2565 
2566     if (print_summary && rc != pcmk_rc_ok) {
2567         if (!printed_header) {
2568             PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2569             print_resource_header(out, show_opts);
2570             printed_header = true;
2571         }
2572 
2573         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2574             out->list_item(out, NULL, "No inactive resources");
2575         } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2576             out->list_item(out, NULL, "No resources");
2577         } else {
2578             out->list_item(out, NULL, "No active resources");
2579         }
2580     }
2581 
2582     if (printed_header) {
2583         out->end_list(out);
2584     }
2585 
2586     return rc;
2587 }
2588 
2589 PCMK__OUTPUT_ARGS("resource-operation-list", "pe_working_set_t *", "pe_resource_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
2590                   "pe_node_t *", "GList *", "unsigned int")
2591 static int
2592 resource_operation_list(pcmk__output_t *out, va_list args)
2593 {
2594     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2595     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2596     pe_node_t *node = va_arg(args, pe_node_t *);
2597     GList *op_list = va_arg(args, GList *);
2598     unsigned int show_opts = va_arg(args, unsigned int);
2599 
2600     GList *gIter = NULL;
2601     int rc = pcmk_rc_no_output;
2602 
2603     /* Print each operation */
2604     for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
2605         xmlNode *xml_op = (xmlNode *) gIter->data;
2606         const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
2607         const char *interval_ms_s = crm_element_value(xml_op,
2608                                                       XML_LRM_ATTR_INTERVAL_MS);
2609         const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
2610         int op_rc_i;
2611 
2612         pcmk__scan_min_int(op_rc, &op_rc_i, 0);
2613 
2614         /* Display 0-interval monitors as "probe" */
2615         if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)
2616             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
2617             task = "probe";
2618         }
2619 
2620         /* If this is the first printed operation, print heading for resource */
2621         if (rc == pcmk_rc_no_output) {
2622             time_t last_failure = 0;
2623             int failcount = pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
2624                                              NULL, data_set);
2625 
2626             out->message(out, "resource-history", rsc, rsc_printable_id(rsc), TRUE,
2627                          failcount, last_failure, TRUE);
2628             rc = pcmk_rc_ok;
2629         }
2630 
2631         /* Print the operation */
2632         out->message(out, "op-history", xml_op, task, interval_ms_s,
2633                      op_rc_i, show_opts);
2634     }
2635 
2636     /* Free the list we created (no need to free the individual items) */
2637     g_list_free(op_list);
2638 
2639     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2640     return rc;
2641 }
2642 
2643 PCMK__OUTPUT_ARGS("resource-util", "pe_resource_t *", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2644 static int
2645 resource_util(pcmk__output_t *out, va_list args)
2646 {
2647     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2648     pe_node_t *node = va_arg(args, pe_node_t *);
2649     const char *fn = va_arg(args, const char *);
2650 
2651     char *dump_text = crm_strdup_printf("%s: %s utilization on %s:",
2652                                         fn, rsc->id, node->details->uname);
2653 
2654     g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
2655     out->list_item(out, NULL, "%s", dump_text);
2656     free(dump_text);
2657 
2658     return pcmk_rc_ok;
2659 }
2660 
2661 PCMK__OUTPUT_ARGS("resource-util", "pe_resource_t *", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2662 static int
2663 resource_util_xml(pcmk__output_t *out, va_list args)
2664 {
2665     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2666     pe_node_t *node = va_arg(args, pe_node_t *);
2667     const char *fn = va_arg(args, const char *);
2668 
2669     xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "utilization",
2670                                                        "resource", rsc->id,
2671                                                        "node", node->details->uname,
2672                                                        "function", fn,
2673                                                        NULL);
2674     g_hash_table_foreach(rsc->utilization, add_dump_node, xml_node);
2675 
2676     return pcmk_rc_ok;
2677 }
2678 
2679 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
2680 static int
2681 ticket_html(pcmk__output_t *out, va_list args) {
2682     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
2683 
2684     if (ticket->last_granted > -1) {
2685         char *time = pcmk__format_named_time("last-granted",
2686                                              ticket->last_granted);
2687 
2688         out->list_item(out, NULL, "%s:\t%s%s %s", ticket->id,
2689                        ticket->granted ? "granted" : "revoked",
2690                        ticket->standby ? " [standby]" : "",
2691                        time);
2692         free(time);
2693     } else {
2694         out->list_item(out, NULL, "%s:\t%s%s", ticket->id,
2695                        ticket->granted ? "granted" : "revoked",
2696                        ticket->standby ? " [standby]" : "");
2697     }
2698 
2699     return pcmk_rc_ok;
2700 }
2701 
2702 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
2703 static int
2704 ticket_text(pcmk__output_t *out, va_list args) {
2705     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
2706 
2707     if (ticket->last_granted > -1) {
2708         char *time = pcmk__format_named_time("last-granted",
2709                                              ticket->last_granted);
2710 
2711         out->list_item(out, ticket->id, "%s%s %s",
2712                        ticket->granted ? "granted" : "revoked",
2713                        ticket->standby ? " [standby]" : "",
2714                        time);
2715         free(time);
2716     } else {
2717         out->list_item(out, ticket->id, "%s%s",
2718                        ticket->granted ? "granted" : "revoked",
2719                        ticket->standby ? " [standby]" : "");
2720     }
2721 
2722     return pcmk_rc_ok;
2723 }
2724 
2725 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
2726 static int
2727 ticket_xml(pcmk__output_t *out, va_list args) {
2728     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
2729 
2730     xmlNodePtr node = NULL;
2731 
2732     node = pcmk__output_create_xml_node(out, "ticket",
2733                                         "id", ticket->id,
2734                                         "status", ticket->granted ? "granted" : "revoked",
2735                                         "standby", pcmk__btoa(ticket->standby),
2736                                         NULL);
2737 
2738     if (ticket->last_granted > -1) {
2739         crm_xml_add(node, "last-granted", pcmk__epoch2str(&ticket->last_granted));
2740     }
2741 
2742     return pcmk_rc_ok;
2743 }
2744 
2745 PCMK__OUTPUT_ARGS("ticket-list", "pe_working_set_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2746 static int
2747 ticket_list(pcmk__output_t *out, va_list args) {
2748     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2749     gboolean print_spacer = va_arg(args, gboolean);
2750 
2751     GHashTableIter iter;
2752     gpointer key, value;
2753 
2754     if (g_hash_table_size(data_set->tickets) == 0) {
2755         return pcmk_rc_no_output;
2756     }
2757 
2758     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2759 
2760     /* Print section heading */
2761     out->begin_list(out, NULL, NULL, "Tickets");
2762 
2763     /* Print each ticket */
2764     g_hash_table_iter_init(&iter, data_set->tickets);
2765     while (g_hash_table_iter_next(&iter, &key, &value)) {
2766         pe_ticket_t *ticket = (pe_ticket_t *) value;
2767         out->message(out, "ticket", ticket);
2768     }
2769 
2770     /* Close section */
2771     out->end_list(out);
2772     return pcmk_rc_ok;
2773 }
2774 
2775 static pcmk__message_entry_t fmt_functions[] = {
2776     { "ban", "default", ban_text },
2777     { "ban", "html", ban_html },
2778     { "ban", "xml", ban_xml },
2779     { "ban-list", "default", ban_list },
2780     { "bundle", "default", pe__bundle_text },
2781     { "bundle", "xml",  pe__bundle_xml },
2782     { "bundle", "html",  pe__bundle_html },
2783     { "clone", "default", pe__clone_default },
2784     { "clone", "xml",  pe__clone_xml },
2785     { "cluster-counts", "default", cluster_counts_text },
2786     { "cluster-counts", "html", cluster_counts_html },
2787     { "cluster-counts", "xml", cluster_counts_xml },
2788     { "cluster-dc", "default", cluster_dc_text },
2789     { "cluster-dc", "html", cluster_dc_html },
2790     { "cluster-dc", "xml", cluster_dc_xml },
2791     { "cluster-options", "default", cluster_options_text },
2792     { "cluster-options", "html", cluster_options_html },
2793     { "cluster-options", "log", cluster_options_log },
2794     { "cluster-options", "xml", cluster_options_xml },
2795     { "cluster-summary", "default", cluster_summary },
2796     { "cluster-summary", "html", cluster_summary_html },
2797     { "cluster-stack", "default", cluster_stack_text },
2798     { "cluster-stack", "html", cluster_stack_html },
2799     { "cluster-stack", "xml", cluster_stack_xml },
2800     { "cluster-times", "default", cluster_times_text },
2801     { "cluster-times", "html", cluster_times_html },
2802     { "cluster-times", "xml", cluster_times_xml },
2803     { "failed-action", "default", failed_action_default },
2804     { "failed-action", "xml", failed_action_xml },
2805     { "failed-action-list", "default", failed_action_list },
2806     { "group", "default",  pe__group_default},
2807     { "group", "xml",  pe__group_xml },
2808     { "maint-mode", "text", cluster_maint_mode_text },
2809     { "node", "default", node_text },
2810     { "node", "html", node_html },
2811     { "node", "xml", node_xml },
2812     { "node-and-op", "default", node_and_op },
2813     { "node-and-op", "xml", node_and_op_xml },
2814     { "node-capacity", "default", node_capacity },
2815     { "node-capacity", "xml", node_capacity_xml },
2816     { "node-history-list", "default", node_history_list },
2817     { "node-list", "default", node_list_text },
2818     { "node-list", "html", node_list_html },
2819     { "node-list", "xml", node_list_xml },
2820     { "node-weight", "default", node_weight },
2821     { "node-weight", "xml", node_weight_xml },
2822     { "node-attribute", "default", node_attribute_text },
2823     { "node-attribute", "html", node_attribute_html },
2824     { "node-attribute", "xml", node_attribute_xml },
2825     { "node-attribute-list", "default", node_attribute_list },
2826     { "node-summary", "default", node_summary },
2827     { "op-history", "default", op_history_text },
2828     { "op-history", "xml", op_history_xml },
2829     { "primitive", "default",  pe__resource_text },
2830     { "primitive", "xml",  pe__resource_xml },
2831     { "primitive", "html",  pe__resource_html },
2832     { "promotion-score", "default", promotion_score },
2833     { "promotion-score", "xml", promotion_score_xml },
2834     { "resource-config", "default", resource_config },
2835     { "resource-history", "default", resource_history_text },
2836     { "resource-history", "xml", resource_history_xml },
2837     { "resource-list", "default", resource_list },
2838     { "resource-operation-list", "default", resource_operation_list },
2839     { "resource-util", "default", resource_util },
2840     { "resource-util", "xml", resource_util_xml },
2841     { "ticket", "default", ticket_text },
2842     { "ticket", "html", ticket_html },
2843     { "ticket", "xml", ticket_xml },
2844     { "ticket-list", "default", ticket_list },
2845 
2846     { NULL, NULL, NULL }
2847 };
2848 
2849 void
2850 pe__register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
2851     pcmk__register_messages(out, fmt_functions);
2852 }
2853 
2854 void
2855 pe__output_node(pe_node_t *node, gboolean details, pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
2856 {
2857     if (node == NULL) {
2858         crm_trace("<NULL>");
2859         return;
2860     }
2861 
2862     CRM_ASSERT(node->details);
2863     crm_trace("%sNode %s: (weight=%d, fixed=%s)",
2864               node->details->online ? "" : "Unavailable/Unclean ",
2865               node->details->uname, node->weight, node->fixed ? "True" : "False");
2866 
2867     if (details) {
2868         char *pe_mutable = strdup("\t\t");
2869         GList *gIter = node->details->running_rsc;
2870         GList *all = NULL;
2871 
2872         all = g_list_prepend(all, (gpointer) "*");
2873 
2874         crm_trace("\t\t===Node Attributes");
2875         g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
2876         free(pe_mutable);
2877 
2878         crm_trace("\t\t=== Resources");
2879 
2880         for (; gIter != NULL; gIter = gIter->next) {
2881             pe_resource_t *rsc = (pe_resource_t *) gIter->data;
2882 
2883             out->message(out, crm_map_element_name(rsc->xml),
2884                          pe_print_pending, rsc, all, all);
2885         }
2886 
2887         g_list_free(all);
2888     }
2889 }

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