root/tools/crm_mon_print.c

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

DEFINITIONS

This source file includes following definitions.
  1. build_uname_list
  2. build_rsc_list
  3. failure_count
  4. get_operation_list
  5. print_rsc_history
  6. print_node_history
  7. add_extra_info
  8. print_node_attribute
  9. print_node_summary
  10. print_cluster_tickets
  11. print_neg_locations
  12. print_node_attributes
  13. print_failed_actions
  14. print_status
  15. print_xml_status
  16. print_html_status

   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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <glib.h>
  11 #include <stdio.h>
  12 #include <stdlib.h>
  13 #include <time.h>
  14 
  15 #ifndef PCMK__CONFIG_H
  16 #  define PCMK__CONFIG_H
  17 #  include <config.h>
  18 #endif
  19 
  20 #include <crm/cib/util.h>
  21 #include <crm/common/curses_internal.h>
  22 #include <crm/common/iso8601_internal.h>
  23 #include <crm/common/xml.h>
  24 #include <crm/msg_xml.h>
  25 #include <crm/pengine/internal.h>
  26 #include <crm/pengine/pe_types.h>
  27 #include <crm/stonith-ng.h>
  28 #include <crm/common/internal.h>
  29 #include <crm/common/xml_internal.h>
  30 #include <crm/common/util.h>
  31 #include <crm/fencing/internal.h>
  32 
  33 #include "crm_mon.h"
  34 
  35 static int print_rsc_history(pcmk__output_t *out, pe_working_set_t *data_set,
  36                              pe_node_t *node, xmlNode *rsc_entry, unsigned int mon_ops,
  37                              GListPtr op_list);
  38 static int print_node_history(pcmk__output_t *out, pe_working_set_t *data_set,
  39                               pe_node_t *node, xmlNode *node_state, gboolean operations,
  40                               unsigned int mon_ops, GListPtr only_node, GListPtr only_rsc);
  41 static gboolean add_extra_info(pcmk__output_t *out, pe_node_t * node, GListPtr rsc_list,
  42                                const char *attrname, int *expected_score);
  43 static void print_node_attribute(gpointer name, gpointer user_data);
  44 static int print_node_summary(pcmk__output_t *out, pe_working_set_t * data_set,
  45                               gboolean operations, unsigned int mon_ops,
  46                               GListPtr only_node, GListPtr only_rsc, gboolean print_spacer);
  47 static int print_cluster_tickets(pcmk__output_t *out, pe_working_set_t * data_set,
  48                                  gboolean print_spacer);
  49 static int print_neg_locations(pcmk__output_t *out, pe_working_set_t *data_set,
  50                                unsigned int mon_ops, const char *prefix,
  51                                GListPtr only_rsc, gboolean print_spacer);
  52 static int print_node_attributes(pcmk__output_t *out, pe_working_set_t *data_set,
  53                                  unsigned int mon_ops, GListPtr only_node,
  54                                  GListPtr only_rsc, gboolean print_spacer);
  55 static int print_failed_actions(pcmk__output_t *out, pe_working_set_t *data_set,
  56                                 GListPtr only_node, GListPtr only_rsc, gboolean print_spacer);
  57 
  58 static GListPtr
  59 build_uname_list(pe_working_set_t *data_set, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
  60     GListPtr unames = NULL;
  61 
  62     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
  63         /* Nothing was given so return a list of all node names.  Or, '*' was
  64          * given.  This would normally fall into the pe__unames_with_tag branch
  65          * where it will return an empty list.  Catch it here instead.
  66          */
  67         unames = g_list_prepend(unames, strdup("*"));
  68     } else {
  69         pe_node_t *node = pe_find_node(data_set->nodes, s);
  70 
  71         if (node) {
  72             /* The given string was a valid uname for a node.  Return a
  73              * singleton list containing just that uname.
  74              */
  75             unames = g_list_prepend(unames, strdup(s));
  76         } else {
  77             /* The given string was not a valid uname.  It's either a tag or
  78              * it's a typo or something.  In the first case, we'll return a
  79              * list of all the unames of the nodes with the given tag.  In the
  80              * second case, we'll return a NULL pointer and nothing will
  81              * get displayed.
  82              */
  83             unames = pe__unames_with_tag(data_set, s);
  84         }
  85     }
  86 
  87     return unames;
  88 }
  89 
  90 static GListPtr
  91 build_rsc_list(pe_working_set_t *data_set, const char *s) {
     /* [previous][next][first][last][top][bottom][index][help] */
  92     GListPtr resources = NULL;
  93 
  94     if (pcmk__str_eq(s, "*", pcmk__str_null_matches)) {
  95         resources = g_list_prepend(resources, strdup("*"));
  96     } else {
  97         pe_resource_t *rsc = pe_find_resource_with_flags(data_set->resources, s,
  98                                                          pe_find_renamed|pe_find_any);
  99 
 100         if (rsc) {
 101             /* A colon in the name we were given means we're being asked to filter
 102              * on a specific instance of a cloned resource.  Put that exact string
 103              * into the filter list.  Otherwise, use the printable ID of whatever
 104              * resource was found that matches what was asked for.
 105              */
 106             if (strstr(s, ":") != NULL) {
 107                 resources = g_list_prepend(resources, strdup(rsc->id));
 108             } else {
 109                 resources = g_list_prepend(resources, strdup(rsc_printable_id(rsc)));
 110             }
 111         } else {
 112             /* The given string was not a valid resource name.  It's either
 113              * a tag or it's a typo or something.  See build_uname_list for
 114              * more detail.
 115              */
 116             resources = pe__rscs_with_tag(data_set, s);
 117         }
 118     }
 119 
 120     return resources;
 121 }
 122 
 123 static int
 124 failure_count(pe_working_set_t *data_set, pe_node_t *node, pe_resource_t *rsc, time_t *last_failure) {
     /* [previous][next][first][last][top][bottom][index][help] */
 125     return rsc ? pe_get_failcount(node, rsc, last_failure, pe_fc_default,
 126                                   NULL, data_set)
 127                : 0;
 128 }
 129 
 130 static GListPtr
 131 get_operation_list(xmlNode *rsc_entry) {
     /* [previous][next][first][last][top][bottom][index][help] */
 132     GListPtr op_list = NULL;
 133     xmlNode *rsc_op = NULL;
 134 
 135     for (rsc_op = pcmk__xe_first_child(rsc_entry); rsc_op != NULL;
 136          rsc_op = pcmk__xe_next(rsc_op)) {
 137         const char *task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
 138         const char *interval_ms_s = crm_element_value(rsc_op,
 139                                                       XML_LRM_ATTR_INTERVAL_MS);
 140         const char *op_rc = crm_element_value(rsc_op, XML_LRM_ATTR_RC);
 141         int op_rc_i = crm_parse_int(op_rc, "0");
 142 
 143         /* Display 0-interval monitors as "probe" */
 144         if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)
 145             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
 146             task = "probe";
 147         }
 148 
 149         /* Ignore notifies and some probes */
 150         if (pcmk__str_eq(task, CRMD_ACTION_NOTIFY, pcmk__str_casei) || (pcmk__str_eq(task, "probe", pcmk__str_casei) && (op_rc_i == 7))) {
 151             continue;
 152         }
 153 
 154         if (pcmk__str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, pcmk__str_none)) {
 155             op_list = g_list_append(op_list, rsc_op);
 156         }
 157     }
 158 
 159     op_list = g_list_sort(op_list, sort_op_by_callid);
 160     return op_list;
 161 }
 162 
 163 /*!
 164  * \internal
 165  * \brief Print resource operation/failure history
 166  *
 167  * \param[in] out       The output functions structure.
 168  * \param[in] data_set  Cluster state to display.
 169  * \param[in] node      Node that ran this resource.
 170  * \param[in] rsc_entry Root of XML tree describing resource status.
 171  * \param[in] mon_ops   Bitmask of mon_op_*.
 172  * \param[in] op_list   A list of operations to print.
 173  */
 174 static int
 175 print_rsc_history(pcmk__output_t *out, pe_working_set_t *data_set, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
 176                   xmlNode *rsc_entry, unsigned int mon_ops, GListPtr op_list)
 177 {
 178     GListPtr gIter = NULL;
 179     int rc = pcmk_rc_no_output;
 180     const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
 181     pe_resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
 182 
 183     /* Print each operation */
 184     for (gIter = op_list; gIter != NULL; gIter = gIter->next) {
 185         xmlNode *xml_op = (xmlNode *) gIter->data;
 186         const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
 187         const char *interval_ms_s = crm_element_value(xml_op,
 188                                                       XML_LRM_ATTR_INTERVAL_MS);
 189         const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC);
 190         int op_rc_i = crm_parse_int(op_rc, "0");
 191 
 192         /* Display 0-interval monitors as "probe" */
 193         if (pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_casei)
 194             && pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches | pcmk__str_casei)) {
 195             task = "probe";
 196         }
 197 
 198         /* If this is the first printed operation, print heading for resource */
 199         if (rc == pcmk_rc_no_output) {
 200             time_t last_failure = 0;
 201             int failcount = failure_count(data_set, node, rsc, &last_failure);
 202 
 203             out->message(out, "resource-history", rsc, rsc_id, TRUE, failcount, last_failure, TRUE);
 204             rc = pcmk_rc_ok;
 205         }
 206 
 207         /* Print the operation */
 208         out->message(out, "op-history", xml_op, task, interval_ms_s,
 209                      op_rc_i, pcmk_is_set(mon_ops, mon_op_print_timing));
 210     }
 211 
 212     /* Free the list we created (no need to free the individual items) */
 213     g_list_free(op_list);
 214 
 215     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 216     return rc;
 217 }
 218 
 219 /*!
 220  * \internal
 221  * \brief Print node operation/failure history
 222  *
 223  * \param[in] out        The output functions structure.
 224  * \param[in] data_set   Cluster state to display.
 225  * \param[in] node_state Root of XML tree describing node status.
 226  * \param[in] operations Whether to print operations or just failcounts.
 227  * \param[in] mon_ops    Bitmask of mon_op_*.
 228  */
 229 static int
 230 print_node_history(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 231                    pe_node_t *node, xmlNode *node_state, gboolean operations,
 232                    unsigned int mon_ops, GListPtr only_node, GListPtr only_rsc)
 233 {
 234     xmlNode *lrm_rsc = NULL;
 235     xmlNode *rsc_entry = NULL;
 236     int rc = pcmk_rc_no_output;
 237 
 238     lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
 239     lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE);
 240 
 241     /* Print history of each of the node's resources */
 242     for (rsc_entry = pcmk__xe_first_child(lrm_rsc); rsc_entry != NULL;
 243          rsc_entry = pcmk__xe_next(rsc_entry)) {
 244 
 245         const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID);
 246         pe_resource_t *rsc = pe_find_resource(data_set->resources, rsc_id);
 247 
 248         if (!pcmk__str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, pcmk__str_none)) {
 249             continue;
 250         }
 251 
 252         /* We can't use is_filtered here to filter group resources.  For is_filtered,
 253          * we have to decide whether to check the parent or not.  If we check the
 254          * parent, all elements of a group will always be printed because that's how
 255          * is_filtered works for groups.  If we do not check the parent, sometimes
 256          * this will filter everything out.
 257          *
 258          * For other resource types, is_filtered is okay.
 259          */
 260         if (uber_parent(rsc)->variant == pe_group) {
 261             if (!pcmk__str_in_list(only_rsc, rsc_printable_id(rsc)) &&
 262                 !pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(rsc)))) {
 263                 continue;
 264             }
 265         } else {
 266             if (rsc->fns->is_filtered(rsc, only_rsc, TRUE)) {
 267                 continue;
 268             }
 269         }
 270 
 271         if (operations == FALSE) {
 272             time_t last_failure = 0;
 273             int failcount = failure_count(data_set, node, rsc, &last_failure);
 274 
 275             if (failcount <= 0) {
 276                 continue;
 277             }
 278 
 279             if (rc == pcmk_rc_no_output) {
 280                 rc = pcmk_rc_ok;
 281                 out->message(out, "node", node, get_resource_display_options(mon_ops),
 282                              FALSE, NULL,
 283                              pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 284                              pcmk_is_set(mon_ops, mon_op_print_brief),
 285                              pcmk_is_set(mon_ops, mon_op_group_by_node),
 286                              only_node, only_rsc);
 287             }
 288 
 289             out->message(out, "resource-history", rsc, rsc_id, FALSE,
 290                          failcount, last_failure, FALSE);
 291         } else {
 292             GListPtr op_list = get_operation_list(rsc_entry);
 293 
 294             if (op_list == NULL) {
 295                 continue;
 296             }
 297 
 298             if (rc == pcmk_rc_no_output) {
 299                 rc = pcmk_rc_ok;
 300                 out->message(out, "node", node, get_resource_display_options(mon_ops),
 301                              FALSE, NULL,
 302                              pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 303                              pcmk_is_set(mon_ops, mon_op_print_brief),
 304                              pcmk_is_set(mon_ops, mon_op_group_by_node),
 305                              only_node, only_rsc);
 306             }
 307 
 308             print_rsc_history(out, data_set, node, rsc_entry, mon_ops, op_list);
 309         }
 310     }
 311 
 312     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 313     return rc;
 314 }
 315 
 316 /*!
 317  * \internal
 318  * \brief Determine whether extended information about an attribute should be added.
 319  *
 320  * \param[in]  out            The output functions structure.
 321  * \param[in]  node           Node that ran this resource.
 322  * \param[in]  rsc_list       The list of resources for this node.
 323  * \param[in]  attrname       The attribute to find.
 324  * \param[out] expected_score The expected value for this attribute.
 325  *
 326  * \return TRUE if extended information should be printed, FALSE otherwise
 327  * \note Currently, extended information is only supported for ping/pingd
 328  *       resources, for which a message will be printed if connectivity is lost
 329  *       or degraded.
 330  */
 331 static gboolean
 332 add_extra_info(pcmk__output_t *out, pe_node_t *node, GListPtr rsc_list,
     /* [previous][next][first][last][top][bottom][index][help] */
 333                const char *attrname, int *expected_score)
 334 {
 335     GListPtr gIter = NULL;
 336 
 337     for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) {
 338         pe_resource_t *rsc = (pe_resource_t *) gIter->data;
 339         const char *type = g_hash_table_lookup(rsc->meta, "type");
 340         const char *name = NULL;
 341 
 342         if (rsc->children != NULL) {
 343             if (add_extra_info(out, node, rsc->children, attrname, expected_score)) {
 344                 return TRUE;
 345             }
 346         }
 347 
 348         if (!pcmk__strcase_any_of(type, "ping", "pingd", NULL)) {
 349             continue;
 350         }
 351 
 352         name = g_hash_table_lookup(rsc->parameters, "name");
 353 
 354         if (name == NULL) {
 355             name = "pingd";
 356         }
 357 
 358         /* To identify the resource with the attribute name. */
 359         if (pcmk__str_eq(name, attrname, pcmk__str_casei)) {
 360             int host_list_num = 0;
 361             /* int value = crm_parse_int(attrvalue, "0"); */
 362             const char *hosts = g_hash_table_lookup(rsc->parameters, "host_list");
 363             const char *multiplier = g_hash_table_lookup(rsc->parameters, "multiplier");
 364 
 365             if (hosts) {
 366                 char **host_list = g_strsplit(hosts, " ", 0);
 367                 host_list_num = g_strv_length(host_list);
 368                 g_strfreev(host_list);
 369             }
 370 
 371             /* pingd multiplier is the same as the default value. */
 372             *expected_score = host_list_num * crm_parse_int(multiplier, "1");
 373 
 374             return TRUE;
 375         }
 376     }
 377     return FALSE;
 378 }
 379 
 380 /* structure for passing multiple user data to g_list_foreach() */
 381 struct mon_attr_data {
 382     pcmk__output_t *out;
 383     pe_node_t *node;
 384 };
 385 
 386 static void
 387 print_node_attribute(gpointer name, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389     const char *value = NULL;
 390     int expected_score = 0;
 391     gboolean add_extra = FALSE;
 392     struct mon_attr_data *data = (struct mon_attr_data *) user_data;
 393 
 394     value = pe_node_attribute_raw(data->node, name);
 395 
 396     add_extra = add_extra_info(data->out, data->node, data->node->details->running_rsc,
 397                                name, &expected_score);
 398 
 399     /* Print attribute name and value */
 400     data->out->message(data->out, "node-attribute", name, value, add_extra,
 401                        expected_score);
 402 }
 403 
 404 /*!
 405  * \internal
 406  * \brief Print history for all nodes.
 407  *
 408  * \param[in] out        The output functions structure.
 409  * \param[in] data_set   Cluster state to display.
 410  * \param[in] operations Whether to print operations or just failcounts.
 411  * \param[in] mon_ops    Bitmask of mon_op_*.
 412  */
 413 static int
 414 print_node_summary(pcmk__output_t *out, pe_working_set_t * data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 415                    gboolean operations, unsigned int mon_ops, GListPtr only_node,
 416                    GListPtr only_rsc, gboolean print_spacer)
 417 {
 418     xmlNode *node_state = NULL;
 419     xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
 420     int rc = pcmk_rc_no_output;
 421 
 422     if (xmlChildElementCount(cib_status) == 0) {
 423         return rc;
 424     }
 425 
 426     /* Print each node in the CIB status */
 427     for (node_state = pcmk__xe_first_child(cib_status); node_state != NULL;
 428          node_state = pcmk__xe_next(node_state)) {
 429         pe_node_t *node;
 430 
 431         if (!pcmk__str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, pcmk__str_none)) {
 432             continue;
 433         }
 434 
 435         node = pe_find_node_id(data_set->nodes, ID(node_state));
 436 
 437         if (!node || !node->details || !node->details->online) {
 438             continue;
 439         }
 440 
 441         if (!pcmk__str_in_list(only_node, node->details->uname)) {
 442             continue;
 443         }
 444 
 445         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, operations ? "Operations" : "Migration Summary");
 446 
 447         print_node_history(out, data_set, node, node_state, operations, mon_ops,
 448                            only_node, only_rsc);
 449     }
 450 
 451     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 452     return rc;
 453 }
 454 
 455 /*!
 456  * \internal
 457  * \brief Print all tickets.
 458  *
 459  * \param[in] out      The output functions structure.
 460  * \param[in] data_set Cluster state to display.
 461  */
 462 static int
 463 print_cluster_tickets(pcmk__output_t *out, pe_working_set_t * data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 464                       gboolean print_spacer)
 465 {
 466     GHashTableIter iter;
 467     gpointer key, value;
 468 
 469     if (g_hash_table_size(data_set->tickets) == 0) {
 470         return pcmk_rc_no_output;
 471     }
 472 
 473     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
 474 
 475     /* Print section heading */
 476     out->begin_list(out, NULL, NULL, "Tickets");
 477 
 478     /* Print each ticket */
 479     g_hash_table_iter_init(&iter, data_set->tickets);
 480     while (g_hash_table_iter_next(&iter, &key, &value)) {
 481         pe_ticket_t *ticket = (pe_ticket_t *) value;
 482         out->message(out, "ticket", ticket);
 483     }
 484 
 485     /* Close section */
 486     out->end_list(out);
 487     return pcmk_rc_ok;
 488 }
 489 
 490 /*!
 491  * \internal
 492  * \brief Print section for negative location constraints
 493  *
 494  * \param[in] out      The output functions structure.
 495  * \param[in] data_set Cluster state to display.
 496  * \param[in] mon_ops  Bitmask of mon_op_*.
 497  * \param[in] prefix   ID prefix to filter results by.
 498  */
 499 static int
 500 print_neg_locations(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 501                     unsigned int mon_ops, const char *prefix, GListPtr only_rsc,
 502                     gboolean print_spacer)
 503 {
 504     GListPtr gIter, gIter2;
 505     int rc = pcmk_rc_no_output;
 506 
 507     /* Print each ban */
 508     for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
 509         pe__location_t *location = gIter->data;
 510 
 511         if (prefix != NULL && !g_str_has_prefix(location->id, prefix))
 512             continue;
 513 
 514         if (!pcmk__str_in_list(only_rsc, rsc_printable_id(location->rsc_lh)) &&
 515             !pcmk__str_in_list(only_rsc, rsc_printable_id(uber_parent(location->rsc_lh)))) {
 516             continue;
 517         }
 518 
 519         for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) {
 520             pe_node_t *node = (pe_node_t *) gIter2->data;
 521 
 522             if (node->weight < 0) {
 523                 PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Negative Location Constraints");
 524                 out->message(out, "ban", node, location,
 525                              pcmk_is_set(mon_ops, mon_op_print_clone_detail));
 526             }
 527         }
 528     }
 529 
 530     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 531     return rc;
 532 }
 533 
 534 /*!
 535  * \internal
 536  * \brief Print node attributes section
 537  *
 538  * \param[in] out      The output functions structure.
 539  * \param[in] data_set Cluster state to display.
 540  * \param[in] mon_ops  Bitmask of mon_op_*.
 541  */
 542 static int
 543 print_node_attributes(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 544                       unsigned int mon_ops, GListPtr only_node,
 545                       GListPtr only_rsc, gboolean print_spacer)
 546 {
 547     GListPtr gIter = NULL;
 548     int rc = pcmk_rc_no_output;
 549 
 550     /* Unpack all resource parameters (it would be more efficient to do this
 551      * only when needed for the first time in add_extra_info())
 552      */
 553     for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
 554         crm_mon_get_parameters(gIter->data, data_set);
 555     }
 556 
 557     /* Display each node's attributes */
 558     for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
 559         struct mon_attr_data data;
 560 
 561         data.out = out;
 562         data.node = (pe_node_t *) gIter->data;
 563 
 564         if (data.node && data.node->details && data.node->details->online) {
 565             GList *attr_list = NULL;
 566             GHashTableIter iter;
 567             gpointer key, value;
 568 
 569             g_hash_table_iter_init(&iter, data.node->details->attrs);
 570             while (g_hash_table_iter_next (&iter, &key, &value)) {
 571                 attr_list = append_attr_list(attr_list, key);
 572             }
 573 
 574             if (attr_list == NULL) {
 575                 continue;
 576             }
 577 
 578             if (!pcmk__str_in_list(only_node, data.node->details->uname)) {
 579                 continue;
 580             }
 581 
 582             PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Node Attributes");
 583 
 584             out->message(out, "node", data.node, get_resource_display_options(mon_ops),
 585                          FALSE, NULL,
 586                          pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 587                          pcmk_is_set(mon_ops, mon_op_print_brief),
 588                          pcmk_is_set(mon_ops, mon_op_group_by_node),
 589                          only_node, only_rsc);
 590             g_list_foreach(attr_list, print_node_attribute, &data);
 591             g_list_free(attr_list);
 592             out->end_list(out);
 593         }
 594     }
 595 
 596     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 597     return rc;
 598 }
 599 
 600 /*!
 601  * \internal
 602  * \brief Print a section for failed actions
 603  *
 604  * \param[in] out      The output functions structure.
 605  * \param[in] data_set Cluster state to display.
 606  */
 607 static int
 608 print_failed_actions(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 609                      GListPtr only_node, GListPtr only_rsc, gboolean print_spacer)
 610 {
 611     xmlNode *xml_op = NULL;
 612     int rc = pcmk_rc_no_output;
 613 
 614     const char *id = NULL;
 615 
 616     if (xmlChildElementCount(data_set->failed) == 0) {
 617         return rc;
 618     }
 619 
 620     for (xml_op = pcmk__xml_first_child(data_set->failed); xml_op != NULL;
 621          xml_op = pcmk__xml_next(xml_op)) {
 622         char *rsc = NULL;
 623 
 624         if (!pcmk__str_in_list(only_node, crm_element_value(xml_op, XML_ATTR_UNAME))) {
 625             continue;
 626         }
 627 
 628         id = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY);
 629         if (parse_op_key(id ? id : ID(xml_op), &rsc, NULL, NULL) == FALSE) {
 630             continue;
 631         }
 632 
 633         if (!pcmk__str_in_list(only_rsc, rsc)) {
 634             free(rsc);
 635             continue;
 636         }
 637 
 638         free(rsc);
 639 
 640         PCMK__OUTPUT_LIST_HEADER(out, print_spacer, rc, "Failed Resource Actions");
 641         out->message(out, "failed-action", xml_op);
 642     }
 643 
 644     PCMK__OUTPUT_LIST_FOOTER(out, rc);
 645     return rc;
 646 }
 647 
 648 #define CHECK_RC(retcode, retval)   \
 649     if (retval == pcmk_rc_ok) {     \
 650         retcode = pcmk_rc_ok;       \
 651     }
 652 
 653 /*!
 654  * \internal
 655  * \brief Top-level printing function for text/curses output.
 656  *
 657  * \param[in] out             The output functions structure.
 658  * \param[in] data_set        Cluster state to display.
 659  * \param[in] stonith_history List of stonith actions.
 660  * \param[in] mon_ops         Bitmask of mon_op_*.
 661  * \param[in] show            Bitmask of mon_show_*.
 662  * \param[in] prefix          ID prefix to filter results by.
 663  */
 664 void
 665 print_status(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 666              stonith_history_t *stonith_history, unsigned int mon_ops,
 667              unsigned int show, char *prefix, char *only_node, char *only_rsc)
 668 {
 669     GListPtr unames = NULL;
 670     GListPtr resources = NULL;
 671 
 672     unsigned int print_opts = get_resource_display_options(mon_ops);
 673     int rc = pcmk_rc_no_output;
 674 
 675     CHECK_RC(rc, out->message(out, "cluster-summary", data_set,
 676                               pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 677                               pcmk_is_set(show, mon_show_stack),
 678                               pcmk_is_set(show, mon_show_dc),
 679                               pcmk_is_set(show, mon_show_times),
 680                               pcmk_is_set(show, mon_show_counts),
 681                               pcmk_is_set(show, mon_show_options)));
 682 
 683     unames = build_uname_list(data_set, only_node);
 684     resources = build_rsc_list(data_set, only_rsc);
 685 
 686     if (pcmk_is_set(show, mon_show_nodes) && unames) {
 687         PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
 688         CHECK_RC(rc, out->message(out, "node-list", data_set->nodes, unames,
 689                                   resources, print_opts,
 690                                   pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 691                                   pcmk_is_set(mon_ops, mon_op_print_brief),
 692                                   pcmk_is_set(mon_ops, mon_op_group_by_node)));
 693     }
 694 
 695     /* Print resources section, if needed */
 696     if (pcmk_is_set(show, mon_show_resources)) {
 697         CHECK_RC(rc, out->message(out, "resource-list", data_set, print_opts,
 698                                   pcmk_is_set(mon_ops, mon_op_group_by_node),
 699                                   pcmk_is_set(mon_ops, mon_op_inactive_resources),
 700                                   pcmk_is_set(mon_ops, mon_op_print_brief), TRUE, unames,
 701                                   resources, rc == pcmk_rc_ok));
 702     }
 703 
 704     /* print Node Attributes section if requested */
 705     if (pcmk_is_set(show, mon_show_attributes)) {
 706         CHECK_RC(rc, print_node_attributes(out, data_set, mon_ops, unames, resources,
 707                                            rc == pcmk_rc_ok));
 708     }
 709 
 710     /* If requested, print resource operations (which includes failcounts)
 711      * or just failcounts
 712      */
 713     if (pcmk_is_set(show, mon_show_operations)
 714         || pcmk_is_set(show, mon_show_failcounts)) {
 715 
 716         CHECK_RC(rc, print_node_summary(out, data_set,
 717                                         pcmk_is_set(show, mon_show_operations),
 718                                         mon_ops, unames, resources,
 719                                         (rc == pcmk_rc_ok)));
 720     }
 721 
 722     /* If there were any failed actions, print them */
 723     if (pcmk_is_set(show, mon_show_failures)
 724         && xml_has_children(data_set->failed)) {
 725 
 726         CHECK_RC(rc, print_failed_actions(out, data_set, unames, resources,
 727                                           rc == pcmk_rc_ok));
 728     }
 729 
 730     /* Print failed stonith actions */
 731     if (pcmk_is_set(show, mon_show_fence_failed)
 732         && pcmk_is_set(mon_ops, mon_op_fence_history)) {
 733 
 734         stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_eq,
 735                                                               GINT_TO_POINTER(st_failed));
 736 
 737         if (hp) {
 738             CHECK_RC(rc, out->message(out, "failed-fencing-history", stonith_history, unames,
 739                                       pcmk_is_set(mon_ops, mon_op_fence_full_history),
 740                                       rc == pcmk_rc_ok));
 741         }
 742     }
 743 
 744     /* Print tickets if requested */
 745     if (pcmk_is_set(show, mon_show_tickets)) {
 746         CHECK_RC(rc, print_cluster_tickets(out, data_set, rc == pcmk_rc_ok));
 747     }
 748 
 749     /* Print negative location constraints if requested */
 750     if (pcmk_is_set(show, mon_show_bans)) {
 751         CHECK_RC(rc, print_neg_locations(out, data_set, mon_ops, prefix, resources,
 752                                          rc == pcmk_rc_ok));
 753     }
 754 
 755     /* Print stonith history */
 756     if (pcmk_is_set(mon_ops, mon_op_fence_history)) {
 757         if (pcmk_is_set(show, mon_show_fence_worked)) {
 758             stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_neq,
 759                                                                   GINT_TO_POINTER(st_failed));
 760 
 761             if (hp) {
 762                 CHECK_RC(rc, out->message(out, "fencing-history", hp, unames,
 763                                           pcmk_is_set(mon_ops, mon_op_fence_full_history),
 764                                           rc == pcmk_rc_ok));
 765             }
 766         } else if (pcmk_is_set(show, mon_show_fence_pending)) {
 767             stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_pending, NULL);
 768 
 769             if (hp) {
 770                 CHECK_RC(rc, out->message(out, "pending-fencing-actions", hp, unames,
 771                                           pcmk_is_set(mon_ops, mon_op_fence_full_history),
 772                                           rc == pcmk_rc_ok));
 773             }
 774         }
 775     }
 776 
 777     g_list_free_full(unames, free);
 778 }
 779 
 780 /*!
 781  * \internal
 782  * \brief Top-level printing function for XML output.
 783  *
 784  * \param[in] out             The output functions structure.
 785  * \param[in] data_set        Cluster state to display.
 786  * \param[in] stonith_history List of stonith actions.
 787  * \param[in] mon_ops         Bitmask of mon_op_*.
 788  * \param[in] show            Bitmask of mon_show_*.
 789  * \param[in] prefix          ID prefix to filter results by.
 790  */
 791 void
 792 print_xml_status(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 793                  crm_exit_t history_rc, stonith_history_t *stonith_history,
 794                  unsigned int mon_ops, unsigned int show, char *prefix,
 795                  char *only_node, char *only_rsc)
 796 {
 797     GListPtr unames = NULL;
 798     GListPtr resources = NULL;
 799     unsigned int print_opts = get_resource_display_options(mon_ops);
 800 
 801     out->message(out, "cluster-summary", data_set,
 802                  pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 803                  pcmk_is_set(show, mon_show_stack),
 804                  pcmk_is_set(show, mon_show_dc),
 805                  pcmk_is_set(show, mon_show_times),
 806                  pcmk_is_set(show, mon_show_counts),
 807                  pcmk_is_set(show, mon_show_options));
 808 
 809     unames = build_uname_list(data_set, only_node);
 810     resources = build_rsc_list(data_set, only_rsc);
 811 
 812     /*** NODES ***/
 813     if (pcmk_is_set(show, mon_show_nodes)) {
 814         out->message(out, "node-list", data_set->nodes, unames,
 815                      resources, print_opts,
 816                      pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 817                      pcmk_is_set(mon_ops, mon_op_print_brief),
 818                      pcmk_is_set(mon_ops, mon_op_group_by_node));
 819     }
 820 
 821     /* Print resources section, if needed */
 822     if (pcmk_is_set(show, mon_show_resources)) {
 823         out->message(out, "resource-list", data_set, print_opts,
 824                      pcmk_is_set(mon_ops, mon_op_group_by_node),
 825                      pcmk_is_set(mon_ops, mon_op_inactive_resources),
 826                      FALSE, FALSE, unames, resources, FALSE);
 827     }
 828 
 829     /* print Node Attributes section if requested */
 830     if (pcmk_is_set(show, mon_show_attributes)) {
 831         print_node_attributes(out, data_set, mon_ops, unames, resources, FALSE);
 832     }
 833 
 834     /* If requested, print resource operations (which includes failcounts)
 835      * or just failcounts
 836      */
 837     if (pcmk_is_set(show, mon_show_operations)
 838         || pcmk_is_set(show, mon_show_failcounts)) {
 839 
 840         print_node_summary(out, data_set,
 841                            pcmk_is_set(show, mon_show_operations),
 842                            mon_ops, unames, resources, FALSE);
 843     }
 844 
 845     /* If there were any failed actions, print them */
 846     if (pcmk_is_set(show, mon_show_failures)
 847         && xml_has_children(data_set->failed)) {
 848 
 849         print_failed_actions(out, data_set, unames, resources, FALSE);
 850     }
 851 
 852     /* Print stonith history */
 853     if (pcmk_is_set(show, mon_show_fencing_all)
 854         && pcmk_is_set(mon_ops, mon_op_fence_history)) {
 855 
 856         out->message(out, "full-fencing-history", history_rc, stonith_history,
 857                      unames, pcmk_is_set(mon_ops, mon_op_fence_full_history),
 858                      FALSE);
 859     }
 860 
 861     /* Print tickets if requested */
 862     if (pcmk_is_set(show, mon_show_tickets)) {
 863         print_cluster_tickets(out, data_set, FALSE);
 864     }
 865 
 866     /* Print negative location constraints if requested */
 867     if (pcmk_is_set(show, mon_show_bans)) {
 868         print_neg_locations(out, data_set, mon_ops, prefix, resources, FALSE);
 869     }
 870 
 871     g_list_free_full(unames, free);
 872     g_list_free_full(resources, free);
 873 }
 874 
 875 /*!
 876  * \internal
 877  * \brief Top-level printing function for HTML output.
 878  *
 879  * \param[in] out             The output functions structure.
 880  * \param[in] data_set        Cluster state to display.
 881  * \param[in] stonith_history List of stonith actions.
 882  * \param[in] mon_ops         Bitmask of mon_op_*.
 883  * \param[in] show            Bitmask of mon_show_*.
 884  * \param[in] prefix          ID prefix to filter results by.
 885  */
 886 int
 887 print_html_status(pcmk__output_t *out, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 888                   stonith_history_t *stonith_history, unsigned int mon_ops,
 889                   unsigned int show, char *prefix, char *only_node,
 890                   char *only_rsc)
 891 {
 892     GListPtr unames = NULL;
 893     GListPtr resources = NULL;
 894 
 895     unsigned int print_opts = get_resource_display_options(mon_ops);
 896 
 897     out->message(out, "cluster-summary", data_set,
 898                  pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 899                  pcmk_is_set(show, mon_show_stack),
 900                  pcmk_is_set(show, mon_show_dc),
 901                  pcmk_is_set(show, mon_show_times),
 902                  pcmk_is_set(show, mon_show_counts),
 903                  pcmk_is_set(show, mon_show_options));
 904 
 905     unames = build_uname_list(data_set, only_node);
 906     resources = build_rsc_list(data_set, only_rsc);
 907 
 908     /*** NODE LIST ***/
 909     if (pcmk_is_set(show, mon_show_nodes) && unames) {
 910         out->message(out, "node-list", data_set->nodes, unames,
 911                      resources, print_opts,
 912                      pcmk_is_set(mon_ops, mon_op_print_clone_detail),
 913                      pcmk_is_set(mon_ops, mon_op_print_brief),
 914                      pcmk_is_set(mon_ops, mon_op_group_by_node));
 915     }
 916 
 917     /* Print resources section, if needed */
 918     if (pcmk_is_set(show, mon_show_resources)) {
 919         out->message(out, "resource-list", data_set, print_opts,
 920                      pcmk_is_set(mon_ops, mon_op_group_by_node),
 921                      pcmk_is_set(mon_ops, mon_op_inactive_resources),
 922                      pcmk_is_set(mon_ops, mon_op_print_brief), TRUE, unames,
 923                      resources, FALSE);
 924     }
 925 
 926     /* print Node Attributes section if requested */
 927     if (pcmk_is_set(show, mon_show_attributes)) {
 928         print_node_attributes(out, data_set, mon_ops, unames, resources, FALSE);
 929     }
 930 
 931     /* If requested, print resource operations (which includes failcounts)
 932      * or just failcounts
 933      */
 934     if (pcmk_is_set(show, mon_show_operations)
 935         || pcmk_is_set(show, mon_show_failcounts)) {
 936 
 937         print_node_summary(out, data_set,
 938                            pcmk_is_set(show, mon_show_operations),
 939                            mon_ops, unames, resources, FALSE);
 940     }
 941 
 942     /* If there were any failed actions, print them */
 943     if (pcmk_is_set(show, mon_show_failures)
 944         && xml_has_children(data_set->failed)) {
 945 
 946         print_failed_actions(out, data_set, unames, resources, FALSE);
 947     }
 948 
 949     /* Print failed stonith actions */
 950     if (pcmk_is_set(show, mon_show_fence_failed)
 951         && pcmk_is_set(mon_ops, mon_op_fence_history)) {
 952 
 953         stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_eq,
 954                                                               GINT_TO_POINTER(st_failed));
 955 
 956         if (hp) {
 957             out->message(out, "failed-fencing-history", stonith_history, unames,
 958                          pcmk_is_set(mon_ops, mon_op_fence_full_history), FALSE);
 959         }
 960     }
 961 
 962     /* Print stonith history */
 963     if (pcmk_is_set(mon_ops, mon_op_fence_history)) {
 964         if (pcmk_is_set(show, mon_show_fence_worked)) {
 965             stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_neq,
 966                                                                   GINT_TO_POINTER(st_failed));
 967 
 968             if (hp) {
 969                 out->message(out, "fencing-history", hp, unames,
 970                              pcmk_is_set(mon_ops, mon_op_fence_full_history),
 971                              FALSE);
 972             }
 973         } else if (pcmk_is_set(show, mon_show_fence_pending)) {
 974             stonith_history_t *hp = stonith__first_matching_event(stonith_history, stonith__event_state_pending, NULL);
 975 
 976             if (hp) {
 977                 out->message(out, "pending-fencing-actions", hp, unames,
 978                              pcmk_is_set(mon_ops, mon_op_fence_full_history),
 979                              FALSE);
 980             }
 981         }
 982     }
 983 
 984     /* Print tickets if requested */
 985     if (pcmk_is_set(show, mon_show_tickets)) {
 986         print_cluster_tickets(out, data_set, FALSE);
 987     }
 988 
 989     /* Print negative location constraints if requested */
 990     if (pcmk_is_set(show, mon_show_bans)) {
 991         print_neg_locations(out, data_set, mon_ops, prefix, resources, FALSE);
 992     }
 993 
 994     g_list_free_full(unames, free);
 995     g_list_free_full(resources, free);
 996     return 0;
 997 }

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