python: Added support for subsecond interval.
[collectd.git] / src / netapp.c
index b11e9fa..1640cfd 100644 (file)
@@ -38,8 +38,8 @@ typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *dat
 
 struct cna_interval_s
 {
-       time_t interval;
-       time_t last_read;
+       cdtime_t interval;
+       cdtime_t last_read;
 };
 typedef struct cna_interval_s cna_interval_t;
 
@@ -79,7 +79,7 @@ typedef struct {
        cna_interval_t interval;
        na_elem_t *query;
 
-       time_t timestamp;
+       cdtime_t timestamp;
        uint64_t name_cache_hit;
        uint64_t name_cache_miss;
        uint64_t find_dir_hit;
@@ -104,7 +104,7 @@ typedef struct {
 typedef struct disk_s {
        char *name;
        uint32_t flags;
-       time_t timestamp;
+       cdtime_t timestamp;
        uint64_t disk_busy;
        uint64_t base_for_disk_busy;
        double disk_busy_percent;
@@ -153,7 +153,7 @@ typedef struct data_volume_perf_s data_volume_perf_t;
 struct data_volume_perf_s {
        char *name;
        uint32_t flags;
-       time_t timestamp;
+       cdtime_t timestamp;
 
        uint64_t read_bytes;
        uint64_t write_bytes;
@@ -242,7 +242,7 @@ struct host_config_s {
        int port;
        char *username;
        char *password;
-       int interval;
+       cdtime_t interval;
 
        na_server_t *srv;
        cfg_wafl_t *cfg_wafl;
@@ -253,11 +253,6 @@ struct host_config_s {
 
        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}
-
-static host_config_t *global_host_config;
 
 /*
  * Free functions
@@ -571,7 +566,7 @@ static int submit_values (const char *host, /* {{{ */
                const char *plugin_inst,
                const char *type, const char *type_inst,
                value_t *values, int values_len,
-               time_t timestamp)
+               cdtime_t timestamp)
 {
        value_list_t vl = VALUE_LIST_INIT;
 
@@ -597,7 +592,7 @@ static int submit_values (const char *host, /* {{{ */
 
 static int submit_two_counters (const char *host, const char *plugin_inst, /* {{{ */
                const char *type, const char *type_inst, counter_t val0, counter_t val1,
-               time_t timestamp)
+               cdtime_t timestamp)
 {
        value_t values[2];
 
@@ -609,7 +604,7 @@ static int submit_two_counters (const char *host, const char *plugin_inst, /* {{
 } /* }}} int submit_two_counters */
 
 static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */
-               const char *type, const char *type_inst, counter_t counter, time_t timestamp)
+               const char *type, const char *type_inst, counter_t counter, cdtime_t timestamp)
 {
        value_t v;
 
@@ -621,7 +616,7 @@ static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */
 
 static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */
                const char *type, const char *type_inst, gauge_t val0, gauge_t val1,
-               time_t timestamp)
+               cdtime_t timestamp)
 {
        value_t values[2];
 
@@ -633,7 +628,7 @@ static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ *
 } /* }}} int submit_two_gauge */
 
 static int submit_double (const char *host, const char *plugin_inst, /* {{{ */
-               const char *type, const char *type_inst, double d, time_t timestamp)
+               const char *type, const char *type_inst, double d, cdtime_t timestamp)
 {
        value_t v;
 
@@ -652,7 +647,7 @@ static int submit_cache_ratio (const char *host, /* {{{ */
                uint64_t new_misses,
                uint64_t old_hits,
                uint64_t old_misses,
-               time_t timestamp)
+               cdtime_t timestamp)
 {
        value_t v;
 
@@ -817,6 +812,16 @@ static int submit_volume_perf_data (const char *hostname, /* {{{ */
        return (0);
 } /* }}} int submit_volume_perf_data */
 
+static cdtime_t cna_child_get_cdtime (na_elem_t *data) /* {{{ */
+{
+       time_t t;
+
+       t = (time_t) na_child_get_uint64 (data, "timestamp", /* default = */ 0);
+
+       return (TIME_T_TO_CDTIME_T (t));
+} /* }}} cdtime_t cna_child_get_cdtime */
+
+
 /* 
  * Query functions
  *
@@ -836,13 +841,14 @@ static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /*
 
        memset (&perf_data, 0, sizeof (perf_data));
        
-       perf_data.timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
+       perf_data.timestamp = cna_child_get_cdtime (data);
 
        instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
        if (instances == NULL)
        {
                ERROR ("netapp plugin: cna_handle_wafl_data: "
-                               "na_elem_child (\"instances\") failed.");
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
                return (-1);
        }
 
@@ -850,7 +856,8 @@ static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /*
        if (plugin_inst == NULL)
        {
                ERROR ("netapp plugin: cna_handle_wafl_data: "
-                               "na_child_get_string (\"name\") failed.");
+                               "na_child_get_string (\"name\") failed "
+                               "for host %s.", hostname);
                return (-1);
        }
 
@@ -897,7 +904,8 @@ static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /*
                        perf_data.flags |= HAVE_WAFL_INODE_CACHE_MISS;
                } else {
                        DEBUG("netapp plugin: cna_handle_wafl_data: "
-                                       "Found unexpected child: %s", name);
+                                       "Found unexpected child: %s "
+                                       "for host %s.", name, hostname);
                }
        }
 
@@ -930,14 +938,14 @@ static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */
                ERROR ("netapp plugin: na_elem_new failed.");
                return (-1);
        }
-       na_child_add_string(e, "foo", "name_cache_hit");
-       na_child_add_string(e, "foo", "name_cache_miss");
-       na_child_add_string(e, "foo", "find_dir_hit");
-       na_child_add_string(e, "foo", "find_dir_miss");
-       na_child_add_string(e, "foo", "buf_hash_hit");
-       na_child_add_string(e, "foo", "buf_hash_miss");
-       na_child_add_string(e, "foo", "inode_cache_hit");
-       na_child_add_string(e, "foo", "inode_cache_miss");
+       na_child_add_string(e, "counter", "name_cache_hit");
+       na_child_add_string(e, "counter", "name_cache_miss");
+       na_child_add_string(e, "counter", "find_dir_hit");
+       na_child_add_string(e, "counter", "find_dir_miss");
+       na_child_add_string(e, "counter", "buf_hash_hit");
+       na_child_add_string(e, "counter", "buf_hash_miss");
+       na_child_add_string(e, "counter", "inode_cache_hit");
+       na_child_add_string(e, "counter", "inode_cache_miss");
 
        na_child_add(cw->query, e);
 
@@ -948,7 +956,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -957,7 +965,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
        if (host->cfg_wafl == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_wafl->interval.interval + host->cfg_wafl->interval.last_read) > now)
                return (0);
 
@@ -969,8 +977,8 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
        data = na_server_invoke_elem(host->srv, host->cfg_wafl->query);
        if (na_results_status (data) != NA_OK)
        {
-               ERROR ("netapp plugin: cna_query_wafl: na_server_invoke_elem failed: %s",
-                               na_results_reason (data));
+               ERROR ("netapp plugin: cna_query_wafl: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
                na_elem_free (data);
                return (-1);
        }
@@ -988,7 +996,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */
 static int cna_handle_disk_data (const char *hostname, /* {{{ */
                cfg_disk_t *cfg_disk, na_elem_t *data)
 {
-       time_t timestamp;
+       cdtime_t timestamp;
        na_elem_t *instances;
        na_elem_t *instance;
        na_elem_iter_t instance_iter;
@@ -997,13 +1005,14 @@ static int cna_handle_disk_data (const char *hostname, /* {{{ */
        if ((cfg_disk == NULL) || (data == NULL))
                return (EINVAL);
        
-       timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0);
+       timestamp = cna_child_get_cdtime (data);
 
        instances = na_elem_child (data, "instances");
        if (instances == NULL)
        {
                ERROR ("netapp plugin: cna_handle_disk_data: "
-                               "na_elem_child (\"instances\") failed.");
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
                return (-1);
        }
 
@@ -1130,8 +1139,8 @@ static int cna_setup_disk (cfg_disk_t *cd) /* {{{ */
                ERROR ("netapp plugin: na_elem_new failed.");
                return (-1);
        }
-       na_child_add_string(e, "foo", "disk_busy");
-       na_child_add_string(e, "foo", "base_for_disk_busy");
+       na_child_add_string(e, "counter", "disk_busy");
+       na_child_add_string(e, "counter", "base_for_disk_busy");
        na_child_add(cd->query, e);
 
        return (0);
@@ -1141,7 +1150,7 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1151,7 +1160,7 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
        if (host->cfg_disk == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_disk->interval.interval + host->cfg_disk->interval.last_read) > now)
                return (0);
 
@@ -1163,8 +1172,8 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
        data = na_server_invoke_elem(host->srv, host->cfg_disk->query);
        if (na_results_status (data) != NA_OK)
        {
-               ERROR ("netapp plugin: cna_query_disk: na_server_invoke_elem failed: %s",
-                               na_results_reason (data));
+               ERROR ("netapp plugin: cna_query_disk: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
                na_elem_free (data);
                return (-1);
        }
@@ -1182,18 +1191,19 @@ static int cna_query_disk (host_config_t *host) /* {{{ */
 static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */
                cfg_volume_perf_t *cvp, na_elem_t *data)
 {
-       time_t timestamp;
+       cdtime_t timestamp;
        na_elem_t *elem_instances;
        na_elem_iter_t iter_instances;
        na_elem_t *elem_instance;
        
-       timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0);
+       timestamp = cna_child_get_cdtime (data);
 
        elem_instances = na_elem_child(data, "instances");
        if (elem_instances == NULL)
        {
                ERROR ("netapp plugin: handle_volume_perf_data: "
-                               "na_elem_child (\"instances\") failed.");
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
                return (-1);
        }
 
@@ -1296,13 +1306,12 @@ static int cna_setup_volume_perf (cfg_volume_perf_t *cd) /* {{{ */
                ERROR ("netapp plugin: na_elem_new failed.");
                return (-1);
        }
