+
+ vl.values = values;
+ vl.values_len = values_len;
+
+ if (timestamp > 0)
+ vl.time = timestamp;
+
+ if (host != NULL)
+ sstrncpy (vl.host, host, sizeof (vl.host));
+ else
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "netapp", sizeof (vl.plugin));
+ if (plugin_inst != NULL)
+ sstrncpy (vl.plugin_instance, plugin_inst, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ if (type_inst != NULL)
+ sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+
+ return (plugin_dispatch_values (&vl));
+} /* }}} int submit_uint64 */
+
+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)
+{
+ value_t values[2];
+
+ values[0].counter = val0;
+ values[1].counter = val1;
+
+ return (submit_values (host, plugin_inst, type, type_inst,
+ values, 2, timestamp));
+} /* }}} 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)
+{
+ value_t v;
+
+ v.counter = counter;
+
+ return (submit_values (host, plugin_inst, type, type_inst,
+ &v, 1, timestamp));
+} /* }}} int submit_counter */
+
+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)
+{
+ value_t values[2];
+
+ values[0].gauge = val0;
+ values[1].gauge = val1;
+
+ return (submit_values (host, plugin_inst, type, type_inst,
+ values, 2, timestamp));
+} /* }}} 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)
+{
+ value_t v;
+
+ v.gauge = (gauge_t) d;
+
+ return (submit_values (host, plugin_inst, type, type_inst,
+ &v, 1, timestamp));
+} /* }}} int submit_uint64 */
+
+/* Calculate hit ratio from old and new counters and submit the resulting
+ * percentage. Used by "submit_wafl_data". */
+static int submit_cache_ratio (const char *host, /* {{{ */
+ const char *plugin_inst,
+ const char *type_inst,
+ uint64_t new_hits,
+ uint64_t new_misses,
+ uint64_t old_hits,
+ uint64_t old_misses,
+ time_t timestamp)
+{
+ value_t v;
+
+ if ((new_hits >= old_hits) && (new_misses >= old_misses)) {
+ uint64_t hits;
+ uint64_t misses;
+
+ hits = new_hits - old_hits;
+ misses = new_misses - old_misses;
+
+ v.gauge = 100.0 * ((gauge_t) hits) / ((gauge_t) (hits + misses));
+ } else {
+ v.gauge = NAN;
+ }
+
+ return (submit_values (host, plugin_inst, "cache_ratio", type_inst,
+ &v, 1, timestamp));
+} /* }}} int submit_cache_ratio */
+
+/* Submits all the caches used by WAFL. Uses "submit_cache_ratio". */
+static int submit_wafl_data (const char *hostname, const char *instance, /* {{{ */
+ cfg_wafl_t *old_data, const cfg_wafl_t *new_data)
+{
+ /* Submit requested counters */
+ if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_NAME_CACHE))
+ submit_cache_ratio (hostname, instance, "name_cache_hit",
+ new_data->name_cache_hit, new_data->name_cache_miss,
+ old_data->name_cache_hit, old_data->name_cache_miss,
+ new_data->timestamp);
+
+ if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR))
+ submit_cache_ratio (hostname, instance, "find_dir_hit",
+ new_data->find_dir_hit, new_data->find_dir_miss,
+ old_data->find_dir_hit, old_data->find_dir_miss,
+ new_data->timestamp);
+
+ if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH))
+ submit_cache_ratio (hostname, instance, "buf_hash_hit",
+ new_data->buf_hash_hit, new_data->buf_hash_miss,
+ old_data->buf_hash_hit, old_data->buf_hash_miss,
+ new_data->timestamp);
+
+ if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE))
+ submit_cache_ratio (hostname, instance, "inode_cache_hit",
+ new_data->inode_cache_hit, new_data->inode_cache_miss,
+ old_data->inode_cache_hit, old_data->inode_cache_miss,
+ new_data->timestamp);
+
+ /* Clear old HAVE_* flags */
+ old_data->flags &= ~HAVE_WAFL_ALL;
+
+ /* Copy all counters */
+ old_data->timestamp = new_data->timestamp;
+ old_data->name_cache_hit = new_data->name_cache_hit;
+ old_data->name_cache_miss = new_data->name_cache_miss;
+ old_data->find_dir_hit = new_data->find_dir_hit;
+ old_data->find_dir_miss = new_data->find_dir_miss;
+ old_data->buf_hash_hit = new_data->buf_hash_hit;
+ old_data->buf_hash_miss = new_data->buf_hash_miss;
+ old_data->inode_cache_hit = new_data->inode_cache_hit;
+ old_data->inode_cache_miss = new_data->inode_cache_miss;
+
+ /* Copy HAVE_* flags */
+ old_data->flags |= (new_data->flags & HAVE_WAFL_ALL);
+
+ return (0);
+} /* }}} int submit_wafl_data */
+
+/* Submits volume performance data to the daemon, taking care to honor and
+ * update flags appropriately. */
+static int submit_volume_perf_data (const host_config_t *host, /* {{{ */
+ volume_t *volume,
+ const data_volume_perf_t *new_data)
+{
+ /* Check for and submit disk-octet values */
+ if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_IO)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE))
+ {
+ submit_two_counters (host->name, volume->name, "disk_octets", /* type instance = */ NULL,
+ (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp);
+ }
+
+ /* Check for and submit disk-operations values */
+ if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_OPS)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE))
+ {
+ submit_two_counters (host->name, volume->name, "disk_ops", /* type instance = */ NULL,
+ (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp);
+ }
+
+ /* Check for, calculate and submit disk-latency values */
+ if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_LATENCY
+ | HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
+ | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE)
+ && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
+ | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE))
+ {
+ gauge_t latency_per_op_read;
+ gauge_t latency_per_op_write;
+
+ latency_per_op_read = NAN;
+ latency_per_op_write = NAN;
+
+ /* Check if a counter wrapped around. */
+ if ((new_data->read_ops > volume->perf_data.read_ops)
+ && (new_data->read_latency > volume->perf_data.read_latency))
+ {
+ uint64_t diff_ops_read;
+ uint64_t diff_latency_read;
+
+ diff_ops_read = new_data->read_ops - volume->perf_data.read_ops;
+ diff_latency_read = new_data->read_latency - volume->perf_data.read_latency;
+
+ if (diff_ops_read > 0)
+ latency_per_op_read = ((gauge_t) diff_latency_read) / ((gauge_t) diff_ops_read);
+ }
+
+ if ((new_data->write_ops > volume->perf_data.write_ops)
+ && (new_data->write_latency > volume->perf_data.write_latency))
+ {
+ uint64_t diff_ops_write;
+ uint64_t diff_latency_write;
+
+ diff_ops_write = new_data->write_ops - volume->perf_data.write_ops;
+ diff_latency_write = new_data->write_latency - volume->perf_data.write_latency;
+
+ if (diff_ops_write > 0)
+ latency_per_op_write = ((gauge_t) diff_latency_write) / ((gauge_t) diff_ops_write);
+ }
+
+ submit_two_gauge (host->name, volume->name, "disk_latency", /* type instance = */ NULL,
+ latency_per_op_read, latency_per_op_write, new_data->timestamp);
+ }
+
+ /* Clear all HAVE_* flags. */
+ volume->perf_data.flags &= ~HAVE_VOLUME_PERF_ALL;
+
+ /* Copy all counters */
+ volume->perf_data.timestamp = new_data->timestamp;
+ volume->perf_data.read_bytes = new_data->read_bytes;
+ volume->perf_data.write_bytes = new_data->write_bytes;
+ volume->perf_data.read_ops = new_data->read_ops;
+ volume->perf_data.write_ops = new_data->write_ops;
+ volume->perf_data.read_latency = new_data->read_latency;
+ volume->perf_data.write_latency = new_data->write_latency;
+
+ /* Copy the HAVE_* flags */
+ volume->perf_data.flags |= (new_data->flags & HAVE_VOLUME_PERF_ALL);
+
+ return (0);
+} /* }}} int submit_volume_perf_data */
+
+/*
+ * Query functions
+ *
+ * These functions are called with appropriate data returned by the libnetapp
+ * interface which is parsed and submitted with the above functions.
+ */
+/* Data corresponding to <WAFL /> */
+static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */
+ na_elem_t *data)
+{
+ cfg_wafl_t perf_data;
+ const char *plugin_inst;
+
+ na_elem_t *instances;
+ na_elem_t *counter;
+ na_elem_iter_t counter_iter;
+
+ memset (&perf_data, 0, sizeof (perf_data));