root/src/noit_check_tools.c

Revision cf168a0bcc66e128c10882a0a604b67fe82012f7, 12.7 kB (checked in by Philip Maddox <pmaddox@circonus.com>, 6 months ago)

Report NULL Values Coming Back In JSON Documents

There was a bug where if you had JSON in a "_type"/"_value" format
and the value was NULL, this value would not be returned as a metric.
Changed it so that it will be returned as a metric.

  • 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 void
61 noit_check_recur_name_details(char *buf, int buflen,
62                               eventer_t e, void *closure) {
63   char id_str[UUID_STR_LEN+1];
64   recur_closure_t *rcl = e ? e->closure : NULL;
65   if(!e) {
66     snprintf(buf, buflen, "noit_check_recur_handler");
67     return;
68   }
69   uuid_unparse_lower(rcl->check->checkid, id_str);
70   snprintf(buf, buflen, "fire(%s)", id_str);
71   return;
72 }
73 static int
74 noit_check_recur_handler(eventer_t e, int mask, void *closure,
75                               struct timeval *now) {
76   recur_closure_t *rcl = closure;
77   int ms;
78   rcl->check->fire_event = NULL; /* This is us, we get free post-return */
79   noit_check_resolve(rcl->check);
80   ms = noit_check_schedule_next(rcl->self, NULL, rcl->check, now,
81                                 rcl->dispatch, NULL);
82   if(NOIT_CHECK_RESOLVED(rcl->check)) {
83     if(NOIT_HOOK_CONTINUE ==
84        check_preflight_hook_invoke(rcl->self, rcl->check, rcl->cause)) {
85       if(NOIT_CHECK_DISPATCH_ENABLED()) {
86         char id[UUID_STR_LEN+1];
87         uuid_unparse_lower(rcl->check->checkid, id);
88         NOIT_CHECK_DISPATCH(id, rcl->check->module, rcl->check->name,
89                             rcl->check->target);
90       }
91       if(ms < rcl->check->timeout && !(rcl->check->flags & NP_TRANSIENT))
92         noitL(noit_error, "%s might not finish in %dms (timeout %dms)\n",
93               rcl->check->name, ms, rcl->check->timeout);
94       rcl->dispatch(rcl->self, rcl->check, rcl->cause);
95     }
96     check_postflight_hook_invoke(rcl->self, rcl->check, rcl->cause);
97   }
98   else
99     noitL(noit_debug, "skipping %s`%s`%s, unresolved\n",
100           rcl->check->target, rcl->check->module, rcl->check->name);
101   free(rcl);
102   return 0;
103 }
104
105 int
106 noit_check_schedule_next(noit_module_t *self,
107                          struct timeval *last_check, noit_check_t *check,
108                          struct timeval *now, dispatch_func_t dispatch,
109                          noit_check_t *cause) {
110   eventer_t newe;
111   struct timeval period, earliest, diff;
112   int64_t diffms, periodms, offsetms;
113   recur_closure_t *rcl;
114   int initial = last_check ? 1 : 0;
115
116   assert(cause == NULL);
117   assert(check->fire_event == NULL);
118   if(check->period == 0) return 0;
119
120   /* if last_check is not passed, we use the initial_schedule_time
121    * otherwise, we set the initial_schedule_time
122    */
123   if(!last_check) last_check = &check->initial_schedule_time;
124   else memcpy(&check->initial_schedule_time, last_check, sizeof(*last_check));
125
126   if(NOIT_CHECK_DISABLED(check) || NOIT_CHECK_KILLED(check)) {
127     if(!(check->flags & NP_TRANSIENT)) check_slots_dec_tv(last_check);
128     memset(&check->initial_schedule_time, 0, sizeof(struct timeval));
129     return 0;
130   }
131
132   /* If we have an event, we know when we intended it to fire.  This means
133    * we should schedule that point + period.
134    */
135   if(now)
136     memcpy(&earliest, now, sizeof(earliest));
137   else
138     gettimeofday(&earliest, NULL);
139
140   /* If the check is unconfigured and needs resolving, we'll set the
141    * period down a bit lower so we can pick up the resolution quickly.
142    * The one exception is if this is the initial run.
143    */
144   if(!initial &&
145      !NOIT_CHECK_RESOLVED(check) && NOIT_CHECK_SHOULD_RESOLVE(check) &&
146      check->period > 1000) {
147     period.tv_sec = 1;
148     period.tv_usec = 0;
149   }
150   else {
151     period.tv_sec = check->period / 1000;
152     period.tv_usec = (check->period % 1000) * 1000;
153   }
154   periodms = period.tv_sec * 1000 + period.tv_usec / 1000;
155
156   newe = eventer_alloc();
157   /* calculate the differnet between the initial schedule time and "now" */
158   if(compare_timeval(earliest, *last_check) >= 0) {
159     sub_timeval(earliest, *last_check, &diff);
160     diffms = (int64_t)diff.tv_sec * 1000 + diff.tv_usec / 1000;
161   }
162   else {
163     noitL(noit_error, "time is going backwards. abort.\n");
164     abort();
165   }
166   /* determine the offset from initial schedule time that would place
167    * us at the next period-aligned point past "now" */
168   offsetms = ((diffms / periodms) + 1) * periodms;
169   diff.tv_sec = offsetms / 1000;
170   diff.tv_usec = (offsetms % 1000) * 1000;
171
172   memcpy(&newe->whence, last_check, sizeof(*last_check));
173   add_timeval(newe->whence, diff, &newe->whence);
174
175   sub_timeval(newe->whence, earliest, &diff);
176   diffms = (int64_t)diff.tv_sec * 1000 + (int)diff.tv_usec / 1000;
177   assert(compare_timeval(newe->whence, earliest) > 0);
178   newe->mask = EVENTER_TIMER;
179   newe->callback = noit_check_recur_handler;
180   rcl = calloc(1, sizeof(*rcl));
181   rcl->self = self;
182   rcl->check = check;
183   rcl->cause = cause;
184   rcl->dispatch = dispatch;
185   newe->closure = rcl;
186
187   eventer_add(newe);
188   check->fire_event = newe;
189   return diffms;
190 }
191
192 void
193 noit_check_run_full_asynch_opts(noit_check_t *check, eventer_func_t callback,
194                                 int mask) {
195   struct timeval __now, p_int;
196   eventer_t e;
197   e = eventer_alloc();
198   e->fd = -1;
199   e->mask = EVENTER_ASYNCH | mask;
200   gettimeofday(&__now, NULL);
201   memcpy(&e->whence, &__now, sizeof(__now));
202   p_int.tv_sec = check->timeout / 1000;
203   p_int.tv_usec = (check->timeout % 1000) * 1000;
204   add_timeval(e->whence, p_int, &e->whence);
205   e->callback = callback;
206   e->closure =  check->closure;
207   eventer_add(e);
208 }
209 void
210 noit_check_run_full_asynch(noit_check_t *check, eventer_func_t callback) {
211   noit_check_run_full_asynch_opts(check, callback,
212                                   EVENTER_DEFAULT_ASYNCH_ABORT);
213 }
214
215 void
216 noit_check_tools_init() {
217   noit_check_tools_shared_init();
218   eventer_name_callback_ext("noit_check_recur_handler",
219                             noit_check_recur_handler,
220                             noit_check_recur_name_details, NULL);
221 }
222
223 static int
224 populate_stats_from_resmon_formatted_json(noit_check_t *check,
225                                           stats_t *s, struct json_object *o,
226                                           const char *prefix) {
227   int count = 0;
228   char keybuff[256];
229 #define MKKEY(fmt, arg) do { \
230   if(prefix) snprintf(keybuff, sizeof(keybuff), "%s`" fmt, prefix, arg); \
231   else snprintf(keybuff, sizeof(keybuff), fmt, arg); \
232 } while(0)
233   if(o == NULL) {
234     if(prefix) {
235       noit_stats_set_metric(check, s, prefix, METRIC_STRING, NULL);
236       count++;
237     }
238     return count;
239   }
240   switch(json_object_get_type(o)) {
241     /* sub callers */
242     case json_type_array:
243     {
244       int i, alen = json_object_array_length(o);
245       for(i=0;i<alen;i++) {
246         struct json_object *item = json_object_array_get_idx(o, i);
247         MKKEY("%d", i);
248         count += populate_stats_from_resmon_formatted_json(check, s, item, keybuff);
249       }
250     }
251     break;
252     case json_type_object:
253     {
254       struct jl_lh_table *lh;
255       struct jl_lh_entry *el;
256       struct json_object *has_type = NULL, *has_value = NULL;
257       lh = json_object_get_object(o);
258       jl_lh_foreach(lh, el) {
259         if(!strcmp(el->k, "_type")) has_type = (struct json_object *)el->v;
260         else if(!strcmp(el->k, "_value")) has_value = (struct json_object *)el->v;
261         else {
262           struct json_object *item = (struct json_object *)el->v;
263           MKKEY("%s", (const char *)el->k);
264           count += populate_stats_from_resmon_formatted_json(check, s, item, keybuff);
265         }
266       }
267       if(prefix && has_type &&
268          json_object_is_type(has_type, json_type_string)) {
269         const char *type_str = json_object_get_string(has_type);
270
271 #define COERCE_JSON_OBJECT(type, item) do { \
272   const char *value_str = NULL; \
273   if(json_object_is_type(item, json_type_string)) \
274     value_str = json_object_get_string(item); \
275   else if(!json_object_is_type(item, json_type_null)) \
276     value_str = json_object_to_json_string(item); \
277   switch(type) { \
278     case METRIC_INT32: case METRIC_UINT32: case METRIC_INT64: \
279     case METRIC_UINT64: case METRIC_DOUBLE: case METRIC_STRING: \
280       noit_stats_set_metric_coerce(check, s, prefix, \
281                                    (metric_type_t)type, value_str); \
282       count++; \
283     default: \
284       break; \
285   } \
286 } while(0)
287
288
289         if (has_value == NULL) {
290           noit_stats_set_metric_coerce(check, s, prefix, (metric_type_t)*type_str, NULL);
291           count++;
292         }
293         else if(json_object_is_type(has_value, json_type_array)) {
294           int i, alen = json_object_array_length(has_value);
295           for(i=0;i<alen;i++) {
296             struct json_object *item = json_object_array_get_idx(has_value, i);
297             COERCE_JSON_OBJECT(*type_str, item);
298           }
299         }
300         else {
301           COERCE_JSON_OBJECT(*type_str, has_value);
302         }
303       }
304       break;
305     }
306
307     /* directs */
308     case json_type_string:
309       if(prefix) {
310         noit_stats_set_metric(check, s, prefix, METRIC_GUESS,
311                               (char *)json_object_get_string(o));
312         count++;
313       }
314       break;
315     case json_type_boolean:
316       if(prefix) {
317         int val = json_object_get_boolean(o) ? 1 : 0;
318         noit_stats_set_metric(check, s, prefix, METRIC_INT32, &val);
319         count++;
320       }
321       break;
322     case json_type_null:
323       if(prefix) {
324         noit_stats_set_metric(check, s, prefix, METRIC_STRING, NULL);
325         count++;
326       }
327       break;
328     case json_type_double:
329       if(prefix) {
330         double val = json_object_get_double(o);
331         noit_stats_set_metric(check, s, prefix, METRIC_DOUBLE, &val);
332         count++;
333       }
334       break;
335     case json_type_int:
336       if(prefix) {
337         int64_t i64;
338         uint64_t u64;
339         switch(json_object_get_int_overflow(o)) {
340           case json_overflow_int:
341             i64 = json_object_get_int(o);
342             noit_stats_set_metric(check, s, prefix, METRIC_INT64, &i64);
343             count++;
344             break;
345           case json_overflow_int64:
346             i64 = json_object_get_int64(o);
347             noit_stats_set_metric(check, s, prefix, METRIC_INT64, &i64);
348             count++;
349             break;
350           case json_overflow_uint64:
351             u64 = json_object_get_uint64(o);
352             noit_stats_set_metric(check, s, prefix, METRIC_UINT64, &u64);
353             count++;
354             break;
355         }
356       }
357   }
358   return count;
359 }
360 int
361 noit_check_stats_from_json_str(noit_check_t *check, stats_t *s,
362                                const char *json_str, int len) {
363   int rv = -1;
364   struct json_tokener *tok = NULL;
365   struct json_object *root = NULL;
366   tok = json_tokener_new();
367   root = json_tokener_parse_ex(tok, json_str, len);
368   if(root) rv = populate_stats_from_resmon_formatted_json(check, s, root, NULL);
369   if(tok) json_tokener_free(tok);
370   if(root) json_object_put(root);
371   return rv;
372 }
Note: See TracBrowser for help on using the browser.