Code

collectdctl: Base the "show" implementation on the new LISTVAL syntax.
authorFlorian Forster <octo@collectd.org>
Thu, 17 Mar 2011 15:09:28 +0000 (16:09 +0100)
committerFlorian Forster <octo@huhu.verplant.org>
Thu, 17 Mar 2011 15:17:53 +0000 (16:17 +0100)
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
src/collectdctl.pod

index befe3276b606e245735fb96b8bae119e5abe4b3a..2895dc86ff51053429bae5539c20e2372ac32c37 100644 (file)
@@ -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 <selector> <aggregation> "
-          "[<aggregation> ...]\n"
+  printf ("Usage: collectdctl show [<Selection>] [<Aggregation> <Grouping>]\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>                      Regex for the host name.\n"
+          "  plugin=<regex>                    Regex for the plugin.\n"
+          "  plugin_instance=<regex>           Regex for the plugin instance.\n"
+          "  type=<regex>                      Regex for the type.\n"
+          "  type_instance=<regex>             Regex for the type instance.\n"
           "\n"
           "Aggregation:\n"
-          "  count\n"
-          "  min\n"
-          "  max\n"
-          "  avg\n"
+          "\n"
+          "  aggregate=<aggr>[,<aggr>[...]]    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=<field>[,<field>[...]]      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)
index 1933c80c90bdfa67c0700553a92c40dabacd7519..4c5c357ca5a3df1501679527bec528b1f3ee7f86 100644 (file)
@@ -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<types.db(5)> for details).
 
-=item B<show> I<E<lt>selectorE<gt>> I<E<lt>aggregationE<gt>> [I<E<lt>aggregationE<gt>> ...]
+=item B<show> [I<E<lt>SelectionE<gt>>] [I<E<lt>AggregationE<gt>> I<E<lt>GroupingE<gt>>]
 
-Show values or an aggregation of values. The I<selector> selects which values
-to show. It is basically an I<identifier> 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<Selection> selects which values
+to show. The selection consists of the five options B<host>, B<plugin>,
+B<plugin_instance>, B<type> and B<type_instance> which take a regular
+expression each. The regular expressions are passed on to the C<LISTVAL>
+command so they will behave exactly as documented in L<collectd-unixsock(5)>.
 
-  +/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</"IDENTIFIERS"> for a description of identifiers.
+    collectdctl show plugin="^cpu$" type="^cpu$"
 
-The I<aggregation> 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<Aggregation> and I<Grouping> options to get an overview over your
+system(s) and such. The two options to this effect are:
+
+=over 4
+
+=item B<aggregate=>I<aggr>[B<,>I<aggr>[...]]
+
+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 B<group=>I<field>[B<,>I<field>[...]]
+
+Chose the fields of the I<identifier> you want to group values by. Valid
+I<fields> are B<host>, B<plugin>, B<plugin_instance>, B<type> and
+B<type_instance>. In 99E<nbsp>% of all cases, you want to make sure all values
+have the same I<type> -- either by using a construct like C<type='^foo$'>
+or by adding B<type> to the B<group> option (e.g. C<group=type>).
+
+=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<types.db(5)>
 
 =head1 AUTHOR
 
-collectd has been written by Florian Forster E<lt>octo at verplant.orgE<gt>
+collectd has been written by Florian Forster E<lt>octo at collectd.orgE<gt>
 and many contributors (see `AUTHORS').
 
 collectdctl has been written by
-Håkon J Dugstad Johnsen E<lt>hakon-dugstad.johnsenE<nbsp>atE<nbsp>telenor.comE<gt>
-and Sebastian Harl E<lt>sh at tokkee.orgE<gt>.
+Håkon J Dugstad Johnsen E<lt>hakon-dugstad.johnsenE<nbsp>atE<nbsp>telenor.comE<gt>,
+Sebastian Harl E<lt>sh at tokkee.orgE<gt> and
+Florian Forster E<lt>octo at collectd.orgE<gt>.
 
 =cut