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