@cryptotaxi247 / netdata-1 / commits / b87473c48

Use sqlite to store the health log and alert configurations. (#11399)

* Rebased * use sql health log if it exists * store alert config in sqlite * move unlock before loop * fix warnings * remove hash message * check return from counting health log * remove check of hostname when reading log * try to create the health log table to catch accidental removals of it * fix warnings, cast values, report config_hash_id * use snprintfz, add info logging * remove unnecessary strdup and free * check if stored config hash is null * return if prepare statement fails * replace with static variables * remove replace info, free edit_command * remove setting cfg entries to NULL * change uuid_copy * check return of uuid_parse, and exit if its not valid * also free cfg * use address * removed health_alarm_entry_sql2json and sql_health_alarm_log_select_all * remove check for is_valid_alarm_id * replace lengths with GUID_LEN * use uuid_unparse_lower_fix * removed web api endopoint to get alert config * check for non null values for name, chart and family * include a date_updated field in alert_hash * for config hash, digest NULL string if value to digest is null * Use empty string instead of null

Emmanuel Vasilakis committed Sep 19, 2021 at 14:11 UTC b87473c481283bbd936661fb15c3bd63fa74dbca
16 files changed +1210 -40
CMakeLists.txt
+2
@@ -631,6 +631,8 @@ set(RRD_PLUGIN_FILES
631 database/sqlite/sqlite_functions.h
632 database/sqlite/sqlite_aclk.c
633 database/sqlite/sqlite_aclk.h
634 + database/sqlite/sqlite_health.c
635 + database/sqlite/sqlite_health.h
636 database/sqlite/sqlite3.c
637 database/sqlite/sqlite3.h
638 database/engine/rrdengine.c
Makefile.am
+2
@@ -411,6 +411,8 @@ RRD_PLUGIN_FILES = \
411 database/sqlite/sqlite_functions.h \
412 database/sqlite/sqlite_aclk.c \
413 database/sqlite/sqlite_aclk.h \
414 + database/sqlite/sqlite_health.c \
415 + database/sqlite/sqlite_health.h \
416 database/sqlite/sqlite3.c \
417 database/sqlite/sqlite3.h \
418 $(NULL)
database/rrd.h
+2
@@ -35,6 +35,7 @@ struct pg_cache_page_index;
35 #include "rrdcalctemplate.h"
36 #include "streaming/rrdpush.h"
37 #include "aclk/aclk_rrdhost_state.h"
38 +#include "sqlite/sqlite_health.h"
39
40 enum {
41 CONTEXT_FLAGS_ARCHIVE = 0x01,
@@ -650,6 +651,7 @@ struct alarm_entry {
651 uint32_t unique_id;
652 uint32_t alarm_id;
653 uint32_t alarm_event_id;
654 + uuid_t config_hash_id;
655
656 time_t when;
657 time_t duration;
database/rrdcalc.c
+4
@@ -87,6 +87,7 @@ static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
87 host,
88 rc->id,
89 rc->next_event_id++,
90 + rc->config_hash_id,
91 now,
92 rc->name,
93 rc->rrdset->id,
@@ -164,6 +165,7 @@ inline void rrdsetcalc_unlink(RRDCALC *rc) {
165 host,
166 rc->id,
167 rc->next_event_id++,
168 + rc->config_hash_id,
169 now,
170 rc->name,
171 rc->rrdset->id,
@@ -398,6 +400,7 @@ inline RRDCALC *rrdcalc_create_from_template(RRDHOST *host, RRDCALCTEMPLATE *rt,
400 rc->hash = simple_hash(rc->name);
401 rc->chart = strdupz(chart);
402 rc->hash_chart = simple_hash(rc->chart);
403 + uuid_copy(rc->config_hash_id, rt->config_hash_id);
404
405 rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id);
406
@@ -513,6 +516,7 @@ inline RRDCALC *rrdcalc_create_from_rrdcalc(RRDCALC *rc, RRDHOST *host, const ch
516 newrc->hash = simple_hash(newrc->name);
517 newrc->chart = strdupz(rc->chart);
518 newrc->hash_chart = simple_hash(rc->chart);
519 + uuid_copy(newrc->config_hash_id, *((uuid_t *) &rc->config_hash_id));
520
521 newrc->dimensions = strdupz(dimension);
522 newrc->foreachdim = NULL;
database/rrdcalc.h
+38
@@ -38,6 +38,7 @@ struct rrdcalc {
38
39 char *name; // the name of this alarm
40 uint32_t hash; // the hash of the alarm name
41 + uuid_t config_hash_id; // a predictable hash_id based on specific alert configuration
42
43 char *exec; // the command to execute when this alarm switches state
44 char *recipient; // the recipient of the alarm (the first parameter to exec)
@@ -149,6 +150,43 @@ struct rrdcalc {
150 struct rrdcalc *next;
151 };
152
153 +struct alert_config {
154 + char *alarm;
155 + char *template_key;
156 + char *os;
157 + char *host;
158 + char *on;
159 + char *families;
160 + char *plugin;
161 + char *module;
162 + char *charts;
163 + char *lookup;
164 + char *calc;
165 + char *warn;
166 + char *crit;
167 + char *every;
168 + char *green;
169 + char *red;
170 + char *exec;
171 + char *to;
172 + char *units;
173 + char *info;
174 + char *classification;
175 + char *component;
176 + char *type;
177 + char *delay;
178 + char *options;
179 + char *repeat;
180 + char *host_labels;
181 +
182 + char *p_db_lookup_dimensions;
183 + char *p_db_lookup_method;
184 + uint32_t p_db_lookup_options;
185 + int32_t p_db_lookup_after;
186 + int32_t p_db_lookup_before;
187 + int32_t p_update_every;
188 +};
189 +
190 extern int alarm_isrepeating(RRDHOST *host, uint32_t alarm_id);
191 extern int alarm_entry_isrepeating(RRDHOST *host, ALARM_ENTRY *ae);
192 extern RRDCALC *alarm_max_last_repeat(RRDHOST *host, char *alarm_name, uint32_t hash);
database/rrdcalctemplate.h
+1
@@ -11,6 +11,7 @@
11 struct rrdcalctemplate {
12 char *name;
13 uint32_t hash_name;
14 + uuid_t config_hash_id;
15
16 char *exec;
17 char *recipient;
database/rrdhost.c
+32 -5
@@ -295,9 +295,6 @@ RRDHOST *rrdhost_create(const char *hostname,
295 rrdhost_wrlock(host);
296 health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL);
297 rrdhost_unlock(host);
298 -
299 - health_alarm_log_load(host);
300 - health_alarm_log_open(host);
298 }
299
300 RRDHOST *t = rrdhost_index_add(host);
@@ -313,6 +310,23 @@ RRDHOST *rrdhost_create(const char *hostname,
310 if (unlikely(rc))
311 error_report("Failed to store machine GUID to the database");
312 sql_load_node_id(host);
313 + if (host->health_enabled) {
314 + if (!file_is_migrated(host->health_log_filename)) {
315 + int rc = sql_create_health_log_table(host);
316 + if (unlikely(rc)) {
317 + error_report("Failed to create health log table in the database");
318 + health_alarm_log_load(host);
319 + health_alarm_log_open(host);
320 + }
321 + else {
322 + health_alarm_log_load(host);
323 + add_migrated_file(host->health_log_filename, 0);
324 + }
325 + } else {
326 + sql_create_health_log_table(host);
327 + sql_health_alarm_log_load(host);
328 + }
329 + }
330 }
331 else
332 error_report("Host machine GUID %s is not valid", host->machine_guid);
@@ -506,8 +520,21 @@ void rrdhost_update(RRDHOST *host
520 health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL);
521 rrdhost_unlock(host);
522
509 - health_alarm_log_load(host);
510 - health_alarm_log_open(host);
523 + if (!file_is_migrated(host->health_log_filename)) {
524 + int rc = sql_create_health_log_table(host);
525 + if (unlikely(rc)) {
526 + error_report("Failed to create health log table in the database");
527 +
528 + health_alarm_log_load(host);
529 + health_alarm_log_open(host);
530 + } else {
531 + health_alarm_log_load(host);
532 + add_migrated_file(host->health_log_filename, 0);
533 + }
534 + } else {
535 + sql_create_health_log_table(host);
536 + sql_health_alarm_log_load(host);
537 + }
538 }
539 rrd_hosts_available++;
540 info("Host %s is not in archived mode anymore", host->hostname);
database/sqlite/sqlite_functions.c
+8 -2
@@ -20,6 +20,12 @@ const char *database_config[] = {
20 "CREATE TABLE IF NOT EXISTS chart_label(chart_id blob, source_type int, label_key text, "
21 "label_value text, date_created int, PRIMARY KEY (chart_id, label_key));",
22 "CREATE TABLE IF NOT EXISTS node_instance (host_id blob PRIMARY KEY, claim_id, node_id, date_created);",
23 + "CREATE TABLE IF NOT EXISTS alert_hash(hash_id blob PRIMARY KEY, date_updated int, alarm text, template text, "
24 + "on_key text, class text, component text, type text, os text, hosts text, lookup text, "
25 + "every text, units text, calc text, families text, plugin text, module text, charts text, green text, "
26 + "red text, warn text, crit text, exec text, to_key text, info text, delay text, options text, "
27 + "repeat text, host_labels text, p_db_lookup_dimensions text, p_db_lookup_method text, p_db_lookup_options int, "
28 + "p_db_lookup_after int, p_db_lookup_before int, p_update_every int);",
29 "delete from chart_active;",
30 "delete from dimension_active;",
31 "delete from chart where chart_id not in (select chart_id from dimension);",
@@ -32,7 +38,7 @@ sqlite3 *db_meta = NULL;
38
39 static uv_mutex_t sqlite_transaction_lock;
40
35 -static int execute_insert(sqlite3_stmt *res)
41 +int execute_insert(sqlite3_stmt *res)
42 {
43 int rc;
44
@@ -66,7 +72,7 @@ static void add_stmt_to_list(sqlite3_stmt *res)
72 statements[idx++] = res;
73 }
74
69 -static int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement) {
75 +int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement) {
76 int rc = sqlite3_prepare_v2(database, query, -1, statement, 0);
77 if (likely(rc == SQLITE_OK))
78 add_stmt_to_list(*statement);
database/sqlite/sqlite_functions.h
+2
@@ -78,4 +78,6 @@ extern int get_host_id(uuid_t *node_id, uuid_t *host_id);
78 extern void invalidate_node_instances(uuid_t *host_id, uuid_t *claim_id);
79 extern struct node_instance_list *get_node_list(void);
80 extern void sql_load_node_id(RRDHOST *host);
81 +extern int execute_insert(sqlite3_stmt *res);
82 +extern int prepare_statement(sqlite3 *database, char *query, sqlite3_stmt **statement);
83 #endif //NETDATA_SQLITE_FUNCTIONS_H
database/sqlite/sqlite_health.c new
+931
@@ -0,0 +1,931 @@
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 +}
database/sqlite/sqlite_health.h new
+16
@@ -0,0 +1,16 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_SQLITE_HEALTH_H
4 +#define NETDATA_SQLITE_HEALTH_H
5 +#include "../../daemon/common.h"
6 +#include "sqlite3.h"
7 +
8 +extern sqlite3 *db_meta;
9 +extern void sql_health_alarm_log_load(RRDHOST *host);
10 +extern int sql_create_health_log_table(RRDHOST *host);
11 +extern void sql_health_alarm_log_update(RRDHOST *host, ALARM_ENTRY *ae);
12 +extern void sql_health_alarm_log_insert(RRDHOST *host, ALARM_ENTRY *ae);
13 +extern void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae);
14 +extern void sql_health_alarm_log_cleanup(RRDHOST *host);
15 +extern int alert_hash_and_store_config(uuid_t hash_id, struct alert_config *cfg);
16 +#endif //NETDATA_SQLITE_HEALTH_H
health/health.c
+7 -2
@@ -662,6 +662,8 @@ void *health_main(void *ptr) {
662 int min_run_every = (int)config_get_number(CONFIG_SECTION_HEALTH, "run at least every seconds", 10);
663 if(min_run_every < 1) min_run_every = 1;
664
665 + int cleanup_sql_every_loop = 7200 / min_run_every;
666 +
667 time_t now = now_realtime_sec();
668 time_t hibernation_delay = config_get_number(CONFIG_SECTION_HEALTH, "postpone alarms during hibernation for seconds", 60);
669
@@ -716,6 +718,9 @@ void *health_main(void *ptr) {
718 host->health_delay_up_to = 0;
719 }
720
721 + if(likely(!host->health_log_fp) && (loop == 1 || loop % cleanup_sql_every_loop == 0))
722 + sql_health_alarm_log_cleanup(host);
723 +
724 rrdhost_rdlock(host);
725
726 // the first loop is to lookup values from the db
@@ -956,7 +961,7 @@ void *health_main(void *ptr) {
961
962 if(likely(!rrdcalc_isrepeating(rc))) {
963 ALARM_ENTRY *ae = health_create_alarm_entry(
959 - host, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id,
964 + host, rc->id, rc->next_event_id++, rc->config_hash_id, now, rc->name, rc->rrdset->id,
965 rc->rrdset->family, rc->classification, rc->component, rc->type, rc->exec, rc->recipient, now - rc->last_status_change,
966 rc->old_value, rc->value, rc->status, status, rc->source, rc->units, rc->info,
967 rc->delay_last,
@@ -1006,7 +1011,7 @@ void *health_main(void *ptr) {
1011 if(unlikely(repeat_every > 0 && (rc->last_repeat + repeat_every) <= now)) {
1012 rc->last_repeat = now;
1013 ALARM_ENTRY *ae = health_create_alarm_entry(
1009 - host, rc->id, rc->next_event_id++, now, rc->name, rc->rrdset->id,
1014 + host, rc->id, rc->next_event_id++, rc->config_hash_id, now, rc->name, rc->rrdset->id,
1015 rc->rrdset->family, rc->classification, rc->component, rc->type, rc->exec, rc->recipient, now - rc->last_status_change,
1016 rc->old_value, rc->value, rc->old_status, rc->status, rc->source, rc->units, rc->info,
1017 rc->delay_last,
health/health.h
+1
@@ -63,6 +63,7 @@ extern ALARM_ENTRY* health_create_alarm_entry(
63 RRDHOST *host,
64 uint32_t alarm_id,
65 uint32_t alarm_event_id,
66 + uuid_t config_hash_id,
67 time_t when,
68 const char *name,
69 const char *chart,
health/health_config.c
+122 -7
@@ -503,6 +503,40 @@ static inline void strip_quotes(char *s) {
503 }
504 }
505
506 +static inline void alert_config_free(struct alert_config *cfg)
507 +{
508 + freez(cfg->alarm);
509 + freez(cfg->template_key);
510 + freez(cfg->os);
511 + freez(cfg->host);
512 + freez(cfg->on);
513 + freez(cfg->families);
514 + freez(cfg->plugin);
515 + freez(cfg->module);
516 + freez(cfg->charts);
517 + freez(cfg->lookup);
518 + freez(cfg->calc);
519 + freez(cfg->warn);
520 + freez(cfg->crit);
521 + freez(cfg->every);
522 + freez(cfg->green);
523 + freez(cfg->red);
524 + freez(cfg->exec);
525 + freez(cfg->to);
526 + freez(cfg->units);
527 + freez(cfg->info);
528 + freez(cfg->classification);
529 + freez(cfg->component);
530 + freez(cfg->type);
531 + freez(cfg->delay);
532 + freez(cfg->options);
533 + freez(cfg->repeat);
534 + freez(cfg->host_labels);
535 + freez(cfg->p_db_lookup_dimensions);
536 + freez(cfg->p_db_lookup_method);
537 + freez(cfg);
538 +}
539 +
540 static int health_readfile(const char *filename, void *data) {
541 RRDHOST *host = (RRDHOST *)data;
542
@@ -577,6 +611,7 @@ static int health_readfile(const char *filename, void *data) {
611
612 RRDCALC *rc = NULL;
613 RRDCALCTEMPLATE *rt = NULL;
614 + struct alert_config *alert_cfg = NULL;
615
616 int ignore_this = 0;
617 size_t line = 0, append = 0;
@@ -626,16 +661,18 @@ static int health_readfile(const char *filename, void *data) {
661
662 if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
663 if(rc) {
629 - if(ignore_this || !rrdcalc_add_alarm_from_config(host, rc)) {
664 + if(ignore_this || !alert_hash_and_store_config(rc->config_hash_id, alert_cfg) || !rrdcalc_add_alarm_from_config(host, rc)) {
665 rrdcalc_free(rc);
666 + alert_config_free(alert_cfg);
667 }
668 // health_add_alarms_loop(host, rc, ignore_this) ;
669 }
670
671 if(rt) {
636 - if (ignore_this || !rrdcalctemplate_add_template_from_config(host, rt))
672 + if (ignore_this || !alert_hash_and_store_config(rt->config_hash_id, alert_cfg) || !rrdcalctemplate_add_template_from_config(host, rt)) {
673 rrdcalctemplate_free(rt);
638 -
674 + alert_config_free(alert_cfg);
675 + }
676 rt = NULL;
677 }
678
@@ -652,25 +689,30 @@ static int health_readfile(const char *filename, void *data) {
689 rc->old_status = RRDCALC_STATUS_UNINITIALIZED;
690 rc->warn_repeat_every = host->health_default_warn_repeat_every;
691 rc->crit_repeat_every = host->health_default_crit_repeat_every;
692 + alert_cfg = callocz(1, sizeof(struct alert_config));
693
694 if(rrdvar_fix_name(rc->name))
695 error("Health configuration renamed alarm '%s' to '%s'", value, rc->name);
696
697 + alert_cfg->alarm = strdupz(rc->name);
698 ignore_this = 0;
699 }
700 else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
701 if(rc) {
702 // health_add_alarms_loop(host, rc, ignore_this) ;
664 - if(ignore_this || !rrdcalc_add_alarm_from_config(host, rc)) {
703 + if(ignore_this || !alert_hash_and_store_config(rc->config_hash_id, alert_cfg) || !rrdcalc_add_alarm_from_config(host, rc)) {
704 rrdcalc_free(rc);
705 + alert_config_free(alert_cfg);
706 }
707
708 rc = NULL;
709 }
710
711 if(rt) {
672 - if(ignore_this || !rrdcalctemplate_add_template_from_config(host, rt))
712 + if(ignore_this || !alert_hash_and_store_config(rt->config_hash_id, alert_cfg) || !rrdcalctemplate_add_template_from_config(host, rt)) {
713 rrdcalctemplate_free(rt);
714 + alert_config_free(alert_cfg);
715 + }
716 }
717
718 rt = callocz(1, sizeof(RRDCALCTEMPLATE));
@@ -682,14 +724,17 @@ static int health_readfile(const char *filename, void *data) {
724 rt->delay_multiplier = 1.0;
725 rt->warn_repeat_every = host->health_default_warn_repeat_every;
726 rt->crit_repeat_every = host->health_default_crit_repeat_every;
727 + alert_cfg = callocz(1, sizeof(struct alert_config));
728
729 if(rrdvar_fix_name(rt->name))
730 error("Health configuration renamed template '%s' to '%s'", value, rt->name);
731
732 + alert_cfg->template_key = strdupz(rt->name);
733 ignore_this = 0;
734 }
735 else if(hash == hash_os && !strcasecmp(key, HEALTH_OS_KEY)) {
736 char *os_match = value;
737 + alert_cfg->os = strdupz(value);
738 SIMPLE_PATTERN *os_pattern = simple_pattern_create(os_match, NULL, SIMPLE_PATTERN_EXACT);
739
740 if(!simple_pattern_matches(os_pattern, host->os)) {
@@ -706,6 +751,7 @@ static int health_readfile(const char *filename, void *data) {
751 }
752 else if(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
753 char *host_match = value;
754 + alert_cfg->host = strdupz(value);
755 SIMPLE_PATTERN *host_pattern = simple_pattern_create(host_match, NULL, SIMPLE_PATTERN_EXACT);
756
757 if(!simple_pattern_matches(host_pattern, host->hostname)) {
@@ -722,6 +768,7 @@ static int health_readfile(const char *filename, void *data) {
768 }
769 else if(rc) {
770 if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
771 + alert_cfg->on = strdupz(value);
772 if(rc->chart) {
773 if(strcmp(rc->chart, value) != 0)
774 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -733,6 +780,7 @@ static int health_readfile(const char *filename, void *data) {
780 rc->hash_chart = simple_hash(rc->chart);
781 }
782 else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) {
783 + alert_cfg->classification = strdupz(value);
784 if(rc->classification) {
785 if(strcmp(rc->classification, value) != 0)
786 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -744,6 +792,7 @@ static int health_readfile(const char *filename, void *data) {
792 strip_quotes(rc->classification);
793 }
794 else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) {
795 + alert_cfg->component = strdupz(value);
796 if(rc->component) {
797 if(strcmp(rc->component, value) != 0)
798 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -755,6 +804,7 @@ static int health_readfile(const char *filename, void *data) {
804 strip_quotes(rc->component);
805 }
806 else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) {
807 + alert_cfg->type = strdupz(value);
808 if(rc->type) {
809 if(strcmp(rc->type, value) != 0)
810 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -766,18 +816,32 @@ static int health_readfile(const char *filename, void *data) {
816 strip_quotes(rc->type);
817 }
818 else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
819 + alert_cfg->lookup = strdupz(value);
820 health_parse_db_lookup(line, filename, value, &rc->group, &rc->after, &rc->before,
821 &rc->update_every, &rc->options, &rc->dimensions, &rc->foreachdim);
822 if(rc->foreachdim) {
823 rc->spdim = health_pattern_from_foreach(rc->foreachdim);
824 }
825 + if (rc->after) {
826 + if (rc->dimensions)
827 + alert_cfg->p_db_lookup_dimensions = strdupz(rc->dimensions);
828 + if (rc->group)
829 + alert_cfg->p_db_lookup_method = strdupz(group_method2string(rc->group));
830 + alert_cfg->p_db_lookup_options = rc->options;
831 + alert_cfg->p_db_lookup_after = rc->after;
832 + alert_cfg->p_db_lookup_before = rc->before;
833 + alert_cfg->p_update_every = rc->update_every;
834 + }
835 }
836 else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
837 + alert_cfg->every = strdupz(value);
838 if(!config_parse_duration(value, &rc->update_every))
839 error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
840 line, filename, rc->name, key, value);
841 + alert_cfg->p_update_every = rc->update_every;
842 }
843 else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
844 + alert_cfg->green = strdupz(value);
845 char *e;
846 rc->green = str2ld(value, &e);
847 if(e && *e) {
@@ -786,6 +850,7 @@ static int health_readfile(const char *filename, void *data) {
850 }
851 }
852 else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
853 + alert_cfg->red = strdupz(value);
854 char *e;
855 rc->red = str2ld(value, &e);
856 if(e && *e) {
@@ -794,6 +859,7 @@ static int health_readfile(const char *filename, void *data) {
859 }
860 }
861 else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
862 + alert_cfg->calc = strdupz(value);
863 const char *failed_at = NULL;
864 int error = 0;
865 rc->calculation = expression_parse(value, &failed_at, &error);
@@ -803,6 +869,7 @@ static int health_readfile(const char *filename, void *data) {
869 }
870 }
871 else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
872 + alert_cfg->warn = strdupz(value);
873 const char *failed_at = NULL;
874 int error = 0;
875 rc->warning = expression_parse(value, &failed_at, &error);
@@ -812,6 +879,7 @@ static int health_readfile(const char *filename, void *data) {
879 }
880 }
881 else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
882 + alert_cfg->crit = strdupz(value);
883 const char *failed_at = NULL;
884 int error = 0;
885 rc->critical = expression_parse(value, &failed_at, &error);
@@ -821,6 +889,7 @@ static int health_readfile(const char *filename, void *data) {
889 }
890 }
891 else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
892 + alert_cfg->exec = strdupz(value);
893 if(rc->exec) {
894 if(strcmp(rc->exec, value) != 0)
895 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -831,6 +900,7 @@ static int health_readfile(const char *filename, void *data) {
900 rc->exec = strdupz(value);
901 }
902 else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
903 + alert_cfg->to = strdupz(value);
904 if(rc->recipient) {
905 if(strcmp(rc->recipient, value) != 0)
906 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -841,6 +911,7 @@ static int health_readfile(const char *filename, void *data) {
911 rc->recipient = strdupz(value);
912 }
913 else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
914 + alert_cfg->units = strdupz(value);
915 if(rc->units) {
916 if(strcmp(rc->units, value) != 0)
917 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -852,6 +923,7 @@ static int health_readfile(const char *filename, void *data) {
923 strip_quotes(rc->units);
924 }
925 else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
926 + alert_cfg->info = strdupz(value);
927 if(rc->info) {
928 if(strcmp(rc->info, value) != 0)
929 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -863,17 +935,21 @@ static int health_readfile(const char *filename, void *data) {
935 strip_quotes(rc->info);
936 }
937 else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
938 + alert_cfg->delay = strdupz(value);
939 health_parse_delay(line, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
940 }
941 else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
942 + alert_cfg->options = strdupz(value);
943 rc->options |= health_parse_options(value);
944 }
945 else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){
946 + alert_cfg->repeat = strdupz(value);
947 health_parse_repeat(line, filename, value,
948 &rc->warn_repeat_every,
949 &rc->crit_repeat_every);
950 }
951 else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) {
952 + alert_cfg->host_labels = strdupz(value);
953 if(rc->labels) {
954 if(strcmp(rc->labels, value) != 0)
955 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'.",
@@ -887,6 +963,7 @@ static int health_readfile(const char *filename, void *data) {
963 rc->splabels = simple_pattern_create(rc->labels, NULL, SIMPLE_PATTERN_EXACT);
964 }
965 else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) {
966 + alert_cfg->plugin = strdupz(value);
967 freez(rc->plugin_match);
968 simple_pattern_free(rc->plugin_pattern);
969
@@ -894,6 +971,7 @@ static int health_readfile(const char *filename, void *data) {
971 rc->plugin_pattern = simple_pattern_create(rc->plugin_match, NULL, SIMPLE_PATTERN_EXACT);
972 }
973 else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) {
974 + alert_cfg->module = strdupz(value);
975 freez(rc->module_match);
976 simple_pattern_free(rc->module_pattern);
977
@@ -907,6 +985,7 @@ static int health_readfile(const char *filename, void *data) {
985 }
986 else if(rt) {
987 if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
988 + alert_cfg->on = strdupz(value);
989 if(rt->context) {
990 if(strcmp(rt->context, value) != 0)
991 error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -918,6 +997,7 @@ static int health_readfile(const char *filename, void *data) {
997 rt->hash_context = simple_hash(rt->context);
998 }
999 else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) {
1000 + alert_cfg->classification = strdupz(value);
1001 if(rt->classification) {
1002 if(strcmp(rt->classification, value) != 0)
1003 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -929,6 +1009,7 @@ static int health_readfile(const char *filename, void *data) {
1009 strip_quotes(rt->classification);
1010 }
1011 else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) {
1012 + alert_cfg->component = strdupz(value);
1013 if(rt->component) {
1014 if(strcmp(rt->component, value) != 0)
1015 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -940,6 +1021,7 @@ static int health_readfile(const char *filename, void *data) {
1021 strip_quotes(rt->component);
1022 }
1023 else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) {
1024 + alert_cfg->type = strdupz(value);
1025 if(rt->type) {
1026 if(strcmp(rt->type, value) != 0)
1027 error("Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -951,6 +1033,7 @@ static int health_readfile(const char *filename, void *data) {
1033 strip_quotes(rt->type);
1034 }
1035 else if(hash == hash_families && !strcasecmp(key, HEALTH_FAMILIES_KEY)) {
1036 + alert_cfg->families = strdupz(value);
1037 freez(rt->family_match);
1038 simple_pattern_free(rt->family_pattern);
1039
@@ -958,6 +1041,7 @@ static int health_readfile(const char *filename, void *data) {
1041 rt->family_pattern = simple_pattern_create(rt->family_match, NULL, SIMPLE_PATTERN_EXACT);
1042 }
1043 else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) {
1044 + alert_cfg->plugin = strdupz(value);
1045 freez(rt->plugin_match);
1046 simple_pattern_free(rt->plugin_pattern);
1047
@@ -965,6 +1049,7 @@ static int health_readfile(const char *filename, void *data) {
1049 rt->plugin_pattern = simple_pattern_create(rt->plugin_match, NULL, SIMPLE_PATTERN_EXACT);
1050 }
1051 else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) {
1052 + alert_cfg->module = strdupz(value);
1053 freez(rt->module_match);
1054 simple_pattern_free(rt->module_pattern);
1055
@@ -972,6 +1057,7 @@ static int health_readfile(const char *filename, void *data) {
1057 rt->module_pattern = simple_pattern_create(rt->module_match, NULL, SIMPLE_PATTERN_EXACT);
1058 }
1059 else if(hash == hash_charts && !strcasecmp(key, HEALTH_CHARTS_KEY)) {
1060 + alert_cfg->charts = strdupz(value);
1061 freez(rt->charts_match);
1062 simple_pattern_free(rt->charts_pattern);
1063
@@ -979,18 +1065,32 @@ static int health_readfile(const char *filename, void *data) {
1065 rt->charts_pattern = simple_pattern_create(rt->charts_match, NULL, SIMPLE_PATTERN_EXACT);
1066 }
1067 else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
1068 + alert_cfg->lookup = strdupz(value);
1069 health_parse_db_lookup(line, filename, value, &rt->group, &rt->after, &rt->before,
1070 &rt->update_every, &rt->options, &rt->dimensions, &rt->foreachdim);
1071 if(rt->foreachdim) {
1072 rt->spdim = health_pattern_from_foreach(rt->foreachdim);
1073 }
1074 + if (rt->after) {
1075 + if (rt->dimensions)
1076 + alert_cfg->p_db_lookup_dimensions = strdupz(rt->dimensions);
1077 + if (rt->group)
1078 + alert_cfg->p_db_lookup_method = strdupz(group_method2string(rt->group));
1079 + alert_cfg->p_db_lookup_options = rt->options;
1080 + alert_cfg->p_db_lookup_after = rt->after;
1081 + alert_cfg->p_db_lookup_before = rt->before;
1082 + alert_cfg->p_update_every = rt->update_every;
1083 + }
1084 }
1085 else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
1086 + alert_cfg->every = strdupz(value);
1087 if(!config_parse_duration(value, &rt->update_every))
1088 error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
1089 line, filename, rt->name, key, value);
1090 + alert_cfg->p_update_every = rt->update_every;
1091 }
1092 else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
1093 + alert_cfg->green = strdupz(value);
1094 char *e;
1095 rt->green = str2ld(value, &e);
1096 if(e && *e) {
@@ -999,6 +1099,7 @@ static int health_readfile(const char *filename, void *data) {
1099 }
1100 }
1101 else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
1102 + alert_cfg->red = strdupz(value);
1103 char *e;
1104 rt->red = str2ld(value, &e);
1105 if(e && *e) {
@@ -1007,6 +1108,7 @@ static int health_readfile(const char *filename, void *data) {
1108 }
1109 }
1110 else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
1111 + alert_cfg->calc = strdupz(value);
1112 const char *failed_at = NULL;
1113 int error = 0;
1114 rt->calculation = expression_parse(value, &failed_at, &error);
@@ -1016,6 +1118,7 @@ static int health_readfile(const char *filename, void *data) {
1118 }
1119 }
1120 else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
1121 + alert_cfg->warn = strdupz(value);
1122 const char *failed_at = NULL;
1123 int error = 0;
1124 rt->warning = expression_parse(value, &failed_at, &error);
@@ -1025,6 +1128,7 @@ static int health_readfile(const char *filename, void *data) {
1128 }
1129 }
1130 else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
1131 + alert_cfg->crit = strdupz(value);
1132 const char *failed_at = NULL;
1133 int error = 0;
1134 rt->critical = expression_parse(value, &failed_at, &error);
@@ -1034,6 +1138,7 @@ static int health_readfile(const char *filename, void *data) {
1138 }
1139 }
1140 else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
1141 + alert_cfg->exec = strdupz(value);
1142 if(rt->exec) {
1143 if(strcmp(rt->exec, value) != 0)
1144 error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -1044,6 +1149,7 @@ static int health_readfile(const char *filename, void *data) {
1149 rt->exec = strdupz(value);
1150 }
1151 else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
1152 + alert_cfg->to = strdupz(value);
1153 if(rt->recipient) {
1154 if(strcmp(rt->recipient, value) != 0)
1155 error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -1054,6 +1160,7 @@ static int health_readfile(const char *filename, void *data) {
1160 rt->recipient = strdupz(value);
1161 }
1162 else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
1163 + alert_cfg->units = strdupz(value);
1164 if(rt->units) {
1165 if(strcmp(rt->units, value) != 0)
1166 error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -1065,6 +1172,7 @@ static int health_readfile(const char *filename, void *data) {
1172 strip_quotes(rt->units);
1173 }
1174 else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
1175 + alert_cfg->info = strdupz(value);
1176 if(rt->info) {
1177 if(strcmp(rt->info, value) != 0)
1178 error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -1076,17 +1184,21 @@ static int health_readfile(const char *filename, void *data) {
1184 strip_quotes(rt->info);
1185 }
1186 else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
1187 + alert_cfg->delay = strdupz(value);
1188 health_parse_delay(line, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
1189 }
1190 else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
1191 + alert_cfg->options = strdupz(value);
1192 rt->options |= health_parse_options(value);
1193 }
1194 else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){
1195 + alert_cfg->repeat = strdupz(value);
1196 health_parse_repeat(line, filename, value,
1197 &rt->warn_repeat_every,
1198 &rt->crit_repeat_every);
1199 }
1200 else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) {
1201 + alert_cfg->host_labels = strdupz(value);
1202 if(rt->labels) {
1203 if(strcmp(rt->labels, value) != 0)
1204 error("Health configuration at line %zu of file '%s' for template '%s' has key '%s' twice, once with value '%s' and later with value '%s'. Using ('%s').",
@@ -1112,14 +1224,17 @@ static int health_readfile(const char *filename, void *data) {
1224
1225 if(rc) {
1226 //health_add_alarms_loop(host, rc, ignore_this) ;
1115 - if(ignore_this || !rrdcalc_add_alarm_from_config(host, rc)) {
1227 + if(ignore_this || !alert_hash_and_store_config(rc->config_hash_id, alert_cfg) || !rrdcalc_add_alarm_from_config(host, rc)) {
1228 rrdcalc_free(rc);
1229 + alert_config_free(alert_cfg);
1230 }
1231 }
1232
1233 if(rt) {
1121 - if(ignore_this || !rrdcalctemplate_add_template_from_config(host, rt))
1234 + if(ignore_this || !alert_hash_and_store_config(rt->config_hash_id, alert_cfg) || !rrdcalctemplate_add_template_from_config(host, rt)) {
1235 rrdcalctemplate_free(rt);
1236 + alert_config_free(alert_cfg);
1237 + }
1238 }
1239
1240 fclose(fp);
health/health_json.c
+9
@@ -15,6 +15,8 @@ void health_string2json(BUFFER *wb, const char *prefix, const char *label, const
15
16 void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host) {
17 char *edit_command = ae->source ? health_edit_command_from_source(ae->source) : strdupz("UNKNOWN=0");
18 + char config_hash_id[GUID_LEN + 1];
19 + uuid_unparse_lower(ae->config_hash_id, config_hash_id);
20
21 buffer_sprintf(wb,
22 "\n\t{\n"
@@ -24,6 +26,7 @@ void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host)
26 "\t\t\"unique_id\": %u,\n"
27 "\t\t\"alarm_id\": %u,\n"
28 "\t\t\"alarm_event_id\": %u,\n"
29 + "\t\t\"config_hash_id\": \"%s\",\n"
30 "\t\t\"name\": \"%s\",\n"
31 "\t\t\"chart\": \"%s\",\n"
32 "\t\t\"family\": \"%s\",\n"
@@ -59,6 +62,7 @@ void health_alarm_entry2json_nolock(BUFFER *wb, ALARM_ENTRY *ae, RRDHOST *host)
62 , ae->unique_id
63 , ae->alarm_id
64 , ae->alarm_event_id
65 + , config_hash_id
66 , ae->name
67 , ae->chart
68 , ae->family
@@ -187,9 +191,13 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC
191 }
192 }
193
194 + char hash_id[GUID_LEN + 1];
195 + uuid_unparse_lower(rc->config_hash_id, hash_id);
196 +
197 buffer_sprintf(wb,
198 "\t\t\"%s.%s\": {\n"
199 "\t\t\t\"id\": %lu,\n"
200 + "\t\t\t\"config_hash_id\": \"%s\",\n"
201 "\t\t\t\"name\": \"%s\",\n"
202 "\t\t\t\"chart\": \"%s\",\n"
203 "\t\t\t\"family\": \"%s\",\n"
@@ -221,6 +229,7 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC
229 "\t\t\t\"last_repeat\": \"%lu\",\n"
230 , rc->chart, rc->name
231 , (unsigned long)rc->id
232 + , hash_id
233 , rc->name
234 , rc->chart
235 , (rc->rrdset && rc->rrdset->family)?rc->rrdset->family:""
health/health_log.c
+33 -24
@@ -38,39 +38,41 @@ static inline void health_log_rotate(RRDHOST *host) {
38 }
39
40 if(unlikely(host->health_log_entries_written > rotate_every)) {
41 - health_alarm_log_close(host);
41 + if(unlikely(host->health_log_fp)) {
42 + health_alarm_log_close(host);
43
43 - char old_filename[FILENAME_MAX + 1];
44 - snprintfz(old_filename, FILENAME_MAX, "%s.old", host->health_log_filename);
44 + char old_filename[FILENAME_MAX + 1];
45 + snprintfz(old_filename, FILENAME_MAX, "%s.old", host->health_log_filename);
46
46 - if(unlink(old_filename) == -1 && errno != ENOENT)
47 - error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, old_filename);
47 + if(unlink(old_filename) == -1 && errno != ENOENT)
48 + error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, old_filename);
49
49 - if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT)
50 - error("HEALTH [%s]: cannot move file '%s' to '%s'.", host->hostname, host->health_log_filename, old_filename);
50 + if(link(host->health_log_filename, old_filename) == -1 && errno != ENOENT)
51 + error("HEALTH [%s]: cannot move file '%s' to '%s'.", host->hostname, host->health_log_filename, old_filename);
52
52 - if(unlink(host->health_log_filename) == -1 && errno != ENOENT)
53 - error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, host->health_log_filename);
53 + if(unlink(host->health_log_filename) == -1 && errno != ENOENT)
54 + error("HEALTH [%s]: cannot remove old alarms log file '%s'", host->hostname, host->health_log_filename);
55
55 - // open it with truncate
56 - host->health_log_fp = fopen(host->health_log_filename, "w");
56 + // open it with truncate
57 + host->health_log_fp = fopen(host->health_log_filename, "w");
58
58 - if(host->health_log_fp)
59 - fclose(host->health_log_fp);
60 - else
61 - error("HEALTH [%s]: cannot truncate health log '%s'", host->hostname, host->health_log_filename);
59 + if(host->health_log_fp)
60 + fclose(host->health_log_fp);
61 + else
62 + error("HEALTH [%s]: cannot truncate health log '%s'", host->hostname, host->health_log_filename);
63
63 - host->health_log_fp = NULL;
64 + host->health_log_fp = NULL;
65
65 - host->health_log_entries_written = 0;
66 - health_alarm_log_open(host);
66 + host->health_log_entries_written = 0;
67 + health_alarm_log_open(host);
68 + }
69 }
70 }
71
72 inline void health_label_log_save(RRDHOST *host) {
73 health_log_rotate(host);
74
73 - if(likely(host->health_log_fp)) {
75 + if(unlikely(host->health_log_fp)) {
76 BUFFER *wb = buffer_create(1024);
77 rrdhost_check_rdlock(host);
78 netdata_rwlock_rdlock(&host->labels.labels_rwlock);
@@ -101,7 +103,7 @@ inline void health_label_log_save(RRDHOST *host) {
103
104 inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) {
105 health_log_rotate(host);
104 - if(likely(host->health_log_fp)) {
106 + if(unlikely(host->health_log_fp)) {
107 if(unlikely(fprintf(host->health_log_fp
108 , "%c\t%s"
109 "\t%08x\t%08x\t%08x\t%08x\t%08x"
@@ -155,7 +157,9 @@ inline void health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae) {
157 ae->flags |= HEALTH_ENTRY_FLAG_SAVED;
158 host->health_log_entries_written++;
159 }
158 - }
160 + }else
161 + sql_health_alarm_log_save(host, ae);
162 +
163 #ifdef ENABLE_ACLK
164 if (netdata_cloud_setting) {
165 if ((ae->new_status == RRDCALC_STATUS_WARNING || ae->new_status == RRDCALC_STATUS_CRITICAL) ||
@@ -392,9 +396,13 @@ static inline ssize_t health_alarm_log_read(RRDHOST *host, FILE *fp, const char
396 if(unlikely(*pointers[0] == 'A')) {
397 ae->next = host->health_log.alarms;
398 host->health_log.alarms = ae;
399 + sql_health_alarm_log_insert(host, ae);
400 loaded++;
401 }
397 - else updated++;
402 + else {
403 + sql_health_alarm_log_update(host, ae);
404 + updated++;
405 + }
406
407 if(unlikely(ae->unique_id > host->health_max_unique_id))
408 host->health_max_unique_id = ae->unique_id;
@@ -444,8 +452,6 @@ inline void health_alarm_log_load(RRDHOST *host) {
452 health_alarm_log_read(host, fp, host->health_log_filename);
453 fclose(fp);
454 }
447 -
448 - health_alarm_log_open(host);
455 }
456
457
@@ -456,6 +462,7 @@ inline ALARM_ENTRY* health_create_alarm_entry(
462 RRDHOST *host,
463 uint32_t alarm_id,
464 uint32_t alarm_event_id,
465 + uuid_t config_hash_id,
466 time_t when,
467 const char *name,
468 const char *chart,
@@ -487,6 +494,8 @@ inline ALARM_ENTRY* health_create_alarm_entry(
494 ae->hash_chart = simple_hash(ae->chart);
495 }
496
497 + uuid_copy(ae->config_hash_id, *((uuid_t *) config_hash_id));
498 +
499 if(family)
500 ae->family = strdupz(family);
501