master
c 445 lines 17.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "rrdcontext-internal.h"
4
5 static void rrd_flags_to_buffer_json_array_items(RRD_FLAGS flags, BUFFER *wb) {
6 if(flags & RRD_FLAG_QUEUED_FOR_HUB)
7 buffer_json_add_array_item_string(wb, "QUEUED");
8
9 if(flags & RRD_FLAG_DELETED)
10 buffer_json_add_array_item_string(wb, "DELETED");
11
12 if(flags & RRD_FLAG_COLLECTED)
13 buffer_json_add_array_item_string(wb, "COLLECTED");
14
15 if(flags & RRD_FLAG_UPDATED)
16 buffer_json_add_array_item_string(wb, "UPDATED");
17
18 if(flags & RRD_FLAG_ARCHIVED)
19 buffer_json_add_array_item_string(wb, "ARCHIVED");
20
21 if(flags & RRD_FLAG_OWN_LABELS)
22 buffer_json_add_array_item_string(wb, "OWN_LABELS");
23
24 if(flags & RRD_FLAG_LIVE_RETENTION)
25 buffer_json_add_array_item_string(wb, "LIVE_RETENTION");
26
27 if(flags & RRD_FLAG_HIDDEN)
28 buffer_json_add_array_item_string(wb, "HIDDEN");
29
30 if(flags & RRD_FLAG_QUEUED_FOR_PP)
31 buffer_json_add_array_item_string(wb, "PENDING_UPDATES");
32 }
33
34 // ----------------------------------------------------------------------------
35 // /api/v1/context(s) API
36
37 struct rrdcontext_to_json {
38 BUFFER *wb;
39 RRDCONTEXT_TO_JSON_OPTIONS options;
40 time_t after;
41 time_t before;
42 SIMPLE_PATTERN *chart_label_key;
43 SIMPLE_PATTERN *chart_labels_filter;
44 SIMPLE_PATTERN *chart_dimensions;
45 size_t written;
46 time_t now;
47 time_t combined_first_time_s;
48 time_t combined_last_time_s;
49 RRD_FLAGS combined_flags;
50 };
51
52 static inline int rrdmetric_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
53 const char *id = dictionary_acquired_item_name(item);
54 struct rrdcontext_to_json * t = data;
55 RRDMETRIC *rm = value;
56 BUFFER *wb = t->wb;
57 RRDCONTEXT_TO_JSON_OPTIONS options = t->options;
58 time_t after = t->after;
59 time_t before = t->before;
60
61 if(unlikely(rrd_flag_is_deleted(rm) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
62 return 0;
63
64 if(after && (!rm->last_time_s || after > rm->last_time_s))
65 return 0;
66
67 if(before && (!rm->first_time_s || before < rm->first_time_s))
68 return 0;
69
70 if(t->chart_dimensions
71 && !simple_pattern_matches_string(t->chart_dimensions, rm->id)
72 && rm->name != rm->id
73 && !simple_pattern_matches_string(t->chart_dimensions, rm->name))
74 return 0;
75
76 if(t->written) {
77 t->combined_first_time_s = MIN(t->combined_first_time_s, rm->first_time_s);
78 t->combined_last_time_s = MAX(t->combined_last_time_s, rm->last_time_s);
79 t->combined_flags |= rrd_flags_get(rm);
80 }
81 else {
82 t->combined_first_time_s = rm->first_time_s;
83 t->combined_last_time_s = rm->last_time_s;
84 t->combined_flags = rrd_flags_get(rm);
85 }
86
87 buffer_json_member_add_object(wb, id);
88
89 if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
90 char uuid[UUID_STR_LEN];
91 uuid_unparse(*uuidmap_uuid_ptr(rm->uuid), uuid);
92 buffer_json_member_add_string(wb, "uuid", uuid);
93 }
94
95 buffer_json_member_add_string(wb, "name", string2str(rm->name));
96 buffer_json_member_add_time_t_formatted(wb, "first_time_t", rm->first_time_s, options & RRDCONTEXT_OPTION_RFC3339);
97 buffer_json_member_add_time_t_formatted(wb, "last_time_t", rrd_flag_is_collected(rm) ? (long long)t->now : (long long)rm->last_time_s, options & RRDCONTEXT_OPTION_RFC3339);
98 buffer_json_member_add_boolean(wb, "collected", rrd_flag_is_collected(rm));
99
100 if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
101 buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(rm));
102
103 if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
104 buffer_json_member_add_array(wb, "flags");
105 rrd_flags_to_buffer_json_array_items(rrd_flags_get(rm), wb);
106 buffer_json_array_close(wb);
107 }
108
109 buffer_json_object_close(wb);
110 t->written++;
111 return 1;
112 }
113
114 static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
115 const char *id = dictionary_acquired_item_name(item);
116
117 struct rrdcontext_to_json *t_parent = data;
118 RRDINSTANCE *ri = value;
119 BUFFER *wb = t_parent->wb;
120 RRDCONTEXT_TO_JSON_OPTIONS options = t_parent->options;
121 time_t after = t_parent->after;
122 time_t before = t_parent->before;
123 bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions;
124
125 if(unlikely(rrd_flag_is_deleted(ri) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
126 return 0;
127
128 if(after && (!ri->last_time_s || after > ri->last_time_s))
129 return 0;
130
131 if(before && (!ri->first_time_s || before < ri->first_time_s))
132 return 0;
133
134 RRDLABELS *labels = rrdinstance_labels(ri);
135
136 if(t_parent->chart_label_key && rrdlabels_match_simple_pattern_parsed(
137 labels,
138 t_parent->chart_label_key,
139 '\0', NULL) != SP_MATCHED_POSITIVE)
140 return 0;
141
142 if(t_parent->chart_labels_filter && rrdlabels_match_simple_pattern_parsed(
143 labels,
144 t_parent->chart_labels_filter, ':',
145 NULL) != SP_MATCHED_POSITIVE)
146 return 0;
147
148 time_t first_time_s = ri->first_time_s;
149 time_t last_time_s = ri->last_time_s;
150 RRD_FLAGS flags = rrd_flags_get(ri);
151
152 BUFFER *wb_metrics = NULL;
153 if(options & RRDCONTEXT_OPTION_SHOW_METRICS || t_parent->chart_dimensions) {
154
155 wb_metrics = buffer_create(4096, &netdata_buffers_statistics.buffers_api);
156 buffer_json_initialize(wb_metrics, "\"", "\"", wb->json.depth + 2, false, BUFFER_JSON_OPTIONS_DEFAULT);
157
158 struct rrdcontext_to_json t_metrics = {
159 .wb = wb_metrics,
160 .options = options,
161 .chart_label_key = t_parent->chart_label_key,
162 .chart_labels_filter = t_parent->chart_labels_filter,
163 .chart_dimensions = t_parent->chart_dimensions,
164 .after = after,
165 .before = before,
166 .written = 0,
167 .now = t_parent->now,
168 };
169 dictionary_walkthrough_read(ri->rrdmetrics, rrdmetric_to_json_callback, &t_metrics);
170
171 if(has_filter && !t_metrics.written) {
172 buffer_free(wb_metrics);
173 return 0;
174 }
175
176 first_time_s = t_metrics.combined_first_time_s;
177 last_time_s = t_metrics.combined_last_time_s;
178 flags = t_metrics.combined_flags;
179 }
180
181 if(t_parent->written) {
182 t_parent->combined_first_time_s = MIN(t_parent->combined_first_time_s, first_time_s);
183 t_parent->combined_last_time_s = MAX(t_parent->combined_last_time_s, last_time_s);
184 t_parent->combined_flags |= flags;
185 }
186 else {
187 t_parent->combined_first_time_s = first_time_s;
188 t_parent->combined_last_time_s = last_time_s;
189 t_parent->combined_flags = flags;
190 }
191
192 buffer_json_member_add_object(wb, id);
193
194 if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
195 char uuid[UUID_STR_LEN];
196 uuid_unparse(*uuidmap_uuid_ptr(ri->uuid), uuid);
197 buffer_json_member_add_string(wb, "uuid", uuid);
198 }
199
200 buffer_json_member_add_string(wb, "name", string2str(ri->name));
201 buffer_json_member_add_string(wb, "context", string2str(ri->rc->id));
202 buffer_json_member_add_string(wb, "title", string2str(ri->title));
203 buffer_json_member_add_string(wb, "units", string2str(ri->units));
204 buffer_json_member_add_string(wb, "family", string2str(ri->family));
205 buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(ri->chart_type));
206 buffer_json_member_add_uint64(wb, "priority", ri->priority);
207 buffer_json_member_add_time_t(wb, "update_every", ri->update_every_s);
208 buffer_json_member_add_time_t_formatted(wb, "first_time_t", first_time_s, options & RRDCONTEXT_OPTION_RFC3339);
209 buffer_json_member_add_time_t_formatted(wb, "last_time_t", (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_s, options & RRDCONTEXT_OPTION_RFC3339);
210 buffer_json_member_add_boolean(wb, "collected", flags & RRD_FLAG_COLLECTED);
211
212 if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
213 buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(ri));
214
215 if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
216 buffer_json_member_add_array(wb, "flags");
217 rrd_flags_to_buffer_json_array_items(rrd_flags_get(ri), wb);
218 buffer_json_array_close(wb);
219 }
220
221 if(options & RRDCONTEXT_OPTION_SHOW_LABELS && rrdlabels_entries(labels)) {
222 buffer_json_member_add_object(wb, "labels");
223 rrdlabels_to_buffer_json_members(labels, wb);
224 buffer_json_object_close(wb);
225 }
226
227 if(wb_metrics) {
228 buffer_json_member_add_object(wb, "dimensions");
229 buffer_fast_strcat(wb, buffer_tostring(wb_metrics), buffer_strlen(wb_metrics));
230 buffer_json_object_close(wb);
231
232 buffer_free(wb_metrics);
233 }
234
235 buffer_json_object_close(wb);
236 t_parent->written++;
237 return 1;
238 }
239
240 static inline int rrdcontext_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
241 const char *id = dictionary_acquired_item_name(item);
242 struct rrdcontext_to_json *t_parent = data;
243 RRDCONTEXT *rc = value;
244 BUFFER *wb = t_parent->wb;
245 RRDCONTEXT_TO_JSON_OPTIONS options = t_parent->options;
246 time_t after = t_parent->after;
247 time_t before = t_parent->before;
248 bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions;
249
250 if(unlikely(rrd_flag_check(rc, RRD_FLAG_HIDDEN) && !(options & RRDCONTEXT_OPTION_SHOW_HIDDEN)))
251 return 0;
252
253 if(unlikely(rrd_flag_is_deleted(rc) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
254 return 0;
255
256 if(options & RRDCONTEXT_OPTION_DEEPSCAN)
257 rrdcontext_recalculate_context_retention(rc, RRD_FLAG_NONE, false);
258
259 if(after && (!rc->last_time_s || after > rc->last_time_s))
260 return 0;
261
262 if(before && (!rc->first_time_s || before < rc->first_time_s))
263 return 0;
264
265 time_t first_time_s = rc->first_time_s;
266 time_t last_time_s = rc->last_time_s;
267 RRD_FLAGS flags = rrd_flags_get(rc);
268
269 BUFFER *wb_instances = NULL;
270 if((options & (RRDCONTEXT_OPTION_SHOW_LABELS|RRDCONTEXT_OPTION_SHOW_INSTANCES|RRDCONTEXT_OPTION_SHOW_METRICS))
271 || t_parent->chart_label_key
272 || t_parent->chart_labels_filter
273 || t_parent->chart_dimensions) {
274
275 wb_instances = buffer_create(4096, &netdata_buffers_statistics.buffers_api);
276 buffer_json_initialize(wb_instances, "\"", "\"", wb->json.depth + 2, false, BUFFER_JSON_OPTIONS_DEFAULT);
277
278 struct rrdcontext_to_json t_instances = {
279 .wb = wb_instances,
280 .options = options,
281 .chart_label_key = t_parent->chart_label_key,
282 .chart_labels_filter = t_parent->chart_labels_filter,
283 .chart_dimensions = t_parent->chart_dimensions,
284 .after = after,
285 .before = before,
286 .written = 0,
287 .now = t_parent->now,
288 };
289 dictionary_walkthrough_read(rc->rrdinstances, rrdinstance_to_json_callback, &t_instances);
290
291 if(has_filter && !t_instances.written) {
292 buffer_free(wb_instances);
293 return 0;
294 }
295
296 first_time_s = t_instances.combined_first_time_s;
297 last_time_s = t_instances.combined_last_time_s;
298 flags = t_instances.combined_flags;
299 }
300
301 if(!(options & RRDCONTEXT_OPTION_SKIP_ID))
302 buffer_json_member_add_object(wb, id);
303
304 rrdcontext_lock(rc);
305
306 buffer_json_member_add_string(wb, "title", string2str(rc->title));
307 buffer_json_member_add_string(wb, "units", string2str(rc->units));
308 buffer_json_member_add_string(wb, "family", string2str(rc->family));
309 buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rc->chart_type));
310 buffer_json_member_add_uint64(wb, "priority", rc->priority);
311 buffer_json_member_add_time_t_formatted(wb, "first_time_t", first_time_s, options & RRDCONTEXT_OPTION_RFC3339);
312 buffer_json_member_add_time_t_formatted(wb, "last_time_t", (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_s, options & RRDCONTEXT_OPTION_RFC3339);
313 buffer_json_member_add_boolean(wb, "collected", (flags & RRD_FLAG_COLLECTED));
314
315 if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
316 buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(rc));
317
318 if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
319 buffer_json_member_add_array(wb, "flags");
320 rrd_flags_to_buffer_json_array_items(rrd_flags_get(rc), wb);
321 buffer_json_array_close(wb);
322 }
323
324 if(options & RRDCONTEXT_OPTION_SHOW_QUEUED) {
325 buffer_json_member_add_array(wb, "queued_reasons");
326 rrd_reasons_to_buffer_json_array_items(rc->queue.queued_flags, wb);
327 buffer_json_array_close(wb);
328
329 buffer_json_member_add_time_t_formatted(wb, "last_queued", (time_t)(rc->queue.queued_ut / USEC_PER_SEC), options & RRDCONTEXT_OPTION_RFC3339);
330 buffer_json_member_add_time_t_formatted(wb, "scheduled_dispatch", (time_t)(rc->queue.scheduled_dispatch_ut / USEC_PER_SEC), options & RRDCONTEXT_OPTION_RFC3339);
331 buffer_json_member_add_time_t_formatted(wb, "last_dequeued", (time_t)(rc->queue.dequeued_ut / USEC_PER_SEC), options & RRDCONTEXT_OPTION_RFC3339);
332 buffer_json_member_add_uint64(wb, "dispatches", rc->queue.dispatches);
333 buffer_json_member_add_uint64(wb, "hub_version", rc->hub.version);
334 buffer_json_member_add_uint64(wb, "version", rc->version);
335
336 buffer_json_member_add_array(wb, "pp_reasons");
337 rrd_reasons_to_buffer_json_array_items(rc->pp.queued_flags, wb);
338 buffer_json_array_close(wb);
339
340 buffer_json_member_add_time_t_formatted(wb, "pp_last_queued", (time_t)(rc->pp.queued_ut / USEC_PER_SEC), options & RRDCONTEXT_OPTION_RFC3339);
341 buffer_json_member_add_time_t_formatted(wb, "pp_last_dequeued", (time_t)(rc->pp.dequeued_ut / USEC_PER_SEC), options & RRDCONTEXT_OPTION_RFC3339);
342 buffer_json_member_add_uint64(wb, "pp_executed", rc->pp.executions);
343 }
344
345 rrdcontext_unlock(rc);
346
347 if(wb_instances) {
348 buffer_json_member_add_object(wb, "charts");
349 buffer_fast_strcat(wb, buffer_tostring(wb_instances), buffer_strlen(wb_instances));
350 buffer_json_object_close(wb);
351
352 buffer_free(wb_instances);
353 }
354
355 if(!(options & RRDCONTEXT_OPTION_SKIP_ID))
356 buffer_json_object_close(wb);
357
358 t_parent->written++;
359 return 1;
360 }
361
362 int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, const char *context, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) {
363 if(!host->rrdctx.contexts) {
364 netdata_log_error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host));
365 return HTTP_RESP_NOT_FOUND;
366 }
367
368 RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item(host->rrdctx.contexts, context);
369 if(!rca) return HTTP_RESP_NOT_FOUND;
370
371 RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
372
373 if(after != 0 && before != 0)
374 rrdr_relative_window_to_absolute_query(&after, &before, NULL, false);
375
376 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
377 struct rrdcontext_to_json t_contexts = {
378 .wb = wb,
379 .options = options|RRDCONTEXT_OPTION_SKIP_ID,
380 .chart_label_key = chart_label_key,
381 .chart_labels_filter = chart_labels_filter,
382 .chart_dimensions = chart_dimensions,
383 .after = after,
384 .before = before,
385 .written = 0,
386 .now = now_realtime_sec(),
387 };
388 rrdcontext_to_json_callback((DICTIONARY_ITEM *)rca, rc, &t_contexts);
389 buffer_json_finalize(wb);
390
391 rrdcontext_release(rca);
392
393 if(!t_contexts.written)
394 return HTTP_RESP_NOT_FOUND;
395
396 return HTTP_RESP_OK;
397 }
398
399 int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) {
400 if(!host->rrdctx.contexts) {
401 netdata_log_error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host));
402 return HTTP_RESP_NOT_FOUND;
403 }
404
405 char node_uuid[UUID_STR_LEN] = "";
406
407 if(!UUIDiszero(host->node_id))
408 uuid_unparse_lower(host->node_id.uuid, node_uuid);
409
410 if(after != 0 && before != 0)
411 rrdr_relative_window_to_absolute_query(&after, &before, NULL, false);
412
413 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
414 buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(host));
415 buffer_json_member_add_string(wb, "machine_guid", host->machine_guid);
416 buffer_json_member_add_string(wb, "node_id", node_uuid);
417 CLAIM_ID claim_id = rrdhost_claim_id_get(host);
418 buffer_json_member_add_string(wb, "claim_id", claim_id.str);
419
420 if(options & RRDCONTEXT_OPTION_SHOW_LABELS) {
421 buffer_json_member_add_object(wb, "host_labels");
422 rrdlabels_to_buffer_json_members(host->rrdlabels, wb);
423 buffer_json_object_close(wb);
424 }
425
426 buffer_json_member_add_object(wb, "contexts");
427 struct rrdcontext_to_json t_contexts = {
428 .wb = wb,
429 .options = options,
430 .chart_label_key = chart_label_key,
431 .chart_labels_filter = chart_labels_filter,
432 .chart_dimensions = chart_dimensions,
433 .after = after,
434 .before = before,
435 .written = 0,
436 .now = now_realtime_sec(),
437 };
438 dictionary_walkthrough_read(host->rrdctx.contexts, rrdcontext_to_json_callback, &t_contexts);
439 buffer_json_object_close(wb);
440
441 buffer_json_finalize(wb);
442
443 return HTTP_RESP_OK;
444 }
445