Add InterfaceFormat setting to libvirt plugin
[collectd.git] / src / libvirt.c
index 5acff29..d97f90a 100644 (file)
@@ -24,6 +24,7 @@
 #include "plugin.h"
 #include "configfile.h"
 #include "utils_ignorelist.h"
+#include "utils_complain.h"
 
 #include <libvirt/libvirt.h>
 #include <libvirt/virterror.h>
@@ -42,6 +43,7 @@ static const char *config_keys[] = {
     "IgnoreSelected",
 
     "HostnameFormat",
+    "InterfaceFormat",
 
     NULL
 };
@@ -49,6 +51,8 @@ static const char *config_keys[] = {
 
 /* Connection. */
 static virConnectPtr conn = 0;
+static char *conn_string = NULL;
+static c_complain_t conn_complain = C_COMPLAIN_INIT_STATIC;
 
 /* Seconds between list refreshes, 0 disables completely. */
 static int interval = 60;
@@ -86,13 +90,14 @@ static int add_block_device (virDomainPtr dom, const char *path);
 struct interface_device {
     virDomainPtr dom;           /* domain */
     char *path;                 /* name of interface device */
+    char *address;              /* mac address of interface device */
 };
 
 static struct interface_device *interface_devices = NULL;
 static int nr_interface_devices = 0;
 
 static void free_interface_devices (void);
-static int add_interface_device (virDomainPtr dom, const char *path);
+static int add_interface_device (virDomainPtr dom, const char *path, const char *address);
 
 /* HostnameFormat. */
 #define HF_MAX_FIELDS 3
@@ -107,6 +112,15 @@ enum hf_field {
 static enum hf_field hostname_format[HF_MAX_FIELDS] =
     { hf_name };
 
+/* InterfaceFormat. */
+
+enum if_field {
+    if_address,
+    if_name
+};
+
+static enum if_field interface_format = if_name;
+
 /* Time that we last refreshed. */
 static time_t last_refresh = (time_t) 0;
 
@@ -153,15 +167,13 @@ lv_config (const char *key, const char *value)
         il_interface_devices = ignorelist_create (1);
 
     if (strcasecmp (key, "Connection") == 0) {
-        if (conn != 0) {
-            ERROR ("Connection may only be given once in config file");
-            return 1;
-        }
-        conn = virConnectOpenReadOnly (value);
-        if (!conn) {
-            VIRT_ERROR (NULL, "connection failed");
+        char *tmp = strdup (value);
+        if (tmp == NULL) {
+            ERROR ("libvirt plugin: Connection strdup failed.");
             return 1;
         }
+        sfree (conn_string);
+        conn_string = tmp;
         return 0;
     }
 
@@ -186,9 +198,7 @@ lv_config (const char *key, const char *value)
     }
 
     if (strcasecmp (key, "IgnoreSelected") == 0) {
-        if (strcasecmp (value, "True") == 0 ||
-            strcasecmp (value, "Yes") == 0 ||
-            strcasecmp (value, "On") == 0)
+        if (IS_TRUE (value))
         {
             ignorelist_set_invert (il_domains, 0);
             ignorelist_set_invert (il_block_devices, 0);
@@ -242,6 +252,28 @@ lv_config (const char *key, const char *value)
         return 0;
     }
 
+    if (strcasecmp (key, "InterfaceFormat") == 0) {
+        char *value_copy;
+
+        value_copy = strdup (value);
+        if (value_copy == NULL) {
+            ERROR ("libvirt plugin: strdup failed.");
+            return -1;
+        }
+
+        if (strcasecmp (value_copy, "name") == 0)
+            interface_format = if_name;
+        else if (strcasecmp (value_copy, "address") == 0)
+            interface_format = if_address;
+        else {
+            free (value_copy);
+            ERROR ("unknown InterfaceFormat: %s", value_copy);
+            return -1;
+        }
+        free (value_copy);
+        return 0;
+    }
+
     /* Unrecognised option. */
     return -1;
 }
@@ -253,19 +285,29 @@ lv_read (void)
     int i;
 
     if (conn == NULL) {
-        ERROR ("libvirt plugin: Not connected. Use Connection in "
-                "config file to supply connection URI.  For more information "
-                "see <http://libvirt.org/uri.html>");
-        return -1;
+        /* `conn_string == NULL' is acceptable. */
+        conn = virConnectOpenReadOnly (conn_string);
+        if (conn == NULL) {
+            c_complain (LOG_ERR, &conn_complain,
+                    "libvirt plugin: Unable to connect: "
+                    "virConnectOpenReadOnly failed.");
+            return -1;
+        }
     }
+    c_release (LOG_NOTICE, &conn_complain,
+            "libvirt plugin: Connection established.");
 
     time (&t);
 
     /* Need to refresh domain or device lists? */
     if ((last_refresh == (time_t) 0) ||
             ((interval > 0) && ((last_refresh + interval) <= t))) {
-        if (refresh_lists () != 0)
+        if (refresh_lists () != 0) {
+            if (conn != NULL)
+                virConnectClose (conn);
+            conn = NULL;
             return -1;
+        }
         last_refresh = t;
     }
 
@@ -334,6 +376,10 @@ lv_read (void)
     /* Get interface stats for each domain. */
     for (i = 0; i < nr_interface_devices; ++i) {
         struct _virDomainInterfaceStats stats;
+        char *display_name = interface_devices[i].path;
+
+        if (interface_format == if_address)
+            display_name = interface_devices[i].address;
 
         if (virDomainInterfaceStats (interface_devices[i].dom,
                     interface_devices[i].path,
@@ -343,22 +389,22 @@ lv_read (void)
        if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1))
            submit_counter2 ("if_octets",
                    (counter_t) stats.rx_bytes, (counter_t) stats.tx_bytes,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+                   t, interface_devices[i].dom, display_name);
 
        if ((stats.rx_packets != -1) && (stats.tx_packets != -1))
            submit_counter2 ("if_packets",
                    (counter_t) stats.rx_packets, (counter_t) stats.tx_packets,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+                   t, interface_devices[i].dom, display_name);
 
        if ((stats.rx_errs != -1) && (stats.tx_errs != -1))
            submit_counter2 ("if_errors",
                    (counter_t) stats.rx_errs, (counter_t) stats.tx_errs,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+                   t, interface_devices[i].dom, display_name);
 
        if ((stats.rx_drop != -1) && (stats.tx_drop != -1))
            submit_counter2 ("if_dropped",
                    (counter_t) stats.rx_drop, (counter_t) stats.tx_drop,
-                   t, interface_devices[i].dom, interface_devices[i].path);
+                   t, interface_devices[i].dom, display_name);
     } /* for (nr_interface_devices) */
 
     return 0;
@@ -473,28 +519,44 @@ refresh_lists (void)
 
             /* Network interfaces. */
             xpath_obj = xmlXPathEval
-                ((xmlChar *) "/domain/devices/interface/target[@dev]",
+                ((xmlChar *) "/domain/devices/interface[target[@dev]]",
                  xpath_ctx);
             if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET ||
                 xpath_obj->nodesetval == NULL)
                 goto cont;
 
-            for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) {
-                xmlNodePtr node;
+            xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval;
+
+            for (j = 0; j < xml_interfaces->nodeNr; ++j) {
                 char *path = NULL;
+                char *address = NULL;
+                xmlNodePtr xml_interface;
 
-                node = xpath_obj->nodesetval->nodeTab[j];
-                if (!node) continue;
-                path = (char *) xmlGetProp (node, (xmlChar *) "dev");
-                if (!path) continue;
+                xml_interface = xml_interfaces->nodeTab[j];
+                if (!xml_interface) continue;
+                xmlNodePtr child = NULL;
+
+                for (child = xml_interface->children; child; child = child->next) {
+                    if (child->type != XML_ELEMENT_NODE) continue;
+
+                    if (xmlStrEqual(child->name, (const xmlChar *) "target")) {
+                        path = (char *) xmlGetProp (child, (const xmlChar *) "dev");
+                        if (!path) continue;
+                    } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) {
+                        address = (char *) xmlGetProp (child, (const xmlChar *) "address");
+                        if (!address) continue;
+                    }
+                }
 
                 if (il_interface_devices &&
-                    ignore_device_match (il_interface_devices, name, path) != 0)
+                    (ignore_device_match (il_interface_devices, name, path) != 0 ||
+                     ignore_device_match (il_interface_devices, name, address) != 0))
                     goto cont3;
 
