From: Florian Forster Date: Sat, 10 Nov 2007 12:47:49 +0000 (+0100) Subject: libvirt plugin: Renamed the `libvirtstats' plugin to `libvirt'. X-Git-Tag: collectd-4.3.0beta0~108 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=553376a3a6c63875c84b406962e56b908dc4e675;p=collectd.git libvirt plugin: Renamed the `libvirtstats' plugin to `libvirt'. --- diff --git a/configure.in b/configure.in index 88323f30..2936058c 100644 --- a/configure.in +++ b/configure.in @@ -1843,7 +1843,7 @@ plugin_entropy="no" plugin_interface="no" plugin_ipvs="no" plugin_irq="no" -plugin_libvirtstats="no" +plugin_libvirt="no" plugin_load="no" plugin_memory="no" plugin_multimeter="no" @@ -1937,7 +1937,7 @@ fi if test "x$with_libxml2" = "xyes" && test "x$with_libvirt" = "xyes" then - plugin_libvirtstats="yes" + plugin_libvirt="yes" fi if test "x$have_getloadavg" = "xyes" @@ -1998,7 +1998,7 @@ AC_PLUGIN([interface], [$plugin_interface], [Interface traffic statistics]) AC_PLUGIN([iptables], [$with_libiptc], [IPTables rule counters]) AC_PLUGIN([ipvs], [$plugin_ipvs], [IPVS connection statistics]) AC_PLUGIN([irq], [$plugin_irq], [IRQ statistics]) -AC_PLUGIN([libvirtstats],[$plugin_libvirtstats], [Virtual machine statistics]) +AC_PLUGIN([libvirt], [$plugin_libvirt], [Virtual machine statistics]) AC_PLUGIN([load], [$plugin_load], [System load]) AC_PLUGIN([logfile], [yes], [File logging plugin]) AC_PLUGIN([mbmon], [yes], [Query mbmond]) @@ -2128,7 +2128,7 @@ Configuration: iptables . . . . . $enable_iptables ipvs . . . . . . . $enable_ipvs irq . . . . . . . . $enable_irq - libvirtstats . . . $enable_libvirtstats + libvirt . . . . . . $enable_libvirt load . . . . . . . $enable_load logfile . . . . . . $enable_logfile mbmon . . . . . . . $enable_mbmon diff --git a/src/Makefile.am b/src/Makefile.am index bc78912c..b7f343fa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -278,14 +278,14 @@ collectd_LDADD += "-dlopen" irq.la collectd_DEPENDENCIES += irq.la endif -if BUILD_PLUGIN_LIBVIRTSTATS -pkglib_LTLIBRARIES += libvirtstats.la -libvirtstats_la_SOURCES = libvirtstats.c -libvirtstats_la_CFLAGS = $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS) -libvirtstats_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) -libvirtstats_la_LDFLAGS = -module -avoid-version -collectd_LDADD += "-dlopen" libvirtstats.la -collectd_DEPENDENCIES += libvirtstats.la +if BUILD_PLUGIN_LIBVIRT +pkglib_LTLIBRARIES += libvirt.la +libvirt_la_SOURCES = libvirt.c +libvirt_la_CFLAGS = $(BUILD_WITH_LIBVIRT_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS) +libvirt_la_LIBADD = $(BUILD_WITH_LIBVIRT_LIBS) $(BUILD_WITH_LIBXML2_LIBS) +libvirt_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" libvirt.la +collectd_DEPENDENCIES += libvirt.la endif if BUILD_PLUGIN_LOAD diff --git a/src/libvirt.c b/src/libvirt.c new file mode 100644 index 00000000..0a998228 --- /dev/null +++ b/src/libvirt.c @@ -0,0 +1,804 @@ +/** + * collectd - src/libvirt.c + * Copyright (C) 2006,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: + * Richard W.M. Jones + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" +#include "utils_ignorelist.h" + +#include +#include +#include +#include +#include + +static const char *config_keys[] = { + "Connection", + + "RefreshInterval", + + "Domain", + "BlockDevice", + "InterfaceDevice", + "IgnoreSelected", + + "HostnameFormat", + + NULL +}; +#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1) + +/* Connection. */ +static virConnectPtr conn = 0; + +/* Seconds between list refreshes, 0 disables completely. */ +static int interval = 60; + +/* List of domains, if specified. */ +static ignorelist_t *il_domains = NULL; +/* List of block devices, if specified. */ +static ignorelist_t *il_block_devices = NULL; +/* List of network interface devices, if specified. */ +static ignorelist_t *il_interface_devices = NULL; + +static int ignore_device_match (ignorelist_t *, + const char *domname, const char *devpath); + +/* Actual list of domains found on last refresh. */ +static virDomainPtr *domains = NULL; +static int nr_domains = 0; + +static void free_domains (void); +static int add_domain (virDomainPtr dom); + +/* Actual list of block devices found on last refresh. */ +struct block_device { + virDomainPtr dom; /* domain */ + char *path; /* name of block device */ +}; + +static struct block_device *block_devices = NULL; +static int nr_block_devices = 0; + +static void free_block_devices (void); +static int add_block_device (virDomainPtr dom, const char *path); + +/* Actual list of network interfaces found on last refresh. */ +struct interface_device { + virDomainPtr dom; /* domain */ + char *path; /* name 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); + +/* HostnameFormat. */ +#define HF_MAX_FIELDS 3 + +enum hf_field { + hf_none = 0, + hf_hostname, + hf_name, + hf_uuid +}; + +static enum hf_field hostname_format[HF_MAX_FIELDS] = + { hf_name }; + +/* Time that we last refreshed. */ +static time_t last_refresh = (time_t) 0; + +static int refresh_lists (void); + +/* Submit functions. */ +static void cpu_submit (unsigned long long cpu_time, + time_t t, + virDomainPtr dom, const char *type); +static void vcpu_submit (unsigned long long cpu_time, + time_t t, + virDomainPtr dom, int vcpu_nr, const char *type); +static void submit_counter2 (const char *type, counter_t v0, counter_t v1, + time_t t, + virDomainPtr dom, const char *devname); + +/* ERROR(...) macro for virterrors. */ +#define VIRT_ERROR(conn,s) do { \ + virErrorPtr err; \ + err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \ + if (err) ERROR ("%s: %s", (s), err->message); \ + } while(0) + +static int +lv_init (void) +{ + if (virInitialize () != 0) + return -1; + + return 0; +} + +static int +lv_config (const char *key, const char *value) +{ + if (virInitialize () != 0) + return 1; + + if (il_domains == NULL) + il_domains = ignorelist_create (1); + if (il_block_devices == NULL) + il_block_devices = ignorelist_create (1); + if (il_interface_devices == NULL) + 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"); + return 1; + } + return 0; + } + + if (strcasecmp (key, "RefreshInterval") == 0) { + char *eptr = NULL; + interval = strtol (value, &eptr, 10); + if (eptr == NULL || *eptr != '\0') return 1; + return 0; + } + + if (strcasecmp (key, "Domain") == 0) { + if (ignorelist_add (il_domains, value)) return 1; + return 0; + } + if (strcasecmp (key, "BlockDevice") == 0) { + if (ignorelist_add (il_block_devices, value)) return 1; + return 0; + } + if (strcasecmp (key, "InterfaceDevice") == 0) { + if (ignorelist_add (il_interface_devices, value)) return 1; + return 0; + } + + if (strcasecmp (key, "IgnoreSelected") == 0) { + if (strcasecmp (value, "True") == 0 || + strcasecmp (value, "Yes") == 0 || + strcasecmp (value, "On") == 0) + { + ignorelist_set_invert (il_domains, 0); + ignorelist_set_invert (il_block_devices, 0); + ignorelist_set_invert (il_interface_devices, 0); + } + else + { + ignorelist_set_invert (il_domains, 1); + ignorelist_set_invert (il_block_devices, 1); + ignorelist_set_invert (il_interface_devices, 1); + } + return 0; + } + + if (strcasecmp (key, "HostnameFormat") == 0) { + char *value_copy; + char *fields[HF_MAX_FIELDS]; + int i, n; + + value_copy = strdup (value); + if (value_copy == NULL) { + ERROR ("libvirt plugin: strdup failed."); + return -1; + } + + n = strsplit (value_copy, fields, HF_MAX_FIELDS); + if (n < 1) { + free (value_copy); + ERROR ("HostnameFormat: no fields"); + return -1; + } + + for (i = 0; i < n; ++i) { + if (strcasecmp (fields[i], "hostname") == 0) + hostname_format[i] = hf_hostname; + else if (strcasecmp (fields[i], "name") == 0) + hostname_format[i] = hf_name; + else if (strcasecmp (fields[i], "uuid") == 0) + hostname_format[i] = hf_uuid; + else { + free (value_copy); + ERROR ("unknown HostnameFormat field: %s", fields[i]); + return -1; + } + } + free (value_copy); + + for (i = n; i < HF_MAX_FIELDS; ++i) + hostname_format[i] = hf_none; + + return 0; + } + + /* Unrecognised option. */ + return -1; +} + +static int +lv_read (void) +{ + time_t t; + int i; + + if (conn == NULL) { + ERROR ("libvirt plugin: Not connected. Use Connection in " + "config file to supply connection URI. For more information " + "see "); + return -1; + } + + 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) + return -1; + last_refresh = t; + } + +#if 0 + for (i = 0; i < nr_domains; ++i) + fprintf (stderr, "domain %s\n", virDomainGetName (domains[i])); + for (i = 0; i < nr_block_devices; ++i) + fprintf (stderr, "block device %d %s:%s\n", + i, virDomainGetName (block_devices[i].dom), + block_devices[i].path); + for (i = 0; i < nr_interface_devices; ++i) + fprintf (stderr, "interface device %d %s:%s\n", + i, virDomainGetName (interface_devices[i].dom), + interface_devices[i].path); +#endif + + /* Get CPU usage, VCPU usage for each domain. */ + for (i = 0; i < nr_domains; ++i) { + virDomainInfo info; + virVcpuInfoPtr vinfo = NULL; + int j; + + if (virDomainGetInfo (domains[i], &info) != 0) + continue; + + cpu_submit (info.cpuTime, t, domains[i], "virt_cpu_total"); + + vinfo = malloc (info.nrVirtCpu * sizeof vinfo[0]); + if (vinfo == NULL) { + ERROR ("libvirt plugin: malloc failed."); + continue; + } + + if (virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu, + NULL, 0) != 0) { + free (vinfo); + continue; + } + + for (j = 0; j < info.nrVirtCpu; ++j) + vcpu_submit (vinfo[j].cpuTime, + t, domains[i], vinfo[j].number, "virt_vcpu"); + + free (vinfo); + } + + /* Get block device stats for each domain. */ + for (i = 0; i < nr_block_devices; ++i) { + struct _virDomainBlockStats stats; + + if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path, + &stats, sizeof stats) != 0) + continue; + + if ((stats.rd_req != -1) && (stats.wr_req != -1)) + submit_counter2 ("disk_ops", + (counter_t) stats.rd_req, (counter_t) stats.wr_req, + t, block_devices[i].dom, block_devices[i].path); + + if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1)) + submit_counter2 ("disk_octets", + (counter_t) stats.rd_bytes, (counter_t) stats.wr_bytes, + t, block_devices[i].dom, block_devices[i].path); + } /* for (nr_block_devices) */ + + /* Get interface stats for each domain. */ + for (i = 0; i < nr_interface_devices; ++i) { + struct _virDomainInterfaceStats stats; + + if (virDomainInterfaceStats (interface_devices[i].dom, + interface_devices[i].path, + &stats, sizeof stats) != 0) + continue; + + 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); + + 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); + + 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); + + 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); + } /* for (nr_interface_devices) */ + + return 0; +} + +static int +refresh_lists (void) +{ + int n; + + n = virConnectNumOfDomains (conn); + if (n < 0) { + VIRT_ERROR (conn, "reading number of domains"); + return -1; + } + + if (n > 0) { + int i; + int *domids; + + /* Get list of domains. */ + domids = malloc (sizeof (int) * n); + if (domids == 0) { + ERROR ("libvirt plugin: malloc failed."); + return -1; + } + + n = virConnectListDomains (conn, domids, n); + if (n < 0) { + VIRT_ERROR (conn, "reading list of domains"); + free (domids); + return -1; + } + + free_block_devices (); + free_interface_devices (); + free_domains (); + + /* Fetch each domain and add it to the list, unless ignore. */ + for (i = 0; i < n; ++i) { + virDomainPtr dom = NULL; + const char *name; + char *xml = NULL; + xmlDocPtr xml_doc = NULL; + xmlXPathContextPtr xpath_ctx = NULL; + xmlXPathObjectPtr xpath_obj = NULL; + int j; + + dom = virDomainLookupByID (conn, domids[i]); + if (dom == NULL) { + VIRT_ERROR (conn, "virDomainLookupByID"); + /* Could be that the domain went away -- ignore it anyway. */ + continue; + } + + name = virDomainGetName (dom); + if (name == NULL) { + VIRT_ERROR (conn, "virDomainGetName"); + goto cont; + } + + if (il_domains && ignorelist_match (il_domains, name) != 0) + goto cont; + + if (add_domain (dom) < 0) { + ERROR ("libvirt plugin: malloc failed."); + goto cont; + } + + /* Get a list of devices for this domain. */ + xml = virDomainGetXMLDesc (dom, 0); + if (!xml) { + VIRT_ERROR (conn, "virDomainGetXMLDesc"); + goto cont; + } + + /* Yuck, XML. Parse out the devices. */ + xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET); + if (xml_doc == NULL) { + VIRT_ERROR (conn, "xmlReadDoc"); + goto cont; + } + + xpath_ctx = xmlXPathNewContext (xml_doc); + + /* Block devices. */ + xpath_obj = xmlXPathEval + ((xmlChar *) "/domain/devices/disk/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; + char *path = NULL; + + node = xpath_obj->nodesetval->nodeTab[j]; + if (!node) continue; + path = (char *) xmlGetProp (node, (xmlChar *) "dev"); + if (!path) continue; + + if (il_block_devices && + ignore_device_match (il_block_devices, name, path) != 0) + goto cont2; + + add_block_device (dom, path); + cont2: + if (path) xmlFree (path); + } + xmlXPathFreeObject (xpath_obj); + + /* Network interfaces. */ + xpath_obj = xmlXPathEval + ((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; + char *path = NULL; + + node = xpath_obj->nodesetval->nodeTab[j]; + if (!node) continue; + path = (char *) xmlGetProp (node, (xmlChar *) "dev"); + if (!path) continue; + + if (il_interface_devices && + ignore_device_match (il_interface_devices, name, path) != 0) + goto cont3; + + add_interface_device (dom, path); + cont3: + if (path) xmlFree (path); + } + + cont: + if (xpath_obj) xmlXPathFreeObject (xpath_obj); + if (xpath_ctx) xmlXPathFreeContext (xpath_ctx); + if (xml_doc) xmlFreeDoc (xml_doc); + if (xml) free (xml); + } + + free (domids); + } + + return 0; +} + +static void +free_domains () +{ + int i; + + if (domains) { + for (i = 0; i < nr_domains; ++i) + virDomainFree (domains[i]); + free (domains); + } + domains = NULL; + nr_domains = 0; +} + +static int +add_domain (virDomainPtr dom) +{ + virDomainPtr *new_ptr; + int new_size = sizeof (domains[0]) * (nr_domains+1); + + if (domains) + new_ptr = realloc (domains, new_size); + else + new_ptr = malloc (new_size); + + if (new_ptr == NULL) + return -1; + + domains = new_ptr; + domains[nr_domains] = dom; + return nr_domains++; +} + +static void +free_block_devices () +{ + int i; + + if (block_devices) { + for (i = 0; i < nr_block_devices; ++i) + free (block_devices[i].path); + free (block_devices); + } + block_devices = NULL; + nr_block_devices = 0; +} + +static int +add_block_device (virDomainPtr dom, const char *path) +{ + struct block_device *new_ptr; + int new_size = sizeof (block_devices[0]) * (nr_block_devices+1); + char *path_copy; + + path_copy = strdup (path); + if (!path_copy) + return -1; + + if (block_devices) + new_ptr = realloc (block_devices, new_size); + else + new_ptr = malloc (new_size); + + if (new_ptr == NULL) { + free (path_copy); + return -1; + } + block_devices = new_ptr; + block_devices[nr_block_devices].dom = dom; + block_devices[nr_block_devices].path = path_copy; + return nr_block_devices++; +} + +static void +free_interface_devices () +{ + int i; + + if (interface_devices) { + for (i = 0; i < nr_interface_devices; ++i) + free (interface_devices[i].path); + free (interface_devices); + } + interface_devices = NULL; + nr_interface_devices = 0; +} + +static int +add_interface_device (virDomainPtr dom, const char *path) +{ + struct interface_device *new_ptr; + int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1); + char *path_copy; + + path_copy = strdup (path); + if (!path_copy) return -1; + + if (interface_devices) + new_ptr = realloc (interface_devices, new_size); + else + new_ptr = malloc (new_size); + + if (new_ptr == NULL) { + free (path_copy); + return -1; + } + interface_devices = new_ptr; + interface_devices[nr_interface_devices].dom = dom; + interface_devices[nr_interface_devices].path = path_copy; + return nr_interface_devices++; +} + +static int +ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath) +{ + char *name; + int n, r; + + n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2); + name = malloc (n); + if (name == NULL) { + ERROR ("libvirt plugin: malloc failed."); + return 0; + } + snprintf (name, n, "%s:%s", domname, devpath); + r = ignorelist_match (il, name); + free (name); + return r; +} + +static void +init_value_list (value_list_t *vl, time_t t, virDomainPtr dom) +{ + int i; + char *host_ptr; + size_t host_len; + + vl->time = t; + vl->interval = interval_g; + + strncpy (vl->plugin, "libvirt", sizeof (vl->plugin)); + vl->plugin[sizeof (vl->plugin) - 1] = '\0'; + + vl->host[0] = '\0'; + host_ptr = vl->host; + host_len = sizeof (vl->host); + + /* Construct the hostname field according to HostnameFormat. */ + for (i = 0; i < HF_MAX_FIELDS; ++i) { + int status = 0; + + switch (hostname_format[i]) { + case hf_none: + /* do nothing */ + break; + + case hf_hostname: + status = snprintf (host_ptr, host_len, ":%s", hostname_g); + break; + + case hf_name: + { + const char *name = virDomainGetName (dom); + if (name != NULL) + status = snprintf (host_ptr, host_len, ":%s", name); + break; + } + case hf_uuid: + { + char uuid[VIR_UUID_STRING_BUFLEN]; + if (virDomainGetUUIDString (dom, uuid) == 0) { + uuid[sizeof (uuid) - 1] = '\0'; + status = snprintf (host_ptr, host_len, ":%s", uuid); + } + break; + } + } /* switch (hostname_format[i]) */ + + /* If status >= host_len + * => the buffer is full, there's no null-byte at the end and + * continuing with this loop doesn't make any sense. */ + if (status >= host_len) { + host_len = 0; + host_ptr = NULL; + } + /* else: Test if anything was added to the buffer */ + else if (status > 0) { + host_len -= status; + host_ptr += status; + } + + if (host_len <= 0) + break; + } /* for (i) */ + + vl->host[sizeof (vl->host) - 1] = '\0'; +} /* void init_value_list */ + +static void +cpu_submit (unsigned long long cpu_time, + time_t t, + virDomainPtr dom, const char *type) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + init_value_list (&vl, t, dom); + + values[0].counter = cpu_time; + + vl.values = values; + vl.values_len = 1; + + plugin_dispatch_values (type, &vl); +} + +static void +vcpu_submit (counter_t cpu_time, + time_t t, + virDomainPtr dom, int vcpu_nr, const char *type) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + init_value_list (&vl, t, dom); + + values[0].counter = cpu_time; + vl.values = values; + vl.values_len = 1; + + snprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr); + vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + + plugin_dispatch_values (type, &vl); +} + +static void +submit_counter2 (const char *type, counter_t v0, counter_t v1, + time_t t, + virDomainPtr dom, const char *devname) +{ + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; + + init_value_list (&vl, t, dom); + + values[0].counter = v0; + values[1].counter = v1; + vl.values = values; + vl.values_len = 2; + + strncpy (vl.type_instance, devname, sizeof (vl.type_instance)); + vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; + + plugin_dispatch_values (type, &vl); +} /* void submit_counter2 */ + +static int +lv_shutdown (void) +{ + free_block_devices (); + free_interface_devices (); + free_domains (); + + if (conn != NULL) + virConnectClose (conn); + conn = NULL; + + ignorelist_free (il_domains); + il_domains = NULL; + ignorelist_free (il_block_devices); + il_block_devices = NULL; + ignorelist_free (il_interface_devices); + il_interface_devices = NULL; + + return 0; +} + +void +module_register (void) +{ + plugin_register_config ("libvirt", + lv_config, + config_keys, NR_CONFIG_KEYS); + plugin_register_init ("libvirt", lv_init); + plugin_register_read ("libvirt", lv_read); + plugin_register_shutdown ("libvirt", lv_shutdown); +} + +/* + * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker + */ diff --git a/src/libvirtstats.c b/src/libvirtstats.c deleted file mode 100644 index 66b5781d..00000000 --- a/src/libvirtstats.c +++ /dev/null @@ -1,804 +0,0 @@ -/** - * collectd - src/libvirtstats.c - * Copyright (C) 2006,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: - * Richard W.M. Jones - **/ - -#include "collectd.h" -#include "common.h" -#include "plugin.h" -#include "configfile.h" -#include "utils_ignorelist.h" - -#include -#include -#include -#include -#include - -static const char *config_keys[] = { - "Connection", - - "RefreshInterval", - - "Domain", - "BlockDevice", - "InterfaceDevice", - "IgnoreSelected", - - "HostnameFormat", - - NULL -}; -#define NR_CONFIG_KEYS ((sizeof config_keys / sizeof config_keys[0]) - 1) - -/* Connection. */ -static virConnectPtr conn = 0; - -/* Seconds between list refreshes, 0 disables completely. */ -static int interval = 60; - -/* List of domains, if specified. */ -static ignorelist_t *il_domains = NULL; -/* List of block devices, if specified. */ -static ignorelist_t *il_block_devices = NULL; -/* List of network interface devices, if specified. */ -static ignorelist_t *il_interface_devices = NULL; - -static int ignore_device_match (ignorelist_t *, - const char *domname, const char *devpath); - -/* Actual list of domains found on last refresh. */ -static virDomainPtr *domains = NULL; -static int nr_domains = 0; - -static void free_domains (void); -static int add_domain (virDomainPtr dom); - -/* Actual list of block devices found on last refresh. */ -struct block_device { - virDomainPtr dom; /* domain */ - char *path; /* name of block device */ -}; - -static struct block_device *block_devices = NULL; -static int nr_block_devices = 0; - -static void free_block_devices (void); -static int add_block_device (virDomainPtr dom, const char *path); - -/* Actual list of network interfaces found on last refresh. */ -struct interface_device { - virDomainPtr dom; /* domain */ - char *path; /* name 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); - -/* HostnameFormat. */ -#define HF_MAX_FIELDS 3 - -enum hf_field { - hf_none = 0, - hf_hostname, - hf_name, - hf_uuid -}; - -static enum hf_field hostname_format[HF_MAX_FIELDS] = - { hf_name }; - -/* Time that we last refreshed. */ -static time_t last_refresh = (time_t) 0; - -static int refresh_lists (void); - -/* Submit functions. */ -static void cpu_submit (unsigned long long cpu_time, - time_t t, - virDomainPtr dom, const char *type); -static void vcpu_submit (unsigned long long cpu_time, - time_t t, - virDomainPtr dom, int vcpu_nr, const char *type); -static void submit_counter2 (const char *type, counter_t v0, counter_t v1, - time_t t, - virDomainPtr dom, const char *devname); - -/* ERROR(...) macro for virterrors. */ -#define VIRT_ERROR(conn,s) do { \ - virErrorPtr err; \ - err = (conn) ? virConnGetLastError ((conn)) : virGetLastError (); \ - if (err) ERROR ("%s: %s", (s), err->message); \ - } while(0) - -static int -libvirtstats_init (void) -{ - if (virInitialize () != 0) - return -1; - - return 0; -} - -static int -libvirtstats_config (const char *key, const char *value) -{ - if (virInitialize () != 0) - return 1; - - if (il_domains == NULL) - il_domains = ignorelist_create (1); - if (il_block_devices == NULL) - il_block_devices = ignorelist_create (1); - if (il_interface_devices == NULL) - 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"); - return 1; - } - return 0; - } - - if (strcasecmp (key, "RefreshInterval") == 0) { - char *eptr = NULL; - interval = strtol (value, &eptr, 10); - if (eptr == NULL || *eptr != '\0') return 1; - return 0; - } - - if (strcasecmp (key, "Domain") == 0) { - if (ignorelist_add (il_domains, value)) return 1; - return 0; - } - if (strcasecmp (key, "BlockDevice") == 0) { - if (ignorelist_add (il_block_devices, value)) return 1; - return 0; - } - if (strcasecmp (key, "InterfaceDevice") == 0) { - if (ignorelist_add (il_interface_devices, value)) return 1; - return 0; - } - - if (strcasecmp (key, "IgnoreSelected") == 0) { - if (strcasecmp (value, "True") == 0 || - strcasecmp (value, "Yes") == 0 || - strcasecmp (value, "On") == 0) - { - ignorelist_set_invert (il_domains, 0); - ignorelist_set_invert (il_block_devices, 0); - ignorelist_set_invert (il_interface_devices, 0); - } - else - { - ignorelist_set_invert (il_domains, 1); - ignorelist_set_invert (il_block_devices, 1); - ignorelist_set_invert (il_interface_devices, 1); - } - return 0; - } - - if (strcasecmp (key, "HostnameFormat") == 0) { - char *value_copy; - char *fields[HF_MAX_FIELDS]; - int i, n; - - value_copy = strdup (value); - if (value_copy == NULL) { - ERROR ("libvirtstats plugin: strdup failed."); - return -1; - } - - n = strsplit (value_copy, fields, HF_MAX_FIELDS); - if (n < 1) { - free (value_copy); - ERROR ("HostnameFormat: no fields"); - return -1; - } - - for (i = 0; i < n; ++i) { - if (strcasecmp (fields[i], "hostname") == 0) - hostname_format[i] = hf_hostname; - else if (strcasecmp (fields[i], "name") == 0) - hostname_format[i] = hf_name; - else if (strcasecmp (fields[i], "uuid") == 0) - hostname_format[i] = hf_uuid; - else { - free (value_copy); - ERROR ("unknown HostnameFormat field: %s", fields[i]); - return -1; - } - } - free (value_copy); - - for (i = n; i < HF_MAX_FIELDS; ++i) - hostname_format[i] = hf_none; - - return 0; - } - - /* Unrecognised option. */ - return -1; -} - -static int -libvirtstats_read (void) -{ - time_t t; - int i; - - if (conn == NULL) { - ERROR ("libvirtstats plugin: Not connected. Use Connection in " - "config file to supply connection URI. For more information " - "see "); - return -1; - } - - 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) - return -1; - last_refresh = t; - } - -#if 0 - for (i = 0; i < nr_domains; ++i) - fprintf (stderr, "domain %s\n", virDomainGetName (domains[i])); - for (i = 0; i < nr_block_devices; ++i) - fprintf (stderr, "block device %d %s:%s\n", - i, virDomainGetName (block_devices[i].dom), - block_devices[i].path); - for (i = 0; i < nr_interface_devices; ++i) - fprintf (stderr, "interface device %d %s:%s\n", - i, virDomainGetName (interface_devices[i].dom), - interface_devices[i].path); -#endif - - /* Get CPU usage, VCPU usage for each domain. */ - for (i = 0; i < nr_domains; ++i) { - virDomainInfo info; - virVcpuInfoPtr vinfo = NULL; - int j; - - if (virDomainGetInfo (domains[i], &info) != 0) - continue; - - cpu_submit (info.cpuTime, t, domains[i], "virt_cpu_total"); - - vinfo = malloc (info.nrVirtCpu * sizeof vinfo[0]); - if (vinfo == NULL) { - ERROR ("libvirtstats plugin: malloc failed."); - continue; - } - - if (virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu, - NULL, 0) != 0) { - free (vinfo); - continue; - } - - for (j = 0; j < info.nrVirtCpu; ++j) - vcpu_submit (vinfo[j].cpuTime, - t, domains[i], vinfo[j].number, "virt_vcpu"); - - free (vinfo); - } - - /* Get block device stats for each domain. */ - for (i = 0; i < nr_block_devices; ++i) { - struct _virDomainBlockStats stats; - - if (virDomainBlockStats (block_devices[i].dom, block_devices[i].path, - &stats, sizeof stats) != 0) - continue; - - if ((stats.rd_req != -1) && (stats.wr_req != -1)) - submit_counter2 ("disk_ops", - (counter_t) stats.rd_req, (counter_t) stats.wr_req, - t, block_devices[i].dom, block_devices[i].path); - - if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1)) - submit_counter2 ("disk_octets", - (counter_t) stats.rd_bytes, (counter_t) stats.wr_bytes, - t, block_devices[i].dom, block_devices[i].path); - } /* for (nr_block_devices) */ - - /* Get interface stats for each domain. */ - for (i = 0; i < nr_interface_devices; ++i) { - struct _virDomainInterfaceStats stats; - - if (virDomainInterfaceStats (interface_devices[i].dom, - interface_devices[i].path, - &stats, sizeof stats) != 0) - continue; - - 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); - - 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); - - 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); - - 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); - } /* for (nr_interface_devices) */ - - return 0; -} - -static int -refresh_lists (void) -{ - int n; - - n = virConnectNumOfDomains (conn); - if (n < 0) { - VIRT_ERROR (conn, "reading number of domains"); - return -1; - } - - if (n > 0) { - int i; - int *domids; - - /* Get list of domains. */ - domids = malloc (sizeof (int) * n); - if (domids == 0) { - ERROR ("libvirtstats plugin: malloc failed."); - return -1; - } - - n = virConnectListDomains (conn, domids, n); - if (n < 0) { - VIRT_ERROR (conn, "reading list of domains"); - free (domids); - return -1; - } - - free_block_devices (); - free_interface_devices (); - free_domains (); - - /* Fetch each domain and add it to the list, unless ignore. */ - for (i = 0; i < n; ++i) { - virDomainPtr dom = NULL; - const char *name; - char *xml = NULL; - xmlDocPtr xml_doc = NULL; - xmlXPathContextPtr xpath_ctx = NULL; - xmlXPathObjectPtr xpath_obj = NULL; - int j; - - dom = virDomainLookupByID (conn, domids[i]); - if (dom == NULL) { - VIRT_ERROR (conn, "virDomainLookupByID"); - /* Could be that the domain went away -- ignore it anyway. */ - continue; - } - - name = virDomainGetName (dom); - if (name == NULL) { - VIRT_ERROR (conn, "virDomainGetName"); - goto cont; - } - - if (il_domains && ignorelist_match (il_domains, name) != 0) - goto cont; - - if (add_domain (dom) < 0) { - ERROR ("libvirtstats plugin: malloc failed."); - goto cont; - } - - /* Get a list of devices for this domain. */ - xml = virDomainGetXMLDesc (dom, 0); - if (!xml) { - VIRT_ERROR (conn, "virDomainGetXMLDesc"); - goto cont; - } - - /* Yuck, XML. Parse out the devices. */ - xml_doc = xmlReadDoc ((xmlChar *) xml, NULL, NULL, XML_PARSE_NONET); - if (xml_doc == NULL) { - VIRT_ERROR (conn, "xmlReadDoc"); - goto cont; - } - - xpath_ctx = xmlXPathNewContext (xml_doc); - - /* Block devices. */ - xpath_obj = xmlXPathEval - ((xmlChar *) "/domain/devices/disk/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; - char *path = NULL; - - node = xpath_obj->nodesetval->nodeTab[j]; - if (!node) continue; - path = (char *) xmlGetProp (node, (xmlChar *) "dev"); - if (!path) continue; - - if (il_block_devices && - ignore_device_match (il_block_devices, name, path) != 0) - goto cont2; - - add_block_device (dom, path); - cont2: - if (path) xmlFree (path); - } - xmlXPathFreeObject (xpath_obj); - - /* Network interfaces. */ - xpath_obj = xmlXPathEval - ((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; - char *path = NULL; - - node = xpath_obj->nodesetval->nodeTab[j]; - if (!node) continue; - path = (char *) xmlGetProp (node, (xmlChar *) "dev"); - if (!path) continue; - - if (il_interface_devices && - ignore_device_match (il_interface_devices, name, path) != 0) - goto cont3; - - add_interface_device (dom, path); - cont3: - if (path) xmlFree (path); - } - - cont: - if (xpath_obj) xmlXPathFreeObject (xpath_obj); - if (xpath_ctx) xmlXPathFreeContext (xpath_ctx); - if (xml_doc) xmlFreeDoc (xml_doc); - if (xml) free (xml); - } - - free (domids); - } - - return 0; -} - -static void -free_domains () -{ - int i; - - if (domains) { - for (i = 0; i < nr_domains; ++i) - virDomainFree (domains[i]); - free (domains); - } - domains = NULL; - nr_domains = 0; -} - -static int -add_domain (virDomainPtr dom) -{ - virDomainPtr *new_ptr; - int new_size = sizeof (domains[0]) * (nr_domains+1); - - if (domains) - new_ptr = realloc (domains, new_size); - else - new_ptr = malloc (new_size); - - if (new_ptr == NULL) - return -1; - - domains = new_ptr; - domains[nr_domains] = dom; - return nr_domains++; -} - -static void -free_block_devices () -{ - int i; - - if (block_devices) { - for (i = 0; i < nr_block_devices; ++i) - free (block_devices[i].path); - free (block_devices); - } - block_devices = NULL; - nr_block_devices = 0; -} - -static int -add_block_device (virDomainPtr dom, const char *path) -{ - struct block_device *new_ptr; - int new_size = sizeof (block_devices[0]) * (nr_block_devices+1); - char *path_copy; - - path_copy = strdup (path); - if (!path_copy) - return -1; - - if (block_devices) - new_ptr = realloc (block_devices, new_size); - else - new_ptr = malloc (new_size); - - if (new_ptr == NULL) { - free (path_copy); - return -1; - } - block_devices = new_ptr; - block_devices[nr_block_devices].dom = dom; - block_devices[nr_block_devices].path = path_copy; - return nr_block_devices++; -} - -static void -free_interface_devices () -{ - int i; - - if (interface_devices) { - for (i = 0; i < nr_interface_devices; ++i) - free (interface_devices[i].path); - free (interface_devices); - } - interface_devices = NULL; - nr_interface_devices = 0; -} - -static int -add_interface_device (virDomainPtr dom, const char *path) -{ - struct interface_device *new_ptr; - int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1); - char *path_copy; - - path_copy = strdup (path); - if (!path_copy) return -1; - - if (interface_devices) - new_ptr = realloc (interface_devices, new_size); - else - new_ptr = malloc (new_size); - - if (new_ptr == NULL) { - free (path_copy); - return -1; - } - interface_devices = new_ptr; - interface_devices[nr_interface_devices].dom = dom; - interface_devices[nr_interface_devices].path = path_copy; - return nr_interface_devices++; -} - -static int -ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath) -{ - char *name; - int n, r; - - n = sizeof (char) * (strlen (domname) + strlen (devpath) + 2); - name = malloc (n); - if (name == NULL) { - ERROR ("libvirtstats plugin: malloc failed."); - return 0; - } - snprintf (name, n, "%s:%s", domname, devpath); - r = ignorelist_match (il, name); - free (name); - return r; -} - -static void -init_value_list (value_list_t *vl, time_t t, virDomainPtr dom) -{ - int i; - char *host_ptr; - size_t host_len; - - vl->time = t; - vl->interval = interval_g; - - strncpy (vl->plugin, "libvirtstats", sizeof (vl->plugin)); - vl->plugin[sizeof (vl->plugin) - 1] = '\0'; - - vl->host[0] = '\0'; - host_ptr = vl->host; - host_len = sizeof (vl->host); - - /* Construct the hostname field according to HostnameFormat. */ - for (i = 0; i < HF_MAX_FIELDS; ++i) { - int status = 0; - - switch (hostname_format[i]) { - case hf_none: - /* do nothing */ - break; - - case hf_hostname: - status = snprintf (host_ptr, host_len, ":%s", hostname_g); - break; - - case hf_name: - { - const char *name = virDomainGetName (dom); - if (name != NULL) - status = snprintf (host_ptr, host_len, ":%s", name); - break; - } - case hf_uuid: - { - char uuid[VIR_UUID_STRING_BUFLEN]; - if (virDomainGetUUIDString (dom, uuid) == 0) { - uuid[sizeof (uuid) - 1] = '\0'; - status = snprintf (host_ptr, host_len, ":%s", uuid); - } - break; - } - } /* switch (hostname_format[i]) */ - - /* If status >= host_len - * => the buffer is full, there's no null-byte at the end and - * continuing with this loop doesn't make any sense. */ - if (status >= host_len) { - host_len = 0; - host_ptr = NULL; - } - /* else: Test if anything was added to the buffer */ - else if (status > 0) { - host_len -= status; - host_ptr += status; - } - - if (host_len <= 0) - break; - } /* for (i) */ - - vl->host[sizeof (vl->host) - 1] = '\0'; -} /* void init_value_list */ - -static void -cpu_submit (unsigned long long cpu_time, - time_t t, - virDomainPtr dom, const char *type) -{ - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; - - init_value_list (&vl, t, dom); - - values[0].counter = cpu_time; - - vl.values = values; - vl.values_len = 1; - - plugin_dispatch_values (type, &vl); -} - -static void -vcpu_submit (counter_t cpu_time, - time_t t, - virDomainPtr dom, int vcpu_nr, const char *type) -{ - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; - - init_value_list (&vl, t, dom); - - values[0].counter = cpu_time; - vl.values = values; - vl.values_len = 1; - - snprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr); - vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; - - plugin_dispatch_values (type, &vl); -} - -static void -submit_counter2 (const char *type, counter_t v0, counter_t v1, - time_t t, - virDomainPtr dom, const char *devname) -{ - value_t values[2]; - value_list_t vl = VALUE_LIST_INIT; - - init_value_list (&vl, t, dom); - - values[0].counter = v0; - values[1].counter = v1; - vl.values = values; - vl.values_len = 2; - - strncpy (vl.type_instance, devname, sizeof (vl.type_instance)); - vl.type_instance[sizeof (vl.type_instance) - 1] = '\0'; - - plugin_dispatch_values (type, &vl); -} /* void submit_counter2 */ - -static int -libvirtstats_shutdown (void) -{ - free_block_devices (); - free_interface_devices (); - free_domains (); - - if (conn != NULL) - virConnectClose (conn); - conn = NULL; - - ignorelist_free (il_domains); - il_domains = NULL; - ignorelist_free (il_block_devices); - il_block_devices = NULL; - ignorelist_free (il_interface_devices); - il_interface_devices = NULL; - - return 0; -} - -void -module_register (void) -{ - plugin_register_config ("libvirtstats", - libvirtstats_config, - config_keys, NR_CONFIG_KEYS); - plugin_register_init ("libvirtstats", libvirtstats_init); - plugin_register_read ("libvirtstats", libvirtstats_read); - plugin_register_shutdown ("libvirtstats", libvirtstats_shutdown); -} - -/* - * vim: shiftwidth=4 tabstop=8 softtabstop=4 expandtab fdm=marker - */