Fixed VolumePerf data collection.
[collectd.git] / src / netapp.c
index b50b490..f47b036 100644 (file)
@@ -29,6 +29,7 @@
 #include "utils_ignorelist.h"
 
 #include <netapp_api.h>
+#include <netapp_errno.h>
 
 #define HAS_ALL_FLAGS(has,needs) (((has) & (needs)) == (needs))
 
@@ -195,6 +196,8 @@ struct data_volume_usage_s {
        char *name;
        uint32_t flags;
 
+       na_elem_t *snap_query;
+
        uint64_t norm_free;
        uint64_t norm_used;
        uint64_t snap_reserved;
@@ -231,33 +234,6 @@ typedef struct {
 } cfg_system_t;
 /* }}} cfg_system_t */
 
-typedef struct service_config_s {
-       na_elem_t *query;
-       service_handler_t *handler;
-       int multiplier;
-       int skip_countdown;
-       int interval;
-       void *data;
-       struct service_config_s *next;
-} cfg_service_t;
-#define SERVICE_INIT {0, 0, 1, 1, 0, 0, 0}
-
-/*!
- * \brief Struct representing a volume.
- *
- * A volume currently has a name and two sets of values:
- *
- *  - Performance data, such as bytes read/written, number of operations
- *    performed and average time per operation.
- *
- *  - Usage data, i. e. amount of used and free space in the volume.
- */
-typedef struct volume_s {
-       char *name;
-       data_volume_perf_t perf_data;
-       struct volume_s *next;
-} volume_t;
-
 struct host_config_s {
        char *name;
        na_server_transport_t protocol;
@@ -268,18 +244,16 @@ struct host_config_s {
        int interval;
 
        na_server_t *srv;
-       cfg_service_t *services;
        cfg_wafl_t *cfg_wafl;
        cfg_disk_t *cfg_disk;
        cfg_volume_perf_t *cfg_volume_perf;
        cfg_volume_usage_t *cfg_volume_usage;
        cfg_system_t *cfg_system;
-       volume_t *volumes;
 
        struct host_config_s *next;
 };
 #define HOST_INIT { NULL, NA_SERVER_TRANSPORT_HTTPS, NULL, 0, NULL, NULL, 0, \
-       NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+       NULL, NULL, NULL, NULL, NULL, NULL, \
        NULL}
 
 static host_config_t *global_host_config;
@@ -289,21 +263,6 @@ static host_config_t *global_host_config;
  *
  * Used to free the various structures above.
  */
-static void free_volume (volume_t *volume) /* {{{ */
-{
-       volume_t *next;
-
-       if (volume == NULL)
-               return;
-
-       next = volume->next;
-
-       sfree (volume->name);
-       sfree (volume);
-
-       free_volume (next);
-} /* }}} void free_volume */
-
 static void free_disk (disk_t *disk) /* {{{ */
 {
        disk_t *next;
@@ -387,6 +346,8 @@ static void free_cfg_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
        {
                data_volume_usage_t *next = data->next;
                sfree (data->name);
+               if (data->snap_query != NULL)
+                       na_elem_free(data->snap_query);
                sfree (data);
                data = next;
        }
@@ -408,23 +369,6 @@ static void free_cfg_system (cfg_system_t *cs) /* {{{ */
        sfree (cs);
 } /* }}} void free_cfg_system */
 
-static void free_cfg_service (cfg_service_t *service) /* {{{ */
-{
-       cfg_service_t *next;
-
-       if (service == NULL)
-               return;
-       
-       next = service->next;
-
-       /* FIXME: Free service->data? */
-       na_elem_free(service->query);
-       
-       sfree (service);
-
-       free_cfg_service (next);
-} /* }}} void free_cfg_service */
-
 static void free_host_config (host_config_t *hc) /* {{{ */
 {
        host_config_t *next;
@@ -439,13 +383,14 @@ static void free_host_config (host_config_t *hc) /* {{{ */
        sfree (hc->username);
        sfree (hc->password);
 
-       free_cfg_service (hc->services);
        free_cfg_disk (hc->cfg_disk);
        free_cfg_wafl (hc->cfg_wafl);
        free_cfg_volume_perf (hc->cfg_volume_perf);
        free_cfg_volume_usage (hc->cfg_volume_usage);
        free_cfg_system (hc->cfg_system);
-       free_volume (hc->volumes);
+
+       if (hc->srv != NULL)
+               na_server_close (hc->srv);
 
        sfree (hc);
 
@@ -533,8 +478,14 @@ static data_volume_usage_t *get_volume_usage (cfg_volume_usage_t *cvu, /* {{{ */
 
        if (ignore_capacity == 0)
                new->flags |= CFG_VOLUME_USAGE_DF;
-       if (ignore_snapshot == 0)
+       if (ignore_snapshot == 0) {
                new->flags |= CFG_VOLUME_USAGE_SNAP;
+               new->snap_query = na_elem_new ("snapshot-list-info");
+               na_child_add_string(new->snap_query, "target-type", "volume");
+               na_child_add_string(new->snap_query, "target-name", name);
+       } else {
+               new->snap_query = NULL;
+       }
 
        /* Add to end of list. */
        if (last == NULL)
@@ -1259,7 +1210,7 @@ static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */
                        continue;
 
                /* get_volume_perf may return NULL if this volume is to be ignored. */
-               v = get_volume_perf (cvp, perf_data.name);
+               v = get_volume_perf (cvp, name);
                if (v == NULL)
                        continue;
 
@@ -1397,30 +1348,57 @@ static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
 
        for (v = cfg_volume->volumes; v != NULL; v = v->next)
        {
+               uint64_t norm_used = v->norm_used;
+               uint64_t norm_free = v->norm_free;
+               uint64_t sis_saved = v->sis_saved;
+               uint64_t snap_reserve_used = 0;
+               uint64_t snap_reserve_free = v->snap_reserved;
+               uint64_t snap_norm_used = v->snap_used;
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD)) {
+                       if (v->snap_reserved > v->snap_used) {
+                               snap_reserve_free = v->snap_reserved - v->snap_used;
+                               snap_reserve_used = v->snap_used;
+                               snap_norm_used = 0;
+                       } else {
+                               snap_reserve_free = 0;
+                               snap_reserve_used = v->snap_reserved;
+                               snap_norm_used = v->snap_used - v->snap_reserved;
+                               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED)
+                                               && (norm_used >= snap_norm_used))
+                                       norm_used -= snap_norm_used;
+                       }
+               }
+
                if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_FREE))
                        submit_double (hostname, /* plugin instance = */ v->name,
                                        "df_complex", "free",
-                                       (double) v->norm_free, /* timestamp = */ 0);
+                                       (double) norm_free, /* timestamp = */ 0);
+
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SIS_SAVED))
+                       submit_double (hostname, /* plugin instance = */ v->name,
+                                       "df_complex", "sis_saved",
+                                       (double) sis_saved, /* timestamp = */ 0);
 
                if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED))
                        submit_double (hostname, /* plugin instance = */ v->name,
                                        "df_complex", "used",
-                                       (double) v->norm_used, /* timestamp = */ 0);
+                                       (double) norm_used, /* timestamp = */ 0);
 
                if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_RSVD))
                        submit_double (hostname, /* plugin instance = */ v->name,
                                        "df_complex", "snap_reserved",
-                                       (double) v->snap_reserved, /* timestamp = */ 0);
+                                       (double) snap_reserve_free, /* timestamp = */ 0);
 
