root/src/noit_check_rest.c

Revision 304ec80b8cf842fc0abe5f9029790908b6455957, 32.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 months ago)

Convert to libmtev.

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