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