-               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED))
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD))
                        submit_double (hostname, /* plugin instance = */ v->name,
-                                       "df_complex", "snap_used",
-                                       (double) v->snap_used, /* timestamp = */ 0);
+                                       "df_complex", "snap_reserve_used",
+                                       (double) snap_reserve_used, /* timestamp = */ 0);
 
-               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SIS_SAVED))
+               if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED))
                        submit_double (hostname, /* plugin instance = */ v->name,
-                                       "df_complex", "sis_saved",
-                                       (double) v->sis_saved, /* timestamp = */ 0);
+                                       "df_complex", "snap_normal_used",
+                                       (double) snap_norm_used, /* timestamp = */ 0);
 
                /* Clear all the HAVE_* flags */
                v->flags &= ~HAVE_VOLUME_USAGE_ALL;
@@ -1429,7 +1407,49 @@ static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
        return (0);
 } /* }}} int cna_submit_volume_usage_data */
 
-static int cna_handle_volume_usage_data (const char *hostname, /* {{{ */
+static void cna_handle_volume_snap_usage(const host_config_t *host, data_volume_usage_t *v)
+{
+       uint64_t snap_used = 0, value;
+       na_elem_t *data, *elem_snap, *elem_snapshots;
+       na_elem_iter_t iter_snap;
+
+       data = na_server_invoke_elem(host->srv, v->snap_query);
+       if (na_results_status(data) != NA_OK)
+       {
+               if (na_results_errno(data) != EVOLUMEOFFLINE)
+                       ERROR ("netapp plugin: cna_handle_volume_snap_usage: na_server_invoke_elem for "
+                                       "volume \"%s\" failed with error %d: %s", v->name,
+                                       na_results_errno(data), na_results_reason(data));
+               na_elem_free(data);
+               return;
+       }
+
+       elem_snapshots = na_elem_child (data, "snapshots");
+       if (elem_snapshots == NULL)
+       {
+               ERROR ("netapp plugin: cna_handle_volume_snap_usage: "
+                               "na_elem_child (\"snapshots\") failed.");
+               na_elem_free(data);
+               return;
+       }
+
+       iter_snap = na_child_iterator (elem_snapshots);
+       for (elem_snap = na_iterator_next (&iter_snap);
+                       elem_snap != NULL;
+                       elem_snap = na_iterator_next (&iter_snap))
+       {
+               value = na_child_get_uint64(elem_snap, "cumulative-total", 0);
+               if (value > snap_used)
+                       snap_used = value;
+       }
+       na_elem_free (data);
+       /* snap_used is the total size of the oldest snapshot plus all
+        * newer ones in blocks (1KB). */
+       v->snap_used = snap_used * 1024;
+       v->flags |= HAVE_VOLUME_USAGE_SNAP_USED;
+}
+
+static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
                cfg_volume_usage_t *cfg_volume, na_elem_t *data)
 {
        na_elem_t *elem_volume;
@@ -1449,7 +1469,7 @@ static int cna_handle_volume_usage_data (const char *hostname, /* {{{ */
                        elem_volume != NULL;
                        elem_volume = na_iterator_next (&iter_volume))
        {
-               const char *volume_name;
+               const char *volume_name, *state;
 
                data_volume_usage_t *v;
                uint64_t value;
@@ -1462,11 +1482,18 @@ static int cna_handle_volume_usage_data (const char *hostname, /* {{{ */
                if (volume_name == NULL)
                        continue;
 
+               state = na_child_get_string (elem_volume, "state");
+               if ((state == NULL) || (strcmp(state, "online") != 0))
+                       continue;
+
                /* get_volume_usage may return NULL if the volume is to be ignored. */
                v = get_volume_usage (cfg_volume, volume_name);
                if (v == NULL)
                        continue;
 
+               if ((v->flags & CFG_VOLUME_USAGE_SNAP) != 0)
+                       cna_handle_volume_snap_usage(host, v);
+               
                if ((v->flags & CFG_VOLUME_USAGE_DF) == 0)
                        continue;
 
@@ -1486,7 +1513,7 @@ static int cna_handle_volume_usage_data (const char *hostname, /* {{{ */
                value = na_child_get_uint64(elem_volume, "snapshot-blocks-reserved", UINT64_MAX);
                if (value != UINT64_MAX) {
                        /* 1 block == 1024 bytes  as per API docs */
-                       v->norm_used = 1024 * value;
+                       v->snap_reserved = 1024 * value;
                        v->flags |= HAVE_VOLUME_USAGE_SNAP_RSVD;
                }
 
@@ -1498,13 +1525,9 @@ static int cna_handle_volume_usage_data (const char *hostname, /* {{{ */
                if (sis_state == NULL)
                        continue;
 
-               /* If SIS is not enabled, set the HAVE_VOLUME_USAGE_SIS_SAVED flag and set
-                * sis_saved to UINT64_MAX to signal this condition to the submit function. */
-               if (strcmp ("enabled", sis_state) != 0) {
-                       v->sis_saved = UINT64_MAX;
-                       v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
+               /* If SIS is not enabled, there's nothing left to do for this volume. */
+               if (strcmp ("enabled", sis_state) != 0)
                        continue;
-               }
 
                sis_saved_reported = na_child_get_uint64(sis, "size-saved", UINT64_MAX);
                if (sis_saved_reported == UINT64_MAX)
@@ -1562,7 +1585,7 @@ static int cna_handle_volume_usage_data (const char *hostname, /* {{{ */
                } /* }}} end of 32-bit workaround */
        } /* for (elem_volume) */
 
-       return (cna_submit_volume_usage_data (hostname, cfg_volume));
+       return (cna_submit_volume_usage_data (host->name, cfg_volume));
 } /* }}} int cna_handle_volume_usage_data */
 
 static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
@@ -1580,8 +1603,6 @@ static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
                return (-1);
        }
 
-       /* TODO: cvu->snap_query = na_elem_new("snapshot-list-info"); */
-
        return (0);
 } /* }}} int cna_setup_volume_usage */
 
@@ -1617,7 +1638,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
                return (-1);
        }
 
