ethstat plugin: Collect performance statistics from NICs.
authorCyril Feraudet <collectd@feraudet.com>
Tue, 4 Oct 2011 13:00:04 +0000 (15:00 +0200)
committerFlorian Forster <octo@collectd.org>
Sun, 11 Mar 2012 14:36:35 +0000 (15:36 +0100)
Hi all,

For my needs at work I wrote a plugin for collectd and I wish to share it with
you.

This plugins, temporary called ethstat and wrote in C, collect lot of
performance counter about network interface as "ethtool -S ethX" do by asking
NIC driver module.

At the moment this plugin work only under Linux. Any help would be great for
make it working under Solaris, AIX and co ...

If you think this plugin may be included to collectd sources, what is the
procedure to do it ?

Regards,
Cyril Feraudet

To be added to configuration file to make it work :

LoadPlugin ethstat

<Plugin ethstat>
 Iface "eth0"
 #Iface "eth1"
 #Iface "eth2"
 #Iface "eth3"
</Plugin>

Signed-off-by: Florian Forster <octo@collectd.org>
configure.in
src/Makefile.am
src/ethstat.c [new file with mode: 0644]
src/ethstat.h [new file with mode: 0644]

index 0a0a8f8..0d992e1 100644 (file)
@@ -4745,6 +4745,7 @@ AC_PLUGIN([disk],        [$plugin_disk],       [Disk usage statistics])
 AC_PLUGIN([dns],         [$with_libpcap],      [DNS traffic analysis])
 AC_PLUGIN([email],       [yes],                [EMail statistics])
 AC_PLUGIN([entropy],     [$plugin_entropy],    [Entropy statistics])
+AC_PLUGIN([ethstat],     [yes],                [Stats from NIC driver])
 AC_PLUGIN([exec],        [yes],                [Execution of external programs])
 AC_PLUGIN([filecount],   [yes],                [Count files in directories])
 AC_PLUGIN([fscache],     [$plugin_fscache],    [fscache statistics])
@@ -5076,6 +5077,7 @@ Configuration:
     dns . . . . . . . . . $enable_dns
     email . . . . . . . . $enable_email
     entropy . . . . . . . $enable_entropy
+    ethstat . . . . . . . $enable_ethstat
     exec  . . . . . . . . $enable_exec
     filecount . . . . . . $enable_filecount
     fscache . . . . . . . $enable_fscache
index 05f94d9..5c2eb35 100644 (file)
@@ -378,6 +378,14 @@ collectd_LDADD += "-dlopen" exec.la
 collectd_DEPENDENCIES += exec.la
 endif
 
+if BUILD_PLUGIN_ETHSTAT
+pkglib_LTLIBRARIES += ethstat.la
+ethstat_la_SOURCES = ethstat.c
+ethstat_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" ethstat.la
+collectd_DEPENDENCIES += ethstat.la
+endif
+
 if BUILD_PLUGIN_FILECOUNT
 pkglib_LTLIBRARIES += filecount.la
 filecount_la_SOURCES = filecount.c
