d04be5ca8aadc9b6bc2401ec75211f9a85cb3dcd
1 /**
2 * collectd - src/netapp.c
3 * Copyright (C) 2009 Sven Trenkel
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Sven Trenkel <sven.trenkel at noris.net>
25 **/
27 #include "collectd.h"
28 #include "common.h"
30 #include <netapp_api.h>
32 #define HAS_ALL_FLAGS(has,needs) (((has) & (needs)) == (needs))
34 typedef struct host_config_s host_config_t;
35 typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *data);
37 /*!
38 * \brief Persistent data for system performance counters
39 */
40 #define CFG_SYSTEM_CPU 0x01
41 #define CFG_SYSTEM_NET 0x02
42 #define CFG_SYSTEM_OPS 0x04
43 #define CFG_SYSTEM_DISK 0x08
44 #define CFG_SYSTEM_ALL 0x0F
45 typedef struct {
46 uint32_t flags;
47 } cfg_system_t;
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 "data_wafl_t" struct therefore contains old counter values
54 * along with flags, which are set if the counter is valid.
55 *
56 * The function "query_wafl_data" will fill a new structure of this kind with
57 * new values, then pass both, new and old data, to "submit_wafl_data". That
58 * 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 time_t timestamp;
82 uint64_t name_cache_hit;
83 uint64_t name_cache_miss;
84 uint64_t find_dir_hit;
85 uint64_t find_dir_miss;
86 uint64_t buf_hash_hit;
87 uint64_t buf_hash_miss;
88 uint64_t inode_cache_hit;
89 uint64_t inode_cache_miss;
90 } data_wafl_t;
92 /*!
93 * \brief Persistent data for volume performance data.
94 *
95 * The code below uses the difference of the operations and latency counters to
96 * calculate an average per-operation latency. For this, old counters need to
97 * be stored in the "data_volume_perf_t" structure. The byte-counters are just
98 * kept for completeness sake. The "flags" member indicates if each counter is
99 * valid or not.
100 *
101 * The "query_volume_perf_data" function will fill a new struct of this type
102 * and pass both, old and new data, to "submit_volume_perf_data". In that
103 * function, the per-operation latency is calculated and dispatched, then the
104 * old counters are updated.
105 */
106 #define CFG_VOLUME_PERF_INIT 0x0001
107 #define CFG_VOLUME_PERF_IO 0x0002
108 #define CFG_VOLUME_PERF_OPS 0x0003
109 #define CFG_VOLUME_PERF_LATENCY 0x0008
110 #define CFG_VOLUME_PERF_ALL 0x000F
111 #define HAVE_VOLUME_PERF_BYTES_READ 0x0010
112 #define HAVE_VOLUME_PERF_BYTES_WRITE 0x0020
113 #define HAVE_VOLUME_PERF_OPS_READ 0x0040
114 #define HAVE_VOLUME_PERF_OPS_WRITE 0x0080
115 #define HAVE_VOLUME_PERF_LATENCY_READ 0x0100
116 #define HAVE_VOLUME_PERF_LATENCY_WRITE 0x0200
117 #define HAVE_VOLUME_PERF_ALL 0x03F0
118 typedef struct {
119 uint32_t flags;
120 } cfg_volume_perf_t;
122 typedef struct {
123 uint32_t flags;
124 time_t timestamp;
125 uint64_t read_bytes;
126 uint64_t write_bytes;
127 uint64_t read_ops;
128 uint64_t write_ops;
129 uint64_t read_latency;
130 uint64_t write_latency;
131 } data_volume_perf_t;
133 /*!
134 * \brief Configuration struct for volume usage data (free / used).
135 */
136 #define VOLUME_INIT 0x01
137 #define VOLUME_DF 0x02
138 #define VOLUME_SNAP 0x04
139 typedef struct {
140 uint32_t flags;
141 } cfg_volume_usage_t;
143 typedef struct service_config_s {
144 na_elem_t *query;
145 service_handler_t *handler;
146 int multiplier;
147 int skip_countdown;
148 int interval;
149 void *data;
150 struct service_config_s *next;
151 } cfg_service_t;
152 #define SERVICE_INIT {0, 0, 1, 1, 0, 0, 0}
154 /*!
155 * \brief Struct representing a volume.
156 *
157 * A volume currently has a name and two sets of values:
158 *
159 * - Performance data, such as bytes read/written, number of operations
160 * performed and average time per operation.
161 *
162 * - Usage data, i. e. amount of used and free space in the volume.
163 */
164 typedef struct volume_s {
165 char *name;
166 data_volume_perf_t perf_data;
167 cfg_volume_usage_t cfg_volume_usage;
168 struct volume_s *next;
169 } volume_t;
171 #define CFG_DISK_BUSIEST 0x01
172 #define CFG_DISK_ALL 0x01
173 #define HAVE_DISK_BUSY 0x10
174 #define HAVE_DISK_BASE 0x20
175 #define HAVE_DISK_ALL 0x30
176 typedef struct {
177 uint32_t flags;
178 } cfg_disk_t;
180 /*!
181 * \brief A disk in the NetApp.
182 *
183 * A disk doesn't have any more information than its name at the moment.
184 * The name includes the "disk_" prefix.
185 */
186 typedef struct disk_s {
187 char *name;
188 uint32_t flags;
189 time_t timestamp;
190 uint64_t disk_busy;
191 uint64_t base_for_disk_busy;
192 double disk_busy_percent;
193 struct disk_s *next;
194 } disk_t;
196 struct host_config_s {
197 na_server_t *srv;
198 char *name;
199 na_server_transport_t protocol;
200 char *host;
201 int port;
202 char *username;
203 char *password;
204 int interval;
205 cfg_service_t *services;
206 disk_t *disks;
207 volume_t *volumes;
208 struct host_config_s *next;
209 };
210 #define HOST_INIT {NULL, NULL, NA_SERVER_TRANSPORT_HTTPS, NULL, 0, NULL, NULL, 10, NULL, NULL, NULL, NULL}
212 static host_config_t *host_config;
214 static volume_t *get_volume (host_config_t *host, const char *name, /* {{{ */
215 uint32_t vol_usage_flags, uint32_t vol_perf_flags)
216 {
217 volume_t *v;
219 if (name == NULL)
220 return (NULL);
222 /* Make sure the default flags include the init-bit. */
223 if (vol_usage_flags != 0)
224 vol_usage_flags |= VOLUME_INIT;
225 if (vol_perf_flags != 0)
226 vol_perf_flags |= CFG_VOLUME_PERF_INIT;
228 for (v = host->volumes; v; v = v->next) {
229 if (strcmp(v->name, name) != 0)
230 continue;
232 /* Check if the flags have been initialized. */
233 if (((v->cfg_volume_usage.flags & VOLUME_INIT) == 0)
234 && (vol_usage_flags != 0))
235 v->cfg_volume_usage.flags = vol_usage_flags;
236 if (((v->perf_data.flags & CFG_VOLUME_PERF_INIT) == 0)
237 && (vol_perf_flags != 0))
238 v->perf_data.flags = vol_perf_flags;
240 return v;
241 }
243 DEBUG ("netapp plugin: Allocating new entry for volume %s.", name);
244 v = malloc(sizeof(*v));
245 if (v == NULL)
246 return (NULL);
247 memset (v, 0, sizeof (*v));
249 v->cfg_volume_usage.flags = vol_usage_flags;
250 v->perf_data.flags = vol_perf_flags;
252 v->name = strdup(name);
253 if (v->name == NULL) {
254 sfree (v);
255 return (NULL);
256 }
258 v->next = host->volumes;
259 host->volumes = v;
261 return v;
262 } /* }}} volume_t *get_volume */
264 static disk_t *get_disk(host_config_t *host, const char *name) /* {{{ */
265 {
266 disk_t *v;
268 if (name == NULL)
269 return (NULL);
271 for (v = host->disks; v; v = v->next) {
272 if (strcmp(v->name, name) == 0)
273 return v;
274 }
275 v = malloc(sizeof(*v));
276 if (v == NULL)
277 return (NULL);
278 memset (v, 0, sizeof (*v));
279 v->next = NULL;
281 v->name = strdup(name);
282 if (v->name == NULL) {
283 sfree (v);
284 return (NULL);
285 }
287 v->next = host->disks;
288 host->disks = v;
290 return v;
291 } /* }}} disk_t *get_disk */
293 static int submit_values (const char *host, /* {{{ */
294 const char *plugin_inst,
295 const char *type, const char *type_inst,
296 value_t *values, int values_len,
297 time_t timestamp)
298 {
299 value_list_t vl = VALUE_LIST_INIT;
301 vl.values = values;
302 vl.values_len = values_len;
304 if (timestamp > 0)
305 vl.time = timestamp;
307 if (host != NULL)
308 sstrncpy (vl.host, host, sizeof (vl.host));
309 else
310 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
311 sstrncpy (vl.plugin, "netapp", sizeof (vl.plugin));
312 if (plugin_inst != NULL)
313 sstrncpy (vl.plugin_instance, plugin_inst, sizeof (vl.plugin_instance));
314 sstrncpy (vl.type, type, sizeof (vl.type));
315 if (type_inst != NULL)
316 sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
318 return (plugin_dispatch_values (&vl));
319 } /* }}} int submit_uint64 */
321 static int submit_two_counters (const char *host, const char *plugin_inst, /* {{{ */
322 const char *type, const char *type_inst, counter_t val0, counter_t val1,
323 time_t timestamp)
324 {
325 value_t values[2];
327 values[0].counter = val0;
328 values[1].counter = val1;
330 return (submit_values (host, plugin_inst, type, type_inst,
331 values, 2, timestamp));
332 } /* }}} int submit_two_counters */
334 static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */
335 const char *type, const char *type_inst, counter_t counter, time_t timestamp)
336 {
337 value_t v;
339 v.counter = counter;
341 return (submit_values (host, plugin_inst, type, type_inst,
342 &v, 1, timestamp));
343 } /* }}} int submit_counter */
345 static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */
346 const char *type, const char *type_inst, gauge_t val0, gauge_t val1,
347 time_t timestamp)
348 {
349 value_t values[2];
351 values[0].gauge = val0;
352 values[1].gauge = val1;
354 return (submit_values (host, plugin_inst, type, type_inst,
355 values, 2, timestamp));
356 } /* }}} int submit_two_gauge */
358 static int submit_double (const char *host, const char *plugin_inst, /* {{{ */
359 const char *type, const char *type_inst, double d, time_t timestamp)
360 {
361 value_t v;
363 v.gauge = (gauge_t) d;
365 return (submit_values (host, plugin_inst, type, type_inst,
366 &v, 1, timestamp));
367 } /* }}} int submit_uint64 */
369 static int submit_cache_ratio (const char *host, /* {{{ */
370 const char *plugin_inst,
371 const char *type_inst,
372 uint64_t new_hits,
373 uint64_t new_misses,
374 uint64_t old_hits,
375 uint64_t old_misses,
376 time_t timestamp)
377 {
378 value_t v;
380 if ((new_hits >= old_hits) && (new_misses >= old_misses)) {
381 uint64_t hits;
382 uint64_t misses;
384 hits = new_hits - old_hits;
385 misses = new_misses - old_misses;
387 v.gauge = 100.0 * ((gauge_t) hits) / ((gauge_t) (hits + misses));
388 } else {
389 v.gauge = NAN;
390 }
392 return (submit_values (host, plugin_inst, "cache_ratio", type_inst,
393 &v, 1, timestamp));
394 } /* }}} int submit_cache_ratio */
396 static int submit_wafl_data (const host_config_t *host, const char *instance, /* {{{ */
397 data_wafl_t *old_data, const data_wafl_t *new_data)
398 {
399 /* Submit requested counters */
400 if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
401 && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_NAME_CACHE))
402 submit_cache_ratio (host->name, instance, "name_cache_hit",
403 new_data->name_cache_hit, new_data->name_cache_miss,
404 old_data->name_cache_hit, old_data->name_cache_miss,
405 new_data->timestamp);
407 if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR)
408 && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR))
409 submit_cache_ratio (host->name, instance, "find_dir_hit",
410 new_data->find_dir_hit, new_data->find_dir_miss,
411 old_data->find_dir_hit, old_data->find_dir_miss,
412 new_data->timestamp);
414 if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH)
415 && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH))
416 submit_cache_ratio (host->name, instance, "buf_hash_hit",
417 new_data->buf_hash_hit, new_data->buf_hash_miss,
418 old_data->buf_hash_hit, old_data->buf_hash_miss,
419 new_data->timestamp);
421 if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE)
422 && HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE))
423 submit_cache_ratio (host->name, instance, "inode_cache_hit",
424 new_data->inode_cache_hit, new_data->inode_cache_miss,
425 old_data->inode_cache_hit, old_data->inode_cache_miss,
426 new_data->timestamp);
428 /* Clear old HAVE_* flags */
429 old_data->flags &= ~HAVE_WAFL_ALL;
431 /* Copy all counters */
432 old_data->timestamp = new_data->timestamp;
433 old_data->name_cache_hit = new_data->name_cache_hit;
434 old_data->name_cache_miss = new_data->name_cache_miss;
435 old_data->find_dir_hit = new_data->find_dir_hit;
436 old_data->find_dir_miss = new_data->find_dir_miss;
437 old_data->buf_hash_hit = new_data->buf_hash_hit;
438 old_data->buf_hash_miss = new_data->buf_hash_miss;
439 old_data->inode_cache_hit = new_data->inode_cache_hit;
440 old_data->inode_cache_miss = new_data->inode_cache_miss;
442 /* Copy HAVE_* flags */
443 old_data->flags |= (new_data->flags & HAVE_WAFL_ALL);
445 return (0);
446 } /* }}} int submit_wafl_data */
448 static int submit_volume_perf_data (const host_config_t *host, /* {{{ */
449 volume_t *volume,
450 const data_volume_perf_t *new_data)
451 {
452 /* Check for and submit disk-octet values */
453 if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_IO)
454 && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE))
455 {
456 submit_two_counters (host->name, volume->name, "disk_octets", /* type instance = */ NULL,
457 (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp);
458 }
460 /* Check for and submit disk-operations values */
461 if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_OPS)
462 && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE))
463 {
464 submit_two_counters (host->name, volume->name, "disk_ops", /* type instance = */ NULL,
465 (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp);
466 }
468 /* Check for, calculate and submit disk-latency values */
469 if (HAS_ALL_FLAGS (volume->perf_data.flags, CFG_VOLUME_PERF_LATENCY
470 | HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
471 | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE)
472 && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE
473 | HAVE_VOLUME_PERF_LATENCY_READ | HAVE_VOLUME_PERF_LATENCY_WRITE))
474 {
475 gauge_t latency_per_op_read;
476 gauge_t latency_per_op_write;
478 latency_per_op_read = NAN;
479 latency_per_op_write = NAN;
481 /* Check if a counter wrapped around. */
482 if ((new_data->read_ops > volume->perf_data.read_ops)
483 && (new_data->read_latency > volume->perf_data.read_latency))
484 {
485 uint64_t diff_ops_read;
486 uint64_t diff_latency_read;
488 diff_ops_read = new_data->read_ops - volume->perf_data.read_ops;
489 diff_latency_read = new_data->read_latency - volume->perf_data.read_latency;
491 if (diff_ops_read > 0)
492 latency_per_op_read = ((gauge_t) diff_latency_read) / ((gauge_t) diff_ops_read);
493 }
495 if ((new_data->write_ops > volume->perf_data.write_ops)
496 && (new_data->write_latency > volume->perf_data.write_latency))
497 {
498 uint64_t diff_ops_write;
499 uint64_t diff_latency_write;
501 diff_ops_write = new_data->write_ops - volume->perf_data.write_ops;
502 diff_latency_write = new_data->write_latency - volume->perf_data.write_latency;
504 if (diff_ops_write > 0)
505 latency_per_op_write = ((gauge_t) diff_latency_write) / ((gauge_t) diff_ops_write);
506 }
508 submit_two_gauge (host->name, volume->name, "disk_latency", /* type instance = */ NULL,
509 latency_per_op_read, latency_per_op_write, new_data->timestamp);
510 }
512 /* Clear all HAVE_* flags. */
513 volume->perf_data.flags &= ~HAVE_VOLUME_PERF_ALL;
515 /* Copy all counters */
516 volume->perf_data.timestamp = new_data->timestamp;
517 volume->perf_data.read_bytes = new_data->read_bytes;
518 volume->perf_data.write_bytes = new_data->write_bytes;
519 volume->perf_data.read_ops = new_data->read_ops;
520 volume->perf_data.write_ops = new_data->write_ops;
521 volume->perf_data.read_latency = new_data->read_latency;
522 volume->perf_data.write_latency = new_data->write_latency;
524 /* Copy the HAVE_* flags */
525 volume->perf_data.flags |= (new_data->flags & HAVE_VOLUME_PERF_ALL);
527 return (0);
528 } /* }}} int submit_volume_perf_data */
530 static void query_wafl_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
531 data_wafl_t *wafl = data;
532 data_wafl_t perf_data;
533 const char *plugin_inst;
534 na_elem_t *counter;
536 memset (&perf_data, 0, sizeof (perf_data));
538 perf_data.timestamp = (time_t) na_child_get_uint64(out, "timestamp", 0);
540 out = na_elem_child(na_elem_child(out, "instances"), "instance-data");
541 if (out == NULL)
542 return;
544 plugin_inst = na_child_get_string(out, "name");
545 if (plugin_inst == NULL)
546 return;
548 /* Iterate over all counters */
549 na_elem_iter_t iter = na_child_iterator(na_elem_child(out, "counters"));
550 for (counter = na_iterator_next(&iter); counter; counter = na_iterator_next(&iter)) {
551 const char *name;
552 uint64_t value;
554 name = na_child_get_string(counter, "name");
555 if (name == NULL)
556 continue;
558 value = na_child_get_uint64(counter, "value", UINT64_MAX);
559 if (value == UINT64_MAX)
560 continue;
562 if (!strcmp(name, "name_cache_hit")) {
563 perf_data.name_cache_hit = value;
564 perf_data.flags |= HAVE_WAFL_NAME_CACHE_HIT;
565 } else if (!strcmp(name, "name_cache_miss")) {
566 perf_data.name_cache_miss = value;
567 perf_data.flags |= HAVE_WAFL_NAME_CACHE_MISS;
568 } else if (!strcmp(name, "find_dir_hit")) {
569 perf_data.find_dir_hit = value;
570 perf_data.flags |= HAVE_WAFL_FIND_DIR_HIT;
571 } else if (!strcmp(name, "find_dir_miss")) {
572 perf_data.find_dir_miss = value;
573 perf_data.flags |= HAVE_WAFL_FIND_DIR_MISS;
574 } else if (!strcmp(name, "buf_hash_hit")) {
575 perf_data.buf_hash_hit = value;
576 perf_data.flags |= HAVE_WAFL_BUF_HASH_HIT;
577 } else if (!strcmp(name, "buf_hash_miss")) {
578 perf_data.buf_hash_miss = value;
579 perf_data.flags |= HAVE_WAFL_BUF_HASH_MISS;
580 } else if (!strcmp(name, "inode_cache_hit")) {
581 perf_data.inode_cache_hit = value;
582 perf_data.flags |= HAVE_WAFL_INODE_CACHE_HIT;
583 } else if (!strcmp(name, "inode_cache_miss")) {
584 perf_data.inode_cache_miss = value;
585 perf_data.flags |= HAVE_WAFL_INODE_CACHE_MISS;
586 } else {
587 DEBUG("netapp plugin: query_wafl_data: Found unexpected child: %s",
588 name);
589 }
590 }
592 submit_wafl_data (host, plugin_inst, wafl, &perf_data);
593 } /* }}} void query_wafl_data */
595 static void query_submit_disk_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
596 cfg_disk_t *cfg_disk = data;
597 time_t timestamp;
598 na_elem_t *counter, *inst;
599 disk_t *worst_disk = 0;
601 timestamp = (time_t) na_child_get_uint64(out, "timestamp", 0);
602 out = na_elem_child(out, "instances");
604 /* Iterate over all children */
605 na_elem_iter_t inst_iter = na_child_iterator(out);
606 for (inst = na_iterator_next(&inst_iter); inst; inst = na_iterator_next(&inst_iter)) {
607 disk_t *old_data;
608 disk_t new_data;
610 memset (&new_data, 0, sizeof (new_data));
611 new_data.timestamp = timestamp;
612 new_data.disk_busy_percent = NAN;
614 old_data = get_disk(host, na_child_get_string(inst, "name"));
615 if (old_data == NULL)
616 continue;
618 /* Look for the "disk_busy" and "base_for_disk_busy" counters */
619 na_elem_iter_t count_iter = na_child_iterator(na_elem_child(inst, "counters"));
620 for (counter = na_iterator_next(&count_iter); counter; counter = na_iterator_next(&count_iter)) {
621 const char *name;
622 uint64_t value;
624 name = na_child_get_string(counter, "name");
625 if (name == NULL)
626 continue;
628 value = na_child_get_uint64(counter, "value", UINT64_MAX);
629 if (value == UINT64_MAX)
630 continue;
632 if (strcmp(name, "disk_busy") == 0)
633 {
634 new_data.disk_busy = value;
635 new_data.flags |= HAVE_DISK_BUSY;
636 }
637 else if (strcmp(name, "base_for_disk_busy") == 0)
638 {
639 new_data.base_for_disk_busy = value;
640 new_data.flags |= HAVE_DISK_BASE;
641 }
642 }
644 /* If all required counters are available and did not just wrap around,
645 * calculate the busy percentage. Otherwise, the value is initialized to
646 * NAN at the top of the for-loop. */
647 if (HAS_ALL_FLAGS (old_data->flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
648 && HAS_ALL_FLAGS (new_data.flags, HAVE_DISK_BUSY | HAVE_DISK_BASE)
649 && (new_data.disk_busy >= old_data->disk_busy)
650 && (new_data.base_for_disk_busy > old_data->base_for_disk_busy))
651 {
652 uint64_t busy_diff;
653 uint64_t base_diff;
655 busy_diff = new_data.disk_busy - old_data->disk_busy;
656 base_diff = new_data.base_for_disk_busy - old_data->base_for_disk_busy;
658 new_data.disk_busy_percent = 100.0
659 * ((gauge_t) busy_diff) / ((gauge_t) base_diff);
660 }
662 /* Clear HAVE_* flags */
663 old_data->flags &= ~HAVE_DISK_ALL;
665 /* Copy data */
666 old_data->timestamp = new_data.timestamp;
667 old_data->disk_busy = new_data.disk_busy;
668 old_data->base_for_disk_busy = new_data.base_for_disk_busy;
669 old_data->disk_busy_percent = new_data.disk_busy_percent;
671 /* Copy flags */
672 old_data->flags |= (new_data.flags & HAVE_DISK_ALL);
674 if ((worst_disk == NULL)
675 || (worst_disk->disk_busy_percent < old_data->disk_busy_percent))
676 worst_disk = old_data;
677 } /* for (all disks) */
679 if ((cfg_disk->flags & CFG_DISK_BUSIEST) && (worst_disk != NULL))
680 submit_double (host->name, "system", "percent", "disk_busy",
681 worst_disk->disk_busy_percent, timestamp);
682 } /* }}} void query_submit_disk_data */
684 static void collect_volume_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
685 na_elem_t *inst;
686 volume_t *volume;
687 cfg_volume_usage_t *cfg_volume_data = data;
689 out = na_elem_child(out, "volumes");
690 na_elem_iter_t inst_iter = na_child_iterator(out);
691 for (inst = na_iterator_next(&inst_iter); inst; inst = na_iterator_next(&inst_iter)) {
692 uint64_t size_free = 0, size_used = 0, snap_reserved = 0;
694 na_elem_t *sis;
695 const char *sis_state;
696 uint64_t sis_saved_reported;
697 uint64_t sis_saved;
699 volume = get_volume(host, na_child_get_string(inst, "name"),
700 cfg_volume_data->flags, /* perf_flags = */ 0);
701 if (volume == NULL)
702 continue;
704 if (!(volume->cfg_volume_usage.flags & VOLUME_DF))
705 continue;
707 /* 2^4 exa-bytes? This will take a while ;) */
708 size_free = na_child_get_uint64(inst, "size-available", UINT64_MAX);
709 if (size_free != UINT64_MAX)
710 submit_double (host->name, volume->name, "df_complex", "used",
711 (double) size_used, /* time = */ 0);
713 size_used = na_child_get_uint64(inst, "size-used", UINT64_MAX);
714 if (size_free != UINT64_MAX)
715 submit_double (host->name, volume->name, "df_complex", "free",
716 (double) size_free, /* time = */ 0);
718 snap_reserved = na_child_get_uint64(inst, "snapshot-blocks-reserved", UINT64_MAX);
719 if (snap_reserved != UINT64_MAX)
720 /* 1 block == 1024 bytes as per API docs */
721 submit_double (host->name, volume->name, "df_complex", "snap_reserved",
722 (double) (1024 * snap_reserved), /* time = */ 0);
724 sis = na_elem_child(inst, "sis");
725 if (sis == NULL)
726 continue;
728 sis_state = na_child_get_string(sis, "state");
729 if ((sis_state == NULL)
730 || (strcmp ("enabled", sis_state) != 0))
731 continue;
733 sis_saved_reported = na_child_get_uint64(sis, "size-saved", UINT64_MAX);
734 if (sis_saved_reported == UINT64_MAX)
735 continue;
737 /* size-saved is actually a 32 bit number, so ... time for some guesswork. */
738 if ((sis_saved_reported >> 32) != 0) {
739 /* In case they ever fix this bug. */
740 sis_saved = sis_saved_reported;
741 } else {
742 uint64_t sis_saved_percent;
743 uint64_t sis_saved_guess;
744 uint64_t overflow_guess;
745 uint64_t guess1, guess2, guess3;
747 sis_saved_percent = na_child_get_uint64(sis, "percentage-saved", UINT64_MAX);
748 if (sis_saved_percent > 100)
749 continue;
751 /* The "size-saved" value is a 32bit unsigned integer. This is a bug and
752 * will hopefully be fixed in later versions. To work around the bug, try
753 * to figure out how often the 32bit integer wrapped around by using the
754 * "percentage-saved" value. Because the percentage is in the range
755 * [0-100], this should work as long as the saved space does not exceed
756 * 400 GBytes. */
757 /* percentage-saved = size-saved / (size-saved + size-used) */
758 if (sis_saved_percent < 100)
759 sis_saved_guess = size_used * sis_saved_percent / (100 - sis_saved_percent);
760 else
761 sis_saved_guess = size_used;
763 overflow_guess = sis_saved_guess >> 32;
764 guess1 = overflow_guess ? ((overflow_guess - 1) << 32) + sis_saved_reported : sis_saved_reported;
765 guess2 = (overflow_guess << 32) + sis_saved_reported;
766 guess3 = ((overflow_guess + 1) << 32) + sis_saved_reported;
768 if (sis_saved_guess < guess2) {
769 if ((sis_saved_guess - guess1) < (guess2 - sis_saved_guess))
770 sis_saved = guess1;
771 else
772 sis_saved = guess2;
773 } else {
774 if ((sis_saved_guess - guess2) < (guess3 - sis_saved_guess))
775 sis_saved = guess2;
776 else
777 sis_saved = guess3;
778 }
779 } /* end of 32-bit workaround */
781 submit_double (host->name, volume->name, "df_complex", "sis_saved",
782 (double) sis_saved, /* time = */ 0);
783 }
784 } /* }}} void collect_volume_data */
786 static void query_volume_perf_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
787 cfg_volume_perf_t *cfg_volume_perf = data;
788 time_t timestamp;
789 na_elem_t *counter, *inst;
791 timestamp = (time_t) na_child_get_uint64(out, "timestamp", 0);
793 out = na_elem_child(out, "instances");
794 na_elem_iter_t inst_iter = na_child_iterator(out);
795 for (inst = na_iterator_next(&inst_iter); inst; inst = na_iterator_next(&inst_iter)) {
796 data_volume_perf_t perf_data;
797 volume_t *volume;
799 memset (&perf_data, 0, sizeof (perf_data));
800 perf_data.timestamp = timestamp;
802 volume = get_volume(host, na_child_get_string(inst, "name"),
803 /* data_flags = */ 0, cfg_volume_perf->flags);
804 if (volume == NULL)
805 continue;
807 na_elem_iter_t count_iter = na_child_iterator(na_elem_child(inst, "counters"));
808 for (counter = na_iterator_next(&count_iter); counter; counter = na_iterator_next(&count_iter)) {
809 const char *name;
810 uint64_t value;
812 name = na_child_get_string(counter, "name");
813 if (name == NULL)
814 continue;
816 value = na_child_get_uint64(counter, "value", UINT64_MAX);
817 if (value == UINT64_MAX)
818 continue;
820 if (!strcmp(name, "read_data")) {
821 perf_data.read_bytes = value;
822 perf_data.flags |= HAVE_VOLUME_PERF_BYTES_READ;
823 } else if (!strcmp(name, "write_data")) {
824 perf_data.write_bytes = value;
825 perf_data.flags |= HAVE_VOLUME_PERF_BYTES_WRITE;
826 } else if (!strcmp(name, "read_ops")) {
827 perf_data.read_ops = value;
828 perf_data.flags |= HAVE_VOLUME_PERF_OPS_READ;
829 } else if (!strcmp(name, "write_ops")) {
830 perf_data.write_ops = value;
831 perf_data.flags |= HAVE_VOLUME_PERF_OPS_WRITE;
832 } else if (!strcmp(name, "read_latency")) {
833 perf_data.read_latency = value;
834 perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_READ;
835 } else if (!strcmp(name, "write_latency")) {
836 perf_data.write_latency = value;
837 perf_data.flags |= HAVE_VOLUME_PERF_LATENCY_WRITE;
838 }
839 }
841 submit_volume_perf_data (host, volume, &perf_data);
842 } /* for (volume) */
843 } /* }}} void query_volume_perf_data */
845 static void collect_perf_system_data(host_config_t *host, na_elem_t *out, void *data) { /* {{{ */
846 counter_t disk_read = 0, disk_written = 0;
847 counter_t net_recv = 0, net_sent = 0;
848 counter_t cpu_busy = 0, cpu_total = 0;
849 unsigned int counter_flags = 0;
851 cfg_system_t *cfg_system = data;
852 const char *instance;
853 time_t timestamp;
854 na_elem_t *counter;
856 timestamp = (time_t) na_child_get_uint64(out, "timestamp", 0);
857 out = na_elem_child(na_elem_child(out, "instances"), "instance-data");
858 instance = na_child_get_string(out, "name");
860 na_elem_iter_t iter = na_child_iterator(na_elem_child(out, "counters"));
861 for (counter = na_iterator_next(&iter); counter; counter = na_iterator_next(&iter)) {
862 const char *name;
863 uint64_t value;
865 name = na_child_get_string(counter, "name");
866 if (name == NULL)
867 continue;
869 value = na_child_get_uint64(counter, "value", UINT64_MAX);
870 if (value == UINT64_MAX)
871 continue;
873 if (!strcmp(name, "disk_data_read")) {
874 disk_read = (counter_t) (value * 1024);
875 counter_flags |= 0x01;
876 } else if (!strcmp(name, "disk_data_written")) {
877 disk_written = (counter_t) (value * 1024);
878 counter_flags |= 0x02;
879 } else if (!strcmp(name, "net_data_recv")) {
880 net_recv = (counter_t) (value * 1024);
881 counter_flags |= 0x04;
882 } else if (!strcmp(name, "net_data_sent")) {
883 net_sent = (counter_t) (value * 1024);
884 counter_flags |= 0x08;
885 } else if (!strcmp(name, "cpu_busy")) {
886 cpu_busy = (counter_t) value;
887 counter_flags |= 0x10;
888 } else if (!strcmp(name, "cpu_elapsed_time")) {
889 cpu_total = (counter_t) value;
890 counter_flags |= 0x20;
891 } else if ((cfg_system->flags & CFG_SYSTEM_OPS)
892 && (strlen(name) > 4)
893 && (!strcmp(name + strlen(name) - 4, "_ops"))) {
894 submit_counter (host->name, instance, "disk_ops_complex", name,
895 (counter_t) value, timestamp);
896 }
897 } /* for (counter) */
899 if ((cfg_system->flags & CFG_SYSTEM_DISK)
900 && ((counter_flags & 0x03) == 0x03))
901 submit_two_counters (host->name, instance, "disk_octets", NULL,
902 disk_read, disk_written, timestamp);
904 if ((cfg_system->flags & CFG_SYSTEM_NET)
905 && ((counter_flags & 0x0c) == 0x0c))
906 submit_two_counters (host->name, instance, "if_octets", NULL,
907 net_recv, net_sent, timestamp);
909 if ((cfg_system->flags & CFG_SYSTEM_CPU)
910 && ((counter_flags & 0x30) == 0x30)) {
911 submit_counter (host->name, instance, "cpu", "system",
912 cpu_busy, timestamp);
913 submit_counter (host->name, instance, "cpu", "idle",
914 cpu_total - cpu_busy, timestamp);
915 }
916 } /* }}} void collect_perf_system_data */
918 static int cna_init(void) { /* {{{ */
919 char err[256];
920 na_elem_t *e;
921 host_config_t *host;
922 cfg_service_t *service;
924 if (!host_config) {
925 WARNING("netapp plugin: Plugin loaded but no hosts defined.");
926 return 1;
927 }
929 if (!na_startup(err, sizeof(err))) {
930 ERROR("netapp plugin: Error initializing netapp API: %s", err);
931 return 1;
932 }
934 for (host = host_config; host; host = host->next) {
935 host->srv = na_server_open(host->host, 1, 1);
936 na_server_set_transport_type(host->srv, host->protocol, 0);
937 na_server_set_port(host->srv, host->port);
938 na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
939 na_server_adminuser(host->srv, host->username, host->password);
940 na_server_set_timeout(host->srv, 5);
941 for (service = host->services; service; service = service->next) {
942 service->interval = host->interval * service->multiplier;
943 if (service->handler == collect_perf_system_data) {
944 service->query = na_elem_new("perf-object-get-instances");
945 na_child_add_string(service->query, "objectname", "system");
946 } else if (service->handler == query_volume_perf_data) {
947 service->query = na_elem_new("perf-object-get-instances");
948 na_child_add_string(service->query, "objectname", "volume");
949 /* e = na_elem_new("instances");
950 na_child_add_string(e, "foo", "system");
951 na_child_add(root, e);*/
952 e = na_elem_new("counters");
953 na_child_add_string(e, "foo", "read_ops");
954 na_child_add_string(e, "foo", "write_ops");
955 na_child_add_string(e, "foo", "read_data");
956 na_child_add_string(e, "foo", "write_data");
957 na_child_add_string(e, "foo", "read_latency");
958 na_child_add_string(e, "foo", "write_latency");
959 na_child_add(service->query, e);
960 } else if (service->handler == query_wafl_data) {
961 service->query = na_elem_new("perf-object-get-instances");
962 na_child_add_string(service->query, "objectname", "wafl");
963 /* e = na_elem_new("instances");
964 na_child_add_string(e, "foo", "system");
965 na_child_add(root, e);*/
966 e = na_elem_new("counters");
967 na_child_add_string(e, "foo", "name_cache_hit");
968 na_child_add_string(e, "foo", "name_cache_miss");
969 na_child_add_string(e, "foo", "find_dir_hit");
970 na_child_add_string(e, "foo", "find_dir_miss");
971 na_child_add_string(e, "foo", "buf_hash_hit");
972 na_child_add_string(e, "foo", "buf_hash_miss");
973 na_child_add_string(e, "foo", "inode_cache_hit");
974 na_child_add_string(e, "foo", "inode_cache_miss");
975 /* na_child_add_string(e, "foo", "inode_eject_time"); */
976 /* na_child_add_string(e, "foo", "buf_eject_time"); */
977 na_child_add(service->query, e);
978 } else if (service->handler == query_submit_disk_data) {
979 service->query = na_elem_new("perf-object-get-instances");
980 na_child_add_string(service->query, "objectname", "disk");
981 e = na_elem_new("counters");
982 na_child_add_string(e, "foo", "disk_busy");
983 na_child_add_string(e, "foo", "base_for_disk_busy");
984 na_child_add(service->query, e);
985 } else if (service->handler == collect_volume_data) {
986 service->query = na_elem_new("volume-list-info");
987 /* na_child_add_string(service->query, "objectname", "volume"); */
988 /* } else if (service->handler == collect_snapshot_data) { */
989 /* service->query = na_elem_new("snapshot-list-info"); */
990 }
991 }
992 }
993 return 0;
994 } /* }}} int cna_init */
996 static int config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */
997 uint32_t *flags, uint32_t flag)
998 {
999 if ((ci == NULL) || (flags == NULL))
1000 return (EINVAL);
1002 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
1003 {
1004 WARNING ("netapp plugin: The %s option needs exactly one boolean argument.",
1005 ci->key);
1006 return (-1);
1007 }
1009 if (ci->values[0].value.boolean)
1010 *flags |= flag;
1011 else
1012 *flags &= ~flag;
1014 return (0);
1015 } /* }}} int config_bool_to_flag */
1017 static int config_get_multiplier (const oconfig_item_t *ci, /* {{{ */
1018 cfg_service_t *service)
1019 {
1020 int tmp;
1022 if ((ci == NULL) || (service == NULL))
1023 return (EINVAL);
1025 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
1026 {
1027 WARNING ("netapp plugin: The `Multiplier' option needs exactly one numeric argument.");
1028 return (-1);
1029 }
1031 tmp = (int) (ci->values[0].value.number + .5);
1032 if (tmp < 1)
1033 {
1034 WARNING ("netapp plugin: The `Multiplier' option needs a positive integer argument.");
1035 return (-1);
1036 }
1038 service->multiplier = tmp;
1039 service->skip_countdown = tmp;
1041 return (0);
1042 } /* }}} int config_get_multiplier */
1044 static void set_global_perf_vol_flag(const host_config_t *host, /* {{{ */
1045 uint32_t flag, _Bool set)
1046 {
1047 volume_t *v;
1049 for (v = host->volumes; v; v = v->next) {
1050 if (set)
1051 v->perf_data.flags |= flag;
1052 else /* if (!set) */
1053 v->perf_data.flags &= ~flag;
1054 }
1055 } /* }}} void set_global_perf_vol_flag */
1057 static void set_global_vol_flag(const host_config_t *host, /* {{{ */
1058 uint32_t flag, _Bool set) {
1059 volume_t *v;
1061 for (v = host->volumes; v; v = v->next) {
1062 if (set)
1063 v->cfg_volume_usage.flags |= flag;
1064 else /* if (!set) */
1065 v->cfg_volume_usage.flags &= ~flag;
1066 }
1067 } /* }}} void set_global_vol_flag */
1069 static void process_perf_volume_flag (host_config_t *host, /* {{{ */
1070 cfg_volume_perf_t *perf_volume, const oconfig_item_t *item,
1071 uint32_t flag)
1072 {
1073 int i;
1075 for (i = 0; i < item->values_num; ++i) {
1076 const char *name;
1077 volume_t *v;
1078 _Bool set = true;
1080 if (item->values[i].type != OCONFIG_TYPE_STRING) {
1081 WARNING("netapp plugin: Ignoring non-string argument in "
1082 "\"GetVolumePerfData\" block for host %s", host->name);
1083 continue;
1084 }
1086 name = item->values[i].value.string;
1087 if (name[0] == '+') {
1088 set = true;
1089 ++name;
1090 } else if (name[0] == '-') {
1091 set = false;
1092 ++name;
1093 }
1095 if (!name[0]) {
1096 if (set)
1097 perf_volume->flags |= flag;
1098 else /* if (!set) */
1099 perf_volume->flags &= ~flag;
1101 set_global_perf_vol_flag(host, flag, set);
1102 continue;
1103 }
1105 v = get_volume (host, name, /* data_flags = */ 0, perf_volume->flags);
1106 if (v == NULL)
1107 continue;
1109 if (set)
1110 v->perf_data.flags |= flag;
1111 else /* if (!set) */
1112 v->perf_data.flags &= ~flag;
1113 } /* for (i = 0 .. item->values_num) */
1114 } /* }}} void process_perf_volume_flag */
1116 static void process_volume_flag (host_config_t *host, /* {{{ */
1117 cfg_volume_usage_t *cfg_volume_data, const oconfig_item_t *item, uint32_t flag)
1118 {
1119 int i;
1121 for (i = 0; i < item->values_num; ++i) {
1122 const char *name;
1123 volume_t *v;
1124 _Bool set = true;
1126 if (item->values[i].type != OCONFIG_TYPE_STRING) {
1127 WARNING("netapp plugin: Ignoring non-string argument in \"GetVolData\""
1128 "block for host %s", host->name);
1129 continue;
1130 }
1132 name = item->values[i].value.string;
1133 if (name[0] == '+') {
1134 set = true;
1135 ++name;
1136 } else if (name[0] == '-') {
1137 set = false;
1138 ++name;
1139 }
1141 if (!name[0]) {
1142 if (set)
1143 cfg_volume_data->flags |= flag;
1144 else /* if (!set) */
1145 cfg_volume_data->flags &= ~flag;
1147 set_global_vol_flag(host, flag, set);
1148 continue;
1149 }
1151 v = get_volume(host, name, cfg_volume_data->flags, /* perf_flags = */ 0);
1152 if (v == NULL)
1153 continue;
1155 if (!v->cfg_volume_usage.flags)
1156 v->cfg_volume_usage.flags = cfg_volume_data->flags;
1158 if (set)
1159 v->cfg_volume_usage.flags |= flag;
1160 else /* if (!set) */
1161 v->cfg_volume_usage.flags &= ~flag;
1162 }
1163 } /* }}} void process_volume_flag */
1165 static void build_perf_vol_config(host_config_t *host, const oconfig_item_t *ci) {
1166 int i, had_io = 0, had_ops = 0, had_latency = 0;
1167 cfg_service_t *service;
1168 cfg_volume_perf_t *perf_volume;
1170 service = malloc(sizeof(*service));
1171 service->query = 0;
1172 service->handler = query_volume_perf_data;
1173 perf_volume = service->data = malloc(sizeof(*perf_volume));
1174 perf_volume->flags = CFG_VOLUME_PERF_INIT;
1175 service->next = host->services;
1176 host->services = service;
1177 for (i = 0; i < ci->children_num; ++i) {
1178 oconfig_item_t *item = ci->children + i;
1180 /* if (!item || !item->key || !*item->key) continue; */
1181 if (!strcasecmp(item->key, "Multiplier")) {
1182 config_get_multiplier (item, service);
1183 } else if (!strcasecmp(item->key, "GetIO")) {
1184 had_io = 1;
1185 process_perf_volume_flag(host, perf_volume, item, CFG_VOLUME_PERF_IO);
1186 } else if (!strcasecmp(item->key, "GetOps")) {
1187 had_ops = 1;
1188 process_perf_volume_flag(host, perf_volume, item, CFG_VOLUME_PERF_OPS);
1189 } else if (!strcasecmp(item->key, "GetLatency")) {
1190 had_latency = 1;
1191 process_perf_volume_flag(host, perf_volume, item, CFG_VOLUME_PERF_LATENCY);
1192 }
1193 }
1194 if (!had_io) {
1195 perf_volume->flags |= CFG_VOLUME_PERF_IO;
1196 set_global_perf_vol_flag(host, CFG_VOLUME_PERF_IO, /* set = */ true);
1197 }
1198 if (!had_ops) {
1199 perf_volume->flags |= CFG_VOLUME_PERF_OPS;
1200 set_global_perf_vol_flag(host, CFG_VOLUME_PERF_OPS, /* set = */ true);
1201 }
1202 if (!had_latency) {
1203 perf_volume->flags |= CFG_VOLUME_PERF_LATENCY;
1204 set_global_perf_vol_flag(host, CFG_VOLUME_PERF_LATENCY, /* set = */ true);
1205 }
1206 }
1208 static void build_volume_config(host_config_t *host, oconfig_item_t *ci) {
1209 int i, had_df = 0;
1210 cfg_service_t *service;
1211 cfg_volume_usage_t *cfg_volume_data;
1213 service = malloc(sizeof(*service));
1214 service->query = 0;
1215 service->handler = collect_volume_data;
1216 cfg_volume_data = service->data = malloc(sizeof(*cfg_volume_data));
1217 cfg_volume_data->flags = VOLUME_INIT;
1218 service->next = host->services;
1219 host->services = service;
1220 for (i = 0; i < ci->children_num; ++i) {
1221 oconfig_item_t *item = ci->children + i;
1223 /* if (!item || !item->key || !*item->key) continue; */
1224 if (!strcasecmp(item->key, "Multiplier")) {
1225 config_get_multiplier (item, service);
1226 } else if (!strcasecmp(item->key, "GetDiskUtil")) {
1227 had_df = 1;
1228 process_volume_flag(host, cfg_volume_data, item, VOLUME_DF);
1229 }
1230 }
1231 if (!had_df) {
1232 cfg_volume_data->flags |= VOLUME_DF;
1233 set_global_vol_flag(host, VOLUME_DF, /* set = */ true);
1234 }
1235 }
1237 static void build_perf_disk_config(host_config_t *temp, oconfig_item_t *ci) {
1238 int i;
1239 cfg_service_t *service;
1240 cfg_disk_t *cfg_disk;
1242 service = malloc(sizeof(*service));
1243 service->query = 0;
1244 service->handler = query_submit_disk_data;
1245 cfg_disk = service->data = malloc(sizeof(*cfg_disk));
1246 cfg_disk->flags = CFG_DISK_ALL;
1247 service->next = temp->services;
1248 temp->services = service;
1249 for (i = 0; i < ci->children_num; ++i) {
1250 oconfig_item_t *item = ci->children + i;
1252 /* if (!item || !item->key || !*item->key) continue; */
1253 if (!strcasecmp(item->key, "Multiplier")) {
1254 config_get_multiplier (item, service);
1255 } else if (!strcasecmp(item->key, "GetBusy")) {
1256 config_bool_to_flag (item, &cfg_disk->flags, CFG_SYSTEM_CPU);
1257 }
1258 }
1259 }
1261 static void build_perf_wafl_config(host_config_t *host, oconfig_item_t *ci) { /* {{{ */
1262 int i;
1263 cfg_service_t *service;
1264 data_wafl_t *perf_wafl;
1266 service = malloc(sizeof(*service));
1267 if (service == NULL)
1268 return;
1269 memset (service, 0, sizeof (*service));
1271 service->query = 0;
1272 service->handler = query_wafl_data;
1273 perf_wafl = service->data = malloc(sizeof(*perf_wafl));
1274 perf_wafl->flags = CFG_WAFL_ALL;
1276 for (i = 0; i < ci->children_num; ++i) {
1277 oconfig_item_t *item = ci->children + i;
1279 if (!strcasecmp(item->key, "Multiplier")) {
1280 config_get_multiplier (item, service);
1281 } else if (!strcasecmp(item->key, "GetNameCache")) {
1282 config_bool_to_flag (item, &perf_wafl->flags, CFG_WAFL_NAME_CACHE);
1283 } else if (!strcasecmp(item->key, "GetDirCache")) {
1284 config_bool_to_flag (item, &perf_wafl->flags, CFG_WAFL_DIR_CACHE);
1285 } else if (!strcasecmp(item->key, "GetBufCache")) {
1286 config_bool_to_flag (item, &perf_wafl->flags, CFG_WAFL_BUF_CACHE);
1287 } else if (!strcasecmp(item->key, "GetInodeCache")) {
1288 config_bool_to_flag (item, &perf_wafl->flags, CFG_WAFL_INODE_CACHE);
1289 } else {
1290 WARNING ("netapp plugin: The %s config option is not allowed within "
1291 "`GetWaflPerfData' blocks.", item->key);
1292 }
1293 }
1295 service->next = host->services;
1296 host->services = service;
1297 } /* }}} void build_perf_wafl_config */
1299 static int build_perf_sys_config (host_config_t *host, /* {{{ */
1300 oconfig_item_t *ci, const cfg_service_t *default_service)
1301 {
1302 int i;
1303 cfg_service_t *service;
1304 cfg_system_t *cfg_system;
1306 service = malloc(sizeof(*service));
1307 if (service == NULL)
1308 return (-1);
1309 memset (service, 0, sizeof (*service));
1310 *service = *default_service;
1311 service->handler = collect_perf_system_data;
1313 cfg_system = malloc(sizeof(*cfg_system));
1314 if (cfg_system == NULL) {
1315 sfree (service);
1316 return (-1);
1317 }
1318 memset (cfg_system, 0, sizeof (*cfg_system));
1319 cfg_system->flags = CFG_SYSTEM_ALL;
1320 service->data = cfg_system;
1322 for (i = 0; i < ci->children_num; ++i) {
1323 oconfig_item_t *item = ci->children + i;
1325 if (!strcasecmp(item->key, "Multiplier")) {
1326 config_get_multiplier (item, service);
1327 } else if (!strcasecmp(item->key, "GetCPULoad")) {
1328 config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_CPU);
1329 } else if (!strcasecmp(item->key, "GetInterfaces")) {
1330 config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_NET);
1331 } else if (!strcasecmp(item->key, "GetDiskOps")) {
1332 config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_OPS);
1333 } else if (!strcasecmp(item->key, "GetDiskIO")) {
1334 config_bool_to_flag (item, &cfg_system->flags, CFG_SYSTEM_DISK);
1335 } else {
1336 WARNING ("netapp plugin: The %s config option is not allowed within "
1337 "`GetSystemPerfData' blocks.", item->key);
1338 }
1339 }
1341 service->next = host->services;
1342 host->services = service;
1344 return (0);
1345 } /* }}} int build_perf_sys_config */
1347 static host_config_t *build_host_config(const oconfig_item_t *ci, const host_config_t *default_host, const cfg_service_t *def_def_service) {
1348 int i;
1349 oconfig_item_t *item;
1350 host_config_t *host, *hc, temp = *default_host;
1351 cfg_service_t default_service = *def_def_service;
1353 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
1354 WARNING("netapp plugin: \"Host\" needs exactly one string argument. Ignoring host block.");
1355 return 0;
1356 }
1358 temp.name = ci->values[0].value.string;
1359 for (i = 0; i < ci->children_num; ++i) {
1360 item = ci->children + i;
1362 /* if (!item || !item->key || !*item->key) continue; */
1363 if (!strcasecmp(item->key, "Address")) {
1364 if ((item->values_num != 1) || (item->values[0].type != OCONFIG_TYPE_STRING)) {
1365 WARNING("netapp plugin: \"Name\" needs exactly one string argument. Ignoring host block \"%s\".", ci->values[0].value.string);
1366 return 0;
1367 }
1368 temp.host = item->values[0].value.string;
1369 } else if (!strcasecmp(item->key, "Port")) {
1370 if ((item->values_num != 1) || (item->values[0].type != OCONFIG_TYPE_NUMBER) || (item->values[0].value.number != (int) (item->values[0].value.number)) || (item->values[0].value.number < 1) || (item->values[0].value.number > 65535)) {
1371 WARNING("netapp plugin: \"Port\" needs exactly one integer argument in the range of 1-65535. Ignoring host block \"%s\".", ci->values[0].value.string);
1372 return 0;
1373 }
1374 temp.port = item->values[0].value.number;
1375 } else if (!strcasecmp(item->key, "Protocol")) {
1376 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"))) {
1377 WARNING("netapp plugin: \"Protocol\" needs to be either \"http\" or \"https\". Ignoring host block \"%s\".", ci->values[0].value.string);
1378 return 0;
1379 }
1380 if (!strcasecmp(item->values[0].value.string, "http")) temp.protocol = NA_SERVER_TRANSPORT_HTTP;
1381 else temp.protocol = NA_SERVER_TRANSPORT_HTTPS;
1382 } else if (!strcasecmp(item->key, "Login")) {
1383 if ((item->values_num != 2) || (item->values[0].type != OCONFIG_TYPE_STRING) || (item->values[1].type != OCONFIG_TYPE_STRING)) {
1384 WARNING("netapp plugin: \"Login\" needs exactly two string arguments, username and password. Ignoring host block \"%s\".", ci->values[0].value.string);
1385 return 0;
1386 }
1387 temp.username = item->values[0].value.string;
1388 temp.password = item->values[1].value.string;
1389 } else if (!strcasecmp(item->key, "Interval")) {
1390 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_NUMBER || item->values[0].value.number != (int) item->values[0].value.number || item->values[0].value.number < 2) {
1391 WARNING("netapp plugin: \"Interval\" of host %s needs exactly one integer argument.", ci->values[0].value.string);
1392 continue;
1393 }
1394 temp.interval = item->values[0].value.number;
1395 } else if (!strcasecmp(item->key, "GetVolumePerfData")) {
1396 build_perf_vol_config(&temp, item);
1397 } else if (!strcasecmp(item->key, "GetSystemPerfData")) {
1398 build_perf_sys_config(&temp, item, &default_service);
1399 } else if (!strcasecmp(item->key, "GetWaflPerfData")) {
1400 build_perf_wafl_config(&temp, item);
1401 } else if (!strcasecmp(item->key, "GetDiskPerfData")) {
1402 build_perf_disk_config(&temp, item);
1403 } else if (!strcasecmp(item->key, "GetVolumeData")) {
1404 build_volume_config(&temp, item);
1405 } else {
1406 WARNING("netapp plugin: Ignoring unknown config option \"%s\" in host block \"%s\".",
1407 item->key, ci->values[0].value.string);
1408 }
1409 }
1411 if (!temp.host) temp.host = temp.name;
1412 if (!temp.port) temp.port = temp.protocol == NA_SERVER_TRANSPORT_HTTP ? 80 : 443;
1413 if (!temp.username) {
1414 WARNING("netapp plugin: Please supply login information for host \"%s\". Ignoring host block.", temp.name);
1415 return 0;
1416 }
1417 for (hc = host_config; hc; hc = hc->next) {
1418 if (!strcasecmp(hc->name, temp.name)) WARNING("netapp plugin: Duplicate definition of host \"%s\". This is probably a bad idea.", hc->name);
1419 }
1420 host = malloc(sizeof(*host));
1421 *host = temp;
1422 host->name = strdup(temp.name);
1423 host->protocol = temp.protocol;
1424 host->host = strdup(temp.host);
1425 host->username = strdup(temp.username);
1426 host->password = strdup(temp.password);
1427 host->next = host_config;
1428 host_config = host;
1429 return host;
1430 }
1432 static int cna_config (oconfig_item_t *ci) {
1433 int i;
1434 oconfig_item_t *item;
1435 host_config_t default_host = HOST_INIT;
1436 cfg_service_t default_service = SERVICE_INIT;
1438 for (i = 0; i < ci->children_num; ++i) {
1439 item = ci->children + i;
1441 /* if (!item || !item->key || !*item->key) continue; */
1442 if (!strcasecmp(item->key, "Host")) {
1443 build_host_config(item, &default_host, &default_service);
1444 } else {
1445 WARNING("netapp plugin: Ignoring unknown config option \"%s\".", item->key);
1446 }
1447 }
1448 return 0;
1449 }
1451 static int cna_read(void) {
1452 na_elem_t *out;
1453 host_config_t *host;
1454 cfg_service_t *service;
1456 for (host = host_config; host; host = host->next) {
1457 for (service = host->services; service; service = service->next) {
1458 if (--service->skip_countdown > 0) continue;
1459 service->skip_countdown = service->multiplier;
1460 out = na_server_invoke_elem(host->srv, service->query);
1461 if (na_results_status(out) != NA_OK) {
1462 int netapp_errno = na_results_errno(out);
1463 ERROR("netapp plugin: Error %d from host %s: %s", netapp_errno, host->name, na_results_reason(out));
1464 na_elem_free(out);
1465 if (netapp_errno == EIO || netapp_errno == ETIMEDOUT) {
1466 /* Network problems. Just give up on all other services on this host. */
1467 break;
1468 }
1469 continue;
1470 }
1471 service->handler(host, out, service->data);
1472 na_elem_free(out);
1473 }
1474 }
1475 return 0;
1476 }
1478 void module_register(void) {
1479 plugin_register_complex_config("netapp", cna_config);
1480 plugin_register_init("netapp", cna_init);
1481 plugin_register_read("netapp", cna_read);
1482 }
1484 /* vim: set sw=2 ts=2 noet fdm=marker : */