root/src/modules/httptrap.c

Revision b98abc532cfb9d3411c5dcc28804a98da4d5725b, 14.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

fixes #362

  • 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   httptrap_closure_t *ccl;
150   struct timeval duration;
151   /* We are passive, so we don't do anything for transient checks */
152   if(check->flags & NP_TRANSIENT) return 0;
153
154   noit_httptrap_check_aynsch(self, check);
155   if(!check->closure) {
156     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
157     memset(ccl, 0, sizeof(httptrap_closure_t));
158   } else {
159     // Don't count the first run
160     char human_buffer[256];
161     ccl = (httptrap_closure_t*)check->closure;
162     gettimeofday(&ccl->current.whence, NULL);
163     sub_timeval(ccl->current.whence, check->last_fire_time, &duration);
164     ccl->current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
165
166     snprintf(human_buffer, sizeof(human_buffer),
167              "dur=%d,run=%d,stats=%d", ccl->current.duration,
168              check->generation, ccl->stats_count);
169     noitL(nldeb, "httptrap(%s) [%s]\n", check->target, human_buffer);
170
171     // Not sure what to do here
172     ccl->current.available = (ccl->stats_count > 0) ?
173         NP_AVAILABLE : NP_UNAVAILABLE;
174     ccl->current.state = (ccl->stats_count > 0) ?
175         NP_GOOD : NP_BAD;
176     ccl->current.status = human_buffer;
177     if(check->last_fire_time.tv_sec)
178       noit_check_passive_set_stats(self, check, &ccl->current);
179
180     memcpy(&check->last_fire_time, &ccl->current.whence, sizeof(duration));
181   }
182   clear_closure(ccl);
183   return 0;
184 }
185
186 static int
187 json_parse_descent(noit_check_t *check, noit_boolean immediate,
188                    json_object *o, char *key) {
189   char subkey[256];
190   httptrap_closure_t *ccl;
191   int cnt = 0;
192
193 #define setstat(key, mt, v) do { \
194   cnt++; \
195   noit_stats_set_metric(&ccl->current, key, mt, v); \
196   if(immediate) noit_stats_log_immediate_metric(check, key, mt, v); \
197 } while(0)
198
199   ccl = check->closure;
200   switch(json_object_get_type(o)) {
201     case json_type_array: {
202         int i, alen = json_object_array_length(o);
203         for(i=0;i<alen;i++) {
204           snprintf(subkey, sizeof(subkey), "%s%s%d", key ? key : "",
205                    (key && *key) ? "`" : "", i);
206           cnt += json_parse_descent(check, immediate,
207                                     json_object_array_get_idx(o,i), subkey);
208         }
209       }
210       break;
211
212     case json_type_object: {
213         char *ekey;
214         struct lh_table *table;
215         struct lh_entry *entry;
216         struct json_object *val;
217         table = json_object_get_object(o);
218         if(table->count == 2) {
219           /* this is the special key: { _type: , _value: } notation */
220           json_object *type;
221           type = json_object_object_get(o, "_type");
222           val = json_object_object_get(o, "_value");
223           if(type && json_object_is_type(type, json_type_string) &&
224              val && (json_object_is_type(val, json_type_string) ||
225                      json_object_is_type(val, json_type_null))) {
226             const char *type_str = json_object_get_string(type);
227             const char *val_str = json_object_is_type(val, json_type_null) ? NULL : json_object_get_string(val);
228             if(type_str[1] == '\0') {
229               int32_t __i, *i = &__i;
230               u_int32_t __I, *I = &__I;
231               int64_t __l, *l = &__l;
232               u_int64_t __L, *L = &__L;
233               double __n, *n = &__n;
234               if(val_str == NULL)
235                 i = NULL, I = NULL, l = NULL, L = NULL, n = NULL;
236               switch(*type_str) {
237                 case 'i': if(val_str) __i = strtol(val_str, NULL, 10);
238                           setstat(key, METRIC_INT32, i); break;
239                 case 'I': if(val_str) __I = strtoul(val_str, NULL, 10);
240                           setstat(key, METRIC_UINT32, I); break;
241                 case 'l': if(val_str) __l = strtoll(val_str, NULL, 10);
242                           setstat(key, METRIC_INT64, I); break;
243                 case 'L': if(val_str) __L = strtoull(val_str, NULL, 10);
244                           setstat(key, METRIC_UINT64, I); break;
245                 case 'n': if(val_str) __n = strtod(val_str, NULL);
246                           setstat(key, METRIC_DOUBLE, n); break;
247                 case 's': setstat(key, METRIC_STRING, (void *)val_str); break;
248                 default: break;
249               }
250             }
251             break;
252           }
253         }
254         for(entry = table->head;
255             (entry ? (ekey = (char*)entry->k,
256                val = (struct json_object*)entry->v, entry) : 0);
257             entry = entry->next) {
258           snprintf(subkey, sizeof(subkey), "%s%s%s", key ? key : "",
259                    (key && *key) ? "`" : "", ekey);
260           cnt += json_parse_descent(check, immediate, val, subkey);
261         }
262       }
263       break;
264
265     case json_type_null: {
266         if(!key || !*key) return;
267         break;
268       }
269       break;
270     case json_type_boolean: {
271         if(!key || !*key) return;
272         int32_t value = json_object_get_boolean(o) ? 1 : 0;
273         setstat(key, METRIC_INT32, &value);
274       }
275       break;
276     case json_type_double: {
277         if(!key || !*key) return;
278         double value = json_object_get_double(o);
279         setstat(key, METRIC_DOUBLE, &value);
280       }
281       break;
282     case json_type_int: {
283       if(!key || !*key) return;
284       int32_t value = json_object_get_int(o);
285       setstat(key, METRIC_INT32, &value);
286     }
287     case json_type_string: {
288         if(!key || !*key) return;
289         const char *val = json_object_get_string(o);
290         setstat(key, METRIC_GUESS, (void *)val);
291       }
292       break;
293   }
294   return cnt;
295 }
296 static int
297 push_payload_at_check(noit_check_t *check, json_object *root) {
298   httptrap_closure_t *ccl;
299   noit_boolean immediate;
300   char key[256];
301   int cnt;
302
303   if (check->closure == NULL) return 0;
304   ccl = check->closure;
305   if (!check || strcmp(check->module, "httptrap")) return 0;
306   immediate = noit_httptrap_check_aynsch(ccl->self,check);
307
308   /* do it here */
309   key[0] = '\0';
310   cnt = json_parse_descent(check, immediate, root, key);
311   ccl->stats_count += cnt;
312   return cnt;
313 }
314
315 static int
316 rest_httptrap_handler(noit_http_rest_closure_t *restc,
317                       int npats, char **pats) {
318   int mask, complete = 0, cnt;
319   struct rest_json_payload *rxc = NULL;
320   const char *error = "internal error", *secret = NULL;
321   noit_http_session_ctx *ctx = restc->http_ctx;
322   char json_out[128];
323   noit_check_t *check;
324   uuid_t check_id;
325
326   if(npats != 2) {
327     error = "bad uri";
328     goto error;
329   }
330   if(uuid_parse(pats[0], check_id)) {
331     error = "uuid parse error";
332     goto error;
333   }
334
335   rxc = rest_get_json_upload(restc, &mask, &complete);
336   if(rxc == NULL && !complete) return mask;
337
338   check = noit_poller_lookup(check_id);
339   if(!check || strcmp(check->module, "httptrap")) {
340     error = "no such httptrap check";
341     goto error;
342   }
343   noit_hash_retr_str(check->config, "secret", strlen("secret"), &secret);
344   if(!secret) secret = "";
345   if(strcmp(pats[1], secret)) {
346     error = "secret mismatch";
347     goto error;
348   }
349
350   if(!rxc) goto error;
351   if(!rxc->root) {
352     error = "parse failure";
353     goto error;
354   }
355   if(rxc->error) goto error;
356
357   cnt = push_payload_at_check(check, rxc->root);
358
359   noit_http_response_ok(ctx, "application/json");
360   snprintf(json_out, sizeof(json_out),
361            "{ \"stats\": %d }", cnt);
362   noit_http_response_append(ctx, json_out, strlen(json_out));
363   noit_http_response_end(ctx);
364   return 0;
365
366  error:
367   noit_http_response_server_error(ctx, "application/json");
368   noit_http_response_append(ctx, "{ error: \"", 10);
369   if(rxc && rxc->error) error = rxc->error;
370   noit_http_response_append(ctx, error, strlen(error));
371   noit_http_response_append(ctx, "\" }", 3);
372   noit_http_response_end(ctx);
373   return 0;
374 }
375
376 static int noit_httptrap_initiate_check(noit_module_t *self,
377                                         noit_check_t *check,
378                                         int once, noit_check_t *cause) {
379   if (check->closure == NULL) {
380     httptrap_closure_t *ccl;
381     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
382     ccl->self = self;
383   }
384   INITIATE_CHECK(httptrap_submit, self, check);
385   return 0;
386 }
387
388 static int noit_httptrap_config(noit_module_t *self, noit_hash_table *options) {
389   httptrap_mod_config_t *conf;
390   conf = noit_module_get_userdata(self);
391   if(conf) {
392     if(conf->options) {
393       noit_hash_destroy(conf->options, free, free);
394       free(conf->options);
395     }
396   }
397   else
398     conf = calloc(1, sizeof(*conf));
399   conf->options = options;
400   noit_module_set_userdata(self, conf);
401   return 1;
402 }
403
404 static int noit_httptrap_onload(noit_image_t *self) {
405   if(!nlerr) nlerr = noit_log_stream_find("error/httptrap");
406   if(!nldeb) nldeb = noit_log_stream_find("debug/httptrap");
407   if(!nlerr) nlerr = noit_error;
408   if(!nldeb) nldeb = noit_debug;
409   return 0;
410 }
411
412 static int noit_httptrap_init(noit_module_t *self) {
413   const char *config_val;
414   int sockaddr_len;
415   httptrap_mod_config_t *conf;
416   conf = noit_module_get_userdata(self);
417
418   conf->asynch_metrics = noit_true;
419   if(noit_hash_retr_str(conf->options,
420                         "asynch_metrics", strlen("asynch_metrics"),
421                         (const char **)&config_val)) {
422     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
423       conf->asynch_metrics = noit_false;
424   }
425
426   noit_module_set_userdata(self, conf);
427
428   /* register rest handler */
429   noit_http_rest_register("PUT", "/module/httptrap/",
430                           "^(" UUID_REGEX ")/([^/]*)$",
431                           rest_httptrap_handler);
432   return 0;
433 }
434
435 #include "httptrap.xmlh"
436 noit_module_t httptrap = {
437   {
438     NOIT_MODULE_MAGIC,
439     NOIT_MODULE_ABI_VERSION,
440     "httptrap",
441     "httptrap collection",
442     httptrap_xml_description,
443     noit_httptrap_onload
444   },
445   noit_httptrap_config,
446   noit_httptrap_init,
447   noit_httptrap_initiate_check,
448   NULL
449 };
Note: See TracBrowser for help on using the browser.