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. node_text_status
  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. PCMK__OUTPUT_ARGS
  69. print_resource_header
  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. PCMK__OUTPUT_ARGS
  78. pe__register_messages
  79. pe__output_node

   1 /*
   2  * Copyright 2019-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <stdint.h>
  12 #include <crm/common/xml_internal.h>
  13 #include <crm/common/output.h>
  14 #include <crm/cib/util.h>
  15 #include <crm/msg_xml.h>
  16 #include <crm/pengine/internal.h>
  17 
  18 /* Never display node attributes whose name starts with one of these prefixes */
  19 #define FILTER_STR { PCMK__FAIL_COUNT_PREFIX, PCMK__LAST_FAILURE_PREFIX,   \
  20                      "shutdown", "terminate", "standby", "#", 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 *", "uint32_t", "uint32_t")
     /* [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     uint32_t section_opts = va_arg(args, uint32_t);
 317     uint32_t show_opts = va_arg(args, uint32_t);
 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 *", "uint32_t", "uint32_t")
     /* [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     uint32_t section_opts = va_arg(args, uint32_t);
 379     uint32_t show_opts = va_arg(args, uint32_t);
 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 *", "uint32_t")
     /* [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     uint32_t show_opts = va_arg(args, uint32_t);
 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 *", "uint32_t")
     /* [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     uint32_t show_opts = va_arg(args, uint32_t);
 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 *", "uint32_t")
     /* [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     uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
 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                   "uint32_t", "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     uint32_t show_opts = va_arg(args, uint32_t);
 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     CRM_ASSERT((rsc_id != NULL) && (task != NULL));
1139 
1140     str = g_string_sized_new(256); // Should be sufficient for most messages
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         if (!pcmk__str_empty(exit_reason)) {
1155             g_string_append_printf(str, " (%s)", exit_reason);
1156         }
1157 
1158     } else {
1159         g_string_append_printf(str, " could not be executed (%s",
1160                                pcmk_exec_status_str(status));
1161         if (!pcmk__str_empty(exit_reason)) {
1162             g_string_append_printf(str, ": %s", exit_reason);
1163         }
1164         g_string_append(str, ")");
1165     }
1166 
1167 
1168     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1169                                 &last_change_epoch) == pcmk_ok) {
1170         last_change_str = pcmk__epoch2str(&last_change_epoch);
1171         if (last_change_str != NULL) {
1172             g_string_append_printf(str, " at %s", last_change_str);
1173         }
1174     }
1175     if (!pcmk__str_empty(exec_time)) {
1176         int exec_time_ms = 0;
1177 
1178         if ((pcmk__scan_min_int(exec_time, &exec_time_ms, 0) == pcmk_rc_ok)
1179             && (exec_time_ms > 0)) {
1180             g_string_append_printf(str, " after %s",
1181                                    pcmk__readable_interval(exec_time_ms));
1182         }
1183     }
1184 
1185     out->list_item(out, NULL, "%s", str->str);
1186     g_string_free(str, TRUE);
1187     free(rsc_id);
1188     free(task);
1189 }
1190 
1191 /*!
1192  * \internal
1193  * \brief Display a failed action with technical details
1194  */
1195 static void
1196 failed_action_technical(pcmk__output_t *out, xmlNodePtr xml_op,
     /* [previous][next][first][last][top][bottom][index][help] */
1197                         const char *op_key, const char *node_name, int rc,
1198                         int status, const char *exit_reason,
1199                         const char *exec_time)
1200 {
1201     const char *call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
1202     const char *queue_time = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
1203     const char *exit_status = services_ocf_exitcode_str(rc);
1204     const char *lrm_status = pcmk_exec_status_str(status);
1205     const char *last_change_str = NULL;
1206     time_t last_change_epoch = 0;
1207     GString *str = NULL;
1208 
1209     if (pcmk__str_empty(op_key)) {
1210         op_key = "unknown operation";
1211     }
1212     if (pcmk__str_empty(exit_status)) {
1213         exit_status = "unknown exit status";
1214     }
1215     if (pcmk__str_empty(call_id)) {
1216         call_id = "unknown";
1217     }
1218 
1219     str = g_string_sized_new(strlen(op_key) + strlen(node_name)
1220                              + strlen(exit_status) + strlen(call_id)
1221                              + strlen(lrm_status) + 50); // rough estimate
1222 
1223     g_string_printf(str, "%s on %s '%s' (%d): call=%s, status='%s'",
1224                     op_key, node_name, exit_status, rc, call_id, lrm_status);
1225 
1226     if (!pcmk__str_empty(exit_reason)) {
1227         g_string_append_printf(str, ", exitreason='%s'", exit_reason);
1228     }
1229 
1230     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1231                                 &last_change_epoch) == pcmk_ok) {
1232         last_change_str = pcmk__epoch2str(&last_change_epoch);
1233         if (last_change_str != NULL) {
1234             g_string_append_printf(str, ", " XML_RSC_OP_LAST_CHANGE "='%s'",
1235                                    last_change_str);
1236         }
1237     }
1238     if (!pcmk__str_empty(queue_time)) {
1239         g_string_append_printf(str, ", queued=%sms", queue_time);
1240     }
1241     if (!pcmk__str_empty(exec_time)) {
1242         g_string_append_printf(str, ", exec=%sms", exec_time);
1243     }
1244 
1245     out->list_item(out, NULL, "%s", str->str);
1246     g_string_free(str, TRUE);
1247 }
1248 
1249 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
1250 static int
1251 failed_action_default(pcmk__output_t *out, va_list args)
1252 {
1253     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1254     uint32_t show_opts = va_arg(args, uint32_t);
1255 
1256     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1257     const char *node_name = crm_element_value(xml_op, XML_ATTR_UNAME);
1258     const char *exit_reason = crm_element_value(xml_op,
1259                                                 XML_LRM_ATTR_EXIT_REASON);
1260     const char *exec_time = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
1261 
1262     int rc;
1263     int status;
1264 
1265     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), &rc, 0);
1266 
1267     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1268                        &status, 0);
1269 
1270     if (pcmk__str_empty(op_key)) {
1271         op_key = ID(xml_op);
1272     }
1273     if (pcmk__str_empty(node_name)) {
1274         node_name = "unknown node";
1275     }
1276 
1277     if (pcmk_is_set(show_opts, pcmk_show_failed_detail)) {
1278         failed_action_technical(out, xml_op, op_key, node_name, rc, status,
1279                                 exit_reason, exec_time);
1280     } else {
1281         failed_action_friendly(out, xml_op, op_key, node_name, rc, status,
1282                                exit_reason, exec_time);
1283     }
1284     return pcmk_rc_ok;
1285 }
1286 
1287 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
1288 static int
1289 failed_action_xml(pcmk__output_t *out, va_list args) {
1290     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1291     uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
1292 
1293     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1294     int rc;
1295     int status;
1296     const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
1297 
1298     time_t epoch = 0;
1299     char *rc_s = NULL;
1300     char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none");
1301     xmlNodePtr node = NULL;
1302 
1303     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), &rc, 0);
1304     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1305                        &status, 0);
1306 
1307     rc_s = pcmk__itoa(rc);
1308     node = pcmk__output_create_xml_node(out, "failure",
1309                                         (op_key == NULL)? "id" : "op_key",
1310                                         (op_key == NULL)? ID(xml_op) : op_key,
1311                                         "node", crm_element_value(xml_op, XML_ATTR_UNAME),
1312                                         "exitstatus", services_ocf_exitcode_str(rc),
1313                                         "exitreason", crm_str(reason_s),
1314                                         "exitcode", rc_s,
1315                                         "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1316                                         "status", pcmk_exec_status_str(status),
1317                                         NULL);
1318     free(rc_s);
1319 
1320     if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1321                                  &epoch) == pcmk_ok) && (epoch > 0)) {
1322         guint interval_ms = 0;
1323         char *s = NULL;
1324         crm_time_t *crm_when = crm_time_new_undefined();
1325         char *rc_change = NULL;
1326 
1327         crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
1328         s = pcmk__itoa(interval_ms);
1329 
1330         crm_time_set_timet(crm_when, &epoch);
1331         rc_change = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1332 
1333         pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE, rc_change,
1334                            "queued", crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
1335                            "exec", crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
1336                            "interval", s,
1337                            "task", crm_element_value(xml_op, XML_LRM_ATTR_TASK),
1338                            NULL);
1339 
1340         free(s);
1341         free(rc_change);
1342         crm_time_free(crm_when);
1343     }
1344 
1345     free(reason_s);
1346     return pcmk_rc_ok;
1347 }
1348 
1349 PCMK__OUTPUT_ARGS("failed-action-list", "pe_working_set_t *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
1350                   "GList *", "uint32_t", "gboolean")
1351 static int
1352 failed_action_list(pcmk__output_t *out, va_list args) {
1353     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1354     GList *only_node = va_arg(args, GList *);
1355     GList *only_rsc = va_arg(args, GList *);
1356     uint32_t show_opts = va_arg(args, uint32_t);
1357     gboolean print_spacer = va_arg(args, gboolean);
1358 
1359     xmlNode *xml_op = NULL;
1360     int rc = pcmk_rc_no_output;
1361 
1362     const char *id = NULL;
1363 
1364     if (xmlChildElementCount(data_set->failed) == 0) {
1365         return rc;
1366     }
1367 
1368     for (xml_op = pcmk__xml_first_child(data_set->failed); xml_op != NULL;
1369          xml_op = pcmk__xml_next(xml_op)) {
1370         char *rsc = NULL;
1371 
1372         if (!pcmk__str_in_list(crm_element_value(xml_op, XML_ATTR_UNAME), only_node,
1373                                pcmk__str_star_matches|pcmk__str_casei)) {
1374             continue;
1375         }
1376 
1377         if (pcmk_xe_mask_probe_failure(xml_op)) {
1378             continue;
1379         }
1380 
1381         id = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1382         if (parse_op_key(id ? id : ID(xml_op), &rsc, NULL, NULL) == FALSE) {
1383             continue;
1384         }
1385 
1386         if (!pcmk__str_in_list(rsc, only_rsc, pcmk__str_star_matches)) {
1387             free(rsc);
1388             continue;
1389         }
1390 
1391         free(rsc);
1392 
1393         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Resource Actions");
1394         out->message(out, "failed-action", xml_op, show_opts);
1395     }
1396 
1397     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1398     return rc;
1399 }
1400 
1401 static void
1402 status_node(pe_node_t *node, xmlNodePtr parent)
     /* [previous][next][first][last][top][bottom][index][help] */