-       /* "foo" means: This string has to be here but the content doesn't matter. */
-       na_child_add_string(e, "foo", "read_ops");
-       na_child_add_string(e, "foo", "write_ops");
-       na_child_add_string(e, "foo", "read_data");
-       na_child_add_string(e, "foo", "write_data");
-       na_child_add_string(e, "foo", "read_latency");
-       na_child_add_string(e, "foo", "write_latency");
+       na_child_add_string(e, "counter", "read_ops");
+       na_child_add_string(e, "counter", "write_ops");
+       na_child_add_string(e, "counter", "read_data");
+       na_child_add_string(e, "counter", "write_data");
+       na_child_add_string(e, "counter", "read_latency");
+       na_child_add_string(e, "counter", "write_latency");
        na_child_add(cd->query, e);
 
        return (0);
@@ -1312,7 +1321,7 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1322,7 +1331,7 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */
        if (host->cfg_volume_perf == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_volume_perf->interval.interval + host->cfg_volume_perf->interval.last_read) > now)
                return (0);
 
@@ -1334,8 +1343,8 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */
        data = na_server_invoke_elem (host->srv, host->cfg_volume_perf->query);
        if (na_results_status (data) != NA_OK)
        {
-               ERROR ("netapp plugin: cna_query_volume_perf: na_server_invoke_elem failed: %s",
-                               na_results_reason (data));
+               ERROR ("netapp plugin: cna_query_volume_perf: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
                na_elem_free (data);
                return (-1);
        }
@@ -1390,8 +1399,8 @@ static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
                        else
                        {
                                ERROR ("netapp plugin: (norm_used = %"PRIu64") < (snap_norm_used = "
-                                               "%"PRIu64"). Invalidating both.",
-                                               norm_used, snap_norm_used);
+                                               "%"PRIu64") for host %s. Invalidating both.",
+                                               norm_used, snap_norm_used, hostname);
                                v->flags &= ~(HAVE_VOLUME_USAGE_NORM_USED | HAVE_VOLUME_USAGE_SNAP_USED);
                        }
                }
