@cryptotaxi247 / netdata-1 / commits / cd50bf423

/api/v2 part 4 (#14706)

* expose the order of group by * key renames in json wrapper v2 * added group by context and group by units * added view_average_values * fix for view_average_values when percentage is specified * option group-by-labels is enabling the exposure of all the labels that are used for each of the final grouped dimensions * when executing group by queries, allocate one dimension data at a time - not all of them * respect hidden dimensions * cancel running data query on socket error * use poll to detect socket errors * use POLLRDHUP to detect half closed connections * make sure POLLRDHUP is available * do not destroy aral-by-size arals * completed documentation of /api/v2/data. * moved min, max back to view; updated swagger yaml and json * default format for /api/v2/data is json2

Costa Tsaousis committed Mar 13, 2023 at 23:39 UTC cd50bf42367ed49ed12e944d66a445653c6f038c
35 files changed +3876 -2165
database/contexts/rrdcontext.h
+5
@@ -258,6 +258,8 @@ typedef struct query_metric {
258
259 #define MAX_QUERY_TARGET_ID_LENGTH 255
260
261 +typedef bool (*interrupt_callback_t)(void *data);
262 +
263 typedef struct query_target_request {
264 size_t version;
265
@@ -303,6 +305,9 @@ typedef struct query_target_request {
305 RRDR_GROUP_BY_FUNCTION group_by_aggregate_function;
306
307 usec_t received_ut;
308 +
309 + interrupt_callback_t interrupt_callback;
310 + void *interrupt_callback_data;
311 } QUERY_TARGET_REQUEST;
312
313 #define GROUP_BY_MAX_LABEL_KEYS 10
database/rrdlabels.c
+1 -1
@@ -399,7 +399,7 @@ size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_si
399
400 // find how big this character is (2-4 bytes)
401 size_t utf_character_size = 2;
402 - while(utf_character_size <= 4 && src[utf_character_size] && IS_UTF8_BYTE(src[utf_character_size]) && !IS_UTF8_STARTBYTE(src[utf_character_size]))
402 + while(utf_character_size < 4 && src[utf_character_size] && IS_UTF8_BYTE(src[utf_character_size]) && !IS_UTF8_STARTBYTE(src[utf_character_size]))
403 utf_character_size++;
404
405 if(utf) {
libnetdata/aral/aral.c
+4 -4
@@ -885,10 +885,10 @@ void aral_by_size_release(ARAL *ar) {
885 fatal("ARAL BY SIZE: double release detected");
886
887 aral_by_size_globals.array[size].refcount--;
888 - if(!aral_by_size_globals.array[size].refcount) {
889 - aral_destroy(aral_by_size_globals.array[size].ar);
890 - aral_by_size_globals.array[size].ar = NULL;
891 - }
888 +// if(!aral_by_size_globals.array[size].refcount) {
889 +// aral_destroy(aral_by_size_globals.array[size].ar);
890 +// aral_by_size_globals.array[size].ar = NULL;
891 +// }
892
893 netdata_spinlock_unlock(&aral_by_size_globals.spinlock);
894 }
libnetdata/buffer/buffer.h
+46 -7
@@ -3,6 +3,7 @@
3 #ifndef NETDATA_WEB_BUFFER_H
4 #define NETDATA_WEB_BUFFER_H 1
5
6 +#include "../string/utf8.h"
7 #include "../libnetdata.h"
8
9 #define WEB_DATA_LENGTH_INCREASE_STEP 1024
@@ -203,18 +204,56 @@ static inline void buffer_strcat(BUFFER *wb, const char *txt) {
204 static inline void buffer_json_strcat(BUFFER *wb, const char *txt) {
205 if(unlikely(!txt || !*txt)) return;
206
206 - const char *t = txt;
207 + const unsigned char *t = (const unsigned char *)txt;
208 while(*t) {
208 - buffer_need_bytes(wb, 100);
209 - char *s = &wb->buffer[wb->len];
210 - char *d = s;
211 - const char *e = &wb->buffer[wb->size - 1]; // remove 1 to make room for the escape character
209 + buffer_need_bytes(wb, 110);
210 + unsigned char *s = (unsigned char *)&wb->buffer[wb->len];
211 + unsigned char *d = s;
212 + const unsigned char *e = (unsigned char *)&wb->buffer[wb->size - 10]; // make room for the max escape sequence
213
214 while(*t && d < e) {
214 - if(unlikely(*t == '\\' || *t == '\"'))
215 +#ifdef BUFFER_JSON_ESCAPE_UTF
216 + if(unlikely(IS_UTF8_STARTBYTE(*t) && IS_UTF8_BYTE(t[1]))) {
217 + // UTF-8 multi-byte encoded character
218 +
219 + // find how big this character is (2-4 bytes)
220 + size_t utf_character_size = 2;
221 + while(utf_character_size < 4 && t[utf_character_size] && IS_UTF8_BYTE(t[utf_character_size]) && !IS_UTF8_STARTBYTE(t[utf_character_size]))
222 + utf_character_size++;
223 +
224 + uint32_t code_point = 0;
225 + for (size_t i = 0; i < utf_character_size; i++) {
226 + code_point <<= 6;
227 + code_point |= (t[i] & 0x3F);
228 + }
229 +
230 + t += utf_character_size;
231 +
232 + // encode as \u escape sequence
233 + *d++ = '\\';
234 + *d++ = 'u';
235 + *d++ = hex_digits[(code_point >> 12) & 0xf];
236 + *d++ = hex_digits[(code_point >> 8) & 0xf];
237 + *d++ = hex_digits[(code_point >> 4) & 0xf];
238 + *d++ = hex_digits[code_point & 0xf];
239 + }
240 + else
241 +#endif
242 + if(unlikely(*t < ' ')) {
243 + uint32_t v = *t++;
244 *d++ = '\\';
245 + *d++ = 'u';
246 + *d++ = hex_digits[(v >> 12) & 0xf];
247 + *d++ = hex_digits[(v >> 8) & 0xf];
248 + *d++ = hex_digits[(v >> 4) & 0xf];
249 + *d++ = hex_digits[v & 0xf];
250 + }
251 + else {
252 + if (unlikely(*t == '\\' || *t == '\"'))
253 + *d++ = '\\';
254
217 - *d++ = *t++;
255 + *d++ = *t++;
256 + }
257 }
258
259 wb->len += d - s;
libnetdata/socket/socket.c
+48
@@ -1,5 +1,13 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 +#ifndef _GNU_SOURCE
4 +#define _GNU_SOURCE // for POLLRDHUP
5 +#endif
6 +
7 +#ifndef __BSD_VISIBLE
8 +#define __BSD_VISIBLE // for POLLRDHUP
9 +#endif
10 +
11 #include "../libnetdata.h"
12
13 // --------------------------------------------------------------------------------------------------------------------
@@ -11,6 +19,46 @@
19 #define LARGE_SOCK_SIZE 4096
20 #endif
21
22 +bool fd_is_socket(int fd) {
23 + int type;
24 + socklen_t len = sizeof(type);
25 + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1)
26 + return false;
27 +
28 + return true;
29 +}
30 +
31 +bool sock_has_output_error(int fd) {
32 + if(fd < 0) {
33 + //internal_error(true, "invalid socket %d", fd);
34 + return false;
35 + }
36 +
37 +// if(!fd_is_socket(fd)) {
38 +// //internal_error(true, "fd %d is not a socket", fd);
39 +// return false;
40 +// }
41 +
42 + short int errors = POLLERR | POLLHUP | POLLNVAL;
43 +
44 +#ifdef POLLRDHUP
45 + errors |= POLLRDHUP;
46 +#endif
47 +
48 + struct pollfd pfd = {
49 + .fd = fd,
50 + .events = POLLOUT | errors,
51 + .revents = 0,
52 + };
53 +
54 + if(poll(&pfd, 1, 0) == -1) {
55 + //internal_error(true, "poll() failed");
56 + return false;
57 + }
58 +
59 + return ((pfd.revents & errors) || !(pfd.revents & POLLOUT));
60 +}
61 +
62 int sock_setnonblock(int fd) {
63 int flags;
64
libnetdata/socket/socket.h
+3
@@ -74,6 +74,9 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
74 ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout);
75 #endif
76
77 +bool fd_is_socket(int fd);
78 +bool sock_has_output_error(int fd);
79 +
80 int sock_setnonblock(int fd);
81 int sock_delnonblock(int fd);
82 int sock_setreuse(int fd, int reuse);
libnetdata/string/utf8.h
+2 -2
@@ -3,7 +3,7 @@
3 #ifndef NETDATA_STRING_UTF8_H
4 #define NETDATA_STRING_UTF8_H 1
5
6 -#define IS_UTF8_BYTE(x) (x & 0x80)
7 -#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&(x & 0x40))
6 +#define IS_UTF8_BYTE(x) ((x) & 0x80)
7 +#define IS_UTF8_STARTBYTE(x) (IS_UTF8_BYTE(x)&&((x) & 0x40))
8
9 #endif /* NETDATA_STRING_UTF8_H */
web/api/formatters/json/json.c
+134 -10
@@ -154,7 +154,6 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
154 NETDATA_DOUBLE *cn = &r->v[ i * r->d ];
155 RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
156 NETDATA_DOUBLE *ar = &r->ar[ i * r->d ];
157 - uint32_t *gbc = &r->gbc [ i * r->d ];
157
158 time_t now = r->t[i];
159
@@ -211,15 +210,13 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
210 buffer_fast_strcat(wb, post_date, post_date_len);
211 }
212
214 - if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
213 + if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_AR)))) {
214 total = 0;
215 for(c = 0; c < used ;c++) {
216 if(unlikely(!(r->od[c] & RRDR_DIMENSION_QUERIED))) continue;
217
218 NETDATA_DOUBLE n;
220 - if(unlikely(options & RRDR_OPTION_INTERNAL_GBC))
221 - n = gbc[c];
222 - else if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
219 + if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
220 n = ar[c];
221 else
222 n = cn[c];
@@ -239,9 +236,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
236 continue;
237
238 NETDATA_DOUBLE n;
242 - if(unlikely(options & RRDR_OPTION_INTERNAL_GBC))
243 - n = gbc[c];
244 - else if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
239 + if(unlikely(options & RRDR_OPTION_INTERNAL_AR))
240 n = ar[c];
241 else
242 n = cn[c];
@@ -251,7 +246,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
246 if(unlikely( options & RRDR_OPTION_OBJECTSROWS ))
247 buffer_sprintf(wb, "%s%s%s: ", kq, string2str(r->dn[c]), kq);
248
254 - if(co[c] & RRDR_VALUE_EMPTY && !(options & (RRDR_OPTION_INTERNAL_AR | RRDR_OPTION_INTERNAL_GBC))) {
249 + if(co[c] & RRDR_VALUE_EMPTY && !(options & (RRDR_OPTION_INTERNAL_AR))) {
250 if(unlikely(options & RRDR_OPTION_NULL2ZERO))
251 buffer_fast_strcat(wb, "0", 1);
252 else
@@ -261,7 +256,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
256 if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
257 n = -n;
258
264 - if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
259 + if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_AR)))) {
260 n = n * 100 / total;
261
262 if(unlikely(i == start && c == 0)) {
@@ -285,3 +280,132 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
280 buffer_strcat(wb, finish);
281 //info("RRD2JSON(): %s: END", r->st->id);
282 }
283 +
284 +
285 +void rrdr2json_v2(RRDR *r, BUFFER *wb) {
286 + QUERY_TARGET *qt = r->internal.qt;
287 + RRDR_OPTIONS options = qt->request.options;
288 +
289 + bool expose_gbc = query_target_aggregatable(qt);
290 +
291 + buffer_json_member_add_object(wb, "result");
292 +
293 + buffer_json_member_add_array(wb, "labels");
294 + buffer_json_add_array_item_string(wb, "time");
295 + long d, i;
296 + const long used = (long)r->d;
297 + for(d = 0, i = 0; d < used ; d++) {
298 + if(!rrdr_dimension_should_be_exposed(r->od[d], options))
299 + continue;
300 +
301 + buffer_json_add_array_item_string(wb, string2str(r->dn[d]));
302 + i++;
303 + }
304 + buffer_json_array_close(wb); // labels
305 +
306 + buffer_json_member_add_object(wb, "point");
307 + buffer_json_member_add_uint64(wb, "value", 0);
308 + buffer_json_member_add_uint64(wb, "ar", 1);
309 + buffer_json_member_add_uint64(wb, "pa", 2);
310 + if(expose_gbc)
311 + buffer_json_member_add_uint64(wb, "count", 3);
312 + buffer_json_object_close(wb);
313 +
314 + buffer_json_member_add_array(wb, "data");
315 + if(i) {
316 + long start = 0, end = rrdr_rows(r), step = 1;
317 + if (!(options & RRDR_OPTION_REVERSED)) {
318 + start = rrdr_rows(r) - 1;
319 + end = -1;
320 + step = -1;
321 + }
322 +
323 + // for each line in the array
324 + for (i = start; i != end; i += step) {
325 + NETDATA_DOUBLE *cn = &r->v[ i * r->d ];
326 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
327 + NETDATA_DOUBLE *ar = &r->ar[ i * r->d ];
328 + uint32_t *gbc = &r->gbc [ i * r->d ];
329 + time_t now = r->t[i];
330 +
331 + buffer_json_add_array_item_array(wb); // row
332 +
333 + if (options & RRDR_OPTION_MILLISECONDS)
334 + buffer_json_add_array_item_time_ms(wb, now); // the time
335 + else
336 + buffer_json_add_array_item_time_t(wb, now); // the time
337 +
338 + NETDATA_DOUBLE total = 1;
339 + if(unlikely((options & RRDR_OPTION_PERCENTAGE))) {
340 + total = 0;
341 + for(d = 0; d < used ; d++) {
342 + if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED))) continue;
343 +
344 + NETDATA_DOUBLE n = cn[d];
345 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
346 + n = -n;
347 +
348 + total += n;
349 + }
350 +
351 + // prevent a division by zero
352 + if(total == 0) total = 1;
353 + }
354 +
355 + for (d = 0; d < used; d++) {
356 + if (!rrdr_dimension_should_be_exposed(r->od[d], options))
357 + continue;
358 +
359 + RRDR_VALUE_FLAGS o = co[d];
360 +
361 + buffer_json_add_array_item_array(wb); // point
362 +
363 + // add the value
364 + NETDATA_DOUBLE n = cn[d];
365 +
366 + if(o & RRDR_VALUE_EMPTY) {
367 + if (unlikely(options & RRDR_OPTION_NULL2ZERO))
368 + buffer_json_add_array_item_double(wb, 0);
369 + else
370 + buffer_json_add_array_item_double(wb, NAN);
371 + }
372 + else {
373 + if (unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
374 + n = -n;
375 +
376 + if (unlikely((options & RRDR_OPTION_PERCENTAGE))) {
377 + n = n * 100 / total;
378 + }
379 +
380 + if(unlikely(i == start && d == 0)) {
381 + r->view.min = r->view.max = n;
382 + }
383 + else {
384 + if (n < r->view.min) r->view.min = n;
385 + if (n > r->view.max) r->view.max = n;
386 + }
387 +
388 + buffer_json_add_array_item_double(wb, n);
389 + }
390 +
391 + // add the anomaly
392 + buffer_json_add_array_item_double(wb, ar[d]);
393 +
394 + // add the point annotations
395 + buffer_json_add_array_item_uint64(wb, o);
396 +
397 + // add the count
398 + if(expose_gbc)
399 + buffer_json_add_array_item_uint64(wb, gbc[d]);
400 +
401 + buffer_json_array_close(wb); // point
402 + }
403 +
404 + buffer_json_array_close(wb); // row
405 + }
406 + }
407 +
408 + buffer_json_array_close(wb); // data
409 +
410 + buffer_json_object_close(wb); // annotations
411 +}
web/api/formatters/json/json.h
+1
@@ -6,5 +6,6 @@
6 #include "../rrd2json.h"
7
8 void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable);
9 +void rrdr2json_v2(RRDR *r, BUFFER *wb);
10
11 #endif //NETDATA_API_FORMATTER_JSON_H
web/api/formatters/json_wrapper.c
+143 -185
@@ -676,7 +676,7 @@ static inline long query_target_metrics_latest_values(BUFFER *wb, const char *ke
676 return i;
677 }
678
679 -static inline size_t rrdr_latest_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
679 +static inline size_t rrdr_dimension_latest_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
680 size_t c, i;
681
682 buffer_json_member_add_array(wb, key);
@@ -732,11 +732,54 @@ static inline size_t rrdr_latest_values(BUFFER *wb, const char *key, RRDR *r, RR
732 return i;
733 }
734
735 -void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options,
736 - RRDR_TIME_GROUPING group_method)
737 -{
735 +static inline void rrdr_dimension_average_values(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
736 + if(!r->dv)
737 + return;
738 +
739 + buffer_json_member_add_array(wb, key);
740 +
741 + bool percentage = r->internal.qt->request.options & RRDR_OPTION_PERCENTAGE;
742 + NETDATA_DOUBLE total = 0;
743 + if(percentage) {
744 + for(size_t c = 0; c < r->d ; c++) {
745 + if(!(r->od[c] & RRDR_DIMENSION_QUERIED))
746 + continue;
747 +
748 + total += r->dv[c];
749 + }
750 + }
751 +
752 + for(size_t c = 0; c < r->d ; c++) {
753 + if(!rrdr_dimension_should_be_exposed(r->od[c], options))
754 + continue;
755 +
756 + if(percentage)
757 + buffer_json_add_array_item_double(wb, r->dv[c] * 100.0 / total);
758 + else
759 + buffer_json_add_array_item_double(wb, r->dv[c]);
760 + }
761 +
762 + buffer_json_array_close(wb);
763 +}
764 +
765 +static void rrdr_timings_v12(BUFFER *wb, const char *key, RRDR *r) {
766 QUERY_TARGET *qt = r->internal.qt;
767
768 + qt->timings.finished_ut = now_monotonic_usec();
769 + buffer_json_member_add_object(wb, key);
770 + buffer_json_member_add_double(wb, "prep_ms", (NETDATA_DOUBLE)(qt->timings.preprocessed_ut - qt->timings.received_ut) / USEC_PER_MS);
771 + buffer_json_member_add_double(wb, "query_ms", (NETDATA_DOUBLE)(qt->timings.executed_ut - qt->timings.preprocessed_ut) / USEC_PER_MS);
772 + buffer_json_member_add_double(wb, "group_by_ms", (NETDATA_DOUBLE)(qt->timings.group_by_ut - qt->timings.executed_ut) / USEC_PER_MS);
773 + buffer_json_member_add_double(wb, "output_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.group_by_ut) / USEC_PER_MS);
774 + buffer_json_member_add_double(wb, "total_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.received_ut) / USEC_PER_MS);
775 + buffer_json_object_close(wb);
776 +}
777 +
778 +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb) {
779 + QUERY_TARGET *qt = r->internal.qt;
780 + DATASOURCE_FORMAT format = qt->request.format;
781 + RRDR_OPTIONS options = qt->request.options;
782 +
783 long rows = rrdr_rows(r);
784
785 char kq[2] = "", // key quote
@@ -762,7 +805,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR
805 buffer_json_member_add_time_t(wb, "last_entry", qt->db.last_time_s);
806 buffer_json_member_add_time_t(wb, "after", r->view.after);
807 buffer_json_member_add_time_t(wb, "before", r->view.before);
765 - buffer_json_member_add_string(wb, "group", time_grouping_tostring(group_method));
808 + buffer_json_member_add_string(wb, "group", time_grouping_tostring(qt->request.time_group_method));
809 web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", r->view.options);
810
811 if(!rrdr_dimension_names(wb, "dimension_names", r, options))
@@ -788,7 +831,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR
831 if(!query_target_metrics_latest_values(wb, "latest_values", r, options))
832 rows = 0;
833
791 - size_t dimensions = rrdr_latest_values(wb, "view_latest_values", r, options);
834 + size_t dimensions = rrdr_dimension_latest_values(wb, "view_latest_values", r, options);
835 if(!dimensions)
836 rows = 0;
837
@@ -858,13 +901,53 @@ static void query_target_combined_chart_type(BUFFER *wb, QUERY_TARGET *qt, size_
901 buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rrdcontext_acquired_chart_type(qt->contexts.array[0].rca)));
902 }
903
861 -static void rrdr_dimension_units_array_v2(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) {
904 +static void rrdr_grouped_by_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options __maybe_unused) {
905 + QUERY_TARGET *qt = r->internal.qt;
906 +
907 + buffer_json_member_add_array(wb, key);
908 +
909 + if(qt->request.group_by & RRDR_GROUP_BY_SELECTED)
910 + buffer_json_add_array_item_string(wb, "selected");
911 +
912 + else {
913 +
914 + if(qt->request.group_by & RRDR_GROUP_BY_DIMENSION)
915 + buffer_json_add_array_item_string(wb, "dimension");
916 +
917 + if(qt->request.group_by & RRDR_GROUP_BY_INSTANCE)
918 + buffer_json_add_array_item_string(wb, "instance");
919 +
920 + if(qt->request.group_by & RRDR_GROUP_BY_LABEL) {
921 + BUFFER *b = buffer_create(0, NULL);
922 + for (size_t l = 0; l < qt->group_by.used; l++) {
923 + buffer_flush(b);
924 + buffer_fast_strcat(b, "label:", 6);
925 + buffer_strcat(b, qt->group_by.label_keys[l]);
926 + buffer_json_add_array_item_string(wb, buffer_tostring(b));
927 + }
928 + buffer_free(b);
929 + }
930 +
931 + if(qt->request.group_by & RRDR_GROUP_BY_NODE)
932 + buffer_json_add_array_item_string(wb, "node");
933 +
934 + if(qt->request.group_by & RRDR_GROUP_BY_CONTEXT)
935 + buffer_json_add_array_item_string(wb, "context");
936 +
937 + if(qt->request.group_by & RRDR_GROUP_BY_UNITS)
938 + buffer_json_add_array_item_string(wb, "units");
939 + }
940 +
941 + buffer_json_array_close(wb); // group_by_order
942 +}
943 +
944 +static void rrdr_dimension_units_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
945 if(!r->du)
946 return;
947
948 bool percentage = query_target_has_percentage_units(r->internal.qt);
949
867 - buffer_json_member_add_array(wb, "units");
950 + buffer_json_member_add_array(wb, key);
951 for(size_t c = 0; c < r->d ; c++) {
952 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
953 continue;
@@ -877,11 +960,11 @@ static void rrdr_dimension_units_array_v2(BUFFER *wb, RRDR *r, RRDR_OPTIONS opti
960 buffer_json_array_close(wb);
961 }
962
880 -static void rrdr_dimension_priority_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) {
963 +static void rrdr_dimension_priority_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
964 if(!r->dp)
965 return;
966
884 - buffer_json_member_add_array(wb, "priorities");
967 + buffer_json_member_add_array(wb, key);
968 for(size_t c = 0; c < r->d ; c++) {
969 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
970 continue;
@@ -891,11 +974,11 @@ static void rrdr_dimension_priority_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS opti
974 buffer_json_array_close(wb);
975 }
976
894 -static void rrdr_dimension_grouped_array(BUFFER *wb, RRDR *r, RRDR_OPTIONS options) {
977 +static void rrdr_dimension_aggregated_array_v2(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
978 if(!r->dgbc)
979 return;
980
898 - buffer_json_member_add_array(wb, "grouped");
981 + buffer_json_member_add_array(wb, key);
982 for(size_t c = 0; c < r->d ;c++) {
983 if(!rrdr_dimension_should_be_exposed(r->od[c], options))
984 continue;
@@ -1089,12 +1172,9 @@ static void query_target_detailed_objects_tree(BUFFER *wb, RRDR *r, RRDR_OPTIONS
1172 buffer_json_object_close(wb); // hosts
1173 }
1174
1092 -void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options,
1093 - RRDR_TIME_GROUPING group_method)
1094 -{
1175 +void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb) {
1176 QUERY_TARGET *qt = r->internal.qt;
1096 -
1097 - long rows = rrdr_rows(r);
1177 + RRDR_OPTIONS options = qt->request.options;
1178
1179 char kq[2] = "\"", // key quote
1180 sq[2] = "\""; // string quote
@@ -1181,7 +1261,6 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
1261 buffer_json_member_add_uint64(wb, "contexts_soft_hash", qt->versions.contexts_soft_hash);
1262 buffer_json_object_close(wb);
1263
1184 - size_t contexts;
1264 buffer_json_member_add_object(wb, "summary");
1265 struct summary_total_counts
1266 nodes_totals = { 0 },
@@ -1192,7 +1271,7 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
1271 label_key_value_totals = { 0 };
1272 {
1273 query_target_summary_nodes_v2(wb, qt, "nodes", &nodes_totals);
1195 - contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals);
1274 + r->internal.contexts = query_target_summary_contexts_v2(wb, qt, "contexts", &contexts_totals);
1275 query_target_summary_instances_v2(wb, qt, "instances", &instances_totals);
1276 query_target_summary_dimensions_v12(wb, qt, "dimensions", true, &metrics_totals);
1277 query_target_summary_labels_v12(wb, qt, "labels", true, &label_key_totals, &label_key_value_totals);
@@ -1224,7 +1303,7 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
1303 buffer_json_member_add_time_t(wb, "first_entry", qt->db.first_time_s);
1304 buffer_json_member_add_time_t(wb, "last_entry", qt->db.last_time_s);
1305
1227 - buffer_json_member_add_array(wb, "tiers");
1306 + buffer_json_member_add_array(wb, "per_tier");
1307 for(size_t tier = 0; tier < storage_tiers ; tier++) {
1308 buffer_json_add_array_item_object(wb);
1309 buffer_json_member_add_uint64(wb, "tier", tier);
@@ -1238,39 +1317,6 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
1317 buffer_json_array_close(wb);
1318 }
1319 buffer_json_object_close(wb);
1241 -
1242 - buffer_json_member_add_object(wb, "view");
1243 - {
1244 - query_target_title(wb, qt, contexts);
1245 - buffer_json_member_add_string(wb, "format", rrdr_format_to_string(format));
1246 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", r->view.options);
1247 - buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(group_method));
1248 - buffer_json_member_add_time_t(wb, "update_every", r->view.update_every);
1249 - buffer_json_member_add_time_t(wb, "after", r->view.after);
1250 - buffer_json_member_add_time_t(wb, "before", r->view.before);
1251 -
1252 - buffer_json_member_add_object(wb, "partial_data_trimming");
1253 - buffer_json_member_add_time_t(wb, "max_update_every", r->partial_data_trimming.max_update_every);
1254 - buffer_json_member_add_time_t(wb, "expected_after", r->partial_data_trimming.expected_after);
1255 - buffer_json_member_add_time_t(wb, "trimmed_after", r->partial_data_trimming.trimmed_after);
1256 - buffer_json_object_close(wb);
1257 -
1258 - buffer_json_member_add_uint64(wb, "points", rows);
1259 - query_target_combined_units_v2(wb, qt, contexts);
1260 - query_target_combined_chart_type(wb, qt, contexts);
1261 - buffer_json_member_add_object(wb, "dimensions");
1262 - {
1263 - rrdr_dimension_ids(wb, "ids", r, options);
1264 - rrdr_dimension_names(wb, "names", r, options);
1265 - rrdr_dimension_units_array_v2(wb, r, options);
1266 - rrdr_dimension_priority_array(wb, r, options);
1267 - rrdr_dimension_grouped_array(wb, r, options);
1268 - size_t dims = rrdr_latest_values(wb, "view_latest_values", r, options);
1269 - buffer_json_member_add_uint64(wb, "count", dims);
1270 - }
1271 - buffer_json_object_close(wb);
1272 - }
1273 - buffer_json_object_close(wb);
1320 }
1321
1322 //static void annotations_range_for_value_flags(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options, RRDR_VALUE_FLAGS flags, const char *type) {
@@ -1346,145 +1392,57 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRD
1392 // buffer_json_array_close(wb); // annotations
1393 //}
1394
1349 -void rrdr2json_v2(RRDR *r __maybe_unused, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options) {
1350 - bool expose_gbc = query_target_aggregatable(r->internal.qt);
1351 -
1352 - buffer_json_member_add_object(wb, "result");
1353 -
1354 - buffer_json_member_add_array(wb, "labels");
1355 - buffer_json_add_array_item_string(wb, "time");
1356 - long d, i;
1357 - const long used = (long)r->d;
1358 - for(d = 0, i = 0; d < used ; d++) {
1359 - if(!rrdr_dimension_should_be_exposed(r->od[d], options))
1360 - continue;
1361 -
1362 - buffer_json_add_array_item_string(wb, string2str(r->dn[d]));
1363 - i++;
1364 - }
1365 - buffer_json_array_close(wb); // labels
1366 -
1367 - buffer_json_member_add_object(wb, "point");
1368 - buffer_json_member_add_uint64(wb, "value", 0);
1369 - buffer_json_member_add_uint64(wb, "ar", 1);
1370 - buffer_json_member_add_uint64(wb, "pa", 2);
1371 - if(expose_gbc)
1372 - buffer_json_member_add_uint64(wb, "count", 3);
1373 - buffer_json_object_close(wb);
1374 -
1375 - buffer_json_member_add_array(wb, "data");
1376 - if(i) {
1377 - long start = 0, end = rrdr_rows(r), step = 1;
1378 - if (!(options & RRDR_OPTION_REVERSED)) {
1379 - start = rrdr_rows(r) - 1;
1380 - end = -1;
1381 - step = -1;
1382 - }
1383 -
1384 - // for each line in the array
1385 - for (i = start; i != end; i += step) {
1386 - NETDATA_DOUBLE *cn = &r->v[ i * r->d ];
1387 - RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
1388 - NETDATA_DOUBLE *ar = &r->ar[ i * r->d ];
1389 - uint32_t *gbc = &r->gbc [ i * r->d ];
1390 - time_t now = r->t[i];
1391 -
1392 - buffer_json_add_array_item_array(wb); // row
1393 -
1394 - if (options & RRDR_OPTION_MILLISECONDS)
1395 - buffer_json_add_array_item_time_ms(wb, now); // the time
1396 - else
1397 - buffer_json_add_array_item_time_t(wb, now); // the time
1398 -
1399 - NETDATA_DOUBLE total = 1;
1400 - if(unlikely((options & RRDR_OPTION_PERCENTAGE) && !(options & (RRDR_OPTION_INTERNAL_GBC|RRDR_OPTION_INTERNAL_AR)))) {
1401 - total = 0;
1402 - for(d = 0; d < used ; d++) {
1403 - if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED))) continue;
1404 -
1405 - NETDATA_DOUBLE n = cn[d];
1406 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1407 - n = -n;
1408 -
1409 - total += n;
1410 - }
1411 -
1412 - // prevent a division by zero
1413 - if(total == 0) total = 1;
1414 - }
1415 -
1416 - for (d = 0; d < used; d++) {
1417 - if (!rrdr_dimension_should_be_exposed(r->od[d], options))
1418 - continue;
1419 -
1420 - RRDR_VALUE_FLAGS o = co[d];
1421 -
1422 - buffer_json_add_array_item_array(wb); // point
1423 -
1424 - // add the value
1425 - NETDATA_DOUBLE n = cn[d];
1426 -
1427 - if(o & RRDR_VALUE_EMPTY) {
1428 - if (unlikely(options & RRDR_OPTION_NULL2ZERO))
1429 - buffer_json_add_array_item_double(wb, 0);
1430 - else
1431 - buffer_json_add_array_item_double(wb, NAN);
1432 - }
1433 - else {
1434 - if (unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1435 - n = -n;
1436 -
1437 - if (unlikely((options & RRDR_OPTION_PERCENTAGE))) {
1438 - n = n * 100 / total;
1439 - }
1440 -
1441 - if(unlikely(i == start && d == 0)) {
1442 - r->view.min = r->view.max = n;
1443 - }
1444 - else {
1445 - if (n < r->view.min) r->view.min = n;
1446 - if (n > r->view.max) r->view.max = n;
1447 - }
1448 -
1449 - buffer_json_add_array_item_double(wb, n);
1450 - }
1395 +void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb) {
1396 + buffer_json_member_add_double(wb, "min", r->view.min);
1397 + buffer_json_member_add_double(wb, "max", r->view.max);
1398
1452 - // add the anomaly
1453 - buffer_json_add_array_item_double(wb, ar[d]);
1399 + rrdr_timings_v12(wb, "timings", r);
1400 + buffer_json_finalize(wb);
1401 +}
1402
1455 - // add the point annotations
1456 - buffer_json_add_array_item_uint64(wb, o);
1403 +void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
1404 + QUERY_TARGET *qt = r->internal.qt;
1405 + DATASOURCE_FORMAT format = qt->request.format;
1406 + RRDR_OPTIONS options = qt->request.options;
1407
1458 - // add the count
1459 - if(expose_gbc)
1460 - buffer_json_add_array_item_uint64(wb, gbc[d]);
1408 + buffer_json_member_add_object(wb, "view");
1409 + {
1410 + query_target_title(wb, qt, r->internal.contexts);
1411 + buffer_json_member_add_string(wb, "format", rrdr_format_to_string(format));
1412 + web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", r->view.options);
1413 + buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(qt->request.time_group_method));
1414 + buffer_json_member_add_time_t(wb, "update_every", r->view.update_every);
1415 + buffer_json_member_add_time_t(wb, "after", r->view.after);
1416 + buffer_json_member_add_time_t(wb, "before", r->view.before);
1417
1462 - buffer_json_array_close(wb); // point
1463 - }
1418 + buffer_json_member_add_object(wb, "partial_data_trimming");
1419 + buffer_json_member_add_time_t(wb, "max_update_every", r->partial_data_trimming.max_update_every);
1420 + buffer_json_member_add_time_t(wb, "expected_after", r->partial_data_trimming.expected_after);
1421 + buffer_json_member_add_time_t(wb, "trimmed_after", r->partial_data_trimming.trimmed_after);
1422 + buffer_json_object_close(wb);
1423
1465 - buffer_json_array_close(wb); // row
1424 + buffer_json_member_add_uint64(wb, "points", rrdr_rows(r));
1425 + query_target_combined_units_v2(wb, qt, r->internal.contexts);
1426 + query_target_combined_chart_type(wb, qt, r->internal.contexts);
1427 + buffer_json_member_add_object(wb, "dimensions");
1428 + {
1429 + rrdr_grouped_by_array_v2(wb, "grouped_by", r, options);
1430 + rrdr_dimension_ids(wb, "ids", r, options);
1431 + rrdr_dimension_names(wb, "names", r, options);
1432 + rrdr_dimension_units_array_v2(wb, "units", r, options);
1433 + rrdr_dimension_priority_array_v2(wb, "priorities", r, options);
1434 + rrdr_dimension_aggregated_array_v2(wb, "aggregated", r, options);
1435 + rrdr_dimension_average_values(wb, "view_average_values", r, options);
1436 + size_t dims = rrdr_dimension_latest_values(wb, "view_latest_values", r, options);
1437 + buffer_json_member_add_uint64(wb, "count", dims);
1438 + rrdr_json_group_by_labels(wb, "labels", r, options);
1439 }
1440 + buffer_json_object_close(wb); // dimensions
1441 + buffer_json_member_add_double(wb, "min", r->view.min);
1442 + buffer_json_member_add_double(wb, "max", r->view.max);
1443 }
1444 + buffer_json_object_close(wb); // view
1445
1469 - buffer_json_array_close(wb); // data
1470 -
1471 - buffer_json_object_close(wb); // annotations
1472 -}
1473 -
1474 -void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format __maybe_unused, RRDR_OPTIONS options __maybe_unused) {
1475 - QUERY_TARGET *qt = r->internal.qt;
1476 -
1477 - buffer_json_member_add_double(wb, "min", r->view.min);
1478 - buffer_json_member_add_double(wb, "max", r->view.max);
1479 -
1480 - qt->timings.finished_ut = now_monotonic_usec();
1481 - buffer_json_member_add_object(wb, "timings");
1482 - buffer_json_member_add_double(wb, "prep_ms", (NETDATA_DOUBLE)(qt->timings.preprocessed_ut - qt->timings.received_ut) / USEC_PER_MS);
1483 - buffer_json_member_add_double(wb, "query_ms", (NETDATA_DOUBLE)(qt->timings.executed_ut - qt->timings.preprocessed_ut) / USEC_PER_MS);
1484 - buffer_json_member_add_double(wb, "group_by_ms", (NETDATA_DOUBLE)(qt->timings.group_by_ut - qt->timings.executed_ut) / USEC_PER_MS);
1485 - buffer_json_member_add_double(wb, "output_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.group_by_ut) / USEC_PER_MS);
1486 - buffer_json_member_add_double(wb, "total_ms", (NETDATA_DOUBLE)(qt->timings.finished_ut - qt->timings.received_ut) / USEC_PER_MS);
1487 - buffer_json_object_close(wb);
1488 -
1446 + rrdr_timings_v12(wb, "timings", r);
1447 buffer_json_finalize(wb);
1448 }
web/api/formatters/json_wrapper.h
+6 -6
@@ -6,13 +6,13 @@
6 #include "rrd2json.h"
7 #include "web/api/queries/query.h"
8
9 -typedef void (*wrapper_begin_t)(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method);
10 -typedef void (*wrapper_end_t)(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
9 +typedef void (*wrapper_begin_t)(RRDR *r, BUFFER *wb);
10 +typedef void (*wrapper_end_t)(RRDR *r, BUFFER *wb);
11
12 -void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method);
13 -void rrdr2json_v2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
14 -void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options);
12 +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb);
13 +void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb);
14
16 -void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb, DATASOURCE_FORMAT format, RRDR_OPTIONS options, RRDR_TIME_GROUPING group_method);
15 +void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb);
16 +void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb);
17
18 #endif //NETDATA_API_FORMATTER_JSON_WRAPPER_H
web/api/formatters/rrd2json.c
+39 -488
@@ -148,448 +148,6 @@ cleanup:
148 return ret;
149 }
150
151 -struct group_by_entry {
152 - size_t priority;
153 - size_t count;
154 - STRING *id;
155 - STRING *name;
156 - STRING *units;
157 - RRDR_DIMENSION_FLAGS od;
158 -};
159 -
160 -static int group_by_label_is_space(char c) {
161 - if(c == ',' || c == '|')
162 - return 1;
163 -
164 - return 0;
165 -}
166 -
167 -static RRDR *data_query_group_by(RRDR *r) {
168 - QUERY_TARGET *qt = r->internal.qt;
169 - RRDR_OPTIONS options = qt->request.options;
170 - size_t rows = rrdr_rows(r);
171 -
172 - if(qt->request.group_by == RRDR_GROUP_BY_NONE || !rows)
173 - return r;
174 -
175 - struct group_by_entry *entries = onewayalloc_callocz(r->internal.owa, qt->query.used, sizeof(struct group_by_entry));
176 - DICTIONARY *groups = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
177 -
178 - if(qt->request.group_by & RRDR_GROUP_BY_LABEL && qt->request.group_by_label && *qt->request.group_by_label)
179 - qt->group_by.used = quoted_strings_splitter(qt->request.group_by_label, qt->group_by.label_keys, GROUP_BY_MAX_LABEL_KEYS, group_by_label_is_space);
180 -
181 - if(!qt->group_by.used)
182 - qt->request.group_by &= ~RRDR_GROUP_BY_LABEL;
183 -
184 - if(!(qt->request.group_by & (RRDR_GROUP_BY_NODE | RRDR_GROUP_BY_INSTANCE | RRDR_GROUP_BY_DIMENSION | RRDR_GROUP_BY_LABEL | RRDR_GROUP_BY_SELECTED)))
185 - qt->request.group_by = RRDR_GROUP_BY_DIMENSION;
186 -
187 - int added = 0;
188 - BUFFER *key = buffer_create(0, NULL);
189 - QUERY_INSTANCE *last_qi = NULL;
190 - size_t priority = 0;
191 - time_t update_every_max = 0;
192 - for(size_t d = 0; d < qt->query.used ; d++) {
193 - if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED)))
194 - continue;
195 -
196 - QUERY_METRIC *qm = query_metric(qt, d);
197 - QUERY_INSTANCE *qi = query_instance(qt, qm->link.query_instance_id);
198 - QUERY_NODE *qn = query_node(qt, qm->link.query_node_id);
199 -
200 - if(qi != last_qi) {
201 - priority = 0;
202 - last_qi = qi;
203 -
204 - time_t update_every = rrdinstance_acquired_update_every(qi->ria);
205 - if(update_every > update_every_max)
206 - update_every_max = update_every;
207 - }
208 - else
209 - priority++;
210 -
211 - // --------------------------------------------------------------------
212 - // generate the group by key
213 -
214 - buffer_flush(key);
215 - if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
216 - buffer_strcat(key, "__hidden_dimensions__");
217 - }
218 - else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
219 - buffer_strcat(key, "selected");
220 - }
221 - else {
222 - if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
223 - buffer_fast_strcat(key, "|", 1);
224 - buffer_strcat(key, query_metric_id(qt, qm));
225 - }
226 -
227 - if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
228 - buffer_fast_strcat(key, "|", 1);
229 - buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
230 - }
231 -
232 - if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
233 - DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
234 - for (size_t l = 0; l < qt->group_by.used; l++) {
235 - buffer_fast_strcat(key, "|", 1);
236 - rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
237 - }
238 - }
239 -
240 - if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
241 - buffer_fast_strcat(key, "|", 1);
242 - buffer_strcat(key, qn->rrdhost->machine_guid);
243 - }
244 -
245 - // append the units
246 - if (query_target_has_percentage_units(qt)) {
247 - buffer_fast_strcat(key, "|%", 2);
248 - } else {
249 - buffer_fast_strcat(key, "|", 1);
250 - buffer_strcat(key, rrdinstance_acquired_units(qi->ria));
251 - }
252 - }
253 -
254 - // lookup the key in the dictionary
255 -
256 - int pos = -1;
257 - int *set = dictionary_set(groups, buffer_tostring(key), &pos, sizeof(pos));
258 - if(*set == -1) {
259 - // the key just added to the dictionary
260 -
261 - *set = pos = added++;
262 -
263 - // ----------------------------------------------------------------
264 - // generate the dimension id
265 -
266 - buffer_flush(key);
267 - if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
268 - buffer_strcat(key, "__hidden_dimensions__");
269 - }
270 - else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
271 - buffer_strcat(key, "selected");
272 - }
273 - else {
274 - if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
275 - buffer_strcat(key, query_metric_id(qt, qm));
276 - }
277 -
278 - if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
279 - if (buffer_strlen(key) != 0)
280 - buffer_fast_strcat(key, ",", 1);
281 -
282 - if (qt->request.group_by & RRDR_GROUP_BY_NODE)
283 - buffer_strcat(key, rrdinstance_acquired_id(qi->ria));
284 - else
285 - buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
286 - }
287 -
288 - if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
289 - DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
290 - for (size_t l = 0; l < qt->group_by.used; l++) {
291 - if (buffer_strlen(key) != 0)
292 - buffer_fast_strcat(key, ",", 1);
293 - rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
294 - }
295 - }
296 -
297 - if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
298 - if (buffer_strlen(key) != 0)
299 - buffer_fast_strcat(key, ",", 1);
300 -
301 - buffer_strcat(key, qn->rrdhost->machine_guid);
302 - }
303 - }
304 -
305 - entries[pos].id = string_strdupz(buffer_tostring(key));
306 -
307 - // ----------------------------------------------------------------
308 - // generate the dimension name
309 -
310 - buffer_flush(key);
311 - if(unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) {
312 - buffer_strcat(key, "__hidden_dimensions__");
313 - }
314 - else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
315 - buffer_strcat(key, "selected");
316 - }
317 - else {
318 - if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
319 - buffer_strcat(key, query_metric_name(qt, qm));
320 - }
321 -
322 - if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
323 - if (buffer_strlen(key) != 0)
324 - buffer_fast_strcat(key, ",", 1);
325 -
326 - if (qt->request.group_by & RRDR_GROUP_BY_NODE)
327 - buffer_strcat(key, rrdinstance_acquired_name(qi->ria));
328 - else
329 - buffer_strcat(key, string2str(query_instance_name_fqdn(qt, qi)));
330 - }
331 -
332 - if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
333 - DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
334 - for (size_t l = 0; l < qt->group_by.used; l++) {
335 - if (buffer_strlen(key) != 0)
336 - buffer_fast_strcat(key, ",", 1);
337 - rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
338 - }
339 - }
340 -
341 - if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
342 - if (buffer_strlen(key) != 0)
343 - buffer_fast_strcat(key, ",", 1);
344 -
345 - buffer_strcat(key, rrdhost_hostname(qn->rrdhost));
346 - }
347 - }
348 -
349 - entries[pos].name = string_strdupz(buffer_tostring(key));
350 -
351 - // add the rest of the info
352 - entries[pos].units = rrdinstance_acquired_units_dup(qi->ria);
353 - entries[pos].priority = priority;
354 - }
355 - else {
356 - // the key found in the dictionary
357 - pos = *set;
358 - }
359 -
360 - entries[pos].count++;
361 -
362 - if(unlikely(priority < entries[pos].priority))
363 - entries[pos].priority = priority;
364 -
365 - qm->grouped_as.slot = pos;
366 - qm->grouped_as.id = entries[pos].id;
367 - qm->grouped_as.name = entries[pos].name;
368 - qm->grouped_as.units = entries[pos].units;
369 -
370 - // copy the dimension flags decided by the query target
371 - // we need this, because if a dimension is explicitly selected
372 - // the query target adds to it the non-zero flag
373 - qm->status |= RRDR_DIMENSION_GROUPED | r->od[d];
374 - entries[pos].od |= RRDR_DIMENSION_GROUPED | r->od[d];
375 - }
376 -
377 - // check if we have multiple units
378 - bool multiple_units = false;
379 - for(int i = 1; i < added ; i++) {
380 - if(entries[i].units != entries[0].units) {
381 - multiple_units = true;
382 - break;
383 - }
384 - }
385 -
386 - if(multiple_units) {
387 - // include the units into the id and name of the dimensions
388 - for(int i = 0; i < added ; i++) {
389 - buffer_flush(key);
390 - buffer_strcat(key, string2str(entries[i].id));
391 - buffer_fast_strcat(key, ",", 1);
392 - buffer_strcat(key, string2str(entries[i].units));
393 - STRING *u = string_strdupz(buffer_tostring(key));
394 - string_freez(entries[i].id);
395 - entries[i].id = u;
396 - }
397 - }
398 -
399 - RRDR *r2 = rrdr_create(r->internal.owa, qt, added, rows);
400 - if(!r2)
401 - goto cleanup;
402 -
403 - r2->dp = onewayalloc_callocz(r2->internal.owa, r2->d, sizeof(*r2->dp));
404 - r2->dgbc = onewayalloc_callocz(r2->internal.owa, r2->d, sizeof(*r2->dgbc));
405 - r2->gbc = onewayalloc_callocz(r2->internal.owa, r2->n * r2->d, sizeof(*r2->gbc));
406 -
407 - // copy from previous rrdr
408 - r2->view = r->view;
409 - r2->stats = r->stats;
410 - r2->rows = rows;
411 - r2->stats.result_points_generated = r2->d * r2->n;
412 -
413 - // initialize r2 (dimension options, names, and ids)
414 - for(size_t d2 = 0; d2 < r2->d ; d2++) {
415 - r2->od[d2] = entries[d2].od;
416 - r2->di[d2] = entries[d2].id;
417 - r2->dn[d2] = entries[d2].name;
418 - r2->du[d2] = entries[d2].units;
419 - r2->dp[d2] = entries[d2].priority;
420 - r2->dgbc[d2] = entries[d2].count;
421 - }
422 -
423 - r2->partial_data_trimming.max_update_every = update_every_max;
424 - r2->partial_data_trimming.expected_after =
425 - (!(qt->request.options & RRDR_OPTION_RETURN_RAW) && qt->window.before >= qt->window.now - update_every_max) ?
426 - qt->window.before - update_every_max :
427 - qt->window.before;
428 - r2->partial_data_trimming.trimmed_after = qt->window.before;
429 -
430 - // initialize r2 (timestamps and value flags)
431 - for(size_t i = 0; i != rows ;i++) {
432 - // copy the timestamp
433 - r2->t[i] = r->t[i];
434 -
435 - // make all values empty
436 - NETDATA_DOUBLE *cn2 = &r2->v[ i * r2->d ];
437 - RRDR_VALUE_FLAGS *co2 = &r2->o[ i * r2->d ];
438 - NETDATA_DOUBLE *ar2 = &r2->ar[ i * r2->d ];
439 - for (size_t d2 = 0; d2 < r2->d; d2++) {
440 - cn2[d2] = 0.0;
441 - ar2[d2] = 0.0;
442 - co2[d2] = RRDR_VALUE_EMPTY;
443 - }
444 - }
445 -
446 - // do the group_by
447 - size_t last_row_gbc = 0;
448 - for(size_t i = 0; i != rows ;i++) {
449 -
450 - size_t idx = i * r->d;
451 - NETDATA_DOUBLE *cn_base = &r->v[ idx ];
452 - RRDR_VALUE_FLAGS *co_base = &r->o[ idx ];
453 - NETDATA_DOUBLE *ar_base = &r->ar[ idx ];
454 -
455 - size_t idx2 = i * r2->d;
456 - NETDATA_DOUBLE *cn2_base = &r2->v[ idx2 ];
457 - RRDR_VALUE_FLAGS *co2_base = &r2->o[ idx2 ];
458 - NETDATA_DOUBLE *ar2_base = &r2->ar[ idx2 ];
459 - uint32_t *gbc2_base = &r2->gbc[ idx2 ];
460 -
461 - size_t row_gbc = 0;
462 - for(size_t d = 0; d < r->d ; d++) {
463 - if(unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED)))
464 - continue;
465 -
466 - NETDATA_DOUBLE n = cn_base[d];
467 - RRDR_VALUE_FLAGS o = co_base[d];
468 - NETDATA_DOUBLE ar = ar_base[d];
469 -
470 - if(o & RRDR_VALUE_EMPTY) {
471 - if(options & RRDR_OPTION_NULL2ZERO)
472 - n = 0.0;
473 - else
474 - continue;
475 - }
476 -
477 - if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
478 - n = -n;
479 -
480 - QUERY_METRIC *qm = query_metric(qt, d);
481 - size_t d2 = qm->grouped_as.slot;
482 -
483 - NETDATA_DOUBLE *cn2 = &cn2_base[d2];
484 - RRDR_VALUE_FLAGS *co2 = &co2_base[d2];
485 - NETDATA_DOUBLE *ar2 = &ar2_base[d2];
486 - uint32_t *gbc2 = &gbc2_base[d2];
487 -
488 - switch(qt->request.group_by_aggregate_function) {
489 - default:
490 - case RRDR_GROUP_BY_FUNCTION_AVERAGE:
491 - case RRDR_GROUP_BY_FUNCTION_SUM:
492 - *cn2 += n;
493 - break;
494 -
495 - case RRDR_GROUP_BY_FUNCTION_MIN:
496 - if(n < *cn2)
497 - *cn2 = n;
498 - break;
499 -
500 - case RRDR_GROUP_BY_FUNCTION_MAX:
501 - if(n > *cn2)
502 - *cn2 = n;
503 - break;
504 - }
505 -
506 - *co2 |= (o & (RRDR_VALUE_RESET|RRDR_VALUE_PARTIAL));
507 - *ar2 += ar;
508 - (*gbc2)++;
509 -
510 - row_gbc++;
511 - }
512 -
513 - if(unlikely(r->t[i] > r2->partial_data_trimming.expected_after && row_gbc < last_row_gbc)) {
514 - // discard the rest of the points
515 - r2->partial_data_trimming.trimmed_after = r->t[i];
516 - r2->rows = i;
517 - rows = i;
518 - break;
519 - }
520 - else
521 - last_row_gbc = row_gbc;
522 - }
523 -
524 - // apply averaging, remove RRDR_VALUE_EMPTY, find the non-zero dimensions, min and max
525 - size_t min_max_values = 0;
526 - NETDATA_DOUBLE min = NAN, max = NAN;
527 - for (size_t d2 = 0; d2 < r2->d; d2++) {
528 - size_t non_zero = 0;
529 -
530 - for(size_t i = 0; i != rows ;i++) {
531 - size_t idx2 = i * r2->d + d2;
532 -
533 - NETDATA_DOUBLE *cn2 = &r2->v[ idx2 ];
534 - RRDR_VALUE_FLAGS *co2 = &r2->o[ idx2 ];
535 - NETDATA_DOUBLE *ar2 = &r2->ar[ idx2 ];
536 - uint32_t gbc2 = r2->gbc[ idx2 ];
537 -
538 - if(likely(gbc2)) {
539 - *co2 &= ~RRDR_VALUE_EMPTY;
540 -
541 - if(gbc2 != r2->dgbc[d2])
542 - *co2 |= RRDR_VALUE_PARTIAL;
543 -
544 - NETDATA_DOUBLE n;
545 -
546 - if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_AVERAGE)
547 - n = (*cn2 /= gbc2);
548 - else
549 - n = *cn2;
550 -
551 - if(!query_target_aggregatable(qt))
552 - *ar2 /= gbc2;
553 -
554 - if(islessgreater(n, 0.0))
555 - non_zero++;
556 -
557 - if(unlikely(!min_max_values++)) {
558 - min = n;
559 - max = n;
560 - }
561 - else {
562 - if(n < min)
563 - min = n;
564 -
565 - if(n > max)
566 - max = n;
567 - }
568 - }
569 - }
570 -
571 - if(non_zero)
572 - r2->od[d2] |= RRDR_DIMENSION_NONZERO;
573 - }
574 -
575 - r2->view.min = min;
576 - r2->view.max = max;
577 -
578 -cleanup:
579 - buffer_free(key);
580 -
581 - if(!r2 && entries && added) {
582 - for(int d2 = 0; d2 < added ; d2++) {
583 - string_freez(entries[d2].id);
584 - string_freez(entries[d2].name);
585 - }
586 - }
587 - onewayalloc_freez(r->internal.owa, entries);
588 - dictionary_destroy(groups);
589 -
590 - return r2;
591 -}
592 -
151 static inline void buffer_json_member_add_key_only(BUFFER *wb, const char *key) {
152 buffer_print_json_comma_newline_spacing(wb);
153 buffer_print_json_key(wb, key);
@@ -610,46 +168,46 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
168 wrapper_begin_t wrapper_begin = rrdr_json_wrapper_begin;
169 wrapper_end_t wrapper_end = rrdr_json_wrapper_end;
170
613 - if(qt->request.version == 2)
171 + if(qt->request.version == 2) {
172 wrapper_begin = rrdr_json_wrapper_begin2;
173 + wrapper_end = rrdr_json_wrapper_end2;
174 + }
175
616 - RRDR *r1 = rrd2rrdr(owa, qt);
176 + RRDR *r = rrd2rrdr(owa, qt);
177 qt->timings.executed_ut = now_monotonic_usec();
178
619 - if(!r1) {
179 + if(!r) {
180 buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
181 return HTTP_RESP_INTERNAL_SERVER_ERROR;
182 }
183
624 - if (r1->view.flags & RRDR_RESULT_FLAG_CANCEL) {
625 - rrdr_free(owa, r1);
184 + if (r->view.flags & RRDR_RESULT_FLAG_CANCEL) {
185 + rrdr_free(owa, r);
186 return HTTP_RESP_BACKEND_FETCH_FAILED;
187 }
188
629 - if(r1->view.flags & RRDR_RESULT_FLAG_RELATIVE)
189 + if(r->view.flags & RRDR_RESULT_FLAG_RELATIVE)
190 buffer_no_cacheable(wb);
631 - else if(r1->view.flags & RRDR_RESULT_FLAG_ABSOLUTE)
191 + else if(r->view.flags & RRDR_RESULT_FLAG_ABSOLUTE)
192 buffer_cacheable(wb);
193
634 - if(latest_timestamp && rrdr_rows(r1) > 0)
635 - *latest_timestamp = r1->view.before;
194 + if(latest_timestamp && rrdr_rows(r) > 0)
195 + *latest_timestamp = r->view.before;
196
197 DATASOURCE_FORMAT format = qt->request.format;
198 RRDR_OPTIONS options = qt->request.options;
639 - RRDR_TIME_GROUPING group_method = qt->request.time_group_method;
199
641 - RRDR *r = data_query_group_by(r1);
200 qt->timings.group_by_ut = now_monotonic_usec();
201
202 switch(format) {
203 case DATASOURCE_SSV:
204 if(options & RRDR_OPTION_JSON_WRAP) {
205 wb->content_type = CT_APPLICATION_JSON;
648 - wrapper_begin(r, wb, format, options, group_method);
206 + wrapper_begin(r, wb);
207 buffer_json_member_add_string_open(wb, "result");
208 rrdr2ssv(r, wb, options, "", " ", "");
209 buffer_json_member_add_string_close(wb);
652 - wrapper_end(r, wb, format, options);
210 + wrapper_end(r, wb);
211 }
212 else {
213 wb->content_type = CT_TEXT_PLAIN;
@@ -660,11 +218,11 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
218 case DATASOURCE_SSV_COMMA:
219 if(options & RRDR_OPTION_JSON_WRAP) {
220 wb->content_type = CT_APPLICATION_JSON;
663 - wrapper_begin(r, wb, format, options, group_method);
221 + wrapper_begin(r, wb);
222 buffer_json_member_add_string_open(wb, "result");
223 rrdr2ssv(r, wb, options, "", ",", "");
224 buffer_json_member_add_string_close(wb);
667 - wrapper_end(r, wb, format, options);
225 + wrapper_end(r, wb);
226 }
227 else {
228 wb->content_type = CT_TEXT_PLAIN;
@@ -675,11 +233,11 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
233 case DATASOURCE_JS_ARRAY:
234 if(options & RRDR_OPTION_JSON_WRAP) {
235 wb->content_type = CT_APPLICATION_JSON;
678 - wrapper_begin(r, wb, format, options, group_method);
236 + wrapper_begin(r, wb);
237 buffer_json_member_add_array(wb, "result");
238 rrdr2ssv(r, wb, options, "", ",", "");
239 buffer_json_array_close(wb);
682 - wrapper_end(r, wb, format, options);
240 + wrapper_end(r, wb);
241 }
242 else {
243 wb->content_type = CT_APPLICATION_JSON;
@@ -690,11 +248,11 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
248 case DATASOURCE_CSV:
249 if(options & RRDR_OPTION_JSON_WRAP) {
250 wb->content_type = CT_APPLICATION_JSON;
693 - wrapper_begin(r, wb, format, options, group_method);
251 + wrapper_begin(r, wb);
252 buffer_json_member_add_string_open(wb, "result");
253 rrdr2csv(r, wb, format, options, "", ",", "\\n", "");
254 buffer_json_member_add_string_close(wb);
697 - wrapper_end(r, wb, format, options);
255 + wrapper_end(r, wb);
256 }
257 else {
258 wb->content_type = CT_TEXT_PLAIN;
@@ -705,11 +263,11 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
263 case DATASOURCE_CSV_MARKDOWN:
264 if(options & RRDR_OPTION_JSON_WRAP) {
265 wb->content_type = CT_APPLICATION_JSON;
708 - wrapper_begin(r, wb, format, options, group_method);
266 + wrapper_begin(r, wb);
267 buffer_json_member_add_string_open(wb, "result");
268 rrdr2csv(r, wb, format, options, "", "|", "\\n", "");
269 buffer_json_member_add_string_close(wb);
712 - wrapper_end(r, wb, format, options);
270 + wrapper_end(r, wb);
271 }
272 else {
273 wb->content_type = CT_TEXT_PLAIN;
@@ -720,11 +278,11 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
278 case DATASOURCE_CSV_JSON_ARRAY:
279 wb->content_type = CT_APPLICATION_JSON;
280 if(options & RRDR_OPTION_JSON_WRAP) {
723 - wrapper_begin(r, wb, format, options, group_method);
281 + wrapper_begin(r, wb);
282 buffer_json_member_add_array(wb, "result");
283 rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
284 buffer_json_array_close(wb);
727 - wrapper_end(r, wb, format, options);
285 + wrapper_end(r, wb);
286 }
287 else {
288 wb->content_type = CT_APPLICATION_JSON;
@@ -737,11 +295,11 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
295 case DATASOURCE_TSV:
296 if(options & RRDR_OPTION_JSON_WRAP) {
297 wb->content_type = CT_APPLICATION_JSON;
740 - wrapper_begin(r, wb, format, options, group_method);
298 + wrapper_begin(r, wb);
299 buffer_json_member_add_string_open(wb, "result");
300 rrdr2csv(r, wb, format, options, "", "\t", "\\n", "");
301 buffer_json_member_add_string_close(wb);
744 - wrapper_end(r, wb, format, options);
302 + wrapper_end(r, wb);
303 }
304 else {
305 wb->content_type = CT_TEXT_PLAIN;
@@ -752,13 +310,13 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
310 case DATASOURCE_HTML:
311 if(options & RRDR_OPTION_JSON_WRAP) {
312 wb->content_type = CT_APPLICATION_JSON;
755 - wrapper_begin(r, wb, format, options, group_method);
313 + wrapper_begin(r, wb);
314 buffer_json_member_add_string_open(wb, "result");
315 buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
316 rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
317 buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
318 buffer_json_member_add_string_close(wb);
761 - wrapper_end(r, wb, format, options);
319 + wrapper_end(r, wb);
320 }
321 else {
322 wb->content_type = CT_TEXT_HTML;
@@ -772,14 +330,14 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
330 wb->content_type = CT_APPLICATION_X_JAVASCRIPT;
331
332 if(options & RRDR_OPTION_JSON_WRAP) {
775 - wrapper_begin(r, wb, format, options, group_method);
333 + wrapper_begin(r, wb);
334 buffer_json_member_add_key_only(wb, "result");
335 }
336
337 rrdr2json(r, wb, options, 1);
338
339 if(options & RRDR_OPTION_JSON_WRAP)
782 - wrapper_end(r, wb, format, options);
340 + wrapper_end(r, wb);
341
342 break;
343
@@ -787,28 +345,28 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
345 wb->content_type = CT_APPLICATION_JSON;
346
347 if(options & RRDR_OPTION_JSON_WRAP) {
790 - wrapper_begin(r, wb, format, options, group_method);
348 + wrapper_begin(r, wb);
349 buffer_json_member_add_key_only(wb, "result");
350 }
351
352 rrdr2json(r, wb, options, 1);
353
354 if(options & RRDR_OPTION_JSON_WRAP)
797 - wrapper_end(r, wb, format, options);
355 + wrapper_end(r, wb);
356
357 break;
358
359 case DATASOURCE_JSONP:
360 wb->content_type = CT_APPLICATION_X_JAVASCRIPT;
361 if(options & RRDR_OPTION_JSON_WRAP) {
804 - wrapper_begin(r, wb, format, options, group_method);
362 + wrapper_begin(r, wb);
363 buffer_json_member_add_key_only(wb, "result");
364 }
365
366 rrdr2json(r, wb, options, 0);
367
368 if(options & RRDR_OPTION_JSON_WRAP)
811 - wrapper_end(r, wb, format, options);
369 + wrapper_end(r, wb);
370
371 break;
372
@@ -817,36 +375,29 @@ int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, QUERY_TARGET *qt, time_t *l
375 wb->content_type = CT_APPLICATION_JSON;
376
377 if(options & RRDR_OPTION_JSON_WRAP) {
820 - wrapper_begin(r, wb, format, options, group_method);
378 + wrapper_begin(r, wb);
379 buffer_json_member_add_key_only(wb, "result");
380 }
381
382 rrdr2json(r, wb, options, 0);
383
384 if(options & RRDR_OPTION_JSON_WRAP) {
827 - if (query_target_aggregatable(qt)) {
828 - buffer_json_member_add_key_only(wb, "group_by_count");
829 - rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_GBC, false);
830 - }
385 if (options & RRDR_OPTION_RETURN_JWAR) {
386 buffer_json_member_add_key_only(wb, "anomaly_rates");
387 rrdr2json(r, wb, options | RRDR_OPTION_INTERNAL_AR, false);
388 }
835 - wrapper_end(r, wb, format, options);
389 + wrapper_end(r, wb);
390 }
391 break;
392
393 case DATASOURCE_JSON2:
394 wb->content_type = CT_APPLICATION_JSON;
841 - wrapper_begin(r, wb, format, options, group_method);
842 - rrdr2json_v2(r, wb, format, options);
843 - wrapper_end(r, wb, format, options);
395 + wrapper_begin(r, wb);
396 + rrdr2json_v2(r, wb);
397 + wrapper_end(r, wb);
398 break;
399 }
400
847 - if(r != r1)
848 - rrdr_free(owa, r);
849 -
850 - rrdr_free(owa, r1);
401 + rrdr_free(owa, r);
402 return HTTP_RESP_OK;
403 }
web/api/formatters/rrd2json.h
+2
@@ -59,6 +59,8 @@ const char *rrdr_format_to_string(DATASOURCE_FORMAT format);
59
60 int data_query_execute(ONEWAYALLOC *owa, BUFFER *wb, struct query_target *qt, time_t *latest_timestamp);
61
62 +void rrdr_json_group_by_labels(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options);
63 +
64 struct query_target;
65 bool query_target_has_percentage_units(struct query_target *qt);
66 bool query_target_aggregatable(struct query_target *qt);
web/api/netdata-swagger.json
+2242 -1230
@@ -3,10 +3,10 @@
3 "info": {
4 "title": "Netdata API",
5 "description": "Real-time performance and health monitoring.",
6 - "version": "1.33.1"
6 + "version": "1.38"
7 },
8 "paths": {
9 - "/info": {
9 + "/api/v1/info": {
10 "get": {
11 "summary": "Get netdata basic information",
12 "description": "The info endpoint returns basic information about netdata. It provides:\n* netdata version\n* netdata unique id\n* list of hosts mirrored (includes itself)\n* Operating System, Virtualization, K8s nodes and Container technology information\n* List of active collector plugins and modules\n* Streaming information\n* number of alarms in the host\n * number of alarms in normal state\n * number of alarms in warning state\n * number of alarms in critical state\n",
@@ -27,7 +27,7 @@
27 }
28 }
29 },
30 - "/charts": {
30 + "/api/v1/charts": {
31 "get": {
32 "summary": "Get a list of all charts available at the server",
33 "description": "The charts endpoint returns a summary about all charts stored in the netdata server.",
@@ -45,7 +45,7 @@
45 }
46 }
47 },
48 - "/chart": {
48 + "/api/v1/chart": {
49 "get": {
50 "summary": "Get info about a specific chart",
51 "description": "The chart endpoint returns detailed information about a chart.",
@@ -82,7 +82,7 @@
82 }
83 }
84 },
85 - "/contexts": {
85 + "/api/v1/contexts": {
86 "get": {
87 "summary": "Get a list of all contexts available at the server",
88 "description": "The contexts endpoint returns a summary about all contexts stored in the netdata server.",
@@ -180,7 +180,7 @@
180 }
181 }
182 },
183 - "/context": {
183 + "/api/v1/context": {
184 "get": {
185 "summary": "Get info about a specific context",
186 "description": "The context endpoint returns detailed information about a given context.",
@@ -295,7 +295,7 @@
295 }
296 }
297 },
298 - "/alarm_variables": {
298 + "/api/v1/alarm_variables": {
299 "get": {
300 "summary": "List variables available to configure alarms for a chart",
301 "description": "Returns the basic information of a chart and all the variables that can be used in alarm and template health configurations for the particular chart or family.",
@@ -335,64 +335,114 @@
335 }
336 }
337 },
338 - "/data": {
338 + "/api/v2/data": {
339 "get": {
340 - "summary": "Get collected data for a specific chart",
341 - "description": "The data endpoint returns data stored in the round robin database of a chart.",
340 + "summary": "Query metrics data",
341 + "description": "Multi-node, multi-context, multi-instance, multi-dimension data queries, with time and metric aggregation.\n",
342 "parameters": [
343 {
344 - "name": "chart",
344 + "name": "scope_nodes",
345 "in": "query",
346 - "description": "The id of the chart as returned by the /charts call. Note chart or context must be specified",
346 + "description": "A simple pattern limiting the nodes scope of the query. The scope controls both data and metadata response. The simple pattern is checked against the nodes' machine guid, node id, hostname. The default nodes scope is all nodes for which this agent has data for. Usually the nodes scope is used to slice the entire dashboard (e.g. the Global Nodes Selector at the Netdata Cloud overview dashboard). Both positive and negative simple pattern expressions are supported.\n",
347 "required": false,
348 - "allowEmptyValue": false,
348 "schema": {
349 "type": "string",
351 - "format": "as returned by /charts",
352 - "default": "system.cpu"
350 + "format": "simple pattern",
351 + "default": "*"
352 }
353 },
354 {
356 - "name": "context",
355 + "name": "scope_contexts",
356 "in": "query",
358 - "description": "The context of the chart as returned by the /charts call. Note chart or context must be specified",
357 + "description": "A simple pattern limiting the contexts scope of the query. The scope controls both data and metadata response. The default contexts scope is all contexts for which this agent has data for. Usually the contexts scope is used to slice charts of the dashboard (e.g. each context based chart has its own contexts scope, limiting the chart to all the instances of the selected contexts). Both positive and negative simple pattern expressions are supported.\n",
358 "required": false,
360 - "allowEmptyValue": false,
359 "schema": {
360 "type": "string",
363 - "format": "as returned by /charts"
361 + "format": "simple pattern",
362 + "default": "*"
363 }
364 },
365 {
367 - "name": "dimension",
366 + "name": "nodes",
367 "in": "query",
369 - "description": "Zero, one or more dimension ids or names, as returned by the /chart call, separated with comma or pipe. Netdata simple patterns are supported.",
368 + "description": "A simple pattern matching the nodes to be queried. This only controls the data response, not the metadata. The simple pattern is checked against the nodes' machine guid, node id, hostname. The default nodes selector is all the nodes matched by the nodes scope. Both positive and negative simple pattern expressions are supported.\n",
369 "required": false,
371 - "allowEmptyValue": false,
370 "schema": {
373 - "type": "array",
374 - "items": {
375 - "type": "string",
376 - "format": "as returned by /charts"
377 - }
371 + "type": "string",
372 + "format": "simple pattern",
373 + "default": "*"
374 }
375 },
376 {
381 - "name": "after",
377 + "name": "contexts",
378 "in": "query",
383 - "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (negative, relative to parameter: before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). If not specified the default is -600 seconds. Netdata will adapt this parameter to the boundaries of the round robin database unless the allow_past option is specified.",
384 - "required": true,
385 - "allowEmptyValue": false,
379 + "description": "A simple pattern matching the contexts to be queried. This only controls the data response, not the metadata. Both positive and negative simple pattern expressions are supported.\n",
380 + "required": false,
381 + "schema": {
382 + "type": "string",
383 + "format": "simple pattern",
384 + "default": "*"
385 + }
386 + },
387 + {
388 + "name": "instances",
389 + "in": "query",
390 + "description": "A simple pattern matching the instances to be queried. The simple pattern is checked against the instance `id`, the instance `name`, the fully qualified name of the instance `id` and `name`, like `instance@machine_guid`, where `instance` is either its `id` or `name`. Both positive and negative simple pattern expressions are supported.\n",
391 + "required": false,
392 + "schema": {
393 + "type": "string",
394 + "format": "simple pattern",
395 + "default": "*"
396 + }
397 + },
398 + {
399 + "name": "labels",
400 + "in": "query",
401 + "description": "A simple pattern matching the labels to be queried. The simple pattern is checked against `name:value` of all the labels of all the eligible instances (as filtered by all the above: scope nodes, scope contexts, nodes, contexts and instances). Negative simple patterns should not be used in this filter.\n",
402 + "required": false,
403 + "schema": {
404 + "type": "string",
405 + "format": "simple pattern",
406 + "default": "*"
407 + }
408 + },
409 + {
410 + "name": "alerts",
411 + "in": "query",
412 + "description": "A simple pattern matching the alerts to be queried. The simple pattern is checked against the `name` of alerts and the combination of `name:status`, when status is one of `CLEAR`, `WARNING`, `CRITICAL`, `REMOVED`, `UNDEFINED`, `UNINITIALIZED`, of all the alerts of all the eligible instances (as filtered by all the above). A negative simple pattern will exclude the instances having the labels matched.\n",
413 + "required": false,
414 + "schema": {
415 + "type": "string",
416 + "format": "simple pattern",
417 + "default": "*"
418 + }
419 + },
420 + {
421 + "name": "dimensions",
422 + "in": "query",
423 + "description": "A simple patterns matching the dimensions to be queried. The simple pattern is checked against and `id` and the `name` of the dimensions of the eligible instances (as filtered by all the above). Both positive and negative simple pattern expressions are supported.\n",
424 + "required": false,
425 + "schema": {
426 + "type": "string",
427 + "format": "simple pattern",
428 + "default": "*"
429 + }
430 + },
431 + {
432 + "name": "before",
433 + "in": "query",
434 + "description": "The end timestamp (unix epoch) of the data query, or a negative number specifying the number of seconds\nin the past relative now.\n",
435 + "required": false,
436 "schema": {
437 "type": "number",
438 "format": "integer",
389 - "default": -600
439 + "default": 0
440 }
441 },
442 {
393 - "name": "before",
443 + "name": "after",
444 "in": "query",
395 - "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is zero (i.e. the timestamp of the last value collected).",
445 + "description": "The start timestamp (unix epoch) of the data query, or a negative number specifying the number of seconds\nin the past relative to parameter `before`.\n",
446 "required": false,
447 "schema": {
448 "type": "number",
@@ -403,48 +453,77 @@
453 {
454 "name": "points",
455 "in": "query",
406 - "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration will be returned.",
407 - "required": true,
408 - "allowEmptyValue": false,
456 + "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the database for the given duration, all the available collected values for the given duration will be returned.\n",
457 + "required": false,
458 "schema": {
459 "type": "number",
460 "format": "integer",
412 - "default": 20
461 + "default": 0
462 }
463 },
464 {
416 - "name": "chart_label_key",
465 + "name": "group_by",
466 "in": "query",
418 - "description": "Specify the chart label keys that need to match for context queries as comma separated values. At least one matching key is needed to match the corresponding chart.",
467 + "description": "A comma separated list of the groupings required.\nAll possible values can be combined together, except `selected`. If `selected` is given in the list, all others are ignored.\nThe order they are placed in the list is currently ignored.\n",
468 + "required": false,
469 + "schema": {
470 + "type": "array",
471 + "items": {
472 + "type": "string",
473 + "enum": [
474 + "dimension",
475 + "instance",
476 + "label",
477 + "node",
478 + "context",
479 + "units",
480 + "selected"
481 + ]
482 + },
483 + "default": [
484 + "dimension"
485 + ]
486 + }
487 + },
488 + {
489 + "name": "group_by_label",
490 + "in": "query",
491 + "description": "A comma separated list of the label keys to group by their values. The order of the labels in the list is respected.\n",
492 "required": false,
420 - "allowEmptyValue": false,
493 "schema": {
494 "type": "string",
423 - "format": "key1,key2,key3"
495 + "format": "comma separated list of label keys to group by",
496 + "default": ""
497 }
498 },
499 {
427 - "name": "chart_labels_filter",
500 + "name": "aggregation",
501 "in": "query",
429 - "description": "Specify the chart label keys and values to match for context queries. All keys/values need to match for the chart to be included in the query. The labels are specified as key1:value1,key2:value2",
502 + "description": "The aggregation function to apply when grouping metrics together.\n",
503 "required": false,
431 - "allowEmptyValue": false,
504 "schema": {
505 "type": "string",
434 - "format": "key1:value1,key2:value2,key3:value3"
506 + "enum": [
507 + "min",
508 + "max",
509 + "avg",
510 + "average",
511 + "sum"
512 + ],
513 + "default": "average"
514 }
515 },
516 {
438 - "name": "group",
517 + "name": "time_group",
518 "in": "query",
440 - "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).",
519 + "description": "Time aggregation function. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).\n",
520 "required": true,
442 - "allowEmptyValue": false,
521 "schema": {
522 "type": "string",
523 "enum": [
524 "min",
525 "max",
526 + "avg",
527 "average",
528 "median",
529 "stddev",
@@ -487,21 +566,19 @@
566 }
567 },
568 {
490 - "name": "group_options",
569 + "name": "time_group_options",
570 "in": "query",
492 - "description": "When the group function supports additional parameters, this field can be used to pass them to it. Currently only \"countif\" supports this.",
571 + "description": "When the group function supports additional parameters, this field can be used to pass them to it. Currently `countif`, `trimmed-mean`, `trimmed-median` and `percentile` support this. For `countif` the string may start with `<`, `<=`, `<:`, `<>`, `!=`, `>`, `>=`, `>:`. For all others just a number is expected.\n",
572 "required": false,
494 - "allowEmptyValue": false,
573 "schema": {
574 "type": "string"
575 }
576 },
577 {
500 - "name": "gtime",
578 + "name": "time_resampling",
579 "in": "query",
502 - "description": "The grouping number of seconds. This is used in conjunction with group=average to change the units of metrics (ie when the data is per-second, setting gtime=60 will turn them to per-minute).",
580 + "description": "For incremental values that are \"per second\", this value is used to resample them to \"per minute` (60) or \"per hour\" (3600). It can only be used in conjunction with group=average.\n",
581 "required": false,
504 - "allowEmptyValue": false,
582 "schema": {
583 "type": "number",
584 "format": "integer",
@@ -511,9 +588,8 @@
588 {
589 "name": "timeout",
590 "in": "query",
514 - "description": "Specify a timeout value in milliseconds after which the agent will abort the query and return a 503 error. A value of 0 indicates no timeout.",
591 + "description": "Specify a timeout value in milliseconds after which the agent will abort the query and return a 503 error. A value of 0 indicates no timeout.\n",
592 "required": false,
516 - "allowEmptyValue": false,
593 "schema": {
594 "type": "number",
595 "format": "integer",
@@ -523,13 +599,14 @@
599 {
600 "name": "format",
601 "in": "query",
526 - "description": "The format of the data to be returned.",
602 + "description": "The format of the data to be returned.\n",
603 "required": true,
604 "allowEmptyValue": false,
605 "schema": {
606 "type": "string",
607 "enum": [
608 "json",
609 + "json2",
610 "jsonp",
611 "csv",
612 "tsv",
@@ -543,13 +620,13 @@
620 "array",
621 "csvjsonarray"
622 ],
546 - "default": "json"
623 + "default": "json2"
624 }
625 },
626 {
627 "name": "options",
628 "in": "query",
552 - "description": "Options that affect data generation.",
629 + "description": "Options that affect data generation.\n`raw` changes the output so that the values can be aggregated across multiple such queries.\n",
630 "required": false,
631 "allowEmptyValue": false,
632 "schema": {
@@ -557,24 +634,21 @@
634 "items": {
635 "type": "string",
636 "enum": [
637 + "raw",
638 "nonzero",
639 "flip",
562 - "jsonwrap",
640 "min2max",
641 "seconds",
642 "milliseconds",
643 "abs",
644 "absolute",
568 - "absolute-sum",
645 "null2zero",
570 - "objectrows",
571 - "google_json",
646 "percentage",
647 "unaligned",
648 "match-ids",
649 "match-names",
576 - "allow_past",
577 - "anomaly-bit"
650 + "anomaly-bit",
651 + "group-by-labels"
652 ]
653 },
654 "default": [
@@ -583,12 +657,21 @@
657 ]
658 }
659 },
660 + {
661 + "name": "tier",
662 + "in": "query",
663 + "description": "Use only the specified database tier.\n",
664 + "required": false,
665 + "schema": {
666 + "type": "number",
667 + "format": "integer"
668 + }
669 + },
670 {
671 "name": "callback",
672 "in": "query",
589 - "description": "For JSONP responses, the callback function name.",
673 + "description": "For JSONP responses, the callback function name.\n",
674 "required": false,
591 - "allowEmptyValue": true,
675 "schema": {
676 "type": "string"
677 }
@@ -596,9 +679,8 @@
679 {
680 "name": "filename",
681 "in": "query",
599 - "description": "Add Content-Disposition: attachment; filename= header to the response, that will instruct the browser to save the response with the given filename.",
682 + "description": "Add `Content-Disposition: attachment; filename=` header to the response, that will instruct the browser to save the response with the given filename.\"\n",
683 "required": false,
601 - "allowEmptyValue": true,
684 "schema": {
685 "type": "string"
686 }
@@ -606,9 +688,8 @@
688 {
689 "name": "tqx",
690 "in": "query",
609 - "description": "[Google Visualization API](https://developers.google.com/chart/interactive/docs/dev/implementing_data_source?hl=en) formatted parameter.",
691 + "description": "[Google Visualization API](https://developers.google.com/chart/interactive/docs/dev/implementing_data_source?hl=en) formatted parameter.\n",
692 "required": false,
611 - "allowEmptyValue": true,
693 "schema": {
694 "type": "string"
695 }
@@ -616,37 +697,34 @@
697 ],
698 "responses": {
699 "200": {
619 - "description": "The call was successful. The response includes the data in the format requested. Swagger2.0 does not process the discriminator field to show polymorphism. The response will be one of the sub-types of the data-schema according to the chosen format, e.g. json -> data_json.",
700 + "description": "The call was successful. The response includes the data in the format requested. Swagger2.0 does not process the discriminator field to show polymorphism. The response will be one of the sub-types of the data-schema according to the chosen format, e.g. json -> data_json.\n",
701 "content": {
702 "application/json": {
703 "schema": {
623 - "$ref": "#/components/schemas/data"
704 + "$ref": "#/components/schemas/data_json2"
705 }
706 }
707 }
708 },
709 "400": {
629 - "description": "Bad request - the body will include a message stating what is wrong."
630 - },
631 - "404": {
632 - "description": "Chart or context is not found. The supplied chart or context will be reported."
710 + "description": "Bad request - the body will include a message stating what is wrong.\n"
711 },
712 "500": {
635 - "description": "Internal server error. This usually means the server is out of memory."
713 + "description": "Internal server error. This usually means the server is out of memory.\n"
714 }
715 }
716 }
717 },
640 - "/badge.svg": {
718 + "/api/v1/data": {
719 "get": {
642 - "summary": "Generate a badge in form of SVG image for a chart (or dimension)",
643 - "description": "Successful responses are SVG images.",
720 + "summary": "Get collected data for a specific chart",
721 + "description": "The data endpoint returns data stored in the round robin database of a chart.",
722 "parameters": [
723 {
724 "name": "chart",
725 "in": "query",
648 - "description": "The id of the chart as returned by the /charts call.",
649 - "required": true,
726 + "description": "The id of the chart as returned by the /charts call. Note chart or context must be specified",
727 + "required": false,
728 "allowEmptyValue": false,
729 "schema": {
730 "type": "string",
@@ -655,20 +733,20 @@
733 }
734 },
735 {
658 - "name": "alarm",
736 + "name": "context",
737 "in": "query",
660 - "description": "The name of an alarm linked to the chart.",
738 + "description": "The context of the chart as returned by the /charts call. Note chart or context must be specified",
739 "required": false,
662 - "allowEmptyValue": true,
740 + "allowEmptyValue": false,
741 "schema": {
742 "type": "string",
665 - "format": "any text"
743 + "format": "as returned by /charts"
744 }
745 },
746 {
747 "name": "dimension",
748 "in": "query",
671 - "description": "Zero, one or more dimension ids, as returned by the /chart call.",
749 + "description": "Zero, one or more dimension ids or names, as returned by the /chart call, separated with comma or pipe. Netdata simple patterns are supported.",
750 "required": false,
751 "allowEmptyValue": false,
752 "schema": {
@@ -682,7 +760,7 @@
760 {
761 "name": "after",
762 "in": "query",
685 - "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.",
763 + "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds (negative, relative to parameter: before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). If not specified the default is -600 seconds. Netdata will adapt this parameter to the boundaries of the round robin database unless the allow_past option is specified.",
764 "required": true,
765 "allowEmptyValue": false,
766 "schema": {
@@ -694,7 +772,7 @@
772 {
773 "name": "before",
774 "in": "query",
697 - "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.",
775 + "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). Netdata will adapt this parameter to the boundaries of the round robin database. The default is zero (i.e. the timestamp of the last value collected).",
776 "required": false,
777 "schema": {
778 "type": "number",
@@ -702,10 +780,44 @@
780 "default": 0
781 }
782 },
783 + {
784 + "name": "points",
785 + "in": "query",
786 + "description": "The number of points to be returned. If not given, or it is <= 0, or it is bigger than the points stored in the round robin database for this chart for the given duration, all the available collected values for the given duration will be returned.",
787 + "required": true,
788 + "allowEmptyValue": false,
789 + "schema": {
790 + "type": "number",
791 + "format": "integer",
792 + "default": 20
793 + }
794 + },
795 + {
796 + "name": "chart_label_key",
797 + "in": "query",
798 + "description": "Specify the chart label keys that need to match for context queries as comma separated values. At least one matching key is needed to match the corresponding chart.",
799 + "required": false,
800 + "allowEmptyValue": false,
801 + "schema": {
802 + "type": "string",
803 + "format": "key1,key2,key3"
804 + }
805 + },
806 + {
807 + "name": "chart_labels_filter",
808 + "in": "query",
809 + "description": "Specify the chart label keys and values to match for context queries. All keys/values need to match for the chart to be included in the query. The labels are specified as key1:value1,key2:value2",
810 + "required": false,
811 + "allowEmptyValue": false,
812 + "schema": {
813 + "type": "string",
814 + "format": "key1:value1,key2:value2,key3:value3"
815 + }
816 + },
817 {
818 "name": "group",
819 "in": "query",
708 - "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods are supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).",
820 + "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).",
821 "required": true,
822 "allowEmptyValue": false,
823 "schema": {
@@ -755,222 +867,149 @@
867 }
868 },
869 {
758 - "name": "options",
870 + "name": "group_options",
871 "in": "query",
760 - "description": "Options that affect data generation.",
872 + "description": "When the group function supports additional parameters, this field can be used to pass them to it. Currently only \"countif\" supports this.",
873 "required": false,
762 - "allowEmptyValue": true,
874 + "allowEmptyValue": false,
875 "schema": {
764 - "type": "array",
765 - "items": {
766 - "type": "string",
767 - "enum": [
768 - "abs",
769 - "absolute",
770 - "display-absolute",
771 - "absolute-sum",
772 - "null2zero",
773 - "percentage",
774 - "unaligned",
775 - "anomaly-bit"
776 - ]
777 - },
778 - "default": [
779 - "absolute"
780 - ]
876 + "type": "string"
877 }
878 },
879 {
784 - "name": "label",
880 + "name": "gtime",
881 "in": "query",
786 - "description": "A text to be used as the label.",
882 + "description": "The grouping number of seconds. This is used in conjunction with group=average to change the units of metrics (ie when the data is per-second, setting gtime=60 will turn them to per-minute).",
883 "required": false,
788 - "allowEmptyValue": true,
884 + "allowEmptyValue": false,
885 "schema": {
790 - "type": "string",
791 - "format": "any text"
886 + "type": "number",
887 + "format": "integer",
888 + "default": 0
889 }
890 },
891 {
795 - "name": "units",
892 + "name": "timeout",
893 "in": "query",
797 - "description": "A text to be used as the units.",
894 + "description": "Specify a timeout value in milliseconds after which the agent will abort the query and return a 503 error. A value of 0 indicates no timeout.",
895 "required": false,
799 - "allowEmptyValue": true,
896 + "allowEmptyValue": false,
897 "schema": {
801 - "type": "string",
802 - "format": "any text"
898 + "type": "number",
899 + "format": "integer",
900 + "default": 0
901 }
902 },
903 {
806 - "name": "label_color",
904 + "name": "format",
905 "in": "query",
808 - "description": "A color to be used for the background of the label side(left side) of the badge. One of predefined colors or specific color in hex `RGB` or `RRGGBB` format (without preceding `#` character). If value wrong or not given default color will be used.",
809 - "required": false,
810 - "allowEmptyValue": true,
811 - "schema": {
812 - "oneOf": [
813 - {
814 - "type": "string",
815 - "enum": [
816 - "green",
817 - "brightgreen",
818 - "yellow",
819 - "yellowgreen",
820 - "orange",
821 - "red",
822 - "blue",
823 - "grey",
824 - "gray",
825 - "lightgrey",
826 - "lightgray"
827 - ]
828 - },
829 - {
830 - "type": "string",
831 - "format": "^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
832 - }
833 - ]
834 - }
835 - },
836 - {
837 - "name": "value_color",
838 - "in": "query",
839 - "description": "A color to be used for the background of the value *(right)* part of badge. You can set multiple using a pipe with a condition each, like this: `color<value|color:null` The following operators are supported: >, <, >=, <=, =, :null (to check if no value exists). Each color can be specified in same manner as for `label_color` parameter. Currently only integers are supported as values.",
840 - "required": false,
841 - "allowEmptyValue": true,
906 + "description": "The format of the data to be returned.",
907 + "required": true,
908 + "allowEmptyValue": false,
909 "schema": {
910 "type": "string",
844 - "format": "any text"
845 - }
846 - },
847 - {
848 - "name": "text_color_lbl",
849 - "in": "query",
850 - "description": "Font color for label *(left)* part of the badge. One of predefined colors or as HTML hexadecimal color without preceding `#` character. Formats allowed `RGB` or `RRGGBB`. If no or wrong value given default color will be used.",
851 - "required": false,
852 - "allowEmptyValue": true,
853 - "schema": {
854 - "oneOf": [
855 - {
856 - "type": "string",
857 - "enum": [
858 - "green",
859 - "brightgreen",
860 - "yellow",
861 - "yellowgreen",
862 - "orange",
863 - "red",
864 - "blue",
865 - "grey",
866 - "gray",
867 - "lightgrey",
868 - "lightgray"
869 - ]
870 - },
871 - {
872 - "type": "string",
873 - "format": "^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
874 - }
875 - ]
911 + "enum": [
912 + "json",
913 + "jsonp",
914 + "csv",
915 + "tsv",
916 + "tsv-excel",
917 + "ssv",
918 + "ssvcomma",
919 + "datatable",
920 + "datasource",
921 + "html",
922 + "markdown",
923 + "array",
924 + "csvjsonarray"
925 + ],
926 + "default": "json"
927 }
928 },
929 {
879 - "name": "text_color_val",
930 + "name": "options",
931 "in": "query",
881 - "description": "Font color for value *(right)* part of the badge. One of predefined colors or as HTML hexadecimal color without preceding `#` character. Formats allowed `RGB` or `RRGGBB`. If no or wrong value given default color will be used.",
932 + "description": "Options that affect data generation.",
933 "required": false,
883 - "allowEmptyValue": true,
934 + "allowEmptyValue": false,
935 "schema": {
885 - "oneOf": [
886 - {
887 - "type": "string",
888 - "enum": [
889 - "green",
890 - "brightgreen",
891 - "yellow",
892 - "yellowgreen",
893 - "orange",
894 - "red",
895 - "blue",
896 - "grey",
897 - "gray",
898 - "lightgrey",
899 - "lightgray"
900 - ]
901 - },
902 - {
903 - "type": "string",
904 - "format": "^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
905 - }
936 + "type": "array",
937 + "items": {
938 + "type": "string",
939 + "enum": [
940 + "nonzero",
941 + "flip",
942 + "jsonwrap",
943 + "min2max",
944 + "seconds",
945 + "milliseconds",
946 + "abs",
947 + "absolute",
948 + "absolute-sum",
949 + "null2zero",
950 + "objectrows",
951 + "google_json",
952 + "percentage",
953 + "unaligned",
954 + "match-ids",
955 + "match-names",
956 + "allow_past",
957 + "anomaly-bit"
958 + ]
959 + },
960 + "default": [
961 + "seconds",
962 + "jsonwrap"
963 ]
964 }
965 },
966 {
910 - "name": "multiply",
967 + "name": "callback",
968 "in": "query",
912 - "description": "Multiply the value with this number for rendering it at the image (integer value required).",
969 + "description": "For JSONP responses, the callback function name.",
970 "required": false,
971 "allowEmptyValue": true,
972 "schema": {
916 - "type": "number",
917 - "format": "integer"
973 + "type": "string"
974 }
975 },
976 {
921 - "name": "divide",
977 + "name": "filename",
978 "in": "query",
923 - "description": "Divide the value with this number for rendering it at the image (integer value required).",
979 + "description": "Add Content-Disposition: attachment; filename= header to the response, that will instruct the browser to save the response with the given filename.",
980 "required": false,
981 "allowEmptyValue": true,
982 "schema": {
927 - "type": "number",
928 - "format": "integer"
983 + "type": "string"
984 }
985 },
986 {
932 - "name": "scale",
987 + "name": "tqx",
988 "in": "query",
934 - "description": "Set the scale of the badge (greater or equal to 100).",
989 + "description": "[Google Visualization API](https://developers.google.com/chart/interactive/docs/dev/implementing_data_source?hl=en) formatted parameter.",
990 "required": false,
991 "allowEmptyValue": true,
992 "schema": {
938 - "type": "number",
939 - "format": "integer"
940 - }
941 - },
942 - {
943 - "name": "fixed_width_lbl",
944 - "in": "query",
945 - "description": "This parameter overrides auto-sizing of badge and creates it with fixed width. This parameter determines the size of the label's left side *(label/name)*. You must set this parameter together with `fixed_width_val` otherwise it will be ignored. You should set the label/value widths wide enough to provide space for all the possible values/contents of the badge you're requesting. In case the text cannot fit the space given it will be clipped. The `scale` parameter still applies on the values you give to `fixed_width_lbl` and `fixed_width_val`.",
946 - "required": false,
947 - "allowEmptyValue": false,
948 - "schema": {
949 - "type": "number",
950 - "format": "integer"
951 - }
952 - },
953 - {
954 - "name": "fixed_width_val",
955 - "in": "query",
956 - "description": "This parameter overrides auto-sizing of badge and creates it with fixed width. This parameter determines the size of the label's right side *(value)*. You must set this parameter together with `fixed_width_lbl` otherwise it will be ignored. You should set the label/value widths wide enough to provide space for all the possible values/contents of the badge you're requesting. In case the text cannot fit the space given it will be clipped. The `scale` parameter still applies on the values you give to `fixed_width_lbl` and `fixed_width_val`.",
957 - "required": false,
958 - "allowEmptyValue": false,
959 - "schema": {
960 - "type": "number",
961 - "format": "integer"
993 + "type": "string"
994 }
995 }
996 ],
997 "responses": {
998 "200": {
967 - "description": "The call was successful. The response should be an SVG image."
999 + "description": "The call was successful. The response includes the data in the format requested. Swagger2.0 does not process the discriminator field to show polymorphism. The response will be one of the sub-types of the data-schema according to the chosen format, e.g. json -> data_json.",
1000 + "content": {
1001 + "application/json": {
1002 + "schema": {
1003 + "$ref": "#/components/schemas/data"
1004 + }
1005 + }
1006 + }
1007 },
1008 "400": {
1009 "description": "Bad request - the body will include a message stating what is wrong."
1010 },
1011 "404": {
973 - "description": "No chart with the given id is found."
1012 + "description": "Chart or context is not found. The supplied chart or context will be reported."
1013 },
1014 "500": {
1015 "description": "Internal server error. This usually means the server is out of memory."
@@ -978,348 +1017,340 @@
1017 }
1018 }
1019 },
981 - "/allmetrics": {
1020 + "/api/v1/badge.svg": {
1021 "get": {
983 - "summary": "Get a value of all the metrics maintained by netdata",
984 - "description": "The allmetrics endpoint returns the latest value of all charts and dimensions stored in the netdata server.",
1022 + "summary": "Generate a badge in form of SVG image for a chart (or dimension)",
1023 + "description": "Successful responses are SVG images.",
1024 "parameters": [
1025 {
987 - "name": "format",
1026 + "name": "chart",
1027 "in": "query",
989 - "description": "The format of the response to be returned.",
1028 + "description": "The id of the chart as returned by the /charts call.",
1029 "required": true,
1030 + "allowEmptyValue": false,
1031 "schema": {
1032 "type": "string",
993 - "enum": [
994 - "shell",
995 - "prometheus",
996 - "prometheus_all_hosts",
997 - "json"
998 - ],
999 - "default": "shell"
1033 + "format": "as returned by /charts",
1034 + "default": "system.cpu"
1035 }
1036 },
1037 {
1003 - "name": "filter",
1038 + "name": "alarm",
1039 "in": "query",
1005 - "description": "Allows to filter charts out using simple patterns.",
1040 + "description": "The name of an alarm linked to the chart.",
1041 "required": false,
1042 + "allowEmptyValue": true,
1043 "schema": {
1044 "type": "string",
1045 "format": "any text"
1046 }
1047 },
1048 {
1013 - "name": "variables",
1049 + "name": "dimension",
1050 "in": "query",
1015 - "description": "When enabled, netdata will expose various system configuration metrics.",
1051 + "description": "Zero, one or more dimension ids, as returned by the /chart call.",
1052 "required": false,
1053 + "allowEmptyValue": false,
1054 "schema": {
1018 - "type": "string",
1019 - "enum": [
1020 - "yes",
1021 - "no"
1022 - ],
1023 - "default": "no"
1055 + "type": "array",
1056 + "items": {
1057 + "type": "string",
1058 + "format": "as returned by /charts"
1059 + }
1060 }
1061 },
1062 {
1027 - "name": "help",
1063 + "name": "after",
1064 "in": "query",
1029 - "description": "Enable or disable HELP lines in prometheus output.",
1030 - "required": false,
1065 + "description": "This parameter can either be an absolute timestamp specifying the starting point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.",
1066 + "required": true,
1067 + "allowEmptyValue": false,
1068 "schema": {
1032 - "type": "string",
1033 - "enum": [
1034 - "yes",
1035 - "no"
1036 - ],
1037 - "default": "no"
1069 + "type": "number",
1070 + "format": "integer",
1071 + "default": -600
1072 }
1073 },
1074 {
1041 - "name": "types",
1075 + "name": "before",
1076 "in": "query",
1043 - "description": "Enable or disable TYPE lines in prometheus output.",
1077 + "description": "This parameter can either be an absolute timestamp specifying the ending point of the data to be returned, or a relative number of seconds, to the last collected timestamp. Netdata will assume it is a relative number if it is smaller than the duration of the round robin database for this chart. So, if the round robin database is 3600 seconds, any value from -3600 to 3600 will trigger relative arithmetics. Netdata will adapt this parameter to the boundaries of the round robin database.",
1078 "required": false,
1079 "schema": {
1046 - "type": "string",
1047 - "enum": [
1048 - "yes",
1049 - "no"
1050 - ],
1051 - "default": "no"
1080 + "type": "number",
1081 + "format": "integer",
1082 + "default": 0
1083 }
1084 },
1085 {
1055 - "name": "timestamps",
1086 + "name": "group",
1087 "in": "query",
1057 - "description": "Enable or disable timestamps in prometheus output.",
1058 - "required": false,
1088 + "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods are supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).",
1089 + "required": true,
1090 + "allowEmptyValue": false,
1091 "schema": {
1092 "type": "string",
1093 "enum": [
1062 - "yes",
1063 - "no"
1094 + "min",
1095 + "max",
1096 + "average",
1097 + "median",
1098 + "stddev",
1099 + "sum",
1100 + "incremental-sum",
1101 + "ses",
1102 + "des",
1103 + "cv",
1104 + "countif",
1105 + "percentile",
1106 + "percentile25",
1107 + "percentile50",
1108 + "percentile75",
1109 + "percentile80",
1110 + "percentile90",
1111 + "percentile95",
1112 + "percentile97",
1113 + "percentile98",
1114 + "percentile99",
1115 + "trimmed-mean",
1116 + "trimmed-mean1",
1117 + "trimmed-mean2",
1118 + "trimmed-mean3",
1119 + "trimmed-mean5",
1120 + "trimmed-mean10",
1121 + "trimmed-mean15",
1122 + "trimmed-mean20",
1123 + "trimmed-mean25",
1124 + "trimmed-median",
1125 + "trimmed-median1",
1126 + "trimmed-median2",
1127 + "trimmed-median3",
1128 + "trimmed-median5",
1129 + "trimmed-median10",
1130 + "trimmed-median15",
1131 + "trimmed-median20",
1132 + "trimmed-median25"
1133 ],
1065 - "default": "yes"
1134 + "default": "average"
1135 }
1136 },
1137 {
1069 - "name": "names",
1138 + "name": "options",
1139 "in": "query",
1071 - "description": "When enabled netdata will report dimension names. When disabled netdata will report dimension IDs. The default is controlled in netdata.conf.",
1140 + "description": "Options that affect data generation.",
1141 "required": false,
1142 + "allowEmptyValue": true,
1143 "schema": {
1074 - "type": "string",
1075 - "enum": [
1076 - "yes",
1077 - "no"
1078 - ],
1079 - "default": "yes"
1144 + "type": "array",
1145 + "items": {
1146 + "type": "string",
1147 + "enum": [
1148 + "abs",
1149 + "absolute",
1150 + "display-absolute",
1151 + "absolute-sum",
1152 + "null2zero",
1153 + "percentage",
1154 + "unaligned",
1155 + "anomaly-bit"
1156 + ]
1157 + },
1158 + "default": [
1159 + "absolute"
1160 + ]
1161 }
1162 },
1163 {
1083 - "name": "oldunits",
1164 + "name": "label",
1165 "in": "query",
1085 - "description": "When enabled, netdata will show metric names for the default source=average as they appeared before 1.12, by using the legacy unit naming conventions.",
1166 + "description": "A text to be used as the label.",
1167 "required": false,
1168 + "allowEmptyValue": true,
1169 "schema": {
1170 "type": "string",
1089 - "enum": [
1090 - "yes",
1091 - "no"
1092 - ],
1093 - "default": "yes"
1171 + "format": "any text"
1172 }
1173 },
1174 {
1097 - "name": "hideunits",
1175 + "name": "units",
1176 "in": "query",
1099 - "description": "When enabled, netdata will not include the units in the metric names, for the default source=average.",
1177 + "description": "A text to be used as the units.",
1178 "required": false,
1179 + "allowEmptyValue": true,
1180 "schema": {
1181 "type": "string",
1103 - "enum": [
1104 - "yes",
1105 - "no"
1106 - ],
1107 - "default": "yes"
1182 + "format": "any text"
1183 }
1184 },
1185 {
1111 - "name": "server",
1186 + "name": "label_color",
1187 "in": "query",
1113 - "description": "Set a distinct name of the client querying prometheus metrics. Netdata will use the client IP if this is not set.",
1188 + "description": "A color to be used for the background of the label side(left side) of the badge. One of predefined colors or specific color in hex `RGB` or `RRGGBB` format (without preceding `#` character). If value wrong or not given default color will be used.",
1189 "required": false,
1190 + "allowEmptyValue": true,
1191 "schema": {
1116 - "type": "string",
1117 - "format": "any text"
1192 + "oneOf": [
1193 + {
1194 + "type": "string",
1195 + "enum": [
1196 + "green",
1197 + "brightgreen",
1198 + "yellow",
1199 + "yellowgreen",
1200 + "orange",
1201 + "red",
1202 + "blue",
1203 + "grey",
1204 + "gray",
1205 + "lightgrey",
1206 + "lightgray"
1207 + ]
1208 + },
1209 + {
1210 + "type": "string",
1211 + "format": "^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
1212 + }
1213 + ]
1214 }
1215 },
1216 {
1121 - "name": "prefix",
1217 + "name": "value_color",
1218 "in": "query",
1123 - "description": "Prefix all prometheus metrics with this string.",
1219 + "description": "A color to be used for the background of the value *(right)* part of badge. You can set multiple using a pipe with a condition each, like this: `color<value|color:null` The following operators are supported: >, <, >=, <=, =, :null (to check if no value exists). Each color can be specified in same manner as for `label_color` parameter. Currently only integers are supported as values.",
1220 "required": false,
1221 + "allowEmptyValue": true,
1222 "schema": {
1223 "type": "string",
1224 "format": "any text"
1225 }
1226 },
1227 {
1131 - "name": "data",
1228 + "name": "text_color_lbl",
1229 "in": "query",
1133 - "description": "Select the prometheus response data source. There is a setting in netdata.conf for the default.",
1230 + "description": "Font color for label *(left)* part of the badge. One of predefined colors or as HTML hexadecimal color without preceding `#` character. Formats allowed `RGB` or `RRGGBB`. If no or wrong value given default color will be used.",
1231 "required": false,
1232 + "allowEmptyValue": true,
1233 "schema": {
1136 - "type": "string",
1137 - "enum": [
1138 - "as-collected",
1139 - "average",
1140 - "sum"
1141 - ],
1142 - "default": "average"
1234 + "oneOf": [
1235 + {
1236 + "type": "string",
1237 + "enum": [
1238 + "green",
1239 + "brightgreen",
1240 + "yellow",
1241 + "yellowgreen",
1242 + "orange",
1243 + "red",
1244 + "blue",
1245 + "grey",
1246 + "gray",
1247 + "lightgrey",
1248 + "lightgray"
1249 + ]
1250 + },
1251 + {
1252 + "type": "string",
1253 + "format": "^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
1254 + }
1255 + ]
1256 }
1144 - }
1145 - ],
1146 - "responses": {
1147 - "200": {
1148 - "description": "All the metrics returned in the format requested."
1257 },
1150 - "400": {
1151 - "description": "The format requested is not supported."
1152 - }
1153 - }
1154 - }
1155 - },
1156 - "/alarms": {
1157 - "get": {
1158 - "summary": "Get a list of active or raised alarms on the server",
1159 - "description": "The alarms endpoint returns the list of all raised or enabled alarms on the netdata server. Called without any parameters, the raised alarms in state WARNING or CRITICAL are returned. By passing \"?all\", all the enabled alarms are returned.",
1160 - "parameters": [
1258 {
1162 - "name": "all",
1259 + "name": "text_color_val",
1260 "in": "query",
1164 - "description": "If passed, all enabled alarms are returned.",
1261 + "description": "Font color for value *(right)* part of the badge. One of predefined colors or as HTML hexadecimal color without preceding `#` character. Formats allowed `RGB` or `RRGGBB`. If no or wrong value given default color will be used.",
1262 "required": false,
1263 "allowEmptyValue": true,
1264 "schema": {
1168 - "type": "boolean"
1265 + "oneOf": [
1266 + {
1267 + "type": "string",
1268 + "enum": [
1269 + "green",
1270 + "brightgreen",
1271 + "yellow",
1272 + "yellowgreen",
1273 + "orange",
1274 + "red",
1275 + "blue",
1276 + "grey",
1277 + "gray",
1278 + "lightgrey",
1279 + "lightgray"
1280 + ]
1281 + },
1282 + {
1283 + "type": "string",
1284 + "format": "^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
1285 + }
1286 + ]
1287 }
1288 },
1289 {
1172 - "name": "active",
1290 + "name": "multiply",
1291 "in": "query",
1174 - "description": "If passed, the raised alarms in state WARNING or CRITICAL are returned.",
1292 + "description": "Multiply the value with this number for rendering it at the image (integer value required).",
1293 "required": false,
1294 "allowEmptyValue": true,
1295 "schema": {
1178 - "type": "boolean"
1179 - }
1180 - }
1181 - ],
1182 - "responses": {
1183 - "200": {
1184 - "description": "An object containing general info and a linked list of alarms.",
1185 - "content": {
1186 - "application/json": {
1187 - "schema": {
1188 - "$ref": "#/components/schemas/alarms"
1189 - }
1190 - }
1296 + "type": "number",
1297 + "format": "integer"
1298 }
1192 - }
1193 - }
1194 - }
1195 - },
1196 - "/alarms_values": {
1197 - "get": {
1198 - "summary": "Get a list of active or raised alarms on the server",
1199 - "description": "The alarms_values endpoint returns the list of all raised or enabled alarms on the netdata server. Called without any parameters, the raised alarms in state WARNING or CRITICAL are returned. By passing '?all', all the enabled alarms are returned. This option output differs from `/alarms` in the number of variables delivered. This endpoint gives to user `id`, `value`, `last_updated` time, and alarm `status`.",
1200 - "parameters": [
1299 + },
1300 {
1202 - "name": "all",
1301 + "name": "divide",
1302 "in": "query",
1204 - "description": "If passed, all enabled alarms are returned.",
1303 + "description": "Divide the value with this number for rendering it at the image (integer value required).",
1304 "required": false,
1305 "allowEmptyValue": true,
1306 "schema": {
1208 - "type": "boolean"
1307 + "type": "number",
1308 + "format": "integer"
1309 }
1310 },
1311 {
1212 - "name": "active",
1312 + "name": "scale",
1313 "in": "query",
1214 - "description": "If passed, the raised alarms in state WARNING or CRITICAL are returned.",
1314 + "description": "Set the scale of the badge (greater or equal to 100).",
1315 "required": false,
1316 "allowEmptyValue": true,
1317 "schema": {
1218 - "type": "boolean"
1318 + "type": "number",
1319 + "format": "integer"
1320 }
1220 - }
1221 - ],
1222 - "responses": {
1223 - "200": {
1224 - "description": "An object containing general info and a linked list of alarms.",
1225 - "content": {
1226 - "application/json": {
1227 - "schema": {
1228 - "$ref": "#/components/schemas/alarms_values"
1229 - }
1230 - }
1231 - }
1232 - }
1233 - }
1234 - }
1235 - },
1236 - "/alarm_log": {
1237 - "get": {
1238 - "summary": "Retrieves the entries of the alarm log",
1239 - "description": "Returns an array of alarm_log entries, with historical information on raised and cleared alarms.",
1240 - "parameters": [
1241 - {
1242 - "name": "after",
1243 - "in": "query",
1244 - "description": "Passing the parameter after=UNIQUEID returns all the events in the alarm log that occurred after UNIQUEID. An automated series of calls would call the interface once without after=, store the last UNIQUEID of the returned set, and give it back to get incrementally the next events.",
1245 - "required": false,
1246 - "schema": {
1247 - "type": "integer"
1248 - }
1249 - }
1250 - ],
1251 - "responses": {
1252 - "200": {
1253 - "description": "An array of alarm log entries.",
1254 - "content": {
1255 - "application/json": {
1256 - "schema": {
1257 - "type": "array",
1258 - "items": {
1259 - "$ref": "#/components/schemas/alarm_log_entry"
1260 - }
1261 - }
1262 - }
1263 - }
1264 - }
1265 - }
1266 - }
1267 - },
1268 - "/alarm_count": {
1269 - "get": {
1270 - "summary": "Get an overall status of the chart",
1271 - "description": "Checks multiple charts with the same context and counts number of alarms with given status.",
1272 - "parameters": [
1321 + },
1322 {
1323 + "name": "fixed_width_lbl",
1324 "in": "query",
1275 - "name": "context",
1276 - "description": "Specify context which should be checked.",
1325 + "description": "This parameter overrides auto-sizing of badge and creates it with fixed width. This parameter determines the size of the label's left side *(label/name)*. You must set this parameter together with `fixed_width_val` otherwise it will be ignored. You should set the label/value widths wide enough to provide space for all the possible values/contents of the badge you're requesting. In case the text cannot fit the space given it will be clipped. The `scale` parameter still applies on the values you give to `fixed_width_lbl` and `fixed_width_val`.",
1326 "required": false,
1278 - "allowEmptyValue": true,
1327 + "allowEmptyValue": false,
1328 "schema": {
1280 - "type": "array",
1281 - "items": {
1282 - "type": "string"
1283 - },
1284 - "default": [
1285 - "system.cpu"
1286 - ]
1329 + "type": "number",
1330 + "format": "integer"
1331 }
1332 },
1333 {
1334 + "name": "fixed_width_val",
1335 "in": "query",
1291 - "name": "status",
1292 - "description": "Specify alarm status to count.",
1336 + "description": "This parameter overrides auto-sizing of badge and creates it with fixed width. This parameter determines the size of the label's right side *(value)*. You must set this parameter together with `fixed_width_lbl` otherwise it will be ignored. You should set the label/value widths wide enough to provide space for all the possible values/contents of the badge you're requesting. In case the text cannot fit the space given it will be clipped. The `scale` parameter still applies on the values you give to `fixed_width_lbl` and `fixed_width_val`.",
1337 "required": false,
1294 - "allowEmptyValue": true,
1338 + "allowEmptyValue": false,
1339 "schema": {
1296 - "type": "string",
1297 - "enum": [
1298 - "REMOVED",
1299 - "UNDEFINED",
1300 - "UNINITIALIZED",
1301 - "CLEAR",
1302 - "RAISED",
1303 - "WARNING",
1304 - "CRITICAL"
1305 - ],
1306 - "default": "RAISED"
1340 + "type": "number",
1341 + "format": "integer"
1342 }
1343 }
1344 ],
1345 "responses": {
1346 "200": {
1312 - "description": "An object containing a count of alarms with given status for given contexts.",
1313 - "content": {
1314 - "application/json": {
1315 - "schema": {
1316 - "type": "array",
1317 - "items": {
1318 - "type": "number"
1319 - }
1320 - }
1321 - }
1322 - }
1347 + "description": "The call was successful. The response should be an SVG image."
1348 + },
1349 + "400": {
1350 + "description": "Bad request - the body will include a message stating what is wrong."
1351 + },
1352 + "404": {
1353 + "description": "No chart with the given id is found."
1354 },
1355 "500": {
1356 "description": "Internal server error. This usually means the server is out of memory."
@@ -1327,390 +1358,467 @@
1358 }
1359 }
1360 },
1330 - "/manage/health": {
1361 + "/api/v1/allmetrics": {
1362 "get": {
1332 - "summary": "Accesses the health management API to control health checks and notifications at runtime.",
1333 - "description": "Available from Netdata v1.12 and above, protected via bearer authorization. Especially useful for maintenance periods, the API allows you to disable health checks completely, silence alarm notifications, or Disable/Silence specific alarms that match selectors on alarm/template name, chart, context, host and family. For the simple disable/silence all scenarios, only the cmd parameter is required. The other parameters are used to define alarm selectors. For more information and examples, refer to the netdata documentation.",
1363 + "summary": "Get a value of all the metrics maintained by netdata",
1364 + "description": "The allmetrics endpoint returns the latest value of all charts and dimensions stored in the netdata server.",
1365 "parameters": [
1366 {
1336 - "name": "cmd",
1367 + "name": "format",
1368 "in": "query",
1338 - "description": "DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors. LIST: Show active configuration.",
1339 - "required": false,
1369 + "description": "The format of the response to be returned.",
1370 + "required": true,
1371 "schema": {
1372 "type": "string",
1373 "enum": [
1343 - "DISABLE ALL",
1344 - "SILENCE ALL",
1345 - "DISABLE",
1346 - "SILENCE",
1347 - "RESET",
1348 - "LIST"
1349 - ]
1374 + "shell",
1375 + "prometheus",
1376 + "prometheus_all_hosts",
1377 + "json"
1378 + ],
1379 + "default": "shell"
1380 }
1381 },
1382 {
1353 - "name": "alarm",
1383 + "name": "filter",
1384 "in": "query",
1355 - "description": "The expression provided will match both `alarm` and `template` names.",
1385 + "description": "Allows to filter charts out using simple patterns.",
1386 + "required": false,
1387 "schema": {
1357 - "type": "string"
1388 + "type": "string",
1389 + "format": "any text"
1390 }
1391 },
1392 {
1361 - "name": "chart",
1393 + "name": "variables",
1394 "in": "query",
1363 - "description": "Chart ids/names, as shown on the dashboard. These will match the `on` entry of a configured `alarm`.",
1395 + "description": "When enabled, netdata will expose various system configuration metrics.",
1396 + "required": false,
1397 "schema": {
1365 - "type": "string"
1398 + "type": "string",
1399 + "enum": [
1400 + "yes",
1401 + "no"
1402 + ],
1403 + "default": "no"
1404 }
1405 },
1406 {
1369 - "name": "context",
1407 + "name": "help",
1408 "in": "query",
1371 - "description": "Chart context, as shown on the dashboard. These will match the `on` entry of a configured `template`.",
1409 + "description": "Enable or disable HELP lines in prometheus output.",
1410 + "required": false,
1411 "schema": {
1373 - "type": "string"
1412 + "type": "string",
1413 + "enum": [
1414 + "yes",
1415 + "no"
1416 + ],
1417 + "default": "no"
1418 }
1419 },
1420 {
1377 - "name": "hosts",
1421 + "name": "types",
1422 "in": "query",
1379 - "description": "The hostnames that will need to match.",
1423 + "description": "Enable or disable TYPE lines in prometheus output.",
1424 + "required": false,
1425 "schema": {
1381 - "type": "string"
1426 + "type": "string",
1427 + "enum": [
1428 + "yes",
1429 + "no"
1430 + ],
1431 + "default": "no"
1432 }
1433 },
1434 {
1385 - "name": "families",
1435 + "name": "timestamps",
1436 "in": "query",
1387 - "description": "The alarm families.",
1437 + "description": "Enable or disable timestamps in prometheus output.",
1438 + "required": false,
1439 "schema": {
1389 - "type": "string"
1440 + "type": "string",
1441 + "enum": [
1442 + "yes",
1443 + "no"
1444 + ],
1445 + "default": "yes"
1446 }
1391 - }
1392 - ],
1393 - "responses": {
1394 - "200": {
1395 - "description": "A plain text response based on the result of the command."
1447 },
1397 - "403": {
1398 - "description": "Bearer authentication error."
1399 - }
1400 - }
1401 - }
1402 - },
1403 - "/aclk": {
1404 - "get": {
1405 - "summary": "Get information about current ACLK state",
1406 - "description": "ACLK endpoint returns detailed information about current state of ACLK (Agent to Cloud communication).",
1407 - "responses": {
1408 - "200": {
1409 - "description": "JSON object with ACLK information.",
1410 - "content": {
1411 - "application/json": {
1412 - "schema": {
1413 - "$ref": "#/components/schemas/aclk_state"
1414 - }
1415 - }
1416 - }
1417 - }
1418 - }
1419 - }
1420 - },
1421 - "/metric_correlations": {
1422 - "get": {
1423 - "summary": "Analyze all the metrics to find their correlations",
1424 - "description": "THIS ENDPOINT IS OBSOLETE. Use the /weights endpoint. Given two time-windows (baseline, highlight), it goes through all the available metrics, querying both windows and tries to find how these two windows relate to each other. It supports multiple algorithms to do so. The result is a list of all metrics evaluated, weighted for 0.0 (the two windows are more different) to 1.0 (the two windows are similar). The algorithm adjusts automatically the baseline window to be a power of two multiple of the highlighted (1, 2, 4, 8, etc).",
1425 - "parameters": [
1448 {
1427 - "name": "baseline_after",
1449 + "name": "names",
1450 "in": "query",
1429 - "description": "This parameter can either be an absolute timestamp specifying the starting point of baseline window, or a relative number of seconds (negative, relative to parameter baseline_before). Netdata will assume it is a relative number if it is less that 3 years (in seconds).",
1451 + "description": "When enabled netdata will report dimension names. When disabled netdata will report dimension IDs. The default is controlled in netdata.conf.",
1452 "required": false,
1431 - "allowEmptyValue": false,
1453 "schema": {
1433 - "type": "number",
1434 - "format": "integer",
1435 - "default": -300
1454 + "type": "string",
1455 + "enum": [
1456 + "yes",
1457 + "no"
1458 + ],
1459 + "default": "yes"
1460 }
1461 },
1462 {
1439 - "name": "baseline_before",
1463 + "name": "oldunits",
1464 "in": "query",
1441 - "description": "This parameter can either be an absolute timestamp specifying the ending point of the baseline window, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds).",
1465 + "description": "When enabled, netdata will show metric names for the default source=average as they appeared before 1.12, by using the legacy unit naming conventions.",
1466 "required": false,
1467 "schema": {
1444 - "type": "number",
1445 - "format": "integer",
1446 - "default": -60
1468 + "type": "string",
1469 + "enum": [
1470 + "yes",
1471 + "no"
1472 + ],
1473 + "default": "yes"
1474 }
1475 },
1476 {
1450 - "name": "after",
1477 + "name": "hideunits",
1478 "in": "query",
1452 - "description": "This parameter can either be an absolute timestamp specifying the starting point of highlighted window, or a relative number of seconds (negative, relative to parameter highlight_before). Netdata will assume it is a relative number if it is less that 3 years (in seconds).",
1479 + "description": "When enabled, netdata will not include the units in the metric names, for the default source=average.",
1480 "required": false,
1454 - "allowEmptyValue": false,
1481 "schema": {
1456 - "type": "number",
1457 - "format": "integer",
1458 - "default": -60
1482 + "type": "string",
1483 + "enum": [
1484 + "yes",
1485 + "no"
1486 + ],
1487 + "default": "yes"
1488 }
1489 },
1490 {
1462 - "name": "before",
1491 + "name": "server",
1492 "in": "query",
1464 - "description": "This parameter can either be an absolute timestamp specifying the ending point of the highlighted window, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds).",
1493 + "description": "Set a distinct name of the client querying prometheus metrics. Netdata will use the client IP if this is not set.",
1494 "required": false,
1495 "schema": {
1467 - "type": "number",
1468 - "format": "integer",
1469 - "default": 0
1496 + "type": "string",
1497 + "format": "any text"
1498 }
1499 },
1500 {
1473 - "name": "points",
1501 + "name": "prefix",
1502 "in": "query",
1475 - "description": "The number of points to be evaluated for the highlighted window. The baseline window will be adjusted automatically to receive a proportional amount of points.",
1503 + "description": "Prefix all prometheus metrics with this string.",
1504 "required": false,
1477 - "allowEmptyValue": false,
1505 "schema": {
1479 - "type": "number",
1480 - "format": "integer",
1481 - "default": 500
1506 + "type": "string",
1507 + "format": "any text"
1508 }
1509 },
1510 {
1485 - "name": "method",
1511 + "name": "data",
1512 "in": "query",
1487 - "description": "the algorithm to run",
1513 + "description": "Select the prometheus response data source. There is a setting in netdata.conf for the default.",
1514 "required": false,
1515 "schema": {
1516 "type": "string",
1517 "enum": [
1492 - "ks2",
1493 - "volume"
1518 + "as-collected",
1519 + "average",
1520 + "sum"
1521 ],
1495 - "default": "ks2"
1496 - }
1497 - },
1498 - {
1499 - "name": "timeout",
1500 - "in": "query",
1501 - "description": "Cancel the query if to takes more that this amount of milliseconds.",
1502 - "required": false,
1503 - "allowEmptyValue": false,
1504 - "schema": {
1505 - "type": "number",
1506 - "format": "integer",
1507 - "default": 60000
1522 + "default": "average"
1523 }
1524 + }
1525 + ],
1526 + "responses": {
1527 + "200": {
1528 + "description": "All the metrics returned in the format requested."
1529 },
1530 + "400": {
1531 + "description": "The format requested is not supported."
1532 + }
1533 + }
1534 + }
1535 + },
1536 + "/api/v1/alarms": {
1537 + "get": {
1538 + "summary": "Get a list of active or raised alarms on the server",
1539 + "description": "The alarms endpoint returns the list of all raised or enabled alarms on the netdata server. Called without any parameters, the raised alarms in state WARNING or CRITICAL are returned. By passing \"?all\", all the enabled alarms are returned.",
1540 + "parameters": [
1541 {
1511 - "name": "options",
1542 + "name": "all",
1543 "in": "query",
1513 - "description": "Options that affect data generation.",
1544 + "description": "If passed, all enabled alarms are returned.",
1545 "required": false,
1515 - "allowEmptyValue": false,
1516 - "schema": {
1517 - "type": "array",
1518 - "items": {
1519 - "type": "string",
1520 - "enum": [
1521 - "min2max",
1522 - "abs",
1523 - "absolute",
1524 - "absolute-sum",
1525 - "null2zero",
1526 - "percentage",
1527 - "unaligned",
1528 - "allow_past",
1529 - "nonzero",
1530 - "anomaly-bit",
1531 - "raw"
1532 - ]
1533 - },
1534 - "default": [
1535 - "null2zero",
1536 - "allow_past",
1537 - "nonzero",
1538 - "unaligned"
1539 - ]
1540 - }
1541 - },
1542 - {
1543 - "name": "group",
1544 - "in": "query",
1545 - "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).",
1546 - "required": true,
1547 - "allowEmptyValue": false,
1546 + "allowEmptyValue": true,
1547 "schema": {
1549 - "type": "string",
1550 - "enum": [
1551 - "min",
1552 - "max",
1553 - "average",
1554 - "median",
1555 - "stddev",
1556 - "sum",
1557 - "incremental-sum",
1558 - "ses",
1559 - "des",
1560 - "cv",
1561 - "countif",
1562 - "percentile",
1563 - "percentile25",
1564 - "percentile50",
1565 - "percentile75",
1566 - "percentile80",
1567 - "percentile90",
1568 - "percentile95",
1569 - "percentile97",
1570 - "percentile98",
1571 - "percentile99",
1572 - "trimmed-mean",
1573 - "trimmed-mean1",
1574 - "trimmed-mean2",
1575 - "trimmed-mean3",
1576 - "trimmed-mean5",
1577 - "trimmed-mean10",
1578 - "trimmed-mean15",
1579 - "trimmed-mean20",
1580 - "trimmed-mean25",
1581 - "trimmed-median",
1582 - "trimmed-median1",
1583 - "trimmed-median2",
1584 - "trimmed-median3",
1585 - "trimmed-median5",
1586 - "trimmed-median10",
1587 - "trimmed-median15",
1588 - "trimmed-median20",
1589 - "trimmed-median25"
1590 - ],
1591 - "default": "average"
1548 + "type": "boolean"
1549 }
1550 },
1551 {
1595 - "name": "group_options",
1552 + "name": "active",
1553 "in": "query",
1597 - "description": "When the group function supports additional parameters, this field can be used to pass them to it. Currently only \"countif\" supports this.",
1554 + "description": "If passed, the raised alarms in state WARNING or CRITICAL are returned.",
1555 "required": false,
1599 - "allowEmptyValue": false,
1556 + "allowEmptyValue": true,
1557 "schema": {
1601 - "type": "string"
1558 + "type": "boolean"
1559 }
1560 }
1561 ],
1562 "responses": {
1563 "200": {
1607 - "description": "JSON object with weights for each chart and dimension.",
1564 + "description": "An object containing general info and a linked list of alarms.",
1565 "content": {
1566 "application/json": {
1567 "schema": {
1611 - "$ref": "#/components/schemas/metric_correlations"
1568 + "$ref": "#/components/schemas/alarms"
1569 }
1570 }
1571 }
1615 - },
1616 - "400": {
1617 - "description": "The given parameters are invalid."
1618 - },
1619 - "403": {
1620 - "description": "metrics correlations are not enabled on this Netdata Agent."
1621 - },
1622 - "404": {
1623 - "description": "No charts could be found, or the method that correlated the metrics did not produce any result."
1624 - },
1625 - "504": {
1626 - "description": "Timeout - the query took too long and has been cancelled."
1572 }
1573 }
1574 }
1575 },
1631 - "/function": {
1576 + "/api/v1/alarms_values": {
1577 "get": {
1633 - "summary": "Execute a collector function.",
1578 + "summary": "Get a list of active or raised alarms on the server",
1579 + "description": "The alarms_values endpoint returns the list of all raised or enabled alarms on the netdata server. Called without any parameters, the raised alarms in state WARNING or CRITICAL are returned. By passing '?all', all the enabled alarms are returned. This option output differs from `/alarms` in the number of variables delivered. This endpoint gives to user `id`, `value`, `last_updated` time, and alarm `status`.",
1580 "parameters": [
1581 {
1636 - "name": "function",
1582 + "name": "all",
1583 "in": "query",
1638 - "description": "The name of the function, as returned by the collector.",
1639 - "required": true,
1640 - "allowEmptyValue": false,
1584 + "description": "If passed, all enabled alarms are returned.",
1585 + "required": false,
1586 + "allowEmptyValue": true,
1587 "schema": {
1642 - "type": "string"
1588 + "type": "boolean"
1589 }
1590 },
1591 {
1646 - "name": "timeout",
1592 + "name": "active",
1593 "in": "query",
1648 - "description": "The timeout in seconds to wait for the function to complete.",
1594 + "description": "If passed, the raised alarms in state WARNING or CRITICAL are returned.",
1595 "required": false,
1596 + "allowEmptyValue": true,
1597 "schema": {
1651 - "type": "number",
1652 - "format": "integer",
1653 - "default": 10
1598 + "type": "boolean"
1599 }
1600 }
1601 ],
1602 "responses": {
1603 "200": {
1659 - "description": "The collector function has been executed successfully. Each collector may return a different type of content."
1660 - },
1661 - "400": {
1662 - "description": "The request was rejected by the collector."
1663 - },
1664 - "404": {
1665 - "description": "The requested function is not found."
1666 - },
1667 - "500": {
1668 - "description": "Other internal error, getting this error means there is a bug in Netdata."
1669 - },
1670 - "503": {
1671 - "description": "The collector to execute the function is not currently available."
1672 - },
1673 - "504": {
1674 - "description": "Timeout while waiting for the collector to execute the function."
1675 - },
1676 - "591": {
1677 - "description": "The collector sent a response, but it was invalid or corrupted."
1604 + "description": "An object containing general info and a linked list of alarms.",
1605 + "content": {
1606 + "application/json": {
1607 + "schema": {
1608 + "$ref": "#/components/schemas/alarms_values"
1609 + }
1610 + }
1611 + }
1612 }
1613 }
1614 }
1615 },
1682 - "/functions": {
1616 + "/api/v1/alarm_log": {
1617 "get": {
1684 - "summary": "Get a list of all registered collector functions.",
1685 - "description": "Collector functions are programs that can be executed on demand.",
1618 + "summary": "Retrieves the entries of the alarm log",
1619 + "description": "Returns an array of alarm_log entries, with historical information on raised and cleared alarms.",
1620 + "parameters": [
1621 + {
1622 + "name": "after",
1623 + "in": "query",
1624 + "description": "Passing the parameter after=UNIQUEID returns all the events in the alarm log that occurred after UNIQUEID. An automated series of calls would call the interface once without after=, store the last UNIQUEID of the returned set, and give it back to get incrementally the next events.",
1625 + "required": false,
1626 + "schema": {
1627 + "type": "integer"
1628 + }
1629 + }
1630 + ],
1631 "responses": {
1632 "200": {
1688 - "description": "A JSON object containing one object per supported function."
1633 + "description": "An array of alarm log entries.",
1634 + "content": {
1635 + "application/json": {
1636 + "schema": {
1637 + "type": "array",
1638 + "items": {
1639 + "$ref": "#/components/schemas/alarm_log_entry"
1640 + }
1641 + }
1642 + }
1643 + }
1644 }
1645 }
1646 }
1647 },
1693 - "/weights": {
1648 + "/api/v1/alarm_count": {
1649 "get": {
1695 - "summary": "Analyze all the metrics using an algorithm and score them accordingly",
1696 - "description": "This endpoint goes through all metrics and scores them according to an algorithm.",
1650 + "summary": "Get an overall status of the chart",
1651 + "description": "Checks multiple charts with the same context and counts number of alarms with given status.",
1652 "parameters": [
1653 {
1699 - "name": "baseline_after",
1654 "in": "query",
1701 - "description": "This parameter can either be an absolute timestamp specifying the starting point of baseline window, or a relative number of seconds (negative, relative to parameter baseline_before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). This parameter is used in KS2 and VOLUME algorithms.",
1655 + "name": "context",
1656 + "description": "Specify context which should be checked.",
1657 "required": false,
1703 - "allowEmptyValue": false,
1658 + "allowEmptyValue": true,
1659 "schema": {
1705 - "type": "number",
1706 - "format": "integer",
1707 - "default": -300
1660 + "type": "array",
1661 + "items": {
1662 + "type": "string"
1663 + },
1664 + "default": [
1665 + "system.cpu"
1666 + ]
1667 }
1668 },
1669 {
1711 - "name": "baseline_before",
1670 "in": "query",
1713 - "description": "This parameter can either be an absolute timestamp specifying the ending point of the baseline window, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). This parameter is used in KS2 and VOLUME algorithms.",
1671 + "name": "status",
1672 + "description": "Specify alarm status to count.",
1673 + "required": false,
1674 + "allowEmptyValue": true,
1675 + "schema": {
1676 + "type": "string",
1677 + "enum": [
1678 + "REMOVED",
1679 + "UNDEFINED",
1680 + "UNINITIALIZED",
1681 + "CLEAR",
1682 + "RAISED",
1683 + "WARNING",
1684 + "CRITICAL"
1685 + ],
1686 + "default": "RAISED"
1687 + }
1688 + }
1689 + ],
1690 + "responses": {
1691 + "200": {
1692 + "description": "An object containing a count of alarms with given status for given contexts.",
1693 + "content": {
1694 + "application/json": {
1695 + "schema": {
1696 + "type": "array",
1697 + "items": {
1698 + "type": "number"
1699 + }
1700 + }
1701 + }
1702 + }
1703 + },
1704 + "500": {
1705 + "description": "Internal server error. This usually means the server is out of memory."
1706 + }
1707 + }
1708 + }
1709 + },
1710 + "/api/v1/manage/health": {
1711 + "get": {
1712 + "summary": "Accesses the health management API to control health checks and notifications at runtime.",
1713 + "description": "Available from Netdata v1.12 and above, protected via bearer authorization. Especially useful for maintenance periods, the API allows you to disable health checks completely, silence alarm notifications, or Disable/Silence specific alarms that match selectors on alarm/template name, chart, context, host and family. For the simple disable/silence all scenarios, only the cmd parameter is required. The other parameters are used to define alarm selectors. For more information and examples, refer to the netdata documentation.",
1714 + "parameters": [
1715 + {
1716 + "name": "cmd",
1717 + "in": "query",
1718 + "description": "DISABLE ALL: No alarm criteria are evaluated, nothing is written in the alarm log. SILENCE ALL: No notifications are sent. RESET: Return to the default state. DISABLE/SILENCE: Set the mode to be used for the alarms matching the criteria of the alarm selectors. LIST: Show active configuration.",
1719 + "required": false,
1720 + "schema": {
1721 + "type": "string",
1722 + "enum": [
1723 + "DISABLE ALL",
1724 + "SILENCE ALL",
1725 + "DISABLE",
1726 + "SILENCE",
1727 + "RESET",
1728 + "LIST"
1729 + ]
1730 + }
1731 + },
1732 + {
1733 + "name": "alarm",
1734 + "in": "query",
1735 + "description": "The expression provided will match both `alarm` and `template` names.",
1736 + "schema": {
1737 + "type": "string"
1738 + }
1739 + },
1740 + {
1741 + "name": "chart",
1742 + "in": "query",
1743 + "description": "Chart ids/names, as shown on the dashboard. These will match the `on` entry of a configured `alarm`.",
1744 + "schema": {
1745 + "type": "string"
1746 + }
1747 + },
1748 + {
1749 + "name": "context",
1750 + "in": "query",
1751 + "description": "Chart context, as shown on the dashboard. These will match the `on` entry of a configured `template`.",
1752 + "schema": {
1753 + "type": "string"
1754 + }
1755 + },
1756 + {
1757 + "name": "hosts",
1758 + "in": "query",
1759 + "description": "The hostnames that will need to match.",
1760 + "schema": {
1761 + "type": "string"
1762 + }
1763 + },
1764 + {
1765 + "name": "families",
1766 + "in": "query",
1767 + "description": "The alarm families.",
1768 + "schema": {
1769 + "type": "string"
1770 + }
1771 + }
1772 + ],
1773 + "responses": {
1774 + "200": {
1775 + "description": "A plain text response based on the result of the command."
1776 + },
1777 + "403": {
1778 + "description": "Bearer authentication error."
1779 + }
1780 + }
1781 + }
1782 + },
1783 + "/api/v1/aclk": {
1784 + "get": {
1785 + "summary": "Get information about current ACLK state",
1786 + "description": "ACLK endpoint returns detailed information about current state of ACLK (Agent to Cloud communication).",
1787 + "responses": {
1788 + "200": {
1789 + "description": "JSON object with ACLK information.",
1790 + "content": {
1791 + "application/json": {
1792 + "schema": {
1793 + "$ref": "#/components/schemas/aclk_state"
1794 + }
1795 + }
1796 + }
1797 + }
1798 + }
1799 + }
1800 + },
1801 + "/api/v1/metric_correlations": {
1802 + "get": {
1803 + "summary": "Analyze all the metrics to find their correlations",
1804 + "description": "THIS ENDPOINT IS OBSOLETE. Use the /weights endpoint. Given two time-windows (baseline, highlight), it goes through all the available metrics, querying both windows and tries to find how these two windows relate to each other. It supports multiple algorithms to do so. The result is a list of all metrics evaluated, weighted for 0.0 (the two windows are more different) to 1.0 (the two windows are similar). The algorithm adjusts automatically the baseline window to be a power of two multiple of the highlighted (1, 2, 4, 8, etc).",
1805 + "parameters": [
1806 + {
1807 + "name": "baseline_after",
1808 + "in": "query",
1809 + "description": "This parameter can either be an absolute timestamp specifying the starting point of baseline window, or a relative number of seconds (negative, relative to parameter baseline_before). Netdata will assume it is a relative number if it is less that 3 years (in seconds).",
1810 + "required": false,
1811 + "allowEmptyValue": false,
1812 + "schema": {
1813 + "type": "number",
1814 + "format": "integer",
1815 + "default": -300
1816 + }
1817 + },
1818 + {
1819 + "name": "baseline_before",
1820 + "in": "query",
1821 + "description": "This parameter can either be an absolute timestamp specifying the ending point of the baseline window, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds).",
1822 "required": false,
1823 "schema": {
1824 "type": "number",
@@ -1741,20 +1849,10 @@
1849 "default": 0
1850 }
1851 },
1744 - {
1745 - "name": "context",
1746 - "in": "query",
1747 - "description": "A simple pattern matching the contexts to evaluate.",
1748 - "required": false,
1749 - "allowEmptyValue": false,
1750 - "schema": {
1751 - "type": "string"
1752 - }
1753 - },
1852 {
1853 "name": "points",
1854 "in": "query",
1757 - "description": "The number of points to be evaluated for the highlighted window. The baseline window will be adjusted automatically to receive a proportional amount of points. This parameter is only used by the KS2 algorithm.",
1855 + "description": "The number of points to be evaluated for the highlighted window. The baseline window will be adjusted automatically to receive a proportional amount of points.",
1856 "required": false,
1857 "allowEmptyValue": false,
1858 "schema": {
@@ -1772,21 +1870,9 @@
1870 "type": "string",
1871 "enum": [
1872 "ks2",
1775 - "volume",
1776 - "anomaly-rate"
1873 + "volume"
1874 ],
1778 - "default": "anomaly-rate"
1779 - }
1780 - },
1781 - {
1782 - "name": "tier",
1783 - "in": "query",
1784 - "description": "Use the specified database tier",
1785 - "required": false,
1786 - "allowEmptyValue": false,
1787 - "schema": {
1788 - "type": "number",
1789 - "format": "integer"
1875 + "default": "ks2"
1876 }
1877 },
1878 {
@@ -1819,6 +1905,7 @@
1905 "null2zero",
1906 "percentage",
1907 "unaligned",
1908 + "allow_past",
1909 "nonzero",
1910 "anomaly-bit",
1911 "raw"
@@ -1826,6 +1913,7 @@
1913 },
1914 "default": [
1915 "null2zero",
1916 + "allow_past",
1917 "nonzero",
1918 "unaligned"
1919 ]
@@ -1896,11 +1984,11 @@
1984 ],
1985 "responses": {
1986 "200": {
1899 - "description": "JSON object with weights for each context, chart and dimension.",
1987 + "description": "JSON object with weights for each chart and dimension.",
1988 "content": {
1989 "application/json": {
1990 "schema": {
1903 - "$ref": "#/components/schemas/weights"
1991 + "$ref": "#/components/schemas/metric_correlations"
1992 }
1993 }
1994 }
@@ -1919,305 +2007,600 @@
2007 }
2008 }
2009 }
1922 - }
1923 - },
1924 - "servers": [
1925 - {
1926 - "url": "https://registry.my-netdata.io/api/v1"
2010 },
1928 - {
1929 - "url": "http://registry.my-netdata.io/api/v1"
1930 - }
1931 - ],
1932 - "components": {
1933 - "schemas": {
1934 - "info": {
1935 - "type": "object",
1936 - "properties": {
1937 - "version": {
1938 - "type": "string",
1939 - "description": "netdata version of the server.",
1940 - "example": "1.11.1_rolling"
1941 - },
1942 - "uid": {
1943 - "type": "string",
1944 - "description": "netdata unique id of the server.",
1945 - "example": "24e9fe3c-f2ac-11e8-bafc-0242ac110002"
1946 - },
1947 - "mirrored_hosts": {
1948 - "type": "array",
1949 - "description": "List of hosts mirrored of the server (include itself).",
1950 - "items": {
2011 + "/api/v1/function": {
2012 + "get": {
2013 + "summary": "Execute a collector function.",
2014 + "parameters": [
2015 + {
2016 + "name": "function",
2017 + "in": "query",
2018 + "description": "The name of the function, as returned by the collector.",
2019 + "required": true,
2020 + "allowEmptyValue": false,
2021 + "schema": {
2022 "type": "string"
1952 - },
1953 - "example": [
1954 - "host1.example.com",
1955 - "host2.example.com"
1956 - ]
1957 - },
1958 - "mirrored_hosts_status": {
1959 - "type": "array",
1960 - "description": "List of details of hosts mirrored to this served (including self). Indexes correspond to indexes in \"mirrored_hosts\".",
1961 - "items": {
1962 - "type": "object",
1963 - "description": "Host data",
1964 - "properties": {
1965 - "guid": {
1966 - "type": "string",
1967 - "format": "uuid",
1968 - "nullable": false,
1969 - "description": "Host unique GUID from `netdata.public.unique.id`.",
1970 - "example": "245e4bff-3b34-47c1-a6e5-5c535a9abfb2"
1971 - },
1972 - "reachable": {
1973 - "type": "boolean",
1974 - "nullable": false,
1975 - "description": "Current state of streaming. Always true for localhost/self."
1976 - },
1977 - "claim_id": {
1978 - "type": "string",
1979 - "format": "uuid",
1980 - "nullable": true,
1981 - "description": "Cloud GUID/identifier in case the host is claimed. If child status unknown or unclaimed this field is set to `null`",
1982 - "example": "c3b2a66a-3052-498c-ac52-7fe9e8cccb0c"
1983 - }
1984 - }
2023 }
2024 },
1987 - "os_name": {
1988 - "type": "string",
1989 - "description": "Operating System Name.",
1990 - "example": "Manjaro Linux"
1991 - },
1992 - "os_id": {
1993 - "type": "string",
1994 - "description": "Operating System ID.",
1995 - "example": "manjaro"
1996 - },
1997 - "os_id_like": {
1998 - "type": "string",
1999 - "description": "Known OS similar to this OS.",
2000 - "example": "arch"
2001 - },
2002 - "os_version": {
2003 - "type": "string",
2004 - "description": "Operating System Version.",
2005 - "example": "18.0.4"
2006 - },
2007 - "os_version_id": {
2008 - "type": "string",
2009 - "description": "Operating System Version ID.",
2010 - "example": "unknown"
2025 + {
2026 + "name": "timeout",
2027 + "in": "query",
2028 + "description": "The timeout in seconds to wait for the function to complete.",
2029 + "required": false,
2030 + "schema": {
2031 + "type": "number",
2032 + "format": "integer",
2033 + "default": 10
2034 + }
2035 + }
2036 + ],
2037 + "responses": {
2038 + "200": {
2039 + "description": "The collector function has been executed successfully. Each collector may return a different type of content."
2040 },
2012 - "os_detection": {
2013 - "type": "string",
2014 - "description": "OS parameters detection method.",
2015 - "example": "Mixed"
2041 + "400": {
2042 + "description": "The request was rejected by the collector."
2043 },
2017 - "kernel_name": {
2018 - "type": "string",
2019 - "description": "Kernel Name.",
2020 - "example": "Linux"
2044 + "404": {
2045 + "description": "The requested function is not found."
2046 },
2022 - "kernel_version": {
2023 - "type": "string",
2024 - "description": "Kernel Version.",
2025 - "example": "4.19.32-1-MANJARO"
2047 + "500": {
2048 + "description": "Other internal error, getting this error means there is a bug in Netdata."
2049 },
2027 - "is_k8s_node": {
2028 - "type": "boolean",
2029 - "description": "Netdata is running on a K8s node.",
2030 - "example": false
2050 + "503": {
2051 + "description": "The collector to execute the function is not currently available."
2052 },
2032 - "architecture": {
2033 - "type": "string",
2034 - "description": "Kernel architecture.",
2035 - "example": "x86_64"
2053 + "504": {
2054 + "description": "Timeout while waiting for the collector to execute the function."
2055 },
2037 - "virtualization": {
2038 - "type": "string",
2039 - "description": "Virtualization Type.",
2040 - "example": "kvm"
2056 + "591": {
2057 + "description": "The collector sent a response, but it was invalid or corrupted."
2058 + }
2059 + }
2060 + }
2061 + },
2062 + "/api/v1/functions": {
2063 + "get": {
2064 + "summary": "Get a list of all registered collector functions.",
2065 + "description": "Collector functions are programs that can be executed on demand.",
2066 + "responses": {
2067 + "200": {
2068 + "description": "A JSON object containing one object per supported function."
2069 + }
2070 + }
2071 + }
2072 + },
2073 + "/api/v1/weights": {
2074 + "get": {
2075 + "summary": "Analyze all the metrics using an algorithm and score them accordingly",
2076 + "description": "This endpoint goes through all metrics and scores them according to an algorithm.",
2077 + "parameters": [
2078 + {
2079 + "name": "baseline_after",
2080 + "in": "query",
2081 + "description": "This parameter can either be an absolute timestamp specifying the starting point of baseline window, or a relative number of seconds (negative, relative to parameter baseline_before). Netdata will assume it is a relative number if it is less that 3 years (in seconds). This parameter is used in KS2 and VOLUME algorithms.",
2082 + "required": false,
2083 + "allowEmptyValue": false,
2084 + "schema": {
2085 + "type": "number",
2086 + "format": "integer",
2087 + "default": -300
2088 + }
2089 },
2042 - "virt_detection": {
2043 - "type": "string",
2044 - "description": "Virtualization detection method.",
2045 - "example": "systemd-detect-virt"
2090 + {
2091 + "name": "baseline_before",
2092 + "in": "query",
2093 + "description": "This parameter can either be an absolute timestamp specifying the ending point of the baseline window, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds). This parameter is used in KS2 and VOLUME algorithms.",
2094 + "required": false,
2095 + "schema": {
2096 + "type": "number",
2097 + "format": "integer",
2098 + "default": -60
2099 + }
2100 },
2047 - "container": {
2048 - "type": "string",
2049 - "description": "Container technology.",
2050 - "example": "docker"
2101 + {
2102 + "name": "after",
2103 + "in": "query",
2104 + "description": "This parameter can either be an absolute timestamp specifying the starting point of highlighted window, or a relative number of seconds (negative, relative to parameter highlight_before). Netdata will assume it is a relative number if it is less that 3 years (in seconds).",
2105 + "required": false,
2106 + "allowEmptyValue": false,
2107 + "schema": {
2108 + "type": "number",
2109 + "format": "integer",
2110 + "default": -60
2111 + }
2112 },
2052 - "container_detection": {
2053 - "type": "string",
2054 - "description": "Container technology detection method.",
2055 - "example": "dockerenv"
2113 + {
2114 + "name": "before",
2115 + "in": "query",
2116 + "description": "This parameter can either be an absolute timestamp specifying the ending point of the highlighted window, or a relative number of seconds (negative), relative to the last collected timestamp. Netdata will assume it is a relative number if it is less than 3 years (in seconds).",
2117 + "required": false,
2118 + "schema": {
2119 + "type": "number",
2120 + "format": "integer",
2121 + "default": 0
2122 + }
2123 },
2057 - "stream_compression": {
2058 - "type": "boolean",
2059 - "description": "Stream transmission compression method.",
2060 - "example": true
2124 + {
2125 + "name": "context",
2126 + "in": "query",
2127 + "description": "A simple pattern matching the contexts to evaluate.",
2128 + "required": false,
2129 + "allowEmptyValue": false,
2130 + "schema": {
2131 + "type": "string"
2132 + }
2133 },
2062 - "labels": {
2063 - "type": "object",
2064 - "description": "List of host labels.",
2065 - "properties": {
2066 - "app": {
2067 - "type": "string",
2068 - "description": "Host label.",
2069 - "example": "netdata"
2070 - }
2134 + {
2135 + "name": "points",
2136 + "in": "query",
2137 + "description": "The number of points to be evaluated for the highlighted window. The baseline window will be adjusted automatically to receive a proportional amount of points. This parameter is only used by the KS2 algorithm.",
2138 + "required": false,
2139 + "allowEmptyValue": false,
2140 + "schema": {
2141 + "type": "number",
2142 + "format": "integer",
2143 + "default": 500
2144 }
2145 },
2073 - "collectors": {
2074 - "type": "array",
2075 - "items": {
2076 - "type": "object",
2077 - "description": "Array of collector plugins and modules.",
2078 - "properties": {
2079 - "plugin": {
2080 - "type": "string",
2081 - "description": "Collector plugin.",
2082 - "example": "python.d.plugin"
2083 - },
2084 - "module": {
2085 - "type": "string",
2086 - "description": "Module of the collector plugin.",
2087 - "example": "dockerd"
2088 - }
2089 - }
2146 + {
2147 + "name": "method",
2148 + "in": "query",
2149 + "description": "the algorithm to run",
2150 + "required": false,
2151 + "schema": {
2152 + "type": "string",
2153 + "enum": [
2154 + "ks2",
2155 + "volume",
2156 + "anomaly-rate"
2157 + ],
2158 + "default": "anomaly-rate"
2159 }
2160 },
2092 - "alarms": {
2093 - "type": "object",
2094 - "description": "Number of alarms in the server.",
2095 - "properties": {
2096 - "normal": {
2097 - "type": "integer",
2098 - "description": "Number of alarms in normal state."
2099 - },
2100 - "warning": {
2101 - "type": "integer",
2102 - "description": "Number of alarms in warning state."
2103 - },
2104 - "critical": {
2105 - "type": "integer",
2106 - "description": "Number of alarms in critical state."
2107 - }
2161 + {
2162 + "name": "tier",
2163 + "in": "query",
2164 + "description": "Use the specified database tier",
2165 + "required": false,
2166 + "allowEmptyValue": false,
2167 + "schema": {
2168 + "type": "number",
2169 + "format": "integer"
2170 }
2109 - }
2110 - }
2111 - },
2112 - "chart_summary": {
2113 - "type": "object",
2114 - "properties": {
2115 - "hostname": {
2116 - "type": "string",
2117 - "description": "The hostname of the netdata server."
2171 },
2119 - "version": {
2120 - "type": "string",
2121 - "description": "netdata version of the server."
2172 + {
2173 + "name": "timeout",
2174 + "in": "query",
2175 + "description": "Cancel the query if to takes more that this amount of milliseconds.",
2176 + "required": false,
2177 + "allowEmptyValue": false,
2178 + "schema": {
2179 + "type": "number",
2180 + "format": "integer",
2181 + "default": 60000
2182 + }
2183 },
2123 - "release_channel": {
2124 - "type": "string",
2125 - "description": "The release channel of the build on the server.",
2126 - "example": "nightly"
2127 - },
2128 - "timezone": {
2129 - "type": "string",
2130 - "description": "The current timezone on the server."
2131 - },
2132 - "os": {
2133 - "type": "string",
2134 - "description": "The netdata server host operating system.",
2135 - "enum": [
2136 - "macos",
2137 - "linux",
2138 - "freebsd"
2139 - ]
2140 - },
2141 - "history": {
2142 - "type": "number",
2143 - "description": "The duration, in seconds, of the round robin database maintained by netdata."
2144 - },
2145 - "memory_mode": {
2146 - "type": "string",
2147 - "description": "The name of the database memory mode on the server."
2184 + {
2185 + "name": "options",
2186 + "in": "query",
2187 + "description": "Options that affect data generation.",
2188 + "required": false,
2189 + "allowEmptyValue": false,
2190 + "schema": {
2191 + "type": "array",
2192 + "items": {
2193 + "type": "string",
2194 + "enum": [
2195 + "min2max",
2196 + "abs",
2197 + "absolute",
2198 + "absolute-sum",
2199 + "null2zero",
2200 + "percentage",
2201 + "unaligned",
2202 + "nonzero",
2203 + "anomaly-bit",
2204 + "raw"
2205 + ]
2206 + },
2207 + "default": [
2208 + "null2zero",
2209 + "nonzero",
2210 + "unaligned"
2211 + ]
2212 + }
2213 },
2149 - "update_every": {
2150 - "type": "number",
2151 - "description": "The default update frequency of the netdata server. All charts have an update frequency equal or bigger than this."
2214 + {
2215 + "name": "group",
2216 + "in": "query",
2217 + "description": "The grouping method. If multiple collected values are to be grouped in order to return fewer points, this parameters defines the method of grouping. methods supported \"min\", \"max\", \"average\", \"sum\", \"incremental-sum\". \"max\" is actually calculated on the absolute value collected (so it works for both positive and negative dimensions to return the most extreme value in either direction).",
2218 + "required": true,
2219 + "allowEmptyValue": false,
2220 + "schema": {
2221 + "type": "string",
2222 + "enum": [
2223 + "min",
2224 + "max",
2225 + "average",
2226 + "median",
2227 + "stddev",
2228 + "sum",
2229 + "incremental-sum",
2230 + "ses",
2231 + "des",
2232 + "cv",
2233 + "countif",
2234 + "percentile",
2235 + "percentile25",
2236 + "percentile50",
2237 + "percentile75",
2238 + "percentile80",
2239 + "percentile90",
2240 + "percentile95",
2241 + "percentile97",
2242 + "percentile98",
2243 + "percentile99",
2244 + "trimmed-mean",
2245 + "trimmed-mean1",
2246 + "trimmed-mean2",
2247 + "trimmed-mean3",
2248 + "trimmed-mean5",
2249 + "trimmed-mean10",
2250 + "trimmed-mean15",
2251 + "trimmed-mean20",
2252 + "trimmed-mean25",
2253 + "trimmed-median",
2254 + "trimmed-median1",
2255 + "trimmed-median2",
2256 + "trimmed-median3",
2257 + "trimmed-median5",
2258 + "trimmed-median10",
2259 + "trimmed-median15",
2260 + "trimmed-median20",
2261 + "trimmed-median25"
2262 + ],
2263 + "default": "average"
2264 + }
2265 },
2153 - "charts": {
2154 - "type": "object",
2155 - "description": "An object containing all the chart objects available at the netdata server. This is used as an indexed array. The key of each chart object is the id of the chart.",
2156 - "additionalProperties": {
2157 - "$ref": "#/components/schemas/chart"
2266 + {
2267 + "name": "group_options",
2268 + "in": "query",
2269 + "description": "When the group function supports additional parameters, this field can be used to pass them to it. Currently only \"countif\" supports this.",
2270 + "required": false,
2271 + "allowEmptyValue": false,
2272 + "schema": {
2273 + "type": "string"
2274 + }
2275 + }
2276 + ],
2277 + "responses": {
2278 + "200": {
2279 + "description": "JSON object with weights for each context, chart and dimension.",
2280 + "content": {
2281 + "application/json": {
2282 + "schema": {
2283 + "$ref": "#/components/schemas/weights"
2284 + }
2285 + }
2286 }
2287 },
2160 - "charts_count": {
2161 - "type": "number",
2162 - "description": "The number of charts."
2288 + "400": {
2289 + "description": "The given parameters are invalid."
2290 },
2164 - "dimensions_count": {
2165 - "type": "number",
2166 - "description": "The total number of dimensions."
2291 + "403": {
2292 + "description": "metrics correlations are not enabled on this Netdata Agent."
2293 },
2168 - "alarms_count": {
2169 - "type": "number",
2170 - "description": "The number of alarms."
2294 + "404": {
2295 + "description": "No charts could be found, or the method that correlated the metrics did not produce any result."
2296 },
2172 - "rrd_memory_bytes": {
2173 - "type": "number",
2174 - "description": "The size of the round robin database in bytes."
2297 + "504": {
2298 + "description": "Timeout - the query took too long and has been cancelled."
2299 }
2300 }
2177 - },
2178 - "chart": {
2301 + }
2302 + }
2303 + },
2304 + "servers": [
2305 + {
2306 + "url": "https://registry.my-netdata.io"
2307 + },
2308 + {
2309 + "url": "http://registry.my-netdata.io"
2310 + },
2311 + {
2312 + "url": "http://localhost:19999"
2313 + }
2314 + ],
2315 + "components": {
2316 + "schemas": {
2317 + "info": {
2318 "type": "object",
2319 "properties": {
2181 - "id": {
2320 + "version": {
2321 "type": "string",
2183 - "description": "The unique id of the chart."
2322 + "description": "netdata version of the server.",
2323 + "example": "1.11.1_rolling"
2324 },
2185 - "name": {
2325 + "uid": {
2326 "type": "string",
2187 - "description": "The name of the chart."
2327 + "description": "netdata unique id of the server.",
2328 + "example": "24e9fe3c-f2ac-11e8-bafc-0242ac110002"
2329 },
2189 - "type": {
2190 - "type": "string",
2191 - "description": "The type of the chart. Types are not handled by netdata. You can use this field for anything you like."
2330 + "mirrored_hosts": {
2331 + "type": "array",
2332 + "description": "List of hosts mirrored of the server (include itself).",
2333 + "items": {
2334 + "type": "string"
2335 + },
2336 + "example": [
2337 + "host1.example.com",
2338 + "host2.example.com"
2339 + ]
2340 },
2193 - "family": {
2341 + "mirrored_hosts_status": {
2342 + "type": "array",
2343 + "description": "List of details of hosts mirrored to this served (including self). Indexes correspond to indexes in \"mirrored_hosts\".",
2344 + "items": {
2345 + "type": "object",
2346 + "description": "Host data",
2347 + "properties": {
2348 + "guid": {
2349 + "type": "string",
2350 + "format": "uuid",
2351 + "nullable": false,
2352 + "description": "Host unique GUID from `netdata.public.unique.id`.",
2353 + "example": "245e4bff-3b34-47c1-a6e5-5c535a9abfb2"
2354 + },
2355 + "reachable": {
2356 + "type": "boolean",
2357 + "nullable": false,
2358 + "description": "Current state of streaming. Always true for localhost/self."
2359 + },
2360 + "claim_id": {
2361 + "type": "string",
2362 + "format": "uuid",
2363 + "nullable": true,
2364 + "description": "Cloud GUID/identifier in case the host is claimed. If child status unknown or unclaimed this field is set to `null`",
2365 + "example": "c3b2a66a-3052-498c-ac52-7fe9e8cccb0c"
2366 + }
2367 + }
2368 + }
2369 + },
2370 + "os_name": {
2371 "type": "string",
2195 - "description": "The family of the chart. Families are not handled by netdata. You can use this field for anything you like."
2372 + "description": "Operating System Name.",
2373 + "example": "Manjaro Linux"
2374 },
2197 - "title": {
2375 + "os_id": {
2376 "type": "string",
2199 - "description": "The title of the chart."
2377 + "description": "Operating System ID.",
2378 + "example": "manjaro"
2379 },
2201 - "priority": {
2202 - "type": "number",
2203 - "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."
2380 + "os_id_like": {
2381 + "type": "string",
2382 + "description": "Known OS similar to this OS.",
2383 + "example": "arch"
2384 },
2205 - "enabled": {
2206 - "type": "boolean",
2207 - "description": "True when the chart is enabled. Disabled charts do not currently collect values, but they may have historical values available."
2385 + "os_version": {
2386 + "type": "string",
2387 + "description": "Operating System Version.",
2388 + "example": "18.0.4"
2389 },
2209 - "units": {
2390 + "os_version_id": {
2391 "type": "string",
2211 - "description": "The unit of measurement for the values of all dimensions of the chart."
2392 + "description": "Operating System Version ID.",
2393 + "example": "unknown"
2394 },
2213 - "data_url": {
2395 + "os_detection": {
2396 "type": "string",
2215 - "description": "The absolute path to get data values for this chart. You are expected to use this path as the base when constructing the URL to fetch data values for this chart."
2397 + "description": "OS parameters detection method.",
2398 + "example": "Mixed"
2399 },
2217 - "chart_type": {
2400 + "kernel_name": {
2401 "type": "string",
2219 - "description": "The chart type.",
2220 - "enum": [
2402 + "description": "Kernel Name.",
2403 + "example": "Linux"
2404 + },
2405 + "kernel_version": {
2406 + "type": "string",
2407 + "description": "Kernel Version.",
2408 + "example": "4.19.32-1-MANJARO"
2409 + },
2410 + "is_k8s_node": {
2411 + "type": "boolean",
2412 + "description": "Netdata is running on a K8s node.",
2413 + "example": false
2414 + },
2415 + "architecture": {
2416 + "type": "string",
2417 + "description": "Kernel architecture.",
2418 + "example": "x86_64"
2419 + },
2420 + "virtualization": {
2421 + "type": "string",
2422 + "description": "Virtualization Type.",
2423 + "example": "kvm"
2424 + },
2425 + "virt_detection": {
2426 + "type": "string",
2427 + "description": "Virtualization detection method.",
2428 + "example": "systemd-detect-virt"
2429 + },
2430 + "container": {
2431 + "type": "string",
2432 + "description": "Container technology.",
2433 + "example": "docker"
2434 + },
2435 + "container_detection": {
2436 + "type": "string",
2437 + "description": "Container technology detection method.",
2438 + "example": "dockerenv"
2439 + },
2440 + "stream_compression": {
2441 + "type": "boolean",
2442 + "description": "Stream transmission compression method.",
2443 + "example": true
2444 + },
2445 + "labels": {
2446 + "type": "object",
2447 + "description": "List of host labels.",
2448 + "properties": {
2449 + "app": {
2450 + "type": "string",
2451 + "description": "Host label.",
2452 + "example": "netdata"
2453 + }
2454 + }
2455 + },
2456 + "collectors": {
2457 + "type": "array",
2458 + "items": {
2459 + "type": "object",
2460 + "description": "Array of collector plugins and modules.",
2461 + "properties": {
2462 + "plugin": {
2463 + "type": "string",
2464 + "description": "Collector plugin.",
2465 + "example": "python.d.plugin"
2466 + },
2467 + "module": {
2468 + "type": "string",
2469 + "description": "Module of the collector plugin.",
2470 + "example": "dockerd"
2471 + }
2472 + }
2473 + }
2474 + },
2475 + "alarms": {
2476 + "type": "object",
2477 + "description": "Number of alarms in the server.",
2478 + "properties": {
2479 + "normal": {
2480 + "type": "integer",
2481 + "description": "Number of alarms in normal state."
2482 + },
2483 + "warning": {
2484 + "type": "integer",
2485 + "description": "Number of alarms in warning state."
2486 + },
2487 + "critical": {
2488 + "type": "integer",
2489 + "description": "Number of alarms in critical state."
2490 + }
2491 + }
2492 + }
2493 + }
2494 + },
2495 + "chart_summary": {
2496 + "type": "object",
2497 + "properties": {
2498 + "hostname": {
2499 + "type": "string",
2500 + "description": "The hostname of the netdata server."
2501 + },
2502 + "version": {
2503 + "type": "string",
2504 + "description": "netdata version of the server."
2505 + },
2506 + "release_channel": {
2507 + "type": "string",
2508 + "description": "The release channel of the build on the server.",
2509 + "example": "nightly"
2510 + },
2511 + "timezone": {
2512 + "type": "string",
2513 + "description": "The current timezone on the server."
2514 + },
2515 + "os": {
2516 + "type": "string",
2517 + "description": "The netdata server host operating system.",
2518 + "enum": [
2519 + "macos",
2520 + "linux",
2521 + "freebsd"
2522 + ]
2523 + },
2524 + "history": {
2525 + "type": "number",
2526 + "description": "The duration, in seconds, of the round robin database maintained by netdata."
2527 + },
2528 + "memory_mode": {
2529 + "type": "string",
2530 + "description": "The name of the database memory mode on the server."
2531 + },
2532 + "update_every": {
2533 + "type": "number",
2534 + "description": "The default update frequency of the netdata server. All charts have an update frequency equal or bigger than this."
2535 + },
2536 + "charts": {
2537 + "type": "object",
2538 + "description": "An object containing all the chart objects available at the netdata server. This is used as an indexed array. The key of each chart object is the id of the chart.",
2539 + "additionalProperties": {
2540 + "$ref": "#/components/schemas/chart"
2541 + }
2542 + },
2543 + "charts_count": {
2544 + "type": "number",
2545 + "description": "The number of charts."
2546 + },
2547 + "dimensions_count": {
2548 + "type": "number",
2549 + "description": "The total number of dimensions."
2550 + },
2551 + "alarms_count": {
2552 + "type": "number",
2553 + "description": "The number of alarms."
2554 + },
2555 + "rrd_memory_bytes": {
2556 + "type": "number",
2557 + "description": "The size of the round robin database in bytes."
2558 + }
2559 + }
2560 + },
2561 + "chart": {
2562 + "type": "object",
2563 + "properties": {
2564 + "id": {
2565 + "type": "string",
2566 + "description": "The unique id of the chart."
2567 + },
2568 + "name": {
2569 + "type": "string",
2570 + "description": "The name of the chart."
2571 + },
2572 + "type": {
2573 + "type": "string",
2574 + "description": "The type of the chart. Types are not handled by netdata. You can use this field for anything you like."
2575 + },
2576 + "family": {
2577 + "type": "string",
2578 + "description": "The family of the chart. Families are not handled by netdata. You can use this field for anything you like."
2579 + },
2580 + "title": {
2581 + "type": "string",
2582 + "description": "The title of the chart."
2583 + },
2584 + "priority": {
2585 + "type": "number",
2586 + "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."
2587 + },
2588 + "enabled": {
2589 + "type": "boolean",
2590 + "description": "True when the chart is enabled. Disabled charts do not currently collect values, but they may have historical values available."
2591 + },
2592 + "units": {
2593 + "type": "string",
2594 + "description": "The unit of measurement for the values of all dimensions of the chart."
2595 + },
2596 + "data_url": {
2597 + "type": "string",
2598 + "description": "The absolute path to get data values for this chart. You are expected to use this path as the base when constructing the URL to fetch data values for this chart."
2599 + },
2600 + "chart_type": {
2601 + "type": "string",
2602 + "description": "The chart type.",
2603 + "enum": [
2604 "line",
2605 "area",
2606 "stacked"
@@ -2252,273 +2635,902 @@
2635 }
2636 }
2637 },
2255 - "chart_variables": {
2256 - "type": "object",
2257 - "additionalProperties": {
2258 - "$ref": "#/components/schemas/chart_variables"
2259 - }
2260 - },
2261 - "green": {
2262 - "type": "number",
2263 - "nullable": true,
2264 - "description": "Chart health green threshold."
2265 - },
2266 - "red": {
2267 - "type": "number",
2268 - "nullable": true,
2269 - "description": "Chart health red threshold."
2638 + "chart_variables": {
2639 + "type": "object",
2640 + "additionalProperties": {
2641 + "$ref": "#/components/schemas/chart_variables"
2642 + }
2643 + },
2644 + "green": {
2645 + "type": "number",
2646 + "nullable": true,
2647 + "description": "Chart health green threshold."
2648 + },
2649 + "red": {
2650 + "type": "number",
2651 + "nullable": true,
2652 + "description": "Chart health red threshold."
2653 + }
2654 + }
2655 + },
2656 + "context_summary": {
2657 + "type": "object",
2658 + "properties": {
2659 + "hostname": {
2660 + "type": "string",
2661 + "description": "The hostname of the netdata server."
2662 + },
2663 + "machine_guid": {
2664 + "type": "string",
2665 + "description": "The unique installation id of this netdata server."
2666 + },
2667 + "node_id": {
2668 + "type": "string",
2669 + "description": "The unique node id of this netdata server at the hub.",
2670 + "example": "nightly"
2671 + },
2672 + "claim_id": {
2673 + "type": "string",
2674 + "description": "The unique handshake id of this netdata server and the hub."
2675 + },
2676 + "host_labels": {
2677 + "type": "object",
2678 + "description": "The host labels associated with this netdata server."
2679 + },
2680 + "context": {
2681 + "type": "object",
2682 + "description": "An object containing all the context objects available at the netdata server. This is used as an indexed array. The key of each context object is the id of the context.",
2683 + "additionalProperties": {
2684 + "$ref": "#/components/schemas/context"
2685 + }
2686 + }
2687 + }
2688 + },
2689 + "context": {
2690 + "type": "object",
2691 + "properties": {
2692 + "version": {
2693 + "type": "string",
2694 + "description": "The version of this context. The number are not sequential, but bigger numbers depict a newer object."
2695 + },
2696 + "hub_version": {
2697 + "type": "string",
2698 + "description": "The version of this context, as known by hub."
2699 + },
2700 + "family": {
2701 + "type": "string",
2702 + "description": "The family of the context. When multiple charts of a context have different families, the netdata server replaces the different parts with [x], so that the context can have only one family."
2703 + },
2704 + "title": {
2705 + "type": "string",
2706 + "description": "The title of the context. When multiple charts of a context have different titles, the netdata server replaces the different parts with [x], so that the context can have only one title."
2707 + },
2708 + "priority": {
2709 + "type": "number",
2710 + "description": "The relative priority of the context. When multiple contexts have different priorities, the minimum among them is selected as the priority of the context."
2711 + },
2712 + "units": {
2713 + "type": "string",
2714 + "description": "The unit of measurement for the values of all dimensions of the context. If multiple charts of context have different units, the latest collected is selected."
2715 + },
2716 + "chart_type": {
2717 + "type": "string",
2718 + "description": "The chart type.",
2719 + "enum": [
2720 + "line",
2721 + "area",
2722 + "stacked"
2723 + ]
2724 + },
2725 + "first_time_t": {
2726 + "type": "number",
2727 + "description": "The UNIX timestamp of the first entry (the oldest) in the database."
2728 + },
2729 + "last_time_t": {
2730 + "type": "number",
2731 + "description": "The UNIX timestamp of the latest entry in the database."
2732 + },
2733 + "charts": {
2734 + "type": "object",
2735 + "description": "An object containing all the charts available for the chart. This is used as an indexed array. For each pair in the dictionary, the key is the id of the chart and the value provides all details about the chart."
2736 + }
2737 + }
2738 + },
2739 + "alarm_variables": {
2740 + "type": "object",
2741 + "properties": {
2742 + "chart": {
2743 + "type": "string",
2744 + "description": "The unique id of the chart."
2745 + },
2746 + "chart_name": {
2747 + "type": "string",
2748 + "description": "The name of the chart."
2749 + },
2750 + "cnart_context": {
2751 + "type": "string",
2752 + "description": "The context of the chart. It is shared across multiple monitored software or hardware instances and used in alarm templates."
2753 + },
2754 + "family": {
2755 + "type": "string",
2756 + "description": "The family of the chart."
2757 + },
2758 + "host": {
2759 + "type": "string",
2760 + "description": "The host containing the chart."
2761 + },
2762 + "chart_variables": {
2763 + "type": "object",
2764 + "additionalProperties": {
2765 + "$ref": "#/components/schemas/chart_variables"
2766 + }
2767 + },
2768 + "family_variables": {
2769 + "type": "object",
2770 + "properties": {
2771 + "varname1": {
2772 + "type": "number",
2773 + "format": "float"
2774 + },
2775 + "varname2": {
2776 + "type": "number",
2777 + "format": "float"
2778 + }
2779 + }
2780 + },
2781 + "host_variables": {
2782 + "type": "object",
2783 + "properties": {
2784 + "varname1": {
2785 + "type": "number",
2786 + "format": "float"
2787 + },
2788 + "varname2": {
2789 + "type": "number",
2790 + "format": "float"
2791 + }
2792 + }
2793 + }
2794 + }
2795 + },
2796 + "chart_variables": {
2797 + "type": "object",
2798 + "properties": {
2799 + "varname1": {
2800 + "type": "number",
2801 + "format": "float"
2802 + },
2803 + "varname2": {
2804 + "type": "number",
2805 + "format": "float"
2806 + }
2807 + }
2808 + },
2809 + "data": {
2810 + "type": "object",
2811 + "discriminator": {
2812 + "propertyName": "format"
2813 + },
2814 + "description": "Response will contain the appropriate subtype, e.g. data_json depending on the requested format.",
2815 + "properties": {
2816 + "api": {
2817 + "type": "number",
2818 + "description": "The API version this conforms to, currently 1."
2819 + },
2820 + "id": {
2821 + "type": "string",
2822 + "description": "The unique id of the chart."
2823 + },
2824 + "name": {
2825 + "type": "string",
2826 + "description": "The name of the chart."
2827 + },
2828 + "update_every": {
2829 + "type": "number",
2830 + "description": "The update frequency of this chart, in seconds. One value every this amount of time is kept in the round robin database (independently of the current view)."
2831 + },
2832 + "view_update_every": {
2833 + "type": "number",
2834 + "description": "The current view appropriate update frequency of this chart, in seconds. There is no point to request chart refreshes, using the same settings, more frequently than this."
2835 + },
2836 + "first_entry": {
2837 + "type": "number",
2838 + "description": "The UNIX timestamp of the first entry (the oldest) in the round robin database (independently of the current view)."
2839 + },
2840 + "last_entry": {
2841 + "type": "number",
2842 + "description": "The UNIX timestamp of the latest entry in the round robin database (independently of the current view)."
2843 + },
2844 + "after": {
2845 + "type": "number",
2846 + "description": "The UNIX timestamp of the first entry (the oldest) returned in this response."
2847 + },
2848 + "before": {
2849 + "type": "number",
2850 + "description": "The UNIX timestamp of the latest entry returned in this response."
2851 + },
2852 + "min": {
2853 + "type": "number",
2854 + "description": "The minimum value returned in the current view. This can be used to size the y-series of the chart."
2855 + },
2856 + "max": {
2857 + "type": "number",
2858 + "description": "The maximum value returned in the current view. This can be used to size the y-series of the chart."
2859 + },
2860 + "dimension_names": {
2861 + "description": "The dimension names of the chart as returned in the current view.",
2862 + "type": "array",
2863 + "items": {
2864 + "type": "string"
2865 + }
2866 + },
2867 + "dimension_ids": {
2868 + "description": "The dimension IDs of the chart as returned in the current view.",
2869 + "type": "array",
2870 + "items": {
2871 + "type": "string"
2872 + }
2873 + },
2874 + "latest_values": {
2875 + "description": "The latest values collected for the chart (independently of the current view).",
2876 + "type": "array",
2877 + "items": {
2878 + "type": "string"
2879 + }
2880 + },
2881 + "view_latest_values": {
2882 + "description": "The latest values returned with this response.",
2883 + "type": "array",
2884 + "items": {
2885 + "type": "string"
2886 + }
2887 + },
2888 + "dimensions": {
2889 + "type": "number",
2890 + "description": "The number of dimensions returned."
2891 + },
2892 + "points": {
2893 + "type": "number",
2894 + "description": "The number of rows / points returned."
2895 + },
2896 + "format": {
2897 + "type": "string",
2898 + "description": "The format of the result returned."
2899 + },
2900 + "chart_variables": {
2901 + "type": "object",
2902 + "additionalProperties": {
2903 + "$ref": "#/components/schemas/chart_variables"
2904 + }
2905 + }
2906 + }
2907 + },
2908 + "data_json2": {
2909 + "description": "Data response with `format=json2`\n",
2910 + "type": "object",
2911 + "properties": {
2912 + "versions": {
2913 + "description": "Hashes that allow the caller to detect important database changes of Netdata agents.\n",
2914 + "type": "object",
2915 + "properties": {
2916 + "contexts_hard_hash": {
2917 + "description": "An auto-increment value that reflects the number of changes to the number of contexts maintained by the server. Everytime a context is added or removed, this number gets incremented.\n",
2918 + "type": "integer"
2919 + },
2920 + "contexts_soft_hash": {
2921 + "description": "An auto-increment value that reflects the number of changes to the queue that sends contexts updates to Netdata Cloud. Everytime the contents of a context are updated, this number gets incremented.\n",
2922 + "type": "integer"
2923 + }
2924 + }
2925 + },
2926 + "summary": {
2927 + "description": "Summarized information about nodes, contexts, instances, labels, alerts, and dimensions. The items returned are determined by the scope of the query only, however the statistical data in them are influenced by the filters of the query. Using this information the dashboard allows users to slice and dice the data by filtering and grouping.\n",
2928 + "type": "object",
2929 + "properties": {
2930 + "nodes": {
2931 + "type": "array",
2932 + "items": {
2933 + "type": "object",
2934 + "description": "An object describing a node. `is` stands for instances, `ds` for dimensions, `al` for alerts, `sts` for statistics.\n",
2935 + "properties": {
2936 + "ni": {
2937 + "description": "the node index id, a number that uniquely identifies this node for this query.",
2938 + "type": "integer"
2939 + },
2940 + "mg": {
2941 + "description": "the machine guid of the node.",
2942 + "type": "string",
2943 + "format": "UUID"
2944 + },
2945 + "nd": {
2946 + "description": "the node id of the node.",
2947 + "type": "string",
2948 + "format": "UUID"
2949 + },
2950 + "nm": {
2951 + "description": "the name (hostname) of the node.",
2952 + "type": "string"
2953 + },
2954 + "is": {
2955 + "$ref": "#/components/schemas/data_json2_items_count"
2956 + },
2957 + "ds": {
2958 + "$ref": "#/components/schemas/data_json2_items_count"
2959 + },
2960 + "al": {
2961 + "$ref": "#/components/schemas/data_json2_alerts_count"
2962 + },
2963 + "sts": {
2964 + "oneOf": [
2965 + {
2966 + "$ref": "#/components/schemas/data_json2_sts"
2967 + },
2968 + {
2969 + "$ref": "#/components/schemas/data_json2_sts_raw"
2970 + }
2971 + ]
2972 + }
2973 + }
2974 + }
2975 + },
2976 + "contexts": {
2977 + "type": "array",
2978 + "items": {
2979 + "type": "object",
2980 + "description": "An object describing a unique context. `is` stands for instances, `ds` for dimensions, `al` for alerts, `sts` for statistics.\n",
2981 + "properties": {
2982 + "id": {
2983 + "description": "the context id.",
2984 + "type": "string"
2985 + },
2986 + "is": {
2987 + "$ref": "#/components/schemas/data_json2_items_count"
2988 + },
2989 + "ds": {
2990 + "$ref": "#/components/schemas/data_json2_items_count"
2991 + },
2992 + "al": {
2993 + "$ref": "#/components/schemas/data_json2_alerts_count"
2994 + },
2995 + "sts": {
2996 + "oneOf": [
2997 + {
2998 + "$ref": "#/components/schemas/data_json2_sts"
2999 + },
3000 + {
3001 + "$ref": "#/components/schemas/data_json2_sts_raw"
3002 + }
3003 + ]
3004 + }
3005 + }
3006 + }
3007 + },
3008 + "instances": {
3009 + "type": "array",
3010 + "items": {
3011 + "type": "object",
3012 + "description": "An object describing an instance. `ds` stands for dimensions, `al` for alerts, `sts` for statistics.\n",
3013 + "properties": {
3014 + "id": {
3015 + "description": "the id of the instance.",
3016 + "type": "string"
3017 + },
3018 + "nm": {
3019 + "description": "the name of the instance (may be absent when it is the same with the id)",
3020 + "type": "string"
3021 + },
3022 + "ni": {
3023 + "description": "the node index id this instance belongs to. The UI uses this to compone the fully qualified name of the instance, using the node hostname to present it to users and its machine guid to add it to filters."
3024 + },
3025 + "ds": {
3026 + "$ref": "#/components/schemas/data_json2_items_count"
3027 + },
3028 + "al": {
3029 + "$ref": "#/components/schemas/data_json2_alerts_count"
3030 + },
3031 + "sts": {
3032 + "oneOf": [
3033 + {
3034 + "$ref": "#/components/schemas/data_json2_sts"
3035 + },
3036 + {
3037 + "$ref": "#/components/schemas/data_json2_sts_raw"
3038 + }
3039 + ]
3040 + }
3041 + }
3042 + }
3043 + },
3044 + "dimensions": {
3045 + "type": "array",
3046 + "items": {
3047 + "type": "object",
3048 + "description": "An object describing a unique dimension. `ds` stands for `dimensions`, `sts` for statistics.\n",
3049 + "properties": {
3050 + "id": {
3051 + "description": "the id of the dimension.",
3052 + "type": "string"
3053 + },
3054 + "nm": {
3055 + "description": "the name of the dimension (may be absent when it is the same with the id)",
3056 + "type": "string"
3057 + },
3058 + "ds": {
3059 + "$ref": "#/components/schemas/data_json2_items_count"
3060 + },
3061 + "sts": {
3062 + "oneOf": [
3063 + {
3064 + "$ref": "#/components/schemas/data_json2_sts"
3065 + },
3066 + {
3067 + "$ref": "#/components/schemas/data_json2_sts_raw"
3068 + }
3069 + ]
3070 + }
3071 + }
3072 + }
3073 + },
3074 + "labels": {
3075 + "type": "array",
3076 + "items": {
3077 + "type": "object",
3078 + "description": "An object describing a label key. `ds` stands for `dimensions`, `sts` for statistics.\n",
3079 + "properties": {
3080 + "id": {
3081 + "description": "the key of the label.",
3082 + "type": "string"
3083 + },
3084 + "ds": {
3085 + "$ref": "#/components/schemas/data_json2_items_count"
3086 + },
3087 + "sts": {
3088 + "oneOf": [
3089 + {
3090 + "$ref": "#/components/schemas/data_json2_sts"
3091 + },
3092 + {
3093 + "$ref": "#/components/schemas/data_json2_sts_raw"
3094 + }
3095 + ]
3096 + },
3097 + "vl": {
3098 + "description": "An array of values for this key.\n",
3099 + "type": "array",
3100 + "items": {
3101 + "type": "object",
3102 + "properties": {
3103 + "id": {
3104 + "description": "The value string",
3105 + "type": "string"
3106 + },
3107 + "ds": {
3108 + "$ref": "#/components/schemas/data_json2_items_count"
3109 + },
3110 + "sts": {
3111 + "oneOf": [
3112 + {
3113 + "$ref": "#/components/schemas/data_json2_sts"
3114 + },
3115 + {
3116 + "$ref": "#/components/schemas/data_json2_sts_raw"
3117 + }
3118 + ]
3119 + }
3120 + }
3121 + }
3122 + }
3123 + }
3124 + }
3125 + },
3126 + "alerts": {
3127 + "description": "An array of all the unique alerts running, grouped by alert name (`nm` is available here)\n",
3128 + "type": "array",
3129 + "items": {
3130 + "$ref": "#/components/schemas/data_json2_alerts_count"
3131 + }
3132 + }
3133 + }
3134 + },
3135 + "totals": {
3136 + "type": "object",
3137 + "properties": {
3138 + "nodes": {
3139 + "$ref": "#/components/schemas/data_json2_items_count"
3140 + },
3141 + "contexts": {
3142 + "$ref": "#/components/schemas/data_json2_items_count"
3143 + },
3144 + "instances": {
3145 + "$ref": "#/components/schemas/data_json2_items_count"
3146 + },
3147 + "dimensions": {
3148 + "$ref": "#/components/schemas/data_json2_items_count"
3149 + },
3150 + "label_keys": {
3151 + "$ref": "#/components/schemas/data_json2_items_count"
3152 + },
3153 + "label_key_values": {
3154 + "$ref": "#/components/schemas/data_json2_items_count"
3155 + }
3156 + }
3157 + },
3158 + "functions": {
3159 + "type": "array",
3160 + "items": {
3161 + "type": "string"
3162 + }
3163 + },
3164 + "db": {
3165 + "type": "object",
3166 + "properties": {
3167 + "tiers": {
3168 + "description": "The number of tiers this server is using.\n",
3169 + "type": "integer"
3170 + },
3171 + "update_every": {
3172 + "description": "The minimum update every, in seconds, for all tiers and all metrics aggregated into this query.\n",
3173 + "type": "integer"
3174 + },
3175 + "first_entry": {
3176 + "description": "The minimum unix epoch timestamp of the retention across all tiers for all metrics aggregated into this query.\n",
3177 + "type": "integer"
3178 + },
3179 + "last_entry": {
3180 + "description": "The maximum unix epoch timestamp of the retention across all tier for all metrics aggregated into this query.\n",
3181 + "type": "integer"
3182 + },
3183 + "per_tier": {
3184 + "description": "An array with information for each of the tiers available, related to this query.\n",
3185 + "type": "array",
3186 + "items": {
3187 + "type": "object",
3188 + "properties": {
3189 + "tier": {
3190 + "description": "The tier number of this tier, starting at 0.\n",
3191 + "type": "integer"
3192 + },
3193 + "queries": {
3194 + "description": "The number of queries executed on this tier. Usually one query per metric is made, but the query may cross multiple tier, in which case more than one query per metric is made.\n",
3195 + "type": "integer"
3196 + },
3197 + "points": {
3198 + "description": "The number of points read from this tier.\n",
3199 + "type": "integer"
3200 + },
3201 + "update_every": {
3202 + "description": "The minimum resolution of all metrics queried on this tier.\n",
3203 + "type": "integer"
3204 + },
3205 + "first_entry": {
3206 + "description": "The minimum unix epoch timestamp available across all metrics that used this tier. This reflects the oldest timestamp of the tier's retention.\n",
3207 + "type": "integer"
3208 + },
3209 + "last_entry": {
3210 + "description": "The maximum unix epoch timestamp available across all metrics that used this tier. This reflects the newest timestamp of the tier's retention.\n"
3211 + }
3212 + }
3213 + }
3214 + }
3215 + }
3216 + },
3217 + "view": {
3218 + "type": "object",
3219 + "properties": {
3220 + "title": {
3221 + "description": "The title the chart should have.\n",
3222 + "type": "string"
3223 + },
3224 + "format": {
3225 + "description": "The format the `result` top level member has.\n",
3226 + "type": "string"
3227 + },
3228 + "options": {
3229 + "description": "An array presenting all the options given to the query.\n",
3230 + "type": "array",
3231 + "items": {
3232 + "type": "string"
3233 + }
3234 + },
3235 + "time_group": {
3236 + "description": "The same as the parameter `time_group`.\n",
3237 + "type": "string"
3238 + },
3239 + "after": {
3240 + "description": "The oldest unix epoch timestamp of the data returned in the `result`.\n",
3241 + "type": "integer"
3242 + },
3243 + "before": {
3244 + "description": "The newest unix epoch timestamp of the data returned in the `result`.\n",
3245 + "type": "integer"
3246 + },
3247 + "partial_data_trimming": {
3248 + "description": "Information related to trimming of the last few points of the `result`, that was required to remove (increasing) partial data.\nTrimming is disabled when the `raw` option is given to the query.\n",
3249 + "type": "object",
3250 + "properties": {
3251 + "max_update_every": {
3252 + "description": "The maximum `update_every` for all metrics aggregated into the query.\nTrimming is by default enabled at `view.before - max_update_every`, but only when `view.before >= now - max_update_every`.\n",
3253 + "type": "integer"
3254 + },
3255 + "expected_after": {
3256 + "description": "The timestamp at which trimming can be enabled.\nIf this timestamp is greater or equal to `view.before`, there is no trimming.\n",
3257 + "type": "integer"
3258 + },
3259 + "trimmed_after": {
3260 + "description": "The timestamp at which trimming has been applied.\nIf this timestamp is greater or equal to `view.before`, there is no trimming.\n",
3261 + "type": "integer"
3262 + }
3263 + }
3264 + },
3265 + "points": {
3266 + "description": "The number of points in `result`.\n",
3267 + "type": "integer"
3268 + },
3269 + "units": {
3270 + "description": "The units of the query.\n",
3271 + "oneOf": [
3272 + {
3273 + "type": "string"
3274 + },
3275 + {
3276 + "type": "array",
3277 + "items": {
3278 + "type": "string"
3279 + }
3280 + }
3281 + ]
3282 + },
3283 + "chart_type": {
3284 + "description": "The default chart type of the query.\n",
3285 + "type": "string",
3286 + "enum": [
3287 + "line",
3288 + "area",
3289 + "stacked"
3290 + ]
3291 + },
3292 + "dimensions": {
3293 + "description": "Detailed information about the chart dimensions included in the `result`.\n",
3294 + "type": "object",
3295 + "properties": {
3296 + "grouped_by": {
3297 + "description": "An array with the order of the groupings performed.\n",
3298 + "type": "array",
3299 + "items": {
3300 + "type": "string",
3301 + "enum": [
3302 + "selected",
3303 + "dimension",
3304 + "instance",
3305 + "node",
3306 + "context",
3307 + "units",
3308 + "label:key1",
3309 + "label:key2",
3310 + "label:keyN"
3311 + ]
3312 + }
3313 + },
3314 + "ids": {
3315 + "description": "An array with the dimension ids that uniquely identify the dimensions for this query.\n",
3316 + "type": "array",
3317 + "items": {
3318 + "type": "string"
3319 + }
3320 + },
3321 + "names": {
3322 + "description": "An array with the dimension names to be presented to users. Names may be overlapping, but IDs are not.\n",
3323 + "type": "array",
3324 + "items": {
3325 + "type": "string"
3326 + }
3327 + },
3328 + "units": {
3329 + "description": "An array with the units each dimension has.\n",
3330 + "type": "array",
3331 + "items": {
3332 + "type": "string"
3333 + }
3334 + },
3335 + "priorities": {
3336 + "description": "An array with the relative priorities of the dimensions.\nNumbers may not be sequential or unique. The application is expected to order by this and then by name.\n",
3337 + "type": "array",
3338 + "items": {
3339 + "type": "integer"
3340 + }
3341 + },
3342 + "aggregated": {
3343 + "description": "An array with the number of source metrics aggregated into each dimension.\n",
3344 + "type": "array",
3345 + "items": {
3346 + "type": "integer"
3347 + }
3348 + },
3349 + "view_average_values": {
3350 + "description": "An array of the average value of each dimension across the entire query.\n",
3351 + "type": "array",
3352 + "items": {
3353 + "type": "number"
3354 + }
3355 + },
3356 + "view_latest_values": {
3357 + "description": "An array of the latest value of each dimension, included in this query.\n",
3358 + "type": "array",
3359 + "items": {
3360 + "type": "number"
3361 + }
3362 + },
3363 + "count": {
3364 + "description": "The number of dimensions in the `result`.\n",
3365 + "type": "integer"
3366 + },
3367 + "labels": {
3368 + "description": "The labels associated with each dimension in the query.\nThis object is only available when the `group-by-labels` option is given to the query.\n",
3369 + "type": "object",
3370 + "properties": {
3371 + "label_key1": {
3372 + "description": "An array having one entry for each of the dimensions of the query.\n",
3373 + "type": "array",
3374 + "items": {
3375 + "description": "An array having one entry for each of the values this label key has for the given dimension.\n",
3376 + "type": "array",
3377 + "items": {
3378 + "type": "string"
3379 + }
3380 + }
3381 + }
3382 + }
3383 + }
3384 + }
3385 + },
3386 + "min": {
3387 + "description": "The minimum value of all points included in the `result`.\n",
3388 + "type": "number"
3389 + },
3390 + "max": {
3391 + "description": "The maximum value of all points included in the `result`.\n",
3392 + "type": "number"
3393 + }
3394 + }
3395 + },
3396 + "result": {
3397 + "description": "The result of the query.\nThe format explained here is `json2`.\n",
3398 + "type": "object",
3399 + "properties": {
3400 + "labels": {
3401 + "description": "The IDs of the dimensions returned. The first is always `time`.\n",
3402 + "type": "array",
3403 + "items": {
3404 + "type": "string"
3405 + }
3406 + },
3407 + "point": {
3408 + "description": "The format of each point returned.\n",
3409 + "type": "object",
3410 + "properties": {
3411 + "value": {
3412 + "description": "The index of the value in each point.\n",
3413 + "type": "integer"
3414 + },
3415 + "ar": {
3416 + "description": "The index of the anomaly rate in each point.\n",
3417 + "type": "integer"
3418 + },
3419 + "pa": {
3420 + "description": "The index of the point annotations in each point.\nThis is a bitmap. `EMPTY = 1`, `RESET = 2`, `PARTIAL = 4`.\n`EMPTY` means the point has no value.\n`RESET` means that at least one metric aggregated experienced an overflow (a counter that wrapped).\n`PARTIAL` means that this point should have more metrics aggregated into it, but not all metrics had data.\n",
3421 + "type": "integer"
3422 + },
3423 + "count": {
3424 + "description": "The number of metrics aggregated into this point. This exists only when the option `raw` is given to the query.\n",
3425 + "type": "integer"
3426 + }
3427 + }
3428 + },
3429 + "data": {
3430 + "type": "array",
3431 + "items": {
3432 + "allOf": [
3433 + {
3434 + "type": "integer"
3435 + },
3436 + {
3437 + "type": "array"
3438 + }
3439 + ]
3440 + }
3441 + }
3442 + }
3443 + },
3444 + "timings": {
3445 + "type": "object"
3446 }
3447 }
3448 },
2273 - "context_summary": {
3449 + "data_json2_sts": {
3450 + "description": "Statistical values\n",
3451 "type": "object",
3452 "properties": {
2276 - "hostname": {
2277 - "type": "string",
2278 - "description": "The hostname of the netdata server."
2279 - },
2280 - "machine_guid": {
2281 - "type": "string",
2282 - "description": "The unique installation id of this netdata server."
2283 - },
2284 - "node_id": {
2285 - "type": "string",
2286 - "description": "The unique node id of this netdata server at the hub.",
2287 - "example": "nightly"
2288 - },
2289 - "claim_id": {
2290 - "type": "string",
2291 - "description": "The unique handshake id of this netdata server and the hub."
3453 + "avg": {
3454 + "description": "The average value of all metrics aggregated",
3455 + "type": "number"
3456 },
2293 - "host_labels": {
2294 - "type": "object",
2295 - "description": "The host labels associated with this netdata server."
3457 + "arp": {
3458 + "description": "The average anomaly rate of all metrics aggregated",
3459 + "type": "number"
3460 },
2297 - "context": {
2298 - "type": "object",
2299 - "description": "An object containing all the context objects available at the netdata server. This is used as an indexed array. The key of each context object is the id of the context.",
2300 - "additionalProperties": {
2301 - "$ref": "#/components/schemas/context"
2302 - }
3461 + "con": {
3462 + "description": "The contribution percentage of all the metrics aggregated",
3463 + "type": "number"
3464 }
3465 }
3466 },
2306 - "context": {
3467 + "data_json2_sts_raw": {
3468 + "description": "Statistical values when `raw` option is given.\n",
3469 "type": "object",
3470 "properties": {
2309 - "version": {
2310 - "type": "string",
2311 - "description": "The version of this context. The number are not sequential, but bigger numbers depict a newer object."
2312 - },
2313 - "hub_version": {
2314 - "type": "string",
2315 - "description": "The version of this context, as known by hub."
2316 - },
2317 - "family": {
2318 - "type": "string",
2319 - "description": "The family of the context. When multiple charts of a context have different families, the netdata server replaces the different parts with [x], so that the context can have only one family."
2320 - },
2321 - "title": {
2322 - "type": "string",
2323 - "description": "The title of the context. When multiple charts of a context have different titles, the netdata server replaces the different parts with [x], so that the context can have only one title."
2324 - },
2325 - "priority": {
2326 - "type": "number",
2327 - "description": "The relative priority of the context. When multiple contexts have different priorities, the minimum among them is selected as the priority of the context."
2328 - },
2329 - "units": {
2330 - "type": "string",
2331 - "description": "The unit of measurement for the values of all dimensions of the context. If multiple charts of context have different units, the latest collected is selected."
2332 - },
2333 - "chart_type": {
2334 - "type": "string",
2335 - "description": "The chart type.",
2336 - "enum": [
2337 - "line",
2338 - "area",
2339 - "stacked"
2340 - ]
3471 + "sum": {
3472 + "description": "The sum value of all metrics aggregated",
3473 + "type": "number"
3474 },
2342 - "first_time_t": {
2343 - "type": "number",
2344 - "description": "The UNIX timestamp of the first entry (the oldest) in the database."
3475 + "ars": {
3476 + "description": "The sum anomaly rate of all metrics aggregated",
3477 + "type": "number"
3478 },
2346 - "last_time_t": {
2347 - "type": "number",
2348 - "description": "The UNIX timestamp of the latest entry in the database."
3479 + "vol": {
3480 + "description": "The volume of all the metrics aggregated",
3481 + "type": "number"
3482 },
2350 - "charts": {
2351 - "type": "object",
2352 - "description": "An object containing all the charts available for the chart. This is used as an indexed array. For each pair in the dictionary, the key is the id of the chart and the value provides all details about the chart."
3483 + "cnt": {
3484 + "description": "The count of all metrics aggregated",
3485 + "type": "integer"
3486 }
3487 }
3488 },
2356 - "alarm_variables": {
3489 + "data_json2_items_count": {
3490 + "description": "Depending on the placement of this object, `items` may be `nodes`, `contexts`, `instances`, `dimensions`, `label keys`, `label key-value pairs`. Furthermore, if the whole object is missing it should be assumed that all its members are zero.\n",
3491 "type": "object",
3492 "properties": {
2359 - "chart": {
2360 - "type": "string",
2361 - "description": "The unique id of the chart."
2362 - },
2363 - "chart_name": {
2364 - "type": "string",
2365 - "description": "The name of the chart."
2366 - },
2367 - "cnart_context": {
2368 - "type": "string",
2369 - "description": "The context of the chart. It is shared across multiple monitored software or hardware instances and used in alarm templates."
2370 - },
2371 - "family": {
2372 - "type": "string",
2373 - "description": "The family of the chart."
2374 - },
2375 - "host": {
2376 - "type": "string",
2377 - "description": "The host containing the chart."
2378 - },
2379 - "chart_variables": {
2380 - "type": "object",
2381 - "additionalProperties": {
2382 - "$ref": "#/components/schemas/chart_variables"
2383 - }
3493 + "sl": {
3494 + "description": "The number of items `selected` to query. If absent it is zero.",
3495 + "type": "integer"
3496 },
2385 - "family_variables": {
2386 - "type": "object",
2387 - "properties": {
2388 - "varname1": {
2389 - "type": "number",
2390 - "format": "float"
2391 - },
2392 - "varname2": {
2393 - "type": "number",
2394 - "format": "float"
2395 - }
2396 - }
3497 + "ex": {
3498 + "description": "The number of items `excluded` from querying. If absent it is zero.",
3499 + "type": "integer"
3500 },
2398 - "host_variables": {
2399 - "type": "object",
2400 - "properties": {
2401 - "varname1": {
2402 - "type": "number",
2403 - "format": "float"
2404 - },
2405 - "varname2": {
2406 - "type": "number",
2407 - "format": "float"
2408 - }
2409 - }
2410 - }
2411 - }
2412 - },
2413 - "chart_variables": {
2414 - "type": "object",
2415 - "properties": {
2416 - "varname1": {
2417 - "type": "number",
2418 - "format": "float"
3501 + "qr": {
3502 + "description": "The number of items (out of `selected`) the query successfully `queried`. If absent it is zero.",
3503 + "type": "integer"
3504 },
2420 - "varname2": {
2421 - "type": "number",
2422 - "format": "float"
3505 + "fl": {
3506 + "description": "The number of items (from `selected`) that `failed` to be queried. If absent it is zero.",
3507 + "type": "integer"
3508 }
3509 }
3510 },
2426 - "data": {
3511 + "data_json2_alerts_count": {
3512 + "description": "Counters about alert statuses. If this object is missing, it is assumed that all its members are zero.\n",
3513 "type": "object",
2428 - "discriminator": {
2429 - "propertyName": "format"
2430 - },
2431 - "description": "Response will contain the appropriate subtype, e.g. data_json depending on the requested format.",
3514 "properties": {
2433 - "api": {
2434 - "type": "number",
2435 - "description": "The API version this conforms to, currently 1."
2436 - },
2437 - "id": {
2438 - "type": "string",
2439 - "description": "The unique id of the chart."
2440 - },
2441 - "name": {
2442 - "type": "string",
2443 - "description": "The name of the chart."
2444 - },
2445 - "update_every": {
2446 - "type": "number",
2447 - "description": "The update frequency of this chart, in seconds. One value every this amount of time is kept in the round robin database (independently of the current view)."
2448 - },
2449 - "view_update_every": {
2450 - "type": "number",
2451 - "description": "The current view appropriate update frequency of this chart, in seconds. There is no point to request chart refreshes, using the same settings, more frequently than this."
2452 - },
2453 - "first_entry": {
2454 - "type": "number",
2455 - "description": "The UNIX timestamp of the first entry (the oldest) in the round robin database (independently of the current view)."
2456 - },
2457 - "last_entry": {
2458 - "type": "number",
2459 - "description": "The UNIX timestamp of the latest entry in the round robin database (independently of the current view)."
2460 - },
2461 - "after": {
2462 - "type": "number",
2463 - "description": "The UNIX timestamp of the first entry (the oldest) returned in this response."
2464 - },
2465 - "before": {
2466 - "type": "number",
2467 - "description": "The UNIX timestamp of the latest entry returned in this response."
2468 - },
2469 - "min": {
2470 - "type": "number",
2471 - "description": "The minimum value returned in the current view. This can be used to size the y-series of the chart."
2472 - },
2473 - "max": {
2474 - "type": "number",
2475 - "description": "The maximum value returned in the current view. This can be used to size the y-series of the chart."
2476 - },
2477 - "dimension_names": {
2478 - "description": "The dimension names of the chart as returned in the current view.",
2479 - "type": "array",
2480 - "items": {
2481 - "type": "string"
2482 - }
2483 - },
2484 - "dimension_ids": {
2485 - "description": "The dimension IDs of the chart as returned in the current view.",
2486 - "type": "array",
2487 - "items": {
2488 - "type": "string"
2489 - }
2490 - },
2491 - "latest_values": {
2492 - "description": "The latest values collected for the chart (independently of the current view).",
2493 - "type": "array",
2494 - "items": {
2495 - "type": "string"
2496 - }
2497 - },
2498 - "view_latest_values": {
2499 - "description": "The latest values returned with this response.",
2500 - "type": "array",
2501 - "items": {
2502 - "type": "string"
2503 - }
3515 + "nm": {
3516 + "description": "The name of the alert. Can be absent when the counters refer to more than one alert instances.",
3517 + "type": "string"
3518 },
2505 - "dimensions": {
2506 - "type": "number",
2507 - "description": "The number of dimensions returned."
3519 + "cl": {
3520 + "description": "The number of CLEAR alerts. If absent, it is zero.",
3521 + "type": "integer"
3522 },
2509 - "points": {
2510 - "type": "number",
2511 - "description": "The number of rows / points returned."
3523 + "wr": {
3524 + "description": "The number of WARNING alerts. If absent, it is zero.",
3525 + "type": "integer"
3526 },
2513 - "format": {
2514 - "type": "string",
2515 - "description": "The format of the result returned."
3527 + "cr": {
3528 + "description": "The number of CRITICAL alerts. If absent, it is zero.",
3529 + "type": "integer"
3530 },
2517 - "chart_variables": {
2518 - "type": "object",
2519 - "additionalProperties": {
2520 - "$ref": "#/components/schemas/chart_variables"
2521 - }
3531 + "ot": {
3532 + "description": "The number of alerts that are not CLEAR, WARNING, CRITICAL (so, they are \"other\"). If absent, it is zero.\n",
3533 + "type": "integer"
3534 }
3535 }
3536 },
@@ -2541,7 +3553,7 @@
3553 }
3554 },
3555 "data": {
2544 - "description": "The data requested, one element per sample with each element containing the values of the dimensions described in the labels value.",
3556 + "description": "The data requested, one element per sample with each element containing the values of the dimensions described in the labels value.\n",
3557 "type": "array",
3558 "items": {
3559 "type": "number"
web/api/netdata-swagger.yaml
+263 -18
@@ -357,7 +357,9 @@ paths:
357 - name: group_by
358 in: query
359 description: |
360 - A comma separated list of `dimension`, `label`, `instance`, `node`, `selected`. All possible values can be combined together, except `selected`. If `selected` is given in the list, all others are ignored. The order they are placed in the list is currently ignored. The result will always have in the order given here.
360 + A comma separated list of the groupings required.
361 + All possible values can be combined together, except `selected`. If `selected` is given in the list, all others are ignored.
362 + The order they are placed in the list is currently ignored.
363 required: false
364 schema:
365 type: array
@@ -365,9 +367,11 @@ paths:
367 type: string
368 enum:
369 - dimension
368 - - label
370 - instance
371 + - label
372 - node
373 + - context
374 + - units
375 - selected
376 default:
377 - dimension
@@ -491,11 +495,12 @@ paths:
495 - markdown
496 - array
497 - csvjsonarray
494 - default: json
498 + default: json2
499 - name: options
500 in: query
501 description: |
502 Options that affect data generation.
503 + `raw` changes the output so that the values can be aggregated across multiple such queries.
504 required: false
505 allowEmptyValue: false
506 schema:
@@ -503,24 +508,21 @@ paths:
508 items:
509 type: string
510 enum:
511 + - raw
512 - nonzero
513 - flip
508 - - jsonwrap
514 - min2max
515 - seconds
516 - milliseconds
517 - abs
518 - absolute
514 - - absolute-sum
519 - null2zero
516 - - objectrows
517 - - google_json
520 - percentage
521 - unaligned
522 - match-ids
523 - match-names
522 - - allow_past
524 - anomaly-bit
525 + - group-by-labels
526 default:
527 - seconds
528 - jsonwrap
@@ -2544,12 +2546,12 @@ components:
2546 id:
2547 description: The value string
2548 type: string
2547 - ds:
2548 - $ref: "#/components/schemas/data_json2_items_count"
2549 - sts:
2550 - oneOf:
2551 - - $ref: "#/components/schemas/data_json2_sts"
2552 - - $ref: "#/components/schemas/data_json2_sts_raw"
2549 + ds:
2550 + $ref: "#/components/schemas/data_json2_items_count"
2551 + sts:
2552 + oneOf:
2553 + - $ref: "#/components/schemas/data_json2_sts"
2554 + - $ref: "#/components/schemas/data_json2_sts_raw"
2555 alerts:
2556 description: |
2557 An array of all the unique alerts running, grouped by alert name (`nm` is available here)
@@ -2577,14 +2579,257 @@ components:
2579 type: string
2580 db:
2581 type: object
2582 + properties:
2583 + tiers:
2584 + description: |
2585 + The number of tiers this server is using.
2586 + type: integer
2587 + update_every:
2588 + description: |
2589 + The minimum update every, in seconds, for all tiers and all metrics aggregated into this query.
2590 + type: integer
2591 + first_entry:
2592 + description: |
2593 + The minimum unix epoch timestamp of the retention across all tiers for all metrics aggregated into this query.
2594 + type: integer
2595 + last_entry:
2596 + description: |
2597 + The maximum unix epoch timestamp of the retention across all tier for all metrics aggregated into this query.
2598 + type: integer
2599 + per_tier:
2600 + description: |
2601 + An array with information for each of the tiers available, related to this query.
2602 + type: array
2603 + items:
2604 + type: object
2605 + properties:
2606 + tier:
2607 + description: |
2608 + The tier number of this tier, starting at 0.
2609 + type: integer
2610 + queries:
2611 + description: |
2612 + The number of queries executed on this tier. Usually one query per metric is made, but the query may cross multiple tier, in which case more than one query per metric is made.
2613 + type: integer
2614 + points:
2615 + description: |
2616 + The number of points read from this tier.
2617 + type: integer
2618 + update_every:
2619 + description: |
2620 + The minimum resolution of all metrics queried on this tier.
2621 + type: integer
2622 + first_entry:
2623 + description: |
2624 + The minimum unix epoch timestamp available across all metrics that used this tier. This reflects the oldest timestamp of the tier's retention.
2625 + type: integer
2626 + last_entry:
2627 + description: |
2628 + The maximum unix epoch timestamp available across all metrics that used this tier. This reflects the newest timestamp of the tier's retention.
2629 view:
2630 type: object
2631 + properties:
2632 + title:
2633 + description: |
2634 + The title the chart should have.
2635 + type: string
2636 + format:
2637 + description: |
2638 + The format the `result` top level member has.
2639 + type: string
2640 + options:
2641 + description: |
2642 + An array presenting all the options given to the query.
2643 + type: array
2644 + items:
2645 + type: string
2646 + time_group:
2647 + description: |
2648 + The same as the parameter `time_group`.
2649 + type: string
2650 + after:
2651 + description: |
2652 + The oldest unix epoch timestamp of the data returned in the `result`.
2653 + type: integer
2654 + before:
2655 + description: |
2656 + The newest unix epoch timestamp of the data returned in the `result`.
2657 + type: integer
2658 + partial_data_trimming:
2659 + description: |
2660 + Information related to trimming of the last few points of the `result`, that was required to remove (increasing) partial data.
2661 + Trimming is disabled when the `raw` option is given to the query.
2662 + type: object
2663 + properties:
2664 + max_update_every:
2665 + description: |
2666 + The maximum `update_every` for all metrics aggregated into the query.
2667 + Trimming is by default enabled at `view.before - max_update_every`, but only when `view.before >= now - max_update_every`.
2668 + type: integer
2669 + expected_after:
2670 + description: |
2671 + The timestamp at which trimming can be enabled.
2672 + If this timestamp is greater or equal to `view.before`, there is no trimming.
2673 + type: integer
2674 + trimmed_after:
2675 + description: |
2676 + The timestamp at which trimming has been applied.
2677 + If this timestamp is greater or equal to `view.before`, there is no trimming.
2678 + points:
2679 + description: |
2680 + The number of points in `result`.
2681 + type: integer
2682 + units:
2683 + description: |
2684 + The units of the query.
2685 + oneOf:
2686 + - type: string
2687 + - type: array
2688 + items:
2689 + type: string
2690 + chart_type:
2691 + description: |
2692 + The default chart type of the query.
2693 + type: string
2694 + enum:
2695 + - line
2696 + - area
2697 + - stacked
2698 + dimensions:
2699 + description: |
2700 + Detailed information about the chart dimensions included in the `result`.
2701 + type: object
2702 + properties:
2703 + grouped_by:
2704 + description: |
2705 + An array with the order of the groupings performed.
2706 + type: array
2707 + items:
2708 + type: string
2709 + enum:
2710 + - selected
2711 + - dimension
2712 + - instance
2713 + - node
2714 + - context
2715 + - units
2716 + - "label:key1"
2717 + - "label:key2"
2718 + - "label:keyN"
2719 + ids:
2720 + description: |
2721 + An array with the dimension ids that uniquely identify the dimensions for this query.
2722 + type: array
2723 + items:
2724 + type: string
2725 + names:
2726 + description: |
2727 + An array with the dimension names to be presented to users. Names may be overlapping, but IDs are not.
2728 + type: array
2729 + items:
2730 + type: string
2731 + units:
2732 + description: |
2733 + An array with the units each dimension has.
2734 + type: array
2735 + items:
2736 + type: string
2737 + priorities:
2738 + description: |
2739 + An array with the relative priorities of the dimensions.
2740 + Numbers may not be sequential or unique. The application is expected to order by this and then by name.
2741 + type: array
2742 + items:
2743 + type: integer
2744 + aggregated:
2745 + description: |
2746 + An array with the number of source metrics aggregated into each dimension.
2747 + type: array
2748 + items:
2749 + type: integer
2750 + view_average_values:
2751 + description: |
2752 + An array of the average value of each dimension across the entire query.
2753 + type: array
2754 + items:
2755 + type: number
2756 + view_latest_values:
2757 + description: |
2758 + An array of the latest value of each dimension, included in this query.
2759 + type: array
2760 + items:
2761 + type: number
2762 + count:
2763 + description: |
2764 + The number of dimensions in the `result`.
2765 + type: integer
2766 + labels:
2767 + description: |
2768 + The labels associated with each dimension in the query.
2769 + This object is only available when the `group-by-labels` option is given to the query.
2770 + type: object
2771 + properties:
2772 + label_key1:
2773 + description: |
2774 + An array having one entry for each of the dimensions of the query.
2775 + type: array
2776 + items:
2777 + description: |
2778 + An array having one entry for each of the values this label key has for the given dimension.
2779 + type: array
2780 + items:
2781 + type: string
2782 + min:
2783 + description: |
2784 + The minimum value of all points included in the `result`.
2785 + type: number
2786 + max:
2787 + description: |
2788 + The maximum value of all points included in the `result`.
2789 + type: number
2790 result:
2791 + description: |
2792 + The result of the query.
2793 + The format explained here is `json2`.
2794 type: object
2584 - min:
2585 - type: number
2586 - max:
2587 - type: number
2795 + properties:
2796 + labels:
2797 + description: |
2798 + The IDs of the dimensions returned. The first is always `time`.
2799 + type: array
2800 + items:
2801 + type: string
2802 + point:
2803 + description: |
2804 + The format of each point returned.
2805 + type: object
2806 + properties:
2807 + value:
2808 + description: |
2809 + The index of the value in each point.
2810 + type: integer
2811 + ar:
2812 + description: |
2813 + The index of the anomaly rate in each point.
2814 + type: integer
2815 + pa:
2816 + description: |
2817 + The index of the point annotations in each point.
2818 + This is a bitmap. `EMPTY = 1`, `RESET = 2`, `PARTIAL = 4`.
2819 + `EMPTY` means the point has no value.
2820 + `RESET` means that at least one metric aggregated experienced an overflow (a counter that wrapped).
2821 + `PARTIAL` means that this point should have more metrics aggregated into it, but not all metrics had data.
2822 + type: integer
2823 + count:
2824 + description: |
2825 + The number of metrics aggregated into this point. This exists only when the option `raw` is given to the query.
2826 + type: integer
2827 + data:
2828 + type: array
2829 + items:
2830 + allOf:
2831 + - type: integer
2832 + - type: array
2833 timings:
2834 type: object
2835 data_json2_sts:
web/api/queries/average/average.c
+8 -8
@@ -11,30 +11,30 @@ struct grouping_average {
11 };
12
13 void grouping_create_average(RRDR *r, const char *options __maybe_unused) {
14 - r->grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_average));
14 + r->time_grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_average));
15 }
16
17 // resets when switches dimensions
18 // so, clear everything to restart
19 void grouping_reset_average(RRDR *r) {
20 - struct grouping_average *g = (struct grouping_average *)r->grouping.data;
20 + struct grouping_average *g = (struct grouping_average *)r->time_grouping.data;
21 g->sum = 0;
22 g->count = 0;
23 }
24
25 void grouping_free_average(RRDR *r) {
26 - onewayalloc_freez(r->internal.owa, r->grouping.data);
27 - r->grouping.data = NULL;
26 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
27 + r->time_grouping.data = NULL;
28 }
29
30 void grouping_add_average(RRDR *r, NETDATA_DOUBLE value) {
31 - struct grouping_average *g = (struct grouping_average *)r->grouping.data;
31 + struct grouping_average *g = (struct grouping_average *)r->time_grouping.data;
32 g->sum += value;
33 g->count++;
34 }
35
36 NETDATA_DOUBLE grouping_flush_average(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
37 - struct grouping_average *g = (struct grouping_average *)r->grouping.data;
37 + struct grouping_average *g = (struct grouping_average *)r->time_grouping.data;
38
39 NETDATA_DOUBLE value;
40
@@ -43,8 +43,8 @@ NETDATA_DOUBLE grouping_flush_average(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_opt
43 *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
44 }
45 else {
46 - if(unlikely(r->grouping.resampling_group != 1))
47 - value = g->sum / r->grouping.resampling_divisor;
46 + if(unlikely(r->time_grouping.resampling_group != 1))
47 + value = g->sum / r->time_grouping.resampling_divisor;
48 else
49 value = g->sum / g->count;
50 }
web/api/queries/countif/countif.c
+6 -6
@@ -38,7 +38,7 @@ static size_t countif_greaterequal(NETDATA_DOUBLE v, NETDATA_DOUBLE target) {
38
39 void grouping_create_countif(RRDR *r, const char *options __maybe_unused) {
40 struct grouping_countif *g = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_countif));
41 - r->grouping.data = g;
41 + r->time_grouping.data = g;
42
43 if(options && *options) {
44 // skip any leading spaces
@@ -100,24 +100,24 @@ void grouping_create_countif(RRDR *r, const char *options __maybe_unused) {
100 // resets when switches dimensions
101 // so, clear everything to restart
102 void grouping_reset_countif(RRDR *r) {
103 - struct grouping_countif *g = (struct grouping_countif *)r->grouping.data;
103 + struct grouping_countif *g = (struct grouping_countif *)r->time_grouping.data;
104 g->matched = 0;
105 g->count = 0;
106 }
107
108 void grouping_free_countif(RRDR *r) {
109 - onewayalloc_freez(r->internal.owa, r->grouping.data);
110 - r->grouping.data = NULL;
109 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
110 + r->time_grouping.data = NULL;
111 }
112
113 void grouping_add_countif(RRDR *r, NETDATA_DOUBLE value) {
114 - struct grouping_countif *g = (struct grouping_countif *)r->grouping.data;
114 + struct grouping_countif *g = (struct grouping_countif *)r->time_grouping.data;
115 g->matched += g->comparison(value, g->target);
116 g->count++;
117 }
118
119 NETDATA_DOUBLE grouping_flush_countif(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
120 - struct grouping_countif *g = (struct grouping_countif *)r->grouping.data;
120 + struct grouping_countif *g = (struct grouping_countif *)r->time_grouping.data;
121
122 NETDATA_DOUBLE value;
123
web/api/queries/des/des.c
+7 -7
@@ -37,7 +37,7 @@ static inline NETDATA_DOUBLE window(RRDR *r, struct grouping_des *g) {
37 NETDATA_DOUBLE points;
38 if(r->view.group == 1) {
39 // provide a running DES
40 - points = (NETDATA_DOUBLE)r->grouping.points_wanted;
40 + points = (NETDATA_DOUBLE)r->time_grouping.points_wanted;
41 }
42 else {
43 // provide a SES with flush points
@@ -76,13 +76,13 @@ void grouping_create_des(RRDR *r, const char *options __maybe_unused) {
76 g->level = 0.0;
77 g->trend = 0.0;
78 g->count = 0;
79 - r->grouping.data = g;
79 + r->time_grouping.data = g;
80 }
81
82 // resets when switches dimensions
83 // so, clear everything to restart
84 void grouping_reset_des(RRDR *r) {
85 - struct grouping_des *g = (struct grouping_des *)r->grouping.data;
85 + struct grouping_des *g = (struct grouping_des *)r->time_grouping.data;
86 g->level = 0.0;
87 g->trend = 0.0;
88 g->count = 0;
@@ -92,12 +92,12 @@ void grouping_reset_des(RRDR *r) {
92 }
93
94 void grouping_free_des(RRDR *r) {
95 - onewayalloc_freez(r->internal.owa, r->grouping.data);
96 - r->grouping.data = NULL;
95 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
96 + r->time_grouping.data = NULL;
97 }
98
99 void grouping_add_des(RRDR *r, NETDATA_DOUBLE value) {
100 - struct grouping_des *g = (struct grouping_des *)r->grouping.data;
100 + struct grouping_des *g = (struct grouping_des *)r->time_grouping.data;
101
102 if(likely(g->count > 0)) {
103 // we have at least a number so far
@@ -124,7 +124,7 @@ void grouping_add_des(RRDR *r, NETDATA_DOUBLE value) {
124 }
125
126 NETDATA_DOUBLE grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
127 - struct grouping_des *g = (struct grouping_des *)r->grouping.data;
127 + struct grouping_des *g = (struct grouping_des *)r->time_grouping.data;
128
129 if(unlikely(!g->count || !netdata_double_isnumber(g->level))) {
130 *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
web/api/queries/incremental_sum/incremental_sum.c
+6 -6
@@ -12,25 +12,25 @@ struct grouping_incremental_sum {
12 };
13
14 void grouping_create_incremental_sum(RRDR *r, const char *options __maybe_unused) {
15 - r->grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_incremental_sum));
15 + r->time_grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_incremental_sum));
16 }
17
18 // resets when switches dimensions
19 // so, clear everything to restart
20 void grouping_reset_incremental_sum(RRDR *r) {
21 - struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->grouping.data;
21 + struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->time_grouping.data;
22 g->first = 0;
23 g->last = 0;
24 g->count = 0;
25 }
26
27 void grouping_free_incremental_sum(RRDR *r) {
28 - onewayalloc_freez(r->internal.owa, r->grouping.data);
29 - r->grouping.data = NULL;
28 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
29 + r->time_grouping.data = NULL;
30 }
31
32 void grouping_add_incremental_sum(RRDR *r, NETDATA_DOUBLE value) {
33 - struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->grouping.data;
33 + struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->time_grouping.data;
34
35 if(unlikely(!g->count)) {
36 g->first = value;
@@ -43,7 +43,7 @@ void grouping_add_incremental_sum(RRDR *r, NETDATA_DOUBLE value) {
43 }
44
45 NETDATA_DOUBLE grouping_flush_incremental_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
46 - struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->grouping.data;
46 + struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->time_grouping.data;
47
48 NETDATA_DOUBLE value;
49
web/api/queries/max/max.c
+6 -6
@@ -11,24 +11,24 @@ struct grouping_max {
11 };
12
13 void grouping_create_max(RRDR *r, const char *options __maybe_unused) {
14 - r->grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_max));
14 + r->time_grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_max));
15 }
16
17 // resets when switches dimensions
18 // so, clear everything to restart
19 void grouping_reset_max(RRDR *r) {
20 - struct grouping_max *g = (struct grouping_max *)r->grouping.data;
20 + struct grouping_max *g = (struct grouping_max *)r->time_grouping.data;
21 g->max = 0;
22 g->count = 0;
23 }
24
25 void grouping_free_max(RRDR *r) {
26 - onewayalloc_freez(r->internal.owa, r->grouping.data);
27 - r->grouping.data = NULL;
26 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
27 + r->time_grouping.data = NULL;
28 }
29
30 void grouping_add_max(RRDR *r, NETDATA_DOUBLE value) {
31 - struct grouping_max *g = (struct grouping_max *)r->grouping.data;
31 + struct grouping_max *g = (struct grouping_max *)r->time_grouping.data;
32
33 if(!g->count || fabsndd(value) > fabsndd(g->max)) {
34 g->max = value;
@@ -37,7 +37,7 @@ void grouping_add_max(RRDR *r, NETDATA_DOUBLE value) {
37 }
38
39 NETDATA_DOUBLE grouping_flush_max(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
40 - struct grouping_max *g = (struct grouping_max *)r->grouping.data;
40 + struct grouping_max *g = (struct grouping_max *)r->time_grouping.data;
41
42 NETDATA_DOUBLE value;
43
web/api/queries/median/median.c
+7 -7
@@ -30,7 +30,7 @@ void grouping_create_median_internal(RRDR *r, const char *options, NETDATA_DOUBL
30 }
31
32 g->percent = g->percent / 100.0;
33 - r->grouping.data = g;
33 + r->time_grouping.data = g;
34 }
35
36 void grouping_create_median(RRDR *r, const char *options) {
@@ -64,20 +64,20 @@ void grouping_create_trimmed_median25(RRDR *r, const char *options) {
64 // resets when switches dimensions
65 // so, clear everything to restart
66 void grouping_reset_median(RRDR *r) {
67 - struct grouping_median *g = (struct grouping_median *)r->grouping.data;
67 + struct grouping_median *g = (struct grouping_median *)r->time_grouping.data;
68 g->next_pos = 0;
69 }
70
71 void grouping_free_median(RRDR *r) {
72 - struct grouping_median *g = (struct grouping_median *)r->grouping.data;
72 + struct grouping_median *g = (struct grouping_median *)r->time_grouping.data;
73 if(g) onewayalloc_freez(r->internal.owa, g->series);
74
75 - onewayalloc_freez(r->internal.owa, r->grouping.data);
76 - r->grouping.data = NULL;
75 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
76 + r->time_grouping.data = NULL;
77 }
78
79 void grouping_add_median(RRDR *r, NETDATA_DOUBLE value) {
80 - struct grouping_median *g = (struct grouping_median *)r->grouping.data;
80 + struct grouping_median *g = (struct grouping_median *)r->time_grouping.data;
81
82 if(unlikely(g->next_pos >= g->series_size)) {
83 g->series = onewayalloc_doublesize( r->internal.owa, g->series, g->series_size * sizeof(NETDATA_DOUBLE));
@@ -88,7 +88,7 @@ void grouping_add_median(RRDR *r, NETDATA_DOUBLE value) {
88 }
89
90 NETDATA_DOUBLE grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
91 - struct grouping_median *g = (struct grouping_median *)r->grouping.data;
91 + struct grouping_median *g = (struct grouping_median *)r->time_grouping.data;
92
93 size_t available_slots = g->next_pos;
94 NETDATA_DOUBLE value;
web/api/queries/min/min.c
+6 -6
@@ -11,24 +11,24 @@ struct grouping_min {
11 };
12
13 void grouping_create_min(RRDR *r, const char *options __maybe_unused) {
14 - r->grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_min));
14 + r->time_grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_min));
15 }
16
17 // resets when switches dimensions
18 // so, clear everything to restart
19 void grouping_reset_min(RRDR *r) {
20 - struct grouping_min *g = (struct grouping_min *)r->grouping.data;
20 + struct grouping_min *g = (struct grouping_min *)r->time_grouping.data;
21 g->min = 0;
22 g->count = 0;
23 }
24
25 void grouping_free_min(RRDR *r) {
26 - onewayalloc_freez(r->internal.owa, r->grouping.data);
27 - r->grouping.data = NULL;
26 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
27 + r->time_grouping.data = NULL;
28 }
29
30 void grouping_add_min(RRDR *r, NETDATA_DOUBLE value) {
31 - struct grouping_min *g = (struct grouping_min *)r->grouping.data;
31 + struct grouping_min *g = (struct grouping_min *)r->time_grouping.data;
32
33 if(!g->count || fabsndd(value) < fabsndd(g->min)) {
34 g->min = value;
@@ -37,7 +37,7 @@ void grouping_add_min(RRDR *r, NETDATA_DOUBLE value) {
37 }
38
39 NETDATA_DOUBLE grouping_flush_min(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
40 - struct grouping_min *g = (struct grouping_min *)r->grouping.data;
40 + struct grouping_min *g = (struct grouping_min *)r->time_grouping.data;
41
42 NETDATA_DOUBLE value;
43
web/api/queries/percentile/percentile.c
+7 -7
@@ -30,7 +30,7 @@ static void grouping_create_percentile_internal(RRDR *r, const char *options, NE
30 }
31
32 g->percent = g->percent / 100.0;
33 - r->grouping.data = g;
33 + r->time_grouping.data = g;
34 }
35
36 void grouping_create_percentile25(RRDR *r, const char *options) {
@@ -64,20 +64,20 @@ void grouping_create_percentile99(RRDR *r, const char *options) {
64 // resets when switches dimensions
65 // so, clear everything to restart
66 void grouping_reset_percentile(RRDR *r) {
67 - struct grouping_percentile *g = (struct grouping_percentile *)r->grouping.data;
67 + struct grouping_percentile *g = (struct grouping_percentile *)r->time_grouping.data;
68 g->next_pos = 0;
69 }
70
71 void grouping_free_percentile(RRDR *r) {
72 - struct grouping_percentile *g = (struct grouping_percentile *)r->grouping.data;
72 + struct grouping_percentile *g = (struct grouping_percentile *)r->time_grouping.data;
73 if(g) onewayalloc_freez(r->internal.owa, g->series);
74
75 - onewayalloc_freez(r->internal.owa, r->grouping.data);
76 - r->grouping.data = NULL;
75 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
76 + r->time_grouping.data = NULL;
77 }
78
79 void grouping_add_percentile(RRDR *r, NETDATA_DOUBLE value) {
80 - struct grouping_percentile *g = (struct grouping_percentile *)r->grouping.data;
80 + struct grouping_percentile *g = (struct grouping_percentile *)r->time_grouping.data;
81
82 if(unlikely(g->next_pos >= g->series_size)) {
83 g->series = onewayalloc_doublesize( r->internal.owa, g->series, g->series_size * sizeof(NETDATA_DOUBLE));
@@ -88,7 +88,7 @@ void grouping_add_percentile(RRDR *r, NETDATA_DOUBLE value) {
88 }
89
90 NETDATA_DOUBLE grouping_flush_percentile(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
91 - struct grouping_percentile *g = (struct grouping_percentile *)r->grouping.data;
91 + struct grouping_percentile *g = (struct grouping_percentile *)r->time_grouping.data;
92
93 NETDATA_DOUBLE value;
94 size_t available_slots = g->next_pos;
web/api/queries/query.c
+790 -122
@@ -649,24 +649,24 @@ static void rrdr_set_grouping_function(RRDR *r, RRDR_TIME_GROUPING group_method)
649 int i, found = 0;
650 for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
651 if(api_v1_data_groups[i].value == group_method) {
652 - r->grouping.create = api_v1_data_groups[i].create;
653 - r->grouping.reset = api_v1_data_groups[i].reset;
654 - r->grouping.free = api_v1_data_groups[i].free;
655 - r->grouping.add = api_v1_data_groups[i].add;
656 - r->grouping.flush = api_v1_data_groups[i].flush;
657 - r->grouping.tier_query_fetch = api_v1_data_groups[i].tier_query_fetch;
652 + r->time_grouping.create = api_v1_data_groups[i].create;
653 + r->time_grouping.reset = api_v1_data_groups[i].reset;
654 + r->time_grouping.free = api_v1_data_groups[i].free;
655 + r->time_grouping.add = api_v1_data_groups[i].add;
656 + r->time_grouping.flush = api_v1_data_groups[i].flush;
657 + r->time_grouping.tier_query_fetch = api_v1_data_groups[i].tier_query_fetch;
658 found = 1;
659 }
660 }
661 if(!found) {
662 errno = 0;
663 internal_error(true, "QUERY: grouping method %u not found. Using 'average'", (unsigned int)group_method);
664 - r->grouping.create = grouping_create_average;
665 - r->grouping.reset = grouping_reset_average;
666 - r->grouping.free = grouping_free_average;
667 - r->grouping.add = grouping_add_average;
668 - r->grouping.flush = grouping_flush_average;
669 - r->grouping.tier_query_fetch = TIER_QUERY_FETCH_AVERAGE;
664 + r->time_grouping.create = grouping_create_average;
665 + r->time_grouping.reset = grouping_reset_average;
666 + r->time_grouping.free = grouping_free_average;
667 + r->time_grouping.add = grouping_add_average;
668 + r->time_grouping.flush = grouping_flush_average;
669 + r->time_grouping.tier_query_fetch = TIER_QUERY_FETCH_AVERAGE;
670 }
671 }
672
@@ -677,20 +677,26 @@ RRDR_GROUP_BY group_by_parse(char *s) {
677 char *key = mystrsep(&s, ",| ");
678 if (!key || !*key) continue;
679
680 + if (strcmp(key, "selected") == 0)
681 + group_by |= RRDR_GROUP_BY_SELECTED;
682 +
683 if (strcmp(key, "dimension") == 0)
684 group_by |= RRDR_GROUP_BY_DIMENSION;
685
683 - if (strcmp(key, "node") == 0)
684 - group_by |= RRDR_GROUP_BY_NODE;
685 -
686 if (strcmp(key, "instance") == 0)
687 group_by |= RRDR_GROUP_BY_INSTANCE;
688
689 if (strcmp(key, "label") == 0)
690 group_by |= RRDR_GROUP_BY_LABEL;
691
692 - if (strcmp(key, "selected") == 0)
693 - group_by |= RRDR_GROUP_BY_SELECTED;
692 + if (strcmp(key, "node") == 0)
693 + group_by |= RRDR_GROUP_BY_NODE;
694 +
695 + if (strcmp(key, "context") == 0)
696 + group_by |= RRDR_GROUP_BY_CONTEXT;
697 +
698 + if (strcmp(key, "units") == 0)
699 + group_by |= RRDR_GROUP_BY_UNITS;
700 }
701
702 return group_by;
@@ -703,14 +709,20 @@ void buffer_json_group_by_to_array(BUFFER *wb, RRDR_GROUP_BY group_by) {
709 if(group_by & RRDR_GROUP_BY_DIMENSION)
710 buffer_json_add_array_item_string(wb, "dimension");
711
706 - if(group_by & RRDR_GROUP_BY_NODE)
707 - buffer_json_add_array_item_string(wb, "node");
708 -
712 if(group_by & RRDR_GROUP_BY_INSTANCE)
713 buffer_json_add_array_item_string(wb, "instance");
714
715 if(group_by & RRDR_GROUP_BY_LABEL)
716 buffer_json_add_array_item_string(wb, "label");
717 +
718 + if(group_by & RRDR_GROUP_BY_NODE)
719 + buffer_json_add_array_item_string(wb, "node");
720 +
721 + if(group_by & RRDR_GROUP_BY_CONTEXT)
722 + buffer_json_add_array_item_string(wb, "context");
723 +
724 + if(group_by & RRDR_GROUP_BY_UNITS)
725 + buffer_json_add_array_item_string(wb, "units");
726 }
727
728 RRDR_GROUP_BY_FUNCTION group_by_aggregate_function_parse(const char *s) {
@@ -1390,7 +1402,7 @@ static void rrd2rrdr_query_ops_freeall(RRDR *r __maybe_unused) {
1402 }
1403 }
1404
1393 -static void rrd2rrdr_query_ops_release(RRDR *r __maybe_unused, QUERY_ENGINE_OPS *ops) {
1405 +static void rrd2rrdr_query_ops_release(QUERY_ENGINE_OPS *ops) {
1406 if(!ops) return;
1407
1408 ops->next = released_ops;
@@ -1411,23 +1423,23 @@ static QUERY_ENGINE_OPS *rrd2rrdr_query_ops_get(RRDR *r) {
1423 return ops;
1424 }
1425
1414 -static QUERY_ENGINE_OPS *rrd2rrdr_query_ops_prep(RRDR *r, size_t dim_id_in_rrdr) {
1426 +static QUERY_ENGINE_OPS *rrd2rrdr_query_ops_prep(RRDR *r, size_t query_metric_id) {
1427 QUERY_TARGET *qt = r->internal.qt;
1428
1429 QUERY_ENGINE_OPS *ops = rrd2rrdr_query_ops_get(r);
1430 *ops = (QUERY_ENGINE_OPS) {
1431 .r = r,
1420 - .qm = query_metric(qt, dim_id_in_rrdr),
1421 - .grouping_add = r->grouping.add,
1422 - .grouping_flush = r->grouping.flush,
1423 - .tier_query_fetch = r->grouping.tier_query_fetch,
1432 + .qm = query_metric(qt, query_metric_id),
1433 + .grouping_add = r->time_grouping.add,
1434 + .grouping_flush = r->time_grouping.flush,
1435 + .tier_query_fetch = r->time_grouping.tier_query_fetch,
1436 .view_update_every = r->view.update_every,
1437 .query_granularity = (time_t)(r->view.update_every / r->view.group),
1438 .group_value_flags = RRDR_VALUE_NOTHING,
1439 };
1440
1441 if(!query_plan(ops, qt->window.after, qt->window.before, qt->window.points)) {
1430 - rrd2rrdr_query_ops_release(r, ops);
1442 + rrd2rrdr_query_ops_release(ops);
1443 return NULL;
1444 }
1445
@@ -1436,7 +1448,7 @@ static QUERY_ENGINE_OPS *rrd2rrdr_query_ops_prep(RRDR *r, size_t dim_id_in_rrdr)
1448
1449 static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_OPS *ops) {
1450 QUERY_TARGET *qt = r->internal.qt;
1439 - QUERY_METRIC *qm = query_metric(qt, dim_id_in_rrdr);
1451 + QUERY_METRIC *qm = ops->qm;
1452
1453 size_t points_wanted = qt->window.points;
1454 time_t after_wanted = qt->window.after;
@@ -1758,7 +1770,7 @@ static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_
1770 (NETDATA_DOUBLE)ops->group_anomaly_outlier_points * 100.0 / (NETDATA_DOUBLE)ops->group_anomaly_all_points
1771 : 0.0;
1772
1761 - if(likely(points_added || dim_id_in_rrdr)) {
1773 + if(likely(points_added || r->internal.queries_count)) {
1774 // find the min/max across all dimensions
1775
1776 if(unlikely(group_value < min)) min = group_value;
@@ -1766,7 +1778,7 @@ static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_
1778
1779 }
1780 else {
1769 - // runs only when dim_id_in_rrdr == 0 && points_added == 0
1781 + // runs only when r->internal.queries_count == 0 && points_added == 0
1782 // so, on the first point added for the query.
1783 min = max = group_value;
1784 }
@@ -1821,6 +1833,7 @@ static void rrd2rrdr_query_execute(RRDR *r, size_t dim_id_in_rrdr, QUERY_ENGINE_
1833 points_added++;
1834 }
1835
1836 + r->internal.queries_count++;
1837 r->view.min = min;
1838 r->view.max = max;
1839 r->view.before = max_date;
@@ -1918,8 +1931,9 @@ static void rrd2rrdr_log_request_response_metadata(RRDR *r
1931 , const char *msg
1932 ) {
1933
1921 - time_t first_entry_s = r->internal.qt->db.first_time_s;
1922 - time_t last_entry_s = r->internal.qt->db.last_time_s;
1934 + QUERY_TARGET *qt = r->internal.qt;
1935 + time_t first_entry_s = qt->db.first_time_s;
1936 + time_t last_entry_s = qt->db.last_time_s;
1937
1938 internal_error(
1939 true,
@@ -1929,8 +1943,8 @@ static void rrd2rrdr_log_request_response_metadata(RRDR *r
1943 "duration (got: %ld, want: %ld, req: %ld, db: %ld), "
1944 "points (got: %zu, want: %zu, req: %zu), "
1945 "%s"
1932 - , r->internal.qt->id
1933 - , r->internal.qt->window.query_granularity
1946 + , qt->id
1947 + , qt->window.query_granularity
1948
1949 // grouping
1950 , (aligned) ? "aligned" : "unaligned"
@@ -1952,10 +1966,10 @@ static void rrd2rrdr_log_request_response_metadata(RRDR *r
1966 , last_entry_s
1967
1968 // duration
1955 - , (long)(r->view.before - r->view.after + r->internal.qt->window.query_granularity)
1956 - , (long)(before_wanted - after_wanted + r->internal.qt->window.query_granularity)
1969 + , (long)(r->view.before - r->view.after + qt->window.query_granularity)
1970 + , (long)(before_wanted - after_wanted + qt->window.query_granularity)
1971 , (long)before_requested - after_requested
1958 - , (long)((last_entry_s - first_entry_s) + r->internal.qt->window.query_granularity)
1972 + , (long)((last_entry_s - first_entry_s) + qt->window.query_granularity)
1973
1974 // points
1975 , r->rows
@@ -2341,6 +2355,676 @@ bool query_target_calculate_window(QUERY_TARGET *qt) {
2355 return true;
2356 }
2357
2358 +void query_target_merge_data_statistics(struct query_data_statistics *d, struct query_data_statistics *s) {
2359 + if(!d->group_points)
2360 + *d = *s;
2361 + else {
2362 + d->group_points += s->group_points;
2363 + d->sum += s->sum;
2364 + d->anomaly_sum += s->anomaly_sum;
2365 + d->volume += s->volume;
2366 +
2367 + if(s->min < d->min)
2368 + d->min = s->min;
2369 +
2370 + if(s->max > d->max)
2371 + d->max = s->max;
2372 + }
2373 +}
2374 +
2375 +// ----------------------------------------------------------------------------
2376 +// group by
2377 +
2378 +struct group_by_label_key {
2379 + DICTIONARY *values;
2380 +};
2381 +
2382 +static void group_by_label_key_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
2383 + // add the key to our r->label_keys global keys dictionary
2384 + DICTIONARY *label_keys = data;
2385 + dictionary_set(label_keys, dictionary_acquired_item_name(item), NULL, 0);
2386 +
2387 + // create a dictionary for the values of this key
2388 + struct group_by_label_key *k = value;
2389 + k->values = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE, NULL, 0);
2390 +}
2391 +
2392 +static void group_by_label_key_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
2393 + struct group_by_label_key *k = value;
2394 + dictionary_destroy(k->values);
2395 +}
2396 +
2397 +static int rrdlabels_traversal_cb_to_group_by_label_key(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) {
2398 + DICTIONARY *dl = data;
2399 + struct group_by_label_key *k = dictionary_set(dl, name, NULL, sizeof(struct group_by_label_key));
2400 + dictionary_set(k->values, value, NULL, 0);
2401 + return 1;
2402 +}
2403 +
2404 +void rrdr_json_group_by_labels(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
2405 + if(!r->label_keys || !r->dl)
2406 + return;
2407 +
2408 + buffer_json_member_add_object(wb, key);
2409 +
2410 + void *t;
2411 + dfe_start_read(r->label_keys, t) {
2412 + buffer_json_member_add_array(wb, t_dfe.name);
2413 +
2414 + for(size_t d = 0; d < r->d ;d++) {
2415 + if(!rrdr_dimension_should_be_exposed(r->od[d], options))
2416 + continue;
2417 +
2418 + struct group_by_label_key *k = dictionary_get(r->dl[d], t_dfe.name);
2419 + if(k) {
2420 + buffer_json_add_array_item_array(wb);
2421 + void *tt;
2422 + dfe_start_read(k->values, tt) {
2423 + buffer_json_add_array_item_string(wb, tt_dfe.name);
2424 + }
2425 + dfe_done(tt);
2426 + buffer_json_array_close(wb);
2427 + }
2428 + else
2429 + buffer_json_add_array_item_string(wb, NULL);
2430 + }
2431 +
2432 + buffer_json_array_close(wb);
2433 + }
2434 + dfe_done(t);
2435 +
2436 + buffer_json_object_close(wb); // key
2437 +}
2438 +
2439 +static int group_by_label_is_space(char c) {
2440 + if(c == ',' || c == '|')
2441 + return 1;
2442 +
2443 + return 0;
2444 +}
2445 +
2446 +static RRDR *rrd2rrdr_group_by_initialize(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
2447 + RRDR_OPTIONS options = qt->request.options;
2448 +
2449 + if(qt->request.group_by == RRDR_GROUP_BY_NONE) {
2450 + RRDR *r = rrdr_create(owa, qt, qt->query.used, qt->window.points);
2451 + if(unlikely(!r)) {
2452 + internal_error(true, "QUERY: cannot create RRDR for %s, after=%ld, before=%ld, dimensions=%u, points=%zu",
2453 + qt->id, qt->window.after, qt->window.before, qt->query.used, qt->window.points);
2454 + query_target_release(qt);
2455 + return NULL;
2456 + }
2457 + r->group_by.r = NULL;
2458 +
2459 + for(size_t d = 0; d < qt->query.used ; d++) {
2460 + QUERY_METRIC *qm = query_metric(qt, d);
2461 + QUERY_DIMENSION *qd = query_dimension(qt, qm->link.query_dimension_id);
2462 + r->di[d] = rrdmetric_acquired_id_dup(qd->rma);
2463 + r->dn[d] = rrdmetric_acquired_name_dup(qd->rma);
2464 + }
2465 +
2466 + return r;
2467 + }
2468 +
2469 + struct rrdr_group_by_entry *entries = onewayalloc_callocz(owa, qt->query.used, sizeof(struct rrdr_group_by_entry));
2470 + DICTIONARY *groups = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
2471 +
2472 + if(qt->request.group_by & RRDR_GROUP_BY_LABEL && qt->request.group_by_label && *qt->request.group_by_label)
2473 + qt->group_by.used = quoted_strings_splitter(qt->request.group_by_label, qt->group_by.label_keys, GROUP_BY_MAX_LABEL_KEYS, group_by_label_is_space);
2474 +
2475 + if(!qt->group_by.used)
2476 + qt->request.group_by &= ~RRDR_GROUP_BY_LABEL;
2477 +
2478 + if(!(qt->request.group_by & (RRDR_GROUP_BY_SELECTED | RRDR_GROUP_BY_DIMENSION | RRDR_GROUP_BY_INSTANCE | RRDR_GROUP_BY_LABEL | RRDR_GROUP_BY_NODE | RRDR_GROUP_BY_CONTEXT)))
2479 + qt->request.group_by = RRDR_GROUP_BY_DIMENSION;
2480 +
2481 + DICTIONARY *label_keys = NULL;
2482 + if(options & RRDR_OPTION_GROUP_BY_LABELS)
2483 + label_keys = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE, NULL, 0);
2484 +
2485 + int added = 0;
2486 + BUFFER *key = buffer_create(0, NULL);
2487 + QUERY_INSTANCE *last_qi = NULL;
2488 + size_t priority = 0;
2489 + time_t update_every_max = 0;
2490 + for(size_t d = 0; d < qt->query.used ; d++) {
2491 + QUERY_METRIC *qm = query_metric(qt, d);
2492 + QUERY_INSTANCE *qi = query_instance(qt, qm->link.query_instance_id);
2493 + QUERY_CONTEXT *qc = query_context(qt, qm->link.query_context_id);
2494 + QUERY_NODE *qn = query_node(qt, qm->link.query_node_id);
2495 +
2496 + if(qi != last_qi) {
2497 + priority = 0;
2498 + last_qi = qi;
2499 +
2500 + time_t update_every = rrdinstance_acquired_update_every(qi->ria);
2501 + if(update_every > update_every_max)
2502 + update_every_max = update_every;
2503 + }
2504 + else
2505 + priority++;
2506 +
2507 + // --------------------------------------------------------------------
2508 + // generate the group by key
2509 +
2510 + buffer_flush(key);
2511 + if(unlikely(qm->status & RRDR_DIMENSION_HIDDEN)) {
2512 + buffer_strcat(key, "__hidden_dimensions__");
2513 + }
2514 + else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
2515 + buffer_strcat(key, "selected");
2516 + }
2517 + else {
2518 + if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
2519 + buffer_fast_strcat(key, "|", 1);
2520 + buffer_strcat(key, query_metric_id(qt, qm));
2521 + }
2522 +
2523 + if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
2524 + buffer_fast_strcat(key, "|", 1);
2525 + buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
2526 + }
2527 +
2528 + if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
2529 + DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
2530 + for (size_t l = 0; l < qt->group_by.used; l++) {
2531 + buffer_fast_strcat(key, "|", 1);
2532 + rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
2533 + }
2534 + }
2535 +
2536 + if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
2537 + buffer_fast_strcat(key, "|", 1);
2538 + buffer_strcat(key, qn->rrdhost->machine_guid);
2539 + }
2540 +
2541 + if (qt->request.group_by & RRDR_GROUP_BY_CONTEXT) {
2542 + buffer_fast_strcat(key, "|", 1);
2543 + buffer_strcat(key, rrdcontext_acquired_id(qc->rca));
2544 + }
2545 +
2546 + if (qt->request.group_by & RRDR_GROUP_BY_UNITS) {
2547 + buffer_fast_strcat(key, "|", 1);
2548 + buffer_strcat(key, query_target_has_percentage_units(qt) ? "%" : rrdinstance_acquired_units(qi->ria));
2549 + }
2550 + }
2551 +
2552 + // lookup the key in the dictionary
2553 +
2554 + int pos = -1;
2555 + int *set = dictionary_set(groups, buffer_tostring(key), &pos, sizeof(pos));
2556 + if(*set == -1) {
2557 + // the key just added to the dictionary
2558 +
2559 + *set = pos = added++;
2560 +
2561 + // ----------------------------------------------------------------
2562 + // generate the dimension id
2563 +
2564 + buffer_flush(key);
2565 + if(unlikely(qm->status & RRDR_DIMENSION_HIDDEN)) {
2566 + buffer_strcat(key, "__hidden_dimensions__");
2567 + }
2568 + else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
2569 + buffer_strcat(key, "selected");
2570 + }
2571 + else {
2572 + if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
2573 + buffer_strcat(key, query_metric_id(qt, qm));
2574 + }
2575 +
2576 + if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
2577 + if (buffer_strlen(key) != 0)
2578 + buffer_fast_strcat(key, ",", 1);
2579 +
2580 + if (qt->request.group_by & RRDR_GROUP_BY_NODE)
2581 + buffer_strcat(key, rrdinstance_acquired_id(qi->ria));
2582 + else
2583 + buffer_strcat(key, string2str(query_instance_id_fqdn(qt, qi)));
2584 + }
2585 +
2586 + if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
2587 + DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
2588 + for (size_t l = 0; l < qt->group_by.used; l++) {
2589 + if (buffer_strlen(key) != 0)
2590 + buffer_fast_strcat(key, ",", 1);
2591 + rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
2592 + }
2593 + }
2594 +
2595 + if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
2596 + if (buffer_strlen(key) != 0)
2597 + buffer_fast_strcat(key, ",", 1);
2598 +
2599 + buffer_strcat(key, qn->rrdhost->machine_guid);
2600 + }
2601 +
2602 + if (qt->request.group_by & RRDR_GROUP_BY_CONTEXT) {
2603 + if (buffer_strlen(key) != 0)
2604 + buffer_fast_strcat(key, ",", 1);
2605 +
2606 + buffer_strcat(key, rrdcontext_acquired_id(qc->rca));
2607 + }
2608 +
2609 + if (qt->request.group_by & RRDR_GROUP_BY_UNITS) {
2610 + if (buffer_strlen(key) != 0)
2611 + buffer_fast_strcat(key, ",", 1);
2612 +
2613 + buffer_strcat(key, query_target_has_percentage_units(qt) ? "%" : rrdinstance_acquired_units(qi->ria));
2614 + }
2615 + }
2616 +
2617 + entries[pos].id = string_strdupz(buffer_tostring(key));
2618 +
2619 + // ----------------------------------------------------------------
2620 + // generate the dimension name
2621 +
2622 + buffer_flush(key);
2623 + if(unlikely(qm->status & RRDR_DIMENSION_HIDDEN)) {
2624 + buffer_strcat(key, "__hidden_dimensions__");
2625 + }
2626 + else if(unlikely(qt->request.group_by & RRDR_GROUP_BY_SELECTED)) {
2627 + buffer_strcat(key, "selected");
2628 + }
2629 + else {
2630 + if (qt->request.group_by & RRDR_GROUP_BY_DIMENSION) {
2631 + buffer_strcat(key, query_metric_name(qt, qm));
2632 + }
2633 +
2634 + if (qt->request.group_by & RRDR_GROUP_BY_INSTANCE) {
2635 + if (buffer_strlen(key) != 0)
2636 + buffer_fast_strcat(key, ",", 1);
2637 +
2638 + if (qt->request.group_by & RRDR_GROUP_BY_NODE)
2639 + buffer_strcat(key, rrdinstance_acquired_name(qi->ria));
2640 + else
2641 + buffer_strcat(key, string2str(query_instance_name_fqdn(qt, qi)));
2642 + }
2643 +
2644 + if (qt->request.group_by & RRDR_GROUP_BY_LABEL) {
2645 + DICTIONARY *labels = rrdinstance_acquired_labels(qi->ria);
2646 + for (size_t l = 0; l < qt->group_by.used; l++) {
2647 + if (buffer_strlen(key) != 0)
2648 + buffer_fast_strcat(key, ",", 1);
2649 + rrdlabels_get_value_to_buffer_or_unset(labels, key, qt->group_by.label_keys[l], "[unset]");
2650 + }
2651 + }
2652 +
2653 + if (qt->request.group_by & RRDR_GROUP_BY_NODE) {
2654 + if (buffer_strlen(key) != 0)
2655 + buffer_fast_strcat(key, ",", 1);
2656 +
2657 + buffer_strcat(key, rrdhost_hostname(qn->rrdhost));
2658 + }
2659 +
2660 + if (qt->request.group_by & RRDR_GROUP_BY_CONTEXT) {
2661 + if (buffer_strlen(key) != 0)
2662 + buffer_fast_strcat(key, ",", 1);
2663 +
2664 + buffer_strcat(key, rrdcontext_acquired_id(qc->rca));
2665 + }
2666 +
2667 + if (qt->request.group_by & RRDR_GROUP_BY_UNITS) {
2668 + if (buffer_strlen(key) != 0)
2669 + buffer_fast_strcat(key, ",", 1);
2670 +
2671 + buffer_strcat(key, query_target_has_percentage_units(qt) ? "%" : rrdinstance_acquired_units(qi->ria));
2672 + }
2673 + }
2674 +
2675 + entries[pos].name = string_strdupz(buffer_tostring(key));
2676 +
2677 + // add the rest of the info
2678 + entries[pos].units = rrdinstance_acquired_units_dup(qi->ria);
2679 + entries[pos].priority = priority;
2680 +
2681 + if(options & RRDR_OPTION_GROUP_BY_LABELS) {
2682 + entries[pos].dl = dictionary_create_advanced(
2683 + DICT_OPTION_SINGLE_THREADED | DICT_OPTION_FIXED_SIZE | DICT_OPTION_DONT_OVERWRITE_VALUE,
2684 + NULL, sizeof(struct group_by_label_key));
2685 + dictionary_register_insert_callback(entries[pos].dl, group_by_label_key_insert_cb, label_keys);
2686 + dictionary_register_delete_callback(entries[pos].dl, group_by_label_key_delete_cb, label_keys);
2687 + }
2688 + }
2689 + else {
2690 + // the key found in the dictionary
2691 + pos = *set;
2692 + }
2693 +
2694 + entries[pos].count++;
2695 +
2696 + if(unlikely(priority < entries[pos].priority))
2697 + entries[pos].priority = priority;
2698 +
2699 + qm->grouped_as.slot = pos;
2700 + qm->grouped_as.id = entries[pos].id;
2701 + qm->grouped_as.name = entries[pos].name;
2702 + qm->grouped_as.units = entries[pos].units;
2703 +
2704 + // copy the dimension flags decided by the query target
2705 + // we need this, because if a dimension is explicitly selected
2706 + // the query target adds to it the non-zero flag
2707 + qm->status |= RRDR_DIMENSION_GROUPED;
2708 + entries[pos].od |= qm->status;
2709 +
2710 + if(entries[pos].dl)
2711 + rrdlabels_walkthrough_read(rrdinstance_acquired_labels(qi->ria),
2712 + rrdlabels_traversal_cb_to_group_by_label_key, entries[pos].dl);
2713 + }
2714 +
2715 + // check if we have multiple units
2716 + bool multiple_units = false;
2717 + for(int i = 1; i < added ; i++) {
2718 + if(entries[i].units != entries[0].units) {
2719 + multiple_units = true;
2720 + break;
2721 + }
2722 + }
2723 +
2724 + if(multiple_units) {
2725 + // include the units into the id and name of the dimensions
2726 + for(int i = 0; i < added ; i++) {
2727 + buffer_flush(key);
2728 + buffer_strcat(key, string2str(entries[i].id));
2729 + buffer_fast_strcat(key, ",", 1);
2730 + buffer_strcat(key, string2str(entries[i].units));
2731 + STRING *u = string_strdupz(buffer_tostring(key));
2732 + string_freez(entries[i].id);
2733 + entries[i].id = u;
2734 + }
2735 + }
2736 +
2737 + RRDR *r = rrdr_create(owa, qt, added, qt->window.points);
2738 + if(!r) {
2739 + internal_error(true, "QUERY: cannot create group by RRDR for %s, after=%ld, before=%ld, dimensions=%d, points=%zu",
2740 + qt->id, qt->window.after, qt->window.before, added, qt->window.points);
2741 + goto cleanup;
2742 + }
2743 +
2744 + r->group_by.r = rrdr_create(owa, qt, 1, qt->window.points);
2745 + if(!r->group_by.r) {
2746 + internal_error(true, "QUERY: cannot create group by temporary RRDR for %s, after=%ld, before=%ld, dimensions=%d, points=%zu",
2747 + qt->id, qt->window.after, qt->window.before, 1, qt->window.points);
2748 + goto cleanup;
2749 + }
2750 +
2751 + r->dp = onewayalloc_callocz(r->internal.owa, r->d, sizeof(*r->dp));
2752 + r->dv = onewayalloc_callocz(r->internal.owa, r->d, sizeof(*r->dv));
2753 + r->dgbc = onewayalloc_callocz(r->internal.owa, r->d, sizeof(*r->dgbc));
2754 + r->gbc = onewayalloc_callocz(r->internal.owa, r->n * r->d, sizeof(*r->gbc));
2755 +
2756 + if(options & RRDR_OPTION_GROUP_BY_LABELS) {
2757 + r->dl = onewayalloc_callocz(r->internal.owa, r->d, sizeof(DICTIONARY *));
2758 + r->label_keys = label_keys;
2759 + }
2760 +
2761 + // zero r (dimension options, names, and ids)
2762 + // this is required, because group-by may lead to empty dimensions
2763 + for(size_t d = 0; d < r->d ; d++) {
2764 + r->di[d] = entries[d].id;
2765 + r->dn[d] = entries[d].name;
2766 +
2767 + r->od[d] = entries[d].od;
2768 + r->du[d] = entries[d].units;
2769 + r->dp[d] = entries[d].priority;
2770 + r->dgbc[d] = entries[d].count;
2771 +
2772 + if(r->dl)
2773 + r->dl[d] = entries[d].dl;
2774 + }
2775 +
2776 + // initialize partial trimming
2777 + r->partial_data_trimming.max_update_every = update_every_max;
2778 + r->partial_data_trimming.expected_after =
2779 + (!(qt->request.options & RRDR_OPTION_RETURN_RAW) && qt->window.before >= qt->window.now - update_every_max) ?
2780 + qt->window.before - update_every_max :
2781 + qt->window.before;
2782 + r->partial_data_trimming.trimmed_after = qt->window.before;
2783 +
2784 + // make all values empty
2785 + for(size_t i = 0; i != r->n ;i++) {
2786 + NETDATA_DOUBLE *cn = &r->v[ i * r->d ];
2787 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
2788 + NETDATA_DOUBLE *ar = &r->ar[ i * r->d ];
2789 + for (size_t d = 0; d < r->d; d++) {
2790 + cn[d] = 0.0;
2791 + ar[d] = 0.0;
2792 + co[d] = RRDR_VALUE_EMPTY;
2793 + }
2794 + }
2795 +
2796 +cleanup:
2797 + buffer_free(key);
2798 +
2799 + if(!r) {
2800 + if(entries) {
2801 + for (int d2 = 0; d2 < added; d2++) {
2802 + string_freez(entries[d2].id);
2803 + string_freez(entries[d2].name);
2804 + dictionary_destroy(entries[d2].dl);
2805 + }
2806 + }
2807 + dictionary_destroy(label_keys);
2808 + query_target_release(qt);
2809 + }
2810 + else if(!r->group_by.r) {
2811 + rrdr_free(owa, r);
2812 + r = NULL;
2813 + }
2814 +
2815 + onewayalloc_freez(owa, entries);
2816 + dictionary_destroy(groups);
2817 +
2818 + return r;
2819 +}
2820 +
2821 +static void rrd2rrdr_group_by_add_metric(RRDR *r, size_t query_metric_id) {
2822 + if(!r->group_by.r)
2823 + return;
2824 +
2825 + QUERY_TARGET *qt = r->internal.qt;
2826 + RRDR_OPTIONS options = qt->request.options;
2827 + RRDR *r_tmp = r->group_by.r;
2828 +
2829 + // do the group_by
2830 + for(size_t i = 0; i != rrdr_rows(r_tmp) ; i++) {
2831 +
2832 + size_t idx_tmp = i * r_tmp->d;
2833 + NETDATA_DOUBLE *cn_tmp_base = &r_tmp->v[ idx_tmp ];
2834 + RRDR_VALUE_FLAGS *co_tmp_base = &r_tmp->o[ idx_tmp ];
2835 + NETDATA_DOUBLE *ar_tmp_base = &r_tmp->ar[ idx_tmp ];
2836 +
2837 + size_t idx = i * r->d;
2838 + NETDATA_DOUBLE *cn_base = &r->v[ idx ];
2839 + RRDR_VALUE_FLAGS *co_base = &r->o[ idx ];
2840 + NETDATA_DOUBLE *ar_base = &r->ar[ idx ];
2841 + uint32_t *gbc_base = &r->gbc[ idx ];
2842 +
2843 + for(size_t d_tmp = 0; d_tmp < r_tmp->d ; d_tmp++) {
2844 + if(unlikely(!(r_tmp->od[d_tmp] & RRDR_DIMENSION_QUERIED)))
2845 + continue;
2846 +
2847 + NETDATA_DOUBLE n_tmp = cn_tmp_base[d_tmp];
2848 + RRDR_VALUE_FLAGS o_tmp = co_tmp_base[d_tmp];
2849 + NETDATA_DOUBLE ar_tmp = ar_tmp_base[d_tmp];
2850 +
2851 + if(o_tmp & RRDR_VALUE_EMPTY) {
2852 + if(options & RRDR_OPTION_NULL2ZERO)
2853 + n_tmp = 0.0;
2854 + else
2855 + continue;
2856 + }
2857 +
2858 + if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n_tmp < 0))
2859 + n_tmp = -n_tmp;
2860 +
2861 + QUERY_METRIC *qm = query_metric(qt, query_metric_id);
2862 + size_t d = qm->grouped_as.slot;
2863 +
2864 + r->od[d] |= RRDR_DIMENSION_QUERIED;
2865 +
2866 + NETDATA_DOUBLE *cn = &cn_base[d];
2867 + RRDR_VALUE_FLAGS *co = &co_base[d];
2868 + NETDATA_DOUBLE *ar = &ar_base[d];
2869 + uint32_t *gbc = &gbc_base[d];
2870 +
2871 + switch(qt->request.group_by_aggregate_function) {
2872 + default:
2873 + case RRDR_GROUP_BY_FUNCTION_AVERAGE:
2874 + case RRDR_GROUP_BY_FUNCTION_SUM:
2875 + *cn += n_tmp;
2876 + break;
2877 +
2878 + case RRDR_GROUP_BY_FUNCTION_MIN:
2879 + if(n_tmp < *cn)
2880 + *cn = n_tmp;
2881 + break;
2882 +
2883 + case RRDR_GROUP_BY_FUNCTION_MAX:
2884 + if(n_tmp > *cn)
2885 + *cn = n_tmp;
2886 + break;
2887 + }
2888 +
2889 + *co |= (o_tmp & (RRDR_VALUE_RESET | RRDR_VALUE_PARTIAL));
2890 + *ar += ar_tmp;
2891 + (*gbc)++;
2892 + }
2893 + }
2894 +}
2895 +
2896 +static void rrd2rrdr_group_by_finalize(RRDR *r) {
2897 + if(!r->group_by.r)
2898 + return;
2899 +
2900 + QUERY_TARGET *qt = r->internal.qt;
2901 + RRDR_OPTIONS options = qt->request.options;
2902 +
2903 + // copy the timestamps
2904 + for(size_t i = 0; i != r->n ;i++) {
2905 + r->t[i] = r->group_by.r->t[i];
2906 + }
2907 +
2908 + if(!(options & RRDR_OPTION_RETURN_RAW)) {
2909 + // partial trimming
2910 + size_t last_row_gbc = 0;
2911 + for (size_t i = 0; i != r->n; i++) {
2912 + size_t row_gbc = 0;
2913 + for (size_t d = 0; d < r->d; d++) {
2914 + if (unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED)))
2915 + continue;
2916 +
2917 + row_gbc += r->gbc[ i * r->d + d ];
2918 + }
2919 +
2920 + if (unlikely(r->t[i] > r->partial_data_trimming.expected_after && row_gbc < last_row_gbc)) {
2921 + // discard the rest of the points
2922 + r->partial_data_trimming.trimmed_after = r->t[i];
2923 + r->rows = i;
2924 + break;
2925 + }
2926 + else
2927 + last_row_gbc = row_gbc;
2928 + }
2929 + }
2930 +
2931 + // apply averaging, remove RRDR_VALUE_EMPTY, find the non-zero dimensions, min and max
2932 + size_t min_max_values = 0;
2933 + NETDATA_DOUBLE min = NAN, max = NAN;
2934 + for (size_t d = 0; d < r->d; d++) {
2935 + size_t non_zero = 0;
2936 +
2937 + NETDATA_DOUBLE sum = 0;
2938 + size_t count = 0;
2939 +
2940 + for(size_t i = 0; i != r->n ;i++) {
2941 + size_t idx2 = i * r->d + d;
2942 +
2943 + NETDATA_DOUBLE *cn2 = &r->v[ idx2 ];
2944 + RRDR_VALUE_FLAGS *co2 = &r->o[ idx2 ];
2945 + NETDATA_DOUBLE *ar2 = &r->ar[ idx2 ];
2946 + uint32_t gbc2 = r->gbc[ idx2 ];
2947 +
2948 + if(likely(gbc2)) {
2949 + *co2 &= ~RRDR_VALUE_EMPTY;
2950 +
2951 + if(gbc2 != r->dgbc[d])
2952 + *co2 |= RRDR_VALUE_PARTIAL;
2953 +
2954 + NETDATA_DOUBLE n;
2955 +
2956 + sum += *cn2;
2957 + count += gbc2;
2958 +
2959 + if(qt->request.group_by_aggregate_function == RRDR_GROUP_BY_FUNCTION_AVERAGE)
2960 + n = (*cn2 /= gbc2);
2961 + else
2962 + n = *cn2;
2963 +
2964 + if(!query_target_aggregatable(qt))
2965 + *ar2 /= gbc2;
2966 +
2967 + if(islessgreater(n, 0.0))
2968 + non_zero++;
2969 +
2970 + if(unlikely(!min_max_values++)) {
2971 + min = n;
2972 + max = n;
2973 + }
2974 + else {
2975 + if(n < min)
2976 + min = n;
2977 +
2978 + if(n > max)
2979 + max = n;
2980 + }
2981 + }
2982 + }
2983 +
2984 + if(non_zero)
2985 + r->od[d] |= RRDR_DIMENSION_NONZERO;
2986 +
2987 + r->dv[d] = (count) ? sum / (NETDATA_DOUBLE)count : 0.0;
2988 + }
2989 +
2990 + r->view.min = min;
2991 + r->view.max = max;
2992 +
2993 + // update query instance counts in query host and query context
2994 + {
2995 + size_t h = 0, c = 0, i = 0;
2996 + for(; h < qt->nodes.used ; h++) {
2997 + QUERY_NODE *qn = &qt->nodes.array[h];
2998 +
2999 + for(; c < qt->contexts.used ;c++) {
3000 + QUERY_CONTEXT *qc = &qt->contexts.array[c];
3001 +
3002 + if(!rrdcontext_acquired_belongs_to_host(qc->rca, qn->rrdhost))
3003 + break;
3004 +
3005 + for(; i < qt->instances.used ;i++) {
3006 + QUERY_INSTANCE *qi = &qt->instances.array[i];
3007 +
3008 + if(!rrdinstance_acquired_belongs_to_context(qi->ria, qc->rca))
3009 + break;
3010 +
3011 + if(qi->metrics.queried) {
3012 + qc->instances.queried++;
3013 + qn->instances.queried++;
3014 + }
3015 + else if(qi->metrics.failed) {
3016 + qc->instances.failed++;
3017 + qn->instances.failed++;
3018 + }
3019 + }
3020 + }
3021 + }
3022 + }
3023 +}
3024 +
3025 +// ----------------------------------------------------------------------------
3026 +// query entry point
3027 +
3028 RRDR *rrd2rrdr_legacy(
3029 ONEWAYALLOC *owa,
3030 RRDSET *st, size_t points, time_t after, time_t before,
@@ -2368,23 +3052,6 @@ RRDR *rrd2rrdr_legacy(
3052 return rrd2rrdr(owa, query_target_create(&qtr));
3053 }
3054
2371 -void query_target_merge_data_statistics(struct query_data_statistics *d, struct query_data_statistics *s) {
2372 - if(!d->group_points)
2373 - *d = *s;
2374 - else {
2375 - d->group_points += s->group_points;
2376 - d->sum += s->sum;
2377 - d->anomaly_sum += s->anomaly_sum;
2378 - d->volume += s->volume;
2379 -
2380 - if(s->min < d->min)
2381 - d->min = s->min;
2382 -
2383 - if(s->max > d->max)
2384 - d->max = s->max;
2385 - }
2386 -}
2387 -
3055 RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3056 if(!qt)
3057 return NULL;
@@ -2397,13 +3064,9 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3064 // qt.window members are the WANTED ones.
3065 // qt.request members are the REQUESTED ones.
3066
2400 - RRDR *r = rrdr_create(owa, qt, qt->query.used, qt->window.points);
2401 - if(unlikely(!r)) {
2402 - internal_error(true, "QUERY: cannot create RRDR for %s, after=%ld, before=%ld, points=%zu",
2403 - qt->id, qt->window.after, qt->window.before, qt->window.points);
2404 - query_target_release(qt);
3067 + RRDR *r = rrd2rrdr_group_by_initialize(owa, qt);
3068 + if(!r)
3069 return NULL;
2406 - }
3070
3071 if(qt->window.relative)
3072 r->view.flags |= RRDR_RESULT_FLAG_RELATIVE;
@@ -2417,17 +3080,24 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3080 r->view.update_every = (int) (qt->window.group * qt->window.query_granularity);
3081 r->view.before = qt->window.before;
3082 r->view.after = qt->window.after;
2420 - r->grouping.points_wanted = qt->window.points;
2421 - r->grouping.resampling_group = qt->window.resampling_group;
2422 - r->grouping.resampling_divisor = qt->window.resampling_divisor;
3083 r->view.options = qt->window.options;
3084 + r->time_grouping.points_wanted = qt->window.points;
3085 + r->time_grouping.resampling_group = qt->window.resampling_group;
3086 + r->time_grouping.resampling_divisor = qt->window.resampling_divisor;
3087 +
3088 + if(r->group_by.r) {
3089 + r->group_by.r->view = r->view;
3090 + r->group_by.r->time_grouping = r->time_grouping;
3091 + }
3092 +
3093 + RRDR *r_tmp = r->group_by.r ? r->group_by.r : r;
3094
3095 // -------------------------------------------------------------------------
3096 // assign the processor functions
2427 - rrdr_set_grouping_function(r, qt->window.group_method);
3097 + rrdr_set_grouping_function(r_tmp, qt->window.group_method);
3098
3099 // allocate any memory required by the grouping method
2430 - r->grouping.create(r, qt->window.group_options);
3100 + r_tmp->time_grouping.create(r_tmp, qt->window.group_options);
3101
3102 // -------------------------------------------------------------------------
3103 // do the work for each dimension
@@ -2448,44 +3118,60 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3118
3119 QUERY_ENGINE_OPS **ops = NULL;
3120 if(qt->query.used)
2451 - ops = onewayalloc_callocz(r->internal.owa, qt->query.used, sizeof(QUERY_ENGINE_OPS *));
3121 + ops = onewayalloc_callocz(owa, qt->query.used, sizeof(QUERY_ENGINE_OPS *));
3122
3123 size_t capacity = libuv_worker_threads * 10;
3124 size_t max_queries_to_prepare = (qt->query.used > (capacity - 1)) ? (capacity - 1) : qt->query.used;
3125 size_t queries_prepared = 0;
3126 while(queries_prepared < max_queries_to_prepare) {
3127 // preload another query
2458 - ops[queries_prepared] = rrd2rrdr_query_ops_prep(r, queries_prepared);
3128 + ops[queries_prepared] = rrd2rrdr_query_ops_prep(r_tmp, queries_prepared);
3129 queries_prepared++;
3130 }
3131
2462 - for(size_t d = 0, max = qt->query.used; d < max ; d++) {
3132 + for(size_t d = 0; d < qt->query.used ; d++) {
3133 QUERY_METRIC *qm = query_metric(qt, d);
3134 QUERY_DIMENSION *qd = query_dimension(qt, qm->link.query_dimension_id);
3135 QUERY_INSTANCE *qi = query_instance(qt, qm->link.query_instance_id);
3136 QUERY_CONTEXT *qc = query_context(qt, qm->link.query_context_id);
3137 QUERY_NODE *qn = query_node(qt, qm->link.query_node_id);
3138
2469 - if(queries_prepared < max) {
3139 + if(queries_prepared < qt->query.used) {
3140 // preload another query
2471 - ops[queries_prepared] = rrd2rrdr_query_ops_prep(r, queries_prepared);
3141 + ops[queries_prepared] = rrd2rrdr_query_ops_prep(r_tmp, queries_prepared);
3142 queries_prepared++;
3143 }
3144
3145 + size_t dim_in_rrdr_tmp = (r_tmp != r) ? 0 : d;
3146 +
3147 // set the query target dimension options to rrdr
2476 - r->od[d] = qm->status;
3148 + r_tmp->od[dim_in_rrdr_tmp] = qm->status;
3149
3150 // reset the grouping for the new dimension
2479 - r->grouping.reset(r);
3151 + r_tmp->time_grouping.reset(r_tmp);
3152
3153 if(ops[d]) {
2482 - rrd2rrdr_query_execute(r, d, ops[d]);
2483 - rrd2rrdr_query_ops_release(r, ops[d]); // reuse this ops allocation
2484 - ops[d] = NULL;
3154 + rrd2rrdr_query_execute(r_tmp, dim_in_rrdr_tmp, ops[d]);
3155 + r_tmp->od[dim_in_rrdr_tmp] |= RRDR_DIMENSION_QUERIED;
3156
2486 - r->od[d] |= RRDR_DIMENSION_QUERIED;
2487 - r->di[d] = rrdmetric_acquired_id_dup(qd->rma);
2488 - r->dn[d] = rrdmetric_acquired_name_dup(qd->rma);
3157 + if(r_tmp != r) {
3158 + // copy back whatever got updated from the temporary r
3159 +
3160 + // the query updates RRDR_DIMENSION_NONZERO
3161 + qm->status = r_tmp->od[dim_in_rrdr_tmp];
3162 +
3163 + // the query updates these
3164 + r->view.min = r_tmp->view.min;
3165 + r->view.max = r_tmp->view.max;
3166 + r->view.after = r_tmp->view.after;
3167 + r->view.before = r_tmp->view.before;
3168 + r->rows = r_tmp->rows;
3169 +
3170 + rrd2rrdr_group_by_add_metric(r, d);
3171 + }
3172 +
3173 + rrd2rrdr_query_ops_release(ops[d]); // reuse this ops allocation
3174 + ops[d] = NULL;
3175
3176 qi->metrics.queried++;
3177 qc->metrics.queried++;
@@ -2514,17 +3200,17 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3200
3201 global_statistics_rrdr_query_completed(
3202 1,
2517 - r->stats.db_points_read - last_db_points_read,
2518 - r->stats.result_points_generated - last_result_points_generated,
3203 + r_tmp->stats.db_points_read - last_db_points_read,
3204 + r_tmp->stats.result_points_generated - last_result_points_generated,
3205 qt->request.query_source);
3206
2521 - last_db_points_read = r->stats.db_points_read;
2522 - last_result_points_generated = r->stats.result_points_generated;
3207 + last_db_points_read = r_tmp->stats.db_points_read;
3208 + last_result_points_generated = r_tmp->stats.result_points_generated;
3209
3210 if (qt->request.timeout)
3211 now_realtime_timeval(&query_current_time);
3212
2527 - if(r->od[d] & RRDR_DIMENSION_NONZERO)
3213 + if(qm->status & RRDR_DIMENSION_NONZERO)
3214 dimensions_nonzero++;
3215
3216 // verify all dimensions are aligned
@@ -2557,15 +3243,26 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3243 }
3244
3245 dimensions_used++;
3246 +
3247 + bool cancel = false;
3248 + if (qt->request.interrupt_callback && qt->request.interrupt_callback(qt->request.interrupt_callback_data)) {
3249 + cancel = true;
3250 + log_access("QUERY INTERRUPTED");
3251 + }
3252 +
3253 if (qt->request.timeout && ((NETDATA_DOUBLE)dt_usec(&query_start_time, &query_current_time) / 1000.0) > (NETDATA_DOUBLE)qt->request.timeout) {
3254 + cancel = true;
3255 log_access("QUERY CANCELED RUNTIME EXCEEDED %0.2f ms (LIMIT %lld ms)",
3256 (NETDATA_DOUBLE)dt_usec(&query_start_time, &query_current_time) / 1000.0, (long long)qt->request.timeout);
3257 + }
3258 +
3259 + if(cancel) {
3260 r->view.flags |= RRDR_RESULT_FLAG_CANCEL;
3261
3262 for(size_t i = d + 1; i < queries_prepared ; i++) {
3263 if(ops[i]) {
3264 query_planer_finalize_remaining_plans(ops[i]);
2568 - rrd2rrdr_query_ops_release(r, ops[i]);
3265 + rrd2rrdr_query_ops_release(ops[i]);
3266 ops[i] = NULL;
3267 }
3268 }
@@ -2574,39 +3271,13 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3271 }
3272 }
3273
2577 - // update query instance counts in query host and query context
2578 - if(qt->request.version >= 2) {
2579 - size_t h = 0, c = 0, i = 0;
2580 - for(; h < qt->nodes.used ; h++) {
2581 - QUERY_NODE *qn = &qt->nodes.array[h];
2582 -
2583 - for(; c < qt->contexts.used ;c++) {
2584 - QUERY_CONTEXT *qc = &qt->contexts.array[c];
2585 -
2586 - if(!rrdcontext_acquired_belongs_to_host(qc->rca, qn->rrdhost))
2587 - break;
2588 -
2589 - for(; i < qt->instances.used ;i++) {
2590 - QUERY_INSTANCE *qi = &qt->instances.array[i];
2591 -
2592 - if(!rrdinstance_acquired_belongs_to_context(qi->ria, qc->rca))
2593 - break;
3274 + // free all resources used by the grouping method
3275 + r_tmp->time_grouping.free(r_tmp);
3276
2595 - if(qi->metrics.queried) {
2596 - qc->instances.queried++;
2597 - qn->instances.queried++;
2598 - }
2599 - else if(qi->metrics.failed) {
2600 - qc->instances.failed++;
2601 - qn->instances.failed++;
2602 - }
2603 - }
2604 - }
2605 - }
2606 - }
3277 + rrd2rrdr_group_by_finalize(r);
3278
3279 #ifdef NETDATA_INTERNAL_CHECKS
2609 - if (dimensions_used) {
3280 + if (dimensions_used && !(r->view.flags & RRDR_RESULT_FLAG_CANCEL)) {
3281 if(r->internal.log)
3282 rrd2rrdr_log_request_response_metadata(r, qt->window.options, qt->window.group_method, qt->window.aligned, qt->window.group, qt->request.resampling_time, qt->window.resampling_group,
3283 qt->window.after, qt->request.after, qt->window.before, qt->request.before,
@@ -2653,7 +3324,7 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3324
3325 // free the query pipelining ops
3326 for(size_t d = 0; d < qt->query.used ; d++) {
2656 - rrd2rrdr_query_ops_release(r, ops[d]);
3327 + rrd2rrdr_query_ops_release(ops[d]);
3328 ops[d] = NULL;
3329 }
3330 rrd2rrdr_query_ops_freeall(r);
@@ -2661,19 +3332,16 @@ RRDR *rrd2rrdr(ONEWAYALLOC *owa, QUERY_TARGET *qt) {
3332
3333 onewayalloc_freez(owa, ops);
3334
2664 - // free all resources used by the grouping method
2665 - r->grouping.free(r);
2666 -
3335 if(likely(dimensions_used)) {
3336 // when all the dimensions are zero, we should return all of them
3337 if (unlikely((qt->window.options & RRDR_OPTION_NONZERO) && !dimensions_nonzero &&
3338 !(r->view.flags & RRDR_RESULT_FLAG_CANCEL))) {
3339 // all the dimensions are zero
3340 // mark them as NONZERO to send them all
2673 - for (size_t c = 0, max = qt->query.used; c < max; c++) {
2674 - if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
2675 - if (unlikely(!(r->od[c] & RRDR_DIMENSION_QUERIED))) continue;
2676 - r->od[c] |= RRDR_DIMENSION_NONZERO;
3341 + for (size_t d = 0; d < r->d; d++) {
3342 + if (unlikely(r->od[d] & RRDR_DIMENSION_HIDDEN)) continue;
3343 + if (unlikely(!(r->od[d] & RRDR_DIMENSION_QUERIED))) continue;
3344 + r->od[d] |= RRDR_DIMENSION_NONZERO;
3345 }
3346 }
3347 }
web/api/queries/query.h
+7 -5
@@ -54,11 +54,13 @@ const char *time_grouping_tostring(RRDR_TIME_GROUPING group);
54
55 typedef enum rrdr_group_by {
56 RRDR_GROUP_BY_NONE = 0,
57 - RRDR_GROUP_BY_DIMENSION = (1 << 0),
58 - RRDR_GROUP_BY_NODE = (1 << 1),
59 - RRDR_GROUP_BY_INSTANCE = (1 << 2),
60 - RRDR_GROUP_BY_LABEL = (1 << 3),
61 - RRDR_GROUP_BY_SELECTED = (1 << 4),
57 + RRDR_GROUP_BY_SELECTED = (1 << 0),
58 + RRDR_GROUP_BY_DIMENSION = (1 << 1),
59 + RRDR_GROUP_BY_NODE = (1 << 2),
60 + RRDR_GROUP_BY_INSTANCE = (1 << 3),
61 + RRDR_GROUP_BY_LABEL = (1 << 4),
62 + RRDR_GROUP_BY_CONTEXT = (1 << 5),
63 + RRDR_GROUP_BY_UNITS = (1 << 6),
64 } RRDR_GROUP_BY;
65
66 struct web_buffer;
web/api/queries/rrdr.c
+22 -1
@@ -77,9 +77,30 @@ inline void rrdr_free(ONEWAYALLOC *owa, RRDR *r) {
77 onewayalloc_freez(owa, r->dn);
78 onewayalloc_freez(owa, r->du);
79 onewayalloc_freez(owa, r->dp);
80 + onewayalloc_freez(owa, r->dv);
81 onewayalloc_freez(owa, r->ar);
82 onewayalloc_freez(owa, r->gbc);
83 onewayalloc_freez(owa, r->dgbc);
84 +
85 + if(r->dl) {
86 + for(size_t d = 0; d < r->d ;d++)
87 + dictionary_destroy(r->dl[d]);
88 +
89 + onewayalloc_freez(owa, r->dl);
90 + }
91 +
92 + dictionary_destroy(r->label_keys);
93 +
94 + if(r->group_by.r) {
95 + // prevent accidental infinite recursion
96 + r->group_by.r->group_by.r = NULL;
97 +
98 + // do not release qt twice
99 + r->group_by.r->internal.qt = NULL;
100 +
101 + rrdr_free(owa, r->group_by.r);
102 + }
103 +
104 onewayalloc_freez(owa, r);
105 }
106
@@ -94,7 +115,7 @@ RRDR *rrdr_create(ONEWAYALLOC *owa, QUERY_TARGET *qt, size_t dimensions, size_t
115
116 r->view.before = qt->window.before;
117 r->view.after = qt->window.after;
97 - r->grouping.points_wanted = points;
118 + r->time_grouping.points_wanted = points;
119 r->d = (int)dimensions;
120 r->n = (int)points;
121
web/api/queries/rrdr.h
+24 -4
@@ -44,11 +44,11 @@ typedef enum rrdr_options {
44 RRDR_OPTION_SHOW_DETAILS = (1 << 23), // v2 returns detailed object tree
45 RRDR_OPTION_DEBUG = (1 << 24), // v2 returns request description
46 RRDR_OPTION_MINIFY = (1 << 25), // remove JSON spaces and newlines from JSON output
47 + RRDR_OPTION_GROUP_BY_LABELS = (1 << 26), // v2 returns flattened labels per dimension of the chart
48
49 // internal ones - not to be exposed to the API
49 - RRDR_OPTION_HEALTH_RSRVD1 = (1 << 29), // reserved for RRDCALC_OPTION_NO_CLEAR_NOTIFICATION
50 - RRDR_OPTION_INTERNAL_AR = (1 << 30), // internal use only, to let the formatters know we want to render the anomaly rate
51 - RRDR_OPTION_INTERNAL_GBC = (1 << 31), // internal use only, to let the formatters know we want to render the group by count
50 + RRDR_OPTION_HEALTH_RSRVD1 = (1 << 30), // reserved for RRDCALC_OPTION_NO_CLEAR_NOTIFICATION
51 + RRDR_OPTION_INTERNAL_AR = (1 << 31), // internal use only, to let the formatters know we want to render the anomaly rate
52 } RRDR_OPTIONS;
53
54 typedef enum __attribute__ ((__packed__)) rrdr_value_flag {
@@ -82,6 +82,16 @@ typedef enum __attribute__ ((__packed__)) rrdr_result_flags {
82 RRDR_RESULT_FLAG_CANCEL = (1 << 2), // the query needs to be cancelled
83 } RRDR_RESULT_FLAGS;
84
85 +struct rrdr_group_by_entry {
86 + size_t priority;
87 + size_t count;
88 + STRING *id;
89 + STRING *name;
90 + STRING *units;
91 + RRDR_DIMENSION_FLAGS od;
92 + DICTIONARY *dl;
93 +};
94 +
95 typedef struct rrdresult {
96 size_t d; // the number of dimensions
97 size_t n; // the number of values in the arrays (number of points per dimension)
@@ -94,6 +104,10 @@ typedef struct rrdresult {
104 STRING **du; // array of d dimension units
105 uint32_t *dgbc; // array of d dimension units - NOT ALLOCATED when RRDR is created
106 uint32_t *dp; // array of d dimension priority - NOT ALLOCATED when RRDR is created
107 + NETDATA_DOUBLE *dv; // array of d dimension averages - NOT ALLOCATED when RRDR is created
108 + DICTIONARY **dl; // array of d dimension labels - NOT ALLOCATED when RRDR is created
109 +
110 + DICTIONARY *label_keys;
111
112 time_t *t; // array of n timestamps
113 NETDATA_DOUBLE *v; // array n x d values
@@ -132,7 +146,11 @@ typedef struct rrdresult {
146 size_t points_wanted; // used by SES and DES
147 size_t resampling_group; // used by AVERAGE
148 NETDATA_DOUBLE resampling_divisor; // used by AVERAGE
135 - } grouping;
149 + } time_grouping;
150 +
151 + struct {
152 + struct rrdresult *r;
153 + } group_by;
154
155 struct {
156 time_t max_update_every;
@@ -143,6 +161,8 @@ typedef struct rrdresult {
161 struct {
162 ONEWAYALLOC *owa; // the allocator used
163 struct query_target *qt; // the QUERY_TARGET
164 + size_t contexts; // temp needed between json_wrapper_begin2() and json_wrapper_end2()
165 + size_t queries_count; // temp needed to know if a query is the first executed
166
167 #ifdef NETDATA_INTERNAL_CHECKS
168 const char *log;
web/api/queries/ses/ses.c
+7 -7
@@ -31,7 +31,7 @@ static inline NETDATA_DOUBLE window(RRDR *r, struct grouping_ses *g) {
31 NETDATA_DOUBLE points;
32 if(r->view.group == 1) {
33 // provide a running DES
34 - points = (NETDATA_DOUBLE)r->grouping.points_wanted;
34 + points = (NETDATA_DOUBLE)r->time_grouping.points_wanted;
35 }
36 else {
37 // provide a SES with flush points
@@ -52,24 +52,24 @@ void grouping_create_ses(RRDR *r, const char *options __maybe_unused) {
52 struct grouping_ses *g = (struct grouping_ses *)onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_ses));
53 set_alpha(r, g);
54 g->level = 0.0;
55 - r->grouping.data = g;
55 + r->time_grouping.data = g;
56 }
57
58 // resets when switches dimensions
59 // so, clear everything to restart
60 void grouping_reset_ses(RRDR *r) {
61 - struct grouping_ses *g = (struct grouping_ses *)r->grouping.data;
61 + struct grouping_ses *g = (struct grouping_ses *)r->time_grouping.data;
62 g->level = 0.0;
63 g->count = 0;
64 }
65
66 void grouping_free_ses(RRDR *r) {
67 - onewayalloc_freez(r->internal.owa, r->grouping.data);
68 - r->grouping.data = NULL;
67 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
68 + r->time_grouping.data = NULL;
69 }
70
71 void grouping_add_ses(RRDR *r, NETDATA_DOUBLE value) {
72 - struct grouping_ses *g = (struct grouping_ses *)r->grouping.data;
72 + struct grouping_ses *g = (struct grouping_ses *)r->time_grouping.data;
73
74 if(unlikely(!g->count))
75 g->level = value;
@@ -79,7 +79,7 @@ void grouping_add_ses(RRDR *r, NETDATA_DOUBLE value) {
79 }
80
81 NETDATA_DOUBLE grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
82 - struct grouping_ses *g = (struct grouping_ses *)r->grouping.data;
82 + struct grouping_ses *g = (struct grouping_ses *)r->time_grouping.data;
83
84 if(unlikely(!g->count || !netdata_double_isnumber(g->level))) {
85 *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
web/api/queries/stddev/stddev.c
+7 -7
@@ -15,23 +15,23 @@ struct grouping_stddev {
15 };
16
17 void grouping_create_stddev(RRDR *r, const char *options __maybe_unused) {
18 - r->grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_stddev));
18 + r->time_grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_stddev));
19 }
20
21 // resets when switches dimensions
22 // so, clear everything to restart
23 void grouping_reset_stddev(RRDR *r) {
24 - struct grouping_stddev *g = (struct grouping_stddev *)r->grouping.data;
24 + struct grouping_stddev *g = (struct grouping_stddev *)r->time_grouping.data;
25 g->count = 0;
26 }
27
28 void grouping_free_stddev(RRDR *r) {
29 - onewayalloc_freez(r->internal.owa, r->grouping.data);
30 - r->grouping.data = NULL;
29 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
30 + r->time_grouping.data = NULL;
31 }
32
33 void grouping_add_stddev(RRDR *r, NETDATA_DOUBLE value) {
34 - struct grouping_stddev *g = (struct grouping_stddev *)r->grouping.data;
34 + struct grouping_stddev *g = (struct grouping_stddev *)r->time_grouping.data;
35
36 g->count++;
37
@@ -62,7 +62,7 @@ static inline NETDATA_DOUBLE stddev(struct grouping_stddev *g) {
62 }
63
64 NETDATA_DOUBLE grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
65 - struct grouping_stddev *g = (struct grouping_stddev *)r->grouping.data;
65 + struct grouping_stddev *g = (struct grouping_stddev *)r->time_grouping.data;
66
67 NETDATA_DOUBLE value;
68
@@ -89,7 +89,7 @@ NETDATA_DOUBLE grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_optio
89
90 // https://en.wikipedia.org/wiki/Coefficient_of_variation
91 NETDATA_DOUBLE grouping_flush_coefficient_of_variation(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
92 - struct grouping_stddev *g = (struct grouping_stddev *)r->grouping.data;
92 + struct grouping_stddev *g = (struct grouping_stddev *)r->time_grouping.data;
93
94 NETDATA_DOUBLE value;
95
web/api/queries/sum/sum.c
+6 -6
@@ -11,30 +11,30 @@ struct grouping_sum {
11 };
12
13 void grouping_create_sum(RRDR *r, const char *options __maybe_unused) {
14 - r->grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_sum));
14 + r->time_grouping.data = onewayalloc_callocz(r->internal.owa, 1, sizeof(struct grouping_sum));
15 }
16
17 // resets when switches dimensions
18 // so, clear everything to restart
19 void grouping_reset_sum(RRDR *r) {
20 - struct grouping_sum *g = (struct grouping_sum *)r->grouping.data;
20 + struct grouping_sum *g = (struct grouping_sum *)r->time_grouping.data;
21 g->sum = 0;
22 g->count = 0;
23 }
24
25 void grouping_free_sum(RRDR *r) {
26 - onewayalloc_freez(r->internal.owa, r->grouping.data);
27 - r->grouping.data = NULL;
26 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
27 + r->time_grouping.data = NULL;
28 }
29
30 void grouping_add_sum(RRDR *r, NETDATA_DOUBLE value) {
31 - struct grouping_sum *g = (struct grouping_sum *)r->grouping.data;
31 + struct grouping_sum *g = (struct grouping_sum *)r->time_grouping.data;
32 g->sum += value;
33 g->count++;
34 }
35
36 NETDATA_DOUBLE grouping_flush_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
37 - struct grouping_sum *g = (struct grouping_sum *)r->grouping.data;
37 + struct grouping_sum *g = (struct grouping_sum *)r->time_grouping.data;
38
39 NETDATA_DOUBLE value;
40
web/api/queries/trimmed_mean/trimmed_mean.c
+7 -7
@@ -30,7 +30,7 @@ static void grouping_create_trimmed_mean_internal(RRDR *r, const char *options,
30 }
31
32 g->percent = 1.0 - ((g->percent / 100.0) * 2.0);
33 - r->grouping.data = g;
33 + r->time_grouping.data = g;
34 }
35
36 void grouping_create_trimmed_mean1(RRDR *r, const char *options) {
@@ -61,20 +61,20 @@ void grouping_create_trimmed_mean25(RRDR *r, const char *options) {
61 // resets when switches dimensions
62 // so, clear everything to restart
63 void grouping_reset_trimmed_mean(RRDR *r) {
64 - struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->grouping.data;
64 + struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->time_grouping.data;
65 g->next_pos = 0;
66 }
67
68 void grouping_free_trimmed_mean(RRDR *r) {
69 - struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->grouping.data;
69 + struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->time_grouping.data;
70 if(g) onewayalloc_freez(r->internal.owa, g->series);
71
72 - onewayalloc_freez(r->internal.owa, r->grouping.data);
73 - r->grouping.data = NULL;
72 + onewayalloc_freez(r->internal.owa, r->time_grouping.data);
73 + r->time_grouping.data = NULL;
74 }
75
76 void grouping_add_trimmed_mean(RRDR *r, NETDATA_DOUBLE value) {
77 - struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->grouping.data;
77 + struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->time_grouping.data;
78
79 if(unlikely(g->next_pos >= g->series_size)) {
80 g->series = onewayalloc_doublesize( r->internal.owa, g->series, g->series_size * sizeof(NETDATA_DOUBLE));
@@ -85,7 +85,7 @@ void grouping_add_trimmed_mean(RRDR *r, NETDATA_DOUBLE value) {
85 }
86
87 NETDATA_DOUBLE grouping_flush_trimmed_mean(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
88 - struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->grouping.data;
88 + struct grouping_trimmed_mean *g = (struct grouping_trimmed_mean *)r->time_grouping.data;
89
90 NETDATA_DOUBLE value;
91 size_t available_slots = g->next_pos;
web/api/web_api.c
+5
@@ -57,3 +57,8 @@ RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o) {
57
58 return options;
59 }
60 +
61 +bool web_client_interrupt_callback(void *data) {
62 + struct web_client *w = data;
63 + return sock_has_output_error(w->ofd);
64 +}
\ No newline at end of file
web/api/web_api.h
+2
@@ -29,6 +29,8 @@ static inline void fix_google_param(char *s) {
29 }
30 }
31
32 +bool web_client_interrupt_callback(void *data);
33 +
34 #include "web_api_v1.h"
35 #include "web_api_v2.h"
36
web/api/web_api_v1.c
+2
@@ -43,7 +43,9 @@ static struct {
43 , {"all-dimensions" , 0 , RRDR_OPTION_ALL_DIMENSIONS}
44 , {"details" , 0 , RRDR_OPTION_SHOW_DETAILS}
45 , {"debug" , 0 , RRDR_OPTION_DEBUG}
46 + , {"plan" , 0 , RRDR_OPTION_DEBUG}
47 , {"minify" , 0 , RRDR_OPTION_MINIFY}
48 + , {"group-by-labels" , 0 , RRDR_OPTION_GROUP_BY_LABELS}
49 , {NULL , 0 , 0}
50 };
51
web/api/web_api_v2.c
+5 -2
@@ -79,7 +79,7 @@ static int web_client_api_request_v2_data(RRDHOST *host __maybe_unused, struct w
79 RRDR_TIME_GROUPING time_group = RRDR_GROUPING_AVERAGE;
80 RRDR_GROUP_BY group_by = RRDR_GROUP_BY_DIMENSION;
81 RRDR_GROUP_BY_FUNCTION group_by_aggregate = RRDR_GROUP_BY_FUNCTION_AVERAGE;
82 - DATASOURCE_FORMAT format = DATASOURCE_JSON;
82 + DATASOURCE_FORMAT format = DATASOURCE_JSON2;
83 RRDR_OPTIONS options = RRDR_OPTION_VIRTUAL_POINTS | RRDR_OPTION_JSON_WRAP | RRDR_OPTION_RETURN_JWAR;
84
85 while(url) {
@@ -166,7 +166,7 @@ static int web_client_api_request_v2_data(RRDHOST *host __maybe_unused, struct w
166 if(group_by & RRDR_GROUP_BY_SELECTED)
167 group_by = RRDR_GROUP_BY_SELECTED; // remove all other groupings
168
169 - if(group_by & ~(RRDR_GROUP_BY_DIMENSION))
169 + if((group_by & ~(RRDR_GROUP_BY_DIMENSION)) || (options & RRDR_OPTION_PERCENTAGE))
170 options |= RRDR_OPTION_ABSOLUTE;
171
172 if(options & RRDR_OPTION_DEBUG)
@@ -215,6 +215,9 @@ static int web_client_api_request_v2_data(RRDHOST *host __maybe_unused, struct w
215 .query_source = QUERY_SOURCE_API_DATA,
216 .priority = STORAGE_PRIORITY_NORMAL,
217 .received_ut = received_ut,
218 +
219 + .interrupt_callback = web_client_interrupt_callback,
220 + .interrupt_callback_data = w,
221 };
222 QUERY_TARGET *qt = query_target_create(&qtr);
223 ONEWAYALLOC *owa = NULL;