uuid plugin: Add a plugin which sets the hostname to an UUID.
authorRichard Jones <rjones@redhat.com>
Tue, 6 Nov 2007 14:09:59 +0000 (14:09 +0000)
committerFlorian Forster <octo@huhu.verplant.org>
Tue, 6 Nov 2007 17:15:11 +0000 (18:15 +0100)
Florian Forster wrote:
> Just out of curiosity: Why don't hostnames work for you?

For guests there's the question of what we put in the hostname field. The
previous plugin (libvirtstats) puts the guest's name in this field, but there
are some problems with this:

   physicalserver1 <--- (hostname of physical server)
     |
      \--- database <--- (name of guest)
      \--- web

   physicalserver2
     |
      \--- database
      \--- web

   coldbackupserver
     |
     (no guests)

Guest names aren't really unique. Different physical servers may have guests
with overlapping names as in the example above. Also guest names aren't fixed.
Xen in particular renames guests at will. For example if a guest is about to
migrate then Xen renames the guest as 'migrating-foo' and if the guest is about
to shutdown Xen renames it as 'Zombie-foo'. The administrator of the physical
server can also rename guests.

While you're migrating you'll have an intermediate situation like this:

   physicalserver1
     |
      \--- migrating-database
      \--- migrating-web     |
                             | migration
   coldbackupserver          |
     |                       V
      \--- database
      \--- web

During live migrations the old instance ('migrating-foo') is still running.

The UUID is unique across physical servers, and is copied by migration and
preserved across shutdowns so if you care about which guest your stats "really"
came from then only the UUID tells you this.

Guests also have a hostname which is separate from the guest's name (the
guest's name is stored in the hypervisor, the hostname is stored inside the
guest's kernel). However it's not feasible to access the guest's hostname from
the hypervisor since this would involve some sort of snooping into the guest
kernel. The guest might be running Windows or FreeBSD etc. The only feasible
way to get this is to run an instance of collectd inside each guest, but then
the uuid plugin will also work in this scenario and can get the UUID since it
is exposed inside the guest either through an emulated BIOS or in
/sys/hypervisor/uuid.

Rich.

