From 07e526e0f76c284276f2df78e53a9d8fa17a6e98 Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Thu, 17 Mar 2011 16:09:28 +0100 Subject: [PATCH] collectdctl: Base the "show" implementation on the new LISTVAL syntax. Pass the selector on to collectd to have it filter the values it returns. Presumably this is much more efficient than filtering in the client (i.e. collectdctl). --- src/collectdctl-show.c | 314 ++++++++++++++++++++++++++++------------- src/collectdctl.pod | 52 +++++-- 2 files changed, 253 insertions(+), 113 deletions(-) diff --git a/src/collectdctl-show.c b/src/collectdctl-show.c index befe3276..2895dc86 100644 --- a/src/collectdctl-show.c +++ b/src/collectdctl-show.c @@ -90,8 +90,17 @@ typedef struct aggregation_group_s aggregation_group_t; /* * Global variables */ -static lcc_identifier_t *selector; +/* Selection */ +static const char *re_host = NULL; +static const char *re_plugin = NULL; +static const char *re_plugin_instance = NULL; +static const char *re_type = NULL; +static const char *re_type_instance = NULL; +/* Grouping */ +static uint16_t grouping = 0; + +/* Aggregation */ static int *aggregation_types = NULL; static size_t aggregation_types_num = 0; @@ -176,98 +185,49 @@ static int aggregation_type_add (const char *str_type) /* {{{ */ return (0); } /* }}} int aggregation_type_add */ -static int group_name_from_ident (const lcc_identifier_t *selector, /* {{{ */ - const lcc_identifier_t *identifier, +static int group_name_from_ident (const lcc_identifier_t *identifier, /* {{{ */ char *buffer, size_t buffer_size) { - if ((selector == NULL) - || (identifier == NULL) + if ((identifier == NULL) || (buffer == NULL) || (buffer_size < 2)) return (EINVAL); - /* Check if there is no "grouping" wildcard. If there isn't, return "all" as - * the default value. */ - if ((strcmp ("+", selector->host) != 0) - && (strcmp ("+", selector->plugin) != 0) - && (strcmp ("+", selector->plugin_instance) != 0) - && (strcmp ("+", selector->type) != 0) - && (strcmp ("+", selector->type_instance) != 0)) + if (grouping == 0) { - /* There is no wildcard at all => use the identifier. */ - if ((strcmp ("*", selector->host) != 0) - && (strcmp ("*", selector->plugin) != 0) - && (strcmp ("*", selector->plugin_instance) != 0) - && (strcmp ("*", selector->type) != 0) - && (strcmp ("*", selector->type_instance) != 0)) - lcc_identifier_to_string (/* connection = */ NULL, - buffer, buffer_size, identifier); - else /* there's wildcards but no grouping */ - strncpy (buffer, "all", buffer_size); + lcc_identifier_to_string (/* connection = */ NULL, + buffer, buffer_size, identifier); buffer[buffer_size - 1] = 0; return (0); } memset (buffer, 0, buffer_size); -#define COPY_FIELD(field) do { \ - if (strcmp ("+", selector->field) != 0) \ - break; \ - if (buffer[0] == 0) \ - strncpy (buffer, identifier->field, buffer_size); \ - else \ +#define COPY_FIELD(field,index) do { \ + if ((grouping & (1 << index)) != 0) \ { \ - char tmp[buffer_size]; \ - snprintf (tmp, buffer_size, "%s/%s", buffer, identifier->field); \ - memcpy (buffer, tmp, buffer_size); \ + if (buffer[0] == 0) \ + strncpy (buffer, identifier->field, buffer_size); \ + else \ + { \ + char tmp[buffer_size]; \ + snprintf (tmp, buffer_size, "%s/%s", buffer, identifier->field); \ + memcpy (buffer, tmp, buffer_size); \ + } \ + buffer[buffer_size - 1] = 0; \ } \ - buffer[buffer_size - 1] = 0; \ } while (0) - COPY_FIELD (host); - COPY_FIELD (plugin); - COPY_FIELD (plugin_instance); - COPY_FIELD (type); - COPY_FIELD (type_instance); + COPY_FIELD (host, 0); + COPY_FIELD (plugin, 1); + COPY_FIELD (plugin_instance, 2); + COPY_FIELD (type, 3); + COPY_FIELD (type_instance, 4); #undef COPY_FIELD return (0); } /* }}} int group_name_from_ident */ -static _Bool ident_matches_selector (const lcc_identifier_t *selector, /* {{{ */ - const lcc_identifier_t *identifier) -{ - if ((selector == NULL) || (identifier == NULL)) - return (0); - - if ((strcmp (identifier->host, selector->host) != 0) - && (strcmp ("*", selector->host) != 0) - && (strcmp ("+", selector->host) != 0)) - return (0); - - if ((strcmp (identifier->plugin, selector->plugin) != 0) - && (strcmp ("*", selector->plugin) != 0) - && (strcmp ("+", selector->plugin) != 0)) - return (0); - - if ((strcmp (identifier->plugin_instance, selector->plugin_instance) != 0) - && (strcmp ("*", selector->plugin_instance) != 0) - && (strcmp ("+", selector->plugin_instance) != 0)) - return (0); - - if ((strcmp (identifier->type, selector->type) != 0) - && (strcmp ("*", selector->type) != 0) - && (strcmp ("+", selector->type) != 0)) - return (0); - - if ((strcmp (identifier->type_instance, selector->type_instance) != 0) - && (strcmp ("*", selector->type_instance) != 0) - && (strcmp ("+", selector->type_instance) != 0)) - return (0); - - return (1); -} /* }}} _Bool ident_matches_selector */ - static aggregation_group_t *aggregation_get_group ( const lcc_identifier_t *identifier) /* {{{ */ { char group_name[LCC_NAME_LEN]; @@ -278,7 +238,7 @@ static aggregation_group_t *aggregation_get_group ( const lcc_identifier_t *iden if (identifier == NULL) return (NULL); - status = group_name_from_ident (selector, identifier, + status = group_name_from_ident (identifier, group_name, sizeof (group_name)); if (status != 0) return (NULL); @@ -362,10 +322,17 @@ static int read_data (lcc_connection_t *c) /* {{{ */ int status; size_t i; - status = lcc_listval (c, &ret_ident, &ret_ident_num); + status = lcc_listval_with_selection (c, + re_host, + re_plugin, + re_plugin_instance, + re_type, + re_type_instance, + &ret_ident, &ret_ident_num); if (status != 0) { - fprintf (stderr, "ERROR: lcc_listval: %s\n", lcc_strerror (c)); + fprintf (stderr, "ERROR: lcc_listval_with_selection: %s\n", + lcc_strerror (c)); return (-1); } assert ((ret_ident != NULL) || (ret_ident_num == 0)); @@ -377,9 +344,6 @@ static int read_data (lcc_connection_t *c) /* {{{ */ size_t ret_values_num = 0; gauge_t *ret_values = NULL; - if (!ident_matches_selector (selector, ret_ident + i)) - continue; - status = lcc_getval (c, ret_ident + i, &ret_values_num, &ret_values, /* values_names = */ NULL); if (status != 0) @@ -414,6 +378,8 @@ static int print_horizontal_line (int name_len_max) /* {{{ */ for (j = 0; j < aggregation_types_num; j++) printf ("------------+"); + if (aggregation_types_num == 0) + printf ("------------+"); printf ("\n"); @@ -436,6 +402,8 @@ static int write_data (void) /* {{{ */ printf ("! %-*s !", name_len_max, "Name"); for (i = 0; i < aggregation_types_num; i++) printf (" %10s !", aggr_type_to_string (aggregation_types[i])); + if (aggregation_types_num == 0) + printf (" %10s !", "Value"); printf ("\n"); print_horizontal_line (name_len_max); @@ -478,6 +446,12 @@ static int write_data (void) /* {{{ */ printf (" %10g !", value); } + if (aggregation_types_num == 0) + { + /* g->num may be zero if the value is NAN. */ + assert (g->num < 2); + printf (" %10g !", g->min); + } printf ("\n"); } @@ -490,41 +464,185 @@ static int write_data (void) /* {{{ */ __attribute__((noreturn)) static void exit_usage (int status) /* {{{ */ { - printf ("Usage: collectdctl show " - "[ ...]\n" + printf ("Usage: collectdctl show [] [ ]\n" + "\n" + "Selection:\n" "\n" - "Selector:\n" - " A selector is an identifier, where each part may be replaced " - "with either\n" - " \"*\" or \"+\".\n" + " host= Regex for the host name.\n" + " plugin= Regex for the plugin.\n" + " plugin_instance= Regex for the plugin instance.\n" + " type= Regex for the type.\n" + " type_instance= Regex for the type instance.\n" "\n" "Aggregation:\n" - " count\n" - " min\n" - " max\n" - " avg\n" + "\n" + " aggregate=[,[...]] List of aggregations to use when\n" + " combining multiple values.\n" + " Valid aggregations are:\n" + " count, min, max, avg, sum, stddev\n" + "\n" + "Grouping:\n" + "\n" + " group=[,[...]] List of fields to group by.\n" + " Valid fields are:\n" + " host, plugin, plugin_instance,\n" + " type, type_instance\n" "\n"); exit (status); } /* }}} void exit_usage */ +static int parse_aggregate (const char *aggr) /* {{{ */ +{ + char *aggr_copy; + char *dummy; + char *a; + + aggr_copy = strdup (aggr); + if (aggr_copy == NULL) + return (ENOMEM); + + free (aggregation_types); + aggregation_types = NULL; + aggregation_types_num = 0; + + dummy = aggr_copy; + while ((a = strtok (dummy, ",")) != NULL) + { + int status; + + dummy = NULL; + + status = aggregation_type_add (a); + if (status != 0) + exit_usage (EXIT_FAILURE); + } /* while (strtok) */ + + free (aggr_copy); + + return (0); +} /* }}} int parse_group */ + +static int parse_group (const char *group) /* {{{ */ +{ + char *group_copy; + char *dummy; + char *g; + + group_copy = strdup (group); + if (group_copy == NULL) + return (ENOMEM); + + grouping = 0; + + dummy = group_copy; + while ((g = strtok (dummy, ",")) != NULL) + { + int pos = 0; + + dummy = NULL; + + if (strcasecmp ("host", g) == 0) + pos = 0; + else if (strcasecmp ("plugin", g) == 0) + pos = 1; + else if ((strcasecmp ("plugin_instance", g) == 0) + || (strcasecmp ("plugininstance", g) == 0) + || (strcasecmp ("pinst", g) == 0)) + pos = 2; + else if (strcasecmp ("type", g) == 0) + pos = 3; + else if ((strcasecmp ("type_instance", g) == 0) + || (strcasecmp ("typeinstance", g) == 0) + || (strcasecmp ("tinst", g) == 0)) + pos = 4; + else + { + fprintf (stderr, "Unknown grouping field: \"%s\"\n", g); + exit_usage (EXIT_FAILURE); + } + + grouping |= 1 << pos; + } /* while (strtok) */ + + free (group_copy); + + return (0); +} /* }}} int parse_group */ + +static int parse_arg (const char *arg) /* {{{ */ +{ + if (arg == NULL) + return (EINVAL); + else if (strncasecmp ("host=", arg, strlen ("host=")) == 0) + re_host = arg + strlen ("host="); + else if (strncasecmp ("plugin=", arg, strlen ("plugin=")) == 0) + re_plugin = arg + strlen ("plugin="); + else if (strncasecmp ("plugin_instance=", arg, strlen ("plugin_instance=")) == 0) + re_plugin_instance = arg + strlen ("plugin_instance="); + else if (strncasecmp ("type=", arg, strlen ("type=")) == 0) + re_type = arg + strlen ("type="); + else if (strncasecmp ("type_instance=", arg, strlen ("type_instance=")) == 0) + re_type_instance = arg + strlen ("type_instance="); + + /* Grouping */ + else if (strncasecmp ("group=", arg, strlen ("group=")) == 0) + return (parse_group (arg + strlen ("group="))); + + /* Aggregations */ + else if (strncasecmp ("aggregate=", arg, strlen ("aggregate=")) == 0) + return (parse_aggregate (arg + strlen ("aggregate="))); + + /* Some alternative spellings to make it easier to guess a working argument + * name: */ + else if (strncasecmp ("hostname=", arg, strlen ("hostname=")) == 0) + re_host = arg + strlen ("hostname="); + else if (strncasecmp ("plugininstance=", arg, strlen ("plugininstance=")) == 0) + re_plugin_instance = arg + strlen ("plugininstance="); + else if (strncasecmp ("typeinstance=", arg, strlen ("typeinstance=")) == 0) + re_type_instance = arg + strlen ("typeinstance="); + else if (strncasecmp ("pinst=", arg, strlen ("pinst=")) == 0) + re_plugin_instance = arg + strlen ("pinst="); + else if (strncasecmp ("tinst=", arg, strlen ("tinst=")) == 0) + re_type_instance = arg + strlen ("tinst="); + else if (strncasecmp ("aggr=", arg, strlen ("aggr=")) == 0) + return (parse_aggregate (arg + strlen ("aggr="))); + + /* Don't know what that is ... */ + else + { + fprintf (stderr, "Unknown argument: \"%s\"\n", arg); + exit_usage (EXIT_FAILURE); + } + + return (0); +} /* }}} int parse_arg */ + int show (lcc_connection_t *c, int argc, char **argv) /* {{{ */ { - lcc_identifier_t tmp; int status; int i; size_t j; - if (argc < 3) - exit_usage (EXIT_FAILURE); - - memset (&tmp, 0, sizeof (tmp)); - status = lcc_string_to_identifier (c, &tmp, argv[1]); - if (status != 0) - return (status); - selector = &tmp; + for (i = 1; i < argc; i++) + { + status = parse_arg (argv[i]); + /* parse_arg calls exit_usage() on error. */ + assert (status == 0); + } - for (i = 2; i < argc; i++) - aggregation_type_add (argv[i]); + if ((grouping == 0) && (aggregation_types_num > 0)) + { + fprintf (stderr, "One or more aggregations were specified, but no fields " + "were selected for grouping values. Please use the ""\"group=...\" " + "option.\n"); + exit_usage (EXIT_FAILURE); + } + else if ((grouping != 0) && (aggregation_types_num == 0)) + { + fprintf (stderr, "One or more fields were specified for grouping but no " + "aggregation was given. Please use the \"aggregate=...\" option.\n"); + exit_usage (EXIT_FAILURE); + } status = read_data (c); if (status != 0) diff --git a/src/collectdctl.pod b/src/collectdctl.pod index 1933c80c..4c5c357c 100644 --- a/src/collectdctl.pod +++ b/src/collectdctl.pod @@ -95,22 +95,28 @@ Each of them will be submitted to the daemon. The values have to match the data-set definition specified by the type as given in the identifier (see L for details). -=item B IselectorE> IaggregationE> [IaggregationE> ...] +=item B [ISelectionE>] [IAggregationE> IGroupingE>] -Show values or an aggregation of values. The I selects which values -to show. It is basically an I with special meaning for C<+> and -C<*>. If a hostname, plugin, type or one of the two instances is C<+> or C<*>, -any string will match the selector. The difference between the two is that all -identifiers with the same substitution for C<+> are grouped and aggregated by -the specified aggregation function(s). For example, the selector +Show values or an aggregation of values. The I selects which values +to show. The selection consists of the five options B, B, +B, B and B which take a regular +expression each. The regular expressions are passed on to the C +command so they will behave exactly as documented in L. - +/cpu-*/cpu-+ +Example: Show CPU statistics only. -will return the CPU states of each host, aggregated over all CPUs of each -system. Please see L for a description of identifiers. + collectdctl show plugin="^cpu$" type="^cpu$" -The I setting defines how multiple values are combined into one -value. Valid values are: +If you're not interested in single values, but aggregations of values, you can +use the I and I options to get an overview over your +system(s) and such. The two options to this effect are: + +=over 4 + +=item BI[B<,>I[...]] + +List all the aggregation functions that shall be used to combine multiple +values. Available aggregation functions are: =over 4 @@ -143,6 +149,21 @@ exactly one non-NAN value. =back +=item BI[B<,>I[...]] + +Chose the fields of the I you want to group values by. Valid +I are B, B, B, B and +B. In 99E% of all cases, you want to make sure all values +have the same I -- either by using a construct like C +or by adding B to the B option (e.g. C). + +=back + +Example: Print the minimum, average and maximum time spent in each CPU state +for all your web servers: + + collectdctl show host="^www[0-9]\\." plugin="^cpu$" type="^cpu$" aggregate=min,avg,max group=type_instance + =back =head1 IDENTIFIERS @@ -198,11 +219,12 @@ L =head1 AUTHOR -collectd has been written by Florian Forster Eocto at verplant.orgE +collectd has been written by Florian Forster Eocto at collectd.orgE and many contributors (see `AUTHORS'). collectdctl has been written by -Håkon J Dugstad Johnsen Ehakon-dugstad.johnsenEatEtelenor.comE -and Sebastian Harl Esh at tokkee.orgE. +Håkon J Dugstad Johnsen Ehakon-dugstad.johnsenEatEtelenor.comE, +Sebastian Harl Esh at tokkee.orgE and +Florian Forster Eocto at collectd.orgE. =cut -- 2.30.2