root/src/modules/httptrap.c

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

propagate the cause for causal checks into all of the calls

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2011, 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 #include "noit_defines.h"
33
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <math.h>
39 #include <ctype.h>
40
41 #include "noit_module.h"
42 #include "noit_check.h"
43 #include "noit_check_tools.h"
44 #include "noit_rest.h"
45 #include "json-lib/json.h"
46 #include "utils/noit_log.h"
47 #include "utils/noit_hash.h"
48
49
50 static noit_log_stream_t nlerr = NULL;
51 static noit_log_stream_t nldeb = NULL;
52
53 typedef struct _mod_config {
54   noit_hash_table *options;
55   noit_boolean asynch_metrics;
56 } httptrap_mod_config_t;
57
58 typedef struct httptrap_closure_s {
59   noit_module_t *self;
60   stats_t current;
61   int stats_count;
62 } httptrap_closure_t;
63
64 struct rest_json_payload {
65   struct json_tokener *tok;
66   struct json_object *root;
67   int len;
68   int complete;
69   char *error;
70   int nput;
71 };
72
73 static void
74 rest_json_payload_free(void *f) {
75   struct rest_json_payload *json = f;
76   if(json->tok) json_tokener_free(json->tok);
77   if(json->root) json_object_put(json->root);
78   if(json->error) free(json->error);
79   free(json);
80 }
81
82 static struct rest_json_payload *
83 rest_get_json_upload(noit_http_rest_closure_t *restc,
84                     int *mask, int *complete) {
85   struct rest_json_payload *rxc;
86   noit_http_request *req = noit_http_session_request(restc->http_ctx);
87   int content_length;
88   char buffer[32768];
89
90   content_length = noit_http_request_content_length(req);
91   if(restc->call_closure == NULL) {
92     rxc = restc->call_closure = calloc(1, sizeof(*rxc));
93     rxc->tok = json_tokener_new();
94     restc->call_closure_free = rest_json_payload_free;
95   }
96   rxc = restc->call_closure;
97   while(!rxc->complete) {
98     int len;
99     len = noit_http_session_req_consume(
100             restc->http_ctx, buffer,
101             MIN(content_length - rxc->len, sizeof(buffer)),
102             mask);
103     if(len > 0) {
104       struct json_object *o;
105       o = json_tokener_parse_ex(rxc->tok, buffer, len);
106       rxc->len += len;
107       if(!is_error(o)) {
108         rxc->root = o;
109       }
110     }
111     if(len < 0 && errno == EAGAIN) return NULL;
112     else if(len < 0) {
113       *complete = 1;
114       return NULL;
115     }
116     if(rxc->len == content_length) {
117       rxc->complete = 1;
118     }
119   }
120
121   *complete = 1;
122   return rxc;
123 }
124
125 static noit_boolean
126 noit_httptrap_check_aynsch(noit_module_t *self,
127                            noit_check_t *check) {
128   const char *config_val;
129   httptrap_mod_config_t *conf = noit_module_get_userdata(self);
130   noit_boolean is_asynch = conf->asynch_metrics;
131   if(noit_hash_retr_str(check->config,
132                         "asynch_metrics", strlen("asynch_metrics"),
133                         (const char **)&config_val)) {
134     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
135       is_asynch = noit_false;
136   }
137
138   if(is_asynch) check->flags |= NP_SUPPRESS_METRICS;
139   else check->flags &= ~NP_SUPPRESS_METRICS;
140   return is_asynch;
141 }
142
143 static void clear_closure(httptrap_closure_t *ccl) {
144   ccl->stats_count = 0;
145   noit_check_stats_clear(&ccl->current);
146 }
147
148 static int httptrap_submit(noit_module_t *self, noit_check_t *check,
149                            noit_check_t *cause) {
150   httptrap_closure_t *ccl;
151   struct timeval duration;
152   /* We are passive, so we don't do anything for transient checks */
153   if(check->flags & NP_TRANSIENT) return 0;
154
155   noit_httptrap_check_aynsch(self, check);
156   if(!check->closure) {
157     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
158     memset(ccl, 0, sizeof(httptrap_closure_t));
159   } else {
160     // Don't count the first run
161     char human_buffer[256];
162     ccl = (httptrap_closure_t*)check->closure;
163     gettimeofday(&ccl->current.whence, NULL);
164     sub_timeval(ccl->current.whence, check->last_fire_time, &duration);
165     ccl->current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
166
167     snprintf(human_buffer, sizeof(human_buffer),
168              "dur=%d,run=%d,stats=%d", ccl->current.duration,
169              check->generation, ccl->stats_count);
170     noitL(nldeb, "httptrap(%s) [%s]\n", check->target, human_buffer);
171
172     // Not sure what to do here
173     ccl->current.available = (ccl->stats_count > 0) ?
174         NP_AVAILABLE : NP_UNAVAILABLE;
175     ccl->current.state = (ccl->stats_count > 0) ?
176         NP_GOOD : NP_BAD;
177     ccl->current.status = human_buffer;
178     if(check->last_fire_time.tv_sec)
179       noit_check_passive_set_stats(self, check, &ccl->current);
180
181     memcpy(&check->last_fire_time, &ccl->current.whence, sizeof(duration));
182   }
183   clear_closure(ccl);
184   return 0;
185 }
186
187 static int
188 json_parse_descent(noit_check_t *check, noit_boolean immediate,
189                    json_object *o, char *key) {
190   char subkey[256];
191   httptrap_closure_t *ccl;
192   int cnt = 0;
193
194 #define setstat(key, mt, v) do { \
195   cnt++; \
196   noit_stats_set_metric(&ccl->current, key, mt, v); \
197   if(immediate) noit_stats_log_immediate_metric(check, key, mt, v); \
198 } while(0)
199
200   ccl = check->closure;
201   switch(json_object_get_type(o)) {
202     case json_type_array: {
203         int i, alen = json_object_array_length(o);
204         for(i=0;i<alen;i++) {
205           snprintf(subkey, sizeof(subkey), "%s%s%d", key ? key : "",
206                    (key && *key) ? "`" : "", i);
207           cnt += json_parse_descent(check, immediate,
208                                     json_object_array_get_idx(o,i), subkey);
209         }
210       }
211       break;
212
213     case json_type_object: {
214         char *ekey;
215         struct lh_table *table;
216         struct lh_entry *entry;
217         struct json_object *val;
218         table = json_object_get_object(o);
219         if(table->count == 2) {
220           /* this is the special key: { _type: , _value: } notation */
221           json_object *type;
222           type = json_object_object_get(o, "_type");
223           val = json_object_object_get(o, "_value");
224           if(type && json_object_is_type(type, json_type_string) &&
225              val && (json_object_is_type(val, json_type_string) ||
226                      json_object_is_type(val, json_type_null))) {
227             const char *type_str = json_object_get_string(type);
228             const char *val_str = json_object_is_type(val, json_type_null) ? NULL : json_object_get_string(val);
229             if(type_str[1] == '\0') {
230               int32_t __i, *i = &__i;
231               u_int32_t __I, *I = &__I;
232               int64_t __l, *l = &__l;
233               u_int64_t __L, *L = &__L;
234               double __n, *n = &__n;
235               if(val_str == NULL)
236                 i = NULL, I = NULL, l = NULL, L = NULL, n = NULL;
237               switch(*type_str) {
238                 case 'i': if(val_str) __i = strtol(val_str, NULL, 10);
239                           setstat(key, METRIC_INT32, i); break;
240                 case 'I': if(val_str) __I = strtoul(val_str, NULL, 10);
241                           setstat(key, METRIC_UINT32, I); break;
242                 case 'l': if(val_str) __l = strtoll(val_str, NULL, 10);
243                           setstat(key, METRIC_INT64, I); break;
244                 case 'L': if(val_str) __L = strtoull(val_str, NULL, 10);
245                           setstat(key, METRIC_UINT64, I); break;
246                 case 'n': if(val_str) __n = strtod(val_str, NULL);
247                           setstat(key, METRIC_DOUBLE, n); break;
248                 case 's': setstat(key, METRIC_STRING, (void *)val_str); break;
249                 default: break;
250               }
251             }
252             break;
253           }
254         }
255         for(entry = table->head;
256             (entry ? (ekey = (char*)entry->k,
257                val = (struct json_object*)entry->v, entry) : 0);
258             entry = entry->next) {
259           snprintf(subkey, sizeof(subkey), "%s%s%s", key ? key : "",
260                    (key && *key) ? "`" : "", ekey);
261           cnt += json_parse_descent(check, immediate, val, subkey);
262         }
263       }
264       break;
265
266     case json_type_null: {
267         if(!key || !*key) break;
268         break;
269       }
270       break;
271     case json_type_boolean: {
272         if(!key || !*key) break;
273         int32_t value = json_object_get_boolean(o) ? 1 : 0;
274         setstat(key, METRIC_INT32, &value);
275       }
276       break;
277     case json_type_double: {
278         if(!key || !*key) break;
279         double value = json_object_get_double(o);
280         setstat(key, METRIC_DOUBLE, &value);
281       }
282       break;
283     case json_type_int: {
284       if(!key || !*key) break;
285       int32_t value = json_object_get_int(o);
286       setstat(key, METRIC_INT32, &value);
287     }
288     case json_type_string: {
289         if(!key || !*key) break;
290         const char *val = json_object_get_string(o);
291         setstat(key, METRIC_GUESS, (void *)val);
292       }
293       break;
294   }
295   return cnt;
296 }
297 static int
298 push_payload_at_check(noit_check_t *check, json_object *root) {
299   httptrap_closure_t *ccl;
300   noit_boolean immediate;
301   char key[256];
302   int cnt;
303
304   if (check->closure == NULL) return 0;
305   ccl = check->closure;
306   if (!check || strcmp(check->module, "httptrap")) return 0;
307   immediate = noit_httptrap_check_aynsch(ccl->self,check);
308
309   /* do it here */
310   key[0] = '\0';
311   cnt = json_parse_descent(check, immediate, root, key);
312   ccl->stats_count += cnt;
313   return cnt;
314 }
315
316 static int
317 rest_httptrap_handler(noit_http_rest_closure_t *restc,
318                       int npats, char **pats) {
319   int mask, complete = 0, cnt;
320   struct rest_json_payload *rxc = NULL;
321   const char *error = "internal error", *secret = NULL;
322   noit_http_session_ctx *ctx = restc->http_ctx;
323   char json_out[128];
324   noit_check_t *check;
325   uuid_t check_id;
326
327   if(npats != 2) {
328     error = "bad uri";
329     goto error;
330   }
331   if(uuid_parse(pats[0], check_id)) {
332     error = "uuid parse error";
333     goto error;
334   }
335
336   rxc = rest_get_json_upload(restc, &mask, &complete);
337   if(rxc == NULL && !complete) return mask;
338
339   check = noit_poller_lookup(check_id);
340   if(!check || strcmp(check->module, "httptrap")) {
341     error = "no such httptrap check";
342     goto error;
343   }
344   noit_hash_retr_str(check->config, "secret", strlen("secret"), &secret);
345   if(!secret) secret = "";
346   if(strcmp(pats[1], secret)) {
347     error = "secret mismatch";
348     goto error;
349   }
350
351   if(!rxc) goto error;
352   if(!rxc->root) {
353     error = "parse failure";
354     goto error;
355   }
356   if(rxc->error) goto error;
357
358   cnt = push_payload_at_check(check, rxc->root);
359
360   noit_http_response_ok(ctx, "application/json");
361   snprintf(json_out, sizeof(json_out),
362            "{ \"stats\": %d }", cnt);
363   noit_http_response_append(ctx, json_out, strlen(json_out));
364   noit_http_response_end(ctx);
365   return 0;
366
367  error:
368   noit_http_response_server_error(ctx, "application/json");
369   noit_http_response_append(ctx, "{ error: \"", 10);
370   if(rxc && rxc->error) error = rxc->error;
371   noit_http_response_append(ctx, error, strlen(error));
372   noit_http_response_append(ctx, "\" }", 3);
373   noit_http_response_end(ctx);
374   return 0;
375 }
376
377 static int noit_httptrap_initiate_check(noit_module_t *self,
378                                         noit_check_t *check,
379                                         int once, noit_check_t *cause) {
380   if (check->closure == NULL) {
381     httptrap_closure_t *ccl;
382     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
383     ccl->self = self;
384   }
385   INITIATE_CHECK(httptrap_submit, self, check, cause);
386   return 0;
387 }
388
389 static int noit_httptrap_config(noit_module_t *self, noit_hash_table *options) {
390   httptrap_mod_config_t *conf;
391   conf = noit_module_get_userdata(self);
392   if(conf) {
393     if(conf->options) {
394       noit_hash_destroy(conf->options, free, free);
395       free(conf->options);
396     }
397   }
398   else
399     conf = calloc(1, sizeof(*conf));
400   conf->options = options;
401   noit_module_set_userdata(self, conf);
402   return 1;
403 }
404
405 static int noit_httptrap_onload(noit_image_t *self) {
406   if(!nlerr) nlerr = noit_log_stream_find("error/httptrap");
407   if(!nldeb) nldeb = noit_log_stream_find("debug/httptrap");
408   if(!nlerr) nlerr = noit_error;
409   if(!nldeb) nldeb = noit_debug;
410   return 0;
411 }
412
413 static int noit_httptrap_init(noit_module_t *self) {
414   const char *config_val;
415   int sockaddr_len;
416   httptrap_mod_config_t *conf;
417   conf = noit_module_get_userdata(self);
418
419   conf->asynch_metrics = noit_true;
420   if(noit_hash_retr_str(conf->options,
421                         "asynch_metrics", strlen("asynch_metrics"),
422                         (const char **)&config_val)) {
423     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
424       conf->asynch_metrics = noit_false;
425   }
426
427   noit_module_set_userdata(self, conf);
428
429   /* register rest handler */
430   noit_http_rest_register("PUT", "/module/httptrap/",
431                           "^(" UUID_REGEX ")/([^/]*)$",
432                           rest_httptrap_handler);
433   return 0;
434 }
435
436 #include "httptrap.xmlh"
437 noit_module_t httptrap = {
438   {
439     NOIT_MODULE_MAGIC,
440     NOIT_MODULE_ABI_VERSION,
441     "httptrap",
442     "httptrap collection",
443     httptrap_xml_description,
444     noit_httptrap_onload
445   },
446   noit_httptrap_config,
447   noit_httptrap_init,
448   noit_httptrap_initiate_check,
449   NULL
450 };
Note: See TracBrowser for help on using the browser.