root/src/noit_check_tools.c

Revision 1dbb45b9deca8465c32394889ee40088e6297634, 11.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

If the period is less than 60000, it is possible to schedule the
"last check" (as faked at boot) to be in the future, which then
causes a negative roll in the calculation of the offset causing
the next check to be scheduled sometime after the death of our sun:

Sun Jul 24 08:13:48 584556061

We should handle this case and appropriately adjust the faked last
fire time to be in the past by iteratively subtracting period until
we get there.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include "dtrace_probes.h"
35 #include "noit_check_tools.h"
36 #include "noit_check_tools_shared.h"
37 #include "utils/noit_str.h"
38 #include "json-lib/json.h"
39
40 #include <assert.h>
41
42 NOIT_HOOK_IMPL(check_preflight,
43   (noit_module_t *self, noit_check_t *check, noit_check_t *cause),
44   void *, closure,
45   (void *closure, noit_module_t *self, noit_check_t *check, noit_check_t *cause),
46   (closure,self,check,cause))
47 NOIT_HOOK_IMPL(check_postflight,
48   (noit_module_t *self, noit_check_t *check, noit_check_t *cause),
49   void *, closure,
50   (void *closure, noit_module_t *self, noit_check_t *check, noit_check_t *cause),
51   (closure,self,check,cause))
52
53 typedef struct {
54   noit_module_t *self;
55   noit_check_t *check;
56   noit_check_t *cause;
57   dispatch_func_t dispatch;
58 } recur_closure_t;
59
60 static int
61 noit_check_recur_handler(eventer_t e, int mask, void *closure,
62                               struct timeval *now) {
63   recur_closure_t *rcl = closure;
64   int ms;
65   rcl->check->fire_event = NULL; /* This is us, we get free post-return */
66   noit_check_resolve(rcl->check);
67   ms = noit_check_schedule_next(rcl->self, NULL, rcl->check, now,
68                                 rcl->dispatch, NULL);
69   if(NOIT_CHECK_RESOLVED(rcl->check)) {
70     if(NOIT_HOOK_CONTINUE ==
71        check_preflight_hook_invoke(rcl->self, rcl->check, rcl->cause)) {
72       if(NOIT_CHECK_DISPATCH_ENABLED()) {
73         char id[UUID_STR_LEN+1];
74         uuid_unparse_lower(rcl->check->checkid, id);
75         NOIT_CHECK_DISPATCH(id, rcl->check->module, rcl->check->name,
76                             rcl->check->target);
77       }
78       if(ms < rcl->check->timeout && !(rcl->check->flags & NP_TRANSIENT))
79         noitL(noit_error, "%s might not finish in %dms (timeout %dms)\n",
80               rcl->check->name, ms, rcl->check->timeout);
81       rcl->dispatch(rcl->self, rcl->check, rcl->cause);
82     }
83     check_postflight_hook_invoke(rcl->self, rcl->check, rcl->cause);
84   }
85   else
86     noitL(noit_debug, "skipping %s`%s`%s, unresolved\n",
87           rcl->check->target, rcl->check->module, rcl->check->name);
88   free(rcl);
89   return 0;
90 }
91
92 int
93 noit_check_schedule_next(noit_module_t *self,
94                          struct timeval *last_check, noit_check_t *check,
95                          struct timeval *now, dispatch_func_t dispatch,
96                          noit_check_t *cause) {
97   eventer_t newe;
98   struct timeval period, earliest, diff;
99   int64_t diffms, periodms, offsetms;
100   recur_closure_t *rcl;
101   int initial = last_check ? 1 : 0;
102
103   assert(cause == NULL);
104   assert(check->fire_event == NULL);
105   if(check->period == 0) return 0;
106
107   /* if last_check is not passed, we use the initial_schedule_time
108    * otherwise, we set the initial_schedule_time
109    */
110   if(!last_check) last_check = &check->initial_schedule_time;
111   else memcpy(&check->initial_schedule_time, last_check, sizeof(*last_check));
112
113   if(NOIT_CHECK_DISABLED(check) || NOIT_CHECK_KILLED(check)) {
114     if(!(check->flags & NP_TRANSIENT)) check_slots_dec_tv(last_check);
115     memset(&check->initial_schedule_time, 0, sizeof(struct timeval));
116     return 0;
117   }
118
119   /* If we have an event, we know when we intended it to fire.  This means
120    * we should schedule that point + period.
121    */
122   if(now)
123     memcpy(&earliest, now, sizeof(earliest));
124   else
125     gettimeofday(&earliest, NULL);
126
127   /* If the check is unconfigured and needs resolving, we'll set the
128    * period down a bit lower so we can pick up the resolution quickly.
129    * The one exception is if this is the initial run.
130    */
131   if(!initial &&
132      !NOIT_CHECK_RESOLVED(check) && NOIT_CHECK_SHOULD_RESOLVE(check) &&
133      check->period > 1000) {
134     period.tv_sec = 1;
135     period.tv_usec = 0;
136   }
137   else {
138     period.tv_sec = check->period / 1000;
139     period.tv_usec = (check->period % 1000) * 1000;
140   }
141   periodms = period.tv_sec * 1000 + period.tv_usec / 1000;
142
143   newe = eventer_alloc();
144   /* calculate the differnet between the initial schedule time and "now" */
145   if(compare_timeval(earliest, *last_check) >= 0) {
146     sub_timeval(earliest, *last_check, &diff);
147     diffms = diff.tv_sec * 1000 + diff.tv_usec / 1000;
148   }
149   else {
150     noitL(noit_error, "time is going backwards. abort.\n");
151     abort();
152   }
153   /* determine the offset from initial schedule time that would place
154    * us at the next period-aligned point past "now" */
155   offsetms = ((diffms / periodms) + 1) * periodms;
156   diff.tv_sec = offsetms / 1000;
157   diff.tv_usec = (offsetms % 1000) * 1000;
158
159   memcpy(&newe->whence, last_check, sizeof(*last_check));
160   add_timeval(newe->whence, diff, &newe->whence);
161
162   sub_timeval(newe->whence, earliest, &diff);
163   diffms = (int)diff.tv_sec * 1000 + (int)diff.tv_usec / 1000;
164   assert(compare_timeval(newe->whence, earliest) > 0);
165   newe->mask = EVENTER_TIMER;
166   newe->callback = noit_check_recur_handler;
167   rcl = calloc(1, sizeof(*rcl));
168   rcl->self = self;
169   rcl->check = check;
170   rcl->cause = cause;
171   rcl->dispatch = dispatch;
172   newe->closure = rcl;
173
174   eventer_add(newe);
175   check->fire_event = newe;
176   return diffms;
177 }
178
179 void
180 noit_check_run_full_asynch_opts(noit_check_t *check, eventer_func_t callback,
181                                 int mask) {
182   struct timeval __now, p_int;
183   eventer_t e;
184   e = eventer_alloc();
185   e->fd = -1;
186   e->mask = EVENTER_ASYNCH | mask;
187   gettimeofday(&__now, NULL);
188   memcpy(&e->whence, &__now, sizeof(__now));
189   p_int.tv_sec = check->timeout / 1000;
190   p_int.tv_usec = (check->timeout % 1000) * 1000;
191   add_timeval(e->whence, p_int, &e->whence);
192   e->callback = callback;
193   e->closure =  check->closure;
194   eventer_add(e);
195 }
196 void
197 noit_check_run_full_asynch(noit_check_t *check, eventer_func_t callback) {
198   noit_check_run_full_asynch_opts(check, callback,
199                                   EVENTER_DEFAULT_ASYNCH_ABORT);
200 }
201
202 void
203 noit_check_tools_init() {
204   noit_check_tools_shared_init();
205   eventer_name_callback("noit_check_recur_handler", noit_check_recur_handler);
206 }
207
208 static int
209 populate_stats_from_resmon_formatted_json(noit_check_t *check,
210                                           stats_t *s, struct json_object *o,
211                                           const char *prefix) {
212   int count = 0;
213   char keybuff[256];
214 #define MKKEY(fmt, arg) do { \
215   if(prefix) snprintf(keybuff, sizeof(keybuff), "%s`" fmt, prefix, arg); \
216   else snprintf(keybuff, sizeof(keybuff), fmt, arg); \
217 } while(0)
218   if(o == NULL) {
219     if(prefix) {
220       noit_stats_set_metric(check, s, prefix, METRIC_STRING, NULL);
221       count++;
222     }
223     return count;
224   }
225   switch(json_object_get_type(o)) {
226     /* sub callers */
227     case json_type_array:
228     {
229       int i, alen = json_object_array_length(o);
230       for(i=0;i<alen;i++) {
231         struct json_object *item = json_object_array_get_idx(o, i);
232         MKKEY("%d", i);
233         count += populate_stats_from_resmon_formatted_json(check, s, item, keybuff);
234       }
235     }
236     break;
237     case json_type_object:
238     {
239       struct lh_table *lh;
240       struct lh_entry *el;
241       struct json_object *has_type = NULL, *has_value = NULL;
242       lh = json_object_get_object(o);
243       lh_foreach(lh, el) {
244         if(!strcmp(el->k, "_type")) has_type = (struct json_object *)el->v;
245         else if(!strcmp(el->k, "_value")) has_value = (struct json_object *)el->v;
246         else {
247           struct json_object *item = (struct json_object *)el->v;
248           MKKEY("%s", (const char *)el->k);
249           count += populate_stats_from_resmon_formatted_json(check, s, item, keybuff);
250         }
251       }
252       if(prefix && has_type && has_value &&
253          json_object_is_type(has_type, json_type_string) &&
254          json_object_is_type(has_value, json_type_string)) {
255         const char *type_str = json_object_get_string(has_type);
256         const char *value_str = json_object_get_string(has_value);
257         switch(*type_str) {
258           case METRIC_INT32:
259           case METRIC_UINT32:
260           case METRIC_INT64:
261           case METRIC_UINT64:
262           case METRIC_DOUBLE:
263           case METRIC_STRING:
264             noit_stats_set_metric_coerce(check, s, prefix,
265                                          (metric_type_t)*type_str, value_str);
266             count++;
267           default:
268             break;
269         }
270       }
271       break;
272     }
273
274     /* directs */
275     case json_type_string:
276       if(prefix) {
277         noit_stats_set_metric(check, s, prefix, METRIC_GUESS,
278                               (char *)json_object_get_string(o));
279         count++;
280       }
281       break;
282     case json_type_boolean:
283       if(prefix) {
284         int val = json_object_get_boolean(o) ? 1 : 0;
285         noit_stats_set_metric(check, s, prefix, METRIC_INT32, &val);
286         count++;
287       }
288       break;
289     case json_type_null:
290       if(prefix) {
291         noit_stats_set_metric(check, s, prefix, METRIC_STRING, NULL);
292         count++;
293       }
294       break;
295     case json_type_double:
296       if(prefix) {
297         double val = json_object_get_double(o);
298         noit_stats_set_metric(check, s, prefix, METRIC_DOUBLE, &val);
299         count++;
300       }
301       break;
302     case json_type_int:
303       if(prefix) {
304         int64_t i64;
305         uint64_t u64;
306         switch(json_object_get_int_overflow(o)) {
307           case json_overflow_int:
308             i64 = json_object_get_int(o);
309             noit_stats_set_metric(check, s, prefix, METRIC_INT64, &i64);
310             count++;
311             break;
312           case json_overflow_int64:
313             i64 = json_object_get_int64(o);
314             noit_stats_set_metric(check, s, prefix, METRIC_INT64, &i64);
315             count++;
316             break;
317           case json_overflow_uint64:
318             u64 = json_object_get_uint64(o);
319             noit_stats_set_metric(check, s, prefix, METRIC_UINT64, &u64);
320             count++;
321             break;
322         }
323       }
324   }
325   return count;
326 }
327 int
328 noit_check_stats_from_json_str(noit_check_t *check, stats_t *s,
329                                const char *json_str, int len) {
330   int rv = -1;
331   struct json_tokener *tok = NULL;
332   struct json_object *root = NULL;
333   tok = json_tokener_new();
334   root = json_tokener_parse_ex(tok, json_str, len);
335   if(root) rv = populate_stats_from_resmon_formatted_json(check, s, root, NULL);
336   if(tok) json_tokener_free(tok);
337   if(root) json_object_put(root);
338   return rv;
339 }
Note: See TracBrowser for help on using the browser.