diff --git a/src/ethstat.c b/src/ethstat.c
new file mode 100644 (file)
index 0000000..fbe52c2
--- /dev/null
@@ -0,0 +1,178 @@
+/**
+ * collectd - src/ethstat.c
+ * Copyright (C) 2011       Cyril Feraudet
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Cyril Feraudet <cyril at feraudet.com>
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include <sys/types.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <net/if.h>
+#include <linux/sockios.h> 
+#include "ethstat.h"
+
+# if KERNEL_LINUX
+
+
+static int ethstat_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "Iface") == 0) {
+               ifacelist[ifacenumber] = malloc(strlen(value) + 1);
+               strcpy(ifacelist[ifacenumber++], value);
+               INFO("ethstat: Registred iface %s", value);
+       }
+       return (0);
+}
+
+
+static void ethstat_submit_value (char *devname, char *counter, unsigned long long value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "ethstat", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, devname, sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "derive", sizeof (vl.type));
+       sstrncpy (vl.type_instance, counter, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
+
+static int getstats(char *devname, struct ifreq *ifr) {
+        int fd;
+       struct ethtool_drvinfo drvinfo;
+       struct ethtool_gstrings *strings;
+       struct ethtool_stats *stats;
+       unsigned int n_stats, sz_str, sz_stats, i;
+       int err;
+
+
+       fd = socket(AF_INET, SOCK_DGRAM, 0);
+        if (fd < 0) {
+                ERROR("ethstat - %s : Cannot get control socket", devname);
+                return 1;
+        }
+
+        drvinfo.cmd = ETHTOOL_GDRVINFO;
+        ifr->ifr_data = (caddr_t)&drvinfo;
+        err = ioctl(fd, SIOCETHTOOL, ifr);
+        if (err < 0) {
+                ERROR("ethstat - %s : Cannot get driver information", devname);
+                return 1;
+        }
+
+
+        n_stats = drvinfo.n_stats;
+        if (n_stats < 1) {
+                ERROR("ethstat - %s : No stats available", devname);
+                return 1;
+        }
+
+        sz_str = n_stats * ETH_GSTRING_LEN;
+        sz_stats = n_stats * sizeof(u64);
+
+        strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings));
+        stats = calloc(1, sz_stats + sizeof(struct ethtool_stats));
+        if (!strings || !stats) {
+                ERROR("ethstat - %s No memory available", devname);
+                return 1;
+        }
+
+        strings->cmd = ETHTOOL_GSTRINGS;
+        strings->string_set = ETH_SS_STATS;
+        strings->len = n_stats;
+        ifr->ifr_data = (caddr_t) strings;
+        err = ioctl(fd, SIOCETHTOOL, ifr);
+        if (err < 0) {
+                ERROR("ethstat - %s : Cannot get stats strings information", devname);
+                free(strings);
+                free(stats);
+                return 96;
+        }
+
+        stats->cmd = ETHTOOL_GSTATS;
+        stats->n_stats = n_stats;
+        ifr->ifr_data = (caddr_t) stats;
+        err = ioctl(fd, SIOCETHTOOL, ifr);
+        if (err < 0) {
+                ERROR("ethstat - %s : Cannot get stats information", devname);
+                free(strings);
+                free(stats);
+                return 97;
+        }
+
+        for (i = 0; i < n_stats; i++) {
+                DEBUG("ethstat - %s : %s: %llu",
+                       devname,
+                        &strings->data[i * ETH_GSTRING_LEN],
+                        stats->data[i]);
+               ethstat_submit_value (
+                       devname,
+                       (char*)&strings->data[i * ETH_GSTRING_LEN],
+                       stats->data[i]);
+        }
+        free(strings);
+        free(stats);
+
+
+       return 0;
+}
+
+
+static int ethstat_read(void)
+{
+       struct ifreq ifr;
+       int i;
+
+       for (i = 0 ; i < ifacenumber ; i++) {
+               DEBUG("ethstat - Processing : %s\n", ifacelist[i]);
+               memset(&ifr, 0, sizeof(ifr));
+               strcpy(ifr.ifr_name, ifacelist[i]);
+               getstats(ifacelist[i], &ifr);
+       }
+       return 0;
+}
+
+void module_register (void)
+{
+       ifacelist = malloc(sizeof(char*));
+       plugin_register_config ("ethstat", ethstat_config,
+                       config_keys, config_keys_num);
+       plugin_register_read ("ethstat", ethstat_read);
+}
+
+#endif
diff --git a/src/ethstat.h b/src/ethstat.h
new file mode 100644 (file)
index 0000000..d544db5
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * collectd - src/ethstat.h
+ * Copyright (C) 2011       Cyril Feraudet
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Cyril Feraudet <cyril at feraudet.com>
+ **/
+
+#define ETHTOOL_BUSINFO_LEN     32
+#define ETHTOOL_GDRVINFO       0x00000003 /* Get driver info. */
+#define ETH_GSTRING_LEN                32
+#define ETHTOOL_GSTRINGS       0x0000001b /* get specified string set */
+#define ETHTOOL_GSTATS          0x0000001d /* get NIC-specific statistics */
+
+enum ethtool_stringset {
+        ETH_SS_TEST             = 0,
+        ETH_SS_STATS,
+};
+
+typedef unsigned long long u64;
+typedef unsigned char __u8;
+typedef unsigned short __u16;
+typedef unsigned int __u32;
+typedef unsigned long long  __u64;
+
+
+static const char *config_keys[] =
+{
+       "Iface"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char **ifacelist;
+static int ifacenumber = 0;
+struct ethtool_drvinfo {
+        __u32   cmd;
+        char    driver[32];     /* driver short name, "tulip", "eepro100" */
+        char    version[32];    /* driver version string */
+        char    fw_version[32]; /* firmware version string, if applicable */
+        char    bus_info[ETHTOOL_BUSINFO_LEN];  /* Bus info for this IF. */
+                                /* For PCI devices, use pci_name(pci_dev). */
+        char    reserved1[32];
+        char    reserved2[16];
+        __u32   n_stats;        /* number of u64's from ETHTOOL_GSTATS */
+        __u32   testinfo_len;
+        __u32   eedump_len;     /* Size of data from ETHTOOL_GEEPROM (bytes) */
+        __u32   regdump_len;    /* Size of data from ETHTOOL_GREGS (bytes) */
+};
+
+struct ethtool_gstrings {
+        __u32   cmd;            /* ETHTOOL_GSTRINGS */
+        __u32   string_set;     /* string set id e.c. ETH_SS_TEST, etc*/
+        __u32   len;            /* number of strings in the string set */
+        __u8    data[0];
+};
+
+struct ethtool_stats {
+        __u32   cmd;            /* ETHTOOL_GSTATS */
+        __u32   n_stats;        /* number of u64's being returned */
+        __u64   data[0];
+};
+
+
+