root/lib/pengine/pe_output.c

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

DEFINITIONS

This source file includes following definitions.
  1. failed_action_string
  2. get_cluster_stack
  3. last_changed_string
  4. op_history_string
  5. resource_history_string
  6. PCMK__OUTPUT_ARGS
  7. PCMK__OUTPUT_ARGS
  8. pe__node_display_name
  9. pe__name_and_nvpairs_xml
  10. PCMK__OUTPUT_ARGS
  11. PCMK__OUTPUT_ARGS
  12. PCMK__OUTPUT_ARGS
  13. PCMK__OUTPUT_ARGS
  14. PCMK__OUTPUT_ARGS
  15. PCMK__OUTPUT_ARGS
  16. PCMK__OUTPUT_ARGS
  17. PCMK__OUTPUT_ARGS
  18. PCMK__OUTPUT_ARGS
  19. PCMK__OUTPUT_ARGS
  20. PCMK__OUTPUT_ARGS
  21. PCMK__OUTPUT_ARGS
  22. PCMK__OUTPUT_ARGS
  23. PCMK__OUTPUT_ARGS
  24. PCMK__OUTPUT_ARGS
  25. PCMK__OUTPUT_ARGS
  26. PCMK__OUTPUT_ARGS
  27. PCMK__OUTPUT_ARGS
  28. PCMK__OUTPUT_ARGS
  29. PCMK__OUTPUT_ARGS
  30. PCMK__OUTPUT_ARGS
  31. PCMK__OUTPUT_ARGS
  32. PCMK__OUTPUT_ARGS
  33. PCMK__OUTPUT_ARGS
  34. PCMK__OUTPUT_ARGS
  35. PCMK__OUTPUT_ARGS
  36. PCMK__OUTPUT_ARGS
  37. 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. pe__register_messages
  50. pe__output_node

   1 /*
   2  * Copyright 2019-2020 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/msg_xml.h>
  13 #include <crm/pengine/internal.h>
  14 
  15 static char *
  16 failed_action_string(xmlNodePtr xml_op) {
     /* [previous][next][first][last][top][bottom][index][help] */
  17     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
  18     int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0");
  19     int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0");
  20     const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
  21 
  22     time_t last_change = 0;
  23 
  24     if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE,
  25                                 &last_change) == pcmk_ok) {
  26         crm_time_t *crm_when = crm_time_new(NULL);
  27         char *time_s = NULL;
  28         char *buf = NULL;
  29 
  30         crm_time_set_timet(crm_when, &last_change);
  31         time_s = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
  32 
  33         buf = crm_strdup_printf("%s on %s '%s' (%d): call=%s, status='%s', "
  34                                 "exitreason='%s', " XML_RSC_OP_LAST_CHANGE
  35                                 "='%s', queued=%sms, exec=%sms",
  36                                 op_key ? op_key : ID(xml_op),
  37                                 crm_element_value(xml_op, XML_ATTR_UNAME),
  38                                 services_ocf_exitcode_str(rc), rc,
  39                                 crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
  40                                 services_lrm_status_str(status),
  41                                 exit_reason ? exit_reason : "none",
  42                                 time_s,
  43                                 crm_element_value(xml_op, XML_RSC_OP_T_QUEUE),
  44                                 crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
  45 
  46         crm_time_free(crm_when);
  47         free(time_s);
  48         return buf;
  49     } else {
  50         return crm_strdup_printf("%s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'",
  51                                  op_key ? op_key : ID(xml_op),
  52                                  crm_element_value(xml_op, XML_ATTR_UNAME),
  53                                  services_ocf_exitcode_str(rc), rc,
  54                                  crm_element_value(xml_op, XML_LRM_ATTR_CALLID),
  55                                  services_lrm_status_str(status),
  56                                  exit_reason ? exit_reason : "none");
  57     }
  58 }
  59 
  60 static const char *
  61 get_cluster_stack(pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63     xmlNode *stack = get_xpath_object("//nvpair[@name='cluster-infrastructure']",
  64                                       data_set->input, LOG_DEBUG);
  65     return stack? crm_element_value(stack, XML_NVPAIR_ATTR_VALUE) : "unknown";
  66 }
  67 
  68 static char *
  69 last_changed_string(const char *last_written, const char *user,
     /* [previous][next][first][last][top][bottom][index][help] */
  70                     const char *client, const char *origin) {
  71     if (last_written != NULL || user != NULL || client != NULL || origin != NULL) {
  72         return crm_strdup_printf("%s%s%s%s%s%s%s",
  73                                  last_written ? last_written : "",
  74                                  user ? " by " : "",
  75                                  user ? user : "",
  76                                  client ? " via " : "",
  77                                  client ? client : "",
  78                                  origin ? " on " : "",
  79                                  origin ? origin : "");
  80     } else {
  81         return strdup("");
  82     }
  83 }
  84 
  85 static char *
  86 op_history_string(xmlNode *xml_op, const char *task, const char *interval_ms_s,
     /* [previous][next][first][last][top][bottom][index][help] */
  87                   int rc, gboolean print_timing) {
  88     const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
  89     char *interval_str = NULL;
  90     char *buf = NULL;
  91 
  92     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
  93         char *pair = pcmk_format_nvpair("interval", interval_ms_s, "ms");
  94         interval_str = crm_strdup_printf(" %s", pair);
  95         free(pair);
  96     }
  97 
  98     if (print_timing) {
  99         char *last_change_str = NULL;
 100         char *last_run_str = NULL;
 101         char *exec_str = NULL;
 102         char *queue_str = NULL;
 103 
 104         const char *value = NULL;
 105 
 106         time_t epoch = 0;
 107 
 108         if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &epoch) == pcmk_ok)
 109             && (epoch > 0)) {
 110             char *time = pcmk_format_named_time(XML_RSC_OP_LAST_CHANGE, epoch);
 111             last_change_str = crm_strdup_printf(" %s", time);
 112             free(time);
 113         }
 114 
 115         if ((crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_RUN, &epoch) == pcmk_ok)
 116             && (epoch > 0)) {
 117             char *time = pcmk_format_named_time(XML_RSC_OP_LAST_RUN, epoch);
 118             last_run_str = crm_strdup_printf(" %s", time);
 119             free(time);
 120         }
 121 
 122         value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
 123         if (value) {
 124             char *pair = pcmk_format_nvpair(XML_RSC_OP_T_EXEC, value, "ms");
 125             exec_str = crm_strdup_printf(" %s", pair);
 126             free(pair);
 127         }
 128 
 129         value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
 130         if (value) {
 131             char *pair = pcmk_format_nvpair(XML_RSC_OP_T_QUEUE, value, "ms");
 132             queue_str = crm_strdup_printf(" %s", pair);
 133             free(pair);
 134         }
 135 
 136         buf = crm_strdup_printf("(%s) %s:%s%s%s%s%s rc=%d (%s)", call, task,
 137                                 interval_str ? interval_str : "",
 138                                 last_change_str ? last_change_str : "",
 139                                 last_run_str ? last_run_str : "",
 140                                 exec_str ? exec_str : "",
 141                                 queue_str ? queue_str : "",
 142                                 rc, services_ocf_exitcode_str(rc));
 143 
 144         if (last_change_str) {
 145             free(last_change_str);
 146         }
 147 
 148         if (last_run_str) {
 149             free(last_run_str);
 150         }
 151 
 152         if (exec_str) {
 153             free(exec_str);
 154         }
 155 
 156         if (queue_str) {
 157             free(queue_str);
 158         }
 159     } else {
 160         buf = crm_strdup_printf("(%s) %s%s%s", call, task,
 161                                 interval_str ? ":" : "",
 162                                 interval_str ? interval_str : "");
 163     }
 164 
 165     if (interval_str) {
 166         free(interval_str);
 167     }
 168 
 169     return buf;
 170 }
 171 
 172 static char *
 173 resource_history_string(pe_resource_t *rsc, const char *rsc_id, gboolean all,
     /* [previous][next][first][last][top][bottom][index][help] */
 174                         int failcount, time_t last_failure) {
 175     char *buf = NULL;
 176 
 177     if (rsc == NULL) {
 178         buf = crm_strdup_printf("%s: orphan", rsc_id);
 179     } else if (all || failcount || last_failure > 0) {
 180         char *failcount_s = NULL;
 181         char *lastfail_s = NULL;
 182 
 183         if (failcount > 0) {
 184             failcount_s = crm_strdup_printf(" %s=%d", PCMK__FAIL_COUNT_PREFIX,
 185                                             failcount);
 186         } else {
 187             failcount_s = strdup("");
 188         }
 189         if (last_failure > 0) {
 190             lastfail_s = crm_strdup_printf(" %s='%s'",
 191                                            PCMK__LAST_FAILURE_PREFIX,
 192                                            pcmk__epoch2str(&last_failure));
 193         }
 194 
 195         buf = crm_strdup_printf("%s: migration-threshold=%d%s%s",
 196                                 rsc_id, rsc->migration_threshold, failcount_s,
 197                                 lastfail_s? lastfail_s : "");
 198         free(failcount_s);
 199         free(lastfail_s);
 200     } else {
 201         buf = crm_strdup_printf("%s:", rsc_id);
 202     }
 203 
 204     return buf;
 205 }
 206 
 207 PCMK__OUTPUT_ARGS("cluster-summary", "pe_working_set_t *", "gboolean", "gboolean", "gboolean",
     /* [previous][next][first][last][top][bottom][index][help] */
 208                   "gboolean", "gboolean", "gboolean")
 209 int
 210 pe__cluster_summary(pcmk__output_t *out, va_list args) {
 211     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 212     gboolean print_clone_detail = va_arg(args, gboolean);
 213     gboolean show_stack = va_arg(args, gboolean);
 214     gboolean show_dc = va_arg(args, gboolean);
 215     gboolean show_times = va_arg(args, gboolean);
 216     gboolean show_counts = va_arg(args, gboolean);
 217     gboolean show_options = va_arg(args, gboolean);
 218     int rc = pcmk_rc_no_output;
 219 
 220     const char *stack_s = get_cluster_stack(data_set);
 221 
 222     if (show_stack) {
 223         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 224         out->message(out, "cluster-stack", stack_s);
 225     }
 226 
 227     /* Always print DC if none, even if not requested */
 228     if (data_set->dc_node == NULL || show_dc) {
 229         xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
 230                                                data_set->input, LOG_DEBUG);
 231         const char *dc_version_s = dc_version?
 232                                    crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
 233                                    : NULL;
 234         const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
 235         char *dc_name = data_set->dc_node ? pe__node_display_name(data_set->dc_node, print_clone_detail) : NULL;
 236 
 237         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 238         out->message(out, "cluster-dc", data_set->dc_node, quorum, dc_version_s, dc_name);
 239         free(dc_name);
 240     }
 241 
 242     if (show_times) {
 243         const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
 244         const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
 245         const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
 246         const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
 247 
 248         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 249         out->message(out, "cluster-times", last_written, user, client, origin);
 250     }
 251 
 252     if (show_counts) {
 253         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 254         out->message(out, "cluster-counts", g_list_length(data_set->nodes),
 255                      data_set->ninstances, data_set->disabled_resources,
 256                      data_set->blocked_resources);
 257     }
 258 
 259     if (show_options) {
 260         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 261         out->message(out, "cluster-options", data_set);
 262     }
 263 
 264     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 265 
 266     if (out->message(out, "maint-mode", data_set->flags) == pcmk_rc_ok) {
 267         rc = pcmk_rc_ok;
 268     }
 269 
 270     return rc;
 271 }
 272 
 273 PCMK__OUTPUT_ARGS("cluster-summary", "pe_working_set_t *", "gboolean", "gboolean", "gboolean",
     /* [previous][next][first][last][top][bottom][index][help] */
 274                   "gboolean", "gboolean", "gboolean")
 275 int
 276 pe__cluster_summary_html(pcmk__output_t *out, va_list args) {
 277     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 278     gboolean print_clone_detail = va_arg(args, gboolean);
 279     gboolean show_stack = va_arg(args, gboolean);
 280     gboolean show_dc = va_arg(args, gboolean);
 281     gboolean show_times = va_arg(args, gboolean);
 282     gboolean show_counts = va_arg(args, gboolean);
 283     gboolean show_options = va_arg(args, gboolean);
 284     int rc = pcmk_rc_no_output;
 285 
 286     const char *stack_s = get_cluster_stack(data_set);
 287 
 288     if (show_stack) {
 289         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 290         out->message(out, "cluster-stack", stack_s);
 291     }
 292 
 293     /* Always print DC if none, even if not requested */
 294     if (data_set->dc_node == NULL || show_dc) {
 295         xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']",
 296                                                data_set->input, LOG_DEBUG);
 297         const char *dc_version_s = dc_version?
 298                                    crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)
 299                                    : NULL;
 300         const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM);
 301         char *dc_name = data_set->dc_node ? pe__node_display_name(data_set->dc_node, print_clone_detail) : NULL;
 302 
 303         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 304         out->message(out, "cluster-dc", data_set->dc_node, quorum, dc_version_s, dc_name);
 305         free(dc_name);
 306     }
 307 
 308     if (show_times) {
 309         const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN);
 310         const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER);
 311         const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT);
 312         const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG);
 313 
 314         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 315         out->message(out, "cluster-times", last_written, user, client, origin);
 316     }
 317 
 318     if (show_counts) {
 319         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Cluster Summary");
 320         out->message(out, "cluster-counts", g_list_length(data_set->nodes),
 321                      data_set->ninstances, data_set->disabled_resources,
 322                      data_set->blocked_resources);
 323     }
 324 
 325     if (show_options) {
 326         /* Kind of a hack - close the list we may have opened earlier in this
 327          * function so we can put all the options into their own list.  We
 328          * only want to do this on HTML output, though.
 329          */
 330         PCMK__OUTPUT_LIST_FOOTER(out, rc);
 331 
 332         out->begin_list(out, NULL, NULL, "Config Options");
 333         out->message(out, "cluster-options", data_set);
 334     }
 335 
 336     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 337 
 338     if (out->message(out, "maint-mode", data_set->flags) == pcmk_rc_ok) {
 339         rc = pcmk_rc_ok;
 340     }
 341 
 342     return rc;
 343 }
 344 
 345 char *
 346 pe__node_display_name(pe_node_t *node, bool print_detail)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348     char *node_name;
 349     const char *node_host = NULL;
 350     const char *node_id = NULL;
 351     int name_len;
 352 
 353     CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL));
 354 
 355     /* Host is displayed only if this is a guest node */
 356     if (pe__is_guest_node(node)) {
 357         pe_node_t *host_node = pe__current_node(node->details->remote_rsc);
 358 
 359         if (host_node && host_node->details) {
 360             node_host = host_node->details->uname;
 361         }
 362         if (node_host == NULL) {
 363             node_host = ""; /* so we at least get "uname@" to indicate guest */
 364         }
 365     }
 366 
 367     /* Node ID is displayed if different from uname and detail is requested */
 368     if (print_detail && !pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
 369         node_id = node->details->id;
 370     }
 371 
 372     /* Determine name length */
 373     name_len = strlen(node->details->uname) + 1;
 374     if (node_host) {
 375         name_len += strlen(node_host) + 1; /* "@node_host" */
 376     }
 377     if (node_id) {
 378         name_len += strlen(node_id) + 3; /* + " (node_id)" */
 379     }
 380 
 381     /* Allocate and populate display name */
 382     node_name = malloc(name_len);
 383     CRM_ASSERT(node_name != NULL);
 384     strcpy(node_name, node->details->uname);
 385     if (node_host) {
 386         strcat(node_name, "@");
 387         strcat(node_name, node_host);
 388     }
 389     if (node_id) {
 390         strcat(node_name, " (");
 391         strcat(node_name, node_id);
 392         strcat(node_name, ")");
 393     }
 394     return node_name;
 395 }
 396 
 397 int
 398 pe__name_and_nvpairs_xml(pcmk__output_t *out, bool is_list, const char *tag_name
     /* [previous][next][first][last][top][bottom][index][help] */
 399                          , size_t pairs_count, ...)
 400 {
 401     xmlNodePtr xml_node = NULL;
 402     va_list args;
 403 
 404     CRM_ASSERT(tag_name != NULL);
 405 
 406     xml_node = pcmk__output_xml_peek_parent(out);
 407     CRM_ASSERT(xml_node != NULL);
 408     xml_node = is_list
 409         ? create_xml_node(xml_node, tag_name)
 410         : xmlNewChild(xml_node, NULL, (pcmkXmlStr) tag_name, NULL);
 411 
 412     va_start(args, pairs_count);
 413     while(pairs_count--) {
 414         const char *param_name = va_arg(args, const char *);
 415         const char *param_value = va_arg(args, const char *);
 416         if (param_name && param_value) {
 417             xmlSetProp(xml_node, (pcmkXmlStr)param_name, (pcmkXmlStr)param_value);
 418         }
 419     };
 420     va_end(args);
 421 
 422     if (is_list) {
 423         pcmk__output_xml_push_parent(out, xml_node);
 424     }
 425     return pcmk_rc_ok;
 426 }
 427 
 428 PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
 429 int
 430 pe__ban_html(pcmk__output_t *out, va_list args) {
 431     pe_node_t *pe_node = va_arg(args, pe_node_t *);
 432     pe__location_t *location = va_arg(args, pe__location_t *);
 433     gboolean print_clone_detail = va_arg(args, gboolean);
 434 
 435     char *node_name = pe__node_display_name(pe_node, print_clone_detail);
 436     char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s",
 437                                   location->id, location->rsc_lh->id,
 438                                   location->role_filter == RSC_ROLE_MASTER ? "as Master " : "",
 439                                   node_name);
 440 
 441     pcmk__output_create_html_node(out, "li", NULL, NULL, buf);
 442 
 443     free(node_name);
 444     free(buf);
 445     return pcmk_rc_ok;
 446 }
 447 
 448 PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
 449 int
 450 pe__ban_text(pcmk__output_t *out, va_list args) {
 451     pe_node_t *pe_node = va_arg(args, pe_node_t *);
 452     pe__location_t *location = va_arg(args, pe__location_t *);
 453     gboolean print_clone_detail = va_arg(args, gboolean);
 454 
 455     char *node_name = pe__node_display_name(pe_node, print_clone_detail);
 456     out->list_item(out, NULL, "%s\tprevents %s from running %son %s",
 457                    location->id, location->rsc_lh->id,
 458                    location->role_filter == RSC_ROLE_MASTER ? "as Master " : "",
 459                    node_name);
 460 
 461     free(node_name);
 462     return pcmk_rc_ok;
 463 }
 464 
 465 PCMK__OUTPUT_ARGS("ban", "pe_node_t *", "pe__location_t *", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
 466 int
 467 pe__ban_xml(pcmk__output_t *out, va_list args) {
 468     xmlNodePtr node = pcmk__output_create_xml_node(out, "ban");
 469     pe_node_t *pe_node = va_arg(args, pe_node_t *);
 470     pe__location_t *location = va_arg(args, pe__location_t *);
 471     gboolean print_clone_detail G_GNUC_UNUSED = va_arg(args, gboolean);
 472 
 473     char *weight_s = crm_itoa(pe_node->weight);
 474 
 475     xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) location->id);
 476     xmlSetProp(node, (pcmkXmlStr) "resource", (pcmkXmlStr) location->rsc_lh->id);
 477     xmlSetProp(node, (pcmkXmlStr) "node", (pcmkXmlStr) pe_node->details->uname);
 478     xmlSetProp(node, (pcmkXmlStr) "weight", (pcmkXmlStr) weight_s);
 479     xmlSetProp(node, (pcmkXmlStr) "master_only",
 480                (pcmkXmlStr) pcmk__btoa(location->role_filter == RSC_ROLE_MASTER));
 481 
 482     free(weight_s);
 483     return pcmk_rc_ok;
 484 }
 485 
 486 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 487 int
 488 pe__cluster_counts_html(pcmk__output_t *out, va_list args) {
 489     xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li");
 490     xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li");
 491 
 492     unsigned int nnodes = va_arg(args, unsigned int);
 493     int nresources = va_arg(args, int);
 494     int ndisabled = va_arg(args, int);
 495     int nblocked = va_arg(args, int);
 496 
 497     char *nnodes_str = crm_strdup_printf("%d node%s configured",
 498                                          nnodes, pcmk__plural_s(nnodes));
 499 
 500     pcmk_create_html_node(nodes_node, "span", NULL, NULL, nnodes_str);
 501     free(nnodes_str);
 502 
 503     if (ndisabled && nblocked) {
 504         char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
 505                                     nresources, pcmk__plural_s(nresources),
 506                                     ndisabled);
 507         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 508         free(s);
 509 
 510         pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED");
 511 
 512         s = crm_strdup_printf(", %d ", nblocked);
 513         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 514         free(s);
 515 
 516         pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED");
 517         pcmk_create_html_node(resources_node, "span", NULL, NULL,
 518                               " from further action due to failure)");
 519     } else if (ndisabled && !nblocked) {
 520         char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
 521                                     nresources, pcmk__plural_s(nresources),
 522                                     ndisabled);
 523         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 524         free(s);
 525 
 526         pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED");
 527         pcmk_create_html_node(resources_node, "span", NULL, NULL, ")");
 528     } else if (!ndisabled && nblocked) {
 529         char *s = crm_strdup_printf("%d resource instance%s configured (%d ",
 530                                     nresources, pcmk__plural_s(nresources),
 531                                     nblocked);
 532         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 533         free(s);
 534 
 535         pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED");
 536         pcmk_create_html_node(resources_node, "span", NULL, NULL,
 537                               " from further action due to failure)");
 538     } else {
 539         char *s = crm_strdup_printf("%d resource instance%s configured",
 540                                     nresources, pcmk__plural_s(nresources));
 541         pcmk_create_html_node(resources_node, "span", NULL, NULL, s);
 542         free(s);
 543     }
 544 
 545     return pcmk_rc_ok;
 546 }
 547 
 548 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 549 int
 550 pe__cluster_counts_text(pcmk__output_t *out, va_list args) {
 551     unsigned int nnodes = va_arg(args, unsigned int);
 552     int nresources = va_arg(args, int);
 553     int ndisabled = va_arg(args, int);
 554     int nblocked = va_arg(args, int);
 555 
 556     out->list_item(out, NULL, "%d node%s configured",
 557                    nnodes, pcmk__plural_s(nnodes));
 558 
 559     if (ndisabled && nblocked) {
 560         out->list_item(out, NULL, "%d resource instance%s configured "
 561                                   "(%d DISABLED, %d BLOCKED from "
 562                                   "further action due to failure)",
 563                        nresources, pcmk__plural_s(nresources), ndisabled,
 564                        nblocked);
 565     } else if (ndisabled && !nblocked) {
 566         out->list_item(out, NULL, "%d resource instance%s configured "
 567                                   "(%d DISABLED)",
 568                        nresources, pcmk__plural_s(nresources), ndisabled);
 569     } else if (!ndisabled && nblocked) {
 570         out->list_item(out, NULL, "%d resource instance%s configured "
 571                                   "(%d BLOCKED from further action "
 572                                   "due to failure)",
 573                        nresources, pcmk__plural_s(nresources), nblocked);
 574     } else {
 575         out->list_item(out, NULL, "%d resource instance%s configured",
 576                        nresources, pcmk__plural_s(nresources));
 577     }
 578 
 579     return pcmk_rc_ok;
 580 }
 581 
 582 PCMK__OUTPUT_ARGS("cluster-counts", "unsigned int", "int", "int", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
 583 int
 584 pe__cluster_counts_xml(pcmk__output_t *out, va_list args) {
 585     xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured");
 586     xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured");
 587 
 588     unsigned int nnodes = va_arg(args, unsigned int);
 589     int nresources = va_arg(args, int);
 590     int ndisabled = va_arg(args, int);
 591     int nblocked = va_arg(args, int);
 592 
 593     char *s = crm_itoa(nnodes);
 594     xmlSetProp(nodes_node, (pcmkXmlStr) "number", (pcmkXmlStr) s);
 595     free(s);
 596 
 597     s = crm_itoa(nresources);
 598     xmlSetProp(resources_node, (pcmkXmlStr) "number", (pcmkXmlStr) s);
 599     free(s);
 600 
 601     s = crm_itoa(ndisabled);
 602     xmlSetProp(resources_node, (pcmkXmlStr) "disabled", (pcmkXmlStr) s);
 603     free(s);
 604 
 605     s = crm_itoa(nblocked);
 606     xmlSetProp(resources_node, (pcmkXmlStr) "blocked", (pcmkXmlStr) s);
 607     free(s);
 608 
 609     return pcmk_rc_ok;
 610 }
 611 
 612 PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 613 int
 614 pe__cluster_dc_html(pcmk__output_t *out, va_list args) {
 615     xmlNodePtr node = pcmk__output_create_xml_node(out, "li");
 616 
 617     pe_node_t *dc = va_arg(args, pe_node_t *);
 618     const char *quorum = va_arg(args, const char *);
 619     const char *dc_version_s = va_arg(args, const char *);
 620     char *dc_name = va_arg(args, char *);
 621 
 622     pcmk_create_html_node(node, "span", NULL, "bold", "Current DC: ");
 623 
 624     if (dc) {
 625         if (crm_is_true(quorum)) {
 626             char *buf = crm_strdup_printf("%s (version %s) - partition with quorum",
 627                                           dc_name, dc_version_s ? dc_version_s : "unknown");
 628             pcmk_create_html_node(node, "span", NULL, NULL, buf);
 629             free(buf);
 630         } else {
 631             char *buf = crm_strdup_printf("%s (version %s) - partition",
 632                                           dc_name, dc_version_s ? dc_version_s : "unknown");
 633             pcmk_create_html_node(node, "span", NULL, NULL, buf);
 634             free(buf);
 635 
 636             pcmk_create_html_node(node, "span", NULL, "warning", "WITHOUT");
 637             pcmk_create_html_node(node, "span", NULL, NULL, "quorum");
 638         }
 639     } else {
 640         pcmk_create_html_node(node ,"span", NULL, "warning", "NONE");
 641     }
 642 
 643     return pcmk_rc_ok;
 644 }
 645 
 646 PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 647 int
 648 pe__cluster_dc_text(pcmk__output_t *out, va_list args) {
 649     pe_node_t *dc = va_arg(args, pe_node_t *);
 650     const char *quorum = va_arg(args, const char *);
 651     const char *dc_version_s = va_arg(args, const char *);
 652     char *dc_name = va_arg(args, char *);
 653 
 654     if (dc) {
 655         out->list_item(out, "Current DC", "%s (version %s) - partition %s quorum",
 656                        dc_name, dc_version_s ? dc_version_s : "unknown",
 657                        crm_is_true(quorum) ? "with" : "WITHOUT");
 658     } else {
 659         out->list_item(out, "Current DC", "NONE");
 660     }
 661 
 662     return pcmk_rc_ok;
 663 }
 664 
 665 PCMK__OUTPUT_ARGS("cluster-dc", "pe_node_t *", "const char *", "const char *", "char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 666 int
 667 pe__cluster_dc_xml(pcmk__output_t *out, va_list args) {
 668     xmlNodePtr node = pcmk__output_create_xml_node(out, "current_dc");
 669 
 670     pe_node_t *dc = va_arg(args, pe_node_t *);
 671     const char *quorum = va_arg(args, const char *);
 672     const char *dc_version_s = va_arg(args, const char *);
 673     char *dc_name G_GNUC_UNUSED = va_arg(args, char *);
 674 
 675     if (dc) {
 676         xmlSetProp(node, (pcmkXmlStr) "present", (pcmkXmlStr) "true");
 677         xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) (dc_version_s ? dc_version_s : ""));
 678         xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) dc->details->uname);
 679         xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) dc->details->id);
 680         xmlSetProp(node, (pcmkXmlStr) "with_quorum",
 681                    (pcmkXmlStr) pcmk__btoa(crm_is_true(quorum)));
 682     } else {
 683         xmlSetProp(node, (pcmkXmlStr) "present", (pcmkXmlStr) "false");
 684     }
 685 
 686     return pcmk_rc_ok;
 687 }
 688 
 689 PCMK__OUTPUT_ARGS("maint-mode", "unsigned long long")
     /* [previous][next][first][last][top][bottom][index][help] */
 690 int
 691 pe__cluster_maint_mode_text(pcmk__output_t *out, va_list args) {
 692     unsigned long long flags = va_arg(args, unsigned long long);
 693 
 694     if (pcmk_is_set(flags, pe_flag_maintenance_mode)) {
 695         fprintf(out->dest, "\n              *** Resource management is DISABLED ***");
 696         fprintf(out->dest, "\n  The cluster will not attempt to start, stop or recover services");
 697         fprintf(out->dest, "\n");
 698         return pcmk_rc_ok;
 699     } else if (pcmk_is_set(flags, pe_flag_stop_everything)) {
 700         fprintf(out->dest, "\n    *** Resource management is DISABLED ***");
 701         fprintf(out->dest, "\n  The cluster will keep all resources stopped");
 702         fprintf(out->dest, "\n");
 703         return pcmk_rc_ok;
 704     } else {
 705         return pcmk_rc_no_output;
 706     }
 707 }
 708 
 709 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 710 int
 711 pe__cluster_options_html(pcmk__output_t *out, va_list args) {
 712     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 713 
 714     out->list_item(out, NULL, "STONITH of failed nodes %s",
 715                    pcmk_is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
 716 
 717     out->list_item(out, NULL, "Cluster is %s",
 718                    pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric");
 719 
 720     switch (data_set->no_quorum_policy) {
 721         case no_quorum_freeze:
 722             out->list_item(out, NULL, "No quorum policy: Freeze resources");
 723             break;
 724 
 725         case no_quorum_stop:
 726             out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
 727             break;
 728 
 729         case no_quorum_demote:
 730             out->list_item(out, NULL, "No quorum policy: Demote promotable "
 731                            "resources and stop all other resources");
 732             break;
 733 
 734         case no_quorum_ignore:
 735             out->list_item(out, NULL, "No quorum policy: Ignore");
 736             break;
 737 
 738         case no_quorum_suicide:
 739             out->list_item(out, NULL, "No quorum policy: Suicide");
 740             break;
 741     }
 742 
 743     if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 744         xmlNodePtr node = pcmk__output_create_xml_node(out, "li");
 745 
 746         pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
 747         pcmk_create_html_node(node, "span", NULL, "bold", "DISABLED");
 748         pcmk_create_html_node(node, "span", NULL, NULL,
 749                               " (the cluster will not attempt to start, stop, or recover services)");
 750     } else if (pcmk_is_set(data_set->flags, pe_flag_stop_everything)) {
 751         xmlNodePtr node = pcmk__output_create_xml_node(out, "li");
 752 
 753         pcmk_create_html_node(node, "span", NULL, NULL, "Resource management: ");
 754         pcmk_create_html_node(node, "span", NULL, "bold", "STOPPED");
 755         pcmk_create_html_node(node, "span", NULL, NULL,
 756                               " (the cluster will keep all resources stopped)");
 757     } else {
 758         out->list_item(out, NULL, "Resource management: enabled");
 759     }
 760 
 761     return pcmk_rc_ok;
 762 }
 763 
 764 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 765 int
 766 pe__cluster_options_log(pcmk__output_t *out, va_list args) {
 767     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 768 
 769     if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 770         out->info(out, "Resource management is DISABLED.  The cluster will not attempt to start, stop or recover services.");
 771         return pcmk_rc_ok;
 772     } else if (pcmk_is_set(data_set->flags, pe_flag_stop_everything)) {
 773         out->info(out, "Resource management is DISABLED.  The cluster has stopped all resources.");
 774         return pcmk_rc_ok;
 775     } else {
 776         return pcmk_rc_no_output;
 777     }
 778 }
 779 
 780 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 781 int
 782 pe__cluster_options_text(pcmk__output_t *out, va_list args) {
 783     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 784 
 785     out->list_item(out, NULL, "STONITH of failed nodes %s",
 786                    pcmk_is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled");
 787 
 788     out->list_item(out, NULL, "Cluster is %s",
 789                    pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric");
 790 
 791     switch (data_set->no_quorum_policy) {
 792         case no_quorum_freeze:
 793             out->list_item(out, NULL, "No quorum policy: Freeze resources");
 794             break;
 795 
 796         case no_quorum_stop:
 797             out->list_item(out, NULL, "No quorum policy: Stop ALL resources");
 798             break;
 799 
 800         case no_quorum_demote:
 801             out->list_item(out, NULL, "No quorum policy: Demote promotable "
 802                            "resources and stop all other resources");
 803             break;
 804 
 805         case no_quorum_ignore:
 806             out->list_item(out, NULL, "No quorum policy: Ignore");
 807             break;
 808 
 809         case no_quorum_suicide:
 810             out->list_item(out, NULL, "No quorum policy: Suicide");
 811             break;
 812     }
 813 
 814     return pcmk_rc_ok;
 815 }
 816 
 817 PCMK__OUTPUT_ARGS("cluster-options", "pe_working_set_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
 818 int
 819 pe__cluster_options_xml(pcmk__output_t *out, va_list args) {
 820     xmlNodePtr node = pcmk__output_create_xml_node(out, "cluster_options");
 821     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
 822 
 823     xmlSetProp(node, (pcmkXmlStr) "stonith-enabled",
 824                (pcmkXmlStr) pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_stonith_enabled)));
 825     xmlSetProp(node, (pcmkXmlStr) "symmetric-cluster",
 826                (pcmkXmlStr) pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_symmetric_cluster)));
 827 
 828     switch (data_set->no_quorum_policy) {
 829         case no_quorum_freeze:
 830             xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "freeze");
 831             break;
 832 
 833         case no_quorum_stop:
 834             xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "stop");
 835             break;
 836 
 837         case no_quorum_demote:
 838             xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "demote");
 839             break;
 840 
 841         case no_quorum_ignore:
 842             xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "ignore");
 843             break;
 844 
 845         case no_quorum_suicide:
 846             xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "suicide");
 847             break;
 848     }
 849 
 850     xmlSetProp(node, (pcmkXmlStr) "maintenance-mode",
 851                (pcmkXmlStr) pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)));
 852     xmlSetProp(node, (pcmkXmlStr) "stop-all-resources",
 853                (pcmkXmlStr) pcmk__btoa(pcmk_is_set(data_set->flags, pe_flag_stop_everything)));
 854 
 855     return pcmk_rc_ok;
 856 }
 857 
 858 PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 859 int
 860 pe__cluster_stack_html(pcmk__output_t *out, va_list args) {
 861     xmlNodePtr node = pcmk__output_create_xml_node(out, "li");
 862     const char *stack_s = va_arg(args, const char *);
 863 
 864     pcmk_create_html_node(node, "span", NULL, "bold", "Stack: ");
 865     pcmk_create_html_node(node, "span", NULL, NULL, stack_s);
 866 
 867     return pcmk_rc_ok;
 868 }
 869 
 870 PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 871 int
 872 pe__cluster_stack_text(pcmk__output_t *out, va_list args) {
 873     const char *stack_s = va_arg(args, const char *);
 874     out->list_item(out, "Stack", "%s", stack_s);
 875     return pcmk_rc_ok;
 876 }
 877 
 878 PCMK__OUTPUT_ARGS("cluster-stack", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 879 int
 880 pe__cluster_stack_xml(pcmk__output_t *out, va_list args) {
 881     xmlNodePtr node = pcmk__output_create_xml_node(out, "stack");
 882     const char *stack_s = va_arg(args, const char *);
 883 
 884     xmlSetProp(node, (pcmkXmlStr) "type", (pcmkXmlStr) stack_s);
 885 
 886     return pcmk_rc_ok;
 887 }
 888 
 889 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 890 int
 891 pe__cluster_times_html(pcmk__output_t *out, va_list args) {
 892     xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li");
 893     xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li");
 894 
 895     const char *last_written = va_arg(args, const char *);
 896     const char *user = va_arg(args, const char *);
 897     const char *client = va_arg(args, const char *);
 898     const char *origin = va_arg(args, const char *);
 899 
 900     char *buf = last_changed_string(last_written, user, client, origin);
 901 
 902     pcmk_create_html_node(updated_node, "span", NULL, "bold", "Last updated: ");
 903     pcmk_create_html_node(updated_node, "span", NULL, NULL,
 904                           pcmk__epoch2str(NULL));
 905 
 906     pcmk_create_html_node(changed_node, "span", NULL, "bold", "Last change: ");
 907     pcmk_create_html_node(changed_node, "span", NULL, NULL, buf);
 908 
 909     free(buf);
 910     return pcmk_rc_ok;
 911 }
 912 
 913 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 914 int
 915 pe__cluster_times_xml(pcmk__output_t *out, va_list args) {
 916     xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "last_update");
 917     xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "last_change");
 918 
 919     const char *last_written = va_arg(args, const char *);
 920     const char *user = va_arg(args, const char *);
 921     const char *client = va_arg(args, const char *);
 922     const char *origin = va_arg(args, const char *);
 923 
 924     xmlSetProp(updated_node, (pcmkXmlStr) "time",
 925                (pcmkXmlStr) pcmk__epoch2str(NULL));
 926     xmlSetProp(changed_node, (pcmkXmlStr) "time", (pcmkXmlStr) (last_written ? last_written : ""));
 927     xmlSetProp(changed_node, (pcmkXmlStr) "user", (pcmkXmlStr) (user ? user : ""));
 928     xmlSetProp(changed_node, (pcmkXmlStr) "client", (pcmkXmlStr) (client ? client : ""));
 929     xmlSetProp(changed_node, (pcmkXmlStr) "origin", (pcmkXmlStr) (origin ? origin : ""));
 930 
 931     return pcmk_rc_ok;
 932 }
 933 
 934 PCMK__OUTPUT_ARGS("cluster-times", "const char *", "const char *", "const char *", "const char *")
     /* [previous][next][first][last][top][bottom][index][help] */
 935 int
 936 pe__cluster_times_text(pcmk__output_t *out, va_list args) {
 937     const char *last_written = va_arg(args, const char *);
 938     const char *user = va_arg(args, const char *);
 939     const char *client = va_arg(args, const char *);
 940     const char *origin = va_arg(args, const char *);
 941 
 942     char *buf = last_changed_string(last_written, user, client, origin);
 943 
 944     out->list_item(out, "Last updated", "%s", pcmk__epoch2str(NULL));
 945     out->list_item(out, "Last change", " %s", buf);
 946 
 947     free(buf);
 948     return pcmk_rc_ok;
 949 }
 950 
 951 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr")
     /* [previous][next][first][last][top][bottom][index][help] */
 952 int
 953 pe__failed_action_text(pcmk__output_t *out, va_list args) {
 954     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
 955     char *s = failed_action_string(xml_op);
 956 
 957     out->list_item(out, NULL, "%s", s);
 958     free(s);
 959     return pcmk_rc_ok;
 960 }
 961 
 962 PCMK__OUTPUT_ARGS("failed-action", "xmlNodePtr")
     /* [previous][next][first][last][top][bottom][index][help] */
 963 int
 964 pe__failed_action_xml(pcmk__output_t *out, va_list args) {
 965     xmlNodePtr xml_op = va_arg(args, xmlNodePtr);
 966 
 967     const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
 968     const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE);
 969     int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0");
 970     int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0");
 971     const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON);
 972 
 973     char *rc_s = crm_itoa(rc);
 974     char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none");
 975     xmlNodePtr node = pcmk__output_create_xml_node(out, "failure");
 976 
 977     xmlSetProp(node, (pcmkXmlStr) (op_key ? "op_key" : "id"),
 978                (pcmkXmlStr) (op_key ? op_key : "id"));
 979     xmlSetProp(node, (pcmkXmlStr) "node",
 980                (pcmkXmlStr) crm_element_value(xml_op, XML_ATTR_UNAME));
 981     xmlSetProp(node, (pcmkXmlStr) "exitstatus",
 982                (pcmkXmlStr) services_ocf_exitcode_str(rc));
 983     xmlSetProp(node, (pcmkXmlStr) "exitreason", (pcmkXmlStr) reason_s);
 984     xmlSetProp(node, (pcmkXmlStr) "exitcode", (pcmkXmlStr) rc_s);
 985     xmlSetProp(node, (pcmkXmlStr) "call",
 986                (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_CALLID));
 987     xmlSetProp(node, (pcmkXmlStr) "status",
 988                (pcmkXmlStr) services_lrm_status_str(status));
 989 
 990     if (last) {
 991         guint interval_ms = 0;
 992         char *s = NULL;
 993         time_t when = crm_parse_int(last, "0");
 994         crm_time_t *crm_when = crm_time_new(NULL);
 995         char *rc_change = NULL;
 996 
 997         crm_element_value_ms(xml_op, XML_LRM_ATTR_INTERVAL_MS, &interval_ms);
 998         s = crm_itoa(interval_ms);
 999 
1000         crm_time_set_timet(crm_when, &when);
1001         rc_change = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
1002 
1003         xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_LAST_CHANGE, (pcmkXmlStr) rc_change);
1004         xmlSetProp(node, (pcmkXmlStr) "queued",
1005                    (pcmkXmlStr) crm_element_value(xml_op, XML_RSC_OP_T_QUEUE));
1006         xmlSetProp(node, (pcmkXmlStr) "exec",
1007                    (pcmkXmlStr) crm_element_value(xml_op, XML_RSC_OP_T_EXEC));
1008         xmlSetProp(node, (pcmkXmlStr) "interval", (pcmkXmlStr) s);
1009         xmlSetProp(node, (pcmkXmlStr) "task",
1010                    (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_TASK));
1011 
1012         free(s);
1013         free(rc_change);
1014         crm_time_free(crm_when);
1015     }
1016 
1017     free(reason_s);
1018     free(rc_s);
1019     return pcmk_rc_ok;
1020 }
1021 
1022 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1023                   "gboolean", "gboolean", "gboolean", "GListPtr", "GListPtr")
1024 int
1025 pe__node_html(pcmk__output_t *out, va_list args) {
1026     pe_node_t *node = va_arg(args, pe_node_t *);
1027     unsigned int print_opts = va_arg(args, unsigned int);
1028     gboolean full = va_arg(args, gboolean);
1029     const char *node_mode G_GNUC_UNUSED = va_arg(args, const char *);
1030     gboolean print_clone_detail = va_arg(args, gboolean);
1031     gboolean print_brief = va_arg(args, gboolean);
1032     gboolean group_by_node = va_arg(args, gboolean);
1033     GListPtr only_node = va_arg(args, GListPtr);
1034     GListPtr only_rsc = va_arg(args, GListPtr);
1035 
1036     char *node_name = pe__node_display_name(node, print_clone_detail);
1037     char *buf = crm_strdup_printf("Node: %s", node_name);
1038 
1039     if (full) {
1040         xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li");
1041 
1042         pcmk_create_html_node(item_node, "span", NULL, NULL, buf);
1043 
1044         if (node->details->standby_onfail && node->details->online) {
1045             pcmk_create_html_node(item_node, "span", NULL, "standby", " standby (on-fail)");
1046         } else if (node->details->standby && node->details->online) {
1047             char *s = crm_strdup_printf(" standby%s", node->details->running_rsc ? " (with active resources)" : "");
1048             pcmk_create_html_node(item_node, "span", NULL, " standby", s);
1049             free(s);
1050         } else if (node->details->standby) {
1051             pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE (standby)");
1052         } else if (node->details->maintenance && node->details->online) {
1053             pcmk_create_html_node(item_node, "span", NULL, "maint", " maintenance");
1054         } else if (node->details->maintenance) {
1055             pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE (maintenance)");
1056         } else if (node->details->online) {
1057             pcmk_create_html_node(item_node, "span", NULL, "online", " online");
1058         } else {
1059             pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE");
1060         }
1061         if (print_brief && group_by_node) {
1062             GListPtr rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1063 
1064             if (rscs != NULL) {
1065                 out->begin_list(out, NULL, NULL, NULL);
1066                 pe__rscs_brief_output(out, rscs, print_opts | pe_print_rsconly, FALSE);
1067                 out->end_list(out);
1068             }
1069 
1070         } else if (group_by_node) {
1071             GListPtr lpc2 = NULL;
1072 
1073             out->begin_list(out, NULL, NULL, NULL);
1074             for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) {
1075                 pe_resource_t *rsc = (pe_resource_t *) lpc2->data;
1076                 out->message(out, crm_map_element_name(rsc->xml), print_opts | pe_print_rsconly,
1077                              rsc, only_node, only_rsc);
1078             }
1079             out->end_list(out);
1080         }
1081     } else {
1082         out->begin_list(out, NULL, NULL, "%s", buf);
1083     }
1084 
1085     free(buf);
1086     free(node_name);
1087     return pcmk_rc_ok;
1088 }
1089 
1090 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1091                   "gboolean", "gboolean", "gboolean", "GListPtr", "GListPtr")
1092 int
1093 pe__node_text(pcmk__output_t *out, va_list args) {
1094     pe_node_t *node = va_arg(args, pe_node_t *);
1095     unsigned int print_opts = va_arg(args, unsigned int);
1096     gboolean full = va_arg(args, gboolean);
1097     const char *node_mode = va_arg(args, const char *);
1098     gboolean print_clone_detail = va_arg(args, gboolean);
1099     gboolean print_brief = va_arg(args, gboolean);
1100     gboolean group_by_node = va_arg(args, gboolean);
1101     GListPtr only_node = va_arg(args, GListPtr);
1102     GListPtr only_rsc = va_arg(args, GListPtr);
1103 
1104     if (full) {
1105         char *node_name = pe__node_display_name(node, print_clone_detail);
1106         char *buf = NULL;
1107 
1108         /* Print the node name and status */
1109         if (pe__is_guest_node(node)) {
1110             buf = crm_strdup_printf("GuestNode %s: %s", node_name, node_mode);
1111         } else if (pe__is_remote_node(node)) {
1112             buf = crm_strdup_printf("RemoteNode %s: %s", node_name, node_mode);
1113         } else {
1114             buf = crm_strdup_printf("Node %s: %s", node_name, node_mode);
1115         }
1116 
1117         /* If we're grouping by node, print its resources */
1118         if (group_by_node) {
1119             if (print_brief) {
1120                 GListPtr rscs = pe__filter_rsc_list(node->details->running_rsc, only_rsc);
1121 
1122                 if (rscs != NULL) {
1123                     out->begin_list(out, NULL, NULL, "%s", buf);
1124                     out->begin_list(out, NULL, NULL, "Resources");
1125 
1126                     pe__rscs_brief_output(out, rscs, print_opts | pe_print_rsconly, FALSE);
1127 
1128                     out->end_list(out);
1129                     out->end_list(out);
1130                 }
1131 
1132             } else {
1133                 GListPtr gIter2 = NULL;
1134 
1135                 out->begin_list(out, NULL, NULL, "%s", buf);
1136                 out->begin_list(out, NULL, NULL, "Resources");
1137 
1138                 for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) {
1139                     pe_resource_t *rsc = (pe_resource_t *) gIter2->data;
1140                     out->message(out, crm_map_element_name(rsc->xml), print_opts | pe_print_rsconly,
1141                                  rsc, only_node, only_rsc);
1142                 }
1143 
1144                 out->end_list(out);
1145                 out->end_list(out);
1146             }
1147         } else {
1148             out->list_item(out, NULL, "%s", buf);
1149         }
1150 
1151         free(buf);
1152         free(node_name);
1153     } else {
1154         out->begin_list(out, NULL, NULL, "Node: %s", pe__node_display_name(node, print_clone_detail));
1155     }
1156 
1157     return pcmk_rc_ok;
1158 }
1159 
1160 PCMK__OUTPUT_ARGS("node", "pe_node_t *", "unsigned int", "gboolean", "const char *",
     /* [previous][next][first][last][top][bottom][index][help] */
1161                   "gboolean", "gboolean", "gboolean", "GListPtr", "GListPtr")
1162 int
1163 pe__node_xml(pcmk__output_t *out, va_list args) {
1164     pe_node_t *node = va_arg(args, pe_node_t *);
1165     unsigned int print_opts = va_arg(args, unsigned int);
1166     gboolean full = va_arg(args, gboolean);
1167     const char *node_mode G_GNUC_UNUSED = va_arg(args, const char *);
1168     gboolean print_clone_detail G_GNUC_UNUSED = va_arg(args, gboolean);
1169     gboolean print_brief G_GNUC_UNUSED = va_arg(args, gboolean);
1170     gboolean group_by_node = va_arg(args, gboolean);
1171     GListPtr only_node = va_arg(args, GListPtr);
1172     GListPtr only_rsc = va_arg(args, GListPtr);
1173 
1174     if (full) {
1175         const char *node_type = "unknown";
1176         char *length_s = crm_itoa(g_list_length(node->details->running_rsc));
1177 
1178         switch (node->details->type) {
1179             case node_member:
1180                 node_type = "member";
1181                 break;
1182             case node_remote:
1183                 node_type = "remote";
1184                 break;
1185             case node_ping:
1186                 node_type = "ping";
1187                 break;
1188         }
1189         pe__name_and_nvpairs_xml(out, true, "node", 13,
1190                                  "name", node->details->uname,
1191                                  "id", node->details->id,
1192                                  "online", pcmk__btoa(node->details->online),
1193                                  "standby", pcmk__btoa(node->details->standby),
1194                                  "standby_onfail", pcmk__btoa(node->details->standby_onfail),
1195                                  "maintenance", pcmk__btoa(node->details->maintenance),
1196                                  "pending", pcmk__btoa(node->details->pending),
1197                                  "unclean", pcmk__btoa(node->details->unclean),
1198                                  "shutdown", pcmk__btoa(node->details->shutdown),
1199                                  "expected_up", pcmk__btoa(node->details->expected_up),
1200                                  "is_dc", pcmk__btoa(node->details->is_dc),
1201                                  "resources_running", length_s,
1202                                  "type", node_type);
1203 
1204         if (pe__is_guest_node(node)) {
1205             xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out);
1206             xmlSetProp(xml_node, (pcmkXmlStr) "id_as_resource",
1207                                  (pcmkXmlStr) node->details->remote_rsc->container->id);
1208         }
1209 
1210         if (group_by_node) {
1211             GListPtr lpc = NULL;
1212 
1213             for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
1214                 pe_resource_t *rsc = (pe_resource_t *) lpc->data;
1215                 out->message(out, crm_map_element_name(rsc->xml), print_opts | pe_print_rsconly,
1216                              rsc, only_node, only_rsc);
1217             }
1218         }
1219 
1220         free(length_s);
1221 
1222         out->end_list(out);
1223     } else {
1224         xmlNodePtr parent = pcmk__output_xml_create_parent(out, "node");
1225         xmlSetProp(parent, (pcmkXmlStr) "name", (pcmkXmlStr) node->details->uname);
1226     }
1227 
1228     return pcmk_rc_ok;
1229 }
1230 
1231 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1232 int
1233 pe__node_attribute_text(pcmk__output_t *out, va_list args) {
1234     const char *name = va_arg(args, const char *);
1235     const char *value = va_arg(args, const char *);
1236     gboolean add_extra = va_arg(args, gboolean);
1237     int expected_score = va_arg(args, int);
1238 
1239 
1240     if (add_extra) {
1241         int v = crm_parse_int(value, "0");
1242 
1243         if (v <= 0) {
1244             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value);
1245         } else if (v < expected_score) {
1246             out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score);
1247         } else {
1248             out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1249         }
1250     } else {
1251         out->list_item(out, NULL, "%-32s\t: %-10s", name, value);
1252     }
1253 
1254     return pcmk_rc_ok;
1255 }
1256 
1257 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1258 int
1259 pe__node_attribute_html(pcmk__output_t *out, va_list args) {
1260     const char *name = va_arg(args, const char *);
1261     const char *value = va_arg(args, const char *);
1262     gboolean add_extra = va_arg(args, gboolean);
1263     int expected_score = va_arg(args, int);
1264 
1265     if (add_extra) {
1266         int v = crm_parse_int(value, "0");
1267         char *s = crm_strdup_printf("%s: %s", name, value);
1268         xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li");
1269 
1270         pcmk_create_html_node(item_node, "span", NULL, NULL, s);
1271         free(s);
1272 
1273         if (v <= 0) {
1274             pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)");
1275         } else if (v < expected_score) {
1276             char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score);
1277             pcmk_create_html_node(item_node, "span", NULL, "bold", buf);
1278             free(buf);
1279         }
1280     } else {
1281         out->list_item(out, NULL, "%s: %s", name, value);
1282     }
1283 
1284     return pcmk_rc_ok;
1285 }
1286 
1287 PCMK__OUTPUT_ARGS("node-attribute", "const char *", "const char *", "gboolean", "int")
     /* [previous][next][first][last][top][bottom][index][help] */
