From 03bf87dcb06f7021bfb2df2fa8691593c6148aff Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 30 Nov 2022 19:47:00 +0100 Subject: Adding upstream version 1.37.0. Signed-off-by: Daniel Baumann --- web/api/web_api_v1.c | 443 +++++++++++++++++++++++---------------------------- 1 file changed, 203 insertions(+), 240 deletions(-) (limited to 'web/api/web_api_v1.c') 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) { -- cgit v1.2.3