1403 {
1404     int health = pe__node_health(node);
1405 
1406     // Cluster membership
1407     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     // Standby mode
1414     if (node->details->standby_onfail && (node->details->running_rsc != NULL)) {
1415         pcmk_create_html_node(parent, "span", NULL, "standby",
1416                               " (in standby due to on-fail,"
1417                               " with active resources)");
1418     } else if (node->details->standby_onfail) {
1419         pcmk_create_html_node(parent, "span", NULL, "standby",
1420                               " (in standby due to on-fail)");
1421     } else if (node->details->standby && (node->details->running_rsc != NULL)) {
1422         pcmk_create_html_node(parent, "span", NULL, "standby",
1423                               " (in standby, with active resources)");
1424     } else if (node->details->standby) {
1425         pcmk_create_html_node(parent, "span", NULL, "standby", " (in standby)");
1426     }
1427 
1428     // Maintenance mode
1429     if (node->details->maintenance) {
1430         pcmk_create_html_node(parent, "span", NULL, "maint",
1431                               " (in maintenance mode)");
1432     }
1433 
1434     // Node health
1435     if (health < 0) {
1436         pcmk_create_html_node(parent, "span", NULL, "health_red",
1437                               " (health is RED)");
1438     } else if (health == 0) {
1439         pcmk_create_html_node(parent, "span", NULL, "health_yellow",
1440                               " (health is YELLOW)");
1441     }
1442 }
1443 
1444 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "uint32_t", "gboolean",
     /* [previous][next][first][last][top][bottom][index][help] */
1445                   "GList *", "GList *")
1446 static int
1447 node_html(pcmk__output_t *out, va_list args) {
1448     pe_node_t *node = va_arg(args, pe_node_t *);
1449     uint32_t show_opts = va_arg(args, uint32_t);
1450     gboolean full = va_arg(args, gboolean);
1451     GList *only_node = va_arg(args, GList *);
1452     GList *only_rsc = va_arg(args, GList *);
1453 
1454     char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1455 
1456     if (full) {
1457         xmlNodePtr item_node;
1458 
1459         if (pcmk_all_flags_set(show_opts, pcmk_show_brief | pcmk_show_rscs_by_node)) {
1460             GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1461 
1462             out->begin_list(out, NULL, NULL, "%s:", node_name);
1463             item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1464             pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:");
1465             status_node(node, item_node);
1466 
1467             if (rscs != NULL) {
1468                 uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1469                 out->begin_list(out, NULL, NULL, "Resources");
1470                 pe__rscs_brief_output(out, rscs, new_show_opts);
1471                 out->end_list(out);
1472             }
1473 
1474             pcmk__output_xml_pop_parent(out);
1475             out->end_list(out);
1476 
1477         } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1478             GList *lpc2 = NULL;
1479             int rc = pcmk_rc_no_output;
1480 
1481             out->begin_list(out, NULL, NULL, "%s:", node_name);
1482             item_node = pcmk__output_xml_create_parent(out, "li", NULL);
1483             pcmk_create_html_node(item_node, "span", NULL, NULL, "Status:");
1484             status_node(node, item_node);
1485 
1486             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
1487                 pe_resource_t *rsc = (pe_resource_t *) lpc2->data;
1488                 PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Resources");
1489 
1490                 show_opts |= pcmk_show_rsc_only;
1491                 out->message(out, crm_map_element_name(rsc->xml), show_opts,
1492                              rsc, only_node, only_rsc);
1493             }
1494 
1495             PCMK__OUTPUT_LIST_FOOTER(out, rc);
1496             pcmk__output_xml_pop_parent(out);
1497             out->end_list(out);
1498 
1499         } else {
1500             char *buf = crm_strdup_printf("%s:", node_name);
1501 
1502             item_node = pcmk__output_create_xml_node(out, "li", NULL);
1503             pcmk_create_html_node(item_node, "span", NULL, "bold", buf);
1504             status_node(node, item_node);
1505 
1506             free(buf);
1507         }
1508     } else {
1509         out->begin_list(out, NULL, NULL, "%s:", node_name);
1510     }
1511 
1512     free(node_name);
1513     return pcmk_rc_ok;
1514 }
1515 
1516 /*!
1517  * \internal
1518  * \brief Get a human-friendly textual description of a node's status
1519  *
1520  * \param[in] node  Node to check
1521  *
1522  * \return String representation of node's status
1523  */
1524 static const char *
1525 node_text_status(pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1526 {
1527     if (node->details->unclean) {
1528         if (node->details->online) {
1529             return "UNCLEAN (online)";
1530 
1531         } else if (node->details->pending) {
1532             return "UNCLEAN (pending)";
1533 
1534         } else {
1535             return "UNCLEAN (offline)";
1536         }
1537 
1538     } else if (node->details->pending) {
1539         return "pending";
1540 
1541     } else if (node->details->standby_onfail && node->details->online) {
1542         return "standby (on-fail)";
1543 
1544     } else if (node->details->standby) {
1545         if (node->details->online) {
1546             if (node->details->running_rsc) {
1547                 return "standby (with active resources)";
1548             } else {
1549                 return "standby";
1550             }
1551         } else {
1552             return "OFFLINE (standby)";
1553         }
1554 
1555     } else if (node->details->maintenance) {
1556         if (node->details->online) {
1557             return "maintenance";
1558         } else {
1559             return "OFFLINE (maintenance)";
1560         }
1561 
1562     } else if (node->details->online) {
1563         return "online";
1564     }
1565 
1566     return "OFFLINE";
1567 }
1568 
1569 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "uint32_t", "gboolean",
     /* [previous][next][first][last][top][bottom][index][help] */
1570                   "GList *", "GList *")
1571 static int
1572 node_text(pcmk__output_t *out, va_list args) {
1573     pe_node_t *node = va_arg(args, pe_node_t *);
1574     uint32_t show_opts = va_arg(args, uint32_t);
1575     gboolean full = va_arg(args, gboolean);
1576     GList *only_node = va_arg(args, GList *);
1577     GList *only_rsc = va_arg(args, GList *);
1578 
1579     if (full) {
1580         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1581         GString *str = g_string_sized_new(64);
1582         int health = pe__node_health(node);
1583 
1584         // Create a summary line with node type, name, and status
1585         if (pe__is_guest_node(node)) {
1586             g_string_append(str, "GuestNode");
1587         } else if (pe__is_remote_node(node)) {
1588             g_string_append(str, "RemoteNode");
1589         } else {
1590             g_string_append(str, "Node");
1591         }
1592         g_string_append_printf(str, " %s: %s",
1593                                node_name, node_text_status(node));
1594         if (health < 0) {
1595             g_string_append(str, " (health is RED)");
1596         } else if (health == 0) {
1597             g_string_append(str, " (health is YELLOW)");
1598         }
1599 
1600         /* If we're grouping by node, print its resources */
1601         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1602             if (pcmk_is_set(show_opts, pcmk_show_brief)) {
1603                 GList *rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1604 
1605                 if (rscs != NULL) {
1606                     uint32_t new_show_opts = (show_opts | pcmk_show_rsc_only) & ~pcmk_show_inactive_rscs;
1607                     out->begin_list(out, NULL, NULL, "%s", str->str);
1608                     out->begin_list(out, NULL, NULL, "Resources");
1609 
1610                     pe__rscs_brief_output(out, rscs, new_show_opts);
1611 
1612                     out->end_list(out);
1613                     out->end_list(out);
1614 
1615                     g_list_free(rscs);
1616                 }
1617 
1618             } else {
1619                 GList *gIter2 = NULL;
1620 
1621                 out->begin_list(out, NULL, NULL, "%s", str->str);
1622                 out->begin_list(out, NULL, NULL, "Resources");
1623 
1624                 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
1625                     pe_resource_t *rsc = (pe_resource_t *) gIter2->data;
1626 
1627                     show_opts |= pcmk_show_rsc_only;
1628                     out->message(out, crm_map_element_name(rsc->xml), show_opts,
1629                                  rsc, only_node, only_rsc);
1630                 }
1631 
1632                 out->end_list(out);
1633                 out->end_list(out);
1634             }
1635         } else {
1636             out->list_item(out, NULL, "%s", str->str);
1637         }
1638 
1639         g_string_free(str, TRUE);
1640         free(node_name);
1641     } else {
1642         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
1643         out->begin_list(out, NULL, NULL, "Node: %s", node_name);
1644         free(node_name);
1645     }
1646 
1647     return pcmk_rc_ok;
1648 }
1649 
1650 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "uint32_t", "gboolean",
     /* [previous][next][first][last][top][bottom][index][help] */