1288 int
1289 pe__node_attribute_xml(pcmk__output_t *out, va_list args) {
1290     const char *name = va_arg(args, const char *);
1291     const char *value = va_arg(args, const char *);
1292     gboolean add_extra = va_arg(args, gboolean);
1293     int expected_score = va_arg(args, int);
1294 
1295     xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute");
1296     xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) name);
1297     xmlSetProp(node, (pcmkXmlStr) "value", (pcmkXmlStr) value);
1298 
1299     if (add_extra) {
1300         char *buf = crm_itoa(expected_score);
1301         xmlSetProp(node, (pcmkXmlStr) "expected", (pcmkXmlStr) buf);
1302         free(buf);
1303     }
1304 
1305     return pcmk_rc_ok;
1306 }
1307 
1308 PCMK__OUTPUT_ARGS("node-list", "GListPtr", "GListPtr", "GListPtr", "unsigned int", "gboolean", "gboolean", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1309 int
1310 pe__node_list_html(pcmk__output_t *out, va_list args) {
1311     GListPtr nodes = va_arg(args, GListPtr);
1312     GListPtr only_node = va_arg(args, GListPtr);
1313     GListPtr only_rsc = va_arg(args, GListPtr);
1314     unsigned int print_opts = va_arg(args, unsigned int);
1315     gboolean print_clone_detail = va_arg(args, gboolean);
1316     gboolean print_brief = va_arg(args, gboolean);
1317     gboolean group_by_node = va_arg(args, gboolean);
1318 
1319     int rc = pcmk_rc_no_output;
1320 
1321     for (GListPtr gIter = nodes; gIter != NULL; gIter = gIter->next) {
1322         pe_node_t *node = (pe_node_t *) gIter->data;
1323 
1324         if (!pcmk__str_in_list(only_node, node->details->uname)) {
1325             continue;
1326         }
1327 
1328         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Node List");
1329 
1330         out->message(out, "node", node, print_opts, TRUE, NULL, print_clone_detail,
1331                      print_brief, group_by_node, only_node, only_rsc);
1332     }
1333 
1334     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1335     return rc;
1336 }
1337 
1338 PCMK__OUTPUT_ARGS("node-list", "GListPtr", "GListPtr", "GListPtr", "unsigned int", "gboolean", "gboolean", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1339 int
1340 pe__node_list_text(pcmk__output_t *out, va_list args) {
1341     GListPtr nodes = va_arg(args, GListPtr);
1342     GListPtr only_node = va_arg(args, GListPtr);
1343     GListPtr only_rsc = va_arg(args, GListPtr);
1344     unsigned int print_opts = va_arg(args, unsigned int);
1345     gboolean print_clone_detail = va_arg(args, gboolean);
1346     gboolean print_brief = va_arg(args, gboolean);
1347     gboolean group_by_node = va_arg(args, gboolean);
1348 
1349     /* space-separated lists of node names */
1350     char *online_nodes = NULL;
1351     char *online_remote_nodes = NULL;
1352     char *online_guest_nodes = NULL;
1353     char *offline_nodes = NULL;
1354     char *offline_remote_nodes = NULL;
1355 
1356     size_t online_nodes_len = 0;
1357     size_t online_remote_nodes_len = 0;
1358     size_t online_guest_nodes_len = 0;
1359     size_t offline_nodes_len = 0;
1360     size_t offline_remote_nodes_len = 0;
1361 
1362     int rc = pcmk_rc_no_output;
1363 
1364     for (GListPtr gIter = nodes; gIter != NULL; gIter = gIter->next) {
1365         pe_node_t *node = (pe_node_t *) gIter->data;
1366         const char *node_mode = NULL;
1367         char *node_name = pe__node_display_name(node, print_clone_detail);
1368 
1369         if (!pcmk__str_in_list(only_node, node->details->uname)) {
1370             free(node_name);
1371             continue;
1372         }
1373 
1374         PCMK__OUTPUT_LIST_HEADER(out, FALSE, rc, "Node List");
1375 
1376         /* Get node mode */
1377         if (node->details->unclean) {
1378             if (node->details->online) {
1379                 node_mode = "UNCLEAN (online)";
1380 
1381             } else if (node->details->pending) {
1382                 node_mode = "UNCLEAN (pending)";
1383 
1384             } else {
1385                 node_mode = "UNCLEAN (offline)";
1386             }
1387 
1388         } else if (node->details->pending) {
1389             node_mode = "pending";
1390 
1391         } else if (node->details->standby_onfail && node->details->online) {
1392             node_mode = "standby (on-fail)";
1393 
1394         } else if (node->details->standby) {
1395             if (node->details->online) {
1396                 if (node->details->running_rsc) {
1397                     node_mode = "standby (with active resources)";
1398                 } else {
1399                     node_mode = "standby";
1400                 }
1401             } else {
1402                 node_mode = "OFFLINE (standby)";
1403             }
1404 
1405         } else if (node->details->maintenance) {
1406             if (node->details->online) {
1407                 node_mode = "maintenance";
1408             } else {
1409                 node_mode = "OFFLINE (maintenance)";
1410             }
1411 
1412         } else if (node->details->online) {
1413             node_mode = "online";
1414             if (group_by_node == FALSE) {
1415                 if (pe__is_guest_node(node)) {
1416                     pcmk__add_word(&online_guest_nodes,
1417                                    &online_guest_nodes_len, node_name);
1418                 } else if (pe__is_remote_node(node)) {
1419                     pcmk__add_word(&online_remote_nodes,
1420                                    &online_remote_nodes_len, node_name);
1421                 } else {
1422                     pcmk__add_word(&online_nodes, &online_nodes_len, node_name);
1423                 }
1424                 free(node_name);
1425                 continue;
1426             }
1427 
1428         } else {
1429             node_mode = "OFFLINE";
1430             if (group_by_node == FALSE) {
1431                 if (pe__is_remote_node(node)) {
1432                     pcmk__add_word(&offline_remote_nodes,
1433                                    &offline_remote_nodes_len, node_name);
1434                 } else if (pe__is_guest_node(node)) {
1435                     /* ignore offline guest nodes */
1436                 } else {
1437                     pcmk__add_word(&offline_nodes,
1438                                    &offline_nodes_len, node_name);
1439                 }
1440                 free(node_name);
1441                 continue;
1442             }
1443         }
1444 
1445         /* If we get here, node is in bad state, or we're grouping by node */
1446         out->message(out, "node", node, print_opts, TRUE, node_mode, print_clone_detail,
1447                      print_brief, group_by_node, only_node, only_rsc);
1448         free(node_name);
1449     }
1450 
1451     /* If we're not grouping by node, summarize nodes by status */
1452     if (online_nodes) {
1453         out->list_item(out, "Online", "[ %s ]", online_nodes);
1454         free(online_nodes);
1455     }
1456     if (offline_nodes) {
1457         out->list_item(out, "OFFLINE", "[ %s ]", offline_nodes);
1458         free(offline_nodes);
1459     }
1460     if (online_remote_nodes) {
1461         out->list_item(out, "RemoteOnline", "[ %s ]", online_remote_nodes);
1462         free(online_remote_nodes);
1463     }
1464     if (offline_remote_nodes) {
1465         out->list_item(out, "RemoteOFFLINE", "[ %s ]", offline_remote_nodes);
1466         free(offline_remote_nodes);
1467     }
1468     if (online_guest_nodes) {
1469         out->list_item(out, "GuestOnline", "[ %s ]", online_guest_nodes);
1470         free(online_guest_nodes);
1471     }
1472 
1473     PCMK__OUTPUT_LIST_FOOTER(out, rc);
1474     return rc;
1475 }
1476 
1477 PCMK__OUTPUT_ARGS("node-list", "GListPtr", "GListPtr", "GListPtr", "unsigned int", "gboolean", "gboolean", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1478 int
1479 pe__node_list_xml(pcmk__output_t *out, va_list args) {
1480     GListPtr nodes = va_arg(args, GListPtr);
1481     GListPtr only_node = va_arg(args, GListPtr);
1482     GListPtr only_rsc = va_arg(args, GListPtr);
1483     unsigned int print_opts = va_arg(args, unsigned int);
1484     gboolean print_clone_detail = va_arg(args, gboolean);
1485     gboolean print_brief = va_arg(args, gboolean);
1486     gboolean group_by_node = va_arg(args, gboolean);
1487 
1488     out->begin_list(out, NULL, NULL, "nodes");
1489     for (GListPtr gIter = nodes; gIter != NULL; gIter = gIter->next) {
1490         pe_node_t *node = (pe_node_t *) gIter->data;
1491 
1492         if (!pcmk__str_in_list(only_node, node->details->uname)) {
1493             continue;
1494         }
1495 
1496         out->message(out, "node", node, print_opts, TRUE, NULL, print_clone_detail,
1497                      print_brief, group_by_node, only_node, only_rsc);
1498     }
1499     out->end_list(out);
1500 
1501     return pcmk_rc_ok;
1502 }
1503 
1504 PCMK__OUTPUT_ARGS("op-history", "struct xmlNode *", "const char *", "const char *", "int", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1505 int
1506 pe__op_history_text(pcmk__output_t *out, va_list args) {
1507     xmlNode *xml_op = va_arg(args, xmlNode *);
1508     const char *task = va_arg(args, const char *);
1509     const char *interval_ms_s = va_arg(args, const char *);
1510     int rc = va_arg(args, int);
1511     gboolean print_timing = va_arg(args, gboolean);
1512 
1513     char *buf = op_history_string(xml_op, task, interval_ms_s, rc, print_timing);
1514 
1515     out->list_item(out, NULL, "%s", buf);
1516 
1517     free(buf);
1518     return pcmk_rc_ok;
1519 }
1520 
1521 PCMK__OUTPUT_ARGS("op-history", "struct xmlNode *", "const char *", "const char *", "int", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1522 int
1523 pe__op_history_xml(pcmk__output_t *out, va_list args) {
1524     xmlNode *xml_op = va_arg(args, xmlNode *);
1525     const char *task = va_arg(args, const char *);
1526     const char *interval_ms_s = va_arg(args, const char *);
1527     int rc = va_arg(args, int);
1528     gboolean print_timing = va_arg(args, gboolean);
1529 
1530     char *rc_s = NULL;
1531 
1532     xmlNodePtr node = pcmk__output_create_xml_node(out, "operation_history");
1533 
1534     xmlSetProp(node, (pcmkXmlStr) "call",
1535                (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_CALLID));
1536     xmlSetProp(node, (pcmkXmlStr) "task", (pcmkXmlStr) task);
1537 
1538     if (interval_ms_s && !pcmk__str_eq(interval_ms_s, "0", pcmk__str_casei)) {
1539         char *s = crm_strdup_printf("%sms", interval_ms_s);
1540         xmlSetProp(node, (pcmkXmlStr) "interval", (pcmkXmlStr) s);
1541         free(s);
1542     }
1543 
1544     if (print_timing) {
1545         const char *value = NULL;
1546 
1547         value = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE);
1548         if (value) {
1549             time_t int_value = (time_t) crm_parse_int(value, NULL);
1550             if (int_value > 0) {
1551                 xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_LAST_CHANGE,
1552                            (pcmkXmlStr) pcmk__epoch2str(&int_value));
1553             }
1554         }
1555 
1556         value = crm_element_value(xml_op, XML_RSC_OP_LAST_RUN);
1557         if (value) {
1558             time_t int_value = (time_t) crm_parse_int(value, NULL);
1559             if (int_value > 0) {
1560                 xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_LAST_RUN,
1561                            (pcmkXmlStr) pcmk__epoch2str(&int_value));
1562             }
1563         }
1564 
1565         value = crm_element_value(xml_op, XML_RSC_OP_T_EXEC);
1566         if (value) {
1567             char *s = crm_strdup_printf("%sms", value);
1568             xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_T_EXEC, (pcmkXmlStr) s);
1569             free(s);
1570         }
1571         value = crm_element_value(xml_op, XML_RSC_OP_T_QUEUE);
1572         if (value) {
1573             char *s = crm_strdup_printf("%sms", value);
1574             xmlSetProp(node, (pcmkXmlStr) XML_RSC_OP_T_QUEUE, (pcmkXmlStr) s);
1575             free(s);
1576         }
1577     }
1578 
1579     rc_s = crm_itoa(rc);
1580     xmlSetProp(node, (pcmkXmlStr) "rc", (pcmkXmlStr) rc_s);
1581     xmlSetProp(node, (pcmkXmlStr) "rc_text", (pcmkXmlStr) services_ocf_exitcode_str(rc));
1582     free(rc_s);
1583     return pcmk_rc_ok;
1584 }
1585 
1586 PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1587 int
1588 pe__resource_history_text(pcmk__output_t *out, va_list args) {
1589     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
1590     const char *rsc_id = va_arg(args, const char *);
1591     gboolean all = va_arg(args, gboolean);
1592     int failcount = va_arg(args, int);
1593     time_t last_failure = va_arg(args, int);
1594     gboolean as_header = va_arg(args, gboolean);
1595 
1596     char *buf = resource_history_string(rsc, rsc_id, all, failcount, last_failure);
1597 
1598     if (as_header) {
1599         out->begin_list(out, NULL, NULL, "%s", buf);
1600     } else {
1601         out->list_item(out, NULL, "%s", buf);
1602     }
1603 
1604     free(buf);
1605     return pcmk_rc_ok;
1606 }
1607 
1608 PCMK__OUTPUT_ARGS("resource-history", "pe_resource_t *", "const char *", "gboolean", "int", "time_t", "gboolean")
     /* [previous][next][first][last][top][bottom][index][help] */
1609 int
1610 pe__resource_history_xml(pcmk__output_t *out, va_list args) {
1611     pe_resource_t *rsc = va_arg(args, pe_resource_t *);
1612     const char *rsc_id = va_arg(args, const char *);
1613     gboolean all = va_arg(args, gboolean);
1614     int failcount = va_arg(args, int);
1615     time_t last_failure = va_arg(args, int);
1616     gboolean as_header = va_arg(args, gboolean);
1617 
1618     xmlNodePtr node = pcmk__output_xml_create_parent(out, "resource_history");
1619     xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) rsc_id);
1620 
1621     if (rsc == NULL) {
1622         xmlSetProp(node, (pcmkXmlStr) "orphan", (pcmkXmlStr) "true");
1623     } else if (all || failcount || last_failure > 0) {
1624         char *migration_s = crm_itoa(rsc->migration_threshold);
1625 
1626         xmlSetProp(node, (pcmkXmlStr) "orphan", (pcmkXmlStr) "false");
1627         xmlSetProp(node, (pcmkXmlStr) "migration-threshold",
1628                    (pcmkXmlStr) migration_s);
1629         free(migration_s);
1630 
1631         if (failcount > 0) {
1632             char *s = crm_itoa(failcount);
1633 
1634             xmlSetProp(node, (pcmkXmlStr) PCMK__FAIL_COUNT_PREFIX,
1635                        (pcmkXmlStr) s);
1636             free(s);
1637         }
1638 
1639         if (last_failure > 0) {
1640             xmlSetProp(node, (pcmkXmlStr) PCMK__LAST_FAILURE_PREFIX,
1641                        (pcmkXmlStr) pcmk__epoch2str(&last_failure));
1642         }
1643     }
1644 
1645     if (as_header == FALSE) {
1646         pcmk__output_xml_pop_parent(out);
1647     }
1648 
1649     return pcmk_rc_ok;
1650 }
1651 
1652 PCMK__OUTPUT_ARGS("resource-list", "pe_working_set_t *", "unsigned int", "gboolean",
     /* [previous][next][first][last][top][bottom][index][help] */
1653                   "gboolean", "gboolean", "gboolean", "GListPtr", "GListPtr", "gboolean")
1654 int
1655 pe__resource_list(pcmk__output_t *out, va_list args)
1656 {
1657     pe_working_set_t *data_set = va_arg(args, pe_working_set_t *);
1658     unsigned int print_opts = va_arg(args, unsigned int);
1659     gboolean group_by_node = va_arg(args, gboolean);
1660     gboolean inactive_resources = va_arg(args, gboolean);
1661     gboolean brief_output = va_arg(args, gboolean);
1662     gboolean print_summary = va_arg(args, gboolean);
1663     GListPtr only_node = va_arg(args, GListPtr);
1664     GListPtr only_rsc = va_arg(args, GListPtr);
1665     gboolean print_spacer = va_arg(args, gboolean);
1666 
1667     GListPtr rsc_iter;
1668     int rc = pcmk_rc_no_output;
1669 
1670     /* If we already showed active resources by node, and
1671      * we're not showing inactive resources, we have nothing to do
1672      */
1673     if (group_by_node && !inactive_resources) {
1674         return rc;
1675     }
1676 
1677     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
1678 
1679     if (group_by_node) {
1680         /* Active resources have already been printed by node */
1681         out->begin_list(out, NULL, NULL, "Inactive Resources");
1682     } else if (inactive_resources) {
1683         out->begin_list(out, NULL, NULL, "Full List of Resources");
1684     } else {
1685         out->begin_list(out, NULL, NULL, "Active Resources");
1686     }
1687 
1688     /* If we haven't already printed resources grouped by node,
1689      * and brief output was requested, print resource summary */
1690     if (brief_output && !group_by_node) {
1691         GListPtr rscs = pe__filter_rsc_list(data_set->resources, only_rsc);
1692 
1693         pe__rscs_brief_output(out, rscs, print_opts, inactive_resources);
1694         g_list_free(rscs);
1695     }
1696 
1697     /* For each resource, display it if appropriate */
1698     for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) {
1699         pe_resource_t *rsc = (pe_resource_t *) rsc_iter->data;
1700         int x;
1701 
1702         /* Complex resources may have some sub-resources active and some inactive */
1703         gboolean is_active = rsc->fns->active(rsc, TRUE);
1704         gboolean partially_active = rsc->fns->active(rsc, FALSE);
1705 
1706         /* Skip inactive orphans (deleted but still in CIB) */
1707         if (pcmk_is_set(rsc->flags, pe_rsc_orphan) && !is_active) {
1708             continue;
1709 
1710         /* Skip active resources if we already displayed them by node */
1711         } else if (group_by_node) {
1712             if (is_active) {
1713                 continue;
1714             }
1715 
1716         /* Skip primitives already counted in a brief summary */
1717         } else if (brief_output && (rsc->variant == pe_native)) {
1718             continue;
1719 
1720         /* Skip resources that aren't at least partially active,
1721          * unless we're displaying inactive resources
1722          */
1723         } else if (!partially_active && !inactive_resources) {
1724             continue;
1725 
1726         } else if (partially_active && !pe__rsc_running_on_any_node_in_list(rsc, only_node)) {
1727             continue;
1728         }
1729 
1730         /* Print this resource */
1731         x = out->message(out, crm_map_element_name(rsc->xml), print_opts, rsc,
1732                          only_node, only_rsc);
1733         if (x == pcmk_rc_ok) {
1734             rc = pcmk_rc_ok;
1735         }
1736     }
1737 
1738     if (print_summary && rc != pcmk_rc_ok) {
1739         if (group_by_node) {
1740             out->list_item(out, NULL, "No inactive resources");
1741         } else if (inactive_resources) {
1742             out->list_item(out, NULL, "No resources");
1743         } else {
1744             out->list_item(out, NULL, "No active resources");
1745         }
1746     }
1747 
1748     out->end_list(out);
1749     return rc;
1750 }
1751 
1752 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
1753 int
1754 pe__ticket_html(pcmk__output_t *out, va_list args) {
1755     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
1756 
1757     if (ticket->last_granted > -1) {
1758         char *time = pcmk_format_named_time("last-granted", ticket->last_granted);
1759         out->list_item(out, NULL, "%s:\t%s%s %s", ticket->id,
1760                        ticket->granted ? "granted" : "revoked",
1761                        ticket->standby ? " [standby]" : "",
1762                        time);
1763         free(time);
1764     } else {
1765         out->list_item(out, NULL, "%s:\t%s%s", ticket->id,
1766                        ticket->granted ? "granted" : "revoked",
1767                        ticket->standby ? " [standby]" : "");
1768     }
1769 
1770     return pcmk_rc_ok;
1771 }
1772 
1773 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
1774 int
1775 pe__ticket_text(pcmk__output_t *out, va_list args) {
1776     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
1777 
1778     if (ticket->last_granted > -1) {
1779         char *time = pcmk_format_named_time("last-granted", ticket->last_granted);
1780         out->list_item(out, ticket->id, "\t%s%s %s",
1781                        ticket->granted ? "granted" : "revoked",
1782                        ticket->standby ? " [standby]" : "",
1783                        time);
1784         free(time);
1785     } else {
1786         out->list_item(out, ticket->id, "\t%s%s",
1787                        ticket->granted ? "granted" : "revoked",
1788                        ticket->standby ? " [standby]" : "");
1789     }
1790 
1791     return pcmk_rc_ok;
1792 }
1793 
1794 PCMK__OUTPUT_ARGS("ticket", "pe_ticket_t *")
     /* [previous][next][first][last][top][bottom][index][help] */
