root/src/modules/check_test.c

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

actually link in the xml docs to check_test

  • 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, i, mod_cnt;
101   noit_module_t *m;
102   noit_check_t *c = NULL;
103   xmlNodePtr a, co;
104   noit_hash_table *conf_hash = NULL;
105   noit_hash_table **moptions = NULL;
106
107   for(a = attr->children; a; a = a->next) {
108     if(!strcmp((char *)a->name, "target"))
109       target = (char *)xmlNodeGetContent(a);
110     else if(!strcmp((char *)a->name, "name"))
111       name = (char *)xmlNodeGetContent(a);
112     else if(!strcmp((char *)a->name, "module"))
113       module = (char *)xmlNodeGetContent(a);
114     else if(!strcmp((char *)a->name, "filterset"))
115       filterset = (char *)xmlNodeGetContent(a);
116     else if(!strcmp((char *)a->name, "timeout")) {
117       char *timeout_str = (char *)xmlNodeGetContent(a);
118       timeout = atoi(timeout_str);
119       free(timeout_str);
120     } else if(!strcmp((char *)a->name, "resolve_rtype"))
121       resolve_rtype = (char *)xmlNodeGetContent(a);
122   }
123   m = noit_module_lookup(module);
124   if(!m) {
125     *error = "cannot find requested module";
126     goto error;
127   }
128   conf_hash = calloc(1, sizeof(*conf_hash));
129   if(config) {
130     for(co = config->children; co; co = co->next) {
131       char *name, *val;
132       xmlChar *tmp_val;
133       name = strdup((char *)co->name);
134       tmp_val = xmlNodeGetContent(co);
135       val = strdup(tmp_val ? (char *)tmp_val : "");
136       noit_hash_replace(conf_hash, name, strlen(name), val, free, free);
137       xmlFree(tmp_val);
138     }
139   }
140   mod_cnt = noit_check_registered_module_cnt();
141   if(mod_cnt > 0) {
142     moptions = alloca(mod_cnt * sizeof(*moptions));
143     memset(moptions, 0, mod_cnt * sizeof(*moptions));
144     for(i=0; i<mod_cnt; i++) {
145       const char *name;
146       noit_conf_section_t checks;
147       name = noit_check_registered_module(i);
148       checks = noit_conf_get_section(NULL, "/noit/checks");
149       if(name) moptions[i] = noit_conf_get_namespaced_hash(checks, "config", name);
150     }
151   }
152   if(!m->initiate_check) {
153     *error = "that module cannot run checks";
154     goto error;
155   }
156   flags |= noit_calc_rtype_flag(resolve_rtype);
157   c = calloc(1, sizeof(*c));
158   noit_check_update(c, target, name, filterset,
159                     conf_hash, moptions, 0, timeout, NULL, flags);
160   c->module = strdup(module);
161   uuid_generate(c->checkid);
162   c->flags |= NP_DISABLED; /* this is hack to know we haven't run it yet */
163   if(NOIT_CHECK_SHOULD_RESOLVE(c))
164     noit_check_resolve(c);
165
166  error:
167   if(conf_hash) {
168     noit_hash_destroy(conf_hash, free, free);
169     free(conf_hash);
170   }
171   if(moptions) {
172     for(i=0; i<mod_cnt; i++) {
173       if(moptions[i]) {
174         noit_hash_destroy(moptions[i], free, free);
175         free(moptions[i]);
176       }
177     }
178   }
179   if(target) xmlFree(target);
180   if(name) xmlFree(name);
181   if(module) xmlFree(module);
182   if(filterset) xmlFree(filterset);
183   if (resolve_rtype) xmlFree(resolve_rtype);
184   return c;
185 }
186
187 static void
188 rest_test_check_result(struct check_test_closure *cl) {
189   xmlDocPtr doc = NULL;
190   xmlNodePtr root, state;
191   noit_http_session_ctx *ctx = cl->restc->http_ctx;
192
193   noitL(nlerr, "Flushing check test result\n");
194
195   if(cl->restc->call_closure_free)
196     cl->restc->call_closure_free(cl->restc->call_closure);
197   cl->restc->call_closure_free = NULL;
198   cl->restc->call_closure = NULL;
199   cl->restc->fastpath = NULL;
200
201   if(ctx) {
202     eventer_t conne = noit_http_connection_event(noit_http_session_connection(ctx));
203
204     doc = xmlNewDoc((xmlChar *)"1.0");
205     root = xmlNewDocNode(doc, NULL, (xmlChar *)"check", NULL);
206     xmlDocSetRootElement(doc, root);
207     state = noit_check_state_as_xml(cl->check);
208     xmlAddChild(root, state);
209     noit_http_response_ok(ctx, "text/xml");
210     noit_http_response_xml(ctx, doc);
211     noit_http_response_end(ctx);
212  
213     if(conne) {
214       // The event already exists, why re-add it?  Did we want to update it?
215       //eventer_add(conne);
216       eventer_trigger(conne, EVENTER_READ | EVENTER_WRITE);
217     }
218   }
219
220   noit_poller_free_check(cl->check);
221   xmlFreeDoc(doc);
222   free(cl);
223 }
224
225 static int
226 check_test_sweeper(eventer_t e, int mask, void *closure,
227                    struct timeval *now) {
228   int left = 0;
229   noit_skiplist_node *iter = NULL;
230   sweeper_event = NULL;
231   iter = noit_skiplist_getlist(&in_progress);
232   while(iter) {
233     struct check_test_closure *cl = iter->data;
234     /* advance here, we might delete */
235     noit_skiplist_next(&in_progress,&iter);
236     if(NOIT_CHECK_DISABLED(cl->check)) {
237       if(NOIT_CHECK_SHOULD_RESOLVE(cl->check))
238         noit_check_resolve(cl->check);
239       if(NOIT_CHECK_RESOLVED(cl->check)) {
240         noit_module_t *m = noit_module_lookup(cl->check->module);
241         cl->check->flags &= ~NP_DISABLED;
242         if(NOIT_CHECK_SHOULD_RESOLVE(cl->check))
243           noitL(nldeb, "translated to %s\n", cl->check->target_ip);
244         if(m) m->initiate_check(m, cl->check, 1, NULL);
245       }
246       left++;
247     }
248     else if(NOIT_CHECK_RUNNING(cl->check)) left++;
249     else
250       noit_skiplist_remove(&in_progress, cl->restc,
251                            (noit_freefunc_t)rest_test_check_result);
252   }
253
254   if(left) check_test_schedule_sweeper();
255   return 0;
256 }
257
258 static void
259 check_test_schedule_sweeper() {
260   struct timeval now, diff;
261   if(sweeper_event) return;
262   sweeper_event = eventer_alloc();
263   sweeper_event->mask = EVENTER_TIMER;
264   sweeper_event->callback = check_test_sweeper;
265   diff.tv_sec = 0L;
266   diff.tv_usec = default_sweep_interval * 1000L;
267   gettimeofday(&now, NULL);
268   add_timeval(now, diff, &sweeper_event->whence);
269   eventer_add(sweeper_event);
270 }
271
272 static int
273 rest_test_check_err(noit_http_rest_closure_t *restc,
274                     int npats, char **pats) {
275   noit_http_response *res = noit_http_session_response(restc->http_ctx);
276   noit_skiplist_remove(&in_progress, restc,
277                        (noit_freefunc_t)rest_test_check_result);
278   if(noit_http_response_complete(res) != noit_true) {
279     noit_http_response_standard(restc->http_ctx, 500, "ERROR", "text/html");
280     noit_http_response_end(restc->http_ctx);
281   }
282   return 0;
283 }
284 static int
285 rest_test_check(noit_http_rest_closure_t *restc,
286                 int npats, char **pats) {
287   noit_check_t *tcheck;
288   noit_http_session_ctx *ctx = restc->http_ctx;
289   int mask, complete = 0;
290   int error_code = 500;
291   const char *error = "internal error";
292   xmlDocPtr indoc, doc = NULL;
293   xmlNodePtr attr, config, root;
294
295   indoc = rest_get_xml_upload(restc, &mask, &complete);
296   if(!complete) return mask;
297   if(indoc == NULL) {
298     error = "xml parse error";
299     goto error;
300   }
301   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error))
302     goto error;
303
304   tcheck = noit_fire_check(attr, config, &error);
305   if(tcheck) {
306     /* push the check and the context into a queue to complete on */
307     struct check_test_closure *cl;
308     cl = calloc(1, sizeof(*cl));
309     cl->check = tcheck;
310     cl->restc = restc;
311     noit_skiplist_insert(&in_progress, cl);
312     check_test_schedule_sweeper();
313     if(restc->call_closure_free)
314       restc->call_closure_free(restc->call_closure);
315     restc->call_closure_free = NULL;
316     restc->call_closure = NULL;
317     restc->fastpath = rest_test_check_err;
318     goto cleanup;
319   }
320
321  error:
322   noit_http_response_standard(ctx, error_code, "ERROR", "text/xml");
323   doc = xmlNewDoc((xmlChar *)"1.0");
324   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
325   xmlDocSetRootElement(doc, root);
326   xmlNodeAddContent(root, (xmlChar *)error);
327   noit_http_response_xml(ctx, doc);
328   noit_http_response_end(ctx);
329   goto cleanup;
330
331  cleanup:
332   if(doc) xmlFreeDoc(doc);
333   return 0;
334 }
335
336 static int
337 check_test_init(noit_module_generic_t *self) {
338   assert(noit_http_rest_register(
339     "POST", "/checks/", "^test$",
340     rest_test_check
341   ) == 0);
342   return 0;
343 }
344
345 noit_module_generic_t check_test = {
346   {
347     NOIT_GENERIC_MAGIC,
348     NOIT_GENERIC_ABI_VERSION,
349     "check_test",
350     "Check Tester",
351     check_test_xml_description,
352     check_test_onload
353   },
354   check_test_config,
355   check_test_init
356 };
357
Note: See TracBrowser for help on using the browser.