diff options
Diffstat (limited to 'web/api')
-rw-r--r-- | web/api/formatters/rrd2json.c | 3 | ||||
-rw-r--r-- | web/api/netdata-swagger.json | 4 | ||||
-rw-r--r-- | web/api/netdata-swagger.yaml | 8 | ||||
-rw-r--r-- | web/api/queries/query.c | 20 | ||||
-rw-r--r-- | web/api/queries/rrdr.h | 3 | ||||
-rw-r--r-- | web/api/tests/valid_urls.c | 10 | ||||
-rw-r--r-- | web/api/tests/web_api.c | 8 | ||||
-rw-r--r-- | web/api/web_api_v1.c | 151 |
8 files changed, 179 insertions, 28 deletions
diff --git a/web/api/formatters/rrd2json.c b/web/api/formatters/rrd2json.c index 4344af4ac..29bb4beb5 100644 --- a/web/api/formatters/rrd2json.c +++ b/web/api/formatters/rrd2json.c @@ -18,7 +18,6 @@ static inline void free_single_rrdrim(RRDDIM *temp_rd, int archive_mode) freez(temp_rd->rrdset); } } - freez(temp_rd->state->metric_uuid); freez(temp_rd->state); freez(temp_rd); } @@ -95,8 +94,6 @@ void build_context_param_list(struct context_param **param_list, RRDSET *st) memcpy(rd->state, rd1->state, sizeof(*rd->state)); memcpy(&rd->state->collect_ops, &rd1->state->collect_ops, sizeof(struct rrddim_collect_ops)); memcpy(&rd->state->query_ops, &rd1->state->query_ops, sizeof(struct rrddim_query_ops)); - rd->state->metric_uuid = mallocz(sizeof(uuid_t)); - uuid_copy(*rd->state->metric_uuid, *rd1->state->metric_uuid); rd->next = (*param_list)->rd; (*param_list)->rd = rd; } diff --git a/web/api/netdata-swagger.json b/web/api/netdata-swagger.json index 2beaee92f..5c2bba9a8 100644 --- a/web/api/netdata-swagger.json +++ b/web/api/netdata-swagger.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "title": "NetData API", + "title": "Netdata API", "description": "Real-time performance and health monitoring.", "version": "1.11.1_rolling" }, @@ -1343,7 +1343,7 @@ }, "priority": { "type": "number", - "description": "The relative priority of the chart. NetData does not care about priorities. This is just an indication of importance for the chart viewers to sort charts of higher priority (lower number) closer to the top. Priority sorting should only be used among charts of the same type or family." + "description": "The relative priority of the chart. Netdata does not care about priorities. This is just an indication of importance for the chart viewers to sort charts of higher priority (lower number) closer to the top. Priority sorting should only be used among charts of the same type or family." }, "enabled": { "type": "boolean", diff --git a/web/api/netdata-swagger.yaml b/web/api/netdata-swagger.yaml index ebade7991..19f4ded56 100644 --- a/web/api/netdata-swagger.yaml +++ b/web/api/netdata-swagger.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - title: NetData API + title: Netdata API description: Real-time performance and health monitoring. version: 1.11.1_rolling paths: @@ -456,7 +456,7 @@ paths: required: false allowEmptyValue: true schema: - oneOf: + oneOf: - type: string enum: - green @@ -478,7 +478,7 @@ paths: required: false allowEmptyValue: true schema: - oneOf: + oneOf: - type: string enum: - green @@ -1082,7 +1082,7 @@ components: description: The title of the chart. priority: type: number - description: The relative priority of the chart. NetData does not care about + description: The relative priority of the chart. Netdata does not care about priorities. This is just an indication of importance for the chart viewers to sort charts of higher priority (lower number) closer to the top. Priority sorting should only be used among charts of the diff --git a/web/api/queries/query.c b/web/api/queries/query.c index 56e2e2850..216417ae8 100644 --- a/web/api/queries/query.c +++ b/web/api/queries/query.c @@ -389,6 +389,7 @@ static inline void do_dimension_variablestep( , long dim_id_in_rrdr , time_t after_wanted , time_t before_wanted + , uint32_t options ){ // RRDSET *st = r->st; @@ -445,7 +446,11 @@ static inline void do_dimension_variablestep( // db_now has a different value than above if (likely(now >= db_now)) { if (likely(does_storage_number_exist(n_curr))) { - value = unpack_storage_number(n_curr); + if (options & RRDR_OPTION_ANOMALY_BIT) + value = (n_curr & SN_ANOMALY_BIT) ? 0.0 : 100.0; + else + value = unpack_storage_number(n_curr); + if (likely(value != 0.0)) values_in_group_non_zero++; @@ -530,8 +535,11 @@ static inline void do_dimension_fixedstep( , long dim_id_in_rrdr , time_t after_wanted , time_t before_wanted + , uint32_t options ){ +#ifdef NETDATA_INTERNAL_CHECKS RRDSET *st = r->st; +#endif time_t now = after_wanted, @@ -593,7 +601,11 @@ static inline void do_dimension_fixedstep( error("INTERNAL CHECK: Unaligned query for %s, database time: %ld, expected time: %ld", rd->id, (long)handle.rrdeng.now, (long)now); } #endif - value = unpack_storage_number(n); + if (options & RRDR_OPTION_ANOMALY_BIT) + value = (n & SN_ANOMALY_BIT) ? 0.0 : 100.0; + else + value = unpack_storage_number(n); + if(likely(value != 0.0)) values_in_group_non_zero++; @@ -1100,6 +1112,7 @@ static RRDR *rrd2rrdr_fixedstep( , c , after_wanted , before_wanted + , options ); if(r->od[c] & RRDR_DIMENSION_NONZERO) @@ -1476,6 +1489,7 @@ static RRDR *rrd2rrdr_variablestep( , c , after_wanted , before_wanted + , options ); if(r->od[c] & RRDR_DIMENSION_NONZERO) @@ -1644,4 +1658,4 @@ RRDR *rrd2rrdr( return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method, resampling_time_requested, options, dimensions, rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested, context_param_list); -}
\ No newline at end of file +} diff --git a/web/api/queries/rrdr.h b/web/api/queries/rrdr.h index b302f8bd4..3637df687 100644 --- a/web/api/queries/rrdr.h +++ b/web/api/queries/rrdr.h @@ -24,6 +24,7 @@ typedef enum rrdr_options { RRDR_OPTION_MATCH_NAMES = 0x00008000, // when filtering dimensions, match only names RRDR_OPTION_CUSTOM_VARS = 0x00010000, // when wrapping response in a JSON, return custom variables in response RRDR_OPTION_ALLOW_PAST = 0x00020000, // The after parameter can extend in the past before the first entry + RRDR_OPTION_ANOMALY_BIT = 0x00040000, // Return the anomaly bit stored in each collected_number } RRDR_OPTIONS; typedef enum rrdr_value_flag { @@ -99,7 +100,7 @@ typedef struct rrdresult { #define rrdr_rows(r) ((r)->rows) -#include "../../../database/rrd.h" +#include "database/rrd.h" extern void rrdr_free(RRDR *r); extern RRDR *rrdr_create(struct rrdset *st, long n, struct context_param *context_param_list); diff --git a/web/api/tests/valid_urls.c b/web/api/tests/valid_urls.c index d8c261c51..30ae23247 100644 --- a/web/api/tests/valid_urls.c +++ b/web/api/tests/valid_urls.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include "../../../libnetdata/libnetdata.h" -#include "../../../libnetdata/required_dummies.h" -#include "../../../database/rrd.h" -#include "../../../web/server/web_client.h" +#include "libnetdata/libnetdata.h" +#include "libnetdata/required_dummies.h" +#include "database/rrd.h" +#include "web/server/web_client.h" #include <setjmp.h> #include <cmocka.h> #include <stdbool.h> @@ -410,7 +410,7 @@ static void empty_url(void **state) } /* If the %-escape is being performed at the correct time then the url should not be treated as a query, but instead - as a path "/api/v1/info?blah?" which should despatch into the API with the given values. + as a path "/api/v1/info?blah?" which should dispatch into the API with the given values. */ static void not_a_query(void **state) { diff --git a/web/api/tests/web_api.c b/web/api/tests/web_api.c index 0a741e084..b96213255 100644 --- a/web/api/tests/web_api.c +++ b/web/api/tests/web_api.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later -#include "../../../libnetdata/libnetdata.h" -#include "../../../libnetdata/required_dummies.h" -#include "../../../database/rrd.h" -#include "../../../web/server/web_client.h" +#include "libnetdata/libnetdata.h" +#include "libnetdata/required_dummies.h" +#include "database/rrd.h" +#include "web/server/web_client.h" #include <setjmp.h> #include <cmocka.h> #include <stdbool.h> diff --git a/web/api/web_api_v1.c b/web/api/web_api_v1.c index 96fcf485a..d335dd687 100644 --- a/web/api/web_api_v1.c +++ b/web/api/web_api_v1.c @@ -36,6 +36,7 @@ static struct { , {"match-names" , 0 , RRDR_OPTION_MATCH_NAMES} , {"showcustomvars" , 0 , RRDR_OPTION_CUSTOM_VARS} , {"allow_past" , 0 , RRDR_OPTION_ALLOW_PAST} + , {"anomaly-bit" , 0 , RRDR_OPTION_ANOMALY_BIT} , { NULL, 0, 0} }; @@ -867,8 +868,8 @@ static inline void web_client_api_request_v1_info_mirrored_hosts(BUFFER *wb) { netdata_mutex_lock(&host->receiver_lock); buffer_sprintf( - wb, "\t\t{ \"guid\": \"%s\", \"reachable\": %s, \"claim_id\": ", host->machine_guid, - (host->receiver || host == localhost) ? "true" : "false"); + wb, "\t\t{ \"guid\": \"%s\", \"reachable\": %s, \"hops\": %d, \"claim_id\": ", host->machine_guid, + (host->receiver || host == localhost) ? "true" : "false", host->system_info ? host->system_info->hops : (host == localhost) ? 0 : 1); netdata_mutex_unlock(&host->receiver_lock); rrdhost_aclk_state_lock(host); @@ -980,10 +981,27 @@ 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"); #ifdef ACLK_NG - buffer_strcat(wb, "\t\"aclk-implementation\": \"Next Generation\",\n"); + buffer_strcat(wb, "\t\"aclk-ng-available\": true,\n"); #else - buffer_strcat(wb, "\t\"aclk-implementation\": \"legacy\",\n"); + buffer_strcat(wb, "\t\"aclk-ng-available\": false,\n"); #endif +#if defined(ACLK_NG) && defined(ENABLE_NEW_CLOUD_PROTOCOL) + buffer_strcat(wb, "\t\"aclk-ng-new-cloud-protocol\": true,\n"); +#else + buffer_strcat(wb, "\t\"aclk-ng-new-cloud-protocol\": false,\n"); +#endif +#ifdef ACLK_LEGACY + buffer_strcat(wb, "\t\"aclk-legacy-available\": true,\n"); +#else + buffer_strcat(wb, "\t\"aclk-legacy-available\": false,\n"); +#endif + buffer_strcat(wb, "\t\"aclk-implementation\": \""); + if (aclk_ng) { + buffer_strcat(wb, "Next Generation"); + } else { + buffer_strcat(wb, "legacy"); + } + buffer_strcat(wb, "\",\n"); #else buffer_strcat(wb, "\t\"cloud-available\": false,\n"); #endif @@ -1071,12 +1089,109 @@ inline int web_client_api_request_v1_info_fill_buffer(RRDHOST *host, BUFFER *wb) buffer_strcat(wb, "\t\"metrics-count\": "); analytics_get_data(analytics_data.netdata_metrics_count, wb); - buffer_strcat(wb, "\n"); - buffer_strcat(wb, "}"); +#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); + + free(ml_info); +#endif + + buffer_strcat(wb, "\n}"); return 0; } +#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 = strdup("{\"error\": \"missing after/before parameters\" }\n"); + else { + s = ml_get_anomaly_events(host, "AD1", 1, after, before); + if (!s) + s = strdup("{\"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_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 = strdup("{\"error\": \"missing after/before parameters\" }\n"); + else { + s = ml_get_anomaly_event_info(host, "AD1", 1, after, before); + if (!s) + s = strdup("{\"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; +} +#endif // defined(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; @@ -1090,6 +1205,23 @@ inline int web_client_api_request_v1_info(RRDHOST *host, struct web_client *w, c return HTTP_RESP_OK; } +static int web_client_api_request_v1_aclk_state(RRDHOST *host, struct web_client *w, char *url) { + UNUSED(url); + UNUSED(host); + if (!netdata_ready) return HTTP_RESP_BACKEND_FETCH_FAILED; + + BUFFER *wb = w->response.data; + buffer_flush(wb); + + char *str = aclk_state_json(); + buffer_strcat(wb, str); + freez(str); + + wb->contenttype = CT_APPLICATION_JSON; + buffer_no_cacheable(wb); + return HTTP_RESP_OK; +} + static struct api_command { const char *command; uint32_t hash; @@ -1114,7 +1246,14 @@ static struct api_command { { "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 }, + +#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 }, +#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 }, // terminator { NULL, 0, WEB_CLIENT_ACL_NONE, NULL }, }; |