@cryptotaxi247 / netdata-1 / commits / 8f6f1baf9

Added context parameter to the data endpoint (#9931)

Added functionality to support composite charts

Stelios Fragkakis committed Sep 15, 2020 at 19:41 UTC 8f6f1baf9a5686fbd06273ed081d4e40d51bdc74
16 files changed +244 -91
daemon/unit_test.c
+2 -2
@@ -1673,7 +1673,7 @@ static int test_dbengine_check_rrdr(RRDSET *st[CHARTS], RRDDIM *rd[CHARTS][DIMS]
1673 update_every = REGION_UPDATE_EVERY[current_region];
1674 long points = (time_end - time_start) / update_every - 1;
1675 for (i = 0 ; i < CHARTS ; ++i) {
1676 - RRDR *r = rrd2rrdr(st[i], points, time_start + update_every, time_end, RRDR_GROUPING_AVERAGE, 0, 0, NULL);
1676 + RRDR *r = rrd2rrdr(st[i], points, time_start + update_every, time_end, RRDR_GROUPING_AVERAGE, 0, 0, NULL, NULL);
1677 if (!r) {
1678 fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name);
1679 return ++errors;
@@ -1792,7 +1792,7 @@ int test_dbengine(void)
1792 long points = (time_end[REGIONS - 1] - time_start[0]) / update_every - 1; // cover all time regions with RRDR
1793 long point_offset = (time_start[current_region] - time_start[0]) / update_every;
1794 for (i = 0 ; i < CHARTS ; ++i) {
1795 - RRDR *r = rrd2rrdr(st[i], points, time_start[0] + update_every, time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, 0, NULL);
1795 + RRDR *r = rrd2rrdr(st[i], points, time_start[0] + update_every, time_end[REGIONS - 1], RRDR_GROUPING_AVERAGE, 0, 0, NULL, NULL);
1796 if (!r) {
1797 fprintf(stderr, " DB-engine unittest %s: empty RRDR ### E R R O R ###\n", st[i]->name);
1798 ++errors;
database/engine/rrdengineapi.c
+2 -2
@@ -376,7 +376,7 @@ static inline uint32_t *pginfo_to_points(struct rrdeng_page_info *page_info)
376 * @return number of regions with different data collection intervals.
377 */
378 unsigned rrdeng_variable_step_boundaries(RRDSET *st, time_t start_time, time_t end_time,
379 - struct rrdeng_region_info **region_info_arrayp, unsigned *max_intervalp)
379 + struct rrdeng_region_info **region_info_arrayp, unsigned *max_intervalp, RRDDIM *temp_rd)
380 {
381 struct pg_cache_page_index *page_index;
382 struct rrdengine_instance *ctx;
@@ -397,7 +397,7 @@ unsigned rrdeng_variable_step_boundaries(RRDSET *st, time_t start_time, time_t e
397 page_info_array = NULL;
398
399 rrdset_rdlock(st);
400 - for(rd_iter = st->dimensions, rd = NULL, min_time = (usec_t)-1 ; rd_iter ; rd_iter = rd_iter->next) {
400 + for(rd_iter = temp_rd?temp_rd:st->dimensions, rd = NULL, min_time = (usec_t)-1 ; rd_iter ; rd_iter = rd_iter->next) {
401 /*
402 * Choose oldest dimension as reference. This is not equivalent to the union of all dimensions
403 * but it is a best effort approximation with a bias towards older metrics in a chart. It
database/engine/rrdengineapi.h
+1 -1
@@ -43,7 +43,7 @@ extern void rrdeng_store_metric_next(RRDDIM *rd, usec_t point_in_time, storage_n
43 extern int rrdeng_store_metric_finalize(RRDDIM *rd);
44 extern unsigned
45 rrdeng_variable_step_boundaries(RRDSET *st, time_t start_time, time_t end_time,
46 - struct rrdeng_region_info **region_info_arrayp, unsigned *max_intervalp);
46 + struct rrdeng_region_info **region_info_arrayp, unsigned *max_intervalp, RRDDIM *temp_rd);
47 extern void rrdeng_load_metric_init(RRDDIM *rd, struct rrddim_query_handle *rrdimm_handle,
48 time_t start_time, time_t end_time);
49 extern storage_number rrdeng_load_metric_next(struct rrddim_query_handle *rrdimm_handle, time_t *current_time);
database/rrd.h
+13
@@ -1053,6 +1053,19 @@ static inline time_t rrdset_first_entry_t(RRDSET *st) {
1053 }
1054 }
1055
1056 +// get the timestamp of the last entry in the round robin database
1057 +static inline time_t rrddim_last_entry_t(RRDDIM *rd) {
1058 + if (rd->rrdset->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
1059 + return rd->state->query_ops.latest_time(rd);
1060 + return (time_t)rd->rrdset->last_updated.tv_sec;
1061 +}
1062 +
1063 +static inline time_t rrddim_first_entry_t(RRDDIM *rd) {
1064 + if (rd->rrdset->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
1065 + return rd->state->query_ops.oldest_time(rd);
1066 + return (time_t)(rd->rrdset->last_updated.tv_sec - rrdset_duration(rd->rrdset));
1067 +}
1068 +
1069 time_t rrdhost_last_entry_t(RRDHOST *h);
1070
1071 // get the last slot updated in the round robin database
web/api/formatters/csv/csv.c
+5 -5
@@ -3,7 +3,7 @@
3 #include "libnetdata/libnetdata.h"
4 #include "csv.h"
5
6 -void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines) {
6 +void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines, RRDDIM *temp_rd) {
7 rrdset_check_rdlock(r->st);
8
9 //info("RRD2CSV(): %s: BEGIN", r->st->id);
@@ -11,7 +11,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
11 RRDDIM *d;
12
13 // print the csv header
14 - for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
14 + for(c = 0, i = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) {
15 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
16 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
17
@@ -31,7 +31,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
31
32 if(format == DATASOURCE_CSV_MARKDOWN) {
33 // print the --- line after header
34 - for(c = 0, i = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
34 + for(c = 0, i = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) {
35 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
36 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
37
@@ -89,7 +89,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
89 int set_min_max = 0;
90 if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
91 total = 0;
92 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
92 + for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) {
93 calculated_number n = cn[c];
94
95 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
@@ -103,7 +103,7 @@ void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const
103 }
104
105 // for each dimension
106 - for(c = 0, d = r->st->dimensions; d && c < r->d ;c++, d = d->next) {
106 + for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d && c < r->d ;c++, d = d->next) {
107 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
108 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
109
web/api/formatters/csv/csv.h
+1 -1
@@ -5,7 +5,7 @@
5
6 #include "web/api/queries/rrdr.h"
7
8 -extern void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines);
8 +extern void rrdr2csv(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, const char *startline, const char *separator, const char *endline, const char *betweenlines, RRDDIM *temp_rd);
9
10 #include "../rrd2json.h"
11
web/api/formatters/json/json.c
+7 -5
@@ -5,7 +5,7 @@
5 #define JSON_DATES_JS 1
6 #define JSON_DATES_TIMESTAMP 2
7
8 -void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
8 +void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, RRDDIM *temp_rd) {
9 rrdset_check_rdlock(r->st);
10
11 //info("RRD2JSON(): %s: BEGIN", r->st->id);
@@ -94,12 +94,14 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
94 RRDDIM *rd;
95
96 // print the header lines
97 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
97 + for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
98 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
99 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
100
101 buffer_strcat(wb, pre_label);
102 buffer_strcat(wb, rd->name);
103 +// buffer_strcat(wb, ".");
104 +// buffer_strcat(wb, rd->rrdset->name);
105 buffer_strcat(wb, post_label);
106 i++;
107 }
@@ -154,7 +156,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
156 if(row_annotations) {
157 // google supports one annotation per row
158 int annotation_found = 0;
157 - for(c = 0, rd = r->st->dimensions; rd ;c++, rd = rd->next) {
159 + for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd ;c++, rd = rd->next) {
160 if(unlikely(!(r->od[c] & RRDR_DIMENSION_SELECTED))) continue;
161
162 if(co[c] & RRDR_VALUE_RESET) {
@@ -185,7 +187,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
187 int set_min_max = 0;
188 if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
189 total = 0;
188 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
190 + for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
191 calculated_number n = cn[c];
192
193 if(likely((options & RRDR_OPTION_ABSOLUTE) && n < 0))
@@ -199,7 +201,7 @@ void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable) {
201 }
202
203 // for each dimension
202 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
204 + for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
205 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
206 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
207
web/api/formatters/json/json.h
+1 -1
@@ -5,6 +5,6 @@
5
6 #include "../rrd2json.h"
7
8 -extern void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable);
8 +extern void rrdr2json(RRDR *r, BUFFER *wb, RRDR_OPTIONS options, int datatable, RRDDIM *temp_rd);
9
10 #endif //NETDATA_API_FORMATTER_JSON_H
web/api/formatters/json_wrapper.c
+38 -8
@@ -2,7 +2,7 @@
2
3 #include "json_wrapper.h"
4
5 -void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value) {
5 +void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd) {
6 rrdset_check_rdlock(r->st);
7
8 long rows = rrdr_rows(r);
@@ -34,8 +34,8 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
34 " %safter%s: %u,\n"
35 " %sdimension_names%s: ["
36 , kq, kq
37 - , kq, kq, sq, r->st->id, sq
38 - , kq, kq, sq, r->st->name, sq
37 + , kq, kq, sq, temp_rd?r->st->context:r->st->id, sq
38 + , kq, kq, sq, temp_rd?r->st->context:r->st->name, sq
39 , kq, kq, r->update_every
40 , kq, kq, r->st->update_every
41 , kq, kq, (uint32_t)rrdset_first_entry_t(r->st)
@@ -44,7 +44,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
44 , kq, kq, (uint32_t)r->after
45 , kq, kq);
46
47 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
47 + for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
48 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
49 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
50
@@ -68,7 +68,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
68 " %sdimension_ids%s: ["
69 , kq, kq);
70
71 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
71 + for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
72 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
73 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
74
@@ -85,11 +85,41 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
85 buffer_strcat(wb, sq);
86 }
87
88 + // Composite charts
89 + if (temp_rd) {
90 + buffer_sprintf(
91 + wb,
92 + "],\n"
93 + " %schart_ids%s: [",
94 + kq, kq);
95 +
96 + for (c = 0, i = 0, rd = temp_rd ; rd && c < r->d; c++, rd = rd->next) {
97 + if (unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN))
98 + continue;
99 + if (unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO)))
100 + continue;
101 +
102 + if (i)
103 + buffer_strcat(wb, ", ");
104 + buffer_strcat(wb, sq);
105 + buffer_strcat(wb, rd->rrdset->name);
106 + buffer_strcat(wb, sq);
107 + i++;
108 + }
109 + if (!i) {
110 + rows = 0;
111 + buffer_strcat(wb, sq);
112 + buffer_strcat(wb, "no data");
113 + buffer_strcat(wb, sq);
114 + }
115 + }
116 +
117 +
118 buffer_sprintf(wb, "],\n"
119 " %slatest_values%s: ["
120 , kq, kq);
121
92 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
122 + for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
123 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
124 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
125
@@ -125,7 +155,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
155
156 if(unlikely(options & RRDR_OPTION_PERCENTAGE)) {
157 total = 0;
128 - for(c = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
158 + for(c = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
159 calculated_number *cn = &r->v[ (rrdr_rows(r) - 1) * r->d ];
160 calculated_number n = cn[c];
161
@@ -138,7 +168,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS
168 if(total == 0) total = 1;
169 }
170
141 - for(c = 0, i = 0, rd = r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
171 + for(c = 0, i = 0, rd = temp_rd?temp_rd:r->st->dimensions; rd && c < r->d ;c++, rd = rd->next) {
172 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
173 if(unlikely((options & RRDR_OPTION_NONZERO) && !(r->od[c] & RRDR_DIMENSION_NONZERO))) continue;
174
web/api/formatters/json_wrapper.h
+1 -1
@@ -5,7 +5,7 @@
5
6 #include "rrd2json.h"
7
8 -extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value);
8 +extern void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb, uint32_t format, RRDR_OPTIONS options, int string_value, RRDDIM *temp_rd);
9 extern void rrdr_json_wrapper_end(RRDR *r, BUFFER *wb, uint32_t format, uint32_t options, int string_value);
10
11 #endif //NETDATA_API_FORMATTER_JSON_WRAPPER_H
web/api/formatters/rrd2json.c
+90 -28
@@ -2,6 +2,26 @@
2
3 #include "web/api/web_api_v1.h"
4
5 +static inline void free_temp_rrddim(RRDDIM *temp_rd)
6 +{
7 + if (unlikely(!temp_rd))
8 + return;
9 +
10 + RRDDIM *t;
11 + while (temp_rd) {
12 + t = temp_rd->next;
13 + freez((char *)temp_rd->id);
14 + freez((char *)temp_rd->name);
15 +#ifdef ENABLE_DBENGINE
16 + if (temp_rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
17 + freez(temp_rd->state->metric_uuid);
18 +#endif
19 + freez(temp_rd->state);
20 + freez(temp_rd);
21 + temp_rd = t;
22 + }
23 +}
24 +
25 void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
26 rrdset2json(st, wb, NULL, NULL, 0);
27 }
@@ -69,7 +89,10 @@ int rrdset2value_api_v1(
89 , time_t *db_before
90 , int *value_is_null
91 ) {
72 - RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions);
92 + RRDDIM *temp_rd = NULL;
93 +
94 + RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions, temp_rd);
95 +
96 if(!r) {
97 if(value_is_null) *value_is_null = 1;
98 return HTTP_RESP_INTERNAL_SERVER_ERROR;
@@ -98,6 +121,8 @@ int rrdset2value_api_v1(
121 long i = (!(options & RRDR_OPTION_REVERSED))?rrdr_rows(r) - 1:0;
122 *n = rrdr2value(r, i, options, value_is_null);
123
124 + free_temp_rrddim(temp_rd);
125 +
126 rrdr_free(r);
127 return HTTP_RESP_OK;
128 }
@@ -114,10 +139,45 @@ int rrdset2anything_api_v1(
139 , long group_time
140 , uint32_t options
141 , time_t *latest_timestamp
142 + , char *context
143 ) {
144 st->last_accessed_time = now_realtime_sec();
145
120 - RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL);
146 + RRDDIM *temp_rd = NULL;
147 +
148 + if (context) {
149 + rrdhost_rdlock(st->rrdhost);
150 + RRDSET *st1;
151 + rrdset_foreach_read(st1, st->rrdhost) {
152 + if (strcmp(st1->context, context) == 0) {
153 + // Loop the dimensions of the chart
154 + RRDDIM *rd1;
155 + rrdset_rdlock(st1);
156 + rrddim_foreach_read(rd1, st1) {
157 + RRDDIM *rd = mallocz(rd1->memsize);
158 + memcpy(rd, rd1, rd1->memsize);
159 + rd->id = strdupz(rd1->id);
160 + rd->name = strdupz(rd1->name);
161 + rd->state = mallocz(sizeof(*rd->state));
162 + memcpy(rd->state, rd1->state, sizeof(*rd->state));
163 + memcpy(&rd->state->collect_ops, &rd1->state->collect_ops, sizeof(struct rrddim_collect_ops));
164 + memcpy(&rd->state->query_ops, &rd1->state->query_ops, sizeof(struct rrddim_query_ops));
165 +#ifdef ENABLE_DBENGINE
166 + if (rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
167 + rd->state->metric_uuid = mallocz(sizeof(uuid_t));
168 + uuid_copy(*rd->state->metric_uuid, *rd1->state->metric_uuid);
169 + }
170 +#endif
171 + rd->next = temp_rd;
172 + temp_rd = rd;
173 + }
174 + rrdset_unlock(st1);
175 + }
176 + }
177 + rrdhost_unlock(st->rrdhost);
178 + }
179 +
180 + RRDR *r = rrd2rrdr(st, points, after, before, group_method, group_time, options, dimensions?buffer_tostring(dimensions):NULL, temp_rd);
181 if(!r) {
182 buffer_strcat(wb, "Cannot generate output with these parameters on this chart.");
183 return HTTP_RESP_INTERNAL_SERVER_ERROR;
@@ -135,7 +195,7 @@ int rrdset2anything_api_v1(
195 case DATASOURCE_SSV:
196 if(options & RRDR_OPTION_JSON_WRAP) {
197 wb->contenttype = CT_APPLICATION_JSON;
138 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
198 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
199 rrdr2ssv(r, wb, options, "", " ", "");
200 rrdr_json_wrapper_end(r, wb, format, options, 1);
201 }
@@ -148,7 +208,7 @@ int rrdset2anything_api_v1(
208 case DATASOURCE_SSV_COMMA:
209 if(options & RRDR_OPTION_JSON_WRAP) {
210 wb->contenttype = CT_APPLICATION_JSON;
151 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
211 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
212 rrdr2ssv(r, wb, options, "", ",", "");
213 rrdr_json_wrapper_end(r, wb, format, options, 1);
214 }
@@ -161,7 +221,7 @@ int rrdset2anything_api_v1(
221 case DATASOURCE_JS_ARRAY:
222 if(options & RRDR_OPTION_JSON_WRAP) {
223 wb->contenttype = CT_APPLICATION_JSON;
164 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
224 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
225 rrdr2ssv(r, wb, options, "[", ",", "]");
226 rrdr_json_wrapper_end(r, wb, format, options, 0);
227 }
@@ -174,42 +234,42 @@ int rrdset2anything_api_v1(
234 case DATASOURCE_CSV:
235 if(options & RRDR_OPTION_JSON_WRAP) {
236 wb->contenttype = CT_APPLICATION_JSON;
177 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
178 - rrdr2csv(r, wb, format, options, "", ",", "\\n", "");
237 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
238 + rrdr2csv(r, wb, format, options, "", ",", "\\n", "", temp_rd);
239 rrdr_json_wrapper_end(r, wb, format, options, 1);
240 }
241 else {
242 wb->contenttype = CT_TEXT_PLAIN;
183 - rrdr2csv(r, wb, format, options, "", ",", "\r\n", "");
243 + rrdr2csv(r, wb, format, options, "", ",", "\r\n", "", temp_rd);
244 }
245 break;
246
247 case DATASOURCE_CSV_MARKDOWN:
248 if(options & RRDR_OPTION_JSON_WRAP) {
249 wb->contenttype = CT_APPLICATION_JSON;
190 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
191 - rrdr2csv(r, wb, format, options, "", "|", "\\n", "");
250 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
251 + rrdr2csv(r, wb, format, options, "", "|", "\\n", "", temp_rd);
252 rrdr_json_wrapper_end(r, wb, format, options, 1);
253 }
254 else {
255 wb->contenttype = CT_TEXT_PLAIN;
196 - rrdr2csv(r, wb, format, options, "", "|", "\r\n", "");
256 + rrdr2csv(r, wb, format, options, "", "|", "\r\n", "", temp_rd);
257 }
258 break;
259
260 case DATASOURCE_CSV_JSON_ARRAY:
261 wb->contenttype = CT_APPLICATION_JSON;
262 if(options & RRDR_OPTION_JSON_WRAP) {
203 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
263 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
264 buffer_strcat(wb, "[\n");
205 - rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
265 + rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n", temp_rd);
266 buffer_strcat(wb, "\n]");
267 rrdr_json_wrapper_end(r, wb, format, options, 0);
268 }
269 else {
270 wb->contenttype = CT_APPLICATION_JSON;
271 buffer_strcat(wb, "[\n");
212 - rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n");
272 + rrdr2csv(r, wb, format, options + RRDR_OPTION_LABEL_QUOTES, "[", ",", "]", ",\n", temp_rd);
273 buffer_strcat(wb, "\n]");
274 }
275 break;
@@ -217,29 +277,29 @@ int rrdset2anything_api_v1(
277 case DATASOURCE_TSV:
278 if(options & RRDR_OPTION_JSON_WRAP) {
279 wb->contenttype = CT_APPLICATION_JSON;
220 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
221 - rrdr2csv(r, wb, format, options, "", "\t", "\\n", "");
280 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
281 + rrdr2csv(r, wb, format, options, "", "\t", "\\n", "", temp_rd);
282 rrdr_json_wrapper_end(r, wb, format, options, 1);
283 }
284 else {
285 wb->contenttype = CT_TEXT_PLAIN;
226 - rrdr2csv(r, wb, format, options, "", "\t", "\r\n", "");
286 + rrdr2csv(r, wb, format, options, "", "\t", "\r\n", "", temp_rd);
287 }
288 break;
289
290 case DATASOURCE_HTML:
291 if(options & RRDR_OPTION_JSON_WRAP) {
292 wb->contenttype = CT_APPLICATION_JSON;
233 - rrdr_json_wrapper_begin(r, wb, format, options, 1);
293 + rrdr_json_wrapper_begin(r, wb, format, options, 1, temp_rd);
294 buffer_strcat(wb, "<html>\\n<center>\\n<table border=\\\"0\\\" cellpadding=\\\"5\\\" cellspacing=\\\"5\\\">\\n");
235 - rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "");
295 + rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\\n", "", temp_rd);
296 buffer_strcat(wb, "</table>\\n</center>\\n</html>\\n");
297 rrdr_json_wrapper_end(r, wb, format, options, 1);
298 }
299 else {
300 wb->contenttype = CT_TEXT_HTML;
301 buffer_strcat(wb, "<html>\n<center>\n<table border=\"0\" cellpadding=\"5\" cellspacing=\"5\">\n");
242 - rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\n", "");
302 + rrdr2csv(r, wb, format, options, "<tr><td>", "</td><td>", "</td></tr>\n", "", temp_rd);
303 buffer_strcat(wb, "</table>\n</center>\n</html>\n");
304 }
305 break;
@@ -248,9 +308,9 @@ int rrdset2anything_api_v1(
308 wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
309
310 if(options & RRDR_OPTION_JSON_WRAP)
251 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
311 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
312
253 - rrdr2json(r, wb, options, 1);
313 + rrdr2json(r, wb, options, 1, temp_rd);
314
315 if(options & RRDR_OPTION_JSON_WRAP)
316 rrdr_json_wrapper_end(r, wb, format, options, 0);
@@ -260,9 +320,9 @@ int rrdset2anything_api_v1(
320 wb->contenttype = CT_APPLICATION_JSON;
321
322 if(options & RRDR_OPTION_JSON_WRAP)
263 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
323 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
324
265 - rrdr2json(r, wb, options, 1);
325 + rrdr2json(r, wb, options, 1, temp_rd);
326
327 if(options & RRDR_OPTION_JSON_WRAP)
328 rrdr_json_wrapper_end(r, wb, format, options, 0);
@@ -271,9 +331,9 @@ int rrdset2anything_api_v1(
331 case DATASOURCE_JSONP:
332 wb->contenttype = CT_APPLICATION_X_JAVASCRIPT;
333 if(options & RRDR_OPTION_JSON_WRAP)
274 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
334 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
335
276 - rrdr2json(r, wb, options, 0);
336 + rrdr2json(r, wb, options, 0, temp_rd);
337
338 if(options & RRDR_OPTION_JSON_WRAP)
339 rrdr_json_wrapper_end(r, wb, format, options, 0);
@@ -284,15 +344,17 @@ int rrdset2anything_api_v1(
344 wb->contenttype = CT_APPLICATION_JSON;
345
346 if(options & RRDR_OPTION_JSON_WRAP)
287 - rrdr_json_wrapper_begin(r, wb, format, options, 0);
347 + rrdr_json_wrapper_begin(r, wb, format, options, 0, temp_rd);
348
289 - rrdr2json(r, wb, options, 0);
349 + rrdr2json(r, wb, options, 0, temp_rd);
350
351 if(options & RRDR_OPTION_JSON_WRAP)
352 rrdr_json_wrapper_end(r, wb, format, options, 0);
353 break;
354 }
355
356 + free_temp_rrddim(temp_rd);
357 +
358 rrdr_free(r);
359 return HTTP_RESP_OK;
360 }
web/api/formatters/rrd2json.h
+1
@@ -65,6 +65,7 @@ extern int rrdset2anything_api_v1(
65 , long group_time
66 , uint32_t options
67 , time_t *latest_timestamp
68 + , char *context
69 );
70
71 extern int rrdset2value_api_v1(
web/api/queries/query.c
+33 -21
@@ -281,7 +281,7 @@ RRDR_GROUPING web_client_api_request_v1_data_group(const char *name, RRDR_GROUPI
281
282 // ----------------------------------------------------------------------------
283
284 -static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options, const char *dims) {
284 +static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options, const char *dims, RRDDIM *temp_rd) {
285 rrdset_check_rdlock(r->st);
286
287 if(unlikely(!dims || !*dims || (dims[0] == '*' && dims[1] == '\0'))) return;
@@ -300,7 +300,7 @@ static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options,
300
301 RRDDIM *d;
302 long c, dims_selected = 0, dims_not_hidden_not_zero = 0;
303 - for(c = 0, d = r->st->dimensions; d ;c++, d = d->next) {
303 + for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d ;c++, d = d->next) {
304 if( (match_ids && simple_pattern_matches(pattern, d->id))
305 || (match_names && simple_pattern_matches(pattern, d->name))
306 ) {
@@ -332,7 +332,7 @@ static void rrdr_disable_not_selected_dimensions(RRDR *r, RRDR_OPTIONS options,
332 // but they are all zero
333 // enable the selected ones
334 // to avoid returning an empty chart
335 - for(c = 0, d = r->st->dimensions; d ;c++, d = d->next)
335 + for(c = 0, d = temp_rd?temp_rd:r->st->dimensions; d ;c++, d = d->next)
336 if(unlikely(r->od[c] & RRDR_DIMENSION_SELECTED))
337 r->od[c] |= RRDR_DIMENSION_NONZERO;
338 }
@@ -815,7 +815,8 @@ static RRDR *rrd2rrdr_fixedstep(
815 , int update_every
816 , time_t first_entry_t
817 , time_t last_entry_t
818 - , int absolute_period_requested
818 + , int absolute_period_requested,
819 + RRDDIM *temp_rd
820 ) {
821 int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
822
@@ -824,7 +825,7 @@ static RRDR *rrd2rrdr_fixedstep(
825 long available_points = duration / update_every;
826
827 if(duration <= 0 || available_points <= 0)
827 - return rrdr_create(st, 1);
828 + return rrdr_create(st, 1, temp_rd);
829
830 // check the number of wanted points in the result
831 if(unlikely(points_requested < 0)) points_requested = -points_requested;
@@ -982,7 +983,7 @@ static RRDR *rrd2rrdr_fixedstep(
983 // initialize our result set
984 // this also locks the chart for us
985
985 - RRDR *r = rrdr_create(st, points_wanted);
986 + RRDR *r = rrdr_create(st, points_wanted, temp_rd);
987 if(unlikely(!r)) {
988 #ifdef NETDATA_INTERNAL_CHECKS
989 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);
@@ -1055,7 +1056,7 @@ static RRDR *rrd2rrdr_fixedstep(
1056 rrdset_check_rdlock(st);
1057
1058 if(dimensions)
1058 - rrdr_disable_not_selected_dimensions(r, options, dimensions);
1059 + rrdr_disable_not_selected_dimensions(r, options, dimensions, temp_rd);
1060
1061
1062 // -------------------------------------------------------------------------
@@ -1066,7 +1067,7 @@ static RRDR *rrd2rrdr_fixedstep(
1067
1068 RRDDIM *rd;
1069 long c, dimensions_used = 0, dimensions_nonzero = 0;
1069 - for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1070 + for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1071
1072 // if we need a percentage, we need to calculate all dimensions
1073 if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) {
@@ -1159,7 +1160,7 @@ static RRDR *rrd2rrdr_fixedstep(
1160 if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero)) {
1161 // all the dimensions are zero
1162 // mark them as NONZERO to send them all
1162 - for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1163 + for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1164 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
1165 r->od[c] |= RRDR_DIMENSION_NONZERO;
1166 }
@@ -1184,6 +1185,7 @@ static RRDR *rrd2rrdr_variablestep(
1185 , time_t last_entry_t
1186 , int absolute_period_requested
1187 , struct rrdeng_region_info *region_info_array
1188 + , RRDDIM *temp_rd
1189 ) {
1190 int aligned = !(options & RRDR_OPTION_NOT_ALIGNED);
1191
@@ -1193,7 +1195,7 @@ static RRDR *rrd2rrdr_variablestep(
1195
1196 if(duration <= 0 || available_points <= 0) {
1197 freez(region_info_array);
1196 - return rrdr_create(st, 1);
1198 + return rrdr_create(st, 1, temp_rd);
1199 }
1200
1201 // check the number of wanted points in the result
@@ -1352,7 +1354,7 @@ static RRDR *rrd2rrdr_variablestep(
1354 // initialize our result set
1355 // this also locks the chart for us
1356
1355 - RRDR *r = rrdr_create(st, points_wanted);
1357 + RRDR *r = rrdr_create(st, points_wanted, temp_rd);
1358 if(unlikely(!r)) {
1359 #ifdef NETDATA_INTERNAL_CHECKS
1360 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);
@@ -1428,7 +1430,7 @@ static RRDR *rrd2rrdr_variablestep(
1430 rrdset_check_rdlock(st);
1431
1432 if(dimensions)
1431 - rrdr_disable_not_selected_dimensions(r, options, dimensions);
1433 + rrdr_disable_not_selected_dimensions(r, options, dimensions, temp_rd);
1434
1435
1436 // -------------------------------------------------------------------------
@@ -1439,7 +1441,7 @@ static RRDR *rrd2rrdr_variablestep(
1441
1442 RRDDIM *rd;
1443 long c, dimensions_used = 0, dimensions_nonzero = 0;
1442 - for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1444 + for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1445
1446 // if we need a percentage, we need to calculate all dimensions
1447 if(unlikely(!(options & RRDR_OPTION_PERCENTAGE) && (r->od[c] & RRDR_DIMENSION_HIDDEN))) {
@@ -1533,7 +1535,7 @@ static RRDR *rrd2rrdr_variablestep(
1535 if(unlikely(options & RRDR_OPTION_NONZERO && !dimensions_nonzero)) {
1536 // all the dimensions are zero
1537 // mark them as NONZERO to send them all
1536 - for(rd = st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1538 + for(rd = temp_rd?temp_rd:st->dimensions, c = 0 ; rd && c < dimensions_count ; rd = rd->next, c++) {
1539 if(unlikely(r->od[c] & RRDR_DIMENSION_HIDDEN)) continue;
1540 r->od[c] |= RRDR_DIMENSION_NONZERO;
1541 }
@@ -1554,11 +1556,21 @@ RRDR *rrd2rrdr(
1556 , long resampling_time_requested
1557 , RRDR_OPTIONS options
1558 , const char *dimensions
1557 -) {
1559 + , RRDDIM *temp_rd
1560 +)
1561 +{
1562 int rrd_update_every;
1563 int absolute_period_requested;
1560 - time_t first_entry_t = rrdset_first_entry_t(st);
1561 - time_t last_entry_t = rrdset_last_entry_t(st);
1564 +
1565 + time_t first_entry_t;
1566 + time_t last_entry_t;
1567 + if (temp_rd) {
1568 + first_entry_t = rrddim_first_entry_t(temp_rd);
1569 + last_entry_t = rrddim_last_entry_t(temp_rd);
1570 + } else {
1571 + first_entry_t = rrdset_first_entry_t(st);
1572 + last_entry_t = rrdset_last_entry_t(st);
1573 + }
1574
1575 rrd_update_every = st->update_every;
1576 absolute_period_requested = rrdr_convert_before_after_to_absolute(&after_requested, &before_requested,
@@ -1571,7 +1583,7 @@ RRDR *rrd2rrdr(
1583
1584 /* This call takes the chart read-lock */
1585 regions = rrdeng_variable_step_boundaries(st, after_requested, before_requested,
1574 - &region_info_array, &max_interval);
1586 + &region_info_array, &max_interval, temp_rd);
1587 if (1 == regions) {
1588 if (region_info_array) {
1589 if (rrd_update_every != region_info_array[0].update_every) {
@@ -1585,7 +1597,7 @@ RRDR *rrd2rrdr(
1597 }
1598 return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
1599 resampling_time_requested, options, dimensions, rrd_update_every,
1588 - first_entry_t, last_entry_t, absolute_period_requested);
1600 + first_entry_t, last_entry_t, absolute_period_requested, temp_rd);
1601 } else {
1602 if (rrd_update_every != (uint16_t)max_interval) {
1603 rrd_update_every = (uint16_t) max_interval;
@@ -1596,11 +1608,11 @@ RRDR *rrd2rrdr(
1608 }
1609 return rrd2rrdr_variablestep(st, points_requested, after_requested, before_requested, group_method,
1610 resampling_time_requested, options, dimensions, rrd_update_every,
1599 - first_entry_t, last_entry_t, absolute_period_requested, region_info_array);
1611 + first_entry_t, last_entry_t, absolute_period_requested, region_info_array, temp_rd);
1612 }
1613 }
1614 #endif
1615 return rrd2rrdr_fixedstep(st, points_requested, after_requested, before_requested, group_method,
1616 resampling_time_requested, options, dimensions,
1605 - rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested);
1617 + rrd_update_every, first_entry_t, last_entry_t, absolute_period_requested, temp_rd);
1618 }
\ No newline at end of file
web/api/queries/rrdr.c
+10 -3
@@ -98,7 +98,7 @@ inline void rrdr_free(RRDR *r)
98 freez(r);
99 }
100
101 -RRDR *rrdr_create(RRDSET *st, long n)
101 +RRDR *rrdr_create(struct rrdset *st, long n, struct rrddim *temp_rd)
102 {
103 if(unlikely(!st)) {
104 error("NULL value given!");
@@ -111,7 +111,14 @@ RRDR *rrdr_create(RRDSET *st, long n)
111 rrdr_lock_rrdset(r);
112
113 RRDDIM *rd;
114 - rrddim_foreach_read(rd, st) r->d++;
114 + if (temp_rd) {
115 + RRDDIM *t = temp_rd;
116 + while (t) {
117 + r->d++;
118 + t = t->next;
119 + }
120 + } else
121 + rrddim_foreach_read(rd, st) r->d++;
122
123 r->n = n;
124
@@ -122,7 +129,7 @@ RRDR *rrdr_create(RRDSET *st, long n)
129
130 // set the hidden flag on hidden dimensions
131 int c;
125 - for(c = 0, rd = st->dimensions ; rd ; c++, rd = rd->next) {
132 + for(c = 0, rd = temp_rd?temp_rd:st->dimensions ; rd ; c++, rd = rd->next) {
133 if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_HIDDEN)))
134 r->od[c] = RRDR_DIMENSION_HIDDEN;
135 else
web/api/queries/rrdr.h
+3 -2
@@ -97,13 +97,14 @@ typedef struct rrdresult {
97
98 #define rrdr_rows(r) ((r)->rows)
99
100 +#include "../../../database/rrd.h"
101 extern void rrdr_free(RRDR *r);
101 -extern RRDR *rrdr_create(struct rrdset *st, long n);
102 +extern RRDR *rrdr_create(struct rrdset *st, long n, struct rrddim *id);
103
104 #include "../web_api_v1.h"
105 #include "web/api/queries/query.h"
106
106 -extern RRDR *rrd2rrdr(RRDSET *st, long points_requested, long long after_requested, long long before_requested, RRDR_GROUPING group_method, long resampling_time_requested, RRDR_OPTIONS options, const char *dimensions);
107 +extern RRDR *rrd2rrdr(RRDSET *st, long points_requested, long long after_requested, long long before_requested, RRDR_GROUPING group_method, long resampling_time_requested, RRDR_OPTIONS options, const char *dimensions, RRDDIM *temp_rd);
108
109 #include "query.h"
110
web/api/web_api_v1.c
+36 -11
@@ -397,7 +397,8 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
397 , *before_str = NULL
398 , *after_str = NULL
399 , *group_time_str = NULL
400 - , *points_str = NULL;
400 + , *points_str = NULL
401 + , *context = NULL;
402
403 int group = RRDR_GROUPING_AVERAGE;
404 uint32_t format = DATASOURCE_JSON;
@@ -416,7 +417,8 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
417 // name and value are now the parameters
418 // they are not null and not empty
419
419 - if(!strcmp(name, "chart")) chart = value;
420 + if(!strcmp(name, "context")) context = value;
421 + else if(!strcmp(name, "chart")) chart = value;
422 else if(!strcmp(name, "dimension") || !strcmp(name, "dim") || !strcmp(name, "dimensions") || !strcmp(name, "dims")) {
423 if(!dimensions) dimensions = buffer_create(100);
424 buffer_strcat(dimensions, "|");
@@ -482,18 +484,41 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
484 fix_google_param(responseHandler);
485 fix_google_param(outFileName);
486
485 - if(!chart || !*chart) {
487 + RRDSET *st = NULL;
488 +
489 + if((!chart || !*chart) && (!context)) {
490 buffer_sprintf(w->response.data, "No chart id is given at the request.");
491 goto cleanup;
492 }
493
490 - RRDSET *st = rrdset_find(host, chart);
491 - if(!st) st = rrdset_find_byname(host, chart);
492 - if(!st) {
493 - buffer_strcat(w->response.data, "Chart is not found: ");
494 - buffer_strcat_htmlescape(w->response.data, chart);
495 - ret = HTTP_RESP_NOT_FOUND;
496 - goto cleanup;
494 + if (context) {
495 + st = NULL;
496 + // TODO: Scan all charts of host
497 + rrdhost_rdlock(localhost);
498 + RRDSET *st1;
499 + rrdset_foreach_read(st1, localhost) {
500 + if (strcmp(st1->context, context) == 0) {
501 + st = st1;
502 + break;
503 + }
504 + }
505 + rrdhost_unlock(localhost);
506 + }
507 +
508 + if (!st) {
509 + if (!chart || !*chart) {
510 + buffer_sprintf(w->response.data, "No chart id is given at the request.");
511 + goto cleanup;
512 + }
513 + st = rrdset_find(host, chart);
514 + if (!st)
515 + st = rrdset_find_byname(host, chart);
516 + if (!st) {
517 + buffer_strcat(w->response.data, "Chart is not found: ");
518 + buffer_strcat_htmlescape(w->response.data, chart);
519 + ret = HTTP_RESP_NOT_FOUND;
520 + goto cleanup;
521 + }
522 }
523 st->last_accessed_time = now_realtime_sec();
524
@@ -540,7 +565,7 @@ inline int web_client_api_request_v1_data(RRDHOST *host, struct web_client *w, c
565 }
566
567 ret = rrdset2anything_api_v1(st, w->response.data, dimensions, format, points, after, before, group, group_time
543 - , options, &last_timestamp_in_data);
568 + , options, &last_timestamp_in_data, context);
569
570 if(format == DATASOURCE_DATATABLE_JSONP) {
571 if(google_timestamp < last_timestamp_in_data)