Merge remote branch 'trenkel/st/netapp' into st/netapp
[collectd.git] / src / netapp.c
1 /**
2  * collectd - src/netapp.c
3  * Copyright (C) 2009  Sven Trenkel
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Sven Trenkel <collectd at semidefinite.de>  
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29
30 #include <netapp_api.h>
31
32 #define HAS_ALL_FLAGS(has,needs) (((has) & (needs)) == (needs))
33
34 typedef struct host_config_s host_config_t;
35 typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *data);
36
37 struct cna_interval_s
38 {
39         time_t interval;
40         time_t last_read;
41 };
42 typedef struct cna_interval_s cna_interval_t;
43
44 /*!
45  * \brief Persistent data for system performance counters
46  */
47 #define CFG_SYSTEM_CPU  0x01
48 #define CFG_SYSTEM_NET  0x02
49 #define CFG_SYSTEM_OPS  0x04
50 #define CFG_SYSTEM_DISK 0x08
51 #define CFG_SYSTEM_ALL  0x0F
52 typedef struct {
53         uint32_t flags;
54 } cfg_system_t;
55
56 /*!
57  * \brief Persistent data for WAFL performance counters. (a.k.a. cache performance)
58  *
59  * The cache counters use old counter values to calculate a hit ratio for each
60  * counter. The "cfg_wafl_t" struct therefore contains old counter values along
61  * with flags, which are set if the counter is valid.
62  *
63  * The function "cna_handle_wafl_data" will fill a new structure of this kind
64  * with new values, then pass both, new and old data, to "submit_wafl_data".
65  * That function calculates the hit ratios, submits the calculated values and
66  * updates the old counter values for the next iteration.
67  */
68 #define CFG_WAFL_NAME_CACHE        0x0001
69 #define CFG_WAFL_DIR_CACHE         0x0002
70 #define CFG_WAFL_BUF_CACHE         0x0004
71 #define CFG_WAFL_INODE_CACHE       0x0008
72 #define CFG_WAFL_ALL               0x000F
73 #define HAVE_WAFL_NAME_CACHE_HIT   0x0100
74 #define HAVE_WAFL_NAME_CACHE_MISS  0x0200
75 #define HAVE_WAFL_NAME_CACHE       (HAVE_WAFL_NAME_CACHE_HIT | HAVE_WAFL_NAME_CACHE_MISS)
76 #define HAVE_WAFL_FIND_DIR_HIT     0x0400
77 #define HAVE_WAFL_FIND_DIR_MISS    0x0800
78 #define HAVE_WAFL_FIND_DIR         (HAVE_WAFL_FIND_DIR_HIT | HAVE_WAFL_FIND_DIR_MISS)
79 #define HAVE_WAFL_BUF_HASH_HIT     0x1000
80 #define HAVE_WAFL_BUF_HASH_MISS    0x2000
81 #define HAVE_WAFL_BUF_HASH         (HAVE_WAFL_BUF_HASH_HIT | HAVE_WAFL_BUF_HASH_MISS)
82 #define HAVE_WAFL_INODE_CACHE_HIT  0x4000
83 #define HAVE_WAFL_INODE_CACHE_MISS 0x8000
84 #define HAVE_WAFL_INODE_CACHE      (HAVE_WAFL_INODE_CACHE_HIT | HAVE_WAFL_INODE_CACHE_MISS)
85 #define HAVE_WAFL_ALL              0xff00
86 typedef struct {
87         uint32_t flags;
88         cna_interval_t interval;
89         na_elem_t *query;
90
91         time_t timestamp;
92         uint64_t name_cache_hit;
93         uint64_t name_cache_miss;
94         uint64_t find_dir_hit;
95         uint64_t find_dir_miss;
96         uint64_t buf_hash_hit;
97         uint64_t buf_hash_miss;
98         uint64_t inode_cache_hit;
99         uint64_t inode_cache_miss;
100 } cfg_wafl_t;
101
102 /*!
103  * \brief Persistent data for volume performance data.
104  *
105  * The code below uses the difference of the operations and latency counters to
106  * calculate an average per-operation latency. For this, old counters need to
107  * be stored in the "data_volume_perf_t" structure. The byte-counters are just
108  * kept for completeness sake. The "flags" member indicates if each counter is
109  * valid or not.
110  *
111  * The "query_volume_perf_data" function will fill a new struct of this type
112  * and pass both, old and new data, to "submit_volume_perf_data". In that
113  * function, the per-operation latency is calculated and dispatched, then the
114  * old counters are updated.
115  */
116 #define CFG_VOLUME_PERF_INIT           0x0001
117 #define CFG_VOLUME_PERF_IO             0x0002
118 #define CFG_VOLUME_PERF_OPS            0x0003
119 #define CFG_VOLUME_PERF_LATENCY        0x0008
120 #define CFG_VOLUME_PERF_ALL            0x000F
121 #define HAVE_VOLUME_PERF_BYTES_READ    0x0010
122 #define HAVE_VOLUME_PERF_BYTES_WRITE   0x0020
123 #define HAVE_VOLUME_PERF_OPS_READ      0x0040
124 #define HAVE_VOLUME_PERF_OPS_WRITE     0x0080
125 #define HAVE_VOLUME_PERF_LATENCY_READ  0x0100
126 #define HAVE_VOLUME_PERF_LATENCY_WRITE 0x0200
127 #define HAVE_VOLUME_PERF_ALL           0x03F0
128 typedef struct {
129         uint32_t flags;
130 } cfg_volume_perf_t;
131
132 typedef struct {
133         uint32_t flags;
134         time_t timestamp;
135         uint64_t read_bytes;
136         uint64_t write_bytes;
137         uint64_t read_ops;
138         uint64_t write_ops;
139         uint64_t read_latency;
140         uint64_t write_latency;
141 } data_volume_perf_t;
142
143 /*!
144  * \brief Configuration struct for volume usage data (free / used).
145  */
146 #define CFG_VOLUME_USAGE_INIT           0x0001
147 #define CFG_VOLUME_USAGE_DF             0x0002
148 #define CFG_VOLUME_USAGE_SNAP           0x0004
149 #define HAVE_VOLUME_USAGE_SNAP          0x0008
150 typedef struct {
151         uint32_t flags;
152         uint64_t snap_used;
153 } cfg_volume_usage_t;
154
155 typedef struct service_config_s {
156         na_elem_t *query;
157         service_handler_t *handler;
158         int multiplier;
159         int skip_countdown;
160         int interval;
161         void *data;
162         struct service_config_s *next;
163 } cfg_service_t;
164 #define SERVICE_INIT {0, 0, 1, 1, 0, 0, 0}
165
166 /*!
167  * \brief Struct representing a volume.
168  *
169  * A volume currently has a name and two sets of values:
170  *
171  *  - Performance data, such as bytes read/written, number of operations
172  *    performed and average time per operation.
173  *
174  *  - Usage data, i. e. amount of used and free space in the volume.
175  */
176 typedef struct volume_s {
177         char *name;
178         data_volume_perf_t perf_data;
179         cfg_volume_usage_t cfg_volume_usage;
180         struct volume_s *next;
181 } volume_t;
182
183 /*!
184  * \brief A disk in the NetApp.
185  *
186  * A disk doesn't have any more information than its name at the moment.
187  * The name includes the "disk_" prefix.
188  */
189 #define HAVE_DISK_BUSY   0x10
190 #define HAVE_DISK_BASE   0x20
191 #define HAVE_DISK_ALL    0x30
192 typedef struct disk_s {
193         char *name;
194         uint32_t flags;
195         time_t timestamp;
196         uint64_t disk_busy;
197         uint64_t base_for_disk_busy;
198         double disk_busy_percent;
199         struct disk_s *next;
200 } disk_t;
201
202 #define CFG_DISK_BUSIEST 0x01
203 #define CFG_DISK_ALL     0x01
204 typedef struct {
205         uint32_t flags;
206         cna_interval_t interval;
207         na_elem_t *query;
208         disk_t *disks;
209 } cfg_disk_t;
210
211 struct host_config_s {
212         char *name;
213         na_server_transport_t protocol;
214         char *host;
215         int port;
216         char *username;
217         char *password;
218         int interval;
219
220         na_server_t *srv;
221         cfg_service_t *services;
222         cfg_disk_t *cfg_disk;
223         cfg_wafl_t *cfg_wafl;
224         volume_t *volumes;
225
226         struct host_config_s *next;
227 };
228 #define HOST_INIT { NULL, NA_SERVER_TRANSPORT_HTTPS, NULL, 0, NULL, NULL, 0, \
229         NULL, NULL, NULL, NULL, \
230         NULL}
231
232 static host_config_t *global_host_config;
233
234 /*
235  * Free functions
236  *
237  * Used to free the various structures above.
238  */
239 static void free_volume (volume_t *volume) /* {{{ */
240 {
241         volume_t *next;
242
243         next = volume->next;
244
245         sfree (volume->name);
246         sfree (volume);
247
248         free_volume (next);
249 } /* }}} void free_volume */
250
251 static void free_disk (disk_t *disk) /* {{{ */
252 {
253         disk_t *next;
254
255         next = disk->next;
256
257         sfree (disk->name);
258         sfree (disk);
259
260         free_disk (next);
261 } /* }}} void free_disk */
262
263 static void free_cfg_disk (cfg_disk_t *cfg_disk) /* {{{ */
264 {
265         if (cfg_disk == NULL)
266                 return;
267
268         free_disk (cfg_disk->disks);
269         sfree (cfg_disk);
270 } /* }}} void free_cfg_disk */
271
272 static void free_cfg_service (cfg_service_t *service) /* {{{ */
273 {
274         cfg_service_t *next;
275
276         if (service == NULL)
277                 return;
278         
279         next = service->next;
280
281         /* FIXME: Free service->data? */
282         na_elem_free(service->query);
283         
284         sfree (service);
285
286         free_cfg_service (next);
287 } /* }}} void free_cfg_service */
288
289 static void free_host_config (host_config_t *hc) /* {{{ */
290 {
291         host_config_t *next;
292
293         if (hc == NULL)
294                 return;
295
296         next = hc->next;
297
298         sfree (hc->name);
299         sfree (hc->host);
300         sfree (hc->username);
301         sfree (hc->password);
302
303         free_cfg_service (hc->services);
304         free_cfg_disk (hc->cfg_disk);
305         free_volume (hc->volumes);
306
307         sfree (hc);
308
309         free_host_config (next);
310 } /* }}} void free_host_config */
311
312 /*
313  * Auxiliary functions
314  *
315  * Used to look up volumes and disks or to handle flags.
316  */
317 static volume_t *get_volume (host_config_t *host, const char *name, /* {{{ */
318                 uint32_t vol_usage_flags, uint32_t vol_perf_flags)
319 {
320         volume_t *v;
321
322         if (name == NULL)
323                 return (NULL);
324         
325         /* Make sure the default flags include the init-bit. */
326         if (vol_usage_flags != 0)
327                 vol_usage_flags |= CFG_VOLUME_USAGE_INIT;
328         if (vol_perf_flags != 0)
329                 vol_perf_flags |= CFG_VOLUME_PERF_INIT;
330
331         for (v = host->volumes; v; v = v->next) {
332                 if (strcmp(v->name, name) != 0)
333                         continue;
334
335                 /* Check if the flags have been initialized. */
336                 if (((v->cfg_volume_usage.flags & CFG_VOLUME_USAGE_INIT) == 0)
337                                 && (vol_usage_flags != 0))
338                         v->cfg_volume_usage.flags = vol_usage_flags;
339                 if (((v->perf_data.flags & CFG_VOLUME_PERF_INIT) == 0)
340                                 && (vol_perf_flags != 0))
341                         v->perf_data.flags = vol_perf_flags;
342
343                 return v;
344         }
345
346         DEBUG ("netapp plugin: Allocating new entry for volume %s.", name);
347         v = malloc(sizeof(*v));
348         if (v == NULL)
349                 return (NULL);
350         memset (v, 0, sizeof (*v));
351
352         v->cfg_volume_usage.flags = vol_usage_flags;
353         v->perf_data.flags = vol_perf_flags;
354
355         v->name = strdup(name);
356         if (v->name == NULL) {
357                 sfree (v);
358                 return (NULL);
359         }
360
361         v->next = host->volumes;
362         host->volumes = v;
363
364         return v;
365 } /* }}} volume_t *get_volume */
366
367 static disk_t *get_disk(cfg_disk_t *cd, const char *name) /* {{{ */
368 {
369         disk_t *d;
370
371         if ((cd == NULL) || (name == NULL))
372                 return (NULL);
373
374         for (d = cd->disks; d != NULL; d = d->next) {
375                 if (strcmp(d->name, name) == 0)
376                         return d;
377         }
378
379         d = malloc(sizeof(*d));
380         if (d == NULL)
381                 return (NULL);
382         memset (d, 0, sizeof (*d));
383         d->next = NULL;
384
385         d->name = strdup(name);
386         if (d->name == NULL) {
387                 sfree (d);
388                 return (NULL);
389         }
390
391         d->next = cd->disks;
392         cd->disks = d;
393
394         return d;
395 } /* }}} disk_t *get_disk */
396
397 static void host_set_all_perf_data_flags(const host_config_t *host, /* {{{ */
398                 uint32_t flag, _Bool set)
399 {
400         volume_t *v;
401         
402         for (v = host->volumes; v; v = v->next) {
403                 if (set)
404                         v->perf_data.flags |= flag;
405                 else /* if (!set) */
406                         v->perf_data.flags &= ~flag;
407         }
408 } /* }}} void host_set_all_perf_data_flags */
409
410 static void host_set_all_cfg_volume_usage_flags(const host_config_t *host, /* {{{ */
411                 uint32_t flag, _Bool set) {
412         volume_t *v;
413         
414         for (v = host->volumes; v; v = v->next) {
415                 if (set)
416                         v->cfg_volume_usage.flags |= flag;
417                 else /* if (!set) */
418                         v->cfg_volume_usage.flags &= ~flag;
419         }
420 } /* }}} void host_set_all_cfg_volume_usage_flags */
421
422 /*
423  * Various submit functions.
424  *
425  * They all eventually call "submit_values" which creates a value_list_t and
426  * dispatches it to the daemon.
427  */
428 static int submit_values (const char *host, /* {{{ */
429                 const char *plugin_inst,
430                 const char *type, const char *type_inst,
431                 value_t *values, int values_len,
432                 time_t timestamp)
433 {
434         value_list_t vl = VALUE_LIST_INIT;
435
436         vl.values = values;
437         vl.values_len = values_len;
438
439         if (timestamp > 0)
440                 vl.time = timestamp;
441
442         if (host != NULL)
443                 sstrncpy (vl.host, host, sizeof (vl.host));
444         else
445                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
446         sstrncpy (vl.plugin, "netapp", sizeof (vl.plugin));
447         if (plugin_inst != NULL)
448                 sstrncpy (vl.plugin_instance, plugin_inst, sizeof (vl.plugin_instance));
449         sstrncpy (vl.type, type, sizeof (vl.type));
450         if (type_inst != NULL)
451                 sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
452
453         return (plugin_dispatch_values (&vl));
454 } /* }}} int submit_uint64 */
455
456 static int submit_two_counters (const char *host, const char *plugin_inst, /* {{{ */
457                 const char *type, const char *type_inst, counter_t val0, counter_t val1,
458                 time_t timestamp)
459 {
460         value_t values[2];
461
462         values[0].counter = val0;
463         values[1].counter = val1;
464
465         return (submit_values (host, plugin_inst, type, type_inst,
466                                 values, 2, timestamp));
467 } /* }}} int submit_two_counters */
468
469 static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */
470                 const char *type, const char *type_inst, counter_t counter, time_t timestamp)
471 {
472         value_t v;
473
474         v.counter = counter;
475
476         return (submit_values (host, plugin_inst, type, type_inst,
477                                 &v, 1, timestamp));
478 } /* }}} int submit_counter */
479
480 static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */
481                 const char *type, const char *type_inst, gauge_t val0, gauge_t val1,
482                 time_t timestamp)
483 {
484         value_t values[2];
485
486         values[0].gauge = val0;
487         values[1].gauge = val1;
488
489         return (submit_values (host, plugin_inst, type, type_inst,
490                                 values, 2, timestamp));
491 } /* }}} int submit_two_gauge */
492
493 static int submit_double (const char *host, const char *plugin_inst, /* {{{ */
494                 const char *type, const char *type_inst, double d, time_t timestamp)
495 {
496         value_t v;
497
498         v.gauge = (gauge_t) d;
499
500         return (submit_values (host, plugin_inst, type, type_inst,
501                                 &v, 1, timestamp));
502 } /* }}} int submit_uint64 */
503
504 /* Calculate hit ratio from old and new counters and submit the resulting
505  * percentage. Used by "submit_wafl_data". */
506 static int submit_cache_ratio (const char *host, /* {{{ */
507                 const char *plugin_inst,
508                 const char *type_inst,
509                 uint64_t new_hits,
510                 uint64_t new_misses,
511                 uint64_t old_hits,
512                 uint64_t old_misses,
513                 time_t timestamp)
514 {
515         value_t v;
516
517         if ((new_hits >= old_hits) && (new_misses >= old_misses)) {
518                 uint64_t hits;
519                 uint64_t misses;
520
521                 hits = new_hits - old_hits;
522                 misses = new_misses - old_misses;
523
524                 v.gauge = 100.0 * ((gauge_t) hits) / ((gauge_t) (hits + misses));
525         } else {
526                 v.gauge = NAN;
527         }
528
529         return (submit_values (host, plugin_inst, "cache_ratio", type_inst,
530                                 &v, 1, timestamp));
531 } /* }}} int submit_cache_ratio */
532
533 /* Submits all the caches used by WAFL. Uses "submit_cache_ratio". */
534 static int submit_wafl_data (const char *hostname, const char *instance, /* {{{ */
535                 cfg_wafl_t *old_data, const cfg_wafl_t *new_data)
536 {
537         /* Submit requested counters */
538         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
539                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_NAME_CACHE))
540                 submit_cache_ratio (hostname, instance, "name_cache_hit",
541                                 new_data->name_cache_hit, new_data->name_cache_miss,
542                                 old_data->name_cache_hit, old_data->name_cache_miss,
543                                 new_data->timestamp);
544
545         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR)
546                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR))
547                 submit_cache_ratio (hostname, instance, "find_dir_hit",
548                                 new_data->find_dir_hit, new_data->find_dir_miss,
549                                 old_data->find_dir_hit, old_data->find_dir_miss,
550                                 new_data->timestamp);
551
552         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH)
553                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH))
554                 submit_cache_ratio (hostname, instance, "buf_hash_hit",
555                                 new_data->buf_hash_hit, new_data->buf_hash_miss,
556                                 old_data->buf_hash_hit, old_data->buf_hash_miss,
557                                 new_data->timestamp);
558
559         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE)
560                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE))
561                 submit_cache_ratio (hostname, instance, "inode_cache_hit",
562                                 new_data->inode_cache_hit, new_data->inode_cache_miss,
563                                 old_data->inode_cache_hit, old_data->inode_cache_miss,
564                                 new_data->timestamp);
565
566         /* Clear old HAVE_* flags */
567         old_data->flags &= ~HAVE_WAFL_ALL;
568
569         /* Copy all counters */
570         old_data->timestamp        = new_data->timestamp;
571         old_data->name_cache_hit   = new_data->name_cache_hit;
572         old_data->name_cache_miss  = new_data->name_cache_miss;
573         old_data->find_dir_hit     = new_data->find_dir_hit;
574         old_data->find_dir_miss    = new_data->find_dir_miss;
575         old_data->buf_hash_hit     = new_data->buf_hash_hit;
576         old_data->buf_hash_miss    = new_data->buf_hash_miss;
577         old_data->inode_cache_hit  = new_data->inode_cache_hit;
578         old_data->inode_cache_miss = new_data->inode_cache_miss;
579
580         /* Copy HAVE_* flags */
581         old_data->flags |= (new_data->flags & HAVE_WAFL_ALL);
582
583         return (0);
584 } /* }}} int submit_wafl_data */
585
586 /* Submits volume performance data to the daemon, taking care to honor and
587  * update flags appropriately. */
588 static int submit_volume_perf_data (const host_config_t *host, /* {{{ */
589                 volume_t *volume,
590                 const data_volume_perf_t *new_data)
591 {
592         /* Check for and submit disk-octet values */
593         if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_IO)
594                         && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE))
595         {
596                 submit_two_counters (host->name, volume->name, "disk_octets", /* type instance = */ NULL,
597                                 (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp);
598         }
599
600         /* Check for and submit disk-operations values */
601         if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_OPS)
602                         && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE))
603         {
604                 submit_two_counters (host->name, volume->name, "disk_ops", /* type instance = */ NULL,
605                                 (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp);
606         }
607
608         /* Check for, calculate and submit disk-latency values */
609         if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_LATENCY
610                                 | HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
611                                 | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE)
612                         && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
613                                 | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE))
614         {
615                 gauge_t latency_per_op_read;
616                 gauge_t latency_per_op_write;
617
618                 latency_per_op_read = NAN;
619                 latency_per_op_write = NAN;
620
621                 /* Check if a counter wrapped around. */
622                 if ((new_data->read_ops > volume->perf_data.read_ops)
623                                 && (new_data->read_latency > volume->perf_data.read_latency))
624                 {
625                         uint64_t diff_ops_read;
626                         uint64_t diff_latency_read;
627
628                         diff_ops_read = new_data->read_ops - volume->perf_data.read_ops;
629                         diff_latency_read = new_data->read_latency - volume->perf_data.read_latency;
630
631                         if (diff_ops_read > 0)
632                                 latency_per_op_read = ((gauge_t) diff_latency_read) / ((gauge_t) diff_ops_read);
633                 }
634
635                 if ((new_data->write_ops > volume->perf_data.write_ops)
636                                 && (new_data->write_latency > volume->perf_data.write_latency))
637                 {
638                         uint64_t diff_ops_write;
639                         uint64_t diff_latency_write;
640
641                         diff_ops_write = new_data->write_ops - volume->perf_data.write_ops;
642                         diff_latency_write = new_data->write_latency - volume->perf_data.write_latency;
643
644                         if (diff_ops_write > 0)
645                                 latency_per_op_write = ((gauge_t) diff_latency_write) / ((gauge_t) diff_ops_write);
646                 }
647
648                 submit_two_gauge (host->name, volume->name, "disk_latency", /* type instance = */ NULL,
649                                 latency_per_op_read, latency_per_op_write, new_data->timestamp);
650         }
651
652         /* Clear all HAVE_* flags. */
653         volume->perf_data.flags &= ~HAVE_VOLUME_PERF_ALL;
654
655         /* Copy all counters */
656         volume->perf_data.timestamp = new_data->timestamp;
657         volume->perf_data.read_bytes = new_data->read_bytes;
658         volume->perf_data.write_bytes = new_data->write_bytes;
659         volume->perf_data.read_ops = new_data->read_ops;
660         volume->perf_data.write_ops = new_data->write_ops;
661         volume->perf_data.read_latency = new_data->read_latency;
662         volume->perf_data.write_latency = new_data->write_latency;
663
664         /* Copy the HAVE_* flags */
665         volume->perf_data.flags |= (new_data->flags & HAVE_VOLUME_PERF_ALL);
666
667         return (0);
668 } /* }}} int submit_volume_perf_data */
669
670 /* 
671  * Query functions
672  *
673  * These functions are called with appropriate data returned by the libnetapp
674  * interface which is parsed and submitted with the above functions.
675  */
676 /* Data corresponding to <WAFL /> */
677 static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */
678                 na_elem_t *data)
679 {
680         cfg_wafl_t perf_data;
681         const char *plugin_inst;
682
683         na_elem_t *instances;
684         na_elem_t *counter;
685         na_elem_iter_t counter_iter;
686
687         memset (&perf_data, 0, sizeof (perf_data));
688         
689         perf_data.timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0);
690
691         instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
692         if (instances == NULL)
693         {
694                 ERROR ("netapp plugin: cna_handle_wafl_data: "
695                                 "na_elem_child (\"instances\") failed.");
696                 return (-1);
697         }
698
699         plugin_inst = na_child_get_string(instances, "name");
700         if (plugin_inst == NULL)
701         {
702                 ERROR ("netapp plugin: cna_handle_wafl_data: "
703                                 "na_child_get_string (\"name\") failed.");
704                 return (-1);
705         }
706
707         /* Iterate over all counters */
708         counter_iter = na_child_iterator (na_elem_child (instances, "counters"));
709         for (counter = na_iterator_next (&counter_iter);
710                         counter != NULL;
711                         counter = na_iterator_next (&counter_iter))
712         {
713                 const char *name;
714                 uint64_t value;
715
716                 name = na_child_get_string(counter, "name");
717                 if (name == NULL)
718                         continue;
719
720                 value = na_child_get_uint64(counter, "value", UINT64_MAX);
721                 if (value == UINT64_MAX)
722                         continue;
723
724                 if (!strcmp(name, "name_cache_hit")) {
725                         perf_data.name_cache_hit = value;
726                         perf_data.flags |= HAVE_WAFL_NAME_CACHE_HIT;
727                 } else if (!strcmp(name, "name_cache_miss")) {
728                         perf_data.name_cache_miss = value;
729                         perf_data.flags |= HAVE_WAFL_NAME_CACHE_MISS;
730                 } else if (!strcmp(name, "find_dir_hit")) {
731                         perf_data.find_dir_hit = value;
732                         perf_data.flags |= HAVE_WAFL_FIND_DIR_HIT;
733                 } else if (!strcmp(name, "find_dir_miss")) {
734                         perf_data.find_dir_miss = value;
735                         perf_data.flags |= HAVE_WAFL_FIND_DIR_MISS;
736                 } else if (!strcmp(name, "buf_hash_hit")) {
737                         perf_data.buf_hash_hit = value;
738                         perf_data.flags |= HAVE_WAFL_BUF_HASH_HIT;
739                 } else if (!strcmp(name, "buf_hash_miss")) {
740                         perf_data.buf_hash_miss = value;
741                         perf_data.flags |= HAVE_WAFL_BUF_HASH_MISS;
742                 } else if (!strcmp(name, "inode_cache_hit")) {
743                         perf_data.inode_cache_hit = value;
744                         perf_data.flags |= HAVE_WAFL_INODE_CACHE_HIT;
745                 } else if (!strcmp(name, "inode_cache_miss")) {
746                         perf_data.inode_cache_miss = value;
747                         perf_data.flags |= HAVE_WAFL_INODE_CACHE_MISS;
748                 } else {
749                         DEBUG("netapp plugin: cna_handle_wafl_data: "
750                                         "Found unexpected child: %s", name);
751                 }
752         }
753
754         return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data));
755 } /* }}} void cna_handle_wafl_data */
756
757 static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */
758 {
759         na_elem_t *e;
760
761         if (cw == NULL)
762                 return (EINVAL);
763
764         if (cw->query != NULL)
765                 return (0);
766
767         cw->query = na_elem_new("perf-object-get-instances");
768         if (cw->query == NULL)
769         {
770                 ERROR ("netapp plugin: na_elem_new failed.");
771                 return (-1);
772         }
773         na_child_add_string (cw->query, "objectname", "wafl");
774
775         e = na_elem_new("counters");
776         if (e == NULL)
777         {
778                 na_elem_free (cw->query);
779                 cw->query = NULL;
780                 ERROR ("netapp plugin: na_elem_new failed.");
781                 return (-1);
782         }
783         na_child_add_string(e, "foo", "name_cache_hit");
784         na_child_add_string(e, "foo", "name_cache_miss");
785         na_child_add_string(e, "foo", "find_dir_hit");
786         na_child_add_string(e, "foo", "find_dir_miss");
787         na_child_add_string(e, "foo", "buf_hash_hit");
788         na_child_add_string(e, "foo", "buf_hash_miss");
789         na_child_add_string(e, "foo", "inode_cache_hit");
790         na_child_add_string(e, "foo", "inode_cache_miss");
791
792         na_child_add(cw->query, e);
793
794         return (0);
795 } /* }}} int cna_setup_wafl */
796
797 static int cna_query_wafl (host_config_t *host) /* {{{ */
798 {
799         na_elem_t *data;
800         int status;
801         time_t now;
802
803         if (host == NULL)
804                 return (EINVAL);
805
806         if (host->cfg_wafl == NULL)
807                 return (0);
808
809         now = time (NULL);
810         if ((host->cfg_wafl->interval.interval + host->cfg_wafl->interval.last_read) > now)
811                 return (0);
812
813         status = cna_setup_wafl (host->cfg_wafl);
814         if (status != 0)
815                 return (status);
816         assert (host->cfg_wafl->query != NULL);
817
818         data = na_server_invoke_elem(host->srv, host->cfg_wafl->query);
819         if (na_results_status (data) != NA_OK)
820         {
821                 ERROR ("netapp plugin: cna_query_wafl: na_server_invoke_elem failed: %s",
822                                 na_results_reason (data));
823                 na_elem_free (data);
824                 return (-1);
825         }
826
827         status = cna_handle_wafl_data (host->name, host->cfg_wafl, data);
828
829         if (status == 0)
830                 host->cfg_wafl->interval.last_read = now;
831
832         na_elem_free (data);
833         return (status);
834 } /* }}} int cna_query_wafl */
835
836 /* Data corresponding to <Disks /> */
837 static int cna_handle_disk_data (const char *hostname, /* {{{ */
838                 cfg_disk_t *cfg_disk, na_elem_t *data)
839 {
840         time_t timestamp;
841         na_elem_t *instances;
842         na_elem_t *instance;
843         na_elem_iter_t instance_iter;
844         disk_t *worst_disk = NULL;
845
846         if ((cfg_disk == NULL) || (data == NULL))
847                 return (EINVAL);
848         
849         timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0);
850
851         instances = na_elem_child (data, "instances");
852         if (instances == NULL)
853         {
854                 ERROR ("netapp plugin: cna_handle_disk_data: "
855                                 "na_elem_child (\"instances\") failed.");
856                 return (-1);
857         }
858
859         /* Iterate over all children */
860         instance_iter = na_child_iterator (instances);
861         for (instance = na_iterator_next (&instance_iter);
862                         instance != NULL;
863                         instance = na_iterator_next(&instance_iter))
864         {
865                 disk_t *old_data;
866                 disk_t  new_data;
867
868                 na_elem_iter_t counter_iterator;
869                 na_elem_t *counter;
870
871                 memset (&new_data, 0, sizeof (new_data));
872                 new_data.timestamp = timestamp;
873                 new_data.disk_busy_percent = NAN;
874
875                 old_data = get_disk(cfg_disk, na_child_get_string (instance, "name"));
876                 if (old_data == NULL)
877                         continue;
878
879                 /* Look for the "disk_busy" and "base_for_disk_busy" counters */
880                 counter_iterator = na_child_iterator(na_elem_child(instance, "counters"));
881                 for (counter = na_iterator_next(&counter_iterator);
882                                 counter != NULL;
883                                 counter = na_iterator_next(&counter_iterator))
884                 {
885                         const char *name;
886                         uint64_t value;
887
888                         name = na_child_get_string(counter, "name");
889                         if (name == NULL)
890                                 continue;
891
892                         value = na_child_get_uint64(counter, "value", UINT64_MAX);
893                         if (value == UINT64_MAX)
894                                 continue;
895
896                         if (strcmp(name, "disk_busy") == 0)
897                         {
898                                 new_data.disk_busy = value;
899                                 new_data.flags |= HAVE_DISK_BUSY;
900                         }
901                         else if (strcmp(name, "base_for_disk_busy") == 0)
902                         {
903                                 new_data.base_for_disk_busy = value;
904                                 new_data.flags |= HAVE_DISK_BASE;
905                         }
906                         else
907                         {
908                                 DEBUG ("netapp plugin: cna_handle_disk_data: "
909                                                 "Counter not handled: %s = %"PRIu64,
910                                                 name, value);
911                         }
912                 }
913
914                 /* If all required counters are available and did not just wrap around,
915                  * calculate the busy percentage. Otherwise, the value is initialized to
916                  * NAN at the top of the for-loop. */
917                 if (HAS_ALL_FLAGS (old_data->flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
918                                 && HAS_ALL_FLAGS (new_data.flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
919                                 && (new_data.disk_busy >= old_data->disk_busy)
920                                 && (new_data.base_for_disk_busy > old_data->base_for_disk_busy))
921                 {
922                         uint64_t busy_diff;
923                         uint64_t base_diff;
924
925                         busy_diff = new_data.disk_busy - old_data->disk_busy;
926                         base_diff = new_data.base_for_disk_busy - old_data->base_for_disk_busy;
927
928                         new_data.disk_busy_percent = 100.0
929                                 * ((gauge_t) busy_diff) / ((gauge_t) base_diff);
930                 }
931
932                 /* Clear HAVE_* flags */
933                 old_data->flags &= ~HAVE_DISK_ALL;
934
935                 /* Copy data */
936                 old_data->timestamp = new_data.timestamp;
937                 old_data->disk_busy = new_data.disk_busy;
938                 old_data->base_for_disk_busy = new_data.base_for_disk_busy;
939                 old_data->disk_busy_percent = new_data.disk_busy_percent;
940
941                 /* Copy flags */
942                 old_data->flags |= (new_data.flags & HAVE_DISK_ALL);
943
944                 if ((worst_disk == NULL)
945                                 || (worst_disk->disk_busy_percent < old_data->disk_busy_percent))
946                         worst_disk = old_data;
947         } /* for (all disks) */
948
949         if ((cfg_disk->flags & CFG_DISK_BUSIEST) && (worst_disk != NULL))
950                 submit_double (hostname, "system", "percent", "disk_busy",
951                                 worst_disk->disk_busy_percent, timestamp);
952
953         return (0);
954 } /* }}} int cna_handle_disk_data */
955
956 static int cna_setup_disk (cfg_disk_t *cd) /* {{{ */
957 {
958         na_elem_t *e;
959
960         if (cd == NULL)
961                 return (EINVAL);
962
963         if (cd->query != NULL)
964                 return (0);
965
966         cd->query = na_elem_new ("perf-object-get-instances");
967         if (cd->query == NULL)
968         {
969                 ERROR ("netapp plugin: na_elem_new failed.");
970                 return (-1);
971         }
972         na_child_add_string (cd->query, "objectname", "disk");
973
974         e = na_elem_new("counters");
975         if (e == NULL)
976         {
977                 na_elem_free (cd->query);
978                 cd->query = NULL;
979                 ERROR ("netapp plugin: na_elem_new failed.");
980                 return (-1);
981         }
982         na_child_add_string(e, "foo", "disk_busy");
983         na_child_add_string(e, "foo", "base_for_disk_busy");
984         na_child_add(cd->query, e);
985
986         return (0);
987 } /* }}} int cna_setup_disk */
988
989 static int cna_query_disk (host_config_t *host) /* {{{ */
990 {
991         na_elem_t *data;
992         int status;
993         time_t now;
994
995         if (host == NULL)
996                 return (EINVAL);
997
998         if (host->cfg_disk == NULL)
999                 return (0);
1000
1001         now = time (NULL);
1002         if ((host->cfg_disk->interval.interval + host->cfg_disk->interval.last_read) > now)
1003                 return (0);
1004
1005         status = cna_setup_disk (host->cfg_disk);
1006         if (status != 0)
1007                 return (status);
1008         assert (host->cfg_disk->query != NULL);
1009
1010         data = na_server_invoke_elem(host->srv, host->cfg_disk->query);
1011         if (na_results_status (data) != NA_OK)
1012         {
1013                 ERROR ("netapp plugin: cna_query_disk: na_server_invoke_elem failed: %s",
1014                                 na_results_reason (data));
1015                 na_elem_free (data);
1016                 return (-1);
1017         }
1018
1019         status = cna_handle_disk_data (host->name, host->cfg_disk, data);
1020
1021         if (status == 0)
1022                 host->cfg_disk->interval.last_read = now;
1023
1024         na_elem_free (data);
1025         return (status);
1026 } /* }}} int cna_query_disk */
1027
1028 /* Data corresponding to <GetVolumeData /> */
1029 static void collect_volume_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
1030         na_elem_t *inst;
1031         volume_t *volume;
1032         cfg_volume_usage_t *cfg_volume_data = data;
1033
1034         out = na_elem_child(out, "volumes");
1035         na_elem_iter_t inst_iter = na_child_iterator(out);
1036         for (inst = na_iterator_next(&inst_iter); inst; inst = na_iterator_next(&inst_iter)) {
1037                 uint64_t size_free = 0, size_used = 0, snap_reserved = 0;
1038
1039                 na_elem_t *sis;
1040                 const char *sis_state;
1041                 uint64_t sis_saved_reported;
1042                 uint64_t sis_saved;
1043
1044                 volume = get_volume(host, na_child_get_string(inst, "name"),
1045                                 cfg_volume_data->flags, /* perf_flags = */ 0);
1046                 if (volume == NULL)
1047                         continue;
1048
1049                 if (!(volume->cfg_volume_usage.flags & CFG_VOLUME_USAGE_DF))
1050                         continue;
1051
1052                 /* 2^4 exa-bytes? This will take a while ;) */
1053                 size_free = na_child_get_uint64(inst, "size-available", UINT64_MAX);
1054                 if (size_free != UINT64_MAX)
1055                         submit_double (host->name, volume->name, "df_complex", "free",
1056                                         (double) size_free, /* time = */ 0);
1057
1058                 size_used = na_child_get_uint64(inst, "size-used", UINT64_MAX);
1059                 if (size_used != UINT64_MAX) {
1060                         if ((volume->cfg_volume_usage.flags & HAVE_VOLUME_USAGE_SNAP)
1061                                         && (size_used >= volume->cfg_volume_usage.snap_used))
1062                                 size_used -= volume->cfg_volume_usage.snap_used;
1063                         submit_double (host->name, volume->name, "df_complex", "used",
1064                                         (double) size_used, /* time = */ 0);
1065                 }
1066
1067                 snap_reserved = na_child_get_uint64(inst, "snapshot-blocks-reserved", UINT64_MAX);
1068                 if (!(volume->cfg_volume_usage.flags & HAVE_VOLUME_USAGE_SNAP) && (snap_reserved != UINT64_MAX))
1069                         /* If we have snap usage data this value has already been submitted. */
1070                         /* 1 block == 1024 bytes  as per API docs */
1071                         submit_double (host->name, volume->name, "df_complex", "snap_reserved",
1072                                         (double) (1024 * snap_reserved), /* time = */ 0);
1073
1074                 sis = na_elem_child(inst, "sis");
1075                 if (sis == NULL)
1076                         continue;
1077
1078                 sis_state = na_child_get_string(sis, "state");
1079                 if ((sis_state == NULL)
1080                                 || (strcmp ("enabled", sis_state) != 0))
1081                         continue;
1082
1083                 sis_saved_reported = na_child_get_uint64(sis, "size-saved", UINT64_MAX);
1084                 if (sis_saved_reported == UINT64_MAX)
1085                         continue;
1086
1087                 /* size-saved is actually a 32 bit number, so ... time for some guesswork. */
1088                 if ((sis_saved_reported >> 32) != 0) {
1089                         /* In case they ever fix this bug. */
1090                         sis_saved = sis_saved_reported;
1091                 } else {
1092                         uint64_t sis_saved_percent;
1093                         uint64_t sis_saved_guess;
1094                         uint64_t overflow_guess;
1095                         uint64_t guess1, guess2, guess3;
1096
1097                         sis_saved_percent = na_child_get_uint64(sis, "percentage-saved", UINT64_MAX);
1098                         if (sis_saved_percent > 100)
1099                                 continue;
1100
1101                         /* The "size-saved" value is a 32bit unsigned integer. This is a bug and
1102                          * will hopefully be fixed in later versions. To work around the bug, try
1103                          * to figure out how often the 32bit integer wrapped around by using the
1104                          * "percentage-saved" value. Because the percentage is in the range
1105                          * [0-100], this should work as long as the saved space does not exceed
1106                          * 400 GBytes. */
1107                         /* percentage-saved = size-saved / (size-saved + size-used) */
1108                         if (sis_saved_percent < 100)
1109                                 sis_saved_guess = size_used * sis_saved_percent / (100 - sis_saved_percent);
1110                         else
1111                                 sis_saved_guess = size_used;
1112
1113                         overflow_guess = sis_saved_guess >> 32;
1114                         guess1 = overflow_guess ? ((overflow_guess - 1) << 32) + sis_saved_reported : sis_saved_reported;
1115                         guess2 = (overflow_guess << 32) + sis_saved_reported;
1116                         guess3 = ((overflow_guess + 1) << 32) + sis_saved_reported;
1117
1118                         if (sis_saved_guess < guess2) {
1119                                 if ((sis_saved_guess - guess1) < (guess2 - sis_saved_guess))
1120                                         sis_saved = guess1;
1121                                 else
1122                                         sis_saved = guess2;
1123                         } else {
1124                                 if ((sis_saved_guess - guess2) < (guess3 - sis_saved_guess))
1125                                         sis_saved = guess2;
1126                                 else
1127                                         sis_saved = guess3;
1128                         }
1129                 } /* end of 32-bit workaround */
1130
1131                 submit_double (host->name, volume->name, "df_complex", "sis_saved",
1132                                 (double) sis_saved, /* time = */ 0);
1133         }
1134 } /* }}} void collect_volume_data */
1135
1136 /* Data corresponding to <GetVolumePerfData /> */
1137 static void query_volume_perf_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
1138         cfg_volume_perf_t *cfg_volume_perf = data;
1139         time_t timestamp;
1140         na_elem_t *counter, *inst;
1141         
1142         timestamp = (time_t) na_child_get_uint64(out, "timestamp", 0);
1143
1144         out = na_elem_child(out, "instances");
1145         na_elem_iter_t inst_iter = na_child_iterator(out);
1146         for (inst = na_iterator_next(&inst_iter); inst; inst = na_iterator_next(&inst_iter)) {
1147                 data_volume_perf_t perf_data;
1148                 volume_t *volume;
1149
1150                 memset (&perf_data, 0, sizeof (perf_data));
1151                 perf_data.timestamp = timestamp;
1152
1153                 volume = get_volume(host, na_child_get_string(inst, "name"),
1154                                 /* data_flags = */ 0, cfg_volume_perf->flags);
1155                 if (volume == NULL)
1156                         continue;
1157
1158                 na_elem_iter_t count_iter = na_child_iterator(na_elem_child(inst, "counters"));
1159                 for (counter = na_iterator_next(&count_iter); counter; counter = na_iterator_next(&count_iter)) {
1160                         const char *name;
1161                         uint64_t value;
1162
1163                         name = na_child_get_string(counter, "name");
1164                         if (name == NULL)
1165                                 continue;
1166
1167                         value = na_child_get_uint64(counter, "value", UINT64_MAX);
1168                         if (value == UINT64_MAX)
1169                                 continue;
1170
1171                         if (!strcmp(name, "read_data")) {
1172                                 perf_data.read_bytes = value;
1173                                 perf_data.flags |= HAVE_VOLUME_PERF_BYTES_READ;
1174                         } else if (!strcmp(name, "write_data")) {
1175                                 perf_data.write_bytes = value;
1176                                 perf_data.flags |= HAVE_VOLUME_PERF_BYTES_WRITE;
1177                         } else if (!strcmp(name, "read_ops")) {
1178                                 perf_data.read_ops = value;
1179                                 perf_data.flags |= HAVE_VOLUME_PERF_OPS_READ;
1180                         } else if (!strcmp(name, "write_ops")) {
1181                                 perf_data.write_ops = value;
1182                                 perf_data.flags |= HAVE_VOLUME_PERF_OPS_WRITE;
1183                         } else if (!strcmp(name, "read_latency")) {
1184                                 perf_data.read_latency = value;
1185                                 perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_READ;
1186                         } else if (!strcmp(name, "write_latency")) {
1187                                 perf_data.write_latency = value;
1188                                 perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_WRITE;
1189                         }
1190                 }
1191
1192                 submit_volume_perf_data (host, volume, &perf_data);
1193         } /* for (volume) */
1194 } /* }}} void query_volume_perf_data */
1195
1196 /* Data corresponding to <GetSystemPerfData /> */
1197 static void collect_perf_system_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
1198         counter_t disk_read = 0, disk_written = 0;
1199         counter_t net_recv = 0, net_sent = 0;
1200         counter_t cpu_busy = 0, cpu_total = 0;
1201         unsigned int counter_flags = 0;
1202
1203         cfg_system_t *cfg_system = data;
1204         const char *instance;
1205         time_t timestamp;
1206         na_elem_t *counter;
1207         
1208         timestamp = (time_t) na_child_get_uint64(out, "timestamp", 0);
1209         out = na_elem_child(na_elem_child(out, "instances"), "instance-data");
1210         instance = na_child_get_string(out, "name");
1211
1212         na_elem_iter_t iter = na_child_iterator(na_elem_child(out, "counters"));
1213         for (counter = na_iterator_next(&iter); counter; counter = na_iterator_next(&iter)) {
1214                 const char *name;
1215                 uint64_t value;
1216
1217                 name = na_child_get_string(counter, "name");
1218                 if (name == NULL)
1219                         continue;
1220
1221                 value = na_child_get_uint64(counter, "value", UINT64_MAX);
1222                 if (value == UINT64_MAX)
1223                         continue;
1224
1225                 if (!strcmp(name, "disk_data_read")) {
1226                         disk_read = (counter_t) (value * 1024);
1227                         counter_flags |= 0x01;
1228                 } else if (!strcmp(name, "disk_data_written")) {
1229                         disk_written = (counter_t) (value * 1024);
1230                         counter_flags |= 0x02;
1231                 } else if (!strcmp(name, "net_data_recv")) {
1232                         net_recv = (counter_t) (value * 1024);
1233                         counter_flags |= 0x04;
1234                 } else if (!strcmp(name, "net_data_sent")) {
1235                         net_sent = (counter_t) (value * 1024);
1236                         counter_flags |= 0x08;
1237                 } else if (!strcmp(name, "cpu_busy")) {
1238                         cpu_busy = (counter_t) value;
1239                         counter_flags |= 0x10;
1240                 } else if (!strcmp(name, "cpu_elapsed_time")) {
1241                         cpu_total = (counter_t) value;
1242                         counter_flags |= 0x20;
1243                 } else if ((cfg_system->flags & CFG_SYSTEM_OPS)
1244                                 && (value > 0) && (strlen(name) > 4)
1245                                 && (!strcmp(name + strlen(name) - 4, "_ops"))) {
1246                         submit_counter (host->name, instance, "disk_ops_complex", name,
1247                                         (counter_t) value, timestamp);
1248                 }
1249         } /* for (counter) */
1250
1251         if ((cfg_system->flags & CFG_SYSTEM_DISK)
1252                         && ((counter_flags & 0x03) == 0x03))
1253                 submit_two_counters (host->name, instance, "disk_octets", NULL,
1254                                 disk_read, disk_written, timestamp);
1255                                 
1256         if ((cfg_system->flags & CFG_SYSTEM_NET)
1257                         && ((counter_flags & 0x0c) == 0x0c))
1258                 submit_two_counters (host->name, instance, "if_octets", NULL,
1259                                 net_recv, net_sent, timestamp);
1260
1261         if ((cfg_system->flags & CFG_SYSTEM_CPU)
1262                         && ((counter_flags & 0x30) == 0x30)) {
1263                 submit_counter (host->name, instance, "cpu", "system",
1264                                 cpu_busy, timestamp);
1265                 submit_counter (host->name, instance, "cpu", "idle",
1266                                 cpu_total - cpu_busy, timestamp);
1267         }
1268 } /* }}} void collect_perf_system_data */
1269
1270 /*
1271  * Configuration handling
1272  */
1273 /* Sets a given flag if the boolean argument is true and unsets the flag if it
1274  * is false. On error, the flag-field is not changed. */
1275 static int cna_config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */
1276                 uint32_t *flags, uint32_t flag)
1277 {
1278         if ((ci == NULL) || (flags == NULL))
1279                 return (EINVAL);
1280
1281         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
1282         {
1283                 WARNING ("netapp plugin: The %s option needs exactly one boolean argument.",
1284                                 ci->key);
1285                 return (-1);
1286         }
1287
1288         if (ci->values[0].value.boolean)
1289                 *flags |= flag;
1290         else
1291                 *flags &= ~flag;
1292
1293         return (0);
1294 } /* }}} int cna_config_bool_to_flag */
1295
1296 /* Handling of the "Multiplier" option which is allowed in every block. */
1297 static int cna_config_get_multiplier (const oconfig_item_t *ci, /* {{{ */
1298                 cfg_service_t *service)
1299 {
1300         int tmp;
1301
1302         if ((ci == NULL) || (service == NULL))
1303                 return (EINVAL);
1304
1305         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
1306         {
1307                 WARNING ("netapp plugin: The `Multiplier' option needs exactly one numeric argument.");
1308                 return (-1);
1309         }
1310
1311         tmp = (int) (ci->values[0].value.number + .5);
1312         if (tmp < 1)
1313         {
1314                 WARNING ("netapp plugin: The `Multiplier' option needs a positive integer argument.");
1315                 return (-1);
1316         }
1317
1318         service->multiplier = tmp;
1319         service->skip_countdown = tmp;
1320
1321         return (0);
1322 } /* }}} int cna_config_get_multiplier */
1323
1324 /* Handling of the "Interval" option which is allowed in every block. */
1325 static int cna_config_get_interval (const oconfig_item_t *ci, /* {{{ */
1326                 cna_interval_t *out_interval)
1327 {
1328         time_t tmp;
1329
1330         if ((ci == NULL) || (out_interval == NULL))
1331                 return (EINVAL);
1332
1333         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
1334         {
1335                 WARNING ("netapp plugin: The `Multiplier' option needs exactly one numeric argument.");
1336                 return (-1);
1337         }
1338
1339         tmp = (time_t) (ci->values[0].value.number + .5);
1340         if (tmp < 1)
1341         {
1342                 WARNING ("netapp plugin: The `Multiplier' option needs a positive integer argument.");
1343                 return (-1);
1344         }
1345
1346         out_interval->interval = tmp;
1347         out_interval->last_read = 0;
1348
1349         return (0);
1350 } /* }}} int cna_config_get_interval */
1351
1352 /* Handling of the "GetIO", "GetOps" and "GetLatency" options within a
1353  * <GetVolumePerfData /> block. */
1354 static void cna_config_volume_performance_option (host_config_t *host, /* {{{ */
1355                 cfg_volume_perf_t *perf_volume, const oconfig_item_t *item,
1356                 uint32_t flag)
1357 {
1358         int i;
1359         
1360         for (i = 0; i < item->values_num; ++i) {
1361                 const char *name;
1362                 volume_t *v;
1363                 _Bool set = true;
1364
1365                 if (item->values[i].type != OCONFIG_TYPE_STRING) {
1366                         WARNING("netapp plugin: Ignoring non-string argument in "
1367                                         "\"GetVolumePerfData\" block for host %s", host->name);
1368                         continue;
1369                 }
1370
1371                 name = item->values[i].value.string;
1372                 if (name[0] == '+') {
1373                         set = true;
1374                         ++name;
1375                 } else if (name[0] == '-') {
1376                         set = false;
1377                         ++name;
1378                 }
1379
1380                 if (!name[0]) {
1381                         if (set)
1382                                 perf_volume->flags |= flag;
1383                         else /* if (!set) */
1384                                 perf_volume->flags &= ~flag;
1385
1386                         host_set_all_perf_data_flags(host, flag, set);
1387                         continue;
1388                 }
1389
1390                 v = get_volume (host, name, /* data_flags = */ 0, perf_volume->flags);
1391                 if (v == NULL)
1392                         continue;
1393
1394                 if (set)
1395                         v->perf_data.flags |= flag;
1396                 else /* if (!set) */
1397                         v->perf_data.flags &= ~flag;
1398         } /* for (i = 0 .. item->values_num) */
1399 } /* }}} void cna_config_volume_performance_option */
1400
1401 /* Corresponds to a <GetVolumePerfData /> block */
1402 static void cna_config_volume_performance(host_config_t *host, const oconfig_item_t *ci) { /* {{{ */
1403         int i, had_io = 0, had_ops = 0, had_latency = 0;
1404         cfg_service_t *service;
1405         cfg_volume_perf_t *perf_volume;
1406         
1407         service = malloc(sizeof(*service));
1408         service->query = 0;
1409         service->handler = query_volume_perf_data;
1410         perf_volume = service->data = malloc(sizeof(*perf_volume));
1411         perf_volume->flags = CFG_VOLUME_PERF_INIT;
1412         service->next = host->services;
1413         host->services = service;
1414         for (i = 0; i < ci->children_num; ++i) {
1415                 oconfig_item_t *item = ci->children + i;
1416                 
1417                 /* if (!item || !item->key || !*item->key) continue; */
1418                 if (!strcasecmp(item->key, "Multiplier")) {
1419                         cna_config_get_multiplier (item, service);
1420                 } else if (!strcasecmp(item->key, "GetIO")) {
1421                         had_io = 1;
1422                         cna_config_volume_performance_option(host, perf_volume, item, CFG_VOLUME_PERF_IO);
1423                 } else if (!strcasecmp(item->key, "GetOps")) {
1424                         had_ops = 1;
1425                         cna_config_volume_performance_option(host, perf_volume, item, CFG_VOLUME_PERF_OPS);
1426                 } else if (!strcasecmp(item->key, "GetLatency")) {
1427                         had_latency = 1;
1428                         cna_config_volume_performance_option(host, perf_volume, item, CFG_VOLUME_PERF_LATENCY);
1429                 }
1430         }
1431         if (!had_io) {
1432                 perf_volume->flags |= CFG_VOLUME_PERF_IO;
1433                 host_set_all_perf_data_flags(host, CFG_VOLUME_PERF_IO, /* set = */ true);
1434         }
1435         if (!had_ops) {
1436                 perf_volume->flags |= CFG_VOLUME_PERF_OPS;
1437                 host_set_all_perf_data_flags(host, CFG_VOLUME_PERF_OPS, /* set = */ true);
1438         }
1439         if (!had_latency) {
1440                 perf_volume->flags |= CFG_VOLUME_PERF_LATENCY;
1441                 host_set_all_perf_data_flags(host, CFG_VOLUME_PERF_LATENCY, /* set = */ true);
1442         }
1443 } /* }}} void cna_config_volume_performance */
1444
1445 /* Handling of the "GetDiskUtil" option within a <GetVolumeData /> block. */
1446 static void cna_config_volume_usage_option (host_config_t *host, /* {{{ */
1447                 cfg_volume_usage_t *cfg_volume_data, const oconfig_item_t *item, uint32_t flag)
1448 {
1449         int i;
1450         
1451         for (i = 0; i < item->values_num; ++i) {
1452                 const char *name;
1453                 volume_t *v;
1454                 _Bool set = true;
1455
1456                 if (item->values[i].type != OCONFIG_TYPE_STRING) {
1457                         WARNING("netapp plugin: Ignoring non-string argument in \"GetVolData\""
1458                                         "block for host %s", host->name);
1459                         continue;
1460                 }
1461
1462                 name = item->values[i].value.string;
1463                 if (name[0] == '+') {
1464                         set = true;
1465                         ++name;
1466                 } else if (name[0] == '-') {
1467                         set = false;
1468                         ++name;
1469                 }
1470
1471                 if (!name[0]) {
1472                         if (set)
1473                                 cfg_volume_data->flags |= flag;
1474                         else /* if (!set) */
1475                                 cfg_volume_data->flags &= ~flag;
1476
1477                         host_set_all_cfg_volume_usage_flags(host, flag, set);
1478                         continue;
1479                 }
1480
1481                 v = get_volume(host, name, cfg_volume_data->flags, /* perf_flags = */ 0);
1482                 if (v == NULL)
1483                         continue;
1484
1485                 if (!v->cfg_volume_usage.flags)
1486                         v->cfg_volume_usage.flags = cfg_volume_data->flags;
1487
1488                 if (set)
1489                         v->cfg_volume_usage.flags |= flag;
1490                 else /* if (!set) */
1491                         v->cfg_volume_usage.flags &= ~flag;
1492         }
1493 } /* }}} void cna_config_volume_usage_option */
1494
1495 /* Corresponds to a <GetVolumeData /> block */
1496 static void cna_config_volume_usage(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
1497         int i, had_df = 0;
1498         cfg_service_t *service;
1499         cfg_volume_usage_t *cfg_volume_data;
1500         
1501         service = malloc(sizeof(*service));
1502         service->query = 0;
1503         service->handler = collect_volume_data;
1504         cfg_volume_data = service->data = malloc(sizeof(*cfg_volume_data));
1505         cfg_volume_data->flags = CFG_VOLUME_USAGE_INIT;
1506         service->next = host->services;
1507         host->services = service;
1508         for (i = 0; i < ci->children_num; ++i) {
1509                 oconfig_item_t *item = ci->children + i;
1510                 
1511                 /* if (!item || !item->key || !*item->key) continue; */
1512                 if (!strcasecmp(item->key, "Multiplier")) {
1513                         cna_config_get_multiplier (item, service);
1514                 } else if (!strcasecmp(item->key, "GetDiskUtil")) {
1515                         had_df = 1;
1516                         cna_config_volume_usage_option(host, cfg_volume_data, item, CFG_VOLUME_USAGE_DF);
1517                 } else if (!strcasecmp(item->key, "GetSnapUtil")) {
1518                         had_df = 1;
1519                         cna_config_volume_usage_option(host, cfg_volume_data, item, CFG_VOLUME_USAGE_SNAP);
1520                 }
1521         }
1522         if (!had_df) {
1523                 cfg_volume_data->flags |= CFG_VOLUME_USAGE_DF;
1524                 host_set_all_cfg_volume_usage_flags(host, CFG_VOLUME_USAGE_DF, /* set = */ true);
1525         }
1526         if (cfg_volume_data->flags & CFG_VOLUME_USAGE_SNAP) {
1527                 WARNING("netapp plugin: The \"GetSnapUtil\" option does not support the \"+\" wildcard.");
1528         }
1529 } /* }}} void cna_config_volume_usage */
1530
1531 /* Corresponds to a <Disks /> block */
1532 static int cna_config_disk(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
1533         cfg_disk_t *cfg_disk;
1534         int i;
1535
1536         if ((host == NULL) || (ci == NULL))
1537                 return (EINVAL);
1538
1539         if (host->cfg_disk == NULL)
1540         {
1541                 cfg_disk = malloc (sizeof (*cfg_disk));
1542                 if (cfg_disk == NULL)
1543                         return (ENOMEM);
1544                 memset (cfg_disk, 0, sizeof (*cfg_disk));
1545
1546                 /* Set default flags */
1547                 cfg_disk->flags = CFG_DISK_ALL;
1548                 cfg_disk->query = NULL;
1549                 cfg_disk->disks = NULL;
1550
1551                 host->cfg_disk = cfg_disk;
1552         }
1553         cfg_disk = host->cfg_disk;
1554         
1555         for (i = 0; i < ci->children_num; ++i) {
1556                 oconfig_item_t *item = ci->children + i;
1557                 
1558                 /* if (!item || !item->key || !*item->key) continue; */
1559                 if (strcasecmp(item->key, "Interval") == 0)
1560                         cna_config_get_interval (item, &cfg_disk->interval);
1561                 else if (strcasecmp(item->key, "GetBusy") == 0)
1562                         cna_config_bool_to_flag (item, &cfg_disk->flags, CFG_DISK_BUSIEST);
1563         }
1564
1565         return (0);
1566 } /* }}} int cna_config_disk */
1567
1568 /* Corresponds to a <WAFL /> block */
1569 static int cna_config_wafl(host_config_t *host, oconfig_item_t *ci) /* {{{ */
1570 {
1571         cfg_wafl_t *cfg_wafl;
1572         int i;
1573
1574         if ((host == NULL) || (ci == NULL))
1575                 return (EINVAL);
1576
1577         if (host->cfg_wafl == NULL)
1578         {
1579                 cfg_wafl = malloc (sizeof (*cfg_wafl));
1580                 if (cfg_wafl == NULL)
1581                         return (ENOMEM);
1582                 memset (cfg_wafl, 0, sizeof (*cfg_wafl));
1583
1584                 /* Set default flags */
1585                 cfg_wafl->flags = CFG_WAFL_ALL;
1586
1587                 host->cfg_wafl = cfg_wafl;
1588         }
1589         cfg_wafl = host->cfg_wafl;
1590
1591         for (i = 0; i < ci->children_num; ++i) {
1592                 oconfig_item_t *item = ci->children + i;
1593                 
1594                 if (strcasecmp(item->key, "Interval") == 0)
1595                         cna_config_get_interval (item, &cfg_wafl->interval);
1596                 else if (!strcasecmp(item->key, "GetNameCache"))
1597                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
1598                 else if (!strcasecmp(item->key, "GetDirCache"))
1599                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
1600                 else if (!strcasecmp(item->key, "GetBufferCache"))
1601                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
1602                 else if (!strcasecmp(item->key, "GetInodeCache"))
1603                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
1604                 else
1605                         WARNING ("netapp plugin: The %s config option is not allowed within "
1606                                         "`WAFL' blocks.", item->key);
1607         }
1608
1609         return (0);
1610 } /* }}} int cna_config_wafl */
1611
1612 /* Corresponds to a <GetSystemPerfData /> block */
1613 static int cna_config_system (host_config_t *host, /* {{{ */
1614                 oconfig_item_t *ci, const cfg_service_t *default_service)
1615 {
1616         int i;
1617         cfg_service_t *service;
1618         cfg_system_t *cfg_system;
1619         
1620         service = malloc(sizeof(*service));
1621         if (service == NULL)
1622                 return (-1);
1623         memset (service, 0, sizeof (*service));
1624         *service = *default_service;
1625         service->handler = collect_perf_system_data;
1626
1627         cfg_system = malloc(sizeof(*cfg_system));
1628         if (cfg_system == NULL) {
1629                 sfree (service);
1630                 return (-1);
1631         }
1632         memset (cfg_system, 0, sizeof (*cfg_system));
1633         cfg_system->flags = CFG_SYSTEM_ALL;
1634         service->data = cfg_system;
1635
1636         for (i = 0; i < ci->children_num; ++i) {
1637                 oconfig_item_t *item = ci->children + i;
1638
1639                 if (!strcasecmp(item->key, "Multiplier")) {
1640                         cna_config_get_multiplier (item, service);
1641                 } else if (!strcasecmp(item->key, "GetCPULoad")) {
1642                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_CPU);
1643                 } else if (!strcasecmp(item->key, "GetInterfaces")) {
1644                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_NET);
1645                 } else if (!strcasecmp(item->key, "GetDiskOps")) {
1646                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_OPS);
1647                 } else if (!strcasecmp(item->key, "GetDiskIO")) {
1648                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_DISK);
1649                 } else {
1650                         WARNING ("netapp plugin: The %s config option is not allowed within "
1651                                         "`GetSystemPerfData' blocks.", item->key);
1652                 }
1653         }
1654
1655         service->next = host->services;
1656         host->services = service;
1657
1658         return (0);
1659 } /* }}} int cna_config_system */
1660
1661 /* Corresponds to a <Host /> block. */
1662 static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
1663                 const host_config_t *default_host, const cfg_service_t *def_def_service)
1664 {
1665         oconfig_item_t *item;
1666         host_config_t *host;
1667         cfg_service_t default_service = *def_def_service;
1668         int status;
1669         int i;
1670         
1671         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
1672                 WARNING("netapp plugin: \"Host\" needs exactly one string argument. Ignoring host block.");
1673                 return 0;
1674         }
1675
1676         host = malloc(sizeof(*host));
1677         memcpy (host, default_host, sizeof (*host));
1678
1679         status = cf_util_get_string (ci, &host->name);
1680         if (status != 0)
1681         {
1682                 sfree (host);
1683                 return (NULL);
1684         }
1685
1686         for (i = 0; i < ci->children_num; ++i) {
1687                 item = ci->children + i;
1688
1689                 status = 0;
1690
1691                 if (!strcasecmp(item->key, "Address")) {
1692                         status = cf_util_get_string (item, &host->host);
1693                 } else if (!strcasecmp(item->key, "Port")) {
1694                         int tmp;
1695
1696                         tmp = cf_util_get_port_number (item);
1697                         if (tmp > 0)
1698                                 host->port = tmp;
1699                 } else if (!strcasecmp(item->key, "Protocol")) {
1700                         if ((item->values_num != 1) || (item->values[0].type != OCONFIG_TYPE_STRING) || (strcasecmp(item->values[0].value.string, "http") && strcasecmp(item->values[0].value.string, "https"))) {
1701                                 WARNING("netapp plugin: \"Protocol\" needs to be either \"http\" or \"https\". Ignoring host block \"%s\".", ci->values[0].value.string);
1702                                 return 0;
1703                         }
1704                         if (!strcasecmp(item->values[0].value.string, "http")) host->protocol = NA_SERVER_TRANSPORT_HTTP;
1705                         else host->protocol = NA_SERVER_TRANSPORT_HTTPS;
1706                 } else if (!strcasecmp(item->key, "User")) {
1707                         status = cf_util_get_string (item, &host->username);
1708                 } else if (!strcasecmp(item->key, "Password")) {
1709                         status = cf_util_get_string (item, &host->password);
1710                 } else if (!strcasecmp(item->key, "Interval")) {
1711                         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) {
1712                                 WARNING("netapp plugin: \"Interval\" of host %s needs exactly one integer argument.", ci->values[0].value.string);
1713                                 continue;
1714                         }
1715                         host->interval = item->values[0].value.number;
1716                 } else if (!strcasecmp(item->key, "GetVolumePerfData")) {
1717                         cna_config_volume_performance(host, item);
1718                 } else if (!strcasecmp(item->key, "GetSystemPerfData")) {
1719                         cna_config_system(host, item, &default_service);
1720                 } else if (!strcasecmp(item->key, "WAFL")) {
1721                         cna_config_wafl(host, item);
1722                 } else if (!strcasecmp(item->key, "Disks")) {
1723                         cna_config_disk(host, item);
1724                 } else if (!strcasecmp(item->key, "GetVolumeData")) {
1725                         cna_config_volume_usage(host, item);
1726                 } else {
1727                         WARNING("netapp plugin: Ignoring unknown config option \"%s\" in host block \"%s\".",
1728                                         item->key, ci->values[0].value.string);
1729                 }
1730
1731                 if (status != 0)
1732                         break;
1733         }
1734
1735         if (host->host == NULL)
1736                 host->host = strdup (host->name);
1737
1738         if (host->host == NULL)
1739                 status = -1;
1740
1741         if (host->port <= 0)
1742                 host->port = (host->protocol == NA_SERVER_TRANSPORT_HTTP) ? 80 : 443;
1743
1744         if ((host->username == NULL) || (host->password == NULL)) {
1745                 WARNING("netapp plugin: Please supply login information for host \"%s\". "
1746                                 "Ignoring host block.", host->name);
1747                 status = -1;
1748         }
1749
1750         if (status != 0)
1751         {
1752                 free_host_config (host);
1753                 return (NULL);
1754         }
1755
1756         return host;
1757 } /* }}} host_config_t *cna_config_host */
1758
1759 /*
1760  * Callbacks registered with the daemon
1761  *
1762  * Pretty standard stuff here.
1763  */
1764 static int cna_init(void) { /* {{{ */
1765         char err[256];
1766         na_elem_t *e;
1767         host_config_t *host;
1768         cfg_service_t *service;
1769         
1770         if (!global_host_config) {
1771                 WARNING("netapp plugin: Plugin loaded but no hosts defined.");
1772                 return 1;
1773         }
1774
1775         memset (err, 0, sizeof (err));
1776         if (!na_startup(err, sizeof(err))) {
1777                 err[sizeof (err) - 1] = 0;
1778                 ERROR("netapp plugin: Error initializing netapp API: %s", err);
1779                 return 1;
1780         }
1781
1782         for (host = global_host_config; host; host = host->next) {
1783                 /* Request version 1.1 of the ONTAP API */
1784                 host->srv = na_server_open(host->host,
1785                                 /* major version = */ 1, /* minor version = */ 1); 
1786                 if (host->srv == NULL) {
1787                         ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
1788                         continue;
1789                 }
1790
1791                 if (host->interval < interval_g)
1792                         host->interval = interval_g;
1793
1794                 na_server_set_transport_type(host->srv, host->protocol,
1795                                 /* transportarg = */ NULL);
1796                 na_server_set_port(host->srv, host->port);
1797                 na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
1798                 na_server_adminuser(host->srv, host->username, host->password);
1799                 na_server_set_timeout(host->srv, 5 /* seconds */);
1800
1801                 for (service = host->services; service; service = service->next) {
1802                         service->interval = host->interval * service->multiplier;
1803
1804                         if (service->handler == collect_perf_system_data) {
1805                                 service->query = na_elem_new("perf-object-get-instances");
1806                                 na_child_add_string(service->query, "objectname", "system");
1807                         } else if (service->handler == query_volume_perf_data) {
1808                                 service->query = na_elem_new("perf-object-get-instances");
1809                                 na_child_add_string(service->query, "objectname", "volume");
1810                                 e = na_elem_new("counters");
1811                                 /* "foo" means: This string has to be here but
1812                                    the content doesn't matter. */
1813                                 na_child_add_string(e, "foo", "read_ops");
1814                                 na_child_add_string(e, "foo", "write_ops");
1815                                 na_child_add_string(e, "foo", "read_data");
1816                                 na_child_add_string(e, "foo", "write_data");
1817                                 na_child_add_string(e, "foo", "read_latency");
1818                                 na_child_add_string(e, "foo", "write_latency");
1819                                 na_child_add(service->query, e);
1820                         } else if (service->handler == collect_volume_data) {
1821                                 service->query = na_elem_new("volume-list-info");
1822                                 /* na_child_add_string(service->query, "objectname", "volume"); */
1823                                 /* } else if (service->handler == collect_snapshot_data) { */
1824                                 /* service->query = na_elem_new("snapshot-list-info"); */
1825                         }
1826                 } /* for (host->services) */
1827         }
1828         return 0;
1829 } /* }}} int cna_init */
1830
1831 static int cna_config (oconfig_item_t *ci) { /* {{{ */
1832         int i;
1833         oconfig_item_t *item;
1834         host_config_t default_host = HOST_INIT;
1835         cfg_service_t default_service = SERVICE_INIT;
1836         
1837         for (i = 0; i < ci->children_num; ++i) {
1838                 item = ci->children + i;
1839
1840                 if (!strcasecmp(item->key, "Host")) {
1841                         host_config_t *host;
1842                         host_config_t *tmp;
1843
1844                         host = cna_config_host(item, &default_host, &default_service);
1845                         if (host == NULL)
1846                                 continue;
1847
1848                         for (tmp = global_host_config; tmp != NULL; tmp = tmp->next)
1849                         {
1850                                 if (strcasecmp (host->name, tmp->name) == 0)
1851                                         WARNING ("netapp plugin: Duplicate definition of host `%s'. "
1852                                                         "This is probably a bad idea.",
1853                                                         host->name);
1854
1855                                 if (tmp->next == NULL)
1856                                         break;
1857                         }
1858
1859                         host->next = NULL;
1860                         if (tmp == NULL)
1861                                 global_host_config = host;
1862                         else
1863                                 tmp->next = host;
1864                 } else {
1865                         WARNING("netapp plugin: Ignoring unknown config option \"%s\".", item->key);
1866                 }
1867         }
1868         return 0;
1869 } /* }}} int cna_config */
1870
1871 static int cna_read(void) { /* {{{ */
1872         na_elem_t *out;
1873         host_config_t *host;
1874         cfg_service_t *service;
1875         
1876         for (host = global_host_config; host; host = host->next) {
1877                 for (service = host->services; service; service = service->next) {
1878                         if (--service->skip_countdown > 0) continue;
1879                         service->skip_countdown = service->multiplier;
1880                         out = na_server_invoke_elem(host->srv, service->query);
1881                         if (na_results_status(out) != NA_OK) {
1882                                 int netapp_errno = na_results_errno(out);
1883                                 ERROR("netapp plugin: Error %d from host %s: %s", netapp_errno, host->name, na_results_reason(out));
1884                                 na_elem_free(out);
1885                                 if (netapp_errno == EIO || netapp_errno == ETIMEDOUT) {
1886                                         /* Network problems. Just give up on all other services on this host. */
1887                                         break;
1888                                 }
1889                                 continue;
1890                         }
1891                         service->handler(host, out, service->data);
1892                         na_elem_free(out);
1893                 } /* for (host->services) */
1894
1895                 cna_query_wafl (host);
1896                 cna_query_disk (host);
1897         }
1898         return 0;
1899 } /* }}} int cna_read */
1900
1901 void module_register(void) {
1902         plugin_register_complex_config("netapp", cna_config);
1903         plugin_register_init("netapp", cna_init);
1904         plugin_register_read("netapp", cna_read);
1905 }
1906
1907 /* vim: set sw=2 ts=2 noet fdm=marker : */