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. failed_action_string
  8. get_cluster_stack
  9. last_changed_string
  10. op_history_string
  11. resource_history_string
  12. PCMK__OUTPUT_ARGS
  13. PCMK__OUTPUT_ARGS
  14. pe__node_display_name
  15. pe__name_and_nvpairs_xml
  16. role_desc
  17. PCMK__OUTPUT_ARGS
  18. PCMK__OUTPUT_ARGS
  19. PCMK__OUTPUT_ARGS
  20. PCMK__OUTPUT_ARGS
  21. PCMK__OUTPUT_ARGS
  22. PCMK__OUTPUT_ARGS
  23. PCMK__OUTPUT_ARGS
  24. PCMK__OUTPUT_ARGS
  25. PCMK__OUTPUT_ARGS
  26. PCMK__OUTPUT_ARGS
  27. PCMK__OUTPUT_ARGS
  28. PCMK__OUTPUT_ARGS
  29. PCMK__OUTPUT_ARGS
  30. PCMK__OUTPUT_ARGS
  31. PCMK__OUTPUT_ARGS
  32. PCMK__OUTPUT_ARGS
  33. PCMK__OUTPUT_ARGS
  34. PCMK__OUTPUT_ARGS
  35. PCMK__OUTPUT_ARGS
  36. PCMK__OUTPUT_ARGS
  37. PCMK__OUTPUT_ARGS
  38. PCMK__OUTPUT_ARGS
  39. PCMK__OUTPUT_ARGS
  40. PCMK__OUTPUT_ARGS
  41. PCMK__OUTPUT_ARGS
  42. PCMK__OUTPUT_ARGS
  43. PCMK__OUTPUT_ARGS
  44. PCMK__OUTPUT_ARGS
  45. PCMK__OUTPUT_ARGS
  46. PCMK__OUTPUT_ARGS
  47. PCMK__OUTPUT_ARGS
  48. PCMK__OUTPUT_ARGS
  49. PCMK__OUTPUT_ARGS
  50. PCMK__OUTPUT_ARGS
  51. PCMK__OUTPUT_ARGS
  52. PCMK__OUTPUT_ARGS
  53. PCMK__OUTPUT_ARGS
  54. PCMK__OUTPUT_ARGS
  55. PCMK__OUTPUT_ARGS
  56. PCMK__OUTPUT_ARGS
  57. PCMK__OUTPUT_ARGS
  58. PCMK__OUTPUT_ARGS
  59. PCMK__OUTPUT_ARGS
  60. PCMK__OUTPUT_ARGS
  61. PCMK__OUTPUT_ARGS
  62. PCMK__OUTPUT_ARGS
  63. PCMK__OUTPUT_ARGS
  64. PCMK__OUTPUT_ARGS
  65. PCMK__OUTPUT_ARGS
  66. print_resource_header
  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. PCMK__OUTPUT_ARGS
  74. PCMK__OUTPUT_ARGS
  75. pe__register_messages
  76. pe__output_node

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

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