Merge branch 'collectd-5.4'
[collectd.git] / src / lvm.c
index 12c621d..82d7f6f 100644 (file)
--- a/src/lvm.c
+++ b/src/lvm.c
@@ -1,6 +1,7 @@
 /**
  * collectd - src/lvm.c
  * Copyright (C) 2013       Chad Malfait
+ * Copyright (C) 2014       Carnegie Mellon University
  *
  * 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
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Chad Malfait <malfaitc at yahoo.com>
+ *   Benjamin Gilbert <bgilbert at cs.cmu.edu>
  **/
 
 #include <lvm2app.h>
 #include "common.h"
 #include "plugin.h"
 
+#define NO_VALUE UINT64_MAX
+#define PERCENT_SCALE_FACTOR 1e-8
+
+static uint64_t get_lv_property_int(lv_t lv, char const *property)
+{
+    lvm_property_value_t v;
+
+    v = lvm_lv_get_property(lv, property);
+    if (!v.is_valid || !v.is_integer)
+        return NO_VALUE;
+    /* May be NO_VALUE if @property does not apply to this LV */
+    return v.value.integer;
+}
+
+static char const *get_lv_property_string(lv_t lv, char const *property)
+{
+    lvm_property_value_t v;
+
+    v = lvm_lv_get_property(lv, property);
+    if (!v.is_valid || !v.is_string)
+        return NULL;
+    return v.value.string;
+}
+
 static void lvm_submit (char const *plugin_instance, char const *type_instance,
         uint64_t ivalue)
 {
@@ -45,24 +71,95 @@ static void lvm_submit (char const *plugin_instance, char const *type_instance,
     plugin_dispatch_values (&vl);
 }
 
-static int vg_read(vg_t vg, char const *vg_name)
+static void report_lv_utilization(lv_t lv, char const *vg_name,
+        char const *lv_name, uint64_t lv_size,
+        char const *used_percent_property)
+{
+    uint64_t used_percent_unscaled;
+    uint64_t used_bytes;
+    char plugin_instance[DATA_MAX_NAME_LEN];
+
+    used_percent_unscaled = get_lv_property_int(lv, used_percent_property);
+    if (used_percent_unscaled == NO_VALUE)
+        return;
+    used_bytes = lv_size * (used_percent_unscaled * PERCENT_SCALE_FACTOR);
+
+    ssnprintf(plugin_instance, sizeof(plugin_instance), "%s-%s",
+            vg_name, lv_name);
+    lvm_submit(plugin_instance, "used", used_bytes);
+    lvm_submit(plugin_instance, "free", lv_size - used_bytes);
+}
+
+static void report_thin_pool_utilization(lv_t lv, char const *vg_name,
+        uint64_t lv_size)
+{
+    char const *data_lv;
+    char const *metadata_lv;
+    uint64_t metadata_size;
+
+    data_lv = get_lv_property_string(lv, "data_lv");
+    metadata_lv = get_lv_property_string(lv, "metadata_lv");
+    metadata_size = get_lv_property_int(lv, "lv_metadata_size");
+    if (data_lv == NULL || metadata_lv == NULL || metadata_size == NO_VALUE)
+        return;
+
+    report_lv_utilization(lv, vg_name, data_lv, lv_size, "data_percent");
+    report_lv_utilization(lv, vg_name, metadata_lv, metadata_size,
+            "metadata_percent");
+}
+
+static void vg_read(vg_t vg, char const *vg_name)
 {
     struct dm_list *lvs;
     struct lvm_lv_list *lvl;
+    char const *name;
+    char const *attrs;
+    uint64_t size;
 
     lvm_submit (vg_name, "free", lvm_vg_get_free_size(vg));
 
     lvs = lvm_vg_list_lvs(vg);
     if (!lvs) {
         /* no VGs are defined, which is not an error per se */
-        return (0);
+        return;
     }
 
     dm_list_iterate_items(lvl, lvs) {
          lvm_submit(vg_name, lvm_lv_get_name(lvl->lv), lvm_lv_get_size(lvl->lv));
     }
 
-    return (0);
+    dm_list_iterate_items(lvl, lvs) {
+        name = lvm_lv_get_name(lvl->lv);
+        attrs = get_lv_property_string(lvl->lv, "lv_attr");
+        size = lvm_lv_get_size(lvl->lv);
+        if (name == NULL || attrs == NULL || size == NO_VALUE)
+            continue;
+
+        /* Condition on volume type.  We want the reported sizes in the
+           volume group to sum to the size of the volume group, so we ignore
+           virtual volumes.  */
+        switch (attrs[0]) {
+            case 's':
+            case 'S':
+                /* Snapshot.  Also report used/free space. */
+                report_lv_utilization(lvl->lv, vg_name, name, size,
+                        "data_percent");
+                break;
+            case 't':
+                /* Thin pool virtual volume.  We report the underlying data
+                   and metadata volumes, not this one.  Report used/free
+                   space, then ignore. */
+                report_thin_pool_utilization(lvl->lv, vg_name, size);
+                continue;
+            case 'v':
+                /* Virtual volume.  Ignore. */
+                continue;
+            case 'V':
+                /* Thin volume or thin snapshot.  Ignore. */
+                continue;
+        }
+        lvm_submit(vg_name, name, size);
+    }
 }
 
 static int lvm_read(void)