root/src/modules/check_test.c

Revision 33b7efe6ca32e4a2a683db89cf86828a9b1ab096, 10.4 kB (checked in by Dan Di Spaltro <dan@cloudkick.com>, 3 years ago)

Assert when result is not 0
Fix usage where we re-add an already added event.
Fix usage where we remove after closing, change the order in that case

  • 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       // The event already exists, why re-add it?  Did we want to update it?
194       //eventer_add(conne);
195       eventer_trigger(conne, EVENTER_READ | EVENTER_WRITE);
196     }
197   }
198
199   noit_poller_free_check(cl->check);
200   xmlFreeDoc(doc);
201   free(cl);
202 }
203
204 static int
205 check_test_sweeper(eventer_t e, int mask, void *closure,
206                    struct timeval *now) {
207   int left = 0;
208   noit_skiplist_node *iter = NULL;
209   sweeper_event = NULL;
210   iter = noit_skiplist_getlist(&in_progress);
211   while(iter) {
212     struct check_test_closure *cl = iter->data;
213     /* advance here, we might delete */
214     noit_skiplist_next(&in_progress,&iter);
215     if(NOIT_CHECK_DISABLED(cl->check)) {
216       if(NOIT_CHECK_SHOULD_RESOLVE(cl->check))
217         noit_check_resolve(cl->check);
218       if(NOIT_CHECK_RESOLVED(cl->check)) {
219         noit_module_t *m = noit_module_lookup(cl->check->module);
220         cl->check->flags &= ~NP_DISABLED;
221         if(NOIT_CHECK_SHOULD_RESOLVE(cl->check))
222           noitL(nldeb, "translated to %s\n", cl->check->target_ip);
223         if(m) m->initiate_check(m, cl->check, 1, NULL);
224       }
225       left++;
226     }
227     else if(NOIT_CHECK_RUNNING(cl->check)) left++;
228     else
229       noit_skiplist_remove(&in_progress, cl->restc,
230                            (noit_freefunc_t)rest_test_check_result);
231   }
232
233   if(left) check_test_schedule_sweeper();
234   return 0;
235 }
236
237 static void
238 check_test_schedule_sweeper() {
239   struct timeval now, diff;
240   if(sweeper_event) return;
241   sweeper_event = eventer_alloc();
242   sweeper_event->mask = EVENTER_TIMER;
243   sweeper_event->callback = check_test_sweeper;
244   diff.tv_sec = 0L;
245   diff.tv_usec = default_sweep_interval * 1000L;
246   gettimeofday(&now, NULL);
247   add_timeval(now, diff, &sweeper_event->whence);
248   eventer_add(sweeper_event);
249 }
250
251 static int
252 rest_test_check_err(noit_http_rest_closure_t *restc,
253                     int npats, char **pats) {
254   noit_http_response *res = noit_http_session_response(restc->http_ctx);
255   noit_skiplist_remove(&in_progress, restc,
256                        (noit_freefunc_t)rest_test_check_result);
257   if(noit_http_response_complete(res) != noit_true) {
258     noit_http_response_standard(restc->http_ctx, 500, "ERROR", "text/html");
259     noit_http_response_end(restc->http_ctx);
260   }
261   return 0;
262 }
263 static int
264 rest_test_check(noit_http_rest_closure_t *restc,
265                 int npats, char **pats) {
266   noit_check_t *tcheck;
267   noit_http_session_ctx *ctx = restc->http_ctx;
268   int mask, complete = 0;
269   int error_code = 500;
270   const char *error = "internal error";
271   xmlDocPtr indoc, doc = NULL;
272   xmlNodePtr attr, config, root;
273
274   indoc = rest_get_xml_upload(restc, &mask, &complete);
275   if(!complete) return mask;
276   if(indoc == NULL) {
277     error = "xml parse error";
278     goto error;
279   }
280   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error))
281     goto error;
282
283   tcheck = noit_fire_check(attr, config, &error);
284   if(tcheck) {
285     /* push the check and the context into a queue to complete on */
286     struct check_test_closure *cl;
287     cl = calloc(1, sizeof(*cl));
288     cl->check = tcheck;
289     cl->restc = restc;
290     noit_skiplist_insert(&in_progress, cl);
291     check_test_schedule_sweeper();
292     if(restc->call_closure_free)
293       restc->call_closure_free(restc->call_closure);
294     restc->call_closure_free = NULL;
295     restc->call_closure = NULL;
296     restc->fastpath = rest_test_check_err;
297     goto cleanup;
298   }
299
300  error:
301   noit_http_response_standard(ctx, error_code, "ERROR", "text/xml");
302   doc = xmlNewDoc((xmlChar *)"1.0");
303   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
304   xmlDocSetRootElement(doc, root);
305   xmlNodeAddContent(root, (xmlChar *)error);
306   noit_http_response_xml(ctx, doc);
307   noit_http_response_end(ctx);
308   goto cleanup;
309
310  cleanup:
311   if(doc) xmlFreeDoc(doc);
312   return 0;
313 }
314
315 static int
316 check_test_init(noit_module_generic_t *self) {
317   assert(noit_http_rest_register(
318     "POST", "/checks/", "^test$",
319     rest_test_check
320   ) == 0);
321   return 0;
322 }
323
324 noit_module_generic_t check_test = {
325   {
326     NOIT_GENERIC_MAGIC,
327     NOIT_GENERIC_ABI_VERSION,
328     "check_test",
329     "Check Tester",
330     "",
331     check_test_onload
332   },
333   check_test_config,
334   check_test_init
335 };
336
Note: See TracBrowser for help on using the browser.