1795 int
1796 pe__ticket_xml(pcmk__output_t *out, va_list args) {
1797     xmlNodePtr node = NULL;
1798 
1799     pe_ticket_t *ticket = va_arg(args, pe_ticket_t *);
1800 
1801     node = pcmk__output_create_xml_node(out, "ticket");
1802     xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) ticket->id);
1803     xmlSetProp(node, (pcmkXmlStr) "status", (pcmkXmlStr) (ticket->granted ? "granted" : "revoked"));
1804     xmlSetProp(node, (pcmkXmlStr) "standby",
1805                (pcmkXmlStr) pcmk__btoa(ticket->standby));
1806 
1807     if (ticket->last_granted > -1) {
1808         xmlSetProp(node, (pcmkXmlStr) "last-granted",
1809                    (pcmkXmlStr) pcmk__epoch2str(&ticket->last_granted));
1810     }
1811 
1812     return pcmk_rc_ok;
1813 }
1814 
1815 static pcmk__message_entry_t fmt_functions[] = {
1816     { "ban", "html", pe__ban_html },
1817     { "ban", "log", pe__ban_text },
1818     { "ban", "text", pe__ban_text },
1819     { "ban", "xml", pe__ban_xml },
1820     { "bundle", "xml",  pe__bundle_xml },
1821     { "bundle", "html",  pe__bundle_html },
1822     { "bundle", "text",  pe__bundle_text },
1823     { "bundle", "log",  pe__bundle_text },
1824     { "clone", "xml",  pe__clone_xml },
1825     { "clone", "html",  pe__clone_html },
1826     { "clone", "text",  pe__clone_text },
1827     { "clone", "log",  pe__clone_text },
1828     { "cluster-counts", "html", pe__cluster_counts_html },
1829     { "cluster-counts", "log", pe__cluster_counts_text },
1830     { "cluster-counts", "text", pe__cluster_counts_text },
1831     { "cluster-counts", "xml", pe__cluster_counts_xml },
1832     { "cluster-dc", "html", pe__cluster_dc_html },
1833     { "cluster-dc", "log", pe__cluster_dc_text },
1834     { "cluster-dc", "text", pe__cluster_dc_text },
1835     { "cluster-dc", "xml", pe__cluster_dc_xml },
1836     { "cluster-options", "html", pe__cluster_options_html },
1837     { "cluster-options", "log", pe__cluster_options_log },
1838     { "cluster-options", "text", pe__cluster_options_text },
1839     { "cluster-options", "xml", pe__cluster_options_xml },
1840     { "cluster-summary", "default", pe__cluster_summary },
1841     { "cluster-summary", "html", pe__cluster_summary_html },
1842     { "cluster-stack", "html", pe__cluster_stack_html },
1843     { "cluster-stack", "log", pe__cluster_stack_text },
1844     { "cluster-stack", "text", pe__cluster_stack_text },
1845     { "cluster-stack", "xml", pe__cluster_stack_xml },
1846     { "cluster-times", "html", pe__cluster_times_html },
1847     { "cluster-times", "log", pe__cluster_times_text },
1848     { "cluster-times", "text", pe__cluster_times_text },
1849     { "cluster-times", "xml", pe__cluster_times_xml },
1850     { "failed-action", "html", pe__failed_action_text },
1851     { "failed-action", "log", pe__failed_action_text },
1852     { "failed-action", "text", pe__failed_action_text },
1853     { "failed-action", "xml", pe__failed_action_xml },
1854     { "group", "xml",  pe__group_xml },
1855     { "group", "html",  pe__group_html },
1856     { "group", "text",  pe__group_text },
1857     { "group", "log",  pe__group_text },
1858     { "maint-mode", "text", pe__cluster_maint_mode_text },
1859     { "node", "html", pe__node_html },
1860     { "node", "log", pe__node_text },
1861     { "node", "text", pe__node_text },
1862     { "node", "xml", pe__node_xml },
1863     { "node-list", "html", pe__node_list_html },
1864     { "node-list", "log", pe__node_list_text },
1865     { "node-list", "text", pe__node_list_text },
1866     { "node-list", "xml", pe__node_list_xml },
1867     { "node-attribute", "html", pe__node_attribute_html },
1868     { "node-attribute", "log", pe__node_attribute_text },
1869     { "node-attribute", "text", pe__node_attribute_text },
1870     { "node-attribute", "xml", pe__node_attribute_xml },
1871     { "op-history", "html", pe__op_history_text },
1872     { "op-history", "log", pe__op_history_text },
1873     { "op-history", "text", pe__op_history_text },
1874     { "op-history", "xml", pe__op_history_xml },
1875     { "primitive", "xml",  pe__resource_xml },
1876     { "primitive", "html",  pe__resource_html },
1877     { "primitive", "text",  pe__resource_text },
1878     { "primitive", "log",  pe__resource_text },
1879     { "resource-history", "default", pe__resource_history_text },
1880     { "resource-history", "xml", pe__resource_history_xml },
1881     { "resource-list", "default", pe__resource_list },
1882     { "ticket", "html", pe__ticket_html },
1883     { "ticket", "log", pe__ticket_text },
1884     { "ticket", "text", pe__ticket_text },
1885     { "ticket", "xml", pe__ticket_xml },
1886 
1887     { NULL, NULL, NULL }
1888 };
1889 
1890 void
1891 pe__register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
1892     pcmk__register_messages(out, fmt_functions);
1893 }
1894 
1895 void
1896 pe__output_node(pe_node_t *node, gboolean details, pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
1897 {
1898     if (node == NULL) {
1899         crm_trace("<NULL>");
1900         return;
1901     }
1902 
1903     CRM_ASSERT(node->details);
1904     crm_trace("%sNode %s: (weight=%d, fixed=%s)",
1905               node->details->online ? "" : "Unavailable/Unclean ",
1906               node->details->uname, node->weight, node->fixed ? "True" : "False");
1907 
1908     if (details) {
1909         char *pe_mutable = strdup("\t\t");
1910         GListPtr gIter = node->details->running_rsc;
1911         GListPtr all = NULL;
1912 
1913         all = g_list_prepend(all, strdup("*"));
1914 
1915         crm_trace("\t\t===Node Attributes");
1916         g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable);
1917         free(pe_mutable);
1918 
1919         crm_trace("\t\t=== Resources");
1920 
1921         for (; gIter != NULL; gIter = gIter->next) {
1922             pe_resource_t *rsc = (pe_resource_t *) gIter->data;
1923 
1924             out->message(out, crm_map_element_name(rsc->xml),
1925                          pe_print_pending, rsc, all, all);
1926         }
1927 
1928         g_list_free_full(all, free);
1929     }
1930 }

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