This is an initial implementation of a plugin to collect information about Linux Software-RAID (md) devices. It reports the number of component devices, number of devices in array, number of active, working, failed and spare disks.
Signed-off-by: Michael Hanselmann <[email protected]> --- This is a patch I already sent once in early October 2010. Unfortunately it was not applied at the time. The patch below is rebased on top of the master branch. configure.in | 19 +++++ src/Makefile.am | 8 ++ src/collectd.conf.in | 6 ++ src/collectd.conf.pod | 24 ++++++ src/md.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++ src/types.db | 1 + 6 files changed, 262 insertions(+), 0 deletions(-) create mode 100644 src/md.c diff --git a/configure.in b/configure.in index 8db24ca..d6e1ed7 100644 --- a/configure.in +++ b/configure.in @@ -298,6 +298,21 @@ fi # For hddtemp module AC_CHECK_HEADERS(linux/major.h libgen.h) +# For md module (Linux only) +if test "x$ac_system" = "xLinux" +then + AC_CHECK_HEADERS(linux/raid/md_u.h, + [have_linux_raid_md_u_h="yes"], + [have_linux_raid_md_u_h="no"], +[ +#include <sys/ioctl.h> +#include <linux/major.h> +#include <linux/types.h> +]) +else + have_linux_raid_md_u_h="no" +fi + # For the battery plugin AC_CHECK_HEADERS(IOKit/ps/IOPowerSources.h, [], [], [ @@ -4334,6 +4349,7 @@ plugin_irq="no" plugin_libvirt="no" plugin_load="no" plugin_memory="no" +plugin_md="no" plugin_multimeter="no" plugin_nfs="no" plugin_fscache="no" @@ -4367,6 +4383,7 @@ then plugin_irq="yes" plugin_load="yes" plugin_memory="yes" + plugin_md="$have_linux_raid_md_u_h" plugin_nfs="yes" plugin_fscache="yes" plugin_processes="yes" @@ -4653,6 +4670,7 @@ AC_PLUGIN([match_regex], [yes], [The regex match]) AC_PLUGIN([match_timediff], [yes], [The timediff match]) AC_PLUGIN([match_value], [yes], [The value match]) AC_PLUGIN([mbmon], [yes], [Query mbmond]) +AC_PLUGIN([md], [$plugin_md], [Query md devices]) AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics]) AC_PLUGIN([memcached], [yes], [memcached statistics]) AC_PLUGIN([memory], [$plugin_memory], [Memory usage]) @@ -4980,6 +4998,7 @@ Configuration: match_timediff . . . $enable_match_timediff match_value . . . . . $enable_match_value mbmon . . . . . . . . $enable_mbmon + md . . . . . . . . . $enable_md memcachec . . . . . . $enable_memcachec memcached . . . . . . $enable_memcached memory . . . . . . . $enable_memory diff --git a/src/Makefile.am b/src/Makefile.am index 5728144..b9785fc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -593,6 +593,14 @@ collectd_LDADD += "-dlopen" mbmon.la collectd_DEPENDENCIES += mbmon.la endif +if BUILD_PLUGIN_MD +pkglib_LTLIBRARIES += md.la +md_la_SOURCES = md.c +md_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" md.la +collectd_DEPENDENCIES += md.la +endif + if BUILD_PLUGIN_MEMCACHEC pkglib_LTLIBRARIES += memcachec.la memcachec_la_SOURCES = memcachec.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 94cf2a9..6b6c1c4 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -89,6 +89,7 @@ #@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon +#@BUILD_PLUGIN_MD_TRUE@LoadPlugin md #@BUILD_PLUGIN_MEMCACHEC_TRUE@LoadPlugin memcachec #@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached @BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory @@ -428,6 +429,11 @@ # Port "411" #</Plugin> +#<Plugin md> +# Device "/dev/md0" +# IgnoreSelected false +#</Plugin> + #<Plugin memcachec> # <Page "plugin_instance"> # Server "localhost" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index ed979c4..c63bfff 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -1812,6 +1812,30 @@ TCP-Port to connect to. Defaults to B<411>. =back +=head2 Plugin C<md> + +The C<md plugin> collection information from Linux Software-RAID devices (md). + +All reported values are of the type "md_disks". Reported type instances are +"number" (number of component devices), "raid" (number of devices in the +array), "active", "working", "failed" (number of failed disks) and "spare" +(number of spare disks). + +=over 4 + +=item B<Device> I<Device> + +Select md devices based on device name. See B<IgnoreSelected> for more details. + +=item B<IgnoreSelected> I<true>|I<false> + +Invert device selection: If set to true, all md devices B<except> those listed +using B<Device> are collected. If false (the default), only those listed are +collected. If no configuration is given, the B<md> plugin will collect data +from all md devices. + +=back + =head2 Plugin C<memcachec> The C<memcachec plugin> connects to a memcached server, queries one or more diff --git a/src/md.c b/src/md.c new file mode 100644 index 0000000..65cf3d4 --- /dev/null +++ b/src/md.c @@ -0,0 +1,204 @@ +/** + * collectd - src/md.c + * Copyright (C) 2010, 2011 Michael Hanselmann + * + * 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; only version 2 of the License is applicable. + * + * 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 + * + * Author: + * Michael Hanselmann + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "utils_ignorelist.h" + +#include <sys/ioctl.h> + +#include <linux/major.h> +#include <linux/raid/md_u.h> + +#define PROC_DISKSTATS "/proc/diskstats" +#define DEV_DIR "/dev" + +static const char *config_keys[] = +{ + "Device", + "IgnoreSelected", +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static ignorelist_t *ignorelist = NULL; + +static int md_config (const char *key, const char *value) +{ + if (ignorelist == NULL) + ignorelist = ignorelist_create (/* invert = */ 1); + if (ignorelist == NULL) + return (1); + + if (strcasecmp (key, "Device") == 0) + { + ignorelist_add (ignorelist, value); + } + else if (strcasecmp (key, "IgnoreSelected") == 0) + { + ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1); + } + else + { + return (-1); + } + + return (0); +} + +static void md_submit (const int minor, + const char *type, const char *type_instance, + gauge_t value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = value; + + vl.values = values; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "md", sizeof (vl.plugin)); + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%i", minor); + sstrncpy (vl.type, type, sizeof (vl.type)); + sstrncpy (vl.type_instance, type_instance, + sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* void md_submit */ + +static void md_process (const int minor, const char *path) +{ + char errbuf[1024]; + int fd; + struct stat st; + mdu_array_info_t array; + + fd = open (path, O_RDONLY); + if (fd < 0) + { + WARNING ("md: open(%s): %s", path, + sstrerror (errno, errbuf, sizeof (errbuf))); + return; + } + + if (fstat (fd, &st) < 0) + { + WARNING ("md: Unable to fstat file descriptor for %s: %s", path, + sstrerror (errno, errbuf, sizeof (errbuf))); + close (fd); + return; + } + + if (! S_ISBLK (st.st_mode)) + { + WARNING ("md: %s is no block device", path); + close (fd); + return; + } + + if (st.st_rdev != makedev (MD_MAJOR, minor)) + { + WARNING ("md: Major/minor of %s are %i:%i, should be %i:%i", + path, (int)major(st.st_rdev), (int)minor(st.st_rdev), + (int)MD_MAJOR, minor); + close (fd); + return; + } + + /* Retrieve md information */ + if (ioctl (fd, GET_ARRAY_INFO, &array) < 0) { + WARNING ("md: Unable to retrieve array info from %s: %s", path, + sstrerror (errno, errbuf, sizeof (errbuf))); + close (fd); + return; + } + + close (fd); + + md_submit (minor, "md_disks", "number", array.nr_disks); + md_submit (minor, "md_disks", "raid", array.raid_disks); + md_submit (minor, "md_disks", "active", array.active_disks); + md_submit (minor, "md_disks", "working", array.working_disks); + md_submit (minor, "md_disks", "failed", array.failed_disks); + md_submit (minor, "md_disks", "spare", array.spare_disks); + + return; +} /* void md_process */ + +static int md_read (void) +{ + FILE *fh; + char buffer[1024]; + + fh = fopen (PROC_DISKSTATS, "r"); + if (fh == NULL) { + char errbuf[1024]; + WARNING ("md: Unable to open " PROC_DISKSTATS ": %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + /* Iterate md devices */ + while (fgets (buffer, sizeof (buffer), fh) != NULL) + { + char path[PATH_MAX]; + char *fields[4]; + char *name; + int major, minor; + + /* Extract interesting fields */ + if (strsplit (buffer, fields, STATIC_ARRAY_SIZE(fields)) < 3) + continue; + + major = atoi (fields[0]); + + if (major != MD_MAJOR) + continue; + + minor = atoi (fields[1]); + name = fields[2]; + + /* FIXME: Don't hardcode path. Walk /dev collecting major, + * minor and name, then use lookup table to find device. + * Alternatively create a temporary device file with correct + * major/minor, but that again can be tricky if the filesystem + * with the device file is mounted using the "nodev" option. + */ + ssnprintf (path, sizeof (path), DEV_DIR "/%s", name); + + if (ignorelist_match (ignorelist, path)) + continue; + + md_process (minor, path); + } + + fclose (fh); + + return (0); +} /* int md_read */ + +void module_register (void) +{ + plugin_register_config ("md", md_config, config_keys, config_keys_num); + plugin_register_read ("md", md_read); +} /* void module_register */ diff --git a/src/types.db b/src/types.db index e6345ab..8557fe6 100644 --- a/src/types.db +++ b/src/types.db @@ -84,6 +84,7 @@ irq value:DERIVE:0:U latency value:GAUGE:0:65535 links value:GAUGE:0:U load shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100 +md_disks value:GAUGE:0:U memcached_command value:DERIVE:0:U memcached_connections value:GAUGE:0:U memcached_items value:GAUGE:0:U -- 1.7.1 _______________________________________________ collectd mailing list [email protected] http://mailman.verplant.org/listinfo/collectd
