summaryrefslogtreecommitdiffstats
path: root/web/api
diff options
context:
space:
mode:
Diffstat (limited to 'web/api')
-rw-r--r--web/api/formatters/json_wrapper.c19
-rw-r--r--web/api/formatters/rrdset2json.c5
-rw-r--r--web/api/netdata-swagger.json62
-rw-r--r--web/api/netdata-swagger.yaml41
-rw-r--r--web/api/web_api_v1.c109
5 files changed, 211 insertions, 25 deletions
diff --git a/web/api/formatters/json_wrapper.c b/web/api/formatters/json_wrapper.c
index 3ebe42c99f..811afa8921 100644
--- a/web/api/formatters/json_wrapper.c
+++ b/web/api/formatters/json_wrapper.c
@@ -175,6 +175,25 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
buffer_strcat(wb, "],\n");
}
+ // functions
+ {
+ DICTIONARY *funcs = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE);
+ for (i = 0, rd = temp_rd ? temp_rd : r->st->dimensions; rd; rd = rd->next) {
+ chart_functions_to_dict(rd->rrdset, funcs);
+ }
+
+ buffer_sprintf(wb, " %sfunctions%s: [", kq, kq);
+ void *t; (void)t;
+ dfe_start_read(funcs, t) {
+ const char *comma = "";
+ if(t_dfe.counter) comma = ", ";
+ buffer_sprintf(wb, "%s%s%s%s", comma, sq, t_dfe.name, sq);
+ }
+ dfe_done(t);
+ dictionary_destroy(funcs);
+ buffer_strcat(wb, "],\n");
+ }
+
// Composite charts
if (context_mode && temp_rd) {
buffer_sprintf(
diff --git a/web/api/formatters/rrdset2json.c b/web/api/formatters/rrdset2json.c
index 7758601e29..1e81063359 100644
--- a/web/api/formatters/rrdset2json.c
+++ b/web/api/formatters/rrdset2json.c
@@ -143,8 +143,11 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor
}
buffer_strcat(wb, ",\n\t\t\t\"chart_labels\": {\n");
chart_labels2json(st, wb, 2);
- buffer_strcat(wb, "\t\t\t}\n");
+ buffer_strcat(wb, "\t\t\t}");
+ buffer_strcat(wb, ",\n\t\t\t\"functions\": {\n");
+ chart_functions2json(st, wb, 4, "\"", "\"");
+ buffer_strcat(wb, "\t\t\t}");
buffer_sprintf(wb,
"\n\t\t}"
diff --git a/web/api/netdata-swagger.json b/web/api/netdata-swagger.json
index 029783b55b..7d9cd7b940 100644
--- a/web/api/netdata-swagger.json
+++ b/web/api/netdata-swagger.json
@@ -1629,6 +1629,68 @@
}
}
},
+ "/function": {
+ "get": {
+ "summary": "Execute a collector function.",
+ "parameters": [
+ {
+ "name": "function",
+ "in": "query",
+ "description": "The name of the function, as returned by the collector.",
+ "required": true,
+ "allowEmptyValue": false,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "timeout",
+ "in": "query",
+ "description": "The timeout in seconds to wait for the function to complete.",
+ "required": false,
+ "schema": {
+ "type": "number",
+ "format": "integer",
+ "default": 10
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The collector function has been executed successfully. Each collector may return a different type of content."
+ },
+ "400": {
+ "description": "The request was rejected by the collector."
+ },
+ "404": {
+ "description": "The requested function is not found."
+ },
+ "500": {
+ "description": "Other internal error, getting this error means there is a bug in Netdata."
+ },
+ "503": {
+ "description": "The collector to execute the function is not currently available."
+ },
+ "504": {
+ "description": "Timeout while waiting for the collector to execute the function."
+ },
+ "591": {
+ "description": "The collector sent a response, but it was invalid or corrupted."
+ }
+ }
+ }
+ },
+ "/functions": {
+ "get": {
+ "summary": "Get a list of all registered collector functions.",
+ "description": "Collector functions are programs that can be executed on demand.",
+ "responses": {
+ "200": {
+ "description": "A JSON object containing one object per supported function."
+ }
+ }
+ }
+ },
"/weights": {
"get": {
"summary": "Analyze all the metrics using an algorithm and score them accordingly",
diff --git a/web/api/netdata-swagger.yaml b/web/api/netdata-swagger.yaml
index 2e04e9f20f..586b456831 100644
--- a/web/api/netdata-swagger.yaml
+++ b/web/api/netdata-swagger.yaml
@@ -1351,6 +1351,47 @@ paths:
that correlated the metrics did not produce any result.
"504":
description: Timeout - the query took too long and has been cancelled.
+ /function:
+ get:
+ summary: "Execute a collector function."
+ parameters:
+ - name: function
+ in: query
+ description: The name of the function, as returned by the collector.
+ required: true
+ allowEmptyValue: false
+ schema:
+ type: string
+ - name: timeout
+ in: query
+ description: The timeout in seconds to wait for the function to complete.
+ required: false
+ schema:
+ type: number
+ format: integer
+ default: 10
+ responses:
+ "200":
+ description: The collector function has been executed successfully. Each collector may return a different type of content.
+ "400":
+ description: The request was rejected by the collector.
+ "404":
+ description: The requested function is not found.
+ "500":
+ description: Other internal error, getting this error means there is a bug in Netdata.
+ "503":
+ description: The collector to execute the function is not currently available.
+ "504":
+ description: Timeout while waiting for the collector to execute the function.
+ "591":
+ description: The collector sent a response, but it was invalid or corrupted.
+ /functions:
+ get:
+ summary: Get a list of all registered collector functions.
+ description: Collector functions are programs that can be executed on demand.
+ responses:
+ "200":
+ description: A JSON object containing one object per supported function.
/weights:
get:
summary: "Analyze all the metrics using an algorithm and score them accordingly"
diff --git a/web/api/web_api_v1.c b/web/api/web_api_v1.c
index f261b8ee59..a7cf5d6223 100644
--- a/web/api/web_api_v1.c
+++ b/web/api/web_api_v1.c
@@ -1200,6 +1200,10 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
host_labels2json(host, wb, 2);
buffer_strcat(wb, "\t},\n");
+ buffer_strcat(wb, "\t\"functions\": {\n");
+ host_functions2json(host, wb, 2, "\"", "\"");
+ buffer_strcat(wb, "\t},\n");
+
buffer_strcat(wb, "\t\"collectors\": [");
chartcollectors2json(host, wb);
buffer_strcat(wb, "\n\t],\n");
@@ -1250,7 +1254,7 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
#ifdef ENABLE_COMPRESSION
if(host->sender){
buffer_strcat(wb, "\t\"stream-compression\": ");
- buffer_strcat(wb, (host->sender->rrdpush_compression ? "true" : "false"));
+ buffer_strcat(wb, (host->sender->flags & SENDER_FLAG_COMPRESSION) ? "true" : "false");
buffer_strcat(wb, ",\n");
}else{
buffer_strcat(wb, "\t\"stream-compression\": null,\n");
@@ -1483,6 +1487,53 @@ int web_client_api_request_v1_weights(RRDHOST *host, struct web_client *w, char
return web_client_api_request_v1_weights_internal(host, w, url, WEIGHTS_METHOD_ANOMALY_RATE, WEIGHTS_FORMAT_CONTEXTS);
}
+int web_client_api_request_v1_function(RRDHOST *host, struct web_client *w, char *url) {
+ if (!netdata_ready)
+ return HTTP_RESP_BACKEND_FETCH_FAILED;
+
+ int timeout = 0;
+ const char *function = NULL;
+
+ while (url) {
+ char *value = mystrsep(&url, "&");
+ if (!value || !*value)
+ continue;
+
+ char *name = mystrsep(&value, "=");
+ if (!name || !*name)
+ continue;
+
+ if (!strcmp(name, "function"))
+ function = value;
+
+ else if (!strcmp(name, "timeout"))
+ timeout = (int) strtoul(value, NULL, 0);
+ }
+
+ BUFFER *wb = w->response.data;
+ buffer_flush(wb);
+ wb->contenttype = CT_APPLICATION_JSON;
+ buffer_no_cacheable(wb);
+
+ return rrd_call_function_and_wait(host, wb, timeout, function);
+}
+
+int web_client_api_request_v1_functions(RRDHOST *host, struct web_client *w, char *url __maybe_unused) {
+ if (!netdata_ready)
+ return HTTP_RESP_BACKEND_FETCH_FAILED;
+
+ BUFFER *wb = w->response.data;
+ buffer_flush(wb);
+ wb->contenttype = CT_APPLICATION_JSON;
+ buffer_no_cacheable(wb);
+
+ buffer_strcat(wb, "{\n");
+ host_functions2json(host, wb, 1, "\"", "\"");
+ buffer_strcat(wb, "}");
+
+ return HTTP_RESP_OK;
+}
+
#ifndef ENABLE_DBENGINE
int web_client_api_request_v1_dbengine_stats(RRDHOST *host, struct web_client *w, char *url) {
return HTTP_RESP_NOT_FOUND;
@@ -1585,47 +1636,57 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc
}
#endif
+#ifdef NETDATA_DEV_MODE
+#define ACL_DEV_OPEN_ACCESS WEB_CLIENT_ACL_DASHBOARD
+#else
+#define ACL_DEV_OPEN_ACCESS 0
+#endif
+
static struct api_command {
const char *command;
uint32_t hash;
WEB_CLIENT_ACL acl;
int (*callback)(RRDHOST *host, struct web_client *w, char *url);
} api_commands[] = {
- { "info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_info },
- { "data", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_data },
- { "chart", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_chart },
- { "charts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_charts },
- { "context", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_context },
- { "contexts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_contexts },
- { "archivedcharts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_archivedcharts },
+ { "info", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_info },
+ { "data", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_data },
+ { "chart", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_chart },
+ { "charts", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_charts },
+ { "context", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_context },
+ { "contexts", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_contexts },
+ { "archivedcharts", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_archivedcharts },
// registry checks the ACL by itself, so we allow everything
- { "registry", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v1_registry },
+ { "registry", 0, WEB_CLIENT_ACL_NOCHECK, web_client_api_request_v1_registry },
// badges can be fetched with both dashboard and badge permissions
- { "badge.svg", 0, WEB_CLIENT_ACL_DASHBOARD|WEB_CLIENT_ACL_BADGE, web_client_api_request_v1_badge },
+ { "badge.svg", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_badge },
- { "alarms", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarms },
- { "alarms_values", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarms_values },
- { "alarm_log", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_log },
- { "alarm_variables", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_variables },
- { "alarm_count", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_count },
- { "allmetrics", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_allmetrics },
+ { "alarms", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarms },
+ { "alarms_values", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarms_values },
+ { "alarm_log", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarm_log },
+ { "alarm_variables", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarm_variables },
+ { "alarm_count", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_alarm_count },
+ { "allmetrics", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_allmetrics },
#if defined(ENABLE_ML)
- { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_info },
- { "ml_models", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_models },
+ { "anomaly_events", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_anomaly_events },
+ { "anomaly_event_info", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_anomaly_event_info },
+ { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_ml_info },
#endif
- { "manage/health", 0, WEB_CLIENT_ACL_MGMT, web_client_api_request_v1_mgmt_health },
- { "aclk", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_aclk_state },
- { "metric_correlations", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_metric_correlations },
- { "weights", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_weights },
+ { "manage/health", 0, WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_mgmt_health },
+ { "aclk", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_aclk_state },
+ { "metric_correlations", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_metric_correlations },
+ { "weights", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_weights },
+
+ { "function", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_function },
+ { "functions", 0, WEB_CLIENT_ACL_ACLK | ACL_DEV_OPEN_ACCESS, web_client_api_request_v1_functions },
- { "dbengine_stats", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_dbengine_stats },
+ { "dbengine_stats", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_dbengine_stats },
// terminator
- { NULL, 0, WEB_CLIENT_ACL_NONE, NULL },
+ { NULL, 0, WEB_CLIENT_ACL_NONE, NULL },
};
inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {