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. get_node_feature_set
  12. is_mixed_version
  13. formatted_xml_buf
  14. PCMK__OUTPUT_ARGS
  15. PCMK__OUTPUT_ARGS
  16. pe__node_display_name
  17. pe__name_and_nvpairs_xml
  18. role_desc
  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. PCMK__OUTPUT_ARGS
  38. PCMK__OUTPUT_ARGS
  39. PCMK__OUTPUT_ARGS
  40. failed_action_friendly
  41. failed_action_technical
  42. PCMK__OUTPUT_ARGS
  43. PCMK__OUTPUT_ARGS
  44. PCMK__OUTPUT_ARGS
  45. status_node
  46. PCMK__OUTPUT_ARGS
  47. node_text_status
  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. PCMK__OUTPUT_ARGS
  70. PCMK__OUTPUT_ARGS
  71. PCMK__OUTPUT_ARGS
  72. PCMK__OUTPUT_ARGS
  73. print_resource_header
  74. PCMK__OUTPUT_ARGS
  75. PCMK__OUTPUT_ARGS
  76. PCMK__OUTPUT_ARGS
  77. PCMK__OUTPUT_ARGS
  78. PCMK__OUTPUT_ARGS
  79. PCMK__OUTPUT_ARGS
  80. PCMK__OUTPUT_ARGS
  81. PCMK__OUTPUT_ARGS
  82. pe__register_messages

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

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