Signed-off-by: Florian Forster <octo@huhu.verplant.org>
AUTHORS
configure.in
src/Makefile.am
src/collectd.conf.pod
src/uuid.c [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index ee7e392..d821df7 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -60,6 +60,10 @@ Much time and effort to find a nasty bug in the ntpd-plugin has been
 contributed by:
   Luboš Staněk <lubek at users.sourceforge.net>
 
+uuid module by:
+  Dan Berrange <berrange@redhat.com>
+  Richard W.M. Jones <rjones@redhat.com>
+
 collectd is available at:
   <http://collectd.org/>
 
index 75bc00f..31e0582 100644 (file)
@@ -17,6 +17,7 @@ AC_PROG_CPP
 AC_PROG_INSTALL
 AC_PROG_LN_S
 AC_PROG_MAKE_SET
+AM_PROG_CC_C_O
 AM_CONDITIONAL(COMPILER_IS_GCC, test "x$GCC" = "xyes")
 
 dnl configure libtool
@@ -669,6 +670,20 @@ AC_CHECK_LIB(resolv, res_search,
 [with_libresolv="no"])
 AM_CONDITIONAL(BUILD_WITH_LIBRESOLV, test "x$with_libresolv" = "xyes")
 
+dnl Check for HAL (hardware abstraction library)
+with_libhal="yes"
+AC_CHECK_LIB(hal,libhal_device_property_exists,
+            [AC_DEFINE(HAVE_LIBHAL, 1, [Define to 1 if you have 'hal' library])],
+            [with_libhal="no"])
+if test "x$with_libhal" = "xyes"; then
+       PKG_PROG_PKG_CONFIG
+       if test "x$PKG_CONFIG" != "x"; then
+               BUILD_WITH_LIBHAL_CFLAGS="`pkg-config --cflags hal`"
+               BUILD_WITH_LIBHAL_LIBS="`pkg-config --libs hal`"
+               AC_SUBST(BUILD_WITH_LIBHAL_CFLAGS)
+               AC_SUBST(BUILD_WITH_LIBHAL_LIBS)
+       fi
+fi
 
 m4_divert_once([HELP_WITH], [
 collectd additional packages:])
@@ -1885,6 +1900,7 @@ AC_PLUGIN([tape],        [$plugin_tape],       [Tape drive statistics])
 AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([unixsock],    [yes],                [Unixsock communication plugin])
 AC_PLUGIN([users],       [$plugin_users],      [User statistics])
+AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
 AC_PLUGIN([vserver],     [$plugin_vserver],    [Linux VServer statistics])
 AC_PLUGIN([wireless],    [$plugin_wireless],   [Wireless statistics])
 AC_PLUGIN([xmms],        [$with_libxmms],      [XMMS statistics])
@@ -2011,6 +2027,7 @@ Configuration:
     tcpconns  . . . . . $enable_tcpconns
     unixsock  . . . . . $enable_unixsock
     users . . . . . . . $enable_users
+    uuid  . . . . . . . $enable_uuid
     vserver . . . . . . $enable_vserver
     wireless  . . . . . $enable_wireless
     xmms  . . . . . . . $enable_xmms
index 0107241..8bf6217 100644 (file)
@@ -578,6 +578,16 @@ collectd_LDADD += "-dlopen" users.la
 collectd_DEPENDENCIES += users.la
 endif
 
+if BUILD_PLUGIN_UUID
+pkglib_LTLIBRARIES += uuid.la
+uuid_la_SOURCES = uuid.c
+uuid_la_CFLAGS  = $(BUILD_WITH_LIBHAL_CFLAGS)
+uuid_la_LIBADD  = $(BUILD_WITH_LIBHAL_LIBS)
+uuid_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" uuid.la
+collectd_DEPENDENCIES += uuid.la
+endif
+
 if BUILD_PLUGIN_VSERVER
 pkglib_LTLIBRARIES += vserver.la
 vserver_la_SOURCES = vserver.c
index da12f86..8326704 100644 (file)
@@ -869,6 +869,35 @@ L<chmod(1)>. Defaults to B<0770>.
 
 =back
 
+=head2 Plugin C<uuid>
+
+This plugin, if loaded, causes the Hostname to be taken from the
+machine's UUID.  The UUID is a universally unique designation for the
+machine, usually taken from the machine's BIOS.  This is most useful
+if the machine is running in a virtual environment such as Xen, in
+which case the UUID is preserved across shutdowns and migration.
+
+The following methods are used to find the machine's UUID, in order:
+
+Check I</etc/uuid> (or I<UUIDFile>).
+
+Check for UUID from HAL (L<http://www.freedesktop.org/wiki/Software/hal>)
+if present.
+
+Check for UUID from C<dmidecode> / SMBIOS.
+
+Check for UUID from Xen hypervisor.
+
+If no UUID can be found then the hostname is not modified.
+
+=over 4
+
+=item B<UUIDFile> I<Path>
+
+Take the UUID from the given file (default I</etc/uuid>).
+
+=back
+
 =head2 Plugin C<vserver>
 
 This plugin doesn't have any options. B<VServer> support is only available for
diff --git a/src/uuid.c b/src/uuid.c
new file mode 100644 (file)
index 0000000..d54301a
--- /dev/null
@@ -0,0 +1,289 @@
+/**
+ * collectd - src/uuid.c
+ * Copyright (C) 2007  Red Hat Inc.
+ *
+ * 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
+ *
+ * Authors:
+ *   Dan Berrange <berrange@redhat.com>
+ *   Richard W.M. Jones <rjones@redhat.com>
+ *
+ * Derived from UUID detection code by Dan Berrange <berrange@redhat.com>
+ * http://hg.et.redhat.com/virt/daemons/spectre--devel?f=f6e3a1b06433;file=lib/uuid.c
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "configfile.h"
+#include "plugin.h"
+
+#if HAVE_LIBHAL
+#include <libhal.h>
+#endif
+
+#define UUID_RAW_LENGTH 16
+#define UUID_PRINTABLE_COMPACT_LENGTH  (UUID_RAW_LENGTH * 2)
+#define UUID_PRINTABLE_NORMAL_LENGTH  (UUID_PRINTABLE_COMPACT_LENGTH + 4)
+
+#define HANDLE_PREFIX "Handle"
+#define SYSINFO_PREFIX "System Information"
+#define ALT_SYSINFO_PREFIX "\tSystem Information"
+#define UUID_PREFIX "\tUUID:"
+#define ALT_UUID_PREFIX "\t\tUUID:"
+
+static int
+looks_like_a_uuid (const char *uuid)
+{
+    int len;
+
+    if (!uuid) return 0;
+
+    len = strlen (uuid);
+
+    if (len < UUID_PRINTABLE_COMPACT_LENGTH)
+        return 0;
+
+    while (*uuid) {
+        if (!isxdigit (*uuid) && *uuid != '-') return 0;
+        uuid++;
+    }
+    return 1;
+}
+
+static char *
+uuid_parse_dmidecode(FILE *file)
+{
+    char line[1024];
+    int inSysInfo = 0;
+
+    for (;;) {
+        if (!fgets(line, sizeof(line)/sizeof(char), file)) {
+            return NULL;
+        }
+        if (strncmp(line, HANDLE_PREFIX,
+                    (sizeof(HANDLE_PREFIX)/sizeof(char))-1) == 0) {
+            /*printf("Got handle %s\n", line);*/
+            inSysInfo = 0;
+        } else if (strncmp(line, SYSINFO_PREFIX,
+                           (sizeof(SYSINFO_PREFIX)/sizeof(char))-1) == 0) {
+            /*printf("Got system info %s\n", line);*/
+            inSysInfo = 1;
+        } else if (strncmp(line, ALT_SYSINFO_PREFIX,
+                           (sizeof(ALT_SYSINFO_PREFIX)/sizeof(char))-1) == 0) {
+            /*printf("Got alt system info %s\n", line);*/
+            inSysInfo = 1;
+        }
+        
+        if (inSysInfo) {
+            if (strncmp(line, UUID_PREFIX,
+                        (sizeof(UUID_PREFIX)/sizeof(char))-1) == 0) {
+                char *uuid = line + (sizeof(UUID_PREFIX)/sizeof(char));
+                /*printf("Got uuid [%s]\n", uuid);*/
+                if (looks_like_a_uuid (uuid))
+                    return strdup (uuid);
+            }
+            if (strncmp(line, ALT_UUID_PREFIX,
+                        (sizeof(ALT_UUID_PREFIX)/sizeof(char))-1) == 0) {
+                char *uuid = line + (sizeof(ALT_UUID_PREFIX)/sizeof(char));
+                /*printf("Got alt uuid [%s]\n", uuid);*/
+                if (looks_like_a_uuid (uuid))
+                    return strdup (uuid);
+            }
+        }
+    }
+    return NULL;
+}
+
+static char *
+uuid_get_from_dmidecode(void)
+{
+    FILE *dmidecode = popen("dmidecode 2>/dev/null", "r");
+    char *uuid;
+
+    if (!dmidecode) {
+        return NULL;
+    }
+    
+    uuid = uuid_parse_dmidecode(dmidecode);
+
+    pclose(dmidecode);
+    return uuid;
+}
+
+#if HAVE_LIBHAL
+
+#define UUID_PATH "/org/freedesktop/Hal/devices/computer"
+#define UUID_PROPERTY "smbios.system.uuid"
+
+static char *
+uuid_get_from_hal(void)
+{
+    LibHalContext *ctx;
+
+    DBusError error;
+    DBusConnection *con;
+
+    dbus_error_init(&error);
+
+    if (!(con = dbus_bus_get(DBUS_BUS_SYSTEM, &error)) ) {
+        goto bailout_nobus;
+    }
+
+    ctx = libhal_ctx_new();
+    libhal_ctx_set_dbus_connection(ctx, con);
+
+    if (!libhal_ctx_init(ctx, &error)) {
+        goto bailout;
+    }
+
+    if (!libhal_device_property_exists(ctx,
+                                       UUID_PATH,
+                                       UUID_PROPERTY,
+                                       &error)) {
+        goto bailout;
+    }
+
+    char *uuid  = libhal_device_get_property_string(ctx,
+                                                    UUID_PATH,
+                                                    UUID_PROPERTY,
+                                                    &error);
+    if (looks_like_a_uuid (uuid)) {
+        return uuid;
+    }
+
+ bailout:
+    {
+        DBusError ctxerror;
+        dbus_error_init(&ctxerror);
+        if (!(libhal_ctx_shutdown(ctx, &ctxerror))) {
+            dbus_error_free(&ctxerror);
+        }
+    }
+
+    libhal_ctx_free(ctx);
+    //dbus_connection_unref(con);
+
+ bailout_nobus:
+    if (dbus_error_is_set(&error)) {
+        /*printf("Error %s\n", error.name);*/
+        dbus_error_free(&error);
+    }
+    return NULL;
+}
+#endif
+
+static char *
+uuid_get_from_file(const char *path)
+{
+    FILE *file;
+    char uuid[UUID_PRINTABLE_NORMAL_LENGTH+1];
+
+    if (!(file = fopen(path, "r"))) {
+        return NULL;
+    }
+
+    if (!fgets(uuid, sizeof(uuid), file)) {
+        fclose(file);
+        return NULL;
+    }
+    fclose(file);
+
+    return strdup (uuid);
+}
+
+static char *uuidfile = NULL;
+
+static char *
+uuid_get_local(void)
+{
+    char *uuid;
+
+    /* Check /etc/uuid / UUIDFile before any other method. */
+    if ((uuid = uuid_get_from_file(uuidfile ? uuidfile : "/etc/uuid")) != NULL){
+        return uuid;
+    }
+
+#if HAVE_LIBHAL
+    if ((uuid = uuid_get_from_hal()) != NULL) {
+        return uuid;
+    }
+#endif
+
+    if ((uuid = uuid_get_from_dmidecode()) != NULL) {
+        return uuid;
+    }
+
+    if ((uuid = uuid_get_from_file("/sys/hypervisor/uuid")) != NULL) {
+        return uuid;
+    }
+
+    return NULL;
+}
+
+static const char *config_keys[] = {
+    "UUIDFile",
+    NULL
+};
+#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1)
+
+static int
+uuid_config (const char *key, const char *value)
+{
+    if (strcasecmp (key, "UUIDFile") == 0) {
+        if (uuidfile) {
+            ERROR ("UUIDFile given twice in configuration file");
+            return 1;
+        }
+        uuidfile = strdup (value);
+        return 0;
+    }
+    return 0;
+}
+
+static int
+uuid_init (void)
+{
+    char *uuid = uuid_get_local ();
+
+    if (uuid) {
+        strncpy (hostname_g, uuid, DATA_MAX_NAME_LEN);
+        hostname_g[DATA_MAX_NAME_LEN-1] = '\0';
+        sfree (uuid);
+        return 0;
+    }
+
+    WARNING ("uuid: could not read UUID using any known method");
+    return 0;
+}
+
+void module_register (void)
+{
+       plugin_register_config ("uuid", uuid_config,
+                            config_keys, NR_CONFIG_KEYS);
+       plugin_register_init ("uuid", uuid_init);
+}
+
+/*
+ * vim: set tabstop=4:
+ * vim: set shiftwidth=4:
+ * vim: set expandtab:
+ */
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */