@cryptotaxi247 / netdata-1 / commits / 09e89e937

modularize the query api (#4443)

* modularized exporters * modularized API data queries * optimized queries * modularized API data reduction methods * modularized api queries * added new directories in makefiles * added median db query * moved all RRDR_GROUPING related to query.h * added stddev query * operational median and stddev * working simple exponential smoothing * too complex to do it right * fixed ses * fixed ses * rewrote query engine * fix double-exponential-smoothing * cleanup * fixed bug identified by @vlvkobal at rrdset_first_slot() * enable freeipmi on systems with libipmimonitoring; #4440

Costa Tsaousis committed Oct 23, 2018 at 00:38 UTC 09e89e937af3bcc5948b92c722c8da93970bd987
79 files changed +3242 -2140
CMakeLists.txt
+36 -14
@@ -124,16 +124,15 @@ IF(LINUX)
124 # ${MNL_INCLUDE_DIRS}
125 ENDIF(LINUX)
126
127 +
128 # -----------------------------------------------------------------------------
129 # Detect libmnl
130
130 -IF(LINUX)
131 - pkg_check_modules(NFACCT libnetfilter_acct)
132 - # later we use:
133 - # ${NFACCT_LIBRARIES}
134 - # ${NFACCT_CFLAGS_OTHER}
135 - # ${NFACCT_INCLUDE_DIRS}
136 -ENDIF(LINUX)
131 +pkg_check_modules(NFACCT libnetfilter_acct)
132 +# later we use:
133 +# ${NFACCT_LIBRARIES}
134 +# ${NFACCT_CFLAGS_OTHER}
135 +# ${NFACCT_INCLUDE_DIRS}
136
137
138 # -----------------------------------------------------------------------------
@@ -370,6 +369,30 @@ set(API_PLUGIN_FILES
369 web/api/web_api_v1.h
370 web/api/badges/web_buffer_svg.c
371 web/api/badges/web_buffer_svg.h
372 + web/api/exporters/allmetrics.c
373 + web/api/exporters/allmetrics.h
374 + web/api/exporters/shell/allmetrics_shell.c
375 + web/api/exporters/shell/allmetrics_shell.h
376 + web/api/queries/rrdr.c
377 + web/api/queries/rrdr.h
378 + web/api/queries/query.c
379 + web/api/queries/query.h
380 + web/api/queries/average/average.c
381 + web/api/queries/average/average.h
382 + web/api/queries/incremental_sum/incremental_sum.c
383 + web/api/queries/incremental_sum/incremental_sum.h
384 + web/api/queries/max/max.c
385 + web/api/queries/max/max.h
386 + web/api/queries/min/min.c
387 + web/api/queries/min/min.h
388 + web/api/queries/sum/sum.c
389 + web/api/queries/sum/sum.h
390 + web/api/queries/median/median.c
391 + web/api/queries/median/median.h
392 + web/api/queries/stddev/stddev.c
393 + web/api/queries/stddev/stddev.h
394 + web/api/queries/ses/ses.c
395 + web/api/queries/ses/ses.h
396 )
397
398 set(STREAMING_PLUGIN_FILES
@@ -470,11 +493,6 @@ IF(LINUX)
493
494 SET(ENABLE_PLUGIN_CGROUP_NETWORK True)
495 SET(ENABLE_PLUGIN_APPS True)
473 - IF(IPMI_LIBRARIES)
474 - SET(ENABLE_PLUGIN_FREEIPMI True)
475 - ELSE()
476 - SET(ENABLE_PLUGIN_FREEIPMI False)
477 - ENDIF()
496
497 ELSEIF(FREEBSD)
498 add_executable(netdata config.h ${NETDATA_FILES} ${FREEBSD_PLUGIN_FILES})
@@ -483,7 +501,6 @@ ELSEIF(FREEBSD)
501 target_compile_options(netdata PUBLIC ${NETDATA_COMMON_CFLAGS})
502 SET(ENABLE_PLUGIN_CGROUP_NETWORK False)
503 SET(ENABLE_PLUGIN_APPS True)
486 - SET(ENABLE_PLUGIN_FREEIPMI False)
504
505 ELSEIF(MACOS)
506 add_executable(netdata config.h ${NETDATA_FILES} ${MACOS_PLUGIN_FILES})
@@ -492,10 +509,15 @@ ELSEIF(MACOS)
509 target_compile_options(netdata PUBLIC ${NETDATA_COMMON_CFLAGS})
510 SET(ENABLE_PLUGIN_CGROUP_NETWORK False)
511 SET(ENABLE_PLUGIN_APPS False)
495 - SET(ENABLE_PLUGIN_FREEIPMI False)
512
513 ENDIF()
514
515 +IF(IPMI_LIBRARIES)
516 + SET(ENABLE_PLUGIN_FREEIPMI True)
517 +ELSE()
518 + SET(ENABLE_PLUGIN_FREEIPMI False)
519 +ENDIF()
520 +
521
522 # -----------------------------------------------------------------------------
523 # apps.plugin
Makefile.am
+26 -2
@@ -283,12 +283,36 @@ RRD_PLUGIN_FILES = \
283 $(NULL)
284
285 API_PLUGIN_FILES = \
286 + web/api/badges/web_buffer_svg.c \
287 + web/api/badges/web_buffer_svg.h \
288 + web/api/exporters/allmetrics.c \
289 + web/api/exporters/allmetrics.h \
290 + web/api/exporters/shell/allmetrics_shell.c \
291 + web/api/exporters/shell/allmetrics_shell.h \
292 + web/api/queries/average/average.c \
293 + web/api/queries/average/average.h \
294 + web/api/queries/incremental_sum/incremental_sum.c \
295 + web/api/queries/incremental_sum/incremental_sum.h \
296 + web/api/queries/max/max.c \
297 + web/api/queries/max/max.h \
298 + web/api/queries/median/median.c \
299 + web/api/queries/median/median.h \
300 + web/api/queries/min/min.c \
301 + web/api/queries/min/min.h \
302 + web/api/queries/query.c \
303 + web/api/queries/query.h \
304 + web/api/queries/rrdr.c \
305 + web/api/queries/rrdr.h \
306 + web/api/queries/ses/ses.c \
307 + web/api/queries/ses/ses.h \
308 + web/api/queries/stddev/stddev.c \
309 + web/api/queries/stddev/stddev.h \
310 + web/api/queries/sum/sum.c \
311 + web/api/queries/sum/sum.h \
312 web/api/rrd2json.c \
313 web/api/rrd2json.h \
314 web/api/web_api_v1.c \
315 web/api/web_api_v1.h \
290 - web/api/badges/web_buffer_svg.c \
291 - web/api/badges/web_buffer_svg.h \
316 $(NULL)
317
318 STREAMING_PLUGIN_FILES = \
collectors/cgroups.plugin/cgroup-network.c
+1 -1
@@ -1,6 +1,6 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#include "../../daemon/common.h"
3 +#include "libnetdata/libnetdata.h"
4
5 #ifdef HAVE_SETNS
6 #ifndef _GNU_SOURCE
configure.ac
+12
@@ -604,6 +604,18 @@ AC_CONFIG_FILES([
604 web/Makefile
605 web/api/Makefile
606 web/api/badges/Makefile
607 + web/api/exporters/Makefile
608 + web/api/exporters/shell/Makefile
609 + web/api/exporters/prometheus/Makefile
610 + web/api/queries/Makefile
611 + web/api/queries/average/Makefile
612 + web/api/queries/incremental_sum/Makefile
613 + web/api/queries/max/Makefile
614 + web/api/queries/median/Makefile
615 + web/api/queries/min/Makefile
616 + web/api/queries/ses/Makefile
617 + web/api/queries/stddev/Makefile
618 + web/api/queries/sum/Makefile
619 web/gui/Makefile
620 web/server/Makefile
621 web/server/single/Makefile
database/rrd.h
+72 -16
@@ -15,7 +15,7 @@ typedef struct rrdcalctemplate RRDCALCTEMPLATE;
15 typedef struct alarm_entry ALARM_ENTRY;
16
17 #include "../daemon/common.h"
18 -
18 +#include "web/api/queries/query.h"
19 #include "rrdvar.h"
20 #include "rrdsetvar.h"
21 #include "rrddimvar.h"
@@ -749,28 +749,84 @@ extern void rrdset_isnot_obsolete(RRDSET *st);
749 #define rrdset_first_entry_t(st) ((time_t)(rrdset_last_entry_t(st) - rrdset_duration(st)))
750
751 // get the last slot updated in the round robin database
752 -#define rrdset_last_slot(st) ((unsigned long)(((st)->current_entry == 0) ? (st)->entries - 1 : (st)->current_entry - 1))
752 +#define rrdset_last_slot(st) ((size_t)(((st)->current_entry == 0) ? (st)->entries - 1 : (st)->current_entry - 1))
753
754 // get the first / oldest slot updated in the round robin database
755 -#define rrdset_first_slot(st) ((unsigned long)( (((st)->counter >= ((unsigned long)(st)->entries)) ? (unsigned long)( ((unsigned long)(st)->current_entry > 0) ? ((unsigned long)(st)->current_entry) : ((unsigned long)(st)->entries) ) - 1 : 0) ))
755 +// #define rrdset_first_slot(st) ((size_t)( (((st)->counter >= ((unsigned long)(st)->entries)) ? (unsigned long)( ((unsigned long)(st)->current_entry > 0) ? ((unsigned long)(st)->current_entry) : ((unsigned long)(st)->entries) ) - 1 : 0) ))
756 +
757 +// return the slot that has the oldest value
758 +
759 +static inline size_t rrdset_first_slot(RRDSET *st) {
760 + if(st->counter >= (size_t)st->entries) {
761 + // the database has been rotated at least once
762 + // the oldest entry is the one that will be next
763 + // overwritten by data collection
764 + return (size_t)st->current_entry;
765 + }
766 +
767 + // we do not have rotated the db yet
768 + // so 0 is the first entry
769 + return 0;
770 +}
771
772 // get the slot of the round robin database, for the given timestamp (t)
773 // it always returns a valid slot, although may not be for the time requested if the time is outside the round robin database
759 -#define rrdset_time2slot(st, t) ( \
760 - ( (time_t)(t) >= rrdset_last_entry_t(st)) ? ( rrdset_last_slot(st) ) : \
761 - ( ((time_t)(t) <= rrdset_first_entry_t(st)) ? rrdset_first_slot(st) : \
762 - ( (rrdset_last_slot(st) >= (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) ? \
763 - (rrdset_last_slot(st) - (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) ) : \
764 - (rrdset_last_slot(st) - (unsigned long)((rrdset_last_entry_t(st) - (time_t)(t)) / (unsigned long)((st)->update_every)) + (unsigned long)(st)->entries ) \
765 - )))
774 +static inline size_t rrdset_time2slot(RRDSET *st, time_t t) {
775 + size_t ret = 0;
776 +
777 + if(t >= rrdset_last_entry_t(st)) {
778 + // the requested time is after the last entry we have
779 + ret = rrdset_last_slot(st);
780 + }
781 + else {
782 + if(t <= rrdset_first_entry_t(st)) {
783 + // the requested time is before the first entry we have
784 + ret = rrdset_first_slot(st);
785 + }
786 + else {
787 + if(rrdset_last_slot(st) >= ((rrdset_last_entry_t(st) - t) / (size_t)(st->update_every)))
788 + ret = rrdset_last_slot(st) - ((rrdset_last_entry_t(st) - t) / (size_t)(st->update_every));
789 + else
790 + ret = rrdset_last_slot(st) - ((rrdset_last_entry_t(st) - t) / (size_t)(st->update_every)) + (unsigned long)st->entries;
791 + }
792 + }
793 +
794 + if(unlikely(ret >= (size_t)st->entries)) {
795 + error("INTERNAL ERROR: rrdset_time2slot() on %s returns values outside entries", st->name);
796 + ret = (size_t)(st->entries - 1);
797 + }
798 +
799 + return ret;
800 +}
801
802 // get the timestamp of a specific slot in the round robin database
768 -#define rrdset_slot2time(st, slot) ( rrdset_last_entry_t(st) - \
769 - ((unsigned long)(st)->update_every * ( \
770 - ( (unsigned long)(slot) > rrdset_last_slot(st)) ? \
771 - ( (rrdset_last_slot(st) - (unsigned long)(slot) + (unsigned long)(st)->entries) ) : \
772 - ( (rrdset_last_slot(st) - (unsigned long)(slot)) )) \
773 - ))
803 +static inline time_t rrdset_slot2time(RRDSET *st, size_t slot) {
804 + time_t ret;
805 +
806 + if(slot >= (size_t)st->entries) {
807 + error("INTERNAL ERROR: caller of rrdset_slot2time() gives invalid slot %zu", slot);
808 + slot = (size_t)st->entries - 1;
809 + }
810 +
811 + if(slot > rrdset_last_slot(st)) {
812 + ret = rrdset_last_entry_t(st) - (size_t)st->update_every * (rrdset_last_slot(st) - slot + (size_t)st->entries);
813 + }
814 + else {
815 + ret = rrdset_last_entry_t(st) - (size_t)st->update_every;
816 + }
817 +
818 + if(unlikely(ret < rrdset_first_entry_t(st))) {
819 + error("INTERNAL ERROR: rrdset_slot2time() on %s returns time too far in the past", st->name);
820 + ret = rrdset_first_entry_t(st);
821 + }
822 +
823 + if(unlikely(ret > rrdset_last_entry_t(st))) {
824 + error("INTERNAL ERROR: rrdset_slot2time() on %s returns time into the future", st->name);
825 + ret = rrdset_last_entry_t(st);
826 + }
827 +
828 + return ret;
829 +}
830
831 // ----------------------------------------------------------------------------
832 // RRD DIMENSION functions
database/rrdcalc.c
+1 -1
@@ -363,7 +363,7 @@ inline RRDCALC *rrdcalc_create(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *c
363 (rc->recipient)?rc->recipient:"DEFAULT",
364 rc->green,
365 rc->red,
366 - rc->group,
366 + (int)rc->group,
367 rc->after,
368 rc->before,
369 rc->options,
database/rrdcalc.h
+1 -1
@@ -54,7 +54,7 @@ struct rrdcalc {
54 // database lookup settings
55
56 char *dimensions; // the chart dimensions
57 - int group; // grouping method: average, max, etc.
57 + RRDR_GROUPING group; // grouping method: average, max, etc.
58 int before; // ending point in time-series
59 int after; // starting point in time-series
60 uint32_t options; // calculation options
database/rrdcalctemplate.h
+1 -1
@@ -35,7 +35,7 @@ struct rrdcalctemplate {
35 // database lookup settings
36
37 char *dimensions; // the chart dimensions
38 - int group; // grouping method: average, max, etc.
38 + RRDR_GROUPING group; // grouping method: average, max, etc.
39 int before; // ending point in time-series
40 int after; // starting point in time-series
41 uint32_t options; // calculation options
database/rrdvar.h
+7 -5
@@ -3,7 +3,7 @@
3 #ifndef NETDATA_RRDVAR_H
4 #define NETDATA_RRDVAR_H 1
5
6 -#include "rrd.h"
6 +#include "libnetdata/libnetdata.h"
7
8 extern int rrdvar_compare(void *a, void *b);
9
@@ -45,6 +45,12 @@ struct rrdvar {
45 time_t last_updated;
46 };
47
48 +#define RRDVAR_MAX_LENGTH 1024
49 +
50 +extern int rrdvar_fix_name(char *variable);
51 +
52 +#include "rrd.h"
53 +
54 extern RRDVAR *rrdvar_custom_host_variable_create(RRDHOST *host, const char *name);
55 extern void rrdvar_custom_host_variable_set(RRDHOST *host, RRDVAR *rv, calculated_number value);
56 extern int foreach_host_variable_callback(RRDHOST *host, int (*callback)(RRDVAR *rv, void *data), void *data);
@@ -54,10 +60,6 @@ extern int rrdvar_callback_for_all_host_variables(RRDHOST *host, int (*callback
60
61 extern calculated_number rrdvar2number(RRDVAR *rv);
62
57 -#define RRDVAR_MAX_LENGTH 1024
58 -
59 -extern int rrdvar_fix_name(char *variable);
60 -
63 extern RRDVAR *rrdvar_create_and_index(const char *scope, avl_tree_lock *tree, const char *name, RRDVAR_TYPE type, RRDVAR_OPTIONS options, void *value);
64 extern void rrdvar_free(RRDHOST *host, avl_tree_lock *tree, RRDVAR *rv);
65
health/health.c
-1
@@ -1,6 +1,5 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define NETDATA_HEALTH_INTERNALS
3 #include "health.h"
4
5 unsigned int default_health_enabled = 1;
health/health.d/net.conf
+2 -2
@@ -101,7 +101,7 @@ template: 1m_received_packets_rate
101 os: linux freebsd
102 hosts: *
103 families: *
104 - lookup: average -1m of received
104 + lookup: average -1m unaligned of received
105 units: packets
106 every: 10s
107 info: the average number of packets received during the last minute
@@ -111,7 +111,7 @@ template: 10s_received_packets_storm
111 os: linux freebsd
112 hosts: *
113 families: *
114 - lookup: average -10s of received
114 + lookup: average -10s unaligned of received
115 calc: $this * 100 / (($1m_received_packets_rate < 1000)?(1000):($1m_received_packets_rate))
116 every: 10s
117 units: %
health/health_config.c
+4 -5
@@ -1,6 +1,5 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define NETDATA_HEALTH_INTERNALS
3 #include "health.h"
4
5 #define HEALTH_CONF_MAX_LINE 4096
@@ -54,7 +53,7 @@ static inline int rrdcalc_add_alarm_from_config(RRDHOST *host, RRDCALC *rc) {
53 (rc->recipient)?rc->recipient:"DEFAULT",
54 rc->green,
55 rc->red,
57 - rc->group,
56 + (int)rc->group,
57 rc->after,
58 rc->before,
59 rc->options,
@@ -108,7 +107,7 @@ static inline int rrdcalctemplate_add_template_from_config(RRDHOST *host, RRDCAL
107 (rt->recipient)?rt->recipient:"DEFAULT",
108 rt->green,
109 rt->red,
111 - rt->group,
110 + (int)rt->group,
111 rt->after,
112 rt->before,
113 rt->options,
@@ -288,7 +287,7 @@ static inline uint32_t health_parse_options(const char *s) {
287
288 static inline int health_parse_db_lookup(
289 size_t line, const char *filename, char *string,
291 - int *group_method, int *after, int *before, int *every,
290 + RRDR_GROUPING *group_method, int *after, int *before, int *every,
291 uint32_t *options, char **dimensions
292 ) {
293 debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s: %s", line, filename, string);
@@ -312,7 +311,7 @@ static inline int health_parse_db_lookup(
311 return 0;
312 }
313
315 - if((*group_method = web_client_api_request_v1_data_group(key, -1)) == -1) {
314 + if((*group_method = web_client_api_request_v1_data_group(key, RRDR_GROUPING_UNDEFINED)) == RRDR_GROUPING_UNDEFINED) {
315 error("Health configuration at line %zu of file '%s': invalid group method '%s'",
316 line, filename, key);
317 return 0;
health/health_json.c
-1
@@ -1,6 +1,5 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define NETDATA_HEALTH_INTERNALS
3 #include "health.h"
4
5 static inline void health_string2json(BUFFER *wb, const char *prefix, const char *label, const char *value, const char *suffix) {
health/health_log.c
-1
@@ -1,6 +1,5 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#define NETDATA_HEALTH_INTERNALS
3 #include "health.h"
4
5 // ----------------------------------------------------------------------------
libnetdata/statistical/statistical.c
+90 -99
@@ -2,38 +2,35 @@
2
3 #include "../libnetdata.h"
4
5 -// --------------------------------------------------------------------------------------------------------------------
5 +LONG_DOUBLE default_single_exponential_smoothing_alpha = 0.1;
6
7 -inline LONG_DOUBLE sum_and_count(const LONG_DOUBLE *series, size_t entries, size_t *count) {
8 - if(unlikely(entries == 0)) {
9 - if(likely(count))
10 - *count = 0;
7 +void log_series_to_stderr(LONG_DOUBLE *series, size_t entries, calculated_number result, const char *msg) {
8 + const LONG_DOUBLE *value, *end = &series[entries];
9
12 - return NAN;
10 + fprintf(stderr, "%s of %zu entries [ ", msg, entries);
11 + for(value = series; value < end ;value++) {
12 + if(value != series) fprintf(stderr, ", ");
13 + fprintf(stderr, "%" LONG_DOUBLE_MODIFIER, *value);
14 }
15 + fprintf(stderr, " ] results in " CALCULATED_NUMBER_FORMAT "\n", result);
16 +}
17
15 - if(unlikely(entries == 1)) {
16 - if(likely(count))
17 - *count = (isnan(series[0])?0:1);
18 -
19 - return series[0];
20 - }
18 +// --------------------------------------------------------------------------------------------------------------------
19
22 - size_t i, c = 0;
20 +inline LONG_DOUBLE sum_and_count(const LONG_DOUBLE *series, size_t entries, size_t *count) {
21 + const LONG_DOUBLE *value, *end = &series[entries];
22 LONG_DOUBLE sum = 0;
23 + size_t c = 0;
24
25 - for(i = 0; i < entries ; i++) {
26 - LONG_DOUBLE value = series[i];
27 - if(unlikely(isnan(value) || isinf(value))) continue;
28 - c++;
29 - sum += value;
25 + for(value = series; value < end ; value++) {
26 + if(isnormal(*value)) {
27 + sum += *value;
28 + c++;
29 + }
30 }
31
32 - if(likely(count))
33 - *count = c;
34 -
35 - if(unlikely(c == 0))
36 - return NAN;
32 + if(unlikely(!c)) sum = NAN;
33 + if(likely(count)) *count = c;
34
35 return sum;
36 }
@@ -46,9 +43,7 @@ inline LONG_DOUBLE average(const LONG_DOUBLE *series, size_t entries) {
43 size_t count = 0;
44 LONG_DOUBLE sum = sum_and_count(series, entries, &count);
45
49 - if(unlikely(count == 0))
50 - return NAN;
51 -
46 + if(unlikely(!count)) return NAN;
47 return sum / (LONG_DOUBLE)count;
48 }
49
@@ -67,7 +62,7 @@ LONG_DOUBLE moving_average(const LONG_DOUBLE *series, size_t entries, size_t per
62
63 for(i = 0, count = 0; i < entries; i++) {
64 LONG_DOUBLE value = series[i];
70 - if(unlikely(isnan(value) || isinf(value))) continue;
65 + if(unlikely(!isnormal(value))) continue;
66
67 if(unlikely(count < period)) {
68 sum += value;
@@ -118,33 +113,25 @@ inline LONG_DOUBLE *copy_series(const LONG_DOUBLE *series, size_t entries) {
113 }
114
115 LONG_DOUBLE median_on_sorted_series(const LONG_DOUBLE *series, size_t entries) {
121 - if(unlikely(entries == 0))
122 - return NAN;
116 + if(unlikely(entries == 0)) return NAN;
117 + if(unlikely(entries == 1)) return series[0];
118 + if(unlikely(entries == 2)) return (series[0] + series[1]) / 2;
119
124 - if(unlikely(entries == 1))
125 - return series[0];
126 -
127 - if(unlikely(entries == 2))
128 - return (series[0] + series[1]) / 2;
129 -
130 - LONG_DOUBLE avg;
120 + LONG_DOUBLE average;
121 if(entries % 2 == 0) {
122 size_t m = entries / 2;
133 - avg = (series[m] + series[m + 1]) / 2;
123 + average = (series[m] + series[m + 1]) / 2;
124 }
125 else {
136 - avg = series[entries / 2];
126 + average = series[entries / 2];
127 }
128
139 - return avg;
129 + return average;
130 }
131
132 LONG_DOUBLE median(const LONG_DOUBLE *series, size_t entries) {
143 - if(unlikely(entries == 0))
144 - return NAN;
145 -
146 - if(unlikely(entries == 1))
147 - return series[0];
133 + if(unlikely(entries == 0)) return NAN;
134 + if(unlikely(entries == 1)) return series[0];
135
136 if(unlikely(entries == 2))
137 return (series[0] + series[1]) / 2;
@@ -186,7 +173,7 @@ LONG_DOUBLE running_median_estimate(const LONG_DOUBLE *series, size_t entries) {
173
174 for(i = 0; i < entries ; i++) {
175 LONG_DOUBLE value = series[i];
189 - if(unlikely(isnan(value) || isinf(value))) continue;
176 + if(unlikely(!isnormal(value))) continue;
177
178 average += ( value - average ) * 0.1f; // rough running average.
179 median += copysignl( average * 0.01, value - median );
@@ -198,47 +185,36 @@ LONG_DOUBLE running_median_estimate(const LONG_DOUBLE *series, size_t entries) {
185 // --------------------------------------------------------------------------------------------------------------------
186
187 LONG_DOUBLE standard_deviation(const LONG_DOUBLE *series, size_t entries) {
201 - if(unlikely(entries < 1))
202 - return NAN;
188 + if(unlikely(entries == 0)) return NAN;
189 + if(unlikely(entries == 1)) return series[0];
190
204 - if(unlikely(entries == 1))
205 - return series[0];
191 + const LONG_DOUBLE *value, *end = &series[entries];
192 + size_t count;
193 + LONG_DOUBLE sum;
194
207 - size_t i, count = 0;
208 - LONG_DOUBLE sum = 0;
209 -
210 - for(i = 0; i < entries ; i++) {
211 - LONG_DOUBLE value = series[i];
212 - if(unlikely(isnan(value) || isinf(value))) continue;
213 -
214 - count++;
215 - sum += value;
195 + for(count = 0, sum = 0, value = series ; value < end ;value++) {
196 + if(likely(isnormal(*value))) {
197 + count++;
198 + sum += *value;
199 + }
200 }
201
218 - if(unlikely(count == 0))
219 - return NAN;
220 -
221 - if(unlikely(count == 1))
222 - return sum;
202 + if(unlikely(count == 0)) return NAN;
203 + if(unlikely(count == 1)) return sum;
204
205 LONG_DOUBLE average = sum / (LONG_DOUBLE)count;
206
226 - for(i = 0, count = 0, sum = 0; i < entries ; i++) {
227 - LONG_DOUBLE value = series[i];
228 - if(unlikely(isnan(value) || isinf(value))) continue;
229 -
230 - count++;
231 - sum += powl(value - average, 2);
207 + for(count = 0, sum = 0, value = series ; value < end ;value++) {
208 + if(isnormal(*value)) {
209 + count++;
210 + sum += powl(*value - average, 2);
211 + }
212 }
213
234 - if(unlikely(count == 0))
235 - return NAN;
236 -
237 - if(unlikely(count == 1))
238 - return average;
239 -
240 - LONG_DOUBLE variance = sum / (LONG_DOUBLE)(count - 1); // remove -1 to have a population stddev
214 + if(unlikely(count == 0)) return NAN;
215 + if(unlikely(count == 1)) return average;
216
217 + LONG_DOUBLE variance = sum / (LONG_DOUBLE)(count); // remove -1 from count to have a population stddev
218 LONG_DOUBLE stddev = sqrtl(variance);
219 return stddev;
220 }
@@ -246,21 +222,36 @@ LONG_DOUBLE standard_deviation(const LONG_DOUBLE *series, size_t entries) {
222 // --------------------------------------------------------------------------------------------------------------------
223
224 LONG_DOUBLE single_exponential_smoothing(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha) {
249 - size_t i, count = 0;
250 - LONG_DOUBLE level = 0, sum = 0;
225 + if(unlikely(entries == 0))
226 + return NAN;
227
228 if(unlikely(isnan(alpha)))
253 - alpha = 0.3;
229 + alpha = default_single_exponential_smoothing_alpha;
230
255 - for(i = 0; i < entries ; i++) {
256 - LONG_DOUBLE value = series[i];
257 - if(unlikely(isnan(value) || isinf(value))) continue;
258 - count++;
231 + const LONG_DOUBLE *value = series, *end = &series[entries];
232 + LONG_DOUBLE level = (1.0 - alpha) * (*value);
233
260 - sum += value;
234 + for(value++ ; value < end; value++) {
235 + if(likely(isnormal(*value)))
236 + level = alpha * (*value) + (1.0 - alpha) * level;
237 + }
238
262 - LONG_DOUBLE last_level = level;
263 - level = alpha * value + (1.0 - alpha) * last_level;
239 + return level;
240 +}
241 +
242 +LONG_DOUBLE single_exponential_smoothing_reverse(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha) {
243 + if(unlikely(entries == 0))
244 + return NAN;
245 +
246 + if(unlikely(isnan(alpha)))
247 + alpha = default_single_exponential_smoothing_alpha;
248 +
249 + const LONG_DOUBLE *value = &series[entries -1];
250 + LONG_DOUBLE level = (1.0 - alpha) * (*value);
251 +
252 + for(value++ ; value >= series; value--) {
253 + if(likely(isnormal(*value)))
254 + level = alpha * (*value) + (1.0 - alpha) * level;
255 }
256
257 return level;
@@ -270,8 +261,10 @@ LONG_DOUBLE single_exponential_smoothing(const LONG_DOUBLE *series, size_t entri
261
262 // http://grisha.org/blog/2016/02/16/triple-exponential-smoothing-forecasting-part-ii/
263 LONG_DOUBLE double_exponential_smoothing(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha, LONG_DOUBLE beta, LONG_DOUBLE *forecast) {
273 - size_t i, count = 0;
274 - LONG_DOUBLE level = series[0], trend, sum;
264 + if(unlikely(entries == 0))
265 + return NAN;
266 +
267 + LONG_DOUBLE level, trend;
268
269 if(unlikely(isnan(alpha)))
270 alpha = 0.3;
@@ -279,24 +272,22 @@ LONG_DOUBLE double_exponential_smoothing(const LONG_DOUBLE *series, size_t entri
272 if(unlikely(isnan(beta)))
273 beta = 0.05;
274
275 + level = series[0];
276 +
277 if(likely(entries > 1))
278 trend = series[1] - series[0];
279 else
280 trend = 0;
281
287 - sum = series[0];
288 -
289 - for(i = 1; i < entries ; i++) {
290 - LONG_DOUBLE value = series[i];
291 - if(unlikely(isnan(value) || isinf(value))) continue;
292 - count++;
282 + const LONG_DOUBLE *value = series;
283 + for(value++ ; value >= series; value--) {
284 + if(likely(isnormal(*value))) {
285
294 - sum += value;
286 + LONG_DOUBLE last_level = level;
287 + level = alpha * *value + (1.0 - alpha) * (level + trend);
288 + trend = beta * (level - last_level) + (1.0 - beta) * trend;
289
296 - LONG_DOUBLE last_level = level;
297 -
298 - level = alpha * value + (1.0 - alpha) * (level + trend);
299 - trend = beta * (level - last_level) + (1.0 - beta) * trend;
290 + }
291 }
292
293 if(forecast)
libnetdata/statistical/statistical.h
+7
@@ -5,6 +5,12 @@
5
6 #include "../libnetdata.h"
7
8 +#ifndef isnormal
9 +#define isnormal(x) (fpclassify(x) == FP_NORMAL)
10 +#endif
11 +
12 +extern void log_series_to_stderr(LONG_DOUBLE *series, size_t entries, calculated_number result, const char *msg);
13 +
14 extern LONG_DOUBLE average(const LONG_DOUBLE *series, size_t entries);
15 extern LONG_DOUBLE moving_average(const LONG_DOUBLE *series, size_t entries, size_t period);
16 extern LONG_DOUBLE median(const LONG_DOUBLE *series, size_t entries);
@@ -12,6 +18,7 @@ extern LONG_DOUBLE moving_median(const LONG_DOUBLE *series, size_t entries, size
18 extern LONG_DOUBLE running_median_estimate(const LONG_DOUBLE *series, size_t entries);
19 extern LONG_DOUBLE standard_deviation(const LONG_DOUBLE *series, size_t entries);
20 extern LONG_DOUBLE single_exponential_smoothing(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha);
21 +extern LONG_DOUBLE single_exponential_smoothing_reverse(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha);
22 extern LONG_DOUBLE double_exponential_smoothing(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha, LONG_DOUBLE beta, LONG_DOUBLE *forecast);
23 extern LONG_DOUBLE holtwinters(const LONG_DOUBLE *series, size_t entries, LONG_DOUBLE alpha, LONG_DOUBLE beta, LONG_DOUBLE gamma, LONG_DOUBLE *forecast);
24 extern LONG_DOUBLE sum_and_count(const LONG_DOUBLE *series, size_t entries, size_t *count);
web/api/Makefile.am
+6
@@ -5,8 +5,14 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5
6 SUBDIR = \
7 badges \
8 + queries \
9 $(NULL)
10
11 dist_noinst_DATA = \
12 README.md \
13 $(NULL)
14 +
15 +dist_web_DATA = \
16 + netdata-swagger.yaml \
17 + netdata-swagger.json \
18 + $(NULL)
web/api/README.md
-65
@@ -16,68 +16,3 @@ and this [multi chart, jsfiddle example](https://jsfiddle.net/ktsaou/L5y2eqp2/):
16
17 ![image](https://cloud.githubusercontent.com/assets/2662304/23824766/31a4a68c-0685-11e7-8429-8327cab64be2.png)
18
19 -
20 -## using the api from shell scripts
21 -
22 -Shell scripts can now query netdata easily:
23 -
24 -```sh
25 -eval "$(curl -s 'http://localhost:19999/api/v1/allmetrics')"
26 -```
27 -
28 -after this command, all the netdata metrics are exposed to shell. Check:
29 -
30 -```sh
31 -# source the metrics
32 -eval "$(curl -s 'http://localhost:19999/api/v1/allmetrics')"
33 -
34 -# let's see if there are variables exposed by netdata for system.cpu
35 -set | grep "^NETDATA_SYSTEM_CPU"
36 -
37 -NETDATA_SYSTEM_CPU_GUEST=0
38 -NETDATA_SYSTEM_CPU_GUEST_NICE=0
39 -NETDATA_SYSTEM_CPU_IDLE=95
40 -NETDATA_SYSTEM_CPU_IOWAIT=0
41 -NETDATA_SYSTEM_CPU_IRQ=0
42 -NETDATA_SYSTEM_CPU_NICE=0
43 -NETDATA_SYSTEM_CPU_SOFTIRQ=0
44 -NETDATA_SYSTEM_CPU_STEAL=0
45 -NETDATA_SYSTEM_CPU_SYSTEM=1
46 -NETDATA_SYSTEM_CPU_USER=4
47 -NETDATA_SYSTEM_CPU_VISIBLETOTAL=5
48 -
49 -# let's see the total cpu utilization of the system
50 -echo ${NETDATA_SYSTEM_CPU_VISIBLETOTAL}
51 -5
52 -
53 -# what about alarms?
54 -set | grep "^NETDATA_ALARM_SYSTEM_SWAP_"
55 -NETDATA_ALARM_SYSTEM_SWAP_RAM_IN_SWAP_STATUS=CRITICAL
56 -NETDATA_ALARM_SYSTEM_SWAP_RAM_IN_SWAP_VALUE=53
57 -NETDATA_ALARM_SYSTEM_SWAP_USED_SWAP_STATUS=CLEAR
58 -NETDATA_ALARM_SYSTEM_SWAP_USED_SWAP_VALUE=51
59 -
60 -# let's get the current status of the alarm 'ram in swap'
61 -echo ${NETDATA_ALARM_SYSTEM_SWAP_RAM_IN_SWAP_STATUS}
62 -CRITICAL
63 -
64 -# is it fast?
65 -time curl -s 'http://localhost:19999/api/v1/allmetrics' >/dev/null
66 -
67 -real 0m0,070s
68 -user 0m0,000s
69 -sys 0m0,007s
70 -
71 -# it is...
72 -# 0.07 seconds for curl to be loaded, connect to netdata and fetch the response back...
73 -```
74 -
75 -The `_VISIBLETOTAL` variable sums up all the dimensions of each chart.
76 -
77 -The format of the variables is:
78 -
79 -```sh
80 -NETDATA_${chart_id^^}_${dimension_id^^}="${value}"
81 -```
82 -
83 -The value is rounded to the closest integer, since shell script cannot process decimal numbers.
web/api/badges/web_buffer_svg.c
+253
@@ -887,3 +887,256 @@ void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const ch
887 label_width + value_width / 2 -1, ceil(height - text_offset), value_escaped,
888 label_width + value_width / 2 -1, ceil(height - text_offset - 1.0), value_escaped);
889 }
890 +
891 +int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
892 + int ret = 400;
893 + buffer_flush(w->response.data);
894 +
895 + BUFFER *dimensions = NULL;
896 +
897 + const char *chart = NULL
898 + , *before_str = NULL
899 + , *after_str = NULL
900 + , *points_str = NULL
901 + , *multiply_str = NULL
902 + , *divide_str = NULL
903 + , *label = NULL
904 + , *units = NULL
905 + , *label_color = NULL
906 + , *value_color = NULL
907 + , *refresh_str = NULL
908 + , *precision_str = NULL
909 + , *scale_str = NULL
910 + , *alarm = NULL;
911 +
912 + int group = RRDR_GROUPING_AVERAGE;
913 + uint32_t options = 0x00000000;
914 +
915 + while(url) {
916 + char *value = mystrsep(&url, "/?&");
917 + if(!value || !*value) continue;
918 +
919 + char *name = mystrsep(&value, "=");
920 + if(!name || !*name) continue;
921 + if(!value || !*value) continue;
922 +
923 + debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
924 +
925 + // name and value are now the parameters
926 + // they are not null and not empty
927 +
928 + if(!strcmp(name, "chart")) chart = value;
929 + else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
930 + if(!dimensions)
931 + dimensions = buffer_create(100);
932 +
933 + buffer_strcat(dimensions, "|");
934 + buffer_strcat(dimensions, value);
935 + }
936 + else if(!strcmp(name, "after")) after_str = value;
937 + else if(!strcmp(name, "before")) before_str = value;
938 + else if(!strcmp(name, "points")) points_str = value;
939 + else if(!strcmp(name, "group")) {
940 + group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
941 + }
942 + else if(!strcmp(name, "options")) {
943 + options |= web_client_api_request_v1_data_options(value);
944 + }
945 + else if(!strcmp(name, "label")) label = value;
946 + else if(!strcmp(name, "units")) units = value;
947 + else if(!strcmp(name, "label_color")) label_color = value;
948 + else if(!strcmp(name, "value_color")) value_color = value;
949 + else if(!strcmp(name, "multiply")) multiply_str = value;
950 + else if(!strcmp(name, "divide")) divide_str = value;
951 + else if(!strcmp(name, "refresh")) refresh_str = value;
952 + else if(!strcmp(name, "precision")) precision_str = value;
953 + else if(!strcmp(name, "scale")) scale_str = value;
954 + else if(!strcmp(name, "alarm")) alarm = value;
955 + }
956 +
957 + if(!chart || !*chart) {
958 + buffer_no_cacheable(w->response.data);
959 + buffer_sprintf(w->response.data, "No chart id is given at the request.");
960 + goto cleanup;
961 + }
962 +
963 + int scale = (scale_str && *scale_str)?str2i(scale_str):100;
964 +
965 + RRDSET *st = rrdset_find(host, chart);
966 + if(!st) st = rrdset_find_byname(host, chart);
967 + if(!st) {
968 + buffer_no_cacheable(w->response.data);
969 + buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1, scale, 0);
970 + ret = 200;
971 + goto cleanup;
972 + }
973 + st->last_accessed_time = now_realtime_sec();
974 +
975 + RRDCALC *rc = NULL;
976 + if(alarm) {
977 + rc = rrdcalc_find(st, alarm);
978 + if (!rc) {
979 + buffer_no_cacheable(w->response.data);
980 + buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1, scale, 0);
981 + ret = 200;
982 + goto cleanup;
983 + }
984 + }
985 +
986 + long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1;
987 + long long divide = (divide_str && *divide_str )?str2l(divide_str):1;
988 + long long before = (before_str && *before_str )?str2l(before_str):0;
989 + long long after = (after_str && *after_str )?str2l(after_str):-st->update_every;
990 + int points = (points_str && *points_str )?str2i(points_str):1;
991 + int precision = (precision_str && *precision_str)?str2i(precision_str):-1;
992 +
993 + if(!multiply) multiply = 1;
994 + if(!divide) divide = 1;
995 +
996 + int refresh = 0;
997 + if(refresh_str && *refresh_str) {
998 + if(!strcmp(refresh_str, "auto")) {
999 + if(rc) refresh = rc->update_every;
1000 + else if(options & RRDR_OPTION_NOT_ALIGNED)
1001 + refresh = st->update_every;
1002 + else {
1003 + refresh = (int)(before - after);
1004 + if(refresh < 0) refresh = -refresh;
1005 + }
1006 + }
1007 + else {
1008 + refresh = str2i(refresh_str);
1009 + if(refresh < 0) refresh = -refresh;
1010 + }
1011 + }
1012 +
1013 + if(!label) {
1014 + if(alarm) {
1015 + char *s = (char *)alarm;
1016 + while(*s) {
1017 + if(*s == '_') *s = ' ';
1018 + s++;
1019 + }
1020 + label = alarm;
1021 + }
1022 + else if(dimensions) {
1023 + const char *dim = buffer_tostring(dimensions);
1024 + if(*dim == '|') dim++;
1025 + label = dim;
1026 + }
1027 + else
1028 + label = st->name;
1029 + }
1030 + if(!units) {
1031 + if(alarm) {
1032 + if(rc->units)
1033 + units = rc->units;
1034 + else
1035 + units = "";
1036 + }
1037 + else if(options & RRDR_OPTION_PERCENTAGE)
1038 + units = "%";
1039 + else
1040 + units = st->units;
1041 + }
1042 +
1043 + debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
1044 + , w->id
1045 + , chart
1046 + , alarm?alarm:""
1047 + , (dimensions)?buffer_tostring(dimensions):""
1048 + , after
1049 + , before
1050 + , points
1051 + , group
1052 + , options
1053 + );
1054 +
1055 + if(rc) {
1056 + if (refresh > 0) {
1057 + buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
1058 + w->response.data->expires = now_realtime_sec() + refresh;
1059 + }
1060 + else buffer_no_cacheable(w->response.data);
1061 +
1062 + if(!value_color) {
1063 + switch(rc->status) {
1064 + case RRDCALC_STATUS_CRITICAL:
1065 + value_color = "red";
1066 + break;
1067 +
1068 + case RRDCALC_STATUS_WARNING:
1069 + value_color = "orange";
1070 + break;
1071 +
1072 + case RRDCALC_STATUS_CLEAR:
1073 + value_color = "brightgreen";
1074 + break;
1075 +
1076 + case RRDCALC_STATUS_UNDEFINED:
1077 + value_color = "lightgrey";
1078 + break;
1079 +
1080 + case RRDCALC_STATUS_UNINITIALIZED:
1081 + value_color = "#000";
1082 + break;
1083 +
1084 + default:
1085 + value_color = "grey";
1086 + break;
1087 + }
1088 + }
1089 +
1090 + buffer_svg(w->response.data,
1091 + label,
1092 + (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
1093 + units,
1094 + label_color,
1095 + value_color,
1096 + precision,
1097 + scale,
1098 + options
1099 + );
1100 + ret = 200;
1101 + }
1102 + else {
1103 + time_t latest_timestamp = 0;
1104 + int value_is_null = 1;
1105 + calculated_number n = NAN;
1106 + ret = 500;
1107 +
1108 + // if the collected value is too old, don't calculate its value
1109 + if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
1110 + ret = rrdset2value_api_v1(st, w->response.data, &n, (dimensions) ? buffer_tostring(dimensions) : NULL
1111 + , points, after, before, group, 0, options, NULL, &latest_timestamp, &value_is_null);
1112 +
1113 + // if the value cannot be calculated, show empty badge
1114 + if (ret != 200) {
1115 + buffer_no_cacheable(w->response.data);
1116 + value_is_null = 1;
1117 + n = 0;
1118 + ret = 200;
1119 + }
1120 + else if (refresh > 0) {
1121 + buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
1122 + w->response.data->expires = now_realtime_sec() + refresh;
1123 + }
1124 + else buffer_no_cacheable(w->response.data);
1125 +
1126 + // render the badge
1127 + buffer_svg(w->response.data,
1128 + label,
1129 + (value_is_null)?NAN:(n * multiply / divide),
1130 + units,
1131 + label_color,
1132 + value_color,
1133 + precision,
1134 + scale,
1135 + options
1136 + );
1137 + }
1138 +
1139 + cleanup:
1140 + buffer_free(dimensions);
1141 + return ret;
1142 +}
web/api/badges/web_buffer_svg.h
+6 -1
@@ -3,9 +3,14 @@
3 #ifndef NETDATA_WEB_BUFFER_SVG_H
4 #define NETDATA_WEB_BUFFER_SVG_H 1
5
6 -#include "web/api/web_api_v1.h"
6 +#include "libnetdata/libnetdata.h"
7 +#include "web/server/web_client.h"
8
9 extern void buffer_svg(BUFFER *wb, const char *label, calculated_number value, const char *units, const char *label_color, const char *value_color, int precision, int scale, uint32_t options);
10 extern char *format_value_and_unit(char *value_string, size_t value_string_len, calculated_number value, const char *units, int precision);
11
12 +extern int web_client_api_request_v1_badge(struct rrdhost *host, struct web_client *w, char *url);
13 +
14 +#include "web/api/web_api_v1.h"
15 +
16 #endif /* NETDATA_WEB_BUFFER_SVG_H */
web/api/exporters/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/exporters/README.md
web/api/exporters/allmetrics.c new
+113
@@ -0,0 +1,113 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "allmetrics.h"
4 +
5 +struct prometheus_output_options {
6 + char *name;
7 + PROMETHEUS_OUTPUT_OPTIONS flag;
8 +} prometheus_output_flags_root[] = {
9 + { "help", PROMETHEUS_OUTPUT_HELP },
10 + { "types", PROMETHEUS_OUTPUT_TYPES },
11 + { "names", PROMETHEUS_OUTPUT_NAMES },
12 + { "timestamps", PROMETHEUS_OUTPUT_TIMESTAMPS },
13 + { "variables", PROMETHEUS_OUTPUT_VARIABLES },
14 +
15 + // terminator
16 + { NULL, PROMETHEUS_OUTPUT_NONE },
17 +};
18 +
19 +inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
20 + int format = ALLMETRICS_SHELL;
21 + const char *prometheus_server = w->client_ip;
22 + uint32_t prometheus_backend_options = global_backend_options;
23 + PROMETHEUS_OUTPUT_OPTIONS prometheus_output_options = PROMETHEUS_OUTPUT_TIMESTAMPS | ((global_backend_options & BACKEND_OPTION_SEND_NAMES)?PROMETHEUS_OUTPUT_NAMES:0);
24 + const char *prometheus_prefix = global_backend_prefix;
25 +
26 + while(url) {
27 + char *value = mystrsep(&url, "?&");
28 + if (!value || !*value) continue;
29 +
30 + char *name = mystrsep(&value, "=");
31 + if(!name || !*name) continue;
32 + if(!value || !*value) continue;
33 +
34 + if(!strcmp(name, "format")) {
35 + if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
36 + format = ALLMETRICS_SHELL;
37 + else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
38 + format = ALLMETRICS_PROMETHEUS;
39 + else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS))
40 + format = ALLMETRICS_PROMETHEUS_ALL_HOSTS;
41 + else if(!strcmp(value, ALLMETRICS_FORMAT_JSON))
42 + format = ALLMETRICS_JSON;
43 + else
44 + format = 0;
45 + }
46 + else if(!strcmp(name, "server")) {
47 + prometheus_server = value;
48 + }
49 + else if(!strcmp(name, "prefix")) {
50 + prometheus_prefix = value;
51 + }
52 + else if(!strcmp(name, "data") || !strcmp(name, "source") || !strcmp(name, "data source") || !strcmp(name, "data-source") || !strcmp(name, "data_source") || !strcmp(name, "datasource")) {
53 + prometheus_backend_options = backend_parse_data_source(value, prometheus_backend_options);
54 + }
55 + else {
56 + int i;
57 + for(i = 0; prometheus_output_flags_root[i].name ; i++) {
58 + if(!strcmp(name, prometheus_output_flags_root[i].name)) {
59 + if(!strcmp(value, "yes") || !strcmp(value, "1") || !strcmp(value, "true"))
60 + prometheus_output_options |= prometheus_output_flags_root[i].flag;
61 + else
62 + prometheus_output_options &= ~prometheus_output_flags_root[i].flag;
63 +
64 + break;
65 + }
66 + }
67 + }
68 + }
69 +
70 + buffer_flush(w->response.data);
71 + buffer_no_cacheable(w->response.data);
72 +
73 + switch(format) {
74 + case ALLMETRICS_JSON:
75 + w->response.data->contenttype = CT_APPLICATION_JSON;
76 + rrd_stats_api_v1_charts_allmetrics_json(host, w->response.data);
77 + return 200;
78 +
79 + case ALLMETRICS_SHELL:
80 + w->response.data->contenttype = CT_TEXT_PLAIN;
81 + rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
82 + return 200;
83 +
84 + case ALLMETRICS_PROMETHEUS:
85 + w->response.data->contenttype = CT_PROMETHEUS;
86 + rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(
87 + host
88 + , w->response.data
89 + , prometheus_server
90 + , prometheus_prefix
91 + , prometheus_backend_options
92 + , prometheus_output_options
93 + );
94 + return 200;
95 +
96 + case ALLMETRICS_PROMETHEUS_ALL_HOSTS:
97 + w->response.data->contenttype = CT_PROMETHEUS;
98 + rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(
99 + host
100 + , w->response.data
101 + , prometheus_server
102 + , prometheus_prefix
103 + , prometheus_backend_options
104 + , prometheus_output_options
105 + );
106 + return 200;
107 +
108 + default:
109 + w->response.data->contenttype = CT_TEXT_PLAIN;
110 + buffer_strcat(w->response.data, "Which format? '" ALLMETRICS_FORMAT_SHELL "', '" ALLMETRICS_FORMAT_PROMETHEUS "', '" ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS "' and '" ALLMETRICS_FORMAT_JSON "' are currently supported.");
111 + return 400;
112 + }
113 +}
web/api/exporters/allmetrics.h new
+11
@@ -0,0 +1,11 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_ALLMETRICS_H
4 +#define NETDATA_API_ALLMETRICS_H
5 +
6 +#include "../rrd2json.h"
7 +#include "shell/allmetrics_shell.h"
8 +
9 +extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url);
10 +
11 +#endif //NETDATA_API_ALLMETRICS_H
web/api/exporters/prometheus/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/exporters/prometheus/README.md new
+3
@@ -0,0 +1,3 @@
1 +# prometheus exporter
2 +
3 +The prometheus exporter for netdata is located at the [backends section for prometheus](../../../../backends/prometheus).
web/api/exporters/shell/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/exporters/shell/README.md new
+64
@@ -0,0 +1,64 @@
1 +# shell exporter
2 +
3 +Shell scripts can now query netdata:
4 +
5 +```sh
6 +eval "$(curl -s 'http://localhost:19999/api/v1/allmetrics')"
7 +```
8 +
9 +after this command, all the netdata metrics are exposed to shell. Check:
10 +
11 +```sh
12 +# source the metrics
13 +eval "$(curl -s 'http://localhost:19999/api/v1/allmetrics')"
14 +
15 +# let's see if there are variables exposed by netdata for system.cpu
16 +set | grep "^NETDATA_SYSTEM_CPU"
17 +
18 +NETDATA_SYSTEM_CPU_GUEST=0
19 +NETDATA_SYSTEM_CPU_GUEST_NICE=0
20 +NETDATA_SYSTEM_CPU_IDLE=95
21 +NETDATA_SYSTEM_CPU_IOWAIT=0
22 +NETDATA_SYSTEM_CPU_IRQ=0
23 +NETDATA_SYSTEM_CPU_NICE=0
24 +NETDATA_SYSTEM_CPU_SOFTIRQ=0
25 +NETDATA_SYSTEM_CPU_STEAL=0
26 +NETDATA_SYSTEM_CPU_SYSTEM=1
27 +NETDATA_SYSTEM_CPU_USER=4
28 +NETDATA_SYSTEM_CPU_VISIBLETOTAL=5
29 +
30 +# let's see the total cpu utilization of the system
31 +echo ${NETDATA_SYSTEM_CPU_VISIBLETOTAL}
32 +5
33 +
34 +# what about alarms?
35 +set | grep "^NETDATA_ALARM_SYSTEM_SWAP_"
36 +NETDATA_ALARM_SYSTEM_SWAP_RAM_IN_SWAP_STATUS=CRITICAL
37 +NETDATA_ALARM_SYSTEM_SWAP_RAM_IN_SWAP_VALUE=53
38 +NETDATA_ALARM_SYSTEM_SWAP_USED_SWAP_STATUS=CLEAR
39 +NETDATA_ALARM_SYSTEM_SWAP_USED_SWAP_VALUE=51
40 +
41 +# let's get the current status of the alarm 'ram in swap'
42 +echo ${NETDATA_ALARM_SYSTEM_SWAP_RAM_IN_SWAP_STATUS}
43 +CRITICAL
44 +
45 +# is it fast?
46 +time curl -s 'http://localhost:19999/api/v1/allmetrics' >/dev/null
47 +
48 +real 0m0,070s
49 +user 0m0,000s
50 +sys 0m0,007s
51 +
52 +# it is...
53 +# 0.07 seconds for curl to be loaded, connect to netdata and fetch the response back...
54 +```
55 +
56 +The `_VISIBLETOTAL` variable sums up all the dimensions of each chart.
57 +
58 +The format of the variables is:
59 +
60 +```sh
61 +NETDATA_${chart_id^^}_${dimension_id^^}="${value}"
62 +```
63 +
64 +The value is rounded to the closest integer, since shell script cannot process decimal numbers.
web/api/exporters/shell/allmetrics_shell.c new
+159
@@ -0,0 +1,159 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "allmetrics_shell.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// BASH
7 +// /api/v1/allmetrics?format=bash
8 +
9 +static inline size_t shell_name_copy(char *d, const char *s, size_t usable) {
10 + size_t n;
11 +
12 + for(n = 0; *s && n < usable ; d++, s++, n++) {
13 + register char c = *s;
14 +
15 + if(unlikely(!isalnum(c))) *d = '_';
16 + else *d = (char)toupper(c);
17 + }
18 + *d = '\0';
19 +
20 + return n;
21 +}
22 +
23 +#define SHELL_ELEMENT_MAX 100
24 +
25 +void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
26 + rrdhost_rdlock(host);
27 +
28 + // for each chart
29 + RRDSET *st;
30 + rrdset_foreach_read(st, host) {
31 + calculated_number total = 0.0;
32 + char chart[SHELL_ELEMENT_MAX + 1];
33 + shell_name_copy(chart, st->name?st->name:st->id, SHELL_ELEMENT_MAX);
34 +
35 + buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name);
36 + if(rrdset_is_available_for_viewers(st)) {
37 + rrdset_rdlock(st);
38 +
39 + // for each dimension
40 + RRDDIM *rd;
41 + rrddim_foreach_read(rd, st) {
42 + if(rd->collections_counter) {
43 + char dimension[SHELL_ELEMENT_MAX + 1];
44 + shell_name_copy(dimension, rd->name?rd->name:rd->id, SHELL_ELEMENT_MAX);
45 +
46 + calculated_number n = rd->last_stored_value;
47 +
48 + if(isnan(n) || isinf(n))
49 + buffer_sprintf(wb, "NETDATA_%s_%s=\"\" # %s\n", chart, dimension, st->units);
50 + else {
51 + if(rd->multiplier < 0 || rd->divisor < 0) n = -n;
52 + n = calculated_number_round(n);
53 + if(!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) total += n;
54 + buffer_sprintf(wb, "NETDATA_%s_%s=\"" CALCULATED_NUMBER_FORMAT_ZERO "\" # %s\n", chart, dimension, n, st->units);
55 + }
56 + }
57 + }
58 +
59 + total = calculated_number_round(total);
60 + buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"" CALCULATED_NUMBER_FORMAT_ZERO "\" # %s\n", chart, total, st->units);
61 + rrdset_unlock(st);
62 + }
63 + }
64 +
65 + buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n");
66 +
67 + RRDCALC *rc;
68 + for(rc = host->alarms; rc ;rc = rc->next) {
69 + if(!rc->rrdset) continue;
70 +
71 + char chart[SHELL_ELEMENT_MAX + 1];
72 + shell_name_copy(chart, rc->rrdset->name?rc->rrdset->name:rc->rrdset->id, SHELL_ELEMENT_MAX);
73 +
74 + char alarm[SHELL_ELEMENT_MAX + 1];
75 + shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX);
76 +
77 + calculated_number n = rc->value;
78 +
79 + if(isnan(n) || isinf(n))
80 + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\" # %s\n", chart, alarm, rc->units);
81 + else {
82 + n = calculated_number_round(n);
83 + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"" CALCULATED_NUMBER_FORMAT_ZERO "\" # %s\n", chart, alarm, n, rc->units);
84 + }
85 +
86 + buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status));
87 + }
88 +
89 + rrdhost_unlock(host);
90 +}
91 +
92 +// ----------------------------------------------------------------------------
93 +
94 +void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb) {
95 + rrdhost_rdlock(host);
96 +
97 + buffer_strcat(wb, "{");
98 +
99 + size_t chart_counter = 0;
100 + size_t dimension_counter = 0;
101 +
102 + // for each chart
103 + RRDSET *st;
104 + rrdset_foreach_read(st, host) {
105 + if(rrdset_is_available_for_viewers(st)) {
106 + rrdset_rdlock(st);
107 +
108 + buffer_sprintf(wb, "%s\n"
109 + "\t\"%s\": {\n"
110 + "\t\t\"name\":\"%s\",\n"
111 + "\t\t\"context\":\"%s\",\n"
112 + "\t\t\"units\":\"%s\",\n"
113 + "\t\t\"last_updated\": %ld,\n"
114 + "\t\t\"dimensions\": {"
115 + , chart_counter?",":""
116 + , st->id
117 + , st->name
118 + , st->context
119 + , st->units
120 + , rrdset_last_entry_t(st)
121 + );
122 +
123 + chart_counter++;
124 + dimension_counter = 0;
125 +
126 + // for each dimension
127 + RRDDIM *rd;
128 + rrddim_foreach_read(rd, st) {
129 + if(rd->collections_counter) {
130 +
131 + buffer_sprintf(wb, "%s\n"
132 + "\t\t\t\"%s\": {\n"
133 + "\t\t\t\t\"name\": \"%s\",\n"
134 + "\t\t\t\t\"value\": "
135 + , dimension_counter?",":""
136 + , rd->id
137 + , rd->name
138 + );
139 +
140 + if(isnan(rd->last_stored_value))
141 + buffer_strcat(wb, "null");
142 + else
143 + buffer_sprintf(wb, CALCULATED_NUMBER_FORMAT, rd->last_stored_value);
144 +
145 + buffer_strcat(wb, "\n\t\t\t}");
146 +
147 + dimension_counter++;
148 + }
149 + }
150 +
151 + buffer_strcat(wb, "\n\t\t}\n\t}");
152 + rrdset_unlock(st);
153 + }
154 + }
155 +
156 + buffer_strcat(wb, "\n}");
157 + rrdhost_unlock(host);
158 +}
159 +
web/api/exporters/shell/allmetrics_shell.h new
+21
@@ -0,0 +1,21 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_ALLMETRICS_SHELL_H
4 +#define NETDATA_API_ALLMETRICS_SHELL_H
5 +
6 +#include "../allmetrics.h"
7 +
8 +#define ALLMETRICS_FORMAT_SHELL "shell"
9 +#define ALLMETRICS_FORMAT_PROMETHEUS "prometheus"
10 +#define ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS "prometheus_all_hosts"
11 +#define ALLMETRICS_FORMAT_JSON "json"
12 +
13 +#define ALLMETRICS_SHELL 1
14 +#define ALLMETRICS_PROMETHEUS 2
15 +#define ALLMETRICS_JSON 3
16 +#define ALLMETRICS_PROMETHEUS_ALL_HOSTS 4
17 +
18 +extern void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb);
19 +extern void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb);
20 +
21 +#endif //NETDATA_API_ALLMETRICS_SHELL_H
web/api/netdata-swagger.json renamed
+4
@@ -127,6 +127,8 @@
127 "min",
128 "max",
129 "average",
130 + "median",
131 + "stddev",
132 "sum",
133 "incremental-sum"
134 ],
@@ -310,6 +312,8 @@
312 "min",
313 "max",
314 "average",
315 + "median",
316 + "stddev",
317 "sum",
318 "incremental-sum"
319 ],
web/api/netdata-swagger.yaml renamed
+3 -3
@@ -3,7 +3,7 @@ swagger: '2.0'
3 info:
4 title: NetData API
5 description: 'Real-time performance and health monitoring.'
6 - version: 1.9.11_rolling
6 + version: 1.11.0_rolling
7 host: registry.my-netdata.io
8 schemes:
9 - https
@@ -94,7 +94,7 @@ paths:
94 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 dimesions to return the most extreme value in either direction).'
95 required: true
96 type: string
97 - enum: [ 'min', 'max', 'average', 'sum', 'incremental-sum' ]
97 + enum: [ 'min', 'max', 'average', 'median', 'stddev', 'sum', 'incremental-sum' ]
98 default: 'average'
99 allowEmptyValue: false
100 - name: gtime
@@ -204,7 +204,7 @@ paths:
204 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 dimesions to return the most extreme value in either direction).'
205 required: true
206 type: string
207 - enum: [ 'min', 'max', 'average', 'sum', 'incremental-sum' ]
207 + enum: [ 'min', 'max', 'average', 'median', 'stddev', 'sum', 'incremental-sum' ]
208 default: 'average'
209 allowEmptyValue: false
210 - name: options
web/api/queries/Makefile.am new
+18
@@ -0,0 +1,18 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +SUBDIRS = \
7 + average \
8 + incremental_sum \
9 + max \
10 + min \
11 + sum \
12 + median \
13 + stddev \
14 + $(NULL)
15 +
16 +dist_noinst_DATA = \
17 + README.md \
18 + $(NULL)
web/api/queries/README.md
web/api/queries/average/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/average/README.md
web/api/queries/average/average.c new
+58
@@ -0,0 +1,58 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "average.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// average
7 +
8 +struct grouping_average {
9 + calculated_number sum;
10 + size_t count;
11 +};
12 +
13 +void *grouping_init_average(RRDR *r) {
14 + (void)r;
15 + return callocz(1, sizeof(struct grouping_average));
16 +}
17 +
18 +// resets when switches dimensions
19 +// so, clear everything to restart
20 +void grouping_reset_average(RRDR *r) {
21 + struct grouping_average *g = (struct grouping_average *)r->grouping_data;
22 + g->sum = 0;
23 + g->count = 0;
24 +}
25 +
26 +void grouping_free_average(RRDR *r) {
27 + freez(r->grouping_data);
28 +}
29 +
30 +void grouping_add_average(RRDR *r, calculated_number value) {
31 + if(!isnan(value)) {
32 + struct grouping_average *g = (struct grouping_average *)r->grouping_data;
33 + g->sum += value;
34 + g->count++;
35 + }
36 +}
37 +
38 +calculated_number grouping_flush_average(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
39 + struct grouping_average *g = (struct grouping_average *)r->grouping_data;
40 +
41 + calculated_number value;
42 +
43 + if(unlikely(!g->count)) {
44 + value = 0.0;
45 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
46 + }
47 + else {
48 + if(unlikely(r->group_points != 1))
49 + value = g->sum / r->group_sum_divisor;
50 + else
51 + value = g->sum / g->count;
52 + }
53 +
54 + g->sum = 0.0;
55 + g->count = 0;
56 +
57 + return value;
58 +}
web/api/queries/average/average.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERY_AVERAGE_H
4 +#define NETDATA_API_QUERY_AVERAGE_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_average(RRDR *r);
10 +extern void grouping_reset_average(RRDR *r);
11 +extern void grouping_free_average(RRDR *r);
12 +extern void grouping_add_average(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_average(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERY_AVERAGE_H
web/api/queries/incremental_sum/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/incremental_sum/README.md
web/api/queries/incremental_sum/incremental_sum.c new
+68
@@ -0,0 +1,68 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "incremental_sum.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// incremental sum
7 +
8 +struct grouping_incremental_sum {
9 + calculated_number first;
10 + calculated_number last;
11 + size_t count;
12 +};
13 +
14 +void *grouping_init_incremental_sum(RRDR *r) {
15 + (void)r;
16 + return callocz(1, sizeof(struct grouping_incremental_sum));
17 +}
18 +
19 +// resets when switches dimensions
20 +// so, clear everything to restart
21 +void grouping_reset_incremental_sum(RRDR *r) {
22 + struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->grouping_data;
23 + g->first = 0;
24 + g->last = 0;
25 + g->count = 0;
26 +}
27 +
28 +void grouping_free_incremental_sum(RRDR *r) {
29 + freez(r->grouping_data);
30 +}
31 +
32 +void grouping_add_incremental_sum(RRDR *r, calculated_number value) {
33 + if(!isnan(value)) {
34 + struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->grouping_data;
35 +
36 + if(unlikely(!g->count)) {
37 + g->first = value;
38 + g->count++;
39 + }
40 + else {
41 + g->last = value;
42 + g->count++;
43 + }
44 + }
45 +}
46 +
47 +calculated_number grouping_flush_incremental_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
48 + struct grouping_incremental_sum *g = (struct grouping_incremental_sum *)r->grouping_data;
49 +
50 + calculated_number value;
51 +
52 + if(unlikely(!g->count)) {
53 + value = 0.0;
54 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
55 + }
56 + else if(unlikely(g->count == 1)) {
57 + value = 0.0;
58 + }
59 + else {
60 + value = g->last - g->first;
61 + }
62 +
63 + g->first = 0.0;
64 + g->last = 0.0;
65 + g->count = 0;
66 +
67 + return value;
68 +}
web/api/queries/incremental_sum/incremental_sum.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERY_INCREMENTAL_SUM_H
4 +#define NETDATA_API_QUERY_INCREMENTAL_SUM_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_incremental_sum(RRDR *r);
10 +extern void grouping_reset_incremental_sum(RRDR *r);
11 +extern void grouping_free_incremental_sum(RRDR *r);
12 +extern void grouping_add_incremental_sum(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_incremental_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERY_INCREMENTAL_SUM_H
web/api/queries/max/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/max/README.md
web/api/queries/max/max.c new
+59
@@ -0,0 +1,59 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "max.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// max
7 +
8 +struct grouping_max {
9 + calculated_number max;
10 + size_t count;
11 +};
12 +
13 +void *grouping_init_max(RRDR *r) {
14 + (void)r;
15 + return callocz(1, sizeof(struct grouping_max));
16 +}
17 +
18 +// resets when switches dimensions
19 +// so, clear everything to restart
20 +void grouping_reset_max(RRDR *r) {
21 + struct grouping_max *g = (struct grouping_max *)r->grouping_data;
22 + g->max = 0;
23 + g->count = 0;
24 +}
25 +
26 +void grouping_free_max(RRDR *r) {
27 + freez(r->grouping_data);
28 +}
29 +
30 +void grouping_add_max(RRDR *r, calculated_number value) {
31 + if(!isnan(value)) {
32 + struct grouping_max *g = (struct grouping_max *)r->grouping_data;
33 +
34 + if(!g->count || calculated_number_fabs(value) > calculated_number_fabs(g->max)) {
35 + g->max = value;
36 + g->count++;
37 + }
38 + }
39 +}
40 +
41 +calculated_number grouping_flush_max(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
42 + struct grouping_max *g = (struct grouping_max *)r->grouping_data;
43 +
44 + calculated_number value;
45 +
46 + if(unlikely(!g->count)) {
47 + value = 0.0;
48 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
49 + }
50 + else {
51 + value = g->max;
52 + }
53 +
54 + g->max = 0.0;
55 + g->count = 0;
56 +
57 + return value;
58 +}
59 +
web/api/queries/max/max.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERY_MAX_H
4 +#define NETDATA_API_QUERY_MAX_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_max(RRDR *r);
10 +extern void grouping_reset_max(RRDR *r);
11 +extern void grouping_free_max(RRDR *r);
12 +extern void grouping_add_max(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_max(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERY_MAX_H
web/api/queries/median/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/median/README.md
web/api/queries/median/median.c new
+78
@@ -0,0 +1,78 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "median.h"
4 +
5 +
6 +// ----------------------------------------------------------------------------
7 +// median
8 +
9 +struct grouping_median {
10 + size_t series_size;
11 + size_t next_pos;
12 +
13 + LONG_DOUBLE series[];
14 +};
15 +
16 +void *grouping_init_median(RRDR *r) {
17 + long entries = (r->group > r->group_points) ? r->group : r->group_points;
18 + if(entries < 0) entries = 0;
19 +
20 + struct grouping_median *g = (struct grouping_median *)callocz(1, sizeof(struct grouping_median) + entries * sizeof(LONG_DOUBLE));
21 + g->series_size = (size_t)entries;
22 +
23 + return g;
24 +}
25 +
26 +// resets when switches dimensions
27 +// so, clear everything to restart
28 +void grouping_reset_median(RRDR *r) {
29 + struct grouping_median *g = (struct grouping_median *)r->grouping_data;
30 + g->next_pos = 0;
31 +}
32 +
33 +void grouping_free_median(RRDR *r) {
34 + freez(r->grouping_data);
35 +}
36 +
37 +void grouping_add_median(RRDR *r, calculated_number value) {
38 + struct grouping_median *g = (struct grouping_median *)r->grouping_data;
39 +
40 + if(unlikely(g->next_pos >= g->series_size)) {
41 + error("INTERNAL ERROR: median buffer overflow on chart '%s' - next_pos = %zu, series_size = %zu, r->group = %ld, r->group_points = %ld.", r->st->name, g->next_pos, g->series_size, r->group, r->group_points);
42 + }
43 + else {
44 + if(isnormal(value))
45 + g->series[g->next_pos++] = (LONG_DOUBLE)value;
46 + }
47 +}
48 +
49 +calculated_number grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
50 + struct grouping_median *g = (struct grouping_median *)r->grouping_data;
51 +
52 + calculated_number value;
53 +
54 + if(unlikely(!g->next_pos)) {
55 + value = 0.0;
56 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
57 + }
58 + else {
59 + if(g->next_pos > 1) {
60 + sort_series(g->series, g->next_pos);
61 + value = (calculated_number)median_on_sorted_series(g->series, g->next_pos);
62 + }
63 + else
64 + value = (calculated_number)g->series[0];
65 +
66 + if(!isnormal(value)) {
67 + value = 0.0;
68 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
69 + }
70 +
71 + //log_series_to_stderr(g->series, g->next_pos, value, "median");
72 + }
73 +
74 + g->next_pos = 0;
75 +
76 + return value;
77 +}
78 +
web/api/queries/median/median.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERIES_MEDIAN_H
4 +#define NETDATA_API_QUERIES_MEDIAN_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_median(RRDR *r);
10 +extern void grouping_reset_median(RRDR *r);
11 +extern void grouping_free_median(RRDR *r);
12 +extern void grouping_add_median(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_median(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERIES_MEDIAN_H
web/api/queries/min/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/min/README.md
web/api/queries/min/min.c new
+59
@@ -0,0 +1,59 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "min.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// min
7 +
8 +struct grouping_min {
9 + calculated_number min;
10 + size_t count;
11 +};
12 +
13 +void *grouping_init_min(RRDR *r) {
14 + (void)r;
15 + return callocz(1, sizeof(struct grouping_min));
16 +}
17 +
18 +// resets when switches dimensions
19 +// so, clear everything to restart
20 +void grouping_reset_min(RRDR *r) {
21 + struct grouping_min *g = (struct grouping_min *)r->grouping_data;
22 + g->min = 0;
23 + g->count = 0;
24 +}
25 +
26 +void grouping_free_min(RRDR *r) {
27 + freez(r->grouping_data);
28 +}
29 +
30 +void grouping_add_min(RRDR *r, calculated_number value) {
31 + if(!isnan(value)) {
32 + struct grouping_min *g = (struct grouping_min *)r->grouping_data;
33 +
34 + if(!g->count || calculated_number_fabs(value) < calculated_number_fabs(g->min)) {
35 + g->min = value;
36 + g->count++;
37 + }
38 + }
39 +}
40 +
41 +calculated_number grouping_flush_min(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
42 + struct grouping_min *g = (struct grouping_min *)r->grouping_data;
43 +
44 + calculated_number value;
45 +
46 + if(unlikely(!g->count)) {
47 + value = 0.0;
48 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
49 + }
50 + else {
51 + value = g->min;
52 + }
53 +
54 + g->min = 0.0;
55 + g->count = 0;
56 +
57 + return value;
58 +}
59 +
web/api/queries/min/min.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERY_MIN_H
4 +#define NETDATA_API_QUERY_MIN_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_min(RRDR *r);
10 +extern void grouping_reset_min(RRDR *r);
11 +extern void grouping_free_min(RRDR *r);
12 +extern void grouping_add_min(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_min(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERY_MIN_H
web/api/queries/query.c new
+729
@@ -0,0 +1,729 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "query.h"
4 +#include "../rrd2json.h"
5 +#include "rrdr.h"
6 +
7 +#include "average/average.h"
8 +#include "incremental_sum/incremental_sum.h"
9 +#include "max/max.h"
10 +#include "median/median.h"
11 +#include "min/min.h"
12 +#include "sum/sum.h"
13 +#include "stddev/stddev.h"
14 +#include "ses/ses.h"
15 +
16 +// ----------------------------------------------------------------------------
17 +
18 +static struct {
19 + const char *name;
20 + uint32_t hash;
21 + RRDR_GROUPING value;
22 + void *(*init)(struct rrdresult *r);
23 + void (*reset)(struct rrdresult *r);
24 + void (*free)(struct rrdresult *r);
25 + void (*add)(struct rrdresult *r, calculated_number value);
26 + calculated_number (*flush)(struct rrdresult *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
27 +} api_v1_data_groups[] = {
28 + { "average" , 0, RRDR_GROUPING_AVERAGE , grouping_init_average , grouping_reset_average , grouping_free_average , grouping_add_average , grouping_flush_average }
29 + , { "incremental_sum" , 0, RRDR_GROUPING_INCREMENTAL_SUM, grouping_init_incremental_sum, grouping_reset_incremental_sum, grouping_free_incremental_sum, grouping_add_incremental_sum, grouping_flush_incremental_sum }
30 + , { "incremental-sum" , 0, RRDR_GROUPING_INCREMENTAL_SUM, grouping_init_incremental_sum, grouping_reset_incremental_sum, grouping_free_incremental_sum, grouping_add_incremental_sum, grouping_flush_incremental_sum }
31 + , { "median" , 0, RRDR_GROUPING_MEDIAN , grouping_init_median , grouping_reset_median , grouping_free_median , grouping_add_median , grouping_flush_median }
32 + , { "min" , 0, RRDR_GROUPING_MIN , grouping_init_min , grouping_reset_min , grouping_free_min , grouping_add_min , grouping_flush_min }
33 + , { "max" , 0, RRDR_GROUPING_MAX , grouping_init_max , grouping_reset_max , grouping_free_max , grouping_add_max , grouping_flush_max }
34 + , { "ses" , 0, RRDR_GROUPING_SES , grouping_init_ses , grouping_reset_ses , grouping_free_ses , grouping_add_ses , grouping_flush_ses }
35 + , { "stddev" , 0, RRDR_GROUPING_STDDEV , grouping_init_stddev , grouping_reset_stddev , grouping_free_stddev , grouping_add_stddev , grouping_flush_stddev }
36 + , { "sum" , 0, RRDR_GROUPING_SUM , grouping_init_sum , grouping_reset_sum , grouping_free_sum , grouping_add_sum , grouping_flush_sum }
37 + , { NULL , 0, RRDR_GROUPING_UNDEFINED , grouping_init_average , grouping_reset_average , grouping_free_average , grouping_add_average , grouping_flush_average }
38 +};
39 +
40 +void web_client_api_v1_init_grouping(void) {
41 + int i;
42 +
43 + for(i = 0; api_v1_data_groups[i].name ; i++)
44 + api_v1_data_groups[i].hash = simple_hash(api_v1_data_groups[i].name);
45 +}
46 +
47 +const char *group_method2string(RRDR_GROUPING group) {
48 + int i;
49 +
50 + for(i = 0; api_v1_data_groups[i].name ; i++) {
51 + if(api_v1_data_groups[i].value == group) {
52 + return api_v1_data_groups[i].name;
53 + }
54 + }
55 +
56 + return "unknown-group-method";
57 +}
58 +
59 +RRDR_GROUPING web_client_api_request_v1_data_group(const char *name, RRDR_GROUPING def) {
60 + int i;
61 +
62 + uint32_t hash = simple_hash(name);
63 + for(i = 0; api_v1_data_groups[i].name ; i++)
64 + if(unlikely(hash == api_v1_data_groups[i].hash && !strcmp(name, api_v1_data_groups[i].name)))
65 + return api_v1_data_groups[i].value;
66 +
67 + return def;
68 +}
69 +
70 +// ----------------------------------------------------------------------------
71 +
72 +static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options, const char *dims) {
73 + rrdset_check_rdlock(r->st);
74 +
75 + if(unlikely(!dims || !*dims || (dims[0] == '*' && dims[1] == '\0'))) return;
76 +
77 + int match_ids = 0, match_names = 0;
78 +
79 + if(unlikely(options & RRDR_OPTION_MATCH_IDS))
80 + match_ids = 1;
81 + if(unlikely(options & RRDR_OPTION_MATCH_NAMES))
82 + match_names = 1;
83 +
84 + if(likely(!match_ids && !match_names))
85 + match_ids = match_names = 1;
86 +
87 + SIMPLE_PATTERN *pattern = simple_pattern_create(dims, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);
88 +
89 + RRDDIM *d;
90 + long c, dims_selected = 0, dims_not_hidden_not_zero = 0;
91 + for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
92 + if( (match_ids && simple_pattern_matches(pattern, d->id))
93 + || (match_names && simple_pattern_matches(pattern, d->name))
94 + ) {
95 + r->od[c] |= RRDR_DIMENSION_SELECTED;
96 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) r->od[c] &= ~RRDR_DIMENSION_HIDDEN;
97 + dims_selected++;
98 +
99 + // since the user needs this dimension
100 + // make it appear as NONZERO, to return it
101 + // even if the dimension has only zeros
102 + // unless option non_zero is set
103 + if(unlikely(!(options & RRDR_OPTION_NONZERO)))
104 + r->od[c] |= RRDR_DIMENSION_NONZERO;
105 +
106 + // count the visible dimensions
107 + if(likely(r->od[c] & RRDR_DIMENSION_NONZERO))
108 + dims_not_hidden_not_zero++;
109 + }
110 + else {
111 + r->od[c] |= RRDR_DIMENSION_HIDDEN;
112 + if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED)) r->od[c] &= ~RRDR_DIMENSION_SELECTED;
113 + }
114 + }
115 + simple_pattern_free(pattern);
116 +
117 + // check if all dimensions are hidden
118 + if(unlikely(!dims_not_hidden_not_zero && dims_selected)) {
119 + // there are a few selected dimensions
120 + // but they are all zero
121 + // enable the selected ones
122 + // to avoid returning an empty chart
123 + for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
124 + if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED))
125 + r->od[c] |= RRDR_DIMENSION_NONZERO;
126 + }
127 +}
128 +
129 +// ----------------------------------------------------------------------------
130 +// helpers to find our way in RRDR
131 +
132 +static inline RRDR_VALUE_FLAGS *rrdr_line_options(RRDR *r, long rrdr_line) {
133 + return &r->o[ rrdr_line * r->d ];
134 +}
135 +
136 +static inline calculated_number *rrdr_line_values(RRDR *r, long rrdr_line) {
137 + return &r->v[ rrdr_line * r->d ];
138 +}
139 +
140 +static inline long rrdr_line_init(RRDR *r, time_t t, long rrdr_line) {
141 + rrdr_line++;
142 +
143 + #ifdef NETDATA_INTERNAL_CHECKS
144 +
145 + if(unlikely(rrdr_line >= r->n))
146 + error("INTERNAL ERROR: requested to step above RRDR size for chart '%s'", r->st->name);
147 +
148 + if(unlikely(r->t[rrdr_line] != 0 && r->t[rrdr_line] != t))
149 + error("INTERNAL ERROR: overwriting the timestamp of RRDR line %zu from %zu to %zu, of chart '%s'", (size_t)rrdr_line, (size_t)r->t[rrdr_line], (size_t)t, r->st->name);
150 +
151 + #endif
152 +
153 + // save the time
154 + r->t[rrdr_line] = t;
155 +
156 + return rrdr_line;
157 +}
158 +
159 +static inline void rrdr_done(RRDR *r, long rrdr_line) {
160 + r->rows = rrdr_line + 1;
161 +}
162 +
163 +
164 +// ----------------------------------------------------------------------------
165 +// fill RRDR for a single dimension
166 +
167 +static inline void do_dimension(
168 + RRDR *r
169 + , long points_wanted
170 + , RRDDIM *rd
171 + , long dim_id_in_rrdr
172 + , long after_slot
173 + , long before_slot
174 + , time_t after_wanted
175 + , time_t before_wanted
176 +){
177 + (void) before_slot;
178 +
179 + RRDSET *st = r->st;
180 +
181 + time_t
182 + now = after_wanted,
183 + dt = st->update_every,
184 + max_date = 0,
185 + min_date = 0;
186 +
187 + long
188 + slot = after_slot,
189 + group_size = r->group,
190 + points_added = 0,
191 + values_in_group = 0,
192 + values_in_group_non_zero = 0,
193 + rrdr_line = -1,
194 + entries = st->entries;
195 +
196 + RRDR_VALUE_FLAGS
197 + group_value_flags = RRDR_VALUE_NOTHING;
198 +
199 + for( ; points_added < points_wanted ; now += dt, slot++ ) {
200 + if(unlikely(slot >= entries)) slot = 0;
201 +
202 + // make sure we return data in the proper time range
203 + if(unlikely(now > before_wanted)) {
204 + #ifdef NETDATA_INTERNAL_CHECKS
205 + r->log = "stopped, because attempted to access the db after 'wanted before'";
206 + #endif
207 + break;
208 + }
209 + if(unlikely(now < after_wanted)) {
210 + #ifdef NETDATA_INTERNAL_CHECKS
211 + r->log = "skipped, because attempted to access the db before 'wanted after'";
212 + #endif
213 + continue;
214 + }
215 +
216 + // read the value from the database
217 + storage_number n = rd->values[slot];
218 + calculated_number value = NAN;
219 + if(likely(does_storage_number_exist(n))) {
220 +
221 + value = unpack_storage_number(n);
222 + if(likely(value != 0.0))
223 + values_in_group_non_zero++;
224 +
225 + if(unlikely(did_storage_number_reset(n)))
226 + group_value_flags |= RRDR_VALUE_RESET;
227 +
228 + }
229 +
230 + // add this value for grouping
231 + r->grouping_add(r, value);
232 + values_in_group++;
233 +
234 + if(unlikely(values_in_group == group_size)) {
235 + rrdr_line = rrdr_line_init(r, now, rrdr_line);
236 +
237 + if(unlikely(!min_date)) min_date = now;
238 + max_date = now;
239 +
240 + // find the place to store our values
241 + RRDR_VALUE_FLAGS *rrdr_value_options_ptr = &r->o[rrdr_line * r->d + dim_id_in_rrdr];
242 +
243 + // update the dimension options
244 + if(likely(values_in_group_non_zero))
245 + r->od[dim_id_in_rrdr] |= RRDR_DIMENSION_NONZERO;
246 +
247 + // store the specific point options
248 + *rrdr_value_options_ptr = group_value_flags;
249 +
250 + // store the value
251 + r->v[rrdr_line * r->d + dim_id_in_rrdr] = r->grouping_flush(r, rrdr_value_options_ptr);
252 +
253 + points_added++;
254 + values_in_group = 0;
255 + group_value_flags = RRDR_VALUE_NOTHING;
256 + values_in_group_non_zero = 0;
257 + }
258 + }
259 +
260 + r->before = max_date;
261 + r->after = min_date;
262 + rrdr_done(r, rrdr_line);
263 +
264 + #ifdef NETDATA_INTERNAL_CHECKS
265 + if(unlikely(r->rows != points_added))
266 + error("INTERNAL ERROR: %s.%s added %zu rows, but RRDR says I added %zu.", r->st->name, rd->name, (size_t)points_added, (size_t)r->rows);
267 + #endif
268 +}
269 +
270 +// ----------------------------------------------------------------------------
271 +// fill RRDR for the whole chart
272 +
273 +
274 +static void rrd2rrdr_log_request_response_metdata(RRDR *r
275 + , RRDR_GROUPING group_method
276 + , int aligned
277 + , long group
278 + , long group_time
279 + , long group_points
280 + , time_t after_wanted
281 + , time_t after_requested
282 + , time_t before_wanted
283 + , time_t before_requested
284 + , long points_requested
285 + , long points_wanted
286 + , size_t after_slot
287 + , size_t before_slot
288 + , const char *msg
289 + ) {
290 + info("INTERNAL ERROR: rrd2rrdr() on %s update every %d with %s grouping %s (group: %ld, gtime: %ld, gpoints: %ld), "
291 + "after (got: %zu, want: %zu, req: %zu, db: %zu), "
292 + "before (got: %zu, want: %zu, req: %zu, db: %zu), "
293 + "duration (got: %zu, want: %zu, req: %zu, db: %zu), "
294 + "slot (after: %zu, before: %zu, delta: %zu), "
295 + "points (got: %ld, want: %ld, req: %ld, db: %ld), "
296 + "%s"
297 + , r->st->name
298 + , r->st->update_every
299 +
300 + // grouping
301 + , (aligned) ? "aligned" : "unaligned"
302 + , group_method2string(group_method)
303 + , group
304 + , group_time
305 + , group_points
306 +
307 + // after
308 + , (size_t)r->after - (group - 1) * r->st->update_every
309 + , (size_t)after_wanted
310 + , (size_t)after_requested
311 + , (size_t)rrdset_first_entry_t(r->st)
312 +
313 + // before
314 + , (size_t)r->before
315 + , (size_t)before_wanted
316 + , (size_t)before_requested
317 + , (size_t)rrdset_last_entry_t(r->st)
318 +
319 + // duration
320 + , (size_t)(r->before - r->after + r->st->update_every)
321 + , (size_t)(before_wanted - after_wanted + r->st->update_every)
322 + , (size_t)(before_requested - after_requested + r->st->update_every)
323 + , (size_t)((rrdset_last_entry_t(r->st) - rrdset_first_entry_t(r->st)) + r->st->update_every)
324 +
325 + // slot
326 + , after_slot
327 + , before_slot
328 + , (after_slot > before_slot) ? (r->st->entries - after_slot + before_slot) : (before_slot - after_slot)
329 +
330 + // points
331 + , r->rows
332 + , points_wanted
333 + , points_requested
334 + , r->st->entries
335 +
336 + // message
337 + , msg
338 + );
339 +}
340 +
341 +RRDR *rrd2rrdr(
342 + RRDSET *st
343 + , long points_requested
344 + , long long after_requested
345 + , long long before_requested
346 + , RRDR_GROUPING group_method
347 + , long group_time_requested
348 + , RRDR_OPTIONS options
349 + , const char *dimensions
350 +) {
351 + int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
352 +
353 + int absolute_period_requested = -1;
354 +
355 + time_t first_entry_t = rrdset_first_entry_t(st);
356 + time_t last_entry_t = rrdset_last_entry_t(st);
357 +
358 + if(before_requested == 0 && after_requested == 0) {
359 + // dump the all the data
360 + before_requested = last_entry_t;
361 + after_requested = first_entry_t;
362 + absolute_period_requested = 0;
363 + }
364 +
365 + // allow relative for before (smaller than API_RELATIVE_TIME_MAX)
366 + if(((before_requested < 0)?-before_requested:before_requested) <= API_RELATIVE_TIME_MAX) {
367 + if(abs(before_requested) % st->update_every) {
368 + // make sure it is multiple of st->update_every
369 + if(before_requested < 0) before_requested = before_requested - st->update_every - before_requested % st->update_every;
370 + else before_requested = before_requested + st->update_every - before_requested % st->update_every;
371 + }
372 + if(before_requested > 0) before_requested = first_entry_t + before_requested;
373 + else before_requested = last_entry_t + before_requested;
374 + absolute_period_requested = 0;
375 + }
376 +
377 + // allow relative for after (smaller than API_RELATIVE_TIME_MAX)
378 + if(((after_requested < 0)?-after_requested:after_requested) <= API_RELATIVE_TIME_MAX) {
379 + if(after_requested == 0) after_requested = -st->update_every;
380 + if(abs(after_requested) % st->update_every) {
381 + // make sure it is multiple of st->update_every
382 + if(after_requested < 0) after_requested = after_requested - st->update_every - after_requested % st->update_every;
383 + else after_requested = after_requested + st->update_every - after_requested % st->update_every;
384 + }
385 + after_requested = before_requested + after_requested;
386 + absolute_period_requested = 0;
387 + }
388 +
389 + if(absolute_period_requested == -1)
390 + absolute_period_requested = 1;
391 +
392 + // make sure they are within our timeframe
393 + if(before_requested > last_entry_t) before_requested = last_entry_t;
394 + if(before_requested < first_entry_t) before_requested = first_entry_t;
395 +
396 + if(after_requested > last_entry_t) after_requested = last_entry_t;
397 + if(after_requested < first_entry_t) after_requested = first_entry_t;
398 +
399 + // check if they are reversed
400 + if(after_requested > before_requested) {
401 + time_t tmp = before_requested;
402 + before_requested = after_requested;
403 + after_requested = tmp;
404 + }
405 +
406 + // the duration of the chart
407 + time_t duration = before_requested - after_requested;
408 + long available_points = duration / st->update_every;
409 +
410 + if(duration <= 0 || available_points <= 0)
411 + return rrdr_create(st, 1);
412 +
413 + // check the number of wanted points in the result
414 + if(unlikely(points_requested < 0)) points_requested = -points_requested;
415 + if(unlikely(points_requested > available_points)) points_requested = available_points;
416 + if(unlikely(points_requested == 0)) points_requested = available_points;
417 +
418 + // calculate the desired grouping of source data points
419 + long group = available_points / points_requested;
420 + if(unlikely(group <= 0)) group = 1;
421 + if(unlikely(available_points % points_requested > points_requested / 2)) group++; // rounding to the closest integer
422 +
423 + // group_time enforces a certain grouping multiple
424 + calculated_number group_sum_divisor = 1.0;
425 + long group_points = 1;
426 + if(unlikely(group_time_requested > st->update_every)) {
427 + if (unlikely(group_time_requested > duration)) {
428 + // group_time is above the available duration
429 +
430 + #ifdef NETDATA_INTERNAL_CHECKS
431 + info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, group_time_requested, duration);
432 + #endif
433 +
434 + group = points_requested; // use all the points
435 + }
436 + else {
437 + // the points we should group to satisfy gtime
438 + group_points = group_time_requested / st->update_every;
439 + if(unlikely(group_time_requested % group_points)) {
440 + #ifdef NETDATA_INTERNAL_CHECKS
441 + info("INTERNAL CHECK: %s: requested gtime %ld secs, is not a multiple of the chart's data collection frequency %d secs", st->id, group_time_requested, st->update_every);
442 + #endif
443 +
444 + group_points++;
445 + }
446 +
447 + // adapt group according to group_points
448 + if(unlikely(group < group_points)) group = group_points; // do not allow grouping below the desired one
449 + if(unlikely(group % group_points)) group += group_points - (group % group_points); // make sure group is multiple of group_points
450 +
451 + //group_sum_divisor = group / group_points;
452 + group_sum_divisor = (calculated_number)(group * st->update_every) / (calculated_number)group_time_requested;
453 + }
454 + }
455 +
456 + size_t before_slot, after_slot;
457 +
458 + time_t before_wanted = before_requested - (before_requested % ( ((aligned)?group:1) * st->update_every )) + ( ((aligned)?group:1) * st->update_every );
459 + if(unlikely(before_wanted > last_entry_t)) {
460 + before_wanted = last_entry_t - (last_entry_t % ((aligned) ? group : 1) * st->update_every);
461 +
462 + if(unlikely(before_wanted > last_entry_t)) {
463 + #ifdef NETDATA_INTERNAL_CHECKS
464 + error("INTERNAL ERROR: rrd2rrdr() on %s, before_wanted is after db max", st->name);
465 + #endif
466 +
467 + while(before_wanted > last_entry_t)
468 + before_wanted -= (((aligned) ? group : 1) * st->update_every);
469 + }
470 + }
471 + before_slot = rrdset_time2slot(st, before_wanted);
472 +
473 + // we need to estimate the number of points, for having
474 + // an integer number of values per point
475 + long points_wanted = (before_wanted - after_requested) / st->update_every / group;
476 +
477 + time_t after_wanted = before_wanted - (points_wanted * group * st->update_every) + st->update_every;
478 + if(unlikely(after_wanted < first_entry_t)) {
479 + // hm... we go to the past, calculate again points_wanted using all the db from before_wanted to the beginning
480 + points_wanted = (before_wanted - first_entry_t) / group;
481 +
482 + // recalculate after wanted with the new number of points
483 + after_wanted = before_wanted - (points_wanted * group * st->update_every) + st->update_every;
484 +
485 + if(unlikely(after_wanted < first_entry_t)) {
486 + #ifdef NETDATA_INTERNAL_CHECKS
487 + error("INTERNAL ERROR: rrd2rrdr() on %s, after_wanted is before db min", st->name);
488 + #endif
489 +
490 + while(after_wanted < first_entry_t)
491 + after_wanted += (((aligned) ? group : 1) * st->update_every);
492 + }
493 + }
494 + after_slot = rrdset_time2slot(st, after_wanted);
495 +
496 + // check if they are reversed
497 + if(unlikely(after_wanted > before_wanted)) {
498 + #ifdef NETDATA_INTERNAL_CHECKS
499 + error("INTERNAL ERROR: rrd2rrdr() on %s, reversed wanted after/before", st->name);
500 + #endif
501 + time_t tmp = before_wanted;
502 + before_wanted = after_wanted;
503 + after_wanted = tmp;
504 + }
505 +
506 + // recalculate points_wanted using the final time-frame
507 + points_wanted = (before_wanted - after_wanted) / st->update_every / group + 1;
508 + if(unlikely(points_wanted < 0)) {
509 + #ifdef NETDATA_INTERNAL_CHECKS
510 + error("INTERNAL ERROR: rrd2rrdr() on %s, points_wanted is %ld", st->name, points_wanted);
511 + #endif
512 + points_wanted = 0;
513 + }
514 +
515 + duration = before_wanted - after_wanted;
516 +
517 +#ifdef NETDATA_INTERNAL_CHECKS
518 + if(after_wanted < first_entry_t)
519 + error("INTERNAL CHECK: after_wanted %u is too small, minimum %u", (uint32_t)after_wanted, (uint32_t)first_entry_t);
520 +
521 + if(after_wanted > last_entry_t)
522 + error("INTERNAL CHECK: after_wanted %u is too big, maximum %u", (uint32_t)after_wanted, (uint32_t)last_entry_t);
523 +
524 + if(before_wanted < first_entry_t)
525 + error("INTERNAL CHECK: before_wanted %u is too small, minimum %u", (uint32_t)before_wanted, (uint32_t)first_entry_t);
526 +
527 + if(before_wanted > last_entry_t)
528 + error("INTERNAL CHECK: before_wanted %u is too big, maximum %u", (uint32_t)before_wanted, (uint32_t)last_entry_t);
529 +
530 + if(before_slot >= (size_t)st->entries)
531 + error("INTERNAL CHECK: before_slot is invalid %zu, expected 0 to %ld", before_slot, st->entries - 1);
532 +
533 + if(after_slot >= (size_t)st->entries)
534 + error("INTERNAL CHECK: after_slot is invalid %zu, expected 0 to %ld", after_slot, st->entries - 1);
535 +
536 + if(points_wanted > (before_wanted - after_wanted) / group / st->update_every + 1)
537 + error("INTERNAL CHECK: points_wanted %ld is more than points %ld", points_wanted, (before_wanted - after_wanted) / group / st->update_every + 1);
538 +
539 + if(group < group_points)
540 + error("INTERNAL CHECK: group %ld is less than the desired group points %ld", group, group_points);
541 +
542 + if(group > group_points && group % group_points)
543 + error("INTERNAL CHECK: group %ld is not a multiple of the desired group points %ld", group, group_points);
544 +#endif
545 +
546 + // -------------------------------------------------------------------------
547 + // initialize our result set
548 + // this also locks the chart for us
549 +
550 + RRDR *r = rrdr_create(st, points_wanted);
551 + if(unlikely(!r)) {
552 + #ifdef NETDATA_INTERNAL_CHECKS
553 + error("INTERNAL CHECK: Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (uint32_t)duration, points_wanted);
554 + #endif
555 + return NULL;
556 + }
557 +
558 + if(unlikely(!r->d || !points_wanted)) {
559 + #ifdef NETDATA_INTERNAL_CHECKS
560 + error("INTERNAL CHECK: Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%zu, points=%ld", st->id, (uint32_t)after_wanted, (uint32_t)before_wanted, (size_t)duration, points_wanted);
561 + #endif
562 + return r;
563 + }
564 +
565 + if(unlikely(absolute_period_requested == 1))
566 + r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
567 + else
568 + r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
569 +
570 + // find how many dimensions we have
571 + long dimensions_count = r->d;
572 +
573 + // -------------------------------------------------------------------------
574 + // initialize RRDR
575 +
576 + r->group = group;
577 + r->update_every = (int)group * st->update_every;
578 + r->before = before_wanted;
579 + r->after = after_wanted;
580 + r->group_points = group_points;
581 + r->group_sum_divisor = group_sum_divisor;
582 +
583 +
584 + // -------------------------------------------------------------------------
585 + // assign the processor functions
586 +
587 + {
588 + int i, found = 0;
589 + for(i = 0; !found && api_v1_data_groups[i].name ;i++) {
590 + if(api_v1_data_groups[i].value == group_method) {
591 + r->grouping_init = api_v1_data_groups[i].init;
592 + r->grouping_reset = api_v1_data_groups[i].reset;
593 + r->grouping_free = api_v1_data_groups[i].free;
594 + r->grouping_add = api_v1_data_groups[i].add;
595 + r->grouping_flush = api_v1_data_groups[i].flush;
596 + found = 1;
597 + }
598 + }
599 + if(!found) {
600 + errno = 0;
601 + #ifdef NETDATA_INTERNAL_CHECKS
602 + error("INTERNAL ERROR: grouping method %u not found for chart '%s'. Using 'average'", (unsigned int)group_method, r->st->name);
603 + #endif
604 + r->grouping_init = grouping_init_average;
605 + r->grouping_reset = grouping_reset_average;
606 + r->grouping_free = grouping_free_average;
607 + r->grouping_add = grouping_add_average;
608 + r->grouping_flush = grouping_flush_average;
609 + }
610 + }
611 +
612 + // allocate any memory required by the grouping method
613 + r->grouping_data = r->grouping_init(r);
614 +
615 +
616 + // -------------------------------------------------------------------------
617 + // disable the not-wanted dimensions
618 +
619 + rrdset_check_rdlock(st);
620 +
621 + if(dimensions)
622 + rrdr_disable_not_selected_dimensions(r, options, dimensions);
623 +
624 +
625 + // -------------------------------------------------------------------------
626 + // do the work for each dimension
627 +
628 + time_t max_after = 0, min_before = 0;
629 + long max_rows = 0;
630 +
631 + RRDDIM *rd;
632 + long c, dimensions_used = 0, dimensions_nonzero = 0;
633 + for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
634 +
635 + // if we need a percentage, we need to calculate all dimensions
636 + if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN)))
637 + continue;
638 +
639 + // reset the grouping for the new dimension
640 + r->grouping_reset(r);
641 +
642 + do_dimension(
643 + r
644 + , points_wanted
645 + , rd
646 + , c
647 + , after_slot
648 + , before_slot
649 + , after_wanted
650 + , before_wanted
651 + );
652 +
653 + if(r->od[c] & RRDR_DIMENSION_NONZERO)
654 + dimensions_nonzero++;
655 +
656 + // verify all dimensions are aligned
657 + if(unlikely(!dimensions_used)) {
658 + min_before = r->before;
659 + max_after = r->after;
660 + max_rows = r->rows;
661 + }
662 + else {
663 + if(r->after != max_after) {
664 + #ifdef NETDATA_INTERNAL_CHECKS
665 + error("INTERNAL ERROR: 'after' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
666 + st->name, (size_t)max_after, rd->name, (size_t)r->after);
667 + #endif
668 + r->after = (r->after > max_after) ? r->after : max_after;
669 + }
670 +
671 + if(r->before != min_before) {
672 + #ifdef NETDATA_INTERNAL_CHECKS
673 + error("INTERNAL ERROR: 'before' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
674 + st->name, (size_t)min_before, rd->name, (size_t)r->before);
675 + #endif
676 + r->before = (r->before < min_before) ? r->before : min_before;
677 + }
678 +
679 + if(r->rows != max_rows) {
680 + #ifdef NETDATA_INTERNAL_CHECKS
681 + error("INTERNAL ERROR: 'rows' mismatch between dimensions for chart '%s': max is %zu, dimension '%s' has %zu",
682 + st->name, (size_t)max_rows, rd->name, (size_t)r->rows);
683 + #endif
684 + r->rows = (r->rows > max_rows) ? r->rows : max_rows;
685 + }
686 + }
687 +
688 + dimensions_used++;
689 + }
690 +
691 + #ifdef NETDATA_INTERNAL_CHECKS
692 +
693 + if(r->log)
694 + rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, group_time_requested, group_points, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, r->log);
695 +
696 + if(r->rows != points_wanted)
697 + rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, group_time_requested, group_points, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "got 'points' is not wanted 'points'");
698 +
699 + if(aligned && (r->before % group) != 0)
700 + rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, group_time_requested, group_points, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "'before' is not aligned but alignment is required");
701 +
702 + // 'after' should not be aligned, since we start inside the first group
703 + //if(aligned && (r->after % group) != 0)
704 + // rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, group_time_requested, group_points, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "'after' is not aligned but alignment is required");
705 +
706 + if(r->before != before_wanted)
707 + rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, group_time_requested, group_points, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "got 'before' is not wanted 'before'");
708 +
709 + // reported 'after' varies, depending on group
710 + if((r->after - (group - 1) * r->st->update_every) != after_wanted)
711 + rrd2rrdr_log_request_response_metdata(r, group_method, aligned, group, group_time_requested, group_points, after_wanted, after_requested, before_wanted, before_requested, points_requested, points_wanted, after_slot, before_slot, "got 'after' is not wanted 'after'");
712 +
713 + #endif
714 +
715 + // free all resources used by the grouping method
716 + r->grouping_free(r);
717 +
718 + // when all the dimensions are zero, we should return all of them
719 + if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero)) {
720 + // all the dimensions are zero
721 + // mark them as NONZERO to send them all
722 + for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
723 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
724 + r->od[c] |= RRDR_DIMENSION_NONZERO;
725 + }
726 + }
727 +
728 + return r;
729 +}
web/api/queries/query.h new
+22
@@ -0,0 +1,22 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_DATA_QUERY_H
4 +#define NETDATA_API_DATA_QUERY_H
5 +
6 +typedef enum rrdr_grouping {
7 + RRDR_GROUPING_UNDEFINED = 0,
8 + RRDR_GROUPING_AVERAGE = 1,
9 + RRDR_GROUPING_MIN = 2,
10 + RRDR_GROUPING_MAX = 3,
11 + RRDR_GROUPING_SUM = 4,
12 + RRDR_GROUPING_INCREMENTAL_SUM = 5,
13 + RRDR_GROUPING_MEDIAN = 6,
14 + RRDR_GROUPING_STDDEV = 7,
15 + RRDR_GROUPING_SES = 8,
16 +} RRDR_GROUPING;
17 +
18 +extern const char *group_method2string(RRDR_GROUPING group);
19 +extern void web_client_api_v1_init_grouping(void);
20 +extern RRDR_GROUPING web_client_api_request_v1_data_group(const char *name, RRDR_GROUPING def);
21 +
22 +#endif //NETDATA_API_DATA_QUERY_H
web/api/queries/rrdr.c new
+624
@@ -0,0 +1,624 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "rrdr.h"
4 +
5 +/*
6 +static void rrdr_dump(RRDR *r)
7 +{
8 + long c, i;
9 + RRDDIM *d;
10 +
11 + fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
12 +
13 + for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
14 + fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
15 + , d->id
16 + , d->name
17 + , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
18 + , (r->od[c] & RRDR_RESET)?"RESET ":""
19 + , (r->od[c] & RRDR_DIMENSION_HIDDEN)?"HIDDEN ":""
20 + , (r->od[c] & RRDR_DIMENSION_NONZERO)?"NONZERO ":""
21 + );
22 + }
23 +
24 + if(r->rows <= 0) {
25 + fprintf(stderr, "RRDR does not have any values in it.\n");
26 + return;
27 + }
28 +
29 + fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
30 +
31 + // for each line in the array
32 + for(i = 0; i < r->rows ;i++) {
33 + calculated_number *cn = &r->v[ i * r->d ];
34 + RRDR_DIMENSION_FLAGS *co = &r->o[ i * r->d ];
35 +
36 + // print the id and the timestamp of the line
37 + fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
38 +
39 + // for each dimension
40 + for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
41 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
42 + if(unlikely(!(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
43 +
44 + if(co[c] & RRDR_EMPTY)
45 + fprintf(stderr, "null ");
46 + else
47 + fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
48 + , cn[c]
49 + , (co[c] & RRDR_EMPTY)?"E":" "
50 + , (co[c] & RRDR_RESET)?"R":" "
51 + , (co[c] & RRDR_DIMENSION_HIDDEN)?"H":" "
52 + , (co[c] & RRDR_DIMENSION_NONZERO)?"N":" "
53 + );
54 + }
55 +
56 + fprintf(stderr, "\n");
57 + }
58 +}
59 +*/
60 +
61 +#define JSON_DATES_JS 1
62 +#define JSON_DATES_TIMESTAMP 2
63 +
64 +void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
65 + rrdset_check_rdlock(r->st);
66 +
67 + //info("RRD2JSON(): %s: BEGIN", r->st->id);
68 + int row_annotations = 0, dates, dates_with_new = 0;
69 + char kq[2] = "", // key quote
70 + sq[2] = "", // string quote
71 + pre_label[101] = "", // before each label
72 + post_label[101] = "", // after each label
73 + pre_date[101] = "", // the beginning of line, to the date
74 + post_date[101] = "", // closing the date
75 + pre_value[101] = "", // before each value
76 + post_value[101] = "", // after each value
77 + post_line[101] = "", // at the end of each row
78 + normal_annotation[201] = "", // default row annotation
79 + overflow_annotation[201] = "", // overflow row annotation
80 + data_begin[101] = "", // between labels and values
81 + finish[101] = ""; // at the end of everything
82 +
83 + if(datatable) {
84 + dates = JSON_DATES_JS;
85 + if( options & RRDR_OPTION_GOOGLE_JSON ) {
86 + kq[0] = '\0';
87 + sq[0] = '\'';
88 + }
89 + else {
90 + kq[0] = '"';
91 + sq[0] = '"';
92 + }
93 + row_annotations = 1;
94 + snprintfz(pre_date, 100, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
95 + snprintfz(post_date, 100, "%s}", sq);
96 + snprintfz(pre_label, 100, ",\n {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
97 + snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
98 + snprintfz(pre_value, 100, ",{%sv%s:", kq, kq);
99 + strcpy(post_value, "}");
100 + strcpy(post_line, "]}");
101 + snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
102 + strcpy(finish, "\n ]\n}");
103 +
104 + snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
105 + snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
106 +
107 + buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
108 + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
109 + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
110 + buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
111 +
112 + // remove the valueobjects flag
113 + // google wants its own keys
114 + if(options & RRDR_OPTION_OBJECTSROWS)
115 + options &= ~RRDR_OPTION_OBJECTSROWS;
116 + }
117 + else {
118 + kq[0] = '"';
119 + sq[0] = '"';
120 + if(options & RRDR_OPTION_GOOGLE_JSON) {
121 + dates = JSON_DATES_JS;
122 + dates_with_new = 1;
123 + }
124 + else {
125 + dates = JSON_DATES_TIMESTAMP;
126 + dates_with_new = 0;
127 + }
128 + if( options & RRDR_OPTION_OBJECTSROWS )
129 + strcpy(pre_date, " { ");
130 + else
131 + strcpy(pre_date, " [ ");
132 + strcpy(pre_label, ", \"");
133 + strcpy(post_label, "\"");
134 + strcpy(pre_value, ", ");
135 + if( options & RRDR_OPTION_OBJECTSROWS )
136 + strcpy(post_line, "}");
137 + else
138 + strcpy(post_line, "]");
139 + snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
140 + strcpy(finish, "\n ]\n}");
141 +
142 + buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
143 + buffer_sprintf(wb, "%stime%s", sq, sq);
144 + }
145 +
146 + // -------------------------------------------------------------------------
147 + // print the JSON header
148 +
149 + long c, i;
150 + RRDDIM *rd;
151 +
152 + // print the header lines
153 + for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
154 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
155 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
156 +
157 + buffer_strcat(wb, pre_label);
158 + buffer_strcat(wb, rd->name);
159 + buffer_strcat(wb, post_label);
160 + i++;
161 + }
162 + if(!i) {
163 + buffer_strcat(wb, pre_label);
164 + buffer_strcat(wb, "no data");
165 + buffer_strcat(wb, post_label);
166 + }
167 +
168 + // print the begin of row data
169 + buffer_strcat(wb, data_begin);
170 +
171 + // if all dimensions are hidden, print a null
172 + if(!i) {
173 + buffer_strcat(wb, finish);
174 + return;
175 + }
176 +
177 + long start = 0, end = rrdr_rows(r), step = 1;
178 + if(!(options & RRDR_OPTION_REVERSED)) {
179 + start = rrdr_rows(r) - 1;
180 + end = -1;
181 + step = -1;
182 + }
183 +
184 + // for each line in the array
185 + calculated_number total = 1;
186 + for(i = start; i != end ;i += step) {
187 + calculated_number *cn = &r->v[ i * r->d ];
188 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
189 +
190 + time_t now = r->t[i];
191 +
192 + if(dates == JSON_DATES_JS) {
193 + // generate the local date time
194 + struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
195 + if(!tm) { error("localtime_r() failed."); continue; }
196 +
197 + if(likely(i != start)) buffer_strcat(wb, ",\n");
198 + buffer_strcat(wb, pre_date);
199 +
200 + if( options & RRDR_OPTION_OBJECTSROWS )
201 + buffer_sprintf(wb, "%stime%s: ", kq, kq);
202 +
203 + if(dates_with_new)
204 + buffer_strcat(wb, "new ");
205 +
206 + buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
207 +
208 + buffer_strcat(wb, post_date);
209 +
210 + if(row_annotations) {
211 + // google supports one annotation per row
212 + int annotation_found = 0;
213 + for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
214 + if(co[c] & RRDR_VALUE_RESET) {
215 + buffer_strcat(wb, overflow_annotation);
216 + annotation_found = 1;
217 + break;
218 + }
219 + }
220 + if(!annotation_found)
221 + buffer_strcat(wb, normal_annotation);
222 + }
223 + }
224 + else {
225 + // print the timestamp of the line
226 + if(likely(i != start)) buffer_strcat(wb, ",\n");
227 + buffer_strcat(wb, pre_date);
228 +
229 + if( options & RRDR_OPTION_OBJECTSROWS )
230 + buffer_sprintf(wb, "%stime%s: ", kq, kq);
231 +
232 + buffer_rrd_value(wb, (calculated_number)r->t[i]);
233 + // in ms
234 + if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
235 +
236 + buffer_strcat(wb, post_date);
237 + }
238 +
239 + int set_min_max = 0;
240 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
241 + total = 0;
242 + for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
243 + calculated_number n = cn[c];
244 +
245 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
246 + n = -n;
247 +
248 + total += n;
249 + }
250 + // prevent a division by zero
251 + if(total == 0) total = 1;
252 + set_min_max = 1;
253 + }
254 +
255 + // for each dimension
256 + for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
257 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
258 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
259 +
260 + calculated_number n = cn[c];
261 +
262 + buffer_strcat(wb, pre_value);
263 +
264 + if( options & RRDR_OPTION_OBJECTSROWS )
265 + buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
266 +
267 + if(co[c] & RRDR_VALUE_EMPTY) {
268 + if(options & RRDR_OPTION_NULL2ZERO)
269 + buffer_strcat(wb, "0");
270 + else
271 + buffer_strcat(wb, "null");
272 + }
273 + else {
274 + if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
275 + n = -n;
276 +
277 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
278 + n = n * 100 / total;
279 +
280 + if(unlikely(set_min_max)) {
281 + r->min = r->max = n;
282 + set_min_max = 0;
283 + }
284 +
285 + if(n < r->min) r->min = n;
286 + if(n > r->max) r->max = n;
287 + }
288 +
289 + buffer_rrd_value(wb, n);
290 + }
291 +
292 + buffer_strcat(wb, post_value);
293 + }
294 +
295 + buffer_strcat(wb, post_line);
296 + }
297 +
298 + buffer_strcat(wb, finish);
299 + //info("RRD2JSON(): %s: END", r->st->id);
300 +}
301 +
302 +void rrdr2csv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines) {
303 + rrdset_check_rdlock(r->st);
304 +
305 + //info("RRD2CSV(): %s: BEGIN", r->st->id);
306 + long c, i;
307 + RRDDIM *d;
308 +
309 + // print the csv header
310 + for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
311 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
312 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
313 +
314 + if(!i) {
315 + buffer_strcat(wb, startline);
316 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
317 + buffer_strcat(wb, "time");
318 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
319 + }
320 + buffer_strcat(wb, separator);
321 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
322 + buffer_strcat(wb, d->name);
323 + if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
324 + i++;
325 + }
326 + buffer_strcat(wb, endline);
327 +
328 + if(!i) {
329 + // no dimensions present
330 + return;
331 + }
332 +
333 + long start = 0, end = rrdr_rows(r), step = 1;
334 + if(!(options & RRDR_OPTION_REVERSED)) {
335 + start = rrdr_rows(r) - 1;
336 + end = -1;
337 + step = -1;
338 + }
339 +
340 + // for each line in the array
341 + calculated_number total = 1;
342 + for(i = start; i != end ;i += step) {
343 + calculated_number *cn = &r->v[ i * r->d ];
344 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
345 +
346 + buffer_strcat(wb, betweenlines);
347 + buffer_strcat(wb, startline);
348 +
349 + time_t now = r->t[i];
350 +
351 + if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
352 + // print the timestamp of the line
353 + buffer_rrd_value(wb, (calculated_number)now);
354 + // in ms
355 + if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
356 + }
357 + else {
358 + // generate the local date time
359 + struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
360 + if(!tm) { error("localtime() failed."); continue; }
361 + buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
362 + }
363 +
364 + int set_min_max = 0;
365 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
366 + total = 0;
367 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
368 + calculated_number n = cn[c];
369 +
370 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
371 + n = -n;
372 +
373 + total += n;
374 + }
375 + // prevent a division by zero
376 + if(total == 0) total = 1;
377 + set_min_max = 1;
378 + }
379 +
380 + // for each dimension
381 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
382 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
383 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
384 +
385 + buffer_strcat(wb, separator);
386 +
387 + calculated_number n = cn[c];
388 +
389 + if(co[c] & RRDR_VALUE_EMPTY) {
390 + if(options & RRDR_OPTION_NULL2ZERO)
391 + buffer_strcat(wb, "0");
392 + else
393 + buffer_strcat(wb, "null");
394 + }
395 + else {
396 + if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
397 + n = -n;
398 +
399 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
400 + n = n * 100 / total;
401 +
402 + if(unlikely(set_min_max)) {
403 + r->min = r->max = n;
404 + set_min_max = 0;
405 + }
406 +
407 + if(n < r->min) r->min = n;
408 + if(n > r->max) r->max = n;
409 + }
410 +
411 + buffer_rrd_value(wb, n);
412 + }
413 + }
414 +
415 + buffer_strcat(wb, endline);
416 + }
417 + //info("RRD2CSV(): %s: END", r->st->id);
418 +}
419 +
420 +inline calculated_number rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null) {
421 + rrdset_check_rdlock(r->st);
422 +
423 + long c;
424 + RRDDIM *d;
425 +
426 + calculated_number *cn = &r->v[ i * r->d ];
427 + RRDR_VALUE_FLAGS *co = &r->o[ i * r->d ];
428 +
429 + calculated_number sum = 0, min = 0, max = 0, v;
430 + int all_null = 1, init = 1;
431 +
432 + calculated_number total = 1;
433 + int set_min_max = 0;
434 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
435 + total = 0;
436 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
437 + calculated_number n = cn[c];
438 +
439 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
440 + n = -n;
441 +
442 + total += n;
443 + }
444 + // prevent a division by zero
445 + if(total == 0) total = 1;
446 + set_min_max = 1;
447 + }
448 +
449 + // for each dimension
450 + for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
451 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
452 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
453 +
454 + calculated_number n = cn[c];
455 +
456 + if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
457 + n = -n;
458 +
459 + if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
460 + n = n * 100 / total;
461 +
462 + if(unlikely(set_min_max)) {
463 + r->min = r->max = n;
464 + set_min_max = 0;
465 + }
466 +
467 + if(n < r->min) r->min = n;
468 + if(n > r->max) r->max = n;
469 + }
470 +
471 + if(unlikely(init)) {
472 + if(n > 0) {
473 + min = 0;
474 + max = n;
475 + }
476 + else {
477 + min = n;
478 + max = 0;
479 + }
480 + init = 0;
481 + }
482 +
483 + if(likely(!(co[c] & RRDR_VALUE_EMPTY))) {
484 + all_null = 0;
485 + sum += n;
486 + }
487 +
488 + if(n < min) min = n;
489 + if(n > max) max = n;
490 + }
491 +
492 + if(unlikely(all_null)) {
493 + if(likely(all_values_are_null))
494 + *all_values_are_null = 1;
495 + return 0;
496 + }
497 + else {
498 + if(likely(all_values_are_null))
499 + *all_values_are_null = 0;
500 + }
501 +
502 + if(options & RRDR_OPTION_MIN2MAX)
503 + v = max - min;
504 + else
505 + v = sum;
506 +
507 + return v;
508 +}
509 +
510 +void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix) {
511 + //info("RRD2SSV(): %s: BEGIN", r->st->id);
512 + long i;
513 +
514 + buffer_strcat(wb, prefix);
515 + long start = 0, end = rrdr_rows(r), step = 1;
516 + if(!(options & RRDR_OPTION_REVERSED)) {
517 + start = rrdr_rows(r) - 1;
518 + end = -1;
519 + step = -1;
520 + }
521 +
522 + // for each line in the array
523 + for(i = start; i != end ;i += step) {
524 + int all_values_are_null = 0;
525 + calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
526 +
527 + if(likely(i != start)) {
528 + if(r->min > v) r->min = v;
529 + if(r->max < v) r->max = v;
530 + }
531 + else {
532 + r->min = v;
533 + r->max = v;
534 + }
535 +
536 + if(likely(i != start))
537 + buffer_strcat(wb, separator);
538 +
539 + if(all_values_are_null) {
540 + if(options & RRDR_OPTION_NULL2ZERO)
541 + buffer_strcat(wb, "0");
542 + else
543 + buffer_strcat(wb, "null");
544 + }
545 + else
546 + buffer_rrd_value(wb, v);
547 + }
548 + buffer_strcat(wb, suffix);
549 + //info("RRD2SSV(): %s: END", r->st->id);
550 +}
551 +
552 +inline static void rrdr_lock_rrdset(RRDR *r) {
553 + if(unlikely(!r)) {
554 + error("NULL value given!");
555 + return;
556 + }
557 +
558 + rrdset_rdlock(r->st);
559 + r->has_st_lock = 1;
560 +}
561 +
562 +inline static void rrdr_unlock_rrdset(RRDR *r) {
563 + if(unlikely(!r)) {
564 + error("NULL value given!");
565 + return;
566 + }
567 +
568 + if(likely(r->has_st_lock)) {
569 + rrdset_unlock(r->st);
570 + r->has_st_lock = 0;
571 + }
572 +}
573 +
574 +inline void rrdr_free(RRDR *r)
575 +{
576 + if(unlikely(!r)) {
577 + error("NULL value given!");
578 + return;
579 + }
580 +
581 + rrdr_unlock_rrdset(r);
582 + freez(r->t);
583 + freez(r->v);
584 + freez(r->o);
585 + freez(r->od);
586 + freez(r);
587 +}
588 +
589 +RRDR *rrdr_create(RRDSET *st, long n)
590 +{
591 + if(unlikely(!st)) {
592 + error("NULL value given!");
593 + return NULL;
594 + }
595 +
596 + RRDR *r = callocz(1, sizeof(RRDR));
597 + r->st = st;
598 +
599 + rrdr_lock_rrdset(r);
600 +
601 + RRDDIM *rd;
602 + rrddim_foreach_read(rd, st) r->d++;
603 +
604 + r->n = n;
605 +
606 + r->t = callocz((size_t)n, sizeof(time_t));
607 + r->v = mallocz(n * r->d * sizeof(calculated_number));
608 + r->o = mallocz(n * r->d * sizeof(RRDR_VALUE_FLAGS));
609 + r->od = mallocz(r->d * sizeof(RRDR_DIMENSION_FLAGS));
610 +
611 + // set the hidden flag on hidden dimensions
612 + int c;
613 + for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
614 + if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)))
615 + r->od[c] = RRDR_DIMENSION_HIDDEN;
616 + else
617 + r->od[c] = 0;
618 + }
619 +
620 + r->group = 1;
621 + r->update_every = 1;
622 +
623 + return r;
624 +}
web/api/queries/rrdr.h new
+105
@@ -0,0 +1,105 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_QUERIES_RRDR_H
4 +#define NETDATA_QUERIES_RRDR_H
5 +
6 +#include "libnetdata/libnetdata.h"
7 +#include "../web_api_v1.h"
8 +
9 +typedef enum rrdr_options {
10 + RRDR_OPTION_NONZERO = 0x00000001, // don't output dimensions will just zero values
11 + RRDR_OPTION_REVERSED = 0x00000002, // output the rows in reverse order (oldest to newest)
12 + RRDR_OPTION_ABSOLUTE = 0x00000004, // values positive, for DATASOURCE_SSV before summing
13 + RRDR_OPTION_MIN2MAX = 0x00000008, // when adding dimensions, use max - min, instead of sum
14 + RRDR_OPTION_SECONDS = 0x00000010, // output seconds, instead of dates
15 + RRDR_OPTION_MILLISECONDS = 0x00000020, // output milliseconds, instead of dates
16 + RRDR_OPTION_NULL2ZERO = 0x00000040, // do not show nulls, convert them to zeros
17 + RRDR_OPTION_OBJECTSROWS = 0x00000080, // each row of values should be an object, not an array
18 + RRDR_OPTION_GOOGLE_JSON = 0x00000100, // comply with google JSON/JSONP specs
19 + RRDR_OPTION_JSON_WRAP = 0x00000200, // wrap the response in a JSON header with info about the result
20 + RRDR_OPTION_LABEL_QUOTES = 0x00000400, // in CSV output, wrap header labels in double quotes
21 + RRDR_OPTION_PERCENTAGE = 0x00000800, // give values as percentage of total
22 + RRDR_OPTION_NOT_ALIGNED = 0x00001000, // do not align charts for persistant timeframes
23 + RRDR_OPTION_DISPLAY_ABS = 0x00002000, // for badges, display the absolute value, but calculate colors with sign
24 + RRDR_OPTION_MATCH_IDS = 0x00004000, // when filtering dimensions, match only IDs
25 + RRDR_OPTION_MATCH_NAMES = 0x00008000, // when filtering dimensions, match only names
26 +} RRDR_OPTIONS;
27 +
28 +typedef enum rrdr_value_flag {
29 + RRDR_VALUE_NOTHING = 0x00, // no flag set
30 + RRDR_VALUE_EMPTY = 0x01, // the value is empty
31 + RRDR_VALUE_RESET = 0x02, // the value has been reset
32 +} RRDR_VALUE_FLAGS;
33 +
34 +typedef enum rrdr_dimension_flag {
35 + RRDR_DIMENSION_HIDDEN = 0x04, // the dimension is hidden
36 + RRDR_DIMENSION_NONZERO = 0x08, // the dimension non zero
37 + RRDR_DIMENSION_SELECTED = 0x10, // the dimension is selected
38 +} RRDR_DIMENSION_FLAGS;
39 +
40 +// RRDR result options
41 +typedef enum rrdr_result_flags {
42 + RRDR_RESULT_OPTION_ABSOLUTE = 0x00000001,
43 + RRDR_RESULT_OPTION_RELATIVE = 0x00000002,
44 +} RRDR_RESULT_FLAGS;
45 +
46 +typedef struct rrdresult {
47 + RRDSET *st; // the chart this result refers to
48 +
49 + RRDR_RESULT_FLAGS result_options; // RRDR_RESULT_OPTION_*
50 +
51 + int d; // the number of dimensions
52 + long n; // the number of values in the arrays
53 + long rows; // the number of rows used
54 +
55 + RRDR_DIMENSION_FLAGS *od; // the options for the dimensions
56 +
57 + time_t *t; // array of n timestamps
58 + calculated_number *v; // array n x d values
59 + RRDR_VALUE_FLAGS *o; // array n x d options for each value returned
60 +
61 + long group; // how many collected values were grouped for each row
62 + int update_every; // what is the suggested update frequency in seconds
63 +
64 + calculated_number min;
65 + calculated_number max;
66 +
67 + time_t before;
68 + time_t after;
69 +
70 + int has_st_lock; // if st is read locked by us
71 +
72 + // internal rrd2rrdr() members below this point
73 + long group_points;
74 + calculated_number group_sum_divisor;
75 +
76 + void *(*grouping_init)(struct rrdresult *r);
77 + void (*grouping_reset)(struct rrdresult *r);
78 + void (*grouping_free)(struct rrdresult *r);
79 + void (*grouping_add)(struct rrdresult *r, calculated_number value);
80 + calculated_number (*grouping_flush)(struct rrdresult *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
81 + void *grouping_data;
82 +
83 +#ifdef NETDATA_INTERNAL_CHECKS
84 + const char *log;
85 +#endif
86 +} RRDR;
87 +
88 +#define rrdr_rows(r) ((r)->rows)
89 +
90 +// formatters
91 +extern void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable);
92 +extern void rrdr2csv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines);
93 +extern calculated_number rrdr2value(RRDR *r, long i, RRDR_OPTIONS options, int *all_values_are_null);
94 +extern void rrdr2ssv(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, const char *prefix, const char *separator, const char *suffix);
95 +
96 +extern void rrdr_free(RRDR *r);
97 +extern RRDR *rrdr_create(RRDSET *st, long n);
98 +
99 +#include "web/api/queries/query.h"
100 +
101 +extern RRDR *rrd2rrdr(RRDSET *st, long points_requested, long long after_requested, long long before_requested, RRDR_GROUPING group_method, long group_time_requested, RRDR_OPTIONS options, const char *dimensions);
102 +
103 +#include "query.h"
104 +
105 +#endif //NETDATA_QUERIES_RRDR_H
web/api/queries/ses/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/ses/README.md new
+1
@@ -0,0 +1 @@
1 +# single exponential smoothing
web/api/queries/ses/ses.c new
+74
@@ -0,0 +1,74 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "ses.h"
4 +
5 +
6 +// ----------------------------------------------------------------------------
7 +// single exponential smoothing
8 +
9 +struct grouping_ses {
10 + calculated_number alpha;
11 + calculated_number alpha_older;
12 + calculated_number level;
13 + size_t count;
14 + size_t has_data;
15 +};
16 +
17 +static inline void set_alpha(RRDR *r, struct grouping_ses *g) {
18 + g->alpha = 1.0 / r->group;
19 + g->alpha_older = 1 - g->alpha;
20 +}
21 +
22 +void *grouping_init_ses(RRDR *r) {
23 + struct grouping_ses *g = (struct grouping_ses *)callocz(1, sizeof(struct grouping_ses));
24 + set_alpha(r, g);
25 + g->level = 0.0;
26 + return g;
27 +}
28 +
29 +// resets when switches dimensions
30 +// so, clear everything to restart
31 +void grouping_reset_ses(RRDR *r) {
32 + struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
33 + g->level = 0.0;
34 + g->count = 0;
35 + g->has_data = 0;
36 +}
37 +
38 +void grouping_free_ses(RRDR *r) {
39 + freez(r->grouping_data);
40 + r->grouping_data = NULL;
41 +}
42 +
43 +void grouping_add_ses(RRDR *r, calculated_number value) {
44 + struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
45 +
46 + if(isnormal(value)) {
47 + if(unlikely(!g->has_data)) {
48 + g->level = value;
49 + g->has_data = 1;
50 + }
51 +
52 + g->level = g->alpha * value + g->alpha_older * g->level;
53 +
54 + g->count++;
55 + }
56 +}
57 +
58 +calculated_number grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
59 + struct grouping_ses *g = (struct grouping_ses *)r->grouping_data;
60 +
61 + calculated_number value;
62 +
63 + if(unlikely(!g->count || !isnormal(g->level))) {
64 + value = 0.0;
65 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
66 + }
67 + else {
68 + value = g->level;
69 + }
70 +
71 + g->count = 0;
72 +
73 + return value;
74 +}
web/api/queries/ses/ses.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERIES_SES_H
4 +#define NETDATA_API_QUERIES_SES_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_ses(RRDR *r);
10 +extern void grouping_reset_ses(RRDR *r);
11 +extern void grouping_free_ses(RRDR *r);
12 +extern void grouping_add_ses(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERIES_SES_H
web/api/queries/stddev/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/stddev/README.md
web/api/queries/stddev/stddev.c new
+73
@@ -0,0 +1,73 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "stddev.h"
4 +
5 +
6 +// ----------------------------------------------------------------------------
7 +// stddev
8 +
9 +struct grouping_stddev {
10 + size_t series_size;
11 + size_t next_pos;
12 +
13 + LONG_DOUBLE series[];
14 +};
15 +
16 +void *grouping_init_stddev(RRDR *r) {
17 + long entries = (r->group > r->group_points) ? r->group : r->group_points;
18 + if(entries < 0) entries = 0;
19 +
20 + struct grouping_stddev *g = (struct grouping_stddev *)callocz(1, sizeof(struct grouping_stddev) + entries * sizeof(LONG_DOUBLE));
21 + g->series_size = (size_t)entries;
22 +
23 + return g;
24 +}
25 +
26 +// resets when switches dimensions
27 +// so, clear everything to restart
28 +void grouping_reset_stddev(RRDR *r) {
29 + struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
30 + g->next_pos = 0;
31 +}
32 +
33 +void grouping_free_stddev(RRDR *r) {
34 + freez(r->grouping_data);
35 +}
36 +
37 +void grouping_add_stddev(RRDR *r, calculated_number value) {
38 + struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
39 +
40 + if(unlikely(g->next_pos >= g->series_size)) {
41 + error("INTERNAL ERROR: stddev buffer overflow on chart '%s' - next_pos = %zu, series_size = %zu, r->group = %ld, r->group_points = %ld.", r->st->name, g->next_pos, g->series_size, r->group, r->group_points);
42 + }
43 + else {
44 + if(isnormal(value))
45 + g->series[g->next_pos++] = (LONG_DOUBLE)value;
46 + }
47 +}
48 +
49 +calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
50 + struct grouping_stddev *g = (struct grouping_stddev *)r->grouping_data;
51 +
52 + calculated_number value;
53 +
54 + if(unlikely(!g->next_pos)) {
55 + value = 0.0;
56 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
57 + }
58 + else {
59 + value = standard_deviation(g->series, g->next_pos);
60 +
61 + if(!isnormal(value)) {
62 + value = 0.0;
63 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
64 + }
65 +
66 + //log_series_to_stderr(g->series, g->next_pos, value, "stddev");
67 + }
68 +
69 + g->next_pos = 0;
70 +
71 + return value;
72 +}
73 +
web/api/queries/stddev/stddev.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERIES_STDDEV_H
4 +#define NETDATA_API_QUERIES_STDDEV_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_stddev(RRDR *r);
10 +extern void grouping_reset_stddev(RRDR *r);
11 +extern void grouping_free_stddev(RRDR *r);
12 +extern void grouping_add_stddev(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_stddev(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERIES_STDDEV_H
web/api/queries/sum/Makefile.am new
+8
@@ -0,0 +1,8 @@
1 +# SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +AUTOMAKE_OPTIONS = subdir-objects
4 +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
5 +
6 +dist_noinst_DATA = \
7 + README.md \
8 + $(NULL)
web/api/queries/sum/README.md
web/api/queries/sum/sum.c new
+60
@@ -0,0 +1,60 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "sum.h"
4 +
5 +// ----------------------------------------------------------------------------
6 +// sum
7 +
8 +struct grouping_sum {
9 + calculated_number sum;
10 + size_t count;
11 +};
12 +
13 +void *grouping_init_sum(RRDR *r) {
14 + (void)r;
15 + return callocz(1, sizeof(struct grouping_sum));
16 +}
17 +
18 +// resets when switches dimensions
19 +// so, clear everything to restart
20 +void grouping_reset_sum(RRDR *r) {
21 + struct grouping_sum *g = (struct grouping_sum *)r->grouping_data;
22 + g->sum = 0;
23 + g->count = 0;
24 +}
25 +
26 +void grouping_free_sum(RRDR *r) {
27 + freez(r->grouping_data);
28 +}
29 +
30 +void grouping_add_sum(RRDR *r, calculated_number value) {
31 + if(!isnan(value)) {
32 + struct grouping_sum *g = (struct grouping_sum *)r->grouping_data;
33 +
34 + if(!g->count || calculated_number_fabs(value) > calculated_number_fabs(g->sum)) {
35 + g->sum += value;
36 + g->count++;
37 + }
38 + }
39 +}
40 +
41 +calculated_number grouping_flush_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
42 + struct grouping_sum *g = (struct grouping_sum *)r->grouping_data;
43 +
44 + calculated_number value;
45 +
46 + if(unlikely(!g->count)) {
47 + value = 0.0;
48 + *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
49 + }
50 + else {
51 + value = g->sum;
52 + }
53 +
54 + g->sum = 0.0;
55 + g->count = 0;
56 +
57 + return value;
58 +}
59 +
60 +
web/api/queries/sum/sum.h new
+15
@@ -0,0 +1,15 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_API_QUERY_SUM_H
4 +#define NETDATA_API_QUERY_SUM_H
5 +
6 +#include "../query.h"
7 +#include "../rrdr.h"
8 +
9 +extern void *grouping_init_sum(RRDR *r);
10 +extern void grouping_reset_sum(RRDR *r);
11 +extern void grouping_free_sum(RRDR *r);
12 +extern void grouping_add_sum(RRDR *r, calculated_number value);
13 +extern calculated_number grouping_flush_sum(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr);
14 +
15 +#endif //NETDATA_API_QUERY_SUM_H
web/api/rrd2json.c
+90 -1456
@@ -208,406 +208,56 @@ void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb) {
208 buffer_sprintf(wb, "\n\t]\n}\n");
209 }
210
211 -// ----------------------------------------------------------------------------
212 -// BASH
213 -// /api/v1/allmetrics?format=bash
211
215 -static inline size_t shell_name_copy(char *d, const char *s, size_t usable) {
216 - size_t n;
217 -
218 - for(n = 0; *s && n < usable ; d++, s++, n++) {
219 - register char c = *s;
220 -
221 - if(unlikely(!isalnum(c))) *d = '_';
222 - else *d = (char)toupper(c);
223 - }
224 - *d = '\0';
225 -
226 - return n;
227 -}
228 -
229 -#define SHELL_ELEMENT_MAX 100
230 -
231 -void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb) {
232 - rrdhost_rdlock(host);
233 -
234 - // for each chart
235 - RRDSET *st;
236 - rrdset_foreach_read(st, host) {
237 - calculated_number total = 0.0;
238 - char chart[SHELL_ELEMENT_MAX + 1];
239 - shell_name_copy(chart, st->name?st->name:st->id, SHELL_ELEMENT_MAX);
240 -
241 - buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name);
242 - if(rrdset_is_available_for_viewers(st)) {
243 - rrdset_rdlock(st);
244 -
245 - // for each dimension
246 - RRDDIM *rd;
247 - rrddim_foreach_read(rd, st) {
248 - if(rd->collections_counter) {
249 - char dimension[SHELL_ELEMENT_MAX + 1];
250 - shell_name_copy(dimension, rd->name?rd->name:rd->id, SHELL_ELEMENT_MAX);
251 -
252 - calculated_number n = rd->last_stored_value;
253 -
254 - if(isnan(n) || isinf(n))
255 - buffer_sprintf(wb, "NETDATA_%s_%s=\"\" # %s\n", chart, dimension, st->units);
256 - else {
257 - if(rd->multiplier < 0 || rd->divisor < 0) n = -n;
258 - n = calculated_number_round(n);
259 - if(!rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)) total += n;
260 - buffer_sprintf(wb, "NETDATA_%s_%s=\"" CALCULATED_NUMBER_FORMAT_ZERO "\" # %s\n", chart, dimension, n, st->units);
261 - }
262 - }
263 - }
264 -
265 - total = calculated_number_round(total);
266 - buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"" CALCULATED_NUMBER_FORMAT_ZERO "\" # %s\n", chart, total, st->units);
267 - rrdset_unlock(st);
268 - }
269 - }
270 -
271 - buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n");
272 -
273 - RRDCALC *rc;
274 - for(rc = host->alarms; rc ;rc = rc->next) {
275 - if(!rc->rrdset) continue;
276 -
277 - char chart[SHELL_ELEMENT_MAX + 1];
278 - shell_name_copy(chart, rc->rrdset->name?rc->rrdset->name:rc->rrdset->id, SHELL_ELEMENT_MAX);
279 -
280 - char alarm[SHELL_ELEMENT_MAX + 1];
281 - shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX);
282 -
283 - calculated_number n = rc->value;
284 -
285 - if(isnan(n) || isinf(n))
286 - buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\" # %s\n", chart, alarm, rc->units);
287 - else {
288 - n = calculated_number_round(n);
289 - buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"" CALCULATED_NUMBER_FORMAT_ZERO "\" # %s\n", chart, alarm, n, rc->units);
290 - }
291 -
292 - buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status));
293 - }
294 -
295 - rrdhost_unlock(host);
296 -}
297 -
298 -// ----------------------------------------------------------------------------
299 -
300 -void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb) {
301 - rrdhost_rdlock(host);
302 -
303 - buffer_strcat(wb, "{");
304 -
305 - size_t chart_counter = 0;
306 - size_t dimension_counter = 0;
307 -
308 - // for each chart
309 - RRDSET *st;
310 - rrdset_foreach_read(st, host) {
311 - if(rrdset_is_available_for_viewers(st)) {
312 - rrdset_rdlock(st);
313 -
314 - buffer_sprintf(wb, "%s\n"
315 - "\t\"%s\": {\n"
316 - "\t\t\"name\":\"%s\",\n"
317 - "\t\t\"context\":\"%s\",\n"
318 - "\t\t\"units\":\"%s\",\n"
319 - "\t\t\"last_updated\": %ld,\n"
320 - "\t\t\"dimensions\": {"
321 - , chart_counter?",":""
322 - , st->id
323 - , st->name
324 - , st->context
325 - , st->units
326 - , rrdset_last_entry_t(st)
327 - );
328 -
329 - chart_counter++;
330 - dimension_counter = 0;
331 -
332 - // for each dimension
333 - RRDDIM *rd;
334 - rrddim_foreach_read(rd, st) {
335 - if(rd->collections_counter) {
336 -
337 - buffer_sprintf(wb, "%s\n"
338 - "\t\t\t\"%s\": {\n"
339 - "\t\t\t\t\"name\": \"%s\",\n"
340 - "\t\t\t\t\"value\": "
341 - , dimension_counter?",":""
342 - , rd->id
343 - , rd->name
344 - );
345 -
346 - if(isnan(rd->last_stored_value))
347 - buffer_strcat(wb, "null");
348 - else
349 - buffer_sprintf(wb, CALCULATED_NUMBER_FORMAT, rd->last_stored_value);
350 -
351 - buffer_strcat(wb, "\n\t\t\t}");
352 -
353 - dimension_counter++;
354 - }
355 - }
356 -
357 - buffer_strcat(wb, "\n\t\t}\n\t}");
358 - rrdset_unlock(st);
359 - }
360 - }
361 -
362 - buffer_strcat(wb, "\n}");
363 - rrdhost_unlock(host);
364 -}
365 -
366 -// ----------------------------------------------------------------------------
367 -
368 -// RRDR dimension options
369 -#define RRDR_EMPTY 0x01 // the dimension contains / the value is empty (null)
370 -#define RRDR_RESET 0x02 // the dimension contains / the value is reset
371 -#define RRDR_HIDDEN 0x04 // the dimension contains / the value is hidden
372 -#define RRDR_NONZERO 0x08 // the dimension contains / the value is non-zero
373 -#define RRDR_SELECTED 0x10 // the dimension is selected
374 -
375 -// RRDR result options
376 -#define RRDR_RESULT_OPTION_ABSOLUTE 0x00000001
377 -#define RRDR_RESULT_OPTION_RELATIVE 0x00000002
378 -
379 -typedef struct rrdresult {
380 - RRDSET *st; // the chart this result refers to
381 -
382 - uint32_t result_options; // RRDR_RESULT_OPTION_*
383 -
384 - int d; // the number of dimensions
385 - long n; // the number of values in the arrays
386 - long rows; // the number of rows used
387 -
388 - uint8_t *od; // the options for the dimensions
389 -
390 - time_t *t; // array of n timestamps
391 - calculated_number *v; // array n x d values
392 - uint8_t *o; // array n x d options
393 -
394 - long c; // current line ( -1 ~ n ), ( -1 = none, use rrdr_rows() to get number of rows )
395 -
396 - long group; // how many collected values were grouped for each row
397 - int update_every; // what is the suggested update frequency in seconds
398 -
399 - calculated_number min;
400 - calculated_number max;
401 -
402 - time_t before;
403 - time_t after;
404 -
405 - int has_st_lock; // if st is read locked by us
406 -} RRDR;
407 -
408 -#define rrdr_rows(r) ((r)->rows)
409 -
410 -/*
411 -static void rrdr_dump(RRDR *r)
412 -{
413 - long c, i;
414 - RRDDIM *d;
415 -
416 - fprintf(stderr, "\nCHART %s (%s)\n", r->st->id, r->st->name);
417 -
418 - for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
419 - fprintf(stderr, "DIMENSION %s (%s), %s%s%s%s\n"
420 - , d->id
421 - , d->name
422 - , (r->od[c] & RRDR_EMPTY)?"EMPTY ":""
423 - , (r->od[c] & RRDR_RESET)?"RESET ":""
424 - , (r->od[c] & RRDR_HIDDEN)?"HIDDEN ":""
425 - , (r->od[c] & RRDR_NONZERO)?"NONZERO ":""
426 - );
427 - }
428 -
429 - if(r->rows <= 0) {
430 - fprintf(stderr, "RRDR does not have any values in it.\n");
431 - return;
432 - }
433 -
434 - fprintf(stderr, "RRDR includes %d values in it:\n", r->rows);
435 -
436 - // for each line in the array
437 - for(i = 0; i < r->rows ;i++) {
438 - calculated_number *cn = &r->v[ i * r->d ];
439 - uint8_t *co = &r->o[ i * r->d ];
440 -
441 - // print the id and the timestamp of the line
442 - fprintf(stderr, "%ld %ld ", i + 1, r->t[i]);
443 -
444 - // for each dimension
445 - for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
446 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
447 - if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
448 -
449 - if(co[c] & RRDR_EMPTY)
450 - fprintf(stderr, "null ");
451 - else
452 - fprintf(stderr, CALCULATED_NUMBER_FORMAT " %s%s%s%s "
453 - , cn[c]
454 - , (co[c] & RRDR_EMPTY)?"E":" "
455 - , (co[c] & RRDR_RESET)?"R":" "
456 - , (co[c] & RRDR_HIDDEN)?"H":" "
457 - , (co[c] & RRDR_NONZERO)?"N":" "
458 - );
459 - }
460 -
461 - fprintf(stderr, "\n");
462 - }
463 -}
464 -*/
465 -
466 -void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char *dims) {
467 - rrdset_check_rdlock(r->st);
468 -
469 - if(unlikely(!dims || !*dims || (dims[0] == '*' && dims[1] == '\0'))) return;
470 -
471 - int match_ids = 0, match_names = 0;
472 -
473 - if(unlikely(options & RRDR_OPTION_MATCH_IDS))
474 - match_ids = 1;
475 - if(unlikely(options & RRDR_OPTION_MATCH_NAMES))
476 - match_names = 1;
477 -
478 - if(likely(!match_ids && !match_names))
479 - match_ids = match_names = 1;
480 -
481 - SIMPLE_PATTERN *pattern = simple_pattern_create(dims, ",|\t\r\n\f\v", SIMPLE_PATTERN_EXACT);
482 -
483 - RRDDIM *d;
484 - long c, dims_selected = 0, dims_not_hidden_not_zero = 0;
485 - for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
486 - if( (match_ids && simple_pattern_matches(pattern, d->id))
487 - || (match_names && simple_pattern_matches(pattern, d->name))
488 - ) {
489 - r->od[c] |= RRDR_SELECTED;
490 - if(unlikely(r->od[c] & RRDR_HIDDEN)) r->od[c] &= ~RRDR_HIDDEN;
491 - dims_selected++;
492 -
493 - // since the user needs this dimension
494 - // make it appear as NONZERO, to return it
495 - // even if the dimension has only zeros
496 - // unless option non_zero is set
497 - if(unlikely(!(options & RRDR_OPTION_NONZERO)))
498 - r->od[c] |= RRDR_NONZERO;
499 -
500 - // count the visible dimensions
501 - if(likely(r->od[c] & RRDR_NONZERO))
502 - dims_not_hidden_not_zero++;
503 - }
504 - else {
505 - r->od[c] |= RRDR_HIDDEN;
506 - if(unlikely(r->od[c] & RRDR_SELECTED)) r->od[c] &= ~RRDR_SELECTED;
507 - }
508 - }
509 - simple_pattern_free(pattern);
510 -
511 - // check if all dimensions are hidden
512 - if(unlikely(!dims_not_hidden_not_zero && dims_selected)) {
513 - // there are a few selected dimensions
514 - // but they are all zero
515 - // enable the selected ones
516 - // to avoid returning an empty chart
517 - for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
518 - if(unlikely(r->od[c] & RRDR_SELECTED))
519 - r->od[c] |= RRDR_NONZERO;
520 - }
521 -}
522 -
523 -void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
524 -{
212 +static void rrdr_buffer_print_format(BUFFER *wb, uint32_t format) {
213 switch(format) {
526 - case DATASOURCE_JSON:
527 - buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
528 - break;
529 -
530 - case DATASOURCE_DATATABLE_JSON:
531 - buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
532 - break;
214 + case DATASOURCE_JSON:
215 + buffer_strcat(wb, DATASOURCE_FORMAT_JSON);
216 + break;
217
534 - case DATASOURCE_DATATABLE_JSONP:
535 - buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
536 - break;
218 + case DATASOURCE_DATATABLE_JSON:
219 + buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSON);
220 + break;
221
538 - case DATASOURCE_JSONP:
539 - buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
540 - break;
541 -
542 - case DATASOURCE_SSV:
543 - buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
544 - break;
545 -
546 - case DATASOURCE_CSV:
547 - buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
548 - break;
222 + case DATASOURCE_DATATABLE_JSONP:
223 + buffer_strcat(wb, DATASOURCE_FORMAT_DATATABLE_JSONP);
224 + break;
225
550 - case DATASOURCE_TSV:
551 - buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
552 - break;
226 + case DATASOURCE_JSONP:
227 + buffer_strcat(wb, DATASOURCE_FORMAT_JSONP);
228 + break;
229
554 - case DATASOURCE_HTML:
555 - buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
556 - break;
230 + case DATASOURCE_SSV:
231 + buffer_strcat(wb, DATASOURCE_FORMAT_SSV);
232 + break;
233
558 - case DATASOURCE_JS_ARRAY:
559 - buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
560 - break;
234 + case DATASOURCE_CSV:
235 + buffer_strcat(wb, DATASOURCE_FORMAT_CSV);
236 + break;
237
562 - case DATASOURCE_SSV_COMMA:
563 - buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
564 - break;
238 + case DATASOURCE_TSV:
239 + buffer_strcat(wb, DATASOURCE_FORMAT_TSV);
240 + break;
241
566 - default:
567 - buffer_strcat(wb, "unknown");
568 - break;
569 - }
570 -}
242 + case DATASOURCE_HTML:
243 + buffer_strcat(wb, DATASOURCE_FORMAT_HTML);
244 + break;
245
572 -uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
573 -{
574 - rrdset_check_rdlock(r->st);
246 + case DATASOURCE_JS_ARRAY:
247 + buffer_strcat(wb, DATASOURCE_FORMAT_JS_ARRAY);
248 + break;
249
576 - (void)dims;
577 -
578 - if(options & RRDR_OPTION_NONZERO) {
579 - long i;
580 -
581 - // commented due to #1514
582 -
583 - //if(dims && *dims) {
584 - // the caller wants specific dimensions
585 - // disable NONZERO option
586 - // to make sure we don't accidentally prevent
587 - // the specific dimensions from being returned
588 - // i = 0;
589 - //}
590 - //else {
591 - // find how many dimensions are not zero
592 - long c;
593 - RRDDIM *rd;
594 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ; c++, rd = rd->next) {
595 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
596 - if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
597 - i++;
598 - }
599 - //}
250 + case DATASOURCE_SSV_COMMA:
251 + buffer_strcat(wb, DATASOURCE_FORMAT_SSV_COMMA);
252 + break;
253
601 - // if with nonzero we get i = 0 (no dimensions will be returned)
602 - // disable nonzero to show all dimensions
603 - if(!i) options &= ~RRDR_OPTION_NONZERO;
254 + default:
255 + buffer_strcat(wb, "unknown");
256 + break;
257 }
605 -
606 - return options;
258 }
259
609 -void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
610 -{
260 +static void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value) {
261 rrdset_check_rdlock(r->st);
262
263 long rows = rrdr_rows(r);
@@ -616,7 +266,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
266
267 //info("JSONWRAPPER(): %s: BEGIN", r->st->id);
268 char kq[2] = "", // key quote
619 - sq[2] = ""; // string quote
269 + sq[2] = ""; // string quote
270
271 if( options & RRDR_OPTION_GOOGLE_JSON ) {
272 kq[0] = '\0';
@@ -628,30 +278,30 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
278 }
279
280 buffer_sprintf(wb, "{\n"
631 - " %sapi%s: 1,\n"
632 - " %sid%s: %s%s%s,\n"
633 - " %sname%s: %s%s%s,\n"
634 - " %sview_update_every%s: %d,\n"
635 - " %supdate_every%s: %d,\n"
636 - " %sfirst_entry%s: %u,\n"
637 - " %slast_entry%s: %u,\n"
638 - " %sbefore%s: %u,\n"
639 - " %safter%s: %u,\n"
640 - " %sdimension_names%s: ["
641 - , kq, kq
642 - , kq, kq, sq, r->st->id, sq
643 - , kq, kq, sq, r->st->name, sq
644 - , kq, kq, r->update_every
645 - , kq, kq, r->st->update_every
646 - , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
647 - , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
648 - , kq, kq, (uint32_t)r->before
649 - , kq, kq, (uint32_t)r->after
650 - , kq, kq);
281 + " %sapi%s: 1,\n"
282 + " %sid%s: %s%s%s,\n"
283 + " %sname%s: %s%s%s,\n"
284 + " %sview_update_every%s: %d,\n"
285 + " %supdate_every%s: %d,\n"
286 + " %sfirst_entry%s: %u,\n"
287 + " %slast_entry%s: %u,\n"
288 + " %sbefore%s: %u,\n"
289 + " %safter%s: %u,\n"
290 + " %sdimension_names%s: ["
291 + , kq, kq
292 + , kq, kq, sq, r->st->id, sq
293 + , kq, kq, sq, r->st->name, sq
294 + , kq, kq, r->update_every
295 + , kq, kq, r->st->update_every
296 + , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
297 + , kq, kq, (uint32_t)rrdset_last_entry_t(r->st)
298 + , kq, kq, (uint32_t)r->before
299 + , kq, kq, (uint32_t)r->after
300 + , kq, kq);
301
302 for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
653 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
654 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
303 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
304 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
305
306 if(i) buffer_strcat(wb, ", ");
307 buffer_strcat(wb, sq);
@@ -670,12 +320,12 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
320 }
321
322 buffer_sprintf(wb, "],\n"
673 - " %sdimension_ids%s: ["
674 - , kq, kq);
323 + " %sdimension_ids%s: ["
324 + , kq, kq);
325
326 for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
677 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
678 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
327 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
328 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
329
330 if(i) buffer_strcat(wb, ", ");
331 buffer_strcat(wb, sq);
@@ -691,12 +341,12 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
341 }
342
343 buffer_sprintf(wb, "],\n"
694 - " %slatest_values%s: ["
695 - , kq, kq);
344 + " %slatest_values%s: ["
345 + , kq, kq);
346
347 for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
698 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
699 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
348 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
349 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
350
351 if(i) buffer_strcat(wb, ", ");
352 i++;
@@ -714,8 +364,8 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
364 }
365
366 buffer_sprintf(wb, "],\n"
717 - " %sview_latest_values%s: ["
718 - , kq, kq);
367 + " %sview_latest_values%s: ["
368 + , kq, kq);
369
370 i = 0;
371 if(rows) {
@@ -724,7 +374,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
374 if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
375 total = 0;
376 for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
727 - calculated_number *cn = &r->v[ (0) * r->d ];
377 + calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
378 calculated_number n = cn[c];
379
380 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
@@ -737,17 +387,17 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
387 }
388
389 for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
740 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
741 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
390 + if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
391 + if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
392
393 if(i) buffer_strcat(wb, ", ");
394 i++;
395
746 - calculated_number *cn = &r->v[ (0) * r->d ];
747 - uint8_t *co = &r->o[ (0) * r->d ];
396 + calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
397 + RRDR_VALUE_FLAGS *co = &r->o[ (rrdr_rows(r) - 1) * r->d ];
398 calculated_number n = cn[c];
399
750 - if(co[c] & RRDR_EMPTY) {
400 + if(co[c] & RRDR_VALUE_EMPTY) {
401 if(options & RRDR_OPTION_NULL2ZERO)
402 buffer_strcat(wb, "0");
403 else
@@ -770,32 +420,31 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, uint32_t opti
420 }
421
422 buffer_sprintf(wb, "],\n"
773 - " %sdimensions%s: %ld,\n"
774 - " %spoints%s: %ld,\n"
775 - " %sformat%s: %s"
776 - , kq, kq, i
777 - , kq, kq, rows
778 - , kq, kq, sq
779 - );
423 + " %sdimensions%s: %ld,\n"
424 + " %spoints%s: %ld,\n"
425 + " %sformat%s: %s"
426 + , kq, kq, i
427 + , kq, kq, rows
428 + , kq, kq, sq
429 + );
430
431 rrdr_buffer_print_format(wb, format);
432
433 buffer_sprintf(wb, "%s,\n"
784 - " %sresult%s: "
785 - , sq
786 - , kq, kq
787 - );
434 + " %sresult%s: "
435 + , sq
436 + , kq, kq
437 + );
438
439 if(string_value) buffer_strcat(wb, sq);
440 //info("JSONWRAPPER(): %s: END", r->st->id);
441 }
442
793 -void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value)
794 -{
443 +static void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value) {
444 (void)format;
445
446 char kq[2] = "", // key quote
798 - sq[2] = ""; // string quote
447 + sq[2] = ""; // string quote
448
449 if( options & RRDR_OPTION_GOOGLE_JSON ) {
450 kq[0] = '\0';
@@ -815,1011 +464,6 @@ void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t option
464 buffer_strcat(wb, "\n}\n");
465 }
466
818 -#define JSON_DATES_JS 1
819 -#define JSON_DATES_TIMESTAMP 2
820 -
821 -static void rrdr2json(RRDR *r, BUFFER *wb, uint32_t options, int datatable)
822 -{
823 - rrdset_check_rdlock(r->st);
824 -
825 - //info("RRD2JSON(): %s: BEGIN", r->st->id);
826 - int row_annotations = 0, dates, dates_with_new = 0;
827 - char kq[2] = "", // key quote
828 - sq[2] = "", // string quote
829 - pre_label[101] = "", // before each label
830 - post_label[101] = "", // after each label
831 - pre_date[101] = "", // the beginning of line, to the date
832 - post_date[101] = "", // closing the date
833 - pre_value[101] = "", // before each value
834 - post_value[101] = "", // after each value
835 - post_line[101] = "", // at the end of each row
836 - normal_annotation[201] = "", // default row annotation
837 - overflow_annotation[201] = "", // overflow row annotation
838 - data_begin[101] = "", // between labels and values
839 - finish[101] = ""; // at the end of everything
840 -
841 - if(datatable) {
842 - dates = JSON_DATES_JS;
843 - if( options & RRDR_OPTION_GOOGLE_JSON ) {
844 - kq[0] = '\0';
845 - sq[0] = '\'';
846 - }
847 - else {
848 - kq[0] = '"';
849 - sq[0] = '"';
850 - }
851 - row_annotations = 1;
852 - snprintfz(pre_date, 100, " {%sc%s:[{%sv%s:%s", kq, kq, kq, kq, sq);
853 - snprintfz(post_date, 100, "%s}", sq);
854 - snprintfz(pre_label, 100, ",\n {%sid%s:%s%s,%slabel%s:%s", kq, kq, sq, sq, kq, kq, sq);
855 - snprintfz(post_label, 100, "%s,%spattern%s:%s%s,%stype%s:%snumber%s}", sq, kq, kq, sq, sq, kq, kq, sq, sq);
856 - snprintfz(pre_value, 100, ",{%sv%s:", kq, kq);
857 - strcpy(post_value, "}");
858 - strcpy(post_line, "]}");
859 - snprintfz(data_begin, 100, "\n ],\n %srows%s:\n [\n", kq, kq);
860 - strcpy(finish, "\n ]\n}");
861 -
862 - snprintfz(overflow_annotation, 200, ",{%sv%s:%sRESET OR OVERFLOW%s},{%sv%s:%sThe counters have been wrapped.%s}", kq, kq, sq, sq, kq, kq, sq, sq);
863 - snprintfz(normal_annotation, 200, ",{%sv%s:null},{%sv%s:null}", kq, kq, kq, kq);
864 -
865 - buffer_sprintf(wb, "{\n %scols%s:\n [\n", kq, kq);
866 - buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%stime%s,%spattern%s:%s%s,%stype%s:%sdatetime%s},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq);
867 - buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotation%s}},\n", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
868 - buffer_sprintf(wb, " {%sid%s:%s%s,%slabel%s:%s%s,%spattern%s:%s%s,%stype%s:%sstring%s,%sp%s:{%srole%s:%sannotationText%s}}", kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, sq, sq, kq, kq, kq, kq, sq, sq);
869 -
870 - // remove the valueobjects flag
871 - // google wants its own keys
872 - if(options & RRDR_OPTION_OBJECTSROWS)
873 - options &= ~RRDR_OPTION_OBJECTSROWS;
874 - }
875 - else {
876 - kq[0] = '"';
877 - sq[0] = '"';
878 - if(options & RRDR_OPTION_GOOGLE_JSON) {
879 - dates = JSON_DATES_JS;
880 - dates_with_new = 1;
881 - }
882 - else {
883 - dates = JSON_DATES_TIMESTAMP;
884 - dates_with_new = 0;
885 - }
886 - if( options & RRDR_OPTION_OBJECTSROWS )
887 - strcpy(pre_date, " { ");
888 - else
889 - strcpy(pre_date, " [ ");
890 - strcpy(pre_label, ", \"");
891 - strcpy(post_label, "\"");
892 - strcpy(pre_value, ", ");
893 - if( options & RRDR_OPTION_OBJECTSROWS )
894 - strcpy(post_line, "}");
895 - else
896 - strcpy(post_line, "]");
897 - snprintfz(data_begin, 100, "],\n %sdata%s:\n [\n", kq, kq);
898 - strcpy(finish, "\n ]\n}");
899 -
900 - buffer_sprintf(wb, "{\n %slabels%s: [", kq, kq);
901 - buffer_sprintf(wb, "%stime%s", sq, sq);
902 - }
903 -
904 - // -------------------------------------------------------------------------
905 - // print the JSON header
906 -
907 - long c, i;
908 - RRDDIM *rd;
909 -
910 - // print the header lines
911 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
912 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
913 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
914 -
915 - buffer_strcat(wb, pre_label);
916 - buffer_strcat(wb, rd->name);
917 - buffer_strcat(wb, post_label);
918 - i++;
919 - }
920 - if(!i) {
921 - buffer_strcat(wb, pre_label);
922 - buffer_strcat(wb, "no data");
923 - buffer_strcat(wb, post_label);
924 - }
925 -
926 - // print the begin of row data
927 - buffer_strcat(wb, data_begin);
928 -
929 - // if all dimensions are hidden, print a null
930 - if(!i) {
931 - buffer_strcat(wb, finish);
932 - return;
933 - }
934 -
935 - long start = 0, end = rrdr_rows(r), step = 1;
936 - if((options & RRDR_OPTION_REVERSED)) {
937 - start = rrdr_rows(r) - 1;
938 - end = -1;
939 - step = -1;
940 - }
941 -
942 - // for each line in the array
943 - calculated_number total = 1;
944 - for(i = start; i != end ;i += step) {
945 - calculated_number *cn = &r->v[ i * r->d ];
946 - uint8_t *co = &r->o[ i * r->d ];
947 -
948 - time_t now = r->t[i];
949 -
950 - if(dates == JSON_DATES_JS) {
951 - // generate the local date time
952 - struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
953 - if(!tm) { error("localtime_r() failed."); continue; }
954 -
955 - if(likely(i != start)) buffer_strcat(wb, ",\n");
956 - buffer_strcat(wb, pre_date);
957 -
958 - if( options & RRDR_OPTION_OBJECTSROWS )
959 - buffer_sprintf(wb, "%stime%s: ", kq, kq);
960 -
961 - if(dates_with_new)
962 - buffer_strcat(wb, "new ");
963 -
964 - buffer_jsdate(wb, tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
965 -
966 - buffer_strcat(wb, post_date);
967 -
968 - if(row_annotations) {
969 - // google supports one annotation per row
970 - int annotation_found = 0;
971 - for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
972 - if(co[c] & RRDR_RESET) {
973 - buffer_strcat(wb, overflow_annotation);
974 - annotation_found = 1;
975 - break;
976 - }
977 - }
978 - if(!annotation_found)
979 - buffer_strcat(wb, normal_annotation);
980 - }
981 - }
982 - else {
983 - // print the timestamp of the line
984 - if(likely(i != start)) buffer_strcat(wb, ",\n");
985 - buffer_strcat(wb, pre_date);
986 -
987 - if( options & RRDR_OPTION_OBJECTSROWS )
988 - buffer_sprintf(wb, "%stime%s: ", kq, kq);
989 -
990 - buffer_rrd_value(wb, (calculated_number)r->t[i]);
991 - // in ms
992 - if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
993 -
994 - buffer_strcat(wb, post_date);
995 - }
996 -
997 - int set_min_max = 0;
998 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
999 - total = 0;
1000 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
1001 - calculated_number n = cn[c];
1002 -
1003 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1004 - n = -n;
1005 -
1006 - total += n;
1007 - }
1008 - // prevent a division by zero
1009 - if(total == 0) total = 1;
1010 - set_min_max = 1;
1011 - }
1012 -
1013 - // for each dimension
1014 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
1015 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1016 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1017 -
1018 - calculated_number n = cn[c];
1019 -
1020 - buffer_strcat(wb, pre_value);
1021 -
1022 - if( options & RRDR_OPTION_OBJECTSROWS )
1023 - buffer_sprintf(wb, "%s%s%s: ", kq, rd->name, kq);
1024 -
1025 - if(co[c] & RRDR_EMPTY) {
1026 - if(options & RRDR_OPTION_NULL2ZERO)
1027 - buffer_strcat(wb, "0");
1028 - else
1029 - buffer_strcat(wb, "null");
1030 - }
1031 - else {
1032 - if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1033 - n = -n;
1034 -
1035 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1036 - n = n * 100 / total;
1037 -
1038 - if(unlikely(set_min_max)) {
1039 - r->min = r->max = n;
1040 - set_min_max = 0;
1041 - }
1042 -
1043 - if(n < r->min) r->min = n;
1044 - if(n > r->max) r->max = n;
1045 - }
1046 -
1047 - buffer_rrd_value(wb, n);
1048 - }
1049 -
1050 - buffer_strcat(wb, post_value);
1051 - }
1052 -
1053 - buffer_strcat(wb, post_line);
1054 - }
1055 -
1056 - buffer_strcat(wb, finish);
1057 - //info("RRD2JSON(): %s: END", r->st->id);
1058 -}
1059 -
1060 -static void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t options, const char *startline, const char *separator, const char *endline, const char *betweenlines)
1061 -{
1062 - rrdset_check_rdlock(r->st);
1063 -
1064 - //info("RRD2CSV(): %s: BEGIN", r->st->id);
1065 - long c, i;
1066 - RRDDIM *d;
1067 -
1068 - // print the csv header
1069 - for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1070 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1071 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1072 -
1073 - if(!i) {
1074 - buffer_strcat(wb, startline);
1075 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
1076 - buffer_strcat(wb, "time");
1077 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
1078 - }
1079 - buffer_strcat(wb, separator);
1080 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
1081 - buffer_strcat(wb, d->name);
1082 - if(options & RRDR_OPTION_LABEL_QUOTES) buffer_strcat(wb, "\"");
1083 - i++;
1084 - }
1085 - buffer_strcat(wb, endline);
1086 -
1087 - if(!i) {
1088 - // no dimensions present
1089 - return;
1090 - }
1091 -
1092 - long start = 0, end = rrdr_rows(r), step = 1;
1093 - if((options & RRDR_OPTION_REVERSED)) {
1094 - start = rrdr_rows(r) - 1;
1095 - end = -1;
1096 - step = -1;
1097 - }
1098 -
1099 - // for each line in the array
1100 - calculated_number total = 1;
1101 - for(i = start; i != end ;i += step) {
1102 - calculated_number *cn = &r->v[ i * r->d ];
1103 - uint8_t *co = &r->o[ i * r->d ];
1104 -
1105 - buffer_strcat(wb, betweenlines);
1106 - buffer_strcat(wb, startline);
1107 -
1108 - time_t now = r->t[i];
1109 -
1110 - if((options & RRDR_OPTION_SECONDS) || (options & RRDR_OPTION_MILLISECONDS)) {
1111 - // print the timestamp of the line
1112 - buffer_rrd_value(wb, (calculated_number)now);
1113 - // in ms
1114 - if(options & RRDR_OPTION_MILLISECONDS) buffer_strcat(wb, "000");
1115 - }
1116 - else {
1117 - // generate the local date time
1118 - struct tm tmbuf, *tm = localtime_r(&now, &tmbuf);
1119 - if(!tm) { error("localtime() failed."); continue; }
1120 - buffer_date(wb, tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
1121 - }
1122 -
1123 - int set_min_max = 0;
1124 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1125 - total = 0;
1126 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1127 - calculated_number n = cn[c];
1128 -
1129 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1130 - n = -n;
1131 -
1132 - total += n;
1133 - }
1134 - // prevent a division by zero
1135 - if(total == 0) total = 1;
1136 - set_min_max = 1;
1137 - }
1138 -
1139 - // for each dimension
1140 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1141 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1142 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1143 -
1144 - buffer_strcat(wb, separator);
1145 -
1146 - calculated_number n = cn[c];
1147 -
1148 - if(co[c] & RRDR_EMPTY) {
1149 - if(options & RRDR_OPTION_NULL2ZERO)
1150 - buffer_strcat(wb, "0");
1151 - else
1152 - buffer_strcat(wb, "null");
1153 - }
1154 - else {
1155 - if(unlikely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1156 - n = -n;
1157 -
1158 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1159 - n = n * 100 / total;
1160 -
1161 - if(unlikely(set_min_max)) {
1162 - r->min = r->max = n;
1163 - set_min_max = 0;
1164 - }
1165 -
1166 - if(n < r->min) r->min = n;
1167 - if(n > r->max) r->max = n;
1168 - }
1169 -
1170 - buffer_rrd_value(wb, n);
1171 - }
1172 - }
1173 -
1174 - buffer_strcat(wb, endline);
1175 - }
1176 - //info("RRD2CSV(): %s: END", r->st->id);
1177 -}
1178 -
1179 -inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, int *all_values_are_null) {
1180 - rrdset_check_rdlock(r->st);
1181 -
1182 - long c;
1183 - RRDDIM *d;
1184 -
1185 - calculated_number *cn = &r->v[ i * r->d ];
1186 - uint8_t *co = &r->o[ i * r->d ];
1187 -
1188 - calculated_number sum = 0, min = 0, max = 0, v;
1189 - int all_null = 1, init = 1;
1190 -
1191 - calculated_number total = 1;
1192 - int set_min_max = 0;
1193 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1194 - total = 0;
1195 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1196 - calculated_number n = cn[c];
1197 -
1198 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1199 - n = -n;
1200 -
1201 - total += n;
1202 - }
1203 - // prevent a division by zero
1204 - if(total == 0) total = 1;
1205 - set_min_max = 1;
1206 - }
1207 -
1208 - // for each dimension
1209 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
1210 - if(unlikely(r->od[c] & RRDR_HIDDEN)) continue;
1211 - if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_NONZERO))) continue;
1212 -
1213 - calculated_number n = cn[c];
1214 -
1215 - if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
1216 - n = -n;
1217 -
1218 - if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
1219 - n = n * 100 / total;
1220 -
1221 - if(unlikely(set_min_max)) {
1222 - r->min = r->max = n;
1223 - set_min_max = 0;
1224 - }
1225 -
1226 - if(n < r->min) r->min = n;
1227 - if(n > r->max) r->max = n;
1228 - }
1229 -
1230 - if(unlikely(init)) {
1231 - if(n > 0) {
1232 - min = 0;
1233 - max = n;
1234 - }
1235 - else {
1236 - min = n;
1237 - max = 0;
1238 - }
1239 - init = 0;
1240 - }
1241 -
1242 - if(likely(!(co[c] & RRDR_EMPTY))) {
1243 - all_null = 0;
1244 - sum += n;
1245 - }
1246 -
1247 - if(n < min) min = n;
1248 - if(n > max) max = n;
1249 - }
1250 -
1251 - if(unlikely(all_null)) {
1252 - if(likely(all_values_are_null))
1253 - *all_values_are_null = 1;
1254 - return 0;
1255 - }
1256 - else {
1257 - if(likely(all_values_are_null))
1258 - *all_values_are_null = 0;
1259 - }
1260 -
1261 - if(options & RRDR_OPTION_MIN2MAX)
1262 - v = max - min;
1263 - else
1264 - v = sum;
1265 -
1266 - return v;
1267 -}
1268 -
1269 -static void rrdr2ssv(RRDR *r, BUFFER *wb, uint32_t options, const char *prefix, const char *separator, const char *suffix)
1270 -{
1271 - //info("RRD2SSV(): %s: BEGIN", r->st->id);
1272 - long i;
1273 -
1274 - buffer_strcat(wb, prefix);
1275 - long start = 0, end = rrdr_rows(r), step = 1;
1276 - if((options & RRDR_OPTION_REVERSED)) {
1277 - start = rrdr_rows(r) - 1;
1278 - end = -1;
1279 - step = -1;
1280 - }
1281 -
1282 - // for each line in the array
1283 - for(i = start; i != end ;i += step) {
1284 - int all_values_are_null = 0;
1285 - calculated_number v = rrdr2value(r, i, options, &all_values_are_null);
1286 -
1287 - if(likely(i != start)) {
1288 - if(r->min > v) r->min = v;
1289 - if(r->max < v) r->max = v;
1290 - }
1291 - else {
1292 - r->min = v;
1293 - r->max = v;
1294 - }
1295 -
1296 - if(likely(i != start))
1297 - buffer_strcat(wb, separator);
1298 -
1299 - if(all_values_are_null) {
1300 - if(options & RRDR_OPTION_NULL2ZERO)
1301 - buffer_strcat(wb, "0");
1302 - else
1303 - buffer_strcat(wb, "null");
1304 - }
1305 - else
1306 - buffer_rrd_value(wb, v);
1307 - }
1308 - buffer_strcat(wb, suffix);
1309 - //info("RRD2SSV(): %s: END", r->st->id);
1310 -}
1311 -
1312 -inline static calculated_number *rrdr_line_values(RRDR *r)
1313 -{
1314 - return &r->v[ r->c * r->d ];
1315 -}
1316 -
1317 -inline static uint8_t *rrdr_line_options(RRDR *r)
1318 -{
1319 - return &r->o[ r->c * r->d ];
1320 -}
1321 -
1322 -inline static int rrdr_line_init(RRDR *r, time_t t)
1323 -{
1324 - r->c++;
1325 -
1326 - if(unlikely(r->c >= r->n)) {
1327 - error("requested to step above RRDR size for chart %s", r->st->name);
1328 - r->c = r->n - 1;
1329 - }
1330 -
1331 - // save the time
1332 - r->t[r->c] = t;
1333 -
1334 - return 1;
1335 -}
1336 -
1337 -inline static void rrdr_lock_rrdset(RRDR *r) {
1338 - if(unlikely(!r)) {
1339 - error("NULL value given!");
1340 - return;
1341 - }
1342 -
1343 - rrdset_rdlock(r->st);
1344 - r->has_st_lock = 1;
1345 -}
1346 -
1347 -inline static void rrdr_unlock_rrdset(RRDR *r) {
1348 - if(unlikely(!r)) {
1349 - error("NULL value given!");
1350 - return;
1351 - }
1352 -
1353 - if(likely(r->has_st_lock)) {
1354 - rrdset_unlock(r->st);
1355 - r->has_st_lock = 0;
1356 - }
1357 -}
1358 -
1359 -inline static void rrdr_free(RRDR *r)
1360 -{
1361 - if(unlikely(!r)) {
1362 - error("NULL value given!");
1363 - return;
1364 - }
1365 -
1366 - rrdr_unlock_rrdset(r);
1367 - freez(r->t);
1368 - freez(r->v);
1369 - freez(r->o);
1370 - freez(r->od);
1371 - freez(r);
1372 -}
1373 -
1374 -static inline void rrdr_done(RRDR *r)
1375 -{
1376 - r->rows = r->c + 1;
1377 - r->c = 0;
1378 -}
1379 -
1380 -static RRDR *rrdr_create(RRDSET *st, long n)
1381 -{
1382 - if(unlikely(!st)) {
1383 - error("NULL value given!");
1384 - return NULL;
1385 - }
1386 -
1387 - RRDR *r = callocz(1, sizeof(RRDR));
1388 - r->st = st;
1389 -
1390 - rrdr_lock_rrdset(r);
1391 -
1392 - RRDDIM *rd;
1393 - rrddim_foreach_read(rd, st) r->d++;
1394 -
1395 - r->n = n;
1396 -
1397 - r->t = mallocz(n * sizeof(time_t));
1398 - r->v = mallocz(n * r->d * sizeof(calculated_number));
1399 - r->o = mallocz(n * r->d * sizeof(uint8_t));
1400 - r->od = mallocz(r->d * sizeof(uint8_t));
1401 -
1402 - // set the hidden flag on hidden dimensions
1403 - int c;
1404 - for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
1405 - if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)))
1406 - r->od[c] = RRDR_HIDDEN;
1407 - else
1408 - r->od[c] = 0;
1409 - }
1410 -
1411 - r->c = -1;
1412 - r->group = 1;
1413 - r->update_every = 1;
1414 -
1415 - return r;
1416 -}
1417 -
1418 -RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int group_method, long group_time, int aligned)
1419 -{
1420 -#ifdef NETDATA_INTERNAL_CHECKS
1421 - int debug = rrdset_flag_check(st, RRDSET_FLAG_DEBUG)?1:0;
1422 -#endif
1423 - int absolute_period_requested = -1;
1424 -
1425 - time_t first_entry_t = rrdset_first_entry_t(st);
1426 - time_t last_entry_t = rrdset_last_entry_t(st);
1427 -
1428 - if(before == 0 && after == 0) {
1429 - // dump the all the data
1430 - before = last_entry_t;
1431 - after = first_entry_t;
1432 - absolute_period_requested = 0;
1433 - }
1434 -
1435 - // allow relative for before (smaller than API_RELATIVE_TIME_MAX)
1436 - if(((before < 0)?-before:before) <= API_RELATIVE_TIME_MAX) {
1437 - if(abs(before) % st->update_every) {
1438 - // make sure it is multiple of st->update_every
1439 - if(before < 0) before = before - st->update_every - before % st->update_every;
1440 - else before = before + st->update_every - before % st->update_every;
1441 - }
1442 - if(before > 0) before = first_entry_t + before;
1443 - else before = last_entry_t + before;
1444 - absolute_period_requested = 0;
1445 - }
1446 -
1447 - // allow relative for after (smaller than API_RELATIVE_TIME_MAX)
1448 - if(((after < 0)?-after:after) <= API_RELATIVE_TIME_MAX) {
1449 - if(after == 0) after = -st->update_every;
1450 - if(abs(after) % st->update_every) {
1451 - // make sure it is multiple of st->update_every
1452 - if(after < 0) after = after - st->update_every - after % st->update_every;
1453 - else after = after + st->update_every - after % st->update_every;
1454 - }
1455 - after = before + after;
1456 - absolute_period_requested = 0;
1457 - }
1458 -
1459 - if(absolute_period_requested == -1)
1460 - absolute_period_requested = 1;
1461 -
1462 - // make sure they are within our timeframe
1463 - if(before > last_entry_t) before = last_entry_t;
1464 - if(before < first_entry_t) before = first_entry_t;
1465 -
1466 - if(after > last_entry_t) after = last_entry_t;
1467 - if(after < first_entry_t) after = first_entry_t;
1468 -
1469 - // check if they are upside down
1470 - if(after > before) {
1471 - time_t tmp = before;
1472 - before = after;
1473 - after = tmp;
1474 - }
1475 -
1476 - // the duration of the chart
1477 - time_t duration = before - after;
1478 - long available_points = duration / st->update_every;
1479 -
1480 - if(duration <= 0 || available_points <= 0)
1481 - return rrdr_create(st, 1);
1482 -
1483 - // check the number of wanted points in the result
1484 - if(unlikely(points < 0)) points = -points;
1485 - if(unlikely(points > available_points)) points = available_points;
1486 - if(unlikely(points == 0)) points = available_points;
1487 -
1488 - // calculate the desired grouping of source data points
1489 - long group = available_points / points;
1490 - if(unlikely(group <= 0)) group = 1;
1491 - if(unlikely(available_points % points > points / 2)) group++; // rounding to the closest integer
1492 -
1493 - // group_time enforces a certain grouping multiple
1494 - calculated_number group_sum_divisor = 1.0;
1495 - long group_points = 1;
1496 - if(unlikely(group_time > st->update_every)) {
1497 - if (unlikely(group_time > duration)) {
1498 - // group_time is above the available duration
1499 -
1500 - #ifdef NETDATA_INTERNAL_CHECKS
1501 - info("INTERNAL CHECK: %s: requested gtime %ld secs, is greater than the desired duration %ld secs", st->id, group_time, duration);
1502 - #endif
1503 -
1504 - group = points; // use all the points
1505 - }
1506 - else {
1507 - // the points we should group to satisfy gtime
1508 - group_points = group_time / st->update_every;
1509 - if(unlikely(group_time % group_points)) {
1510 - #ifdef NETDATA_INTERNAL_CHECKS
1511 - info("INTERNAL CHECK: %s: requested gtime %ld secs, is not a multiple of the chart's data collection frequency %d secs", st->id, group_time, st->update_every);
1512 - #endif
1513 -
1514 - group_points++;
1515 - }
1516 -
1517 - // adapt group according to group_points
1518 - if(unlikely(group < group_points)) group = group_points; // do not allow grouping below the desired one
1519 - if(unlikely(group % group_points)) group += group_points - (group % group_points); // make sure group is multiple of group_points
1520 -
1521 - //group_sum_divisor = group / group_points;
1522 - group_sum_divisor = (calculated_number)(group * st->update_every) / (calculated_number)group_time;
1523 - }
1524 - }
1525 -
1526 - time_t after_new = after - (after % ( ((aligned)?group:1) * st->update_every ));
1527 - time_t before_new = before - (before % ( ((aligned)?group:1) * st->update_every ));
1528 - long points_new = (before_new - after_new) / st->update_every / group;
1529 -
1530 - // find the starting and ending slots in our round robin db
1531 - long start_at_slot = rrdset_time2slot(st, before_new),
1532 - stop_at_slot = rrdset_time2slot(st, after_new);
1533 -
1534 -#ifdef NETDATA_INTERNAL_CHECKS
1535 - if(after_new < first_entry_t)
1536 - error("INTERNAL CHECK: after_new %u is too small, minimum %u", (uint32_t)after_new, (uint32_t)first_entry_t);
1537 -
1538 - if(after_new > last_entry_t)
1539 - error("INTERNAL CHECK: after_new %u is too big, maximum %u", (uint32_t)after_new, (uint32_t)last_entry_t);
1540 -
1541 - if(before_new < first_entry_t)
1542 - error("INTERNAL CHECK: before_new %u is too small, minimum %u", (uint32_t)before_new, (uint32_t)first_entry_t);
1543 -
1544 - if(before_new > last_entry_t)
1545 - error("INTERNAL CHECK: before_new %u is too big, maximum %u", (uint32_t)before_new, (uint32_t)last_entry_t);
1546 -
1547 - if(start_at_slot < 0 || start_at_slot >= st->entries)
1548 - error("INTERNAL CHECK: start_at_slot is invalid %ld, expected 0 to %ld", start_at_slot, st->entries - 1);
1549 -
1550 - if(stop_at_slot < 0 || stop_at_slot >= st->entries)
1551 - error("INTERNAL CHECK: stop_at_slot is invalid %ld, expected 0 to %ld", stop_at_slot, st->entries - 1);
1552 -
1553 - if(points_new > (before_new - after_new) / group / st->update_every + 1)
1554 - error("INTERNAL CHECK: points_new %ld is more than points %ld", points_new, (before_new - after_new) / group / st->update_every + 1);
1555 -
1556 - if(group < group_points)
1557 - error("INTERNAL CHECK: group %ld is less than the desired group points %ld", group, group_points);
1558 -
1559 - if(group > group_points && group % group_points)
1560 - error("INTERNAL CHECK: group %ld is not a multiple of the desired group points %ld", group, group_points);
1561 -#endif
1562 -
1563 - //info("RRD2RRDR(): %s: wanted %ld points, got %ld - group=%ld, wanted duration=%u, got %u - wanted %ld - %ld, got %ld - %ld", st->id, points, points_new, group, before - after, before_new - after_new, after, before, after_new, before_new);
1564 -
1565 - after = after_new;
1566 - before = before_new;
1567 - duration = before - after;
1568 - points = points_new;
1569 -
1570 - // Now we have:
1571 - // before = the end time of the calculation
1572 - // after = the start time of the calculation
1573 - // duration = the duration of the calculation
1574 - // group = the number of source points to aggregate / group together
1575 - // method = the method of grouping source points
1576 - // points = the number of points to generate
1577 -
1578 -
1579 - // -------------------------------------------------------------------------
1580 - // initialize our result set
1581 -
1582 - RRDR *r = rrdr_create(st, points);
1583 - if(unlikely(!r)) {
1584 -#ifdef NETDATA_INTERNAL_CHECKS
1585 - error("INTERNAL CHECK: Cannot create RRDR for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
1586 -#endif
1587 - return NULL;
1588 - }
1589 -
1590 - if(unlikely(!r->d)) {
1591 -#ifdef NETDATA_INTERNAL_CHECKS
1592 - error("INTERNAL CHECK: Returning empty RRDR (no dimensions in RRDSET) for %s, after=%u, before=%u, duration=%u, points=%ld", st->id, (uint32_t)after, (uint32_t)before, (uint32_t)duration, points);
1593 -#endif
1594 - return r;
1595 - }
1596 -
1597 - if(unlikely(absolute_period_requested == 1))
1598 - r->result_options |= RRDR_RESULT_OPTION_ABSOLUTE;
1599 - else
1600 - r->result_options |= RRDR_RESULT_OPTION_RELATIVE;
1601 -
1602 - // find how many dimensions we have
1603 - long dimensions = r->d;
1604 -
1605 -
1606 - // -------------------------------------------------------------------------
1607 - // checks for debugging
1608 -#ifdef NETDATA_INTERNAL_CHECKS
1609 - if(debug) debug(D_RRD_STATS, "INFO %s first_t: %u, last_t: %u, all_duration: %u, after: %u, before: %u, duration: %u, points: %ld, group: %ld, group_points: %ld"
1610 - , st->id
1611 - , (uint32_t)first_entry_t
1612 - , (uint32_t)last_entry_t
1613 - , (uint32_t)(last_entry_t - first_entry_t)
1614 - , (uint32_t)after
1615 - , (uint32_t)before
1616 - , (uint32_t)duration
1617 - , points
1618 - , group
1619 - , group_points
1620 - );
1621 -#endif
1622 -
1623 - // -------------------------------------------------------------------------
1624 - // temp arrays for keeping values per dimension
1625 -
1626 - calculated_number last_values[dimensions]; // keep the last value of each dimension
1627 - calculated_number group_values[dimensions]; // keep sums when grouping
1628 - long group_counts[dimensions]; // keep the number of values added to group_values
1629 - uint8_t group_options[dimensions];
1630 - uint8_t found_non_zero[dimensions];
1631 -
1632 -
1633 - // initialize them
1634 - RRDDIM *rd;
1635 - long c;
1636 - rrdset_check_rdlock(st);
1637 - for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1638 - last_values[c] = 0;
1639 - group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
1640 - group_counts[c] = 0;
1641 - group_options[c] = 0;
1642 - found_non_zero[c] = 0;
1643 - }
1644 -
1645 -
1646 - // -------------------------------------------------------------------------
1647 - // the main loop
1648 -
1649 - time_t now = rrdset_slot2time(st, start_at_slot),
1650 - dt = st->update_every,
1651 - group_start_t = 0;
1652 -
1653 -#ifdef NETDATA_INTERNAL_CHECKS
1654 - if(unlikely(debug)) debug(D_RRD_STATS, "BEGIN %s after_t: %u (stop_at_t: %ld), before_t: %u (start_at_t: %ld), start_t(now): %u, current_entry: %ld, entries: %ld"
1655 - , st->id
1656 - , (uint32_t)after
1657 - , stop_at_slot
1658 - , (uint32_t)before
1659 - , start_at_slot
1660 - , (uint32_t)now
1661 - , st->current_entry
1662 - , st->entries
1663 - );
1664 -#endif
1665 -
1666 - r->group = group;
1667 - r->update_every = (int)group * st->update_every;
1668 - r->before = now;
1669 - r->after = now;
1670 -
1671 - //info("RRD2RRDR(): %s: STARTING", st->id);
1672 -
1673 - long slot = start_at_slot, counter = 0, stop_now = 0, added = 0, group_count = 0, add_this = 0;
1674 - for(; !stop_now ; now -= dt, slot--, counter++) {
1675 - if(unlikely(slot < 0)) slot = st->entries - 1;
1676 - if(unlikely(slot == stop_at_slot)) stop_now = counter;
1677 -
1678 -#ifdef NETDATA_INTERNAL_CHECKS
1679 - if(unlikely(debug)) debug(D_RRD_STATS, "ROW %s slot: %ld, entries_counter: %ld, group_count: %ld, added: %ld, now: %ld, %s %s"
1680 - , st->id
1681 - , slot
1682 - , counter
1683 - , group_count + 1
1684 - , added
1685 - , now
1686 - , (group_count + 1 == group)?"PRINT":" - "
1687 - , (now >= after && now <= before)?"RANGE":" - "
1688 - );
1689 -#endif
1690 -
1691 - // make sure we return data in the proper time range
1692 - if(unlikely(now > before)) continue;
1693 - if(unlikely(now < after)) break;
1694 -
1695 - if(unlikely(group_count == 0)) group_start_t = now;
1696 - group_count++;
1697 -
1698 - if(unlikely(group_count == group)) {
1699 - if(unlikely(added >= points)) break;
1700 - add_this = 1;
1701 - }
1702 -
1703 - // do the calculations
1704 - for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1705 - storage_number n = rd->values[slot];
1706 - if(unlikely(!does_storage_number_exist(n))) continue;
1707 -
1708 - group_counts[c]++;
1709 -
1710 - calculated_number value = unpack_storage_number(n);
1711 - if(likely(value != 0.0)) {
1712 - group_options[c] |= RRDR_NONZERO;
1713 - found_non_zero[c] = 1;
1714 - }
1715 -
1716 - if(unlikely(did_storage_number_reset(n)))
1717 - group_options[c] |= RRDR_RESET;
1718 -
1719 - switch(group_method) {
1720 - case GROUP_MIN:
1721 - if(unlikely(isnan(group_values[c])) ||
1722 - calculated_number_fabs(value) < calculated_number_fabs(group_values[c]))
1723 - group_values[c] = value;
1724 - break;
1725 -
1726 - case GROUP_MAX:
1727 - if(unlikely(isnan(group_values[c])) ||
1728 - calculated_number_fabs(value) > calculated_number_fabs(group_values[c]))
1729 - group_values[c] = value;
1730 - break;
1731 -
1732 - default:
1733 - case GROUP_SUM:
1734 - case GROUP_AVERAGE:
1735 - case GROUP_UNDEFINED:
1736 - group_values[c] += value;
1737 - break;
1738 -
1739 - case GROUP_INCREMENTAL_SUM:
1740 - if(unlikely(slot == start_at_slot))
1741 - last_values[c] = value;
1742 -
1743 - group_values[c] += last_values[c] - value;
1744 - last_values[c] = value;
1745 - break;
1746 - }
1747 - }
1748 -
1749 - // added it
1750 - if(unlikely(add_this)) {
1751 - if(unlikely(!rrdr_line_init(r, group_start_t))) break;
1752 -
1753 - r->after = now;
1754 -
1755 - calculated_number *cn = rrdr_line_values(r);
1756 - uint8_t *co = rrdr_line_options(r);
1757 -
1758 - for(rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
1759 -
1760 - // update the dimension options
1761 - if(likely(found_non_zero[c])) r->od[c] |= RRDR_NONZERO;
1762 -
1763 - // store the specific point options
1764 - co[c] = group_options[c];
1765 -
1766 - // store the value
1767 - if(unlikely(group_counts[c] == 0)) {
1768 - cn[c] = 0.0;
1769 - co[c] |= RRDR_EMPTY;
1770 - group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
1771 - }
1772 - else {
1773 - switch(group_method) {
1774 - case GROUP_MIN:
1775 - case GROUP_MAX:
1776 - if(unlikely(isnan(group_values[c])))
1777 - cn[c] = 0;
1778 - else {
1779 - cn[c] = group_values[c];
1780 - group_values[c] = NAN;
1781 - }
1782 - break;
1783 -
1784 - case GROUP_SUM:
1785 - case GROUP_INCREMENTAL_SUM:
1786 - cn[c] = group_values[c];
1787 - group_values[c] = 0;
1788 - break;
1789 -
1790 - default:
1791 - case GROUP_AVERAGE:
1792 - case GROUP_UNDEFINED:
1793 - if(unlikely(group_points != 1))
1794 - cn[c] = group_values[c] / group_sum_divisor;
1795 - else
1796 - cn[c] = group_values[c] / group_counts[c];
1797 -
1798 - group_values[c] = 0;
1799 - break;
1800 - }
1801 -
1802 - if(cn[c] < r->min) r->min = cn[c];
1803 - if(cn[c] > r->max) r->max = cn[c];
1804 - }
1805 -
1806 - // reset for the next loop
1807 - group_counts[c] = 0;
1808 - group_options[c] = 0;
1809 - }
1810 -
1811 - added++;
1812 - group_count = 0;
1813 - add_this = 0;
1814 - }
1815 - }
1816 -
1817 - rrdr_done(r);
1818 - //info("RRD2RRDR(): %s: END %ld loops made, %ld points generated", st->id, counter, rrdr_rows(r));
1819 - //error("SHIFT: %s: wanted %ld points, got %ld", st->id, points, rrdr_rows(r));
1820 - return r;
1821 -}
1822 -
467 int rrdset2value_api_v1(
468 RRDSET *st
469 , BUFFER *wb
@@ -1835,7 +479,7 @@ int rrdset2value_api_v1(
479 , time_t *db_before
480 , int *value_is_null
481 ) {
1838 - RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, !(options & RRDR_OPTION_NOT_ALIGNED));
482 + RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions);
483 if(!r) {
484 if(value_is_null) *value_is_null = 1;
485 return 500;
@@ -1858,15 +502,10 @@ int rrdset2value_api_v1(
502 buffer_cacheable(wb);
503 }
504
1861 - options = rrdr_check_options(r, options, dimensions);
1862 -
1863 - if(dimensions)
1864 - rrdr_disable_not_selected_dimensions(r, options, dimensions);
1865 -
505 if(db_after) *db_after = r->after;
506 if(db_before) *db_before = r->before;
507
1869 - long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
508 + long i = (!(options & RRDR_OPTION_REVERSED))?rrdr_rows(r) - 1:0;
509 *n = rrdr2value(r, i, options, value_is_null);
510
511 rrdr_free(r);
@@ -1888,7 +527,7 @@ int rrdset2anything_api_v1(
527 ) {
528 st->last_accessed_time = now_realtime_sec();
529
1891 - RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, !(options & RRDR_OPTION_NOT_ALIGNED));
530 + RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL);
531 if(!r) {
532 buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
533 return 500;
@@ -1899,11 +538,6 @@ int rrdset2anything_api_v1(
538 else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
539 buffer_cacheable(wb);
540
1902 - options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
1903 -
1904 - if(dimensions)
1905 - rrdr_disable_not_selected_dimensions(r, options, buffer_tostring(dimensions));
1906 -
541 if(latest_timestamp && rrdr_rows(r) > 0)
542 *latest_timestamp = r->before;
543
web/api/rrd2json.h
+2 -37
@@ -4,6 +4,8 @@
4 #define NETDATA_RRD2JSON_H 1
5
6 #include "web_api_v1.h"
7 +#include "exporters/allmetrics.h"
8 +#include "queries/rrdr.h"
9
10 #define HOSTNAME_MAX 1024
11
@@ -35,46 +37,9 @@
37 #define DATASOURCE_FORMAT_SSV_COMMA "ssvcomma"
38 #define DATASOURCE_FORMAT_CSV_JSON_ARRAY "csvjsonarray"
39
38 -#define ALLMETRICS_FORMAT_SHELL "shell"
39 -#define ALLMETRICS_FORMAT_PROMETHEUS "prometheus"
40 -#define ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS "prometheus_all_hosts"
41 -#define ALLMETRICS_FORMAT_JSON "json"
42 -
43 -#define ALLMETRICS_SHELL 1
44 -#define ALLMETRICS_PROMETHEUS 2
45 -#define ALLMETRICS_JSON 3
46 -#define ALLMETRICS_PROMETHEUS_ALL_HOSTS 4
47 -
48 -#define GROUP_UNDEFINED 0
49 -#define GROUP_AVERAGE 1
50 -#define GROUP_MIN 2
51 -#define GROUP_MAX 3
52 -#define GROUP_SUM 4
53 -#define GROUP_INCREMENTAL_SUM 5
54 -
55 -#define RRDR_OPTION_NONZERO 0x00000001 // don't output dimensions will just zero values
56 -#define RRDR_OPTION_REVERSED 0x00000002 // output the rows in reverse order (oldest to newest)
57 -#define RRDR_OPTION_ABSOLUTE 0x00000004 // values positive, for DATASOURCE_SSV before summing
58 -#define RRDR_OPTION_MIN2MAX 0x00000008 // when adding dimensions, use max - min, instead of sum
59 -#define RRDR_OPTION_SECONDS 0x00000010 // output seconds, instead of dates
60 -#define RRDR_OPTION_MILLISECONDS 0x00000020 // output milliseconds, instead of dates
61 -#define RRDR_OPTION_NULL2ZERO 0x00000040 // do not show nulls, convert them to zeros
62 -#define RRDR_OPTION_OBJECTSROWS 0x00000080 // each row of values should be an object, not an array
63 -#define RRDR_OPTION_GOOGLE_JSON 0x00000100 // comply with google JSON/JSONP specs
64 -#define RRDR_OPTION_JSON_WRAP 0x00000200 // wrap the response in a JSON header with info about the result
65 -#define RRDR_OPTION_LABEL_QUOTES 0x00000400 // in CSV output, wrap header labels in double quotes
66 -#define RRDR_OPTION_PERCENTAGE 0x00000800 // give values as percentage of total
67 -#define RRDR_OPTION_NOT_ALIGNED 0x00001000 // do not align charts for persistant timeframes
68 -#define RRDR_OPTION_DISPLAY_ABS 0x00002000 // for badges, display the absolute value, but calculate colors with sign
69 -#define RRDR_OPTION_MATCH_IDS 0x00004000 // when filtering dimensions, match only IDs
70 -#define RRDR_OPTION_MATCH_NAMES 0x00008000 // when filtering dimensions, match only names
71 -
40 extern void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb);
41 extern void rrd_stats_api_v1_charts(RRDHOST *host, BUFFER *wb);
42
75 -extern void rrd_stats_api_v1_charts_allmetrics_json(RRDHOST *host, BUFFER *wb);
76 -extern void rrd_stats_api_v1_charts_allmetrics_shell(RRDHOST *host, BUFFER *wb);
77 -
43 extern int rrdset2anything_api_v1(
44 RRDSET *st
45 , BUFFER *wb
web/api/web_api_v1.c
+4 -393
@@ -5,21 +5,7 @@
5 static struct {
6 const char *name;
7 uint32_t hash;
8 - int value;
9 -} api_v1_data_groups[] = {
10 - { "average" , 0 , GROUP_AVERAGE}
11 - , {"min" , 0 , GROUP_MIN}
12 - , {"max" , 0 , GROUP_MAX}
13 - , {"sum" , 0 , GROUP_SUM}
14 - , {"incremental_sum", 0 , GROUP_INCREMENTAL_SUM}
15 - , {"incremental-sum", 0 , GROUP_INCREMENTAL_SUM}
16 - , { NULL, 0, 0}
17 -};
18 -
19 -static struct {
20 - const char *name;
21 - uint32_t hash;
22 - uint32_t value;
8 + RRDR_OPTIONS value;
9 } api_v1_data_options[] = {
10 { "nonzero" , 0 , RRDR_OPTION_NONZERO}
11 , {"flip" , 0 , RRDR_OPTION_REVERSED}
@@ -86,9 +72,6 @@ static struct {
72 void web_client_api_v1_init(void) {
73 int i;
74
89 - for(i = 0; api_v1_data_groups[i].name ; i++)
90 - api_v1_data_groups[i].hash = simple_hash(api_v1_data_groups[i].name);
91 -
75 for(i = 0; api_v1_data_options[i].name ; i++)
76 api_v1_data_options[i].hash = simple_hash(api_v1_data_options[i].name);
77
@@ -97,17 +80,8 @@ void web_client_api_v1_init(void) {
80
81 for(i = 0; api_v1_data_google_formats[i].name ; i++)
82 api_v1_data_google_formats[i].hash = simple_hash(api_v1_data_google_formats[i].name);
100 -}
101 -
102 -inline int web_client_api_request_v1_data_group(char *name, int def) {
103 - int i;
104 -
105 - uint32_t hash = simple_hash(name);
106 - for(i = 0; api_v1_data_groups[i].name ; i++)
107 - if(unlikely(hash == api_v1_data_groups[i].hash && !strcmp(name, api_v1_data_groups[i].name)))
108 - return api_v1_data_groups[i].value;
83
110 - return def;
84 + web_client_api_v1_init_grouping();
85 }
86
87 inline uint32_t web_client_api_request_v1_data_options(char *o) {
@@ -254,373 +228,10 @@ inline int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w,
228 return 200;
229 }
230
257 -struct prometheus_output_options {
258 - char *name;
259 - PROMETHEUS_OUTPUT_OPTIONS flag;
260 -} prometheus_output_flags_root[] = {
261 - { "help", PROMETHEUS_OUTPUT_HELP },
262 - { "types", PROMETHEUS_OUTPUT_TYPES },
263 - { "names", PROMETHEUS_OUTPUT_NAMES },
264 - { "timestamps", PROMETHEUS_OUTPUT_TIMESTAMPS },
265 - { "variables", PROMETHEUS_OUTPUT_VARIABLES },
266 -
267 - // terminator
268 - { NULL, PROMETHEUS_OUTPUT_NONE },
269 -};
270 -
271 -inline int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url) {
272 - int format = ALLMETRICS_SHELL;
273 - const char *prometheus_server = w->client_ip;
274 - uint32_t prometheus_backend_options = global_backend_options;
275 - PROMETHEUS_OUTPUT_OPTIONS prometheus_output_options = PROMETHEUS_OUTPUT_TIMESTAMPS | ((global_backend_options & BACKEND_OPTION_SEND_NAMES)?PROMETHEUS_OUTPUT_NAMES:0);
276 - const char *prometheus_prefix = global_backend_prefix;
277 -
278 - while(url) {
279 - char *value = mystrsep(&url, "?&");
280 - if (!value || !*value) continue;
281 -
282 - char *name = mystrsep(&value, "=");
283 - if(!name || !*name) continue;
284 - if(!value || !*value) continue;
285 -
286 - if(!strcmp(name, "format")) {
287 - if(!strcmp(value, ALLMETRICS_FORMAT_SHELL))
288 - format = ALLMETRICS_SHELL;
289 - else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS))
290 - format = ALLMETRICS_PROMETHEUS;
291 - else if(!strcmp(value, ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS))
292 - format = ALLMETRICS_PROMETHEUS_ALL_HOSTS;
293 - else if(!strcmp(value, ALLMETRICS_FORMAT_JSON))
294 - format = ALLMETRICS_JSON;
295 - else
296 - format = 0;
297 - }
298 - else if(!strcmp(name, "server")) {
299 - prometheus_server = value;
300 - }
301 - else if(!strcmp(name, "prefix")) {
302 - prometheus_prefix = value;
303 - }
304 - else if(!strcmp(name, "data") || !strcmp(name, "source") || !strcmp(name, "data source") || !strcmp(name, "data-source") || !strcmp(name, "data_source") || !strcmp(name, "datasource")) {
305 - prometheus_backend_options = backend_parse_data_source(value, prometheus_backend_options);
306 - }
307 - else {
308 - int i;
309 - for(i = 0; prometheus_output_flags_root[i].name ; i++) {
310 - if(!strcmp(name, prometheus_output_flags_root[i].name)) {
311 - if(!strcmp(value, "yes") || !strcmp(value, "1") || !strcmp(value, "true"))
312 - prometheus_output_options |= prometheus_output_flags_root[i].flag;
313 - else
314 - prometheus_output_options &= ~prometheus_output_flags_root[i].flag;
315 -
316 - break;
317 - }
318 - }
319 - }
320 - }
321 -
322 - buffer_flush(w->response.data);
323 - buffer_no_cacheable(w->response.data);
324 -
325 - switch(format) {
326 - case ALLMETRICS_JSON:
327 - w->response.data->contenttype = CT_APPLICATION_JSON;
328 - rrd_stats_api_v1_charts_allmetrics_json(host, w->response.data);
329 - return 200;
330 -
331 - case ALLMETRICS_SHELL:
332 - w->response.data->contenttype = CT_TEXT_PLAIN;
333 - rrd_stats_api_v1_charts_allmetrics_shell(host, w->response.data);
334 - return 200;
335 -
336 - case ALLMETRICS_PROMETHEUS:
337 - w->response.data->contenttype = CT_PROMETHEUS;
338 - rrd_stats_api_v1_charts_allmetrics_prometheus_single_host(
339 - host
340 - , w->response.data
341 - , prometheus_server
342 - , prometheus_prefix
343 - , prometheus_backend_options
344 - , prometheus_output_options
345 - );
346 - return 200;
347 -
348 - case ALLMETRICS_PROMETHEUS_ALL_HOSTS:
349 - w->response.data->contenttype = CT_PROMETHEUS;
350 - rrd_stats_api_v1_charts_allmetrics_prometheus_all_hosts(
351 - host
352 - , w->response.data
353 - , prometheus_server
354 - , prometheus_prefix
355 - , prometheus_backend_options
356 - , prometheus_output_options
357 - );
358 - return 200;
359 -
360 - default:
361 - w->response.data->contenttype = CT_TEXT_PLAIN;
362 - buffer_strcat(w->response.data, "Which format? '" ALLMETRICS_FORMAT_SHELL "', '" ALLMETRICS_FORMAT_PROMETHEUS "', '" ALLMETRICS_FORMAT_PROMETHEUS_ALL_HOSTS "' and '" ALLMETRICS_FORMAT_JSON "' are currently supported.");
363 - return 400;
364 - }
365 -}
366 -
231 inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url) {
232 return web_client_api_request_single_chart(host, w, url, rrd_stats_api_v1_chart);
233 }
234
371 -int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
372 - int ret = 400;
373 - buffer_flush(w->response.data);
374 -
375 - BUFFER *dimensions = NULL;
376 -
377 - const char *chart = NULL
378 - , *before_str = NULL
379 - , *after_str = NULL
380 - , *points_str = NULL
381 - , *multiply_str = NULL
382 - , *divide_str = NULL
383 - , *label = NULL
384 - , *units = NULL
385 - , *label_color = NULL
386 - , *value_color = NULL
387 - , *refresh_str = NULL
388 - , *precision_str = NULL
389 - , *scale_str = NULL
390 - , *alarm = NULL;
391 -
392 - int group = GROUP_AVERAGE;
393 - uint32_t options = 0x00000000;
394 -
395 - while(url) {
396 - char *value = mystrsep(&url, "/?&");
397 - if(!value || !*value) continue;
398 -
399 - char *name = mystrsep(&value, "=");
400 - if(!name || !*name) continue;
401 - if(!value || !*value) continue;
402 -
403 - debug(D_WEB_CLIENT, "%llu: API v1 badge.svg query param '%s' with value '%s'", w->id, name, value);
404 -
405 - // name and value are now the parameters
406 - // they are not null and not empty
407 -
408 - if(!strcmp(name, "chart")) chart = value;
409 - else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
410 - if(!dimensions)
411 - dimensions = buffer_create(100);
412 -
413 - buffer_strcat(dimensions, "|");
414 - buffer_strcat(dimensions, value);
415 - }
416 - else if(!strcmp(name, "after")) after_str = value;
417 - else if(!strcmp(name, "before")) before_str = value;
418 - else if(!strcmp(name, "points")) points_str = value;
419 - else if(!strcmp(name, "group")) {
420 - group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
421 - }
422 - else if(!strcmp(name, "options")) {
423 - options |= web_client_api_request_v1_data_options(value);
424 - }
425 - else if(!strcmp(name, "label")) label = value;
426 - else if(!strcmp(name, "units")) units = value;
427 - else if(!strcmp(name, "label_color")) label_color = value;
428 - else if(!strcmp(name, "value_color")) value_color = value;
429 - else if(!strcmp(name, "multiply")) multiply_str = value;
430 - else if(!strcmp(name, "divide")) divide_str = value;
431 - else if(!strcmp(name, "refresh")) refresh_str = value;
432 - else if(!strcmp(name, "precision")) precision_str = value;
433 - else if(!strcmp(name, "scale")) scale_str = value;
434 - else if(!strcmp(name, "alarm")) alarm = value;
435 - }
436 -
437 - if(!chart || !*chart) {
438 - buffer_no_cacheable(w->response.data);
439 - buffer_sprintf(w->response.data, "No chart id is given at the request.");
440 - goto cleanup;
441 - }
442 -
443 - int scale = (scale_str && *scale_str)?str2i(scale_str):100;
444 -
445 - RRDSET *st = rrdset_find(host, chart);
446 - if(!st) st = rrdset_find_byname(host, chart);
447 - if(!st) {
448 - buffer_no_cacheable(w->response.data);
449 - buffer_svg(w->response.data, "chart not found", NAN, "", NULL, NULL, -1, scale, 0);
450 - ret = 200;
451 - goto cleanup;
452 - }
453 - st->last_accessed_time = now_realtime_sec();
454 -
455 - RRDCALC *rc = NULL;
456 - if(alarm) {
457 - rc = rrdcalc_find(st, alarm);
458 - if (!rc) {
459 - buffer_no_cacheable(w->response.data);
460 - buffer_svg(w->response.data, "alarm not found", NAN, "", NULL, NULL, -1, scale, 0);
461 - ret = 200;
462 - goto cleanup;
463 - }
464 - }
465 -
466 - long long multiply = (multiply_str && *multiply_str )?str2l(multiply_str):1;
467 - long long divide = (divide_str && *divide_str )?str2l(divide_str):1;
468 - long long before = (before_str && *before_str )?str2l(before_str):0;
469 - long long after = (after_str && *after_str )?str2l(after_str):-st->update_every;
470 - int points = (points_str && *points_str )?str2i(points_str):1;
471 - int precision = (precision_str && *precision_str)?str2i(precision_str):-1;
472 -
473 - if(!multiply) multiply = 1;
474 - if(!divide) divide = 1;
475 -
476 - int refresh = 0;
477 - if(refresh_str && *refresh_str) {
478 - if(!strcmp(refresh_str, "auto")) {
479 - if(rc) refresh = rc->update_every;
480 - else if(options & RRDR_OPTION_NOT_ALIGNED)
481 - refresh = st->update_every;
482 - else {
483 - refresh = (int)(before - after);
484 - if(refresh < 0) refresh = -refresh;
485 - }
486 - }
487 - else {
488 - refresh = str2i(refresh_str);
489 - if(refresh < 0) refresh = -refresh;
490 - }
491 - }
492 -
493 - if(!label) {
494 - if(alarm) {
495 - char *s = (char *)alarm;
496 - while(*s) {
497 - if(*s == '_') *s = ' ';
498 - s++;
499 - }
500 - label = alarm;
501 - }
502 - else if(dimensions) {
503 - const char *dim = buffer_tostring(dimensions);
504 - if(*dim == '|') dim++;
505 - label = dim;
506 - }
507 - else
508 - label = st->name;
509 - }
510 - if(!units) {
511 - if(alarm) {
512 - if(rc->units)
513 - units = rc->units;
514 - else
515 - units = "";
516 - }
517 - else if(options & RRDR_OPTION_PERCENTAGE)
518 - units = "%";
519 - else
520 - units = st->units;
521 - }
522 -
523 - debug(D_WEB_CLIENT, "%llu: API command 'badge.svg' for chart '%s', alarm '%s', dimensions '%s', after '%lld', before '%lld', points '%d', group '%d', options '0x%08x'"
524 - , w->id
525 - , chart
526 - , alarm?alarm:""
527 - , (dimensions)?buffer_tostring(dimensions):""
528 - , after
529 - , before
530 - , points
531 - , group
532 - , options
533 - );
534 -
535 - if(rc) {
536 - if (refresh > 0) {
537 - buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
538 - w->response.data->expires = now_realtime_sec() + refresh;
539 - }
540 - else buffer_no_cacheable(w->response.data);
541 -
542 - if(!value_color) {
543 - switch(rc->status) {
544 - case RRDCALC_STATUS_CRITICAL:
545 - value_color = "red";
546 - break;
547 -
548 - case RRDCALC_STATUS_WARNING:
549 - value_color = "orange";
550 - break;
551 -
552 - case RRDCALC_STATUS_CLEAR:
553 - value_color = "brightgreen";
554 - break;
555 -
556 - case RRDCALC_STATUS_UNDEFINED:
557 - value_color = "lightgrey";
558 - break;
559 -
560 - case RRDCALC_STATUS_UNINITIALIZED:
561 - value_color = "#000";
562 - break;
563 -
564 - default:
565 - value_color = "grey";
566 - break;
567 - }
568 - }
569 -
570 - buffer_svg(w->response.data,
571 - label,
572 - (isnan(rc->value)||isinf(rc->value)) ? rc->value : rc->value * multiply / divide,
573 - units,
574 - label_color,
575 - value_color,
576 - precision,
577 - scale,
578 - options
579 - );
580 - ret = 200;
581 - }
582 - else {
583 - time_t latest_timestamp = 0;
584 - int value_is_null = 1;
585 - calculated_number n = NAN;
586 - ret = 500;
587 -
588 - // if the collected value is too old, don't calculate its value
589 - if (rrdset_last_entry_t(st) >= (now_realtime_sec() - (st->update_every * st->gap_when_lost_iterations_above)))
590 - ret = rrdset2value_api_v1(st, w->response.data, &n, (dimensions) ? buffer_tostring(dimensions) : NULL
591 - , points, after, before, group, 0, options, NULL, &latest_timestamp, &value_is_null);
592 -
593 - // if the value cannot be calculated, show empty badge
594 - if (ret != 200) {
595 - buffer_no_cacheable(w->response.data);
596 - value_is_null = 1;
597 - n = 0;
598 - ret = 200;
599 - }
600 - else if (refresh > 0) {
601 - buffer_sprintf(w->response.header, "Refresh: %d\r\n", refresh);
602 - w->response.data->expires = now_realtime_sec() + refresh;
603 - }
604 - else buffer_no_cacheable(w->response.data);
605 -
606 - // render the badge
607 - buffer_svg(w->response.data,
608 - label,
609 - (value_is_null)?NAN:(n * multiply / divide),
610 - units,
611 - label_color,
612 - value_color,
613 - precision,
614 - scale,
615 - options
616 - );
617 - }
618 -
619 - cleanup:
620 - buffer_free(dimensions);
621 - return ret;
622 -}
623 -
235 // returns the HTTP code
236 inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url) {
237 debug(D_WEB_CLIENT, "%llu: API v1 data with URL '%s'", w->id, url);
@@ -645,7 +256,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
256 , *group_time_str = NULL
257 , *points_str = NULL;
258
648 - int group = GROUP_AVERAGE;
259 + int group = RRDR_GROUPING_AVERAGE;
260 uint32_t format = DATASOURCE_JSON;
261 uint32_t options = 0x00000000;
262
@@ -673,7 +284,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
284 else if(!strcmp(name, "points")) points_str = value;
285 else if(!strcmp(name, "gtime")) group_time_str = value;
286 else if(!strcmp(name, "group")) {
676 - group = web_client_api_request_v1_data_group(value, GROUP_AVERAGE);
287 + group = web_client_api_request_v1_data_group(value, RRDR_GROUPING_AVERAGE);
288 }
289 else if(!strcmp(name, "format")) {
290 format = web_client_api_request_v1_data_format(value);
web/api/web_api_v1.h
-3
@@ -7,7 +7,6 @@
7 #include "web/api/badges/web_buffer_svg.h"
8 #include "rrd2json.h"
9
10 -extern int web_client_api_request_v1_data_group(char *name, int def);
10 extern uint32_t web_client_api_request_v1_data_options(char *o);
11 extern uint32_t web_client_api_request_v1_data_format(char *name);
12 extern uint32_t web_client_api_request_v1_data_google_format(char *name);
@@ -17,9 +16,7 @@ extern int web_client_api_request_v1_alarm_log(RRDHOST *host, struct web_client
16 extern int web_client_api_request_single_chart(RRDHOST *host, struct web_client *w, char *url, void callback(RRDSET *st, BUFFER *buf));
17 extern int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url);
18 extern int web_client_api_request_v1_charts(RRDHOST *host, struct web_client *w, char *url);
20 -extern int web_client_api_request_v1_allmetrics(RRDHOST *host, struct web_client *w, char *url);
19 extern int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w, char *url);
22 -extern int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url);
20 extern int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, char *url);
21 extern int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *w, char *url);
22 extern int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url);
web/gui/Makefile.am
-2
@@ -23,8 +23,6 @@ dist_web_DATA = \
23 goto-host-from-alarm.html \
24 index.html \
25 infographic.html \
26 - netdata-swagger.yaml \
27 - netdata-swagger.json \
26 robots.txt \
27 refresh-badges.js \
28 registry.html \
web/server/web_client.c
-25
@@ -560,31 +560,6 @@ void buffer_data_options2string(BUFFER *wb, uint32_t options) {
560 }
561 }
562
563 -const char *group_method2string(int group) {
564 - switch(group) {
565 - case GROUP_UNDEFINED:
566 - return "";
567 -
568 - case GROUP_AVERAGE:
569 - return "average";
570 -
571 - case GROUP_MIN:
572 - return "min";
573 -
574 - case GROUP_MAX:
575 - return "max";
576 -
577 - case GROUP_SUM:
578 - return "sum";
579 -
580 - case GROUP_INCREMENTAL_SUM:
581 - return "incremental-sum";
582 -
583 - default:
584 - return "unknown-group-method";
585 - }
586 -}
587 -
563 static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
564 if(unlikely(host->rrd_memory_mode == RRD_MEMORY_MODE_NONE)) {
565 buffer_flush(w->response.data);
web/server/web_client.h
-3
@@ -184,9 +184,6 @@ extern ssize_t web_client_read_file(struct web_client *w);
184 extern void web_client_process_request(struct web_client *w);
185 extern void web_client_request_done(struct web_client *w);
186
187 -extern int web_client_api_request_v1_data_group(char *name, int def);
188 -extern const char *group_method2string(int group);
189 -
187 extern void buffer_data_options2string(BUFFER *wb, uint32_t options);
188
189 extern int mysendfile(struct web_client *w, char *filename);
web/server/web_client_cache.h
+4 -1
@@ -3,7 +3,8 @@
3 #ifndef NETDATA_WEB_CLIENT_CACHE_H
4 #define NETDATA_WEB_CLIENT_CACHE_H
5
6 -#include "web_server.h"
6 +#include "libnetdata/libnetdata.h"
7 +#include "web_client.h"
8
9 struct clients_cache {
10 pid_t pid;
@@ -25,4 +26,6 @@ extern struct web_client *web_client_get_from_cache_or_allocate();
26 extern void web_client_cache_destroy(void);
27 extern void web_client_cache_verify(int force);
28
29 +#include "web_server.h"
30 +
31 #endif //NETDATA_WEB_CLIENT_CACHE_H
web/server/web_server.h
+3 -1
@@ -3,7 +3,7 @@
3 #ifndef NETDATA_WEB_SERVER_H
4 #define NETDATA_WEB_SERVER_H 1
5
6 -#include "daemon/common.h"
6 +#include "libnetdata/libnetdata.h"
7 #include "web_client.h"
8
9 #ifndef API_LISTEN_PORT
@@ -55,4 +55,6 @@ extern struct web_client *web_client_create_on_listenfd(int listener);
55 #include "multi/multi-threaded.h"
56 #include "static/static-threaded.h"
57
58 +#include "daemon/common.h"
59 +
60 #endif /* NETDATA_WEB_SERVER_H */