summaryrefslogtreecommitdiffstats
path: root/web/api
diff options
context:
space:
mode:
Diffstat (limited to 'web/api')
-rw-r--r--web/api/formatters/rrd2json.c3
-rw-r--r--web/api/netdata-swagger.json4
-rw-r--r--web/api/netdata-swagger.yaml8
-rw-r--r--web/api/queries/query.c20
-rw-r--r--web/api/queries/rrdr.h3
-rw-r--r--web/api/tests/valid_urls.c10
-rw-r--r--web/api/tests/web_api.c8
-rw-r--r--web/api/web_api_v1.c151
8 files changed, 179 insertions, 28 deletions
diff --git a/web/api/formatters/rrd2json.c b/web/api/formatters/rrd2json.c
index 4344af4a..29bb4beb 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 2beaee92..5c2bba9a 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 ebade799..19f4ded5 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 56e2e285..216417ae 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 b302f8bd..3637df68 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 d8c261c5..30ae2324 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 0a741e08..b9621325 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 96fcf485..d335dd68 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 },
};