@@ -1441,7 +1450,7 @@ static int cna_change_volume_status (const char *hostname, /* {{{ */
        notification_t n;
 
        memset (&n, 0, sizeof (&n));
-       n.time = time (NULL);
+       n.time = cdtime ();
        sstrncpy (n.host, hostname, sizeof (n.host));
        sstrncpy (n.plugin, "netapp", sizeof (n.plugin));
        sstrncpy (n.plugin_instance, v->name, sizeof (n.plugin_instance));
@@ -1476,8 +1485,8 @@ static void cna_handle_volume_snap_usage(const host_config_t *host, /* {{{ */
                                cna_change_volume_status (host->name, v);
                } else {
                        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));
+                                       "volume \"%s\" on host %s failed with error %d: %s", v->name,
+                                       host->name, na_results_errno(data), na_results_reason(data));
                }
                na_elem_free(data);
                return;
@@ -1490,7 +1499,8 @@ static void cna_handle_volume_snap_usage(const host_config_t *host, /* {{{ */
        if (elem_snapshots == NULL)
        {
                ERROR ("netapp plugin: cna_handle_volume_snap_usage: "
-                               "na_elem_child (\"snapshots\") failed.");
+                               "na_elem_child (\"snapshots\") failed "
+                               "for host %s.", host->name);
                na_elem_free(data);
                return;
        }
