root/src/noit_poller.c

Revision 5f413b75efe299d3b88314bbcc3c21cbbcec36a7, 11.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 11 years ago)

more work... framework for submitting arbitrary metrics

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <assert.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13
14 #include "utils/noit_log.h"
15 #include "utils/noit_hash.h"
16 #include "utils/noit_skiplist.h"
17 #include "noit_conf.h"
18 #include "noit_poller.h"
19 #include "noit_module.h"
20 #include "eventer/eventer.h"
21
22 static noit_hash_table polls = NOIT_HASH_EMPTY;
23 static noit_skiplist polls_by_name = { 0 };
24 static u_int32_t __config_load_generation = 0;
25 struct uuid_dummy {
26   uuid_t foo;
27 };
28
29 #define UUID_SIZE sizeof(struct uuid_dummy)
30
31 static const char *
32 __noit_check_available_string(int16_t available) {
33   switch(available) {
34     case NP_AVAILABLE:    return "available";
35     case NP_UNAVAILABLE:  return "unavailable";
36     case NP_UNKNOWN:      return "unknown";
37   }
38   return "???";
39 }
40 static const char *
41 __noit_check_state_string(int16_t state) {
42   switch(state) {
43     case NP_GOOD:         return "good";
44     case NP_BAD:          return "bad";
45     case NP_UNKNOWN:      return "unknown";
46   }
47   return "???";
48 }
49 static int __check_name_compare(void *a, void *b) {
50   noit_check_t ac = a;
51   noit_check_t bc = b;
52   int rv;
53   if((rv = strcmp(ac->target, bc->target)) != 0) return rv;
54   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
55   return 0;
56 }
57 void
58 noit_poller_load_checks() {
59   int i, cnt = 0;
60   noit_conf_section_t *sec;
61   __config_load_generation++;
62   sec = noit_conf_get_sections(NULL, "/noit/checks//check", &cnt);
63   for(i=0; i<cnt; i++) {
64     char uuid_str[37];
65     char target[256];
66     char module[256];
67     char name[256];
68     char oncheck[1024];
69     int no_period = 0;
70     int no_oncheck = 0;
71     int period = 0, timeout = 0;
72     uuid_t uuid, out_uuid;
73     noit_hash_table *options;
74
75     if(!noit_conf_get_stringbuf(sec[i], "@uuid",
76                                 uuid_str, sizeof(uuid_str))) {
77       noitL(noit_stderr, "check %d has no uuid\n", i+1);
78       continue;
79     }
80     if(uuid_parse(uuid_str, uuid)) {
81       noitL(noit_stderr, "check uuid: '%s' is invalid\n", uuid_str);
82       continue;
83     }
84     if(!noit_conf_get_stringbuf(sec[i], "target", target, sizeof(target))) {
85       if(!noit_conf_get_stringbuf(sec[i], "../target", target, sizeof(target))) {
86          noitL(noit_stderr, "check uuid: '%s' has no target\n",
87                   uuid_str);
88          continue;
89       }
90     }
91     if(!noit_conf_get_stringbuf(sec[i], "module", module, sizeof(module))) {
92       if(!noit_conf_get_stringbuf(sec[i], "../module", module, sizeof(module))) {
93         noitL(noit_stderr, "check uuid: '%s' has no module\n",
94               uuid_str);
95         continue;
96       }
97     }
98     if(!noit_conf_get_stringbuf(sec[i], "name", name, sizeof(name))) {
99       strcpy(name, module);
100     }
101     if(!noit_conf_get_int(sec[i], "period", &period)) {
102       if(!noit_conf_get_int(sec[i], "../period", &period)) {
103         no_period = 1;
104       }
105     }
106     if(!noit_conf_get_stringbuf(sec[i], "oncheck", oncheck, sizeof(oncheck))) {
107       if(!noit_conf_get_stringbuf(sec[i], "../oncheck", oncheck, sizeof(oncheck))) {
108         oncheck[0] = '\0';
109         no_oncheck = 1;
110       }
111     }
112     if(no_period && no_oncheck) {
113       noitL(noit_stderr, "check uuid: '%s' has neither period nor oncheck\n",
114             uuid_str);
115       continue;
116     }
117     if(!(no_period || no_oncheck)) {
118       noitL(noit_stderr, "check uuid: '%s' has has on check and period.\n",
119             uuid_str);
120       continue;
121     }
122     if(!noit_conf_get_int(sec[i], "timeout", &timeout)) {
123       if(!noit_conf_get_int(sec[i], "../timeout", &timeout)) {
124         noitL(noit_stderr, "check uuid: '%s' has no timeout\n", uuid_str);
125         continue;
126       }
127     }
128     if(!no_period && timeout >= period) {
129       noitL(noit_stderr, "check uuid: '%s' timeout > period\n", uuid_str);
130       timeout = period/2;
131     }
132     options = noit_conf_get_hash(sec[i], "config/*");
133     noit_poller_schedule(target, module, name, options,
134                          period, timeout, oncheck[0] ? oncheck : NULL,
135                          uuid, out_uuid);
136     noitL(noit_debug, "loaded uuid: %s\n", uuid_str);
137   }
138 }
139
140 void
141 noit_poller_initiate() {
142   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
143   uuid_t key_id;
144   int klen;
145   noit_check_t check;
146   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
147                        (void **)&check)) {
148     noit_module_t *mod;
149     mod = noit_module_lookup(check->module);
150     if(mod) {
151       if((check->flags & NP_DISABLED) == 0)
152         mod->initiate_check(mod, check, 0, NULL);
153     }
154     else {
155       noitL(noit_stderr, "Cannot find module '%s'\n", check->module);
156       check->flags |= NP_DISABLED;
157     }
158   }
159 }
160
161 void
162 noit_poller_make_causal_map() {
163   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
164   uuid_t key_id;
165   int klen;
166   noit_check_t check, parent;
167   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
168                        (void **)&check)) {
169     if(check->oncheck) {
170       /* This service is causally triggered by another service */
171       char fullcheck[1024];
172       char *name = check->oncheck;
173       char *target = NULL;
174
175       if((target = strchr(check->oncheck, '`')) != NULL) {
176         strlcpy(fullcheck, check->oncheck, target - check->oncheck);
177         name = target + 1;
178         target = fullcheck;
179       }
180       else
181        target = check->target;
182
183       parent = noit_poller_lookup_by_name(target, name);
184       if(!parent) {
185         check->flags |= NP_DISABLED;
186         noitL(noit_stderr, "Disabling check %s/%s, can't find oncheck %s/%s\n",
187               check->target, check->name, target, name);
188       }
189       else {
190         dep_list_t *dep;
191         dep = malloc(sizeof(*dep));
192         dep->check = check;
193         dep->next = parent->causal_checks;
194         parent->causal_checks = dep;
195       }
196     }
197   }
198 }
199 void
200 noit_poller_init() {
201   noit_skiplist_init(&polls_by_name);
202   noit_skiplist_set_compare(&polls_by_name, __check_name_compare,
203                             __check_name_compare);
204   noit_poller_load_checks();
205   noit_poller_make_causal_map();
206   noit_poller_initiate();
207 }
208
209 int
210 noit_poller_schedule(const char *target,
211                      const char *module,
212                      const char *name,
213                      noit_hash_table *config,
214                      u_int32_t period,
215                      u_int32_t timeout,
216                      const char *oncheck,
217                      uuid_t in,
218                      uuid_t out) {
219   int8_t family;
220   int rv;
221   union {
222     struct in_addr addr4;
223     struct in6_addr addr6;
224   } a;
225   noit_check_t new_check;
226
227
228   family = AF_INET;
229   rv = inet_pton(family, target, &a);
230   if(rv != 1) {
231     family = AF_INET6;
232     rv = inet_pton(family, target, &a);
233     if(rv != 1) {
234       noitL(noit_stderr, "Cannot translate '%s' to IP\n", target);
235       return -1;
236     }
237   }
238
239   new_check = calloc(1, sizeof(*new_check));
240   if(!new_check) return -1;
241   new_check->generation = __config_load_generation;
242   new_check->target_family = family;
243   memcpy(&new_check->target_addr, &a, sizeof(a));
244   new_check->target = strdup(target);
245   new_check->module = strdup(module);
246   new_check->name = name ? strdup(name): NULL;
247
248   if(config != NULL) {
249     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
250     const char *k;
251     int klen;
252     void *data;
253     new_check->config = calloc(1, sizeof(*new_check->config));
254     while(noit_hash_next(config, &iter, &k, &klen, &data)) {
255       noit_hash_store(new_check->config, strdup(k), klen, strdup((char *)data));
256     }
257   }
258   new_check->oncheck = oncheck ? strdup(oncheck) : NULL;
259   new_check->period = period;
260   new_check->timeout = timeout;
261   new_check->flags = 0;
262   if(uuid_is_null(in))
263     uuid_generate(new_check->checkid);
264   else
265     uuid_copy(new_check->checkid, in);
266
267   assert(noit_hash_store(&polls,
268                          (char *)new_check->checkid, UUID_SIZE,
269                          new_check));
270   noit_skiplist_insert(&polls_by_name, new_check);
271   uuid_copy(out, new_check->checkid);
272   return 0;
273 }
274
275 int
276 noit_poller_deschedule(uuid_t in) {
277   noit_check_t checker;
278   if(noit_hash_retrieve(&polls,
279                         (char *)in, UUID_SIZE,
280                         (void **)&checker) == 0) {
281     return -1;
282   }
283   if(checker->flags & NP_RUNNING) {
284     checker->flags |= NP_KILLED;
285     return 0;
286   }
287   if(checker->fire_event) {
288      eventer_remove(checker->fire_event);
289      eventer_free(checker->fire_event);
290      checker->fire_event = NULL;
291   }
292   noit_hash_delete(&polls, (char *)in, UUID_SIZE, free, free);
293
294   if(checker->target) free(checker->target);
295   if(checker->module) free(checker->module);
296   if(checker->name) free(checker->name);
297   if(checker->config) {
298     noit_hash_destroy(checker->config, free, free);
299     free(checker->config);
300     checker->config = NULL;
301   }
302   free(checker);
303   return 0;
304 }
305
306 noit_check_t
307 noit_poller_lookup(uuid_t in) {
308   noit_check_t check;
309   if(noit_hash_retrieve(&polls,
310                         (char *)in, UUID_SIZE,
311                         (void **)&check)) {
312     return check;
313   }
314   return NULL;
315 }
316 noit_check_t
317 noit_poller_lookup_by_name(char *target, char *name) {
318   noit_check_t check, tmp_check;
319   tmp_check = calloc(1, sizeof(*tmp_check));
320   tmp_check->target = target;
321   tmp_check->name = name;
322   check = noit_skiplist_find(&polls_by_name, tmp_check, NULL);
323   free(tmp_check);
324   return check;
325 }
326
327 static void
328 __free_metric(void *vm) {
329   metric_t *m = vm;
330   free(m->metric_name);
331   if(m->metric_value.i) free(m->metric_value.i);
332 }
333
334 void
335 __stats_add_metric(stats_t *newstate, metric_t *m) {
336   noit_hash_replace(&newstate->metrics, m->metric_name, strlen(m->metric_name),
337                     m, NULL, __free_metric);
338 }
339
340 void
341 noit_poller_set_metric_int(stats_t *newstate, char *name, int *value) {
342   metric_t *m = calloc(1, sizeof(*m));
343   m->metric_name = strdup(name);
344   m->metric_type = METRIC_INT;
345   if(value) {
346     m->metric_value.i = malloc(sizeof(*value));
347     *(m->metric_value.i) = *value;
348   }
349   __stats_add_metric(newstate, m);
350 }
351
352 void
353 noit_poller_set_metric_float(stats_t *newstate, char *name, float *value) {
354   metric_t *m = calloc(1, sizeof(*m));
355   m->metric_name = strdup(name);
356   m->metric_type = METRIC_FLOAT;
357   if(value) {
358     m->metric_value.f = malloc(sizeof(*value));
359     *(m->metric_value.f) = *value;
360   }
361   __stats_add_metric(newstate, m);
362 }
363
364 void
365 noit_poller_set_metric_string(stats_t *newstate, char *name, char *value) {
366   metric_t *m = calloc(1, sizeof(*m));
367   m->metric_name = strdup(name);
368   m->metric_type = METRIC_STRING;
369   m->metric_value.s = value ? strdup(value) : NULL;
370   __stats_add_metric(newstate, m);
371 }
372
373 void
374 noit_poller_set_state(struct _noit_module *module,
375                       noit_check_t check, stats_t *newstate) {
376   int report_change = 0;
377   dep_list_t *dep;
378   if(check->stats.previous.status)
379     free(check->stats.previous.status);
380   noit_hash_destroy(&check->stats.previous.metrics, NULL, __free_metric);
381   memcpy(&check->stats.previous, &check->stats.current, sizeof(stats_t));
382   memcpy(&check->stats.current, newstate, sizeof(stats_t));
383   if(check->stats.current.status)
384     check->stats.current.status = strdup(check->stats.current.status);
385
386   /* check for state changes */
387   if(check->stats.current.available != 0 &&
388      check->stats.previous.available != 0 &&
389      check->stats.current.available != check->stats.previous.available)
390     report_change = 1;
391   if(check->stats.current.state != 0 &&
392      check->stats.previous.state != 0 &&
393      check->stats.current.state != check->stats.previous.state)
394     report_change = 1;
395
396   noitL(noit_error, "%s/%s <- [%s]\n", check->target, check->module,
397         check->stats.current.status);
398   if(report_change) {
399     noitL(noit_error, "%s/%s -> [%s/%s]\n",
400           check->target, check->module,
401           __noit_check_available_string(check->stats.current.available),
402           __noit_check_state_string(check->stats.current.state));
403   }
404   for(dep = check->causal_checks; dep; dep = dep->next) {
405     noit_module_t *mod;
406     mod = noit_module_lookup(dep->check->module);
407     assert(mod);
408     noitL(noit_debug, "Firing %s/%s in response to %s/%s\n",
409           dep->check->target, dep->check->name,
410           check->target, check->name);
411     mod->initiate_check(mod, dep->check, 1, check);
412   }
413 }
Note: See TracBrowser for help on using the browser.