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