@@ -1524,7 +1534,8 @@ static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
        if (elem_volumes == NULL)
        {
                ERROR ("netapp plugin: cna_handle_volume_usage_data: "
-                               "na_elem_child (\"volumes\") failed.");
+                               "na_elem_child (\"volumes\") failed "
+                               "for host %s.", host->name);
                return (-1);
        }
 
@@ -1674,7 +1685,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1684,7 +1695,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
        if (host->cfg_volume_usage == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_volume_usage->interval.interval + host->cfg_volume_usage->interval.last_read) > now)
                return (0);
 
@@ -1696,8 +1707,8 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */
        data = na_server_invoke_elem(host->srv, host->cfg_volume_usage->query);
        if (na_results_status (data) != NA_OK)
        {
-               ERROR ("netapp plugin: cna_query_volume_usage: na_server_invoke_elem failed: %s",
-                               na_results_reason (data));
+               ERROR ("netapp plugin: cna_query_volume_usage: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
                na_elem_free (data);
                return (-1);
        }
@@ -1725,15 +1736,16 @@ static int cna_handle_system_data (const char *hostname, /* {{{ */
        uint32_t counter_flags = 0;
 
        const char *instance;
-       time_t timestamp;
+       cdtime_t timestamp;
        
-       timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
+       timestamp = cna_child_get_cdtime (data);
 
        instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
        if (instances == NULL)
        {
                ERROR ("netapp plugin: cna_handle_system_data: "
-                               "na_elem_child (\"instances\") failed.");
+                               "na_elem_child (\"instances\") failed "
+                               "for host %s.", hostname);
                return (-1);
        }
 
@@ -1741,7 +1753,8 @@ static int cna_handle_system_data (const char *hostname, /* {{{ */
        if (instance == NULL)
        {
                ERROR ("netapp plugin: cna_handle_system_data: "
-                               "na_child_get_string (\"name\") failed.");
+                               "na_child_get_string (\"name\") failed "
+                               "for host %s.", hostname);
                return (-1);
        }
 
@@ -1832,7 +1845,7 @@ static int cna_query_system (host_config_t *host) /* {{{ */
 {
        na_elem_t *data;
        int status;
-       time_t now;
+       cdtime_t now;
 
        if (host == NULL)
                return (EINVAL);
@@ -1841,7 +1854,7 @@ static int cna_query_system (host_config_t *host) /* {{{ */
        if (host->cfg_system == NULL)
                return (0);
 
-       now = time (NULL);
+       now = cdtime ();
        if ((host->cfg_system->interval.interval + host->cfg_system->interval.last_read) > now)
                return (0);
 
@@ -1853,8 +1866,8 @@ static int cna_query_system (host_config_t *host) /* {{{ */
        data = na_server_invoke_elem(host->srv, host->cfg_system->query);
        if (na_results_status (data) != NA_OK)
        {
-               ERROR ("netapp plugin: cna_query_system: na_server_invoke_elem failed: %s",
-                               na_results_reason (data));
+               ERROR ("netapp plugin: cna_query_system: na_server_invoke_elem failed for host %s: %s",
+                               host->name, na_results_reason (data));
                na_elem_free (data);
                return (-1);
        }
@@ -1898,23 +1911,12 @@ static int cna_config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */
 static int cna_config_get_interval (const oconfig_item_t *ci, /* {{{ */
                cna_interval_t *out_interval)
 {
-       time_t tmp;
-
-       if ((ci == NULL) || (out_interval == NULL))
-               return (EINVAL);
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
-       {
-               WARNING ("netapp plugin: The `Interval' option needs exactly one numeric argument.");
-               return (-1);
-       }
+       cdtime_t tmp = 0;
+       int status;
 
-       tmp = (time_t) (ci->values[0].value.number + .5);
-       if (tmp < 1)
-       {
-               WARNING ("netapp plugin: The `Interval' option needs a positive integer argument.");
-               return (-1);
-       }
+       status = cf_util_get_cdtime (ci, &tmp);
+       if (status != 0)
+               return (status);
 
        out_interval->interval = tmp;
        out_interval->last_read = 0;
@@ -2356,8 +2358,7 @@ static int cna_config_system (host_config_t *host, /* {{{ */
 } /* }}} int cna_config_system */
 
 /* Corresponds to a <Host /> block. */
-static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
-               const host_config_t *default_host)
+static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
 {
        oconfig_item_t *item;
        host_config_t *host;
@@ -2370,7 +2371,18 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
        }
 
        host = malloc(sizeof(*host));
-       memcpy (host, default_host, sizeof (*host));
+       memset (host, 0, sizeof (*host));
+       host->name = NULL;
+       host->protocol = NA_SERVER_TRANSPORT_HTTPS;
+       host->host = NULL;
+       host->username = NULL;
+       host->password = NULL;
+       host->srv = NULL;
+       host->cfg_wafl = NULL;
+       host->cfg_disk = NULL;
+       host->cfg_volume_perf = NULL;
+       host->cfg_volume_usage = NULL;
+       host->cfg_system = NULL;
 
        status = cf_util_get_string (ci, &host->name);
        if (status != 0)
@@ -2404,11 +2416,7 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
                } else if (!strcasecmp(item->key, "Password")) {
                        status = cf_util_get_string (item, &host->password);
                } else if (!strcasecmp(item->key, "Interval")) {
-                       if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_NUMBER || item->values[0].value.number != (int) item->values[0].value.number || item->values[0].value.number < 2) {
-                               WARNING("netapp plugin: \"Interval\" of host %s needs exactly one integer argument.", ci->values[0].value.string);
-                               continue;
-                       }
-                       host->interval = item->values[0].value.number;
+                       status = cf_util_get_cdtime (item, &host->interval);
                } else if (!strcasecmp(item->key, "WAFL")) {
                        cna_config_wafl(host, item);
                } else if (!strcasecmp(item->key, "Disks")) {
@@ -2457,15 +2465,36 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
  *
  * Pretty standard stuff here.
  */
-static int cna_init(void) { /* {{{ */
-       char err[256];
-       host_config_t *host;
-       
-       if (!global_host_config) {
-               WARNING("netapp plugin: Plugin loaded but no hosts defined.");
-               return 1;
+static int cna_init_host (host_config_t *host) /* {{{ */
+{
+       if (host == NULL)
+               return (EINVAL);
+
+       if (host->srv != NULL)
+               return (0);
+
+       /* Request version 1.1 of the ONTAP API */
+       host->srv = na_server_open(host->host,
+                       /* major version = */ 1, /* minor version = */ 1); 
+       if (host->srv == NULL) {
+               ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
+               return (-1);
        }
 
+       na_server_set_transport_type(host->srv, host->protocol,
+                       /* transportarg = */ NULL);
+       na_server_set_port(host->srv, host->port);
+       na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
+       na_server_adminuser(host->srv, host->username, host->password);
+       na_server_set_timeout(host->srv, 5 /* seconds */);
+
+       return 0;
+} /* }}} int cna_init_host */
+
+static int cna_init (void) /* {{{ */
+{
+       char err[256];
+
        memset (err, 0, sizeof (err));
        if (!na_startup(err, sizeof(err))) {
                err[sizeof (err) - 1] = 0;
@@ -2473,84 +2502,75 @@ static int cna_init(void) { /* {{{ */
                return 1;
        }
 
-       for (host = global_host_config; host; host = host->next) {
-               /* Request version 1.1 of the ONTAP API */
-               host->srv = na_server_open(host->host,
-                               /* major version = */ 1, /* minor version = */ 1); 
-               if (host->srv == NULL) {
-                       ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
-                       continue;
-               }
+       return (0);
+} /* }}} cna_init */
 
-               if (host->interval < interval_g)
-                       host->interval = interval_g;
+static int cna_read (user_data_t *ud) { /* {{{ */
+       host_config_t *host;
+       int status;
+
+       if ((ud == NULL) || (ud->data == NULL))
+               return (-1);
+
+       host = ud->data;
+
+       status = cna_init_host (host);
+       if (status != 0)
+               return (status);
+       
+       cna_query_wafl (host);
+       cna_query_disk (host);
+       cna_query_volume_perf (host);
+       cna_query_volume_usage (host);
+       cna_query_system (host);
 
-               na_server_set_transport_type(host->srv, host->protocol,
-                               /* transportarg = */ NULL);
-               na_server_set_port(host->srv, host->port);
-               na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
-               na_server_adminuser(host->srv, host->username, host->password);
-               na_server_set_timeout(host->srv, 5 /* seconds */);
-       }
        return 0;
-} /* }}} int cna_init */
+} /* }}} int cna_read */
 
 static int cna_config (oconfig_item_t *ci) { /* {{{ */
        int i;
        oconfig_item_t *item;
-       host_config_t default_host = HOST_INIT;
-       
+
        for (i = 0; i < ci->children_num; ++i) {
                item = ci->children + i;
 
-               if (!strcasecmp(item->key, "Host")) {
+               if (strcasecmp(item->key, "Host") == 0)
+               {
                        host_config_t *host;
-                       host_config_t *tmp;
+                       char cb_name[256];
+                       struct timespec interval;
+                       user_data_t ud;
 
-                       host = cna_config_host(item, &default_host);
+                       host = cna_config_host (item);
                        if (host == NULL)
                                continue;
 
-                       for (tmp = global_host_config; tmp != NULL; tmp = tmp->next)
-                       {
-                               if (strcasecmp (host->name, tmp->name) == 0)
-                                       WARNING ("netapp plugin: Duplicate definition of host `%s'. "
-                                                       "This is probably a bad idea.",
-                                                       host->name);
+                       ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name);
 
-                               if (tmp->next == NULL)
-                                       break;
-                       }
+                       CDTIME_T_TO_TIMESPEC (host->interval, &interval);
 
-                       host->next = NULL;
-                       if (tmp == NULL)
-                               global_host_config = host;
-                       else
-                               tmp->next = host;
-               } else {
+                       memset (&ud, 0, sizeof (ud));
+                       ud.data = host;
+                       ud.free_func = (void (*) (void *)) free_host_config;
+
+                       plugin_register_complex_read (/* group = */ NULL, cb_name,
+                                       /* callback  = */ cna_read, 
+                                       /* interval  = */ (host->interval > 0) ? &interval : NULL,
+                                       /* user data = */ &ud);
+                       continue;
+               }
+               else /* if (item->key != "Host") */
+               {
                        WARNING("netapp plugin: Ignoring unknown config option \"%s\".", item->key);
                }
        }
        return 0;
 } /* }}} int cna_config */
 
-static int cna_read (void) { /* {{{ */
-       host_config_t *host;
-       
-       for (host = global_host_config; host; host = host->next) {
-               cna_query_wafl (host);
-               cna_query_disk (host);
-               cna_query_volume_perf (host);
-               cna_query_volume_usage (host);
-               cna_query_system (host);
-       }
-       return 0;
-} /* }}} int cna_read */
-
 static int cna_shutdown (void) /* {{{ */
 {
-       free_host_config (global_host_config);
-       global_host_config = NULL;
+       /* Clean up system resources and stuff. */
+       na_shutdown ();
 
        return (0);
 } /* }}} int cna_shutdown */
@@ -2558,7 +2578,6 @@ static int cna_shutdown (void) /* {{{ */
 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);
 }