master
c 334 lines 12.2 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "database/rrd.h"
4
5 typedef struct rrdvar {
6 NETDATA_DOUBLE value;
7 } RRDVAR;
8
9 // ----------------------------------------------------------------------------
10 // RRDVAR management
11
12 inline STRING *rrdvar_name_to_string(const char *name) {
13 char *variable = strdupz(name);
14 rrdvar_fix_name(variable);
15 STRING *name_string = string_strdupz(variable);
16 freez(variable);
17 return name_string;
18 }
19
20 static bool rrdvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
21 RRDVAR *rv = old_value;
22 RRDVAR *nrv = new_value;
23
24 rv->value = nrv->value;
25 return false;
26 }
27
28 DICTIONARY *rrdvariables_create(void) {
29 DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
30 &dictionary_stats_category_rrdhealth, sizeof(RRDVAR));
31 dictionary_register_conflict_callback(dict, rrdvar_conflict_callback, NULL);
32 return dict;
33 }
34
35 void rrdvariables_destroy(DICTIONARY *dict) {
36 dictionary_destroy(dict);
37 }
38
39 static inline const RRDVAR_ACQUIRED *rrdvar_get_and_acquire(DICTIONARY *dict, STRING *name) {
40 return (const RRDVAR_ACQUIRED *)dictionary_get_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name));
41 }
42
43 inline const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(DICTIONARY *dict, STRING *name, NETDATA_DOUBLE value) {
44 if(unlikely(!dict || !name)) return NULL;
45 RRDVAR tmp = {
46 .value = value,
47 };
48 return (const RRDVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(
49 dict, string2str(name), (ssize_t)string_strlen(name),
50 &tmp, sizeof(tmp), NULL);
51 }
52
53 void rrdvar_delete_all(DICTIONARY *dict) {
54 dictionary_flush(dict);
55 }
56
57 void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) {
58 if(unlikely(!dict || !rva)) return; // when health is not enabled
59 dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
60 }
61
62 // ----------------------------------------------------------------------------
63 // CUSTOM HOST VARIABLES
64
65 inline int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data) {
66 if(unlikely(!dict)) return 0; // when health is not enabled
67 return dictionary_walkthrough_read(dict, callback, data);
68 }
69
70 const RRDVAR_ACQUIRED *rrdvar_host_variable_add_and_acquire(RRDHOST *host, const char *name) {
71 if(unlikely(!host->rrdvars)) return NULL; // when health is not enabled
72
73 STRING *name_string = rrdvar_name_to_string(name);
74 const RRDVAR_ACQUIRED *rva = rrdvar_add_and_acquire(host->rrdvars, name_string, NAN);
75
76 string_freez(name_string);
77 return rva;
78 }
79
80 void rrdvar_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) {
81 if(unlikely(!host->rrdvars || !rva)) return; // when health is not enabled
82
83 RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
84 if(rv->value != value) {
85 rv->value = value;
86
87 // if the host is streaming, send this variable upstream immediately
88 stream_sender_send_this_host_variable_now(host, rva);
89 }
90 }
91
92 // ----------------------------------------------------------------------------
93 // CUSTOM CHART VARIABLES
94
95 const RRDVAR_ACQUIRED *rrdvar_chart_variable_add_and_acquire(RRDSET *st, const char *name) {
96 if(unlikely(!st || !st->rrdvars)) return NULL;
97
98 STRING *name_string = rrdvar_name_to_string(name);
99 const RRDVAR_ACQUIRED *rs = rrdvar_add_and_acquire(st->rrdvars, name_string, NAN);
100 string_freez(name_string);
101 return rs;
102 }
103
104 void rrdvar_chart_variable_set(RRDSET *st, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) {
105 if(unlikely(!st || !st->rrdvars || !rva)) return;
106
107 RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
108 if(rv->value != value) {
109 rv->value = value;
110 rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES);
111 }
112 }
113
114 // ----------------------------------------------------------------------------
115 // RRDVAR lookup
116
117 NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva) {
118 if(unlikely(!rva)) return NAN;
119 RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
120 return rv->value;
121 }
122
123 static inline bool rrdvar_get_value(DICTIONARY *dict, STRING *variable, NETDATA_DOUBLE *result) {
124 bool found = false;
125
126 const RRDVAR_ACQUIRED *rva = rrdvar_get_and_acquire(dict, variable);
127 if(rva) {
128 *result = rrdvar2number(rva);
129 found = true;
130 dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
131 }
132
133 return found;
134 }
135
136 bool rrdvar_get_custom_host_variable_value(RRDHOST *host, STRING *variable, NETDATA_DOUBLE *result) {
137 return rrdvar_get_value(host->rrdvars, variable, result);
138 }
139
140 bool rrdvar_get_custom_chart_variable_value(RRDSET *st, STRING *variable, NETDATA_DOUBLE *result) {
141 return rrdvar_get_value(st->rrdvars, variable, result);
142 }
143
144 // ----------------------------------------------------------------------------
145 // RRDVAR to JSON
146
147 void rrdvar_to_json_members(DICTIONARY *dict, BUFFER *wb) {
148 RRDVAR *rv;
149 dfe_start_read(dict, rv) {
150 buffer_json_member_add_double(wb, rv_dfe.name, rv->value);
151 }
152 dfe_done(rv);
153 }
154
155 void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf) {
156 rrdvar_to_json_members(st->rrdvars, buf);
157 }
158
159 void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *wb) {
160
161 // FIXME this list is incomplete
162 // alerts can also access {context}.{dimension} from the entire host database
163
164 RRDHOST *host = st->rrdhost;
165
166 buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
167
168 buffer_json_member_add_string(wb, "chart", rrdset_id(st));
169 buffer_json_member_add_string(wb, "chart_name", rrdset_name(st));
170 buffer_json_member_add_string(wb, "chart_context", rrdset_context(st));
171 buffer_json_member_add_string(wb, "family", rrdset_family(st));
172 buffer_json_member_add_string(wb, "host", rrdhost_hostname(host));
173
174 time_t now = now_realtime_sec();
175
176 buffer_json_member_add_object(wb, "current_alert_values");
177 {
178 buffer_json_member_add_double(wb, "this", NAN);
179 buffer_json_member_add_double(wb, "after", (NETDATA_DOUBLE)now - 1);
180 buffer_json_member_add_double(wb, "before", (NETDATA_DOUBLE)now);
181 buffer_json_member_add_double(wb, "now", (NETDATA_DOUBLE)now);
182 buffer_json_member_add_double(wb, "status", (NETDATA_DOUBLE)RRDCALC_STATUS_REMOVED);
183 buffer_json_member_add_double(wb, "REMOVED", (NETDATA_DOUBLE)RRDCALC_STATUS_REMOVED);
184 buffer_json_member_add_double(wb, "UNDEFINED", (NETDATA_DOUBLE)RRDCALC_STATUS_UNDEFINED);
185 buffer_json_member_add_double(wb, "UNINITIALIZED", (NETDATA_DOUBLE)RRDCALC_STATUS_UNINITIALIZED);
186 buffer_json_member_add_double(wb, "CLEAR", (NETDATA_DOUBLE)RRDCALC_STATUS_CLEAR);
187 buffer_json_member_add_double(wb, "WARNING", (NETDATA_DOUBLE)RRDCALC_STATUS_WARNING);
188 buffer_json_member_add_double(wb, "CRITICAL", (NETDATA_DOUBLE)RRDCALC_STATUS_CRITICAL);
189 buffer_json_member_add_double(wb, "green", NAN);
190 buffer_json_member_add_double(wb, "red", NAN);
191 }
192 buffer_json_object_close(wb);
193
194 buffer_json_member_add_object(wb, "dimensions_last_stored_values");
195 {
196 RRDDIM *rd;
197 dfe_start_read(st->rrddim_root_index, rd) {
198 buffer_json_member_add_double(wb, string2str(rd->id), rd->collector.last_stored_value);
199 if(rd->name != rd->id)
200 buffer_json_member_add_double(wb, string2str(rd->name), rd->collector.last_stored_value);
201 }
202 dfe_done(rd);
203 }
204 buffer_json_object_close(wb);
205
206 buffer_json_member_add_object(wb, "dimensions_last_collected_values");
207 {
208 char name[RRD_ID_LENGTH_MAX + 1 + 100];
209 RRDDIM *rd;
210 dfe_start_read(st->rrddim_root_index, rd) {
211 snprintfz(name, sizeof(name), "%s_raw", string2str(rd->id));
212 if(rrddim_is_float(rd))
213 buffer_json_member_add_double(wb, name, rrddim_last_collected_as_double(rd));
214 else
215 buffer_json_member_add_int64(wb, name, rd->collector.collected.i.last_collected_value);
216 if(rd->name != rd->id) {
217 snprintfz(name, sizeof(name), "%s_raw", string2str(rd->name));
218 if(rrddim_is_float(rd))
219 buffer_json_member_add_double(wb, name, rrddim_last_collected_as_double(rd));
220 else
221 buffer_json_member_add_int64(wb, name, rd->collector.collected.i.last_collected_value);
222 }
223 }
224 dfe_done(rd);
225 }
226 buffer_json_object_close(wb);
227
228 buffer_json_member_add_object(wb, "dimensions_last_collected_time");
229 {
230 char name[RRD_ID_LENGTH_MAX + 1 + 100];
231 RRDDIM *rd;
232 dfe_start_read(st->rrddim_root_index, rd) {
233 snprintfz(name, sizeof(name), "%s_last_collected_t", string2str(rd->id));
234 buffer_json_member_add_int64(wb, name, rd->collector.last_collected_time.tv_sec);
235 if(rd->name != rd->id) {
236 snprintfz(name, sizeof(name), "%s_last_collected_t", string2str(rd->name));
237 buffer_json_member_add_int64(wb, name, rd->collector.last_collected_time.tv_sec);
238 }
239 }
240 dfe_done(rd);
241 }
242 buffer_json_object_close(wb);
243
244 buffer_json_member_add_object(wb, "chart_variables");
245 {
246 buffer_json_member_add_int64(wb, "update_every", st->update_every);
247 buffer_json_member_add_uint64(wb, "last_collected_t", st->last_collected_time.tv_sec);
248
249 rrdvar_to_json_members(st->rrdvars, wb);
250 }
251 buffer_json_object_close(wb);
252
253 buffer_json_member_add_object(wb, "host_variables");
254 {
255 rrdvar_to_json_members(st->rrdhost->rrdvars, wb);
256 }
257 buffer_json_object_close(wb);
258
259 buffer_json_member_add_object(wb, "alerts");
260 {
261 struct scored {
262 bool existing;
263 STRING *chart;
264 STRING *context;
265 NETDATA_DOUBLE value;
266 size_t score;
267 } tmp, *z;
268 DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
269
270 RRDCALC *rc;
271 dfe_start_read(st->rrdhost->rrdcalc_root_index, rc) {
272 tmp = (struct scored) {
273 .existing = false,
274 .chart = string_dup(rc->rrdset->id),
275 .context = string_dup(rc->rrdset->context),
276 .value = rc->value,
277 .score = rrdlabels_common_count(rc->rrdset->rrdlabels, st->rrdlabels),
278 };
279 z = dictionary_set(dict, string2str(rc->config.name), &tmp, sizeof(tmp));
280
281 if(z->existing) {
282 if(tmp.score > z->score)
283 SWAP(*z, tmp);
284 z->existing = true;
285 string_freez(tmp.chart);
286 string_freez(tmp.context);
287 }
288 else
289 z->existing = true;
290 }
291 dfe_done(rc);
292
293 dfe_start_read(dict, z) {
294 buffer_json_member_add_object(wb, z_dfe.name);
295 {
296 buffer_json_member_add_double(wb, "value", z->value);
297 buffer_json_member_add_string(wb, "instance", string2str(z->chart));
298 buffer_json_member_add_string(wb, "context", string2str(z->context));
299 buffer_json_member_add_uint64(wb, "score", z->score);
300 }
301 buffer_json_object_close(wb);
302
303 string_freez(z->chart);
304 string_freez(z->context);
305 }
306 dfe_done(z);
307
308 dictionary_destroy(dict);
309 }
310 buffer_json_object_close(wb);
311
312 buffer_json_finalize(wb);
313 }
314
315 // ----------------------------------------------------------------------------
316 // RRDVAR private members examination
317
318 const char *rrdvar_name(const RRDVAR_ACQUIRED *rva) {
319 return dictionary_acquired_item_name((const DICTIONARY_ITEM *)rva);
320 }
321
322 void rrdvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb) {
323 rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES);
324
325 // send the chart local custom variables
326 RRDVAR *rv;
327 dfe_start_read(st->rrdvars, rv) {
328 buffer_sprintf(wb
329 , "VARIABLE CHART %s = " NETDATA_DOUBLE_FORMAT "\n"
330 , rv_dfe.name, rv->value
331 );
332 }
333 dfe_done(rv);
334 }