diff --git a/lib/common/output_html.c b/lib/common/output_html.c index 870080ac18..7f0f1a7cbb 100644 --- a/lib/common/output_html.c +++ b/lib/common/output_html.c @@ -1,366 +1,370 @@ /* * Copyright 2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include static const char *stylesheet_default = ".bold { font-weight: bold }\n" + ".maint { color: blue }\n" + ".offline { color: red }\n" + ".online { color: green }\n" + ".standby { color: orange }\n" ".warning { color: red, font-weight: bold }"; static gboolean cgi_output = FALSE; static int meta_refresh = 0; static char *stylesheet_link = NULL; static char *title = NULL; GOptionEntry pcmk__html_output_entries[] = { { "output-cgi", 0, 0, G_OPTION_ARG_NONE, &cgi_output, "Add text needed to use output in a CGI program", NULL }, { "output-meta-refresh", 0, 0, G_OPTION_ARG_INT, &meta_refresh, "How often to refresh", "SECONDS" }, { "output-stylesheet-link", 0, 0, G_OPTION_ARG_STRING, &stylesheet_link, "Link to an external CSS stylesheet", "URI" }, { "output-title", 0, 0, G_OPTION_ARG_STRING, &title, "Page title (defaults to command line)", "TITLE" }, { NULL } }; typedef struct private_data_s { xmlNode *root; GQueue *parent_q; GSList *errors; } private_data_t; static void html_free_priv(pcmk__output_t *out) { private_data_t *priv = out->priv; if (priv == NULL) { return; } xmlFreeNode(priv->root); g_queue_free(priv->parent_q); g_slist_free(priv->errors); free(priv); } static bool html_init(pcmk__output_t *out) { private_data_t *priv = NULL; /* If html_init was previously called on this output struct, just return. */ if (out->priv != NULL) { return true; } else { out->priv = calloc(1, sizeof(private_data_t)); if (out->priv == NULL) { return false; } priv = out->priv; } priv->parent_q = g_queue_new(); priv->root = create_xml_node(NULL, "html"); xmlCreateIntSubset(priv->root->doc, (pcmkXmlStr) "html", NULL, NULL); xmlSetProp(priv->root, (pcmkXmlStr) "lang", (pcmkXmlStr) "en"); g_queue_push_tail(priv->parent_q, priv->root); priv->errors = NULL; pcmk__output_xml_create_parent(out, "body"); return true; } static void add_error_node(gpointer data, gpointer user_data) { char *str = (char *) data; pcmk__output_t *out = (pcmk__output_t *) user_data; out->list_item(out, NULL, "%s", str); } static void html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest) { private_data_t *priv = out->priv; htmlNodePtr head_node = NULL; htmlNodePtr charset_node = NULL; /* If root is NULL, html_init failed and we are being called from pcmk__output_free * in the pcmk__output_new path. */ if (priv == NULL || priv->root == NULL) { return; } if (cgi_output && print) { fprintf(out->dest, "Content-Type: text/html\n\n"); } /* Add the head node last - it's not needed earlier because it doesn't contain * anything else that the user could add, and we want it done last to pick up * any options that may have been given. */ head_node = xmlNewNode(NULL, (pcmkXmlStr) "head"); if (title != NULL ) { pcmk_create_xml_text_node(head_node, "title", title); } else if (out->request != NULL) { pcmk_create_xml_text_node(head_node, "title", out->request); } charset_node = create_xml_node(head_node, "meta"); xmlSetProp(charset_node, (pcmkXmlStr) "charset", (pcmkXmlStr) "utf-8"); if (meta_refresh != 0) { htmlNodePtr refresh_node = create_xml_node(head_node, "meta"); xmlSetProp(refresh_node, (pcmkXmlStr) "http-equiv", (pcmkXmlStr) "refresh"); xmlSetProp(refresh_node, (pcmkXmlStr) "content", (pcmkXmlStr) crm_itoa(meta_refresh)); } /* Stylesheets are included two different ways. The first is via a built-in * default (see the stylesheet_default const above). The second is via the * "stylesheet-link" option, and this should obviously be a link to a * stylesheet. The second can override the first. At least one should be * given. */ pcmk_create_xml_text_node(head_node, "style", stylesheet_default); if (stylesheet_link != NULL) { htmlNodePtr link_node = create_xml_node(head_node, "link"); xmlSetProp(link_node, (pcmkXmlStr) "rel", (pcmkXmlStr) "stylesheet"); xmlSetProp(link_node, (pcmkXmlStr) "href", (pcmkXmlStr) stylesheet_link); } xmlAddPrevSibling(priv->root->children, head_node); if (g_slist_length(priv->errors) > 0) { out->begin_list(out, "Errors", NULL, NULL); g_slist_foreach(priv->errors, add_error_node, (gpointer) out); out->end_list(out); } if (print) { htmlDocDump(out->dest, priv->root->doc); } if (copy_dest != NULL) { *copy_dest = copy_xml(priv->root); } } static void html_reset(pcmk__output_t *out) { private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); htmlDocDump(out->dest, priv->root->doc); html_free_priv(out); html_init(out); } static void html_subprocess_output(pcmk__output_t *out, int exit_status, const char *proc_stdout, const char *proc_stderr) { char *rc_buf = NULL; private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); rc_buf = crm_strdup_printf("Return code: %d", exit_status); pcmk__output_create_xml_text_node(out, "h2", "Command Output"); pcmk__output_create_html_node(out, "div", NULL, NULL, rc_buf); if (proc_stdout != NULL) { pcmk__output_create_html_node(out, "div", NULL, NULL, "Stdout"); pcmk__output_create_html_node(out, "div", NULL, "output", proc_stdout); } if (proc_stderr != NULL) { pcmk__output_create_html_node(out, "div", NULL, NULL, "Stderr"); pcmk__output_create_html_node(out, "div", NULL, "output", proc_stderr); } free(rc_buf); } static void html_version(pcmk__output_t *out, bool extended) { private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); pcmk__output_create_xml_text_node(out, "h2", "Version Information"); pcmk__output_create_html_node(out, "div", NULL, NULL, "Program: Pacemaker"); pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Version: %s", PACEMAKER_VERSION)); pcmk__output_create_html_node(out, "div", NULL, NULL, "Author: Andrew Beekhof"); pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Build: %s", BUILD_VERSION)); pcmk__output_create_html_node(out, "div", NULL, NULL, crm_strdup_printf("Features: %s", CRM_FEATURES)); } G_GNUC_PRINTF(2, 3) static void html_err(pcmk__output_t *out, const char *format, ...) { private_data_t *priv = out->priv; int len = 0; char *buf = NULL; va_list ap; CRM_ASSERT(priv != NULL); va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); priv->errors = g_slist_append(priv->errors, buf); } G_GNUC_PRINTF(2, 3) static void html_info(pcmk__output_t *out, const char *format, ...) { /* This function intentially left blank */ } static void html_output_xml(pcmk__output_t *out, const char *name, const char *buf) { htmlNodePtr node = NULL; private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); node = pcmk__output_create_html_node(out, "pre", NULL, NULL, buf); xmlSetProp(node, (pcmkXmlStr) "lang", (pcmkXmlStr) "xml"); } G_GNUC_PRINTF(4, 5) static void html_begin_list(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format, ...) { private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); if (format != NULL) { va_list ap; char *buf = NULL; int len; va_start(ap, format); len = vasprintf(&buf, format, ap); va_end(ap); CRM_ASSERT(len >= 0); pcmk__output_create_xml_text_node(out, "h2", buf); free(buf); } pcmk__output_xml_create_parent(out, "ul"); } G_GNUC_PRINTF(3, 4) static void html_list_item(pcmk__output_t *out, const char *name, const char *format, ...) { private_data_t *priv = out->priv; htmlNodePtr item_node = NULL; va_list ap; char *buf = NULL; int len; CRM_ASSERT(priv != NULL); va_start(ap, format); len = vasprintf(&buf, format, ap); CRM_ASSERT(len >= 0); va_end(ap); item_node = pcmk__output_create_xml_text_node(out, "li", buf); free(buf); if (name != NULL) { xmlSetProp(item_node, (pcmkXmlStr) "class", (pcmkXmlStr) name); } } static void html_end_list(pcmk__output_t *out) { private_data_t *priv = out->priv; CRM_ASSERT(priv != NULL); g_queue_pop_tail(priv->parent_q); } pcmk__output_t * pcmk__mk_html_output(char **argv) { pcmk__output_t *retval = calloc(1, sizeof(pcmk__output_t)); if (retval == NULL) { return NULL; } retval->fmt_name = "html"; retval->request = g_strjoinv(" ", argv); retval->supports_quiet = false; retval->init = html_init; retval->free_priv = html_free_priv; retval->finish = html_finish; retval->reset = html_reset; retval->register_message = pcmk__register_message; retval->message = pcmk__call_message; retval->subprocess_output = html_subprocess_output; retval->version = html_version; retval->info = html_info; retval->err = html_err; retval->output_xml = html_output_xml; retval->begin_list = html_begin_list; retval->list_item = html_list_item; retval->end_list = html_end_list; return retval; } xmlNodePtr pcmk__output_create_html_node(pcmk__output_t *out, const char *element_name, const char *id, const char *class_name, const char *text) { htmlNodePtr node = pcmk__output_create_xml_text_node(out, element_name, text); if (class_name != NULL) { xmlSetProp(node, (pcmkXmlStr) "class", (pcmkXmlStr) class_name); } if (id != NULL) { xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) id); } return node; } diff --git a/tools/crm_mon_output.c b/tools/crm_mon_output.c index 4b7c035de9..9c38dc8ac2 100644 --- a/tools/crm_mon_output.c +++ b/tools/crm_mon_output.c @@ -1,802 +1,939 @@ /* * Copyright 2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "crm_mon.h" static char * time_t_string(time_t when) { crm_time_t *crm_when = crm_time_new(NULL); char *buf = NULL; crm_time_set_timet(crm_when, &when); buf = crm_time_as_string(crm_when, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); crm_time_free(crm_when); return buf; } static char * failed_action_string(xmlNodePtr xml_op) { const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY); int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0"); int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0"); const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON); time_t last_change = 0; if (crm_element_value_epoch(xml_op, XML_RSC_OP_LAST_CHANGE, &last_change) == pcmk_ok) { char *time = time_t_string(last_change); char *buf = crm_strdup_printf("%s on %s '%s' (%d): call=%s, status='%s', exitreason='%s', last-rc-change='%s', queued=%sms, exec=%sms", op_key ? op_key : ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), services_ocf_exitcode_str(rc), rc, crm_element_value(xml_op, XML_LRM_ATTR_CALLID), services_lrm_status_str(status), exit_reason ? exit_reason : "none", time, crm_element_value(xml_op, XML_RSC_OP_T_QUEUE), crm_element_value(xml_op, XML_RSC_OP_T_EXEC)); free(time); return buf; } else { return crm_strdup_printf("%s on %s '%s' (%d): call=%s, status=%s, exitreason='%s'", op_key ? op_key : ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), services_ocf_exitcode_str(rc), rc, crm_element_value(xml_op, XML_LRM_ATTR_CALLID), services_lrm_status_str(status), exit_reason ? exit_reason : "none"); } } static char * last_changed_string(const char *last_written, const char *user, const char *client, const char *origin) { if (last_written != NULL || user != NULL || client != NULL || origin != NULL) { return crm_strdup_printf("%s%s%s%s%s%s%s", last_written ? last_written : "", user ? " by " : "", user ? user : "", client ? " via " : "", client ? client : "", origin ? " origin " : "", origin ? origin : ""); } else { return strdup(""); } } static int ban_html(pcmk__output_t *out, va_list args) { pe_node_t *pe_node = va_arg(args, pe_node_t *); pe__location_t *location = va_arg(args, pe__location_t *); unsigned int mon_ops = va_arg(args, unsigned int); char *node_name = get_node_display_name(pe_node, mon_ops); char *buf = crm_strdup_printf("%s\tprevents %s from running %son %s", location->id, location->rsc_lh->id, location->role_filter == RSC_ROLE_MASTER ? "as Master " : "", node_name); pcmk__output_create_html_node(out, "li", NULL, NULL, buf); free(node_name); free(buf); return 0; } static int ban_text(pcmk__output_t *out, va_list args) { pe_node_t *pe_node = va_arg(args, pe_node_t *); pe__location_t *location = va_arg(args, pe__location_t *); unsigned int mon_ops = va_arg(args, unsigned int); char *node_name = get_node_display_name(pe_node, mon_ops); out->list_item(out, NULL, "%s\tprevents %s from running %son %s", location->id, location->rsc_lh->id, location->role_filter == RSC_ROLE_MASTER ? "as Master " : "", node_name); free(node_name); return 0; } static int ban_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "ban"); pe_node_t *pe_node = va_arg(args, pe_node_t *); pe__location_t *location = va_arg(args, pe__location_t *); char *weight_s = crm_itoa(pe_node->weight); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) location->id); xmlSetProp(node, (pcmkXmlStr) "resource", (pcmkXmlStr) location->rsc_lh->id); xmlSetProp(node, (pcmkXmlStr) "node", (pcmkXmlStr) pe_node->details->uname); xmlSetProp(node, (pcmkXmlStr) "weight", (pcmkXmlStr) weight_s); xmlSetProp(node, (pcmkXmlStr) "master_only", (pcmkXmlStr) (location->role_filter == RSC_ROLE_MASTER ? "true" : "false")); free(weight_s); return 0; } static int cluster_counts_html(pcmk__output_t *out, va_list args) { xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "li"); xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "li"); unsigned int nnodes = va_arg(args, unsigned int); unsigned int nresources = va_arg(args, unsigned int); unsigned int ndisabled = va_arg(args, unsigned int); unsigned int nblocked = va_arg(args, unsigned int); char *nnodes_str = crm_strdup_printf("%d node%s configured", nnodes, s_if_plural(nnodes)); pcmk_create_html_node(nodes_node, "span", NULL, NULL, nnodes_str); free(nnodes_str); if (ndisabled && nblocked) { char *s = crm_strdup_printf("%d resource%s configured (%d ", nresources, s_if_plural(nresources), ndisabled); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED"); s = crm_strdup_printf(", %d ", nblocked); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED"); pcmk_create_html_node(resources_node, "span", NULL, NULL, " from starting due to failure)"); } else if (ndisabled && !nblocked) { char *s = crm_strdup_printf("%d resource%s configured (%d ", nresources, s_if_plural(nresources), ndisabled); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "DISABLED"); pcmk_create_html_node(resources_node, "span", NULL, NULL, ")"); } else if (!ndisabled && nblocked) { char *s = crm_strdup_printf("%d resource%s configured (%d ", nresources, s_if_plural(nresources), nblocked); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); pcmk_create_html_node(resources_node, "span", NULL, "bold", "BLOCKED"); pcmk_create_html_node(resources_node, "span", NULL, NULL, " from starting due to failure)"); } else { char *s = crm_strdup_printf("%d resource%s configured", nresources, s_if_plural(nresources)); pcmk_create_html_node(resources_node, "span", NULL, NULL, s); free(s); } return 0; } static int cluster_counts_text(pcmk__output_t *out, va_list args) { unsigned int nnodes = va_arg(args, unsigned int); unsigned int nresources = va_arg(args, unsigned int); unsigned int ndisabled = va_arg(args, unsigned int); unsigned int nblocked = va_arg(args, unsigned int); out->list_item(out, NULL, "%d node%s configured", nnodes, s_if_plural(nnodes)); if (ndisabled && nblocked) { out->list_item(out, NULL, "%d resource%s configured (%d DISABLED, %d BLOCKED from starting due to failure", nresources, s_if_plural(nresources), ndisabled, nblocked); } else if (ndisabled && !nblocked) { out->list_item(out, NULL, "%d resource%s configured (%d DISABLED)", nresources, s_if_plural(nresources), ndisabled); } else if (!ndisabled && nblocked) { out->list_item(out, NULL, "%d resource%s configured (%d BLOCKED from starting due to failure)", nresources, s_if_plural(nresources), nblocked); } else { out->list_item(out, NULL, "%d resource%s configured", nresources, s_if_plural(nresources)); } return 0; } static int cluster_counts_xml(pcmk__output_t *out, va_list args) { xmlNodePtr nodes_node = pcmk__output_create_xml_node(out, "nodes_configured"); xmlNodePtr resources_node = pcmk__output_create_xml_node(out, "resources_configured"); unsigned int nnodes = va_arg(args, unsigned int); unsigned int nresources = va_arg(args, unsigned int); unsigned int ndisabled = va_arg(args, unsigned int); unsigned int nblocked = va_arg(args, unsigned int); char *s = crm_itoa(nnodes); xmlSetProp(nodes_node, (pcmkXmlStr) "number", (pcmkXmlStr) s); free(s); s = crm_itoa(nresources); xmlSetProp(resources_node, (pcmkXmlStr) "number", (pcmkXmlStr) s); free(s); s = crm_itoa(ndisabled); xmlSetProp(resources_node, (pcmkXmlStr) "disabled", (pcmkXmlStr) s); free(s); s = crm_itoa(nblocked); xmlSetProp(resources_node, (pcmkXmlStr) "blocked", (pcmkXmlStr) s); free(s); return 0; } static int cluster_dc_html(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li"); node_t *dc = va_arg(args, node_t *); const char *quorum = va_arg(args, const char *); const char *dc_version_s = va_arg(args, const char *); const char *dc_name = va_arg(args, const char *); pcmk_create_html_node(node, "span", NULL, "bold", "Current DC: "); if (dc) { if (crm_is_true(quorum)) { char *buf = crm_strdup_printf("%s (version %s) - partition with quorum", dc_name, dc_version_s ? dc_version_s : "unknown"); pcmk_create_html_node(node, "span", NULL, NULL, buf); free(buf); } else { char *buf = crm_strdup_printf("%s (version %s) - partition", dc_name, dc_version_s ? dc_version_s : "unknown"); pcmk_create_html_node(node, "span", NULL, NULL, buf); free(buf); pcmk_create_html_node(node, "span", NULL, "warning", "WITHOUT"); pcmk_create_html_node(node, "span", NULL, NULL, "quorum"); } } else { pcmk_create_html_node(node ,"span", NULL, "warning", "NONE"); } return 0; } static int cluster_dc_text(pcmk__output_t *out, va_list args) { node_t *dc = va_arg(args, node_t *); const char *quorum = va_arg(args, const char *); const char *dc_version_s = va_arg(args, const char *); const char *dc_name = va_arg(args, const char *); if (dc) { out->list_item(out, "Current DC", "%s (version %s) - partition %s quorum", dc_name, dc_version_s ? dc_version_s : "unknown", crm_is_true(quorum) ? "with" : "WITHOUT"); } else { out->list_item(out, "Current DC", "NONE"); } return 0; } static int cluster_dc_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "current_dc"); node_t *dc = va_arg(args, node_t *); const char *quorum = va_arg(args, const char *); const char *dc_version_s = va_arg(args, const char *); if (dc) { xmlSetProp(node, (pcmkXmlStr) "present", (pcmkXmlStr) "true"); xmlSetProp(node, (pcmkXmlStr) "version", (pcmkXmlStr) (dc_version_s ? dc_version_s : "")); xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) dc->details->uname); xmlSetProp(node, (pcmkXmlStr) "id", (pcmkXmlStr) dc->details->id); xmlSetProp(node, (pcmkXmlStr) "with_quorum", (pcmkXmlStr) (crm_is_true(quorum) ? "true" : "false")); } else { xmlSetProp(node, (pcmkXmlStr) "present", (pcmkXmlStr) "false"); } return 0; } static int cluster_options_html(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); /* Kind of a hack - close the list started by print_cluster_summary so we * can put all the options in their own list, but just for HTML output. */ out->end_list(out); /* And then this list will be closed by print_cluster_summary since it * wants to close the list it created unconditionally. */ out->begin_list(out, NULL, NULL, "Config Options"); out->list_item(out, NULL, "STONITH of failed nodes %s", is_set(data_set->flags, pe_flag_stonith_enabled) ? "enabled" : "disabled"); out->list_item(out, NULL, "Cluster is %s", is_set(data_set->flags, pe_flag_symmetric_cluster) ? "symmetric" : "asymmetric"); switch (data_set->no_quorum_policy) { case no_quorum_freeze: out->list_item(out, NULL, "No Quorum policy: Freeze resources"); break; case no_quorum_stop: out->list_item(out, NULL, "No Quorum policy: Stop ALL resources"); break; case no_quorum_ignore: out->list_item(out, NULL, "No Quorum policy: Ignore"); break; case no_quorum_suicide: out->list_item(out, NULL, "No Quorum policy: Suicide"); break; } if (is_set(data_set->flags, pe_flag_maintenance_mode)) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li"); pcmk_create_html_node(node, "span", NULL, "bold", "DISABLED"); pcmk_create_html_node(node, "span", NULL, NULL, " (the cluster will not attempt to start, stop, or recover services)"); } else { out->list_item(out, NULL, "Resource management enabled"); } return 0; } static int cluster_options_text(pcmk__output_t *out, va_list args) { pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); if (is_set(data_set->flags, pe_flag_maintenance_mode)) { fprintf(out->dest, "\n *** Resource management is DISABLED ***"); fprintf(out->dest, "\n The cluster will not attempt to start, stop or recover services"); fprintf(out->dest, "\n"); } return 0; } static int cluster_options_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "cluster_options"); pe_working_set_t *data_set = va_arg(args, pe_working_set_t *); xmlSetProp(node, (pcmkXmlStr) "stonith-enabled", (pcmkXmlStr) (is_set(data_set->flags, pe_flag_stonith_enabled) ? "true" : "false")); xmlSetProp(node, (pcmkXmlStr) "symmetric-cluster", (pcmkXmlStr) (is_set(data_set->flags, pe_flag_symmetric_cluster) ? "true" : "false")); switch (data_set->no_quorum_policy) { case no_quorum_freeze: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "freeze"); break; case no_quorum_stop: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "stop"); break; case no_quorum_ignore: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "ignore"); break; case no_quorum_suicide: xmlSetProp(node, (pcmkXmlStr) "no-quorum-policy", (pcmkXmlStr) "suicide"); break; } xmlSetProp(node, (pcmkXmlStr) "maintenance-mode", (pcmkXmlStr) (is_set(data_set->flags, pe_flag_maintenance_mode) ? "true" : "false")); return 0; } static int cluster_stack_html(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "li"); const char *stack_s = va_arg(args, const char *); pcmk_create_html_node(node, "span", NULL, "bold", "Stack: "); pcmk_create_html_node(node, "span", NULL, NULL, stack_s); return 0; } static int cluster_stack_text(pcmk__output_t *out, va_list args) { const char *stack_s = va_arg(args, const char *); out->list_item(out, "Stack", "%s", stack_s); return 0; } static int cluster_stack_xml(pcmk__output_t *out, va_list args) { xmlNodePtr node = pcmk__output_create_xml_node(out, "stack"); const char *stack_s = va_arg(args, const char *); xmlSetProp(node, (pcmkXmlStr) "type", (pcmkXmlStr) stack_s); return 0; } static int cluster_times_html(pcmk__output_t *out, va_list args) { xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "li"); xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "li"); const char *last_written = va_arg(args, const char *); const char *user = va_arg(args, const char *); const char *client = va_arg(args, const char *); const char *origin = va_arg(args, const char *); char *buf = last_changed_string(last_written, user, client, origin); pcmk_create_html_node(updated_node, "span", NULL, "bold", "Last updated: "); pcmk_create_html_node(updated_node, "span", NULL, NULL, crm_now_string(NULL)); pcmk_create_html_node(changed_node, "span", NULL, "bold", "Last change: "); pcmk_create_html_node(changed_node, "span", NULL, NULL, buf); free(buf); return 0; } static int cluster_times_xml(pcmk__output_t *out, va_list args) { xmlNodePtr updated_node = pcmk__output_create_xml_node(out, "last_update"); xmlNodePtr changed_node = pcmk__output_create_xml_node(out, "last_change"); const char *last_written = va_arg(args, const char *); const char *user = va_arg(args, const char *); const char *client = va_arg(args, const char *); const char *origin = va_arg(args, const char *); xmlSetProp(updated_node, (pcmkXmlStr) "time", (pcmkXmlStr) crm_now_string(NULL)); xmlSetProp(changed_node, (pcmkXmlStr) "time", (pcmkXmlStr) (last_written ? last_written : "")); xmlSetProp(changed_node, (pcmkXmlStr) "user", (pcmkXmlStr) (user ? user : "")); xmlSetProp(changed_node, (pcmkXmlStr) "client", (pcmkXmlStr) (client ? client : "")); xmlSetProp(changed_node, (pcmkXmlStr) "origin", (pcmkXmlStr) (origin ? origin : "")); return 0; } static int cluster_times_text(pcmk__output_t *out, va_list args) { const char *last_written = va_arg(args, const char *); const char *user = va_arg(args, const char *); const char *client = va_arg(args, const char *); const char *origin = va_arg(args, const char *); char *buf = last_changed_string(last_written, user, client, origin); out->list_item(out, "Last updated", "%s", crm_now_string(NULL)); out->list_item(out, "Last change", " %s", buf); free(buf); return 0; } static int failed_action_console(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); char *s = failed_action_string(xml_op); curses_indented_printf(out, "%s\n", s); free(s); return 0; } static int failed_action_html(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); char *s = failed_action_string(xml_op); pcmk__output_create_html_node(out, "li", NULL, NULL, s); free(s); return 0; } static int failed_action_text(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); char *s = failed_action_string(xml_op); pcmk__indented_printf(out, "%s\n", s); free(s); return 0; } static int failed_action_xml(pcmk__output_t *out, va_list args) { xmlNodePtr xml_op = va_arg(args, xmlNodePtr); const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY); const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE); int rc = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_RC), "0"); int status = crm_parse_int(crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS), "0"); const char *exit_reason = crm_element_value(xml_op, XML_LRM_ATTR_EXIT_REASON); char *rc_s = crm_itoa(rc); char *reason_s = crm_xml_escape(exit_reason ? exit_reason : "none"); xmlNodePtr node = pcmk__output_create_xml_node(out, "failure"); xmlSetProp(node, (pcmkXmlStr) (op_key ? "op_key" : "id"), (pcmkXmlStr) (op_key ? op_key : "id")); xmlSetProp(node, (pcmkXmlStr) "node", (pcmkXmlStr) crm_element_value(xml_op, XML_ATTR_UNAME)); xmlSetProp(node, (pcmkXmlStr) "exitstatus", (pcmkXmlStr) services_ocf_exitcode_str(rc)); xmlSetProp(node, (pcmkXmlStr) "exitreason", (pcmkXmlStr) reason_s); xmlSetProp(node, (pcmkXmlStr) "exitcode", (pcmkXmlStr) rc_s); xmlSetProp(node, (pcmkXmlStr) "call", (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_CALLID)); xmlSetProp(node, (pcmkXmlStr) "status", (pcmkXmlStr) services_lrm_status_str(status)); if (last) { char *s = crm_itoa(crm_parse_ms(crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS))); char *rc_change = time_t_string(crm_parse_int(last, "0")); xmlSetProp(node, (pcmkXmlStr) "last-rc-change", (pcmkXmlStr) rc_change); xmlSetProp(node, (pcmkXmlStr) "queued", (pcmkXmlStr) crm_element_value(xml_op, XML_RSC_OP_T_QUEUE)); xmlSetProp(node, (pcmkXmlStr) "exec", (pcmkXmlStr) crm_element_value(xml_op, XML_RSC_OP_T_EXEC)); xmlSetProp(node, (pcmkXmlStr) "interval", (pcmkXmlStr) s); xmlSetProp(node, (pcmkXmlStr) "task", (pcmkXmlStr) crm_element_value(xml_op, XML_LRM_ATTR_TASK)); free(s); free(rc_change); } free(reason_s); free(rc_s); return 0; } static int node_html(pcmk__output_t *out, va_list args) { node_t *node = va_arg(args, node_t *); unsigned int mon_ops = va_arg(args, unsigned int); + gboolean full = va_arg(args, gboolean); + char *node_name = get_node_display_name(node, mon_ops); char *buf = crm_strdup_printf("Node: %s", node_name); + int print_opts = get_resource_display_options(mon_ops, mon_output_html); + + if (full) { + xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li"); - out->message(out, "header", 3, buf); - out->begin_list(out, NULL, NULL, NULL); + pcmk_create_html_node(item_node, "span", NULL, NULL, buf); + + if (node->details->standby_onfail && node->details->online) { + pcmk_create_html_node(item_node, "span", NULL, "standby", " standby (on-fail)"); + } else if (node->details->standby && node->details->online) { + char *s = crm_strdup_printf(" standby%s", node->details->running_rsc ? " (with active resources)" : ""); + pcmk_create_html_node(item_node, "span", NULL, " standby", s); + free(s); + } else if (node->details->standby) { + pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE (standby)"); + } else if (node->details->maintenance && node->details->online) { + pcmk_create_html_node(item_node, "span", NULL, "maint", " maintenance"); + } else if (node->details->maintenance) { + pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE (maintenance)"); + } else if (node->details->online) { + pcmk_create_html_node(item_node, "span", NULL, "online", " online"); + } else { + pcmk_create_html_node(item_node, "span", NULL, "offline", " OFFLINE"); + } + if (is_set(mon_ops, mon_op_print_brief) && is_set(mon_ops, mon_op_group_by_node)) { + out->begin_list(out, NULL, NULL, NULL); + pe__rscs_brief_output_html(out, node->details->running_rsc, + print_opts | pe_print_rsconly, FALSE); + out->end_list(out); + + } else if (is_set(mon_ops, mon_op_group_by_node)) { + GListPtr lpc2 = NULL; + + out->begin_list(out, NULL, NULL, NULL); + for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) { + resource_t *rsc = (resource_t *) lpc2->data; + out->message(out, crm_element_name(rsc->xml), print_opts | pe_print_rsconly, rsc); + } + out->end_list(out); + } + } else { + out->begin_list(out, NULL, NULL, "%s", buf); + } free(buf); free(node_name); return 0; } static int node_text(pcmk__output_t *out, va_list args) { node_t *node = va_arg(args, node_t *); unsigned int mon_ops = va_arg(args, unsigned int); - char *node_name = get_node_display_name(node, mon_ops); + gboolean full = va_arg(args, gboolean); - out->begin_list(out, NULL, NULL, "Node: %s", node_name); + if (full) { + const char *node_mode = va_arg(args, const char *); + + char *node_name = get_node_display_name(node, mon_ops); + int print_opts = get_resource_display_options(mon_ops, mon_output_xml); + char *buf = NULL; + + /* Print the node name and status */ + if (pe__is_guest_node(node)) { + buf = crm_strdup_printf("GuestNode %s: %s", node_name, node_mode); + } else if (pe__is_remote_node(node)) { + buf = crm_strdup_printf("RemoteNode %s: %s", node_name, node_mode); + } else { + buf = crm_strdup_printf("Node %s: %s", node_name, node_mode); + } + + /* If we're grouping by node, print its resources */ + if (is_set(mon_ops, mon_op_group_by_node)) { + out->begin_list(out, NULL, NULL, "%s", buf); + out->begin_list(out, NULL, NULL, "Resources"); + + if (is_set(mon_ops, mon_op_print_brief)) { + pe__rscs_brief_output_text(out, node->details->running_rsc, + print_opts | pe_print_rsconly, FALSE); + } else { + GListPtr gIter2 = NULL; + + for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) { + resource_t *rsc = (resource_t *) gIter2->data; + out->message(out, crm_element_name(rsc->xml), print_opts | pe_print_rsconly, rsc); + } + } + + out->end_list(out); + out->end_list(out); + } else { + out->list_item(out, NULL, "%s", buf); + } + + free(buf); + free(node_name); + } else { + out->begin_list(out, NULL, NULL, "Node: %s", get_node_display_name(node, mon_ops)); + } - free(node_name); return 0; } static int node_xml(pcmk__output_t *out, va_list args) { node_t *node = va_arg(args, node_t *); + unsigned int mon_ops __attribute__((unused)) = va_arg(args, unsigned int); + gboolean full = va_arg(args, gboolean); + + if (full) { + const char *node_type = "unknown"; + int print_opts = get_resource_display_options(mon_ops, mon_output_xml); + char *length_s = crm_itoa(g_list_length(node->details->running_rsc)); + + switch (node->details->type) { + case node_member: + node_type = "member"; + break; + case node_remote: + node_type = "remote"; + break; + case node_ping: + node_type = "ping"; + break; + } + pe__name_and_nvpairs_xml(out, true, "node", 13, + "name", node->details->uname, + "id", node->details->id, + "online", node->details->online ? "true" : "false", + "standby", node->details->standby ? "true" : "false", + "standby_onfail", node->details->standby_onfail ? "true" : "false", + "maintenance", node->details->maintenance ? "true" : "false", + "pending", node->details->pending ? "true" : "false", + "unclean", node->details->unclean ? "true" : "false", + "shutdown", node->details->shutdown ? "true" : "false", + "expected_up", node->details->expected_up ? "true" : "false", + "is_dc", node->details->is_dc ? "true" : "false", + "resources_running", length_s, + "type", node_type); + + if (pe__is_guest_node(node)) { + xmlNodePtr xml_node = pcmk__output_xml_peek_parent(out); + xmlSetProp(xml_node, (pcmkXmlStr) "id_as_resource", + (pcmkXmlStr) node->details->remote_rsc->container->id); + } + + if (is_set(mon_ops, mon_op_group_by_node)) { + GListPtr lpc = NULL; + + for (lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) { + resource_t *rsc = (resource_t *) lpc->data; + out->message(out, crm_element_name(rsc->xml), print_opts | pe_print_rsconly, rsc); + } + } - xmlNodePtr parent = pcmk__output_xml_create_parent(out, "node"); - xmlSetProp(parent, (pcmkXmlStr) "name", (pcmkXmlStr) node->details->uname); + free(length_s); + + out->end_list(out); + } else { + xmlNodePtr parent = pcmk__output_xml_create_parent(out, "node"); + xmlSetProp(parent, (pcmkXmlStr) "name", (pcmkXmlStr) node->details->uname); + } return 0; } static int node_attribute_text(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); gboolean add_extra = va_arg(args, gboolean); int expected_score = va_arg(args, int); if (add_extra) { int v = crm_parse_int(value, "0"); if (v <= 0) { out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is lost", name, value); } else if (v < expected_score) { out->list_item(out, NULL, "%-32s\t: %-10s\t: Connectivity is degraded (Expected=%d)", name, value, expected_score); } else { out->list_item(out, NULL, "%-32s\t: %-10s", name, value); } } else { out->list_item(out, NULL, "%-32s\t: %-10s", name, value); } return 0; } static int node_attribute_html(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); gboolean add_extra = va_arg(args, gboolean); int expected_score = va_arg(args, int); if (add_extra) { int v = crm_parse_int(value, "0"); char *s = crm_strdup_printf("%s: %s", name, value); xmlNodePtr item_node = pcmk__output_create_xml_node(out, "li"); pcmk_create_html_node(item_node, "span", NULL, NULL, s); free(s); if (v <= 0) { pcmk_create_html_node(item_node, "span", NULL, "bold", "(connectivity is lost)"); } else if (v < expected_score) { char *buf = crm_strdup_printf("(connectivity is degraded -- expected %d", expected_score); pcmk_create_html_node(item_node, "span", NULL, "bold", buf); free(buf); } } else { out->list_item(out, NULL, "%s: %s", name, value); } return 0; } static int node_attribute_xml(pcmk__output_t *out, va_list args) { const char *name = va_arg(args, const char *); const char *value = va_arg(args, const char *); gboolean add_extra = va_arg(args, gboolean); int expected_score = va_arg(args, int); xmlNodePtr node = pcmk__output_create_xml_node(out, "attribute"); xmlSetProp(node, (pcmkXmlStr) "name", (pcmkXmlStr) name); xmlSetProp(node, (pcmkXmlStr) "value", (pcmkXmlStr) value); if (add_extra) { char *buf = crm_itoa(expected_score); xmlSetProp(node, (pcmkXmlStr) "expected", (pcmkXmlStr) buf); free(buf); } return 0; } static int stonith_event_console(pcmk__output_t *out, va_list args) { stonith_history_t *event = va_arg(args, stonith_history_t *); int full_history = va_arg(args, int); gboolean later_succeeded = va_arg(args, gboolean); char *buf = NULL; buf = time_t_string(event->completed); switch (event->state) { case st_failed: curses_indented_printf(out, "%s of %s failed: delegate=%s, client=%s, origin=%s, %s='%s %s'\n", stonith_action_str(event->action), event->target, event->delegate ? event->delegate : "", event->client, event->origin, full_history ? "completed" : "last-failed", buf, later_succeeded ? "(a later attempt succeeded)" : ""); break; case st_done: curses_indented_printf(out, "%s of %s successful: delegate=%s, client=%s, origin=%s, %s='%s'\n", stonith_action_str(event->action), event->target, event->delegate ? event->delegate : "", event->client, event->origin, full_history ? "completed" : "last-successful", buf); break; default: curses_indented_printf(out, "%s of %s pending: client=%s, origin=%s\n", stonith_action_str(event->action), event->target, event->client, event->origin); break; } free(buf); return 0; } static int ticket_console(pcmk__output_t *out, va_list args) { ticket_t *ticket = va_arg(args, ticket_t *); if (ticket->last_granted > -1) { char *time = pcmk_format_named_time("last-granted", ticket->last_granted); out->list_item(out, ticket->id, "\t%s%s %s", ticket->granted ? "granted" : "revoked", ticket->standby ? " [standby]" : "", time); free(time); } else { out->list_item(out, ticket->id, "\t%s%s", ticket->granted ? "granted" : "revoked", ticket->standby ? " [standby]" : ""); } return 0; } static pcmk__message_entry_t fmt_functions[] = { { "ban", "console", ban_text }, { "ban", "html", ban_html }, { "ban", "text", ban_text }, { "ban", "xml", ban_xml }, { "bundle", "console", pe__bundle_text }, { "cluster-counts", "console", cluster_counts_text }, { "cluster-counts", "html", cluster_counts_html }, { "cluster-counts", "text", cluster_counts_text }, { "cluster-counts", "xml", cluster_counts_xml }, { "cluster-dc", "console", cluster_dc_text }, { "cluster-dc", "html", cluster_dc_html }, { "cluster-dc", "text", cluster_dc_text }, { "cluster-dc", "xml", cluster_dc_xml }, { "cluster-options", "console", cluster_options_text }, { "cluster-options", "html", cluster_options_html }, { "cluster-options", "text", cluster_options_text }, { "cluster-options", "xml", cluster_options_xml }, { "cluster-stack", "console", cluster_stack_text }, { "cluster-stack", "html", cluster_stack_html }, { "cluster-stack", "text", cluster_stack_text }, { "cluster-stack", "xml", cluster_stack_xml }, { "cluster-times", "console", cluster_times_text }, { "cluster-times", "html", cluster_times_html }, { "cluster-times", "text", cluster_times_text }, { "cluster-times", "xml", cluster_times_xml }, { "failed-action", "console", failed_action_console }, { "failed-action", "html", failed_action_html }, { "failed-action", "text", failed_action_text }, { "failed-action", "xml", failed_action_xml }, { "node", "console", node_text }, { "node", "html", node_html }, { "node", "text", node_text }, { "node", "xml", node_xml }, { "node-attribute", "console", node_attribute_text }, { "node-attribute", "html", node_attribute_html }, { "node-attribute", "text", node_attribute_text }, { "node-attribute", "xml", node_attribute_xml }, { "primitive", "console", pe__resource_text }, { "stonith-event", "console", stonith_event_console }, { "ticket", "console", ticket_console }, { NULL, NULL, NULL } }; void crm_mon_register_messages(pcmk__output_t *out) { pcmk__register_messages(out, fmt_functions); } diff --git a/tools/crm_mon_print.c b/tools/crm_mon_print.c index c51acd2c46..fb68c37004 100644 --- a/tools/crm_mon_print.c +++ b/tools/crm_mon_print.c @@ -1,1719 +1,1614 @@ /* * Copyright 2019 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "crm_mon.h" static void print_node_start(mon_state_t *state, node_t *node, unsigned int mon_ops); static void print_node_end(mon_state_t *state); static void print_resources_heading(mon_state_t *state, unsigned int mon_ops); static void print_resources_closing(mon_state_t *state, gboolean printed_heading, unsigned int mon_ops); static void print_resources(mon_state_t *state, pe_working_set_t *data_set, int print_opts, unsigned int mon_ops); static void print_rsc_history_start(mon_state_t *state, pe_working_set_t *data_set, node_t *node, resource_t *rsc, const char *rsc_id, gboolean all); static void print_rsc_history_end(mon_state_t *state); static void print_op_history(mon_state_t *state, pe_working_set_t *data_set, node_t *node, xmlNode *xml_op, const char *task, const char *interval_ms_s, int rc, unsigned int mon_ops); static void print_rsc_history(mon_state_t *state, pe_working_set_t *data_set, node_t *node, xmlNode *rsc_entry, gboolean operations, unsigned int mon_ops); static void print_node_history(mon_state_t *state, pe_working_set_t *data_set, xmlNode *node_state, gboolean operations, unsigned int mon_ops); static gboolean add_extra_info(mon_state_t *state, node_t * node, GListPtr rsc_list, const char *attrname, const char *attrvalue, int *expected_score); static void print_node_attribute(gpointer name, gpointer user_data); static void print_node_summary(mon_state_t *state, pe_working_set_t * data_set, gboolean operations, unsigned int mon_ops); static void print_cluster_tickets(mon_state_t *state, pe_working_set_t * data_set); static void print_neg_locations(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops, const char *prefix); static void print_node_attributes(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops); static void print_cluster_summary_header(mon_state_t *state); static void print_cluster_times(mon_state_t *state, pe_working_set_t *data_set); static void print_cluster_dc(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops); static void print_cluster_summary(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops, unsigned int show); static void print_failed_actions(mon_state_t *state, pe_working_set_t *data_set); static void print_failed_stonith_actions(mon_state_t *state, stonith_history_t *history, unsigned int mon_ops); static void print_stonith_pending(mon_state_t *state, stonith_history_t *history, unsigned int mon_ops); static void print_stonith_history(mon_state_t *state, stonith_history_t *history, unsigned int mon_ops); /*! * \internal * \brief Print whatever is needed to start a node section * * \param[in] stream File stream to display output to * \param[in] node Node to print */ static void print_node_start(mon_state_t *state, node_t *node, unsigned int mon_ops) { char *node_name; switch (state->output_format) { case mon_output_plain: case mon_output_console: node_name = get_node_display_name(node, mon_ops); print_as(state->output_format, "* Node %s:\n", node_name); free(node_name); break; case mon_output_html: case mon_output_cgi: node_name = get_node_display_name(node, mon_ops); fprintf(state->stream, "

Node: %s

\n
    \n", node_name); free(node_name); break; case mon_output_xml: fprintf(state->stream, " \n", node->details->uname); break; default: break; } } /*! * \internal * \brief Print whatever is needed to end a node section * * \param[in] stream File stream to display output to */ static void print_node_end(mon_state_t *state) { switch (state->output_format) { case mon_output_html: case mon_output_cgi: fprintf(state->stream, "
\n"); break; case mon_output_xml: fprintf(state->stream, " \n"); break; default: break; } } /*! * \internal * \brief Print resources section heading appropriate to options * * \param[in] stream File stream to display output to */ static void print_resources_heading(mon_state_t *state, unsigned int mon_ops) { const char *heading; if (is_set(mon_ops, mon_op_group_by_node)) { /* Active resources have already been printed by node */ heading = is_set(mon_ops, mon_op_inactive_resources) ? "Inactive resources" : NULL; } else if (is_set(mon_ops, mon_op_inactive_resources)) { heading = "Full list of resources"; } else { heading = "Active resources"; } /* Print section heading */ switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, "\n%s:\n\n", heading); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, "
\n

%s

\n", heading); break; case mon_output_xml: fprintf(state->stream, " \n"); break; default: break; } } /*! * \internal * \brief Print whatever resource section closing is appropriate * * \param[in] stream File stream to display output to */ static void print_resources_closing(mon_state_t *state, gboolean printed_heading, unsigned int mon_ops) { const char *heading; /* What type of resources we did or did not display */ if (is_set(mon_ops, mon_op_group_by_node)) { heading = "inactive "; } else if (is_set(mon_ops, mon_op_inactive_resources)) { heading = ""; } else { heading = "active "; } switch (state->output_format) { case mon_output_plain: case mon_output_console: if (!printed_heading) { print_as(state->output_format, "\nNo %sresources\n\n", heading); } break; case mon_output_html: case mon_output_cgi: if (!printed_heading) { fprintf(state->stream, "
\n

No %sresources

\n", heading); } break; case mon_output_xml: fprintf(state->stream, " %s\n", (printed_heading? "
" : "")); break; default: break; } } /*! * \internal * \brief Print whatever resource section(s) are appropriate * * \param[in] stream File stream to display output to * \param[in] data_set Cluster state to display * \param[in] print_opts Bitmask of pe_print_options */ static void print_resources(mon_state_t *state, pe_working_set_t *data_set, int print_opts, unsigned int mon_ops) { GListPtr rsc_iter; const char *prefix = NULL; gboolean printed_heading = FALSE; gboolean brief_output = is_set(mon_ops, mon_op_print_brief); /* If we already showed active resources by node, and * we're not showing inactive resources, we have nothing to do */ if (is_set(mon_ops, mon_op_group_by_node) && is_not_set(mon_ops, mon_op_inactive_resources)) { return; } /* XML uses an indent, and ignores brief option for resources */ if (state->output_format == mon_output_xml) { prefix = " "; brief_output = FALSE; } /* If we haven't already printed resources grouped by node, * and brief output was requested, print resource summary */ if (brief_output && is_not_set(mon_ops, mon_op_group_by_node)) { print_resources_heading(state, mon_ops); printed_heading = TRUE; print_rscs_brief(data_set->resources, NULL, print_opts, state->stream, is_set(mon_ops, mon_op_inactive_resources)); } /* For each resource, display it if appropriate */ for (rsc_iter = data_set->resources; rsc_iter != NULL; rsc_iter = rsc_iter->next) { resource_t *rsc = (resource_t *) rsc_iter->data; /* Complex resources may have some sub-resources active and some inactive */ gboolean is_active = rsc->fns->active(rsc, TRUE); gboolean partially_active = rsc->fns->active(rsc, FALSE); /* Skip inactive orphans (deleted but still in CIB) */ if (is_set(rsc->flags, pe_rsc_orphan) && !is_active) { continue; /* Skip active resources if we already displayed them by node */ } else if (is_set(mon_ops, mon_op_group_by_node)) { if (is_active) { continue; } /* Skip primitives already counted in a brief summary */ } else if (brief_output && (rsc->variant == pe_native)) { continue; /* Skip resources that aren't at least partially active, * unless we're displaying inactive resources */ } else if (!partially_active && is_not_set(mon_ops, mon_op_inactive_resources)) { continue; } /* Print this resource */ if (printed_heading == FALSE) { print_resources_heading(state, mon_ops); printed_heading = TRUE; } rsc->fns->print(rsc, prefix, print_opts, state->stream); } print_resources_closing(state, printed_heading, mon_ops); } /*! * \internal * \brief Print heading for resource history * * \param[in] stream File stream to display output to * \param[in] data_set Current state of CIB * \param[in] node Node that ran this resource * \param[in] rsc Resource to print * \param[in] rsc_id ID of resource to print * \param[in] all Whether to print every resource or just failed ones */ static void print_rsc_history_start(mon_state_t *state, pe_working_set_t *data_set, node_t *node, resource_t *rsc, const char *rsc_id, gboolean all) { time_t last_failure = 0; int failcount = rsc? pe_get_failcount(node, rsc, &last_failure, pe_fc_default, NULL, data_set) : 0; if (!all && !failcount && (last_failure <= 0)) { return; } /* Print resource ID */ switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, " %s:", rsc_id); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, "
  • %s:", rsc_id); break; case mon_output_xml: fprintf(state->stream, " output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, " orphan"); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, " orphan"); break; case mon_output_xml: fprintf(state->stream, " orphan=\"true\""); break; default: break; } /* If resource is not an orphan, print some details */ } else if (all || failcount || (last_failure > 0)) { /* Print migration threshold */ switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, " migration-threshold=%d", rsc->migration_threshold); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, " migration-threshold=%d", rsc->migration_threshold); break; case mon_output_xml: fprintf(state->stream, " orphan=\"false\" migration-threshold=\"%d\"", rsc->migration_threshold); break; default: break; } /* Print fail count if any */ if (failcount > 0) { switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, " " CRM_FAIL_COUNT_PREFIX "=%d", failcount); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, " " CRM_FAIL_COUNT_PREFIX "=%d", failcount); break; case mon_output_xml: fprintf(state->stream, " " CRM_FAIL_COUNT_PREFIX "=\"%d\"", failcount); break; default: break; } } /* Print last failure time if any */ if (last_failure > 0) { switch (state->output_format) { case mon_output_console: case mon_output_plain: { char *time = pcmk_format_named_time(CRM_LAST_FAILURE_PREFIX, last_failure); print_as(state->output_format, " %s", time); free(time); break; } case mon_output_cgi: case mon_output_html: case mon_output_xml: { char *time = pcmk_format_named_time(CRM_LAST_FAILURE_PREFIX, last_failure); fprintf(state->stream, " %s", time); free(time); break; } default: break; } } } /* End the heading */ switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, "\n"); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, "\n
      \n"); break; case mon_output_xml: fprintf(state->stream, ">\n"); break; default: break; } } /*! * \internal * \brief Print closing for resource history * * \param[in] stream File stream to display output to */ static void print_rsc_history_end(mon_state_t *state) { switch (state->output_format) { case mon_output_html: case mon_output_cgi: fprintf(state->stream, "
    \n
  • \n"); break; case mon_output_xml: fprintf(state->stream, " \n"); break; default: break; } } /*! * \internal * \brief Print operation history * * \param[in] stream File stream to display output to * \param[in] data_set Current state of CIB * \param[in] node Node this operation is for * \param[in] xml_op Root of XML tree describing this operation * \param[in] task Task parsed from this operation's XML * \param[in] interval_ms_s Interval parsed from this operation's XML * \param[in] rc Return code parsed from this operation's XML */ static void print_op_history(mon_state_t *state, pe_working_set_t *data_set, node_t *node, xmlNode *xml_op, const char *task, const char *interval_ms_s, int rc, unsigned int mon_ops) { const char *value = NULL; const char *call = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); /* Begin the operation description */ switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, " + (%s) %s:", call, task); if (interval_ms_s && safe_str_neq(interval_ms_s, "0")) { char *pair = pcmk_format_nvpair("interval", interval_ms_s, "ms"); print_as(state->output_format, " %s", pair); free(pair); } break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, "
  • (%s) %s:", call, task); if (interval_ms_s && safe_str_neq(interval_ms_s, "0")) { char *pair = pcmk_format_nvpair("interval", interval_ms_s, "ms"); fprintf(state->stream, " %s", pair); free(pair); } break; case mon_output_xml: fprintf(state->stream, " stream, " %s", pair); free(pair); } break; default: break; } if (is_set(mon_ops, mon_op_print_timing)) { time_t epoch = 0; const char *attr; attr = XML_RSC_OP_LAST_CHANGE; if ((crm_element_value_epoch(xml_op, attr, &epoch) == pcmk_ok) && (epoch > 0)) { switch (state->output_format) { case mon_output_console: case mon_output_plain: { char *time = pcmk_format_named_time(attr, epoch); print_as(state->output_format, " %s", time); free(time); break; } case mon_output_cgi: case mon_output_html: case mon_output_xml: { char *time = pcmk_format_named_time(attr, epoch); fprintf(state->stream, " %s", time); free(time); break; } default: break; } } // last-run is deprecated attr = XML_RSC_OP_LAST_RUN; if ((crm_element_value_epoch(xml_op, attr, &epoch) == pcmk_ok) && (epoch > 0)) { switch (state->output_format) { case mon_output_console: case mon_output_plain: { char *time = pcmk_format_named_time(attr, epoch); print_as(state->output_format, " %s", time); free(time); break; } case mon_output_cgi: case mon_output_html: case mon_output_xml: { char *time = pcmk_format_named_time(attr, epoch); fprintf(state->stream, " %s", time); free(time); break; } default: break; } } attr = XML_RSC_OP_T_EXEC; value = crm_element_value(xml_op, attr); if (value) { switch (state->output_format) { case mon_output_console: case mon_output_plain: { char *pair = pcmk_format_nvpair(attr, value, "ms"); print_as(state->output_format, " %s", pair); free(pair); break; } case mon_output_cgi: case mon_output_html: case mon_output_xml: { char *pair = pcmk_format_nvpair(attr, value, "ms"); fprintf(state->stream, " %s", pair); free(pair); break; } default: break; } } attr = XML_RSC_OP_T_QUEUE; value = crm_element_value(xml_op, attr); if (value) { switch (state->output_format) { case mon_output_console: case mon_output_plain: { char *pair = pcmk_format_nvpair(attr, value, "ms"); print_as(state->output_format, " %s", pair); free(pair); break; } case mon_output_cgi: case mon_output_html: case mon_output_xml: { char *pair = pcmk_format_nvpair(attr, value, "ms"); fprintf(state->stream, " %s", pair); free(pair); break; } default: break; } } } /* End the operation description */ switch (state->output_format) { case mon_output_plain: case mon_output_console: print_as(state->output_format, " rc=%d (%s)\n", rc, services_ocf_exitcode_str(rc)); break; case mon_output_html: case mon_output_cgi: fprintf(state->stream, " rc=%d (%s)
  • \n", rc, services_ocf_exitcode_str(rc)); break; case mon_output_xml: fprintf(state->stream, " rc=\"%d\" rc_text=\"%s\" />\n", rc, services_ocf_exitcode_str(rc)); break; default: break; } } /*! * \internal * \brief Print resource operation/failure history * * \param[in] stream File stream to display output to * \param[in] data_set Current state of CIB * \param[in] node Node that ran this resource * \param[in] rsc_entry Root of XML tree describing resource status * \param[in] operations Whether to print operations or just failcounts */ static void print_rsc_history(mon_state_t *state, pe_working_set_t *data_set, node_t *node, xmlNode *rsc_entry, gboolean operations, unsigned int mon_ops) { GListPtr gIter = NULL; GListPtr op_list = NULL; gboolean printed = FALSE; const char *rsc_id = crm_element_value(rsc_entry, XML_ATTR_ID); resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); xmlNode *rsc_op = NULL; /* If we're not showing operations, just print the resource failure summary */ if (operations == FALSE) { print_rsc_history_start(state, data_set, node, rsc, rsc_id, FALSE); print_rsc_history_end(state); return; } /* Create a list of this resource's operations */ for (rsc_op = __xml_first_child_element(rsc_entry); rsc_op != NULL; rsc_op = __xml_next_element(rsc_op)) { if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { op_list = g_list_append(op_list, rsc_op); } } op_list = g_list_sort(op_list, sort_op_by_callid); /* Print each operation */ for (gIter = op_list; gIter != NULL; gIter = gIter->next) { xmlNode *xml_op = (xmlNode *) gIter->data; const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *interval_ms_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL_MS); const char *op_rc = crm_element_value(xml_op, XML_LRM_ATTR_RC); int rc = crm_parse_int(op_rc, "0"); /* Display 0-interval monitors as "probe" */ if (safe_str_eq(task, CRMD_ACTION_STATUS) && ((interval_ms_s == NULL) || safe_str_eq(interval_ms_s, "0"))) { task = "probe"; } /* Ignore notifies and some probes */ if (safe_str_eq(task, CRMD_ACTION_NOTIFY) || (safe_str_eq(task, "probe") && (rc == 7))) { continue; } /* If this is the first printed operation, print heading for resource */ if (printed == FALSE) { printed = TRUE; print_rsc_history_start(state, data_set, node, rsc, rsc_id, TRUE); } /* Print the operation */ print_op_history(state, data_set, node, xml_op, task, interval_ms_s, rc, mon_ops); } /* Free the list we created (no need to free the individual items) */ g_list_free(op_list); /* If we printed anything, close the resource */ if (printed) { print_rsc_history_end(state); } } /*! * \internal * \brief Print node operation/failure history * * \param[in] stream File stream to display output to * \param[in] data_set Current state of CIB * \param[in] node_state Root of XML tree describing node status * \param[in] operations Whether to print operations or just failcounts */ static void print_node_history(mon_state_t *state, pe_working_set_t *data_set, xmlNode *node_state, gboolean operations, unsigned int mon_ops) { node_t *node = pe_find_node_id(data_set->nodes, ID(node_state)); xmlNode *lrm_rsc = NULL; xmlNode *rsc_entry = NULL; if (node && node->details && node->details->online) { print_node_start(state, node, mon_ops); lrm_rsc = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rsc = find_xml_node(lrm_rsc, XML_LRM_TAG_RESOURCES, FALSE); /* Print history of each of the node's resources */ for (rsc_entry = __xml_first_child_element(lrm_rsc); rsc_entry != NULL; rsc_entry = __xml_next_element(rsc_entry)) { if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) { print_rsc_history(state, data_set, node, rsc_entry, operations, mon_ops); } } print_node_end(state); } } /*! * \internal * \brief Determine whether extended information about an attribute should be added. * * \param[in] data_set Working set of CIB state * * \return TRUE if extended information should be printed, FALSE otherwise * \note Currently, extended information is only supported for ping/pingd * resources, for which a message will be printed if connectivity is lost * or degraded. */ static gboolean add_extra_info(mon_state_t *state, node_t *node, GListPtr rsc_list, const char *attrname, const char *attrvalue, int *expected_score) { GListPtr gIter = NULL; for (gIter = rsc_list; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; const char *type = g_hash_table_lookup(rsc->meta, "type"); const char *name = NULL; if (rsc->children != NULL) { if (add_extra_info(state, node, rsc->children, attrname, attrvalue, expected_score)) { return TRUE; } } if (safe_str_neq(type, "ping") && safe_str_neq(type, "pingd")) { return FALSE; } name = g_hash_table_lookup(rsc->parameters, "name"); if (name == NULL) { name = "pingd"; } /* To identify the resource with the attribute name. */ if (safe_str_eq(name, attrname)) { int host_list_num = 0; /* int value = crm_parse_int(attrvalue, "0"); */ const char *hosts = g_hash_table_lookup(rsc->parameters, "host_list"); const char *multiplier = g_hash_table_lookup(rsc->parameters, "multiplier"); if (hosts) { char **host_list = g_strsplit(hosts, " ", 0); host_list_num = g_strv_length(host_list); g_strfreev(host_list); } /* pingd multiplier is the same as the default value. */ *expected_score = host_list_num * crm_parse_int(multiplier, "1"); return TRUE; } } return FALSE; } /* structure for passing multiple user data to g_list_foreach() */ struct mon_attr_data { mon_state_t *state; node_t *node; }; static void print_node_attribute(gpointer name, gpointer user_data) { const char *value = NULL; int expected_score = 0; gboolean add_extra = FALSE; struct mon_attr_data *data = (struct mon_attr_data *) user_data; value = pe_node_attribute_raw(data->node, name); add_extra = add_extra_info(data->state, data->node, data->node->details->running_rsc, name, value, &expected_score); /* Print attribute name and value */ data->state->out->message(data->state->out, "node-attribute", name, value, add_extra, expected_score); } static void print_node_summary(mon_state_t *state, pe_working_set_t * data_set, gboolean operations, unsigned int mon_ops) { xmlNode *node_state = NULL; xmlNode *cib_status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); /* Print heading */ switch (state->output_format) { case mon_output_plain: case mon_output_console: if (operations) { print_as(state->output_format, "\nOperations:\n"); } else { print_as(state->output_format, "\nMigration Summary:\n"); } break; case mon_output_html: case mon_output_cgi: if (operations) { fprintf(state->stream, "
    \n

    Operations

    \n"); } else { fprintf(state->stream, "
    \n

    Migration Summary

    \n"); } break; case mon_output_xml: fprintf(state->stream, " \n"); break; default: break; } /* Print each node in the CIB status */ for (node_state = __xml_first_child_element(cib_status); node_state != NULL; node_state = __xml_next_element(node_state)) { if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { print_node_history(state, data_set, node_state, operations, mon_ops); } } /* Close section */ switch (state->output_format) { case mon_output_xml: fprintf(state->stream, " \n"); break; default: break; } } static void print_cluster_tickets(mon_state_t *state, pe_working_set_t * data_set) { GHashTableIter iter; gpointer key, value; /* Print section heading */ if (state->output_format == mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "tickets"); } else { state->out->begin_list(state->out, NULL, NULL, "Tickets"); } /* Print each ticket */ g_hash_table_iter_init(&iter, data_set->tickets); while (g_hash_table_iter_next(&iter, &key, &value)) { ticket_t *ticket = (ticket_t *) value; state->out->message(state->out, "ticket", ticket); } /* Close section */ state->out->end_list(state->out); } /*! * \internal * \brief Print section for negative location constraints * * \param[in] stream File stream to display output to * \param[in] data_set Working set corresponding to CIB status to display */ static void print_neg_locations(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops, const char *prefix) { GListPtr gIter, gIter2; if (state->output_format == mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "bans"); } else { state->out->begin_list(state->out, NULL, NULL, "Negative Location Constraints"); } /* Print each ban */ for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) { pe__location_t *location = gIter->data; if (!g_str_has_prefix(location->id, prefix)) continue; for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) { node_t *node = (node_t *) gIter2->data; if (node->weight < 0) { state->out->message(state->out, "ban", node, location, mon_ops); } } } state->out->end_list(state->out); } /*! * \internal * \brief Print node attributes section * * \param[in] stream File stream to display output to * \param[in] data_set Working set of CIB state */ static void print_node_attributes(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops) { GListPtr gIter = NULL; /* Print section heading */ if (state->output_format == mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "node_attributes"); } else { state->out->begin_list(state->out, NULL, NULL, "Node Attributes"); } /* Unpack all resource parameters (it would be more efficient to do this * only when needed for the first time in add_extra_info()) */ for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { crm_mon_get_parameters(gIter->data, data_set); } /* Display each node's attributes */ for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { struct mon_attr_data data; data.state = state; data.node = (node_t *) gIter->data; if (data.node && data.node->details && data.node->details->online) { GList *attr_list = NULL; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, data.node->details->attrs); while (g_hash_table_iter_next (&iter, &key, &value)) { attr_list = append_attr_list(attr_list, key); } if (g_list_length(attr_list) == 0) { g_list_free(attr_list); continue; } - state->out->message(state->out, "node", data.node, mon_ops); + state->out->message(state->out, "node", data.node, mon_ops, FALSE); g_list_foreach(attr_list, print_node_attribute, &data); g_list_free(attr_list); state->out->end_list(state->out); } } /* Print section footer */ state->out->end_list(state->out); } /*! * \internal * \brief Print header for cluster summary if needed * * \param[in] stream File stream to display output to */ static void print_cluster_summary_header(mon_state_t *state) { if (state->output_format == mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "summary"); } else { state->out->begin_list(state->out, NULL, NULL, "Cluster Summary"); } } /*! * \internal * \brief Print times the display was last updated and CIB last changed * * \param[in] stream File stream to display output to * \param[in] data_set Working set of CIB state */ static void print_cluster_times(mon_state_t *state, pe_working_set_t *data_set) { const char *last_written = crm_element_value(data_set->input, XML_CIB_ATTR_WRITTEN); const char *user = crm_element_value(data_set->input, XML_ATTR_UPDATE_USER); const char *client = crm_element_value(data_set->input, XML_ATTR_UPDATE_CLIENT); const char *origin = crm_element_value(data_set->input, XML_ATTR_UPDATE_ORIG); state->out->message(state->out, "cluster-times", last_written, user, client, origin); } /*! * \internal * \brief Print current DC and its version * * \param[in] stream File stream to display output to * \param[in] data_set Working set of CIB state */ static void print_cluster_dc(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops) { node_t *dc = data_set->dc_node; xmlNode *dc_version = get_xpath_object("//nvpair[@name='dc-version']", data_set->input, LOG_DEBUG); const char *dc_version_s = dc_version? crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE) : NULL; const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); char *dc_name = dc? get_node_display_name(dc, mon_ops) : NULL; state->out->message(state->out, "cluster-dc", dc, quorum, dc_version_s, dc_name); free(dc_name); } /*! * \internal * \brief Print a summary of cluster-wide information * * \param[in] stream File stream to display output to * \param[in] data_set Working set of CIB state */ static void print_cluster_summary(mon_state_t *state, pe_working_set_t *data_set, unsigned int mon_ops, unsigned int show) { const char *stack_s = get_cluster_stack(data_set); gboolean header_printed = FALSE; if (show & mon_show_stack) { if (header_printed == FALSE) { print_cluster_summary_header(state); header_printed = TRUE; } state->out->message(state->out, "cluster-stack", stack_s); } /* Always print DC if none, even if not requested */ if ((data_set->dc_node == NULL) || (show & mon_show_dc)) { if (header_printed == FALSE) { print_cluster_summary_header(state); header_printed = TRUE; } print_cluster_dc(state, data_set, mon_ops); } if (show & mon_show_times) { if (header_printed == FALSE) { print_cluster_summary_header(state); header_printed = TRUE; } print_cluster_times(state, data_set); } if (is_set(data_set->flags, pe_flag_maintenance_mode) || data_set->disabled_resources || data_set->blocked_resources || is_set(show, mon_show_count)) { if (header_printed == FALSE) { print_cluster_summary_header(state); header_printed = TRUE; } state->out->message(state->out, "cluster-counts", g_list_length(data_set->nodes), count_resources(data_set, NULL), data_set->disabled_resources, data_set->blocked_resources); } /* There is not a separate option for showing cluster options, so show with * stack for now; a separate option could be added if there is demand */ if (show & mon_show_stack) { state->out->message(state->out, "cluster-options", data_set); } if (header_printed) { state->out->end_list(state->out); } } /*! * \internal * \brief Print a section for failed actions * * \param[in] stream File stream to display output to * \param[in] data_set Working set of CIB state */ static void print_failed_actions(mon_state_t *state, pe_working_set_t *data_set) { xmlNode *xml_op = NULL; /* Print section heading */ if (state->output_format == mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "failures"); } else { state->out->begin_list(state->out, NULL, NULL, "Failed Resource Actions"); } /* Print each failed action */ for (xml_op = __xml_first_child(data_set->failed); xml_op != NULL; xml_op = __xml_next(xml_op)) { state->out->message(state->out, "failed-action", xml_op); } /* End section */ state->out->end_list(state->out); } /*! * \internal * \brief Print a section for failed stonith actions * * \param[in] stream File stream to display output to * \param[in] history List of stonith actions * */ static void print_failed_stonith_actions(mon_state_t *state, stonith_history_t *history, unsigned int mon_ops) { stonith_history_t *hp; for (hp = history; hp; hp = hp->next) { if (hp->state == st_failed) { break; } } if (!hp) { return; } /* Print section heading */ if (state->output_format != mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "Failed Fencing Actions"); } /* Print each failed stonith action */ for (hp = history; hp; hp = hp->next) { if (hp->state == st_failed) { state->out->message(state->out, "stonith-event", hp, mon_ops & mon_op_fence_full_history, history); } } /* End section */ if (state->output_format != mon_output_xml) { state->out->end_list(state->out); } } /*! * \internal * \brief Print pending stonith actions * * \param[in] stream File stream to display output to * \param[in] history List of stonith actions * */ static void print_stonith_pending(mon_state_t *state, stonith_history_t *history, unsigned int mon_ops) { /* xml-output always shows the full history * so we'll never have to show pending-actions * separately */ if (history && (history->state != st_failed) && (history->state != st_done)) { stonith_history_t *hp; /* Print section heading */ if (state->output_format != mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "Pending Fencing Actions"); } history = stonith__sort_history(history); for (hp = history; hp; hp = hp->next) { if ((hp->state == st_failed) || (hp->state == st_done)) { break; } state->out->message(state->out, "stonith-event", hp, mon_ops & mon_op_fence_full_history, NULL); } /* End section */ if (state->output_format != mon_output_xml) { state->out->end_list(state->out); } } } /*! * \internal * \brief Print a section for stonith-history * * \param[in] stream File stream to display output to * \param[in] history List of stonith actions * */ static void print_stonith_history(mon_state_t *state, stonith_history_t *history, unsigned int mon_ops) { stonith_history_t *hp; /* Print section heading */ if (state->output_format == mon_output_xml) { state->out->begin_list(state->out, NULL, NULL, "fence_history"); } else { state->out->begin_list(state->out, NULL, NULL, "Fencing History"); } stonith__sort_history(history); for (hp = history; hp; hp = hp->next) { if ((hp->state != st_failed) || (state->output_format == mon_output_xml)) { state->out->message(state->out, "stonith-event", hp, mon_ops & mon_op_fence_full_history, NULL); } } /* End section */ state->out->end_list(state->out); } void print_status(mon_state_t *state, pe_working_set_t *data_set, stonith_history_t *stonith_history, unsigned int mon_ops, unsigned int show, const char *prefix) { GListPtr gIter = NULL; int print_opts = get_resource_display_options(mon_ops, state->output_format); /* space-separated lists of node names */ char *online_nodes = NULL; char *online_remote_nodes = NULL; char *online_guest_nodes = NULL; char *offline_nodes = NULL; char *offline_remote_nodes = NULL; if (state->output_format == mon_output_console) { blank_screen(); } print_cluster_summary(state, data_set, mon_ops, show); /* Gather node information (and print if in bad state or grouping by node) */ + state->out->begin_list(state->out, NULL, NULL, "Node List"); for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; const char *node_mode = NULL; char *node_name = get_node_display_name(node, mon_ops); /* Get node mode */ if (node->details->unclean) { if (node->details->online) { node_mode = "UNCLEAN (online)"; } else if (node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if (node->details->pending) { node_mode = "pending"; } else if (node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if (node->details->standby) { if (node->details->online) { if (node->details->running_rsc) { node_mode = "standby (with active resources)"; } else { node_mode = "standby"; } } else { node_mode = "OFFLINE (standby)"; } } else if (node->details->maintenance) { if (node->details->online) { node_mode = "maintenance"; } else { node_mode = "OFFLINE (maintenance)"; } } else if (node->details->online) { node_mode = "online"; if (is_not_set(mon_ops, mon_op_group_by_node)) { if (pe__is_guest_node(node)) { online_guest_nodes = add_list_element(online_guest_nodes, node_name); } else if (pe__is_remote_node(node)) { online_remote_nodes = add_list_element(online_remote_nodes, node_name); } else { online_nodes = add_list_element(online_nodes, node_name); } free(node_name); continue; } + } else { node_mode = "OFFLINE"; if (is_not_set(mon_ops, mon_op_group_by_node)) { if (pe__is_remote_node(node)) { offline_remote_nodes = add_list_element(offline_remote_nodes, node_name); } else if (pe__is_guest_node(node)) { /* ignore offline guest nodes */ } else { offline_nodes = add_list_element(offline_nodes, node_name); } free(node_name); continue; } } /* If we get here, node is in bad state, or we're grouping by node */ - - /* Print the node name and status */ - if (pe__is_guest_node(node)) { - print_as(state->output_format, "Guest"); - } else if (pe__is_remote_node(node)) { - print_as(state->output_format, "Remote"); - } - print_as(state->output_format, "Node %s: %s\n", node_name, node_mode); - - /* If we're grouping by node, print its resources */ - if (is_set(mon_ops, mon_op_group_by_node)) { - if (is_set(mon_ops, mon_op_print_brief)) { - print_rscs_brief(node->details->running_rsc, "\t", print_opts | pe_print_rsconly, - state->stream, FALSE); - } else { - GListPtr gIter2 = NULL; - - for (gIter2 = node->details->running_rsc; gIter2 != NULL; gIter2 = gIter2->next) { - resource_t *rsc = (resource_t *) gIter2->data; - - rsc->fns->print(rsc, "\t", print_opts | pe_print_rsconly, state->stream); - } - } - } - free(node_name); + state->out->message(state->out, "node", node, mon_ops, TRUE, node_mode); } /* If we're not grouping by node, summarize nodes by status */ if (online_nodes) { - print_as(state->output_format, "Online: [%s ]\n", online_nodes); + state->out->list_item(state->out, "Online", "[%s ]", online_nodes); free(online_nodes); } if (offline_nodes) { - print_as(state->output_format, "OFFLINE: [%s ]\n", offline_nodes); + state->out->list_item(state->out, "OFFLINE", "[%s ]", offline_nodes); free(offline_nodes); } if (online_remote_nodes) { - print_as(state->output_format, "RemoteOnline: [%s ]\n", online_remote_nodes); + state->out->list_item(state->out, "RemoteOnline", "[%s ]", online_remote_nodes); free(online_remote_nodes); } if (offline_remote_nodes) { - print_as(state->output_format, "RemoteOFFLINE: [%s ]\n", offline_remote_nodes); + state->out->list_item(state->out, "RemoteOFFLINE", "[%s ]", offline_remote_nodes); free(offline_remote_nodes); } if (online_guest_nodes) { - print_as(state->output_format, "GuestOnline: [%s ]\n", online_guest_nodes); + state->out->list_item(state->out, "GuestOnline", "[%s ]", online_guest_nodes); free(online_guest_nodes); } + state->out->end_list(state->out); + /* Print resources section, if needed */ print_resources(state, data_set, print_opts, mon_ops); /* print Node Attributes section if requested */ if (show & mon_show_attributes) { print_node_attributes(state, data_set, mon_ops); } /* If requested, print resource operations (which includes failcounts) * or just failcounts */ if (show & (mon_show_operations | mon_show_failcounts)) { print_node_summary(state, data_set, ((show & mon_show_operations)? TRUE : FALSE), mon_ops); } /* If there were any failed actions, print them */ if (xml_has_children(data_set->failed)) { print_failed_actions(state, data_set); } /* Print failed stonith actions */ if (is_set(mon_ops, mon_op_fence_history)) { print_failed_stonith_actions(state, stonith_history, mon_ops); } /* Print tickets if requested */ if (show & mon_show_tickets) { print_cluster_tickets(state, data_set); } /* Print negative location constraints if requested */ if (show & mon_show_bans) { print_neg_locations(state, data_set, mon_ops, prefix); } /* Print stonith history */ if (is_set(mon_ops, mon_op_fence_history)) { if (show & mon_show_fence_history) { print_stonith_history(state, stonith_history, mon_ops); } else { print_stonith_pending(state, stonith_history, mon_ops); } } #if CURSES_ENABLED if (state->output_format == mon_output_console) { refresh(); } #endif } void print_xml_status(mon_state_t *state, pe_working_set_t *data_set, stonith_history_t *stonith_history, unsigned int mon_ops, unsigned int show, const char *prefix) { GListPtr gIter = NULL; int print_opts = get_resource_display_options(mon_ops, state->output_format); fprintf(state->stream, "\n"); fprintf(state->stream, "\n", VERSION); print_cluster_summary(state, data_set, mon_ops, show); /*** NODES ***/ - fprintf(state->stream, " \n"); + state->out->begin_list(state->out, NULL, NULL, "nodes"); for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; - const char *node_type = "unknown"; - - switch (node->details->type) { - case node_member: - node_type = "member"; - break; - case node_remote: - node_type = "remote"; - break; - case node_ping: - node_type = "ping"; - break; - } - - fprintf(state->stream, " details->uname); - fprintf(state->stream, "id=\"%s\" ", node->details->id); - fprintf(state->stream, "online=\"%s\" ", node->details->online ? "true" : "false"); - fprintf(state->stream, "standby=\"%s\" ", node->details->standby ? "true" : "false"); - fprintf(state->stream, "standby_onfail=\"%s\" ", node->details->standby_onfail ? "true" : "false"); - fprintf(state->stream, "maintenance=\"%s\" ", node->details->maintenance ? "true" : "false"); - fprintf(state->stream, "pending=\"%s\" ", node->details->pending ? "true" : "false"); - fprintf(state->stream, "unclean=\"%s\" ", node->details->unclean ? "true" : "false"); - fprintf(state->stream, "shutdown=\"%s\" ", node->details->shutdown ? "true" : "false"); - fprintf(state->stream, "expected_up=\"%s\" ", node->details->expected_up ? "true" : "false"); - fprintf(state->stream, "is_dc=\"%s\" ", node->details->is_dc ? "true" : "false"); - fprintf(state->stream, "resources_running=\"%d\" ", g_list_length(node->details->running_rsc)); - fprintf(state->stream, "type=\"%s\" ", node_type); - if (pe__is_guest_node(node)) { - fprintf(state->stream, "id_as_resource=\"%s\" ", node->details->remote_rsc->container->id); - } - - if (is_set(mon_ops, mon_op_group_by_node)) { - GListPtr lpc2 = NULL; - - fprintf(state->stream, ">\n"); - for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) { - resource_t *rsc = (resource_t *) lpc2->data; - - rsc->fns->print(rsc, " ", print_opts | pe_print_rsconly, state->stream); - } - fprintf(state->stream, " \n"); - } else { - fprintf(state->stream, "/>\n"); - } + state->out->message(state->out, "node", node, mon_ops, TRUE); } - fprintf(state->stream, " \n"); + state->out->end_list(state->out); /* Print resources section, if needed */ print_resources(state, data_set, print_opts, mon_ops); /* print Node Attributes section if requested */ if (show & mon_show_attributes) { print_node_attributes(state, data_set, mon_ops); } /* If requested, print resource operations (which includes failcounts) * or just failcounts */ if (show & (mon_show_operations | mon_show_failcounts)) { print_node_summary(state, data_set, ((show & mon_show_operations)? TRUE : FALSE), mon_ops); } /* If there were any failed actions, print them */ if (xml_has_children(data_set->failed)) { print_failed_actions(state, data_set); } /* Print stonith history */ if (is_set(mon_ops, mon_op_fence_history)) { print_stonith_history(state, stonith_history, mon_ops); } /* Print tickets if requested */ if (show & mon_show_tickets) { print_cluster_tickets(state, data_set); } /* Print negative location constraints if requested */ if (show & mon_show_bans) { print_neg_locations(state, data_set, mon_ops, prefix); } fprintf(state->stream, "\n"); fflush(state->stream); } int print_html_status(mon_state_t *state, pe_working_set_t *data_set, stonith_history_t *stonith_history, unsigned int mon_ops, unsigned int show, const char *prefix, unsigned int reconnect_msec) { GListPtr gIter = NULL; int print_opts = get_resource_display_options(mon_ops, state->output_format); if (state->output_format == mon_output_cgi) { fprintf(state->stream, "Content-Type: text/html\n\n"); } fprintf(state->stream, "\n"); fprintf(state->stream, " \n"); fprintf(state->stream, " Cluster status\n"); fprintf(state->stream, " \n", reconnect_msec / 1000); fprintf(state->stream, " \n"); fprintf(state->stream, "\n"); print_cluster_summary(state, data_set, mon_ops, show); /*** NODE LIST ***/ - - fprintf(state->stream, "
    \n

    Node List

    \n"); - fprintf(state->stream, "
      \n"); + state->out->begin_list(state->out, NULL, NULL, "Node List"); for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; - char *node_name = get_node_display_name(node, mon_ops); - - fprintf(state->stream, "
    • Node: %s: ", node_name); - if (node->details->standby_onfail && node->details->online) { - fprintf(state->stream, "standby (on-fail)\n"); - } else if (node->details->standby && node->details->online) { - - fprintf(state->stream, "standby%s\n", - node->details->running_rsc?" (with active resources)":""); - } else if (node->details->standby) { - fprintf(state->stream, "OFFLINE (standby)\n"); - } else if (node->details->maintenance && node->details->online) { - fprintf(state->stream, "maintenance\n"); - } else if (node->details->maintenance) { - fprintf(state->stream, "OFFLINE (maintenance)\n"); - } else if (node->details->online) { - fprintf(state->stream, "online\n"); - } else { - fprintf(state->stream, "OFFLINE\n"); - } - if (is_set(mon_ops, mon_op_print_brief) && is_set(mon_ops, mon_op_group_by_node)) { - fprintf(state->stream, "
        \n"); - print_rscs_brief(node->details->running_rsc, NULL, print_opts | pe_print_rsconly, - state->stream, FALSE); - fprintf(state->stream, "
      \n"); - - } else if (is_set(mon_ops, mon_op_group_by_node)) { - GListPtr lpc2 = NULL; - - fprintf(state->stream, "
        \n"); - for (lpc2 = node->details->running_rsc; lpc2 != NULL; lpc2 = lpc2->next) { - resource_t *rsc = (resource_t *) lpc2->data; - - fprintf(state->stream, "
      • "); - rsc->fns->print(rsc, NULL, print_opts | pe_print_rsconly, state->stream); - fprintf(state->stream, "
      • \n"); - } - fprintf(state->stream, "
      \n"); - } - fprintf(state->stream, "
    • \n"); - free(node_name); + state->out->message(state->out, "node", node, mon_ops, TRUE); } - fprintf(state->stream, "
    \n"); + state->out->end_list(state->out); /* Print resources section, if needed */ print_resources(state, data_set, print_opts, mon_ops); /* print Node Attributes section if requested */ if (show & mon_show_attributes) { print_node_attributes(state, data_set, mon_ops); } /* If requested, print resource operations (which includes failcounts) * or just failcounts */ if (show & (mon_show_operations | mon_show_failcounts)) { print_node_summary(state, data_set, ((show & mon_show_operations)? TRUE : FALSE), mon_ops); } /* If there were any failed actions, print them */ if (xml_has_children(data_set->failed)) { print_failed_actions(state, data_set); } /* Print failed stonith actions */ if (is_set(mon_ops, mon_op_fence_history)) { print_failed_stonith_actions(state, stonith_history, mon_ops); } /* Print stonith history */ if (is_set(mon_ops, mon_op_fence_history)) { if (show & mon_show_fence_history) { print_stonith_history(state, stonith_history, mon_ops); } else { print_stonith_pending(state, stonith_history, mon_ops); } } /* Print tickets if requested */ if (show & mon_show_tickets) { print_cluster_tickets(state, data_set); } /* Print negative location constraints if requested */ if (show & mon_show_bans) { print_neg_locations(state, data_set, mon_ops, prefix); } fprintf(state->stream, "\n"); fprintf(state->stream, "\n"); return 0; }