1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "sqlite_health.h"
4
+#include "sqlite_functions.h"
5
+
6
+#define MAX_HEALTH_SQL_SIZE 2048
7
+
8
+/* Health related SQL queries
9
+ Creates a health log table in sqlite, one per host guid
10
+*/
11
+#define SQL_CREATE_HEALTH_LOG_TABLE(guid) "CREATE TABLE IF NOT EXISTS health_log_%s(hostname text, unique_id int, alarm_id int, alarm_event_id int, config_hash_id blob, updated_by_id int, updates_id int, when_key int, duration int, non_clear_duration int, flags int, exec_run_timestamp int, delay_up_to_timestamp int, name text, chart text, family text, exec text, recipient text, source text, units text, info text, exec_code int, new_status real, old_status real, delay int, new_value double, old_value double, last_repeat int, class text, component text, type text);", guid
12
+int sql_create_health_log_table(RRDHOST *host) {
13
+ int rc;
14
+ char *err_msg = NULL, command[MAX_HEALTH_SQL_SIZE + 1];
15
+
16
+ if (unlikely(!db_meta)) {
17
+ if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
18
+ error_report("HEALTH [%s]: Database has not been initialized", host->hostname);
19
+ return 1;
20
+ }
21
+
22
+ char uuid_str[GUID_LEN + 1];
23
+ uuid_unparse_lower_fix(&host->host_uuid, uuid_str);
24
+
25
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_CREATE_HEALTH_LOG_TABLE(uuid_str));
26
+
27
+ rc = sqlite3_exec(db_meta, command, 0, 0, &err_msg);
28
+ if (rc != SQLITE_OK) {
29
+ error_report("HEALTH [%s]: SQLite error during creation of health log table, rc = %d (%s)", host->hostname, rc, err_msg);
30
+ sqlite3_free(err_msg);
31
+ return 1;
32
+ }
33
+
34
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, "CREATE INDEX IF NOT EXISTS "
35
+ "health_log_index_%s ON health_log_%s (unique_id); ", uuid_str, uuid_str);
36
+ db_execute(command);
37
+
38
+ return 0;
39
+}
40
+
41
+/* Health related SQL queries
42
+ Updates an entry in the table
43
+*/
44
+#define SQL_UPDATE_HEALTH_LOG(guid) "UPDATE health_log_%s set updated_by_id = ?, flags = ?, exec_run_timestamp = ?, exec_code = ? where unique_id = ?;", guid
45
+void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae) {
46
+ sqlite3_stmt *res = NULL;
47
+ int rc;
48
+ char command[MAX_HEALTH_SQL_SIZE + 1];
49
+
50
+ if (unlikely(!db_meta)) {
51
+ if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
52
+ error_report("HEALTH [%s]: Database has not been initialized", host->hostname);
53
+ return;
54
+ }
55
+
56
+ char uuid_str[GUID_LEN + 1];
57
+ uuid_unparse_lower_fix(&host->host_uuid, uuid_str);
58
+
59
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_UPDATE_HEALTH_LOG(uuid_str));
60
+
61
+ rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
62
+ if (unlikely(rc != SQLITE_OK)) {
63
+ error_report("HEALTH [%s]: Failed to prepare statement for SQL_UPDATE_HEALTH_LOG", host->hostname);
64
+ return;
65
+ }
66
+
67
+ rc = sqlite3_bind_int64(res, 1, (sqlite3_int64) ae->updated_by_id);
68
+ if (unlikely(rc != SQLITE_OK)) {
69
+ error_report("Failed to bind updated_by_id parameter for SQL_UPDATE_HEALTH_LOG");
70
+ goto failed;
71
+ }
72
+
73
+ rc = sqlite3_bind_int64(res, 2, (sqlite3_int64) ae->flags);
74
+ if (unlikely(rc != SQLITE_OK)) {
75
+ error_report("Failed to bind flags parameter for SQL_UPDATE_HEALTH_LOG");
76
+ goto failed;
77
+ }
78
+
79
+ rc = sqlite3_bind_int64(res, 3, (sqlite3_int64) ae->exec_run_timestamp);
80
+ if (unlikely(rc != SQLITE_OK)) {
81
+ error_report("Failed to bind exec_run_timestamp parameter for SQL_UPDATE_HEALTH_LOG");
82
+ goto failed;
83
+ }
84
+
85
+ rc = sqlite3_bind_int(res, 4, ae->exec_code);
86
+ if (unlikely(rc != SQLITE_OK)) {
87
+ error_report("Failed to bind exec_code parameter for SQL_UPDATE_HEALTH_LOG");
88
+ goto failed;
89
+ }
90
+
91
+ rc = sqlite3_bind_int64(res, 5, (sqlite3_int64) ae->unique_id);
92
+ if (unlikely(rc != SQLITE_OK)) {
93
+ error_report("Failed to bind unique_id parameter for SQL_UPDATE_HEALTH_LOG");
94
+ goto failed;
95
+ }
96
+
97
+ rc = execute_insert(res);
98
+ if (unlikely(rc != SQLITE_DONE)) {
99
+ error_report("HEALTH [%s]: Failed to update health log, rc = %d", host->hostname, rc);
100
+ }
101
+
102
+ failed:
103
+ if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
104
+ error_report("HEALTH [%s]: Failed to finalize the prepared statement for updating health log.", host->hostname);
105
+
106
+ return;
107
+}
108
+
109
+/* Health related SQL queries
110
+ Inserts an entry in the table
111
+*/
112
+#define SQL_INSERT_HEALTH_LOG(guid) "INSERT INTO health_log_%s(hostname, unique_id, alarm_id, alarm_event_id, config_hash_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, name, chart, family, exec, recipient, source, units, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, class, component, type) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", guid
113
+void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae) {
114
+ sqlite3_stmt *res = NULL;
115
+ int rc;
116
+ char command[MAX_HEALTH_SQL_SIZE + 1];
117
+
118
+ if (unlikely(!db_meta)) {
119
+ if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
120
+ error_report("HEALTH [%s]: Database has not been initialized", host->hostname);
121
+ return;
122
+ }
123
+
124
+ char uuid_str[GUID_LEN + 1];
125
+ uuid_unparse_lower_fix(&host->host_uuid, uuid_str);
126
+
127
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_INSERT_HEALTH_LOG(uuid_str));
128
+
129
+ rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
130
+ if (unlikely(rc != SQLITE_OK)) {
131
+ error_report("HEALTH [%s]: Failed to prepare statement for SQL_INSERT_HEALTH_LOG", host->hostname);
132
+ return;
133
+ }
134
+
135
+ rc = sqlite3_bind_text(res, 1, host->hostname, -1, SQLITE_STATIC);
136
+ if (unlikely(rc != SQLITE_OK)) {
137
+ error_report("Failed to bind hostname parameter for SQL_INSERT_HEALTH_LOG");
138
+ goto failed;
139
+ }
140
+
141
+ rc = sqlite3_bind_int64(res, 2, (sqlite3_int64) ae->unique_id);
142
+ if (unlikely(rc != SQLITE_OK)) {
143
+ error_report("Failed to bind unique_id parameter for SQL_INSERT_HEALTH_LOG");
144
+ goto failed;
145
+ }
146
+
147
+ rc = sqlite3_bind_int64(res, 3, (sqlite3_int64) ae->alarm_id);
148
+ if (unlikely(rc != SQLITE_OK)) {
149
+ error_report("Failed to bind alarm_id parameter for SQL_INSERT_HEALTH_LOG");
150
+ goto failed;
151
+ }
152
+
153
+ rc = sqlite3_bind_int64(res, 4, (sqlite3_int64) ae->alarm_event_id);
154
+ if (unlikely(rc != SQLITE_OK)) {
155
+ error_report("Failed to bind alarm_event_id parameter for SQL_INSERT_HEALTH_LOG");
156
+ goto failed;
157
+ }
158
+
159
+ rc = sqlite3_bind_blob(res, 5, &ae->config_hash_id, sizeof(ae->config_hash_id), SQLITE_STATIC);
160
+ if (unlikely(rc != SQLITE_OK)) {
161
+ error_report("Failed to bind config_hash_id parameter for SQL_INSERT_HEALTH_LOG");
162
+ goto failed;
163
+ }
164
+
165
+ rc = sqlite3_bind_int64(res, 6, (sqlite3_int64) ae->updated_by_id);
166
+ if (unlikely(rc != SQLITE_OK)) {
167
+ error_report("Failed to bind updated_by_id parameter for SQL_INSERT_HEALTH_LOG");
168
+ goto failed;
169
+ }
170
+
171
+ rc = sqlite3_bind_int64(res, 7, (sqlite3_int64) ae->updates_id);
172
+ if (unlikely(rc != SQLITE_OK)) {
173
+ error_report("Failed to bind updates_id parameter for SQL_INSERT_HEALTH_LOG");
174
+ goto failed;
175
+ }
176
+
177
+ rc = sqlite3_bind_int64(res, 8, (sqlite3_int64) ae->when);
178
+ if (unlikely(rc != SQLITE_OK)) {
179
+ error_report("Failed to bind when parameter for SQL_INSERT_HEALTH_LOG");
180
+ goto failed;
181
+ }
182
+
183
+ rc = sqlite3_bind_int64(res, 9, (sqlite3_int64) ae->duration);
184
+ if (unlikely(rc != SQLITE_OK)) {
185
+ error_report("Failed to bind duration parameter for SQL_INSERT_HEALTH_LOG");
186
+ goto failed;
187
+ }
188
+
189
+ rc = sqlite3_bind_int64(res, 10, (sqlite3_int64) ae->non_clear_duration);
190
+ if (unlikely(rc != SQLITE_OK)) {
191
+ error_report("Failed to bind non_clear_duration parameter for SQL_INSERT_HEALTH_LOG");
192
+ goto failed;
193
+ }
194
+
195
+ rc = sqlite3_bind_int64(res, 11, (sqlite3_int64) ae->flags);
196
+ if (unlikely(rc != SQLITE_OK)) {
197
+ error_report("Failed to bind flags parameter for SQL_INSERT_HEALTH_LOG");
198
+ goto failed;
199
+ }
200
+
201
+ rc = sqlite3_bind_int64(res, 12, (sqlite3_int64) ae->exec_run_timestamp);
202
+ if (unlikely(rc != SQLITE_OK)) {
203
+ error_report("Failed to bind exec_run_timestamp parameter for SQL_INSERT_HEALTH_LOG");
204
+ goto failed;
205
+ }
206
+
207
+ rc = sqlite3_bind_int64(res, 13, (sqlite3_int64) ae->delay_up_to_timestamp);
208
+ if (unlikely(rc != SQLITE_OK)) {
209
+ error_report("Failed to bind delay_up_to_timestamp parameter for SQL_INSERT_HEALTH_LOG");
210
+ goto failed;
211
+ }
212
+
213
+ rc = sqlite3_bind_text(res, 14, ae->name, -1, SQLITE_STATIC);
214
+ if (unlikely(rc != SQLITE_OK)) {
215
+ error_report("Failed to bind name parameter for SQL_INSERT_HEALTH_LOG");
216
+ goto failed;
217
+ }
218
+
219
+ rc = sqlite3_bind_text(res, 15, ae->chart, -1, SQLITE_STATIC);
220
+ if (unlikely(rc != SQLITE_OK)) {
221
+ error_report("Failed to bind chart parameter for SQL_INSERT_HEALTH_LOG");
222
+ goto failed;
223
+ }
224
+
225
+ rc = sqlite3_bind_text(res, 16, ae->family, -1, SQLITE_STATIC);
226
+ if (unlikely(rc != SQLITE_OK)) {
227
+ error_report("Failed to bind family parameter for SQL_INSERT_HEALTH_LOG");
228
+ goto failed;
229
+ }
230
+
231
+ rc = sqlite3_bind_text(res, 17, ae->exec, -1, SQLITE_STATIC);
232
+ if (unlikely(rc != SQLITE_OK)) {
233
+ error_report("Failed to bind exec parameter for SQL_INSERT_HEALTH_LOG");
234
+ goto failed;
235
+ }
236
+
237
+ rc = sqlite3_bind_text(res, 18, ae->recipient, -1, SQLITE_STATIC);
238
+ if (unlikely(rc != SQLITE_OK)) {
239
+ error_report("Failed to bind recipient parameter for SQL_INSERT_HEALTH_LOG");
240
+ goto failed;
241
+ }
242
+
243
+ rc = sqlite3_bind_text(res, 19, ae->source, -1, SQLITE_STATIC);
244
+ if (unlikely(rc != SQLITE_OK)) {
245
+ error_report("Failed to bind source parameter for SQL_INSERT_HEALTH_LOG");
246
+ goto failed;
247
+ }
248
+
249
+ rc = sqlite3_bind_text(res, 20, ae->units, -1, SQLITE_STATIC);
250
+ if (unlikely(rc != SQLITE_OK)) {
251
+ error_report("Failed to bind host_id parameter to store node instance information");
252
+ goto failed;
253
+ }
254
+
255
+ rc = sqlite3_bind_text(res, 21, ae->info, -1, SQLITE_STATIC);
256
+ if (unlikely(rc != SQLITE_OK)) {
257
+ error_report("Failed to bind info parameter for SQL_INSERT_HEALTH_LOG");
258
+ goto failed;
259
+ }
260
+
261
+ rc = sqlite3_bind_int(res, 22, ae->exec_code);
262
+ if (unlikely(rc != SQLITE_OK)) {
263
+ error_report("Failed to bind exec_code parameter for SQL_INSERT_HEALTH_LOG");
264
+ goto failed;
265
+ }
266
+
267
+ rc = sqlite3_bind_int(res, 23, ae->new_status);
268
+ if (unlikely(rc != SQLITE_OK)) {
269
+ error_report("Failed to bind new_status parameter for SQL_INSERT_HEALTH_LOG");
270
+ goto failed;
271
+ }
272
+
273
+ rc = sqlite3_bind_int(res, 24, ae->old_status);
274
+ if (unlikely(rc != SQLITE_OK)) {
275
+ error_report("Failed to bind old_status parameter for SQL_INSERT_HEALTH_LOG");
276
+ goto failed;
277
+ }
278
+
279
+ rc = sqlite3_bind_int(res, 25, ae->delay);
280
+ if (unlikely(rc != SQLITE_OK)) {
281
+ error_report("Failed to bind delay parameter for SQL_INSERT_HEALTH_LOG");
282
+ goto failed;
283
+ }
284
+
285
+ rc = sqlite3_bind_double(res, 26, ae->new_value);
286
+ if (unlikely(rc != SQLITE_OK)) {
287
+ error_report("Failed to bind new_value parameter for SQL_INSERT_HEALTH_LOG");
288
+ goto failed;
289
+ }
290
+
291
+ rc = sqlite3_bind_double(res, 27, ae->old_value);
292
+ if (unlikely(rc != SQLITE_OK)) {
293
+ error_report("Failed to bind old_value parameter for SQL_INSERT_HEALTH_LOG");
294
+ goto failed;
295
+ }
296
+
297
+ rc = sqlite3_bind_int64(res, 28, (sqlite3_int64) ae->last_repeat);
298
+ if (unlikely(rc != SQLITE_OK)) {
299
+ error_report("Failed to bind last_repeat parameter for SQL_INSERT_HEALTH_LOG");
300
+ goto failed;
301
+ }
302
+
303
+ rc = sqlite3_bind_text(res, 29, ae->classification, -1, SQLITE_STATIC);
304
+ if (unlikely(rc != SQLITE_OK)) {
305
+ error_report("Failed to bind classification parameter for SQL_INSERT_HEALTH_LOG");
306
+ goto failed;
307
+ }
308
+
309
+ rc = sqlite3_bind_text(res, 30, ae->component, -1, SQLITE_STATIC);
310
+ if (unlikely(rc != SQLITE_OK)) {
311
+ error_report("Failed to bind component parameter for SQL_INSERT_HEALTH_LOG");
312
+ goto failed;
313
+ }
314
+
315
+ rc = sqlite3_bind_text(res, 31, ae->type, -1, SQLITE_STATIC);
316
+ if (unlikely(rc != SQLITE_OK)) {
317
+ error_report("Failed to bind type parameter for SQL_INSERT_HEALTH_LOG");
318
+ goto failed;
319
+ }
320
+
321
+ rc = execute_insert(res);
322
+ if (unlikely(rc != SQLITE_DONE)) {
323
+ error_report("HEALTH [%s]: Failed to execute SQL_INSERT_HEALTH_LOG, rc = %d", host->hostname, rc);
324
+ goto failed;
325
+ }
326
+
327
+ ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
328
+ host->health_log_entries_written++;
329
+
330
+ failed:
331
+ if (unlikely(sqlite3_finalize(res) != SQLITE_OK))
332
+ error_report("HEALTH [%s]: Failed to finalize the prepared statement for inserting to health log.", host->hostname);
333
+
334
+ return;
335
+}
336
+
337
+void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae)
338
+{
339
+ if (ae->flags & HEALTH_ENTRY_FLAG_SAVED)
340
+ sql_health_alarm_log_update(host, ae);
341
+ else
342
+ sql_health_alarm_log_insert(host, ae);
343
+}
344
+
345
+/* Health related SQL queries
346
+ Cleans up the health_log table.
347
+*/
348
+#define SQL_CLEANUP_HEALTH_LOG(guid,guid2,limit) "DELETE from health_log_%s where unique_id in (SELECT unique_id from health_log_%s order by unique_id asc LIMIT %lu);", guid, guid2, limit
349
+void sql_health_alarm_log_cleanup(RRDHOST *host) {
350
+ sqlite3_stmt *res = NULL;
351
+ static size_t rotate_every = 0;
352
+ int rc;
353
+ char command[MAX_HEALTH_SQL_SIZE + 1];
354
+
355
+ if(unlikely(rotate_every == 0)) {
356
+ rotate_every = (size_t)config_get_number(CONFIG_SECTION_HEALTH, "rotate log every lines", 2000);
357
+ if(rotate_every < 100) rotate_every = 100;
358
+ }
359
+
360
+ if(likely(host->health_log_entries_written < rotate_every)) {
361
+ return;
362
+ }
363
+
364
+ if (unlikely(!db_meta)) {
365
+ if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
366
+ error_report("Database has not been initialized");
367
+ return;
368
+ }
369
+
370
+ char uuid_str[GUID_LEN + 1];
371
+ uuid_unparse_lower_fix(&host->host_uuid, uuid_str);
372
+
373
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_CLEANUP_HEALTH_LOG(uuid_str, uuid_str, host->health_log_entries_written - rotate_every));
374
+
375
+ rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
376
+ if (unlikely(rc != SQLITE_OK)) {
377
+ error_report("Failed to prepare statement to cleanup health log table");
378
+ return;
379
+ }
380
+
381
+ rc = sqlite3_step(res);
382
+ if (unlikely(rc != SQLITE_DONE))
383
+ error_report("Failed to cleanup health log table, rc = %d", rc);
384
+
385
+ rc = sqlite3_finalize(res);
386
+ if (unlikely(rc != SQLITE_OK))
387
+ error_report("Failed to finalize the prepared statement to cleanup health log table");
388
+
389
+ host->health_log_entries_written = rotate_every;
390
+}
391
+
392
+/* Health related SQL queries
393
+ Get a count of rows from health log table
394
+*/
395
+#define SQL_COUNT_HEALTH_LOG(guid) "SELECT count(1) FROM health_log_%s;", guid
396
+void sql_health_alarm_log_count(RRDHOST *host) {
397
+ sqlite3_stmt *res = NULL;
398
+ int rc;
399
+ char command[MAX_HEALTH_SQL_SIZE + 1];
400
+
401
+ if (unlikely(!db_meta)) {
402
+ if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
403
+ error_report("Database has not been initialized");
404
+ return;
405
+ }
406
+
407
+ char uuid_str[GUID_LEN + 1];
408
+ uuid_unparse_lower_fix(&host->host_uuid, uuid_str);
409
+
410
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_COUNT_HEALTH_LOG(uuid_str));
411
+
412
+ rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
413
+ if (unlikely(rc != SQLITE_OK)) {
414
+ error_report("Failed to prepare statement to count health log entries from db");
415
+ return;
416
+ }
417
+
418
+ rc = sqlite3_step(res);
419
+ if (likely(rc == SQLITE_ROW))
420
+ host->health_log_entries_written = (size_t) sqlite3_column_int64(res, 0);
421
+
422
+ rc = sqlite3_finalize(res);
423
+ if (unlikely(rc != SQLITE_OK))
424
+ error_report("Failed to finalize the prepared statement to count health log entries from db");
425
+
426
+ info("HEALTH [%s]: Table health_log_%s, contains %lu entries.", host->hostname, uuid_str, host->health_log_entries_written);
427
+}
428
+
429
+/* Health related SQL queries
430
+ Load from the health log table
431
+*/
432
+#define SQL_LOAD_HEALTH_LOG(guid,limit) "SELECT hostname, unique_id, alarm_id, alarm_event_id, config_hash_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, name, chart, family, exec, recipient, source, units, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, class, component, type FROM (SELECT hostname, unique_id, alarm_id, alarm_event_id, config_hash_id, updated_by_id, updates_id, when_key, duration, non_clear_duration, flags, exec_run_timestamp, delay_up_to_timestamp, name, chart, family, exec, recipient, source, units, info, exec_code, new_status, old_status, delay, new_value, old_value, last_repeat, class, component, type FROM health_log_%s order by unique_id desc limit %u) order by unique_id asc;", guid, limit
433
+void sql_health_alarm_log_load(RRDHOST *host) {
434
+ sqlite3_stmt *res = NULL;
435
+ int rc;
436
+ ssize_t errored = 0, loaded = 0;
437
+ char command[MAX_HEALTH_SQL_SIZE + 1];
438
+
439
+ host->health_log_entries_written = 0;
440
+
441
+ if (unlikely(!db_meta)) {
442
+ if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE)
443
+ error_report("HEALTH [%s]: Database has not been initialized", host->hostname);
444
+ return;
445
+ }
446
+
447
+ char uuid_str[GUID_LEN + 1];
448
+ uuid_unparse_lower_fix(&host->host_uuid, uuid_str);
449
+
450
+ snprintfz(command, MAX_HEALTH_SQL_SIZE, SQL_LOAD_HEALTH_LOG(uuid_str, host->health_log.max));
451
+
452
+ rc = sqlite3_prepare_v2(db_meta, command, -1, &res, 0);
453
+ if (unlikely(rc != SQLITE_OK)) {
454
+ error_report("HEALTH [%s]: Failed to prepare sql statement to load health log.", host->hostname);
455
+ return;
456
+ }
457
+
458
+ netdata_rwlock_rdlock(&host->health_log.alarm_log_rwlock);
459
+
460
+ while (sqlite3_step(res) == SQLITE_ROW) {
461
+ ALARM_ENTRY *ae = NULL;
462
+
463
+ // check that we have valid ids
464
+ uint32_t unique_id = (uint32_t) sqlite3_column_int64(res, 1);
465
+ if(!unique_id) {
466
+ error_report("HEALTH [%s]: Got invalid unique id. Ignoring it.", host->hostname);
467
+ errored++;
468
+ continue;
469
+ }
470
+
471
+ uint32_t alarm_id = (uint32_t) sqlite3_column_int64(res, 2);
472
+ if(!alarm_id) {
473
+ error_report("HEALTH [%s]: Got invalid alarm id. Ignoring it.", host->hostname);
474
+ errored++;
475
+ continue;
476
+ }
477
+
478
+ //need name, chart and family
479
+ if (sqlite3_column_type(res, 13) == SQLITE_NULL) {
480
+ error_report("HEALTH [%s]: Got null name field. Ignoring it.", host->hostname);
481
+ errored++;
482
+ continue;
483
+ }
484
+
485
+ if (sqlite3_column_type(res, 14) == SQLITE_NULL) {
486
+ error_report("HEALTH [%s]: Got null chart field. Ignoring it.", host->hostname);
487
+ errored++;
488
+ continue;
489
+ }
490
+
491
+ if (sqlite3_column_type(res, 15) == SQLITE_NULL) {
492
+ error_report("HEALTH [%s]: Got null family field. Ignoring it.", host->hostname);
493
+ errored++;
494
+ continue;
495
+ }
496
+
497
+ // Check if we got last_repeat field
498
+ time_t last_repeat = 0;
499
+ last_repeat = (time_t)sqlite3_column_int64(res, 27);
500
+
501
+ RRDCALC *rc = alarm_max_last_repeat(host, (char *) sqlite3_column_text(res, 14), simple_hash((char *) sqlite3_column_text(res, 14)));
502
+ if (!rc) {
503
+ for(rc = host->alarms; rc ; rc = rc->next) {
504
+ RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_name, (avl_t *)rc);
505
+ if(rdcmp != rc) {
506
+ error("Cannot insert the alarm index ID using log %s", rc->name);
507
+ }
508
+ }
509
+
510
+ rc = alarm_max_last_repeat(host, (char *) sqlite3_column_text(res, 14), simple_hash((char *) sqlite3_column_text(res, 14)));
511
+ }
512
+
513
+ if(unlikely(rc)) {
514
+ if (rrdcalc_isrepeating(rc)) {
515
+ rc->last_repeat = last_repeat;
516
+ // We iterate through repeating alarm entries only to
517
+ // find the latest last_repeat timestamp. Otherwise,
518
+ // there is no need to keep them in memory.
519
+ continue;
520
+ }
521
+ }
522
+
523
+ ae = callocz(1, sizeof(ALARM_ENTRY));
524
+
525
+ ae->unique_id = unique_id;
526
+ ae->alarm_id = alarm_id;
527
+
528
+ if (sqlite3_column_type(res, 4) != SQLITE_NULL)
529
+ uuid_copy(ae->config_hash_id, *((uuid_t *) sqlite3_column_blob(res, 4)));
530
+
531
+ ae->alarm_event_id = (uint32_t) sqlite3_column_int64(res, 3);
532
+ ae->updated_by_id = (uint32_t) sqlite3_column_int64(res, 5);
533
+ ae->updates_id = (uint32_t) sqlite3_column_int64(res, 6);
534
+
535
+ ae->when = (time_t) sqlite3_column_int64(res, 7);
536
+ ae->duration = (time_t) sqlite3_column_int64(res, 8);
537
+ ae->non_clear_duration = (time_t) sqlite3_column_int64(res, 9);
538
+
539
+ ae->flags = (uint32_t) sqlite3_column_int64(res, 10);
540
+ ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
541
+
542
+ ae->exec_run_timestamp = (time_t) sqlite3_column_int64(res, 11);
543
+ ae->delay_up_to_timestamp = (time_t) sqlite3_column_int64(res, 12);
544
+
545
+ ae->name = strdupz((char *) sqlite3_column_text(res, 13));
546
+ ae->hash_name = simple_hash(ae->name);
547
+
548
+ ae->chart = strdupz((char *) sqlite3_column_text(res, 14));
549
+ ae->hash_chart = simple_hash(ae->chart);
550
+
551
+ ae->family = strdupz((char *) sqlite3_column_text(res, 15));
552
+
553
+ if (sqlite3_column_type(res, 16) != SQLITE_NULL)
554
+ ae->exec = strdupz((char *) sqlite3_column_text(res, 16));
555
+ else
556
+ ae->exec = NULL;
557
+
558
+ if (sqlite3_column_type(res, 17) != SQLITE_NULL)
559
+ ae->recipient = strdupz((char *) sqlite3_column_text(res, 17));
560
+ else
561
+ ae->recipient = NULL;
562
+
563
+ if (sqlite3_column_type(res, 18) != SQLITE_NULL)
564
+ ae->source = strdupz((char *) sqlite3_column_text(res, 18));
565
+ else
566
+ ae->source = NULL;
567
+
568
+ if (sqlite3_column_type(res, 19) != SQLITE_NULL)
569
+ ae->units = strdupz((char *) sqlite3_column_text(res, 19));
570
+ else
571
+ ae->units = NULL;
572
+
573
+ if (sqlite3_column_type(res, 20) != SQLITE_NULL)
574
+ ae->info = strdupz((char *) sqlite3_column_text(res, 20));
575
+ else
576
+ ae->info = NULL;
577
+
578
+ ae->exec_code = (int) sqlite3_column_int(res, 21);
579
+ ae->new_status = (RRDCALC_STATUS) sqlite3_column_int(res, 22);
580
+ ae->old_status = (RRDCALC_STATUS)sqlite3_column_int(res, 23);
581
+ ae->delay = (int) sqlite3_column_int(res, 24);
582
+
583
+ ae->new_value = (calculated_number) sqlite3_column_double(res, 25);
584
+ ae->old_value = (calculated_number) sqlite3_column_double(res, 26);
585
+
586
+ ae->last_repeat = last_repeat;
587
+
588
+ if (sqlite3_column_type(res, 28) != SQLITE_NULL)
589
+ ae->classification = strdupz((char *) sqlite3_column_text(res, 28));
590
+ else
591
+ ae->classification = NULL;
592
+
593
+ if (sqlite3_column_type(res, 29) != SQLITE_NULL)
594
+ ae->component = strdupz((char *) sqlite3_column_text(res, 29));
595
+ else
596
+ ae->component = NULL;
597
+
598
+ if (sqlite3_column_type(res, 30) != SQLITE_NULL)
599
+ ae->type = strdupz((char *) sqlite3_column_text(res, 30));
600
+ else
601
+ ae->type = NULL;
602
+
603
+ char value_string[100 + 1];
604
+ freez(ae->old_value_string);
605
+ freez(ae->new_value_string);
606
+ ae->old_value_string = strdupz(format_value_and_unit(value_string, 100, ae->old_value, ae->units, -1));
607
+ ae->new_value_string = strdupz(format_value_and_unit(value_string, 100, ae->new_value, ae->units, -1));
608
+
609
+ ae->next = host->health_log.alarms;
610
+ host->health_log.alarms = ae;
611
+
612
+ if(unlikely(ae->unique_id > host->health_max_unique_id))
613
+ host->health_max_unique_id = ae->unique_id;
614
+
615
+ if(unlikely(ae->alarm_id >= host->health_max_alarm_id))
616
+ host->health_max_alarm_id = ae->alarm_id;
617
+
618
+ loaded++;
619
+ }
620
+
621
+ netdata_rwlock_unlock(&host->health_log.alarm_log_rwlock);
622
+
623
+ if(!host->health_max_unique_id) host->health_max_unique_id = (uint32_t)now_realtime_sec();
624
+ if(!host->health_max_alarm_id) host->health_max_alarm_id = (uint32_t)now_realtime_sec();
625
+
626
+ host->health_log.next_log_id = host->health_max_unique_id + 1;
627
+ if (unlikely(!host->health_log.next_alarm_id || host->health_log.next_alarm_id <= host->health_max_alarm_id))
628
+ host->health_log.next_alarm_id = host->health_max_alarm_id + 1;
629
+
630
+ info("HEALTH [%s]: Table health_log_%s, loaded %zd alarm entries, errors in %zd entries.", host->hostname, uuid_str, loaded, errored);
631
+
632
+ rc = sqlite3_finalize(res);
633
+ if (unlikely(rc != SQLITE_OK))
634
+ error_report("Failed to finalize the health log read statement");
635
+
636
+ sql_health_alarm_log_count(host);
637
+}
638
+
639
+/*
640
+ * Store an alert config hash in the database
641
+ */
642
+#define SQL_STORE_ALERT_CONFIG_HASH "insert or replace into alert_hash (hash_id, date_updated, alarm, template, on_key, class, component, type, os, hosts, lookup, every, units, calc, families, plugin, module, charts, green, red, warn, crit, exec, to_key, info, delay, options, repeat, host_labels, p_db_lookup_dimensions, p_db_lookup_method, p_db_lookup_options, p_db_lookup_after, p_db_lookup_before, p_update_every) values (?1,strftime('%s'),?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,?16,?17,?18,?19,?20,?21,?22,?23,?24,?25,?26,?27,?28,?29,?30,?31,?32,?33,?34);"
643
+int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg)
644
+{
645
+ static __thread sqlite3_stmt *res = NULL;
646
+ int rc, param = 0;
647
+
648
+ if (unlikely(!db_meta)) {
649
+ if (default_rrd_memory_mode != RRD_MEMORY_MODE_DBENGINE)
650
+ return 0;
651
+ error_report("Database has not been initialized");
652
+ return 1;
653
+ }
654
+
655
+ if (unlikely(!res)) {
656
+ rc = prepare_statement(db_meta, SQL_STORE_ALERT_CONFIG_HASH, &res);
657
+ if (unlikely(rc != SQLITE_OK)) {
658
+ error_report("Failed to prepare statement to store alert configuration, rc = %d", rc);
659
+ return 1;
660
+ }
661
+ }
662
+
663
+ param++;
664
+ rc = sqlite3_bind_blob(res, 1, hash_id, sizeof(*hash_id), SQLITE_STATIC);
665
+ if (unlikely(rc != SQLITE_OK))
666
+ goto bind_fail;
667
+
668
+ param++;
669
+ if (cfg->alarm && *cfg->alarm)
670
+ rc = sqlite3_bind_text(res, 2, cfg->alarm, -1, SQLITE_STATIC);
671
+ else
672
+ rc = sqlite3_bind_null(res, 2);
673
+ if (unlikely(rc != SQLITE_OK))
674
+ goto bind_fail;
675
+
676
+ param++;
677
+ if (cfg->template_key && *cfg->template_key)
678
+ rc = sqlite3_bind_text(res, 3, cfg->template_key, -1, SQLITE_STATIC);
679
+ else
680
+ rc = sqlite3_bind_null(res, 3);
681
+ if (unlikely(rc != SQLITE_OK))
682
+ goto bind_fail;
683
+
684
+ param++;
685
+ rc = sqlite3_bind_text(res, 4, cfg->on, -1, SQLITE_STATIC);
686
+ if (unlikely(rc != SQLITE_OK))
687
+ goto bind_fail;
688
+
689
+ param++;
690
+ rc = sqlite3_bind_text(res, 5, cfg->classification, -1, SQLITE_STATIC);
691
+ if (unlikely(rc != SQLITE_OK))
692
+ goto bind_fail;
693
+
694
+ param++;
695
+ rc = sqlite3_bind_text(res, 6, cfg->component, -1, SQLITE_STATIC);
696
+ if (unlikely(rc != SQLITE_OK))
697
+ goto bind_fail;
698
+
699
+ param++;
700
+ rc = sqlite3_bind_text(res, 7, cfg->type, -1, SQLITE_STATIC);
701
+ if (unlikely(rc != SQLITE_OK))
702
+ goto bind_fail;
703
+
704
+ param++;
705
+ rc = sqlite3_bind_text(res, 8, cfg->os, -1, SQLITE_STATIC);
706
+ if (unlikely(rc != SQLITE_OK))
707
+ goto bind_fail;
708
+
709
+ param++;
710
+ rc = sqlite3_bind_text(res, 9, cfg->host, -1, SQLITE_STATIC);
711
+ if (unlikely(rc != SQLITE_OK))
712
+ goto bind_fail;
713
+
714
+ param++;
715
+ rc = sqlite3_bind_text(res, 10, cfg->lookup, -1, SQLITE_STATIC);
716
+ if (unlikely(rc != SQLITE_OK))
717
+ goto bind_fail;
718
+
719
+ param++;
720
+ rc = sqlite3_bind_text(res, 11, cfg->every, -1, SQLITE_STATIC);
721
+ if (unlikely(rc != SQLITE_OK))
722
+ goto bind_fail;
723
+
724
+ param++;
725
+ rc = sqlite3_bind_text(res, 12, cfg->units, -1, SQLITE_STATIC);
726
+ if (unlikely(rc != SQLITE_OK))
727
+ goto bind_fail;
728
+
729
+ param++;
730
+ rc = sqlite3_bind_text(res, 13, cfg->calc, -1, SQLITE_STATIC);
731
+ if (unlikely(rc != SQLITE_OK))
732
+ goto bind_fail;
733
+
734
+ param++;
735
+ rc = sqlite3_bind_text(res, 14, cfg->families, -1, SQLITE_STATIC);
736
+ if (unlikely(rc != SQLITE_OK))
737
+ goto bind_fail;
738
+
739
+ param++;
740
+ rc = sqlite3_bind_text(res, 15, cfg->plugin, -1, SQLITE_STATIC);
741
+ if (unlikely(rc != SQLITE_OK))
742
+ goto bind_fail;
743
+
744
+ param++;
745
+ rc = sqlite3_bind_text(res, 16, cfg->module, -1, SQLITE_STATIC);
746
+ if (unlikely(rc != SQLITE_OK))
747
+ goto bind_fail;
748
+
749
+ param++;
750
+ rc = sqlite3_bind_text(res, 17, cfg->charts, -1, SQLITE_STATIC);
751
+ if (unlikely(rc != SQLITE_OK))
752
+ goto bind_fail;
753
+
754
+ param++;
755
+ rc = sqlite3_bind_text(res, 18, cfg->green, -1, SQLITE_STATIC);
756
+ if (unlikely(rc != SQLITE_OK))
757
+ goto bind_fail;
758
+
759
+ param++;
760
+ rc = sqlite3_bind_text(res, 19, cfg->red, -1, SQLITE_STATIC);
761
+ if (unlikely(rc != SQLITE_OK))
762
+ goto bind_fail;
763
+
764
+ param++;
765
+ rc = sqlite3_bind_text(res, 20, cfg->warn, -1, SQLITE_STATIC);
766
+ if (unlikely(rc != SQLITE_OK))
767
+ goto bind_fail;
768
+
769
+ param++;
770
+ rc = sqlite3_bind_text(res, 21, cfg->crit, -1, SQLITE_STATIC);
771
+ if (unlikely(rc != SQLITE_OK))
772
+ goto bind_fail;
773
+
774
+ param++;
775
+ rc = sqlite3_bind_text(res, 22, cfg->exec, -1, SQLITE_STATIC);
776
+ if (unlikely(rc != SQLITE_OK))
777
+ goto bind_fail;
778
+
779
+ param++;
780
+ rc = sqlite3_bind_text(res, 23, cfg->to, -1, SQLITE_STATIC);
781
+ if (unlikely(rc != SQLITE_OK))
782
+ goto bind_fail;
783
+
784
+ param++;
785
+ rc = sqlite3_bind_text(res, 24, cfg->info, -1, SQLITE_STATIC);
786
+ if (unlikely(rc != SQLITE_OK))
787
+ goto bind_fail;
788
+
789
+ param++;
790
+ rc = sqlite3_bind_text(res, 25, cfg->delay, -1, SQLITE_STATIC);
791
+ if (unlikely(rc != SQLITE_OK))
792
+ goto bind_fail;
793
+
794
+ param++;
795
+ rc = sqlite3_bind_text(res, 26, cfg->options, -1, SQLITE_STATIC);
796
+ if (unlikely(rc != SQLITE_OK))
797
+ goto bind_fail;
798
+
799
+ param++;
800
+ rc = sqlite3_bind_text(res, 27, cfg->repeat, -1, SQLITE_STATIC);
801
+ if (unlikely(rc != SQLITE_OK))
802
+ goto bind_fail;
803
+
804
+ param++;
805
+ rc = sqlite3_bind_text(res, 28, cfg->host_labels, -1, SQLITE_STATIC);
806
+ if (unlikely(rc != SQLITE_OK))
807
+ goto bind_fail;
808
+
809
+ if (cfg->p_db_lookup_after) {
810
+ param++;
811
+ rc = sqlite3_bind_text(res, 29, cfg->p_db_lookup_dimensions, -1, SQLITE_STATIC);
812
+ if (unlikely(rc != SQLITE_OK))
813
+ goto bind_fail;
814
+
815
+ param++;
816
+ rc = sqlite3_bind_text(res, 30, cfg->p_db_lookup_method, -1, SQLITE_STATIC);
817
+ if (unlikely(rc != SQLITE_OK))
818
+ goto bind_fail;
819
+
820
+ param++;
821
+ rc = sqlite3_bind_int(res, 31, cfg->p_db_lookup_options);
822
+ if (unlikely(rc != SQLITE_OK))
823
+ goto bind_fail;
824
+
825
+ param++;
826
+ rc = sqlite3_bind_int(res, 32, cfg->p_db_lookup_after);
827
+ if (unlikely(rc != SQLITE_OK))
828
+ goto bind_fail;
829
+
830
+ param++;
831
+ rc = sqlite3_bind_int(res, 33, cfg->p_db_lookup_before);
832
+ if (unlikely(rc != SQLITE_OK))
833
+ goto bind_fail;
834
+ } else {
835
+ param++;
836
+ rc = sqlite3_bind_null(res, 29);
837
+ if (unlikely(rc != SQLITE_OK))
838
+ goto bind_fail;
839
+ param++;
840
+ rc = sqlite3_bind_null(res, 30);
841
+ if (unlikely(rc != SQLITE_OK))
842
+ goto bind_fail;
843
+ param++;
844
+ rc = sqlite3_bind_null(res, 31);
845
+ if (unlikely(rc != SQLITE_OK))
846
+ goto bind_fail;
847
+ param++;
848
+ rc = sqlite3_bind_null(res, 32);
849
+ if (unlikely(rc != SQLITE_OK))
850
+ goto bind_fail;
851
+ param++;
852
+ rc = sqlite3_bind_null(res, 33);
853
+ if (unlikely(rc != SQLITE_OK))
854
+ goto bind_fail;
855
+ }
856
+
857
+ param++;
858
+ rc = sqlite3_bind_int(res, 34, cfg->p_update_every);
859
+ if (unlikely(rc != SQLITE_OK))
860
+ goto bind_fail;
861
+
862
+ rc = execute_insert(res);
863
+ if (unlikely(rc != SQLITE_DONE))
864
+ error_report("Failed to store alert config, rc = %d", rc);
865
+
866
+ rc = sqlite3_reset(res);
867
+ if (unlikely(rc != SQLITE_OK))
868
+ error_report("Failed to reset statement in alert hash_id store function, rc = %d", rc);
869
+
870
+ return 0;
871
+
872
+ bind_fail:
873
+ error_report("Failed to bind parameter %d to store alert hash_id, rc = %d", param, rc);
874
+ rc = sqlite3_reset(res);
875
+ if (unlikely(rc != SQLITE_OK))
876
+ error_report("Failed to reset statement in alert hash_id store function, rc = %d", rc);
877
+ return 1;
878
+}
879
+
880
+#define DIGEST_ALERT_CONFIG_VAL(v) ((v) ? EVP_DigestUpdate(evpctx, (v), strlen((v))) : EVP_DigestUpdate(evpctx, "", 1))
881
+int alert_hash_and_store_config(
882
+ uuid_t hash_id,
883
+ struct alert_config *cfg)
884
+{
885
+ EVP_MD_CTX *evpctx;
886
+ unsigned char hash_value[EVP_MAX_MD_SIZE];
887
+ unsigned int hash_len;
888
+ evpctx = EVP_MD_CTX_create();
889
+ EVP_DigestInit_ex(evpctx, EVP_sha256(), NULL);
890
+
891
+ DIGEST_ALERT_CONFIG_VAL(cfg->alarm);
892
+ DIGEST_ALERT_CONFIG_VAL(cfg->template_key);
893
+ DIGEST_ALERT_CONFIG_VAL(cfg->os);
894
+ DIGEST_ALERT_CONFIG_VAL(cfg->host);
895
+ DIGEST_ALERT_CONFIG_VAL(cfg->on);
896
+ DIGEST_ALERT_CONFIG_VAL(cfg->families);
897
+ DIGEST_ALERT_CONFIG_VAL(cfg->plugin);
898
+ DIGEST_ALERT_CONFIG_VAL(cfg->module);
899
+ DIGEST_ALERT_CONFIG_VAL(cfg->charts);
900
+ DIGEST_ALERT_CONFIG_VAL(cfg->lookup);
901
+ DIGEST_ALERT_CONFIG_VAL(cfg->calc);
902
+ DIGEST_ALERT_CONFIG_VAL(cfg->every);
903
+ DIGEST_ALERT_CONFIG_VAL(cfg->green);
904
+ DIGEST_ALERT_CONFIG_VAL(cfg->red);
905
+ DIGEST_ALERT_CONFIG_VAL(cfg->warn);
906
+ DIGEST_ALERT_CONFIG_VAL(cfg->crit);
907
+ DIGEST_ALERT_CONFIG_VAL(cfg->exec);
908
+ DIGEST_ALERT_CONFIG_VAL(cfg->to);
909
+ DIGEST_ALERT_CONFIG_VAL(cfg->units);
910
+ DIGEST_ALERT_CONFIG_VAL(cfg->info);
911
+ DIGEST_ALERT_CONFIG_VAL(cfg->classification);
912
+ DIGEST_ALERT_CONFIG_VAL(cfg->component);
913
+ DIGEST_ALERT_CONFIG_VAL(cfg->type);
914
+ DIGEST_ALERT_CONFIG_VAL(cfg->delay);
915
+ DIGEST_ALERT_CONFIG_VAL(cfg->options);
916
+ DIGEST_ALERT_CONFIG_VAL(cfg->repeat);
917
+ DIGEST_ALERT_CONFIG_VAL(cfg->host_labels);
918
+
919
+ EVP_DigestFinal_ex(evpctx, hash_value, &hash_len);
920
+ EVP_MD_CTX_destroy(evpctx);
921
+ fatal_assert(hash_len > sizeof(uuid_t));
922
+
923
+ char uuid_str[GUID_LEN + 1];
924
+ uuid_unparse_lower(*((uuid_t *)&hash_value), uuid_str);
925
+ uuid_copy(hash_id, *((uuid_t *)&hash_value));
926
+
927
+ /* store everything, so it can be recreated when not in memory or just a subset ? */
928
+ (void)sql_store_alert_config_hash( (uuid_t *)&hash_value, cfg);
929
+
930
+ return 1;
931
+}