netapp plugin: Added support for SnapVaultĀ® statistics.
[collectd.git] / src / netapp.c
1 /**
2  * collectd - src/netapp.c
3  * Copyright (C) 2009,2010  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 #include "utils_ignorelist.h"
30
31 #include <netapp_api.h>
32 #include <netapp_errno.h>
33
34 #define HAS_ALL_FLAGS(has,needs) (((has) & (needs)) == (needs))
35
36 typedef struct host_config_s host_config_t;
37 typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *data);
38
39 struct cna_interval_s
40 {
41         cdtime_t interval;
42         cdtime_t last_read;
43 };
44 typedef struct cna_interval_s cna_interval_t;
45
46 /*! Data types for WAFL statistics {{{
47  *
48  * \brief Persistent data for WAFL performance counters. (a.k.a. cache performance)
49  *
50  * The cache counters use old counter values to calculate a hit ratio for each
51  * counter. The "cfg_wafl_t" struct therefore contains old counter values along
52  * with flags, which are set if the counter is valid.
53  *
54  * The function "cna_handle_wafl_data" will fill a new structure of this kind
55  * with new values, then pass both, new and old data, to "submit_wafl_data".
56  * That function calculates the hit ratios, submits the calculated values and
57  * updates the old counter values for the next iteration.
58  */
59 #define CFG_WAFL_NAME_CACHE        0x0001
60 #define CFG_WAFL_DIR_CACHE         0x0002
61 #define CFG_WAFL_BUF_CACHE         0x0004
62 #define CFG_WAFL_INODE_CACHE       0x0008
63 #define CFG_WAFL_ALL               0x000F
64 #define HAVE_WAFL_NAME_CACHE_HIT   0x0100
65 #define HAVE_WAFL_NAME_CACHE_MISS  0x0200
66 #define HAVE_WAFL_NAME_CACHE       (HAVE_WAFL_NAME_CACHE_HIT | HAVE_WAFL_NAME_CACHE_MISS)
67 #define HAVE_WAFL_FIND_DIR_HIT     0x0400
68 #define HAVE_WAFL_FIND_DIR_MISS    0x0800
69 #define HAVE_WAFL_FIND_DIR         (HAVE_WAFL_FIND_DIR_HIT | HAVE_WAFL_FIND_DIR_MISS)
70 #define HAVE_WAFL_BUF_HASH_HIT     0x1000
71 #define HAVE_WAFL_BUF_HASH_MISS    0x2000
72 #define HAVE_WAFL_BUF_HASH         (HAVE_WAFL_BUF_HASH_HIT | HAVE_WAFL_BUF_HASH_MISS)
73 #define HAVE_WAFL_INODE_CACHE_HIT  0x4000
74 #define HAVE_WAFL_INODE_CACHE_MISS 0x8000
75 #define HAVE_WAFL_INODE_CACHE      (HAVE_WAFL_INODE_CACHE_HIT | HAVE_WAFL_INODE_CACHE_MISS)
76 #define HAVE_WAFL_ALL              0xff00
77 typedef struct {
78         uint32_t flags;
79         cna_interval_t interval;
80         na_elem_t *query;
81
82         cdtime_t timestamp;
83         uint64_t name_cache_hit;
84         uint64_t name_cache_miss;
85         uint64_t find_dir_hit;
86         uint64_t find_dir_miss;
87         uint64_t buf_hash_hit;
88         uint64_t buf_hash_miss;
89         uint64_t inode_cache_hit;
90         uint64_t inode_cache_miss;
91 } cfg_wafl_t;
92 /* }}} cfg_wafl_t */
93
94 /*! Data types for disk statistics {{{
95  *
96  * \brief A disk in the NetApp.
97  *
98  * A disk doesn't have any more information than its name at the moment.
99  * The name includes the "disk_" prefix.
100  */
101 #define HAVE_DISK_BUSY   0x10
102 #define HAVE_DISK_BASE   0x20
103 #define HAVE_DISK_ALL    0x30
104 typedef struct disk_s {
105         char *name;
106         uint32_t flags;
107         cdtime_t timestamp;
108         uint64_t disk_busy;
109         uint64_t base_for_disk_busy;
110         double disk_busy_percent;
111         struct disk_s *next;
112 } disk_t;
113
114 #define CFG_DISK_BUSIEST 0x01
115 #define CFG_DISK_ALL     0x01
116 typedef struct {
117         uint32_t flags;
118         cna_interval_t interval;
119         na_elem_t *query;
120         disk_t *disks;
121 } cfg_disk_t;
122 /* }}} cfg_disk_t */
123
124 /*! Data types for volume performance statistics {{{
125  *
126  * \brief Persistent data for volume performance data.
127  *
128  * The code below uses the difference of the operations and latency counters to
129  * calculate an average per-operation latency. For this, old counters need to
130  * be stored in the "data_volume_perf_t" structure. The byte-counters are just
131  * kept for completeness sake. The "flags" member indicates if each counter is
132  * valid or not.
133  *
134  * The "cna_handle_volume_perf_data" function will fill a new struct of this
135  * type and pass both, old and new data, to "submit_volume_perf_data". In that
136  * function, the per-operation latency is calculated and dispatched, then the
137  * old counters are updated.
138  */
139 #define CFG_VOLUME_PERF_INIT           0x0001
140 #define CFG_VOLUME_PERF_IO             0x0002
141 #define CFG_VOLUME_PERF_OPS            0x0003
142 #define CFG_VOLUME_PERF_LATENCY        0x0008
143 #define CFG_VOLUME_PERF_ALL            0x000F
144 #define HAVE_VOLUME_PERF_BYTES_READ    0x0010
145 #define HAVE_VOLUME_PERF_BYTES_WRITE   0x0020
146 #define HAVE_VOLUME_PERF_OPS_READ      0x0040
147 #define HAVE_VOLUME_PERF_OPS_WRITE     0x0080
148 #define HAVE_VOLUME_PERF_LATENCY_READ  0x0100
149 #define HAVE_VOLUME_PERF_LATENCY_WRITE 0x0200
150 #define HAVE_VOLUME_PERF_ALL           0x03F0
151 struct data_volume_perf_s;
152 typedef struct data_volume_perf_s data_volume_perf_t;
153 struct data_volume_perf_s {
154         char *name;
155         uint32_t flags;
156         cdtime_t timestamp;
157
158         uint64_t read_bytes;
159         uint64_t write_bytes;
160         uint64_t read_ops;
161         uint64_t write_ops;
162         uint64_t read_latency;
163         uint64_t write_latency;
164
165         data_volume_perf_t *next;
166 };
167
168 typedef struct {
169         cna_interval_t interval;
170         na_elem_t *query;
171
172         ignorelist_t *il_octets;
173         ignorelist_t *il_operations;
174         ignorelist_t *il_latency;
175
176         data_volume_perf_t *volumes;
177 } cfg_volume_perf_t;
178 /* }}} data_volume_perf_t */
179
180 /*! Data types for volume usage statistics {{{
181  *
182  * \brief Configuration struct for volume usage data (free / used).
183  */
184 #define CFG_VOLUME_USAGE_DF             0x0002
185 #define CFG_VOLUME_USAGE_SNAP           0x0004
186 #define CFG_VOLUME_USAGE_ALL            0x0006
187 #define HAVE_VOLUME_USAGE_NORM_FREE     0x0010
188 #define HAVE_VOLUME_USAGE_NORM_USED     0x0020
189 #define HAVE_VOLUME_USAGE_SNAP_RSVD     0x0040
190 #define HAVE_VOLUME_USAGE_SNAP_USED     0x0080
191 #define HAVE_VOLUME_USAGE_SIS_SAVED     0x0100
192 #define HAVE_VOLUME_USAGE_ALL           0x01f0
193 #define IS_VOLUME_USAGE_OFFLINE         0x0200
194 struct data_volume_usage_s;
195 typedef struct data_volume_usage_s data_volume_usage_t;
196 struct data_volume_usage_s {
197         char *name;
198         uint32_t flags;
199
200         na_elem_t *snap_query;
201
202         uint64_t norm_free;
203         uint64_t norm_used;
204         uint64_t snap_reserved;
205         uint64_t snap_used;
206         uint64_t sis_saved;
207
208         data_volume_usage_t *next;
209 };
210
211 typedef struct {
212         cna_interval_t interval;
213         na_elem_t *query;
214
215         ignorelist_t *il_capacity;
216         ignorelist_t *il_snapshot;
217
218         data_volume_usage_t *volumes;
219 } cfg_volume_usage_t;
220 /* }}} cfg_volume_usage_t */
221
222 /*! Data types for SnapVault statistics {{{
223  *
224  * \brief Persistent data for SnapVault(R) statistics
225  */
226 typedef struct {
227         cna_interval_t interval;
228         na_elem_t *query;
229 } cfg_snapvault_t;
230 /* }}} cfg_snapvault_t */
231
232 /*! Data types for system statistics {{{
233  *
234  * \brief Persistent data for system performance counters
235  */
236 #define CFG_SYSTEM_CPU  0x01
237 #define CFG_SYSTEM_NET  0x02
238 #define CFG_SYSTEM_OPS  0x04
239 #define CFG_SYSTEM_DISK 0x08
240 #define CFG_SYSTEM_ALL  0x0F
241 typedef struct {
242         uint32_t flags;
243         cna_interval_t interval;
244         na_elem_t *query;
245 } cfg_system_t;
246 /* }}} cfg_system_t */
247
248 struct host_config_s {
249         char *name;
250         na_server_transport_t protocol;
251         char *host;
252         int port;
253         char *username;
254         char *password;
255         cdtime_t interval;
256
257         na_server_t *srv;
258         cfg_wafl_t *cfg_wafl;
259         cfg_disk_t *cfg_disk;
260         cfg_volume_perf_t *cfg_volume_perf;
261         cfg_volume_usage_t *cfg_volume_usage;
262         cfg_snapvault_t *cfg_snapvault;
263         cfg_system_t *cfg_system;
264
265         struct host_config_s *next;
266 };
267
268 /*
269  * Free functions
270  *
271  * Used to free the various structures above.
272  */
273 static void free_disk (disk_t *disk) /* {{{ */
274 {
275         disk_t *next;
276
277         if (disk == NULL)
278                 return;
279
280         next = disk->next;
281
282         sfree (disk->name);
283         sfree (disk);
284
285         free_disk (next);
286 } /* }}} void free_disk */
287
288 static void free_cfg_wafl (cfg_wafl_t *cw) /* {{{ */
289 {
290         if (cw == NULL)
291                 return;
292
293         if (cw->query != NULL)
294                 na_elem_free (cw->query);
295
296         sfree (cw);
297 } /* }}} void free_cfg_wafl */
298
299 static void free_cfg_disk (cfg_disk_t *cfg_disk) /* {{{ */
300 {
301         if (cfg_disk == NULL)
302                 return;
303
304         if (cfg_disk->query != NULL)
305                 na_elem_free (cfg_disk->query);
306
307         free_disk (cfg_disk->disks);
308         sfree (cfg_disk);
309 } /* }}} void free_cfg_disk */
310
311 static void free_cfg_volume_perf (cfg_volume_perf_t *cvp) /* {{{ */
312 {
313         data_volume_perf_t *data;
314
315         if (cvp == NULL)
316                 return;
317
318         /* Free the ignorelists */
319         ignorelist_free (cvp->il_octets);
320         ignorelist_free (cvp->il_operations);
321         ignorelist_free (cvp->il_latency);
322
323         /* Free the linked list of volumes */
324         data = cvp->volumes;
325         while (data != NULL)
326         {
327                 data_volume_perf_t *next = data->next;
328                 sfree (data->name);
329                 sfree (data);
330                 data = next;
331         }
332
333         if (cvp->query != NULL)
334                 na_elem_free (cvp->query);
335
336         sfree (cvp);
337 } /* }}} void free_cfg_volume_perf */
338
339 static void free_cfg_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
340 {
341         data_volume_usage_t *data;
342
343         if (cvu == NULL)
344                 return;
345
346         /* Free the ignorelists */
347         ignorelist_free (cvu->il_capacity);
348         ignorelist_free (cvu->il_snapshot);
349
350         /* Free the linked list of volumes */
351         data = cvu->volumes;
352         while (data != NULL)
353         {
354                 data_volume_usage_t *next = data->next;
355                 sfree (data->name);
356                 if (data->snap_query != NULL)
357                         na_elem_free(data->snap_query);
358                 sfree (data);
359                 data = next;
360         }
361
362         if (cvu->query != NULL)
363                 na_elem_free (cvu->query);
364
365         sfree (cvu);
366 } /* }}} void free_cfg_volume_usage */
367
368 static void free_cfg_snapvault (cfg_snapvault_t *sv) /* {{{ */
369 {
370         if (sv == NULL)
371                 return;
372
373         if (sv->query != NULL)
374                 na_elem_free (sv->query);
375
376         sfree (sv);
377 } /* }}} void free_cfg_snapvault */
378
379 static void free_cfg_system (cfg_system_t *cs) /* {{{ */
380 {
381         if (cs == NULL)
382                 return;
383
384         if (cs->query != NULL)
385                 na_elem_free (cs->query);
386
387         sfree (cs);
388 } /* }}} void free_cfg_system */
389
390 static void free_host_config (host_config_t *hc) /* {{{ */
391 {
392         host_config_t *next;
393
394         if (hc == NULL)
395                 return;
396
397         next = hc->next;
398
399         sfree (hc->name);
400         sfree (hc->host);
401         sfree (hc->username);
402         sfree (hc->password);
403
404         free_cfg_disk (hc->cfg_disk);
405         free_cfg_wafl (hc->cfg_wafl);
406         free_cfg_volume_perf (hc->cfg_volume_perf);
407         free_cfg_volume_usage (hc->cfg_volume_usage);
408         free_cfg_snapvault (hc->cfg_snapvault);
409         free_cfg_system (hc->cfg_system);
410
411         if (hc->srv != NULL)
412                 na_server_close (hc->srv);
413
414         sfree (hc);
415
416         free_host_config (next);
417 } /* }}} void free_host_config */
418
419 /*
420  * Auxiliary functions
421  *
422  * Used to look up volumes and disks or to handle flags.
423  */
424 static disk_t *get_disk(cfg_disk_t *cd, const char *name) /* {{{ */
425 {
426         disk_t *d;
427
428         if ((cd == NULL) || (name == NULL))
429                 return (NULL);
430
431         for (d = cd->disks; d != NULL; d = d->next) {
432                 if (strcmp(d->name, name) == 0)
433                         return d;
434         }
435
436         d = malloc(sizeof(*d));
437         if (d == NULL)
438                 return (NULL);
439         memset (d, 0, sizeof (*d));
440         d->next = NULL;
441
442         d->name = strdup(name);
443         if (d->name == NULL) {
444                 sfree (d);
445                 return (NULL);
446         }
447
448         d->next = cd->disks;
449         cd->disks = d;
450
451         return d;
452 } /* }}} disk_t *get_disk */
453
454 static data_volume_usage_t *get_volume_usage (cfg_volume_usage_t *cvu, /* {{{ */
455                 const char *name)
456 {
457         data_volume_usage_t *last;
458         data_volume_usage_t *new;
459
460         int ignore_capacity = 0;
461         int ignore_snapshot = 0;
462
463         if ((cvu == NULL) || (name == NULL))
464                 return (NULL);
465
466         last = cvu->volumes;
467         while (last != NULL)
468         {
469                 if (strcmp (last->name, name) == 0)
470                         return (last);
471
472                 if (last->next == NULL)
473                         break;
474
475                 last = last->next;
476         }
477
478         /* Check the ignorelists. If *both* tell us to ignore a volume, return NULL. */
479         ignore_capacity = ignorelist_match (cvu->il_capacity, name);
480         ignore_snapshot = ignorelist_match (cvu->il_snapshot, name);
481         if ((ignore_capacity != 0) && (ignore_snapshot != 0))
482                 return (NULL);
483
484         /* Not found: allocate. */
485         new = malloc (sizeof (*new));
486         if (new == NULL)
487                 return (NULL);
488         memset (new, 0, sizeof (*new));
489         new->next = NULL;
490
491         new->name = strdup (name);
492         if (new->name == NULL)
493         {
494                 sfree (new);
495                 return (NULL);
496         }
497
498         if (ignore_capacity == 0)
499                 new->flags |= CFG_VOLUME_USAGE_DF;
500         if (ignore_snapshot == 0) {
501                 new->flags |= CFG_VOLUME_USAGE_SNAP;
502                 new->snap_query = na_elem_new ("snapshot-list-info");
503                 na_child_add_string(new->snap_query, "target-type", "volume");
504                 na_child_add_string(new->snap_query, "target-name", name);
505         } else {
506                 new->snap_query = NULL;
507         }
508
509         /* Add to end of list. */
510         if (last == NULL)
511                 cvu->volumes = new;
512         else
513                 last->next = new;
514
515         return (new);
516 } /* }}} data_volume_usage_t *get_volume_usage */
517
518 static data_volume_perf_t *get_volume_perf (cfg_volume_perf_t *cvp, /* {{{ */
519                 const char *name)
520 {
521         data_volume_perf_t *last;
522         data_volume_perf_t *new;
523
524         int ignore_octets = 0;
525         int ignore_operations = 0;
526         int ignore_latency = 0;
527
528         if ((cvp == NULL) || (name == NULL))
529                 return (NULL);
530
531         last = cvp->volumes;
532         while (last != NULL)
533         {
534                 if (strcmp (last->name, name) == 0)
535                         return (last);
536
537                 if (last->next == NULL)
538                         break;
539
540                 last = last->next;
541         }
542
543         /* Check the ignorelists. If *all three* tell us to ignore a volume, return
544          * NULL. */
545         ignore_octets = ignorelist_match (cvp->il_octets, name);
546         ignore_operations = ignorelist_match (cvp->il_operations, name);
547         ignore_latency = ignorelist_match (cvp->il_latency, name);
548         if ((ignore_octets != 0) || (ignore_operations != 0)
549                         || (ignore_latency != 0))
550                 return (NULL);
551
552         /* Not found: allocate. */
553         new = malloc (sizeof (*new));
554         if (new == NULL)
555                 return (NULL);
556         memset (new, 0, sizeof (*new));
557         new->next = NULL;
558
559         new->name = strdup (name);
560         if (new->name == NULL)
561         {
562                 sfree (new);
563                 return (NULL);
564         }
565
566         if (ignore_octets == 0)
567                 new->flags |= CFG_VOLUME_PERF_IO;
568         if (ignore_operations == 0)
569                 new->flags |= CFG_VOLUME_PERF_OPS;
570         if (ignore_latency == 0)
571                 new->flags |= CFG_VOLUME_PERF_LATENCY;
572
573         /* Add to end of list. */
574         if (last == NULL)
575                 cvp->volumes = new;
576         else
577                 last->next = new;
578
579         return (new);
580 } /* }}} data_volume_perf_t *get_volume_perf */
581
582 /*
583  * Various submit functions.
584  *
585  * They all eventually call "submit_values" which creates a value_list_t and
586  * dispatches it to the daemon.
587  */
588 static int submit_values (const char *host, /* {{{ */
589                 const char *plugin_inst,
590                 const char *type, const char *type_inst,
591                 value_t *values, int values_len,
592                 cdtime_t timestamp, cdtime_t interval)
593 {
594         value_list_t vl = VALUE_LIST_INIT;
595
596         vl.values = values;
597         vl.values_len = values_len;
598
599         if (timestamp > 0)
600                 vl.time = timestamp;
601
602         if (interval > 0)
603                 vl.interval = interval;
604
605         if (host != NULL)
606                 sstrncpy (vl.host, host, sizeof (vl.host));
607         else
608                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
609         sstrncpy (vl.plugin, "netapp", sizeof (vl.plugin));
610         if (plugin_inst != NULL)
611                 sstrncpy (vl.plugin_instance, plugin_inst, sizeof (vl.plugin_instance));
612         sstrncpy (vl.type, type, sizeof (vl.type));
613         if (type_inst != NULL)
614                 sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
615
616         return (plugin_dispatch_values (&vl));
617 } /* }}} int submit_uint64 */
618
619 static int submit_two_derive (const char *host, const char *plugin_inst, /* {{{ */
620                 const char *type, const char *type_inst, derive_t val0, derive_t val1,
621                 cdtime_t timestamp, cdtime_t interval)
622 {
623         value_t values[2];
624
625         values[0].derive = val0;
626         values[1].derive = val1;
627
628         return (submit_values (host, plugin_inst, type, type_inst,
629                                 values, 2, timestamp, interval));
630 } /* }}} int submit_two_derive */
631
632 static int submit_derive (const char *host, const char *plugin_inst, /* {{{ */
633                 const char *type, const char *type_inst, derive_t counter,
634                 cdtime_t timestamp, cdtime_t interval)
635 {
636         value_t v;
637
638         v.derive = counter;
639
640         return (submit_values (host, plugin_inst, type, type_inst,
641                                 &v, 1, timestamp, interval));
642 } /* }}} int submit_derive */
643
644 static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */
645                 const char *type, const char *type_inst, gauge_t val0, gauge_t val1,
646                 cdtime_t timestamp, cdtime_t interval)
647 {
648         value_t values[2];
649
650         values[0].gauge = val0;
651         values[1].gauge = val1;
652
653         return (submit_values (host, plugin_inst, type, type_inst,
654                                 values, 2, timestamp, interval));
655 } /* }}} int submit_two_gauge */
656
657 static int submit_double (const char *host, const char *plugin_inst, /* {{{ */
658                 const char *type, const char *type_inst, double d,
659                 cdtime_t timestamp, cdtime_t interval)
660 {
661         value_t v;
662
663         v.gauge = (gauge_t) d;
664
665         return (submit_values (host, plugin_inst, type, type_inst,
666                                 &v, 1, timestamp, interval));
667 } /* }}} int submit_uint64 */
668
669 /* Calculate hit ratio from old and new counters and submit the resulting
670  * percentage. Used by "submit_wafl_data". */
671 static int submit_cache_ratio (const char *host, /* {{{ */
672                 const char *plugin_inst,
673                 const char *type_inst,
674                 uint64_t new_hits,
675                 uint64_t new_misses,
676                 uint64_t old_hits,
677                 uint64_t old_misses,
678                 cdtime_t timestamp,
679                 cdtime_t interval)
680 {
681         value_t v;
682
683         if ((new_hits >= old_hits) && (new_misses >= old_misses)) {
684                 uint64_t hits;
685                 uint64_t misses;
686
687                 hits = new_hits - old_hits;
688                 misses = new_misses - old_misses;
689
690                 v.gauge = 100.0 * ((gauge_t) hits) / ((gauge_t) (hits + misses));
691         } else {
692                 v.gauge = NAN;
693         }
694
695         return (submit_values (host, plugin_inst, "cache_ratio", type_inst,
696                                 &v, 1, timestamp, interval));
697 } /* }}} int submit_cache_ratio */
698
699 /* Submits all the caches used by WAFL. Uses "submit_cache_ratio". */
700 static int submit_wafl_data (const char *hostname, const char *instance, /* {{{ */
701                 cfg_wafl_t *old_data, const cfg_wafl_t *new_data, int interval)
702 {
703         /* Submit requested counters */
704         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
705                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_NAME_CACHE))
706                 submit_cache_ratio (hostname, instance, "name_cache_hit",
707                                 new_data->name_cache_hit, new_data->name_cache_miss,
708                                 old_data->name_cache_hit, old_data->name_cache_miss,
709                                 new_data->timestamp, interval);
710
711         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR)
712                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR))
713                 submit_cache_ratio (hostname, instance, "find_dir_hit",
714                                 new_data->find_dir_hit, new_data->find_dir_miss,
715                                 old_data->find_dir_hit, old_data->find_dir_miss,
716                                 new_data->timestamp, interval);
717
718         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH)
719                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH))
720                 submit_cache_ratio (hostname, instance, "buf_hash_hit",
721                                 new_data->buf_hash_hit, new_data->buf_hash_miss,
722                                 old_data->buf_hash_hit, old_data->buf_hash_miss,
723                                 new_data->timestamp, interval);
724
725         if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE)
726                         && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE))
727                 submit_cache_ratio (hostname, instance, "inode_cache_hit",
728                                 new_data->inode_cache_hit, new_data->inode_cache_miss,
729                                 old_data->inode_cache_hit, old_data->inode_cache_miss,
730                                 new_data->timestamp, interval);
731
732         /* Clear old HAVE_* flags */
733         old_data->flags &= ~HAVE_WAFL_ALL;
734
735         /* Copy all counters */
736         old_data->timestamp        = new_data->timestamp;
737         old_data->name_cache_hit   = new_data->name_cache_hit;
738         old_data->name_cache_miss  = new_data->name_cache_miss;
739         old_data->find_dir_hit     = new_data->find_dir_hit;
740         old_data->find_dir_miss    = new_data->find_dir_miss;
741         old_data->buf_hash_hit     = new_data->buf_hash_hit;
742         old_data->buf_hash_miss    = new_data->buf_hash_miss;
743         old_data->inode_cache_hit  = new_data->inode_cache_hit;
744         old_data->inode_cache_miss = new_data->inode_cache_miss;
745
746         /* Copy HAVE_* flags */
747         old_data->flags |= (new_data->flags & HAVE_WAFL_ALL);
748
749         return (0);
750 } /* }}} int submit_wafl_data */
751
752 /* Submits volume performance data to the daemon, taking care to honor and
753  * update flags appropriately. */
754 static int submit_volume_perf_data (const char *hostname, /* {{{ */
755                 data_volume_perf_t *old_data,
756                 const data_volume_perf_t *new_data, int interval)
757 {
758         char plugin_instance[DATA_MAX_NAME_LEN];
759
760         if ((hostname == NULL) || (old_data == NULL) || (new_data == NULL))
761                 return (-1);
762
763         ssnprintf (plugin_instance, sizeof (plugin_instance),
764                         "volume-%s", old_data->name);
765
766         /* Check for and submit disk-octet values */
767         if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_IO)
768                         && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE))
769         {
770                 submit_two_derive (hostname, plugin_instance, "disk_octets", /* type instance = */ NULL,
771                                 (derive_t) new_data->read_bytes, (derive_t) new_data->write_bytes, new_data->timestamp, interval);
772         }
773
774         /* Check for and submit disk-operations values */
775         if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_OPS)
776                         && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE))
777         {
778                 submit_two_derive (hostname, plugin_instance, "disk_ops", /* type instance = */ NULL,
779                                 (derive_t) new_data->read_ops, (derive_t) new_data->write_ops, new_data->timestamp, interval);
780         }
781
782         /* Check for, calculate and submit disk-latency values */
783         if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_LATENCY
784                                 | HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
785                                 | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE)
786                         && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
787                                 | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE))
788         {
789                 gauge_t latency_per_op_read;
790                 gauge_t latency_per_op_write;
791
792                 latency_per_op_read = NAN;
793                 latency_per_op_write = NAN;
794
795                 /* Check if a counter wrapped around. */
796                 if ((new_data->read_ops > old_data->read_ops)
797                                 && (new_data->read_latency > old_data->read_latency))
798                 {
799                         uint64_t diff_ops_read;
800                         uint64_t diff_latency_read;
801
802                         diff_ops_read = new_data->read_ops - old_data->read_ops;
803                         diff_latency_read = new_data->read_latency - old_data->read_latency;
804
805                         if (diff_ops_read > 0)
806                                 latency_per_op_read = ((gauge_t) diff_latency_read) / ((gauge_t) diff_ops_read);
807                 }
808
809                 if ((new_data->write_ops > old_data->write_ops)
810                                 && (new_data->write_latency > old_data->write_latency))
811                 {
812                         uint64_t diff_ops_write;
813                         uint64_t diff_latency_write;
814
815                         diff_ops_write = new_data->write_ops - old_data->write_ops;
816                         diff_latency_write = new_data->write_latency - old_data->write_latency;
817
818                         if (diff_ops_write > 0)
819                                 latency_per_op_write = ((gauge_t) diff_latency_write) / ((gauge_t) diff_ops_write);
820                 }
821
822                 submit_two_gauge (hostname, plugin_instance, "disk_latency", /* type instance = */ NULL,
823                                 latency_per_op_read, latency_per_op_write, new_data->timestamp, interval);
824         }
825
826         /* Clear all HAVE_* flags. */
827         old_data->flags &= ~HAVE_VOLUME_PERF_ALL;
828
829         /* Copy all counters */
830         old_data->timestamp = new_data->timestamp;
831         old_data->read_bytes = new_data->read_bytes;
832         old_data->write_bytes = new_data->write_bytes;
833         old_data->read_ops = new_data->read_ops;
834         old_data->write_ops = new_data->write_ops;
835         old_data->read_latency = new_data->read_latency;
836         old_data->write_latency = new_data->write_latency;
837
838         /* Copy the HAVE_* flags */
839         old_data->flags |= (new_data->flags & HAVE_VOLUME_PERF_ALL);
840
841         return (0);
842 } /* }}} int submit_volume_perf_data */
843
844 static cdtime_t cna_child_get_cdtime (na_elem_t *data) /* {{{ */
845 {
846         time_t t;
847
848         t = (time_t) na_child_get_uint64 (data, "timestamp", /* default = */ 0);
849
850         return (TIME_T_TO_CDTIME_T (t));
851 } /* }}} cdtime_t cna_child_get_cdtime */
852
853
854 /* 
855  * Query functions
856  *
857  * These functions are called with appropriate data returned by the libnetapp
858  * interface which is parsed and submitted with the above functions.
859  */
860 /* Data corresponding to <WAFL /> */
861 static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */
862                 na_elem_t *data, int interval)
863 {
864         cfg_wafl_t perf_data;
865         const char *plugin_inst;
866
867         na_elem_t *instances;
868         na_elem_t *counter;
869         na_elem_iter_t counter_iter;
870
871         memset (&perf_data, 0, sizeof (perf_data));
872         
873         perf_data.timestamp = cna_child_get_cdtime (data);
874
875         instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
876         if (instances == NULL)
877         {
878                 ERROR ("netapp plugin: cna_handle_wafl_data: "
879                                 "na_elem_child (\"instances\") failed "
880                                 "for host %s.", hostname);
881                 return (-1);
882         }
883
884         plugin_inst = na_child_get_string(instances, "name");
885         if (plugin_inst == NULL)
886         {
887                 ERROR ("netapp plugin: cna_handle_wafl_data: "
888                                 "na_child_get_string (\"name\") failed "
889                                 "for host %s.", hostname);
890                 return (-1);
891         }
892
893         /* Iterate over all counters */
894         counter_iter = na_child_iterator (na_elem_child (instances, "counters"));
895         for (counter = na_iterator_next (&counter_iter);
896                         counter != NULL;
897                         counter = na_iterator_next (&counter_iter))
898         {
899                 const char *name;
900                 uint64_t value;
901
902                 name = na_child_get_string(counter, "name");
903                 if (name == NULL)
904                         continue;
905
906                 value = na_child_get_uint64(counter, "value", UINT64_MAX);
907                 if (value == UINT64_MAX)
908                         continue;
909
910                 if (!strcmp(name, "name_cache_hit")) {
911                         perf_data.name_cache_hit = value;
912                         perf_data.flags |= HAVE_WAFL_NAME_CACHE_HIT;
913                 } else if (!strcmp(name, "name_cache_miss")) {
914                         perf_data.name_cache_miss = value;
915                         perf_data.flags |= HAVE_WAFL_NAME_CACHE_MISS;
916                 } else if (!strcmp(name, "find_dir_hit")) {
917                         perf_data.find_dir_hit = value;
918                         perf_data.flags |= HAVE_WAFL_FIND_DIR_HIT;
919                 } else if (!strcmp(name, "find_dir_miss")) {
920                         perf_data.find_dir_miss = value;
921                         perf_data.flags |= HAVE_WAFL_FIND_DIR_MISS;
922                 } else if (!strcmp(name, "buf_hash_hit")) {
923                         perf_data.buf_hash_hit = value;
924                         perf_data.flags |= HAVE_WAFL_BUF_HASH_HIT;
925                 } else if (!strcmp(name, "buf_hash_miss")) {
926                         perf_data.buf_hash_miss = value;
927                         perf_data.flags |= HAVE_WAFL_BUF_HASH_MISS;
928                 } else if (!strcmp(name, "inode_cache_hit")) {
929                         perf_data.inode_cache_hit = value;
930                         perf_data.flags |= HAVE_WAFL_INODE_CACHE_HIT;
931                 } else if (!strcmp(name, "inode_cache_miss")) {
932                         perf_data.inode_cache_miss = value;
933                         perf_data.flags |= HAVE_WAFL_INODE_CACHE_MISS;
934                 } else {
935                         DEBUG("netapp plugin: cna_handle_wafl_data: "
936                                         "Found unexpected child: %s "
937                                         "for host %s.", name, hostname);
938                 }
939         }
940
941         return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data, interval));
942 } /* }}} void cna_handle_wafl_data */
943
944 static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */
945 {
946         na_elem_t *e;
947
948         if (cw == NULL)
949                 return (EINVAL);
950
951         if (cw->query != NULL)
952                 return (0);
953
954         cw->query = na_elem_new("perf-object-get-instances");
955         if (cw->query == NULL)
956         {
957                 ERROR ("netapp plugin: na_elem_new failed.");
958                 return (-1);
959         }
960         na_child_add_string (cw->query, "objectname", "wafl");
961
962         e = na_elem_new("counters");
963         if (e == NULL)
964         {
965                 na_elem_free (cw->query);
966                 cw->query = NULL;
967                 ERROR ("netapp plugin: na_elem_new failed.");
968                 return (-1);
969         }
970         na_child_add_string(e, "counter", "name_cache_hit");
971         na_child_add_string(e, "counter", "name_cache_miss");
972         na_child_add_string(e, "counter", "find_dir_hit");
973         na_child_add_string(e, "counter", "find_dir_miss");
974         na_child_add_string(e, "counter", "buf_hash_hit");
975         na_child_add_string(e, "counter", "buf_hash_miss");
976         na_child_add_string(e, "counter", "inode_cache_hit");
977         na_child_add_string(e, "counter", "inode_cache_miss");
978
979         na_child_add(cw->query, e);
980
981         return (0);
982 } /* }}} int cna_setup_wafl */
983
984 static int cna_query_wafl (host_config_t *host) /* {{{ */
985 {
986         na_elem_t *data;
987         int status;
988         cdtime_t now;
989
990         if (host == NULL)
991                 return (EINVAL);
992
993         /* If WAFL was not configured, return without doing anything. */
994         if (host->cfg_wafl == NULL)
995                 return (0);
996
997         now = cdtime ();
998         if ((host->cfg_wafl->interval.interval + host->cfg_wafl->interval.last_read) > now)
999                 return (0);
1000
1001         status = cna_setup_wafl (host->cfg_wafl);
1002         if (status != 0)
1003                 return (status);
1004         assert (host->cfg_wafl->query != NULL);
1005
1006         data = na_server_invoke_elem(host->srv, host->cfg_wafl->query);
1007         if (na_results_status (data) != NA_OK)
1008         {
1009                 ERROR ("netapp plugin: cna_query_wafl: na_server_invoke_elem failed for host %s: %s",
1010                                 host->name, na_results_reason (data));
1011                 na_elem_free (data);
1012                 return (-1);
1013         }
1014
1015         status = cna_handle_wafl_data (host->name, host->cfg_wafl, data, host->interval);
1016
1017         if (status == 0)
1018                 host->cfg_wafl->interval.last_read = now;
1019
1020         na_elem_free (data);
1021         return (status);
1022 } /* }}} int cna_query_wafl */
1023
1024 /* Data corresponding to <Disks /> */
1025 static int cna_handle_disk_data (const char *hostname, /* {{{ */
1026                 cfg_disk_t *cfg_disk, na_elem_t *data, cdtime_t interval)
1027 {
1028         cdtime_t timestamp;
1029         na_elem_t *instances;
1030         na_elem_t *instance;
1031         na_elem_iter_t instance_iter;
1032         disk_t *worst_disk = NULL;
1033
1034         if ((cfg_disk == NULL) || (data == NULL))
1035                 return (EINVAL);
1036         
1037         timestamp = cna_child_get_cdtime (data);
1038
1039         instances = na_elem_child (data, "instances");
1040         if (instances == NULL)
1041         {
1042                 ERROR ("netapp plugin: cna_handle_disk_data: "
1043                                 "na_elem_child (\"instances\") failed "
1044                                 "for host %s.", hostname);
1045                 return (-1);
1046         }
1047
1048         /* Iterate over all children */
1049         instance_iter = na_child_iterator (instances);
1050         for (instance = na_iterator_next (&instance_iter);
1051                         instance != NULL;
1052                         instance = na_iterator_next(&instance_iter))
1053         {
1054                 disk_t *old_data;
1055                 disk_t  new_data;
1056
1057                 na_elem_iter_t counter_iterator;
1058                 na_elem_t *counter;
1059
1060                 memset (&new_data, 0, sizeof (new_data));
1061                 new_data.timestamp = timestamp;
1062                 new_data.disk_busy_percent = NAN;
1063
1064                 old_data = get_disk(cfg_disk, na_child_get_string (instance, "name"));
1065                 if (old_data == NULL)
1066                         continue;
1067
1068                 /* Look for the "disk_busy" and "base_for_disk_busy" counters */
1069                 counter_iterator = na_child_iterator(na_elem_child(instance, "counters"));
1070                 for (counter = na_iterator_next(&counter_iterator);
1071                                 counter != NULL;
1072                                 counter = na_iterator_next(&counter_iterator))
1073                 {
1074                         const char *name;
1075                         uint64_t value;
1076
1077                         name = na_child_get_string(counter, "name");
1078                         if (name == NULL)
1079                                 continue;
1080
1081                         value = na_child_get_uint64(counter, "value", UINT64_MAX);
1082                         if (value == UINT64_MAX)
1083                                 continue;
1084
1085                         if (strcmp(name, "disk_busy") == 0)
1086                         {
1087                                 new_data.disk_busy = value;
1088                                 new_data.flags |= HAVE_DISK_BUSY;
1089                         }
1090                         else if (strcmp(name, "base_for_disk_busy") == 0)
1091                         {
1092                                 new_data.base_for_disk_busy = value;
1093                                 new_data.flags |= HAVE_DISK_BASE;
1094                         }
1095                         else
1096                         {
1097                                 DEBUG ("netapp plugin: cna_handle_disk_data: "
1098                                                 "Counter not handled: %s = %"PRIu64,
1099                                                 name, value);
1100                         }
1101                 }
1102
1103                 /* If all required counters are available and did not just wrap around,
1104                  * calculate the busy percentage. Otherwise, the value is initialized to
1105                  * NAN at the top of the for-loop. */
1106                 if (HAS_ALL_FLAGS (old_data->flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
1107                                 && HAS_ALL_FLAGS (new_data.flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
1108                                 && (new_data.disk_busy >= old_data->disk_busy)
1109                                 && (new_data.base_for_disk_busy > old_data->base_for_disk_busy))
1110                 {
1111                         uint64_t busy_diff;
1112                         uint64_t base_diff;
1113
1114                         busy_diff = new_data.disk_busy - old_data->disk_busy;
1115                         base_diff = new_data.base_for_disk_busy - old_data->base_for_disk_busy;
1116
1117                         new_data.disk_busy_percent = 100.0
1118                                 * ((gauge_t) busy_diff) / ((gauge_t) base_diff);
1119                 }
1120
1121                 /* Clear HAVE_* flags */
1122                 old_data->flags &= ~HAVE_DISK_ALL;
1123
1124                 /* Copy data */
1125                 old_data->timestamp = new_data.timestamp;
1126                 old_data->disk_busy = new_data.disk_busy;
1127                 old_data->base_for_disk_busy = new_data.base_for_disk_busy;
1128                 old_data->disk_busy_percent = new_data.disk_busy_percent;
1129
1130                 /* Copy flags */
1131                 old_data->flags |= (new_data.flags & HAVE_DISK_ALL);
1132
1133                 if ((worst_disk == NULL)
1134                                 || (worst_disk->disk_busy_percent < old_data->disk_busy_percent))
1135                         worst_disk = old_data;
1136         } /* for (all disks) */
1137
1138         if ((cfg_disk->flags & CFG_DISK_BUSIEST) && (worst_disk != NULL))
1139                 submit_double (hostname, "system", "percent", "disk_busy",
1140                                 worst_disk->disk_busy_percent, timestamp, interval);
1141
1142         return (0);
1143 } /* }}} int cna_handle_disk_data */
1144
1145 static int cna_setup_disk (cfg_disk_t *cd) /* {{{ */
1146 {
1147         na_elem_t *e;
1148
1149         if (cd == NULL)
1150                 return (EINVAL);
1151
1152         if (cd->query != NULL)
1153                 return (0);
1154
1155         cd->query = na_elem_new ("perf-object-get-instances");
1156         if (cd->query == NULL)
1157         {
1158                 ERROR ("netapp plugin: na_elem_new failed.");
1159                 return (-1);
1160         }
1161         na_child_add_string (cd->query, "objectname", "disk");
1162
1163         e = na_elem_new("counters");
1164         if (e == NULL)
1165         {
1166                 na_elem_free (cd->query);
1167                 cd->query = NULL;
1168                 ERROR ("netapp plugin: na_elem_new failed.");
1169                 return (-1);
1170         }
1171         na_child_add_string(e, "counter", "disk_busy");
1172         na_child_add_string(e, "counter", "base_for_disk_busy");
1173         na_child_add(cd->query, e);
1174
1175         return (0);
1176 } /* }}} int cna_setup_disk */
1177
1178 static int cna_query_disk (host_config_t *host) /* {{{ */
1179 {
1180         na_elem_t *data;
1181         int status;
1182         cdtime_t now;
1183
1184         if (host == NULL)
1185                 return (EINVAL);
1186
1187         /* If the user did not configure disk statistics, return without doing
1188          * anything. */
1189         if (host->cfg_disk == NULL)
1190                 return (0);
1191
1192         now = cdtime ();
1193         if ((host->cfg_disk->interval.interval + host->cfg_disk->interval.last_read) > now)
1194                 return (0);
1195
1196         status = cna_setup_disk (host->cfg_disk);
1197         if (status != 0)
1198                 return (status);
1199         assert (host->cfg_disk->query != NULL);
1200
1201         data = na_server_invoke_elem(host->srv, host->cfg_disk->query);
1202         if (na_results_status (data) != NA_OK)
1203         {
1204                 ERROR ("netapp plugin: cna_query_disk: na_server_invoke_elem failed for host %s: %s",
1205                                 host->name, na_results_reason (data));
1206                 na_elem_free (data);
1207                 return (-1);
1208         }
1209
1210         status = cna_handle_disk_data (host->name, host->cfg_disk, data, host->interval);
1211
1212         if (status == 0)
1213                 host->cfg_disk->interval.last_read = now;
1214
1215         na_elem_free (data);
1216         return (status);
1217 } /* }}} int cna_query_disk */
1218
1219 /* Data corresponding to <VolumePerf /> */
1220 static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */
1221                 cfg_volume_perf_t *cvp, na_elem_t *data, cdtime_t interval)
1222 {
1223         cdtime_t timestamp;
1224         na_elem_t *elem_instances;
1225         na_elem_iter_t iter_instances;
1226         na_elem_t *elem_instance;
1227         
1228         timestamp = cna_child_get_cdtime (data);
1229
1230         elem_instances = na_elem_child(data, "instances");
1231         if (elem_instances == NULL)
1232         {
1233                 ERROR ("netapp plugin: handle_volume_perf_data: "
1234                                 "na_elem_child (\"instances\") failed "
1235                                 "for host %s.", hostname);
1236                 return (-1);
1237         }
1238
1239         iter_instances = na_child_iterator (elem_instances);
1240         for (elem_instance = na_iterator_next(&iter_instances);
1241                         elem_instance != NULL;
1242                         elem_instance = na_iterator_next(&iter_instances))
1243         {
1244                 const char *name;
1245
1246                 data_volume_perf_t perf_data;
1247                 data_volume_perf_t *v;
1248
1249                 na_elem_t *elem_counters;
1250                 na_elem_iter_t iter_counters;
1251                 na_elem_t *elem_counter;
1252
1253                 memset (&perf_data, 0, sizeof (perf_data));
1254                 perf_data.timestamp = timestamp;
1255
1256                 name = na_child_get_string (elem_instance, "name");
1257                 if (name == NULL)
1258                         continue;
1259
1260                 /* get_volume_perf may return NULL if this volume is to be ignored. */
1261                 v = get_volume_perf (cvp, name);
1262                 if (v == NULL)
1263                         continue;
1264
1265                 elem_counters = na_elem_child (elem_instance, "counters");
1266                 if (elem_counters == NULL)
1267                         continue;
1268
1269                 iter_counters = na_child_iterator (elem_counters);
1270                 for (elem_counter = na_iterator_next(&iter_counters);
1271                                 elem_counter != NULL;
1272                                 elem_counter = na_iterator_next(&iter_counters))
1273                 {
1274                         const char *name;
1275                         uint64_t value;
1276
1277                         name = na_child_get_string (elem_counter, "name");
1278                         if (name == NULL)
1279                                 continue;
1280
1281                         value = na_child_get_uint64 (elem_counter, "value", UINT64_MAX);
1282                         if (value == UINT64_MAX)
1283                                 continue;
1284
1285                         if (!strcmp(name, "read_data")) {
1286                                 perf_data.read_bytes = value;
1287                                 perf_data.flags |= HAVE_VOLUME_PERF_BYTES_READ;
1288                         } else if (!strcmp(name, "write_data")) {
1289                                 perf_data.write_bytes = value;
1290                                 perf_data.flags |= HAVE_VOLUME_PERF_BYTES_WRITE;
1291                         } else if (!strcmp(name, "read_ops")) {
1292                                 perf_data.read_ops = value;
1293                                 perf_data.flags |= HAVE_VOLUME_PERF_OPS_READ;
1294                         } else if (!strcmp(name, "write_ops")) {
1295                                 perf_data.write_ops = value;
1296                                 perf_data.flags |= HAVE_VOLUME_PERF_OPS_WRITE;
1297                         } else if (!strcmp(name, "read_latency")) {
1298                                 perf_data.read_latency = value;
1299                                 perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_READ;
1300                         } else if (!strcmp(name, "write_latency")) {
1301                                 perf_data.write_latency = value;
1302                                 perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_WRITE;
1303                         }
1304                 } /* for (elem_counter) */
1305
1306                 submit_volume_perf_data (hostname, v, &perf_data, interval);
1307         } /* for (volume) */
1308
1309         return (0);
1310 } /* }}} int cna_handle_volume_perf_data */
1311
1312 static int cna_setup_volume_perf (cfg_volume_perf_t *cd) /* {{{ */
1313 {
1314         na_elem_t *e;
1315
1316         if (cd == NULL)
1317                 return (EINVAL);
1318
1319         if (cd->query != NULL)
1320                 return (0);
1321
1322         cd->query = na_elem_new ("perf-object-get-instances");
1323         if (cd->query == NULL)
1324         {
1325                 ERROR ("netapp plugin: na_elem_new failed.");
1326                 return (-1);
1327         }
1328         na_child_add_string (cd->query, "objectname", "volume");
1329
1330         e = na_elem_new("counters");
1331         if (e == NULL)
1332         {
1333                 na_elem_free (cd->query);
1334                 cd->query = NULL;
1335                 ERROR ("netapp plugin: na_elem_new failed.");
1336                 return (-1);
1337         }
1338         na_child_add_string(e, "counter", "read_ops");
1339         na_child_add_string(e, "counter", "write_ops");
1340         na_child_add_string(e, "counter", "read_data");
1341         na_child_add_string(e, "counter", "write_data");
1342         na_child_add_string(e, "counter", "read_latency");
1343         na_child_add_string(e, "counter", "write_latency");
1344         na_child_add(cd->query, e);
1345
1346         return (0);
1347 } /* }}} int cna_setup_volume_perf */
1348
1349 static int cna_query_volume_perf (host_config_t *host) /* {{{ */
1350 {
1351         na_elem_t *data;
1352         int status;
1353         cdtime_t now;
1354
1355         if (host == NULL)
1356                 return (EINVAL);
1357
1358         /* If the user did not configure volume performance statistics, return
1359          * without doing anything. */
1360         if (host->cfg_volume_perf == NULL)
1361                 return (0);
1362
1363         now = cdtime ();
1364         if ((host->cfg_volume_perf->interval.interval + host->cfg_volume_perf->interval.last_read) > now)
1365                 return (0);
1366
1367         status = cna_setup_volume_perf (host->cfg_volume_perf);
1368         if (status != 0)
1369                 return (status);
1370         assert (host->cfg_volume_perf->query != NULL);
1371
1372         data = na_server_invoke_elem (host->srv, host->cfg_volume_perf->query);
1373         if (na_results_status (data) != NA_OK)
1374         {
1375                 ERROR ("netapp plugin: cna_query_volume_perf: na_server_invoke_elem failed for host %s: %s",
1376                                 host->name, na_results_reason (data));
1377                 na_elem_free (data);
1378                 return (-1);
1379         }
1380
1381         status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data, host->interval);
1382
1383         if (status == 0)
1384                 host->cfg_volume_perf->interval.last_read = now;
1385
1386         na_elem_free (data);
1387         return (status);
1388 } /* }}} int cna_query_volume_perf */
1389
1390 /* Data corresponding to <VolumeUsage /> */
1391 static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
1392                 cfg_volume_usage_t *cfg_volume, int interval)
1393 {
1394         data_volume_usage_t *v;
1395
1396         for (v = cfg_volume->volumes; v != NULL; v = v->next)
1397         {
1398                 char plugin_instance[DATA_MAX_NAME_LEN];
1399
1400                 uint64_t norm_used = v->norm_used;
1401                 uint64_t norm_free = v->norm_free;
1402                 uint64_t sis_saved = v->sis_saved;
1403                 uint64_t snap_reserve_used = 0;
1404                 uint64_t snap_reserve_free = v->snap_reserved;
1405                 uint64_t snap_norm_used = v->snap_used;
1406
1407                 ssnprintf (plugin_instance, sizeof (plugin_instance),
1408                                 "volume-%s", v->name);
1409
1410                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD)) {
1411                         if (v->snap_reserved > v->snap_used) {
1412                                 snap_reserve_free = v->snap_reserved - v->snap_used;
1413                                 snap_reserve_used = v->snap_used;
1414                                 snap_norm_used = 0;
1415                         } else {
1416                                 snap_reserve_free = 0;
1417                                 snap_reserve_used = v->snap_reserved;
1418                                 snap_norm_used = v->snap_used - v->snap_reserved;
1419                         }
1420                 }
1421
1422                 /* The space used by snapshots but not reserved for them is included in
1423                  * both, norm_used and snap_norm_used. If possible, subtract this here. */
1424                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED | HAVE_VOLUME_USAGE_SNAP_USED))
1425                 {
1426                         if (norm_used >= snap_norm_used)
1427                                 norm_used -= snap_norm_used;
1428                         else
1429                         {
1430                                 ERROR ("netapp plugin: (norm_used = %"PRIu64") < (snap_norm_used = "
1431                                                 "%"PRIu64") for host %s. Invalidating both.",
1432                                                 norm_used, snap_norm_used, hostname);
1433                                 v->flags &= ~(HAVE_VOLUME_USAGE_NORM_USED | HAVE_VOLUME_USAGE_SNAP_USED);
1434                         }
1435                 }
1436
1437                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_FREE))
1438                         submit_double (hostname, /* plugin instance = */ plugin_instance,
1439                                         "df_complex", "free",
1440                                         (double) norm_free, /* timestamp = */ 0, interval);
1441
1442                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SIS_SAVED))
1443                         submit_double (hostname, /* plugin instance = */ plugin_instance,
1444                                         "df_complex", "sis_saved",
1445                                         (double) sis_saved, /* timestamp = */ 0, interval);
1446
1447                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED))
1448                         submit_double (hostname, /* plugin instance = */ plugin_instance,
1449                                         "df_complex", "used",
1450                                         (double) norm_used, /* timestamp = */ 0, interval);
1451
1452                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_RSVD))
1453                         submit_double (hostname, /* plugin instance = */ plugin_instance,
1454                                         "df_complex", "snap_reserved",
1455                                         (double) snap_reserve_free, /* timestamp = */ 0, interval);
1456
1457                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD))
1458                         submit_double (hostname, /* plugin instance = */ plugin_instance,
1459                                         "df_complex", "snap_reserve_used",
1460                                         (double) snap_reserve_used, /* timestamp = */ 0, interval);
1461
1462                 if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED))
1463                         submit_double (hostname, /* plugin instance = */ plugin_instance,
1464                                         "df_complex", "snap_normal_used",
1465                                         (double) snap_norm_used, /* timestamp = */ 0, interval);
1466
1467                 /* Clear all the HAVE_* flags */
1468                 v->flags &= ~HAVE_VOLUME_USAGE_ALL;
1469         } /* for (v = cfg_volume->volumes) */
1470
1471         return (0);
1472 } /* }}} int cna_submit_volume_usage_data */
1473
1474 /* Switch the state of a volume between online and offline and send out a
1475  * notification. */
1476 static int cna_change_volume_status (const char *hostname, /* {{{ */
1477                 data_volume_usage_t *v)
1478 {
1479         notification_t n;
1480
1481         memset (&n, 0, sizeof (&n));
1482         n.time = cdtime ();
1483         sstrncpy (n.host, hostname, sizeof (n.host));
1484         sstrncpy (n.plugin, "netapp", sizeof (n.plugin));
1485         sstrncpy (n.plugin_instance, v->name, sizeof (n.plugin_instance));
1486
1487         if ((v->flags & IS_VOLUME_USAGE_OFFLINE) != 0) {
1488                 n.severity = NOTIF_OKAY;
1489                 ssnprintf (n.message, sizeof (n.message),
1490                                 "Volume %s is now online.", v->name);
1491                 v->flags &= ~IS_VOLUME_USAGE_OFFLINE;
1492         } else {
1493                 n.severity = NOTIF_WARNING;
1494                 ssnprintf (n.message, sizeof (n.message),
1495                                 "Volume %s is now offline.", v->name);
1496                 v->flags |= IS_VOLUME_USAGE_OFFLINE;
1497         }
1498
1499         return (plugin_dispatch_notification (&n));
1500 } /* }}} int cna_change_volume_status */
1501
1502 static void cna_handle_volume_snap_usage(const host_config_t *host, /* {{{ */
1503                 data_volume_usage_t *v)
1504 {
1505         uint64_t snap_used = 0, value;
1506         na_elem_t *data, *elem_snap, *elem_snapshots;
1507         na_elem_iter_t iter_snap;
1508
1509         data = na_server_invoke_elem(host->srv, v->snap_query);
1510         if (na_results_status(data) != NA_OK)
1511         {
1512                 if (na_results_errno(data) == EVOLUMEOFFLINE) {
1513                         if ((v->flags & IS_VOLUME_USAGE_OFFLINE) == 0)
1514                                 cna_change_volume_status (host->name, v);
1515                 } else {
1516                         ERROR ("netapp plugin: cna_handle_volume_snap_usage: na_server_invoke_elem for "
1517                                         "volume \"%s\" on host %s failed with error %d: %s", v->name,
1518                                         host->name, na_results_errno(data), na_results_reason(data));
1519                 }
1520                 na_elem_free(data);
1521                 return;
1522         }
1523
1524         if ((v->flags & IS_VOLUME_USAGE_OFFLINE) != 0)
1525                 cna_change_volume_status (host->name, v);
1526
1527         elem_snapshots = na_elem_child (data, "snapshots");
1528         if (elem_snapshots == NULL)
1529         {
1530                 ERROR ("netapp plugin: cna_handle_volume_snap_usage: "
1531                                 "na_elem_child (\"snapshots\") failed "
1532                                 "for host %s.", host->name);
1533                 na_elem_free(data);
1534                 return;
1535         }
1536
1537         iter_snap = na_child_iterator (elem_snapshots);
1538         for (elem_snap = na_iterator_next (&iter_snap);
1539                         elem_snap != NULL;
1540                         elem_snap = na_iterator_next (&iter_snap))
1541         {
1542                 value = na_child_get_uint64(elem_snap, "cumulative-total", 0);
1543                 /* "cumulative-total" is the total size of the oldest snapshot plus all
1544                  * newer ones in blocks (1KB). We therefore are looking for the highest
1545                  * number of all snapshots - that's the size required for the snapshots. */
1546                 if (value > snap_used)
1547                         snap_used = value;
1548         }
1549         na_elem_free (data);
1550         /* snap_used is in 1024 byte blocks */
1551         v->snap_used = snap_used * 1024;
1552         v->flags |= HAVE_VOLUME_USAGE_SNAP_USED;
1553 } /* }}} void cna_handle_volume_snap_usage */
1554
1555 static int cna_handle_volume_usage_data (const host_config_t *host, /* {{{ */
1556                 cfg_volume_usage_t *cfg_volume, na_elem_t *data)
1557 {
1558         na_elem_t *elem_volume;
1559         na_elem_t *elem_volumes;
1560         na_elem_iter_t iter_volume;
1561
1562         elem_volumes = na_elem_child (data, "volumes");
1563         if (elem_volumes == NULL)
1564         {
1565                 ERROR ("netapp plugin: cna_handle_volume_usage_data: "
1566                                 "na_elem_child (\"volumes\") failed "
1567                                 "for host %s.", host->name);
1568                 return (-1);
1569         }
1570
1571         iter_volume = na_child_iterator (elem_volumes);
1572         for (elem_volume = na_iterator_next (&iter_volume);
1573                         elem_volume != NULL;
1574                         elem_volume = na_iterator_next (&iter_volume))
1575         {
1576                 const char *volume_name, *state;
1577
1578                 data_volume_usage_t *v;
1579                 uint64_t value;
1580
1581                 na_elem_t *sis;
1582                 const char *sis_state;
1583                 uint64_t sis_saved_reported;
1584
1585                 volume_name = na_child_get_string (elem_volume, "name");
1586                 if (volume_name == NULL)
1587                         continue;
1588
1589                 state = na_child_get_string (elem_volume, "state");
1590                 if ((state == NULL) || (strcmp(state, "online") != 0))
1591                         continue;
1592
1593                 /* get_volume_usage may return NULL if the volume is to be ignored. */
1594                 v = get_volume_usage (cfg_volume, volume_name);
1595                 if (v == NULL)
1596                         continue;
1597
1598                 if ((v->flags & CFG_VOLUME_USAGE_SNAP) != 0)
1599                         cna_handle_volume_snap_usage(host, v);
1600                 
1601                 if ((v->flags & CFG_VOLUME_USAGE_DF) == 0)
1602                         continue;
1603
1604                 /* 2^4 exa-bytes? This will take a while ;) */
1605                 value = na_child_get_uint64(elem_volume, "size-available", UINT64_MAX);
1606                 if (value != UINT64_MAX) {
1607                         v->norm_free = value;
1608                         v->flags |= HAVE_VOLUME_USAGE_NORM_FREE;
1609                 }
1610
1611                 value = na_child_get_uint64(elem_volume, "size-used", UINT64_MAX);
1612                 if (value != UINT64_MAX) {
1613                         v->norm_used = value;
1614                         v->flags |= HAVE_VOLUME_USAGE_NORM_USED;
1615                 }
1616
1617                 value = na_child_get_uint64(elem_volume, "snapshot-blocks-reserved", UINT64_MAX);
1618                 if (value != UINT64_MAX) {
1619                         /* 1 block == 1024 bytes  as per API docs */
1620                         v->snap_reserved = 1024 * value;
1621                         v->flags |= HAVE_VOLUME_USAGE_SNAP_RSVD;
1622                 }
1623
1624                 sis = na_elem_child(elem_volume, "sis");
1625                 if (sis == NULL)
1626                         continue;
1627
1628                 if (na_elem_child(sis, "sis-info"))
1629                         sis = na_elem_child(sis, "sis-info");
1630                 
1631                 sis_state = na_child_get_string(sis, "state");
1632                 if (sis_state == NULL)
1633                         continue;
1634
1635                 /* If SIS is not enabled, there's nothing left to do for this volume. */
1636                 if (strcmp ("enabled", sis_state) != 0)
1637                         continue;
1638
1639                 sis_saved_reported = na_child_get_uint64(sis, "size-saved", UINT64_MAX);
1640                 if (sis_saved_reported == UINT64_MAX)
1641                         continue;
1642
1643                 /* size-saved is actually a 32 bit number, so ... time for some guesswork. */
1644                 if ((sis_saved_reported >> 32) != 0) {
1645                         /* In case they ever fix this bug. */
1646                         v->sis_saved = sis_saved_reported;
1647                         v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
1648                 } else { /* really hacky work-around code. {{{ */
1649                         uint64_t sis_saved_percent;
1650                         uint64_t sis_saved_guess;
1651                         uint64_t overflow_guess;
1652                         uint64_t guess1, guess2, guess3;
1653
1654                         /* Check if we have v->norm_used. Without it, we cannot calculate
1655                          * sis_saved_guess. */
1656                         if ((v->flags & HAVE_VOLUME_USAGE_NORM_USED) == 0)
1657                                 continue;
1658
1659                         sis_saved_percent = na_child_get_uint64(sis, "percentage-saved", UINT64_MAX);
1660                         if (sis_saved_percent > 100)
1661                                 continue;
1662
1663                         /* The "size-saved" value is a 32bit unsigned integer. This is a bug and
1664                          * will hopefully be fixed in later versions. To work around the bug, try
1665                          * to figure out how often the 32bit integer wrapped around by using the
1666                          * "percentage-saved" value. Because the percentage is in the range
1667                          * [0-100], this should work as long as the saved space does not exceed
1668                          * 400 GBytes. */
1669                         /* percentage-saved = size-saved / (size-saved + size-used) */
1670                         if (sis_saved_percent < 100)
1671                                 sis_saved_guess = v->norm_used * sis_saved_percent / (100 - sis_saved_percent);
1672                         else
1673                                 sis_saved_guess = v->norm_used;
1674
1675                         overflow_guess = sis_saved_guess >> 32;
1676                         guess1 = overflow_guess ? ((overflow_guess - 1) << 32) + sis_saved_reported : sis_saved_reported;
1677                         guess2 = (overflow_guess << 32) + sis_saved_reported;
1678                         guess3 = ((overflow_guess + 1) << 32) + sis_saved_reported;
1679
1680                         if (sis_saved_guess < guess2) {
1681                                 if ((sis_saved_guess - guess1) < (guess2 - sis_saved_guess))
1682                                         v->sis_saved = guess1;
1683                                 else
1684                                         v->sis_saved = guess2;
1685                         } else {
1686                                 if ((sis_saved_guess - guess2) < (guess3 - sis_saved_guess))
1687                                         v->sis_saved = guess2;
1688                                 else
1689                                         v->sis_saved = guess3;
1690                         }
1691                         v->flags |= HAVE_VOLUME_USAGE_SIS_SAVED;
1692                 } /* }}} end of 32-bit workaround */
1693         } /* for (elem_volume) */
1694
1695         return (cna_submit_volume_usage_data (host->name, cfg_volume, host->interval));
1696 } /* }}} int cna_handle_volume_usage_data */
1697
1698 static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
1699 {
1700         if (cvu == NULL)
1701                 return (EINVAL);
1702
1703         if (cvu->query != NULL)
1704                 return (0);
1705
1706         cvu->query = na_elem_new ("volume-list-info");
1707         if (cvu->query == NULL)
1708         {
1709                 ERROR ("netapp plugin: na_elem_new failed.");
1710                 return (-1);
1711         }
1712
1713         return (0);
1714 } /* }}} int cna_setup_volume_usage */
1715
1716 static int cna_query_volume_usage (host_config_t *host) /* {{{ */
1717 {
1718         na_elem_t *data;
1719         int status;
1720         cdtime_t now;
1721
1722         if (host == NULL)
1723                 return (EINVAL);
1724
1725         /* If the user did not configure volume_usage statistics, return without
1726          * doing anything. */
1727         if (host->cfg_volume_usage == NULL)
1728                 return (0);
1729
1730         now = cdtime ();
1731         if ((host->cfg_volume_usage->interval.interval + host->cfg_volume_usage->interval.last_read) > now)
1732                 return (0);
1733
1734         status = cna_setup_volume_usage (host->cfg_volume_usage);
1735         if (status != 0)
1736                 return (status);
1737         assert (host->cfg_volume_usage->query != NULL);
1738
1739         data = na_server_invoke_elem(host->srv, host->cfg_volume_usage->query);
1740         if (na_results_status (data) != NA_OK)
1741         {
1742                 ERROR ("netapp plugin: cna_query_volume_usage: na_server_invoke_elem failed for host %s: %s",
1743                                 host->name, na_results_reason (data));
1744                 na_elem_free (data);
1745                 return (-1);
1746         }
1747
1748         status = cna_handle_volume_usage_data (host, host->cfg_volume_usage, data);
1749
1750         if (status == 0)
1751                 host->cfg_volume_usage->interval.last_read = now;
1752
1753         na_elem_free (data);
1754         return (status);
1755 } /* }}} int cna_query_volume_usage */
1756
1757 /* Data corresponding to <SnapVault /> */
1758 static int cna_handle_snapvault_data (const char *hostname, /* {{{ */
1759                 cfg_snapvault_t *cfg_snapvault, na_elem_t *data, cdtime_t interval)
1760 {
1761         na_elem_t *status;
1762         na_elem_iter_t status_iter;
1763
1764         status = na_elem_child (data, "status-list");
1765         if (! status) {
1766                 ERROR ("netapp plugin: SnapVault status record missing status-list");
1767                 return (0);
1768         }
1769
1770         status_iter = na_child_iterator (status);
1771         for (status = na_iterator_next (&status_iter);
1772                         status != NULL;
1773                         status = na_iterator_next (&status_iter))
1774         {
1775                 const char *dest_sys, *dest_path, *src_sys, *src_path;
1776                 char plugin_instance[DATA_MAX_NAME_LEN];
1777                 uint64_t value;
1778
1779                 dest_sys  = na_child_get_string (status, "destination-system");
1780                 dest_path = na_child_get_string (status, "destination-path");
1781                 src_sys   = na_child_get_string (status, "source-system");
1782                 src_path  = na_child_get_string (status, "source-path");
1783
1784                 if ((! dest_sys) || (! dest_path) || (! src_sys) || (! src_path))
1785                         continue;
1786
1787                 value = na_child_get_uint64 (status, "lag-time", UINT64_MAX);
1788                 if (value == UINT64_MAX) /* no successful baseline transfer yet */
1789                         continue;
1790
1791                 /* possible TODO: make plugin instance configurable */
1792                 ssnprintf (plugin_instance, sizeof (plugin_instance),
1793                                 "snapvault-%s", dest_path);
1794                 submit_double (hostname, plugin_instance, /* type = */ "delay", NULL,
1795                                 (double)value, /* timestamp = */ 0, interval);
1796
1797                 value = na_child_get_uint64 (status, "last-transfer-duration", UINT64_MAX);
1798                 if (value != UINT64_MAX)
1799                         submit_double (hostname, plugin_instance, /* type = */ "duration", "last_transfer",
1800                                         (double)value, /* timestamp = */ 0, interval);
1801
1802                 value = na_child_get_uint64 (status, "transfer-progress", UINT64_MAX);
1803                 if (value == UINT64_MAX)
1804                         value = na_child_get_uint64 (status, "last-transfer-size", UINT64_MAX);
1805                 if (value != UINT64_MAX) {
1806                         value *= 1024; /* this is kilobytes */
1807                         submit_derive (hostname, plugin_instance, /* type = */ "if_rx_octets", "transferred",
1808                                         value, /* timestamp = */ 0, interval);
1809                 }
1810         } /* for (status) */
1811
1812         return (0);
1813 } /* }}} int cna_handle_snapvault_data */
1814
1815 static int cna_handle_snapvault_iter (host_config_t *host, /* {{{ */
1816                 na_elem_t *data)
1817 {
1818         const char *tag;
1819
1820         uint32_t records_count;
1821         uint32_t i;
1822
1823         records_count = na_child_get_uint32 (data, "records", UINT32_MAX);
1824         if (records_count == UINT32_MAX)
1825                 return 0;
1826
1827         tag = na_child_get_string (data, "tag");
1828         if (! tag)
1829                 return 0;
1830
1831         DEBUG ("netapp plugin: Iterating %u SV records (tag = %s)", records_count, tag);
1832
1833         for (i = 0; i < records_count; ++i) {
1834                 na_elem_t *elem;
1835
1836                 elem = na_server_invoke (host->srv,
1837                                 "snapvault-secondary-relationship-status-list-iter-next",
1838                                 "maximum", "1", "tag", tag, NULL);
1839
1840                 if (na_results_status (elem) != NA_OK)
1841                 {
1842                         ERROR ("netapp plugin: cna_handle_snapvault_iter: "
1843                                         "na_server_invoke failed for host %s: %s",
1844                                         host->name, na_results_reason (data));
1845                         na_elem_free (elem);
1846                         return (-1);
1847                 }
1848
1849                 cna_handle_snapvault_data (host->name, host->cfg_snapvault, elem, host->interval);
1850                 na_elem_free (elem);
1851         }
1852
1853         na_elem_free (na_server_invoke (host->srv,
1854                         "snapvault-secondary-relationship-status-list-iter-end",
1855                         "tag", tag, NULL));
1856         return (0);
1857 } /* }}} int cna_handle_snapvault_iter */
1858
1859 static int cna_setup_snapvault (cfg_snapvault_t *sv) /* {{{ */
1860 {
1861         if (sv == NULL)
1862                 return (EINVAL);
1863
1864         if (sv->query != NULL)
1865                 return (0);
1866
1867         sv->query = na_elem_new ("snapvault-secondary-relationship-status-list-iter-start");
1868         if (sv->query == NULL)
1869         {
1870                 ERROR ("netapp plugin: na_elem_new failed.");
1871                 return (-1);
1872         }
1873
1874         return (0);
1875 } /* }}} int cna_setup_snapvault */
1876
1877 static int cna_query_snapvault (host_config_t *host) /* {{{ */
1878 {
1879         na_elem_t *data;
1880         int status;
1881         cdtime_t now;
1882
1883         if (host == NULL)
1884                 return EINVAL;
1885
1886         if (host->cfg_snapvault == NULL)
1887                 return 0;
1888
1889         now = cdtime ();
1890         if ((host->cfg_snapvault->interval.interval + host->cfg_snapvault->interval.last_read) > now)
1891                 return (0);
1892
1893         status = cna_setup_snapvault (host->cfg_snapvault);
1894         if (status != 0)
1895                 return (status);
1896         assert (host->cfg_snapvault->query != NULL);
1897
1898         data = na_server_invoke_elem (host->srv, host->cfg_snapvault->query);
1899         if (na_results_status (data) != NA_OK)
1900         {
1901                 ERROR ("netapp plugin: cna_query_snapvault: na_server_invoke_elem failed for host %s: %s",
1902                                 host->name, na_results_reason (data));
1903                 na_elem_free (data);
1904                 return (-1);
1905         }
1906
1907         status = cna_handle_snapvault_iter (host, data);
1908
1909         if (status == 0)
1910                 host->cfg_snapvault->interval.last_read = now;
1911
1912         na_elem_free (data);
1913         return (status);
1914 } /* }}} int cna_query_snapvault */
1915
1916 /* Data corresponding to <System /> */
1917 static int cna_handle_system_data (const char *hostname, /* {{{ */
1918                 cfg_system_t *cfg_system, na_elem_t *data, int interval)
1919 {
1920         na_elem_t *instances;
1921         na_elem_t *counter;
1922         na_elem_iter_t counter_iter;
1923
1924         derive_t disk_read = 0, disk_written = 0;
1925         derive_t net_recv = 0, net_sent = 0;
1926         derive_t cpu_busy = 0, cpu_total = 0;
1927         uint32_t counter_flags = 0;
1928
1929         const char *instance;
1930         cdtime_t timestamp;
1931         
1932         timestamp = cna_child_get_cdtime (data);
1933
1934         instances = na_elem_child(na_elem_child (data, "instances"), "instance-data");
1935         if (instances == NULL)
1936         {
1937                 ERROR ("netapp plugin: cna_handle_system_data: "
1938                                 "na_elem_child (\"instances\") failed "
1939                                 "for host %s.", hostname);
1940                 return (-1);
1941         }
1942
1943         instance = na_child_get_string (instances, "name");
1944         if (instance == NULL)
1945         {
1946                 ERROR ("netapp plugin: cna_handle_system_data: "
1947                                 "na_child_get_string (\"name\") failed "
1948                                 "for host %s.", hostname);
1949                 return (-1);
1950         }
1951
1952         counter_iter = na_child_iterator (na_elem_child (instances, "counters"));
1953         for (counter = na_iterator_next (&counter_iter);
1954                         counter != NULL;
1955                         counter = na_iterator_next (&counter_iter))
1956         {
1957                 const char *name;
1958                 uint64_t value;
1959
1960                 name = na_child_get_string(counter, "name");
1961                 if (name == NULL)
1962                         continue;
1963
1964                 value = na_child_get_uint64(counter, "value", UINT64_MAX);
1965                 if (value == UINT64_MAX)
1966                         continue;
1967
1968                 if (!strcmp(name, "disk_data_read")) {
1969                         disk_read = (derive_t) (value * 1024);
1970                         counter_flags |= 0x01;
1971                 } else if (!strcmp(name, "disk_data_written")) {
1972                         disk_written = (derive_t) (value * 1024);
1973                         counter_flags |= 0x02;
1974                 } else if (!strcmp(name, "net_data_recv")) {
1975                         net_recv = (derive_t) (value * 1024);
1976                         counter_flags |= 0x04;
1977                 } else if (!strcmp(name, "net_data_sent")) {
1978                         net_sent = (derive_t) (value * 1024);
1979                         counter_flags |= 0x08;
1980                 } else if (!strcmp(name, "cpu_busy")) {
1981                         cpu_busy = (derive_t) value;
1982                         counter_flags |= 0x10;
1983                 } else if (!strcmp(name, "cpu_elapsed_time")) {
1984                         cpu_total = (derive_t) value;
1985                         counter_flags |= 0x20;
1986                 } else if ((cfg_system->flags & CFG_SYSTEM_OPS)
1987                                 && (value > 0) && (strlen(name) > 4)
1988                                 && (!strcmp(name + strlen(name) - 4, "_ops"))) {
1989                         submit_derive (hostname, instance, "disk_ops_complex", name,
1990                                         (derive_t) value, timestamp, interval);
1991                 }
1992         } /* for (counter) */
1993
1994         if ((cfg_system->flags & CFG_SYSTEM_DISK)
1995                         && (HAS_ALL_FLAGS (counter_flags, 0x01 | 0x02)))
1996                 submit_two_derive (hostname, instance, "disk_octets", NULL,
1997                                 disk_read, disk_written, timestamp, interval);
1998                                 
1999         if ((cfg_system->flags & CFG_SYSTEM_NET)
2000                         && (HAS_ALL_FLAGS (counter_flags, 0x04 | 0x08)))
2001                 submit_two_derive (hostname, instance, "if_octets", NULL,
2002                                 net_recv, net_sent, timestamp, interval);
2003
2004         if ((cfg_system->flags & CFG_SYSTEM_CPU)
2005                         && (HAS_ALL_FLAGS (counter_flags, 0x10 | 0x20)))
2006         {
2007                 submit_derive (hostname, instance, "cpu", "system",
2008                                 cpu_busy, timestamp, interval);
2009                 submit_derive (hostname, instance, "cpu", "idle",
2010                                 cpu_total - cpu_busy, timestamp, interval);
2011         }
2012
2013         return (0);
2014 } /* }}} int cna_handle_system_data */
2015
2016 static int cna_setup_system (cfg_system_t *cs) /* {{{ */
2017 {
2018         if (cs == NULL)
2019                 return (EINVAL);
2020
2021         if (cs->query != NULL)
2022                 return (0);
2023
2024         cs->query = na_elem_new ("perf-object-get-instances");
2025         if (cs->query == NULL)
2026         {
2027                 ERROR ("netapp plugin: na_elem_new failed.");
2028                 return (-1);
2029         }
2030         na_child_add_string (cs->query, "objectname", "system");
2031
2032         return (0);
2033 } /* }}} int cna_setup_system */
2034
2035 static int cna_query_system (host_config_t *host) /* {{{ */
2036 {
2037         na_elem_t *data;
2038         int status;
2039         cdtime_t now;
2040
2041         if (host == NULL)
2042                 return (EINVAL);
2043
2044         /* If system statistics were not configured, return without doing anything. */
2045         if (host->cfg_system == NULL)
2046                 return (0);
2047
2048         now = cdtime ();
2049         if ((host->cfg_system->interval.interval + host->cfg_system->interval.last_read) > now)
2050                 return (0);
2051
2052         status = cna_setup_system (host->cfg_system);
2053         if (status != 0)
2054                 return (status);
2055         assert (host->cfg_system->query != NULL);
2056
2057         data = na_server_invoke_elem(host->srv, host->cfg_system->query);
2058         if (na_results_status (data) != NA_OK)
2059         {
2060                 ERROR ("netapp plugin: cna_query_system: na_server_invoke_elem failed for host %s: %s",
2061                                 host->name, na_results_reason (data));
2062                 na_elem_free (data);
2063                 return (-1);
2064         }
2065
2066         status = cna_handle_system_data (host->name, host->cfg_system, data, host->interval);
2067
2068         if (status == 0)
2069                 host->cfg_system->interval.last_read = now;
2070
2071         na_elem_free (data);
2072         return (status);
2073 } /* }}} int cna_query_system */
2074
2075 /*
2076  * Configuration handling
2077  */
2078 /* Sets a given flag if the boolean argument is true and unsets the flag if it
2079  * is false. On error, the flag-field is not changed. */
2080 static int cna_config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */
2081                 uint32_t *flags, uint32_t flag)
2082 {
2083         if ((ci == NULL) || (flags == NULL))
2084                 return (EINVAL);
2085
2086         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
2087         {
2088                 WARNING ("netapp plugin: The %s option needs exactly one boolean argument.",
2089                                 ci->key);
2090                 return (-1);
2091         }
2092
2093         if (ci->values[0].value.boolean)
2094                 *flags |= flag;
2095         else
2096                 *flags &= ~flag;
2097
2098         return (0);
2099 } /* }}} int cna_config_bool_to_flag */
2100
2101 /* Handling of the "Interval" option which is allowed in every block. */
2102 static int cna_config_get_interval (const oconfig_item_t *ci, /* {{{ */
2103                 cna_interval_t *out_interval)
2104 {
2105         cdtime_t tmp = 0;
2106         int status;
2107
2108         status = cf_util_get_cdtime (ci, &tmp);
2109         if (status != 0)
2110                 return (status);
2111
2112         out_interval->interval = tmp;
2113         out_interval->last_read = 0;
2114
2115         return (0);
2116 } /* }}} int cna_config_get_interval */
2117
2118 /* Handling of the "GetIO", "GetOps" and "GetLatency" options within a
2119  * <VolumePerf /> block. */
2120 static void cna_config_volume_perf_option (cfg_volume_perf_t *cvp, /* {{{ */
2121                 const oconfig_item_t *ci)
2122 {
2123         char *name;
2124         ignorelist_t * il;
2125
2126         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
2127         {
2128                 WARNING ("netapp plugin: The %s option requires exactly one string argument.",
2129                                 ci->key);
2130                 return;
2131         }
2132
2133         name = ci->values[0].value.string;
2134
2135         if (strcasecmp ("GetIO", ci->key) == 0)
2136                 il = cvp->il_octets;
2137         else if (strcasecmp ("GetOps", ci->key) == 0)
2138                 il = cvp->il_operations;
2139         else if (strcasecmp ("GetLatency", ci->key) == 0)
2140                 il = cvp->il_latency;
2141         else
2142                 return;
2143
2144         ignorelist_add (il, name);
2145 } /* }}} void cna_config_volume_perf_option */
2146
2147 /* Handling of the "IgnoreSelectedIO", "IgnoreSelectedOps" and
2148  * "IgnoreSelectedLatency" options within a <VolumePerf /> block. */
2149 static void cna_config_volume_perf_default (cfg_volume_perf_t *cvp, /* {{{ */
2150                 const oconfig_item_t *ci)
2151 {
2152         ignorelist_t *il;
2153
2154         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
2155         {
2156                 WARNING ("netapp plugin: The %s option requires exactly one string argument.",
2157                                 ci->key);
2158                 return;
2159         }
2160
2161         if (strcasecmp ("IgnoreSelectedIO", ci->key) == 0)
2162                 il = cvp->il_octets;
2163         else if (strcasecmp ("IgnoreSelectedOps", ci->key) == 0)
2164                 il = cvp->il_operations;
2165         else if (strcasecmp ("IgnoreSelectedLatency", ci->key) == 0)
2166                 il = cvp->il_latency;
2167         else
2168                 return;
2169
2170         if (ci->values[0].value.boolean)
2171                 ignorelist_set_invert (il, /* invert = */ 0);
2172         else
2173                 ignorelist_set_invert (il, /* invert = */ 1);
2174 } /* }}} void cna_config_volume_perf_default */
2175
2176 /* Corresponds to a <Disks /> block */
2177 /*
2178  * <VolumePerf>
2179  *   GetIO "vol0"
2180  *   GetIO "vol1"
2181  *   IgnoreSelectedIO false
2182  *
2183  *   GetOps "vol0"
2184  *   GetOps "vol2"
2185  *   IgnoreSelectedOps false
2186  *
2187  *   GetLatency "vol2"
2188  *   GetLatency "vol3"
2189  *   IgnoreSelectedLatency false
2190  * </VolumePerf>
2191  */
2192 /* Corresponds to a <VolumePerf /> block */
2193 static int cna_config_volume_performance (host_config_t *host, /* {{{ */
2194                 const oconfig_item_t *ci)
2195 {
2196         cfg_volume_perf_t *cfg_volume_perf;
2197         int i;
2198
2199         if ((host == NULL) || (ci == NULL))
2200                 return (EINVAL);
2201
2202         if (host->cfg_volume_perf == NULL)
2203         {
2204                 cfg_volume_perf = malloc (sizeof (*cfg_volume_perf));
2205                 if (cfg_volume_perf == NULL)
2206                         return (ENOMEM);
2207                 memset (cfg_volume_perf, 0, sizeof (*cfg_volume_perf));
2208
2209                 /* Set default flags */
2210                 cfg_volume_perf->query = NULL;
2211                 cfg_volume_perf->volumes = NULL;
2212
2213                 cfg_volume_perf->il_octets = ignorelist_create (/* invert = */ 1);
2214                 if (cfg_volume_perf->il_octets == NULL)
2215                 {
2216                         sfree (cfg_volume_perf);
2217                         return (ENOMEM);
2218                 }
2219
2220                 cfg_volume_perf->il_operations = ignorelist_create (/* invert = */ 1);
2221                 if (cfg_volume_perf->il_operations == NULL)
2222                 {
2223                         ignorelist_free (cfg_volume_perf->il_octets);
2224                         sfree (cfg_volume_perf);
2225                         return (ENOMEM);
2226                 }
2227
2228                 cfg_volume_perf->il_latency = ignorelist_create (/* invert = */ 1);
2229                 if (cfg_volume_perf->il_latency == NULL)
2230                 {
2231                         ignorelist_free (cfg_volume_perf->il_octets);
2232                         ignorelist_free (cfg_volume_perf->il_operations);
2233                         sfree (cfg_volume_perf);
2234                         return (ENOMEM);
2235                 }
2236
2237                 host->cfg_volume_perf = cfg_volume_perf;
2238         }
2239         cfg_volume_perf = host->cfg_volume_perf;
2240         
2241         for (i = 0; i < ci->children_num; ++i) {
2242                 oconfig_item_t *item = ci->children + i;
2243                 
2244                 /* if (!item || !item->key || !*item->key) continue; */
2245                 if (strcasecmp(item->key, "Interval") == 0)
2246                         cna_config_get_interval (item, &cfg_volume_perf->interval);
2247                 else if (!strcasecmp(item->key, "GetIO"))
2248                         cna_config_volume_perf_option (cfg_volume_perf, item);
2249                 else if (!strcasecmp(item->key, "GetOps"))
2250                         cna_config_volume_perf_option (cfg_volume_perf, item);
2251                 else if (!strcasecmp(item->key, "GetLatency"))
2252                         cna_config_volume_perf_option (cfg_volume_perf, item);
2253                 else if (!strcasecmp(item->key, "IgnoreSelectedIO"))
2254                         cna_config_volume_perf_default (cfg_volume_perf, item);
2255                 else if (!strcasecmp(item->key, "IgnoreSelectedOps"))
2256                         cna_config_volume_perf_default (cfg_volume_perf, item);
2257                 else if (!strcasecmp(item->key, "IgnoreSelectedLatency"))
2258                         cna_config_volume_perf_default (cfg_volume_perf, item);
2259                 else
2260                         WARNING ("netapp plugin: The option %s is not allowed within "
2261                                         "`VolumePerf' blocks.", item->key);
2262         }
2263
2264         return (0);
2265 } /* }}} int cna_config_volume_performance */
2266
2267 /* Handling of the "GetCapacity" and "GetSnapshot" options within a
2268  * <VolumeUsage /> block. */
2269 static void cna_config_volume_usage_option (cfg_volume_usage_t *cvu, /* {{{ */
2270                 const oconfig_item_t *ci)
2271 {
2272         char *name;
2273         ignorelist_t * il;
2274
2275         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
2276         {
2277                 WARNING ("netapp plugin: The %s option requires exactly one string argument.",
2278                                 ci->key);
2279                 return;
2280         }
2281
2282         name = ci->values[0].value.string;
2283
2284         if (strcasecmp ("GetCapacity", ci->key) == 0)
2285                 il = cvu->il_capacity;
2286         else if (strcasecmp ("GetSnapshot", ci->key) == 0)
2287                 il = cvu->il_snapshot;
2288         else
2289                 return;
2290
2291         ignorelist_add (il, name);
2292 } /* }}} void cna_config_volume_usage_option */
2293
2294 /* Handling of the "IgnoreSelectedCapacity" and "IgnoreSelectedSnapshot"
2295  * options within a <VolumeUsage /> block. */
2296 static void cna_config_volume_usage_default (cfg_volume_usage_t *cvu, /* {{{ */
2297                 const oconfig_item_t *ci)
2298 {
2299         ignorelist_t *il;
2300
2301         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
2302         {
2303                 WARNING ("netapp plugin: The %s option requires exactly one string argument.",
2304                                 ci->key);
2305                 return;
2306         }
2307
2308         if (strcasecmp ("IgnoreSelectedCapacity", ci->key) == 0)
2309                 il = cvu->il_capacity;
2310         else if (strcasecmp ("IgnoreSelectedSnapshot", ci->key) == 0)
2311                 il = cvu->il_snapshot;
2312         else
2313                 return;
2314
2315         if (ci->values[0].value.boolean)
2316                 ignorelist_set_invert (il, /* invert = */ 0);
2317         else
2318                 ignorelist_set_invert (il, /* invert = */ 1);
2319 } /* }}} void cna_config_volume_usage_default */
2320
2321 /* Corresponds to a <Disks /> block */
2322 static int cna_config_disk(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
2323         cfg_disk_t *cfg_disk;
2324         int i;
2325
2326         if ((host == NULL) || (ci == NULL))
2327                 return (EINVAL);
2328
2329         if (host->cfg_disk == NULL)
2330         {
2331                 cfg_disk = malloc (sizeof (*cfg_disk));
2332                 if (cfg_disk == NULL)
2333                         return (ENOMEM);
2334                 memset (cfg_disk, 0, sizeof (*cfg_disk));
2335
2336                 /* Set default flags */
2337                 cfg_disk->flags = CFG_DISK_ALL;
2338                 cfg_disk->query = NULL;
2339                 cfg_disk->disks = NULL;
2340
2341                 host->cfg_disk = cfg_disk;
2342         }
2343         cfg_disk = host->cfg_disk;
2344         
2345         for (i = 0; i < ci->children_num; ++i) {
2346                 oconfig_item_t *item = ci->children + i;
2347                 
2348                 /* if (!item || !item->key || !*item->key) continue; */
2349                 if (strcasecmp(item->key, "Interval") == 0)
2350                         cna_config_get_interval (item, &cfg_disk->interval);
2351                 else if (strcasecmp(item->key, "GetBusy") == 0)
2352                         cna_config_bool_to_flag (item, &cfg_disk->flags, CFG_DISK_BUSIEST);
2353         }
2354
2355         if ((cfg_disk->flags & CFG_DISK_ALL) == 0)
2356         {
2357                 NOTICE ("netapp plugin: All disk related values have been disabled. "
2358                                 "Collection of per-disk data will be disabled entirely.");
2359                 free_cfg_disk (host->cfg_disk);
2360                 host->cfg_disk = NULL;
2361         }
2362
2363         return (0);
2364 } /* }}} int cna_config_disk */
2365
2366 /* Corresponds to a <WAFL /> block */
2367 static int cna_config_wafl(host_config_t *host, oconfig_item_t *ci) /* {{{ */
2368 {
2369         cfg_wafl_t *cfg_wafl;
2370         int i;
2371
2372         if ((host == NULL) || (ci == NULL))
2373                 return (EINVAL);
2374
2375         if (host->cfg_wafl == NULL)
2376         {
2377                 cfg_wafl = malloc (sizeof (*cfg_wafl));
2378                 if (cfg_wafl == NULL)
2379                         return (ENOMEM);
2380                 memset (cfg_wafl, 0, sizeof (*cfg_wafl));
2381
2382                 /* Set default flags */
2383                 cfg_wafl->flags = CFG_WAFL_ALL;
2384
2385                 host->cfg_wafl = cfg_wafl;
2386         }
2387         cfg_wafl = host->cfg_wafl;
2388
2389         for (i = 0; i < ci->children_num; ++i) {
2390                 oconfig_item_t *item = ci->children + i;
2391                 
2392                 if (strcasecmp(item->key, "Interval") == 0)
2393                         cna_config_get_interval (item, &cfg_wafl->interval);
2394                 else if (!strcasecmp(item->key, "GetNameCache"))
2395                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_NAME_CACHE);
2396                 else if (!strcasecmp(item->key, "GetDirCache"))
2397                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_DIR_CACHE);
2398                 else if (!strcasecmp(item->key, "GetBufferCache"))
2399                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_BUF_CACHE);
2400                 else if (!strcasecmp(item->key, "GetInodeCache"))
2401                         cna_config_bool_to_flag (item, &cfg_wafl->flags, CFG_WAFL_INODE_CACHE);
2402                 else
2403                         WARNING ("netapp plugin: The %s config option is not allowed within "
2404                                         "`WAFL' blocks.", item->key);
2405         }
2406
2407         if ((cfg_wafl->flags & CFG_WAFL_ALL) == 0)
2408         {
2409                 NOTICE ("netapp plugin: All WAFL related values have been disabled. "
2410                                 "Collection of WAFL data will be disabled entirely.");
2411                 free_cfg_wafl (host->cfg_wafl);
2412                 host->cfg_wafl = NULL;
2413         }
2414
2415         return (0);
2416 } /* }}} int cna_config_wafl */
2417
2418 /*
2419  * <VolumeUsage>
2420  *   GetCapacity "vol0"
2421  *   GetCapacity "vol1"
2422  *   GetCapacity "vol2"
2423  *   GetCapacity "vol3"
2424  *   GetCapacity "vol4"
2425  *   IgnoreSelectedCapacity false
2426  *
2427  *   GetSnapshot "vol0"
2428  *   GetSnapshot "vol3"
2429  *   GetSnapshot "vol4"
2430  *   GetSnapshot "vol7"
2431  *   IgnoreSelectedSnapshot false
2432  * </VolumeUsage>
2433  */
2434 /* Corresponds to a <VolumeUsage /> block */
2435 static int cna_config_volume_usage(host_config_t *host, /* {{{ */
2436                 const oconfig_item_t *ci)
2437 {
2438         cfg_volume_usage_t *cfg_volume_usage;
2439         int i;
2440
2441         if ((host == NULL) || (ci == NULL))
2442                 return (EINVAL);
2443
2444         if (host->cfg_volume_usage == NULL)
2445         {
2446                 cfg_volume_usage = malloc (sizeof (*cfg_volume_usage));
2447                 if (cfg_volume_usage == NULL)
2448                         return (ENOMEM);
2449                 memset (cfg_volume_usage, 0, sizeof (*cfg_volume_usage));
2450
2451                 /* Set default flags */
2452                 cfg_volume_usage->query = NULL;
2453                 cfg_volume_usage->volumes = NULL;
2454
2455                 cfg_volume_usage->il_capacity = ignorelist_create (/* invert = */ 1);
2456                 if (cfg_volume_usage->il_capacity == NULL)
2457                 {
2458                         sfree (cfg_volume_usage);
2459                         return (ENOMEM);
2460                 }
2461
2462                 cfg_volume_usage->il_snapshot = ignorelist_create (/* invert = */ 1);
2463                 if (cfg_volume_usage->il_snapshot == NULL)
2464                 {
2465                         ignorelist_free (cfg_volume_usage->il_capacity);
2466                         sfree (cfg_volume_usage);
2467                         return (ENOMEM);
2468                 }
2469
2470                 host->cfg_volume_usage = cfg_volume_usage;
2471         }
2472         cfg_volume_usage = host->cfg_volume_usage;
2473         
2474         for (i = 0; i < ci->children_num; ++i) {
2475                 oconfig_item_t *item = ci->children + i;
2476                 
2477                 /* if (!item || !item->key || !*item->key) continue; */
2478                 if (strcasecmp(item->key, "Interval") == 0)
2479                         cna_config_get_interval (item, &cfg_volume_usage->interval);
2480                 else if (!strcasecmp(item->key, "GetCapacity"))
2481                         cna_config_volume_usage_option (cfg_volume_usage, item);
2482                 else if (!strcasecmp(item->key, "GetSnapshot"))
2483                         cna_config_volume_usage_option (cfg_volume_usage, item);
2484                 else if (!strcasecmp(item->key, "IgnoreSelectedCapacity"))
2485                         cna_config_volume_usage_default (cfg_volume_usage, item);
2486                 else if (!strcasecmp(item->key, "IgnoreSelectedSnapshot"))
2487                         cna_config_volume_usage_default (cfg_volume_usage, item);
2488                 else
2489                         WARNING ("netapp plugin: The option %s is not allowed within "
2490                                         "`VolumeUsage' blocks.", item->key);
2491         }
2492
2493         return (0);
2494 } /* }}} int cna_config_volume_usage */
2495
2496 /* Corresponds to a <SnapVault /> block */
2497 static int cna_config_snapvault (host_config_t *host, /* {{{ */
2498                 const oconfig_item_t *ci)
2499 {
2500         cfg_snapvault_t *cfg_snapvault;
2501         int i;
2502
2503         if ((host == NULL) || (ci == NULL))
2504                 return EINVAL;
2505
2506         if (host->cfg_snapvault == NULL)
2507         {
2508                 cfg_snapvault = malloc (sizeof (*cfg_snapvault));
2509                 if (cfg_snapvault == NULL)
2510                         return ENOMEM;
2511                 memset (cfg_snapvault, 0, sizeof (*cfg_snapvault));
2512                 cfg_snapvault->query = NULL;
2513
2514                 host->cfg_snapvault = cfg_snapvault;
2515         }
2516
2517         cfg_snapvault = host->cfg_snapvault;
2518
2519         for (i = 0; i < ci->children_num; ++i) {
2520                 oconfig_item_t *item = ci->children + i;
2521
2522                 if (strcasecmp (item->key, "Interval") == 0)
2523                         cna_config_get_interval (item, &cfg_snapvault->interval);
2524                 else
2525                         WARNING ("netapp plugin: The option %s is not allowed within "
2526                                         "`SnapVault' blocks.", item->key);
2527         }
2528
2529         return 0;
2530 } /* }}} int cna_config_snapvault */
2531
2532 /* Corresponds to a <System /> block */
2533 static int cna_config_system (host_config_t *host, /* {{{ */
2534                 oconfig_item_t *ci)
2535 {
2536         cfg_system_t *cfg_system;
2537         int i;
2538         
2539         if ((host == NULL) || (ci == NULL))
2540                 return (EINVAL);
2541
2542         if (host->cfg_system == NULL)
2543         {
2544                 cfg_system = malloc (sizeof (*cfg_system));
2545                 if (cfg_system == NULL)
2546                         return (ENOMEM);
2547                 memset (cfg_system, 0, sizeof (*cfg_system));
2548
2549                 /* Set default flags */
2550                 cfg_system->flags = CFG_SYSTEM_ALL;
2551                 cfg_system->query = NULL;
2552
2553                 host->cfg_system = cfg_system;
2554         }
2555         cfg_system = host->cfg_system;
2556
2557         for (i = 0; i < ci->children_num; ++i) {
2558                 oconfig_item_t *item = ci->children + i;
2559
2560                 if (strcasecmp(item->key, "Interval") == 0) {
2561                         cna_config_get_interval (item, &cfg_system->interval);
2562                 } else if (!strcasecmp(item->key, "GetCPULoad")) {
2563                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_CPU);
2564                 } else if (!strcasecmp(item->key, "GetInterfaces")) {
2565                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_NET);
2566                 } else if (!strcasecmp(item->key, "GetDiskOps")) {
2567                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_OPS);
2568                 } else if (!strcasecmp(item->key, "GetDiskIO")) {
2569                         cna_config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_DISK);
2570                 } else {
2571                         WARNING ("netapp plugin: The %s config option is not allowed within "
2572                                         "`System' blocks.", item->key);
2573                 }
2574         }
2575
2576         if ((cfg_system->flags & CFG_SYSTEM_ALL) == 0)
2577         {
2578                 NOTICE ("netapp plugin: All system related values have been disabled. "
2579                                 "Collection of system data will be disabled entirely.");
2580                 free_cfg_system (host->cfg_system);
2581                 host->cfg_system = NULL;
2582         }
2583
2584         return (0);
2585 } /* }}} int cna_config_system */
2586
2587 /* Corresponds to a <Host /> block. */
2588 static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
2589 {
2590         oconfig_item_t *item;
2591         host_config_t *host;
2592         int status;
2593         int i;
2594         
2595         if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
2596                 WARNING("netapp plugin: \"Host\" needs exactly one string argument. Ignoring host block.");
2597                 return 0;
2598         }
2599
2600         host = malloc(sizeof(*host));
2601         memset (host, 0, sizeof (*host));
2602         host->name = NULL;
2603         host->protocol = NA_SERVER_TRANSPORT_HTTPS;
2604         host->host = NULL;
2605         host->username = NULL;
2606         host->password = NULL;
2607         host->srv = NULL;
2608         host->cfg_wafl = NULL;
2609         host->cfg_disk = NULL;
2610         host->cfg_volume_perf = NULL;
2611         host->cfg_volume_usage = NULL;
2612         host->cfg_snapvault = NULL;
2613         host->cfg_system = NULL;
2614
2615         status = cf_util_get_string (ci, &host->name);
2616         if (status != 0)
2617         {
2618                 sfree (host);
2619                 return (NULL);
2620         }
2621
2622         for (i = 0; i < ci->children_num; ++i) {
2623                 item = ci->children + i;
2624
2625                 status = 0;
2626
2627                 if (!strcasecmp(item->key, "Address")) {
2628                         status = cf_util_get_string (item, &host->host);
2629                 } else if (!strcasecmp(item->key, "Port")) {
2630                         int tmp;
2631
2632                         tmp = cf_util_get_port_number (item);
2633                         if (tmp > 0)
2634                                 host->port = tmp;
2635                 } else if (!strcasecmp(item->key, "Protocol")) {
2636                         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"))) {
2637                                 WARNING("netapp plugin: \"Protocol\" needs to be either \"http\" or \"https\". Ignoring host block \"%s\".", ci->values[0].value.string);
2638                                 return 0;
2639                         }
2640                         if (!strcasecmp(item->values[0].value.string, "http")) host->protocol = NA_SERVER_TRANSPORT_HTTP;
2641                         else host->protocol = NA_SERVER_TRANSPORT_HTTPS;
2642                 } else if (!strcasecmp(item->key, "User")) {
2643                         status = cf_util_get_string (item, &host->username);
2644                 } else if (!strcasecmp(item->key, "Password")) {
2645                         status = cf_util_get_string (item, &host->password);
2646                 } else if (!strcasecmp(item->key, "Interval")) {
2647                         status = cf_util_get_cdtime (item, &host->interval);
2648                 } else if (!strcasecmp(item->key, "WAFL")) {
2649                         cna_config_wafl(host, item);
2650                 } else if (!strcasecmp(item->key, "Disks")) {
2651                         cna_config_disk(host, item);
2652                 } else if (!strcasecmp(item->key, "VolumePerf")) {
2653                         cna_config_volume_performance(host, item);
2654                 } else if (!strcasecmp(item->key, "VolumeUsage")) {
2655                         cna_config_volume_usage(host, item);
2656                 } else if (!strcasecmp(item->key, "SnapVault")) {
2657                         cna_config_snapvault(host, item);
2658                 } else if (!strcasecmp(item->key, "System")) {
2659                         cna_config_system(host, item);
2660                 } else {
2661                         WARNING("netapp plugin: Ignoring unknown config option \"%s\" in host block \"%s\".",
2662                                         item->key, ci->values[0].value.string);
2663                 }
2664
2665                 if (status != 0)
2666                         break;
2667         }
2668
2669         if (host->host == NULL)
2670                 host->host = strdup (host->name);
2671
2672         if (host->host == NULL)
2673                 status = -1;
2674
2675         if (host->port <= 0)
2676                 host->port = (host->protocol == NA_SERVER_TRANSPORT_HTTP) ? 80 : 443;
2677
2678         if ((host->username == NULL) || (host->password == NULL)) {
2679                 WARNING("netapp plugin: Please supply login information for host \"%s\". "
2680                                 "Ignoring host block.", host->name);
2681                 status = -1;
2682         }
2683
2684         if (status != 0)
2685         {
2686                 free_host_config (host);
2687                 return (NULL);
2688         }
2689
2690         return host;
2691 } /* }}} host_config_t *cna_config_host */
2692
2693 /*
2694  * Callbacks registered with the daemon
2695  *
2696  * Pretty standard stuff here.
2697  */
2698 static int cna_init_host (host_config_t *host) /* {{{ */
2699 {
2700         if (host == NULL)
2701                 return (EINVAL);
2702
2703         if (host->srv != NULL)
2704                 return (0);
2705
2706         /* Request version 1.1 of the ONTAP API */
2707         host->srv = na_server_open(host->host,
2708                         /* major version = */ 1, /* minor version = */ 1); 
2709         if (host->srv == NULL) {
2710                 ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
2711                 return (-1);
2712         }
2713
2714         na_server_set_transport_type(host->srv, host->protocol,
2715                         /* transportarg = */ NULL);
2716         na_server_set_port(host->srv, host->port);
2717         na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
2718         na_server_adminuser(host->srv, host->username, host->password);
2719         na_server_set_timeout(host->srv, 5 /* seconds */);
2720
2721         return (0);
2722 } /* }}} int cna_init_host */
2723
2724 static int cna_init (void) /* {{{ */
2725 {
2726         char err[256];
2727
2728         memset (err, 0, sizeof (err));
2729         if (!na_startup(err, sizeof(err))) {
2730                 err[sizeof (err) - 1] = 0;
2731                 ERROR("netapp plugin: Error initializing netapp API: %s", err);
2732                 return 1;
2733         }
2734
2735         return (0);
2736 } /* }}} cna_init */
2737
2738 static int cna_read_internal (host_config_t *host) { /* {{{ */
2739         int status;
2740
2741         status = cna_query_wafl (host);
2742         if (status != 0)
2743                 return (status);
2744
2745         status = cna_query_disk (host);
2746         if (status != 0)
2747                 return (status);
2748
2749         status = cna_query_volume_perf (host);
2750         if (status != 0)
2751                 return (status);
2752
2753         status = cna_query_volume_usage (host);
2754         if (status != 0)
2755                 return (status);
2756
2757         status = cna_query_snapvault (host);
2758         if (status != 0)
2759                 return (status);
2760
2761         status = cna_query_system (host);
2762         if (status != 0)
2763                 return (status);
2764
2765         return 0;
2766 } /* }}} int cna_read_internal */
2767
2768 static int cna_read (user_data_t *ud) { /* {{{ */
2769         host_config_t *host;
2770         int status;
2771
2772         if ((ud == NULL) || (ud->data == NULL))
2773                 return (-1);
2774
2775         host = ud->data;
2776
2777         status = cna_init_host (host);
2778         if (status != 0)
2779                 return (status);
2780
2781         status = cna_read_internal (host);
2782         if (status != 0)
2783         {
2784                 if (host->srv != NULL)
2785                         na_server_close (host->srv);
2786                 host->srv = NULL;
2787         }
2788
2789         return 0;
2790 } /* }}} int cna_read */
2791
2792 static int cna_config (oconfig_item_t *ci) { /* {{{ */
2793         int i;
2794         oconfig_item_t *item;
2795
2796         for (i = 0; i < ci->children_num; ++i) {
2797                 item = ci->children + i;
2798
2799                 if (strcasecmp(item->key, "Host") == 0)
2800                 {
2801                         host_config_t *host;
2802                         char cb_name[256];
2803                         struct timespec interval;
2804                         user_data_t ud;
2805
2806                         host = cna_config_host (item);
2807                         if (host == NULL)
2808                                 continue;
2809
2810                         ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name);
2811
2812                         CDTIME_T_TO_TIMESPEC (host->interval, &interval);
2813
2814                         memset (&ud, 0, sizeof (ud));
2815                         ud.data = host;
2816                         ud.free_func = (void (*) (void *)) free_host_config;
2817
2818                         plugin_register_complex_read (/* group = */ NULL, cb_name,
2819                                         /* callback  = */ cna_read, 
2820                                         /* interval  = */ (host->interval > 0) ? &interval : NULL,
2821                                         /* user data = */ &ud);
2822                         continue;
2823                 }
2824                 else /* if (item->key != "Host") */
2825                 {
2826                         WARNING("netapp plugin: Ignoring unknown config option \"%s\".", item->key);
2827                 }
2828         }
2829         return 0;
2830 } /* }}} int cna_config */
2831
2832 static int cna_shutdown (void) /* {{{ */
2833 {
2834         /* Clean up system resources and stuff. */
2835         na_shutdown ();
2836
2837         return (0);
2838 } /* }}} int cna_shutdown */
2839
2840 void module_register(void) {
2841         plugin_register_complex_config("netapp", cna_config);
2842         plugin_register_init("netapp", cna_init);
2843         plugin_register_shutdown("netapp", cna_shutdown);
2844 }
2845
2846 /* vim: set sw=2 ts=2 noet fdm=marker : */