From a5c5caa3ce8ae94a48030e625b1d0f0d7d0d053e Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Wed, 18 Mar 2015 10:06:55 -0400 Subject: [PATCH 1/4] Fix: tools: make crm_mon last updated header consistent across formats This refactors a new function crm_now_string() so that the plain text, XML and HTML output formats use the same logic for displaying the Last Updated header. This fixes potential segmentation faults in the extremely unlikely case of system time functions failing. --- tools/crm_mon.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/tools/crm_mon.c b/tools/crm_mon.c index fd112cbeac7..66210b2f9b3 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1184,6 +1184,28 @@ crm_mon_get_parameters(resource_t *rsc, pe_working_set_t * data_set) } } +/*! + * \internal + * \brief Return human-friendly string representing current time + * + * \return Current time as string (as by ctime() but without newline) on success + * or "Could not determine current time" on error + * \note The return value points to a statically allocated string which might be + * overwritten by subsequent calls to any of the C library date and time functions. + */ +static const char * +crm_now_string(void) +{ + time_t a_time = time(NULL); + char *since_epoch = ctime(&a_time); + + if ((a_time == (time_t) -1) || (since_epoch == NULL)) { + return "Could not determine current time"; + } + since_epoch[strlen(since_epoch) - 1] = EOS; /* trim newline */ + return (since_epoch); +} + static int print_status(pe_working_set_t * data_set) { @@ -1191,7 +1213,6 @@ print_status(pe_working_set_t * data_set) GListPtr gIter = NULL; node_t *dc = NULL; - char *since_epoch = NULL; char *online_nodes = NULL; char *online_remote_nodes = NULL; char *online_remote_containers = NULL; @@ -1201,7 +1222,6 @@ print_status(pe_working_set_t * data_set) xmlNode *dc_version = NULL; xmlNode *quorum_node = NULL; xmlNode *stack = NULL; - time_t a_time = time(NULL); int print_opts = pe_print_ncurses; const char *quorum_votes = "unknown"; @@ -1223,14 +1243,9 @@ print_status(pe_working_set_t * data_set) updates++; dc = data_set->dc_node; - if (a_time == (time_t) - 1) { - crm_perror(LOG_ERR, "set_node_tstamp(): Invalid time returned"); - return 1; - } - since_epoch = ctime(&a_time); - if (since_epoch != NULL && print_last_updated && !hide_headers) { - print_as("Last updated: %s", since_epoch); + if (print_last_updated && !hide_headers) { + print_as("Last updated: %s\n", crm_now_string()); } if (print_last_change && !hide_headers) { @@ -1575,11 +1590,7 @@ print_xml_status(pe_working_set_t * data_set) fprintf(stream, " \n"); if (print_last_updated) { - time_t now = time(NULL); - char *now_str = ctime(&now); - - now_str[24] = EOS; /* replace the newline */ - fprintf(stream, " \n", now_str); + fprintf(stream, " \n", crm_now_string()); } if (print_last_change) { @@ -1812,14 +1823,7 @@ print_html_status(pe_working_set_t * data_set, const char *filename, gboolean we /*** SUMMARY ***/ fprintf(stream, "

Cluster summary

"); - { - char *now_str = NULL; - time_t now = time(NULL); - - now_str = ctime(&now); - now_str[24] = EOS; /* replace the newline */ - fprintf(stream, "Last updated: %s
\n", now_str); - } + fprintf(stream, "Last updated: %s
\n", crm_now_string()); if (dc == NULL) { fprintf(stream, "Current DC: NONE
"); From d5934b96ea518c888010b2ae7a20f4755c1441cf Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Wed, 18 Mar 2015 12:02:31 -0400 Subject: [PATCH 2/4] Fix: tools: Improve crm_mon output with certain option combinations This refactors a new function get_resource_display_options() to use the same logic to set the pe_print_options bitmask for plain, XML and HTML output formats. The only behavioral changes this makes are: * If --show-detail and --as-html are both specified, clone details are now shown. * If --group-by-node, --inactive and --brief are all specified, an inactive group will now correctly be displayed briefly rather than in long format. --- tools/crm_mon.c | 60 +++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 66210b2f9b3..274477aa07c 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1184,6 +1184,41 @@ crm_mon_get_parameters(resource_t *rsc, pe_working_set_t * data_set) } } +/*! + * \internal + * \brief Return resource display options corresponding to command-line choices + * + * \return Bitmask of pe_print_options suitable for resource print functions + */ +static int +get_resource_display_options(void) +{ + int print_opts = as_console? pe_print_ncurses : pe_print_printf; + + /* Determine basic output format */ + if (as_xml) { + print_opts = pe_print_xml; + } else if (as_html_file || web_cgi) { + print_opts = pe_print_html; + } else if (as_console) { + print_opts = pe_print_ncurses; + } else { + print_opts = pe_print_printf; + } + + /* Add optional display elements */ + if (print_pending) { + print_opts |= pe_print_pending; + } + if (print_clone_detail) { + print_opts |= pe_print_clone_details; + } + if (print_brief) { + print_opts |= pe_print_brief; + } + return print_opts; +} + /*! * \internal * \brief Return human-friendly string representing current time @@ -1223,22 +1258,13 @@ print_status(pe_working_set_t * data_set) xmlNode *quorum_node = NULL; xmlNode *stack = NULL; - int print_opts = pe_print_ncurses; + int print_opts = get_resource_display_options(); const char *quorum_votes = "unknown"; if (as_console) { blank_screen(); - } else { - print_opts = pe_print_printf; } - if (print_pending) { - print_opts |= pe_print_pending; - } - - if (print_clone_detail) { - print_opts |= pe_print_clone_details; - } updates++; dc = data_set->dc_node; @@ -1437,7 +1463,6 @@ print_status(pe_working_set_t * data_set) print_as("\n"); if (print_brief && group_by_node == FALSE) { - print_opts |= pe_print_brief; print_rscs_brief(data_set->resources, NULL, print_opts, stdout, inactive_resources); } @@ -1575,14 +1600,10 @@ print_xml_status(pe_working_set_t * data_set) xmlNode *stack = NULL; xmlNode *quorum_node = NULL; const char *quorum_votes = "unknown"; - int print_opts = pe_print_xml; + int print_opts = get_resource_display_options(); dc = data_set->dc_node; - if (print_pending) { - print_opts |= pe_print_pending; - } - fprintf(stream, "\n"); fprintf(stream, "\n", VERSION); @@ -1790,11 +1811,7 @@ print_html_status(pe_working_set_t * data_set, const char *filename, gboolean we node_t *dc = NULL; static int updates = 0; char *filename_tmp = NULL; - int print_opts = pe_print_html; - - if (print_pending) { - print_opts |= pe_print_pending; - } + int print_opts = get_resource_display_options(); if (web_cgi) { stream = stdout; @@ -1923,7 +1940,6 @@ print_html_status(pe_working_set_t * data_set, const char *filename, gboolean we if (group_by_node == FALSE || inactive_resources) { if (print_brief && group_by_node == FALSE) { - print_opts |= pe_print_brief; print_rscs_brief(data_set->resources, NULL, print_opts, stream, inactive_resources); } From 1c761c38ca45a9719bb3cac64c66d5630a7f2223 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Thu, 19 Mar 2015 11:14:06 -0400 Subject: [PATCH 3/4] tools: display node names more consistently in crm_mon output This refactors a new function get_node_display_name() to display a node name (with node ID and/or container ID when appropriate) and modifies the plaintext and HTML outputs to use it when displaying node names. Also, the XML output will now add a container_id attribute to node tags when appropriate. --- tools/crm_mon.c | 143 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 43 deletions(-) diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 274477aa07c..3e050c056ca 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -345,7 +345,7 @@ static struct crm_option long_options[] = { {"neg-locations", 2, 0, 'L', "Display negative location constraints [optionally filtered by id prefix]"}, {"show-node-attributes", 0, 0, 'A', "Display node attributes" }, {"hide-headers", 0, 0, 'D', "\tHide all headers" }, - {"show-detail", 0, 0, 'R', "\tShow more details of cloned resources" }, + {"show-detail", 0, 0, 'R', "\tShow more details (node IDs, individual clone instances)" }, {"brief", 0, 0, 'b', "\t\tBrief output" }, {"pending", 0, 0, 'j', "\t\tDisplay pending state if 'record-pending' is enabled" }, @@ -1149,23 +1149,86 @@ print_cluster_tickets(pe_working_set_t * data_set) return; } +/*! + * \internal + * \brief Return human-friendly string representing node name + * + * The returned string will be in the format + * uname[:containerID] [(nodeID)] + * ":containerID" will be printed if the node is a remote container node. + * "(nodeID)" will be printed if the node ID is different from the node uname, + * and detailed output has been requested. + * + * \param[in] node Node to represent + * \return Newly allocated string with representation of node name + * \note It is the caller's responsibility to free the result with free(). + */ +static char * +get_node_display_name(node_t *node) +{ + char *node_name; + const char *node_container_id = NULL; + const char *node_id = NULL; + int name_len; + + CRM_ASSERT((node != NULL) && (node->details != NULL) && (node->details->uname != NULL)); + + /* Container ID is displayed only if this is a remote container node */ + if (is_container_remote_node(node)) { + node_container_id = node->details->remote_rsc->container->id; + } + + /* Node ID is displayed if different from uname and detail is requested */ + if (print_clone_detail && safe_str_neq(node->details->uname, node->details->id)) { + node_id = node->details->id; + } + + /* Determine name length */ + name_len = strlen(node->details->uname) + 1; + if (node_container_id) { + name_len += strlen(node_container_id) + 1; /* ":node_container_id" */ + } + if (node_id) { + name_len += strlen(node_id) + 3; /* + " (node_id)" */ + } + + /* Allocate and populate display name */ + node_name = malloc(name_len); + CRM_ASSERT(node_name != NULL); + strcpy(node_name, node->details->uname); + if (node_container_id) { + strcat(node_name, ":"); + strcat(node_name, node_container_id); + } + if (node_id) { + strcat(node_name, " ("); + strcat(node_name, node_id); + strcat(node_name, ")"); + } + return node_name; +} + static void print_neg_locations(pe_working_set_t *data_set) { GListPtr gIter, gIter2; - print_as("\nFencing constraints:\n"); + print_as("\nNegative location constraints:\n"); for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) { rsc_to_node_t *location = (rsc_to_node_t *) gIter->data; if (!g_str_has_prefix(location->id, print_neg_location_prefix)) continue; for (gIter2 = location->node_list_rh; gIter2 != NULL; gIter2 = gIter2->next) { node_t *node = (node_t *) gIter2->data; - if (node->weight >= 0) /* != -INFINITY ??? */ - continue; - print_as(" %s\tprevents %s from running %son %s\n", - location->id, location->rsc_lh->id, - location->role_filter == RSC_ROLE_MASTER ? "as Master " : "", - node->details->uname); + + if (node->weight < 0) { + char *node_name = get_node_display_name(node); + + print_as(" %s\tprevents %s from running %son %s\n", + location->id, location->rsc_lh->id, + location->role_filter == RSC_ROLE_MASTER ? "as Master " : "", + node_name); + free(node_name); + } } } } @@ -1307,16 +1370,14 @@ print_status(pe_working_set_t * data_set) print_as("Current DC: NONE\n"); } else if (!hide_headers) { const char *quorum = crm_element_value(data_set->input, XML_ATTR_HAVE_QUORUM); + char *dc_name = get_node_display_name(dc); - if (safe_str_neq(dc->details->uname, dc->details->id)) { - print_as("Current DC: %s (%s)", dc->details->uname, dc->details->id); - } else { - print_as("Current DC: %s", dc->details->uname); - } + print_as("Current DC: %s", dc_name); print_as(" - partition %s quorum\n", crm_is_true(quorum) ? "with" : "WITHOUT"); if (dc_version) { print_as("Version: %s\n", crm_element_value(dc_version, XML_NVPAIR_ATTR_VALUE)); } + free(dc_name); } quorum_node = @@ -1340,13 +1401,7 @@ print_status(pe_working_set_t * data_set) 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 = NULL; - - if (is_container_remote_node(node)) { - node_name = crm_strdup_printf("%s:%s", node->details->uname, node->details->remote_rsc->container->id); - } else { - node_name = crm_strdup_printf("%s", node->details->uname); - } + char *node_name = get_node_display_name(node); if (node->details->unclean) { if (node->details->online && node->details->unclean) { @@ -1406,14 +1461,11 @@ print_status(pe_working_set_t * data_set) } if (is_container_remote_node(node)) { - print_as("ContainerNode %s: %s\n", node_name, node_mode); + print_as("Container"); } else if (is_baremetal_remote_node(node)) { - print_as("RemoteNode %s: %s\n", node_name, node_mode); - } else if (safe_str_eq(node->details->uname, node->details->id)) { - print_as("Node %s: %s\n", node_name, node_mode); - } else { - print_as("Node %s (%s): %s\n", node_name, node->details->id, node_mode); + print_as("Remote"); } + print_as("Node %s: %s\n", node_name, node_mode); if (print_brief && group_by_node) { print_rscs_brief(node->details->running_rsc, "\t", print_opts | pe_print_rsconly, @@ -1501,11 +1553,16 @@ print_status(pe_working_set_t * data_set) for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; + char *node_name; if (node == NULL || node->details->online == FALSE) { continue; } - print_as("* Node %s:\n", node->details->uname); + + node_name = get_node_display_name(node); + print_as("* Node %s:\n", node_name); + free(node_name); + g_hash_table_foreach(node->details->attrs, create_attr_list, NULL); g_list_foreach(attr_list, print_node_attribute, node); g_list_free(attr_list); @@ -1693,6 +1750,9 @@ print_xml_status(pe_working_set_t * data_set) fprintf(stream, "is_dc=\"%s\" ", node->details->is_dc ? "true" : "false"); fprintf(stream, "resources_running=\"%d\" ", g_list_length(node->details->running_rsc)); fprintf(stream, "type=\"%s\" ", node_type); + if (is_container_remote_node(node)) { + fprintf(stream, "container_id=\"%s\" ", node->details->remote_rsc->container->id); + } if (group_by_node) { GListPtr lpc2 = NULL; @@ -1845,7 +1905,10 @@ print_html_status(pe_working_set_t * data_set, const char *filename, gboolean we if (dc == NULL) { fprintf(stream, "Current DC: NONE
"); } else { - fprintf(stream, "Current DC: %s (%s)
", dc->details->uname, dc->details->id); + char *dc_name = get_node_display_name(dc); + + fprintf(stream, "Current DC: %s
\n", dc_name); + free(dc_name); } fprintf(stream, "%d Nodes configured.
", g_list_length(data_set->nodes)); fprintf(stream, "%d Resources configured.
", count_resources(data_set, NULL)); @@ -1884,29 +1947,23 @@ print_html_status(pe_working_set_t * data_set, const char *filename, gboolean we fprintf(stream, "
    \n"); 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); - fprintf(stream, "
  • "); + fprintf(stream, "
  • Node: %s: ", node_name); if (node->details->standby_onfail && node->details->online) { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "standby (on-fail)\n"); + fprintf(stream, "standby (on-fail)\n"); } else if (node->details->standby && node->details->online) { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "standby\n"); + fprintf(stream, "standby\n"); } else if (node->details->standby) { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "OFFLINE (standby)\n"); + fprintf(stream, "OFFLINE (standby)\n"); } else if (node->details->maintenance && node->details->online) { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "maintenance\n"); + fprintf(stream, "maintenance\n"); } else if (node->details->maintenance) { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "OFFLINE (maintenance)\n"); + fprintf(stream, "OFFLINE (maintenance)\n"); } else if (node->details->online) { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "online\n"); + fprintf(stream, "online\n"); } else { - fprintf(stream, "Node: %s (%s): %s", node->details->uname, node->details->id, - "OFFLINE\n"); + fprintf(stream, "OFFLINE\n"); } if (print_brief && group_by_node) { fprintf(stream, "
      \n"); From b2ae244cafeef69e2e4e1675cfe1f3fa45852867 Mon Sep 17 00:00:00 2001 From: Ken Gaillot Date: Thu, 19 Mar 2015 16:04:20 -0400 Subject: [PATCH 4/4] tools: crm_mon prints Stopped clones only if --inactive was specified This introduces a new pe_print_options flag pe_print_clone_active; if set, the print method for clone resources will print only instances that are active. crm_mon sets this new flag unless the --inactive command-line option was used. --- include/crm/pengine/common.h | 1 + lib/pengine/clone.c | 48 +++++++++++++++++++----------------- tools/crm_mon.c | 3 +++ 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/include/crm/pengine/common.h b/include/crm/pengine/common.h index 0d853c0b97f..cc9347a7aa1 100644 --- a/include/crm/pengine/common.h +++ b/include/crm/pengine/common.h @@ -111,6 +111,7 @@ enum pe_print_options { pe_print_brief = 0x0800, pe_print_pending = 0x1000, pe_print_clone_details = 0x2000, + pe_print_clone_active = 0x4000, /* print clone instances only if active */ }; /* *INDENT-ON* */ diff --git a/lib/pengine/clone.c b/lib/pengine/clone.c index ea1a9b23ea2..6213694a7bf 100644 --- a/lib/pengine/clone.c +++ b/lib/pengine/clone.c @@ -426,7 +426,8 @@ clone_print(resource_t * rsc, const char *pre_text, long options, void *print_da } else if (is_set(rsc->flags, pe_rsc_unique)) { print_full = TRUE; - } else { + + } else if (is_not_set(options, pe_print_clone_active)) { stopped_list = add_list_element(stopped_list, child_rsc->id); } @@ -508,37 +509,38 @@ clone_print(resource_t * rsc, const char *pre_text, long options, void *print_da free(list_text); list_text = NULL; - /* Stopped */ - if(is_not_set(rsc->flags, pe_rsc_unique) - && (clone_data->clone_max > active_instances)) { + if (is_not_set(options, pe_print_clone_active)) { + /* Stopped */ + if (is_not_set(rsc->flags, pe_rsc_unique) + && (clone_data->clone_max > active_instances)) { - GListPtr nIter; - GListPtr list = g_hash_table_get_values(rsc->allowed_nodes); + GListPtr nIter; + GListPtr list = g_hash_table_get_values(rsc->allowed_nodes); - /* Custom stopped list for non-unique clones */ - free(stopped_list); stopped_list = NULL; + /* Custom stopped list for non-unique clones */ + free(stopped_list); stopped_list = NULL; - if(g_list_length(list) == 0) { - /* Clusters with symmetrical=false haven't calculated allowed_nodes yet - * If we've not probed for them yet, the Stopped list will be empty - */ - list = g_hash_table_get_values(rsc->known_on); - } + if (g_list_length(list) == 0) { + /* Clusters with symmetrical=false haven't calculated allowed_nodes yet + * If we've not probed for them yet, the Stopped list will be empty + */ + list = g_hash_table_get_values(rsc->known_on); + } - list = g_list_sort(list, sort_node_uname); - for (nIter = list; nIter != NULL; nIter = nIter->next) { - node_t *node = (node_t *)nIter->data; + list = g_list_sort(list, sort_node_uname); + for (nIter = list; nIter != NULL; nIter = nIter->next) { + node_t *node = (node_t *)nIter->data; - if(pe_find_node(rsc->running_on, node->details->uname) == NULL) { - stopped_list = add_list_element(stopped_list, node->details->uname); + if (pe_find_node(rsc->running_on, node->details->uname) == NULL) { + stopped_list = add_list_element(stopped_list, node->details->uname); + } } + g_list_free(list); } - g_list_free(list); + short_print(stopped_list, child_text, "Stopped", options, print_data); + free(stopped_list); } - short_print(stopped_list, child_text, "Stopped", options, print_data); - free(stopped_list); - if (options & pe_print_html) { status_print("
    \n"); } diff --git a/tools/crm_mon.c b/tools/crm_mon.c index 3e050c056ca..52343a28015 100644 --- a/tools/crm_mon.c +++ b/tools/crm_mon.c @@ -1276,6 +1276,9 @@ get_resource_display_options(void) if (print_clone_detail) { print_opts |= pe_print_clone_details; } + if (!inactive_resources) { + print_opts |= pe_print_clone_active; + } if (print_brief) { print_opts |= pe_print_brief; }