summaryrefslogtreecommitdiffstats
path: root/web/api/web_api_v1.c
diff options
context:
space:
mode:
Diffstat (limited to 'web/api/web_api_v1.c')
-rw-r--r--web/api/web_api_v1.c443
1 files changed, 203 insertions, 240 deletions
diff --git a/web/api/web_api_v1.c b/web/api/web_api_v1.c
index 8bfc617fd..93f501f9e 100644
--- a/web/api/web_api_v1.c
+++ b/web/api/web_api_v1.c
@@ -34,20 +34,20 @@ static struct {
, {"match-ids" , 0 , RRDR_OPTION_MATCH_IDS}
, {"match_names" , 0 , RRDR_OPTION_MATCH_NAMES}
, {"match-names" , 0 , RRDR_OPTION_MATCH_NAMES}
- , {"showcustomvars" , 0 , RRDR_OPTION_CUSTOM_VARS}
, {"anomaly-bit" , 0 , RRDR_OPTION_ANOMALY_BIT}
, {"selected-tier" , 0 , RRDR_OPTION_SELECTED_TIER}
, {"raw" , 0 , RRDR_OPTION_RETURN_RAW}
, {"jw-anomaly-rates" , 0 , RRDR_OPTION_RETURN_JWAR}
, {"natural-points" , 0 , RRDR_OPTION_NATURAL_POINTS}
, {"virtual-points" , 0 , RRDR_OPTION_VIRTUAL_POINTS}
+ , {"all-dimensions" , 0 , RRDR_OPTION_ALL_DIMENSIONS}
, {NULL , 0 , 0}
};
static struct {
const char *name;
uint32_t hash;
- uint32_t value;
+ DATASOURCE_FORMAT value;
} api_v1_data_formats[] = {
{ DATASOURCE_FORMAT_DATATABLE_JSON , 0 , DATASOURCE_DATATABLE_JSON}
, {DATASOURCE_FORMAT_DATATABLE_JSONP, 0 , DATASOURCE_DATATABLE_JSONP}
@@ -68,7 +68,7 @@ static struct {
static struct {
const char *name;
uint32_t hash;
- uint32_t value;
+ DATASOURCE_FORMAT value;
} api_v1_data_google_formats[] = {
// this is not error - when google requests json, it expects javascript
// https://developers.google.com/chart/interactive/docs/dev/implementing_data_source#responseformat
@@ -185,20 +185,46 @@ inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) {
return ret;
}
-void web_client_api_request_v1_data_options_to_string(BUFFER *wb, RRDR_OPTIONS options) {
+void web_client_api_request_v1_data_options_to_buffer(BUFFER *wb, RRDR_OPTIONS options) {
RRDR_OPTIONS used = 0; // to prevent adding duplicates
int added = 0;
for(int i = 0; api_v1_data_options[i].name ; i++) {
if (unlikely((api_v1_data_options[i].value & options) && !(api_v1_data_options[i].value & used))) {
+ const char *name = api_v1_data_options[i].name;
+ used |= api_v1_data_options[i].value;
+
if(added) buffer_strcat(wb, ",");
- buffer_strcat(wb, api_v1_data_options[i].name);
+ buffer_strcat(wb, name);
+
+ added++;
+ }
+ }
+}
+
+void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RRDR_OPTIONS options) {
+ char *write = buf;
+ char *end = &buf[size - 1];
+
+ RRDR_OPTIONS used = 0; // to prevent adding duplicates
+ int added = 0;
+ for(int i = 0; api_v1_data_options[i].name ; i++) {
+ if (unlikely((api_v1_data_options[i].value & options) && !(api_v1_data_options[i].value & used))) {
+ const char *name = api_v1_data_options[i].name;
used |= api_v1_data_options[i].value;
+
+ if(added && write < end)
+ *write++ = ',';
+
+ while(*name && write < end)
+ *write++ = *name++;
+
added++;
}
}
+ *write = *end = '\0';
}
-inline uint32_t web_client_api_request_v1_data_format(char *name) {
+inline DATASOURCE_FORMAT web_client_api_request_v1_data_format(char *name) {
uint32_t hash = simple_hash(name);
int i;
@@ -584,16 +610,14 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
char *group_time_str = NULL;
char *points_str = NULL;
char *timeout_str = NULL;
- char *max_anomaly_rates_str = NULL;
char *context = NULL;
char *chart_label_key = NULL;
char *chart_labels_filter = NULL;
char *group_options = NULL;
- int tier = 0;
- int group = RRDR_GROUPING_AVERAGE;
- int show_dimensions = 0;
- uint32_t format = DATASOURCE_JSON;
- uint32_t options = 0x00000000;
+ size_t tier = 0;
+ RRDR_GROUPING group = RRDR_GROUPING_AVERAGE;
+ DATASOURCE_FORMAT format = DATASOURCE_JSON;
+ RRDR_OPTIONS options = 0;
while(url) {
char *value = mystrsep(&url, "&");
@@ -617,7 +641,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
buffer_strcat(dimensions, "|");
buffer_strcat(dimensions, value);
}
- else if(!strcmp(name, "show_dimensions")) show_dimensions = 1;
+ else if(!strcmp(name, "show_dimensions")) options |= RRDR_OPTION_ALL_DIMENSIONS;
else if(!strcmp(name, "after")) after_str = value;
else if(!strcmp(name, "before")) before_str = value;
else if(!strcmp(name, "points")) points_str = value;
@@ -670,13 +694,12 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
outFileName = tqx_value;
}
}
- else if(!strcmp(name, "max_anomaly_rates")) {
- max_anomaly_rates_str = value;
- }
else if(!strcmp(name, "tier")) {
- tier = str2i(value);
- if(tier >= 0 && tier < storage_tiers)
+ tier = str2ul(value);
+ if(tier < storage_tiers)
options |= RRDR_OPTION_SELECTED_TIER;
+ else
+ tier = 0;
}
}
@@ -690,81 +713,17 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
RRDSET *st = NULL;
ONEWAYALLOC *owa = onewayalloc_create(0);
+ QUERY_TARGET *qt = NULL;
- if((!chart || !*chart) && (!context)) {
- buffer_sprintf(w->response.data, "No chart id is given at the request.");
+ if(!is_valid_sp(chart) && !is_valid_sp(context)) {
+ buffer_sprintf(w->response.data, "No chart or context is given.");
goto cleanup;
}
- struct context_param *context_param_list = NULL;
-
- if (context && !chart) {
- RRDSET *st1;
-
- uint32_t context_hash = simple_hash(context);
-
- SIMPLE_PATTERN *chart_label_key_pattern = NULL;
- if(chart_label_key)
- chart_label_key_pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);
-
- SIMPLE_PATTERN *chart_labels_filter_pattern = NULL;
- if(chart_labels_filter)
- chart_labels_filter_pattern = simple_pattern_create(chart_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);
-
- rrdhost_rdlock(host);
- rrdset_foreach_read(st1, host) {
- if (st1->hash_context == context_hash && !strcmp(st1->context, context) &&
- (!chart_label_key_pattern || rrdlabels_match_simple_pattern_parsed(st1->state->chart_labels, chart_label_key_pattern, ':')) &&
- (!chart_labels_filter_pattern || rrdlabels_match_simple_pattern_parsed(st1->state->chart_labels, chart_labels_filter_pattern, ':')))
- build_context_param_list(owa, &context_param_list, st1);
- }
- rrdhost_unlock(host);
-
- if (likely(context_param_list && context_param_list->rd)) // Just set the first one
- st = context_param_list->rd->rrdset;
- else {
- if (!chart_label_key && !chart_labels_filter)
- sql_build_context_param_list(owa, &context_param_list, host, context, NULL);
- }
- }
- else {
+ if(chart && !context) {
+ // check if this is a specific chart
st = rrdset_find(host, chart);
- if (!st)
- st = rrdset_find_byname(host, chart);
- if (likely(st))
- st->last_accessed_time = now_realtime_sec();
- else
- sql_build_context_param_list(owa, &context_param_list, host, NULL, chart);
- }
-
- if (!st) {
- if (likely(context_param_list && context_param_list->rd && context_param_list->rd->rrdset))
- st = context_param_list->rd->rrdset;
- else {
- free_context_param_list(owa, &context_param_list);
- context_param_list = NULL;
- }
- }
-
- if (!st && !context_param_list) {
- if (context && !chart) {
- if (!chart_label_key) {
- buffer_strcat(w->response.data, "Context is not found: ");
- buffer_strcat_htmlescape(w->response.data, context);
- } else {
- buffer_strcat(w->response.data, "Context: ");
- buffer_strcat_htmlescape(w->response.data, context);
- buffer_strcat(w->response.data, " or chart label key: ");
- buffer_strcat_htmlescape(w->response.data, chart_label_key);
- buffer_strcat(w->response.data, " not found");
- }
- }
- else {
- buffer_strcat(w->response.data, "Chart is not found: ");
- buffer_strcat_htmlescape(w->response.data, chart);
- }
- ret = HTTP_RESP_NOT_FOUND;
- goto cleanup;
+ if (!st) st = rrdset_find_byname(host, chart);
}
long long before = (before_str && *before_str)?str2l(before_str):0;
@@ -772,7 +731,35 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
int points = (points_str && *points_str)?str2i(points_str):0;
int timeout = (timeout_str && *timeout_str)?str2i(timeout_str): 0;
long group_time = (group_time_str && *group_time_str)?str2l(group_time_str):0;
- int max_anomaly_rates = (max_anomaly_rates_str && *max_anomaly_rates_str) ? str2i(max_anomaly_rates_str) : 0;
+
+ QUERY_TARGET_REQUEST qtr = {
+ .after = after,
+ .before = before,
+ .host = host,
+ .st = st,
+ .hosts = NULL,
+ .contexts = context,
+ .charts = chart,
+ .dimensions = (dimensions)?buffer_tostring(dimensions):NULL,
+ .timeout = timeout,
+ .points = points,
+ .format = format,
+ .options = options,
+ .group_method = group,
+ .group_options = group_options,
+ .resampling_time = group_time,
+ .tier = tier,
+ .chart_label_key = chart_label_key,
+ .charts_labels_filter = chart_labels_filter,
+ .query_source = QUERY_SOURCE_API_DATA,
+ };
+ qt = query_target_create(&qtr);
+
+ if(!qt || !qt->query.used) {
+ buffer_sprintf(w->response.data, "No metrics where matched to query.");
+ ret = HTTP_RESP_NOT_FOUND;
+ goto cleanup;
+ }
if (timeout) {
struct timeval now;
@@ -782,21 +769,13 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
if (timeout <= 0) {
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "Query timeout exceeded");
- return HTTP_RESP_BACKEND_FETCH_FAILED;
+ ret = HTTP_RESP_BACKEND_FETCH_FAILED;
+ goto cleanup;
}
}
- debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', format '%u', options '0x%08x'"
- , w->id
- , chart
- , (dimensions)?buffer_tostring(dimensions):""
- , after
- , before
- , points
- , group
- , format
- , options
- );
+ debug(D_WEB_CLIENT, "%llu: API command 'data' for chart '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%u', format '%u', options '0x%08x'"
+ , w->id, chart, (dimensions)?buffer_tostring(dimensions):"", after, before , points, group, format, options);
if(outFileName && *outFileName) {
buffer_sprintf(w->response.header, "Content-Disposition: attachment; filename=\"%s\"\r\n", outFileName);
@@ -827,18 +806,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
buffer_strcat(w->response.data, "(");
}
- QUERY_PARAMS query_params = {
- .context_param_list = context_param_list,
- .timeout = timeout,
- .max_anomaly_rates = max_anomaly_rates,
- .show_dimensions = show_dimensions,
- .chart_label_key = chart_label_key,
- .wb = w->response.data};
-
- ret = rrdset2anything_api_v1(owa, st, &query_params, dimensions, format,
- points, after, before, group, group_options, group_time, options, &last_timestamp_in_data, tier);
-
- free_context_param_list(owa, &context_param_list);
+ ret = data_query_execute(owa, w->response.data, qt, &last_timestamp_in_data);
if(format == DATASOURCE_DATATABLE_JSONP) {
if(google_timestamp < last_timestamp_in_data)
@@ -856,6 +824,10 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
buffer_strcat(w->response.data, ");");
cleanup:
+ if(qt && qt->used) {
+ internal_error(true, "QUERY_TARGET: left non-released on query '%s'", qt->id);
+ query_target_release(qt);
+ }
onewayalloc_destroy(owa);
buffer_free(dimensions);
return ret;
@@ -1054,8 +1026,7 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
static inline void web_client_api_request_v1_info_summary_alarm_statuses(RRDHOST *host, BUFFER *wb) {
int alarm_normal = 0, alarm_warn = 0, alarm_crit = 0;
RRDCALC *rc;
- rrdhost_rdlock(host);
- for(rc = host->alarms; rc ; rc = rc->next) {
+ foreach_rrdcalc_in_rrdhost_read(host, rc) {
if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
continue;
@@ -1070,7 +1041,7 @@ static inline void web_client_api_request_v1_info_summary_alarm_statuses(RRDHOST
alarm_normal++;
}
}
- rrdhost_unlock(host);
+ foreach_rrdcalc_in_rrdhost_done(rc);
buffer_sprintf(wb, "\t\t\"normal\": %d,\n", alarm_normal);
buffer_sprintf(wb, "\t\t\"warning\": %d,\n", alarm_warn);
buffer_sprintf(wb, "\t\t\"critical\": %d\n", alarm_crit);
@@ -1086,7 +1057,7 @@ static inline void web_client_api_request_v1_info_mirrored_hosts(BUFFER *wb) {
if (count > 0)
buffer_strcat(wb, ",\n");
- buffer_sprintf(wb, "\t\t\"%s\"", host->hostname);
+ buffer_sprintf(wb, "\t\t\"%s\"", rrdhost_hostname(host));
count++;
}
@@ -1101,7 +1072,7 @@ static inline void web_client_api_request_v1_info_mirrored_hosts(BUFFER *wb) {
buffer_sprintf(
wb, "\t\t{ \"guid\": \"%s\", \"hostname\": \"%s\", \"reachable\": %s, \"hops\": %d"
, host->machine_guid
- , host->hostname
+ , rrdhost_hostname(host)
, (host->receiver || host == localhost) ? "true" : "false"
, host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1
);
@@ -1140,7 +1111,7 @@ inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) {
indentation--;
}
- rrdlabels_to_buffer(host->host_labels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL);
+ rrdlabels_to_buffer(host->rrdlabels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL);
buffer_strcat(wb, "\n");
}
@@ -1148,7 +1119,7 @@ extern int aclk_connected;
inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
{
buffer_strcat(wb, "{\n");
- buffer_sprintf(wb, "\t\"version\": \"%s\",\n", host->program_version);
+ buffer_sprintf(wb, "\t\"version\": \"%s\",\n", rrdhost_program_version(host));
buffer_sprintf(wb, "\t\"uid\": \"%s\",\n", host->machine_guid);
web_client_api_request_v1_info_mirrored_hosts(wb);
@@ -1202,6 +1173,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");
@@ -1215,14 +1190,8 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
#ifdef ENABLE_ACLK
buffer_strcat(wb, "\t\"cloud-available\": true,\n");
- buffer_strcat(wb, "\t\"aclk-ng-available\": true,\n");
- buffer_strcat(wb, "\t\"aclk-ng-new-cloud-protocol\": true,\n");
- buffer_strcat(wb, "\t\"aclk-legacy-available\": false,\n");
- buffer_strcat(wb, "\t\"aclk-implementation\": \"Next Generation\",\n");
#else
buffer_strcat(wb, "\t\"cloud-available\": false,\n");
- buffer_strcat(wb, "\t\"aclk-ng-available\": false,\n");
- buffer_strcat(wb, "\t\"aclk-legacy-available\": false,\n");
#endif
char *agent_id = get_agent_claimid();
if (agent_id == NULL)
@@ -1234,11 +1203,10 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
#ifdef ENABLE_ACLK
if (aclk_connected) {
buffer_strcat(wb, "\t\"aclk-available\": true,\n");
- buffer_strcat(wb, "\t\"aclk-available-protocol\": \"New\",\n");
}
else
#endif
- buffer_strcat(wb, "\t\"aclk-available\": false,\n\t\"aclk-available-protocol\": null,\n"); // Intentionally valid with/without #ifdef above
+ buffer_strcat(wb, "\t\"aclk-available\": false,\n"); // Intentionally valid with/without #ifdef above
buffer_strcat(wb, "\t\"memory-mode\": ");
analytics_get_data(analytics_data.netdata_config_memory_mode, wb);
@@ -1259,7 +1227,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, stream_has_capability(host->sender, STREAM_CAP_COMPRESSION) ? "true" : "false");
buffer_strcat(wb, ",\n");
}else{
buffer_strcat(wb, "\t\"stream-compression\": null,\n");
@@ -1330,7 +1298,7 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
buffer_strcat(wb, "\t\"ml-info\": ");
buffer_strcat(wb, ml_info);
- free(ml_info);
+ freez(ml_info);
#endif
buffer_strcat(wb, "\n}");
@@ -1338,81 +1306,15 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb)
}
#if defined(ENABLE_ML)
-int web_client_api_request_v1_anomaly_events(RRDHOST *host, struct web_client *w, char *url) {
- if (!netdata_ready)
- return HTTP_RESP_BACKEND_FETCH_FAILED;
-
- uint32_t after = 0, before = 0;
-
- while (url) {
- char *value = mystrsep(&url, "&");
- if (!value || !*value)
- continue;
-
- char *name = mystrsep(&value, "=");
- if (!name || !*name)
- continue;
- if (!value || !*value)
- continue;
-
- if (!strcmp(name, "after"))
- after = (uint32_t) (strtoul(value, NULL, 0) / 1000);
- else if (!strcmp(name, "before"))
- before = (uint32_t) (strtoul(value, NULL, 0) / 1000);
- }
-
- char *s;
- if (!before || !after)
- s = strdupz("{\"error\": \"missing after/before parameters\" }\n");
- else {
- s = ml_get_anomaly_events(host, "AD1", 1, after, before);
- if (!s)
- s = strdupz("{\"error\": \"json string is empty\" }\n");
- }
-
- BUFFER *wb = w->response.data;
- buffer_flush(wb);
-
- wb->contenttype = CT_APPLICATION_JSON;
- buffer_strcat(wb, s);
- buffer_no_cacheable(wb);
-
- freez(s);
-
- return HTTP_RESP_OK;
-}
+int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char *url) {
+ (void) url;
-int web_client_api_request_v1_anomaly_event_info(RRDHOST *host, struct web_client *w, char *url) {
if (!netdata_ready)
return HTTP_RESP_BACKEND_FETCH_FAILED;
- uint32_t after = 0, before = 0;
-
- while (url) {
- char *value = mystrsep(&url, "&");
- if (!value || !*value)
- continue;
-
- char *name = mystrsep(&value, "=");
- if (!name || !*name)
- continue;
- if (!value || !*value)
- continue;
-
- if (!strcmp(name, "after"))
- after = (uint32_t) strtoul(value, NULL, 0);
- else if (!strcmp(name, "before"))
- before = (uint32_t) strtoul(value, NULL, 0);
- }
-
- char *s;
- if (!before || !after)
- s = strdupz("{\"error\": \"missing after/before parameters\" }\n");
- else {
- s = ml_get_anomaly_event_info(host, "AD1", 1, after, before);
- if (!s)
- s = strdupz("{\"error\": \"json string is empty\" }\n");
- }
+ char *s = ml_get_host_runtime_info(host);
+ if (!s)
+ s = strdupz("{\"error\": \"json string is empty\" }\n");
BUFFER *wb = w->response.data;
buffer_flush(wb);
@@ -1424,13 +1326,13 @@ int web_client_api_request_v1_anomaly_event_info(RRDHOST *host, struct web_clien
return HTTP_RESP_OK;
}
-int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char *url) {
+int web_client_api_request_v1_ml_models(RRDHOST *host, struct web_client *w, char *url) {
(void) url;
if (!netdata_ready)
return HTTP_RESP_BACKEND_FETCH_FAILED;
- char *s = ml_get_host_runtime_info(host);
+ char *s = ml_get_host_models(host);
if (!s)
s = strdupz("{\"error\": \"json string is empty\" }\n");
@@ -1443,8 +1345,7 @@ int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char
freez(s);
return HTTP_RESP_OK;
}
-
-#endif // defined(ENABLE_ML)
+#endif
inline int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url) {
(void)url;
@@ -1485,7 +1386,7 @@ static int web_client_api_request_v1_weights_internal(RRDHOST *host, struct web_
int options_count = 0;
RRDR_GROUPING group = RRDR_GROUPING_AVERAGE;
int timeout = 0;
- int tier = 0;
+ size_t tier = 0;
const char *group_options = NULL, *contexts_str = NULL;
while (url) {
@@ -1533,9 +1434,11 @@ static int web_client_api_request_v1_weights_internal(RRDHOST *host, struct web_
contexts_str = value;
else if(!strcmp(name, "tier")) {
- tier = str2i(value);
- if(tier >= 0 && tier < storage_tiers)
+ tier = str2ul(value);
+ if(tier < storage_tiers)
options |= RRDR_OPTION_SELECTED_TIER;
+ else
+ tier = 0;
}
}
@@ -1559,12 +1462,59 @@ 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;
}
#else
-static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, int tier) {
+static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, size_t tier) {
RRDENG_SIZE_STATS stats = rrdeng_size_statistics(multidb_ctx[tier]);
buffer_sprintf(wb,
@@ -1588,11 +1538,11 @@ static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, int tier) {
",\n\t\t\"metrics_pages\":%zu"
",\n\t\t\"extents_compressed_bytes\":%zu"
",\n\t\t\"pages_uncompressed_bytes\":%zu"
- ",\n\t\t\"pages_duration_secs\":%ld"
+ ",\n\t\t\"pages_duration_secs\":%lld"
",\n\t\t\"single_point_pages\":%zu"
",\n\t\t\"first_t\":%llu"
",\n\t\t\"last_t\":%llu"
- ",\n\t\t\"database_retention_secs\":%ld"
+ ",\n\t\t\"database_retention_secs\":%lld"
",\n\t\t\"average_compression_savings\":%0.2f"
",\n\t\t\"average_point_duration_secs\":%0.2f"
",\n\t\t\"average_metric_retention_secs\":%0.2f"
@@ -1623,11 +1573,11 @@ static void web_client_api_v1_dbengine_stats_for_tier(BUFFER *wb, int tier) {
, stats.metrics_pages
, stats.extents_compressed_bytes
, stats.pages_uncompressed_bytes
- , stats.pages_duration_secs
+ , (long long)stats.pages_duration_secs
, stats.single_point_pages
, stats.first_t
, stats.last_t
- , stats.database_retention_secs
+ , (long long)stats.database_retention_secs
, stats.average_compression_savings
, stats.average_point_duration_secs
, stats.average_metric_retention_secs
@@ -1646,12 +1596,17 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc
BUFFER *wb = w->response.data;
buffer_flush(wb);
+
+ if(!dbengine_enabled) {
+ buffer_strcat(wb, "dbengine is not enabled");
+ return HTTP_RESP_NOT_FOUND;
+ }
+
wb->contenttype = CT_APPLICATION_JSON;
buffer_no_cacheable(wb);
-
buffer_strcat(wb, "{");
- for(int tier = 0; tier < storage_tiers ;tier++) {
- buffer_sprintf(wb, "%s\n\t\"tier%d\": {", tier?",":"", tier);
+ for(size_t tier = 0; tier < storage_tiers ;tier++) {
+ buffer_sprintf(wb, "%s\n\t\"tier%zu\": {", tier?",":"", tier);
web_client_api_v1_dbengine_stats_for_tier(wb, tier);
buffer_strcat(wb, "\n\t}");
}
@@ -1661,48 +1616,56 @@ 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)
- { "anomaly_events", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_anomaly_events },
- { "anomaly_event_info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_anomaly_event_info },
- { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_info },
+ { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_ACLK, web_client_api_request_v1_ml_info },
+ { "ml_models", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_ml_models },
#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) {