-       status = cna_handle_volume_usage_data (host->name, host->cfg_volume_usage, data);
+       status = cna_handle_volume_usage_data (host, host->cfg_volume_usage, data);
 
        if (status == 0)
                host->cfg_volume_usage->interval.last_read = now;
@@ -1820,14 +1841,14 @@ static int cna_config_get_interval (const oconfig_item_t *ci, /* {{{ */
 
        if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
        {
-               WARNING ("netapp plugin: The `Multiplier' option needs exactly one numeric argument.");
+               WARNING ("netapp plugin: The `Interval' option needs exactly one numeric argument.");
                return (-1);
        }
 
        tmp = (time_t) (ci->values[0].value.number + .5);
        if (tmp < 1)
        {
-               WARNING ("netapp plugin: The `Multiplier' option needs a positive integer argument.");
+               WARNING ("netapp plugin: The `Interval' option needs a positive integer argument.");
                return (-1);
        }
 
@@ -1986,8 +2007,8 @@ static int cna_config_volume_performance (host_config_t *host, /* {{{ */
        return (0);
 } /* }}} int cna_config_volume_performance */
 
-/* Handling of the "Capacity" and "Snapshot" options within a <VolumeUsage />
- * block. */
+/* Handling of the "GetCapacity" and "GetSnapshot" options within a
+ * <VolumeUsage /> block. */
 static void cna_config_volume_usage_option (cfg_volume_usage_t *cvu, /* {{{ */
                const oconfig_item_t *ci)
 {
@@ -2003,9 +2024,9 @@ static void cna_config_volume_usage_option (cfg_volume_usage_t *cvu, /* {{{ */
 
        name = ci->values[0].value.string;
 
-       if (strcasecmp ("Capacity", ci->key) == 0)
+       if (strcasecmp ("GetCapacity", ci->key) == 0)
                il = cvu->il_capacity;
-       else if (strcasecmp ("Snapshot", ci->key) == 0)
+       else if (strcasecmp ("GetSnapshot", ci->key) == 0)
                il = cvu->il_snapshot;
        else
                return;
@@ -2139,17 +2160,17 @@ static int cna_config_wafl(host_config_t *host, oconfig_item_t *ci) /* {{{ */
 
 /*
  * <VolumeUsage>
- *   Capacity "vol0"
- *   Capacity "vol1"
- *   Capacity "vol2"
- *   Capacity "vol3"
- *   Capacity "vol4"
+ *   GetCapacity "vol0"
+ *   GetCapacity "vol1"
+ *   GetCapacity "vol2"
+ *   GetCapacity "vol3"
+ *   GetCapacity "vol4"
  *   IgnoreSelectedCapacity false
  *
- *   Snapshot "vol0"
- *   Snapshot "vol3"
- *   Snapshot "vol4"
- *   Snapshot "vol7"
+ *   GetSnapshot "vol0"
+ *   GetSnapshot "vol3"
+ *   GetSnapshot "vol4"
+ *   GetSnapshot "vol7"
  *   IgnoreSelectedSnapshot false
  * </VolumeUsage>
  */
@@ -2199,9 +2220,9 @@ static int cna_config_volume_usage(host_config_t *host, /* {{{ */
                /* if (!item || !item->key || !*item->key) continue; */
                if (strcasecmp(item->key, "Interval") == 0)
                        cna_config_get_interval (item, &cfg_volume_usage->interval);
-               else if (!strcasecmp(item->key, "Capacity"))
+               else if (!strcasecmp(item->key, "GetCapacity"))
                        cna_config_volume_usage_option (cfg_volume_usage, item);
-               else if (!strcasecmp(item->key, "Snapshot"))
+               else if (!strcasecmp(item->key, "GetSnapshot"))
                        cna_config_volume_usage_option (cfg_volume_usage, item);
                else if (!strcasecmp(item->key, "IgnoreSelectedCapacity"))
                        cna_config_volume_usage_default (cfg_volume_usage, item);
@@ -2217,7 +2238,7 @@ static int cna_config_volume_usage(host_config_t *host, /* {{{ */
 
 /* Corresponds to a <System /> block */
 static int cna_config_system (host_config_t *host, /* {{{ */
-               oconfig_item_t *ci, const cfg_service_t *default_service)
+               oconfig_item_t *ci)
 {
        cfg_system_t *cfg_system;
        int i;
@@ -2272,11 +2293,10 @@ static int cna_config_system (host_config_t *host, /* {{{ */
 
 /* Corresponds to a <Host /> block. */
 static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
-               const host_config_t *default_host, const cfg_service_t *def_def_service)
+               const host_config_t *default_host)
 {
        oconfig_item_t *item;
        host_config_t *host;
-       cfg_service_t default_service = *def_def_service;
        int status;
        int i;
        
@@ -2334,7 +2354,7 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
                } else if (!strcasecmp(item->key, "VolumeUsage")) {
                        cna_config_volume_usage(host, item);
                } else if (!strcasecmp(item->key, "System")) {
-                       cna_config_system(host, item, &default_service);
+                       cna_config_system(host, item);
                } else {
                        WARNING("netapp plugin: Ignoring unknown config option \"%s\" in host block \"%s\".",
                                        item->key, ci->values[0].value.string);
@@ -2415,7 +2435,6 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */
        int i;
        oconfig_item_t *item;
        host_config_t default_host = HOST_INIT;
-       cfg_service_t default_service = SERVICE_INIT;
        
        for (i = 0; i < ci->children_num; ++i) {
                item = ci->children + i;
@@ -2424,7 +2443,7 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */
                        host_config_t *host;
                        host_config_t *tmp;
 
-                       host = cna_config_host(item, &default_host, &default_service);
+                       host = cna_config_host(item, &default_host);
                        if (host == NULL)
                                continue;
 
@@ -2452,29 +2471,9 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */
 } /* }}} int cna_config */
 
 static int cna_read (void) { /* {{{ */
-       na_elem_t *out;
        host_config_t *host;
-       cfg_service_t *service;
        
        for (host = global_host_config; host; host = host->next) {
-               for (service = host->services; service; service = service->next) {
-                       if (--service->skip_countdown > 0) continue;
-                       service->skip_countdown = service->multiplier;
-                       out = na_server_invoke_elem(host->srv, service->query);
-                       if (na_results_status(out) != NA_OK) {
-                               int netapp_errno = na_results_errno(out);
-                               ERROR("netapp plugin: Error %d from host %s: %s", netapp_errno, host->name, na_results_reason(out));
-                               na_elem_free(out);
-                               if (netapp_errno == EIO || netapp_errno == ETIMEDOUT) {
-                                       /* Network problems. Just give up on all other services on this host. */
-                                       break;
-                               }
-                               continue;
-                       }
-                       service->handler(host, out, service->data);
-                       na_elem_free(out);
-               } /* for (host->services) */
-
                cna_query_wafl (host);
                cna_query_disk (host);
                cna_query_volume_perf (host);
@@ -2484,10 +2483,19 @@ static int cna_read (void) { /* {{{ */
        return 0;
 } /* }}} int cna_read */
 
+static int cna_shutdown (void) /* {{{ */
+{
+       free_host_config (global_host_config);
+       global_host_config = NULL;
+
+       return (0);
+} /* }}} int cna_shutdown */
+
 void module_register(void) {
        plugin_register_complex_config("netapp", cna_config);
        plugin_register_init("netapp", cna_init);
        plugin_register_read("netapp", cna_read);
+       plugin_register_shutdown("netapp", cna_shutdown);
 }
 
 /* vim: set sw=2 ts=2 noet fdm=marker : */