root/src/modules/check_test.c

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

Merge branch 'dns_resolve_rtype' of https://github.com/gdusbabek/reconnoiter into dns_resolve_rtype

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2011, Circonus, 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 Circonus, 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 "noit_module.h"
35 #include "noit_check.h"
36 #include "noit_rest.h"
37 #include "noit_check_rest.h"
38 #include "utils/noit_log.h"
39 #include "check_test.xmlh"
40
41 #include <assert.h>
42
43 static void check_test_schedule_sweeper();
44 static noit_log_stream_t nlerr = NULL;
45 static noit_log_stream_t nldeb = NULL;
46
47 struct check_test_closure {
48   noit_check_t *check;
49   noit_http_rest_closure_t *restc;
50 };
51
52 static noit_skiplist in_progress;
53 static eventer_t sweeper_event = NULL;
54 static int default_sweep_interval = 10; /* 10ms seems good */
55
56 static int
57 check_complete_heap_key(const void *av, const void *bv) {
58   const noit_http_rest_closure_t *restc = av;
59   const struct check_test_closure *b = bv;
60   if(restc < b->restc) return -1;
61   if(restc == b->restc) return 0;
62   return 1;
63 }
64 static int
65 check_complete_heap(const void *av, const void *bv) {
66   const struct check_test_closure *a = av;
67   const struct check_test_closure *b = bv;
68   if(a->restc < b->restc) return -1;
69   if(a->restc == b->restc) return 0;
70   return 1;
71 }
72
73 static int
74 check_test_onload(noit_image_t *self) {
75   nlerr = noit_log_stream_find("error/checktest");
76   nldeb = noit_log_stream_find("debug/checktest");
77   if(!nlerr) nlerr = noit_stderr;
78   if(!nldeb) nldeb = noit_debug;
79   noit_skiplist_init(&in_progress);
80   noit_skiplist_set_compare(&in_progress, check_complete_heap,
81                             check_complete_heap_key);
82   return 0;
83 }
84
85 static int
86 check_test_config(noit_module_generic_t *self, noit_hash_table *o) {
87   const char *str;
88   int new_interval = 0;
89   if(noit_hash_retr_str(o, "sweep_interval", strlen("sweep_interval"),
90                         &str))
91     new_interval = atoi(str);
92   if(new_interval > 0) default_sweep_interval = new_interval;
93   return 0;
94 }
95
96 noit_check_t *
97 noit_fire_check(xmlNodePtr attr, xmlNodePtr config, const char **error) {
98   char *target = NULL, *name = NULL, *module = NULL, *filterset = NULL;
99   char *resolve_rtype = NULL;
100   int timeout = 0, flags = NP_TRANSIENT;
101   noit_module_t *m;
102   noit_check_t *c = NULL;
103   xmlNodePtr a, co;
104   noit_hash_table *conf_hash = NULL;
105
106   for(a = attr->children; a; a = a->next) {
107     if(!strcmp((char *)a->name, "target"))
108       target = (char *)xmlNodeGetContent(a);
109     else if(!strcmp((char *)a->name, "name"))
110       name = (char *)xmlNodeGetContent(a);
111     else if(!strcmp((char *)a->name, "module"))
112       module = (char *)xmlNodeGetContent(a);
113     else if(!strcmp((char *)a->name, "filterset"))
114       filterset = (char *)xmlNodeGetContent(a);
115     else if(!strcmp((char *)a->name, "timeout")) {
116       char *timeout_str = (char *)xmlNodeGetContent(a);
117       timeout = atoi(timeout_str);
118       free(timeout_str);
119     } else if(!strcmp((char *)a->name, "resolve_rtype"))
120       resolve_rtype = (char *)xmlNodeGetContent(a);
121   }
122   m = noit_module_lookup(module);
123   if(!m) {
124     *error = "cannot find requested module";
125     goto error;
126   }
127   conf_hash = calloc(1, sizeof(*conf_hash));
128   if(config) {
129     for(co = config->children; co; co = co->next) {
130       char *name, *val;
131       xmlChar *tmp_val;
132       name = strdup((char *)co->name);
133       tmp_val = xmlNodeGetContent(co);
134       val = strdup(tmp_val ? (char *)tmp_val : "");
135       noit_hash_replace(conf_hash, name, strlen(name), val, free, free);
136       xmlFree(tmp_val);
137     }
138   }
139   if(!m->initiate_check) {
140     *error = "that module cannot run checks";
141     goto error;
142   }
143   flags |= noit_calc_rtype_flag(resolve_rtype);
144   c = calloc(1, sizeof(*c));
145   noit_check_update(c, target, name, filterset,
146                     conf_hash, 0, timeout, NULL, flags);
147   c->module = strdup(module);
148   uuid_generate(c->checkid);
149   c->flags |= NP_DISABLED; /* this is hack to know we haven't run it yet */
150   if(NOIT_CHECK_SHOULD_RESOLVE(c))
151     noit_check_resolve(c);
152
153  error:
154   if(conf_hash) {
155     noit_hash_destroy(conf_hash, free, free);
156     free(conf_hash);
157   }
158   if(target) xmlFree(target);
159   if(name) xmlFree(name);
160   if(module) xmlFree(module);
161   if(filterset) xmlFree(filterset);
162   if (resolve_rtype) xmlFree(resolve_rtype);
163   return c;
164 }
165
166 static void
167 rest_test_check_result(struct check_test_closure *cl) {
168   xmlDocPtr doc = NULL;
169   xmlNodePtr root, state;
170   noit_http_session_ctx *ctx = cl->restc->http_ctx;
171
172   noitL(nlerr, "Flushing check test result\n");
173
174   if(cl->restc->call_closure_free)
175     cl->restc->call_closure_free(cl->restc->call_closure);
176   cl->restc->call_closure_free = NULL;
177   cl->restc->call_closure = NULL;
178   cl->restc->fastpath = NULL;
179
180   if(ctx) {
181     eventer_t conne = noit_http_connection_event(noit_http_session_connection(ctx));
182
183     doc = xmlNewDoc((xmlChar *)"1.0");
184     root = xmlNewDocNode(doc, NULL, (xmlChar *)"check", NULL);
185     xmlDocSetRootElement(doc, root);
186     state = noit_check_state_as_xml(cl->check);
187     xmlAddChild(root, state);
188     noit_http_response_ok(ctx, "text/xml");
189     noit_http_response_xml(ctx, doc);
190     noit_http_response_end(ctx);
191  
192     if(conne) {
193       eventer_add(conne);
194       eventer_trigger(conne, EVENTER_READ | EVENTER_WRITE);
195     }
196   }
197
198   noit_poller_free_check(cl->check);
199   xmlFreeDoc(doc);
200   free(cl);
201 }
202
203 static int
204 check_test_sweeper(eventer_t e, int mask, void *closure,
205                    struct timeval *now) {
206   int left = 0;
207   noit_skiplist_node *iter = NULL;
208   sweeper_event = NULL;
209   iter = noit_skiplist_getlist(&in_progress);
210   while(iter) {
211     struct check_test_closure *cl = iter->data;
212     /* advance here, we might delete */
213     noit_skiplist_next(&in_progress,&iter);
214     if(NOIT_CHECK_DISABLED(cl->check)) {
215       if(NOIT_CHECK_SHOULD_RESOLVE(cl->check))
216         noit_check_resolve(cl->check);
217       if(NOIT_CHECK_RESOLVED(cl->check)) {
218         noit_module_t *m = noit_module_lookup(cl->check->module);
219         cl->check->flags &= ~NP_DISABLED;
220         if(NOIT_CHECK_SHOULD_RESOLVE(cl->check))
221           noitL(nldeb, "translated to %s\n", cl->check->target_ip);
222         if(m) m->initiate_check(m, cl->check, 1, NULL);
223       }
224       left++;
225     }
226     else if(NOIT_CHECK_RUNNING(cl->check)) left++;
227     else
228       noit_skiplist_remove(&in_progress, cl->restc,
229                            (noit_freefunc_t)rest_test_check_result);
230   }
231
232   if(left) check_test_schedule_sweeper();
233   return 0;
234 }
235
236 static void
237 check_test_schedule_sweeper() {
238   struct timeval now, diff;
239   if(sweeper_event) return;
240   sweeper_event = eventer_alloc();
241   sweeper_event->mask = EVENTER_TIMER;
242   sweeper_event->callback = check_test_sweeper;
243   diff.tv_sec = 0L;
244   diff.tv_usec = default_sweep_interval * 1000L;
245   gettimeofday(&now, NULL);
246   add_timeval(now, diff, &sweeper_event->whence);
247   eventer_add(sweeper_event);
248 }
249
250 static int
251 rest_test_check_err(noit_http_rest_closure_t *restc,
252                     int npats, char **pats) {
253   noit_http_response *res = noit_http_session_response(restc->http_ctx);
254   noit_skiplist_remove(&in_progress, restc,
255                        (noit_freefunc_t)rest_test_check_result);
256   if(noit_http_response_complete(res) != noit_true) {
257     noit_http_response_standard(restc->http_ctx, 500, "ERROR", "text/html");
258     noit_http_response_end(restc->http_ctx);
259   }
260   return 0;
261 }
262 static int
263 rest_test_check(noit_http_rest_closure_t *restc,
264                 int npats, char **pats) {
265   noit_check_t *tcheck;
266   noit_http_session_ctx *ctx = restc->http_ctx;
267   int mask, complete = 0;
268   int error_code = 500;
269   const char *error = "internal error";
270   xmlDocPtr indoc, doc = NULL;
271   xmlNodePtr attr, config, root;
272
273   indoc = rest_get_xml_upload(restc, &mask, &complete);
274   if(!complete) return mask;
275   if(indoc == NULL) {
276     error = "xml parse error";
277     goto error;
278   }
279   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error))
280     goto error;
281
282   tcheck = noit_fire_check(attr, config, &error);
283   if(tcheck) {
284     /* push the check and the context into a queue to complete on */
285     struct check_test_closure *cl;
286     cl = calloc(1, sizeof(*cl));
287     cl->check = tcheck;
288     cl->restc = restc;
289     noit_skiplist_insert(&in_progress, cl);
290     check_test_schedule_sweeper();
291     if(restc->call_closure_free)
292       restc->call_closure_free(restc->call_closure);
293     restc->call_closure_free = NULL;
294     restc->call_closure = NULL;
295     restc->fastpath = rest_test_check_err;
296     goto cleanup;
297   }
298
299  error:
300   noit_http_response_standard(ctx, error_code, "ERROR", "text/xml");
301   doc = xmlNewDoc((xmlChar *)"1.0");
302   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
303   xmlDocSetRootElement(doc, root);
304   xmlNodeAddContent(root, (xmlChar *)error);
305   noit_http_response_xml(ctx, doc);
306   noit_http_response_end(ctx);
307   goto cleanup;
308
309  cleanup:
310   if(doc) xmlFreeDoc(doc);
311   return 0;
312 }
313
314 static int
315 check_test_init(noit_module_generic_t *self) {
316   assert(noit_http_rest_register(
317     "POST", "/checks/", "^test$",
318     rest_test_check
319   ) == 0);
320   return 0;
321 }
322
323 noit_module_generic_t check_test = {
324   {
325     NOIT_GENERIC_MAGIC,
326     NOIT_GENERIC_ABI_VERSION,
327     "check_test",
328     "Check Tester",
329     "",
330     check_test_onload
331   },
332   check_test_config,
333   check_test_init
334 };
335
Note: See TracBrowser for help on using the browser.