root/src/noit_check_rest.c

Revision f328a7edae498ab69ceb9588d7f4b2a038c66435, 34.2 kB (checked in by Phil Maddox <philip.maddox@circonus.com>, 1 month ago)

Remove Debug Print Statement

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2009, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2015, Circonus, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <mtev_defines.h>
35
36 #include <assert.h>
37 #include <errno.h>
38 #include <libxml/parser.h>
39 #include <libxml/tree.h>
40 #include <libxml/xpath.h>
41
42 #include <mtev_listener.h>
43 #include <mtev_http.h>
44 #include <mtev_rest.h>
45 #include <mtev_conf.h>
46 #include <mtev_conf_private.h>
47 #include <mtev_json.h>
48
49 #include "noit_mtev_bridge.h"
50 #include "noit_filters.h"
51 #include "noit_check.h"
52 #include "noit_check_resolver.h"
53 #include "noit_check_tools.h"
54
55 #define FAIL(a) do { error = (a); goto error; } while(0)
56
57 #define NS_NODE_CONTENT(parent, ns, k, v, followup) do { \
58   xmlNodePtr tmp; \
59   if(v) { \
60     tmp = xmlNewNode(ns, (xmlChar *)(k)); \
61     xmlNodeAddContent(tmp, (xmlChar *)(v)); \
62     followup \
63     xmlAddChild(parent, tmp); \
64   } \
65 } while(0)
66 #define NODE_CONTENT(parent, k, v) NS_NODE_CONTENT(parent, NULL, k, v, )
67
68 static int
69 rest_show_config(mtev_http_rest_closure_t *, int, char **);
70
71 static void
72 add_metrics_to_node(stats_t *c, xmlNodePtr metrics, const char *type,
73                     int include_time) {
74   mtev_hash_table *mets;
75   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
76   const char *k;
77   int klen;
78   void *data;
79   xmlNodePtr tmp;
80
81   mets = noit_check_stats_metrics(c);
82   while(mtev_hash_next(mets, &iter, &k, &klen, &data)) {
83     char buff[256];
84     metric_t *m = (metric_t *)data;
85     xmlAddChild(metrics, (tmp = xmlNewNode(NULL, (xmlChar *)"metric")));
86     xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)m->metric_name);
87     buff[0] = m->metric_type; buff[1] = '\0';
88     xmlSetProp(tmp, (xmlChar *)"type", (xmlChar *)buff);
89     if(m->metric_value.s) {
90       int rv;
91       rv = noit_stats_snprint_metric_value(buff, sizeof(buff), m);
92       if(rv < 0)
93         xmlSetProp(tmp, (xmlChar *)"error", (xmlChar *)"unknown type");
94       else
95         xmlNodeAddContent(tmp, (xmlChar *)buff);
96     }
97   }
98   xmlSetProp(metrics, (xmlChar *)"type", (const xmlChar *) type);
99   if(include_time) {
100     struct timeval *f = noit_check_stats_whence(c, NULL);
101     char timestr[20];
102     snprintf(timestr, sizeof(timestr), "%0.3f",
103              f->tv_sec + (f->tv_usec / 1000000.0));
104     xmlSetProp(metrics, (xmlChar *)"timestamp", (xmlChar *)timestr);
105   }
106 }
107 xmlNodePtr
108 noit_check_state_as_xml(noit_check_t *check, int full) {
109   xmlNodePtr state, tmp, metrics;
110   struct timeval now, *whence;
111   stats_t *c = noit_check_get_stats_current(check);
112
113   gettimeofday(&now, NULL);
114   state = xmlNewNode(NULL, (xmlChar *)"state");
115   NODE_CONTENT(state, "running", NOIT_CHECK_RUNNING(check)?"true":"false");
116   NODE_CONTENT(state, "killed", NOIT_CHECK_KILLED(check)?"true":"false");
117   NODE_CONTENT(state, "configured",
118                NOIT_CHECK_CONFIGURED(check)?"true":"false");
119   NODE_CONTENT(state, "disabled", NOIT_CHECK_DISABLED(check)?"true":"false");
120   NODE_CONTENT(state, "target_ip", check->target_ip);
121   xmlAddChild(state, (tmp = xmlNewNode(NULL, (xmlChar *)"last_run")));
122   whence = noit_check_stats_whence(c, NULL);
123   if(whence->tv_sec) {
124     char timestr[20];
125     snprintf(timestr, sizeof(timestr), "%0.3f",
126              now.tv_sec + (now.tv_usec / 1000000.0));
127     xmlSetProp(tmp, (xmlChar *)"now", (xmlChar *)timestr);
128     snprintf(timestr, sizeof(timestr), "%0.3f",
129              whence->tv_sec + (whence->tv_usec / 1000000.0));
130     xmlNodeAddContent(tmp, (xmlChar *)timestr);
131   }
132   if(full) {
133     stats_t *previous;
134     struct timeval *whence;
135     uint8_t available = noit_check_stats_available(c, NULL);
136     if(available) { /* truth here means the check has been run */
137       char buff[20], *compiler_warning;
138       snprintf(buff, sizeof(buff), "%0.3f",
139                (float)noit_check_stats_duration(c, NULL)/1000.0);
140       compiler_warning = buff;
141       NODE_CONTENT(state, "runtime", compiler_warning);
142     }
143     NODE_CONTENT(state, "availability",
144                  noit_check_available_string(available));
145     NODE_CONTENT(state, "state", noit_check_state_string(noit_check_stats_state(c, NULL)));
146     NODE_CONTENT(state, "status", noit_check_stats_status(c, NULL));
147     xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
148
149     add_metrics_to_node(noit_check_get_stats_inprogress(check), metrics, "inprogress", 0);
150     whence = noit_check_stats_whence(c, NULL);
151     if(whence->tv_sec) {
152       xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
153       add_metrics_to_node(c, metrics, "current", 1);
154     }
155     previous = noit_check_get_stats_previous(check);
156     whence = noit_check_stats_whence(previous, NULL);
157     if(whence->tv_sec) {
158       xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
159       add_metrics_to_node(previous, metrics, "previous", 1);
160     }
161   }
162   return state;
163 }
164
165 static struct json_object *
166 stats_to_json(stats_t *c) {
167   struct json_object *doc;
168   doc = json_object_new_object();
169   mtev_hash_table *metrics;
170   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
171   const char *k;
172   int klen;
173   void *data;
174
175   metrics = noit_check_stats_metrics(c);
176   while(mtev_hash_next(metrics, &iter, &k, &klen, &data)) {
177     char buff[256];
178     metric_t *m = (metric_t *)data;
179     struct json_object *metric = json_object_new_object();
180     buff[0] = m->metric_type; buff[1] = '\0';
181     json_object_object_add(metric, "_type", json_object_new_string(buff));
182     if(m->metric_value.s) {
183       int rv;
184       rv = noit_stats_snprint_metric_value(buff, sizeof(buff), m);
185       if(rv >= 0)
186         json_object_object_add(metric, "_value", json_object_new_string(buff));
187     }
188     json_object_object_add(doc, m->metric_name, metric);
189   }
190   return doc;
191 }
192
193 struct json_object *
194 noit_check_state_as_json(noit_check_t *check, int full) {
195   stats_t *c;
196   char seq_str[64];
197   char id_str[UUID_STR_LEN+1];
198   struct json_object *j_last_run, *j_next_run;
199   struct timeval *t;
200   u_int64_t ms = 0;
201   struct json_object *doc;
202   uuid_unparse_lower(check->checkid, id_str);
203
204   doc = json_object_new_object();
205   json_object_object_add(doc, "id", json_object_new_string(id_str));
206   json_object_object_add(doc, "name", json_object_new_string(check->name));
207   json_object_object_add(doc, "module", json_object_new_string(check->module));
208   json_object_object_add(doc, "target", json_object_new_string(check->target));
209   json_object_object_add(doc, "target_ip", json_object_new_string(check->target_ip));
210   json_object_object_add(doc, "filterset", json_object_new_string(check->filterset));
211   snprintf(seq_str, sizeof(seq_str), "%lld", (long long)check->config_seq);
212   json_object_object_add(doc, "seq", json_object_new_string(seq_str));
213   json_object_object_add(doc, "period", json_object_new_int(check->period));
214   json_object_object_add(doc, "timeout", json_object_new_int(check->timeout));
215   json_object_object_add(doc, "flags", json_object_new_int(check->flags));
216
217   c = noit_check_get_stats_current(check);
218   t = noit_check_stats_whence(c, NULL);
219   j_last_run = json_object_new_int(ms);
220   json_object_set_int_overflow(j_last_run, json_overflow_uint64);
221   ms = t->tv_sec;
222   ms *= 1000ULL;
223   ms += t->tv_usec/1000;
224   json_object_set_uint64(j_last_run, ms);
225   json_object_object_add(doc, "last_run", j_last_run);
226
227   t = check->fire_event ? &check->fire_event->whence : NULL;
228   if(t) {
229     j_next_run = json_object_new_int(ms);
230     json_object_set_int_overflow(j_next_run, json_overflow_uint64);
231     ms = t->tv_sec;
232     ms *= 1000ULL;
233     ms += t->tv_usec/1000;
234     json_object_set_uint64(j_next_run, ms);
235     json_object_object_add(doc, "next_run", j_next_run);
236   }
237
238   if(full) {
239     mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
240     const char *k;
241     int klen;
242     void *data;
243     mtev_hash_table *configh;
244     char timestr[20];
245     struct json_object *status, *metrics, *config;
246
247     /* config */
248     config = json_object_new_object();
249     configh = check->config;
250     while(mtev_hash_next(configh, &iter, &k, &klen, &data))
251       json_object_object_add(config, k, json_object_new_string(data));
252     json_object_object_add(doc, "config", config);
253
254     /* status */
255     status = json_object_new_object();
256     switch(noit_check_stats_available(c, NULL)) {
257       case NP_UNKNOWN: break;
258       case NP_AVAILABLE:
259         json_object_object_add(status, "available", json_object_new_boolean(1));
260         break;
261       case NP_UNAVAILABLE:
262         json_object_object_add(status, "available", json_object_new_boolean(0));
263         break;
264     }
265     switch(noit_check_stats_state(c, NULL)) {
266       case NP_UNKNOWN: break;
267       case NP_GOOD:
268         json_object_object_add(status, "good", json_object_new_boolean(1));
269         break;
270       case NP_BAD:
271         json_object_object_add(status, "good", json_object_new_boolean(0));
272         break;
273     }
274     json_object_object_add(doc, "status", status);
275     metrics = json_object_new_object();
276
277     t = noit_check_stats_whence(c, NULL);
278     if(t->tv_sec) {
279       json_object_object_add(metrics, "current", stats_to_json(c));
280       snprintf(timestr, sizeof(timestr), "%llu%03d",
281                (unsigned long long int)t->tv_sec, (int)(t->tv_usec / 1000));
282       json_object_object_add(metrics, "current_timestamp", json_object_new_string(timestr));
283     }
284
285     c = noit_check_get_stats_inprogress(check);
286     t = noit_check_stats_whence(c, NULL);
287     if(t->tv_sec) {
288       json_object_object_add(metrics, "inprogress", stats_to_json(c));
289       snprintf(timestr, sizeof(timestr), "%llu%03d",
290                (unsigned long long int)t->tv_sec, (int)(t->tv_usec / 1000));
291       json_object_object_add(metrics, "inprogress_timestamp", json_object_new_string(timestr));
292     }
293
294     c = noit_check_get_stats_previous(check);
295     t = noit_check_stats_whence(c, NULL);
296     if(t->tv_sec) {
297       json_object_object_add(metrics, "previous", stats_to_json(c));
298       snprintf(timestr, sizeof(timestr), "%llu%03d",
299                (unsigned long long int)t->tv_sec, (int)(t->tv_usec / 1000));
300       json_object_object_add(metrics, "previous_timestamp", json_object_new_string(timestr));
301     }
302
303     json_object_object_add(doc, "metrics", metrics);
304   }
305   return doc;
306 }
307
308 static int
309 json_check_accum(noit_check_t *check, void *closure) {
310   struct json_object *cobj, *doc = closure;
311   char id_str[UUID_STR_LEN+1];
312   uuid_unparse_lower(check->checkid, id_str);
313   cobj = noit_check_state_as_json(check, 0);
314   json_object_object_del(cobj, "id");
315   json_object_object_add(doc, id_str, cobj);
316   return 1;
317 }
318 static int
319 rest_show_checks_json(mtev_http_rest_closure_t *restc,
320                       int npats, char **pats) {
321   const char *jsonstr;
322   struct json_object *doc;
323   doc = json_object_new_object();
324   noit_poller_do(json_check_accum, doc);
325
326   mtev_http_response_ok(restc->http_ctx, "application/json");
327   jsonstr = json_object_to_json_string(doc);
328   mtev_http_response_append(restc->http_ctx, jsonstr, strlen(jsonstr));
329   mtev_http_response_append(restc->http_ctx, "\n", 1);
330   json_object_put(doc);
331   mtev_http_response_end(restc->http_ctx);
332   return 0;
333 }
334 static int
335 rest_show_checks(mtev_http_rest_closure_t *restc,
336                  int npats, char **pats) {
337   char *cpath = "/checks";
338
339   if(npats == 1 && !strcmp(pats[0], ".json"))
340     return rest_show_checks_json(restc, npats, pats);
341
342   return rest_show_config(restc, 1, &cpath);
343 }
344
345 static int
346 rest_show_check_json(mtev_http_rest_closure_t *restc,
347                      uuid_t checkid) {
348   noit_check_t *check;
349   struct json_object *doc;
350   const char *jsonstr;
351   check = noit_poller_lookup(checkid);
352   if(!check) {
353     mtev_http_response_not_found(restc->http_ctx, "application/json");
354     mtev_http_response_end(restc->http_ctx);
355     return 0;
356   }
357
358   doc = noit_check_state_as_json(check, 1);
359  
360   mtev_http_response_ok(restc->http_ctx, "application/json");
361   jsonstr = json_object_to_json_string(doc);
362   mtev_http_response_append(restc->http_ctx, jsonstr, strlen(jsonstr));
363   mtev_http_response_append(restc->http_ctx, "\n", 1);
364   json_object_put(doc);
365   mtev_http_response_end(restc->http_ctx);
366   return 0;
367 }
368 static int
369 rest_show_check(mtev_http_rest_closure_t *restc,
370                 int npats, char **pats) {
371   mtev_http_session_ctx *ctx = restc->http_ctx;
372   xmlXPathObjectPtr pobj = NULL;
373   xmlXPathContextPtr xpath_ctxt = NULL;
374   xmlDocPtr doc = NULL;
375   xmlNodePtr node, root, attr, config, state, tmp, anode;
376   uuid_t checkid;
377   noit_check_t *check;
378   char xpath[1024], *uuid_conf, *module = NULL, *value = NULL;
379   int rv, mod, mod_cnt, cnt, error_code = 500;
380   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
381   const char *k;
382   int klen;
383   void *data;
384   mtev_hash_table *configh;
385
386   if(npats != 2 && npats != 3) goto error;
387
388   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
389   if(rv == 0) goto not_found;
390   if(rv < 0) goto error;
391
392   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
393   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
394   if(!pobj || pobj->type != XPATH_NODESET ||
395      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto not_found;
396   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
397   if(cnt != 1) goto error;
398
399   node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
400   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
401   if(!uuid_conf || uuid_parse(uuid_conf, checkid)) goto error;
402
403   if(npats == 3 && !strcmp(pats[2], ".json")) {
404     return rest_show_check_json(restc, checkid);
405   }
406
407   doc = xmlNewDoc((xmlChar *)"1.0");
408   root = xmlNewDocNode(doc, NULL, (xmlChar *)"check", NULL);
409   xmlDocSetRootElement(doc, root);
410
411 #define MYATTR(node,a,n,b) _mtev_conf_get_string(node, &(n), "@" #a, &(b))
412 #define INHERIT(node,a,n,b) \
413   _mtev_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
414 #define SHOW_ATTR(parent, node, a) do { \
415   char *_value = NULL; \
416   INHERIT(node, a, anode, _value); \
417   if(_value != NULL) { \
418     int clen, plen;\
419     char *_cpath, *_apath; \
420     xmlNodePtr child; \
421     _cpath = node ? (char *)xmlGetNodePath(node) : strdup(""); \
422     _apath = anode ? (char *)xmlGetNodePath(anode) : strdup(""); \
423     clen = strlen(_cpath); \
424     plen = strlen("/noit/checks"); \
425     child = xmlNewNode(NULL, (xmlChar *)#a); \
426     xmlNodeAddContent(child, (xmlChar *)_value); \
427     if(!strncmp(_cpath, _apath, clen) && _apath[clen] == '/') { \
428     } \
429     else { \
430       xmlSetProp(child, (xmlChar *)"inherited", (xmlChar *)_apath+plen); \
431     } \
432     xmlAddChild(parent, child); \
433     free(_cpath); \
434     free(_apath); \
435     free(_value); \
436   } \
437 } while(0)
438
439   attr = xmlNewNode(NULL, (xmlChar *)"attributes");
440   xmlAddChild(root, attr);
441
442   SHOW_ATTR(attr,node,uuid);
443   SHOW_ATTR(attr,node,seq);
444
445   /* Name is odd, it falls back transparently to module */
446   if(!INHERIT(node, module, tmp, module)) module = NULL;
447   xmlAddChild(attr, (tmp = xmlNewNode(NULL, (xmlChar *)"name")));
448   if(MYATTR(node, name, anode, value))
449     xmlNodeAddContent(tmp, (xmlChar *)value);
450   else if(module)
451     xmlNodeAddContent(tmp, (xmlChar *)module);
452   if(value) free(value);
453   if(module) free(module);
454
455   SHOW_ATTR(attr,node,module);
456   SHOW_ATTR(attr,node,target);
457   SHOW_ATTR(attr,node,resolve_rtype);
458   SHOW_ATTR(attr,node,seq);
459   SHOW_ATTR(attr,node,period);
460   SHOW_ATTR(attr,node,timeout);
461   SHOW_ATTR(attr,node,oncheck);
462   SHOW_ATTR(attr,node,filterset);
463   SHOW_ATTR(attr,node,disable);
464
465   /* Add the config */
466   config = xmlNewNode(NULL, (xmlChar *)"config");
467   configh = mtev_conf_get_hash(node, "config");
468   while(mtev_hash_next(configh, &iter, &k, &klen, &data))
469     NODE_CONTENT(config, k, data);
470   mtev_hash_destroy(configh, free, free);
471   free(configh);
472
473   mod_cnt = noit_check_registered_module_cnt();
474   for(mod=0; mod<mod_cnt; mod++) {
475     xmlNsPtr ns;
476     const char *nsname;
477     char buff[256];
478
479     nsname = noit_check_registered_module(mod);
480  
481     snprintf(buff, sizeof(buff), "noit://module/%s", nsname);
482     ns = xmlSearchNs(root->doc, root, (xmlChar *)nsname);
483     if(!ns) ns = xmlNewNs(root, (xmlChar *)buff, (xmlChar *)nsname);
484     if(ns) {
485       configh = mtev_conf_get_namespaced_hash(node, "config", nsname);
486       if(configh) {
487         memset(&iter, 0, sizeof(iter));
488         while(mtev_hash_next(configh, &iter, &k, &klen, &data)) {
489           NS_NODE_CONTENT(config, ns, "value", data,
490             xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)k);
491           );
492         }
493         mtev_hash_destroy(configh, free, free);
494         free(configh);
495       }
496     }
497   }
498   xmlAddChild(root, config);
499
500   /* Add the state */
501   check = noit_poller_lookup(checkid);
502   if(!check) {
503     state = xmlNewNode(NULL, (xmlChar *)"state");
504     xmlSetProp(state, (xmlChar *)"error", (xmlChar *)"true");
505   }
506   else
507     state = noit_check_state_as_xml(check, 1);
508   xmlAddChild(root, state);
509   mtev_http_response_ok(ctx, "text/xml");
510   mtev_http_response_xml(ctx, doc);
511   mtev_http_response_end(ctx);
512   goto cleanup;
513
514  not_found:
515   mtev_http_response_not_found(ctx, "text/html");
516   mtev_http_response_end(ctx);
517   goto cleanup;
518
519  error:
520   mtev_http_response_standard(ctx, error_code, "ERROR", "text/html");
521   mtev_http_response_end(ctx);
522   goto cleanup;
523
524  cleanup:
525   if(pobj) xmlXPathFreeObject(pobj);
526   if(doc) xmlFreeDoc(doc);
527   return 0;
528 }
529
530 static int
531 missing_namespaces(xmlNodePtr ctx, xmlNodePtr q) {
532   xmlNodePtr n;
533   if(q->ns && !xmlSearchNs(ctx->doc, ctx, q->ns->prefix)) return 1;
534   for(n=q->next; n; n=n->next) return missing_namespaces(ctx, n);
535   for(n=q->children; n; n=n->next) return missing_namespaces(ctx, n);
536   return 0;
537 }
538 int
539 noit_validate_check_rest_post(xmlDocPtr doc, xmlNodePtr *a, xmlNodePtr *c,
540                               const char **error) {
541   mtev_conf_section_t toplevel;
542   xmlNodePtr root, tl, an, master_config_root;
543   int name=0, module=0, target=0, period=0, timeout=0, filterset=0;
544   *a = *c = NULL;
545   root = xmlDocGetRootElement(doc);
546   /* Make sure any present namespaces are in the master document already */
547   toplevel = mtev_conf_get_section(NULL, "/*");
548   master_config_root = (xmlNodePtr)toplevel;
549   if(!master_config_root) {
550     *error = "no document";
551     return 0;
552   }
553   if(missing_namespaces(master_config_root, root)) {
554     *error = "invalid namespace provided";
555     return 0;
556   }
557      
558   if(strcmp((char *)root->name, "check")) return 0;
559   for(tl = root->children; tl; tl = tl->next) {
560     if(!strcmp((char *)tl->name, "attributes")) {
561       *a = tl;
562       for(an = tl->children; an; an = an->next) {
563 #define CHECK_N_SET(a) if(!strcmp((char *)an->name, #a))
564         CHECK_N_SET(name) {
565           xmlChar *tmp;
566           pcre *valid_name = mtev_conf_get_valid_name_checker();
567           int ovector[30], valid;
568           tmp = xmlNodeGetContent(an);
569           valid = (pcre_exec(valid_name, NULL,
570                              (char *)tmp, strlen((char *)tmp), 0, 0,
571                              ovector, sizeof(ovector)/sizeof(*ovector)) > 0);
572           xmlFree(tmp);
573           if(!valid) { *error = "invalid name"; return 0; }
574           name = 1;
575         }
576         else CHECK_N_SET(module) module = 1; /* This is validated by called */
577         else CHECK_N_SET(target) {
578           mtev_boolean should_resolve;
579           int valid;
580           xmlChar *tmp;
581           tmp = xmlNodeGetContent(an);
582           valid = noit_check_is_valid_target((char *)tmp);
583           xmlFree(tmp);
584           if(noit_check_should_resolve_targets(&should_resolve) &&
585              should_resolve == mtev_false &&
586              !valid) {
587             *error = "invalid target";
588             return 0;
589           }
590           target = 1;
591         }
592         else CHECK_N_SET(resolve_rtype) {
593           xmlChar *tmp;
594           mtev_boolean invalid;
595           tmp = xmlNodeGetContent(an);
596           invalid = strcmp((char *)tmp, PREFER_IPV4) &&
597                     strcmp((char *)tmp, PREFER_IPV6) &&
598                     strcmp((char *)tmp, FORCE_IPV4) &&
599                     strcmp((char *)tmp, FORCE_IPV6);
600           xmlFree(tmp);
601           if(invalid) {
602             *error = "invalid reslove_rtype";
603             return 0;
604           }
605         }
606         else CHECK_N_SET(period) {
607           int pint;
608           xmlChar *tmp;
609           tmp = xmlNodeGetContent(an);
610           pint = mtev_conf_string_to_int((char *)tmp);
611           xmlFree(tmp);
612           if(pint < 1000 || pint > 300000) {
613             *error = "invalid period";
614             return 0;
615           }
616           period = 1;
617         }
618         else CHECK_N_SET(timeout) {
619           int pint;
620           xmlChar *tmp;
621           tmp = xmlNodeGetContent(an);
622           pint = mtev_conf_string_to_int((char *)tmp);
623           xmlFree(tmp);
624           if(pint < 0 || pint > 300000) {
625             *error = "invalid timeout";
626             return 0;
627           }
628           timeout = 1;
629         }
630         else CHECK_N_SET(filterset) {
631           xmlChar *tmp;
632           tmp = xmlNodeGetContent(an);
633           if(!noit_filter_exists((char *)tmp)) {
634             *error = "filterset does not exist";
635             return 0;
636           }
637           filterset = 1;
638         }
639         else CHECK_N_SET(disable) { /* not required */
640           int valid;
641           xmlChar *tmp;
642           tmp = xmlNodeGetContent(an);
643           valid = (!strcasecmp((char *)tmp, "true") ||
644                    !strcasecmp((char *)tmp, "on") ||
645                    !strcasecmp((char *)tmp, "false") ||
646                    !strcasecmp((char *)tmp, "off"));
647           xmlFree(tmp);
648           if(!valid) { *error = "bad disable parameter"; return 0; }
649           target = 1;
650         }
651         else CHECK_N_SET(seq) {}
652         else {
653           *error = "unknown option specified";
654           return 0;
655         }
656       }
657     }
658     else if(!strcmp((char *)tl->name, "config")) {
659       *c = tl;
660       /* Noop, anything goes */
661     }
662     else return 0;
663   }
664   if(name && module && target && period && timeout && filterset) return 1;
665   *error = "insufficient information";
666   return 0;
667 }
668 static void
669 configure_xml_check(xmlNodePtr parent, xmlNodePtr check, xmlNodePtr a, xmlNodePtr c, int64_t *seq) {
670   xmlNodePtr n, config, oldconfig;
671   if(seq) *seq = 0;
672   for(n = a->children; n; n = n->next) {
673 #define ATTR2PROP(attr) do { \
674   if(!strcmp((char *)n->name, #attr)) { \
675     xmlChar *v = xmlNodeGetContent(n); \
676     if(v) xmlSetProp(check, n->name, v); \
677     else xmlUnsetProp(check, n->name); \
678     if(v) xmlFree(v); \
679   } \
680 } while(0)
681     ATTR2PROP(name);
682     ATTR2PROP(target);
683     ATTR2PROP(resolve_rtype);
684     ATTR2PROP(module);
685     ATTR2PROP(period);
686     ATTR2PROP(timeout);
687     ATTR2PROP(disable);
688     ATTR2PROP(filterset);
689     ATTR2PROP(seq);
690     if(seq && !strcmp((char *)n->name, "seq")) {
691       xmlChar *v = xmlNodeGetContent(n);
692       *seq = strtoll((const char *)v, NULL, 10);
693       xmlFree(v);
694     }
695   }
696   for(oldconfig = check->children; oldconfig; oldconfig = oldconfig->next)
697     if(!strcmp((char *)oldconfig->name, "config")) break;
698   config = xmlNewNode(NULL, (xmlChar *)"config");
699   if(c) {
700     xmlAttrPtr inherit;
701     if((inherit = xmlHasProp(c, (xmlChar *)"inherit")) != NULL &&
702         inherit->children && inherit->children->content)
703       xmlSetProp(config, (xmlChar *)"inherit", inherit->children->content);
704     for(n = c->children; n; n = n->next) {
705       xmlNsPtr targetns = NULL;
706       xmlChar *v = xmlNodeGetContent(n);
707       if(n->ns) {
708         targetns = xmlSearchNs(parent->doc, xmlDocGetRootElement(parent->doc),
709                                n->ns->prefix);
710         if(!targetns) {
711           targetns = xmlSearchNs(config->doc, config, n->ns->prefix);
712           if(!targetns) targetns = xmlNewNs(config, n->ns->href, n->ns->prefix);
713           mtevL(noit_debug, "Setting a config value in a new namespace (%p)\n", targetns);
714         }
715         else {
716           mtevL(noit_debug,"Setting a config value in a namespace (%p)\n", targetns);
717         }
718       }
719       xmlNodePtr co = xmlNewNode(targetns, n->name);
720       if(n->ns && !strcmp((char *)n->name, "value")) {
721         xmlChar *name = xmlGetProp(n, (xmlChar *)"name");
722         if(name) xmlSetProp(co, (xmlChar *)"name", name);
723       }
724       xmlNodeAddContent(co, v);
725       xmlFree(v);
726       xmlAddChild(config, co);
727     }
728   }
729   if(oldconfig) {
730     xmlReplaceNode(oldconfig, config);
731     xmlFreeNode(oldconfig);
732   }
733   else xmlAddChild(check, config);
734   CONF_DIRTY(config);
735 }
736 static xmlNodePtr
737 make_conf_path(char *path) {
738   xmlNodePtr start, tmp;
739   char fullpath[1024], *tok, *brk;
740   if(!path || strlen(path) < 1) return NULL;
741   snprintf(fullpath, sizeof(fullpath), "%s", path+1);
742   fullpath[strlen(fullpath)-1] = '\0';
743   start = mtev_conf_get_section(NULL, "/noit/checks");
744   if(!start) return NULL;
745   for (tok = strtok_r(fullpath, "/", &brk);
746        tok;
747        tok = strtok_r(NULL, "/", &brk)) {
748     if(!xmlValidateNameValue((xmlChar *)tok)) return NULL;
749     if(!strcmp(tok, "check")) return NULL;  /* These two paths */
750     if(!strcmp(tok, "config")) return NULL; /* are off limits. */
751     for (tmp = start->children; tmp; tmp = tmp->next) {
752       if(!strcmp((char *)tmp->name, tok)) break;
753     }
754     if(!tmp) {
755       tmp = xmlNewNode(NULL, (xmlChar *)tok);
756       xmlAddChild(start, tmp);
757       CONF_DIRTY(tmp);
758     }
759     start = tmp;
760   }
761   return start;
762 }
763 static int
764 rest_delete_check(mtev_http_rest_closure_t *restc,
765                   int npats, char **pats) {
766   mtev_http_session_ctx *ctx = restc->http_ctx;
767   xmlXPathObjectPtr pobj = NULL;
768   xmlXPathContextPtr xpath_ctxt = NULL;
769   xmlNodePtr node;
770   uuid_t checkid;
771   noit_check_t *check;
772   const char *error;
773   char xpath[1024], *uuid_conf;
774   int rv, cnt, error_code = 500;
775   mtev_boolean exists = mtev_false;
776
777   if(npats != 2) goto error;
778
779   if(uuid_parse(pats[1], checkid)) goto error;
780   check = noit_poller_lookup(checkid);
781   if(check)
782     exists = mtev_true;
783
784   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
785   if(rv == 0) FAIL("uuid not valid");
786   if(rv < 0) FAIL("Tricky McTrickster... No");
787
788   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
789   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
790   if(!pobj || pobj->type != XPATH_NODESET ||
791      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
792     if(exists) { error_code = 403; FAIL("uuid not yours"); }
793     goto not_found;
794   }
795   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
796   if(cnt != 1) FAIL("internal error, |checkid| > 1");
797   node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
798   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
799   if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
800     FAIL("internal error uuid");
801
802   /* delete this here */
803   if(check) noit_poller_deschedule(check->checkid);
804   CONF_REMOVE(node);
805   xmlUnlinkNode(node);
806   xmlFreeNode(node);
807   mtev_conf_mark_changed();
808   if(mtev_conf_write_file(NULL) != 0)
809     mtevL(noit_error, "local config write failed\n");
810   mtev_http_response_ok(ctx, "text/html");
811   mtev_http_response_end(ctx);
812   goto cleanup;
813
814  not_found:
815   mtev_http_response_not_found(ctx, "text/html");
816   mtev_http_response_end(ctx);
817   goto cleanup;
818
819  error:
820   mtev_http_response_standard(ctx, error_code, "ERROR", "text/html");
821   mtev_http_response_end(ctx);
822   goto cleanup;
823
824  cleanup:
825   if(pobj) xmlXPathFreeObject(pobj);
826   (void)error;
827   return 0;
828 }
829
830 static int
831 rest_set_check(mtev_http_rest_closure_t *restc,
832                int npats, char **pats) {
833   mtev_http_session_ctx *ctx = restc->http_ctx;
834   xmlXPathObjectPtr pobj = NULL;
835   xmlXPathContextPtr xpath_ctxt = NULL;
836   xmlDocPtr doc = NULL, indoc = NULL;
837   xmlNodePtr node, root, attr, config, parent;
838   uuid_t checkid;
839   noit_check_t *check;
840   char xpath[1024], *uuid_conf;
841   int rv, cnt, error_code = 500, complete = 0, mask = 0;
842   const char *error = "internal error";
843   mtev_boolean exists = mtev_false;
844
845   if(npats != 2) goto error;
846
847   indoc = rest_get_xml_upload(restc, &mask, &complete);
848   if(!complete) return mask;
849   if(indoc == NULL) FAIL("xml parse error");
850   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error)) goto error;
851
852   if(uuid_parse(pats[1], checkid)) goto error;
853   check = noit_poller_lookup(checkid);
854   if(check)
855     exists = mtev_true;
856
857   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
858   if(rv == 0) FAIL("uuid not valid");
859   if(rv < 0) FAIL("Tricky McTrickster... No");
860
861   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
862   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
863   if(!pobj || pobj->type != XPATH_NODESET ||
864      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
865     if(exists) { error_code = 403; FAIL("uuid not yours"); }
866     else {
867       int64_t seq, old_seq = 0;
868       char *target = NULL, *name = NULL, *module = NULL;
869       noit_module_t *m = NULL;
870       noit_check_t *check = NULL;
871       xmlNodePtr a, newcheck = NULL;
872       /* make sure this isn't a dup */
873       for(a = attr->children; a; a = a->next) {
874         if(!strcmp((char *)a->name, "target"))
875           target = (char *)xmlNodeGetContent(a);
876         if(!strcmp((char *)a->name, "name"))
877           name = (char *)xmlNodeGetContent(a);
878         if(!strcmp((char *)a->name, "module"))
879           module = (char *)xmlNodeGetContent(a);
880       }
881       exists = (!target || (check = noit_poller_lookup_by_name(target, name)) != NULL);
882       if(check) old_seq = check->config_seq;
883       if(module) m = noit_module_lookup(module);
884       if(target) xmlFree(target);
885       if(name) xmlFree(name);
886       if(module) xmlFree(module);
887       if(exists) FAIL("target`name already registered");
888       if(!m) FAIL("module does not exist");
889       /* create a check here */
890       newcheck = xmlNewNode(NULL, (xmlChar *)"check");
891       xmlSetProp(newcheck, (xmlChar *)"uuid", (xmlChar *)pats[1]);
892       parent = make_conf_path(pats[0]);
893       if(!parent) FAIL("invalid path");
894       configure_xml_check(parent, newcheck, attr, config, &seq);
895       if(old_seq > seq) FAIL("invalid sequence");
896       xmlAddChild(parent, newcheck);
897       CONF_DIRTY(newcheck);
898     }
899   }
900   if(exists) {
901     int64_t seq, old_seq = 0;
902     int module_change;
903     char *target = NULL, *name = NULL, *module = NULL;
904     xmlNodePtr a;
905     noit_check_t *ocheck;
906
907     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
908     if(cnt != 1) FAIL("internal error, |checkid| > 1");
909     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
910     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
911     if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
912       FAIL("internal error uuid");
913
914     /* make sure this isn't a dup */
915     for(a = attr->children; a; a = a->next) {
916       if(!strcmp((char *)a->name, "target"))
917         target = (char *)xmlNodeGetContent(a);
918       if(!strcmp((char *)a->name, "name"))
919         name = (char *)xmlNodeGetContent(a);
920       if(!strcmp((char *)a->name, "module"))
921         module = (char *)xmlNodeGetContent(a);
922     }
923     ocheck = noit_poller_lookup_by_name(target, name);
924     module_change = strcmp(check->module, module);
925     xmlFree(target);
926     xmlFree(name);
927     xmlFree(module);
928     if(ocheck && ocheck != check) FAIL("new target`name would collide");
929     if(module_change) FAIL("cannot change module");
930     parent = make_conf_path(pats[0]);
931     if(!parent) FAIL("invalid path");
932     configure_xml_check(parent, node, attr, config, &seq);
933     if(check) old_seq = check->config_seq;
934     if(old_seq > seq) FAIL("invalid sequence");
935     xmlUnlinkNode(node);
936     xmlAddChild(parent, node);
937     CONF_DIRTY(node);
938   }
939
940   mtev_conf_mark_changed();
941   if(mtev_conf_write_file(NULL) != 0)
942     mtevL(noit_error, "local config write failed\n");
943   noit_poller_reload(xpath);
944   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
945   restc->call_closure_free = NULL;
946   restc->call_closure = NULL;
947   if(pobj) xmlXPathFreeObject(pobj);
948   restc->fastpath = rest_show_check;
949   return restc->fastpath(restc, restc->nparams, restc->params);
950
951  error:
952   mtev_http_response_standard(ctx, error_code, "ERROR", "text/xml");
953   doc = xmlNewDoc((xmlChar *)"1.0");
954   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
955   xmlDocSetRootElement(doc, root);
956   xmlNodeAddContent(root, (xmlChar *)error);
957   mtev_http_response_xml(ctx, doc);
958   mtev_http_response_end(ctx);
959   goto cleanup;
960
961  cleanup:
962   if(pobj) xmlXPathFreeObject(pobj);
963   if(doc) xmlFreeDoc(doc);
964   return 0;
965 }
966
967 static int
968 rest_show_config(mtev_http_rest_closure_t *restc,
969                  int npats, char **pats) {
970   mtev_http_session_ctx *ctx = restc->http_ctx;
971   xmlDocPtr doc = NULL;
972   xmlNodePtr node, root;
973   char xpath[1024];
974
975   snprintf(xpath, sizeof(xpath), "/noit%s", pats ? pats[0] : "");
976   node = mtev_conf_get_section(NULL, xpath);
977
978   if(!node) {
979     mtev_http_response_not_found(ctx, "text/xml");
980     mtev_http_response_end(ctx);
981   }
982   else {
983     doc = xmlNewDoc((xmlChar *)"1.0");
984     root = xmlCopyNode(node, 1);
985     xmlDocSetRootElement(doc, root);
986     mtev_http_response_ok(ctx, "text/xml");
987     mtev_http_response_xml(ctx, doc);
988     mtev_http_response_end(ctx);
989   }
990
991   if(doc) xmlFreeDoc(doc);
992
993   return 0;
994 }
995
996 void
997 noit_check_rest_init() {
998   assert(mtev_http_rest_register_auth(
999     "GET", "/", "^config(/.*)?$",
1000     rest_show_config, mtev_http_rest_client_cert_auth
1001   ) == 0);
1002   assert(mtev_http_rest_register_auth(
1003     "GET", "/checks/", "^show(\\.json)?$",
1004     rest_show_checks, mtev_http_rest_client_cert_auth
1005   ) == 0);
1006   assert(mtev_http_rest_register_auth(
1007     "GET", "/checks/", "^show(/.*)(?<=/)(" UUID_REGEX ")(\\.json)?$",
1008     rest_show_check, mtev_http_rest_client_cert_auth
1009   ) == 0);
1010   assert(mtev_http_rest_register_auth(
1011     "PUT", "/checks/", "^set(/.*)(?<=/)(" UUID_REGEX ")$",
1012     rest_set_check, mtev_http_rest_client_cert_auth
1013   ) == 0);
1014   assert(mtev_http_rest_register_auth(
1015     "DELETE", "/checks/", "^delete(/.*)(?<=/)(" UUID_REGEX ")$",
1016     rest_delete_check, mtev_http_rest_client_cert_auth
1017   ) == 0);
1018 }
1019
Note: See TracBrowser for help on using the browser.