master
c 1,735 lines 77.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "sqlite_health.h"
4 #include "sqlite_functions.h"
5 #include "sqlite_db_migration.h"
6 #include "health/health_internals.h"
7 #include "health/health-alert-entry.h"
8
9 extern __thread bool is_health_thread;
10
11 #define MAX_HEALTH_SQL_SIZE 2048
12 #define SQLITE3_BIND_STRING_OR_NULL(res, param, key) \
13 ((key) ? sqlite3_bind_text((res), (param), string2str(key), -1, SQLITE_STATIC) : sqlite3_bind_null((res), (param)))
14
15 #define SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, param, key) \
16 ((key) ? sqlite3_bind_text((res), (param), string2str(key), -1, SQLITE_TRANSIENT) : sqlite3_bind_null((res), (param)))
17
18 #define SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, param) \
19 ({ \
20 int _param = (param); \
21 sqlite3_column_type((res), (_param)) != SQLITE_NULL ? \
22 string_strdupz((char *)sqlite3_column_text((res), (_param))) : \
23 NULL; \
24 })
25
26 /* Health related SQL queries
27 Updates an entry in the table
28 */
29 #define SQL_UPDATE_HEALTH_LOG \
30 "UPDATE health_log_detail SET updated_by_id = @updated_by, flags = @flags, exec_run_timestamp = @exec_time, " \
31 "exec_code = @exec_code WHERE unique_id = @unique_id AND alarm_id = @alarm_id AND transition_id = @transaction"
32
33 static void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae)
34 {
35 static __thread sqlite3_stmt *compiled_res = NULL;
36 sqlite3_stmt *res = NULL;
37
38 if (is_health_thread) {
39 if (!compiled_res) {
40 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_UPDATE_HEALTH_LOG, &compiled_res))
41 return;
42 }
43 res = compiled_res;
44 } else {
45 if (!PREPARE_STATEMENT(db_meta, SQL_UPDATE_HEALTH_LOG, &res))
46 return;
47 }
48
49 int rc;
50
51 int param = 0;
52 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->updated_by_id));
53 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->flags));
54 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->exec_run_timestamp));
55 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ae->exec_code));
56 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->unique_id));
57 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->alarm_id));
58 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &ae->transition_id, sizeof(ae->transition_id), SQLITE_STATIC));
59
60 param = 0;
61 rc = sqlite3_step_monitored(res);
62 if (unlikely(rc != SQLITE_DONE)) {
63 error_report("HEALTH [%s]: Failed to update health log, rc = %d", rrdhost_hostname(host), rc);
64 }
65
66 done:
67 REPORT_BIND_FAIL(res, param);
68 if (is_health_thread)
69 SQLITE_RESET(res);
70 else
71 SQLITE_FINALIZE(res);
72 }
73
74 /* Health related SQL queries
75 *
76 * Inserts an entry in the tables
77 * alert_queue
78 * health_log
79 * health_log_detail
80 *
81 */
82
83 int calculate_delay(RRDCALC_STATUS old_status, RRDCALC_STATUS new_status)
84 {
85 int delay = ALERT_TRANSITION_DELAY_NONE;
86 switch(old_status) {
87 case RRDCALC_STATUS_REMOVED:
88 switch (new_status) {
89 case RRDCALC_STATUS_UNINITIALIZED:
90 delay = ALERT_TRANSITION_DELAY_LONG;
91 break;
92 case RRDCALC_STATUS_CLEAR:
93 delay = ALERT_TRANSITION_DELAY_SHORT;
94 break;
95 default:
96 delay = ALERT_TRANSITION_DELAY_NONE;
97 break;
98 }
99 break;
100 case RRDCALC_STATUS_UNDEFINED:
101 case RRDCALC_STATUS_UNINITIALIZED:
102 switch (new_status) {
103 case RRDCALC_STATUS_REMOVED:
104 case RRDCALC_STATUS_UNINITIALIZED:
105 case RRDCALC_STATUS_UNDEFINED:
106 delay = ALERT_TRANSITION_DELAY_LONG;
107 break;
108 case RRDCALC_STATUS_CLEAR:
109 delay = ALERT_TRANSITION_DELAY_SHORT;
110 break;
111 default:
112 delay = ALERT_TRANSITION_DELAY_NONE;
113 break;
114 }
115 break;
116 case RRDCALC_STATUS_CLEAR:
117 switch (new_status) {
118 case RRDCALC_STATUS_REMOVED:
119 case RRDCALC_STATUS_UNINITIALIZED:
120 case RRDCALC_STATUS_UNDEFINED:
121 delay = ALERT_TRANSITION_DELAY_LONG;
122 break;
123 case RRDCALC_STATUS_WARNING:
124 case RRDCALC_STATUS_CRITICAL:
125 default:
126 delay = ALERT_TRANSITION_DELAY_NONE;
127 break;
128
129 }
130 break;
131 case RRDCALC_STATUS_WARNING:
132 case RRDCALC_STATUS_CRITICAL:
133 switch (new_status) {
134 case RRDCALC_STATUS_UNINITIALIZED:
135 case RRDCALC_STATUS_UNDEFINED:
136 delay = ALERT_TRANSITION_DELAY_LONG;
137 break;
138 case RRDCALC_STATUS_REMOVED:
139 case RRDCALC_STATUS_CLEAR:
140 delay = ALERT_TRANSITION_DELAY_SHORT;
141 break;
142 default:
143 delay = ALERT_TRANSITION_DELAY_NONE;
144 break;
145 }
146 break;
147 default:
148 delay = ALERT_TRANSITION_DELAY_NONE;
149 break;
150 }
151 return delay;
152 }
153
154 #define SQL_INSERT_ALERT_PENDING_QUEUE \
155 "INSERT INTO alert_queue (host_id, health_log_id, unique_id, alarm_id, status, date_scheduled)" \
156 " VALUES (@host_id, @health_log_id, @unique_id, @alarm_id, @new_status, @delay)" \
157 " ON CONFLICT (host_id, health_log_id, alarm_id)" \
158 " DO UPDATE SET status = excluded.status, unique_id = excluded.unique_id, " \
159 " date_scheduled = MIN(date_scheduled, excluded.date_scheduled)"
160
161 static void insert_alert_queue(
162 RRDHOST *host,
163 uint64_t health_log_id,
164 int64_t unique_id,
165 uint32_t alarm_id,
166 RRDCALC_STATUS old_status,
167 RRDCALC_STATUS new_status,
168 time_t trigger_time)
169 {
170 static __thread sqlite3_stmt *compiled_res = NULL;
171 sqlite3_stmt *res = NULL;
172
173 if (is_health_thread) {
174 if (!compiled_res) {
175 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_INSERT_ALERT_PENDING_QUEUE, &compiled_res))
176 return;
177 }
178 res = compiled_res;
179 } else {
180 if (!PREPARE_STATEMENT(db_meta, SQL_INSERT_ALERT_PENDING_QUEUE, &res))
181 return;
182 }
183
184 int rc;
185
186 struct aclk_sync_cfg_t *aclk_host_config = __atomic_load_n(&host->aclk_host_config, __ATOMIC_ACQUIRE);
187 if (!aclk_host_config)
188 return;
189
190 time_t submit_delay = trigger_time + calculate_delay(old_status, new_status);
191
192 int param = 0;
193 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
194 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)health_log_id));
195 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, unique_id));
196 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, alarm_id));
197 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, new_status));
198 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, submit_delay));
199
200 param = 0;
201 rc = sqlite3_step_monitored(res);
202 if (rc != SQLITE_DONE)
203 error_report(
204 "HEALTH [%s]: Failed to execute insert_alert_queue, rc = %d", rrdhost_hostname(host), rc);
205
206 done:
207 REPORT_BIND_FAIL(res, param);
208 if (is_health_thread)
209 SQLITE_RESET(res);
210 else
211 SQLITE_FINALIZE(res);
212 }
213
214 #define SQL_INSERT_HEALTH_LOG_DETAIL \
215 "INSERT INTO health_log_detail (health_log_id, unique_id, alarm_id, alarm_event_id, " \
216 "updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, " \
217 "info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, transition_id, global_id, summary) " \
218 " VALUES (@health_log_id,@unique_id,@alarm_id,@alarm_event_id,@updated_by_id,@updates_id,@when_key,@duration," \
219 "@non_clear_duration,@flags,@exec_run_timestamp,@delay_up_to_timestamp, @info,@exec_code,@new_status,@old_status," \
220 "@delay,@new_value,@old_value,@last_repeat,@transition_id,@global_id,@summary)"
221
222 static void sql_health_alarm_log_insert_detail(RRDHOST *host, uint64_t health_log_id, ALARM_ENTRY *ae)
223 {
224 static __thread sqlite3_stmt *compiled_res = NULL;
225 sqlite3_stmt *res = NULL;
226
227 if (is_health_thread) {
228 if (!compiled_res) {
229 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_INSERT_HEALTH_LOG_DETAIL, &compiled_res))
230 return;
231 }
232 res = compiled_res;
233 } else {
234 if (!PREPARE_STATEMENT(db_meta, SQL_INSERT_HEALTH_LOG_DETAIL, &res))
235 return;
236 }
237
238 int rc;
239
240 int param = 0;
241 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)health_log_id));
242 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->unique_id));
243 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->alarm_id));
244 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->alarm_event_id));
245 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->updated_by_id));
246 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->updates_id));
247 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->when));
248 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->duration));
249 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->non_clear_duration));
250 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->flags));
251 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->exec_run_timestamp));
252 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->delay_up_to_timestamp));
253 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->info));
254 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ae->exec_code));
255 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ae->new_status));
256 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ae->old_status));
257 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ae->delay));
258 SQLITE_BIND_FAIL(done, sqlite3_bind_double(res, ++param, ae->new_value));
259 SQLITE_BIND_FAIL(done, sqlite3_bind_double(res, ++param, ae->old_value));
260 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->last_repeat));
261 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &ae->transition_id, sizeof(ae->transition_id), SQLITE_STATIC));
262 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)ae->global_id));
263 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->summary));
264
265 param = 0;
266 rc = sqlite3_step_monitored(res);
267 if (rc == SQLITE_DONE)
268 ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
269 else
270 error_report(
271 "HEALTH [%s]: Failed to execute SQL_INSERT_HEALTH_LOG_DETAIL, rc = %d", rrdhost_hostname(host), rc);
272
273 done:
274 REPORT_BIND_FAIL(res, param);
275 if (is_health_thread)
276 SQLITE_RESET(res);
277 else
278 SQLITE_FINALIZE(res);
279 }
280
281 #define SQL_INSERT_HEALTH_LOG \
282 "INSERT INTO health_log (host_id, alarm_id, " \
283 "config_hash_id, name, chart, exec, recipient, units, chart_context, last_transition_id, chart_name) " \
284 "VALUES (@host_id,@alarm_id, @config_hash_id,@name,@chart,@exec,@recipient,@units,@chart_context," \
285 "@last_transition_id,@chart_name) ON CONFLICT (host_id, alarm_id) DO UPDATE " \
286 "SET last_transition_id = excluded.last_transition_id, chart_name = excluded.chart_name, " \
287 "config_hash_id=excluded.config_hash_id RETURNING health_log_id"
288
289 static void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae)
290 {
291 static __thread sqlite3_stmt *compiled_res = NULL;
292 sqlite3_stmt *res = NULL;
293 int rc;
294 uint64_t health_log_id;
295
296 if (is_health_thread) {
297 if (!compiled_res) {
298 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_INSERT_HEALTH_LOG, &compiled_res))
299 return;
300 }
301 res = compiled_res;
302 } else {
303 if (!PREPARE_STATEMENT(db_meta, SQL_INSERT_HEALTH_LOG, &res))
304 return;
305 }
306
307 int param = 0;
308 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
309 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) ae->alarm_id));
310 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &ae->config_hash_id, sizeof(ae->config_hash_id), SQLITE_STATIC));
311 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->name));
312 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->chart));
313 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->exec));
314 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->recipient));
315 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->units));
316 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->chart_context));
317 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &ae->transition_id, sizeof(ae->transition_id), SQLITE_STATIC));
318 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, ae->chart_name));
319
320 param = 0;
321 rc = sqlite3_step_monitored(res);
322 if (rc == SQLITE_ROW) {
323 health_log_id = (size_t)sqlite3_column_int64(res, 0);
324 sql_health_alarm_log_insert_detail(host, health_log_id, ae);
325 insert_alert_queue(
326 host, health_log_id, (int64_t)ae->unique_id, (int64_t)ae->alarm_id, ae->old_status, ae->new_status, ae->when);
327 } else
328 error_report("HEALTH [%s]: Failed to execute SQL_INSERT_HEALTH_LOG, rc = %d", rrdhost_hostname(host), rc);
329
330 done:
331 REPORT_BIND_FAIL(res, param);
332 if (is_health_thread)
333 SQLITE_RESET(res);
334 else
335 SQLITE_FINALIZE(res);
336 }
337
338 void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae)
339 {
340 if (!REQUIRE_HEALTH_DB_OPEN())
341 return;
342
343 if (ae->flags & HEALTH_ENTRY_FLAG_SAVED)
344 sql_health_alarm_log_update(host, ae);
345 else
346 sql_health_alarm_log_insert(host, ae);
347 }
348
349 /*
350 *
351 * Health related SQL queries
352 * Cleans up the health_log_detail table on a non-claimed or claimed host
353 *
354 */
355
356 #define SQL_CLEANUP_HEALTH_LOG_DETAIL \
357 "DELETE FROM health_log_detail WHERE health_log_id IN " \
358 " (SELECT health_log_id FROM health_log WHERE host_id = @host_id) AND when_key < UNIXEPOCH() - @history " \
359 " AND updated_by_id <> 0 AND transition_id NOT IN " \
360 " (SELECT last_transition_id FROM health_log hl WHERE hl.host_id = @host_id)"
361
362 void sql_health_alarm_log_cleanup(RRDHOST *host)
363 {
364 sqlite3_stmt *res = NULL;
365 int rc;
366
367 if (!PREPARE_STATEMENT(db_meta, SQL_CLEANUP_HEALTH_LOG_DETAIL, &res))
368 return;
369
370 int param = 0;
371 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
372 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)host->health_log.health_log_retention_s));
373
374 param = 0;
375 rc = sqlite3_step_monitored(res);
376 if (unlikely(rc != SQLITE_DONE))
377 error_report("Failed to cleanup health log detail table, rc = %d", rc);
378
379 done:
380 REPORT_BIND_FAIL(res, param);
381 SQLITE_FINALIZE(res);
382
383 // After cleaning up SQLite entries, also clean up in-memory entries
384 health_alarm_log_cleanup(host);
385 }
386
387 #define SQL_UPDATE_TRANSITION_IN_HEALTH_LOG \
388 "UPDATE health_log SET last_transition_id = @transition WHERE alarm_id = @alarm_id AND " \
389 " last_transition_id = @prev_trans AND host_id = @host_id"
390
391 bool sql_update_transition_in_health_log(RRDHOST *host, uint32_t alarm_id, nd_uuid_t *transition_id, nd_uuid_t *last_transition)
392 {
393 int rc = 0;
394 sqlite3_stmt *res;
395
396 if (!PREPARE_STATEMENT(db_meta, SQL_UPDATE_TRANSITION_IN_HEALTH_LOG, &res))
397 return false;
398
399 int param = 0;
400 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, transition_id, sizeof(*transition_id), SQLITE_STATIC));
401 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)alarm_id));
402 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, last_transition, sizeof(*last_transition), SQLITE_STATIC));
403 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
404
405 param = 0;
406 rc = sqlite3_step_monitored(res);
407 if (unlikely(rc != SQLITE_DONE))
408 error_report("HEALTH [N/A]: Failed to execute SQL_INJECT_REMOVED_UPDATE_DETAIL, rc = %d", rc);
409
410 done:
411 REPORT_BIND_FAIL(res, param);
412 SQLITE_FINALIZE(res);
413
414 return (param == 0 && rc == SQLITE_DONE);
415 }
416
417 #define SQL_SET_UPDATED_BY_IN_HEALTH_LOG_DETAIL \
418 "UPDATE health_log_detail SET flags = flags | @flag, updated_by_id = @updated_by WHERE" \
419 " unique_id = @unique_id AND transition_id = @transition_id"
420
421 bool sql_set_updated_by_in_health_log_detail(uint32_t unique_id, uint32_t max_unique_id, nd_uuid_t *prev_transition_id)
422 {
423 int rc = 0;
424 sqlite3_stmt *res;
425
426 if (!PREPARE_STATEMENT(db_meta, SQL_SET_UPDATED_BY_IN_HEALTH_LOG_DETAIL, &res))
427 return false;
428
429 int param = 0;
430 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) HEALTH_ENTRY_FLAG_UPDATED));
431 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) max_unique_id));
432 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) unique_id));
433 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, prev_transition_id, sizeof(*prev_transition_id), SQLITE_STATIC));
434
435 param = 0;
436 rc = sqlite3_step_monitored(res);
437 if (unlikely(rc != SQLITE_DONE))
438 error_report("HEALTH [N/A]: Failed to execute SQL_INJECT_REMOVED_UPDATE_DETAIL, rc = %d", rc);
439
440 done:
441 REPORT_BIND_FAIL(res, param);
442 SQLITE_FINALIZE(res);
443
444 return (param == 0 && rc == SQLITE_DONE);
445 }
446
447 #define SQL_INJECT_REMOVED \
448 "INSERT INTO health_log_detail (health_log_id, unique_id, alarm_id, alarm_event_id, updated_by_id, updates_id, when_key, " \
449 "duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, info, exec_code, new_status, old_status, " \
450 "delay, new_value, old_value, last_repeat, transition_id, global_id, summary) " \
451 "SELECT health_log_id, @max_unique_id, @alarm_id, @alarm_event_id, 0, @unique_id, UNIXEPOCH(), 0, 0, flags, " \
452 " exec_run_timestamp, UNIXEPOCH(), info, exec_code, -2, " \
453 " new_status, delay, NULL, new_value, 0, @transition_id, NOW_USEC(0), summary FROM health_log_detail " \
454 " WHERE unique_id = @unique_id AND transition_id = @last_transition_id RETURNING health_log_id, old_status"
455
456 static void sql_inject_removed_status(
457 RRDHOST *host,
458 uint32_t alarm_id,
459 uint32_t alarm_event_id,
460 uint32_t unique_id,
461 uint32_t max_unique_id,
462 nd_uuid_t *last_transition)
463 {
464 if (!alarm_id || !alarm_event_id || !unique_id || !max_unique_id)
465 return;
466
467 sqlite3_stmt *res = NULL;
468
469 if (!PREPARE_STATEMENT(db_meta, SQL_INJECT_REMOVED, &res))
470 return;
471
472 nd_uuid_t transition_id;
473 uuid_generate_random(transition_id);
474
475 int param = 0;
476 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) max_unique_id));
477 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) alarm_id));
478 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) alarm_event_id + 1));
479 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) unique_id));
480 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &transition_id, sizeof(transition_id), SQLITE_STATIC));
481 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, last_transition, sizeof(*last_transition), SQLITE_STATIC));
482
483 param = 0;
484 time_t now = now_realtime_sec();
485 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
486 //update the old entry in health_log_detail
487 sql_set_updated_by_in_health_log_detail(unique_id, max_unique_id, last_transition);
488 //update the old entry in health_log
489 sql_update_transition_in_health_log(host, alarm_id, &transition_id, last_transition);
490
491 int64_t health_log_id = sqlite3_column_int64(res, 0);
492 RRDCALC_STATUS old_status = (RRDCALC_STATUS)sqlite3_column_double(res, 1);
493 insert_alert_queue(
494 host, health_log_id, (int64_t)max_unique_id, (int64_t)alarm_id, old_status, RRDCALC_STATUS_REMOVED, now);
495 }
496
497 done:
498 REPORT_BIND_FAIL(res, param);
499 SQLITE_FINALIZE(res);
500 }
501
502 #define SQL_SELECT_MAX_UNIQUE_ID \
503 "SELECT MAX(hld.unique_id) FROM health_log_detail hld, health_log hl " \
504 "WHERE hl.host_id = @host_id AND hl.health_log_id = hld.health_log_id"
505
506 uint32_t sql_get_max_unique_id (RRDHOST *host)
507 {
508 uint32_t max_unique_id = 0;
509
510 sqlite3_stmt *res = NULL;
511
512 if (!PREPARE_STATEMENT(db_meta, SQL_SELECT_MAX_UNIQUE_ID, &res))
513 return 0;
514
515 int param = 0;
516 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
517
518 param = 0;
519 while (sqlite3_step_monitored(res) == SQLITE_ROW)
520 max_unique_id = (uint32_t)sqlite3_column_int64(res, 0);
521
522 done:
523 REPORT_BIND_FAIL(res, param);
524 SQLITE_FINALIZE(res);
525 return max_unique_id;
526 }
527
528 #define SQL_SELECT_LAST_STATUSES \
529 "SELECT hld.new_status, hld.unique_id, hld.alarm_id, hld.alarm_event_id, hld.transition_id FROM health_log hl, " \
530 "health_log_detail hld WHERE hl.host_id = @host_id AND hl.last_transition_id = hld.transition_id"
531
532 void sql_check_removed_alerts_state(RRDHOST *host)
533 {
534 uint32_t max_unique_id = 0;
535 sqlite3_stmt *res = NULL;
536 nd_uuid_t transition_id;
537
538 if (!PREPARE_STATEMENT(db_meta, SQL_SELECT_LAST_STATUSES, &res))
539 return;
540
541 int param = 0;
542 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
543
544 param = 0;
545 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
546 const nd_uuid_t *transition_uuid = sqlite3_column_uuid_ptr(res, 4);
547
548 RRDCALC_STATUS status = (RRDCALC_STATUS)sqlite3_column_int(res, 0);
549 uint32_t unique_id = (uint32_t)sqlite3_column_int64(res, 1);
550 uint32_t alarm_id = (uint32_t)sqlite3_column_int64(res, 2);
551 uint32_t alarm_event_id = (uint32_t)sqlite3_column_int64(res, 3);
552
553 if (unlikely(!transition_uuid)) {
554 error_report("HEALTH [%s]: Got invalid transition id while checking removed alerts. Ignoring it.",
555 rrdhost_hostname(host));
556 continue;
557 }
558
559 uuid_copy(transition_id, *transition_uuid);
560
561 if (unlikely(status != RRDCALC_STATUS_REMOVED)) {
562 if (unlikely(!max_unique_id))
563 max_unique_id = sql_get_max_unique_id(host);
564
565 sql_inject_removed_status(host, alarm_id, alarm_event_id, unique_id, ++max_unique_id, &transition_id);
566 }
567 if (!service_running(SERVICE_HEALTH))
568 break;
569 }
570 done:
571 REPORT_BIND_FAIL(res, param);
572 SQLITE_FINALIZE(res);
573 }
574
575 #define SQL_DELETE_MISSING_CHART_ALERT \
576 "DELETE FROM health_log WHERE host_id = @host_id AND chart NOT IN " \
577 "(SELECT type||'.'||id FROM chart WHERE host_id = @host_id)"
578
579 static void sql_remove_alerts_from_deleted_charts(RRDHOST *host, nd_uuid_t *host_uuid)
580 {
581 sqlite3_stmt *res = NULL;
582 int ret;
583
584 nd_uuid_t *actual_uuid = host ? &host->host_id.uuid : host_uuid;
585 if (!actual_uuid)
586 return;
587
588 if (!PREPARE_STATEMENT(db_meta, SQL_DELETE_MISSING_CHART_ALERT, &res))
589 return;
590
591 int param = 0;
592 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, actual_uuid, sizeof(*actual_uuid), SQLITE_STATIC));
593
594 param = 0;
595 ret = sqlite3_step_monitored(res);
596 if (ret != SQLITE_DONE)
597 error_report("Failed to execute command to delete missing charts from health_log");
598
599 done:
600 REPORT_BIND_FAIL(res, param);
601 SQLITE_FINALIZE(res);
602 }
603
604 static int clean_host_alerts(void *data, int argc, char **argv, char **column)
605 {
606 UNUSED(argc);
607 UNUSED(data);
608 UNUSED(column);
609
610 char guid[UUID_STR_LEN];
611 uuid_unparse_lower(*(nd_uuid_t *)argv[0], guid);
612
613 netdata_log_info("Checking host %s (%s)", guid, (const char *) argv[1]);
614 sql_remove_alerts_from_deleted_charts(NULL, (nd_uuid_t *)argv[0]);
615
616 return 0;
617 }
618
619
620 #define SQL_HEALTH_CHECK_ALL_HOSTS "SELECT host_id, hostname FROM host"
621
622 void sql_alert_cleanup(bool cli)
623 {
624 UNUSED(cli);
625
626 errno_clear();
627 if (sql_init_meta_database(DB_CHECK_NONE, 0)) {
628 netdata_log_error("Failed to open database");
629 return;
630 }
631 netdata_log_info("Alert cleanup running ...");
632 int rc = sqlite3_exec_monitored(db_meta, SQL_HEALTH_CHECK_ALL_HOSTS, clean_host_alerts, NULL, NULL);
633 if (rc != SQLITE_OK)
634 netdata_log_error("Failed to check host alerts");
635 else
636 netdata_log_info("Alert cleanup done");
637
638 }
639 /* Health related SQL queries
640 Load from the health log table
641 */
642 #define SQL_LOAD_HEALTH_LOG \
643 "SELECT hld.unique_id, hld.alarm_id, hld.alarm_event_id, hl.config_hash_id, hld.updated_by_id, " \
644 "hld.updates_id, hld.when_key, hld.duration, hld.non_clear_duration, hld.flags, hld.exec_run_timestamp, " \
645 "hld.delay_up_to_timestamp, hl.name, hl.chart, hl.exec, hl.recipient, ah.source, hl.units, " \
646 "hld.info, hld.exec_code, hld.new_status, hld.old_status, hld.delay, hld.new_value, hld.old_value, " \
647 "hld.last_repeat, ah.class, ah.component, ah.type, hl.chart_context, hld.transition_id, hld.global_id, " \
648 "hl.chart_name, hld.summary FROM health_log hl, alert_hash ah, health_log_detail hld " \
649 "WHERE hl.config_hash_id = ah.hash_id and hl.host_id = @host_id and hl.last_transition_id = hld.transition_id"
650
651 void sql_health_alarm_log_load(RRDHOST *host)
652 {
653 sqlite3_stmt *res = NULL;
654 ssize_t errored = 0, loaded = 0;
655
656 if (!REQUIRE_DB(db_meta))
657 return;
658
659 sql_check_removed_alerts_state(host);
660
661 if (!PREPARE_STATEMENT(db_meta, SQL_LOAD_HEALTH_LOG, &res))
662 return;
663
664 int param = 0;
665 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
666
667 DICTIONARY *all_rrdcalcs = dictionary_create(
668 DICT_OPTION_NAME_LINK_DONT_CLONE | DICT_OPTION_VALUE_LINK_DONT_CLONE | DICT_OPTION_DONT_OVERWRITE_VALUE);
669
670 RRDCALC *rc;
671 foreach_rrdcalc_in_rrdhost_read(host, rc) {
672 dictionary_set(all_rrdcalcs, rrdcalc_name(rc), rc, sizeof(*rc));
673 }
674 foreach_rrdcalc_in_rrdhost_done(rc);
675
676 param = 0;
677 rw_spinlock_write_lock(&host->health_log.spinlock);
678
679 while (service_running(SERVICE_HEALTH) && sqlite3_step_monitored(res) == SQLITE_ROW) {
680 ALARM_ENTRY *ae = NULL;
681
682 // check that we have valid ids
683 uint32_t unique_id = (uint32_t) sqlite3_column_int64(res, 0);
684 if(!unique_id) {
685 error_report("HEALTH [%s]: Got invalid unique id. Ignoring it.", rrdhost_hostname(host));
686 errored++;
687 continue;
688 }
689
690 uint32_t alarm_id = (uint32_t) sqlite3_column_int64(res, 1);
691 if(!alarm_id) {
692 error_report("HEALTH [%s]: Got invalid alarm id. Ignoring it.", rrdhost_hostname(host));
693 errored++;
694 continue;
695 }
696
697 //need name and chart
698 if (sqlite3_column_type(res, 12) == SQLITE_NULL) {
699 error_report("HEALTH [%s]: Got null name field. Ignoring it.", rrdhost_hostname(host));
700 errored++;
701 continue;
702 }
703
704 if (sqlite3_column_type(res, 13) == SQLITE_NULL) {
705 error_report("HEALTH [%s]: Got null chart field. Ignoring it.", rrdhost_hostname(host));
706 errored++;
707 continue;
708 }
709
710 // Check if we got last_repeat field
711 time_t last_repeat = (time_t)sqlite3_column_int64(res, 25);
712
713 rc = dictionary_get(all_rrdcalcs, (char *) sqlite3_column_text(res, 13));
714 if(unlikely(rc)) {
715 if (rrdcalc_isrepeating(rc)) {
716 rc->last_repeat = last_repeat;
717 // We iterate through repeating alarm entries only to
718 // find the latest last_repeat timestamp. Otherwise,
719 // there is no need to keep them in memory.
720 continue;
721 }
722 }
723
724 if (sqlite3_column_type(res, 30) != SQLITE_NULL &&
725 unlikely(!sqlite3_column_uuid_ptr(res, 30))) {
726 error_report("HEALTH [%s]: Got invalid transition id. Ignoring entry.", rrdhost_hostname(host));
727 errored++;
728 continue;
729 }
730
731 ae = health_alarm_entry_create();
732
733 ae->unique_id = unique_id;
734 ae->alarm_id = alarm_id;
735
736 if (sqlite3_column_type(res, 3) != SQLITE_NULL) {
737 if (unlikely(!sqlite3_column_uuid_copy(res, 3, ae->config_hash_id))) {
738 error_report("HEALTH [%s]: Got invalid config hash id. Ignoring entry.", rrdhost_hostname(host));
739 errored++;
740 health_alarm_entry_destroy(ae);
741 continue;
742 }
743 }
744
745 ae->alarm_event_id = (uint32_t) sqlite3_column_int64(res, 2);
746 ae->updated_by_id = (uint32_t) sqlite3_column_int64(res, 4);
747 ae->updates_id = (uint32_t) sqlite3_column_int64(res, 5);
748
749 ae->when = (time_t) sqlite3_column_int64(res, 6);
750 ae->duration = (time_t) sqlite3_column_int64(res, 7);
751 ae->non_clear_duration = (time_t) sqlite3_column_int64(res, 8);
752
753 ae->flags = (uint32_t) sqlite3_column_int64(res, 9);
754 ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
755
756 ae->exec_run_timestamp = (time_t) sqlite3_column_int64(res, 10);
757 ae->delay_up_to_timestamp = (time_t) sqlite3_column_int64(res, 11);
758
759 ae->name = string_strdupz((char *) sqlite3_column_text(res, 12));
760 ae->chart = string_strdupz((char *) sqlite3_column_text(res, 13));
761
762 ae->exec = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 14);
763 ae->recipient = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 15);
764 ae->source = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 16);
765 ae->units = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 17);
766 ae->info = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 18);
767
768 ae->exec_code = (int) sqlite3_column_int(res, 19);
769 ae->new_status = (RRDCALC_STATUS) sqlite3_column_int(res, 20);
770 ae->old_status = (RRDCALC_STATUS)sqlite3_column_int(res, 21);
771 ae->delay = (int) sqlite3_column_int(res, 22);
772
773 ae->new_value = (NETDATA_DOUBLE) sqlite3_column_double(res, 23);
774 ae->old_value = (NETDATA_DOUBLE) sqlite3_column_double(res, 24);
775
776 ae->last_repeat = last_repeat;
777
778 ae->classification = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 26);
779 ae->component = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 27);
780 ae->type = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 28);
781 ae->chart_context = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 29);
782
783 if (sqlite3_column_type(res, 30) != SQLITE_NULL) {
784 bool copied = sqlite3_column_uuid_copy(res, 30, ae->transition_id);
785 internal_fatal(!copied, "HEALTH [%s]: transition id validation invariant violated while loading health log.",
786 rrdhost_hostname(host));
787 }
788
789 if (sqlite3_column_type(res, 31) != SQLITE_NULL)
790 ae->global_id = sqlite3_column_int64(res, 31);
791
792 ae->chart_name = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 32);
793 ae->summary = SQLITE3_COLUMN_STRINGDUP_OR_NULL(res, 33);
794
795 char value_string[100 + 1];
796 ae->old_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae_units(ae), -1));
797 ae->new_value_string = string_strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae_units(ae), -1));
798
799 DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(host->health_log.alarms, ae, prev, next);
800
801 if(unlikely(ae->unique_id > host->health_max_unique_id))
802 host->health_max_unique_id = ae->unique_id;
803
804 if(unlikely(ae->alarm_id >= host->health_max_alarm_id))
805 host->health_max_alarm_id = ae->alarm_id;
806 loaded++;
807 }
808
809 rw_spinlock_write_unlock(&host->health_log.spinlock);
810
811 dictionary_destroy(all_rrdcalcs);
812 all_rrdcalcs = NULL;
813
814 if (!host->health_max_unique_id)
815 host->health_max_unique_id = get_uint32_id();
816 if (!host->health_max_alarm_id)
817 host->health_max_alarm_id = get_uint32_id();
818
819 host->health_log.next_log_id = host->health_max_unique_id + 1;
820 if (unlikely(!host->health_log.next_alarm_id || host->health_log.next_alarm_id <= host->health_max_alarm_id))
821 host->health_log.next_alarm_id = host->health_max_alarm_id + 1;
822
823 nd_log(NDLS_DAEMON, errored ? NDLP_WARNING : NDLP_DEBUG,
824 "[%s]: Table health_log, loaded %zd alarm entries, errors in %zd entries.",
825 rrdhost_hostname(host), loaded, errored);
826
827 // Clean up old entries based on retention settings
828 health_alarm_log_cleanup(host);
829 done:
830 REPORT_BIND_FAIL(res, param);
831 SQLITE_FINALIZE(res);
832 }
833
834 /*
835 * Store an alert config hash in the database
836 */
837 #define SQL_STORE_ALERT_CONFIG_HASH \
838 "INSERT OR REPLACE INTO alert_hash (hash_id, date_updated, alarm, template, " \
839 "on_key, class, component, type, lookup, every, units, calc, " \
840 "green, red, warn, crit, exec, to_key, info, delay, options, repeat, host_labels, " \
841 "p_db_lookup_dimensions, p_db_lookup_method, p_db_lookup_options, p_db_lookup_after, " \
842 "p_db_lookup_before, p_update_every, source, chart_labels, summary, time_group_condition, " \
843 "time_group_value, dims_group, data_source) " \
844 "VALUES (@hash_id,UNIXEPOCH(),@alarm,@template," \
845 "@on_key,@class,@component,@type,@lookup,@every,@units,@calc," \
846 "@green,@red,@warn,@crit,@exec,@to_key,@info,@delay,@options,@repeat,@host_labels," \
847 "@p_db_lookup_dimensions,@p_db_lookup_method,@p_db_lookup_options,@p_db_lookup_after," \
848 "@p_db_lookup_before,@p_update_every,@source,@chart_labels,@summary, @time_group_condition, " \
849 "@time_group_value, @dims_group, @data_source)"
850
851 void sql_alert_store_config(RRD_ALERT_PROTOTYPE *ap)
852 {
853 sqlite3_stmt *res = NULL;
854 int param = 0;
855
856 if (!PREPARE_STATEMENT(db_meta, SQL_STORE_ALERT_CONFIG_HASH, &res))
857 return;
858
859 CLEAN_BUFFER *buf = buffer_create(128, NULL);
860
861 SQLITE_BIND_FAIL(
862 done, sqlite3_bind_blob(res, ++param, &ap->config.hash_id, sizeof(ap->config.hash_id), SQLITE_TRANSIENT));
863
864 if (ap->match.is_template) {
865 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, NULL));
866 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.name));
867 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->match.on.context));
868 }
869 else {
870 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.name));
871 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, NULL));
872 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->match.on.chart));
873 }
874
875 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.classification));
876 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.component));
877 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.type));
878 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, NULL)); // lookup
879 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ap->config.update_every));
880 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.units));
881
882 if (ap->config.calculation)
883 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, expression_source(ap->config.calculation), -1, SQLITE_TRANSIENT));
884 else
885 SQLITE_BIND_FAIL(done,sqlite3_bind_null(res, ++param));
886
887 NETDATA_DOUBLE nan_value = NAN;
888 SQLITE_BIND_FAIL(done, sqlite3_bind_double(res, ++param, nan_value));
889 SQLITE_BIND_FAIL(done, sqlite3_bind_double(res, ++param, nan_value));
890
891 if (ap->config.warning)
892 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, expression_source(ap->config.warning), -1, SQLITE_TRANSIENT));
893 else
894 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
895
896 if (ap->config.critical)
897 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, expression_source(ap->config.critical), -1, SQLITE_TRANSIENT));
898 else
899 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
900
901 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.exec));
902 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.recipient));
903 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.info));
904
905 if (ap->config.delay_up_duration)
906 buffer_sprintf(buf, "up %ds ", ap->config.delay_up_duration);
907
908 if (ap->config.delay_down_duration)
909 buffer_sprintf(buf, "down %ds ", ap->config.delay_down_duration);
910
911 if (ap->config.delay_multiplier)
912 buffer_sprintf(buf, "multiplier %.1f ", ap->config.delay_multiplier);
913
914 if (ap->config.delay_max_duration)
915 buffer_sprintf(buf, "max %ds", ap->config.delay_max_duration);
916
917 // delay
918 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, buffer_tostring(buf), -1, SQLITE_TRANSIENT));
919
920 if (ap->config.alert_action_options & ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION)
921 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, "no-clear-notification", -1, SQLITE_TRANSIENT));
922 else
923 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
924
925 char repeat[255];
926 if (!ap->config.has_custom_repeat_config)
927 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
928 else {
929 snprintfz(repeat, sizeof(repeat) - 1, "warning %us critical %us", ap->config.warn_repeat_every, ap->config.crit_repeat_every);
930 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, repeat, -1, SQLITE_TRANSIENT));
931 }
932
933 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->match.host_labels));
934
935 if (ap->config.after) {
936 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.dimensions));
937 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, time_grouping_id2txt(ap->config.time_group), -1, SQLITE_TRANSIENT));
938 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (int) RRDR_OPTIONS_REMOVE_OVERLAPPING(ap->config.options)));
939 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (int) ap->config.after));
940 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (int) ap->config.before));
941 } else {
942 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
943 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
944 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
945 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
946 SQLITE_BIND_FAIL(done, sqlite3_bind_null(res, ++param));
947 }
948
949 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ap->config.update_every));
950 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.source));
951 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->match.chart_labels));
952 SQLITE_BIND_FAIL(done, SQLITE3_BIND_TRANSIENT_STRING_OR_NULL(res, ++param, ap->config.summary));
953
954 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ap->config.time_group_condition));
955 SQLITE_BIND_FAIL(done, sqlite3_bind_double(res, ++param, ap->config.time_group_value));
956 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ap->config.dims_group));
957 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, ap->config.data_source));
958
959 metadata_execute_store_statement(res);
960 return;
961 done:
962 REPORT_BIND_FAIL(res, param);
963 SQLITE_FINALIZE(res);
964 }
965
966 #define SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT \
967 "SELECT hld.new_status FROM health_log hl, health_log_detail hld " \
968 "WHERE hl.host_id = @host_id AND hl.alarm_id = @alarm_id AND hld.unique_id != @unique_id AND hld.flags & @flags " \
969 "AND hl.health_log_id = hld.health_log_id ORDER BY hld.unique_id DESC LIMIT 1"
970
971 int sql_health_get_last_executed_event(RRDHOST *host, ALARM_ENTRY *ae, RRDCALC_STATUS *last_executed_status)
972 {
973 if (!REQUIRE_HEALTH_DB_OPEN())
974 return -1;
975
976 int ret = -1;
977 static __thread sqlite3_stmt *compiled_res = NULL;
978 sqlite3_stmt *res = NULL;
979
980 if (is_health_thread) {
981 if (!compiled_res) {
982 if (!PREPARE_COMPILED_STATEMENT(db_meta, SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT, &compiled_res))
983 return ret;
984 }
985 res = compiled_res;
986 } else {
987 if (!PREPARE_STATEMENT(db_meta, SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT, &res))
988 return ret;
989 }
990
991 int param = 0;
992 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
993 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (int) ae->alarm_id));
994 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (int) ae->unique_id));
995 SQLITE_BIND_FAIL(done, sqlite3_bind_int(res, ++param, (uint32_t) HEALTH_ENTRY_FLAG_EXEC_RUN));
996
997 param = 0;
998 ret = 0;
999 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
1000 *last_executed_status = (RRDCALC_STATUS) sqlite3_column_int(res, 0);
1001 ret = 1;
1002 }
1003
1004 done:
1005 REPORT_BIND_FAIL(res, param);
1006 if (is_health_thread)
1007 SQLITE_RESET(res);
1008 else
1009 SQLITE_FINALIZE(res);
1010 return ret;
1011 }
1012
1013 #define SQL_SELECT_HEALTH_LOG \
1014 "SELECT hld.unique_id, hld.alarm_id, hld.alarm_event_id, hl.config_hash_id, hld.updated_by_id, hld.updates_id, " \
1015 "hld.when_key, hld.duration, hld.non_clear_duration, hld.flags, hld.exec_run_timestamp, " \
1016 "hld.delay_up_to_timestamp, hl.name, hl.chart, hl.exec, hl.recipient, ah.source, " \
1017 "hl.units, hld.info, hld.exec_code, hld.new_status, hld.old_status, hld.delay, hld.new_value, hld.old_value, " \
1018 "hld.last_repeat, ah.class, ah.component, ah.type, hl.chart_context, hld.transition_id, hld.summary " \
1019 "FROM health_log hl, alert_hash ah, health_log_detail hld WHERE hl.config_hash_id = ah.hash_id and " \
1020 "hl.health_log_id = hld.health_log_id and hl.host_id = @host_id AND hld.unique_id > @after "
1021
1022 void sql_health_alarm_log2json(RRDHOST *host, BUFFER *wb, time_t after, const char *chart)
1023 {
1024 unsigned int max = host->health_log.max;
1025
1026 sqlite3_stmt *stmt_query;
1027
1028 int rc;
1029
1030 BUFFER *command = buffer_create(MAX_HEALTH_SQL_SIZE, NULL);
1031 buffer_sprintf(command, SQL_SELECT_HEALTH_LOG);
1032
1033 if (chart)
1034 buffer_strcat(command, " AND hl.chart = @chart ");
1035
1036 buffer_strcat(command, " ORDER BY hld.unique_id DESC LIMIT @limit");
1037
1038 rc = PREPARE_STATEMENT(db_meta, buffer_tostring(command), &stmt_query);
1039 buffer_free(command);
1040
1041 if (unlikely(rc != SQLITE_OK)) {
1042 error_report("Failed to prepare statement SQL_SELECT_HEALTH_LOG");
1043 return;
1044 }
1045
1046 int param = 0;
1047 rc = sqlite3_bind_blob(stmt_query, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC);
1048 if (unlikely(rc != SQLITE_OK)) {
1049 error_report("Failed to bind host_id for SQL_SELECT_HEALTH_LOG.");
1050 goto finish;
1051 }
1052
1053 rc = sqlite3_bind_int64(stmt_query, ++param, after);
1054 if (unlikely(rc != SQLITE_OK)) {
1055 error_report("Failed to bind after for SQL_SELECT_HEALTH_LOG.");
1056 goto finish;
1057 }
1058
1059 if (chart) {
1060 rc = sqlite3_bind_text(stmt_query, ++param, chart, -1, SQLITE_STATIC);
1061 if (unlikely(rc != SQLITE_OK)) {
1062 error_report("Failed to bind after for SQL_SELECT_HEALTH_LOG.");
1063 goto finish;
1064 }
1065 }
1066
1067 rc = sqlite3_bind_int64(stmt_query, ++param, max);
1068 if (unlikely(rc != SQLITE_OK)) {
1069 error_report("Failed to bind max lines for SQL_SELECT_HEALTH_LOG.");
1070 goto finish;
1071 }
1072
1073 buffer_json_initialize(wb, "\"", "\"", 0, false, BUFFER_JSON_OPTIONS_DEFAULT);
1074 buffer_json_member_add_array(wb, NULL);
1075
1076 while (sqlite3_step(stmt_query) == SQLITE_ROW) {
1077 char old_value_string[100 + 1];
1078 char new_value_string[100 + 1];
1079
1080 char config_hash_id[UUID_STR_LEN];
1081 if (unlikely(!sqlite3_column_uuid_unparse_lower(stmt_query, 3, config_hash_id))) {
1082 error_report("HEALTH [%s]: Got invalid config hash id while exporting health log. Ignoring entry.",
1083 rrdhost_hostname(host));
1084 continue;
1085 }
1086
1087 char transition_id[UUID_STR_LEN] = {0};
1088 if (sqlite3_column_type(stmt_query, 30) != SQLITE_NULL &&
1089 unlikely(!sqlite3_column_uuid_unparse_lower(stmt_query, 30, transition_id))) {
1090 error_report("HEALTH [%s]: Got invalid transition id while exporting health log. Ignoring entry.",
1091 rrdhost_hostname(host));
1092 continue;
1093 }
1094
1095 char *edit_command = sqlite3_column_bytes(stmt_query, 16) > 0 ?
1096 health_edit_command_from_source((char *)sqlite3_column_text(stmt_query, 16)) :
1097 strdupz("UNKNOWN=0=UNKNOWN");
1098
1099 buffer_json_add_array_item_object(wb); // this node
1100
1101 buffer_json_member_add_string_or_empty(wb, "hostname", rrdhost_hostname(host));
1102 {
1103 RRDHOST_TZ host_tz = rrdhost_tz_get(host);
1104 buffer_json_member_add_int64(wb, "utc_offset", (int64_t)host_tz.utc_offset);
1105 buffer_json_member_add_string_or_empty(wb, "timezone", host_tz.abbrev_timezone);
1106 rrdhost_tz_free(&host_tz);
1107 }
1108 buffer_json_member_add_int64(wb, "unique_id", (int64_t) sqlite3_column_int64(stmt_query, 0));
1109 buffer_json_member_add_int64(wb, "alarm_id", (int64_t) sqlite3_column_int64(stmt_query, 1));
1110 buffer_json_member_add_int64(wb, "alarm_event_id", (int64_t) sqlite3_column_int64(stmt_query, 2));
1111 buffer_json_member_add_string_or_empty(wb, "config_hash_id", config_hash_id);
1112 buffer_json_member_add_string_or_empty(wb, "transition_id", transition_id);
1113 buffer_json_member_add_string_or_empty(wb, "name", (const char *) sqlite3_column_text(stmt_query, 12));
1114 buffer_json_member_add_string_or_empty(wb, "chart", (const char *) sqlite3_column_text(stmt_query, 13));
1115 buffer_json_member_add_string_or_empty(wb, "context", (const char *) sqlite3_column_text(stmt_query, 29));
1116 buffer_json_member_add_string_or_empty(wb, "class", sqlite3_column_text(stmt_query, 26) ? (const char *) sqlite3_column_text(stmt_query, 26) : (char *) "Unknown");
1117 buffer_json_member_add_string_or_empty(wb, "component", sqlite3_column_text(stmt_query, 27) ? (const char *) sqlite3_column_text(stmt_query, 27) : (char *) "Unknown");
1118 buffer_json_member_add_string_or_empty(wb, "type", sqlite3_column_text(stmt_query, 28) ? (const char *) sqlite3_column_text(stmt_query, 28) : (char *) "Unknown");
1119 buffer_json_member_add_boolean(wb, "processed", (sqlite3_column_int64(stmt_query, 9) & HEALTH_ENTRY_FLAG_PROCESSED));
1120 buffer_json_member_add_boolean(wb, "updated", (sqlite3_column_int64(stmt_query, 9) & HEALTH_ENTRY_FLAG_UPDATED));
1121 buffer_json_member_add_int64(wb, "exec_run", (int64_t)sqlite3_column_int64(stmt_query, 10));
1122 buffer_json_member_add_boolean(wb, "exec_failed", (sqlite3_column_int64(stmt_query, 9) & HEALTH_ENTRY_FLAG_EXEC_FAILED));
1123 buffer_json_member_add_string_or_empty(wb, "exec", sqlite3_column_text(stmt_query, 14) ? (const char *) sqlite3_column_text(stmt_query, 14) : string2str(host->health.default_exec));
1124 buffer_json_member_add_string_or_empty(wb, "recipient", sqlite3_column_text(stmt_query, 15) ? (const char *) sqlite3_column_text(stmt_query, 15) : string2str(host->health.default_recipient));
1125 buffer_json_member_add_int64(wb, "exec_code", sqlite3_column_int(stmt_query, 19));
1126 buffer_json_member_add_string_or_empty(wb, "source", sqlite3_column_text(stmt_query, 16) ? (const char *) sqlite3_column_text(stmt_query, 16) : (char *) "Unknown");
1127 buffer_json_member_add_string_or_empty(wb, "command", edit_command);
1128 buffer_json_member_add_string_or_empty(wb, "units", (const char *) sqlite3_column_text(stmt_query, 17));
1129 buffer_json_member_add_int64(wb, "when", (int64_t)sqlite3_column_int64(stmt_query, 6));
1130 buffer_json_member_add_int64(wb, "duration", (int64_t)sqlite3_column_int64(stmt_query, 7));
1131 buffer_json_member_add_int64(wb, "non_clear_duration", (int64_t)sqlite3_column_int64(stmt_query, 8));
1132 buffer_json_member_add_string_or_empty(wb, "status", rrdcalc_status2string(sqlite3_column_int(stmt_query, 20)));
1133 buffer_json_member_add_string_or_empty(wb, "old_status", rrdcalc_status2string(sqlite3_column_int(stmt_query, 21)));
1134 buffer_json_member_add_int64(wb, "delay", sqlite3_column_int(stmt_query, 22));
1135 buffer_json_member_add_int64(wb, "delay_up_to_timestamp",(int64_t)sqlite3_column_int64(stmt_query, 11));
1136 buffer_json_member_add_int64(wb, "updated_by_id", (unsigned int)sqlite3_column_int64(stmt_query, 4));
1137 buffer_json_member_add_int64(wb, "updates_id", (unsigned int)sqlite3_column_int64(stmt_query, 5));
1138 buffer_json_member_add_string_or_empty(wb, "value_string", sqlite3_column_type(stmt_query, 23) == SQLITE_NULL ? "-" :
1139 format_value_and_unit(new_value_string, 100, sqlite3_column_double(stmt_query, 23), (char *) sqlite3_column_text(stmt_query, 17), -1));
1140 buffer_json_member_add_string_or_empty(wb, "old_value_string", sqlite3_column_type(stmt_query, 24) == SQLITE_NULL ? "-" :
1141 format_value_and_unit(old_value_string, 100, sqlite3_column_double(stmt_query, 24), (char *) sqlite3_column_text(stmt_query, 17), -1));
1142 buffer_json_member_add_int64(wb, "last_repeat", (int64_t)sqlite3_column_int64(stmt_query, 25));
1143 buffer_json_member_add_boolean(wb, "silenced", (sqlite3_column_int64(stmt_query, 9) & HEALTH_ENTRY_FLAG_SILENCED));
1144 buffer_json_member_add_string_or_empty(wb, "summary", (const char *) sqlite3_column_text(stmt_query, 31));
1145 buffer_json_member_add_string_or_empty(wb, "info", (const char *) sqlite3_column_text(stmt_query, 18));
1146 buffer_json_member_add_boolean(wb, "no_clear_notification",(sqlite3_column_int64(stmt_query, 9) & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION));
1147
1148 if (sqlite3_column_type(stmt_query, 23) == SQLITE_NULL)
1149 buffer_json_member_add_string(wb, "value", NULL);
1150 else
1151 buffer_json_member_add_double(wb, "value", sqlite3_column_double(stmt_query, 23));
1152
1153 if (sqlite3_column_type(stmt_query, 24) == SQLITE_NULL)
1154 buffer_json_member_add_string(wb, "old_value", NULL);
1155 else
1156 buffer_json_member_add_double(wb, "old_value", sqlite3_column_double(stmt_query, 24));
1157
1158 freez(edit_command);
1159
1160 buffer_json_object_close(wb);
1161 }
1162
1163 buffer_json_array_close(wb);
1164 buffer_json_finalize(wb);
1165
1166 finish:
1167 SQLITE_FINALIZE(stmt_query);
1168 }
1169
1170 #define SQL_COPY_HEALTH_LOG(table) "INSERT OR IGNORE INTO health_log (host_id, alarm_id, config_hash_id, name, chart, family, exec, recipient, units, chart_context) SELECT ?1, alarm_id, config_hash_id, name, chart, family, exec, recipient, units, chart_context from %s", table
1171 #define SQL_COPY_HEALTH_LOG_DETAIL(table) "INSERT INTO health_log_detail (unique_id, alarm_id, alarm_event_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, transition_id, global_id, host_id) SELECT unique_id, alarm_id, alarm_event_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, transition_id, now_usec(1), ?1 from %s", table
1172 #define SQL_UPDATE_HEALTH_LOG_DETAIL_TRANSITION_ID "update health_log_detail set transition_id = uuid_random() where transition_id is null"
1173 #define SQL_UPDATE_HEALTH_LOG_DETAIL_HEALTH_LOG_ID "update health_log_detail set health_log_id = (select health_log_id from health_log where host_id = ?1 and alarm_id = health_log_detail.alarm_id) where health_log_id is null and host_id = ?2"
1174 #define SQL_UPDATE_HEALTH_LOG_LAST_TRANSITION_ID "update health_log set last_transition_id = (select transition_id from health_log_detail where health_log_id = health_log.health_log_id and alarm_id = health_log.alarm_id group by (alarm_id) having max(alarm_event_id)) where host_id = ?1"
1175 int health_migrate_old_health_log_table(char *table) {
1176 if (!table)
1177 return 0;
1178
1179 //table should contain guid. We need to
1180 //keep it in the new table along with it's data
1181 //health_log_XXXXXXXX_XXXX_XXXX_XXXX_XXXXXXXXXXXX
1182 if (strnlen(table, 46) != 46) {
1183 return 0;
1184 }
1185
1186 char *uuid_from_table = strdupz(table + 11);
1187 nd_uuid_t uuid;
1188 if (uuid_parse_fix(uuid_from_table, uuid)) {
1189 freez(uuid_from_table);
1190 return 0;
1191 }
1192
1193 int rc;
1194 char command[MAX_HEALTH_SQL_SIZE + 1];
1195 sqlite3_stmt *res = NULL;
1196 snprintfz(command, sizeof(command) - 1, SQL_COPY_HEALTH_LOG(table));
1197 rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
1198 if (unlikely(rc != SQLITE_OK)) {
1199 error_report("Failed to prepare statement to copy health log, rc = %d", rc);
1200 freez(uuid_from_table);
1201 return 0;
1202 }
1203
1204 rc = sqlite3_bind_blob(res, 1, &uuid, sizeof(uuid), SQLITE_STATIC);
1205 if (unlikely(rc != SQLITE_OK)) {
1206 SQLITE_FINALIZE(res);
1207 freez(uuid_from_table);
1208 return 0;
1209 }
1210
1211 rc = execute_insert(res);
1212 if (unlikely(rc != SQLITE_DONE)) {
1213 error_report("Failed to execute SQL_COPY_HEALTH_LOG, rc = %d", rc);
1214 SQLITE_FINALIZE(res);
1215 freez(uuid_from_table);
1216 }
1217
1218 //detail
1219 snprintfz(command, sizeof(command) - 1, SQL_COPY_HEALTH_LOG_DETAIL(table));
1220 rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
1221 if (unlikely(rc != SQLITE_OK)) {
1222 error_report("Failed to prepare statement to copy health log detail, rc = %d", rc);
1223 return 0;
1224 }
1225
1226 rc = sqlite3_bind_blob(res, 1, &uuid, sizeof(uuid), SQLITE_STATIC);
1227 if (unlikely(rc != SQLITE_OK)) {
1228 SQLITE_FINALIZE(res);
1229 return 0;
1230 }
1231
1232 rc = execute_insert(res);
1233 if (unlikely(rc != SQLITE_DONE)) {
1234 error_report("Failed to execute SQL_COPY_HEALTH_LOG_DETAIL, rc = %d", rc);
1235 SQLITE_FINALIZE(res);
1236 return 0;
1237 }
1238
1239 //update transition ids
1240 rc = sqlite3_prepare_v2(db_meta, SQL_UPDATE_HEALTH_LOG_DETAIL_TRANSITION_ID, -1, &res, 0);
1241 if (unlikely(rc != SQLITE_OK)) {
1242 error_report("Failed to prepare statement to update health log detail with transition ids, rc = %d", rc);
1243 return 0;
1244 }
1245
1246 rc = execute_insert(res);
1247 if (unlikely(rc != SQLITE_DONE)) {
1248 error_report("Failed to execute SQL_UPDATE_HEALTH_LOG_DETAIL_TRANSITION_ID, rc = %d", rc);
1249 SQLITE_FINALIZE(res);
1250 return 0;
1251 }
1252
1253 //update health_log_id
1254 rc = sqlite3_prepare_v2(db_meta, SQL_UPDATE_HEALTH_LOG_DETAIL_HEALTH_LOG_ID, -1, &res, 0);
1255 if (unlikely(rc != SQLITE_OK)) {
1256 error_report("Failed to prepare statement to update health log detail with health log ids, rc = %d", rc);
1257 return 0;
1258 }
1259
1260 rc = sqlite3_bind_blob(res, 1, &uuid, sizeof(uuid), SQLITE_STATIC);
1261 if (unlikely(rc != SQLITE_OK)) {
1262 SQLITE_FINALIZE(res);
1263 return 0;
1264 }
1265
1266 rc = sqlite3_bind_blob(res, 2, &uuid, sizeof(uuid), SQLITE_STATIC);
1267 if (unlikely(rc != SQLITE_OK)) {
1268 SQLITE_FINALIZE(res);
1269 return 0;
1270 }
1271
1272 rc = execute_insert(res);
1273 if (unlikely(rc != SQLITE_DONE)) {
1274 error_report("Failed to execute SQL_UPDATE_HEALTH_LOG_DETAIL_HEALTH_LOG_ID, rc = %d", rc);
1275 SQLITE_FINALIZE(res);
1276 }
1277
1278 //update last transition id
1279 rc = sqlite3_prepare_v2(db_meta, SQL_UPDATE_HEALTH_LOG_LAST_TRANSITION_ID, -1, &res, 0);
1280 if (unlikely(rc != SQLITE_OK)) {
1281 error_report("Failed to prepare statement to update health log with last transition id, rc = %d", rc);
1282 return 0;
1283 }
1284
1285 rc = sqlite3_bind_blob(res, 1, &uuid, sizeof(uuid), SQLITE_STATIC);
1286 if (unlikely(rc != SQLITE_OK)) {
1287 SQLITE_FINALIZE(res);
1288 return 0;
1289 }
1290
1291 rc = execute_insert(res);
1292 if (unlikely(rc != SQLITE_DONE)) {
1293 error_report("Failed to execute SQL_UPDATE_HEALTH_LOG_LAST_TRANSITION_ID, rc = %d", rc);
1294 SQLITE_FINALIZE(res);
1295 }
1296
1297 return 1;
1298 }
1299
1300 #define SQL_GET_EVENT_ID \
1301 "SELECT MAX(alarm_event_id)+1 FROM health_log_detail WHERE health_log_id = @health_log_id AND alarm_id = @alarm_id"
1302
1303 static uint32_t get_next_alarm_event_id(uint64_t health_log_id, uint32_t alarm_id)
1304 {
1305 int rc;
1306 sqlite3_stmt *res = NULL;
1307 uint32_t next_event_id = alarm_id;
1308
1309 rc = sqlite3_prepare_v2(db_meta, SQL_GET_EVENT_ID, -1, &res, 0);
1310 if (rc != SQLITE_OK) {
1311 error_report("Failed to prepare statement when trying to get an event id");
1312 return alarm_id;
1313 }
1314
1315 int param = 0;
1316 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) health_log_id));
1317 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64) alarm_id));
1318
1319 param = 0;
1320 while (sqlite3_step_monitored(res) == SQLITE_ROW)
1321 next_event_id = (uint32_t)sqlite3_column_int64(res, 0);
1322
1323 done:
1324 REPORT_BIND_FAIL(res, param);
1325 SQLITE_FINALIZE(res);
1326 return next_event_id;
1327 }
1328
1329 #define SQL_GET_ALARM_ID \
1330 "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart AND name = @name"
1331
1332 uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id)
1333 {
1334 sqlite3_stmt *res = NULL;
1335 uint32_t alarm_id = 0;
1336 uint64_t health_log_id = 0;
1337
1338 if (!PREPARE_STATEMENT(db_meta, SQL_GET_ALARM_ID, &res))
1339 return alarm_id;
1340
1341 int param = 0;
1342 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &host->host_id.uuid, sizeof(host->host_id.uuid), SQLITE_STATIC));
1343 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, chart));
1344 SQLITE_BIND_FAIL(done, SQLITE3_BIND_STRING_OR_NULL(res, ++param, name));
1345
1346 param = 0;
1347 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
1348 alarm_id = (uint32_t)sqlite3_column_int64(res, 0);
1349 health_log_id = (uint64_t)sqlite3_column_int64(res, 1);
1350 }
1351
1352 if (alarm_id)
1353 *next_event_id = get_next_alarm_event_id(health_log_id, alarm_id);
1354 done:
1355 REPORT_BIND_FAIL(res, param);
1356 SQLITE_FINALIZE(res);
1357 return alarm_id;
1358 }
1359
1360 #define SQL_GET_ALARM_ID_FROM_TRANSITION_ID \
1361 "SELECT hld.alarm_id, hl.host_id, hl.chart_context FROM health_log_detail hld, health_log hl " \
1362 "WHERE hld.transition_id = @transition_id " \
1363 "AND hld.health_log_id = hl.health_log_id"
1364
1365 bool sql_find_alert_transition(
1366 const char *transition,
1367 void (*cb)(const char *machine_guid, const char *context, time_t alert_id, void *data),
1368 void *data)
1369 {
1370 sqlite3_stmt *res = NULL;
1371
1372 char machine_guid[UUID_STR_LEN];
1373
1374 nd_uuid_t transition_uuid;
1375 if (uuid_parse(transition, transition_uuid))
1376 return false;
1377
1378 if (!PREPARE_STATEMENT(db_meta, SQL_GET_ALARM_ID_FROM_TRANSITION_ID, &res))
1379 return false;
1380
1381 bool ok = false;
1382
1383 int param = 0;
1384 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &transition_uuid, sizeof(transition_uuid), SQLITE_STATIC));
1385
1386 param = 0;
1387 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
1388 if (unlikely(!sqlite3_column_uuid_unparse_lower(res, 1, machine_guid))) {
1389 error_report("HEALTH: Got invalid machine guid while looking up alert transition. Ignoring it.");
1390 continue;
1391 }
1392
1393 cb(machine_guid, (const char *) sqlite3_column_text(res, 2), sqlite3_column_int(res, 0), data);
1394 ok = true;
1395 }
1396
1397 done:
1398 REPORT_BIND_FAIL(res, param);
1399 SQLITE_FINALIZE(res);
1400 return ok;
1401 }
1402
1403 #define SQL_BUILD_ALERT_TRANSITION "CREATE TEMP TABLE IF NOT EXISTS v_%p (host_id blob)"
1404
1405 #define SQL_POPULATE_TEMP_ALERT_TRANSITION_TABLE "INSERT INTO v_%p (host_id) VALUES (@host_id)"
1406
1407 #define SQL_SEARCH_ALERT_TRANSITION_SELECT \
1408 "SELECT h.host_id, h.alarm_id, h.config_hash_id, h.name, h.chart, h.chart_name, h.family, h.recipient, h.units, h.exec, " \
1409 "h.chart_context, d.when_key, d.duration, d.non_clear_duration, d.flags, d.delay_up_to_timestamp, " \
1410 "d.info, d.exec_code, d.new_status, d.old_status, d.delay, d.new_value, d.old_value, d.last_repeat, " \
1411 "d.transition_id, d.global_id, ah.class, ah.type, ah.component, d.exec_run_timestamp, d.summary"
1412
1413 #define SQL_SEARCH_ALERT_TRANSITION_COMMON_WHERE "h.config_hash_id = ah.hash_id AND h.health_log_id = d.health_log_id"
1414
1415 #define SQL_SEARCH_ALERT_TRANSITION \
1416 SQL_SEARCH_ALERT_TRANSITION_SELECT \
1417 " FROM health_log h, health_log_detail d, v_%p t, alert_hash ah " \
1418 " WHERE h.host_id = t.host_id AND " SQL_SEARCH_ALERT_TRANSITION_COMMON_WHERE \
1419 " AND ( d.new_status > 2 OR d.old_status > 2 ) AND d.global_id BETWEEN @after AND @before "
1420
1421 #define SQL_SEARCH_ALERT_TRANSITION_DIRECT \
1422 SQL_SEARCH_ALERT_TRANSITION_SELECT " FROM health_log h, health_log_detail d, alert_hash ah " \
1423 " WHERE " SQL_SEARCH_ALERT_TRANSITION_COMMON_WHERE \
1424 " AND transition_id = @transition "
1425
1426 void sql_alert_transitions(
1427 DICTIONARY *nodes,
1428 time_t after,
1429 time_t before,
1430 const char *context,
1431 const char *alert_name,
1432 const char *transition,
1433 void (*cb)(struct sql_alert_transition_data *, void *),
1434 void *data,
1435 bool debug __maybe_unused)
1436 {
1437 nd_uuid_t transition_uuid;
1438 char sql[512];
1439 int rc;
1440 sqlite3_stmt *res = NULL;
1441 BUFFER *command = NULL;
1442
1443 if (unlikely(!nodes))
1444 return;
1445
1446 int param = 0;
1447 if (transition) {
1448 if (uuid_parse(transition, transition_uuid)) {
1449 error_report("Invalid transition given %s", transition);
1450 return;
1451 }
1452
1453 if (!PREPARE_STATEMENT(db_meta, SQL_SEARCH_ALERT_TRANSITION_DIRECT, &res))
1454 goto done_only_drop;
1455
1456 SQLITE_BIND_FAIL(done, sqlite3_bind_blob(res, ++param, &transition_uuid, sizeof(transition_uuid), SQLITE_STATIC));
1457 goto run_query;
1458 }
1459
1460 snprintfz(sql, sizeof(sql) - 1, SQL_BUILD_ALERT_TRANSITION, nodes);
1461 rc = db_execute(db_meta, sql, NULL);
1462 if (rc)
1463 return;
1464
1465 snprintfz(sql, sizeof(sql) - 1, SQL_POPULATE_TEMP_ALERT_TRANSITION_TABLE, nodes);
1466
1467 // Prepare statement to add things
1468 rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0);
1469 if (unlikely(rc != SQLITE_OK)) {
1470 error_report("Failed to prepare statement to INSERT into v_%p", nodes);
1471 goto done_only_drop;
1472 }
1473
1474 void *t;
1475 dfe_start_read(nodes, t) {
1476 nd_uuid_t host_uuid;
1477 uuid_parse( t_dfe.name, host_uuid);
1478
1479 rc = sqlite3_bind_blob(res, 1, &host_uuid, sizeof(host_uuid), SQLITE_STATIC);
1480 if (unlikely(rc != SQLITE_OK))
1481 error_report("Failed to bind host_id parameter.");
1482
1483 rc = sqlite3_step_monitored(res);
1484 if (rc != SQLITE_DONE)
1485 error_report("Error while populating temp table");
1486
1487 SQLITE_RESET(res);
1488 }
1489 dfe_done(t);
1490
1491 SQLITE_FINALIZE(res);
1492
1493 command = buffer_create(MAX_HEALTH_SQL_SIZE, NULL);
1494
1495 buffer_sprintf(command, SQL_SEARCH_ALERT_TRANSITION, nodes);
1496
1497 if (context)
1498 buffer_sprintf(command, " AND h.chart_context = @context");
1499
1500 if (alert_name)
1501 buffer_sprintf(command, " AND h.name = @alert_name");
1502
1503 buffer_strcat(command, " ORDER BY d.global_id DESC");
1504
1505 rc = sqlite3_prepare_v2(db_meta, buffer_tostring(command), -1, &res, 0);
1506 if (unlikely(rc != SQLITE_OK)) {
1507 error_report("Failed to prepare statement sql_alert_transitions");
1508 goto done_only_drop;
1509 }
1510
1511 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)(after * USEC_PER_SEC)));
1512 SQLITE_BIND_FAIL(done, sqlite3_bind_int64(res, ++param, (sqlite3_int64)(before * USEC_PER_SEC)));
1513
1514 if (context)
1515 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, context, -1, SQLITE_STATIC));
1516
1517 if (alert_name)
1518 SQLITE_BIND_FAIL(done, sqlite3_bind_text(res, ++param, alert_name, -1, SQLITE_STATIC));
1519
1520 run_query:;
1521
1522 struct sql_alert_transition_data atd = {0 };
1523 nd_uuid_t host_id;
1524 nd_uuid_t config_hash_id;
1525 nd_uuid_t transition_id;
1526 size_t invalid_host_ids = 0;
1527 size_t invalid_config_hash_ids = 0;
1528 size_t invalid_transition_ids = 0;
1529
1530 param = 0;
1531 while (sqlite3_step(res) == SQLITE_ROW) {
1532 if (unlikely(!sqlite3_column_uuid_copy(res, 0, host_id))) {
1533 invalid_host_ids++;
1534 continue;
1535 }
1536
1537 atd.host_id = &host_id;
1538 atd.alarm_id = sqlite3_column_int64(res, 1);
1539 if (unlikely(!sqlite3_column_uuid_copy(res, 2, config_hash_id))) {
1540 invalid_config_hash_ids++;
1541 continue;
1542 }
1543
1544 atd.config_hash_id = &config_hash_id;
1545 atd.alert_name = (const char *) sqlite3_column_text(res, 3);
1546 atd.chart = (const char *) sqlite3_column_text(res, 4);
1547 atd.chart_name = (const char *) sqlite3_column_text(res, 5);
1548 atd.family = (const char *) sqlite3_column_text(res, 6);
1549 atd.recipient = (const char *) sqlite3_column_text(res, 7);
1550 atd.units = (const char *) sqlite3_column_text(res, 8);
1551 atd.exec = (const char *) sqlite3_column_text(res, 9);
1552 atd.chart_context = (const char *) sqlite3_column_text(res, 10);
1553 atd.when_key = sqlite3_column_int64(res, 11);
1554 atd.duration = sqlite3_column_int64(res, 12);
1555 atd.non_clear_duration = sqlite3_column_int64(res, 13);
1556 atd.flags = sqlite3_column_int64(res, 14);
1557 atd.delay_up_to_timestamp = sqlite3_column_int64(res, 15);
1558 atd.info = (const char *) sqlite3_column_text(res, 16);
1559 atd.exec_code = sqlite3_column_int(res, 17);
1560 atd.new_status = sqlite3_column_int(res, 18);
1561 atd.old_status = sqlite3_column_int(res, 19);
1562 atd.delay = (int) sqlite3_column_int(res, 20);
1563 atd.new_value = (NETDATA_DOUBLE) sqlite3_column_double(res, 21);
1564 atd.old_value = (NETDATA_DOUBLE) sqlite3_column_double(res, 22);
1565 atd.last_repeat = sqlite3_column_int64(res, 23);
1566 if (unlikely(!sqlite3_column_uuid_copy(res, 24, transition_id))) {
1567 invalid_transition_ids++;
1568 continue;
1569 }
1570
1571 atd.transition_id = &transition_id;
1572 atd.global_id = sqlite3_column_int64(res, 25);
1573 atd.classification = (const char *) sqlite3_column_text(res, 26);
1574 atd.type = (const char *) sqlite3_column_text(res, 27);
1575 atd.component = (const char *) sqlite3_column_text(res, 28);
1576 atd.exec_run_timestamp = sqlite3_column_int64(res, 29);
1577 atd.summary = (const char *) sqlite3_column_text(res, 30);
1578
1579 cb(&atd, data);
1580 }
1581
1582 if (unlikely(invalid_host_ids || invalid_config_hash_ids || invalid_transition_ids)) {
1583 error_report("HEALTH: Ignored invalid alert transition rows (host_id=%zu, config_hash_id=%zu, transition_id=%zu).",
1584 invalid_host_ids, invalid_config_hash_ids, invalid_transition_ids);
1585 }
1586
1587 done:
1588 REPORT_BIND_FAIL(res, param);
1589 SQLITE_FINALIZE(res);
1590
1591 done_only_drop:
1592 if (likely(!transition)) {
1593 (void)snprintfz(sql, sizeof(sql) - 1, "DROP TABLE IF EXISTS v_%p", nodes);
1594 (void)db_execute(db_meta, sql, NULL);
1595 buffer_free(command);
1596 }
1597 }
1598
1599 #define SQL_BUILD_CONFIG_TARGET_LIST "CREATE TEMP TABLE IF NOT EXISTS c_%p (hash_id blob)"
1600
1601 #define SQL_POPULATE_TEMP_CONFIG_TARGET_TABLE "INSERT INTO c_%p (hash_id) VALUES (@hash_id)"
1602
1603 #define SQL_SEARCH_CONFIG_LIST \
1604 "SELECT ah.hash_id, alarm, template, on_key, class, component, type, lookup, every, " \
1605 " units, calc, families, green, red, warn, crit, " \
1606 " exec, to_key, info, delay, options, repeat, host_labels, p_db_lookup_dimensions, p_db_lookup_method, " \
1607 " p_db_lookup_options, p_db_lookup_after, p_db_lookup_before, p_update_every, source, chart_labels, summary, " \
1608 " time_group_condition, time_group_value, dims_group, data_source " \
1609 " FROM alert_hash ah, c_%p t where ah.hash_id = t.hash_id"
1610
1611 int sql_get_alert_configuration(
1612 DICTIONARY *configs,
1613 void (*cb)(struct sql_alert_config_data *, void *),
1614 void *data,
1615 bool debug __maybe_unused)
1616 {
1617 int added = -1;
1618 char sql[512];
1619 int rc;
1620 sqlite3_stmt *res = NULL;
1621 BUFFER *command = NULL;
1622
1623 if (unlikely(!configs))
1624 return added;
1625
1626 snprintfz(sql, sizeof(sql) - 1, SQL_BUILD_CONFIG_TARGET_LIST, configs);
1627 rc = db_execute(db_meta, sql, NULL);
1628 if (rc)
1629 return added;
1630
1631 snprintfz(sql, sizeof(sql) - 1, SQL_POPULATE_TEMP_CONFIG_TARGET_TABLE, configs);
1632
1633 // Prepare statement to add things
1634 rc = sqlite3_prepare_v2(db_meta, sql, -1, &res, 0);
1635 if (unlikely(rc != SQLITE_OK)) {
1636 error_report("Failed to prepare statement to INSERT into c_%p", configs);
1637 goto fail_only_drop;
1638 }
1639
1640 void *t;
1641 dfe_start_read(configs, t) {
1642 nd_uuid_t hash_id;
1643 uuid_parse( t_dfe.name, hash_id);
1644
1645 rc = sqlite3_bind_blob(res, 1, &hash_id, sizeof(hash_id), SQLITE_STATIC);
1646 if (unlikely(rc != SQLITE_OK))
1647 error_report("Failed to bind host_id parameter.");
1648
1649 rc = sqlite3_step_monitored(res);
1650 if (rc != SQLITE_DONE)
1651 error_report("Error while populating temp table");
1652
1653 SQLITE_RESET(res);
1654 }
1655 dfe_done(t);
1656
1657 SQLITE_FINALIZE(res);
1658
1659 command = buffer_create(MAX_HEALTH_SQL_SIZE, NULL);
1660
1661 buffer_sprintf(command, SQL_SEARCH_CONFIG_LIST, configs);
1662
1663 rc = sqlite3_prepare_v2(db_meta, buffer_tostring(command), -1, &res, 0);
1664 if (unlikely(rc != SQLITE_OK)) {
1665 error_report("Failed to prepare statement sql_get_alert_configuration");
1666 goto fail_only_drop;
1667 }
1668
1669 struct sql_alert_config_data acd = {0 };
1670 nd_uuid_t config_hash_id;
1671 size_t invalid_config_hash_ids = 0;
1672
1673 added = 0;
1674 int param;
1675 while (sqlite3_step(res) == SQLITE_ROW) {
1676 param = 0;
1677 if (unlikely(!sqlite3_column_uuid_copy(res, param++, config_hash_id))) {
1678 invalid_config_hash_ids++;
1679 continue;
1680 }
1681
1682 acd.config_hash_id = &config_hash_id;
1683 acd.name = (const char *) sqlite3_column_text(res, param++);
1684 acd.selectors.on_template = (const char *) sqlite3_column_text(res, param++);
1685 acd.selectors.on_key = (const char *) sqlite3_column_text(res, param++);
1686 acd.classification = (const char *) sqlite3_column_text(res, param++);
1687 acd.component = (const char *) sqlite3_column_text(res, param++);
1688 acd.type = (const char *) sqlite3_column_text(res, param++);
1689 acd.value.db.lookup = (const char *) sqlite3_column_text(res, param++);
1690 acd.value.every = (const char *) sqlite3_column_text(res, param++);
1691 acd.value.units = (const char *) sqlite3_column_text(res, param++);
1692 acd.value.calc = (const char *) sqlite3_column_text(res, param++);
1693 acd.selectors.families = (const char *) sqlite3_column_text(res, param++);
1694 acd.status.green = (const char *) sqlite3_column_text(res, param++);
1695 acd.status.red = (const char *) sqlite3_column_text(res, param++);
1696 acd.status.warn = (const char *) sqlite3_column_text(res, param++);
1697 acd.status.crit = (const char *) sqlite3_column_text(res, param++);
1698 acd.notification.exec = (const char *) sqlite3_column_text(res, param++);
1699 acd.notification.to_key = (const char *) sqlite3_column_text(res, param++);
1700 acd.info = (const char *) sqlite3_column_text(res, param++);
1701 acd.notification.delay = (const char *) sqlite3_column_text(res, param++);
1702 acd.notification.options = (const char *) sqlite3_column_text(res, param++);
1703 acd.notification.repeat = (const char *) sqlite3_column_text(res, param++);
1704 acd.selectors.host_labels = (const char *) sqlite3_column_text(res, param++);
1705 acd.value.db.dimensions = (const char *) sqlite3_column_text(res, param++);
1706 acd.value.db.method = (const char *) sqlite3_column_text(res, param++);
1707 acd.value.db.options = (uint32_t) sqlite3_column_int(res, param++);
1708 acd.value.db.after = (int32_t) sqlite3_column_int(res, param++);
1709 acd.value.db.before = (int32_t) sqlite3_column_int(res, param++);
1710 acd.value.update_every = (int32_t) sqlite3_column_int(res, param++);
1711 acd.source = (const char *) sqlite3_column_text(res, param++);
1712 acd.selectors.chart_labels = (const char *) sqlite3_column_text(res, param++);
1713 acd.summary = (const char *) sqlite3_column_text(res, param++);
1714 acd.value.db.time_group_condition =(int32_t) sqlite3_column_int(res, param++);
1715 acd.value.db.time_group_value = sqlite3_column_double(res, param++);
1716 acd.value.db.dims_group = (int32_t) sqlite3_column_int(res, param++);
1717 acd.value.db.data_source = (int32_t) sqlite3_column_int(res, param++);
1718
1719 cb(&acd, data);
1720 added++;
1721 }
1722
1723 if (unlikely(invalid_config_hash_ids)) {
1724 error_report("HEALTH: Ignored %zu alert configuration rows with invalid config_hash_id.",
1725 invalid_config_hash_ids);
1726 }
1727
1728 SQLITE_FINALIZE(res);
1729
1730 fail_only_drop:
1731 (void)snprintfz(sql, sizeof(sql) - 1, "DROP TABLE IF EXISTS c_%p", configs);
1732 (void)db_execute(db_meta, sql, NULL);
1733 buffer_free(command);
1734 return added;
1735 }