-                add_interface_device (dom, path);
-            cont3:
-                if (path) xmlFree (path);
+                add_interface_device (dom, path, address);
+                cont3:
+                    if (path) xmlFree (path);
+                    if (address) xmlFree (address);
             }
 
         cont:
@@ -589,8 +651,10 @@ free_interface_devices ()
     int i;
 
     if (interface_devices) {
-        for (i = 0; i < nr_interface_devices; ++i)
+        for (i = 0; i < nr_interface_devices; ++i) {
             free (interface_devices[i].path);
+            free (interface_devices[i].address);
+        }
         free (interface_devices);
     }
     interface_devices = NULL;
@@ -598,15 +662,18 @@ free_interface_devices ()
 }
 
 static int
-add_interface_device (virDomainPtr dom, const char *path)
+add_interface_device (virDomainPtr dom, const char *path, const char *address)
 {
     struct interface_device *new_ptr;
     int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1);
-    char *path_copy;
+    char *path_copy, *address_copy;
 
     path_copy = strdup (path);
     if (!path_copy) return -1;
 
+    address_copy = strdup (address);
+    if (!address_copy) return -1;
+
     if (interface_devices)
         new_ptr = realloc (interface_devices, new_size);
     else
@@ -614,11 +681,13 @@ add_interface_device (virDomainPtr dom, const char *path)
 
     if (new_ptr == NULL) {
         free (path_copy);
+        free (address_copy);
         return -1;
     }
     interface_devices = new_ptr;
     interface_devices[nr_interface_devices].dom = dom;
     interface_devices[nr_interface_devices].path = path_copy;
+    interface_devices[nr_interface_devices].address = address_copy;
     return nr_interface_devices++;
 }