1651                   "GList *", "GList *")
1652 static int
1653 node_xml(pcmk__output_t *out, va_list args) {
1654     pe_node_t *node = va_arg(args, pe_node_t *);
1655     uint32_t show_opts G_GNUC_UNUSED = va_arg(args, uint32_t);
1656     gboolean full = va_arg(args, gboolean);
1657     GList *only_node = va_arg(args, GList *);
1658     GList *only_rsc = va_arg(args, GList *);
1659 
1660     if (full) {
1661         const char *node_type = "unknown";
1662         char *length_s = pcmk__itoa(g_list_length(node->details->running_rsc));
1663         int health = pe__node_health(node);
1664         const char *health_s = NULL;
1665 
1666         switch (node->details->type) {
1667             case node_member:
1668                 node_type = "member";
1669                 break;
1670             case node_remote:
1671                 node_type = "remote";
1672                 break;
1673             case node_ping:
1674                 node_type = "ping";
1675                 break;
1676         }
1677 
1678         if (health < 0) {
1679             health_s = "red";
1680         } else if (health == 0) {
1681             health_s = "yellow";
1682         } else {
1683             health_s = "green";
1684         }
1685 
1686         pe__name_and_nvpairs_xml(out, true, "node", 14,
1687                                  "name", node->details->uname,
1688                                  "id", node->details->id,
1689                                  "online", pcmk__btoa(node->details->online),
1690                                  "standby", pcmk__btoa(node->details->standby),
1691                                  "standby_onfail", pcmk__btoa(node->details->standby_onfail),
1692                                  "maintenance", pcmk__btoa(node->details->maintenance),
1693                                  "pending", pcmk__btoa(node->details->pending),
1694                                  "unclean", pcmk__btoa(node->details->unclean),
1695                                  "health", health_s,
1696                                  "shutdown", pcmk__btoa(node->details->shutdown),
1697                                  "expected_up", pcmk__btoa(node->details->expected_up),
1698                                  "is_dc", pcmk__btoa(node->details->is_dc),
1699                                  "resources_running", length_s,
1700                                  "type", node_type);
1701 
1702         if (pe__is_guest_node(node)) {
1703             xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
1704             crm_xml_add(xml_node, "id_as_resource", node->details->remote_rsc->container->id);
1705         }
1706 
1707         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
1708             GList *lpc = NULL;
1709 
1710             for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
1711                 pe_resource_t *rsc = (pe_resource_t *) lpc->data;
1712 
1713                 show_opts |= pcmk_show_rsc_only;
1714                 out->message(out, crm_map_element_name(rsc->xml), show_opts,
1715                              rsc, only_node, only_rsc);
1716             }
1717         }
1718 
1719         free(length_s);
1720 
1721         out->end_list(out);
1722     } else {
1723         pcmk__output_xml_create_parent(out, "node",
1724                                        "name", node->details->uname,
1725                                        NULL);
1726     }
1727 
1728     return pcmk_rc_ok;
1729 }
1730 
1731 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1732 static int
1733 node_attribute_text(pcmk__output_t *out, va_list args) {
1734     const char *name = va_arg(args, const char *);
1735     const char *value = va_arg(args, const char *);
1736     gboolean add_extra = va_arg(args, gboolean);
1737     int expected_score = va_arg(args, int);
1738 
1739     if (add_extra) {
1740         int v;
1741 
1742         if (value == NULL) {
1743             v = 0;
1744         } else {
1745             pcmk__scan_min_int(value, &v, INT_MIN);
1746         }
1747         if (v <= 0) {
1748             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value);
1749         } else if (v < expected_score) {
1750             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score);
1751         } else {
1752             out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1753         }
1754     } else {
1755         out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1756     }
1757 
1758     return pcmk_rc_ok;
1759 }
1760 
1761 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1762 static int
1763 node_attribute_html(pcmk__output_t *out, va_list args) {
1764     const char *name = va_arg(args, const char *);
1765     const char *value = va_arg(args, const char *);
1766     gboolean add_extra = va_arg(args, gboolean);
1767     int expected_score = va_arg(args, int);
1768 
1769     if (add_extra) {
1770         int v;
1771         char *s = crm_strdup_printf("%s: %s", name, value);
1772         xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li", NULL);
1773 
1774         if (value == NULL) {
1775             v = 0;
1776         } else {
1777             pcmk__scan_min_int(value, &v, INT_MIN);
1778         }
1779 
1780         pcmk_create_html_node(item_node, "span", NULL, NULL, s);
1781         free(s);
1782 
1783         if (v <= 0) {
1784             pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)");
1785         } else if (v < expected_score) {
1786             char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score);
1787             pcmk_create_html_node(item_node, "span", NULL, "bold", buf);
1788             free(buf);
1789         }
1790     } else {
1791         out->list_item(out, NULL, "%s: %s", name, value);
1792     }
1793 
1794     return pcmk_rc_ok;
1795 }
1796 
1797 PCMK__OUTPUT_ARGS("node-and-op", "pe_working_set_t *", "xmlNodePtr")
     /* [previous][next][first][last][top][bottom][index][help] */
1798 static int
1799 node_and_op(pcmk__output_t *out, va_list args) {
1800     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1801     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1802 
1803     pe_resource_t *rsc = NULL;
1804     gchar *node_str = NULL;
1805     char *last_change_str = NULL;
1806 
1807     const char *op_rsc = crm_element_value(xml_op, "resource");
1808     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1809     int status;
1810     time_t last_change = 0;
1811 
1812     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1813                        &status, 0);
1814 
1815     rsc = pe_find_resource(data_set->resources, op_rsc);
1816 
1817     if (rsc) {
1818         pe_node_t *node = pe__current_node(rsc);
1819         const char *target_role = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
1820         uint32_t show_opts = pcmk_show_rsc_only | pcmk_show_pending;
1821 
1822         if (node == NULL) {
1823             node = rsc->pending_node;
1824         }
1825 
1826         node_str = pcmk__native_output_string(rsc, rsc_printable_id(rsc), node,
1827                                               show_opts, target_role, false);
1828     } else {
1829         node_str = crm_strdup_printf("Unknown resource %s", op_rsc);
1830     }
1831 
1832     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1833                                 &last_change) == pcmk_ok) {
1834         last_change_str = crm_strdup_printf(", %s=%s, exec=%sms",
1835                                             XML_RSC_OP_LAST_CHANGE,
1836                                             pcmk__trim(ctime(&last_change)),
1837                                             crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
1838     }
1839 
1840     out->list_item(out, NULL, "%s: %s (node=%s, call=%s, rc=%s%s): %s",
1841                    node_str, op_key ? op_key : ID(xml_op),
1842                    crm_element_value(xml_op, XML_ATTR_UNAME),
1843                    crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1844                    crm_element_value(xml_op, XML_LRM_ATTR_RC),
1845                    last_change_str ? last_change_str : "",
1846                    pcmk_exec_status_str(status));
1847 
1848     g_free(node_str);
1849     free(last_change_str);
1850     return pcmk_rc_ok;
1851 }
1852 
1853 PCMK__OUTPUT_ARGS("node-and-op", "pe_working_set_t *", "xmlNodePtr")
     /* [previous][next][first][last][top][bottom][index][help] */
1854 static int
1855 node_and_op_xml(pcmk__output_t *out, va_list args) {
1856     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1857     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
1858 
1859     pe_resource_t *rsc = NULL;
1860     const char *op_rsc = crm_element_value(xml_op, "resource");
1861     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
1862     int status;
1863     time_t last_change = 0;
1864     xmlNode *node = NULL;
1865 
1866     pcmk__scan_min_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS),
1867                        &status, 0);
1868     node = pcmk__output_create_xml_node(out, "operation",
1869                                         "op", op_key ? op_key : ID(xml_op),
1870                                         "node", crm_element_value(xml_op, XML_ATTR_UNAME),
1871                                         "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
1872                                         "rc", crm_element_value(xml_op, XML_LRM_ATTR_RC),
1873                                         "status", pcmk_exec_status_str(status),
1874                                         NULL);
1875 
1876     rsc = pe_find_resource(data_set->resources, op_rsc);
1877 
1878     if (rsc) {
1879         const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
1880         const char *kind = crm_element_value(rsc->xml, XML_ATTR_TYPE);
1881         char *agent_tuple = NULL;
1882 
1883         agent_tuple = crm_strdup_printf("%s:%s:%s", class,
1884                                         pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider) ? crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER) : "",
1885                                         kind);
1886 
1887         pcmk__xe_set_props(node, "rsc", rsc_printable_id(rsc),
1888                            "agent", agent_tuple,
1889                            NULL);
1890         free(agent_tuple);
1891     }
1892 
1893     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
1894                                 &last_change) == pcmk_ok) {
1895         pcmk__xe_set_props(node, XML_RSC_OP_LAST_CHANGE,
1896                            pcmk__trim(ctime(&last_change)),
1897                            XML_RSC_OP_T_EXEC, crm_element_value(xml_op, XML_RSC_OP_T_EXEC),
1898                            NULL);
1899     }
1900 
1901     return pcmk_rc_ok;
1902 }
1903 
1904 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1905 static int
1906 node_attribute_xml(pcmk__output_t *out, va_list args) {
1907     const char *name = va_arg(args, const char *);
1908     const char *value = va_arg(args, const char *);
1909     gboolean add_extra = va_arg(args, gboolean);
1910     int expected_score = va_arg(args, int);
1911 
1912     xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute",
1913                                                    "name", name,
1914                                                    "value", value,
1915                                                    NULL);
1916 
1917     if (add_extra) {
1918         char *buf = pcmk__itoa(expected_score);
1919         crm_xml_add(node, "expected", buf);
1920         free(buf);
1921     }
1922 
1923     return pcmk_rc_ok;
1924 }
1925 
1926 PCMK__OUTPUT_ARGS("node-attribute-list", "pe_working_set_t *", "uint32_t",
     /* [previous][next][first][last][top][bottom][index][help] */
1927                   "gboolean", "GList *", "GList *")
1928 static int
1929 node_attribute_list(pcmk__output_t *out, va_list args) {
1930     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1931     uint32_t show_opts = va_arg(args, uint32_t);
1932     gboolean print_spacer = va_arg(args, gboolean);
1933     GList *only_node = va_arg(args, GList *);
1934     GList *only_rsc = va_arg(args, GList *);
1935 
1936     int rc = pcmk_rc_no_output;
1937 
1938     /* Display each node's attributes */
1939     for (GList *gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
1940         pe_node_t *node = gIter->data;
1941 
1942         GList *attr_list = NULL;
1943         GHashTableIter iter;
1944         gpointer key;
1945 
1946         if (!node || !node->details || !node->details->online) {
1947             continue;
1948         }
1949 
1950         g_hash_table_iter_init(&iter, node->details->attrs);
1951         while (g_hash_table_iter_next (&iter, &key, NULL)) {
1952             attr_list = filter_attr_list(attr_list, key);
1953         }
1954 
1955         if (attr_list == NULL) {
1956             continue;
1957         }
1958 
1959         if (!pcmk__str_in_list(node->details->uname, only_node, pcmk__str_star_matches|pcmk__str_casei)) {
1960             g_list_free(attr_list);
1961             continue;
1962         }
1963 
1964         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node Attributes");
1965 
1966         out->message(out, "node", node, show_opts, FALSE, only_node, only_rsc);
1967 
1968         for (GList *aIter = attr_list; aIter != NULL; aIter = aIter->next) {
1969             const char *name = aIter->data;
1970             const char *value = NULL;
1971             int expected_score = 0;
1972             gboolean add_extra = FALSE;
1973 
1974             value = pe_node_attribute_raw(node, name);
1975 
1976             add_extra = add_extra_info(node, node->details->running_rsc,
1977                                        data_set, name, &expected_score);
1978 
1979             /* Print attribute name and value */
1980             out->message(out, "node-attribute", name, value, add_extra,
1981                          expected_score);
1982         }
1983 
1984         g_list_free(attr_list);
1985         out->end_list(out);
1986     }
1987 
1988     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1989     return rc;
1990 }
1991 
1992 PCMK__OUTPUT_ARGS("node-capacity", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
1993 static int
1994 node_capacity(pcmk__output_t *out, va_list args)
1995 {
1996     pe_node_t *node = va_arg(args, pe_node_t *);
1997     const char *comment = va_arg(args, const char *);
1998 
1999     char *dump_text = crm_strdup_printf("%s: %s capacity:",
2000                                         comment, node->details->uname);
2001 
2002     g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
2003     out->list_item(out, NULL, "%s", dump_text);
2004     free(dump_text);
2005 
2006     return pcmk_rc_ok;
2007 }
2008 
2009 PCMK__OUTPUT_ARGS("node-capacity", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2010 static int
2011 node_capacity_xml(pcmk__output_t *out, va_list args)
2012 {
2013     pe_node_t *node = va_arg(args, pe_node_t *);
2014     const char *comment = va_arg(args, const char *);
2015 
2016     xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "capacity",
2017                                                        "node", node->details->uname,
2018                                                        "comment", comment,
2019                                                        NULL);
2020     g_hash_table_foreach(node->details->utilization, add_dump_node, xml_node);
2021 
2022     return pcmk_rc_ok;
2023 }
2024 
2025 PCMK__OUTPUT_ARGS("node-history-list", "pe_working_set_t *", "pe_node_t *", "xmlNodePtr",
     /* [previous][next][first][last][top][bottom][index][help] */
2026                   "GList *", "GList *", "uint32_t", "uint32_t")
2027 static int
2028 node_history_list(pcmk__output_t *out, va_list args) {
2029     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2030     pe_node_t *node = va_arg(args, pe_node_t *);
2031     xmlNode *node_state = va_arg(args, xmlNode *);
2032     GList *only_node = va_arg(args, GList *);
2033     GList *only_rsc = va_arg(args, GList *);
2034     uint32_t section_opts = va_arg(args, uint32_t);
2035     uint32_t show_opts = va_arg(args, uint32_t);
2036 
2037     xmlNode *lrm_rsc = NULL;
2038     xmlNode *rsc_entry = NULL;
2039     int rc = pcmk_rc_no_output;
2040 
2041     lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
2042     lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
2043 
2044     /* Print history of each of the node's resources */
2045     for (rsc_entry = first_named_child(lrm_rsc, XML_LRM_TAG_RESOURCE);
2046          rsc_entry != NULL; rsc_entry = crm_next_same_xml(rsc_entry)) {
2047         const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
2048         pe_resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
2049 
2050         /* We can't use is_filtered here to filter group resources.  For is_filtered,
2051          * we have to decide whether to check the parent or not.  If we check the
2052          * parent, all elements of a group will always be printed because that's how
2053          * is_filtered works for groups.  If we do not check the parent, sometimes
2054          * this will filter everything out.
2055          *
2056          * For other resource types, is_filtered is okay.
2057          */
2058         if (uber_parent(rsc)->variant == pe_group) {
2059             if (!pcmk__str_in_list(rsc_printable_id(rsc), only_rsc, pcmk__str_star_matches) &&
2060                 !pcmk__str_in_list(rsc_printable_id(uber_parent(rsc)), only_rsc, pcmk__str_star_matches)) {
2061                 continue;
2062             }
2063         } else {
2064             if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
2065                 continue;
2066             }
2067         }
2068 
2069         if (!pcmk_is_set(section_opts, pcmk_section_operations)) {
2070             time_t last_failure = 0;
2071             int failcount = pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
2072                                              NULL, data_set);
2073 
2074             if (failcount <= 0) {
2075                 continue;
2076             }
2077 
2078             if (rc == pcmk_rc_no_output) {
2079                 rc = pcmk_rc_ok;
2080                 out->message(out, "node", node, show_opts, FALSE, only_node,
2081                              only_rsc);
2082             }
2083 
2084             out->message(out, "resource-history", rsc, rsc_id, FALSE,
2085                          failcount, last_failure, FALSE);
2086         } else {
2087             GList *op_list = get_operation_list(rsc_entry);
2088             pe_resource_t *rsc = pe_find_resource(data_set->resources,
2089                                                   crm_element_value(rsc_entry, XML_ATTR_ID));
2090 
2091             if (op_list == NULL) {
2092                 continue;
2093             }
2094 
2095             if (rc == pcmk_rc_no_output) {
2096                 rc = pcmk_rc_ok;
2097                 out->message(out, "node", node, show_opts, FALSE, only_node,
2098                              only_rsc);
2099             }
2100 
2101             out->message(out, "resource-operation-list", data_set, rsc, node,
2102                          op_list, show_opts);
2103         }
2104     }
2105 
2106     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2107     return rc;
2108 }
2109 
2110 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2111 static int
2112 node_list_html(pcmk__output_t *out, va_list args) {
2113     GList *nodes = va_arg(args, GList *);
2114     GList *only_node = va_arg(args, GList *);
2115     GList *only_rsc = va_arg(args, GList *);
2116     uint32_t show_opts = va_arg(args, uint32_t);
2117     gboolean print_spacer G_GNUC_UNUSED = va_arg(args, gboolean);
2118 
2119     int rc = pcmk_rc_no_output;
2120 
2121     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2122         pe_node_t *node = (pe_node_t *) gIter->data;
2123 
2124         if (!pcmk__str_in_list(node->details->uname, only_node,
2125                                pcmk__str_star_matches|pcmk__str_casei)) {
2126             continue;
2127         }
2128 
2129         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Node List");
2130 
2131         out->message(out, "node", node, show_opts, TRUE, only_node, only_rsc);
2132     }
2133 
2134     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2135     return rc;
2136 }
2137 
2138 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2139 static int
2140 node_list_text(pcmk__output_t *out, va_list args) {
2141     GList *nodes = va_arg(args, GList *);
2142     GList *only_node = va_arg(args, GList *);
2143     GList *only_rsc = va_arg(args, GList *);
2144     uint32_t show_opts = va_arg(args, uint32_t);
2145     gboolean print_spacer = va_arg(args, gboolean);
2146 
2147     /* space-separated lists of node names */
2148     char *online_nodes = NULL;
2149     char *online_remote_nodes = NULL;
2150     char *online_guest_nodes = NULL;
2151     char *offline_nodes = NULL;
2152     char *offline_remote_nodes = NULL;
2153 
2154     size_t online_nodes_len = 0;
2155     size_t online_remote_nodes_len = 0;
2156     size_t online_guest_nodes_len = 0;
2157     size_t offline_nodes_len = 0;
2158     size_t offline_remote_nodes_len = 0;
2159 
2160     int rc = pcmk_rc_no_output;
2161 
2162     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2163         pe_node_t *node = (pe_node_t *) gIter->data;
2164         char *node_name = pe__node_display_name(node, pcmk_is_set(show_opts, pcmk_show_node_id));
2165 
2166         if (!pcmk__str_in_list(node->details->uname, only_node,
2167                                pcmk__str_star_matches|pcmk__str_casei)) {
2168             free(node_name);
2169             continue;
2170         }
2171 
2172         PCMK__OUTPUT_LIST_HEADER(out, print_spacer == TRUE, rc, "Node List");
2173 
2174         // Determine whether to display node individually or in a list
2175         if (node->details->unclean || node->details->pending
2176             || (node->details->standby_onfail && node->details->online)
2177             || node->details->standby || node->details->maintenance
2178             || pcmk_is_set(show_opts, pcmk_show_rscs_by_node)
2179             || (pe__node_health(node) <= 0)) {
2180             // Display node individually
2181 
2182         } else if (node->details->online) {
2183             // Display online node in a list
2184             if (pe__is_guest_node(node)) {
2185                 pcmk__add_word(&online_guest_nodes,
2186                                &online_guest_nodes_len, node_name);
2187             } else if (pe__is_remote_node(node)) {
2188                 pcmk__add_word(&online_remote_nodes,
2189                                &online_remote_nodes_len, node_name);
2190             } else {
2191                 pcmk__add_word(&online_nodes, &online_nodes_len, node_name);
2192             }
2193             free(node_name);
2194             continue;
2195 
2196         } else {
2197             // Display offline node in a list
2198             if (pe__is_remote_node(node)) {
2199                 pcmk__add_word(&offline_remote_nodes,
2200                                &offline_remote_nodes_len, node_name);
2201             } else if (pe__is_guest_node(node)) {
2202                 /* ignore offline guest nodes */
2203             } else {
2204                 pcmk__add_word(&offline_nodes,
2205                                &offline_nodes_len, node_name);
2206             }
2207             free(node_name);
2208             continue;
2209         }
2210 
2211         /* If we get here, node is in bad state, or we're grouping by node */
2212         out->message(out, "node", node, show_opts, TRUE, only_node, only_rsc);
2213         free(node_name);
2214     }
2215 
2216     /* If we're not grouping by node, summarize nodes by status */
2217     if (online_nodes) {
2218         out->list_item(out, "Online", "[ %s ]", online_nodes);
2219         free(online_nodes);
2220     }
2221     if (offline_nodes) {
2222         out->list_item(out, "OFFLINE", "[ %s ]", offline_nodes);
2223         free(offline_nodes);
2224     }
2225     if (online_remote_nodes) {
2226         out->list_item(out, "RemoteOnline", "[ %s ]", online_remote_nodes);
2227         free(online_remote_nodes);
2228     }
2229     if (offline_remote_nodes) {
2230         out->list_item(out, "RemoteOFFLINE", "[ %s ]", offline_remote_nodes);
2231         free(offline_remote_nodes);
2232     }
2233     if (online_guest_nodes) {
2234         out->list_item(out, "GuestOnline", "[ %s ]", online_guest_nodes);
2235         free(online_guest_nodes);
2236     }
2237 
2238     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2239     return rc;
2240 }
2241 
2242 PCMK__OUTPUT_ARGS("node-list", "GList *", "GList *", "GList *", "uint32_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2243 static int
2244 node_list_xml(pcmk__output_t *out, va_list args) {
2245     GList *nodes = va_arg(args, GList *);
2246     GList *only_node = va_arg(args, GList *);
2247     GList *only_rsc = va_arg(args, GList *);
2248     uint32_t show_opts = va_arg(args, uint32_t);
2249     gboolean print_spacer G_GNUC_UNUSED = va_arg(args, gboolean);
2250 
2251     out->begin_list(out, NULL, NULL, "nodes");
2252     for (GList *gIter = nodes; gIter != NULL; gIter = gIter->next) {
2253         pe_node_t *node = (pe_node_t *) gIter->data;
2254 
2255         if (!pcmk__str_in_list(node->details->uname, only_node,
2256                                pcmk__str_star_matches|pcmk__str_casei)) {
2257             continue;
2258         }
2259 
2260         out->message(out, "node", node, show_opts, TRUE, only_node, only_rsc);
2261     }
2262     out->end_list(out);
2263 
2264     return pcmk_rc_ok;
2265 }
2266 
2267 PCMK__OUTPUT_ARGS("node-summary", "pe_working_set_t *", "GList *", "GList *",
     /* [previous][next][first][last][top][bottom][index][help] */
2268                   "uint32_t", "uint32_t", "gboolean")
2269 static int
2270 node_summary(pcmk__output_t *out, va_list args) {
2271     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2272     GList *only_node = va_arg(args, GList *);
2273     GList *only_rsc = va_arg(args, GList *);
2274     uint32_t section_opts = va_arg(args, uint32_t);
2275     uint32_t show_opts = va_arg(args, uint32_t);
2276     gboolean print_spacer = va_arg(args, gboolean);
2277 
2278     xmlNode *node_state = NULL;
2279     xmlNode *cib_status = pcmk_find_cib_element(data_set->input,
2280                                                 XML_CIB_TAG_STATUS);
2281     int rc = pcmk_rc_no_output;
2282 
2283     if (xmlChildElementCount(cib_status) == 0) {
2284         return rc;
2285     }
2286 
2287     for (node_state = first_named_child(cib_status, XML_CIB_TAG_STATE);
2288          node_state != NULL; node_state = crm_next_same_xml(node_state)) {
2289         pe_node_t *node = pe_find_node_id(data_set->nodes, ID(node_state));
2290 
2291         if (!node || !node->details || !node->details->online) {
2292             continue;
2293         }
2294 
2295         if (!pcmk__str_in_list(node->details->uname, only_node,
2296                                pcmk__str_star_matches|pcmk__str_casei)) {
2297             continue;
2298         }
2299 
2300         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc,
2301                                  pcmk_is_set(section_opts, pcmk_section_operations) ? "Operations" : "Migration Summary");
2302 
2303         out->message(out, "node-history-list", data_set, node, node_state,
2304                      only_node, only_rsc, section_opts, show_opts);
2305     }
2306 
2307     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2308     return rc;
2309 }
2310 
2311 PCMK__OUTPUT_ARGS("node-weight", "pe_resource_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2312 static int
2313 node_weight(pcmk__output_t *out, va_list args)
2314 {
2315     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2316     const char *prefix = va_arg(args, const char *);
2317     const char *uname = va_arg(args, const char *);
2318     char *score = va_arg(args, char *);
2319 
2320     if (rsc) {
2321         out->list_item(out, NULL, "%s: %s allocation score on %s: %s",
2322                        prefix, rsc->id, uname, score);
2323     } else {
2324         out->list_item(out, NULL, "%s: %s = %s", prefix, uname, score);
2325     }
2326 
2327     return pcmk_rc_ok;
2328 }
2329 
2330 PCMK__OUTPUT_ARGS("node-weight", "pe_resource_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2331 static int
2332 node_weight_xml(pcmk__output_t *out, va_list args)
2333 {
2334     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2335     const char *prefix = va_arg(args, const char *);
2336     const char *uname = va_arg(args, const char *);
2337     char *score = va_arg(args, char *);
2338 
2339     xmlNodePtr node = pcmk__output_create_xml_node(out, "node_weight",
2340                                                    "function", prefix,
2341                                                    "node", uname,
2342                                                    "score", score,
2343                                                    NULL);
2344 
2345     if (rsc) {
2346         crm_xml_add(node, "id", rsc->id);
2347     }
2348 
2349     return pcmk_rc_ok;
2350 }
2351 
2352 PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
2353 static int
2354 op_history_text(pcmk__output_t *out, va_list args) {
2355     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2356     const char *task = va_arg(args, const char *);
2357     const char *interval_ms_s = va_arg(args, const char *);
2358     int rc = va_arg(args, int);
2359     uint32_t show_opts = va_arg(args, uint32_t);
2360 
2361     char *buf = op_history_string(xml_op, task, interval_ms_s, rc,
2362                                   pcmk_is_set(show_opts, pcmk_show_timing));
2363 
2364     out->list_item(out, NULL, "%s", buf);
2365 
2366     free(buf);
2367     return pcmk_rc_ok;
2368 }
2369 
2370 PCMK__OUTPUT_ARGS("op-history", "xmlNodePtr", "const char *", "const char *", "int", "uint32_t")
     /* [previous][next][first][last][top][bottom][index][help] */
2371 static int
2372 op_history_xml(pcmk__output_t *out, va_list args) {
2373     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
2374     const char *task = va_arg(args, const char *);
2375     const char *interval_ms_s = va_arg(args, const char *);
2376     int rc = va_arg(args, int);
2377     uint32_t show_opts = va_arg(args, uint32_t);
2378 
2379     char *rc_s = pcmk__itoa(rc);
2380     xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history",
2381                                                    "call", crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
2382                                                    "task", task,
2383                                                    "rc", rc_s,
2384                                                    "rc_text", services_ocf_exitcode_str(rc),
2385                                                    NULL);
2386     free(rc_s);
2387 
2388     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
2389         char *s = crm_strdup_printf("%sms", interval_ms_s);
2390         crm_xml_add(node, "interval", s);
2391         free(s);
2392     }
2393 
2394     if (pcmk_is_set(show_opts, pcmk_show_timing)) {
2395         const char *value = NULL;
2396         time_t epoch = 0;
2397 
2398         if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
2399                                      &epoch) == pcmk_ok) && (epoch > 0)) {
2400             crm_xml_add(node, XML_RSC_OP_LAST_CHANGE, pcmk__epoch2str(&epoch));
2401         }
2402 
2403         value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
2404         if (value) {
2405             char *s = crm_strdup_printf("%sms", value);
2406             crm_xml_add(node, XML_RSC_OP_T_EXEC, s);
2407             free(s);
2408         }
2409         value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
2410         if (value) {
2411             char *s = crm_strdup_printf("%sms", value);
2412             crm_xml_add(node, XML_RSC_OP_T_QUEUE, s);
2413             free(s);
2414         }
2415     }
2416 
2417     return pcmk_rc_ok;
2418 }
2419 
2420 PCMK__OUTPUT_ARGS("promotion-score", "pe_resource_t *", "pe_node_t *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2421 static int
2422 promotion_score(pcmk__output_t *out, va_list args)
2423 {
2424     pe_resource_t *child_rsc = va_arg(args, pe_resource_t *);
2425     pe_node_t *chosen = va_arg(args, pe_node_t *);
2426     char *score = va_arg(args, char *);
2427 
2428     out->list_item(out, NULL, "%s promotion score on %s: %s",
2429                    child_rsc->id,
2430                    chosen? chosen->details->uname : "none",
2431                    score);
2432     return pcmk_rc_ok;
2433 }
2434 
2435 PCMK__OUTPUT_ARGS("promotion-score", "pe_resource_t *", "pe_node_t *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2436 static int
2437 promotion_score_xml(pcmk__output_t *out, va_list args)
2438 {
2439     pe_resource_t *child_rsc = va_arg(args, pe_resource_t *);
2440     pe_node_t *chosen = va_arg(args, pe_node_t *);
2441     char *score = va_arg(args, char *);
2442 
2443     xmlNodePtr node = pcmk__output_create_xml_node(out, "promotion_score",
2444                                                    "id", child_rsc->id,
2445                                                    "score", score,
2446                                                    NULL);
2447 
2448     if (chosen) {
2449         crm_xml_add(node, "node", chosen->details->uname);
2450     }
2451 
2452     return pcmk_rc_ok;
2453 }
2454 
2455 PCMK__OUTPUT_ARGS("resource-config", "pe_resource_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2456 static int
2457 resource_config(pcmk__output_t *out, va_list args) {
2458     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2459     gboolean raw = va_arg(args, gboolean);
2460 
2461     char *rsc_xml = NULL;
2462 
2463     if (raw) {
2464         rsc_xml = dump_xml_formatted(rsc->orig_xml ? rsc->orig_xml : rsc->xml);
2465     } else {
2466         rsc_xml = dump_xml_formatted(rsc->xml);
2467     }
2468 
2469     pcmk__formatted_printf(out, "Resource XML:\n");
2470     out->output_xml(out, "xml", rsc_xml);
2471 
2472     free(rsc_xml);
2473     return pcmk_rc_ok;
2474 }
2475 
2476 PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2477 static int
2478 resource_history_text(pcmk__output_t *out, va_list args) {
2479     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2480     const char *rsc_id = va_arg(args, const char *);
2481     gboolean all = va_arg(args, gboolean);
2482     int failcount = va_arg(args, int);
2483     time_t last_failure = va_arg(args, int);
2484     gboolean as_header = va_arg(args, gboolean);
2485 
2486     char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure);
2487 
2488     if (as_header) {
2489         out->begin_list(out, NULL, NULL, "%s", buf);
2490     } else {
2491         out->list_item(out, NULL, "%s", buf);
2492     }
2493 
2494     free(buf);
2495     return pcmk_rc_ok;
2496 }
2497 
2498 PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2499 static int
2500 resource_history_xml(pcmk__output_t *out, va_list args) {
2501     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2502     const char *rsc_id = va_arg(args, const char *);
2503     gboolean all = va_arg(args, gboolean);
2504     int failcount = va_arg(args, int);
2505     time_t last_failure = va_arg(args, int);
2506     gboolean as_header = va_arg(args, gboolean);
2507 
2508     xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource_history",
2509                                                      "id", rsc_id,
2510                                                      NULL);
2511 
2512     if (rsc == NULL) {
2513         pcmk__xe_set_bool_attr(node, "orphan", true);
2514     } else if (all || failcount || last_failure > 0) {
2515         char *migration_s = pcmk__itoa(rsc->migration_threshold);
2516 
2517         pcmk__xe_set_props(node, "orphan", "false",
2518                            "migration-threshold", migration_s,
2519                            NULL);
2520         free(migration_s);
2521 
2522         if (failcount > 0) {
2523             char *s = pcmk__itoa(failcount);
2524 
2525             crm_xml_add(node, PCMK__FAIL_COUNT_PREFIX, s);
2526             free(s);
2527         }
2528 
2529         if (last_failure > 0) {
2530             crm_xml_add(node, PCMK__LAST_FAILURE_PREFIX, pcmk__epoch2str(&last_failure));
2531         }
2532     }
2533 
2534     if (as_header == FALSE) {
2535         pcmk__output_xml_pop_parent(out);
2536     }
2537 
2538     return pcmk_rc_ok;
2539 }
2540 
2541 static void
2542 print_resource_header(pcmk__output_t *out, uint32_t show_opts)
     /* [previous][next][first][last][top][bottom][index][help] */
2543 {
2544     if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2545         /* Active resources have already been printed by node */
2546         out->begin_list(out, NULL, NULL, "Inactive Resources");
2547     } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2548         out->begin_list(out, NULL, NULL, "Full List of Resources");
2549     } else {
2550         out->begin_list(out, NULL, NULL, "Active Resources");
2551     }
2552 }
2553 
2554 
2555 PCMK__OUTPUT_ARGS("resource-list", "pe_working_set_t *", "uint32_t",
     /* [previous][next][first][last][top][bottom][index][help] */
2556                   "gboolean", "GList *", "GList *", "gboolean")
2557 static int
2558 resource_list(pcmk__output_t *out, va_list args)
2559 {
2560     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2561     uint32_t show_opts = va_arg(args, uint32_t);
2562     gboolean print_summary = va_arg(args, gboolean);
2563     GList *only_node = va_arg(args, GList *);
2564     GList *only_rsc = va_arg(args, GList *);
2565     gboolean print_spacer = va_arg(args, gboolean);
2566 
2567     GList *rsc_iter;
2568     int rc = pcmk_rc_no_output;
2569     bool printed_header = false;
2570 
2571     /* If we already showed active resources by node, and
2572      * we're not showing inactive resources, we have nothing to do
2573      */
2574     if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node) &&
2575         !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2576         return rc;
2577     }
2578 
2579     /* If we haven't already printed resources grouped by node,
2580      * and brief output was requested, print resource summary */
2581     if (pcmk_is_set(show_opts, pcmk_show_brief) && !pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2582         GList *rscs = pe__filter_rsc_list(data_set->resources, only_rsc);
2583 
2584         PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2585         print_resource_header(out, show_opts);
2586         printed_header = true;
2587 
2588         rc = pe__rscs_brief_output(out, rscs, show_opts);
2589         g_list_free(rscs);
2590     }
2591 
2592     /* For each resource, display it if appropriate */
2593     for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
2594         pe_resource_t *rsc = (pe_resource_t *) rsc_iter->data;
2595         int x;
2596 
2597         /* Complex resources may have some sub-resources active and some inactive */
2598         gboolean is_active = rsc->fns->active(rsc, TRUE);
2599         gboolean partially_active = rsc->fns->active(rsc, FALSE);
2600 
2601         /* Skip inactive orphans (deleted but still in CIB) */
2602         if (pcmk_is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
2603             continue;
2604 
2605         /* Skip active resources if we already displayed them by node */
2606         } else if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2607             if (is_active) {
2608                 continue;
2609             }
2610 
2611         /* Skip primitives already counted in a brief summary */
2612         } else if (pcmk_is_set(show_opts, pcmk_show_brief) && (rsc->variant == pe_native)) {
2613             continue;
2614 
2615         /* Skip resources that aren't at least partially active,
2616          * unless we're displaying inactive resources
2617          */
2618         } else if (!partially_active && !pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2619             continue;
2620 
2621         } else if (partially_active && !pe__rsc_running_on_any(rsc, only_node)) {
2622             continue;
2623         }
2624 
2625         if (!printed_header) {
2626             PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2627             print_resource_header(out, show_opts);
2628             printed_header = true;
2629         }
2630 
2631         /* Print this resource */
2632         x = out->message(out, crm_map_element_name(rsc->xml), show_opts, rsc,
2633                          only_node, only_rsc);
2634         if (x == pcmk_rc_ok) {
2635             rc = pcmk_rc_ok;
2636         }
2637     }
2638 
2639     if (print_summary && rc != pcmk_rc_ok) {
2640         if (!printed_header) {
2641             PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2642             print_resource_header(out, show_opts);
2643             printed_header = true;
2644         }
2645 
2646         if (pcmk_is_set(show_opts, pcmk_show_rscs_by_node)) {
2647             out->list_item(out, NULL, "No inactive resources");
2648         } else if (pcmk_is_set(show_opts, pcmk_show_inactive_rscs)) {
2649             out->list_item(out, NULL, "No resources");
2650         } else {
2651             out->list_item(out, NULL, "No active resources");
2652         }
2653     }
2654 
2655     if (printed_header) {
2656         out->end_list(out);
2657     }
2658 
2659     return rc;
2660 }
2661 
2662 PCMK__OUTPUT_ARGS("resource-operation-list", "pe_working_set_t *", "pe_resource_t *",
     /* [previous][next][first][last][top][bottom][index][help] */
2663                   "pe_node_t *", "GList *", "uint32_t")
2664 static int
2665 resource_operation_list(pcmk__output_t *out, va_list args)
2666 {
2667     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2668     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2669     pe_node_t *node = va_arg(args, pe_node_t *);
2670     GList *op_list = va_arg(args, GList *);
2671     uint32_t show_opts = va_arg(args, uint32_t);
2672 
2673     GList *gIter = NULL;
2674     int rc = pcmk_rc_no_output;
2675 
2676     /* Print each operation */
2677     for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
2678         xmlNode *xml_op = (xmlNode *) gIter->data;
2679         const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
2680         const char *interval_ms_s = crm_element_value(xml_op,
2681                                                       XML_LRM_ATTR_INTERVAL_MS);
2682         const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
2683         int op_rc_i;
2684 
2685         pcmk__scan_min_int(op_rc, &op_rc_i, 0);
2686 
2687         /* Display 0-interval monitors as "probe" */
2688         if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)
2689             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
2690             task = "probe";
2691         }
2692 
2693         /* If this is the first printed operation, print heading for resource */
2694         if (rc == pcmk_rc_no_output) {
2695             time_t last_failure = 0;
2696             int failcount = pe_get_failcount(node, rsc, &last_failure, pe_fc_default,
2697                                              NULL, data_set);
2698 
2699             out->message(out, "resource-history", rsc, rsc_printable_id(rsc), TRUE,
2700                          failcount, last_failure, TRUE);
2701             rc = pcmk_rc_ok;
2702         }
2703 
2704         /* Print the operation */
2705         out->message(out, "op-history", xml_op, task, interval_ms_s,
2706                      op_rc_i, show_opts);
2707     }
2708 
2709     /* Free the list we created (no need to free the individual items) */
2710     g_list_free(op_list);
2711 
2712     PCMK__OUTPUT_LIST_FOOTER(out, rc);
2713     return rc;
2714 }
2715 
2716 PCMK__OUTPUT_ARGS("resource-util", "pe_resource_t *", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2717 static int
2718 resource_util(pcmk__output_t *out, va_list args)
2719 {
2720     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2721     pe_node_t *node = va_arg(args, pe_node_t *);
2722     const char *fn = va_arg(args, const char *);
2723 
2724     char *dump_text = crm_strdup_printf("%s: %s utilization on %s:",
2725                                         fn, rsc->id, node->details->uname);
2726 
2727     g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
2728     out->list_item(out, NULL, "%s", dump_text);
2729     free(dump_text);
2730 
2731     return pcmk_rc_ok;
2732 }
2733 
2734 PCMK__OUTPUT_ARGS("resource-util", "pe_resource_t *", "pe_node_t *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
2735 static int
2736 resource_util_xml(pcmk__output_t *out, va_list args)
2737 {
2738     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
2739     pe_node_t *node = va_arg(args, pe_node_t *);
2740     const char *fn = va_arg(args, const char *);
2741 
2742     xmlNodePtr xml_node = pcmk__output_create_xml_node(out, "utilization",
2743                                                        "resource", rsc->id,
2744                                                        "node", node->details->uname,
2745                                                        "function", fn,
2746                                                        NULL);
2747     g_hash_table_foreach(rsc->utilization, add_dump_node, xml_node);
2748 
2749     return pcmk_rc_ok;
2750 }
2751 
2752 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
2753 static int
2754 ticket_html(pcmk__output_t *out, va_list args) {
2755     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
2756 
2757     if (ticket->last_granted > -1) {
2758         char *time = pcmk__format_named_time("last-granted",
2759                                              ticket->last_granted);
2760 
2761         out->list_item(out, NULL, "%s:\t%s%s %s", ticket->id,
2762                        ticket->granted ? "granted" : "revoked",
2763                        ticket->standby ? " [standby]" : "",
2764                        time);
2765         free(time);
2766     } else {
2767         out->list_item(out, NULL, "%s:\t%s%s", ticket->id,
2768                        ticket->granted ? "granted" : "revoked",
2769                        ticket->standby ? " [standby]" : "");
2770     }
2771 
2772     return pcmk_rc_ok;
2773 }
2774 
2775 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
2776 static int
2777 ticket_text(pcmk__output_t *out, va_list args) {
2778     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
2779 
2780     if (ticket->last_granted > -1) {
2781         char *time = pcmk__format_named_time("last-granted",
2782                                              ticket->last_granted);
2783 
2784         out->list_item(out, ticket->id, "%s%s %s",
2785                        ticket->granted ? "granted" : "revoked",
2786                        ticket->standby ? " [standby]" : "",
2787                        time);
2788         free(time);
2789     } else {
2790         out->list_item(out, ticket->id, "%s%s",
2791                        ticket->granted ? "granted" : "revoked",
2792                        ticket->standby ? " [standby]" : "");
2793     }
2794 
2795     return pcmk_rc_ok;
2796 }
2797 
2798 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
2799 static int
2800 ticket_xml(pcmk__output_t *out, va_list args) {
2801     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
2802 
2803     xmlNodePtr node = NULL;
2804 
2805     node = pcmk__output_create_xml_node(out, "ticket",
2806                                         "id", ticket->id,
2807                                         "status", ticket->granted ? "granted" : "revoked",
2808                                         "standby", pcmk__btoa(ticket->standby),
2809                                         NULL);
2810 
2811     if (ticket->last_granted > -1) {
2812         crm_xml_add(node, "last-granted", pcmk__epoch2str(&ticket->last_granted));
2813     }
2814 
2815     return pcmk_rc_ok;
2816 }
2817 
2818 PCMK__OUTPUT_ARGS("ticket-list", "pe_working_set_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
2819 static int
2820 ticket_list(pcmk__output_t *out, va_list args) {
2821     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
2822     gboolean print_spacer = va_arg(args, gboolean);
2823 
2824     GHashTableIter iter;
2825     gpointer key, value;
2826 
2827     if (g_hash_table_size(data_set->tickets) == 0) {
2828         return pcmk_rc_no_output;
2829     }
2830 
2831     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
2832 
2833     /* Print section heading */
2834     out->begin_list(out, NULL, NULL, "Tickets");
2835 
2836     /* Print each ticket */
2837     g_hash_table_iter_init(&iter, data_set->tickets);
2838     while (g_hash_table_iter_next(&iter, &key, &value)) {
2839         pe_ticket_t *ticket = (pe_ticket_t *) value;
2840         out->message(out, "ticket", ticket);
2841     }
2842 
2843     /* Close section */
2844     out->end_list(out);
2845     return pcmk_rc_ok;
2846 }
2847 
2848 static pcmk__message_entry_t fmt_functions[] = {
2849     { "ban", "default", ban_text },
2850     { "ban", "html", ban_html },
2851     { "ban", "xml", ban_xml },
2852     { "ban-list", "default", ban_list },
2853     { "bundle", "default", pe__bundle_text },
2854     { "bundle", "xml",  pe__bundle_xml },
2855     { "bundle", "html",  pe__bundle_html },
2856     { "clone", "default", pe__clone_default },
2857     { "clone", "xml",  pe__clone_xml },
2858     { "cluster-counts", "default", cluster_counts_text },
2859     { "cluster-counts", "html", cluster_counts_html },
2860     { "cluster-counts", "xml", cluster_counts_xml },
2861     { "cluster-dc", "default", cluster_dc_text },
2862     { "cluster-dc", "html", cluster_dc_html },
2863     { "cluster-dc", "xml", cluster_dc_xml },
2864     { "cluster-options", "default", cluster_options_text },
2865     { "cluster-options", "html", cluster_options_html },
2866     { "cluster-options", "log", cluster_options_log },
2867     { "cluster-options", "xml", cluster_options_xml },
2868     { "cluster-summary", "default", cluster_summary },
2869     { "cluster-summary", "html", cluster_summary_html },
2870     { "cluster-stack", "default", cluster_stack_text },
2871     { "cluster-stack", "html", cluster_stack_html },
2872     { "cluster-stack", "xml", cluster_stack_xml },
2873     { "cluster-times", "default", cluster_times_text },
2874     { "cluster-times", "html", cluster_times_html },
2875     { "cluster-times", "xml", cluster_times_xml },
2876     { "failed-action", "default", failed_action_default },
2877     { "failed-action", "xml", failed_action_xml },
2878     { "failed-action-list", "default", failed_action_list },
2879     { "group", "default",  pe__group_default},
2880     { "group", "xml",  pe__group_xml },
2881     { "maint-mode", "text", cluster_maint_mode_text },
2882     { "node", "default", node_text },
2883     { "node", "html", node_html },
2884     { "node", "xml", node_xml },
2885     { "node-and-op", "default", node_and_op },
2886     { "node-and-op", "xml", node_and_op_xml },
2887     { "node-capacity", "default", node_capacity },
2888     { "node-capacity", "xml", node_capacity_xml },
2889     { "node-history-list", "default", node_history_list },
2890     { "node-list", "default", node_list_text },
2891     { "node-list", "html", node_list_html },
2892     { "node-list", "xml", node_list_xml },
2893     { "node-weight", "default", node_weight },
2894     { "node-weight", "xml", node_weight_xml },
2895     { "node-attribute", "default", node_attribute_text },
2896     { "node-attribute", "html", node_attribute_html },
2897     { "node-attribute", "xml", node_attribute_xml },
2898     { "node-attribute-list", "default", node_attribute_list },
2899     { "node-summary", "default", node_summary },
2900     { "op-history", "default", op_history_text },
2901     { "op-history", "xml", op_history_xml },
2902     { "primitive", "default",  pe__resource_text },
2903     { "primitive", "xml",  pe__resource_xml },
2904     { "primitive", "html",  pe__resource_html },
2905     { "promotion-score", "default", promotion_score },
2906     { "promotion-score", "xml", promotion_score_xml },
2907     { "resource-config", "default", resource_config },
2908     { "resource-history", "default", resource_history_text },
2909     { "resource-history", "xml", resource_history_xml },
2910     { "resource-list", "default", resource_list },
2911     { "resource-operation-list", "default", resource_operation_list },
2912     { "resource-util", "default", resource_util },
2913     { "resource-util", "xml", resource_util_xml },
2914     { "ticket", "default", ticket_text },
2915     { "ticket", "html", ticket_html },
2916     { "ticket", "xml", ticket_xml },
2917     { "ticket-list", "default", ticket_list },
2918 
2919     { NULL, NULL, NULL }
2920 };
2921 
2922 void
2923 pe__register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
2924     pcmk__register_messages(out, fmt_functions);
2925 }
2926 
2927 void
2928 pe__output_node(pe_node_t *node, gboolean details, pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
2929 {
2930     if (node == NULL) {
2931         crm_trace("<NULL>");
2932         return;
2933     }
2934 
2935     CRM_ASSERT(node->details);
2936     crm_trace("%sNode %s: (weight=%d, fixed=%s)",
2937               node->details->online ? "" : "Unavailable/Unclean ",
2938               node->details->uname, node->weight, node->fixed ? "True" : "False");
2939 
2940     if (details) {
2941         char *pe_mutable = strdup("\t\t");
2942         GList *gIter = node->details->running_rsc;
2943         GList *all = NULL;
2944 
2945         all = g_list_prepend(all, (gpointer) "*");
2946 
2947         crm_trace("\t\t===Node Attributes");
2948         g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
2949         free(pe_mutable);
2950 
2951         crm_trace("\t\t=== Resources");
2952 
2953         for (; gIter != NULL; gIter = gIter->next) {
2954             pe_resource_t *rsc = (pe_resource_t *) gIter->data;
2955             uint32_t show_opts = pe_print_pending;
2956 
2957             out->message(out, crm_map_element_name(rsc->xml), show_opts,
2958                          rsc, all, all);
2959         }
2960 
2961         g_list_free(all);
2962     }
2963 }

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