root/src/noit_check.c

Revision 67a2095e250dc562985ebc225064b0ef2a68ba2f, 13.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 11 years ago)

convert elements to attributes... it seems more appropriate

  • 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 <stdlib.h>
10 #include <unistd.h>
11 #include <assert.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14
15 #include "utils/noit_log.h"
16 #include "utils/noit_hash.h"
17 #include "utils/noit_skiplist.h"
18 #include "noit_conf.h"
19 #include "noit_check.h"
20 #include "noit_module.h"
21 #include "noit_console.h"
22 #include "eventer/eventer.h"
23
24 /* 60 seconds of possible stutter */
25 #define MAX_INITIAL_STUTTER (60*1000)
26
27 static noit_hash_table polls = NOIT_HASH_EMPTY;
28 static noit_skiplist polls_by_name = { 0 };
29 static u_int32_t __config_load_generation = 0;
30 struct uuid_dummy {
31   uuid_t foo;
32 };
33
34 static void register_console_check_commands();
35
36 #define UUID_SIZE sizeof(struct uuid_dummy)
37
38 static const char *
39 __noit_check_available_string(int16_t available) {
40   switch(available) {
41     case NP_AVAILABLE:    return "available";
42     case NP_UNAVAILABLE:  return "unavailable";
43     case NP_UNKNOWN:      return "unknown";
44   }
45   return "???";
46 }
47 static const char *
48 __noit_check_state_string(int16_t state) {
49   switch(state) {
50     case NP_GOOD:         return "good";
51     case NP_BAD:          return "bad";
52     case NP_UNKNOWN:      return "unknown";
53   }
54   return "???";
55 }
56 static int __check_name_compare(void *a, void *b) {
57   noit_check_t *ac = a;
58   noit_check_t *bc = b;
59   int rv;
60   if((rv = strcmp(ac->target, bc->target)) != 0) return rv;
61   if((rv = strcmp(ac->name, bc->name)) != 0) return rv;
62   return 0;
63 }
64 int
65 noit_check_max_initial_stutter() {
66   return MAX_INITIAL_STUTTER;
67 }
68 void
69 noit_check_fake_last_check(noit_check_t *check,
70                            struct timeval *lc, struct timeval *_now) {
71   struct timeval now, period;
72   double r;
73   int offset;
74
75   r = drand48();
76   offset = r * (MIN(MAX_INITIAL_STUTTER, check->period));
77   period.tv_sec = (check->period - offset) / 1000;
78   period.tv_usec = ((check->period - offset) % 1000) * 1000;
79   if(!_now) {
80     gettimeofday(&now, NULL);
81     _now = &now;
82   }
83   sub_timeval(*_now, period, lc);
84 }
85 void
86 noit_poller_load_checks() {
87   int i, cnt = 0;
88   noit_conf_section_t *sec;
89   __config_load_generation++;
90   sec = noit_conf_get_sections(NULL, "/noit/checks//check", &cnt);
91   for(i=0; i<cnt; i++) {
92     char uuid_str[37];
93     char target[256];
94     char module[256];
95     char name[256];
96     char oncheck[1024] = "";
97     int no_period = 0;
98     int no_oncheck = 0;
99     int period = 0, timeout = 0;
100     uuid_t uuid, out_uuid;
101     noit_hash_table *options;
102
103 #define NEXT(...) noitL(noit_stderr, __VA_ARGS__); continue
104 #define MYATTR(type,a,...) noit_conf_get_##type(sec[i], "@" #a, __VA_ARGS__)
105 #define INHERIT(type,a,...) \
106   noit_conf_get_##type(sec[i], "ancestor-or-self::node()/@" #a, __VA_ARGS__)
107
108     if(!MYATTR(stringbuf, uuid, uuid_str, sizeof(uuid_str))) {
109       noitL(noit_stderr, "check %d has no uuid\n", i+1);
110       continue;
111     }
112
113     if(uuid_parse(uuid_str, uuid)) {
114       noitL(noit_stderr, "check uuid: '%s' is invalid\n", uuid_str);
115       continue;
116     }
117
118     if(!INHERIT(stringbuf, target, target, sizeof(target))) {
119       noitL(noit_stderr, "check uuid: '%s' has no target\n", uuid_str);
120       continue;
121     }
122     if(!INHERIT(stringbuf, module, module, sizeof(module))) {
123       noitL(noit_stderr, "check uuid: '%s' has no module\n", uuid_str);
124       continue;
125     }
126
127     if(!MYATTR(stringbuf, name, name, sizeof(name)))
128       strlcpy(name, module, sizeof(name));
129
130     if(!INHERIT(int, period, &period) || period == 0)
131       no_period = 1;
132
133     if(!INHERIT(stringbuf, oncheck, oncheck, sizeof(oncheck)) || !oncheck[0])
134       no_oncheck = 1;
135
136     if(no_period && no_oncheck) {
137       noitL(noit_stderr, "check uuid: '%s' has neither period nor oncheck\n",
138             uuid_str);
139       continue;
140     }
141     if(!(no_period || no_oncheck)) {
142       noitL(noit_stderr, "check uuid: '%s' has oncheck and period.\n",
143             uuid_str);
144       continue;
145     }
146     if(!INHERIT(int, timeout, &timeout)) {
147       noitL(noit_stderr, "check uuid: '%s' has no timeout\n", uuid_str);
148       continue;
149     }
150     if(!no_period && timeout >= period) {
151       noitL(noit_stderr, "check uuid: '%s' timeout > period\n", uuid_str);
152       timeout = period/2;
153     }
154     options = noit_conf_get_hash(sec[i], "ancestor-or-self::node()/config/*");
155     noit_poller_schedule(target, module, name, options,
156                          period, timeout, oncheck[0] ? oncheck : NULL,
157                          uuid, out_uuid);
158     noitL(noit_debug, "loaded uuid: %s\n", uuid_str);
159   }
160 }
161
162 void
163 noit_poller_initiate() {
164   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
165   uuid_t key_id;
166   int klen;
167   noit_check_t *check;
168   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
169                        (void **)&check)) {
170     noit_module_t *mod;
171     mod = noit_module_lookup(check->module);
172     if(mod) {
173       if((check->flags & NP_DISABLED) == 0)
174         mod->initiate_check(mod, check, 0, NULL);
175     }
176     else {
177       noitL(noit_stderr, "Cannot find module '%s'\n", check->module);
178       check->flags |= NP_DISABLED;
179     }
180   }
181 }
182
183 void
184 noit_poller_make_causal_map() {
185   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
186   uuid_t key_id;
187   int klen;
188   noit_check_t *check, *parent;
189   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
190                        (void **)&check)) {
191     if(check->oncheck) {
192       /* This service is causally triggered by another service */
193       char fullcheck[1024];
194       char *name = check->oncheck;
195       char *target = NULL;
196
197       if((target = strchr(check->oncheck, '`')) != NULL) {
198         strlcpy(fullcheck, check->oncheck, target - check->oncheck);
199         name = target + 1;
200         target = fullcheck;
201       }
202       else
203        target = check->target;
204
205       parent = noit_poller_lookup_by_name(target, name);
206       if(!parent) {
207         check->flags |= NP_DISABLED;
208         noitL(noit_stderr, "Disabling check %s`%s, can't find oncheck %s`%s\n",
209               check->target, check->name, target, name);
210       }
211       else {
212         dep_list_t *dep;
213         dep = malloc(sizeof(*dep));
214         dep->check = check;
215         dep->next = parent->causal_checks;
216         parent->causal_checks = dep;
217         noitL(noit_debug, "Causal map %s`%s --> %s`%s\n",
218               parent->target, parent->name, check->target, check->name);
219       }
220     }
221   }
222 }
223 void
224 noit_poller_init() {
225   noit_skiplist_init(&polls_by_name);
226   noit_skiplist_set_compare(&polls_by_name, __check_name_compare,
227                             __check_name_compare);
228   noit_poller_load_checks();
229   noit_poller_make_causal_map();
230   register_console_check_commands();
231   noit_poller_initiate();
232 }
233
234 int
235 noit_poller_schedule(const char *target,
236                      const char *module,
237                      const char *name,
238                      noit_hash_table *config,
239                      u_int32_t period,
240                      u_int32_t timeout,
241                      const char *oncheck,
242                      uuid_t in,
243                      uuid_t out) {
244   int8_t family;
245   int rv;
246   union {
247     struct in_addr addr4;
248     struct in6_addr addr6;
249   } a;
250   noit_check_t *new_check;
251
252
253   family = AF_INET;
254   rv = inet_pton(family, target, &a);
255   if(rv != 1) {
256     family = AF_INET6;
257     rv = inet_pton(family, target, &a);
258     if(rv != 1) {
259       noitL(noit_stderr, "Cannot translate '%s' to IP\n", target);
260       return -1;
261     }
262   }
263
264   new_check = calloc(1, sizeof(*new_check));
265   if(!new_check) return -1;
266   new_check->generation = __config_load_generation;
267   new_check->target_family = family;
268   memcpy(&new_check->target_addr, &a, sizeof(a));
269   new_check->target = strdup(target);
270   new_check->module = strdup(module);
271   new_check->name = name ? strdup(name): NULL;
272
273   if(config != NULL) {
274     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
275     const char *k;
276     int klen;
277     void *data;
278     new_check->config = calloc(1, sizeof(*new_check->config));
279     while(noit_hash_next(config, &iter, &k, &klen, &data)) {
280       noit_hash_store(new_check->config, strdup(k), klen, strdup((char *)data));
281     }
282   }
283   new_check->oncheck = oncheck ? strdup(oncheck) : NULL;
284   new_check->period = period;
285   new_check->timeout = timeout;
286   new_check->flags = 0;
287   if(uuid_is_null(in))
288     uuid_generate(new_check->checkid);
289   else
290     uuid_copy(new_check->checkid, in);
291
292   assert(noit_hash_store(&polls,
293                          (char *)new_check->checkid, UUID_SIZE,
294                          new_check));
295   noit_skiplist_insert(&polls_by_name, new_check);
296   uuid_copy(out, new_check->checkid);
297   return 0;
298 }
299
300 int
301 noit_poller_deschedule(uuid_t in) {
302   noit_check_t *checker;
303   if(noit_hash_retrieve(&polls,
304                         (char *)in, UUID_SIZE,
305                         (void **)&checker) == 0) {
306     return -1;
307   }
308   if(checker->flags & NP_RUNNING) {
309     checker->flags |= NP_KILLED;
310     return 0;
311   }
312   if(checker->fire_event) {
313      eventer_remove(checker->fire_event);
314      eventer_free(checker->fire_event);
315      checker->fire_event = NULL;
316   }
317   noit_hash_delete(&polls, (char *)in, UUID_SIZE, free, free);
318
319   if(checker->target) free(checker->target);
320   if(checker->module) free(checker->module);
321   if(checker->name) free(checker->name);
322   if(checker->config) {
323     noit_hash_destroy(checker->config, free, free);
324     free(checker->config);
325     checker->config = NULL;
326   }
327   free(checker);
328   return 0;
329 }
330
331 noit_check_t *
332 noit_poller_lookup(uuid_t in) {
333   noit_check_t *check;
334   if(noit_hash_retrieve(&polls,
335                         (char *)in, UUID_SIZE,
336                         (void **)&check)) {
337     return check;
338   }
339   return NULL;
340 }
341 noit_check_t *
342 noit_poller_lookup_by_name(char *target, char *name) {
343   noit_check_t *check, *tmp_check;
344   tmp_check = calloc(1, sizeof(*tmp_check));
345   tmp_check->target = target;
346   tmp_check->name = name;
347   check = noit_skiplist_find(&polls_by_name, tmp_check, NULL);
348   free(tmp_check);
349   return check;
350 }
351
352 static void
353 __free_metric(void *vm) {
354   metric_t *m = vm;
355   free(m->metric_name);
356   if(m->metric_value.i) free(m->metric_value.i);
357 }
358
359 void
360 __stats_add_metric(stats_t *newstate, metric_t *m) {
361   noit_hash_replace(&newstate->metrics, m->metric_name, strlen(m->metric_name),
362                     m, NULL, __free_metric);
363 }
364
365 void
366 noit_stats_set_metric_int(stats_t *newstate, char *name, int *value) {
367   metric_t *m = calloc(1, sizeof(*m));
368   m->metric_name = strdup(name);
369   m->metric_type = METRIC_INT;
370   if(value) {
371     m->metric_value.i = malloc(sizeof(*value));
372     *(m->metric_value.i) = *value;
373   }
374   __stats_add_metric(newstate, m);
375 }
376
377 void
378 noit_stats_set_metric_float(stats_t *newstate, char *name, float *value) {
379   metric_t *m = calloc(1, sizeof(*m));
380   m->metric_name = strdup(name);
381   m->metric_type = METRIC_FLOAT;
382   if(value) {
383     m->metric_value.f = malloc(sizeof(*value));
384     *(m->metric_value.f) = *value;
385   }
386   __stats_add_metric(newstate, m);
387 }
388
389 void
390 noit_stats_set_metric_string(stats_t *newstate, char *name, char *value) {
391   metric_t *m = calloc(1, sizeof(*m));
392   m->metric_name = strdup(name);
393   m->metric_type = METRIC_STRING;
394   m->metric_value.s = value ? strdup(value) : NULL;
395   __stats_add_metric(newstate, m);
396 }
397
398 void
399 noit_check_set_stats(struct _noit_module *module,
400                      noit_check_t *check, stats_t *newstate) {
401   int report_change = 0;
402   dep_list_t *dep;
403   if(check->stats.previous.status)
404     free(check->stats.previous.status);
405   noit_hash_destroy(&check->stats.previous.metrics, NULL, __free_metric);
406   memcpy(&check->stats.previous, &check->stats.current, sizeof(stats_t));
407   memcpy(&check->stats.current, newstate, sizeof(stats_t));
408   if(check->stats.current.status)
409     check->stats.current.status = strdup(check->stats.current.status);
410
411   /* check for state changes */
412   if(check->stats.current.available != 0 &&
413      check->stats.previous.available != 0 &&
414      check->stats.current.available != check->stats.previous.available)
415     report_change = 1;
416   if(check->stats.current.state != 0 &&
417      check->stats.previous.state != 0 &&
418      check->stats.current.state != check->stats.previous.state)
419     report_change = 1;
420
421   noitL(noit_error, "%s`%s <- [%s]\n", check->target, check->name,
422         check->stats.current.status);
423   if(report_change) {
424     noitL(noit_error, "%s`%s -> [%s:%s]\n",
425           check->target, check->name,
426           __noit_check_available_string(check->stats.current.available),
427           __noit_check_state_string(check->stats.current.state));
428   }
429   for(dep = check->causal_checks; dep; dep = dep->next) {
430     noit_module_t *mod;
431     mod = noit_module_lookup(dep->check->module);
432     assert(mod);
433     noitL(noit_debug, "Firing %s`%s in response to %s`%s\n",
434           dep->check->target, dep->check->name,
435           check->target, check->name);
436     mod->initiate_check(mod, dep->check, 1, check);
437   }
438 }
439
440 static void
441 nc_printf_check_brief(noit_console_closure_t ncct,
442                       noit_check_t *check) {
443   char out[512];
444   char uuid_str[37];
445   snprintf(out, sizeof(out), "%s`%s", check->target, check->name);
446   uuid_unparse_lower(check->checkid, uuid_str);
447   nc_printf(ncct, "%s %s\n", uuid_str, out);
448   if(check->stats.current.status)
449     nc_printf(ncct, "\t%s\n", check->stats.current.status);
450 }
451
452 static int
453 noit_console_show_checks(noit_console_closure_t ncct,
454                          int argc, char **argv,
455                          noit_console_state_t *dstate,
456                          void *closure) {
457   struct timeval _now;
458   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
459   uuid_t key_id;
460   int klen;
461   noit_check_t *check;
462
463   gettimeofday(&_now, NULL);
464   while(noit_hash_next(&polls, &iter, (const char **)key_id, &klen,
465                        (void **)&check)) {
466     nc_printf_check_brief(ncct, check);
467   }
468   return 0;
469 }
470 static void
471 register_console_check_commands() {
472   noit_console_state_t *tl;
473   cmd_info_t *showcmd;
474
475   tl = noit_console_state_initial();
476   showcmd = noit_console_state_get_cmd(tl, "show");
477   assert(showcmd && showcmd->dstate);
478
479   noit_console_state_add_cmd(showcmd->dstate,
480     NCSCMD("checks", noit_console_show_checks, NULL, NULL));
481 }
482
Note: See TracBrowser for help on using the browser.