diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-05-08 16:27:08 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-05-08 16:27:08 +0000 |
commit | 81581f9719bc56f01d5aa08952671d65fda9867a (patch) | |
tree | 0f5c6b6138bf169c23c9d24b1fc0a3521385cb18 /web/api/web_api_v1.c | |
parent | Releasing debian version 1.38.1-1. (diff) | |
download | netdata-81581f9719bc56f01d5aa08952671d65fda9867a.tar.xz netdata-81581f9719bc56f01d5aa08952671d65fda9867a.zip |
Merging upstream version 1.39.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'web/api/web_api_v1.c')
-rw-r--r-- | web/api/web_api_v1.c | 764 |
1 files changed, 279 insertions, 485 deletions
diff --git a/web/api/web_api_v1.c b/web/api/web_api_v1.c index 1b38a33b1..6e23549d4 100644 --- a/web/api/web_api_v1.c +++ b/web/api/web_api_v1.c @@ -41,7 +41,12 @@ static struct { , {"natural-points" , 0 , RRDR_OPTION_NATURAL_POINTS} , {"virtual-points" , 0 , RRDR_OPTION_VIRTUAL_POINTS} , {"all-dimensions" , 0 , RRDR_OPTION_ALL_DIMENSIONS} - , {"plan" , 0 , RRDR_OPTION_SHOW_PLAN} + , {"details" , 0 , RRDR_OPTION_SHOW_DETAILS} + , {"debug" , 0 , RRDR_OPTION_DEBUG} + , {"plan" , 0 , RRDR_OPTION_DEBUG} + , {"minify" , 0 , RRDR_OPTION_MINIFY} + , {"group-by-labels" , 0 , RRDR_OPTION_GROUP_BY_LABELS} + , {"label-quotes" , 0 , RRDR_OPTION_LABEL_QUOTES} , {NULL , 0 , 0} }; @@ -53,6 +58,7 @@ static struct { { DATASOURCE_FORMAT_DATATABLE_JSON , 0 , DATASOURCE_DATATABLE_JSON} , {DATASOURCE_FORMAT_DATATABLE_JSONP, 0 , DATASOURCE_DATATABLE_JSONP} , {DATASOURCE_FORMAT_JSON , 0 , DATASOURCE_JSON} + , {DATASOURCE_FORMAT_JSON2 , 0 , DATASOURCE_JSON2} , {DATASOURCE_FORMAT_JSONP , 0 , DATASOURCE_JSONP} , {DATASOURCE_FORMAT_SSV , 0 , DATASOURCE_SSV} , {DATASOURCE_FORMAT_CSV , 0 , DATASOURCE_CSV} @@ -63,7 +69,9 @@ static struct { , {DATASOURCE_FORMAT_SSV_COMMA , 0 , DATASOURCE_SSV_COMMA} , {DATASOURCE_FORMAT_CSV_JSON_ARRAY , 0 , DATASOURCE_CSV_JSON_ARRAY} , {DATASOURCE_FORMAT_CSV_MARKDOWN , 0 , DATASOURCE_CSV_MARKDOWN} - , { NULL, 0, 0} + + // terminator + , {NULL, 0, 0} }; static struct { @@ -92,7 +100,7 @@ void web_client_api_v1_init(void) { for(i = 0; api_v1_data_google_formats[i].name ; i++) api_v1_data_google_formats[i].hash = simple_hash(api_v1_data_google_formats[i].name); - web_client_api_v1_init_grouping(); + time_grouping_init(); uuid_t uuid; @@ -170,7 +178,7 @@ inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) { RRDR_OPTIONS ret = 0x00000000; char *tok; - while(o && *o && (tok = mystrsep(&o, ", |"))) { + while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) { if(!*tok) continue; uint32_t hash = simple_hash(tok); @@ -186,20 +194,20 @@ inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) { return ret; } -void web_client_api_request_v1_data_options_to_buffer(BUFFER *wb, RRDR_OPTIONS options) { +void web_client_api_request_v1_data_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options) { + buffer_json_member_add_array(wb, key); + 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, name); - - added++; + buffer_json_add_array_item_string(wb, name); } } + + buffer_json_array_close(wb); } void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RRDR_OPTIONS options) { @@ -254,11 +262,11 @@ inline uint32_t web_client_api_request_v1_data_google_format(char *name) { int web_client_api_request_v1_alarms_select (char *url) { int all = 0; while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if (!value || !*value) continue; - if(!strcmp(value, "all")) all = 1; - else if(!strcmp(value, "active")) all = 0; + if(!strcmp(value, "all") || !strcmp(value, "all=true")) all = 1; + else if(!strcmp(value, "active") || !strcmp(value, "active=true")) all = 0; } return all; @@ -268,7 +276,7 @@ inline int web_client_api_request_v1_alarms(RRDHOST *host, struct web_client *w, int all = web_client_api_request_v1_alarms_select(url); buffer_flush(w->response.data); - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; health_alarms2json(host, w->response.data, all); buffer_no_cacheable(w->response.data); return HTTP_RESP_OK; @@ -278,7 +286,7 @@ inline int web_client_api_request_v1_alarms_values(RRDHOST *host, struct web_cli int all = web_client_api_request_v1_alarms_select(url); buffer_flush(w->response.data); - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; health_alarms_values2json(host, w->response.data, all); buffer_no_cacheable(w->response.data); return HTTP_RESP_OK; @@ -292,10 +300,10 @@ inline int web_client_api_request_v1_alarm_count(RRDHOST *host, struct web_clien buffer_sprintf(w->response.data, "["); while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if(!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if(!name || !*name) continue; if(!value || !*value) continue; @@ -321,7 +329,7 @@ inline int web_client_api_request_v1_alarm_count(RRDHOST *host, struct web_clien health_aggregate_alarms(host, w->response.data, contexts, status); buffer_sprintf(w->response.data, "]\n"); - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; buffer_no_cacheable(w->response.data); buffer_free(contexts); @@ -333,10 +341,10 @@ inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client char *chart = NULL; while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if (!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if(!name || !*name) continue; if(!value || !*value) continue; @@ -345,7 +353,7 @@ inline int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client } buffer_flush(w->response.data); - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; health_alarm_log2json(host, w->response.data, after, chart); return HTTP_RESP_OK; } @@ -357,10 +365,10 @@ inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client buffer_flush(w->response.data); while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if(!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if(!name || !*name) continue; if(!value || !*value) continue; @@ -388,7 +396,7 @@ inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client goto cleanup; } - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; st->last_accessed_time_s = now_realtime_sec(); callback(st, w->response.data); return HTTP_RESP_OK; @@ -401,38 +409,6 @@ inline int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_c return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json); } -static RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o) { - RRDCONTEXT_TO_JSON_OPTIONS options = RRDCONTEXT_OPTION_NONE; - char *tok; - - while(o && *o && (tok = mystrsep(&o, ", |"))) { - if(!*tok) continue; - - if(!strcmp(tok, "full") || !strcmp(tok, "all")) - options |= RRDCONTEXT_OPTIONS_ALL; - else if(!strcmp(tok, "charts") || !strcmp(tok, "instances")) - options |= RRDCONTEXT_OPTION_SHOW_INSTANCES; - else if(!strcmp(tok, "dimensions") || !strcmp(tok, "metrics")) - options |= RRDCONTEXT_OPTION_SHOW_METRICS; - else if(!strcmp(tok, "queue")) - options |= RRDCONTEXT_OPTION_SHOW_QUEUED; - else if(!strcmp(tok, "flags")) - options |= RRDCONTEXT_OPTION_SHOW_FLAGS; - else if(!strcmp(tok, "uuids")) - options |= RRDCONTEXT_OPTION_SHOW_UUIDS; - else if(!strcmp(tok, "deleted")) - options |= RRDCONTEXT_OPTION_SHOW_DELETED; - else if(!strcmp(tok, "labels")) - options |= RRDCONTEXT_OPTION_SHOW_LABELS; - else if(!strcmp(tok, "deepscan")) - options |= RRDCONTEXT_OPTION_DEEPSCAN; - else if(!strcmp(tok, "hidden")) - options |= RRDCONTEXT_OPTION_SHOW_HIDDEN; - } - - return options; -} - static int web_client_api_request_v1_context(RRDHOST *host, struct web_client *w, char *url) { char *context = NULL; RRDCONTEXT_TO_JSON_OPTIONS options = RRDCONTEXT_OPTION_NONE; @@ -443,10 +419,10 @@ static int web_client_api_request_v1_context(RRDHOST *host, struct web_client *w buffer_flush(w->response.data); while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if(!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if(!name || !*name) continue; if(!value || !*value) continue; @@ -476,17 +452,19 @@ static int web_client_api_request_v1_context(RRDHOST *host, struct web_client *w SIMPLE_PATTERN *chart_dimensions_pattern = NULL; if(chart_label_key) - chart_label_key_pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); + chart_label_key_pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT, true); if(chart_labels_filter) - chart_labels_filter_pattern = simple_pattern_create(chart_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); + chart_labels_filter_pattern = simple_pattern_create(chart_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT, + true); if(dimensions) { - chart_dimensions_pattern = simple_pattern_create(buffer_tostring(dimensions), ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); + chart_dimensions_pattern = simple_pattern_create(buffer_tostring(dimensions), ",|\t\r\n\f\v", + SIMPLE_PATTERN_EXACT, true); buffer_free(dimensions); } - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; int ret = rrdcontext_to_json(host, w->response.data, after, before, options, context, chart_label_key_pattern, chart_labels_filter_pattern, chart_dimensions_pattern); simple_pattern_free(chart_label_key_pattern); @@ -505,10 +483,10 @@ static int web_client_api_request_v1_contexts(RRDHOST *host, struct web_client * buffer_flush(w->response.data); while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if(!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if(!name || !*name) continue; if(!value || !*value) continue; @@ -532,17 +510,19 @@ static int web_client_api_request_v1_contexts(RRDHOST *host, struct web_client * SIMPLE_PATTERN *chart_dimensions_pattern = NULL; if(chart_label_key) - chart_label_key_pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); + chart_label_key_pattern = simple_pattern_create(chart_label_key, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT, true); if(chart_labels_filter) - chart_labels_filter_pattern = simple_pattern_create(chart_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); + chart_labels_filter_pattern = simple_pattern_create(chart_labels_filter, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT, + true); if(dimensions) { - chart_dimensions_pattern = simple_pattern_create(buffer_tostring(dimensions), ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT); + chart_dimensions_pattern = simple_pattern_create(buffer_tostring(dimensions), ",|\t\r\n\f\v", + SIMPLE_PATTERN_EXACT, true); buffer_free(dimensions); } - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; int ret = rrdcontexts_to_json(host, w->response.data, after, before, options, chart_label_key_pattern, chart_labels_filter_pattern, chart_dimensions_pattern); simple_pattern_free(chart_label_key_pattern); @@ -556,7 +536,7 @@ inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, (void)url; buffer_flush(w->response.data); - w->response.data->contenttype = CT_APPLICATION_JSON; + w->response.data->content_type = CT_APPLICATION_JSON; charts2json(host, w->response.data, 0, 0); return HTTP_RESP_OK; } @@ -565,18 +545,8 @@ inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart); } -void fix_google_param(char *s) { - if(unlikely(!s)) return; - - for( ; *s ;s++) { - if(!isalnum(*s) && *s != '.' && *s != '_' && *s != '-') - *s = '_'; - } -} - - // returns the HTTP code -inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) { +static inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) { debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url); int ret = HTTP_RESP_BAD_REQUEST; @@ -604,15 +574,15 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c char *chart_labels_filter = NULL; char *group_options = NULL; size_t tier = 0; - RRDR_GROUPING group = RRDR_GROUPING_AVERAGE; + RRDR_TIME_GROUPING group = RRDR_GROUPING_AVERAGE; DATASOURCE_FORMAT format = DATASOURCE_JSON; RRDR_OPTIONS options = 0; while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if(!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if(!name || !*name) continue; if(!value || !*value) continue; @@ -638,7 +608,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c else if(!strcmp(name, "gtime")) group_time_str = value; else if(!strcmp(name, "group_options")) group_options = value; else if(!strcmp(name, "group")) { - group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE); + group = time_grouping_parse(value, RRDR_GROUPING_AVERAGE); } else if(!strcmp(name, "format")) { format = web_client_api_request_v1_data_format(value); @@ -658,10 +628,10 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c char *tqx_name, *tqx_value; while(value) { - tqx_value = mystrsep(&value, ";"); + tqx_value = strsep_skip_consecutive_separators(&value, ";"); if(!tqx_value || !*tqx_value) continue; - tqx_name = mystrsep(&tqx_value, ":"); + tqx_name = strsep_skip_consecutive_separators(&tqx_value, ":"); if(!tqx_name || !*tqx_name) continue; if(!tqx_value || !*tqx_value) continue; @@ -722,26 +692,29 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c long group_time = (group_time_str && *group_time_str)?str2l(group_time_str):0; QUERY_TARGET_REQUEST qtr = { + .version = 1, .after = after, .before = before, .host = host, .st = st, - .hosts = NULL, + .nodes = NULL, .contexts = context, - .charts = chart, + .instances = chart, .dimensions = (dimensions)?buffer_tostring(dimensions):NULL, - .timeout = timeout, + .timeout_ms = timeout, .points = points, .format = format, .options = options, - .group_method = group, - .group_options = group_options, + .time_group_method = group, + .time_group_options = group_options, .resampling_time = group_time, .tier = tier, .chart_label_key = chart_label_key, - .charts_labels_filter = chart_labels_filter, + .labels = chart_labels_filter, .query_source = QUERY_SOURCE_API_DATA, .priority = STORAGE_PRIORITY_NORMAL, + .interrupt_callback = web_client_interrupt_callback, + .interrupt_callback_data = w, }; qt = query_target_create(&qtr); @@ -751,22 +724,12 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c goto cleanup; } - if (timeout) { - struct timeval now; - now_realtime_timeval(&now); - int inqueue = (int)dt_usec(&w->tv_in, &now) / 1000; - timeout -= inqueue; - if (timeout <= 0) { - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Query timeout exceeded"); - ret = HTTP_RESP_BACKEND_FETCH_FAILED; - goto cleanup; - } + web_client_timeout_checkpoint_set(w, timeout); + if(web_client_timeout_checkpoint_and_check(w, NULL)) { + ret = w->response.code; + goto cleanup; } - 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); debug(D_WEB_CLIENT, "%llu: generating outfilename header: '%s'", w->id, outFileName); @@ -814,10 +777,7 @@ 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); - } + query_target_release(qt); onewayalloc_destroy(owa); buffer_free(dimensions); return ret; @@ -886,10 +846,10 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client * buffer_no_cacheable(w->response.data); while(url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if (!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if (!name || !*name) continue; if (!value || !*value) continue; @@ -1013,8 +973,10 @@ 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; +void web_client_api_request_v1_info_summary_alarm_statuses(RRDHOST *host, BUFFER *wb, const char *key) { + buffer_json_member_add_object(wb, key); + + size_t normal = 0, warning = 0, critical = 0; RRDCALC *rc; foreach_rrdcalc_in_rrdhost_read(host, rc) { if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) @@ -1022,274 +984,217 @@ static inline void web_client_api_request_v1_info_summary_alarm_statuses(RRDHOST switch(rc->status) { case RRDCALC_STATUS_WARNING: - alarm_warn++; + warning++; break; case RRDCALC_STATUS_CRITICAL: - alarm_crit++; + critical++; break; default: - alarm_normal++; + normal++; } } 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); + + buffer_json_member_add_uint64(wb, "normal", normal); + buffer_json_member_add_uint64(wb, "warning", warning); + buffer_json_member_add_uint64(wb, "critical", critical); + + buffer_json_object_close(wb); +} + +static inline void web_client_api_request_v1_info_mirrored_hosts_status(BUFFER *wb, RRDHOST *host) { + buffer_json_add_array_item_object(wb); + + buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(host)); + buffer_json_member_add_uint64(wb, "hops", host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1); + buffer_json_member_add_boolean(wb, "reachable", (host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN))); + + buffer_json_member_add_string(wb, "guid", host->machine_guid); + buffer_json_member_add_uuid(wb, "node_id", host->node_id); + rrdhost_aclk_state_lock(host); + buffer_json_member_add_string(wb, "claim_id", host->aclk_state.claimed_id); + rrdhost_aclk_state_unlock(host); + + buffer_json_object_close(wb); } static inline void web_client_api_request_v1_info_mirrored_hosts(BUFFER *wb) { RRDHOST *host; - int count = 0; - buffer_strcat(wb, "\t\"mirrored_hosts\": [\n"); rrd_rdlock(); - rrdhost_foreach_read(host) { - if (count > 0) - buffer_strcat(wb, ",\n"); - buffer_sprintf(wb, "\t\t\"%s\"", rrdhost_hostname(host)); - count++; - } - - buffer_strcat(wb, "\n\t],\n\t\"mirrored_hosts_status\": [\n"); - count = 0; + buffer_json_member_add_array(wb, "mirrored_hosts"); rrdhost_foreach_read(host) - { - if (count > 0) - buffer_strcat(wb, ",\n"); + buffer_json_add_array_item_string(wb, rrdhost_hostname(host)); + buffer_json_array_close(wb); - buffer_sprintf( - wb, "\t\t{ \"guid\": \"%s\", \"hostname\": \"%s\", \"reachable\": %s, \"hops\": %d" - , host->machine_guid - , rrdhost_hostname(host) - , (host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN)) ? "true" : "false" - , host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1 - ); - - rrdhost_aclk_state_lock(host); - if (host->aclk_state.claimed_id) - buffer_sprintf(wb, ", \"claim_id\": \"%s\"", host->aclk_state.claimed_id); - else - buffer_strcat(wb, ", \"claim_id\": null"); - rrdhost_aclk_state_unlock(host); - - if (host->node_id) { - char node_id_str[GUID_LEN + 1]; - uuid_unparse_lower(*host->node_id, node_id_str); - buffer_sprintf(wb, ", \"node_id\": \"%s\" }", node_id_str); - } else - buffer_strcat(wb, ", \"node_id\": null }"); - - count++; + buffer_json_member_add_array(wb, "mirrored_hosts_status"); + rrdhost_foreach_read(host) { + if ((host == localhost || !rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN))) { + web_client_api_request_v1_info_mirrored_hosts_status(wb, host); + } } + rrdhost_foreach_read(host) { + if ((host != localhost && rrdhost_flag_check(host, RRDHOST_FLAG_ORPHAN))) { + web_client_api_request_v1_info_mirrored_hosts_status(wb, host); + } + } + buffer_json_array_close(wb); + rrd_unlock(); +} - buffer_strcat(wb, "\n\t],\n"); +void host_labels2json(RRDHOST *host, BUFFER *wb, const char *key) { + buffer_json_member_add_object(wb, key); + rrdlabels_to_buffer_json_members(host->rrdlabels, wb); + buffer_json_object_close(wb); } -inline void host_labels2json(RRDHOST *host, BUFFER *wb, size_t indentation) { - char tabs[11]; +static void host_collectors(RRDHOST *host, BUFFER *wb) { + buffer_json_member_add_array(wb, "collectors"); + + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_DONT_OVERWRITE_VALUE); + RRDSET *st; + char name[500]; - if (indentation > 10) - indentation = 10; + time_t now = now_realtime_sec(); - tabs[0] = '\0'; - while (indentation) { - strcat(tabs, "\t"); - indentation--; + rrdset_foreach_read(st, host) { + if (!rrdset_is_available_for_viewers(st)) + continue; + + sprintf(name, "%s:%s", rrdset_plugin_name(st), rrdset_module_name(st)); + + bool old = 0; + bool *set = dictionary_set(dict, name, &old, sizeof(bool)); + if(!*set) { + *set = true; + st->last_accessed_time_s = now; + buffer_json_add_array_item_object(wb); + buffer_json_member_add_string(wb, "plugin", rrdset_plugin_name(st)); + buffer_json_member_add_string(wb, "module", rrdset_module_name(st)); + buffer_json_object_close(wb); + } } + rrdset_foreach_done(st); + dictionary_destroy(dict); - rrdlabels_to_buffer(host->rrdlabels, wb, tabs, ":", "\"", ",\n", NULL, NULL, NULL, NULL); - buffer_strcat(wb, "\n"); + buffer_json_array_close(wb); } 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", rrdhost_program_version(host)); - buffer_sprintf(wb, "\t\"uid\": \"%s\",\n", host->machine_guid); +inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) { + buffer_json_initialize(wb, "\"", "\"", 0, true, false); + buffer_json_member_add_string(wb, "version", rrdhost_program_version(host)); + buffer_json_member_add_string(wb, "uid", host->machine_guid); + + buffer_json_member_add_uint64(wb, "hosts-available", rrdhost_hosts_available()); web_client_api_request_v1_info_mirrored_hosts(wb); - buffer_strcat(wb, "\t\"alarms\": {\n"); - web_client_api_request_v1_info_summary_alarm_statuses(host, wb); - buffer_strcat(wb, "\t},\n"); - - buffer_sprintf(wb, "\t\"os_name\": \"%s\",\n", (host->system_info->host_os_name) ? host->system_info->host_os_name : ""); - buffer_sprintf(wb, "\t\"os_id\": \"%s\",\n", (host->system_info->host_os_id) ? host->system_info->host_os_id : ""); - buffer_sprintf(wb, "\t\"os_id_like\": \"%s\",\n", (host->system_info->host_os_id_like) ? host->system_info->host_os_id_like : ""); - buffer_sprintf(wb, "\t\"os_version\": \"%s\",\n", (host->system_info->host_os_version) ? host->system_info->host_os_version : ""); - buffer_sprintf(wb, "\t\"os_version_id\": \"%s\",\n", (host->system_info->host_os_version_id) ? host->system_info->host_os_version_id : ""); - buffer_sprintf(wb, "\t\"os_detection\": \"%s\",\n", (host->system_info->host_os_detection) ? host->system_info->host_os_detection : ""); - buffer_sprintf(wb, "\t\"cores_total\": \"%s\",\n", (host->system_info->host_cores) ? host->system_info->host_cores : ""); - buffer_sprintf(wb, "\t\"total_disk_space\": \"%s\",\n", (host->system_info->host_disk_space) ? host->system_info->host_disk_space : ""); - buffer_sprintf(wb, "\t\"cpu_freq\": \"%s\",\n", (host->system_info->host_cpu_freq) ? host->system_info->host_cpu_freq : ""); - buffer_sprintf(wb, "\t\"ram_total\": \"%s\",\n", (host->system_info->host_ram_total) ? host->system_info->host_ram_total : ""); - - if (host->system_info->container_os_name) - buffer_sprintf(wb, "\t\"container_os_name\": \"%s\",\n", host->system_info->container_os_name); - if (host->system_info->container_os_id) - buffer_sprintf(wb, "\t\"container_os_id\": \"%s\",\n", host->system_info->container_os_id); - if (host->system_info->container_os_id_like) - buffer_sprintf(wb, "\t\"container_os_id_like\": \"%s\",\n", host->system_info->container_os_id_like); - if (host->system_info->container_os_version) - buffer_sprintf(wb, "\t\"container_os_version\": \"%s\",\n", host->system_info->container_os_version); - if (host->system_info->container_os_version_id) - buffer_sprintf(wb, "\t\"container_os_version_id\": \"%s\",\n", host->system_info->container_os_version_id); - if (host->system_info->container_os_detection) - buffer_sprintf(wb, "\t\"container_os_detection\": \"%s\",\n", host->system_info->container_os_detection); - if (host->system_info->is_k8s_node) - buffer_sprintf(wb, "\t\"is_k8s_node\": \"%s\",\n", host->system_info->is_k8s_node); - - buffer_sprintf(wb, "\t\"kernel_name\": \"%s\",\n", (host->system_info->kernel_name) ? host->system_info->kernel_name : ""); - buffer_sprintf(wb, "\t\"kernel_version\": \"%s\",\n", (host->system_info->kernel_version) ? host->system_info->kernel_version : ""); - buffer_sprintf(wb, "\t\"architecture\": \"%s\",\n", (host->system_info->architecture) ? host->system_info->architecture : ""); - buffer_sprintf(wb, "\t\"virtualization\": \"%s\",\n", (host->system_info->virtualization) ? host->system_info->virtualization : ""); - buffer_sprintf(wb, "\t\"virt_detection\": \"%s\",\n", (host->system_info->virt_detection) ? host->system_info->virt_detection : ""); - buffer_sprintf(wb, "\t\"container\": \"%s\",\n", (host->system_info->container) ? host->system_info->container : ""); - buffer_sprintf(wb, "\t\"container_detection\": \"%s\",\n", (host->system_info->container_detection) ? host->system_info->container_detection : ""); - - if (host->system_info->cloud_provider_type) - buffer_sprintf(wb, "\t\"cloud_provider_type\": \"%s\",\n", host->system_info->cloud_provider_type); - if (host->system_info->cloud_instance_type) - buffer_sprintf(wb, "\t\"cloud_instance_type\": \"%s\",\n", host->system_info->cloud_instance_type); - if (host->system_info->cloud_instance_region) - buffer_sprintf(wb, "\t\"cloud_instance_region\": \"%s\",\n", host->system_info->cloud_instance_region); - - buffer_strcat(wb, "\t\"host_labels\": {\n"); - 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"); + web_client_api_request_v1_info_summary_alarm_statuses(host, wb, "alarms"); + + buffer_json_member_add_string_or_empty(wb, "os_name", host->system_info->host_os_name); + buffer_json_member_add_string_or_empty(wb, "os_id", host->system_info->host_os_id); + buffer_json_member_add_string_or_empty(wb, "os_id_like", host->system_info->host_os_id_like); + buffer_json_member_add_string_or_empty(wb, "os_version", host->system_info->host_os_version); + buffer_json_member_add_string_or_empty(wb, "os_version_id", host->system_info->host_os_version_id); + buffer_json_member_add_string_or_empty(wb, "os_detection", host->system_info->host_os_detection); + buffer_json_member_add_string_or_empty(wb, "cores_total", host->system_info->host_cores); + buffer_json_member_add_string_or_empty(wb, "total_disk_space", host->system_info->host_disk_space); + buffer_json_member_add_string_or_empty(wb, "cpu_freq", host->system_info->host_cpu_freq); + buffer_json_member_add_string_or_empty(wb, "ram_total", host->system_info->host_ram_total); + + buffer_json_member_add_string_or_omit(wb, "container_os_name", host->system_info->container_os_name); + buffer_json_member_add_string_or_omit(wb, "container_os_id", host->system_info->container_os_id); + buffer_json_member_add_string_or_omit(wb, "container_os_id_like", host->system_info->container_os_id_like); + buffer_json_member_add_string_or_omit(wb, "container_os_version", host->system_info->container_os_version); + buffer_json_member_add_string_or_omit(wb, "container_os_version_id", host->system_info->container_os_version_id); + buffer_json_member_add_string_or_omit(wb, "container_os_detection", host->system_info->container_os_detection); + buffer_json_member_add_string_or_omit(wb, "is_k8s_node", host->system_info->is_k8s_node); + + buffer_json_member_add_string_or_empty(wb, "kernel_name", host->system_info->kernel_name); + buffer_json_member_add_string_or_empty(wb, "kernel_version", host->system_info->kernel_version); + buffer_json_member_add_string_or_empty(wb, "architecture", host->system_info->architecture); + buffer_json_member_add_string_or_empty(wb, "virtualization", host->system_info->virtualization); + buffer_json_member_add_string_or_empty(wb, "virt_detection", host->system_info->virt_detection); + buffer_json_member_add_string_or_empty(wb, "container", host->system_info->container); + buffer_json_member_add_string_or_empty(wb, "container_detection", host->system_info->container_detection); + + buffer_json_member_add_string_or_omit(wb, "cloud_provider_type", host->system_info->cloud_provider_type); + buffer_json_member_add_string_or_omit(wb, "cloud_instance_type", host->system_info->cloud_instance_type); + buffer_json_member_add_string_or_omit(wb, "cloud_instance_region", host->system_info->cloud_instance_region); + + host_labels2json(host, wb, "host_labels"); + host_functions2json(host, wb); + host_collectors(host, wb); #ifdef DISABLE_CLOUD - buffer_strcat(wb, "\t\"cloud-enabled\": false,\n"); + buffer_json_member_add_boolean(wb, "cloud-enabled", false); #else - buffer_sprintf(wb, "\t\"cloud-enabled\": %s,\n", - appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", 1) ? "true" : "false"); + buffer_json_member_add_boolean(wb, "cloud-enabled", + appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", true)); #endif #ifdef ENABLE_ACLK - buffer_strcat(wb, "\t\"cloud-available\": true,\n"); + buffer_json_member_add_boolean(wb, "cloud-available", true); #else - buffer_strcat(wb, "\t\"cloud-available\": false,\n"); + buffer_json_member_add_boolean(wb, "cloud-available", false); #endif + char *agent_id = get_agent_claimid(); - if (agent_id == NULL) - buffer_strcat(wb, "\t\"agent-claimed\": false,\n"); - else { - buffer_strcat(wb, "\t\"agent-claimed\": true,\n"); - freez(agent_id); - } + buffer_json_member_add_boolean(wb, "agent-claimed", agent_id != NULL); + freez(agent_id); + #ifdef ENABLE_ACLK - if (aclk_connected) { - buffer_strcat(wb, "\t\"aclk-available\": true,\n"); - } - else + buffer_json_member_add_boolean(wb, "aclk-available", aclk_connected); +#else + buffer_json_member_add_boolean(wb, "aclk-available", false); #endif - 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); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"multidb-disk-quota\": "); - analytics_get_data(analytics_data.netdata_config_multidb_disk_quota, wb); - buffer_strcat(wb, ",\n"); - buffer_strcat(wb, "\t\"page-cache-size\": "); - analytics_get_data(analytics_data.netdata_config_page_cache_size, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"stream-enabled\": "); - analytics_get_data(analytics_data.netdata_config_stream_enabled, wb); - buffer_strcat(wb, ",\n"); + buffer_json_member_add_string(wb, "memory-mode", rrd_memory_mode_name(host->rrd_memory_mode)); +#ifdef ENABLE_DBENGINE + buffer_json_member_add_uint64(wb, "multidb-disk-quota", default_multidb_disk_quota_mb); + buffer_json_member_add_uint64(wb, "page-cache-size", default_rrdeng_page_cache_mb); +#endif // ENABLE_DBENGINE + buffer_json_member_add_boolean(wb, "web-enabled", web_server_mode != WEB_SERVER_MODE_NONE); + buffer_json_member_add_boolean(wb, "stream-enabled", default_rrdpush_enabled); #ifdef ENABLE_COMPRESSION - if(host->sender){ - buffer_strcat(wb, "\t\"stream-compression\": "); - 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"); - } + buffer_json_member_add_boolean(wb, "stream-compression", + host->sender && stream_has_capability(host->sender, STREAM_CAP_COMPRESSION)); #else - buffer_strcat(wb, "\t\"stream-compression\": null,\n"); -#endif //ENABLE_COMPRESSION - - buffer_strcat(wb, "\t\"hosts-available\": "); - analytics_get_data(analytics_data.netdata_config_hosts_available, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"https-enabled\": "); - analytics_get_data(analytics_data.netdata_config_https_enabled, wb); - buffer_strcat(wb, ",\n"); + buffer_json_member_add_boolean(wb, "stream-compression", false); +#endif //ENABLE_COMPRESSION - buffer_strcat(wb, "\t\"buildinfo\": "); - analytics_get_data(analytics_data.netdata_buildinfo, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"release-channel\": "); - analytics_get_data(analytics_data.netdata_config_release_channel, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"web-enabled\": "); - analytics_get_data(analytics_data.netdata_config_web_enabled, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"notification-methods\": "); - analytics_get_data(analytics_data.netdata_notification_methods, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"exporting-enabled\": "); - analytics_get_data(analytics_data.netdata_config_exporting_enabled, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"exporting-connectors\": "); - analytics_get_data(analytics_data.netdata_exporting_connectors, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"allmetrics-prometheus-used\": "); - analytics_get_data(analytics_data.netdata_allmetrics_prometheus_used, wb); - buffer_strcat(wb, ",\n"); - - buffer_strcat(wb, "\t\"allmetrics-shell-used\": "); - analytics_get_data(analytics_data.netdata_allmetrics_shell_used, wb); - buffer_strcat(wb, ",\n"); +#ifdef ENABLE_HTTPS + buffer_json_member_add_boolean(wb, "https-enabled", true); +#else + buffer_json_member_add_boolean(wb, "https-enabled", false); +#endif - buffer_strcat(wb, "\t\"allmetrics-json-used\": "); - analytics_get_data(analytics_data.netdata_allmetrics_json_used, wb); - buffer_strcat(wb, ",\n"); + buffer_json_member_add_quoted_string(wb, "buildinfo", analytics_data.netdata_buildinfo); + buffer_json_member_add_quoted_string(wb, "release-channel", analytics_data.netdata_config_release_channel); + buffer_json_member_add_quoted_string(wb, "notification-methods", analytics_data.netdata_notification_methods); - buffer_strcat(wb, "\t\"dashboard-used\": "); - analytics_get_data(analytics_data.netdata_dashboard_used, wb); - buffer_strcat(wb, ",\n"); + buffer_json_member_add_boolean(wb, "exporting-enabled", analytics_data.exporting_enabled); + buffer_json_member_add_quoted_string(wb, "exporting-connectors", analytics_data.netdata_exporting_connectors); - buffer_strcat(wb, "\t\"charts-count\": "); - analytics_get_data(analytics_data.netdata_charts_count, wb); - buffer_strcat(wb, ",\n"); + buffer_json_member_add_uint64(wb, "allmetrics-prometheus-used", analytics_data.prometheus_hits); + buffer_json_member_add_uint64(wb, "allmetrics-shell-used", analytics_data.shell_hits); + buffer_json_member_add_uint64(wb, "allmetrics-json-used", analytics_data.json_hits); + buffer_json_member_add_uint64(wb, "dashboard-used", analytics_data.dashboard_hits); - buffer_strcat(wb, "\t\"metrics-count\": "); - analytics_get_data(analytics_data.netdata_metrics_count, wb); + buffer_json_member_add_uint64(wb, "charts-count", analytics_data.charts_count); + buffer_json_member_add_uint64(wb, "metrics-count", analytics_data.metrics_count); #if defined(ENABLE_ML) - buffer_strcat(wb, ",\n"); - char *ml_info = ml_get_host_info(host); - - buffer_strcat(wb, "\t\"ml-info\": "); - buffer_strcat(wb, ml_info); - - freez(ml_info); + buffer_json_member_add_object(wb, "ml-info"); + ml_host_get_info(host, wb); + buffer_json_object_close(wb); #endif - buffer_strcat(wb, "\n}"); + buffer_json_finalize(wb); return 0; } @@ -1300,17 +1205,16 @@ int web_client_api_request_v1_ml_info(RRDHOST *host, struct web_client *w, char if (!netdata_ready) return HTTP_RESP_BACKEND_FETCH_FAILED; - 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); - wb->contenttype = CT_APPLICATION_JSON; - buffer_strcat(wb, s); + wb->content_type = CT_APPLICATION_JSON; + + buffer_json_initialize(wb, "\"", "\"", 0, true, false); + ml_host_get_detection_info(host, wb); + buffer_json_finalize(wb); + buffer_no_cacheable(wb); - freez(s); return HTTP_RESP_OK; } @@ -1320,27 +1224,22 @@ int web_client_api_request_v1_ml_models(RRDHOST *host, struct web_client *w, cha if (!netdata_ready) return HTTP_RESP_BACKEND_FETCH_FAILED; - char *s = ml_get_host_models(host); - 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); + wb->content_type = CT_APPLICATION_JSON; + ml_host_get_models(host, wb); buffer_no_cacheable(wb); - freez(s); return HTTP_RESP_OK; } -#endif +#endif // ENABLE_ML inline int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, char *url) { (void)url; if (!netdata_ready) return HTTP_RESP_BACKEND_FETCH_FAILED; BUFFER *wb = w->response.data; buffer_flush(wb); - wb->contenttype = CT_APPLICATION_JSON; + wb->content_type = CT_APPLICATION_JSON; web_client_api_request_v1_info_fill_buffer(host, wb); @@ -1360,94 +1259,19 @@ static int web_client_api_request_v1_aclk_state(RRDHOST *host, struct web_client buffer_strcat(wb, str); freez(str); - wb->contenttype = CT_APPLICATION_JSON; + wb->content_type = CT_APPLICATION_JSON; buffer_no_cacheable(wb); return HTTP_RESP_OK; } -static int web_client_api_request_v1_weights_internal(RRDHOST *host, struct web_client *w, char *url, WEIGHTS_METHOD method, WEIGHTS_FORMAT format) { - if (!netdata_ready) - return HTTP_RESP_BACKEND_FETCH_FAILED; - - long long baseline_after = 0, baseline_before = 0, after = 0, before = 0, points = 0; - RRDR_OPTIONS options = RRDR_OPTION_NOT_ALIGNED | RRDR_OPTION_NONZERO | RRDR_OPTION_NULL2ZERO; - int options_count = 0; - RRDR_GROUPING group = RRDR_GROUPING_AVERAGE; - int timeout = 0; - size_t tier = 0; - const char *group_options = NULL, *contexts_str = NULL; - - 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, "baseline_after")) - baseline_after = (long long) strtoul(value, NULL, 0); - - else if (!strcmp(name, "baseline_before")) - baseline_before = (long long) strtoul(value, NULL, 0); - - else if (!strcmp(name, "after") || !strcmp(name, "highlight_after")) - after = (long long) strtoul(value, NULL, 0); - - else if (!strcmp(name, "before") || !strcmp(name, "highlight_before")) - before = (long long) strtoul(value, NULL, 0); - - else if (!strcmp(name, "points") || !strcmp(name, "max_points")) - points = (long long) strtoul(value, NULL, 0); - - else if (!strcmp(name, "timeout")) - timeout = (int) strtoul(value, NULL, 0); - - else if(!strcmp(name, "group")) - group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE); - - else if(!strcmp(name, "options")) { - if(!options_count) options = RRDR_OPTION_NOT_ALIGNED | RRDR_OPTION_NULL2ZERO; - options |= web_client_api_request_v1_data_options(value); - options_count++; - } - - else if(!strcmp(name, "method")) - method = weights_string_to_method(value); - - else if(!strcmp(name, "context") || !strcmp(name, "contexts")) - contexts_str = value; - - else if(!strcmp(name, "tier")) { - tier = str2ul(value); - if(tier < storage_tiers) - options |= RRDR_OPTION_SELECTED_TIER; - else - tier = 0; - } - } - - BUFFER *wb = w->response.data; - buffer_flush(wb); - wb->contenttype = CT_APPLICATION_JSON; - - SIMPLE_PATTERN *contexts = (contexts_str) ? simple_pattern_create(contexts_str, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT) : NULL; - - int ret = web_api_v1_weights(host, wb, method, format, group, group_options, baseline_after, baseline_before, after, before, points, options, contexts, tier, timeout); - - simple_pattern_free(contexts); - return ret; -} - int web_client_api_request_v1_metric_correlations(RRDHOST *host, struct web_client *w, char *url) { - return web_client_api_request_v1_weights_internal(host, w, url, default_metric_correlations_method, WEIGHTS_FORMAT_CHARTS); + return web_client_api_request_weights(host, w, url, default_metric_correlations_method, + WEIGHTS_FORMAT_CHARTS, 1); } int web_client_api_request_v1_weights(RRDHOST *host, struct web_client *w, char *url) { - return web_client_api_request_v1_weights_internal(host, w, url, WEIGHTS_METHOD_ANOMALY_RATE, WEIGHTS_FORMAT_CONTEXTS); + return web_client_api_request_weights(host, w, url, WEIGHTS_METHOD_ANOMALY_RATE, + WEIGHTS_FORMAT_CONTEXTS, 1); } int web_client_api_request_v1_function(RRDHOST *host, struct web_client *w, char *url) { @@ -1458,11 +1282,11 @@ int web_client_api_request_v1_function(RRDHOST *host, struct web_client *w, char const char *function = NULL; while (url) { - char *value = mystrsep(&url, "&"); + char *value = strsep_skip_consecutive_separators(&url, "&"); if (!value || !*value) continue; - char *name = mystrsep(&value, "="); + char *name = strsep_skip_consecutive_separators(&value, "="); if (!name || !*name) continue; @@ -1475,7 +1299,7 @@ int web_client_api_request_v1_function(RRDHOST *host, struct web_client *w, char BUFFER *wb = w->response.data; buffer_flush(wb); - wb->contenttype = CT_APPLICATION_JSON; + wb->content_type = CT_APPLICATION_JSON; buffer_no_cacheable(wb); return rrd_call_function_and_wait(host, wb, timeout, function); @@ -1487,12 +1311,12 @@ int web_client_api_request_v1_functions(RRDHOST *host, struct web_client *w, cha BUFFER *wb = w->response.data; buffer_flush(wb); - wb->contenttype = CT_APPLICATION_JSON; + wb->content_type = CT_APPLICATION_JSON; buffer_no_cacheable(wb); - buffer_strcat(wb, "{\n"); - host_functions2json(host, wb, 1, "\"", "\""); - buffer_strcat(wb, "}"); + buffer_json_initialize(wb, "\"", "\"", 0, true, false); + host_functions2json(host, wb); + buffer_json_finalize(wb); return HTTP_RESP_OK; } @@ -1576,7 +1400,7 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc return HTTP_RESP_NOT_FOUND; } - wb->contenttype = CT_APPLICATION_JSON; + wb->content_type = CT_APPLICATION_JSON; buffer_no_cacheable(wb); buffer_strcat(wb, "{"); for(size_t tier = 0; tier < storage_tiers ;tier++) { @@ -1596,85 +1420,55 @@ int web_client_api_request_v1_dbengine_stats(RRDHOST *host __maybe_unused, struc #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_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 }, +static struct web_api_command api_commands_v1[] = { + { "info", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_info }, + { "data", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_data }, + { "chart", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_chart }, + { "charts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_charts }, + { "context", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_context }, + { "contexts", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_contexts }, // registry checks the ACL by itself, so we allow everything { "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_ACL_ACLK, web_client_api_request_v1_badge }, + { "badge.svg", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC | WEB_CLIENT_ACL_BADGE, web_client_api_request_v1_badge }, - { "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 }, + { "alarms", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarms }, + { "alarms_values", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarms_values }, + { "alarm_log", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarm_log }, + { "alarm_variables", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarm_variables }, + { "alarm_count", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarm_count }, + { "allmetrics", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_allmetrics }, #if defined(ENABLE_ML) - { "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 }, + { "ml_info", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, 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_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 }, + { "aclk", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_aclk_state }, + { "metric_correlations", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_metric_correlations }, + { "weights", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, 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_ACL_ACLK, web_client_api_request_v1_dbengine_stats }, + { "dbengine_stats", 0, WEB_CLIENT_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_dbengine_stats }, // terminator { NULL, 0, WEB_CLIENT_ACL_NONE, NULL }, }; -inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) { +inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url_path_endpoint) { static int initialized = 0; - int i; if(unlikely(initialized == 0)) { initialized = 1; - for(i = 0; api_commands[i].command ; i++) - api_commands[i].hash = simple_hash(api_commands[i].command); + for(int i = 0; api_commands_v1[i].command ; i++) + api_commands_v1[i].hash = simple_hash(api_commands_v1[i].command); } - // get the command - if(url) { - debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, url); - uint32_t hash = simple_hash(url); - - for(i = 0; api_commands[i].command ;i++) { - if(unlikely(hash == api_commands[i].hash && !strcmp(url, api_commands[i].command))) { - if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NOCHECK) && !(w->acl & api_commands[i].acl)) - return web_client_permission_denied(w); - - //return api_commands[i].callback(host, w, url); - return api_commands[i].callback(host, w, (w->decoded_query_string + 1)); - } - } - - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Unsupported v1 API command: "); - buffer_strcat_htmlescape(w->response.data, url); - return HTTP_RESP_NOT_FOUND; - } - else { - buffer_flush(w->response.data); - buffer_sprintf(w->response.data, "Which API v1 command?"); - return HTTP_RESP_BAD_REQUEST; - } + return web_client_api_request_vX(host, w, url_path_endpoint, api_commands_v1); } |