@cryptotaxi247 / netdata-1 / commits / f466b8aef

DYNCFG: dynamically configured alerts (#16779)

* cleanup alerts * fix references * fix references * fix references * load alerts once and apply them to each node * simplify health_create_alarm_entry() * Compile without warnings with compiler flags: -Wall -Wextra -Wformat=2 -Wshadow -Wno-format-nonliteral -Winit-self * code re-organization and cleanup * generate patterns when applying prototypes; give unique dyncfg names to all alerts * eval expressions keep the source and the parsed_as as STRING pointers * renamed host to node in dyncfg ids * renamed host to node in dyncfg ids * add all cloud roles to the list of parsed X-Netdata-Role header and also default to member access level * working functionality * code re-organization: moved health event-loop to a new file, moved health globals to health.c * rrdcalctemplate is removed; alert_cfg is removed; foreach dimension is removed; RRDCALCs are now instanciated only when they are linked to RRDSETs * dyncfg alert prototypes initialization for alerts * health dyncfg split to separate file * cleanup not-needed code * normalize matches between parsing and json * also detect !* for disabled alerts * dyncfg capability disabled * Store alert config part1 * Add rrdlabels_common_count * wip health variables lookup without indexes * Improve rrdlabels_common_count by reusing rrdlabels_find_label_with_key_unsafe with an additional parameter * working variables with runtime lookup * working variables with runtime lookup * delete rrddimvar and rrdfamily index * remove rrdsetvar; now all variables are in RRDVARs inside hosts and charts * added /api/v1/variable that resolves a variable the same way alerts do * remove rrdcalc from eval * remove debug code * remove duplicate assignment * Fix memory leak * all alert variables are now handled by alert_variable_lookup() and EVAL is now independent of alerts * hide all internal structures of EVAL * Enable -Wformat flag Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud> * Adjust binding for calculation, warning, critical * Remove unused macro * Update config hash id * use the right info and summary in alerts log * use synchronous queries for alerts * Handle cases when config_hash_id is missing from health_log * remove deadlock from health worker * parsing to json payload for health alert prototypes * cleaner parsing and avoiding memory leaks in case of duplicate members in json * fix left-over rename of function * Keep original lookup field to send to the cloud Cleanup / rename function to store config Remove unused DEFINEs, functions * Use ac->lookup * link jobs to the host when the template is registered; do not accept running a function without a host * full dyncfg support for health alerts, except action TEST * working dyncfg additions, updates, removals * fixed missing source, wrong status updates * add alerts by type, component, classification, recipient and module at the /api/v2/alerts endpoint * fix dyncfg unittest * rename functions * generalize the json-c parser macros and move them to libnetdata * report progress when enabling and disabling dyncfg templates * moved rrdcalc and rrdvar to health * update alarms * added schema for alerts; separated alert_action_options from rrdr_options; restructured the json payload for alerts * enable parsed json alerts; allow sending back accepted but disabled * added format_version for alerts payload; enables/disables status now is also inheritted by the status of the rules; fixed variable names in json output * remove the RRDHOST pointer from DYNCFG * Fix command field submitted to the cloud * do not send updates to creation requests, for DYNCFG jobs --------- Signed-off-by: Tasos Katsoulas <tasos@netdata.cloud> Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com> Co-authored-by: Tasos Katsoulas <tasos@netdata.cloud> Co-authored-by: ilyam8 <ilya@netdata.cloud>

Costa Tsaousis committed Jan 23, 2024 at 20:20 UTC f466b8aef52c1ea394651f5fe6b4586a5e39e5af
129 files changed +7250 -6804
CMakeLists.txt
+24 -22
@@ -595,8 +595,6 @@ set(LIBNETDATA_FILES
595 libnetdata/functions_evloop/functions_evloop.h
596 libnetdata/gorilla/gorilla.cc
597 libnetdata/gorilla/gorilla.h
598 - libnetdata/health/health.c
599 - libnetdata/health/health.h
598 libnetdata/july/july.c
599 libnetdata/july/july.h
600 libnetdata/inlined.h
@@ -657,6 +655,7 @@ set(LIBNETDATA_FILES
655 libnetdata/http/content_type.h
656 libnetdata/config/dyncfg.c
657 libnetdata/config/dyncfg.h
658 + libnetdata/json/json-c-parser-inline.h
659 )
660
661 if(ENABLE_PLUGIN_EBPF)
@@ -863,7 +862,6 @@ set(API_PLUGIN_FILES
862 web/api/formatters/charts2json.h
863 web/api/formatters/rrdset2json.c
864 web/api/formatters/rrdset2json.h
866 - web/api/health/health_cmdapi.c
865 web/api/ilove/ilove.c
866 web/api/ilove/ilove.h
867 web/rtc/webrtc.c
@@ -896,6 +894,19 @@ set(HEALTH_PLUGIN_FILES
894 health/health_config.c
895 health/health_json.c
896 health/health_log.c
897 + health/health_prototypes.c
898 + health/health_prototypes.h
899 + health/health_silencers.c
900 + health/health_silencers.h
901 + health/health_internals.h
902 + health/health_notifications.c
903 + health/health_event_loop.c
904 + health/health_dyncfg.c
905 + health/health_variable.c
906 + health/rrdcalc.c
907 + health/rrdcalc.h
908 + health/rrdvar.c
909 + health/rrdvar.h
910 )
911
912 set(IDLEJITTER_PLUGIN_FILES collectors/idlejitter.plugin/plugin_idlejitter.c)
@@ -944,16 +955,9 @@ set(RRD_PLUGIN_FILES
955 database/contexts/rrdcontext.c
956 database/contexts/rrdcontext.h
957 database/contexts/worker.c
947 - database/rrdcalc.c
948 - database/rrdcalc.h
949 - database/rrdcalctemplate.c
950 - database/rrdcalctemplate.h
958 database/rrdcollector.c
959 database/rrdcollector.h
960 database/rrddim.c
954 - database/rrddimvar.c
955 - database/rrddimvar.h
956 - database/rrdfamily.c
961 database/rrdfunctions.c
962 database/rrdfunctions.h
963 database/rrdfunctions-inline.c
@@ -967,10 +971,6 @@ set(RRD_PLUGIN_FILES
971 database/rrd.c
972 database/rrd.h
973 database/rrdset.c
970 - database/rrdsetvar.c
971 - database/rrdsetvar.h
972 - database/rrdvar.c
973 - database/rrdvar.h
974 database/storage_engine.c
975 database/storage_engine.h
976 database/ram/rrddim_mem.c
@@ -1410,11 +1410,11 @@ if(ENABLE_H2O)
1410 )
1411
1412 target_compile_options(h2o PRIVATE
1413 - -Wno-old-style-declaration
1413 + -Wno-all -Wno-extra
1414 + -Wno-shadow
1415 -Wno-deprecated-declarations
1415 - -Wno-unused-parameter
1416 - -Wno-sign-compare
1417 - -Wno-missing-field-initializers)
1416 + -Wformat
1417 + )
1418
1419 target_compile_options(h2o PUBLIC -DH2O_USE_LIBUV=0)
1420
@@ -1429,12 +1429,13 @@ endif()
1429
1430 add_library(judy STATIC ${LIBJUDY_SOURCES})
1431
1432 -target_compile_options(judy PUBLIC
1433 - -Wno-sign-compare
1434 - -Wno-implicit-fallthrough
1432 +target_compile_options(judy PRIVATE
1433 + -Wno-all -Wno-extra
1434 + -Wno-shadow
1435 + -Wformat
1436 )
1437
1437 -target_compile_definitions(judy PUBLIC
1438 +target_compile_definitions(judy PRIVATE
1439 JUDYL
1440 $<$<EQUAL:${CMAKE_SIZEOF_VOID_P},8>:JU_64BIT>
1441 )
@@ -2437,6 +2438,7 @@ install(FILES
2438
2439 install(FILES
2440 collectors/systemd-journal.plugin/schema.d/systemd-journal:monitored-directories.json
2441 + health/schema.d/health:alert:prototype.json
2442 DESTINATION usr/lib/netdata/conf.d/schema.d)
2443
2444 #
aclk/aclk_capas.c
+2
@@ -18,6 +18,7 @@ const struct capability *aclk_get_agent_capas()
18 { .name = "http_api_v2", .version = HTTP_API_V2_VERSION, .enabled = 1 },
19 { .name = "health", .version = 1, .enabled = 0 }, // index 7, below
20 { .name = "req_cancel", .version = 1, .enabled = 1 },
21 + //{ .name = "dyncfg", .version = 1, .enabled = 1 },
22 { .name = NULL, .version = 0, .enabled = 0 }
23 };
24 agent_capabilities[2].version = ml_capable() ? 1 : 0;
@@ -46,6 +47,7 @@ struct capability *aclk_get_node_instance_capas(RRDHOST *host)
47 { .name = "http_api_v2", .version = HTTP_API_V2_VERSION, .enabled = 1 },
48 { .name = "health", .version = 1, .enabled = host->health.health_enabled },
49 { .name = "req_cancel", .version = 1, .enabled = 1 },
50 + //{ .name = "dyncfg", .version = 1, .enabled = 1 },
51 { .name = NULL, .version = 0, .enabled = 0 }
52 };
53
aclk/mqtt_websockets/mqtt_wss_client.c
+5 -5
@@ -145,16 +145,16 @@ static void mws_connack_callback_ng(void *user_ctx, int code)
145
146 static ssize_t mqtt_send_cb(void *user_ctx, const void* buf, size_t len)
147 {
148 - mqtt_wss_client mqtt_wss_client = user_ctx;
148 + mqtt_wss_client client = user_ctx;
149 #ifdef DEBUG_ULTRA_VERBOSE
150 - mws_debug(mqtt_wss_client->log, "mqtt_pal_sendall(len=%d)", len);
150 + mws_debug(client->log, "mqtt_pal_sendall(len=%d)", len);
151 #endif
152 - int ret = ws_client_send(mqtt_wss_client->ws_client, WS_OP_BINARY_FRAME, buf, len);
152 + int ret = ws_client_send(client->ws_client, WS_OP_BINARY_FRAME, buf, len);
153 if (ret >= 0 && (size_t)ret != len) {
154 #ifdef DEBUG_ULTRA_VERBOSE
155 - mws_debug(mqtt_wss_client->log, "Not complete message sent (Msg=%d,Sent=%d). Need to arm POLLOUT!", len, ret);
155 + mws_debug(client->log, "Not complete message sent (Msg=%d,Sent=%d). Need to arm POLLOUT!", len, ret);
156 #endif
157 - mqtt_wss_client->mqtt_didnt_finish_write = 1;
157 + client->mqtt_didnt_finish_write = 1;
158 }
159 return ret;
160 }
collectors/apps.plugin/apps_plugin.c
+2 -2
@@ -1349,8 +1349,8 @@ void arl_callback_status_nonvoluntary_ctxt_switches(const char *name, uint32_t h
1349 pid_incremental_rate(stat, p->status_nonvoluntary_ctxt_switches, str2kernel_uint_t(procfile_lineword(aptr->ff, aptr->line, 1)));
1350 }
1351
1352 -static void update_proc_state_count(char proc_state) {
1353 - switch (proc_state) {
1352 +static void update_proc_state_count(char proc_stt) {
1353 + switch (proc_stt) {
1354 case 'S':
1355 proc_state_count[PROC_STATUS_SLEEPING] += 1;
1356 break;
collectors/cgroups.plugin/cgroup-charts.c
+1 -1
@@ -96,7 +96,7 @@ void update_cpu_utilization_limit_chart(struct cgroup *cg, NETDATA_DOUBLE cpu_li
96
97 cg->prev_cpu_usage = cpu_usage;
98
99 - rrdsetvar_custom_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, cpu_limit);
99 + rrdvar_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, cpu_limit);
100 rrdset_done(chart);
101 }
102
collectors/cgroups.plugin/cgroup-internals.h
+3 -3
@@ -287,16 +287,16 @@ struct cgroup {
287 char *filename_cpu_cfs_quota;
288 unsigned long long cpu_cfs_quota;
289
290 - const RRDSETVAR_ACQUIRED *chart_var_cpu_limit;
290 + const RRDVAR_ACQUIRED *chart_var_cpu_limit;
291 NETDATA_DOUBLE prev_cpu_usage;
292
293 char *filename_memory_limit;
294 unsigned long long memory_limit;
295 - const RRDSETVAR_ACQUIRED *chart_var_memory_limit;
295 + const RRDVAR_ACQUIRED *chart_var_memory_limit;
296
297 char *filename_memoryswap_limit;
298 unsigned long long memoryswap_limit;
299 - const RRDSETVAR_ACQUIRED *chart_var_memoryswap_limit;
299 + const RRDVAR_ACQUIRED *chart_var_memoryswap_limit;
300
301 const DICTIONARY_ITEM *cgroup_netdev_link;
302
collectors/cgroups.plugin/sys_fs_cgroup.c
+9 -7
@@ -1282,12 +1282,12 @@ cpu_limits2_err:
1282
1283 static inline int update_memory_limits(struct cgroup *cg) {
1284 char **filename = &cg->filename_memory_limit;
1285 - const RRDSETVAR_ACQUIRED **chart_var = &cg->chart_var_memory_limit;
1285 + const RRDVAR_ACQUIRED **chart_var = &cg->chart_var_memory_limit;
1286 unsigned long long *value = &cg->memory_limit;
1287
1288 if(*filename) {
1289 if(unlikely(!*chart_var)) {
1290 - *chart_var = rrdsetvar_custom_chart_variable_add_and_acquire(cg->st_mem_usage, "memory_limit");
1290 + *chart_var = rrdvar_chart_variable_add_and_acquire(cg->st_mem_usage, "memory_limit");
1291 if(!*chart_var) {
1292 collector_error("Cannot create cgroup %s chart variable '%s'. Will not update its limit anymore.", cg->id, "memory_limit");
1293 freez(*filename);
@@ -1303,7 +1303,8 @@ static inline int update_memory_limits(struct cgroup *cg) {
1303 *filename = NULL;
1304 }
1305 else {
1306 - rrdsetvar_custom_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1306 + rrdvar_chart_variable_set(
1307 + cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1308 return 1;
1309 }
1310 } else {
@@ -1318,11 +1319,12 @@ static inline int update_memory_limits(struct cgroup *cg) {
1319 char *s = "max\n\0";
1320 if(strcmp(s, buffer) == 0){
1321 *value = UINT64_MAX;
1321 - rrdsetvar_custom_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1322 + rrdvar_chart_variable_set(
1323 + cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1324 return 1;
1325 }
1326 *value = str2ull(buffer, NULL);
1325 - rrdsetvar_custom_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1327 + rrdvar_chart_variable_set(cg->st_mem_usage, *chart_var, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1328 return 1;
1329 }
1330 }
@@ -1400,7 +1402,7 @@ void update_cgroup_charts() {
1402 }
1403
1404 if(unlikely(!cg->chart_var_cpu_limit)) {
1403 - cg->chart_var_cpu_limit = rrdsetvar_custom_chart_variable_add_and_acquire(cg->st_cpu, "cpu_limit");
1405 + cg->chart_var_cpu_limit = rrdvar_chart_variable_add_and_acquire(cg->st_cpu, "cpu_limit");
1406 if(!cg->chart_var_cpu_limit) {
1407 collector_error("Cannot create cgroup %s chart variable 'cpu_limit'. Will not update its limit anymore.", cg->id);
1408 if(cg->filename_cpuset_cpus) freez(cg->filename_cpuset_cpus);
@@ -1430,7 +1432,7 @@ void update_cgroup_charts() {
1432 rrdset_is_obsolete___safe_from_collector_thread(cg->st_cpu_limit);
1433 cg->st_cpu_limit = NULL;
1434 }
1433 - rrdsetvar_custom_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, NAN);
1435 + rrdvar_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, NAN);
1436 }
1437 }
1438 }
collectors/cgroups.plugin/tests/test_doubles.c
+2 -2
@@ -101,7 +101,7 @@ collected_number rrddim_set_by_pointer(RRDSET *st, RRDDIM *rd, collected_number
101 return 0;
102 }
103
104 -const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name)
104 +const RRDVAR_ACQUIRED *rrdvar_chart_variable_add_and_acquire(RRDSET *st, const char *name)
105 {
106 UNUSED(st);
107 UNUSED(name);
@@ -109,7 +109,7 @@ const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET
109 return NULL;
110 }
111
112 -void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value)
112 +void rrdvar_chart_variable_set(RRDSET *st, const RRDVAR_ACQUIRED *rsa, NETDATA_DOUBLE value)
113 {
114 UNUSED(st);
115 UNUSED(rsa);
collectors/freeipmi.plugin/freeipmi_plugin.c
+66 -67
@@ -66,9 +66,8 @@ static void netdata_update_ipmi_sensor_reading(
66 , int sensor_bitmask_type
67 , int sensor_bitmask
68 , char **sensor_bitmask_strings
69 - , struct netdata_ipmi_state *state
70 -);
71 -static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *state, uint32_t events);
69 + , struct netdata_ipmi_state *stt);
70 +static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stt, uint32_t events);
71
72 // END NETDATA CODE
73 // ----------------------------------------------------------------------------
@@ -906,7 +905,7 @@ const char *netdata_collect_type_to_string(IPMI_COLLECTION_TYPE type) {
905 return "unknown";
906 }
907
909 -static void netdata_sensor_set_value(struct sensor *sn, void *sensor_reading, struct netdata_ipmi_state *state __maybe_unused) {
908 +static void netdata_sensor_set_value(struct sensor *sn, void *sensor_reading, struct netdata_ipmi_state *stt __maybe_unused) {
909 switch(sn->sensor_reading_type) {
910 case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL:
911 sn->sensor_reading.bool_value = *((uint8_t *)sensor_reading);
@@ -940,8 +939,7 @@ static void netdata_update_ipmi_sensor_reading(
939 , int sensor_bitmask_type __maybe_unused
940 , int sensor_bitmask __maybe_unused
941 , char **sensor_bitmask_strings __maybe_unused
943 - , struct netdata_ipmi_state *state
944 -) {
942 + , struct netdata_ipmi_state *stt) {
943 if(unlikely(sensor_state == IPMI_MONITORING_STATE_UNKNOWN &&
944 sensor_type == IPMI_MONITORING_SENSOR_TYPE_UNKNOWN &&
945 sensor_units == IPMI_MONITORING_SENSOR_UNITS_UNKNOWN &&
@@ -953,38 +951,38 @@ static void netdata_update_ipmi_sensor_reading(
951 if(unlikely(!sensor_name || !*sensor_name))
952 sensor_name = "UNNAMED";
953
956 - state->sensors.collected++;
954 + stt->sensors.collected++;
955
956 char key[SENSORS_DICT_KEY_SIZE + 1];
957 snprintfz(key, SENSORS_DICT_KEY_SIZE, "i%d_n%d_t%d_u%d_%s",
958 record_id, sensor_number, sensor_reading_type, sensor_units, sensor_name);
959
960 // find the sensor record
963 - const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(state->sensors.dict, key);
961 + const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(stt->sensors.dict, key);
962 if(likely(item)) {
963 // recurring collection
964
967 - if(state->debug)
965 + if(stt->debug)
966 fprintf(stderr, "%s: reusing sensor record for sensor '%s', id %d, number %d, type %d, state %d, units %d, reading_type %d\n",
967 program_name, sensor_name, record_id, sensor_number, sensor_type, sensor_state, sensor_units, sensor_reading_type);
968
969 struct sensor *sn = dictionary_acquired_item_value(item);
970
971 if(sensor_reading) {
974 - netdata_sensor_set_value(sn, sensor_reading, state);
975 - sn->last_collected_metric_ut = state->sensors.now_ut;
972 + netdata_sensor_set_value(sn, sensor_reading, stt);
973 + sn->last_collected_metric_ut = stt->sensors.now_ut;
974 }
975
976 sn->sensor_state = sensor_state;
977
980 - sn->last_collected_state_ut = state->sensors.now_ut;
978 + sn->last_collected_state_ut = stt->sensors.now_ut;
979
982 - dictionary_acquired_item_release(state->sensors.dict, item);
980 + dictionary_acquired_item_release(stt->sensors.dict, item);
981
982 return;
983 }
984
987 - if(state->debug)
985 + if(stt->debug)
986 fprintf(stderr, "Allocating new sensor data record for sensor '%s', id %d, number %d, type %d, state %d, units %d, reading_type %d\n",
987 sensor_name, record_id, sensor_number, sensor_type, sensor_state, sensor_units, sensor_reading_type);
988
@@ -993,12 +991,12 @@ static void netdata_update_ipmi_sensor_reading(
991 bool excluded_state = excluded_status_record_ids_check(record_id);
992
993 if(excluded_metric) {
996 - if(state->debug)
994 + if(stt->debug)
995 fprintf(stderr, "Sensor '%s' is excluded by excluded_record_ids_check()\n", sensor_name);
996 }
997
998 if(excluded_state) {
1001 - if(state->debug)
999 + if(stt->debug)
1000 fprintf(stderr, "Sensor '%s' is excluded for status check, by excluded_status_record_ids_check()\n", sensor_name);
1001 }
1002
@@ -1023,7 +1021,7 @@ static void netdata_update_ipmi_sensor_reading(
1021 t.units = "Celsius";
1022 t.family = "temperatures";
1023 t.chart_type = "line";
1026 - t.priority = state->sensors.priority + 10;
1024 + t.priority = stt->sensors.priority + 10;
1025 break;
1026
1027 case IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT:
@@ -1033,7 +1031,7 @@ static void netdata_update_ipmi_sensor_reading(
1031 t.units = "Fahrenheit";
1032 t.family = "temperatures";
1033 t.chart_type = "line";
1036 - t.priority = state->sensors.priority + 20;
1034 + t.priority = stt->sensors.priority + 20;
1035 break;
1036
1037 case IPMI_MONITORING_SENSOR_UNITS_VOLTS:
@@ -1043,7 +1041,7 @@ static void netdata_update_ipmi_sensor_reading(
1041 t.units = "Volts";
1042 t.family = "voltages";
1043 t.chart_type = "line";
1046 - t.priority = state->sensors.priority + 30;
1044 + t.priority = stt->sensors.priority + 30;
1045 break;
1046
1047 case IPMI_MONITORING_SENSOR_UNITS_AMPS:
@@ -1053,7 +1051,7 @@ static void netdata_update_ipmi_sensor_reading(
1051 t.units = "Amps";
1052 t.family = "current";
1053 t.chart_type = "line";
1056 - t.priority = state->sensors.priority + 40;
1054 + t.priority = stt->sensors.priority + 40;
1055 break;
1056
1057 case IPMI_MONITORING_SENSOR_UNITS_RPM:
@@ -1063,7 +1061,7 @@ static void netdata_update_ipmi_sensor_reading(
1061 t.units = "RPM";
1062 t.family = "fans";
1063 t.chart_type = "line";
1066 - t.priority = state->sensors.priority + 50;
1064 + t.priority = stt->sensors.priority + 50;
1065 break;
1066
1067 case IPMI_MONITORING_SENSOR_UNITS_WATTS:
@@ -1073,7 +1071,7 @@ static void netdata_update_ipmi_sensor_reading(
1071 t.units = "Watts";
1072 t.family = "power";
1073 t.chart_type = "line";
1076 - t.priority = state->sensors.priority + 60;
1074 + t.priority = stt->sensors.priority + 60;
1075 break;
1076
1077 case IPMI_MONITORING_SENSOR_UNITS_PERCENT:
@@ -1083,11 +1081,11 @@ static void netdata_update_ipmi_sensor_reading(
1081 t.units = "%%";
1082 t.family = "other";
1083 t.chart_type = "line";
1086 - t.priority = state->sensors.priority + 70;
1084 + t.priority = stt->sensors.priority + 70;
1085 break;
1086
1087 default:
1090 - t.priority = state->sensors.priority + 80;
1088 + t.priority = stt->sensors.priority + 80;
1089 t.do_metric = false;
1090 break;
1091 }
@@ -1108,57 +1106,57 @@ static void netdata_update_ipmi_sensor_reading(
1106 }
1107
1108 if(sensor_reading) {
1111 - netdata_sensor_set_value(&t, sensor_reading, state);
1112 - t.last_collected_metric_ut = state->sensors.now_ut;
1109 + netdata_sensor_set_value(&t, sensor_reading, stt);
1110 + t.last_collected_metric_ut = stt->sensors.now_ut;
1111 }
1114 - t.last_collected_state_ut = state->sensors.now_ut;
1112 + t.last_collected_state_ut = stt->sensors.now_ut;
1113
1116 - dictionary_set(state->sensors.dict, key, &t, sizeof(t));
1114 + dictionary_set(stt->sensors.dict, key, &t, sizeof(t));
1115 }
1116
1119 -static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *state, uint32_t events) {
1120 - state->sel.events = events;
1117 +static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stt, uint32_t events) {
1118 + stt->sel.events = events;
1119 }
1120
1123 -int netdata_ipmi_collect_data(struct ipmi_monitoring_ipmi_config *ipmi_config, IPMI_COLLECTION_TYPE type, struct netdata_ipmi_state *state) {
1121 +int netdata_ipmi_collect_data(struct ipmi_monitoring_ipmi_config *ipmi_config, IPMI_COLLECTION_TYPE type, struct netdata_ipmi_state *stt) {
1122 errno = 0;
1123
1124 if(type & IPMI_COLLECT_TYPE_SENSORS) {
1127 - state->sensors.collected = 0;
1128 - state->sensors.now_ut = now_monotonic_usec();
1125 + stt->sensors.collected = 0;
1126 + stt->sensors.now_ut = now_monotonic_usec();
1127
1130 - if (netdata_read_ipmi_sensors(ipmi_config, state) < 0) return -1;
1128 + if (netdata_read_ipmi_sensors(ipmi_config, stt) < 0) return -1;
1129 }
1130
1131 if(type & IPMI_COLLECT_TYPE_SEL) {
1134 - state->sel.events = 0;
1135 - state->sel.now_ut = now_monotonic_usec();
1136 - if(netdata_get_ipmi_sel_events_count(ipmi_config, state) < 0) return -2;
1132 + stt->sel.events = 0;
1133 + stt->sel.now_ut = now_monotonic_usec();
1134 + if(netdata_get_ipmi_sel_events_count(ipmi_config, stt) < 0) return -2;
1135 }
1136
1137 return 0;
1138 }
1139
1142 -int netdata_ipmi_detect_speed_secs(struct ipmi_monitoring_ipmi_config *ipmi_config, IPMI_COLLECTION_TYPE type, struct netdata_ipmi_state *state) {
1140 +int netdata_ipmi_detect_speed_secs(struct ipmi_monitoring_ipmi_config *ipmi_config, IPMI_COLLECTION_TYPE type, struct netdata_ipmi_state *stt) {
1141 int i, checks = SPEED_TEST_ITERATIONS, successful = 0;
1142 usec_t total = 0;
1143
1144 for(i = 0 ; i < checks ; i++) {
1147 - if(unlikely(state->debug))
1145 + if(unlikely(stt->debug))
1146 fprintf(stderr, "%s: checking %s data collection speed iteration %d of %d\n",
1147 program_name, netdata_collect_type_to_string(type), i + 1, checks);
1148
1149 // measure the time a data collection needs
1150 usec_t start = now_realtime_usec();
1151
1154 - if(netdata_ipmi_collect_data(ipmi_config, type, state) < 0)
1152 + if(netdata_ipmi_collect_data(ipmi_config, type, stt) < 0)
1153 continue;
1154
1155 usec_t end = now_realtime_usec();
1156
1157 successful++;
1158
1161 - if(unlikely(state->debug))
1159 + if(unlikely(stt->debug))
1160 fprintf(stderr, "%s: %s data collection speed was %"PRIu64" usec\n",
1161 program_name, netdata_collect_type_to_string(type), end - start);
1162
@@ -1298,31 +1296,32 @@ static inline bool is_sensor_updated(usec_t last_collected_ut, usec_t now_ut, us
1296 return (now_ut - last_collected_ut < freq * 2) ? true : false;
1297 }
1298
1301 -static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *state) {
1302 - if(state->sensors.status != ICS_RUNNING) {
1303 - if(unlikely(state->debug))
1299 +static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *stt) {
1300 + if(stt->sensors.status != ICS_RUNNING) {
1301 + if(unlikely(stt->debug))
1302 fprintf(stderr, "%s: %s() sensors state is not RUNNING\n",
1303 program_name, __FUNCTION__ );
1304 return 0;
1305 }
1306
1307 size_t total_sensors_sent = 0;
1310 - int update_every = (int)(state->sensors.freq_ut / USEC_PER_SEC);
1308 + int update_every_s = (int)(stt->sensors.freq_ut / USEC_PER_SEC);
1309 struct sensor *sn;
1310
1311 netdata_mutex_lock(&stdout_mutex);
1312 // generate the CHART/DIMENSION lines, if we have to
1315 - dfe_start_reentrant(state->sensors.dict, sn) {
1313 + dfe_start_reentrant(stt->sensors.dict, sn) {
1314 if(unlikely(!sn->do_metric && !sn->do_state))
1315 continue;
1316
1317 bool did_metric = false, did_state = false;
1318
1319 if(likely(sn->do_metric)) {
1322 - if(unlikely(!is_sensor_updated(sn->last_collected_metric_ut, state->updates.now_ut, state->sensors.freq_ut))) {
1323 - if(unlikely(state->debug))
1320 + if(unlikely(!is_sensor_updated(sn->last_collected_metric_ut, stt->updates.now_ut, stt->sensors.freq_ut))) {
1321 + if(unlikely(stt->debug))
1322 fprintf(stderr, "%s: %s() sensor '%s' metric is not UPDATED (last updated %"PRIu64", now %"PRIu64", freq %"PRIu64"\n",
1325 - program_name, __FUNCTION__, sn->sensor_name, sn->last_collected_metric_ut, state->updates.now_ut, state->sensors.freq_ut);
1323 + program_name, __FUNCTION__, sn->sensor_name, sn->last_collected_metric_ut,
1324 + stt->updates.now_ut, stt->sensors.freq_ut);
1325 }
1326 else {
1327 if (unlikely(!sn->metric_chart_sent)) {
@@ -1330,7 +1329,8 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1329
1330 printf("CHART '%s_%s' '' '%s' '%s' '%s' '%s' '%s' %d %d '' '%s' '%s'\n",
1331 sn->context, sn_dfe.name, sn->title, sn->units, sn->family, sn->context,
1333 - sn->chart_type, sn->priority + 1, update_every, program_name, "sensors");
1332 + sn->chart_type, sn->priority + 1,
1333 + update_every_s, program_name, "sensors");
1334
1335 printf("CLABEL 'sensor' '%s' 1\n", sn->sensor_name);
1336 printf("CLABEL 'type' '%s' 1\n", sn->type);
@@ -1344,19 +1344,16 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1344
1345 switch (sn->sensor_reading_type) {
1346 case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32:
1347 - printf("SET '%s' = %u\n", sn->dimension, sn->sensor_reading.uint32_value
1348 - );
1347 + printf("SET '%s' = %u\n", sn->dimension, sn->sensor_reading.uint32_value);
1348 break;
1349
1350 case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE:
1351 printf("SET '%s' = %lld\n", sn->dimension,
1353 - (long long int) (sn->sensor_reading.double_value * sn->multiplier)
1354 - );
1352 + (long long int) (sn->sensor_reading.double_value * sn->multiplier));
1353 break;
1354
1355 case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL:
1358 - printf("SET '%s' = %u\n", sn->dimension, sn->sensor_reading.bool_value
1359 - );
1356 + printf("SET '%s' = %u\n", sn->dimension, sn->sensor_reading.bool_value);
1357 break;
1358
1359 default:
@@ -1372,17 +1369,18 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1369 }
1370
1371 if(likely(sn->do_state)) {
1375 - if(unlikely(!is_sensor_updated(sn->last_collected_state_ut, state->updates.now_ut, state->sensors.freq_ut))) {
1376 - if (unlikely(state->debug))
1372 + if(unlikely(!is_sensor_updated(sn->last_collected_state_ut, stt->updates.now_ut, stt->sensors.freq_ut))) {
1373 + if (unlikely(stt->debug))
1374 fprintf(stderr, "%s: %s() sensor '%s' state is not UPDATED (last updated %"PRIu64", now %"PRIu64", freq %"PRIu64"\n",
1378 - program_name, __FUNCTION__, sn->sensor_name, sn->last_collected_state_ut, state->updates.now_ut, state->sensors.freq_ut);
1375 + program_name, __FUNCTION__, sn->sensor_name, sn->last_collected_state_ut,
1376 + stt->updates.now_ut, stt->sensors.freq_ut);
1377 }
1378 else {
1379 if (unlikely(!sn->state_chart_sent)) {
1380 sn->state_chart_sent = true;
1381
1382 printf("CHART 'ipmi.sensor_state_%s' '' 'IPMI Sensor State' 'state' 'states' 'ipmi.sensor_state' 'line' %d %d '' '%s' '%s'\n",
1385 - sn_dfe.name, sn->priority, update_every, program_name, "sensors");
1383 + sn_dfe.name, sn->priority, update_every_s, program_name, "sensors");
1384
1385 printf("CLABEL 'sensor' '%s' 1\n", sn->sensor_name);
1386 printf("CLABEL 'type' '%s' 1\n", sn->type);
@@ -1415,17 +1413,17 @@ static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *sta
1413 return total_sensors_sent;
1414 }
1415
1418 -static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state) {
1416 +static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *stt) {
1417 static bool sel_chart_generated = false;
1418
1419 netdata_mutex_lock(&stdout_mutex);
1420
1423 - if(likely(state->sel.status == ICS_RUNNING)) {
1421 + if(likely(stt->sel.status == ICS_RUNNING)) {
1422 if(unlikely(!sel_chart_generated)) {
1423 sel_chart_generated = true;
1424 printf("CHART ipmi.events '' 'IPMI Events' 'events' 'events' ipmi.sel area %d %d '' '%s' '%s'\n"
1427 - , state->sel.priority + 2
1428 - , (int)(state->sel.freq_ut / USEC_PER_SEC)
1425 + , stt->sel.priority + 2
1426 + , (int)(stt->sel.freq_ut / USEC_PER_SEC)
1427 , program_name
1428 , "sel"
1429 );
@@ -1436,13 +1434,14 @@ static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *state)
1434 "BEGIN ipmi.events\n"
1435 "SET events = %zu\n"
1436 "END\n"
1439 - , state->sel.events
1437 + ,
1438 + stt->sel.events
1439 );
1440 }
1441
1442 netdata_mutex_unlock(&stdout_mutex);
1443
1445 - return state->sel.events;
1444 + return stt->sel.events;
1445 }
1446
1447 // ----------------------------------------------------------------------------
@@ -1618,7 +1617,7 @@ static void freeimi_function_sensors(const char *transaction, char *function __m
1617 // ----------------------------------------------------------------------------
1618 // main, command line arguments parsing
1619
1621 -static void plugin_exit(int code) {
1620 +static NORETURN void plugin_exit(int code) {
1621 fflush(stdout);
1622 function_plugin_should_exit = true;
1623 exit(code);
collectors/plugins.d/README.md
+2 -1
@@ -693,7 +693,8 @@ When responding to additions and updates, Netdata uses the following success res
693
694 - `200`, responding with 200, means the configuration has been accepted and it is running.
695 - `202`, responding with 202, means the configuration has been accepted but it is not yet running. A subsequent `status` action will update it.
696 -- `299`, responding with 299, means the configuration has been accepted but a restart is required to apply it.
696 +- `298`, responding with 298, means the configuration has been accepted but it is disabled for some reason (probably because it matches nothing or the contents are not useful - use the `message` to provide additional information).
697 +- `299`, responding with 299, means the configuration has been accepted but a restart is required to apply it.
698
699 ## Data collection
700
collectors/plugins.d/pluginsd_parser.c
+7 -7
@@ -190,7 +190,7 @@ static inline PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, si
190 default_rrd_update_every,
191 default_rrd_history_entries,
192 default_rrd_memory_mode,
193 - default_health_enabled,
193 + health_plugin_enabled(),
194 default_rrdpush_enabled,
195 default_rrdpush_destination,
196 default_rrdpush_api_key,
@@ -568,20 +568,20 @@ static inline PARSER_RC pluginsd_variable(char **words, size_t num_words, PARSER
568 }
569
570 if (global) {
571 - const RRDVAR_ACQUIRED *rva = rrdvar_custom_host_variable_add_and_acquire(host, name);
571 + const RRDVAR_ACQUIRED *rva = rrdvar_host_variable_add_and_acquire(host, name);
572 if (rva) {
573 - rrdvar_custom_host_variable_set(host, rva, v);
574 - rrdvar_custom_host_variable_release(host, rva);
573 + rrdvar_host_variable_set(host, rva, v);
574 + rrdvar_host_variable_release(host, rva);
575 }
576 else
577 netdata_log_error("PLUGINSD: 'host:%s' cannot find/create HOST VARIABLE '%s'",
578 rrdhost_hostname(host),
579 name);
580 } else {
581 - const RRDSETVAR_ACQUIRED *rsa = rrdsetvar_custom_chart_variable_add_and_acquire(st, name);
581 + const RRDVAR_ACQUIRED *rsa = rrdvar_chart_variable_add_and_acquire(st, name);
582 if (rsa) {
583 - rrdsetvar_custom_chart_variable_set(st, rsa, v);
584 - rrdsetvar_custom_chart_variable_release(st, rsa);
583 + rrdvar_chart_variable_set(st, rsa, v);
584 + rrdvar_chart_variable_release(st, rsa);
585 }
586 else
587 netdata_log_error("PLUGINSD: 'host:%s/chart:%s' cannot find/create CHART VARIABLE '%s'",
collectors/proc.plugin/ipc.c
+6 -4
@@ -352,8 +352,8 @@ int do_ipc(int update_every, usec_t dt) {
352 }
353
354 // variables
355 - semaphores_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "ipc_semaphores_max");
356 - arrays_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "ipc_semaphores_arrays_max");
355 + semaphores_max = rrdvar_host_variable_add_and_acquire(localhost, "ipc_semaphores_max");
356 + arrays_max = rrdvar_host_variable_add_and_acquire(localhost, "ipc_semaphores_arrays_max");
357 }
358
359 struct stat stbuf;
@@ -373,8 +373,10 @@ int do_ipc(int update_every, usec_t dt) {
373 collector_error("Unable to fetch semaphore limits.");
374 }
375 else {
376 - if(semaphores_max) rrdvar_custom_host_variable_set(localhost, semaphores_max, limits.semmns);
377 - if(arrays_max) rrdvar_custom_host_variable_set(localhost, arrays_max, limits.semmni);
376 + if(semaphores_max)
377 + rrdvar_host_variable_set(localhost, semaphores_max, limits.semmns);
378 + if(arrays_max)
379 + rrdvar_host_variable_set(localhost, arrays_max, limits.semmni);
380
381 st_arrays->red = limits.semmni;
382 st_semaphores->red = limits.semmns;
collectors/proc.plugin/proc_loadavg.c
+3 -3
@@ -95,7 +95,7 @@ int do_proc_loadavg(int update_every, usec_t dt) {
95 if(likely(do_all_processes)) {
96 static RRDSET *processes_chart = NULL;
97 static RRDDIM *rd_active = NULL;
98 - static const RRDSETVAR_ACQUIRED *rd_pidmax;
98 + static const RRDVAR_ACQUIRED *rd_pidmax;
99
100 if(unlikely(!processes_chart)) {
101 processes_chart = rrdset_create_localhost(
@@ -114,11 +114,11 @@ int do_proc_loadavg(int update_every, usec_t dt) {
114 );
115
116 rd_active = rrddim_add(processes_chart, "active", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
117 - rd_pidmax = rrdsetvar_custom_chart_variable_add_and_acquire(processes_chart, "pidmax");
117 + rd_pidmax = rrdvar_chart_variable_add_and_acquire(processes_chart, "pidmax");
118 }
119
120 rrddim_set_by_pointer(processes_chart, rd_active, active_processes);
121 - rrdsetvar_custom_chart_variable_set(processes_chart, rd_pidmax, max_processes);
121 + rrdvar_chart_variable_set(processes_chart, rd_pidmax, max_processes);
122 rrdset_done(processes_chart);
123 }
124
collectors/proc.plugin/proc_net_dev.c
+14 -14
@@ -237,7 +237,7 @@ static struct netdev {
237 RRDDIM *rd_mtu;
238
239 char *filename_speed;
240 - const RRDSETVAR_ACQUIRED *chart_var_speed;
240 + const RRDVAR_ACQUIRED *chart_var_speed;
241
242 char *filename_duplex;
243 char *filename_operstate;
@@ -1241,20 +1241,20 @@ int do_proc_net_dev(int update_every, usec_t dt) {
1241 d->flipped ? d->rd_tbytes->collector.last_stored_value : -d->rd_rbytes->collector.last_stored_value,
1242 d->flipped ? -d->rd_rbytes->collector.last_stored_value : d->rd_tbytes->collector.last_stored_value);
1243
1244 - // update the interface speed
1245 - if(d->filename_speed) {
1246 - if(unlikely(!d->chart_var_speed)) {
1247 - d->chart_var_speed =
1248 - rrdsetvar_custom_chart_variable_add_and_acquire(d->st_bandwidth, "nic_speed_max");
1249 - if(!d->chart_var_speed) {
1250 - collector_error(
1251 - "Cannot create interface %s chart variable 'nic_speed_max'. Will not update its speed anymore.",
1252 - d->name);
1253 - freez(d->filename_speed);
1254 - d->filename_speed = NULL;
1255 - }
1244 + if(unlikely(!d->chart_var_speed)) {
1245 + d->chart_var_speed = rrdvar_chart_variable_add_and_acquire(d->st_bandwidth, "nic_speed_max");
1246 + if(!d->chart_var_speed) {
1247 + collector_error(
1248 + "Cannot create interface %s chart variable 'nic_speed_max'. Will not update its speed anymore.",
1249 + d->name);
1250 }
1251 + else {
1252 + rrdvar_chart_variable_set(d->st_bandwidth, d->chart_var_speed, NAN);
1253 + }
1254 + }
1255
1256 + // update the interface speed
1257 + if(d->filename_speed) {
1258 if (d->filename_speed && d->chart_var_speed) {
1259 int ret = 0;
1260
@@ -1300,7 +1300,7 @@ int do_proc_net_dev(int update_every, usec_t dt) {
1300 rrdset_done(d->st_speed);
1301 }
1302
1303 - rrdsetvar_custom_chart_variable_set(
1303 + rrdvar_chart_variable_set(
1304 d->st_bandwidth, d->chart_var_speed, (NETDATA_DOUBLE)d->speed * KILOBITS_IN_A_MEGABIT);
1305
1306 if (d->speed) {
collectors/proc.plugin/proc_net_netstat.c
+2 -2
@@ -1647,7 +1647,7 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
1647 arl_expect(arl_udplite, "InCsumErrors", &snmp_root.udplite_InCsumErrors);
1648 arl_expect(arl_udplite, "IgnoredMulti", &snmp_root.udplite_IgnoredMulti);
1649
1650 - tcp_max_connections_var = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_max_connections");
1650 + tcp_max_connections_var = rrdvar_host_variable_add_and_acquire(localhost, "tcp_max_connections");
1651 }
1652
1653 size_t lines, l, words;
@@ -2713,7 +2713,7 @@ int do_proc_net_netstat(int update_every, usec_t dt) {
2713 // snmp Tcp charts
2714
2715 // this is smart enough to update it, only when it is changed
2716 - rrdvar_custom_host_variable_set(localhost, tcp_max_connections_var, snmp_root.tcp_MaxConn);
2716 + rrdvar_host_variable_set(localhost, tcp_max_connections_var, snmp_root.tcp_MaxConn);
2717
2718 // see http://net-snmp.sourceforge.net/docs/mibs/tcp.html
2719 if(do_tcp_sockets == CONFIG_BOOLEAN_YES || (do_tcp_sockets == CONFIG_BOOLEAN_AUTO &&
collectors/proc.plugin/proc_net_sockstat.c
+8 -8
@@ -32,9 +32,9 @@ static int read_tcp_mem(void) {
32 *tcp_mem_high_threshold = NULL;
33
34 if(unlikely(!tcp_mem_low_threshold)) {
35 - tcp_mem_low_threshold = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_mem_low");
36 - tcp_mem_pressure_threshold = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_mem_pressure");
37 - tcp_mem_high_threshold = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_mem_high");
35 + tcp_mem_low_threshold = rrdvar_host_variable_add_and_acquire(localhost, "tcp_mem_low");
36 + tcp_mem_pressure_threshold = rrdvar_host_variable_add_and_acquire(localhost, "tcp_mem_pressure");
37 + tcp_mem_high_threshold = rrdvar_host_variable_add_and_acquire(localhost, "tcp_mem_high");
38 }
39
40 if(unlikely(!filename)) {
@@ -60,9 +60,9 @@ static int read_tcp_mem(void) {
60
61 // fprintf(stderr, "TCP MEM low = %llu, pressure = %llu, high = %llu\n", low, pressure, high);
62
63 - rrdvar_custom_host_variable_set(localhost, tcp_mem_low_threshold, low * sysconf(_SC_PAGESIZE) / 1024.0);
64 - rrdvar_custom_host_variable_set(localhost, tcp_mem_pressure_threshold, pressure * sysconf(_SC_PAGESIZE) / 1024.0);
65 - rrdvar_custom_host_variable_set(localhost, tcp_mem_high_threshold, high * sysconf(_SC_PAGESIZE) / 1024.0);
63 + rrdvar_host_variable_set(localhost, tcp_mem_low_threshold, low * sysconf(_SC_PAGESIZE) / 1024.0);
64 + rrdvar_host_variable_set(localhost, tcp_mem_pressure_threshold, pressure * sysconf(_SC_PAGESIZE) / 1024.0);
65 + rrdvar_host_variable_set(localhost, tcp_mem_high_threshold, high * sysconf(_SC_PAGESIZE) / 1024.0);
66
67 return 0;
68 }
@@ -81,9 +81,9 @@ static kernel_uint_t read_tcp_max_orphans(void) {
81 if(read_single_number_file(filename, &tcp_max_orphans) == 0) {
82
83 if(unlikely(!tcp_max_orphans_var))
84 - tcp_max_orphans_var = rrdvar_custom_host_variable_add_and_acquire(localhost, "tcp_max_orphans");
84 + tcp_max_orphans_var = rrdvar_host_variable_add_and_acquire(localhost, "tcp_max_orphans");
85
86 - rrdvar_custom_host_variable_set(localhost, tcp_max_orphans_var, tcp_max_orphans);
86 + rrdvar_host_variable_set(localhost, tcp_max_orphans_var, tcp_max_orphans);
87 return tcp_max_orphans;
88 }
89
collectors/proc.plugin/proc_net_stat_conntrack.c
+2 -2
@@ -50,7 +50,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
50 if(!do_sockets && !read_full)
51 return 1;
52
53 - rrdvar_max = rrdvar_custom_host_variable_add_and_acquire(localhost, "netfilter_conntrack_max");
53 + rrdvar_max = rrdvar_host_variable_add_and_acquire(localhost, "netfilter_conntrack_max");
54 }
55
56 if(likely(read_full)) {
@@ -125,7 +125,7 @@ int do_proc_net_stat_conntrack(int update_every, usec_t dt) {
125
126 unsigned long long max;
127 if(likely(!read_single_number_file(nf_conntrack_max_filename, &max)))
128 - rrdvar_custom_host_variable_set(localhost, rrdvar_max, max);
128 + rrdvar_host_variable_set(localhost, rrdvar_max, max);
129 }
130
131 // --------------------------------------------------------------------
collectors/proc.plugin/proc_stat.c
+2 -2
@@ -730,7 +730,7 @@ int do_proc_stat(int update_every, usec_t dt) {
730 }
731
732 if(unlikely(core == 0 && cpus_var == NULL))
733 - cpus_var = rrdvar_custom_host_variable_add_and_acquire(localhost, "active_processors");
733 + cpus_var = rrdvar_host_variable_add_and_acquire(localhost, "active_processors");
734 }
735
736 rrddim_set_by_pointer(cpu_chart->st, cpu_chart->rd_user, user);
@@ -1075,7 +1075,7 @@ int do_proc_stat(int update_every, usec_t dt) {
1075 }
1076
1077 if(cpus_var)
1078 - rrdvar_custom_host_variable_set(localhost, cpus_var, cores_found);
1078 + rrdvar_host_variable_set(localhost, cpus_var, cores_found);
1079
1080 return 0;
1081 }
collectors/proc.plugin/sys_class_drm.c
-2
@@ -849,8 +849,6 @@ int do_sys_class_drm(int update_every, usec_t dt) {
849 (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue;
850
851 if(de->d_type == DT_LNK && !strncmp(de->d_name, "card", 4) && !strchr(de->d_name, '-')) {
852 - char filename[FILENAME_MAX + 1];
853 -
852 snprintfz(filename, FILENAME_MAX, "%s/%s/%s", drm_dir_name, de->d_name, "device/uevent");
853 if(check_card_is_amdgpu(filename)) continue;
854
collectors/proc.plugin/sys_class_infiniband.c
+3 -3
@@ -184,7 +184,7 @@ static struct ibport {
184 RRDSET *st_hwpackets;
185 RRDSET *st_hwerrors;
186
187 - const RRDSETVAR_ACQUIRED *stv_speed;
187 + const RRDVAR_ACQUIRED *stv_speed;
188
189 usec_t speed_last_collected_usec;
190
@@ -545,14 +545,14 @@ int do_sys_class_infiniband(int update_every, usec_t dt)
545 // x4 lanes multiplier as per Documentation/ABI/stable/sysfs-class-infiniband
546 FOREACH_COUNTER_BYTES(GEN_RRD_DIM_ADD_CUSTOM, port, port->width * 8, 1000, RRD_ALGORITHM_INCREMENTAL)
547
548 - port->stv_speed = rrdsetvar_custom_chart_variable_add_and_acquire(port->st_bytes, "link_speed");
548 + port->stv_speed = rrdvar_chart_variable_add_and_acquire(port->st_bytes, "link_speed");
549 }
550
551 // Link read values to dimensions
552 FOREACH_COUNTER_BYTES(GEN_RRD_DIM_SETP, port)
553
554 // For link speed set only variable
555 - rrdsetvar_custom_chart_variable_set(port->st_bytes, port->stv_speed, port->speed);
555 + rrdvar_chart_variable_set(port->st_bytes, port->stv_speed, port->speed);
556
557 rrdset_done(port->st_bytes);
558 }
collectors/statsd.plugin/statsd.c
+1 -2
@@ -1531,8 +1531,7 @@ static int statsd_readfile(const char *filename, STATSD_APP *app, STATSD_APP_CHA
1531 return 0;
1532 }
1533
1534 -static int statsd_file_callback(const char *filename, void *data) {
1535 - (void)data;
1534 +static int statsd_file_callback(const char *filename, void *data __maybe_unused, bool stock_config __maybe_unused) {
1535 return statsd_readfile(filename, NULL, NULL, NULL);
1536 }
1537
collectors/systemd-journal.plugin/systemd-journal-dyncfg.c
+1
@@ -59,6 +59,7 @@ static int systemd_journal_directories_dyncfg_get(BUFFER *wb) {
59 static int systemd_journal_directories_dyncfg_cb(const char *transaction,
60 const char *id,
61 DYNCFG_CMDS cmd,
62 + const char *add_name __maybe_unused,
63 BUFFER *payload,
64 usec_t *stop_monotonic_ut __maybe_unused,
65 bool *cancelled __maybe_unused,
collectors/systemd-journal.plugin/systemd-units.c
+13 -13
@@ -1207,19 +1207,19 @@ static void systemd_unit_priority(UnitInfo *u, size_t units) {
1207 u->prio = (prio * units) + u->prio;
1208 }
1209
1210 -#define if_less(current, max, target) ({ \
1211 - typeof(current) _wanted = (current); \
1212 - if((current) < (target)) \
1213 - _wanted = (target) > (max) ? (max) : (target); \
1214 - _wanted; \
1215 -})
1216 -
1217 -#define if_normal(current, max, target) ({ \
1218 - typeof(current) _wanted = (current); \
1219 - if((current) == FACET_ROW_SEVERITY_NORMAL) \
1220 - _wanted = (target) > (max) ? (max) : (target); \
1221 - _wanted; \
1222 -})
1210 +static inline FACET_ROW_SEVERITY if_less(FACET_ROW_SEVERITY current, FACET_ROW_SEVERITY max, FACET_ROW_SEVERITY target) {
1211 + FACET_ROW_SEVERITY wanted = current;
1212 + if(current < target)
1213 + wanted = target > max ? max : target;
1214 + return wanted;
1215 +}
1216 +
1217 +static inline FACET_ROW_SEVERITY if_normal(FACET_ROW_SEVERITY current, FACET_ROW_SEVERITY max, FACET_ROW_SEVERITY target) {
1218 + FACET_ROW_SEVERITY wanted = current;
1219 + if(current == FACET_ROW_SEVERITY_NORMAL)
1220 + wanted = target > max ? max : target;
1221 + return wanted;
1222 +}
1223
1224 FACET_ROW_SEVERITY system_unit_severity(UnitInfo *u) {
1225 FACET_ROW_SEVERITY severity, max_severity;
daemon/analytics.c
+2 -2
@@ -612,8 +612,8 @@ void *analytics_main(void *ptr)
612
613 analytics_gather_mutable_meta_data();
614
615 - analytics_statistic_t statistic = { "META", "-", "-" };
616 - analytics_statistic_send(&statistic);
615 + analytics_statistic_t stt = { "META", "-", "-" };
616 + analytics_statistic_send(&stt);
617 analytics_log_data();
618
619 sec = 0;
daemon/commands.c
+1 -1
@@ -142,7 +142,7 @@ static cmd_status_t cmd_reload_health_execute(char *args, char **message)
142
143 nd_log_limits_unlimited();
144 netdata_log_info("COMMAND: Reloading HEALTH configuration.");
145 - health_reload();
145 + health_plugin_reload();
146 nd_log_limits_reset();
147
148 return CMD_STATUS_SUCCESS;
daemon/common.h
+3 -2
@@ -38,6 +38,9 @@
38
39 #include "global_statistics.h"
40
41 +// health monitoring and alarm notifications
42 +#include "health/health.h"
43 +
44 // the netdata database
45 #include "database/rrd.h"
46
@@ -52,8 +55,6 @@
55 // streaming metrics between netdata servers
56 #include "streaming/rrdpush.h"
57
55 -// health monitoring and alarm notifications
56 -#include "health/health.h"
58
59 // anomaly detection
60 #include "ml/ml.h"
daemon/config/dyncfg-echo.c
+81 -15
@@ -14,23 +14,41 @@ struct dyncfg_echo {
14 const DICTIONARY_ITEM *item;
15 DYNCFG *df;
16 BUFFER *wb;
17 + const char *cmd;
18 };
19
20 void dyncfg_echo_cb(BUFFER *wb __maybe_unused, int code __maybe_unused, void *result_cb_data) {
21 struct dyncfg_echo *e = result_cb_data;
22
23 + if(!DYNCFG_RESP_SUCCESS(code))
24 + nd_log(NDLS_DAEMON, NDLP_ERR,
25 + "DYNCFG: received response code %d on request to id '%s', cmd: %s",
26 + code, dictionary_acquired_item_name(e->item), e->cmd);
27 +
28 buffer_free(e->wb);
29 dictionary_acquired_item_release(dyncfg_globals.nodes, e->item);
30
31 e->wb = NULL;
32 e->df = NULL;
33 e->item = NULL;
34 + freez((void *)e->cmd);
35 + e->cmd = NULL;
36 freez(e);
37 }
38
39 +// ----------------------------------------------------------------------------
40 +
41 void dyncfg_echo(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id __maybe_unused, DYNCFG_CMDS cmd) {
32 - if(!(df->cmds & cmd))
42 + RRDHOST *host = dyncfg_rrdhost(df);
43 + if(!host) {
44 + nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot find host of configuration id '%s'", id);
45 return;
46 + }
47 +
48 + if(!(df->cmds & cmd)) {
49 + nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: attempted to echo a cmd that is not supported");
50 + return;
51 + }
52
53 const char *cmd_str = dyncfg_id2cmd_one(cmd);
54 if(!cmd_str) {
@@ -42,42 +60,90 @@ void dyncfg_echo(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id __maybe
60 e->item = dictionary_acquired_item_dup(dyncfg_globals.nodes, item);
61 e->wb = buffer_create(0, NULL);
62 e->df = df;
63 + e->cmd = strdupz(cmd_str);
64
65 char buf[string_strlen(df->function) + strlen(cmd_str) + 20];
66 snprintfz(buf, sizeof(buf), "%s %s", string2str(df->function), cmd_str);
67
49 - rrd_function_run(df->host, e->wb, 10, HTTP_ACCESS_ADMIN, buf, false, NULL,
50 - dyncfg_echo_cb, e,
51 - NULL, NULL,
52 - NULL, NULL,
53 - NULL, NULL);
68 + rrd_function_run(
69 + host, e->wb, 10, HTTP_ACCESS_ADMIN, buf, false, NULL,
70 + dyncfg_echo_cb, e,
71 + NULL, NULL,
72 + NULL, NULL,
73 + NULL, string2str(df->source));
74 }
75
56 -static void dyncfg_echo_payload(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id __maybe_unused, const char *cmd) {
57 - if(!df->payload)
76 +// ----------------------------------------------------------------------------
77 +
78 +static void dyncfg_echo_payload(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id, const char *cmd) {
79 + RRDHOST *host = dyncfg_rrdhost(df);
80 + if(!host) {
81 + nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot find host of configuration id '%s'", id);
82 + return;
83 + }
84 +
85 + if(!df->payload) {
86 + nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: requested to send a '%s' to '%s', but there is no payload", cmd, id);
87 return;
88 + }
89
90 struct dyncfg_echo *e = callocz(1, sizeof(struct dyncfg_echo));
91 e->item = dictionary_acquired_item_dup(dyncfg_globals.nodes, item);
92 e->wb = buffer_create(0, NULL);
93 e->df = df;
94 + e->cmd = strdupz(cmd);
95
96 char buf[string_strlen(df->function) + strlen(cmd) + 20];
97 snprintfz(buf, sizeof(buf), "%s %s", string2str(df->function), cmd);
98
68 - rrd_function_run(df->host, e->wb, 10, HTTP_ACCESS_ADMIN, buf, false, NULL,
69 - dyncfg_echo_cb, e,
70 - NULL, NULL,
71 - NULL, NULL,
72 - df->payload, NULL);
99 + rrd_function_run(
100 + host, e->wb, 10, HTTP_ACCESS_ADMIN, buf, false, NULL,
101 + dyncfg_echo_cb, e,
102 + NULL, NULL,
103 + NULL, NULL,
104 + df->payload, string2str(df->source));
105 }
106
107 void dyncfg_echo_update(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id) {
108 dyncfg_echo_payload(item, df, id, "update");
109 }
110
79 -void dyncfg_echo_add(const DICTIONARY_ITEM *template_item, DYNCFG *template_df, const char *template_id, const char *job_name) {
111 +// ----------------------------------------------------------------------------
112 +
113 +static void dyncfg_echo_payload_add(const DICTIONARY_ITEM *item_template __maybe_unused, const DICTIONARY_ITEM *item_job, DYNCFG *df_template, DYNCFG *df_job, const char *id_template, const char *cmd) {
114 + RRDHOST *host = dyncfg_rrdhost(df_template);
115 + if(!host) {
116 + nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot find host of configuration id '%s'", id_template);
117 + return;
118 + }
119 +
120 + if(!df_job->payload) {
121 + nd_log(NDLS_DAEMON, NDLP_ERR,
122 + "DYNCFG: requested to send a '%s' to '%s', but there is no payload",
123 + cmd, id_template);
124 + return;
125 + }
126 +
127 + struct dyncfg_echo *e = callocz(1, sizeof(struct dyncfg_echo));
128 + e->item = dictionary_acquired_item_dup(dyncfg_globals.nodes, item_job);
129 + e->wb = buffer_create(0, NULL);
130 + e->df = df_job;
131 + e->cmd = strdupz(cmd);
132 +
133 + char buf[string_strlen(df_template->function) + strlen(cmd) + 20];
134 + snprintfz(buf, sizeof(buf), "%s %s", string2str(df_template->function), cmd);
135 +
136 + rrd_function_run(
137 + host, e->wb, 10, HTTP_ACCESS_ADMIN, buf, false, NULL,
138 + dyncfg_echo_cb, e,
139 + NULL, NULL,
140 + NULL, NULL,
141 + df_job->payload, string2str(df_job->source));
142 +}
143 +
144 +void dyncfg_echo_add(const DICTIONARY_ITEM *item_template, const DICTIONARY_ITEM *item_job, DYNCFG *df_template, DYNCFG *df_job, const char *template_id, const char *job_name) {
145 char buf[strlen(job_name) + 20];
146 snprintfz(buf, sizeof(buf), "add %s", job_name);
82 - dyncfg_echo_payload(template_item, template_df, template_id, buf);
147 + dyncfg_echo_payload_add(item_template, item_job, df_template, df_job, template_id, buf);
148 }
149 +
daemon/config/dyncfg-files.c
+2 -3
@@ -30,7 +30,7 @@ void dyncfg_file_save(const char *id, DYNCFG *df) {
30 fprintf(fp, "template=%s\n", string2str(df->template));
31
32 char uuid_str[UUID_COMPACT_STR_LEN];
33 - uuid_unparse_lower_compact(df->host_uuid, uuid_str);
33 + uuid_unparse_lower_compact(df->host_uuid.uuid, uuid_str);
34 fprintf(fp, "host=%s\n", uuid_str);
35
36 fprintf(fp, "path=%s\n", string2str(df->path));
@@ -67,7 +67,6 @@ void dyncfg_file_load(const char *filename) {
67 }
68
69 DYNCFG tmp = {
70 - .host = NULL,
70 .status = DYNCFG_STATUS_ORPHAN,
71 };
72
@@ -110,7 +109,7 @@ void dyncfg_file_load(const char *filename) {
109 } else if (strcmp(key, "template") == 0) {
110 tmp.template = string_strdupz(value);
111 } else if (strcmp(key, "host") == 0) {
113 - uuid_parse_flexi(value, tmp.host_uuid);
112 + uuid_parse_flexi(value, tmp.host_uuid.uuid);
113 } else if (strcmp(key, "path") == 0) {
114 tmp.path = string_strdupz(value);
115 } else if (strcmp(key, "type") == 0) {
daemon/config/dyncfg-inline.c
+12 -11
@@ -29,21 +29,22 @@ static int dyncfg_inline_callback(struct rrd_function_execute *rfe, void *data _
29
30 bool dyncfg_add(RRDHOST *host, const char *id, const char *path, DYNCFG_STATUS status, DYNCFG_TYPE type, DYNCFG_SOURCE_TYPE source_type, const char *source, DYNCFG_CMDS cmds, dyncfg_cb_t cb, void *data) {
31
32 - if(dyncfg_add_low_level(host, id, path, status, type, source_type, source, cmds,
32 + struct dyncfg_node tmp = {
33 + .cmds = cmds,
34 + .type = type,
35 + .cb = cb,
36 + .data = data,
37 + };
38 + dictionary_set(dyncfg_nodes, id, &tmp, sizeof(tmp));
39 +
40 + if(!dyncfg_add_low_level(host, id, path, status, type, source_type, source, cmds,
41 0, 0, true,
42 dyncfg_inline_callback, NULL)) {
35 - struct dyncfg_node tmp = {
36 - .cmds = cmds,
37 - .type = type,
38 - .cb = cb,
39 - .data = data,
40 - };
41 - dictionary_set(dyncfg_nodes, id, &tmp, sizeof(tmp));
42 -
43 - return true;
43 + dictionary_del(dyncfg_nodes, id);
44 + return false;
45 }
46
46 - return false;
47 + return true;
48 }
49
50 void dyncfg_del(RRDHOST *host, const char *id) {
daemon/config/dyncfg-intercept.c
+77 -37
@@ -19,15 +19,34 @@ struct dyncfg_call {
19 };
20
21 DYNCFG_STATUS dyncfg_status_from_successful_response(int code) {
22 - DYNCFG_STATUS status;
23 - if(code == DYNCFG_RESP_RUNNING)
24 - status = DYNCFG_STATUS_RUNNING;
25 - else if(code == DYNCFG_RESP_ACCEPTED || code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
26 - status = DYNCFG_STATUS_ACCEPTED;
22 + DYNCFG_STATUS status = DYNCFG_STATUS_ACCEPTED;
23 +
24 + switch(code) {
25 + default:
26 + case DYNCFG_RESP_ACCEPTED:
27 + case DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED:
28 + status = DYNCFG_STATUS_ACCEPTED;
29 + break;
30 +
31 + case DYNCFG_RESP_ACCEPTED_DISABLED:
32 + status = DYNCFG_STATUS_DISABLED;
33 + break;
34 +
35 + case DYNCFG_RESP_RUNNING:
36 + status = DYNCFG_STATUS_RUNNING;
37 + break;
38 +
39 + }
40
41 return status;
42 }
43
44 +static void dyncfg_function_intercept_keep_source(DYNCFG *df, const char *source) {
45 + STRING *old = df->source;
46 + df->source = string_strdupz(source);
47 + string_freez(old);
48 +}
49 +
50 void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_data) {
51 struct dyncfg_call *dc = result_cb_data;
52
@@ -47,31 +66,40 @@ void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_d
66 char id[strlen(dc->id) + 1 + strlen(dc->add_name) + 1];
67 snprintfz(id, sizeof(id), "%s:%s", dc->id, dc->add_name);
68
50 - const DICTIONARY_ITEM *new_item = dyncfg_add_internal(
51 - df->host,
52 - id,
53 - string2str(df->path),
54 - dyncfg_status_from_successful_response(code),
55 - DYNCFG_TYPE_JOB,
56 - DYNCFG_SOURCE_TYPE_DYNCFG,
57 - dc->source,
58 - (df->cmds & ~DYNCFG_CMD_ADD) | DYNCFG_CMD_GET | DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_REMOVE,
59 - 0,
60 - 0,
61 - df->sync,
62 - df->execute_cb, df->execute_cb_data, false);
63 -
64 - DYNCFG *new_df = dictionary_acquired_item_value(new_item);
65 - SWAP(new_df->payload, dc->payload);
66 - if(code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
67 - new_df->restart_required = true;
68 -
69 - dyncfg_file_save(id, new_df);
70 - dictionary_acquired_item_release(dyncfg_globals.nodes, new_item);
69 + RRDHOST *host = dyncfg_rrdhost(df);
70 + if(!host) {
71 + nd_log(NDLS_DAEMON, NDLP_ERR,
72 + "DYNCFG: cannot add job '%s' because host is missing", id);
73 + }
74 + else {
75 + const DICTIONARY_ITEM *new_item = dyncfg_add_internal(
76 + host,
77 + id,
78 + string2str(df->path),
79 + dyncfg_status_from_successful_response(code),
80 + DYNCFG_TYPE_JOB,
81 + DYNCFG_SOURCE_TYPE_DYNCFG,
82 + dc->source,
83 + (df->cmds & ~DYNCFG_CMD_ADD) | DYNCFG_CMD_GET | DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST |
84 + DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE | DYNCFG_CMD_REMOVE,
85 + 0,
86 + 0,
87 + df->sync,
88 + df->execute_cb,
89 + df->execute_cb_data,
90 + false);
91 +
92 + DYNCFG *new_df = dictionary_acquired_item_value(new_item);
93 + SWAP(new_df->payload, dc->payload);
94 + if (code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
95 + new_df->restart_required = true;
96 +
97 + dyncfg_file_save(id, new_df);
98 + dictionary_acquired_item_release(dyncfg_globals.nodes, new_item);
99 + }
100 } else if (dc->cmd == DYNCFG_CMD_UPDATE) {
101 df->source_type = DYNCFG_SOURCE_TYPE_DYNCFG;
73 - string_freez(df->source);
74 - df->source = string_strdupz(dc->source);
102 + dyncfg_function_intercept_keep_source(df, dc->source);
103
104 df->status = dyncfg_status_from_successful_response(code);
105 SWAP(df->payload, dc->payload);
@@ -79,10 +107,13 @@ void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_d
107 save_required = true;
108 } else if (dc->cmd == DYNCFG_CMD_ENABLE) {
109 df->user_disabled = false;
110 + dyncfg_function_intercept_keep_source(df, dc->source);
111 } else if (dc->cmd == DYNCFG_CMD_DISABLE) {
112 df->user_disabled = true;
113 + dyncfg_function_intercept_keep_source(df, dc->source);
114 } else if (dc->cmd == DYNCFG_CMD_REMOVE) {
115 dyncfg_file_delete(dc->id);
116 + dictionary_del(dyncfg_globals.nodes, dc->id);
117 }
118
119 if(dc->cmd != DYNCFG_CMD_ADD && code == DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED)
@@ -151,10 +182,20 @@ void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_d
182
183 // ----------------------------------------------------------------------------
184
154 -static void dyncfg_apply_action_on_all_template_jobs(const char *template_id, DYNCFG_CMDS c) {
185 +static void dyncfg_apply_action_on_all_template_jobs(struct rrd_function_execute *rfe, const char *template_id, DYNCFG_CMDS c) {
186 STRING *template = string_strdupz(template_id);
156 -
187 DYNCFG *df;
188 +
189 + size_t all = 0, done = 0;
190 + dfe_start_read(dyncfg_globals.nodes, df) {
191 + if(df->template == template && df->type == DYNCFG_TYPE_JOB)
192 + all++;
193 + }
194 + dfe_done(df);
195 +
196 + if(rfe->progress.cb)
197 + rfe->progress.cb(rfe->progress.data, done, all);
198 +
199 dfe_start_reentrant(dyncfg_globals.nodes, df) {
200 if(df->template == template && df->type == DYNCFG_TYPE_JOB) {
201 DYNCFG_CMDS cmd_to_send_to_plugin = c;
@@ -165,6 +206,9 @@ static void dyncfg_apply_action_on_all_template_jobs(const char *template_id, DY
206 cmd_to_send_to_plugin = DYNCFG_CMD_DISABLE;
207
208 dyncfg_echo(df_dfe.item, df, df_dfe.name, cmd_to_send_to_plugin);
209 +
210 + if(rfe->progress.cb)
211 + rfe->progress.cb(rfe->progress.data, ++done, all);
212 }
213 }
214 dfe_done(df);
@@ -260,12 +304,8 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
304 else if(c == DYNCFG_CMD_SCHEMA) {
305 bool loaded = false;
306 if(df->type == DYNCFG_TYPE_JOB) {
263 - char template[strlen(id) + 1];
264 - memcpy(template, id, sizeof(template));
265 - char *colon = strrchr(template, ':');
266 - if(colon) *colon = '\0';
267 - if(template[0])
268 - loaded = dyncfg_get_schema(template, rfe->result.wb);
307 + if(df->template)
308 + loaded = dyncfg_get_schema(string2str(df->template), rfe->result.wb);
309 }
310 else
311 loaded = dyncfg_get_schema(id, rfe->result.wb);
@@ -289,7 +329,7 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
329 dyncfg_file_save(id, df);
330 }
331
292 - dyncfg_apply_action_on_all_template_jobs(id, c);
332 + dyncfg_apply_action_on_all_template_jobs(rfe, id, c);
333
334 rc = HTTP_RESP_OK;
335 dyncfg_default_response(rfe->result.wb, rc, "applied");
daemon/config/dyncfg-internals.h
+5 -3
@@ -10,8 +10,7 @@
10 #include "../../database/rrdcollector-internals.h"
11
12 typedef struct dyncfg {
13 - RRDHOST *host;
14 - uuid_t host_uuid;
13 + UUID host_uuid;
14 STRING *function;
15 STRING *template;
16 STRING *path;
@@ -54,7 +53,7 @@ bool dyncfg_get_schema(const char *id, BUFFER *dst);
53 void dyncfg_echo_cb(BUFFER *wb, int code, void *result_cb_data);
54 void dyncfg_echo(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id, DYNCFG_CMDS cmd);
55 void dyncfg_echo_update(const DICTIONARY_ITEM *item, DYNCFG *df, const char *id);
57 -void dyncfg_echo_add(const DICTIONARY_ITEM *template_item, DYNCFG *template_df, const char *template_id, const char *job_name);
56 +void dyncfg_echo_add(const DICTIONARY_ITEM *item_template, const DICTIONARY_ITEM *item_job, DYNCFG *df_template, DYNCFG *df_job, const char *template_id, const char *job_name);
57
58 const DICTIONARY_ITEM *dyncfg_add_internal(RRDHOST *host, const char *id, const char *path, DYNCFG_STATUS status, DYNCFG_TYPE type, DYNCFG_SOURCE_TYPE source_type, const char *source, DYNCFG_CMDS cmds, usec_t created_ut, usec_t modified_ut, bool sync, rrd_function_execute_cb_t execute_cb, void *execute_cb_data, bool overwrite_cb);
59 int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data);
@@ -62,4 +61,7 @@ void dyncfg_cleanup(DYNCFG *v);
61
62 bool dyncfg_is_user_disabled(const char *id);
63
64 +RRDHOST *dyncfg_rrdhost_by_uuid(UUID *uuid);
65 +RRDHOST *dyncfg_rrdhost(DYNCFG *df);
66 +
67 #endif //NETDATA_DYNCFG_INTERNALS_H
daemon/config/dyncfg-tree.c
+5 -5
@@ -60,15 +60,15 @@ static void dyncfg_tree_for_host(RRDHOST *host, BUFFER *wb, const char *path, co
60 if(id && *id)
61 template = string_strdupz(id);
62
63 + UUID host_uuid = uuid2UUID(host->host_uuid);
64 +
65 size_t path_len = strlen(path);
66 DYNCFG *df;
67 dfe_start_read(dyncfg_globals.nodes, df) {
66 - if(!df->host) {
67 - if(uuid_memcmp(&df->host_uuid, &host->host_uuid) == 0)
68 - df->host = host;
69 - }
68 + if(!UUIDeq(df->host_uuid, host_uuid))
69 + continue;
70
71 - if(df->host != host || strncmp(string2str(df->path), path, path_len) != 0)
71 + if(strncmp(string2str(df->path), path, path_len) != 0)
72 continue;
73
74 if(!rrd_function_available(host, string2str(df->function)))
daemon/config/dyncfg-unittest.c
+5
@@ -450,6 +450,11 @@ static int dyncfg_unittest_run(const char *cmd, BUFFER *wb, const char *payload,
450 if(c == DYNCFG_CMD_UPDATE)
451 memset(&t->current.value, 0, sizeof(t->current.value));
452
453 + if(c & (DYNCFG_CMD_UPDATE) || (c & (DYNCFG_CMD_DISABLE|DYNCFG_CMD_ENABLE) && t->type != DYNCFG_TYPE_TEMPLATE)) {
454 + freez((void *)t->source);
455 + t->source = strdupz(source);
456 + }
457 +
458 buffer_flush(wb);
459
460 CLEAN_BUFFER *pld = NULL;
daemon/config/dyncfg.c
+43 -21
@@ -5,6 +5,21 @@
5
6 struct dyncfg_globals dyncfg_globals = { 0 };
7
8 +RRDHOST *dyncfg_rrdhost_by_uuid(UUID *uuid) {
9 + char uuid_str[UUID_STR_LEN];
10 + uuid_unparse_lower(uuid->uuid, uuid_str);
11 +
12 + RRDHOST *host = rrdhost_find_by_guid(uuid_str);
13 + if(!host)
14 + nd_log(NDLS_DAEMON, NDLP_ERR, "DYNCFG: cannot find host with UUID '%s'", uuid_str);
15 +
16 + return host;
17 +}
18 +
19 +RRDHOST *dyncfg_rrdhost(DYNCFG *df) {
20 + return dyncfg_rrdhost_by_uuid(&df->host_uuid);
21 +}
22 +
23 void dyncfg_cleanup(DYNCFG *v) {
24 buffer_free(v->payload);
25 v->payload = NULL;
@@ -69,8 +84,8 @@ static bool dyncfg_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, void
84
85 dyncfg_normalize(nv);
86
72 - if(v->host != nv->host) {
73 - SWAP(v->host, nv->host);
87 + if(!UUIDeq(v->host_uuid, nv->host_uuid)) {
88 + SWAP(v->host_uuid, nv->host_uuid);
89 changes++;
90 }
91
@@ -164,7 +179,7 @@ void dyncfg_init_low_level(bool load_saved) {
179
180 const DICTIONARY_ITEM *dyncfg_add_internal(RRDHOST *host, const char *id, const char *path, DYNCFG_STATUS status, DYNCFG_TYPE type, DYNCFG_SOURCE_TYPE source_type, const char *source, DYNCFG_CMDS cmds, usec_t created_ut, usec_t modified_ut, bool sync, rrd_function_execute_cb_t execute_cb, void *execute_cb_data, bool overwrite_cb) {
181 DYNCFG tmp = {
167 - .host = host,
182 + .host_uuid = uuid2UUID(host->host_uuid),
183 .path = string_strdupz(path),
184 .status = status,
185 .type = type,
@@ -181,7 +196,6 @@ const DICTIONARY_ITEM *dyncfg_add_internal(RRDHOST *host, const char *id, const
196 .execute_cb_data = execute_cb_data,
197 .overwrite_cb = overwrite_cb,
198 };
184 - uuid_copy(tmp.host_uuid, host->host_uuid);
199
200 return dictionary_set_and_acquire_item_advanced(dyncfg_globals.nodes, id, -1, &tmp, sizeof(tmp), NULL);
201 }
@@ -196,21 +210,26 @@ static void dyncfg_send_updates(const char *id) {
210 DYNCFG *df = dictionary_acquired_item_value(item);
211
212 if(df->type == DYNCFG_TYPE_SINGLE || df->type == DYNCFG_TYPE_JOB) {
199 - if (df->cmds & DYNCFG_CMD_UPDATE)
213 + if (df->cmds & DYNCFG_CMD_UPDATE && df->source_type == DYNCFG_SOURCE_TYPE_DYNCFG && df->payload && buffer_strlen(df->payload))
214 dyncfg_echo_update(item, df, id);
215 }
216 else if(df->type == DYNCFG_TYPE_TEMPLATE && (df->cmds & DYNCFG_CMD_ADD)) {
217 STRING *template = string_strdupz(id);
218
219 size_t len = strlen(id);
206 - DYNCFG *tf;
207 - dfe_start_reentrant(dyncfg_globals.nodes, tf) {
208 - const char *t_id = tf_dfe.name;
209 - if(tf->type == DYNCFG_TYPE_JOB && tf->template == template && strncmp(t_id, id, len) == 0 && t_id[len] == ':' && t_id[len + 1]) {
210 - dyncfg_echo_add(item, df, id, &t_id[len + 1]);
220 + DYNCFG *df_job;
221 + dfe_start_reentrant(dyncfg_globals.nodes, df_job) {
222 + const char *id_template = df_job_dfe.name;
223 + if(df_job->type == DYNCFG_TYPE_JOB && // it is a job
224 + df_job->source_type == DYNCFG_SOURCE_TYPE_DYNCFG && // it is dynamically configured
225 + df_job->template == template && // it has the same template name
226 + strncmp(id_template, id, len) == 0 && // the template name matches (redundant)
227 + id_template[len] == ':' && // immediately after the template there is ':'
228 + id_template[len + 1]) { // and there is something else after the ':'
229 + dyncfg_echo_add(item, df_job_dfe.item, df, df_job, id, &id_template[len + 1]);
230 }
231 }
213 - dfe_done(tf);
232 + dfe_done(df_job);
233
234 string_freez(template);
235 }
@@ -279,11 +298,7 @@ bool dyncfg_add_low_level(RRDHOST *host, const char *id, const char *path, DYNCF
298 }
299
300 // remove
282 - if(source_type == DYNCFG_SOURCE_TYPE_DYNCFG && type == DYNCFG_TYPE_JOB) {
283 - // remove is only available for dyncfg jobs
284 - cmds |= DYNCFG_CMD_REMOVE;
285 - }
286 - else {
301 + if(source_type != DYNCFG_SOURCE_TYPE_DYNCFG || type != DYNCFG_TYPE_JOB) {
302 // remove is only available for dyncfg jobs
303 cmds &= ~DYNCFG_CMD_REMOVE;
304 }
@@ -323,12 +338,19 @@ bool dyncfg_add_low_level(RRDHOST *host, const char *id, const char *path, DYNCF
338 dyncfg_function_intercept_cb,
339 NULL);
340
326 - DYNCFG_CMDS status_to_send_to_plugin = df->user_disabled ? DYNCFG_CMD_DISABLE : DYNCFG_CMD_ENABLE;
327 - if(status_to_send_to_plugin == DYNCFG_CMD_ENABLE && dyncfg_is_user_disabled(string2str(df->template)))
328 - status_to_send_to_plugin = DYNCFG_CMD_DISABLE;
341 + if(df->type != DYNCFG_TYPE_TEMPLATE) {
342 + DYNCFG_CMDS status_to_send_to_plugin =
343 + (df->user_disabled || df->status == DYNCFG_STATUS_DISABLED) ? DYNCFG_CMD_DISABLE : DYNCFG_CMD_ENABLE;
344 +
345 + if (status_to_send_to_plugin == DYNCFG_CMD_ENABLE && dyncfg_is_user_disabled(string2str(df->template)))
346 + status_to_send_to_plugin = DYNCFG_CMD_DISABLE;
347 +
348 + dyncfg_echo(item, df, id, status_to_send_to_plugin);
349 + }
350 +
351 + if(!(df->source_type == DYNCFG_SOURCE_TYPE_DYNCFG && df->type == DYNCFG_TYPE_JOB))
352 + dyncfg_send_updates(id);
353
330 - dyncfg_echo(item, df, id, status_to_send_to_plugin);
331 - dyncfg_send_updates(id);
354 dictionary_acquired_item_release(dyncfg_globals.nodes, item);
355
356 return true;
daemon/main.c
+5 -11
@@ -1369,12 +1369,6 @@ int get_system_info(struct rrdhost_system_info *system_info) {
1369 return 0;
1370 }
1371
1372 -void set_silencers_filename() {
1373 - char filename[FILENAME_MAX + 1];
1374 - snprintfz(filename, FILENAME_MAX, "%s/health.silencers.json", netdata_configured_varlib_dir);
1375 - silencers_filename = config_get(CONFIG_SECTION_HEALTH, "silencers file", filename);
1376 -}
1377 -
1372 /* Any config setting that can be accessed without a default value i.e. configget(...,...,NULL) *MUST*
1373 be set in this procedure to be called in all the relevant code paths.
1374 */
@@ -1407,7 +1401,7 @@ int unittest_prepare_rrd(char **user) {
1401 get_netdata_configured_variables();
1402 default_rrd_update_every = 1;
1403 default_rrd_memory_mode = RRD_MEMORY_MODE_RAM;
1410 - default_health_enabled = 0;
1404 + health_plugin_disable();
1405 storage_tiers = 1;
1406 registry_init();
1407 if(rrd_init("unittest", NULL, true)) {
@@ -2006,7 +2000,7 @@ int main(int argc, char **argv) {
2000 // --------------------------------------------------------------------
2001 // This is the safest place to start the SILENCERS structure
2002
2009 - set_silencers_filename();
2003 + health_set_silencers_filename();
2004 health_initialize_global_silencers();
2005
2006 // --------------------------------------------------------------------
@@ -2071,9 +2065,9 @@ int main(int argc, char **argv) {
2065
2066 #ifdef ENABLE_H2O
2067 delta_startup_time("initialize h2o server");
2074 - for (int i = 0; static_threads[i].name; i++) {
2075 - if (static_threads[i].start_routine == h2o_main)
2076 - static_threads[i].enabled = httpd_is_enabled();
2068 + for (int t = 0; static_threads[t].name; t++) {
2069 + if (static_threads[t].start_routine == h2o_main)
2070 + static_threads[t].enabled = httpd_is_enabled();
2071 }
2072 #endif
2073 }
daemon/service.c
+1 -5
@@ -35,8 +35,6 @@ static void svc_rrddim_obsolete_to_archive(RRDDIM *rd) {
35 rrddim_flag_clear(rd, RRDDIM_FLAG_OBSOLETE);
36
37 if (rd->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
38 - rrddimvar_delete_all(rd);
39 -
38 /* only a collector can mark a chart as obsolete, so we must remove the reference */
39
40 size_t tiers_available = 0, tiers_said_no_retention = 0;
@@ -115,9 +113,7 @@ static void svc_rrdset_obsolete_to_free(RRDSET *st) {
113
114 worker_is_busy(WORKER_JOB_FREE_CHART);
115
118 - rrdcalc_unlink_all_rrdset_alerts(st);
119 -
120 - rrdsetvar_release_and_delete_all(st);
116 + rrdcalc_unlink_and_delete_all_rrdset_alerts(st);
117
118 // has to be run after all dimensions are archived - or use-after-free will occur
119 rrdvar_delete_all(st->rrdvars);
daemon/unit_test.c
+11 -11
@@ -1635,40 +1635,40 @@ int unit_test(long delay, long shift)
1635
1636 int test_sqlite(void) {
1637 fprintf(stderr, "%s() running...\n", __FUNCTION__ );
1638 - sqlite3 *db_meta;
1638 + sqlite3 *db_mt;
1639 fprintf(stderr, "Testing SQLIte\n");
1640
1641 - int rc = sqlite3_open(":memory:", &db_meta);
1641 + int rc = sqlite3_open(":memory:", &db_mt);
1642 if (rc != SQLITE_OK) {
1643 fprintf(stderr,"Failed to test SQLite: DB init failed\n");
1644 return 1;
1645 }
1646
1647 - rc = sqlite3_exec_monitored(db_meta, "CREATE TABLE IF NOT EXISTS mine (id1, id2);", 0, 0, NULL);
1647 + rc = sqlite3_exec_monitored(db_mt, "CREATE TABLE IF NOT EXISTS mine (id1, id2);", 0, 0, NULL);
1648 if (rc != SQLITE_OK) {
1649 fprintf(stderr,"Failed to test SQLite: Create table failed\n");
1650 return 1;
1651 }
1652
1653 - rc = sqlite3_exec_monitored(db_meta, "DELETE FROM MINE LIMIT 1;", 0, 0, NULL);
1653 + rc = sqlite3_exec_monitored(db_mt, "DELETE FROM MINE LIMIT 1;", 0, 0, NULL);
1654 if (rc != SQLITE_OK) {
1655 fprintf(stderr,"Failed to test SQLite: Delete with LIMIT failed\n");
1656 return 1;
1657 }
1658
1659 - rc = sqlite3_exec_monitored(db_meta, "UPDATE MINE SET id1=1 LIMIT 1;", 0, 0, NULL);
1659 + rc = sqlite3_exec_monitored(db_mt, "UPDATE MINE SET id1=1 LIMIT 1;", 0, 0, NULL);
1660 if (rc != SQLITE_OK) {
1661 fprintf(stderr,"Failed to test SQLite: Update with LIMIT failed\n");
1662 return 1;
1663 }
1664
1665 - rc = sqlite3_create_function(db_meta, "now_usec", 1, SQLITE_ANY, 0, sqlite_now_usec, 0, 0);
1665 + rc = sqlite3_create_function(db_mt, "now_usec", 1, SQLITE_ANY, 0, sqlite_now_usec, 0, 0);
1666 if (unlikely(rc != SQLITE_OK)) {
1667 fprintf(stderr, "Failed to register internal now_usec function");
1668 return 1;
1669 }
1670
1671 - rc = sqlite3_exec_monitored(db_meta, "UPDATE MINE SET id1=now_usec(0);", 0, 0, NULL);
1671 + rc = sqlite3_exec_monitored(db_mt, "UPDATE MINE SET id1=now_usec(0);", 0, 0, NULL);
1672 if (rc != SQLITE_OK) {
1673 fprintf(stderr,"Failed to test SQLite: Update with now_usec() failed\n");
1674 return 1;
@@ -1678,16 +1678,16 @@ int test_sqlite(void) {
1678 char *uuid_str = "0000_000";
1679
1680 buffer_sprintf(sql, TABLE_ACLK_ALERT, uuid_str);
1681 - rc = sqlite3_exec_monitored(db_meta, buffer_tostring(sql), 0, 0, NULL);
1681 + rc = sqlite3_exec_monitored(db_mt, buffer_tostring(sql), 0, 0, NULL);
1682 if (rc != SQLITE_OK)
1683 goto error;
1684
1685 buffer_free(sql);
1686 fprintf(stderr,"SQLite is OK\n");
1687 - rc = sqlite3_close_v2(db_meta);
1687 + rc = sqlite3_close_v2(db_mt);
1688 return 0;
1689 error:
1690 - rc = sqlite3_close_v2(db_meta);
1690 + rc = sqlite3_close_v2(db_mt);
1691 fprintf(stderr,"SQLite statement failed: %s\n", buffer_tostring(sql));
1692 buffer_free(sql);
1693 fprintf(stderr,"SQLite tests failed\n");
@@ -1837,7 +1837,7 @@ static RRDHOST *dbengine_rrdhost_find_or_create(char *name)
1837 default_rrd_update_every,
1838 default_rrd_history_entries,
1839 RRD_MEMORY_MODE_DBENGINE,
1840 - default_health_enabled,
1840 + health_plugin_enabled(),
1841 default_rrdpush_enabled,
1842 default_rrdpush_destination,
1843 default_rrdpush_api_key,
database/contexts/api_v2.c
+204 -38
@@ -183,6 +183,13 @@ struct context_v2_entry {
183 FTS_MATCH match;
184 };
185
186 +struct alert_counts {
187 + size_t critical;
188 + size_t warning;
189 + size_t clear;
190 + size_t error;
191 +};
192 +
193 struct alert_v2_entry {
194 RRDCALC *tmp;
195
@@ -191,16 +198,25 @@ struct alert_v2_entry {
198
199 size_t ati;
200
194 - size_t critical;
195 - size_t warning;
196 - size_t clear;
197 - size_t error;
201 + struct alert_counts counts;
202
203 size_t instances;
204 DICTIONARY *nodes;
205 DICTIONARY *configs;
206 };
207
208 +struct alert_by_x_entry {
209 + struct {
210 + struct alert_counts counts;
211 + size_t silent;
212 + size_t total;
213 + } running;
214 +
215 + struct {
216 + size_t available;
217 + } prototypes;
218 +};
219 +
220 typedef struct full_text_search_index {
221 size_t searches;
222 size_t string_searches;
@@ -254,8 +270,14 @@ struct rrdcontext_to_json_v2_data {
270
271 size_t ati;
272
257 - DICTIONARY *alerts;
273 + DICTIONARY *summary;
274 DICTIONARY *alert_instances;
275 +
276 + DICTIONARY *by_type;
277 + DICTIONARY *by_component;
278 + DICTIONARY *by_classification;
279 + DICTIONARY *by_recipient;
280 + DICTIONARY *by_module;
281 } alerts;
282
283 struct {
@@ -279,9 +301,7 @@ struct rrdcontext_to_json_v2_data {
301 struct query_timings timings;
302 };
303
282 -static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
283 - t->instances++;
284 -
304 +static void alert_counts_add(struct alert_counts *t, RRDCALC *rc) {
305 switch(rc->status) {
306 case RRDCALC_STATUS_CRITICAL:
307 t->critical++;
@@ -306,20 +326,51 @@ static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
326
327 break;
328 }
329 +}
330 +
331 +static void alerts_v2_add(struct alert_v2_entry *t, RRDCALC *rc) {
332 + t->instances++;
333 +
334 + alert_counts_add(&t->counts, rc);
335
336 dictionary_set(t->nodes, rc->rrdset->rrdhost->machine_guid, NULL, 0);
337
338 char key[UUID_STR_LEN + 1];
313 - uuid_unparse_lower(rc->config_hash_id, key);
339 + uuid_unparse_lower(rc->config.hash_id, key);
340 dictionary_set(t->configs, key, NULL, 0);
341 }
342
343 +static void alerts_by_x_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
344 + static STRING *silent = NULL;
345 + if(unlikely(!silent)) silent = string_strdupz("silent");
346 +
347 + struct alert_by_x_entry *b = value;
348 + RRDCALC *rc = data;
349 + if(!rc) {
350 + // prototype
351 + b->prototypes.available++;
352 + }
353 + else {
354 + alert_counts_add(&b->running.counts, rc);
355 +
356 + b->running.total++;
357 +
358 + if (rc->config.recipient == silent)
359 + b->running.silent++;
360 + }
361 +}
362 +
363 +static bool alerts_by_x_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value __maybe_unused, void *data __maybe_unused) {
364 + alerts_by_x_insert_callback(item, old_value, data);
365 + return false;
366 +}
367 +
368 static void alerts_v2_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
369 struct rrdcontext_to_json_v2_data *ctl = data;
370 struct alert_v2_entry *t = value;
371 RRDCALC *rc = t->tmp;
321 - t->name = rc->name;
322 - t->summary = rc->original_summary;
372 + t->name = rc->config.name;
373 + t->summary = rc->config.summary; // the original summary
374 t->ati = ctl->alerts.ati++;
375
376 t->nodes = dictionary_create(DICT_OPTION_SINGLE_THREADED|DICT_OPTION_VALUE_LINK_DONT_CLONE|DICT_OPTION_NAME_LINK_DONT_CLONE);
@@ -350,16 +401,16 @@ static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __may
401 t->chart_id = rc->rrdset->id;
402 t->chart_name = rc->rrdset->name;
403 t->family = rc->rrdset->family;
353 - t->units = rc->units;
354 - t->classification = rc->classification;
355 - t->type = rc->type;
356 - t->recipient = rc->recipient;
357 - t->component = rc->component;
358 - t->name = rc->name;
359 - t->source = rc->source;
404 + t->units = rc->config.units;
405 + t->classification = rc->config.classification;
406 + t->type = rc->config.type;
407 + t->recipient = rc->config.recipient;
408 + t->component = rc->config.component;
409 + t->name = rc->config.name;
410 + t->source = rc->config.source;
411 t->status = rc->status;
412 t->flags = rc->run_flags;
362 - t->info = rc->info;
413 + t->info = rc->config.info;
414 t->summary = rc->summary;
415 t->value = rc->value;
416 t->last_updated = rc->last_updated;
@@ -368,10 +419,8 @@ static void alert_instances_v2_insert_callback(const DICTIONARY_ITEM *item __may
419 t->host = rc->rrdset->rrdhost;
420 t->alarm_id = rc->id;
421 t->ni = ctl->nodes.ni;
371 - t->name = rc->name;
372 -
373 - uuid_copy(t->config_hash_id, rc->config_hash_id);
422
423 + uuid_copy(t->config_hash_id, rc->config.hash_id);
424 health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(rc, &t->global_id, &t->last_transition_id);
425 }
426
@@ -437,12 +486,12 @@ static FTS_MATCH rrdcontext_to_json_v2_full_text_search(struct rrdcontext_to_jso
486 RRDSET *st = ri->rrdset;
487 rw_spinlock_read_lock(&st->alerts.spinlock);
488 for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
440 - if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->name))) {
489 + if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->config.name))) {
490 matched = FTS_MATCHED_ALERT;
491 break;
492 }
493
445 - if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->info))) {
494 + if(unlikely(full_text_search_string(&ctl->q.fts, q, rcl->config.info))) {
495 matched = FTS_MATCHED_ALERT_INFO;
496 break;
497 }
@@ -462,7 +511,7 @@ static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRD
511 RRDSET *st = ri->rrdset;
512 rw_spinlock_read_lock(&st->alerts.spinlock);
513 for (RRDCALC *rcl = st->alerts.base; rcl; rcl = rcl->next) {
465 - if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->name))
514 + if(ctl->alerts.alert_name_pattern && !simple_pattern_matches_string(ctl->alerts.alert_name_pattern, rcl->config.name))
515 continue;
516
517 if(ctl->alerts.alarm_id_filter && ctl->alerts.alarm_id_filter != rcl->id)
@@ -502,11 +551,51 @@ static bool rrdcontext_matches_alert(struct rrdcontext_to_json_v2_data *ctl, RRD
551 struct alert_v2_entry t = {
552 .tmp = rcl,
553 };
505 - struct alert_v2_entry *a2e = dictionary_set(ctl->alerts.alerts, string2str(rcl->name), &t,
506 - sizeof(struct alert_v2_entry));
554 + struct alert_v2_entry *a2e =
555 + dictionary_set(ctl->alerts.summary, string2str(rcl->config.name),
556 + &t, sizeof(struct alert_v2_entry));
557 size_t ati = a2e->ati;
558 matches++;
559
560 + dictionary_set_advanced(ctl->alerts.by_type,
561 + string2str(rcl->config.type),
562 + (ssize_t)string_strlen(rcl->config.type),
563 + NULL,
564 + sizeof(struct alert_by_x_entry),
565 + rcl);
566 +
567 + dictionary_set_advanced(ctl->alerts.by_component,
568 + string2str(rcl->config.component),
569 + (ssize_t)string_strlen(rcl->config.component),
570 + NULL,
571 + sizeof(struct alert_by_x_entry),
572 + rcl);
573 +
574 + dictionary_set_advanced(ctl->alerts.by_classification,
575 + string2str(rcl->config.classification),
576 + (ssize_t)string_strlen(rcl->config.classification),
577 + NULL,
578 + sizeof(struct alert_by_x_entry),
579 + rcl);
580 +
581 + dictionary_set_advanced(ctl->alerts.by_recipient,
582 + string2str(rcl->config.recipient),
583 + (ssize_t)string_strlen(rcl->config.recipient),
584 + NULL,
585 + sizeof(struct alert_by_x_entry),
586 + rcl);
587 +
588 + char *module = NULL;
589 + rrdlabels_get_value_strdup_or_null(st->rrdlabels, &module, "_collect_module");
590 + if(!module || !*module) module = "[unset]";
591 +
592 + dictionary_set_advanced(ctl->alerts.by_module,
593 + module,
594 + -1,
595 + NULL,
596 + sizeof(struct alert_by_x_entry),
597 + rcl);
598 +
599 if (ctl->options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES | CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) {
600 char key[20 + 1];
601 snprintfz(key, sizeof(key) - 1, "%p", rcl);
@@ -1254,7 +1343,7 @@ static void contexts_v2_alert_config_to_json_from_sql_alert_config_data(struct s
1343 buffer_json_member_add_time_t(wb, "before", t->value.db.before);
1344 buffer_json_member_add_string(wb, "method", t->value.db.method);
1345 buffer_json_member_add_string(wb, "dimensions", t->value.db.dimensions);
1257 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options",(RRDR_OPTIONS) t->value.db.options);
1346 + rrdr_options_to_buffer_json_array(wb, "options", (RRDR_OPTIONS)t->value.db.options);
1347 }
1348 buffer_json_object_close(wb); // db
1349 }
@@ -1394,6 +1483,41 @@ static int contexts_v2_alert_instance_to_json_callback(const DICTIONARY_ITEM *it
1483 return 1;
1484 }
1485
1486 +static void contexts_v2_alerts_by_x_update_prototypes(void *data, STRING *type, STRING *component, STRING *classification, STRING *recipient) {
1487 + struct rrdcontext_to_json_v2_data *ctl = data;
1488 +
1489 + dictionary_set_advanced(ctl->alerts.by_type, string2str(type), (ssize_t)string_strlen(type), NULL, sizeof(struct alert_by_x_entry), NULL);
1490 + dictionary_set_advanced(ctl->alerts.by_component, string2str(component), (ssize_t)string_strlen(component), NULL, sizeof(struct alert_by_x_entry), NULL);
1491 + dictionary_set_advanced(ctl->alerts.by_classification, string2str(classification), (ssize_t)string_strlen(classification), NULL, sizeof(struct alert_by_x_entry), NULL);
1492 + dictionary_set_advanced(ctl->alerts.by_recipient, string2str(recipient), (ssize_t)string_strlen(recipient), NULL, sizeof(struct alert_by_x_entry), NULL);
1493 +}
1494 +
1495 +static void contexts_v2_alerts_by_x_to_json(BUFFER *wb, DICTIONARY *dict, const char *key) {
1496 + buffer_json_member_add_array(wb, key);
1497 + {
1498 + struct alert_by_x_entry *b;
1499 + dfe_start_read(dict, b) {
1500 + buffer_json_add_array_item_object(wb);
1501 + {
1502 + buffer_json_member_add_string(wb, "name", b_dfe.name);
1503 + buffer_json_member_add_uint64(wb, "cr", b->running.counts.critical);
1504 + buffer_json_member_add_uint64(wb, "wr", b->running.counts.warning);
1505 + buffer_json_member_add_uint64(wb, "cl", b->running.counts.clear);
1506 + buffer_json_member_add_uint64(wb, "er", b->running.counts.error);
1507 + buffer_json_member_add_uint64(wb, "running", b->running.total);
1508 +
1509 + buffer_json_member_add_uint64(wb, "running_silent", b->running.silent);
1510 +
1511 + if(b->prototypes.available)
1512 + buffer_json_member_add_uint64(wb, "available", b->prototypes.available);
1513 + }
1514 + buffer_json_object_close(wb);
1515 + }
1516 + dfe_done(b);
1517 + }
1518 + buffer_json_array_close(wb);
1519 +}
1520 +
1521 static void contexts_v2_alert_instances_to_json(BUFFER *wb, const char *key, struct rrdcontext_to_json_v2_data *ctl, bool debug) {
1522 buffer_json_member_add_array(wb, key);
1523 {
@@ -1413,7 +1537,7 @@ static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_
1537 buffer_json_member_add_array(wb, "alerts");
1538 {
1539 struct alert_v2_entry *t;
1416 - dfe_start_read(ctl->alerts.alerts, t)
1540 + dfe_start_read(ctl->alerts.summary, t)
1541 {
1542 buffer_json_add_array_item_object(wb);
1543 {
@@ -1421,10 +1545,10 @@ static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_
1545 buffer_json_member_add_string(wb, "nm", string2str(t->name));
1546 buffer_json_member_add_string(wb, "sum", string2str(t->summary));
1547
1424 - buffer_json_member_add_uint64(wb, "cr", t->critical);
1425 - buffer_json_member_add_uint64(wb, "wr", t->warning);
1426 - buffer_json_member_add_uint64(wb, "cl", t->clear);
1427 - buffer_json_member_add_uint64(wb, "er", t->error);
1548 + buffer_json_member_add_uint64(wb, "cr", t->counts.critical);
1549 + buffer_json_member_add_uint64(wb, "wr", t->counts.warning);
1550 + buffer_json_member_add_uint64(wb, "cl", t->counts.clear);
1551 + buffer_json_member_add_uint64(wb, "er", t->counts.error);
1552
1553 buffer_json_member_add_uint64(wb, "in", t->instances);
1554 buffer_json_member_add_uint64(wb, "nd", dictionary_entries(t->nodes));
@@ -1435,6 +1559,13 @@ static void contexts_v2_alerts_to_json(BUFFER *wb, struct rrdcontext_to_json_v2_
1559 dfe_done(t);
1560 }
1561 buffer_json_array_close(wb); // alerts
1562 +
1563 + health_prototype_metadata_foreach(ctl, contexts_v2_alerts_by_x_update_prototypes);
1564 + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_type, "alerts_by_type");
1565 + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_component, "alerts_by_component");
1566 + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_classification, "alerts_by_classification");
1567 + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_recipient, "alerts_by_recipient");
1568 + contexts_v2_alerts_by_x_to_json(wb, ctl->alerts.by_module, "alerts_by_module");
1569 }
1570
1571 if(ctl->request->options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES|CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) {
@@ -1942,12 +2073,42 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
2073 }
2074 }
2075
1945 - ctl.alerts.alerts = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2076 + ctl.alerts.summary = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2077 NULL, sizeof(struct alert_v2_entry));
2078
1948 - dictionary_register_insert_callback(ctl.alerts.alerts, alerts_v2_insert_callback, &ctl);
1949 - dictionary_register_conflict_callback(ctl.alerts.alerts, alerts_v2_conflict_callback, &ctl);
1950 - dictionary_register_delete_callback(ctl.alerts.alerts, alerts_v2_delete_callback, &ctl);
2079 + dictionary_register_insert_callback(ctl.alerts.summary, alerts_v2_insert_callback, &ctl);
2080 + dictionary_register_conflict_callback(ctl.alerts.summary, alerts_v2_conflict_callback, &ctl);
2081 + dictionary_register_delete_callback(ctl.alerts.summary, alerts_v2_delete_callback, &ctl);
2082 +
2083 + ctl.alerts.by_type = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2084 + NULL, sizeof(struct alert_by_x_entry));
2085 +
2086 + dictionary_register_insert_callback(ctl.alerts.by_type, alerts_by_x_insert_callback, NULL);
2087 + dictionary_register_conflict_callback(ctl.alerts.by_type, alerts_by_x_conflict_callback, NULL);
2088 +
2089 + ctl.alerts.by_component = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2090 + NULL, sizeof(struct alert_by_x_entry));
2091 +
2092 + dictionary_register_insert_callback(ctl.alerts.by_component, alerts_by_x_insert_callback, NULL);
2093 + dictionary_register_conflict_callback(ctl.alerts.by_component, alerts_by_x_conflict_callback, NULL);
2094 +
2095 + ctl.alerts.by_classification = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2096 + NULL, sizeof(struct alert_by_x_entry));
2097 +
2098 + dictionary_register_insert_callback(ctl.alerts.by_classification, alerts_by_x_insert_callback, NULL);
2099 + dictionary_register_conflict_callback(ctl.alerts.by_classification, alerts_by_x_conflict_callback, NULL);
2100 +
2101 + ctl.alerts.by_recipient = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2102 + NULL, sizeof(struct alert_by_x_entry));
2103 +
2104 + dictionary_register_insert_callback(ctl.alerts.by_recipient, alerts_by_x_insert_callback, NULL);
2105 + dictionary_register_conflict_callback(ctl.alerts.by_recipient, alerts_by_x_conflict_callback, NULL);
2106 +
2107 + ctl.alerts.by_module = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
2108 + NULL, sizeof(struct alert_by_x_entry));
2109 +
2110 + dictionary_register_insert_callback(ctl.alerts.by_module, alerts_by_x_insert_callback, NULL);
2111 + dictionary_register_conflict_callback(ctl.alerts.by_module, alerts_by_x_conflict_callback, NULL);
2112
2113 if(ctl.options & (CONTEXT_V2_OPTION_ALERTS_WITH_INSTANCES | CONTEXT_V2_OPTION_ALERTS_WITH_VALUES)) {
2114 ctl.alerts.alert_instances = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
@@ -2150,8 +2311,13 @@ cleanup:
2311 dictionary_destroy(ctl.nodes.dict);
2312 dictionary_destroy(ctl.contexts.dict);
2313 dictionary_destroy(ctl.functions.dict);
2153 - dictionary_destroy(ctl.alerts.alerts);
2314 + dictionary_destroy(ctl.alerts.summary);
2315 dictionary_destroy(ctl.alerts.alert_instances);
2316 + dictionary_destroy(ctl.alerts.by_type);
2317 + dictionary_destroy(ctl.alerts.by_component);
2318 + dictionary_destroy(ctl.alerts.by_classification);
2319 + dictionary_destroy(ctl.alerts.by_recipient);
2320 + dictionary_destroy(ctl.alerts.by_module);
2321 simple_pattern_free(ctl.nodes.scope_pattern);
2322 simple_pattern_free(ctl.nodes.pattern);
2323 simple_pattern_free(ctl.contexts.pattern);
database/contexts/query_target.c
+2 -2
@@ -627,7 +627,7 @@ static bool query_target_match_alert_pattern(RRDINSTANCE_ACQUIRED *ria, SIMPLE_P
627 rw_spinlock_read_lock(&st->alerts.spinlock);
628 if (st->alerts.base) {
629 for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) {
630 - SIMPLE_PATTERN_RESULT ret = simple_pattern_matches_string_extract(pattern, rc->name, NULL, 0);
630 + SIMPLE_PATTERN_RESULT ret = simple_pattern_matches_string_extract(pattern, rc->config.name, NULL, 0);
631
632 if(ret == SP_MATCHED_POSITIVE) {
633 matched = true;
@@ -641,7 +641,7 @@ static bool query_target_match_alert_pattern(RRDINSTANCE_ACQUIRED *ria, SIMPLE_P
641 else
642 buffer_flush(wb);
643
644 - buffer_fast_strcat(wb, string2str(rc->name), string_strlen(rc->name));
644 + buffer_fast_strcat(wb, string2str(rc->config.name), string_strlen(rc->config.name));
645 buffer_fast_strcat(wb, ":", 1);
646 buffer_strcat(wb, rrdcalc_status2string(rc->status));
647
database/engine/rrdengine.c
+3 -3
@@ -229,7 +229,7 @@ static void after_work_standard_callback(uv_work_t* req, int status) {
229 worker_is_idle();
230 }
231
232 -static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct completion *completion, enum rrdeng_opcode opcode, work_cb work_cb, after_work_cb after_work_cb) {
232 +static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct completion *completion, enum rrdeng_opcode opcode, work_cb do_work_cb, after_work_cb do_after_work_cb) {
233 struct rrdeng_work *work_request = NULL;
234
235 internal_fatal(rrdeng_main.tid != gettid(), "work_dispatch() can only be run from the event loop thread");
@@ -240,8 +240,8 @@ static bool work_dispatch(struct rrdengine_instance *ctx, void *data, struct com
240 work_request->ctx = ctx;
241 work_request->data = data;
242 work_request->completion = completion;
243 - work_request->work_cb = work_cb;
244 - work_request->after_work_cb = after_work_cb;
243 + work_request->work_cb = do_work_cb;
244 + work_request->after_work_cb = do_after_work_cb;
245 work_request->opcode = opcode;
246
247 if(uv_queue_work(&rrdeng_main.loop, &work_request->req, work_standard_worker, after_work_standard_callback)) {
database/rrd.h
+5 -29
@@ -20,14 +20,11 @@ typedef struct rrdhost RRDHOST;
20 typedef struct rrddim RRDDIM;
21 typedef struct rrdset RRDSET;
22 typedef struct rrdcalc RRDCALC;
23 -typedef struct rrdcalctemplate RRDCALCTEMPLATE;
23 typedef struct alarm_entry ALARM_ENTRY;
24
25 typedef struct rrdlabels RRDLABELS;
26
28 -typedef struct rrdfamily_acquired RRDFAMILY_ACQUIRED;
27 typedef struct rrdvar_acquired RRDVAR_ACQUIRED;
30 -typedef struct rrdsetvar_acquired RRDSETVAR_ACQUIRED;
28 typedef struct rrdcalc_acquired RRDCALC_ACQUIRED;
29
30 typedef struct rrdhost_acquired RRDHOST_ACQUIRED;
@@ -106,11 +103,8 @@ struct ml_metrics_statistics {
103 #include "daemon/common.h"
104 #include "web/api/queries/query.h"
105 #include "web/api/queries/rrdr.h"
109 -#include "rrdvar.h"
110 -#include "rrdsetvar.h"
111 -#include "rrddimvar.h"
112 -#include "rrdcalc.h"
113 -#include "rrdcalctemplate.h"
106 +#include "health/rrdvar.h"
107 +#include "health/rrdcalc.h"
108 #include "rrdlabels.h"
109 #include "streaming/rrdpush.h"
110 #include "aclk/aclk_rrdhost_state.h"
@@ -214,16 +208,6 @@ typedef enum __attribute__ ((__packed__)) rrd_algorithm {
208 RRD_ALGORITHM rrd_algorithm_id(const char *name);
209 const char *rrd_algorithm_name(RRD_ALGORITHM algorithm);
210
217 -// ----------------------------------------------------------------------------
218 -// RRD FAMILY
219 -
220 -const RRDFAMILY_ACQUIRED *rrdfamily_add_and_acquire(RRDHOST *host, const char *id);
221 -void rrdfamily_release(RRDHOST *host, const RRDFAMILY_ACQUIRED *rfa);
222 -void rrdfamily_index_init(RRDHOST *host);
223 -void rrdfamily_index_destroy(RRDHOST *host);
224 -DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rf);
225 -
226 -
211 // ----------------------------------------------------------------------------
212 // flags & options
213
@@ -740,9 +724,6 @@ struct rrdset {
724 int32_t update_every; // data collection frequency
725
726 RRDLABELS *rrdlabels; // chart labels
743 - DICTIONARY *rrdsetvar_root_index; // chart variables
744 - DICTIONARY *rrddimvar_root_index; // dimension variables
745 - // we use this dictionary to manage their allocation
727
728 uint32_t version; // the metadata version (auto-increment)
729
@@ -833,7 +814,6 @@ struct rrdset {
814 NETDATA_DOUBLE red; // red threshold for this chart
815
816 DICTIONARY *rrdvars; // RRDVAR index for this chart
836 - const RRDFAMILY_ACQUIRED *rrdfamily; // pointer to RRDFAMILY dictionary item, this chart belongs to
817
818 struct {
819 RW_SPINLOCK spinlock; // protection for RRDCALC *base
@@ -1274,9 +1254,6 @@ struct rrdhost {
1254 // all RRDCALCs are primarily allocated and linked here
1255 DICTIONARY *rrdcalc_root_index;
1256
1277 - // templates of alarms
1278 - DICTIONARY *rrdcalctemplate_root_index;
1279 -
1257 ALARM_LOG health_log; // alarms historical events (event log)
1258 uint32_t health_last_processed_id; // the last processed health id from the log
1259 uint32_t health_max_unique_id; // the max alarm log unique id given for the host
@@ -1306,7 +1283,6 @@ struct rrdhost {
1283 DICTIONARY *rrdset_root_index; // the host's charts index (by id)
1284 DICTIONARY *rrdset_root_index_name; // the host's charts index (by name)
1285
1309 - DICTIONARY *rrdfamily_root_index; // the host's chart families index
1286 DICTIONARY *rrdvars; // the host's chart variables index
1287 // this includes custom host variables
1288
@@ -1414,8 +1390,8 @@ RRDHOST *rrdhost_find_or_create(
1390 const char *abbrev_timezone,
1391 int32_t utc_offset,
1392 const char *tags,
1417 - const char *program_name,
1418 - const char *program_version,
1393 + const char *prog_name,
1394 + const char *prog_version,
1395 int update_every,
1396 long history,
1397 RRD_MEMORY_MODE mode,
@@ -1606,7 +1582,7 @@ void rrdset_reset(RRDSET *st);
1582 void set_host_properties(
1583 RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode, const char *registry_hostname,
1584 const char *os, const char *tags, const char *tzone, const char *abbrev_tzone, int32_t utc_offset,
1609 - const char *program_name, const char *program_version);
1585 + const char *prog_name, const char *prog_version);
1586
1587 size_t get_tier_grouping(size_t tier);
1588 void store_metric_collection_completed(void);
database/rrdcalc.c deleted
-867
@@ -1,867 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrd.h"
4 -
5 -// ----------------------------------------------------------------------------
6 -// RRDCALC helpers
7 -
8 -void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags) {
9 - buffer_json_member_add_array(wb, key);
10 -
11 - if(flags & RRDCALC_FLAG_DB_ERROR)
12 - buffer_json_add_array_item_string(wb, "DB_ERROR");
13 - if(flags & RRDCALC_FLAG_DB_NAN)
14 - buffer_json_add_array_item_string(wb, "DB_NAN");
15 - if(flags & RRDCALC_FLAG_CALC_ERROR)
16 - buffer_json_add_array_item_string(wb, "CALC_ERROR");
17 - if(flags & RRDCALC_FLAG_WARN_ERROR)
18 - buffer_json_add_array_item_string(wb, "WARN_ERROR");
19 - if(flags & RRDCALC_FLAG_CRIT_ERROR)
20 - buffer_json_add_array_item_string(wb, "CRIT_ERROR");
21 - if(flags & RRDCALC_FLAG_RUNNABLE)
22 - buffer_json_add_array_item_string(wb, "RUNNABLE");
23 - if(flags & RRDCALC_FLAG_DISABLED)
24 - buffer_json_add_array_item_string(wb, "DISABLED");
25 - if(flags & RRDCALC_FLAG_SILENCED)
26 - buffer_json_add_array_item_string(wb, "SILENCED");
27 - if(flags & RRDCALC_FLAG_RUN_ONCE)
28 - buffer_json_add_array_item_string(wb, "RUN_ONCE");
29 - if(flags & RRDCALC_FLAG_FROM_TEMPLATE)
30 - buffer_json_add_array_item_string(wb, "FROM_TEMPLATE");
31 -
32 - buffer_json_array_close(wb);
33 -}
34 -
35 -inline const char *rrdcalc_status2string(RRDCALC_STATUS status) {
36 - switch(status) {
37 - case RRDCALC_STATUS_REMOVED:
38 - return "REMOVED";
39 -
40 - case RRDCALC_STATUS_UNDEFINED:
41 - return "UNDEFINED";
42 -
43 - case RRDCALC_STATUS_UNINITIALIZED:
44 - return "UNINITIALIZED";
45 -
46 - case RRDCALC_STATUS_CLEAR:
47 - return "CLEAR";
48 -
49 - case RRDCALC_STATUS_RAISED:
50 - return "RAISED";
51 -
52 - case RRDCALC_STATUS_WARNING:
53 - return "WARNING";
54 -
55 - case RRDCALC_STATUS_CRITICAL:
56 - return "CRITICAL";
57 -
58 - default:
59 - netdata_log_error("Unknown alarm status %d", status);
60 - return "UNKNOWN";
61 - }
62 -}
63 -
64 -uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id) {
65 - rw_spinlock_read_lock(&host->health_log.spinlock);
66 -
67 - // re-use old IDs, by looking them up in the alarm log
68 - ALARM_ENTRY *ae = NULL;
69 - for(ae = host->health_log.alarms; ae ;ae = ae->next) {
70 - if(unlikely(name == ae->name && chart == ae->chart && !uuid_memcmp(&ae->config_hash_id, config_hash_id))) {
71 - if(next_event_id) *next_event_id = ae->alarm_event_id + 1;
72 - break;
73 - }
74 - }
75 -
76 - uint32_t alarm_id;
77 -
78 - if(ae)
79 - alarm_id = ae->alarm_id;
80 -
81 - else {
82 - alarm_id = sql_get_alarm_id(host, chart, name, next_event_id, config_hash_id);
83 -
84 - if (!alarm_id) {
85 - //check possible stored config hash as zeroes or null
86 - alarm_id = sql_get_alarm_id_check_zero_hash(host, chart, name, next_event_id, config_hash_id);
87 - if (!alarm_id) {
88 - if (unlikely(!host->health_log.next_alarm_id))
89 - host->health_log.next_alarm_id = (uint32_t)now_realtime_sec();
90 -
91 - alarm_id = host->health_log.next_alarm_id++;
92 - }
93 - }
94 - }
95 -
96 - rw_spinlock_read_unlock(&host->health_log.spinlock);
97 - return alarm_id;
98 -}
99 -
100 -// ----------------------------------------------------------------------------
101 -// RRDCALC replacing info/summary text variables with RRDSET labels
102 -
103 -static STRING *rrdcalc_replace_variables_with_rrdset_labels(const char *line, RRDCALC *rc) {
104 - if (!line || !*line)
105 - return NULL;
106 -
107 - size_t pos = 0;
108 - char *temp = strdupz(line);
109 - char var[RRDCALC_VAR_MAX];
110 - char *m, *lbl_value = NULL;
111 -
112 - while ((m = strchr(temp + pos, '$')) && *(m+1) == '{') {
113 - int i = 0;
114 - char *e = m;
115 - while (*e) {
116 - var[i++] = *e;
117 -
118 - if (*e == '}' || i == RRDCALC_VAR_MAX - 1)
119 - break;
120 -
121 - e++;
122 - }
123 -
124 - var[i] = '\0';
125 - pos = m - temp + 1;
126 -
127 - if (!strcmp(var, RRDCALC_VAR_FAMILY)) {
128 - char *buf = find_and_replace(temp, var, (rc->rrdset && rc->rrdset->family) ? rrdset_family(rc->rrdset) : "", m);
129 - freez(temp);
130 - temp = buf;
131 - }
132 - else if (!strncmp(var, RRDCALC_VAR_LABEL, RRDCALC_VAR_LABEL_LEN)) {
133 - char label_val[RRDCALC_VAR_MAX + RRDCALC_VAR_LABEL_LEN + 1] = { 0 };
134 - strcpy(label_val, var+RRDCALC_VAR_LABEL_LEN);
135 - label_val[i - RRDCALC_VAR_LABEL_LEN - 1] = '\0';
136 -
137 - if(likely(rc->rrdset && rc->rrdset->rrdlabels)) {
138 - lbl_value = NULL;
139 - rrdlabels_get_value_strdup_or_null(rc->rrdset->rrdlabels, &lbl_value, label_val);
140 - if (lbl_value) {
141 - char *buf = find_and_replace(temp, var, lbl_value, m);
142 - freez(temp);
143 - temp = buf;
144 - freez(lbl_value);
145 - }
146 - }
147 - }
148 - }
149 -
150 - STRING *ret = string_strdupz(temp);
151 - freez(temp);
152 -
153 - return ret;
154 -}
155 -
156 -void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc) {
157 - if(!rc->rrdset || !rc->original_info || !rc->rrdset->rrdlabels) return;
158 -
159 - size_t labels_version = rrdlabels_version(rc->rrdset->rrdlabels);
160 - if(rc->labels_version != labels_version) {
161 -
162 - if (rc->original_info) {
163 - STRING *old = rc->info;
164 - rc->info = rrdcalc_replace_variables_with_rrdset_labels(rrdcalc_original_info(rc), rc);
165 - string_freez(old);
166 - }
167 -
168 - if (rc->original_summary) {
169 - STRING *old = rc->summary;
170 - rc->summary = rrdcalc_replace_variables_with_rrdset_labels(rrdcalc_original_summary(rc), rc);
171 - string_freez(old);
172 - }
173 -
174 - rc->labels_version = labels_version;
175 - }
176 -}
177 -
178 -// ----------------------------------------------------------------------------
179 -// RRDCALC index management for RRDSET
180 -
181 -// the dictionary requires a unique key for every item
182 -// we use {chart id}.{alert name} for both the RRDHOST and RRDSET alert indexes.
183 -
184 -#define RRDCALC_MAX_KEY_SIZE 1024
185 -static size_t rrdcalc_key(char *dst, size_t dst_len, const char *chart, const char *alert) {
186 - return snprintfz(dst, dst_len, "%s/%s", chart, alert);
187 -}
188 -
189 -const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name) {
190 - char key[RRDCALC_MAX_KEY_SIZE + 1];
191 - size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st), alert_name);
192 -
193 - const RRDCALC_ACQUIRED *rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)key_len);
194 -
195 - if(!rca) {
196 - key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_name(st), alert_name);
197 - rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)key_len);
198 - }
199 -
200 - return rca;
201 -}
202 -
203 -void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca) {
204 - if(!rca) return;
205 -
206 - dictionary_acquired_item_release(st->rrdhost->rrdcalc_root_index, (const DICTIONARY_ITEM *)rca);
207 -}
208 -
209 -RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca) {
210 - if(rca)
211 - return dictionary_acquired_item_value((const DICTIONARY_ITEM *)rca);
212 -
213 - return NULL;
214 -}
215 -
216 -// ----------------------------------------------------------------------------
217 -// RRDCALC managing the linking with RRDSET
218 -
219 -static void rrdcalc_link_to_rrdset(RRDSET *st, RRDCALC *rc) {
220 - RRDHOST *host = st->rrdhost;
221 -
222 - netdata_log_debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host));
223 -
224 - rc->last_status_change_value = rc->value;
225 - rc->last_status_change = now_realtime_sec();
226 - rc->rrdset = st;
227 -
228 - rw_spinlock_write_lock(&st->alerts.spinlock);
229 - DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(st->alerts.base, rc, prev, next);
230 - rw_spinlock_write_unlock(&st->alerts.spinlock);
231 -
232 - if(rc->update_every < rc->rrdset->update_every) {
233 - netdata_log_info("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every);
234 - rc->update_every = rc->rrdset->update_every;
235 - }
236 -
237 - if(!isnan(rc->green) && isnan(st->green)) {
238 - netdata_log_debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from " NETDATA_DOUBLE_FORMAT_AUTO
239 - " to " NETDATA_DOUBLE_FORMAT_AUTO ".", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->rrdset->green, rc->green);
240 - st->green = rc->green;
241 - }
242 -
243 - if(!isnan(rc->red) && isnan(st->red)) {
244 - netdata_log_debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from " NETDATA_DOUBLE_FORMAT_AUTO " to " NETDATA_DOUBLE_FORMAT_AUTO
245 - ".", rrdset_id(rc->rrdset), rrdcalc_name(rc), rc->rrdset->red, rc->red);
246 - st->red = rc->red;
247 - }
248 -
249 - char buf[RRDVAR_MAX_LENGTH + 1];
250 - snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), rrdcalc_name(rc));
251 - STRING *rrdset_name_rrdcalc_name = string_strdupz(buf);
252 - snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), rrdcalc_name(rc));
253 - STRING *rrdset_id_rrdcalc_name = string_strdupz(buf);
254 -
255 - rc->rrdvar_local = rrdvar_add_and_acquire(
256 - "local",
257 - st->rrdvars,
258 - rc->name,
259 - RRDVAR_TYPE_CALCULATED,
260 - RRDVAR_FLAG_RRDCALC_LOCAL_VAR,
261 - &rc->value);
262 -
263 - rc->rrdvar_family = rrdvar_add_and_acquire(
264 - "family",
265 - rrdfamily_rrdvars_dict(st->rrdfamily),
266 - rc->name,
267 - RRDVAR_TYPE_CALCULATED,
268 - RRDVAR_FLAG_RRDCALC_FAMILY_VAR,
269 - &rc->value);
270 -
271 - rc->rrdvar_host_chart_name = rrdvar_add_and_acquire(
272 - "host",
273 - host->rrdvars,
274 - rrdset_name_rrdcalc_name,
275 - RRDVAR_TYPE_CALCULATED,
276 - RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR,
277 - &rc->value);
278 -
279 - rc->rrdvar_host_chart_id = rrdvar_add_and_acquire(
280 - "host",
281 - host->rrdvars,
282 - rrdset_id_rrdcalc_name,
283 - RRDVAR_TYPE_CALCULATED,
284 - RRDVAR_FLAG_RRDCALC_HOST_CHARTID_VAR | ((rc->rrdvar_host_chart_name) ? 0 : RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR),
285 - &rc->value);
286 -
287 - string_freez(rrdset_id_rrdcalc_name);
288 - string_freez(rrdset_name_rrdcalc_name);
289 -
290 - if(!rc->units)
291 - rc->units = string_dup(st->units);
292 -
293 - rrdvar_store_for_chart(host, st);
294 -
295 - rrdcalc_update_info_using_rrdset_labels(rc);
296 -
297 - if(!rc->summary) {
298 - rc->summary = string_dup(rc->name);
299 - rc->original_summary = string_dup(rc->name);
300 - }
301 -
302 - time_t now = now_realtime_sec();
303 -
304 - ALARM_ENTRY *ae = health_create_alarm_entry(
305 - host,
306 - rc->id,
307 - rc->next_event_id++,
308 - rc->config_hash_id,
309 - now,
310 - rc->name,
311 - rc->rrdset->id,
312 - rc->rrdset->context,
313 - rc->rrdset->name,
314 - rc->classification,
315 - rc->component,
316 - rc->type,
317 - rc->exec,
318 - rc->recipient,
319 - now - rc->last_status_change,
320 - rc->old_value,
321 - rc->value,
322 - RRDCALC_STATUS_REMOVED,
323 - rc->status,
324 - rc->source,
325 - rc->units,
326 - rc->summary,
327 - rc->info,
328 - 0,
329 - rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0);
330 -
331 - health_alarm_log_add_entry(host, ae);
332 - rrdset_flag_set(st, RRDSET_FLAG_HAS_RRDCALC_LINKED);
333 -}
334 -
335 -static void rrdcalc_unlink_from_rrdset(RRDCALC *rc, bool having_ll_wrlock) {
336 - RRDSET *st = rc->rrdset;
337 -
338 - if(!st) {
339 - netdata_log_debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rrdcalc_chart_name(rc), rrdcalc_name(rc));
340 - netdata_log_error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rrdcalc_chart_name(rc), rrdcalc_name(rc));
341 - return;
342 - }
343 -
344 - RRDHOST *host = st->rrdhost;
345 -
346 - time_t now = now_realtime_sec();
347 -
348 - if (likely(rc->status != RRDCALC_STATUS_REMOVED)) {
349 - ALARM_ENTRY *ae = health_create_alarm_entry(
350 - host,
351 - rc->id,
352 - rc->next_event_id++,
353 - rc->config_hash_id,
354 - now,
355 - rc->name,
356 - rc->rrdset->id,
357 - rc->rrdset->context,
358 - rc->rrdset->name,
359 - rc->classification,
360 - rc->component,
361 - rc->type,
362 - rc->exec,
363 - rc->recipient,
364 - now - rc->last_status_change,
365 - rc->old_value,
366 - rc->value,
367 - rc->status,
368 - RRDCALC_STATUS_REMOVED,
369 - rc->source,
370 - rc->units,
371 - rc->summary,
372 - rc->info,
373 - 0,
374 - 0);
375 -
376 - health_alarm_log_add_entry(host, ae);
377 - }
378 -
379 - netdata_log_debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rrdcalc_chart_name(rc), rrdcalc_name(rc), rrdset_id(st), rrdhost_hostname(host));
380 -
381 - // unlink it
382 -
383 - if(!having_ll_wrlock)
384 - rw_spinlock_write_lock(&st->alerts.spinlock);
385 -
386 - DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(st->alerts.base, rc, prev, next);
387 -
388 - if(!having_ll_wrlock)
389 - rw_spinlock_write_unlock(&st->alerts.spinlock);
390 -
391 - rc->rrdset = NULL;
392 -
393 - rrdvar_release_and_del(st->rrdvars, rc->rrdvar_local);
394 - rc->rrdvar_local = NULL;
395 -
396 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rc->rrdvar_family);
397 - rc->rrdvar_family = NULL;
398 -
399 - rrdvar_release_and_del(host->rrdvars, rc->rrdvar_host_chart_id);
400 - rc->rrdvar_host_chart_id = NULL;
401 -
402 - rrdvar_release_and_del(host->rrdvars, rc->rrdvar_host_chart_name);
403 - rc->rrdvar_host_chart_name = NULL;
404 -
405 - // RRDCALC will remain in RRDHOST
406 - // so that if the matching chart is found in the future
407 - // it will be applied automatically
408 -}
409 -
410 -static inline bool rrdcalc_check_if_it_matches_rrdset(RRDCALC *rc, RRDSET *st) {
411 - if ( (rc->chart != st->id)
412 - && (rc->chart != st->name))
413 - return false;
414 -
415 - if (rc->module_pattern && !simple_pattern_matches_string(rc->module_pattern, st->module_name))
416 - return false;
417 -
418 - if (rc->plugin_pattern && !simple_pattern_matches_string(rc->plugin_pattern, st->module_name))
419 - return false;
420 -
421 - if (st->rrdhost->rrdlabels && rc->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(
422 - st->rrdhost->rrdlabels, rc->host_labels_pattern, '=', NULL))
423 - return false;
424 -
425 - if (st->rrdlabels && rc->chart_labels_pattern && !rrdlabels_match_simple_pattern_parsed(
426 - st->rrdlabels, rc->chart_labels_pattern, '=', NULL))
427 - return false;
428 -
429 - return true;
430 -}
431 -
432 -void rrdcalc_link_matching_alerts_to_rrdset(RRDSET *st) {
433 - RRDHOST *host = st->rrdhost;
434 - // netdata_log_debug(D_HEALTH, "find matching alarms for chart '%s'", st->id);
435 -
436 - RRDCALC *rc;
437 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
438 - if(rc->rrdset)
439 - continue;
440 -
441 - if(unlikely(rrdcalc_check_if_it_matches_rrdset(rc, st)))
442 - rrdcalc_link_to_rrdset(st, rc);
443 - }
444 - foreach_rrdcalc_in_rrdhost_done(rc);
445 -}
446 -
447 -static inline int rrdcalc_check_and_link_rrdset_callback(RRDSET *st, void *rrdcalc) {
448 - RRDCALC *rc = rrdcalc;
449 -
450 - if(unlikely(rrdcalc_check_if_it_matches_rrdset(rc, st))) {
451 - rrdcalc_link_to_rrdset(st, rc);
452 - return -1;
453 - }
454 -
455 - return 0;
456 -}
457 -
458 -// ----------------------------------------------------------------------------
459 -// RRDCALC rrdhost index management - constructor
460 -
461 -struct rrdcalc_constructor {
462 - RRDHOST *rrdhost; // the host we operate upon
463 - RRDCALC *from_config; // points to the original RRDCALC, as loaded from the config
464 - RRDCALCTEMPLATE *from_rrdcalctemplate; // the template this alert is generated from
465 - RRDSET *rrdset; // when this comes from rrdcalctemplate, we have a matching rrdset
466 - const char *overwrite_alert_name; // when we have a dimension foreach, the alert is renamed
467 - const char *overwrite_dimensions; // when we have a dimension foreach, the dimensions filter is renamed
468 -
469 - enum {
470 - RRDCALC_REACT_NONE,
471 - RRDCALC_REACT_NEW,
472 - } react_action;
473 -
474 - bool existing_from_template;
475 -};
476 -
477 -static void rrdcalc_rrdhost_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) {
478 - RRDCALC *rc = rrdcalc;
479 - struct rrdcalc_constructor *ctr = constructor_data;
480 - RRDHOST *host = ctr->rrdhost;
481 -
482 - rc->key = string_strdupz(dictionary_acquired_item_name(item));
483 -
484 - if(ctr->from_rrdcalctemplate) {
485 - rc->run_flags |= RRDCALC_FLAG_FROM_TEMPLATE;
486 -
487 - RRDCALCTEMPLATE *rt = ctr->from_rrdcalctemplate;
488 - RRDSET *st = ctr->rrdset;
489 -
490 - rc->next_event_id = 1;
491 - rc->name = (ctr->overwrite_alert_name) ? string_strdupz(ctr->overwrite_alert_name) : string_dup(rt->name);
492 - rc->chart = string_dup(st->id);
493 - uuid_copy(rc->config_hash_id, rt->config_hash_id);
494 -
495 - rc->dimensions = (ctr->overwrite_dimensions) ? string_strdupz(ctr->overwrite_dimensions) : string_dup(rt->dimensions);
496 - rc->foreach_dimension = NULL;
497 - rc->foreach_dimension_pattern = NULL;
498 -
499 - rc->green = rt->green;
500 - rc->red = rt->red;
501 - rc->value = NAN;
502 - rc->old_value = NAN;
503 -
504 - rc->delay_up_duration = rt->delay_up_duration;
505 - rc->delay_down_duration = rt->delay_down_duration;
506 - rc->delay_max_duration = rt->delay_max_duration;
507 - rc->delay_multiplier = rt->delay_multiplier;
508 -
509 - rc->last_repeat = 0;
510 - rc->times_repeat = 0;
511 - rc->warn_repeat_every = rt->warn_repeat_every;
512 - rc->crit_repeat_every = rt->crit_repeat_every;
513 -
514 - rc->group = rt->group;
515 - rc->after = rt->after;
516 - rc->before = rt->before;
517 - rc->update_every = rt->update_every;
518 - rc->options = rt->options;
519 -
520 - rc->exec = string_dup(rt->exec);
521 - rc->recipient = string_dup(rt->recipient);
522 - rc->source = string_dup(rt->source);
523 - rc->units = string_dup(rt->units);
524 - rc->info = string_dup(rt->info);
525 - rc->original_info = string_dup(rt->info);
526 -
527 - if (!rt->summary)
528 - rt->summary = string_dup(rc->name);
529 - rc->summary = string_dup(rt->summary);
530 - rc->original_summary = string_dup(rt->summary);
531 -
532 - rc->classification = string_dup(rt->classification);
533 - rc->component = string_dup(rt->component);
534 - rc->type = string_dup(rt->type);
535 -
536 - if(rt->calculation) {
537 - rc->calculation = expression_parse(rt->calculation->source, NULL, NULL);
538 - if(!rc->calculation)
539 - netdata_log_error("Health alarm '%s.%s': failed to parse calculation expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->calculation->source);
540 - }
541 - if(rt->warning) {
542 - rc->warning = expression_parse(rt->warning->source, NULL, NULL);
543 - if(!rc->warning)
544 - netdata_log_error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->warning->source);
545 - }
546 - if(rt->critical) {
547 - rc->critical = expression_parse(rt->critical->source, NULL, NULL);
548 - if(!rc->critical)
549 - netdata_log_error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", rrdset_id(st), rrdcalctemplate_name(rt), rt->critical->source);
550 - }
551 - }
552 - else if(ctr->from_config) {
553 - // dictionary has already copied all the members values and pointers
554 - // no need for additional work in this case
555 - ;
556 - }
557 -
558 - rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id, &rc->config_hash_id);
559 -
560 - if(rc->calculation) {
561 - rc->calculation->status = &rc->status;
562 - rc->calculation->myself = &rc->value;
563 - rc->calculation->after = &rc->db_after;
564 - rc->calculation->before = &rc->db_before;
565 - rc->calculation->rrdcalc = rc;
566 - }
567 -
568 - if(rc->warning) {
569 - rc->warning->status = &rc->status;
570 - rc->warning->myself = &rc->value;
571 - rc->warning->after = &rc->db_after;
572 - rc->warning->before = &rc->db_before;
573 - rc->warning->rrdcalc = rc;
574 - }
575 -
576 - if(rc->critical) {
577 - rc->critical->status = &rc->status;
578 - rc->critical->myself = &rc->value;
579 - rc->critical->after = &rc->db_after;
580 - rc->critical->before = &rc->db_before;
581 - rc->critical->rrdcalc = rc;
582 - }
583 -
584 - netdata_log_debug(D_HEALTH, "Health added alarm '%s.%s': exec '%s', recipient '%s', green " NETDATA_DOUBLE_FORMAT_AUTO
585 - ", red " NETDATA_DOUBLE_FORMAT_AUTO
586 - ", lookup: group %d, after %d, before %d, options %u, dimensions '%s', for each dimension '%s', update every %d, calculation '%s', warning '%s', critical '%s', source '%s', delay up %d, delay down %d, delay max %d, delay_multiplier %f, warn_repeat_every %u, crit_repeat_every %u",
587 - rrdcalc_chart_name(rc),
588 - rrdcalc_name(rc),
589 - (rc->exec)?rrdcalc_exec(rc):"DEFAULT",
590 - (rc->recipient)?rrdcalc_recipient(rc):"DEFAULT",
591 - rc->green,
592 - rc->red,
593 - (int)rc->group,
594 - rc->after,
595 - rc->before,
596 - rc->options,
597 - (rc->dimensions)?rrdcalc_dimensions(rc):"NONE",
598 - (rc->foreach_dimension)?rrdcalc_foreachdim(rc):"NONE",
599 - rc->update_every,
600 - (rc->calculation)?rc->calculation->parsed_as:"NONE",
601 - (rc->warning)?rc->warning->parsed_as:"NONE",
602 - (rc->critical)?rc->critical->parsed_as:"NONE",
603 - rrdcalc_source(rc),
604 - rc->delay_up_duration,
605 - rc->delay_down_duration,
606 - rc->delay_max_duration,
607 - rc->delay_multiplier,
608 - rc->warn_repeat_every,
609 - rc->crit_repeat_every
610 - );
611 -
612 - ctr->react_action = RRDCALC_REACT_NEW;
613 -}
614 -
615 -static bool rrdcalc_rrdhost_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdcalc_new __maybe_unused, void *constructor_data ) {
616 - RRDCALC *rc = rrdcalc;
617 - struct rrdcalc_constructor *ctr = constructor_data;
618 -
619 - if(rc->run_flags & RRDCALC_FLAG_FROM_TEMPLATE)
620 - ctr->existing_from_template = true;
621 - else
622 - ctr->existing_from_template = false;
623 -
624 - ctr->react_action = RRDCALC_REACT_NONE;
625 -
626 - return false;
627 -}
628 -
629 -static void rrdcalc_rrdhost_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) {
630 - RRDCALC *rc = rrdcalc;
631 - struct rrdcalc_constructor *ctr = constructor_data;
632 - RRDHOST *host = ctr->rrdhost;
633 -
634 - if(ctr->react_action == RRDCALC_REACT_NEW) {
635 - if(ctr->rrdset)
636 - rrdcalc_link_to_rrdset(ctr->rrdset, rc);
637 -
638 - else if (ctr->from_rrdcalctemplate)
639 - rrdcontext_foreach_instance_with_rrdset_in_context(host, string2str(ctr->from_rrdcalctemplate->context), rrdcalc_check_and_link_rrdset_callback, rc);
640 - }
641 -}
642 -
643 -// ----------------------------------------------------------------------------
644 -// RRDCALC rrdhost index management - destructor
645 -
646 -static void rrdcalc_free_internals(RRDCALC *rc) {
647 - if(unlikely(!rc)) return;
648 -
649 - expression_free(rc->calculation);
650 - expression_free(rc->warning);
651 - expression_free(rc->critical);
652 -
653 - string_freez(rc->key);
654 - string_freez(rc->name);
655 - string_freez(rc->chart);
656 - string_freez(rc->dimensions);
657 - string_freez(rc->foreach_dimension);
658 - string_freez(rc->exec);
659 - string_freez(rc->recipient);
660 - string_freez(rc->source);
661 - string_freez(rc->units);
662 - string_freez(rc->info);
663 - string_freez(rc->original_info);
664 - string_freez(rc->classification);
665 - string_freez(rc->component);
666 - string_freez(rc->type);
667 - string_freez(rc->host_labels);
668 - string_freez(rc->module_match);
669 - string_freez(rc->plugin_match);
670 - string_freez(rc->chart_labels);
671 -
672 - simple_pattern_free(rc->foreach_dimension_pattern);
673 - simple_pattern_free(rc->host_labels_pattern);
674 - simple_pattern_free(rc->module_pattern);
675 - simple_pattern_free(rc->plugin_pattern);
676 - simple_pattern_free(rc->chart_labels_pattern);
677 -}
678 -
679 -static void rrdcalc_rrdhost_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdhost __maybe_unused) {
680 - RRDCALC *rc = rrdcalc;
681 - //RRDHOST *host = rrdhost;
682 -
683 - if(unlikely(rc->rrdset))
684 - rrdcalc_unlink_from_rrdset(rc, false);
685 -
686 - // any destruction actions that require other locks
687 - // have to be placed in rrdcalc_del(), because the object is actually locked for deletion
688 -
689 - rrdcalc_free_internals(rc);
690 -}
691 -
692 -// ----------------------------------------------------------------------------
693 -// RRDCALC rrdhost index management - index API
694 -
695 -void rrdcalc_rrdhost_index_init(RRDHOST *host) {
696 - if(!host->rrdcalc_root_index) {
697 - host->rrdcalc_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
698 - &dictionary_stats_category_rrdhealth, sizeof(RRDCALC));
699 -
700 - dictionary_register_insert_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_insert_callback, NULL);
701 - dictionary_register_conflict_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_conflict_callback, NULL);
702 - dictionary_register_react_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_react_callback, NULL);
703 - dictionary_register_delete_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_delete_callback, host);
704 - }
705 -}
706 -
707 -void rrdcalc_rrdhost_index_destroy(RRDHOST *host) {
708 - dictionary_destroy(host->rrdcalc_root_index);
709 - host->rrdcalc_root_index = NULL;
710 -}
711 -
712 -void rrdcalc_add_from_rrdcalctemplate(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st, const char *overwrite_alert_name, const char *overwrite_dimensions) {
713 - char key[RRDCALC_MAX_KEY_SIZE + 1];
714 - size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st),
715 - overwrite_alert_name?overwrite_alert_name:string2str(rt->name));
716 -
717 - struct rrdcalc_constructor tmp = {
718 - .rrdhost = host,
719 - .from_config = NULL,
720 - .from_rrdcalctemplate = rt,
721 - .rrdset = st,
722 - .overwrite_alert_name = overwrite_alert_name,
723 - .overwrite_dimensions = overwrite_dimensions,
724 - .react_action = RRDCALC_REACT_NONE,
725 - .existing_from_template = false,
726 - };
727 -
728 - dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)key_len, NULL, sizeof(RRDCALC), &tmp);
729 - if(tmp.react_action != RRDCALC_REACT_NEW && tmp.existing_from_template == false)
730 - netdata_log_error("RRDCALC: from template '%s' on chart '%s' with key '%s', failed to be added to host '%s'. It is manually configured.",
731 - string2str(rt->name), rrdset_id(st), key, rrdhost_hostname(host));
732 -}
733 -
734 -int rrdcalc_add_from_config(RRDHOST *host, RRDCALC *rc) {
735 - if(!rc->chart) {
736 - netdata_log_error("Health configuration for alarm '%s' does not have a chart", rrdcalc_name(rc));
737 - return 0;
738 - }
739 -
740 - if(!rc->update_every) {
741 - netdata_log_error("Health configuration for alarm '%s.%s' has no frequency (parameter 'every'). Ignoring it.", rrdcalc_chart_name(rc), rrdcalc_name(rc));
742 - return 0;
743 - }
744 -
745 - if(!RRDCALC_HAS_DB_LOOKUP(rc) && !rc->calculation && !rc->warning && !rc->critical) {
746 - netdata_log_error("Health configuration for alarm '%s.%s' is useless (no db lookup, no calculation, no warning and no critical expressions)", rrdcalc_chart_name(rc), rrdcalc_name(rc));
747 - return 0;
748 - }
749 -
750 - char key[RRDCALC_MAX_KEY_SIZE + 1];
751 - size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, string2str(rc->chart), string2str(rc->name));
752 -
753 - struct rrdcalc_constructor tmp = {
754 - .rrdhost = host,
755 - .from_config = rc,
756 - .from_rrdcalctemplate = NULL,
757 - .rrdset = NULL,
758 - .react_action = RRDCALC_REACT_NONE,
759 - };
760 -
761 - int ret = 1;
762 - RRDCALC *t = dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)key_len, rc, sizeof(RRDCALC), &tmp);
763 - if(tmp.react_action == RRDCALC_REACT_NEW) {
764 - // we copied rc into the dictionary, so we have to free the container here
765 - freez(rc);
766 - rc = t;
767 -
768 - // since we loaded this config from configuration, we need to check if we can link it to alarms
769 - RRDSET *st;
770 - rrdset_foreach_read(st, host) {
771 - if (unlikely(rrdcalc_check_and_link_rrdset_callback(st, rc) == -1))
772 - break;
773 - }
774 - rrdset_foreach_done(st);
775 - }
776 - else {
777 - netdata_log_error(
778 - "RRDCALC: from config '%s' on chart '%s' failed to be added to host '%s'. It already exists.",
779 - string2str(rc->name),
780 - string2str(rc->chart),
781 - rrdhost_hostname(host));
782 -
783 - ret = 0;
784 -
785 - // free all of it, internals and the container
786 - rrdcalc_free_unused_rrdcalc_loaded_from_config(rc);
787 - }
788 -
789 - return ret;
790 -}
791 -
792 -static void rrdcalc_unlink_and_delete(RRDHOST *host, RRDCALC *rc, bool having_ll_wrlock) {
793 - if(rc->rrdset)
794 - rrdcalc_unlink_from_rrdset(rc, having_ll_wrlock);
795 -
796 - dictionary_del_advanced(host->rrdcalc_root_index, string2str(rc->key), (ssize_t)string_strlen(rc->key));
797 -}
798 -
799 -
800 -// ----------------------------------------------------------------------------
801 -// RRDCALC cleanup API functions
802 -
803 -void rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(RRDHOST *host) {
804 - RRDCALC *rc;
805 - foreach_rrdcalc_in_rrdhost_reentrant(host, rc) {
806 - if (!rc->host_labels)
807 - continue;
808 -
809 - if(!rrdlabels_match_simple_pattern_parsed(host->rrdlabels, rc->host_labels_pattern, '=', NULL)) {
810 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
811 - "Health configuration for alarm '%s' cannot be applied, "
812 - "because the host %s does not have the label(s) '%s'",
813 - rrdcalc_name(rc), rrdhost_hostname(host), rrdcalc_host_labels(rc));
814 -
815 - rrdcalc_unlink_and_delete(host, rc, false);
816 - }
817 - }
818 - foreach_rrdcalc_in_rrdhost_done(rc);
819 -}
820 -
821 -void rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts() {
822 - RRDHOST *host;
823 - dfe_start_reentrant(rrdhost_root_index, host) {
824 - if (unlikely(!host->health.health_enabled))
825 - continue;
826 -
827 - if (host->rrdlabels)
828 - rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host);
829 - }
830 - dfe_done(host);
831 -}
832 -
833 -void rrdcalc_unlink_all_rrdset_alerts(RRDSET *st) {
834 - RRDCALC *rc, *last = NULL;
835 - rw_spinlock_write_lock(&st->alerts.spinlock);
836 - while((rc = st->alerts.base)) {
837 - if(last == rc) {
838 - netdata_log_error("RRDCALC: malformed list of alerts linked to chart - cannot cleanup - giving up.");
839 - break;
840 - }
841 - last = rc;
842 -
843 - if(rc->run_flags & RRDCALC_FLAG_FROM_TEMPLATE) {
844 - // if the alert comes from a template we can just delete it
845 - rrdcalc_unlink_and_delete(st->rrdhost, rc, true);
846 - }
847 - else {
848 - // this is a configuration for a specific chart
849 - // it should stay in the list
850 - rrdcalc_unlink_from_rrdset(rc, true);
851 - }
852 -
853 - }
854 - rw_spinlock_write_unlock(&st->alerts.spinlock);
855 -}
856 -
857 -void rrdcalc_delete_all(RRDHOST *host) {
858 - dictionary_flush(host->rrdcalc_root_index);
859 -}
860 -
861 -void rrdcalc_free_unused_rrdcalc_loaded_from_config(RRDCALC *rc) {
862 - if(rc->rrdset)
863 - rrdcalc_unlink_from_rrdset(rc, false);
864 -
865 - rrdcalc_free_internals(rc);
866 - freez(rc);
867 -}
database/rrdcalc.h deleted
-271
@@ -1,271 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrd.h"
4 -
5 -#ifndef NETDATA_RRDCALC_H
6 -#define NETDATA_RRDCALC_H 1
7 -
8 -// calculated variables (defined in health configuration)
9 -// These aggregate time-series data at fixed intervals
10 -// (defined in their update_every member below)
11 -// They increase the overhead of netdata.
12 -//
13 -// These calculations are stored under RRDHOST.
14 -// Then are also linked to RRDSET (of course only when a
15 -// matching chart is found).
16 -
17 -typedef enum {
18 - RRDCALC_FLAG_DB_ERROR = (1 << 0),
19 - RRDCALC_FLAG_DB_NAN = (1 << 1),
20 - // RRDCALC_FLAG_DB_STALE = (1 << 2),
21 - RRDCALC_FLAG_CALC_ERROR = (1 << 3),
22 - RRDCALC_FLAG_WARN_ERROR = (1 << 4),
23 - RRDCALC_FLAG_CRIT_ERROR = (1 << 5),
24 - RRDCALC_FLAG_RUNNABLE = (1 << 6),
25 - RRDCALC_FLAG_DISABLED = (1 << 7),
26 - RRDCALC_FLAG_SILENCED = (1 << 8),
27 - RRDCALC_FLAG_RUN_ONCE = (1 << 9),
28 - RRDCALC_FLAG_FROM_TEMPLATE = (1 << 10), // the rrdcalc has been created from a template
29 -} RRDCALC_FLAGS;
30 -
31 -void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags);
32 -
33 -typedef enum {
34 - // This list uses several other options from RRDR_OPTIONS for db lookups.
35 - // To add an item here, you need to reserve a bit in RRDR_OPTIONS.
36 - RRDCALC_OPTION_NO_CLEAR_NOTIFICATION = RRDR_OPTION_HEALTH_RSRVD1,
37 -} RRDCALC_OPTIONS;
38 -
39 -#define RRDCALC_ALL_OPTIONS_EXCLUDING_THE_RRDR_ONES (RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)
40 -
41 -struct rrdcalc {
42 - STRING *key; // the unique key in the host's rrdcalc_root_index
43 -
44 - uint32_t id; // the unique id of this alarm
45 - uint32_t next_event_id; // the next event id that will be used for this alarm
46 -
47 - uuid_t config_hash_id; // a predictable hash_id based on specific alert configuration
48 -
49 - STRING *name; // the name of this alarm
50 - STRING *chart; // the chart id this should be linked to
51 -
52 - STRING *exec; // the command to execute when this alarm switches state
53 - STRING *recipient; // the recipient of the alarm (the first parameter to exec)
54 -
55 - STRING *classification; // the class that this alarm belongs
56 - STRING *component; // the component that this alarm refers to
57 - STRING *type; // type of the alarm
58 -
59 - STRING *plugin_match; // the plugin name that should be linked to
60 - SIMPLE_PATTERN *plugin_pattern;
61 -
62 - STRING *module_match; // the module name that should be linked to
63 - SIMPLE_PATTERN *module_pattern;
64 -
65 - STRING *source; // the source of this alarm
66 - STRING *units; // the units of the alarm
67 - STRING *summary; // a short alert summary
68 - STRING *original_summary; // the original summary field before any variable replacement
69 - STRING *original_info; // the original info field before any variable replacement
70 - STRING *info; // a description of the alarm
71 -
72 - int update_every; // update frequency for the alarm
73 -
74 - // the red and green threshold of this alarm (to be set to the chart)
75 - NETDATA_DOUBLE green;
76 - NETDATA_DOUBLE red;
77 -
78 - // ------------------------------------------------------------------------
79 - // database lookup settings
80 -
81 - STRING *dimensions; // the chart dimensions
82 - STRING *foreach_dimension; // the group of dimensions that the `foreach` will be applied.
83 - SIMPLE_PATTERN *foreach_dimension_pattern; // used if and only if there is a simple pattern for the chart.
84 - RRDR_TIME_GROUPING group; // grouping method: average, max, etc.
85 - int before; // ending point in time-series
86 - int after; // starting point in time-series
87 - RRDCALC_OPTIONS options; // configuration options
88 -
89 - // ------------------------------------------------------------------------
90 - // expressions related to the alarm
91 -
92 - EVAL_EXPRESSION *calculation; // expression to calculate the value of the alarm
93 - EVAL_EXPRESSION *warning; // expression to check the warning condition
94 - EVAL_EXPRESSION *critical; // expression to check the critical condition
95 -
96 - // ------------------------------------------------------------------------
97 - // notification delay settings
98 -
99 - int delay_up_duration; // duration to delay notifications when alarm raises
100 - int delay_down_duration; // duration to delay notifications when alarm lowers
101 - int delay_max_duration; // the absolute max delay to apply to this alarm
102 - float delay_multiplier; // multiplier for all delays when alarms switch status
103 - // while now < delay_up_to
104 -
105 - // ------------------------------------------------------------------------
106 - // notification repeat settings
107 -
108 - uint32_t warn_repeat_every; // interval between repeating warning notifications
109 - uint32_t crit_repeat_every; // interval between repeating critical notifications
110 -
111 - // ------------------------------------------------------------------------
112 - // Labels settings
113 - STRING *host_labels; // the label read from an alarm file
114 - SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels
115 -
116 - STRING *chart_labels; // the chart label read from an alarm file
117 - SIMPLE_PATTERN *chart_labels_pattern; // the simple pattern of chart labels
118 -
119 - // ------------------------------------------------------------------------
120 - // runtime information
121 -
122 - RRDCALC_STATUS old_status; // the old status of the alarm
123 - RRDCALC_STATUS status; // the current status of the alarm
124 -
125 - NETDATA_DOUBLE value; // the current value of the alarm
126 - NETDATA_DOUBLE old_value; // the previous value of the alarm
127 - NETDATA_DOUBLE last_status_change_value; // the value at the last status change
128 -
129 - RRDCALC_FLAGS run_flags; // check RRDCALC_FLAG_*
130 -
131 - time_t last_updated; // the last update timestamp of the alarm
132 - time_t next_update; // the next update timestamp of the alarm
133 - time_t last_status_change; // the timestamp of the last time this alarm changed status
134 - time_t last_repeat; // the last time the alarm got repeated
135 - uint32_t times_repeat; // number of times the alarm got repeated
136 -
137 - time_t db_after; // the first timestamp evaluated by the db lookup
138 - time_t db_before; // the last timestamp evaluated by the db lookup
139 -
140 - time_t delay_up_to_timestamp; // the timestamp up to which we should delay notifications
141 - int delay_up_current; // the current up notification delay duration
142 - int delay_down_current; // the current down notification delay duration
143 - int delay_last; // the last delay we used
144 -
145 - // ------------------------------------------------------------------------
146 - // variables this alarm exposes to the rest of the alarms
147 -
148 - const RRDVAR_ACQUIRED *rrdvar_local;
149 - const RRDVAR_ACQUIRED *rrdvar_family;
150 - const RRDVAR_ACQUIRED *rrdvar_host_chart_id;
151 - const RRDVAR_ACQUIRED *rrdvar_host_chart_name;
152 -
153 - // ------------------------------------------------------------------------
154 - // the chart this alarm it is linked to
155 -
156 - size_t labels_version;
157 - struct rrdset *rrdset;
158 -
159 - struct rrdcalc *next;
160 - struct rrdcalc *prev;
161 -};
162 -
163 -#define rrdcalc_name(rc) string2str((rc)->name)
164 -#define rrdcalc_chart_name(rc) string2str((rc)->chart)
165 -#define rrdcalc_exec(rc) string2str((rc)->exec)
166 -#define rrdcalc_recipient(rc) string2str((rc)->recipient)
167 -#define rrdcalc_classification(rc) string2str((rc)->classification)
168 -#define rrdcalc_component(rc) string2str((rc)->component)
169 -#define rrdcalc_type(rc) string2str((rc)->type)
170 -#define rrdcalc_plugin_match(rc) string2str((rc)->plugin_match)
171 -#define rrdcalc_module_match(rc) string2str((rc)->module_match)
172 -#define rrdcalc_source(rc) string2str((rc)->source)
173 -#define rrdcalc_units(rc) string2str((rc)->units)
174 -#define rrdcalc_original_summary(rc) string2str((rc)->original_summary)
175 -#define rrdcalc_summary(rc) string2str((rc)->summary)
176 -#define rrdcalc_original_info(rc) string2str((rc)->original_info)
177 -#define rrdcalc_info(rc) string2str((rc)->info)
178 -#define rrdcalc_dimensions(rc) string2str((rc)->dimensions)
179 -#define rrdcalc_foreachdim(rc) string2str((rc)->foreach_dimension)
180 -#define rrdcalc_host_labels(rc) string2str((rc)->host_labels)
181 -#define rrdcalc_chart_labels(rc) string2str((rc)->chart_labels)
182 -
183 -#define foreach_rrdcalc_in_rrdhost_read(host, rc) \
184 - dfe_start_read((host)->rrdcalc_root_index, rc) \
185 -
186 -#define foreach_rrdcalc_in_rrdhost_reentrant(host, rc) \
187 - dfe_start_reentrant((host)->rrdcalc_root_index, rc)
188 -
189 -#define foreach_rrdcalc_in_rrdhost_done(rc) \
190 - dfe_done(rc)
191 -
192 -struct alert_config {
193 - STRING *alarm;
194 - STRING *template_key;
195 - STRING *os;
196 - STRING *host;
197 - STRING *on;
198 - STRING *plugin;
199 - STRING *module;
200 - STRING *charts;
201 - STRING *lookup;
202 - STRING *calc;
203 - STRING *warn;
204 - STRING *crit;
205 - STRING *every;
206 - STRING *green;
207 - STRING *red;
208 - STRING *exec;
209 - STRING *to;
210 - STRING *units;
211 - STRING *summary;
212 - STRING *info;
213 - STRING *classification;
214 - STRING *component;
215 - STRING *type;
216 - STRING *delay;
217 - STRING *options;
218 - STRING *repeat;
219 - STRING *host_labels;
220 - STRING *chart_labels;
221 - STRING *source;
222 -
223 - STRING *p_db_lookup_dimensions;
224 - STRING *p_db_lookup_method;
225 -
226 - uint32_t p_db_lookup_options;
227 - int32_t p_db_lookup_after;
228 - int32_t p_db_lookup_before;
229 - int32_t p_update_every;
230 -};
231 -
232 -#define RRDCALC_HAS_DB_LOOKUP(rc) ((rc)->after)
233 -
234 -void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc);
235 -
236 -void rrdcalc_link_matching_alerts_to_rrdset(RRDSET *st);
237 -
238 -const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name);
239 -void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca);
240 -RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca);
241 -
242 -const char *rrdcalc_status2string(RRDCALC_STATUS status);
243 -
244 -void rrdcalc_free_unused_rrdcalc_loaded_from_config(RRDCALC *rc);
245 -
246 -uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id);
247 -void rrdcalc_add_from_rrdcalctemplate(RRDHOST *host, RRDCALCTEMPLATE *rt, RRDSET *st, const char *overwrite_alert_name, const char *overwrite_dimensions);
248 -int rrdcalc_add_from_config(RRDHOST *host, RRDCALC *rc);
249 -
250 -void rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts();
251 -void rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(RRDHOST *host);
252 -
253 -static inline int rrdcalc_isrepeating(RRDCALC *rc) {
254 - if (unlikely(rc->warn_repeat_every > 0 || rc->crit_repeat_every > 0)) {
255 - return 1;
256 - }
257 - return 0;
258 -}
259 -
260 -void rrdcalc_unlink_all_rrdset_alerts(RRDSET *st);
261 -void rrdcalc_delete_all(RRDHOST *host);
262 -
263 -void rrdcalc_rrdhost_index_init(RRDHOST *host);
264 -void rrdcalc_rrdhost_index_destroy(RRDHOST *host);
265 -
266 -#define RRDCALC_VAR_MAX 100
267 -#define RRDCALC_VAR_FAMILY "${family}"
268 -#define RRDCALC_VAR_LABEL "${label:"
269 -#define RRDCALC_VAR_LABEL_LEN (sizeof(RRDCALC_VAR_LABEL)-1)
270 -
271 -#endif //NETDATA_RRDCALC_H
database/rrdcalctemplate.c deleted
-242
@@ -1,242 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrd.h"
4 -
5 -// ----------------------------------------------------------------------------
6 -// RRDCALCTEMPLATE management
7 -/**
8 - * RRDCALC TEMPLATE LINK MATCHING
9 - *
10 - * @param rt is the template used to create the chart.
11 - * @param st is the chart where the alarm will be attached.
12 - */
13 -
14 -static char *rrdcalc_alert_name_with_dimension(const char *name, size_t namelen, const char *dim, size_t dimlen) {
15 - char *newname,*move;
16 -
17 - newname = mallocz(namelen + dimlen + 2);
18 - move = newname;
19 - memcpy(move, name, namelen);
20 - move += namelen;
21 -
22 - *move++ = '_';
23 - memcpy(move, dim, dimlen);
24 - move += dimlen;
25 - *move = '\0';
26 -
27 - return newname;
28 -}
29 -
30 -bool rrdcalctemplate_check_rrdset_conditions(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) {
31 - if(rt->context != st->context)
32 - return false;
33 -
34 - if(rt->foreach_dimension_pattern && !rrdset_number_of_dimensions(st))
35 - return false;
36 -
37 - if (rt->charts_pattern && !simple_pattern_matches_string(rt->charts_pattern, st->name) && !simple_pattern_matches_string(rt->charts_pattern, st->id))
38 - return false;
39 -
40 - if (rt->module_pattern && !simple_pattern_matches_string(rt->module_pattern, st->module_name))
41 - return false;
42 -
43 - if (rt->plugin_pattern && !simple_pattern_matches_string(rt->plugin_pattern, st->plugin_name))
44 - return false;
45 -
46 - if(host->rrdlabels && rt->host_labels_pattern && !rrdlabels_match_simple_pattern_parsed(host->rrdlabels,
47 - rt->host_labels_pattern,
48 - '=', NULL))
49 - return false;
50 -
51 - if(st->rrdlabels && rt->chart_labels_pattern && !rrdlabels_match_simple_pattern_parsed(st->rrdlabels,
52 - rt->chart_labels_pattern,
53 - '=', NULL))
54 - return false;
55 -
56 - return true;
57 -}
58 -
59 -void rrdcalctemplate_check_rrddim_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDDIM *rd, RRDHOST *host) {
60 - if (simple_pattern_matches_string(rt->foreach_dimension_pattern, rd->id) ||
61 - simple_pattern_matches_string(rt->foreach_dimension_pattern, rd->name)) {
62 - char *overwrite_alert_name = rrdcalc_alert_name_with_dimension(
63 - rrdcalctemplate_name(rt), string_strlen(rt->name), rrddim_name(rd), string_strlen(rd->name));
64 - rrdcalc_add_from_rrdcalctemplate(host, rt, st, overwrite_alert_name, rrddim_name(rd));
65 - freez(overwrite_alert_name);
66 - }
67 -}
68 -
69 -void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host) {
70 - if(!rrdcalctemplate_check_rrdset_conditions(rt, st, host))
71 - return;
72 -
73 - if(!rt->foreach_dimension_pattern) {
74 - rrdcalc_add_from_rrdcalctemplate(host, rt, st, NULL, NULL);
75 - return;
76 - }
77 -
78 - RRDDIM *rd;
79 - rrddim_foreach_read(rd, st) {
80 - rrdcalctemplate_check_rrddim_conditions_and_link(rt, st, rd, host);
81 - }
82 - rrddim_foreach_done(rd);
83 -}
84 -
85 -void rrdcalctemplate_link_matching_templates_to_rrdset(RRDSET *st) {
86 - RRDHOST *host = st->rrdhost;
87 -
88 - RRDCALCTEMPLATE *rt;
89 - foreach_rrdcalctemplate_read(host, rt) {
90 - rrdcalctemplate_check_conditions_and_link(rt, st, host);
91 - }
92 - foreach_rrdcalctemplate_done(rt);
93 -}
94 -
95 -static void rrdcalctemplate_free_internals(RRDCALCTEMPLATE *rt) {
96 - expression_free(rt->calculation);
97 - expression_free(rt->warning);
98 - expression_free(rt->critical);
99 -
100 - string_freez(rt->plugin_match);
101 - simple_pattern_free(rt->plugin_pattern);
102 -
103 - string_freez(rt->module_match);
104 - simple_pattern_free(rt->module_pattern);
105 -
106 - string_freez(rt->charts_match);
107 - simple_pattern_free(rt->charts_pattern);
108 -
109 - string_freez(rt->name);
110 - string_freez(rt->exec);
111 - string_freez(rt->recipient);
112 - string_freez(rt->classification);
113 - string_freez(rt->component);
114 - string_freez(rt->type);
115 - string_freez(rt->context);
116 - string_freez(rt->source);
117 - string_freez(rt->units);
118 - string_freez(rt->info);
119 - string_freez(rt->dimensions);
120 - string_freez(rt->foreach_dimension);
121 - string_freez(rt->host_labels);
122 - string_freez(rt->chart_labels);
123 - simple_pattern_free(rt->foreach_dimension_pattern);
124 - simple_pattern_free(rt->host_labels_pattern);
125 - simple_pattern_free(rt->chart_labels_pattern);
126 -}
127 -
128 -void rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(RRDCALCTEMPLATE *rt) {
129 - if(unlikely(!rt)) return;
130 -
131 - rrdcalctemplate_free_internals(rt);
132 - freez(rt);
133 -}
134 -static void rrdcalctemplate_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalctemplate, void *added_bool) {
135 - RRDCALCTEMPLATE *rt = rrdcalctemplate; (void)rt;
136 -
137 - bool *added = added_bool;
138 - *added = true;
139 -
140 - netdata_log_debug(D_HEALTH, "Health configuration adding template '%s'"
141 - ": context '%s'"
142 - ", exec '%s'"
143 - ", recipient '%s'"
144 - ", green " NETDATA_DOUBLE_FORMAT_AUTO
145 - ", red " NETDATA_DOUBLE_FORMAT_AUTO
146 - ", lookup: group %d"
147 - ", after %d"
148 - ", before %d"
149 - ", options %u"
150 - ", dimensions '%s'"
151 - ", for each dimension '%s'"
152 - ", update every %d"
153 - ", calculation '%s'"
154 - ", warning '%s'"
155 - ", critical '%s'"
156 - ", source '%s'"
157 - ", delay up %d"
158 - ", delay down %d"
159 - ", delay max %d"
160 - ", delay_multiplier %f"
161 - ", warn_repeat_every %u"
162 - ", crit_repeat_every %u",
163 - rrdcalctemplate_name(rt),
164 - (rt->context)?string2str(rt->context):"NONE",
165 - (rt->exec)?rrdcalctemplate_exec(rt):"DEFAULT",
166 - (rt->recipient)?rrdcalctemplate_recipient(rt):"DEFAULT",
167 - rt->green,
168 - rt->red,
169 - (int)rt->group,
170 - rt->after,
171 - rt->before,
172 - rt->options,
173 - (rt->dimensions)?rrdcalctemplate_dimensions(rt):"NONE",
174 - (rt->foreach_dimension)?rrdcalctemplate_foreachdim(rt):"NONE",
175 - rt->update_every,
176 - (rt->calculation)?rt->calculation->parsed_as:"NONE",
177 - (rt->warning)?rt->warning->parsed_as:"NONE",
178 - (rt->critical)?rt->critical->parsed_as:"NONE",
179 - rrdcalctemplate_source(rt),
180 - rt->delay_up_duration,
181 - rt->delay_down_duration,
182 - rt->delay_max_duration,
183 - rt->delay_multiplier,
184 - rt->warn_repeat_every,
185 - rt->crit_repeat_every
186 - );
187 -}
188 -
189 -static void rrdcalctemplate_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalctemplate, void *rrdhost __maybe_unused) {
190 - RRDCALCTEMPLATE *rt = rrdcalctemplate;
191 - rrdcalctemplate_free_internals(rt);
192 -}
193 -
194 -void rrdcalctemplate_index_init(RRDHOST *host) {
195 - if(!host->rrdcalctemplate_root_index) {
196 - host->rrdcalctemplate_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
197 - &dictionary_stats_category_rrdhealth, sizeof(RRDCALCTEMPLATE));
198 -
199 - dictionary_register_insert_callback(host->rrdcalctemplate_root_index, rrdcalctemplate_insert_callback, NULL);
200 - dictionary_register_delete_callback(host->rrdcalctemplate_root_index, rrdcalctemplate_delete_callback, host);
201 - }
202 -}
203 -
204 -void rrdcalctemplate_index_destroy(RRDHOST *host) {
205 - dictionary_destroy(host->rrdcalctemplate_root_index);
206 - host->rrdcalctemplate_root_index = NULL;
207 -}
208 -
209 -inline void rrdcalctemplate_delete_all(RRDHOST *host) {
210 - dictionary_flush(host->rrdcalctemplate_root_index);
211 -}
212 -
213 -#define RRDCALCTEMPLATE_MAX_KEY_SIZE 1024
214 -void rrdcalctemplate_add_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt) {
215 - if(unlikely(!rt->context)) {
216 - netdata_log_error("Health configuration for template '%s' does not have a context", rrdcalctemplate_name(rt));
217 - return;
218 - }
219 -
220 - if(unlikely(!rt->update_every)) {
221 - netdata_log_error("Health configuration for template '%s' has no frequency (parameter 'every'). Ignoring it.", rrdcalctemplate_name(rt));
222 - return;
223 - }
224 -
225 - if(unlikely(!RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) && !rt->calculation && !rt->warning && !rt->critical)) {
226 - netdata_log_error("Health configuration for template '%s' is useless (no calculation, no warning and no critical evaluation)", rrdcalctemplate_name(rt));
227 - return;
228 - }
229 -
230 - char key[RRDCALCTEMPLATE_MAX_KEY_SIZE + 1];
231 - size_t key_len = snprintfz(key, RRDCALCTEMPLATE_MAX_KEY_SIZE, "%s", rrdcalctemplate_name(rt));
232 -
233 - bool added = false;
234 - dictionary_set_advanced(host->rrdcalctemplate_root_index, key, (ssize_t)key_len, rt, sizeof(*rt), &added);
235 -
236 - if(added)
237 - freez(rt);
238 - else {
239 - netdata_log_info("Health configuration template '%s' already exists for host '%s'.", rrdcalctemplate_name(rt), rrdhost_hostname(host));
240 - rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt);
241 - }
242 -}
database/rrdcalctemplate.h deleted
-130
@@ -1,130 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_RRDCALCTEMPLATE_H
4 -#define NETDATA_RRDCALCTEMPLATE_H 1
5 -
6 -#include "rrd.h"
7 -
8 -// RRDCALCTEMPLATE
9 -// these are to be applied to charts found dynamically
10 -// based on their context.
11 -struct rrdcalctemplate {
12 - uuid_t config_hash_id;
13 -
14 - STRING *name;
15 -
16 - STRING *exec;
17 - STRING *recipient;
18 -
19 - STRING *classification;
20 - STRING *component;
21 - STRING *type;
22 -
23 - STRING *context;
24 -
25 - STRING *plugin_match;
26 - SIMPLE_PATTERN *plugin_pattern;
27 -
28 - STRING *module_match;
29 - SIMPLE_PATTERN *module_pattern;
30 -
31 - STRING *charts_match;
32 - SIMPLE_PATTERN *charts_pattern;
33 -
34 - STRING *source; // the source of this alarm
35 - STRING *units; // the units of the alarm
36 - STRING *summary; // a short summary of the alarm
37 - STRING *info; // a description of the alarm
38 -
39 - int update_every; // update frequency for the alarm
40 -
41 - // the red and green threshold of this alarm (to be set to the chart)
42 - NETDATA_DOUBLE green;
43 - NETDATA_DOUBLE red;
44 -
45 - // ------------------------------------------------------------------------
46 - // database lookup settings
47 -
48 - STRING *dimensions; // the chart dimensions
49 - STRING *foreach_dimension; // the group of dimensions that the lookup will be applied.
50 - SIMPLE_PATTERN *foreach_dimension_pattern; // used if and only if there is a simple pattern for the chart.
51 - RRDR_TIME_GROUPING group; // grouping method: average, max, etc.
52 - int before; // ending point in time-series
53 - int after; // starting point in time-series
54 - RRDCALC_OPTIONS options; // configuration options
55 -
56 - // ------------------------------------------------------------------------
57 - // notification delay settings
58 -
59 - int delay_up_duration; // duration to delay notifications when alarm raises
60 - int delay_down_duration; // duration to delay notifications when alarm lowers
61 - int delay_max_duration; // the absolute max delay to apply to this alarm
62 - float delay_multiplier; // multiplier for all delays when alarms switch status
63 -
64 - // ------------------------------------------------------------------------
65 - // notification repeat settings
66 -
67 - uint32_t warn_repeat_every; // interval between repeating warning notifications
68 - uint32_t crit_repeat_every; // interval between repeating critical notifications
69 -
70 - // ------------------------------------------------------------------------
71 - // Labels settings
72 - STRING *host_labels; // the label read from an alarm file
73 - SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels
74 -
75 - STRING *chart_labels; // the chart label read from an alarm file
76 - SIMPLE_PATTERN *chart_labels_pattern; // the simple pattern of chart labels
77 -
78 - // ------------------------------------------------------------------------
79 - // expressions related to the alarm
80 -
81 - EVAL_EXPRESSION *calculation;
82 - EVAL_EXPRESSION *warning;
83 - EVAL_EXPRESSION *critical;
84 -
85 - struct rrdcalctemplate *next;
86 - struct rrdcalctemplate *prev;
87 -};
88 -
89 -#define foreach_rrdcalctemplate_read(host, rt) \
90 - dfe_start_read((host)->rrdcalctemplate_root_index, rt)
91 -
92 -#define foreach_rrdcalctemplate_done(rt) \
93 - dfe_done(rt)
94 -
95 -#define rrdcalctemplate_name(rt) string2str((rt)->name)
96 -#define rrdcalctemplate_exec(rt) string2str((rt)->exec)
97 -#define rrdcalctemplate_recipient(rt) string2str((rt)->recipient)
98 -#define rrdcalctemplate_classification(rt) string2str((rt)->classification)
99 -#define rrdcalctemplate_component(rt) string2str((rt)->component)
100 -#define rrdcalctemplate_type(rt) string2str((rt)->type)
101 -#define rrdcalctemplate_plugin_match(rt) string2str((rt)->plugin_match)
102 -#define rrdcalctemplate_module_match(rt) string2str((rt)->module_match)
103 -#define rrdcalctemplate_charts_match(rt) string2str((rt)->charts_match)
104 -#define rrdcalctemplate_units(rt) string2str((rt)->units)
105 -#define rrdcalctemplate_summary(rt) string2str((rt)->summary)
106 -#define rrdcalctemplate_info(rt) string2str((rt)->info)
107 -#define rrdcalctemplate_source(rt) string2str((rt)->source)
108 -#define rrdcalctemplate_dimensions(rt) string2str((rt)->dimensions)
109 -#define rrdcalctemplate_foreachdim(rt) string2str((rt)->foreach_dimension)
110 -#define rrdcalctemplate_host_labels(rt) string2str((rt)->host_labels)
111 -#define rrdcalctemplate_chart_labels(rt) string2str((rt)->chart_labels)
112 -
113 -#define RRDCALCTEMPLATE_HAS_DB_LOOKUP(rt) ((rt)->after)
114 -
115 -void rrdcalctemplate_link_matching_templates_to_rrdset(RRDSET *st);
116 -
117 -void rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(RRDCALCTEMPLATE *rt);
118 -void rrdcalctemplate_delete_all(RRDHOST *host);
119 -void rrdcalctemplate_add_from_config(RRDHOST *host, RRDCALCTEMPLATE *rt);
120 -
121 -void rrdcalctemplate_check_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host);
122 -
123 -bool rrdcalctemplate_check_rrdset_conditions(RRDCALCTEMPLATE *rt, RRDSET *st, RRDHOST *host);
124 -void rrdcalctemplate_check_rrddim_conditions_and_link(RRDCALCTEMPLATE *rt, RRDSET *st, RRDDIM *rd, RRDHOST *host);
125 -
126 -
127 -void rrdcalctemplate_index_init(RRDHOST *host);
128 -void rrdcalctemplate_index_destroy(RRDHOST *host);
129 -
130 -#endif //NETDATA_RRDCALCTEMPLATE_H
database/rrddim.c
-4
@@ -203,8 +203,6 @@ static void rrddim_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
203 metaqueue_delete_dimension_uuid(&rd->metric_uuid);
204 }
205
206 - rrddimvar_delete_all(rd);
207 -
206 for(size_t tier = 0; tier < storage_tiers ;tier++) {
207 if(!rd->tiers[tier].smh) continue;
208
@@ -356,8 +354,6 @@ inline int rrddim_reset_name(RRDSET *st __maybe_unused, RRDDIM *rd, const char *
354 rd->name = rrd_string_strdupz(name);
355 string_freez(old);
356
359 - rrddimvar_rename_all(rd);
360 -
357 rrddim_metadata_updated(rd);
358
359 return 1;
database/rrddimvar.c deleted
-273
@@ -1,273 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrd.h"
4 -
5 -typedef struct rrddimvar {
6 - struct rrddim *rrddim;
7 -
8 - STRING *prefix;
9 - STRING *suffix;
10 - void *value;
11 -
12 - const RRDVAR_ACQUIRED *rrdvar_local_dim_id;
13 - const RRDVAR_ACQUIRED *rrdvar_local_dim_name;
14 -
15 - const RRDVAR_ACQUIRED *rrdvar_family_id;
16 - const RRDVAR_ACQUIRED *rrdvar_family_name;
17 - const RRDVAR_ACQUIRED *rrdvar_family_context_dim_id;
18 - const RRDVAR_ACQUIRED *rrdvar_family_context_dim_name;
19 -
20 - const RRDVAR_ACQUIRED *rrdvar_host_chart_id_dim_id;
21 - const RRDVAR_ACQUIRED *rrdvar_host_chart_id_dim_name;
22 - const RRDVAR_ACQUIRED *rrdvar_host_chart_name_dim_id;
23 - const RRDVAR_ACQUIRED *rrdvar_host_chart_name_dim_name;
24 -
25 - RRDVAR_FLAGS flags:24;
26 - RRDVAR_TYPE type:8;
27 -} RRDDIMVAR;
28 -
29 -// ----------------------------------------------------------------------------
30 -// RRDDIMVAR management
31 -// DIMENSION VARIABLES
32 -
33 -#define RRDDIMVAR_ID_MAX 1024
34 -
35 -static inline void rrddimvar_free_variables_unsafe(RRDDIMVAR *rs) {
36 - RRDDIM *rd = rs->rrddim;
37 - RRDSET *st = rd->rrdset;
38 - RRDHOST *host = st->rrdhost;
39 -
40 - // CHART VARIABLES FOR THIS DIMENSION
41 -
42 - if(st->rrdvars) {
43 - rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local_dim_id);
44 - rs->rrdvar_local_dim_id = NULL;
45 -
46 - rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local_dim_name);
47 - rs->rrdvar_local_dim_name = NULL;
48 - }
49 -
50 - // FAMILY VARIABLES FOR THIS DIMENSION
51 -
52 - if(st->rrdfamily) {
53 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_id);
54 - rs->rrdvar_family_id = NULL;
55 -
56 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_name);
57 - rs->rrdvar_family_name = NULL;
58 -
59 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_context_dim_id);
60 - rs->rrdvar_family_context_dim_id = NULL;
61 -
62 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_context_dim_name);
63 - rs->rrdvar_family_context_dim_name = NULL;
64 - }
65 -
66 - // HOST VARIABLES FOR THIS DIMENSION
67 -
68 - if(host->rrdvars && host->health.health_enabled) {
69 - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id_dim_id);
70 - rs->rrdvar_host_chart_id_dim_id = NULL;
71 -
72 - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id_dim_name);
73 - rs->rrdvar_host_chart_id_dim_name = NULL;
74 -
75 - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name_dim_id);
76 - rs->rrdvar_host_chart_name_dim_id = NULL;
77 -
78 - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name_dim_name);
79 - rs->rrdvar_host_chart_name_dim_name = NULL;
80 - }
81 -}
82 -
83 -static inline void rrddimvar_update_variables_unsafe(RRDDIMVAR *rs) {
84 - rrddimvar_free_variables_unsafe(rs);
85 -
86 - RRDDIM *rd = rs->rrddim;
87 - RRDSET *st = rd->rrdset;
88 - RRDHOST *host = st->rrdhost;
89 -
90 - char buffer[RRDDIMVAR_ID_MAX + 1];
91 -
92 - // KEYS
93 -
94 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", string2str(rs->prefix), rrddim_id(rd), string2str(rs->suffix));
95 - STRING *key_dim_id = string_strdupz(buffer);
96 -
97 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s%s%s", string2str(rs->prefix), rrddim_name(rd), string2str(rs->suffix));
98 - STRING *key_dim_name = string_strdupz(buffer);
99 -
100 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_id(st), string2str(key_dim_id));
101 - STRING *key_chart_id_dim_id = string_strdupz(buffer);
102 -
103 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_id(st), string2str(key_dim_name));
104 - STRING *key_chart_id_dim_name = string_strdupz(buffer);
105 -
106 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_context(st), string2str(key_dim_id));
107 - STRING *key_context_dim_id = string_strdupz(buffer);
108 -
109 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_context(st), string2str(key_dim_name));
110 - STRING *key_context_dim_name = string_strdupz(buffer);
111 -
112 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_name(st), string2str(key_dim_id));
113 - STRING *key_chart_name_dim_id = string_strdupz(buffer);
114 -
115 - snprintfz(buffer, RRDDIMVAR_ID_MAX, "%s.%s", rrdset_name(st), string2str(key_dim_name));
116 - STRING *key_chart_name_dim_name = string_strdupz(buffer);
117 -
118 - // CHART VARIABLES FOR THIS DIMENSION
119 - // -----------------------------------
120 - //
121 - // dimensions are available as:
122 - // - $id
123 - // - $name
124 -
125 - if(st->rrdvars) {
126 - rs->rrdvar_local_dim_id = rrdvar_add_and_acquire("local", st->rrdvars, key_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value);
127 - rs->rrdvar_local_dim_name = rrdvar_add_and_acquire("local", st->rrdvars, key_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value);
128 - }
129 -
130 - // FAMILY VARIABLES FOR THIS DIMENSION
131 - // -----------------------------------
132 - //
133 - // dimensions are available as:
134 - // - $id (only the first, when multiple overlap)
135 - // - $name (only the first, when multiple overlap)
136 - // - $chart-context.id
137 - // - $chart-context.name
138 -
139 - if(st->rrdfamily) {
140 - rs->rrdvar_family_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value);
141 - rs->rrdvar_family_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value);
142 - rs->rrdvar_family_context_dim_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_context_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value);
143 - rs->rrdvar_family_context_dim_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_context_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value);
144 - }
145 -
146 - // HOST VARIABLES FOR THIS DIMENSION
147 - // -----------------------------------
148 - //
149 - // dimensions are available as:
150 - // - $chart-id.id
151 - // - $chart-id.name
152 - // - $chart-name.id
153 - // - $chart-name.name
154 -
155 - if(host->rrdvars && host->health.health_enabled) {
156 - rs->rrdvar_host_chart_id_dim_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value);
157 - rs->rrdvar_host_chart_id_dim_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value);
158 - rs->rrdvar_host_chart_name_dim_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name_dim_id, rs->type, RRDVAR_FLAG_NONE, rs->value);
159 - rs->rrdvar_host_chart_name_dim_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name_dim_name, rs->type, RRDVAR_FLAG_NONE, rs->value);
160 - }
161 -
162 - // free the keys
163 -
164 - string_freez(key_dim_id);
165 - string_freez(key_dim_name);
166 - string_freez(key_chart_id_dim_id);
167 - string_freez(key_chart_id_dim_name);
168 - string_freez(key_context_dim_id);
169 - string_freez(key_context_dim_name);
170 - string_freez(key_chart_name_dim_id);
171 - string_freez(key_chart_name_dim_name);
172 -}
173 -
174 -struct rrddimvar_constructor {
175 - RRDDIM *rrddim;
176 - const char *prefix;
177 - const char *suffix;
178 - void *value;
179 - RRDVAR_FLAGS flags :16;
180 - RRDVAR_TYPE type:8;
181 -};
182 -
183 -static void rrddimvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *constructor_data) {
184 - RRDDIMVAR *rs = rrddimvar;
185 - struct rrddimvar_constructor *ctr = constructor_data;
186 -
187 - if(!ctr->prefix) ctr->prefix = "";
188 - if(!ctr->suffix) ctr->suffix = "";
189 -
190 - rs->prefix = string_strdupz(ctr->prefix);
191 - rs->suffix = string_strdupz(ctr->suffix);
192 -
193 - rs->type = ctr->type;
194 - rs->value = ctr->value;
195 - rs->flags = ctr->flags;
196 - rs->rrddim = ctr->rrddim;
197 -
198 - rrddimvar_update_variables_unsafe(rs);
199 -}
200 -
201 -static bool rrddimvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *new_rrddimvar __maybe_unused, void *constructor_data __maybe_unused) {
202 - RRDDIMVAR *rs = rrddimvar;
203 - rrddimvar_update_variables_unsafe(rs);
204 -
205 - return true;
206 -}
207 -
208 -static void rrddimvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrddimvar, void *rrdset __maybe_unused) {
209 - RRDDIMVAR *rs = rrddimvar;
210 - rrddimvar_free_variables_unsafe(rs);
211 - string_freez(rs->prefix);
212 - string_freez(rs->suffix);
213 -}
214 -
215 -void rrddimvar_index_init(RRDSET *st) {
216 - if(!st->rrddimvar_root_index) {
217 - st->rrddimvar_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
218 - &dictionary_stats_category_rrdhealth, sizeof(RRDDIMVAR));
219 -
220 - dictionary_register_insert_callback(st->rrddimvar_root_index, rrddimvar_insert_callback, NULL);
221 - dictionary_register_conflict_callback(st->rrddimvar_root_index, rrddimvar_conflict_callback, NULL);
222 - dictionary_register_delete_callback(st->rrddimvar_root_index, rrddimvar_delete_callback, st);
223 - }
224 -}
225 -
226 -void rrddimvar_index_destroy(RRDSET *st) {
227 - dictionary_destroy(st->rrddimvar_root_index);
228 - st->rrddimvar_root_index = NULL;
229 -}
230 -
231 -void rrddimvar_add_and_leave_released(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_FLAGS flags) {
232 - if(!prefix) prefix = "";
233 - if(!suffix) suffix = "";
234 -
235 - char key[RRDDIMVAR_ID_MAX + 1];
236 - size_t key_len = snprintfz(key, RRDDIMVAR_ID_MAX, "%s_%s_%s", prefix, rrddim_id(rd), suffix);
237 -
238 - struct rrddimvar_constructor tmp = {
239 - .suffix = suffix,
240 - .prefix = prefix,
241 - .type = type,
242 - .flags = flags,
243 - .value = value,
244 - .rrddim = rd
245 - };
246 - dictionary_set_advanced(rd->rrdset->rrddimvar_root_index, key, (ssize_t)key_len, NULL, sizeof(RRDDIMVAR), &tmp);
247 -}
248 -
249 -void rrddimvar_rename_all(RRDDIM *rd) {
250 - RRDSET *st = rd->rrdset;
251 -
252 - netdata_log_debug(D_VARIABLES, "RRDDIMVAR rename for chart id '%s' name '%s', dimension id '%s', name '%s'", rrdset_id(st), rrdset_name(st), rrddim_id(rd), rrddim_name(rd));
253 -
254 - RRDDIMVAR *rs;
255 - dfe_start_write(st->rrddimvar_root_index, rs) {
256 - if(unlikely(rs->rrddim == rd))
257 - rrddimvar_update_variables_unsafe(rs);
258 - }
259 - dfe_done(rs);
260 -}
261 -
262 -void rrddimvar_delete_all(RRDDIM *rd) {
263 - RRDSET *st = rd->rrdset;
264 -
265 - netdata_log_debug(D_VARIABLES, "RRDDIMVAR delete for chart id '%s' name '%s', dimension id '%s', name '%s'", rrdset_id(st), rrdset_name(st), rrddim_id(rd), rrddim_name(rd));
266 -
267 - RRDDIMVAR *rs;
268 - dfe_start_write(st->rrddimvar_root_index, rs) {
269 - if(unlikely(rs->rrddim == rd))
270 - dictionary_del(st->rrddimvar_root_index, rs_dfe.name);
271 - }
272 - dfe_done(rs);
273 -}
database/rrddimvar.h deleted
-21
@@ -1,21 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_RRDDIMVAR_H
4 -#define NETDATA_RRDDIMVAR_H 1
5 -
6 -#include "rrd.h"
7 -
8 -// variables linked to individual dimensions
9 -// We link variables to point the values that are already
10 -// calculated / processed by the normal data collection process
11 -// This means, there will be no speed penalty for using
12 -// these variables
13 -
14 -void rrddimvar_rename_all(RRDDIM *rd);
15 -void rrddimvar_add_and_leave_released(RRDDIM *rd, RRDVAR_TYPE type, const char *prefix, const char *suffix, void *value, RRDVAR_FLAGS flags);
16 -void rrddimvar_delete_all(RRDDIM *rd);
17 -
18 -void rrddimvar_index_init(RRDSET *st);
19 -void rrddimvar_index_destroy(RRDSET *st);
20 -
21 -#endif //NETDATA_RRDDIMVAR_H
database/rrdfamily.c deleted
-69
@@ -1,69 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#define NETDATA_RRD_INTERNALS
4 -#include "rrd.h"
5 -
6 -typedef struct rrdfamily {
7 - STRING *family;
8 - DICTIONARY *rrdvars;
9 -} RRDFAMILY;
10 -
11 -// ----------------------------------------------------------------------------
12 -// RRDFAMILY index
13 -
14 -struct rrdfamily_constructor {
15 - const char *family;
16 -};
17 -
18 -static void rrdfamily_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdfamily, void *constructor_data) {
19 - RRDFAMILY *rf = rrdfamily;
20 - struct rrdfamily_constructor *ctr = constructor_data;
21 -
22 - rf->family = string_strdupz(ctr->family);
23 - rf->rrdvars = rrdvariables_create();
24 -}
25 -
26 -static void rrdfamily_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdfamily, void *rrdhost __maybe_unused) {
27 - RRDFAMILY *rf = rrdfamily;
28 - string_freez(rf->family);
29 - rrdvariables_destroy(rf->rrdvars);
30 - rf->family = NULL;
31 - rf->rrdvars = NULL;
32 -}
33 -
34 -void rrdfamily_index_init(RRDHOST *host) {
35 - if(!host->rrdfamily_root_index) {
36 - host->rrdfamily_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
37 - &dictionary_stats_category_rrdhealth, sizeof(RRDFAMILY));
38 -
39 - dictionary_register_insert_callback(host->rrdfamily_root_index, rrdfamily_insert_callback, NULL);
40 - dictionary_register_delete_callback(host->rrdfamily_root_index, rrdfamily_delete_callback, host);
41 - }
42 -}
43 -
44 -void rrdfamily_index_destroy(RRDHOST *host) {
45 - dictionary_destroy(host->rrdfamily_root_index);
46 - host->rrdfamily_root_index = NULL;
47 -}
48 -
49 -
50 -// ----------------------------------------------------------------------------
51 -// RRDFAMILY management
52 -
53 -const RRDFAMILY_ACQUIRED *rrdfamily_add_and_acquire(RRDHOST *host, const char *id) {
54 - struct rrdfamily_constructor tmp = {
55 - .family = id,
56 - };
57 - return (const RRDFAMILY_ACQUIRED *)dictionary_set_and_acquire_item_advanced(host->rrdfamily_root_index, id, -1, NULL, sizeof(RRDFAMILY), &tmp);
58 -}
59 -
60 -void rrdfamily_release(RRDHOST *host, const RRDFAMILY_ACQUIRED *rfa) {
61 - if(unlikely(!rfa)) return;
62 - dictionary_acquired_item_release(host->rrdfamily_root_index, (const DICTIONARY_ITEM *)rfa);
63 -}
64 -
65 -DICTIONARY *rrdfamily_rrdvars_dict(const RRDFAMILY_ACQUIRED *rfa) {
66 - if(unlikely(!rfa)) return NULL;
67 - RRDFAMILY *rf = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rfa);
68 - return(rf->rrdvars);
69 -}
database/rrdfunctions-inflight.c
+13
@@ -402,6 +402,19 @@ int rrd_function_run(RRDHOST *host, BUFFER *result_wb, int timeout_s, HTTP_ACCES
402 char sanitized_source[(source ? strlen(source) : 0) + 1];
403 rrd_functions_sanitize(sanitized_source, source ? source : "", sizeof(sanitized_source));
404
405 + // ------------------------------------------------------------------------
406 + // check for the host
407 + if(!host) {
408 + code = HTTP_RESP_INTERNAL_SERVER_ERROR;
409 +
410 + rrd_call_function_error(result_wb, "no host given for running the function", code);
411 +
412 + if(result_cb)
413 + result_cb(result_wb, code, result_cb_data);
414 +
415 + return code;
416 + }
417 +
418 // ------------------------------------------------------------------------
419 // find the function
420
database/rrdhost.c
+74 -73
@@ -230,8 +230,8 @@ static inline void rrdhost_init_timezone(RRDHOST *host, const char *timezone, co
230
231 void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory_mode,
232 const char *registry_hostname, const char *os, const char *tags,
233 - const char *tzone, const char *abbrev_tzone, int32_t utc_offset, const char *program_name,
234 - const char *program_version)
233 + const char *tzone, const char *abbrev_tzone, int32_t utc_offset, const char *prog_name,
234 + const char *prog_version)
235 {
236
237 host->rrd_update_every = update_every;
@@ -241,8 +241,8 @@ void set_host_properties(RRDHOST *host, int update_every, RRD_MEMORY_MODE memory
241 rrdhost_init_timezone(host, tzone, abbrev_tzone, utc_offset);
242 rrdhost_init_tags(host, tags);
243
244 - host->program_name = string_strdupz((program_name && *program_name) ? program_name : "unknown");
245 - host->program_version = string_strdupz((program_version && *program_version) ? program_version : "unknown");
244 + host->program_name = string_strdupz((prog_name && *prog_name) ? prog_name : "unknown");
245 + host->program_version = string_strdupz((prog_version && *prog_version) ? prog_version : "unknown");
246 host->registry_hostname = string_strdupz((registry_hostname && *registry_hostname) ? registry_hostname : rrdhost_hostname(host));
247 }
248
@@ -288,8 +288,8 @@ static RRDHOST *rrdhost_create(
288 const char *abbrev_timezone,
289 int32_t utc_offset,
290 const char *tags,
291 - const char *program_name,
292 - const char *program_version,
291 + const char *prog_name,
292 + const char *prog_version,
293 int update_every,
294 long entries,
295 RRD_MEMORY_MODE memory_mode,
@@ -326,7 +326,9 @@ int is_legacy = 1;
326 strncpyz(host->machine_guid, guid, GUID_LEN + 1);
327
328 set_host_properties(host, (update_every > 0)?update_every:1, memory_mode, registry_hostname, os,
329 - tags, timezone, abbrev_timezone, utc_offset, program_name, program_version);
329 + tags, timezone, abbrev_timezone, utc_offset,
330 + prog_name,
331 + prog_version);
332
333 rrdhost_init_hostname(host, hostname, false);
334
@@ -407,8 +409,6 @@ int is_legacy = 1;
409 else
410 error_report("Host machine GUID %s is not valid", host->machine_guid);
411
410 - rrdfamily_index_init(host);
411 - rrdcalctemplate_index_init(host);
412 rrdcalc_rrdhost_index_init(host);
413
414 if (host->rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE) {
@@ -583,28 +583,28 @@ int is_legacy = 1;
583 }
584
585 static void rrdhost_update(RRDHOST *host
586 - , const char *hostname
587 - , const char *registry_hostname
588 - , const char *guid
589 - , const char *os
590 - , const char *timezone
591 - , const char *abbrev_timezone
592 - , int32_t utc_offset
593 - , const char *tags
594 - , const char *program_name
595 - , const char *program_version
596 - , int update_every
597 - , long history
598 - , RRD_MEMORY_MODE mode
599 - , unsigned int health_enabled
600 - , unsigned int rrdpush_enabled
601 - , char *rrdpush_destination
602 - , char *rrdpush_api_key
603 - , char *rrdpush_send_charts_matching
604 - , bool rrdpush_enable_replication
605 - , time_t rrdpush_seconds_to_replicate
606 - , time_t rrdpush_replication_step
607 - , struct rrdhost_system_info *system_info
586 + , const char *hostname
587 + , const char *registry_hostname
588 + , const char *guid
589 + , const char *os
590 + , const char *timezone
591 + , const char *abbrev_timezone
592 + , int32_t utc_offset
593 + , const char *tags
594 + , const char *prog_name
595 + , const char *prog_version
596 + , int update_every
597 + , long history
598 + , RRD_MEMORY_MODE mode
599 + , unsigned int health_enabled
600 + , unsigned int rrdpush_enabled
601 + , char *rrdpush_destination
602 + , char *rrdpush_api_key
603 + , char *rrdpush_send_charts_matching
604 + , bool rrdpush_enable_replication
605 + , time_t rrdpush_seconds_to_replicate
606 + , time_t rrdpush_replication_step
607 + , struct rrdhost_system_info *system_info
608 )
609 {
610 UNUSED(guid);
@@ -636,23 +636,25 @@ static void rrdhost_update(RRDHOST *host
636 rrdhost_index_add_hostname(host);
637 }
638
639 - if(strcmp(rrdhost_program_name(host), program_name) != 0) {
639 + if(strcmp(rrdhost_program_name(host), prog_name) != 0) {
640 nd_log(NDLS_DAEMON, NDLP_NOTICE,
641 "Host '%s' switched program name from '%s' to '%s'",
642 - rrdhost_hostname(host), rrdhost_program_name(host), program_name);
642 + rrdhost_hostname(host), rrdhost_program_name(host),
643 + prog_name);
644
645 STRING *t = host->program_name;
645 - host->program_name = string_strdupz(program_name);
646 + host->program_name = string_strdupz(prog_name);
647 string_freez(t);
648 }
649
649 - if(strcmp(rrdhost_program_version(host), program_version) != 0) {
650 + if(strcmp(rrdhost_program_version(host), prog_version) != 0) {
651 nd_log(NDLS_DAEMON, NDLP_NOTICE,
652 "Host '%s' switched program version from '%s' to '%s'",
652 - rrdhost_hostname(host), rrdhost_program_version(host), program_version);
653 + rrdhost_hostname(host), rrdhost_program_version(host),
654 + prog_version);
655
656 STRING *t = host->program_version;
655 - host->program_version = string_strdupz(program_version);
657 + host->program_version = string_strdupz(prog_version);
658 string_freez(t);
659 }
660
@@ -703,8 +705,6 @@ static void rrdhost_update(RRDHOST *host
705 rrdpush_api_key,
706 rrdpush_send_charts_matching);
707
706 - rrdfamily_index_init(host);
707 - rrdcalctemplate_index_init(host);
708 rrdcalc_rrdhost_index_init(host);
709
710 if(rrdpush_enable_replication)
@@ -727,29 +727,29 @@ static void rrdhost_update(RRDHOST *host
727 }
728
729 RRDHOST *rrdhost_find_or_create(
730 - const char *hostname
731 - , const char *registry_hostname
732 - , const char *guid
733 - , const char *os
734 - , const char *timezone
735 - , const char *abbrev_timezone
736 - , int32_t utc_offset
737 - , const char *tags
738 - , const char *program_name
739 - , const char *program_version
740 - , int update_every
741 - , long history
742 - , RRD_MEMORY_MODE mode
743 - , unsigned int health_enabled
744 - , unsigned int rrdpush_enabled
745 - , char *rrdpush_destination
746 - , char *rrdpush_api_key
747 - , char *rrdpush_send_charts_matching
748 - , bool rrdpush_enable_replication
749 - , time_t rrdpush_seconds_to_replicate
750 - , time_t rrdpush_replication_step
751 - , struct rrdhost_system_info *system_info
752 - , bool archived
730 + const char *hostname
731 + , const char *registry_hostname
732 + , const char *guid
733 + , const char *os
734 + , const char *timezone
735 + , const char *abbrev_timezone
736 + , int32_t utc_offset
737 + , const char *tags
738 + , const char *prog_name
739 + , const char *prog_version
740 + , int update_every
741 + , long history
742 + , RRD_MEMORY_MODE mode
743 + , unsigned int health_enabled
744 + , unsigned int rrdpush_enabled
745 + , char *rrdpush_destination
746 + , char *rrdpush_api_key
747 + , char *rrdpush_send_charts_matching
748 + , bool rrdpush_enable_replication
749 + , time_t rrdpush_seconds_to_replicate
750 + , time_t rrdpush_replication_step
751 + , struct rrdhost_system_info *system_info
752 + , bool archived
753 ) {
754 RRDHOST *host = rrdhost_find_by_guid(guid);
755 if (unlikely(host && host->rrd_memory_mode != mode && rrdhost_flag_check(host, RRDHOST_FLAG_ARCHIVED))) {
@@ -780,9 +780,9 @@ RRDHOST *rrdhost_find_or_create(
780 , abbrev_timezone
781 , utc_offset
782 , tags
783 - , program_name
784 - , program_version
785 - , update_every
783 + ,
784 + prog_name,
785 + prog_version, update_every
786 , history
787 , mode
788 , health_enabled
@@ -809,9 +809,9 @@ RRDHOST *rrdhost_find_or_create(
809 , abbrev_timezone
810 , utc_offset
811 , tags
812 - , program_name
813 - , program_version
814 - , update_every
812 + ,
813 + prog_name,
814 + prog_version, update_every
815 , history
816 , mode
817 , health_enabled
@@ -1046,7 +1046,6 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt
1046 dbengine_enabled = true;
1047 }
1048 else {
1049 - health_init();
1049 rrdpush_init();
1050
1051 if (default_rrd_memory_mode == RRD_MEMORY_MODE_DBENGINE || rrdpush_receiver_needs_dbengine()) {
@@ -1094,7 +1093,7 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt
1093 , default_rrd_update_every
1094 , default_rrd_history_entries
1095 , default_rrd_memory_mode
1097 - , default_health_enabled
1096 + , health_plugin_enabled()
1097 , default_rrdpush_enabled
1098 , default_rrdpush_destination
1099 , default_rrdpush_api_key
@@ -1112,6 +1111,10 @@ int rrd_init(char *hostname, struct rrdhost_system_info *system_info, bool unitt
1111
1112 dyncfg_host_init(localhost);
1113
1114 + if(!unittest) {
1115 + health_plugin_init();
1116 + }
1117 +
1118 // we register this only on localhost
1119 // for the other nodes, the origin server should register it
1120 rrd_function_add_inline(localhost, NULL, "streaming", 10,
@@ -1270,7 +1273,6 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) {
1273 // delete all the RRDSETs of the host
1274 rrdset_index_destroy(host);
1275 rrdcalc_rrdhost_index_destroy(host);
1273 - rrdcalctemplate_index_destroy(host);
1276
1277 // cleanup ML resources
1278 ml_host_delete(host);
@@ -1321,11 +1323,10 @@ void rrdhost_free___while_having_rrd_wrlock(RRDHOST *host, bool force) {
1323 simple_pattern_free(host->rrdpush_send_charts_matching);
1324 freez(host->node_id);
1325
1324 - rrdfamily_index_destroy(host);
1326 rrd_functions_host_destroy(host);
1327 rrdvariables_destroy(host->rrdvars);
1328 if (host == localhost)
1328 - rrdvariables_destroy(health_rrdvars);
1329 + health_plugin_destroy();
1330
1331 rrdhost_destroy_rrdcontexts(host);
1332
database/rrdlabels.c
+40 -4
@@ -671,8 +671,11 @@ void rrdlabels_destroy(RRDLABELS *labels)
671 freez(labels);
672 }
673
674 +//
675 // Check in labels to see if we have the key specified in label
675 -static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABEL *label)
676 +// same_value indicates if the value should also be matched
677 +//
678 +static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABEL *label, bool same_value)
679 {
680 if (unlikely(!labels))
681 return NULL;
@@ -683,7 +686,7 @@ static RRDLABEL *rrdlabels_find_label_with_key_unsafe(RRDLABELS *labels, RRDLABE
686 RRDLABEL *found = NULL;
687 while ((PValue = JudyLFirstThenNext(labels->JudyL, &Index, &first_then_next))) {
688 RRDLABEL *lb = (RRDLABEL *)Index;
686 - if (lb->index.key == label->index.key && lb != label) {
689 + if (lb->index.key == label->index.key && ((lb == label) == same_value)) {
690 found = (RRDLABEL *)Index;
691 break;
692 }
@@ -718,7 +721,7 @@ static void labels_add_already_sanitized(RRDLABELS *labels, const char *key, con
721 new_ls |= RRDLABEL_FLAG_NEW;
722 *((RRDLABEL_SRC *)PValue) = new_ls;
723
721 - RRDLABEL *old_label_with_same_key = rrdlabels_find_label_with_key_unsafe(labels, new_label);
724 + RRDLABEL *old_label_with_same_key = rrdlabels_find_label_with_key_unsafe(labels, new_label, false);
725 if (old_label_with_same_key) {
726 (void) JudyLDel(&labels->JudyL, (Word_t) old_label_with_same_key, PJE0);
727 delete_label(old_label_with_same_key);
@@ -1027,6 +1030,39 @@ void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src) {
1030 spinlock_unlock(&dst->spinlock);
1031 }
1032
1033 +//
1034 +//
1035 +// Return the common labels count in labels1, labels2
1036 +//
1037 +size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2)
1038 +{
1039 + if (!labels1 || !labels2)
1040 + return 0;
1041 +
1042 + if (labels1 == labels2)
1043 + return rrdlabels_entries(labels1);
1044 +
1045 + RRDLABEL *label;
1046 + RRDLABEL_SRC ls;
1047 +
1048 + spinlock_lock(&labels1->spinlock);
1049 + spinlock_lock(&labels2->spinlock);
1050 +
1051 + size_t count = 0;
1052 + lfe_start_nolock(labels2, label, ls)
1053 + {
1054 + RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(labels1, label, true);
1055 + if (old_label_with_key)
1056 + count++;
1057 + }
1058 + lfe_done_nolock();
1059 +
1060 + spinlock_unlock(&labels2->spinlock);
1061 + spinlock_unlock(&labels1->spinlock);
1062 + return count;
1063 +}
1064 +
1065 +
1066 void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src)
1067 {
1068 if (!dst || !src || (dst == src))
@@ -1042,7 +1078,7 @@ void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src)
1078 bool update_statistics = false;
1079 lfe_start_nolock(src, label, ls)
1080 {
1045 - RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(dst, label);
1081 + RRDLABEL *old_label_with_key = rrdlabels_find_label_with_key_unsafe(dst, label, false);
1082 Pvoid_t *PValue = JudyLIns(&dst->JudyL, (Word_t)label, PJE0);
1083 if(unlikely(!PValue || PValue == PJERR))
1084 fatal("RRDLABELS: corrupted labels array");
database/rrdlabels.h
+1
@@ -50,6 +50,7 @@ void rrdlabels_to_buffer_json_members(RRDLABELS *labels, BUFFER *wb);
50
51 void rrdlabels_migrate_to_these(RRDLABELS *dst, RRDLABELS *src);
52 void rrdlabels_copy(RRDLABELS *dst, RRDLABELS *src);
53 +size_t rrdlabels_common_count(RRDLABELS *labels1, RRDLABELS *labels2);
54
55 int rrdlabels_unittest(void);
56
database/rrdset.c
+7 -35
@@ -282,15 +282,7 @@ static void rrdset_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, v
282
283 rrddim_index_init(st);
284
285 - // chart variables - we need this for data collection to work (collector given chart variables) - not only health
286 - rrdsetvar_index_init(st);
287 -
288 - if (host->health.health_enabled) {
289 - st->rrdfamily = rrdfamily_add_and_acquire(host, rrdset_family(st));
290 - st->rrdvars = rrdvariables_create();
291 - rrddimvar_index_init(st);
292 - }
293 -
285 + st->rrdvars = rrdvariables_create();
286 st->rrdlabels = rrdlabels_create();
287 rrdset_update_permanent_labels(st);
288
@@ -346,36 +338,24 @@ static void rrdset_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
338 // release the collector info
339 dictionary_destroy(st->functions_view);
340
349 - rrdcalc_unlink_all_rrdset_alerts(st);
341 + rrdcalc_unlink_and_delete_all_rrdset_alerts(st);
342
343 // ------------------------------------------------------------------------
344 // the order of destruction is important here
345
354 - // 1. delete RRDDIMVAR index - this will speed up the destruction of RRDDIMs
355 - // because each dimension loops to find its own variables in this index.
356 - // There are no references to the items on this index from the dimensions.
357 - // To find their own, they have to walk-through the dictionary.
358 - rrddimvar_index_destroy(st); // destroy the rrddimvar index
359 -
360 - // 2. delete RRDSETVAR index
361 - rrdsetvar_index_destroy(st); // destroy the rrdsetvar index
362 -
363 - // 3. delete RRDVAR index after the above, to avoid triggering its garbage collector (they have references on this)
346 + // 1. delete RRDVAR index after the above, to avoid triggering its garbage collector (they have references on this)
347 rrdvariables_destroy(st->rrdvars); // free all variables and destroy the rrdvar dictionary
348
366 - // 4. delete RRDFAMILY - this has to be last, because RRDDIMVAR and RRDSETVAR need the reference counter
367 - rrdfamily_release(host, st->rrdfamily); // release the acquired rrdfamily -- has to be after all variables
368 -
369 - // 5. delete RRDDIMs, now their variables are not existing, so this is fast
349 + // 2. delete RRDDIMs, now their variables are not existing, so this is fast
350 rrddim_index_destroy(st); // free all the dimensions and destroy the dimensions index
351
372 - // 6. this has to be after the dimensions are freed, but before labels are freed (contexts need the labels)
352 + // 3. this has to be after the dimensions are freed, but before labels are freed (contexts need the labels)
353 rrdcontext_removed_rrdset(st); // let contexts know
354
375 - // 7. destroy the chart labels
355 + // 4. destroy the chart labels
356 rrdlabels_destroy(st->rrdlabels); // destroy the labels, after letting the contexts know
357
378 - // 8. destroy the ml handle
358 + // 5. destroy the ml handle
359 ml_chart_delete(st);
360
361 // ------------------------------------------------------------------------
@@ -461,8 +441,6 @@ static bool rrdset_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused,
441 if(old_family != st->family)
442 ctr->react_action |= RRDSET_REACT_UPDATED;
443 string_freez(old_family);
464 -
465 - // TODO - we should rename RRDFAMILY variables
444 }
445
446 if(ctr->context && *ctr->context) {
@@ -643,16 +621,10 @@ int rrdset_reset_name(RRDSET *st, const char *name) {
621 rrdset_index_del_name(host, st);
622 string_freez(st->name);
623 st->name = name_string;
646 - rrdsetvar_rename_all(st);
624 }
625 else
626 st->name = name_string;
627
651 - RRDDIM *rd;
652 - rrddim_foreach_read(rd, st)
653 - rrddimvar_rename_all(rd);
654 - rrddim_foreach_done(rd);
655 -
628 rrdset_index_add_name(host, st);
629
630 rrdset_flag_clear(st, RRDSET_FLAG_EXPORTING_SEND);
database/rrdsetvar.c deleted
-299
@@ -1,299 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrd.h"
4 -
5 -typedef struct rrdsetvar {
6 - STRING *name; // variable name
7 - void *value; // we need this to maintain the allocation for custom chart variables
8 -
9 - const RRDVAR_ACQUIRED *rrdvar_local;
10 - const RRDVAR_ACQUIRED *rrdvar_family_chart_id;
11 - const RRDVAR_ACQUIRED *rrdvar_family_chart_name;
12 - const RRDVAR_ACQUIRED *rrdvar_host_chart_id;
13 - const RRDVAR_ACQUIRED *rrdvar_host_chart_name;
14 -
15 - RRDVAR_FLAGS flags:24;
16 - RRDVAR_TYPE type:8;
17 -} RRDSETVAR;
18 -
19 -// should only be called while the rrdsetvar dict is write locked
20 -// otherwise, 2+ threads may be setting the same variables at the same time
21 -static inline void rrdsetvar_free_rrdvars_unsafe(RRDSET *st, RRDSETVAR *rs) {
22 - RRDHOST *host = st->rrdhost;
23 -
24 - // ------------------------------------------------------------------------
25 - // CHART
26 -
27 - if(st->rrdvars) {
28 - rrdvar_release_and_del(st->rrdvars, rs->rrdvar_local);
29 - rs->rrdvar_local = NULL;
30 - }
31 -
32 - // ------------------------------------------------------------------------
33 - // FAMILY
34 -
35 - if(st->rrdfamily) {
36 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_chart_id);
37 - rs->rrdvar_family_chart_id = NULL;
38 -
39 - rrdvar_release_and_del(rrdfamily_rrdvars_dict(st->rrdfamily), rs->rrdvar_family_chart_name);
40 - rs->rrdvar_family_chart_name = NULL;
41 - }
42 -
43 - // ------------------------------------------------------------------------
44 - // HOST
45 -
46 - if(host->rrdvars && host->health.health_enabled) {
47 - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_id);
48 - rs->rrdvar_host_chart_id = NULL;
49 -
50 - rrdvar_release_and_del(host->rrdvars, rs->rrdvar_host_chart_name);
51 - rs->rrdvar_host_chart_name = NULL;
52 - }
53 -}
54 -
55 -// should only be called while the rrdsetvar dict is write locked
56 -// otherwise, 2+ threads may be setting the same variables at the same time
57 -static inline void rrdsetvar_update_rrdvars_unsafe(RRDSET *st, RRDSETVAR *rs) {
58 - RRDHOST *host = st->rrdhost;
59 -
60 - RRDVAR_FLAGS options = rs->flags;
61 - options &= ~RRDVAR_OPTIONS_REMOVED_WHEN_PROPAGATING_TO_RRDVAR;
62 -
63 - // ------------------------------------------------------------------------
64 - // free the old ones (if any)
65 -
66 - rrdsetvar_free_rrdvars_unsafe(st, rs);
67 -
68 - // ------------------------------------------------------------------------
69 - // KEYS
70 -
71 - char buffer[RRDVAR_MAX_LENGTH + 1];
72 - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), string2str(rs->name));
73 - STRING *key_chart_id = string_strdupz(buffer);
74 -
75 - snprintfz(buffer, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), string2str(rs->name));
76 - STRING *key_chart_name = string_strdupz(buffer);
77 -
78 - // ------------------------------------------------------------------------
79 - // CHART
80 -
81 - if(st->rrdvars) {
82 - rs->rrdvar_local = rrdvar_add_and_acquire("local", st->rrdvars, rs->name, rs->type, options, rs->value);
83 - }
84 -
85 - // ------------------------------------------------------------------------
86 - // FAMILY
87 -
88 - if(st->rrdfamily) {
89 - rs->rrdvar_family_chart_id = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_chart_id, rs->type, options, rs->value);
90 - rs->rrdvar_family_chart_name = rrdvar_add_and_acquire("family", rrdfamily_rrdvars_dict(st->rrdfamily), key_chart_name, rs->type, options, rs->value);
91 - }
92 -
93 - // ------------------------------------------------------------------------
94 - // HOST
95 -
96 - if(host->rrdvars && host->health.health_enabled) {
97 - rs->rrdvar_host_chart_id = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_id, rs->type, options, rs->value);
98 - rs->rrdvar_host_chart_name = rrdvar_add_and_acquire("host", host->rrdvars, key_chart_name, rs->type, options, rs->value);
99 - }
100 -
101 - // free the keys
102 - string_freez(key_chart_id);
103 - string_freez(key_chart_name);
104 -}
105 -
106 -static void rrdsetvar_free_value_unsafe(RRDSETVAR *rs) {
107 - if(rs->flags & RRDVAR_FLAG_ALLOCATED) {
108 - void *old = rs->value;
109 - rs->value = NULL;
110 - rs->flags &= ~RRDVAR_FLAG_ALLOCATED;
111 - freez(old);
112 - }
113 -}
114 -
115 -static void rrdsetvar_set_value_unsafe(RRDSETVAR *rs, void *new_value) {
116 - rrdsetvar_free_value_unsafe(rs);
117 -
118 - if(new_value)
119 - rs->value = new_value;
120 - else {
121 - NETDATA_DOUBLE *n = mallocz(sizeof(NETDATA_DOUBLE));
122 - *n = NAN;
123 - rs->value = n;
124 - rs->flags |= RRDVAR_FLAG_ALLOCATED;
125 - }
126 -}
127 -
128 -struct rrdsetvar_constructor {
129 - RRDSET *rrdset;
130 - const char *name;
131 - void *value;
132 - RRDVAR_FLAGS flags :16;
133 - RRDVAR_TYPE type:8;
134 -};
135 -
136 -static void rrdsetvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *constructor_data) {
137 - RRDSETVAR *rs = rrdsetvar;
138 - struct rrdsetvar_constructor *ctr = constructor_data;
139 -
140 - ctr->flags &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS;
141 -
142 - rs->name = string_strdupz(ctr->name);
143 - rs->type = ctr->type;
144 - rs->flags = ctr->flags;
145 - rrdsetvar_set_value_unsafe(rs, ctr->value);
146 -
147 - // create the rrdvariables while we are having a write lock to the dictionary
148 - rrdsetvar_update_rrdvars_unsafe(ctr->rrdset, rs);
149 -}
150 -
151 -static bool rrdsetvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *new_rrdsetvar __maybe_unused, void *constructor_data) {
152 - RRDSETVAR *rs = rrdsetvar;
153 - struct rrdsetvar_constructor *ctr = constructor_data;
154 -
155 - ctr->flags &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS;
156 -
157 - RRDVAR_FLAGS options = rs->flags;
158 - options &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS;
159 -
160 - if(((ctr->value == NULL && rs->value != NULL && rs->flags & RRDVAR_FLAG_ALLOCATED) || (rs->value == ctr->value))
161 - && ctr->flags == options && rs->type == ctr->type) {
162 - // don't reset it - everything is the same, or as it should...
163 - return false;
164 - }
165 -
166 - internal_error(true, "RRDSETVAR: resetting variable '%s' of chart '%s' of host '%s', options from 0x%x to 0x%x, type from %d to %d",
167 - string2str(rs->name), rrdset_id(ctr->rrdset), rrdhost_hostname(ctr->rrdset->rrdhost),
168 - options, ctr->flags, rs->type, ctr->type);
169 -
170 - rrdsetvar_free_value_unsafe(rs); // we are going to change the options, so free it before setting it
171 - rs->flags = ctr->flags;
172 - rs->type = ctr->type;
173 - rrdsetvar_set_value_unsafe(rs, ctr->value);
174 -
175 - // recreate the rrdvariables while we are having a write lock to the dictionary
176 - rrdsetvar_update_rrdvars_unsafe(ctr->rrdset, rs);
177 - return true;
178 -}
179 -
180 -static void rrdsetvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdsetvar, void *rrdset __maybe_unused) {
181 - RRDSET *st = rrdset;
182 - RRDSETVAR *rs = rrdsetvar;
183 -
184 - rrdsetvar_free_rrdvars_unsafe(st, rs);
185 - rrdsetvar_free_value_unsafe(rs);
186 - string_freez(rs->name);
187 - rs->name = NULL;
188 -}
189 -
190 -void rrdsetvar_index_init(RRDSET *st) {
191 - if(!st->rrdsetvar_root_index) {
192 - st->rrdsetvar_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
193 - &dictionary_stats_category_rrdhealth, sizeof(RRDSETVAR));
194 -
195 - dictionary_register_insert_callback(st->rrdsetvar_root_index, rrdsetvar_insert_callback, NULL);
196 - dictionary_register_conflict_callback(st->rrdsetvar_root_index, rrdsetvar_conflict_callback, NULL);
197 - dictionary_register_delete_callback(st->rrdsetvar_root_index, rrdsetvar_delete_callback, st);
198 - }
199 -}
200 -
201 -void rrdsetvar_index_destroy(RRDSET *st) {
202 - dictionary_destroy(st->rrdsetvar_root_index);
203 - st->rrdsetvar_root_index = NULL;
204 -}
205 -
206 -const RRDSETVAR_ACQUIRED *rrdsetvar_add_and_acquire(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags) {
207 - struct rrdsetvar_constructor tmp = {
208 - .name = name,
209 - .type = type,
210 - .value = value,
211 - .flags = flags,
212 - .rrdset = st,
213 - };
214 -
215 - const RRDSETVAR_ACQUIRED *rsa = (const RRDSETVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(st->rrdsetvar_root_index, name, -1, NULL, sizeof(RRDSETVAR), &tmp);
216 - return rsa;
217 -}
218 -
219 -void rrdsetvar_add_and_leave_released(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags) {
220 - const RRDSETVAR_ACQUIRED *rsa = rrdsetvar_add_and_acquire(st, name, type, value, flags);
221 - dictionary_acquired_item_release(st->rrdsetvar_root_index, (const DICTIONARY_ITEM *)rsa);
222 -}
223 -
224 -void rrdsetvar_rename_all(RRDSET *st) {
225 - netdata_log_debug(D_VARIABLES, "RRDSETVAR rename for chart id '%s' name '%s'", rrdset_id(st), rrdset_name(st));
226 -
227 - RRDSETVAR *rs;
228 - dfe_start_write(st->rrdsetvar_root_index, rs) {
229 - // should only be called while the rrdsetvar dict is write locked
230 - rrdsetvar_update_rrdvars_unsafe(st, rs);
231 - }
232 - dfe_done(rs);
233 -
234 - rrdcalc_link_matching_alerts_to_rrdset(st);
235 -}
236 -
237 -void rrdsetvar_release_and_delete_all(RRDSET *st) {
238 - RRDSETVAR *rs;
239 - dfe_start_write(st->rrdsetvar_root_index, rs) {
240 - dictionary_del_advanced(st->rrdsetvar_root_index, string2str(rs->name), (ssize_t)string_strlen(rs->name));
241 - }
242 - dfe_done(rs);
243 -}
244 -
245 -void rrdsetvar_release(DICTIONARY *dict, const RRDSETVAR_ACQUIRED *rsa) {
246 - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rsa);
247 -}
248 -
249 -// --------------------------------------------------------------------------------------------------------------------
250 -// custom chart variables
251 -
252 -const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name) {
253 - STRING *name_string = rrdvar_name_to_string(name);
254 - const RRDSETVAR_ACQUIRED *rs = rrdsetvar_add_and_acquire(st, string2str(name_string), RRDVAR_TYPE_CALCULATED, NULL, RRDVAR_FLAG_CUSTOM_CHART_VAR);
255 - string_freez(name_string);
256 - return rs;
257 -}
258 -
259 -void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value) {
260 - if(!rsa) return;
261 -
262 - RRDSETVAR *rs = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rsa);
263 -
264 - if(rs->type != RRDVAR_TYPE_CALCULATED || !(rs->flags & RRDVAR_FLAG_CUSTOM_CHART_VAR) || !(rs->flags & RRDVAR_FLAG_ALLOCATED)) {
265 - netdata_log_error("RRDSETVAR: requested to set variable '%s' of chart '%s' on host '%s' to value " NETDATA_DOUBLE_FORMAT
266 - " but the variable is not a custom chart one (it has options 0x%x, value pointer %p). Ignoring request.",
267 - string2str(rs->name),
268 - rrdset_id(st),
269 - rrdhost_hostname(st->rrdhost),
270 - value,
271 - (uint32_t)rs->flags, rs->value);
272 - }
273 - else {
274 - NETDATA_DOUBLE *v = rs->value;
275 - if(*v != value) {
276 - *v = value;
277 - rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES);
278 - }
279 - }
280 -}
281 -
282 -void rrdsetvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb) {
283 - rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES);
284 -
285 - // send the chart local custom variables
286 - RRDSETVAR *rs;
287 - dfe_start_read(st->rrdsetvar_root_index, rs) {
288 - if(unlikely(rs->type == RRDVAR_TYPE_CALCULATED && rs->flags & RRDVAR_FLAG_CUSTOM_CHART_VAR)) {
289 - NETDATA_DOUBLE *value = (NETDATA_DOUBLE *) rs->value;
290 -
291 - buffer_sprintf(wb
292 - , "VARIABLE CHART %s = " NETDATA_DOUBLE_FORMAT "\n"
293 - , string2str(rs->name)
294 - , *value
295 - );
296 - }
297 - }
298 - dfe_done(rs);
299 -}
database/rrdsetvar.h deleted
-30
@@ -1,30 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_RRDSETVAR_H
4 -#define NETDATA_RRDSETVAR_H 1
5 -
6 -#include "rrd.h"
7 -
8 -// variables linked to charts
9 -// We link variables to point to the values that are already
10 -// calculated / processed by the normal data collection process
11 -// This means, there will be no speed penalty for using
12 -// these variables
13 -
14 -void rrdsetvar_index_init(RRDSET *st);
15 -void rrdsetvar_index_destroy(RRDSET *st);
16 -void rrdsetvar_release_and_delete_all(RRDSET *st);
17 -
18 -#define rrdsetvar_custom_chart_variable_release(st, rsa) rrdsetvar_release((st)->rrdsetvar_root_index, rsa)
19 -void rrdsetvar_release(DICTIONARY *dict, const RRDSETVAR_ACQUIRED *rsa);
20 -
21 -const RRDSETVAR_ACQUIRED *rrdsetvar_custom_chart_variable_add_and_acquire(RRDSET *st, const char *name);
22 -void rrdsetvar_custom_chart_variable_set(RRDSET *st, const RRDSETVAR_ACQUIRED *rsa, NETDATA_DOUBLE value);
23 -
24 -void rrdsetvar_rename_all(RRDSET *st);
25 -const RRDSETVAR_ACQUIRED *rrdsetvar_add_and_acquire(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags);
26 -void rrdsetvar_add_and_leave_released(RRDSET *st, const char *name, RRDVAR_TYPE type, void *value, RRDVAR_FLAGS flags);
27 -
28 -void rrdsetvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb);
29 -
30 -#endif //NETDATA_RRDSETVAR_H
database/rrdvar.c deleted
-392
@@ -1,392 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "rrd.h"
4 -
5 -// the variables as stored in the variables indexes
6 -// there are 3 indexes:
7 -// 1. at each chart (RRDSET.rrdvar_root_index)
8 -// 2. at each context (RRDFAMILY.rrdvar_root_index)
9 -// 3. at each host (RRDHOST.rrdvar_root_index)
10 -typedef struct rrdvar {
11 - STRING *name;
12 - void *value;
13 - RRDVAR_FLAGS flags:24;
14 - RRDVAR_TYPE type:8;
15 -} RRDVAR;
16 -
17 -// ----------------------------------------------------------------------------
18 -// RRDVAR management
19 -
20 -inline int rrdvar_fix_name(char *variable) {
21 - int fixed = 0;
22 - while(*variable) {
23 - if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
24 - *variable++ = '_';
25 - fixed++;
26 - }
27 - else
28 - variable++;
29 - }
30 -
31 - return fixed;
32 -}
33 -
34 -inline STRING *rrdvar_name_to_string(const char *name) {
35 - char *variable = strdupz(name);
36 - rrdvar_fix_name(variable);
37 - STRING *name_string = string_strdupz(variable);
38 - freez(variable);
39 - return name_string;
40 -}
41 -
42 -struct rrdvar_constructor {
43 - STRING *name;
44 - void *value;
45 - RRDVAR_FLAGS options:16;
46 - RRDVAR_TYPE type:8;
47 -
48 - enum {
49 - RRDVAR_REACT_NONE = 0,
50 - RRDVAR_REACT_NEW = (1 << 0),
51 - } react_action;
52 -};
53 -
54 -static void rrdvar_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar, void *constructor_data) {
55 - RRDVAR *rv = rrdvar;
56 - struct rrdvar_constructor *ctr = constructor_data;
57 -
58 - ctr->options &= ~RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS;
59 -
60 - rv->name = string_dup(ctr->name);
61 - rv->type = ctr->type;
62 - rv->flags = ctr->options;
63 -
64 - if(!ctr->value) {
65 - NETDATA_DOUBLE *v = mallocz(sizeof(NETDATA_DOUBLE));
66 - *v = NAN;
67 - rv->value = v;
68 - rv->flags |= RRDVAR_FLAG_ALLOCATED;
69 - }
70 - else
71 - rv->value = ctr->value;
72 -
73 - ctr->react_action = RRDVAR_REACT_NEW;
74 -}
75 -
76 -static void rrdvar_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdvar, void *nothing __maybe_unused) {
77 - RRDVAR *rv = rrdvar;
78 -
79 - if(rv->flags & RRDVAR_FLAG_ALLOCATED)
80 - freez(rv->value);
81 -
82 - string_freez(rv->name);
83 - rv->name = NULL;
84 -}
85 -
86 -DICTIONARY *rrdvariables_create(void) {
87 - DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
88 - &dictionary_stats_category_rrdhealth, sizeof(RRDVAR));
89 -
90 - dictionary_register_insert_callback(dict, rrdvar_insert_callback, NULL);
91 - dictionary_register_delete_callback(dict, rrdvar_delete_callback, NULL);
92 -
93 - return dict;
94 -}
95 -
96 -DICTIONARY *health_rrdvariables_create(void) {
97 - DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_NONE, &dictionary_stats_category_rrdhealth, 0);
98 -
99 - dictionary_register_insert_callback(dict, rrdvar_insert_callback, NULL);
100 - dictionary_register_delete_callback(dict, rrdvar_delete_callback, NULL);
101 -
102 - return dict;
103 -}
104 -
105 -void rrdvariables_destroy(DICTIONARY *dict) {
106 - dictionary_destroy(dict);
107 -}
108 -
109 -static inline const RRDVAR_ACQUIRED *rrdvar_get_and_acquire(DICTIONARY *dict, STRING *name) {
110 - return (const RRDVAR_ACQUIRED *)dictionary_get_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name));
111 -}
112 -
113 -inline void rrdvar_release_and_del(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) {
114 - if(unlikely(!dict || !rva)) return;
115 -
116 - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
117 -
118 - dictionary_del_advanced(dict, string2str(rv->name), (ssize_t)string_strlen(rv->name));
119 -
120 - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
121 -}
122 -
123 -inline const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value) {
124 - if(unlikely(!dict || !name)) return NULL;
125 -
126 - struct rrdvar_constructor tmp = {
127 - .name = name,
128 - .value = value,
129 - .type = type,
130 - .options = options,
131 - .react_action = RRDVAR_REACT_NONE,
132 - };
133 - return (const RRDVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name), NULL, sizeof(RRDVAR), &tmp);
134 -}
135 -
136 -inline void rrdvar_add(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value) {
137 - if(unlikely(!dict || !name)) return;
138 -
139 - struct rrdvar_constructor tmp = {
140 - .name = name,
141 - .value = value,
142 - .type = type,
143 - .options = options,
144 - .react_action = RRDVAR_REACT_NONE,
145 - };
146 - dictionary_set_advanced(dict, string2str(name), (ssize_t)string_strlen(name), NULL, sizeof(RRDVAR), &tmp);
147 -}
148 -
149 -void rrdvar_delete_all(DICTIONARY *dict) {
150 - dictionary_flush(dict);
151 -}
152 -
153 -
154 -// ----------------------------------------------------------------------------
155 -// CUSTOM HOST VARIABLES
156 -
157 -inline int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data) {
158 - if(unlikely(!dict)) return 0; // when health is not enabled
159 - return dictionary_walkthrough_read(dict, callback, data);
160 -}
161 -
162 -const RRDVAR_ACQUIRED *rrdvar_custom_host_variable_add_and_acquire(RRDHOST *host, const char *name) {
163 - DICTIONARY *dict = host->rrdvars;
164 - if(unlikely(!dict)) return NULL; // when health is not enabled
165 -
166 - STRING *name_string = rrdvar_name_to_string(name);
167 -
168 - const RRDVAR_ACQUIRED *rva = rrdvar_add_and_acquire("host", dict, name_string, RRDVAR_TYPE_CALCULATED, RRDVAR_FLAG_CUSTOM_HOST_VAR, NULL);
169 -
170 - string_freez(name_string);
171 - return rva;
172 -}
173 -
174 -void rrdvar_custom_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) {
175 - if(unlikely(!host->rrdvars || !rva)) return; // when health is not enabled
176 -
177 - if(rrdvar_type(rva) != RRDVAR_TYPE_CALCULATED || !(rrdvar_flags(rva) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_ALLOCATED)))
178 - netdata_log_error("requested to set variable '%s' to value " NETDATA_DOUBLE_FORMAT " but the variable is not a custom one.", rrdvar_name(rva), value);
179 - else {
180 - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
181 - NETDATA_DOUBLE *v = rv->value;
182 - if(*v != value) {
183 - *v = value;
184 -
185 - // if the host is streaming, send this variable upstream immediately
186 - rrdpush_sender_send_this_host_variable_now(host, rva);
187 - }
188 - }
189 -}
190 -
191 -void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) {
192 - if(unlikely(!dict || !rva)) return; // when health is not enabled
193 - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
194 -}
195 -
196 -// ----------------------------------------------------------------------------
197 -// RRDVAR lookup
198 -
199 -NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva) {
200 - if(unlikely(!rva)) return NAN;
201 -
202 - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
203 -
204 - switch(rv->type) {
205 - case RRDVAR_TYPE_CALCULATED: {
206 - NETDATA_DOUBLE *n = (NETDATA_DOUBLE *)rv->value;
207 - return *n;
208 - }
209 -
210 - case RRDVAR_TYPE_TIME_T: {
211 - time_t *n = (time_t *)rv->value;
212 - return (NETDATA_DOUBLE)*n;
213 - }
214 -
215 - case RRDVAR_TYPE_COLLECTED: {
216 - collected_number *n = (collected_number *)rv->value;
217 - return (NETDATA_DOUBLE)*n;
218 - }
219 -
220 - case RRDVAR_TYPE_TOTAL: {
221 - total_number *n = (total_number *)rv->value;
222 - return (NETDATA_DOUBLE)*n;
223 - }
224 -
225 - case RRDVAR_TYPE_INT: {
226 - int *n = (int *)rv->value;
227 - return *n;
228 - }
229 -
230 - default:
231 - netdata_log_error("I don't know how to convert RRDVAR type %u to NETDATA_DOUBLE", rv->type);
232 - return NAN;
233 - }
234 -}
235 -
236 -int health_variable_check(DICTIONARY *dict, RRDSET *st, RRDDIM *rd) {
237 - if (!dict || !st || !rd) return 0;
238 -
239 - STRING *helper_str;
240 - char helper[RRDVAR_MAX_LENGTH + 1];
241 - snprintfz(helper, RRDVAR_MAX_LENGTH, "%s.%s", string2str(st->name), string2str(rd->name));
242 - helper_str = string_strdupz(helper);
243 -
244 - const RRDVAR_ACQUIRED *rva;
245 - rva = rrdvar_get_and_acquire(dict, helper_str);
246 - if(rva) {
247 - dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
248 - string_freez(helper_str);
249 - return 1;
250 - }
251 -
252 - string_freez(helper_str);
253 -
254 - return 0;
255 -}
256 -
257 -void rrdvar_store_for_chart(RRDHOST *host, RRDSET *st) {
258 - if (!st) return;
259 -
260 - if(!st->rrdfamily)
261 - st->rrdfamily = rrdfamily_add_and_acquire(host, rrdset_family(st));
262 -
263 - if(!st->rrdvars)
264 - st->rrdvars = rrdvariables_create();
265 -
266 - rrddimvar_index_init(st);
267 -
268 - rrdsetvar_add_and_leave_released(st, "last_collected_t", RRDVAR_TYPE_TIME_T, &st->last_collected_time.tv_sec, RRDVAR_FLAG_NONE);
269 - rrdsetvar_add_and_leave_released(st, "green", RRDVAR_TYPE_CALCULATED, &st->green, RRDVAR_FLAG_NONE);
270 - rrdsetvar_add_and_leave_released(st, "red", RRDVAR_TYPE_CALCULATED, &st->red, RRDVAR_FLAG_NONE);
271 - rrdsetvar_add_and_leave_released(st, "update_every", RRDVAR_TYPE_INT, &st->update_every, RRDVAR_FLAG_NONE);
272 -
273 - RRDDIM *rd;
274 - rrddim_foreach_read(rd, st) {
275 - rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_CALCULATED, NULL, NULL, &rd->collector.last_stored_value, RRDVAR_FLAG_NONE);
276 - rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_COLLECTED, NULL, "_raw", &rd->collector.last_collected_value, RRDVAR_FLAG_NONE);
277 - rrddimvar_add_and_leave_released(rd, RRDVAR_TYPE_TIME_T, NULL, "_last_collected_t", &rd->collector.last_collected_time.tv_sec, RRDVAR_FLAG_NONE);
278 - }
279 - rrddim_foreach_done(rd);
280 -}
281 -
282 -int health_variable_lookup(STRING *variable, RRDCALC *rc, NETDATA_DOUBLE *result) {
283 - RRDSET *st = rc->rrdset;
284 - if(!st) return 0;
285 -
286 - RRDHOST *host = st->rrdhost;
287 - const RRDVAR_ACQUIRED *rva;
288 -
289 - rva = rrdvar_get_and_acquire(st->rrdvars, variable);
290 - if(rva) {
291 - *result = rrdvar2number(rva);
292 - dictionary_acquired_item_release(st->rrdvars, (const DICTIONARY_ITEM *)rva);
293 - return 1;
294 - }
295 -
296 - rva = rrdvar_get_and_acquire(rrdfamily_rrdvars_dict(st->rrdfamily), variable);
297 - if(rva) {
298 - *result = rrdvar2number(rva);
299 - dictionary_acquired_item_release(rrdfamily_rrdvars_dict(st->rrdfamily), (const DICTIONARY_ITEM *)rva);
300 - return 1;
301 - }
302 -
303 - rva = rrdvar_get_and_acquire(host->rrdvars, variable);
304 - if(rva) {
305 - *result = rrdvar2number(rva);
306 - dictionary_acquired_item_release(host->rrdvars, (const DICTIONARY_ITEM *)rva);
307 - return 1;
308 - }
309 -
310 - return 0;
311 -}
312 -
313 -// ----------------------------------------------------------------------------
314 -// RRDVAR to JSON
315 -
316 -struct variable2json_helper {
317 - BUFFER *buf;
318 - RRDVAR_FLAGS options;
319 -};
320 -
321 -static int single_variable2json_callback(const DICTIONARY_ITEM *item __maybe_unused, void *entry __maybe_unused, void *helper_data) {
322 - struct variable2json_helper *helper = (struct variable2json_helper *)helper_data;
323 - const RRDVAR_ACQUIRED *rva = (const RRDVAR_ACQUIRED *)item;
324 - NETDATA_DOUBLE value = rrdvar2number(rva);
325 -
326 - if (helper->options == RRDVAR_FLAG_NONE || rrdvar_flags(rva) & helper->options) {
327 - if(unlikely(isnan(value) || isinf(value)))
328 - buffer_json_member_add_string(helper->buf, rrdvar_name(rva), NULL);
329 - else
330 - buffer_json_member_add_double(helper->buf, rrdvar_name(rva), (NETDATA_DOUBLE)value);
331 - }
332 -
333 - return 0;
334 -}
335 -
336 -void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf) {
337 - struct variable2json_helper helper = {.buf = buf, .options = RRDVAR_FLAG_CUSTOM_CHART_VAR};
338 -
339 - rrdvar_walkthrough_read(st->rrdvars, single_variable2json_callback, &helper);
340 -}
341 -
342 -void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf) {
343 - RRDHOST *host = st->rrdhost;
344 -
345 - struct variable2json_helper helper = {.buf = buf, .options = RRDVAR_FLAG_NONE};
346 -
347 - buffer_json_initialize(buf, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
348 -
349 - buffer_json_member_add_string(buf, "chart", rrdset_id(st));
350 - buffer_json_member_add_string(buf, "chart_name", rrdset_name(st));
351 - buffer_json_member_add_string(buf, "chart_context", rrdset_context(st));
352 -
353 - {
354 - buffer_json_member_add_object(buf, "chart_variables");
355 - rrdvar_walkthrough_read(st->rrdvars, single_variable2json_callback, &helper);
356 - buffer_json_object_close(buf);
357 - }
358 -
359 - buffer_json_member_add_string(buf, "family", rrdset_family(st));
360 -
361 - {
362 - buffer_json_member_add_object(buf, "family_variables");
363 - rrdvar_walkthrough_read(rrdfamily_rrdvars_dict(st->rrdfamily), single_variable2json_callback, &helper);
364 - buffer_json_object_close(buf);
365 - }
366 -
367 - buffer_json_member_add_string(buf, "host", rrdhost_hostname(host));
368 -
369 - {
370 - buffer_json_member_add_object(buf, "host_variables");
371 - rrdvar_walkthrough_read(host->rrdvars, single_variable2json_callback, &helper);
372 - buffer_json_object_close(buf);
373 - }
374 -
375 - buffer_json_finalize(buf);
376 -}
377 -
378 -// ----------------------------------------------------------------------------
379 -// RRDVAR private members examination
380 -
381 -const char *rrdvar_name(const RRDVAR_ACQUIRED *rva) {
382 - return dictionary_acquired_item_name((const DICTIONARY_ITEM *)rva);
383 -}
384 -
385 -RRDVAR_FLAGS rrdvar_flags(const RRDVAR_ACQUIRED *rva) {
386 - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
387 - return rv->flags;
388 -}
389 -RRDVAR_TYPE rrdvar_type(const RRDVAR_ACQUIRED *rva) {
390 - RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
391 - return rv->type;
392 -}
database/rrdvar.h deleted
-77
@@ -1,77 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_RRDVAR_H
4 -#define NETDATA_RRDVAR_H 1
5 -
6 -#include "libnetdata/libnetdata.h"
7 -
8 -typedef enum rrdvar_type {
9 - RRDVAR_TYPE_CALCULATED = 1,
10 - RRDVAR_TYPE_TIME_T = 2,
11 - RRDVAR_TYPE_COLLECTED = 3,
12 - RRDVAR_TYPE_TOTAL = 4,
13 - RRDVAR_TYPE_INT = 5
14 -
15 - // this is 8 bit
16 - // to increase it you have to set change the bitfield in
17 - // rrdvar, rrdsetvar, rrddimvar
18 -} RRDVAR_TYPE;
19 -
20 -typedef enum rrdvar_options {
21 - RRDVAR_FLAG_NONE = 0,
22 - RRDVAR_FLAG_ALLOCATED = (1 << 0), // the value ptr is allocated (not a reference)
23 - RRDVAR_FLAG_CUSTOM_HOST_VAR = (1 << 1), // this is a custom host variable, not associated with a dimension
24 - RRDVAR_FLAG_CUSTOM_CHART_VAR = (1 << 2), // this is a custom chart variable, not associated with a dimension
25 - RRDVAR_FLAG_RRDCALC_LOCAL_VAR = (1 << 3), // this is a an alarm variable, attached to a chart
26 - RRDVAR_FLAG_RRDCALC_FAMILY_VAR = (1 << 4), // this is a an alarm variable, attached to a family
27 - RRDVAR_FLAG_RRDCALC_HOST_CHARTID_VAR = (1 << 5), // this is a an alarm variable, attached to the host, using the chart id
28 - RRDVAR_FLAG_RRDCALC_HOST_CHARTNAME_VAR = (1 << 6), // this is a an alarm variable, attached to the host, using the chart name
29 - RRDVAR_FLAG_CONFIG_VAR = (1 << 7), // this is a an alarm variable, read from alarm config
30 -
31 - // this is 24 bit
32 - // to increase it you have to set change the bitfield in
33 - // rrdvar, rrdsetvar, rrddimvar
34 -} RRDVAR_FLAGS;
35 -
36 -#define RRDVAR_OPTIONS_REMOVED_ON_NEW_OBJECTS \
37 - (RRDVAR_FLAG_ALLOCATED)
38 -
39 -#define RRDVAR_OPTIONS_REMOVED_WHEN_PROPAGATING_TO_RRDVAR \
40 - (RRDVAR_FLAG_ALLOCATED)
41 -
42 -#define RRDVAR_MAX_LENGTH 1024
43 -
44 -int rrdvar_fix_name(char *variable);
45 -
46 -#include "rrd.h"
47 -
48 -STRING *rrdvar_name_to_string(const char *name);
49 -
50 -const RRDVAR_ACQUIRED *rrdvar_custom_host_variable_add_and_acquire(RRDHOST *host, const char *name);
51 -void rrdvar_add(const char *scope __maybe_unused, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value);
52 -void rrdvar_custom_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value);
53 -
54 -int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data);
55 -
56 -#define rrdvar_custom_host_variable_release(host, rva) rrdvar_release((host)->rrdvars, rva)
57 -void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva);
58 -
59 -NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva);
60 -
61 -const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(const char *scope, DICTIONARY *dict, STRING *name, RRDVAR_TYPE type, RRDVAR_FLAGS options, void *value);
62 -void rrdvar_release_and_del(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva);
63 -
64 -DICTIONARY *rrdvariables_create(void);
65 -DICTIONARY *health_rrdvariables_create(void);
66 -void rrdvariables_destroy(DICTIONARY *dict);
67 -
68 -void rrdvar_store_for_chart(RRDHOST *host, RRDSET *st);
69 -int health_variable_check(DICTIONARY *dict, RRDSET *st, RRDDIM *rd);
70 -
71 -void rrdvar_delete_all(DICTIONARY *dict);
72 -
73 -const char *rrdvar_name(const RRDVAR_ACQUIRED *rva);
74 -RRDVAR_FLAGS rrdvar_flags(const RRDVAR_ACQUIRED *rva);
75 -RRDVAR_TYPE rrdvar_type(const RRDVAR_ACQUIRED *rva);
76 -
77 -#endif //NETDATA_RRDVAR_H
database/sqlite/sqlite_aclk_alert.c
+4 -4
@@ -821,7 +821,7 @@ void health_alarm_entry2proto_nolock(struct alarm_log_entry *alarm_log, ALARM_EN
821 #endif
822
823 #ifdef ENABLE_ACLK
824 -static bool have_recent_alarm(RRDHOST *host, int64_t alarm_id, int64_t mark)
824 +static bool have_recent_alarm_unsafe(RRDHOST *host, int64_t alarm_id, int64_t mark)
825 {
826 ALARM_ENTRY *ae = host->health_log.alarms;
827
@@ -882,7 +882,7 @@ void aclk_push_alert_snapshot_event(char *node_id __maybe_unused)
882 if (unlikely(ae->new_status == RRDCALC_STATUS_UNINITIALIZED))
883 continue;
884
885 - if (have_recent_alarm(host, ae->alarm_id, ae->unique_id))
885 + if (have_recent_alarm_unsafe(host, ae->alarm_id, ae->unique_id))
886 continue;
887
888 if (is_event_from_alert_variable_config(ae->unique_id, &host->host_uuid))
@@ -911,7 +911,7 @@ void aclk_push_alert_snapshot_event(char *node_id __maybe_unused)
911 if (likely(ae->updated_by_id) || unlikely(ae->new_status == RRDCALC_STATUS_UNINITIALIZED))
912 continue;
913
914 - if (have_recent_alarm(host, ae->alarm_id, ae->unique_id))
914 + if (have_recent_alarm_unsafe(host, ae->alarm_id, ae->unique_id))
915 continue;
916
917 if (is_event_from_alert_variable_config(ae->unique_id, &host->host_uuid))
@@ -1090,7 +1090,7 @@ void aclk_push_alarm_checkpoint(RRDHOST *host __maybe_unused)
1090 }
1091
1092 active_alerts[cnt].name = (char *)rrdcalc_name(rc);
1093 - len += string_strlen(rc->name);
1093 + len += string_strlen(rc->config.name);
1094 active_alerts[cnt].chart = (char *)rrdcalc_chart_name(rc);
1095 len += string_strlen(rc->chart);
1096 active_alerts[cnt].status = rc->status;
database/sqlite/sqlite_health.c
+82 -223
@@ -3,6 +3,7 @@
3 #include "sqlite_health.h"
4 #include "sqlite_functions.h"
5 #include "sqlite_db_migration.h"
6 +#include "../../health/health_internals.h"
7
8 #define MAX_HEALTH_SQL_SIZE 2048
9 #define SQLITE3_BIND_STRING_OR_NULL(res, key, param) \
@@ -101,7 +102,8 @@ failed:
102 "config_hash_id, name, chart, exec, recipient, units, chart_context, last_transition_id, chart_name) " \
103 "VALUES (@host_id,@alarm_id, @config_hash_id,@name,@chart,@exec,@recipient,@units,@chart_context," \
104 "@last_transition_id,@chart_name) ON CONFLICT (host_id, alarm_id) DO UPDATE " \
104 - "SET last_transition_id = excluded.last_transition_id, chart_name = excluded.chart_name RETURNING health_log_id"
105 + "SET last_transition_id = excluded.last_transition_id, chart_name = excluded.chart_name, " \
106 + "config_hash_id=excluded.config_hash_id RETURNING health_log_id"
107
108 #define SQL_INSERT_HEALTH_LOG_DETAIL \
109 "INSERT INTO health_log_detail (health_log_id, unique_id, alarm_id, alarm_event_id, " \
@@ -958,7 +960,7 @@ void sql_health_alarm_log_load(RRDHOST *host)
960 "@p_db_lookup_dimensions,@p_db_lookup_method,@p_db_lookup_options,@p_db_lookup_after," \
961 "@p_db_lookup_before,@p_update_every,@source,@chart_labels,@summary)"
962
961 -int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg)
963 +int sql_alert_store_config(RRD_ALERT_PROTOTYPE *ap __maybe_unused)
964 {
965 static __thread sqlite3_stmt *res = NULL;
966 int rc, param = 0;
@@ -977,133 +979,170 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg)
979 return 1;
980 }
981 }
982 + BUFFER *buf = buffer_create(128, NULL);
983
981 - rc = sqlite3_bind_blob(res, ++param, hash_id, sizeof(*hash_id), SQLITE_STATIC);
984 + rc = sqlite3_bind_blob(res, ++param, &ap->config.hash_id, sizeof(ap->config.hash_id), SQLITE_STATIC);
985 if (unlikely(rc != SQLITE_OK))
986 goto bind_fail;
987
985 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->alarm, ++param);
988 + if (ap->match.is_template)
989 + rc = SQLITE3_BIND_STRING_OR_NULL(res, NULL, ++param);
990 + else
991 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.name, ++param);
992 +
993 if (unlikely(rc != SQLITE_OK))
994 goto bind_fail;
995
989 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->template_key, ++param);
996 + if (ap->match.is_template)
997 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.name, ++param);
998 + else
999 + rc = SQLITE3_BIND_STRING_OR_NULL(res, NULL, ++param);
1000 +
1001 if (unlikely(rc != SQLITE_OK))
1002 goto bind_fail;
1003
993 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->on, ++param);
1004 + if (ap->match.is_template)
1005 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.on.context, ++param);
1006 + else
1007 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.on.chart, ++param);
1008 if (unlikely(rc != SQLITE_OK))
1009 goto bind_fail;
1010
997 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->classification, ++param);
1011 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.classification, ++param);
1012 if (unlikely(rc != SQLITE_OK))
1013 goto bind_fail;
1014
1001 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->component, ++param);
1015 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.component, ++param);
1016 if (unlikely(rc != SQLITE_OK))
1017 goto bind_fail;
1018
1005 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->type, ++param);
1019 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.type, ++param);
1020 if (unlikely(rc != SQLITE_OK))
1021 goto bind_fail;
1022
1009 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->os, ++param);
1023 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.os, ++param);
1024 if (unlikely(rc != SQLITE_OK))
1025 goto bind_fail;
1026
1013 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->host, ++param);
1027 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.host, ++param);
1028 if (unlikely(rc != SQLITE_OK))
1029 goto bind_fail;
1030
1017 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->lookup, ++param);
1031 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.lookup, ++param);
1032 if (unlikely(rc != SQLITE_OK))
1033 goto bind_fail;
1034
1021 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->every, ++param);
1035 + rc = sqlite3_bind_int(res, ++param, ap->config.update_every);
1036 if (unlikely(rc != SQLITE_OK))
1037 goto bind_fail;
1038
1025 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->units, ++param);
1039 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.units, ++param);
1040 if (unlikely(rc != SQLITE_OK))
1041 goto bind_fail;
1042
1029 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->calc, ++param);
1043 + if (ap->config.calculation)
1044 + rc = sqlite3_bind_text(res, ++param, expression_source(ap->config.calculation), -1, SQLITE_STATIC);
1045 + else
1046 + rc = sqlite3_bind_null(res, ++param);
1047 if (unlikely(rc != SQLITE_OK))
1048 goto bind_fail;
1049
1033 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->plugin, ++param);
1050 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.plugin, ++param);
1051 if (unlikely(rc != SQLITE_OK))
1052 goto bind_fail;
1053
1037 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->module, ++param);
1054 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.module, ++param);
1055 if (unlikely(rc != SQLITE_OK))
1056 goto bind_fail;
1057
1041 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->charts, ++param);
1058 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.charts, ++param);
1059 if (unlikely(rc != SQLITE_OK))
1060 goto bind_fail;
1061
1045 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->green, ++param);
1062 + rc = sqlite3_bind_double(res, ++param, ap->config.green);
1063 if (unlikely(rc != SQLITE_OK))
1064 goto bind_fail;
1065
1049 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->red, ++param);
1066 + rc = sqlite3_bind_double(res, ++param, ap->config.red);
1067 if (unlikely(rc != SQLITE_OK))
1068 goto bind_fail;
1069
1053 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->warn, ++param);
1070 + if (ap->config.warning)
1071 + rc = sqlite3_bind_text(res, ++param, expression_source(ap->config.warning), -1, SQLITE_STATIC);
1072 + else
1073 + rc = sqlite3_bind_null(res, ++param);
1074 if (unlikely(rc != SQLITE_OK))
1075 goto bind_fail;
1076
1057 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->crit, ++param);
1077 + if (ap->config.critical)
1078 + rc = sqlite3_bind_text(res, ++param, expression_source(ap->config.critical), -1, SQLITE_STATIC);
1079 + else
1080 + rc = sqlite3_bind_null(res, ++param);
1081 if (unlikely(rc != SQLITE_OK))
1082 goto bind_fail;
1083
1061 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->exec, ++param);
1084 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.exec, ++param);
1085 if (unlikely(rc != SQLITE_OK))
1086 goto bind_fail;
1087
1065 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->to, ++param);
1088 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.recipient, ++param);
1089 if (unlikely(rc != SQLITE_OK))
1090 goto bind_fail;
1091
1069 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->info, ++param);
1092 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.info, ++param);
1093 if (unlikely(rc != SQLITE_OK))
1094 goto bind_fail;
1095
1073 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->delay, ++param);
1096 + if (ap->config.delay_up_duration)
1097 + buffer_sprintf(buf, "up %ds ", ap->config.delay_up_duration);
1098 +
1099 + if (ap->config.delay_down_duration)
1100 + buffer_sprintf(buf, "down %ds ", ap->config.delay_down_duration);
1101 +
1102 + if (ap->config.delay_multiplier)
1103 + buffer_sprintf(buf, "multiplier %.1f ", ap->config.delay_multiplier);
1104 +
1105 + if (ap->config.delay_max_duration)
1106 + buffer_sprintf(buf, "max %ds", ap->config.delay_max_duration);
1107 +
1108 + // delay
1109 + rc = sqlite3_bind_text(res, ++param, buffer_tostring(buf), -1, SQLITE_STATIC);
1110 if (unlikely(rc != SQLITE_OK))
1111 goto bind_fail;
1112
1077 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->options, ++param);
1113 + if (ap->config.alert_action_options & ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION)
1114 + rc = sqlite3_bind_text(res, ++param, "no-clear-notification", -1, SQLITE_STATIC);
1115 + else
1116 + rc = sqlite3_bind_null(res, ++param);
1117 if (unlikely(rc != SQLITE_OK))
1118 goto bind_fail;
1119
1081 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->repeat, ++param);
1120 + rc = sqlite3_bind_int(res, ++param, ap->config.update_every);
1121 if (unlikely(rc != SQLITE_OK))
1122 goto bind_fail;
1123
1085 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->host_labels, ++param);
1124 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.host_labels, ++param);
1125 if (unlikely(rc != SQLITE_OK))
1126 goto bind_fail;
1127
1089 - if (cfg->p_db_lookup_after) {
1090 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->p_db_lookup_dimensions, ++param);
1128 + if (ap->config.after) {
1129 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.dimensions, ++param);
1130 if (unlikely(rc != SQLITE_OK))
1131 goto bind_fail;
1132
1094 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->p_db_lookup_method, ++param);
1133 + rc = sqlite3_bind_text(res, ++param, time_grouping_id2txt(ap->config.group), -1, SQLITE_STATIC);
1134 if (unlikely(rc != SQLITE_OK))
1135 goto bind_fail;
1136
1098 - rc = sqlite3_bind_int(res, ++param, (int) cfg->p_db_lookup_options);
1137 + rc = sqlite3_bind_int(res, ++param, (int) ap->config.options);
1138 if (unlikely(rc != SQLITE_OK))
1139 goto bind_fail;
1140
1102 - rc = sqlite3_bind_int(res, ++param, (int) cfg->p_db_lookup_after);
1141 + rc = sqlite3_bind_int64(res, ++param, (int) ap->config.after);
1142 if (unlikely(rc != SQLITE_OK))
1143 goto bind_fail;
1144
1106 - rc = sqlite3_bind_int(res, ++param, (int) cfg->p_db_lookup_before);
1145 + rc = sqlite3_bind_int64(res, ++param, (int) ap->config.before);
1146 if (unlikely(rc != SQLITE_OK))
1147 goto bind_fail;
1148 } else {
@@ -1128,19 +1167,19 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg)
1167 goto bind_fail;
1168 }
1169
1131 - rc = sqlite3_bind_int(res, ++param, cfg->p_update_every);
1170 + rc = sqlite3_bind_int(res, ++param, ap->config.update_every);
1171 if (unlikely(rc != SQLITE_OK))
1172 goto bind_fail;
1173
1135 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->source, ++param);
1174 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.source, ++param);
1175 if (unlikely(rc != SQLITE_OK))
1176 goto bind_fail;
1177
1139 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->chart_labels, ++param);
1178 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->match.chart_labels, ++param);
1179 if (unlikely(rc != SQLITE_OK))
1180 goto bind_fail;
1181
1143 - rc = SQLITE3_BIND_STRING_OR_NULL(res, cfg->summary, ++param);
1182 + rc = SQLITE3_BIND_STRING_OR_NULL(res, ap->config.summary, ++param);
1183 if (unlikely(rc != SQLITE_OK))
1184 goto bind_fail;
1185
@@ -1152,9 +1191,11 @@ int sql_store_alert_config_hash(uuid_t *hash_id, struct alert_config *cfg)
1191 if (unlikely(rc != SQLITE_OK))
1192 error_report("Failed to reset statement in alert hash_id store function, rc = %d", rc);
1193
1194 + buffer_free(buf);
1195 return 0;
1196
1197 bind_fail:
1198 + buffer_free(buf);
1199 error_report("Failed to bind parameter %d to store alert hash_id, rc = %d", param, rc);
1200 rc = sqlite3_reset(res);
1201 if (unlikely(rc != SQLITE_OK))
@@ -1162,75 +1203,6 @@ bind_fail:
1203 return 1;
1204 }
1205
1165 -/*
1166 - alert hashes are used for cloud communication.
1167 - if cloud is disabled or openssl is not available (which will prevent cloud connectivity)
1168 - skip hash calculations
1169 -*/
1170 -#if defined ENABLE_HTTPS
1171 -#define DIGEST_ALERT_CONFIG_VAL(v) ((v) ? EVP_DigestUpdate(evpctx, (string2str(v)), string_strlen((v))) : EVP_DigestUpdate(evpctx, "", 1))
1172 -#endif
1173 -int alert_hash_and_store_config(
1174 - uuid_t hash_id,
1175 - struct alert_config *cfg,
1176 - int store_hash)
1177 -{
1178 -#if defined ENABLE_HTTPS
1179 - EVP_MD_CTX *evpctx;
1180 - unsigned char hash_value[EVP_MAX_MD_SIZE];
1181 - unsigned int hash_len;
1182 - evpctx = EVP_MD_CTX_create();
1183 - EVP_DigestInit_ex(evpctx, EVP_sha256(), NULL);
1184 -
1185 - DIGEST_ALERT_CONFIG_VAL(cfg->alarm);
1186 - DIGEST_ALERT_CONFIG_VAL(cfg->template_key);
1187 - DIGEST_ALERT_CONFIG_VAL(cfg->os);
1188 - DIGEST_ALERT_CONFIG_VAL(cfg->host);
1189 - DIGEST_ALERT_CONFIG_VAL(cfg->on);
1190 - DIGEST_ALERT_CONFIG_VAL(cfg->plugin);
1191 - DIGEST_ALERT_CONFIG_VAL(cfg->module);
1192 - DIGEST_ALERT_CONFIG_VAL(cfg->charts);
1193 - DIGEST_ALERT_CONFIG_VAL(cfg->lookup);
1194 - DIGEST_ALERT_CONFIG_VAL(cfg->calc);
1195 - DIGEST_ALERT_CONFIG_VAL(cfg->every);
1196 - DIGEST_ALERT_CONFIG_VAL(cfg->green);
1197 - DIGEST_ALERT_CONFIG_VAL(cfg->red);
1198 - DIGEST_ALERT_CONFIG_VAL(cfg->warn);
1199 - DIGEST_ALERT_CONFIG_VAL(cfg->crit);
1200 - DIGEST_ALERT_CONFIG_VAL(cfg->exec);
1201 - DIGEST_ALERT_CONFIG_VAL(cfg->to);
1202 - DIGEST_ALERT_CONFIG_VAL(cfg->units);
1203 - DIGEST_ALERT_CONFIG_VAL(cfg->info);
1204 - DIGEST_ALERT_CONFIG_VAL(cfg->classification);
1205 - DIGEST_ALERT_CONFIG_VAL(cfg->component);
1206 - DIGEST_ALERT_CONFIG_VAL(cfg->type);
1207 - DIGEST_ALERT_CONFIG_VAL(cfg->delay);
1208 - DIGEST_ALERT_CONFIG_VAL(cfg->options);
1209 - DIGEST_ALERT_CONFIG_VAL(cfg->repeat);
1210 - DIGEST_ALERT_CONFIG_VAL(cfg->host_labels);
1211 - DIGEST_ALERT_CONFIG_VAL(cfg->chart_labels);
1212 - DIGEST_ALERT_CONFIG_VAL(cfg->summary);
1213 -
1214 - EVP_DigestFinal_ex(evpctx, hash_value, &hash_len);
1215 - EVP_MD_CTX_destroy(evpctx);
1216 - fatal_assert(hash_len > sizeof(uuid_t));
1217 -
1218 - char uuid_str[UUID_STR_LEN];
1219 - uuid_unparse_lower(*((uuid_t *)&hash_value), uuid_str);
1220 - uuid_copy(hash_id, *((uuid_t *)&hash_value));
1221 -
1222 - /* store everything, so it can be recreated when not in memory or just a subset ? */
1223 - if (store_hash)
1224 - (void)sql_store_alert_config_hash( (uuid_t *)&hash_value, cfg);
1225 -#else
1226 - UNUSED(hash_id);
1227 - UNUSED(cfg);
1228 - UNUSED(store_hash);
1229 -#endif
1230 -
1231 - return 1;
1232 -}
1233 -
1206 #define SQL_SELECT_HEALTH_LAST_EXECUTED_EVENT \
1207 "SELECT hld.new_status FROM health_log hl, health_log_detail hld " \
1208 "WHERE hl.host_id = @host_id AND hl.alarm_id = @alarm_id AND hld.unique_id != @unique_id AND hld.flags & @flags " \
@@ -1634,10 +1606,9 @@ static uint32_t get_next_alarm_event_id(uint64_t health_log_id, uint32_t alarm_i
1606 }
1607
1608 #define SQL_GET_ALARM_ID \
1637 - "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart " \
1638 - "AND name = @name AND config_hash_id = @config_hash_id"
1609 + "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart AND name = @name"
1610
1640 -uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id)
1611 +uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id)
1612 {
1613 int rc = 0;
1614 sqlite3_stmt *res = NULL;
@@ -1671,13 +1642,6 @@ uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *
1642 return alarm_id;
1643 }
1644
1674 - rc = sqlite3_bind_blob(res, 4, config_hash_id, sizeof(*config_hash_id), SQLITE_STATIC);
1675 - if (unlikely(rc != SQLITE_OK)) {
1676 - error_report("Failed to bind config_hash_id parameter for SQL_GET_ALARM_ID.");
1677 - sqlite3_finalize(res);
1678 - return alarm_id;
1679 - }
1680 -
1645 while (sqlite3_step_monitored(res) == SQLITE_ROW) {
1646 alarm_id = (uint32_t) sqlite3_column_int64(res, 0);
1647 health_log_id = (uint64_t) sqlite3_column_int64(res, 1);
@@ -1693,111 +1657,6 @@ uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *
1657 return alarm_id;
1658 }
1659
1696 -#define SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH \
1697 - "UPDATE health_log SET config_hash_id = @config_hash_id WHERE host_id = @host_id AND alarm_id = @alarm_id " \
1698 - "AND health_log_id = @health_log_id"
1699 -
1700 -void sql_update_alarm_with_config_hash(RRDHOST *host, uint32_t alarm_id, uint64_t health_log_id, uuid_t *config_hash_id)
1701 -{
1702 - int rc = 0;
1703 - sqlite3_stmt *res = NULL;
1704 -
1705 - rc = sqlite3_prepare_v2(db_meta, SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH, -1, &res, 0);
1706 - if (rc != SQLITE_OK) {
1707 - error_report("Failed to prepare statement when trying to update an alarm id with a config hash.");
1708 - return;
1709 - }
1710 -
1711 - rc = sqlite3_bind_blob(res, 1, config_hash_id, sizeof(*config_hash_id), SQLITE_STATIC);
1712 - if (unlikely(rc != SQLITE_OK)) {
1713 - error_report("Failed to bind config_hash_id parameter for SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH.");
1714 - goto done;
1715 - }
1716 -
1717 - rc = sqlite3_bind_blob(res, 2, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC);
1718 - if (unlikely(rc != SQLITE_OK)) {
1719 - error_report("Failed to bind host_id parameter for SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH.");
1720 - goto done;
1721 - }
1722 -
1723 - rc = sqlite3_bind_int64(res, 3, (sqlite3_int64) alarm_id);
1724 - if (unlikely(rc != SQLITE_OK)) {
1725 - error_report("Failed to bind alarm_id parameter for SQL_GET_ALARM_ID.");
1726 - goto done;
1727 - }
1728 -
1729 - rc = sqlite3_bind_int64(res, 4, (sqlite3_int64) health_log_id);
1730 - if (unlikely(rc != SQLITE_OK)) {
1731 - error_report("Failed to bind alarm_id parameter for SQL_GET_ALARM_ID.");
1732 - goto done;
1733 - }
1734 -
1735 - rc = execute_insert(res);
1736 - if (unlikely(rc != SQLITE_DONE))
1737 - error_report("Failed to execute SQL_UPDATE_ALARM_ID_WITH_CONFIG_HASH, rc = %d", rc);
1738 -
1739 -done:
1740 - rc = sqlite3_finalize(res);
1741 - if (unlikely(rc != SQLITE_OK))
1742 - error_report("Failed to reset statement to update health log detail table with config hash ids, rc = %d", rc);
1743 -
1744 -}
1745 -
1746 -#define SQL_GET_ALARM_ID_CHECK_ZERO_HASH \
1747 - "SELECT alarm_id, health_log_id FROM health_log WHERE host_id = @host_id AND chart = @chart " \
1748 - "AND name = @name AND (config_hash_id IS NULL OR config_hash_id = ZEROBLOB(16))"
1749 -
1750 -uint32_t sql_get_alarm_id_check_zero_hash(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id)
1751 -{
1752 - int rc = 0;
1753 - sqlite3_stmt *res = NULL;
1754 - uint32_t alarm_id = 0;
1755 - uint64_t health_log_id = 0;
1756 -
1757 - rc = sqlite3_prepare_v2(db_meta, SQL_GET_ALARM_ID_CHECK_ZERO_HASH, -1, &res, 0);
1758 - if (rc != SQLITE_OK) {
1759 - error_report("Failed to prepare statement when trying to get an alarm id with zero hash");
1760 - return alarm_id;
1761 - }
1762 -
1763 - rc = sqlite3_bind_blob(res, 1, &host->host_uuid, sizeof(host->host_uuid), SQLITE_STATIC);
1764 - if (unlikely(rc != SQLITE_OK)) {
1765 - error_report("Failed to bind host_id parameter for SQL_GET_ALARM_ID_CHECK_ZERO_HASH.");
1766 - sqlite3_finalize(res);
1767 - return alarm_id;
1768 - }
1769 -
1770 - rc = SQLITE3_BIND_STRING_OR_NULL(res, chart, 2);
1771 - if (unlikely(rc != SQLITE_OK)) {
1772 - error_report("Failed to bind char parameter for SQL_GET_ALARM_ID_CHECK_ZERO_HASH.");
1773 - sqlite3_finalize(res);
1774 - return alarm_id;
1775 - }
1776 -
1777 - rc = SQLITE3_BIND_STRING_OR_NULL(res, name, 3);
1778 - if (unlikely(rc != SQLITE_OK)) {
1779 - error_report("Failed to bind name parameter for SQL_GET_ALARM_ID_CHECK_ZERO_HASH.");
1780 - sqlite3_finalize(res);
1781 - return alarm_id;
1782 - }
1783 -
1784 - while (sqlite3_step_monitored(res) == SQLITE_ROW) {
1785 - alarm_id = (uint32_t) sqlite3_column_int64(res, 0);
1786 - health_log_id = (uint64_t) sqlite3_column_int64(res, 1);
1787 - }
1788 -
1789 - rc = sqlite3_finalize(res);
1790 - if (unlikely(rc != SQLITE_OK))
1791 - error_report("Failed to finalize the statement while getting an alarm id.");
1792 -
1793 - if (alarm_id) {
1794 - sql_update_alarm_with_config_hash(host, alarm_id, health_log_id, config_hash_id);
1795 - *next_event_id = get_next_alarm_event_id(health_log_id, alarm_id);
1796 - }
1797 -
1798 - return alarm_id;
1799 -}
1800 -
1660 #define SQL_GET_ALARM_ID_FROM_TRANSITION_ID \
1661 "SELECT hld.alarm_id, hl.host_id, hl.chart_context FROM health_log_detail hld, health_log hl " \
1662 "WHERE hld.transition_id = @transition_id " \
database/sqlite/sqlite_health.h
+3 -3
@@ -7,16 +7,16 @@
7
8 struct sql_alert_transition_data;
9 struct sql_alert_config_data;
10 +struct rrd_alert_prototype;
11 void sql_health_alarm_log_load(RRDHOST *host);
12 void sql_health_alarm_log_save(RRDHOST *host, ALARM_ENTRY *ae);
13 void sql_health_alarm_log_cleanup(RRDHOST *host, bool claimed);
13 -int alert_hash_and_store_config(uuid_t hash_id, struct alert_config *cfg, int store_hash);
14 +int sql_alert_store_config(struct rrd_alert_prototype *ap);
15 void sql_aclk_alert_clean_dead_entries(RRDHOST *host);
16 int sql_health_get_last_executed_event(RRDHOST *host, ALARM_ENTRY *ae, RRDCALC_STATUS *last_executed_status);
17 void sql_health_alarm_log2json(RRDHOST *host, BUFFER *wb, time_t after, const char *chart);
18 int health_migrate_old_health_log_table(char *table);
18 -uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id);
19 -uint32_t sql_get_alarm_id_check_zero_hash(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id);
19 +uint32_t sql_get_alarm_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id);
20 void sql_alert_transitions(
21 DICTIONARY *nodes,
22 time_t after,
exporting/prometheus/prometheus.c
+41 -47
@@ -374,59 +374,55 @@ static int print_host_variables_callback(const DICTIONARY_ITEM *item __maybe_unu
374
375 struct host_variables_callback_options *opts = data;
376
377 - if (rrdvar_flags(rv) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_CUSTOM_CHART_VAR)) {
378 - if (!opts->host_header_printed) {
379 - opts->host_header_printed = 1;
377 + if (!opts->host_header_printed) {
378 + opts->host_header_printed = 1;
379
381 - if (opts->output_options & PROMETHEUS_OUTPUT_HELP) {
382 - buffer_sprintf(opts->wb, "\n# COMMENT global host and chart variables\n");
383 - }
380 + if (opts->output_options & PROMETHEUS_OUTPUT_HELP) {
381 + buffer_sprintf(opts->wb, "\n# COMMENT global host and chart variables\n");
382 }
383 + }
384
386 - NETDATA_DOUBLE value = rrdvar2number(rv);
387 - if (isnan(value) || isinf(value)) {
388 - if (opts->output_options & PROMETHEUS_OUTPUT_HELP)
389 - buffer_sprintf(
390 - opts->wb, "# COMMENT variable \"%s\" is %s. Skipped.\n", rrdvar_name(rv), (isnan(value)) ? "NAN" : "INF");
385 + NETDATA_DOUBLE value = rrdvar2number(rv);
386 + if (isnan(value) || isinf(value)) {
387 + if (opts->output_options & PROMETHEUS_OUTPUT_HELP)
388 + buffer_sprintf(
389 + opts->wb, "# COMMENT variable \"%s\" is %s. Skipped.\n", rrdvar_name(rv), (isnan(value)) ? "NAN" : "INF");
390
392 - return 0;
393 - }
391 + return 0;
392 + }
393
395 - char *label_pre = "";
396 - char *label_post = "";
397 - if (opts->labels && *opts->labels) {
398 - label_pre = "{";
399 - label_post = "}";
400 - }
394 + char *label_pre = "";
395 + char *label_post = "";
396 + if (opts->labels && *opts->labels) {
397 + label_pre = "{";
398 + label_post = "}";
399 + }
400
402 - prometheus_name_copy(opts->name, rrdvar_name(rv), sizeof(opts->name));
401 + prometheus_name_copy(opts->name, rrdvar_name(rv), sizeof(opts->name));
402
404 - if (opts->output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
405 - buffer_sprintf(
406 - opts->wb,
407 - "%s_%s%s%s%s " NETDATA_DOUBLE_FORMAT " %llu\n",
408 - opts->prefix,
409 - opts->name,
410 - label_pre,
411 - opts->labels,
412 - label_post,
413 - value,
414 - opts->now * 1000ULL);
415 - else
416 - buffer_sprintf(
417 - opts->wb,
418 - "%s_%s%s%s%s " NETDATA_DOUBLE_FORMAT "\n",
419 - opts->prefix,
420 - opts->name,
421 - label_pre,
422 - opts->labels,
423 - label_post,
424 - value);
425 -
426 - return 1;
427 - }
403 + if (opts->output_options & PROMETHEUS_OUTPUT_TIMESTAMPS)
404 + buffer_sprintf(
405 + opts->wb,
406 + "%s_%s%s%s%s " NETDATA_DOUBLE_FORMAT " %llu\n",
407 + opts->prefix,
408 + opts->name,
409 + label_pre,
410 + opts->labels,
411 + label_post,
412 + value,
413 + opts->now * 1000ULL);
414 + else
415 + buffer_sprintf(
416 + opts->wb,
417 + "%s_%s%s%s%s " NETDATA_DOUBLE_FORMAT "\n",
418 + opts->prefix,
419 + opts->name,
420 + label_pre,
421 + opts->labels,
422 + label_post,
423 + value);
424
429 - return 0;
425 + return 1;
426 }
427
428 struct gen_parameters {
@@ -534,7 +530,6 @@ static void prometheus_print_os_info(
530 FILE *fp;
531 char filename[FILENAME_MAX + 1];
532 char buf[BUFSIZ + 1];
537 - int first_line = 1;
533
534 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/etc/os-release");
535 fp = fopen(filename, "r");
@@ -596,7 +591,6 @@ static void prometheus_print_os_info(
591 *val = '\0';
592 val++;
593 buffer_sprintf(wb, ",%s=\"%s\"", key, val);
599 - first_line = 0;
594 }
595 }
596
exporting/prometheus/remote_write/remote_write.c
+13 -15
@@ -330,24 +330,22 @@ static int format_variable_prometheus_remote_write_callback(const DICTIONARY_ITE
330
331 struct prometheus_remote_write_variables_callback_options *opts = data;
332
333 - if (rrdvar_flags(rv) & (RRDVAR_FLAG_CUSTOM_HOST_VAR | RRDVAR_FLAG_CUSTOM_CHART_VAR)) {
334 - RRDHOST *host = opts->host;
335 - struct instance *instance = opts->instance;
336 - struct simple_connector_data *simple_connector_data =
337 - (struct simple_connector_data *)instance->connector_specific_data;
338 - struct prometheus_remote_write_specific_data *connector_specific_data =
339 - (struct prometheus_remote_write_specific_data *)simple_connector_data->connector_specific_data;
333 + RRDHOST *host = opts->host;
334 + struct instance *instance = opts->instance;
335 + struct simple_connector_data *simple_connector_data =
336 + (struct simple_connector_data *)instance->connector_specific_data;
337 + struct prometheus_remote_write_specific_data *connector_specific_data =
338 + (struct prometheus_remote_write_specific_data *)simple_connector_data->connector_specific_data;
339
341 - char name[PROMETHEUS_LABELS_MAX + 1];
342 - char *suffix = "";
340 + char name[PROMETHEUS_LABELS_MAX + 1];
341 + char *suffix = "";
342
344 - prometheus_name_copy(context, rrdvar_name(rv), PROMETHEUS_ELEMENT_MAX);
345 - snprintf(name, PROMETHEUS_LABELS_MAX, "%s_%s%s", instance->config.prefix, context, suffix);
343 + prometheus_name_copy(context, rrdvar_name(rv), PROMETHEUS_ELEMENT_MAX);
344 + snprintf(name, PROMETHEUS_LABELS_MAX, "%s_%s%s", instance->config.prefix, context, suffix);
345
347 - NETDATA_DOUBLE value = rrdvar2number(rv);
348 - add_variable(connector_specific_data->write_request, name,
349 - (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), value, opts->now / USEC_PER_MS);
350 - }
346 + NETDATA_DOUBLE value = rrdvar2number(rv);
347 + add_variable(connector_specific_data->write_request, name,
348 + (host == localhost) ? instance->config.hostname : rrdhost_hostname(host), value, opts->now / USEC_PER_MS);
349
350 return 0;
351 }
health/health.c
+112 -1618
@@ -1,1678 +1,172 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "health.h"
4 +#include "health_internals.h"
5
5 -#define WORKER_HEALTH_JOB_RRD_LOCK 0
6 -#define WORKER_HEALTH_JOB_HOST_LOCK 1
7 -#define WORKER_HEALTH_JOB_DB_QUERY 2
8 -#define WORKER_HEALTH_JOB_CALC_EVAL 3
9 -#define WORKER_HEALTH_JOB_WARNING_EVAL 4
10 -#define WORKER_HEALTH_JOB_CRITICAL_EVAL 5
11 -#define WORKER_HEALTH_JOB_ALARM_LOG_ENTRY 6
12 -#define WORKER_HEALTH_JOB_ALARM_LOG_PROCESS 7
13 -#define WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET 8
14 -#define WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM 9
6 +struct health_plugin_globals health_globals = {
7 + .initialization = {
8 + .spinlock = NETDATA_SPINLOCK_INITIALIZER,
9 + .done = false,
10 + },
11 + .config = {
12 + .enabled = true,
13 + .stock_enabled = true,
14 + .use_summary_for_notifications = true,
15
16 -#if WORKER_UTILIZATION_MAX_JOB_TYPES < 10
17 -#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 10
18 -#endif
16 + .health_log_entries_max = HEALTH_LOG_ENTRIES_DEFAULT,
17 + .health_log_history = HEALTH_LOG_HISTORY_DEFAULT,
18
20 -unsigned int default_health_enabled = 1;
21 -char *silencers_filename;
22 -SIMPLE_PATTERN *conf_enabled_alarms = NULL;
23 -DICTIONARY *health_rrdvars;
19 + .default_warn_repeat_every = 0,
20 + .default_crit_repeat_every = 0,
21
25 -bool health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(RRDCALC *rc, usec_t *global_id, uuid_t *transitions_id) {
26 - if(!rc->rrdset)
27 - return false;
28 -
29 - RRDHOST *host = rc->rrdset->rrdhost;
30 -
31 - rw_spinlock_read_lock(&host->health_log.spinlock);
32 -
33 - ALARM_ENTRY *ae;
34 - for(ae = host->health_log.alarms; ae ; ae = ae->next) {
35 - if(unlikely(ae->alarm_id == rc->id))
36 - break;
37 - }
38 -
39 - if(ae) {
40 - *global_id = ae->global_id;
41 - uuid_copy(*transitions_id, ae->transition_id);
42 - }
43 - else {
44 - *global_id = 0;
45 - uuid_clear(*transitions_id);
22 + .run_at_least_every_seconds = 10,
23 + .postpone_alarms_during_hibernation_for_seconds = 60,
24 + },
25 + .prototypes = {
26 + .dict = NULL,
27 }
28 +};
29
48 - rw_spinlock_read_unlock(&host->health_log.spinlock);
49 -
50 - return ae != NULL;
30 +bool health_plugin_enabled(void) {
31 + return health_globals.config.enabled;
32 }
33
53 -void health_entry_flags_to_json_array(BUFFER *wb, const char *key, HEALTH_ENTRY_FLAGS flags) {
54 - buffer_json_member_add_array(wb, key);
55 -
56 - if(flags & HEALTH_ENTRY_FLAG_PROCESSED)
57 - buffer_json_add_array_item_string(wb, "PROCESSED");
58 - if(flags & HEALTH_ENTRY_FLAG_UPDATED)
59 - buffer_json_add_array_item_string(wb, "UPDATED");
60 - if(flags & HEALTH_ENTRY_FLAG_EXEC_RUN)
61 - buffer_json_add_array_item_string(wb, "EXEC_RUN");
62 - if(flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)
63 - buffer_json_add_array_item_string(wb, "EXEC_FAILED");
64 - if(flags & HEALTH_ENTRY_FLAG_SILENCED)
65 - buffer_json_add_array_item_string(wb, "SILENCED");
66 - if(flags & HEALTH_ENTRY_RUN_ONCE)
67 - buffer_json_add_array_item_string(wb, "RUN_ONCE");
68 - if(flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS)
69 - buffer_json_add_array_item_string(wb, "EXEC_IN_PROGRESS");
70 - if(flags & HEALTH_ENTRY_FLAG_IS_REPEATING)
71 - buffer_json_add_array_item_string(wb, "RECURRING");
72 - if(flags & HEALTH_ENTRY_FLAG_SAVED)
73 - buffer_json_add_array_item_string(wb, "SAVED");
74 - if(flags & HEALTH_ENTRY_FLAG_ACLK_QUEUED)
75 - buffer_json_add_array_item_string(wb, "ACLK_QUEUED");
76 - if(flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)
77 - buffer_json_add_array_item_string(wb, "NO_CLEAR_NOTIFICATION");
78 -
79 - buffer_json_array_close(wb);
34 +void health_plugin_disable(void) {
35 + health_globals.config.enabled = false;
36 }
37
82 -static bool prepare_command(BUFFER *wb,
83 - const char *exec,
84 - const char *recipient,
85 - const char *registry_hostname,
86 - uint32_t unique_id,
87 - uint32_t alarm_id,
88 - uint32_t alarm_event_id,
89 - uint32_t when,
90 - const char *alert_name,
91 - const char *alert_chart_name,
92 - const char *new_status,
93 - const char *old_status,
94 - NETDATA_DOUBLE new_value,
95 - NETDATA_DOUBLE old_value,
96 - const char *alert_source,
97 - uint32_t duration,
98 - uint32_t non_clear_duration,
99 - const char *alert_units,
100 - const char *alert_info,
101 - const char *new_value_string,
102 - const char *old_value_string,
103 - const char *source,
104 - const char *error_msg,
105 - int n_warn,
106 - int n_crit,
107 - const char *warn_alarms,
108 - const char *crit_alarms,
109 - const char *classification,
110 - const char *edit_command,
111 - const char *machine_guid,
112 - uuid_t *transition_id,
113 - const char *summary,
114 - const char *context,
115 - const char *component,
116 - const char *type
117 -) {
118 - char buf[8192];
119 - size_t n = sizeof(buf) - 1;
120 -
121 - buffer_strcat(wb, "exec");
122 -
123 - if (!sanitize_command_argument_string(buf, exec, n))
124 - return false;
125 - buffer_sprintf(wb, " '%s'", buf);
126 -
127 - if (!sanitize_command_argument_string(buf, recipient, n))
128 - return false;
129 - buffer_sprintf(wb, " '%s'", buf);
130 -
131 - if (!sanitize_command_argument_string(buf, registry_hostname, n))
132 - return false;
133 - buffer_sprintf(wb, " '%s'", buf);
134 -
135 - buffer_sprintf(wb, " '%u'", unique_id);
136 -
137 - buffer_sprintf(wb, " '%u'", alarm_id);
138 -
139 - buffer_sprintf(wb, " '%u'", alarm_event_id);
140 -
141 - buffer_sprintf(wb, " '%u'", when);
142 -
143 - if (!sanitize_command_argument_string(buf, alert_name, n))
144 - return false;
145 - buffer_sprintf(wb, " '%s'", buf);
146 -
147 - if (!sanitize_command_argument_string(buf, alert_chart_name, n))
148 - return false;
149 - buffer_sprintf(wb, " '%s'", buf);
150 -
151 - if (!sanitize_command_argument_string(buf, new_status, n))
152 - return false;
153 - buffer_sprintf(wb, " '%s'", buf);
154 -
155 - if (!sanitize_command_argument_string(buf, old_status, n))
156 - return false;
157 - buffer_sprintf(wb, " '%s'", buf);
38
159 - buffer_sprintf(wb, " '" NETDATA_DOUBLE_FORMAT_ZERO "'", new_value);
160 -
161 - buffer_sprintf(wb, " '" NETDATA_DOUBLE_FORMAT_ZERO "'", old_value);
162 -
163 - if (!sanitize_command_argument_string(buf, alert_source, n))
164 - return false;
165 - buffer_sprintf(wb, " '%s'", buf);
166 -
167 - buffer_sprintf(wb, " '%u'", duration);
168 -
169 - buffer_sprintf(wb, " '%u'", non_clear_duration);
170 -
171 - if (!sanitize_command_argument_string(buf, alert_units, n))
172 - return false;
173 - buffer_sprintf(wb, " '%s'", buf);
174 -
175 - if (!sanitize_command_argument_string(buf, alert_info, n))
176 - return false;
177 - buffer_sprintf(wb, " '%s'", buf);
178 -
179 - if (!sanitize_command_argument_string(buf, new_value_string, n))
180 - return false;
181 - buffer_sprintf(wb, " '%s'", buf);
182 -
183 - if (!sanitize_command_argument_string(buf, old_value_string, n))
184 - return false;
185 - buffer_sprintf(wb, " '%s'", buf);
186 -
187 - if (!sanitize_command_argument_string(buf, source, n))
188 - return false;
189 - buffer_sprintf(wb, " '%s'", buf);
190 -
191 - if (!sanitize_command_argument_string(buf, error_msg, n))
192 - return false;
193 - buffer_sprintf(wb, " '%s'", buf);
39 +static void health_load_config_defaults(void) {
40 + char filename[FILENAME_MAX + 1];
41
195 - buffer_sprintf(wb, " '%d'", n_warn);
42 + health_globals.config.enabled =
43 + config_get_boolean(CONFIG_SECTION_HEALTH,
44 + "enabled",
45 + health_globals.config.enabled);
46
197 - buffer_sprintf(wb, " '%d'", n_crit);
47 + health_globals.config.stock_enabled =
48 + config_get_boolean(CONFIG_SECTION_HEALTH,
49 + "enable stock health configuration",
50 + health_globals.config.stock_enabled);
51
199 - if (!sanitize_command_argument_string(buf, warn_alarms, n))
200 - return false;
201 - buffer_sprintf(wb, " '%s'", buf);
52 + health_globals.config.use_summary_for_notifications =
53 + config_get_boolean(CONFIG_SECTION_HEALTH,
54 + "use summary for notifications",
55 + health_globals.config.use_summary_for_notifications);
56
203 - if (!sanitize_command_argument_string(buf, crit_alarms, n))
204 - return false;
205 - buffer_sprintf(wb, " '%s'", buf);
57 + health_globals.config.default_warn_repeat_every =
58 + config_get_duration(CONFIG_SECTION_HEALTH, "default repeat warning", "never");
59
207 - if (!sanitize_command_argument_string(buf, classification, n))
208 - return false;
209 - buffer_sprintf(wb, " '%s'", buf);
60 + health_globals.config.default_crit_repeat_every =
61 + config_get_duration(CONFIG_SECTION_HEALTH, "default repeat critical", "never");
62
211 - if (!sanitize_command_argument_string(buf, edit_command, n))
212 - return false;
213 - buffer_sprintf(wb, " '%s'", buf);
63 + health_globals.config.health_log_entries_max =
64 + config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries",
65 + health_globals.config.health_log_entries_max);
66
215 - if (!sanitize_command_argument_string(buf, machine_guid, n))
216 - return false;
217 - buffer_sprintf(wb, " '%s'", buf);
67 + health_globals.config.health_log_history =
68 + config_get_number(CONFIG_SECTION_HEALTH, "health log history", HEALTH_LOG_DEFAULT_HISTORY);
69
219 - char tr_id[UUID_STR_LEN];
220 - uuid_unparse_lower(*transition_id, tr_id);
221 - if (!sanitize_command_argument_string(buf, tr_id, n))
222 - return false;
223 - buffer_sprintf(wb, " '%s'", buf);
70 + snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_primary_plugins_dir);
71 + health_globals.config.default_exec =
72 + string_strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename));
73
225 - if (!sanitize_command_argument_string(buf, summary, n))
226 - return false;
227 - buffer_sprintf(wb, " '%s'", buf);
74 + health_globals.config.enabled_alerts =
75 + simple_pattern_create(config_get(CONFIG_SECTION_HEALTH, "enabled alarms", "*"),
76 + NULL, SIMPLE_PATTERN_EXACT, true);
77
229 - if (!sanitize_command_argument_string(buf, context, n))
230 - return false;
231 - buffer_sprintf(wb, " '%s'", buf);
78 + health_globals.config.run_at_least_every_seconds =
79 + (int)config_get_number(CONFIG_SECTION_HEALTH,
80 + "run at least every seconds",
81 + health_globals.config.run_at_least_every_seconds);
82
233 - if (!sanitize_command_argument_string(buf, component, n))
234 - return false;
235 - buffer_sprintf(wb, " '%s'", buf);
83 + health_globals.config.postpone_alarms_during_hibernation_for_seconds =
84 + config_get_number(CONFIG_SECTION_HEALTH,
85 + "postpone alarms during hibernation for seconds",
86 + health_globals.config.postpone_alarms_during_hibernation_for_seconds);
87
237 - if (!sanitize_command_argument_string(buf, type, n))
238 - return false;
239 - buffer_sprintf(wb, " '%s'", buf);
88 + health_globals.config.default_recipient =
89 + string_strdupz("root");
90
241 - return true;
242 -}
243 -
244 -// the queue of executed alarm notifications that haven't been waited for yet
245 -static struct {
246 - ALARM_ENTRY *head; // oldest
247 - ALARM_ENTRY *tail; // latest
248 -} alarm_notifications_in_progress = {NULL, NULL};
91 + // ------------------------------------------------------------------------
92 + // verify after loading
93
250 -typedef struct active_alerts {
251 - char *name;
252 - time_t last_status_change;
253 - RRDCALC_STATUS status;
254 -} active_alerts_t;
94 + if(health_globals.config.run_at_least_every_seconds < 1)
95 + health_globals.config.run_at_least_every_seconds = 1;
96
256 -static inline void enqueue_alarm_notify_in_progress(ALARM_ENTRY *ae)
257 -{
258 - ae->prev_in_progress = NULL;
259 - ae->next_in_progress = NULL;
97 + if(health_globals.config.health_log_entries_max < HEALTH_LOG_ENTRIES_MIN) {
98 + nd_log(NDLS_DAEMON, NDLP_WARNING,
99 + "Health configuration has invalid max log entries %u, using minimum of %u",
100 + health_globals.config.health_log_entries_max,
101 + HEALTH_LOG_ENTRIES_MIN);
102
261 - if (NULL != alarm_notifications_in_progress.tail) {
262 - ae->prev_in_progress = alarm_notifications_in_progress.tail;
263 - alarm_notifications_in_progress.tail->next_in_progress = ae;
264 - }
265 - if (NULL == alarm_notifications_in_progress.head) {
266 - alarm_notifications_in_progress.head = ae;
103 + health_globals.config.health_log_entries_max = HEALTH_LOG_ENTRIES_MIN;
104 + config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries",
105 + (long)health_globals.config.health_log_entries_max);
106 }
268 - alarm_notifications_in_progress.tail = ae;
107 + else if(health_globals.config.health_log_entries_max > HEALTH_LOG_ENTRIES_MAX) {
108 + nd_log(NDLS_DAEMON, NDLP_WARNING,
109 + "Health configuration has invalid max log entries %u, using maximum of %u",
110 + health_globals.config.health_log_entries_max,
111 + HEALTH_LOG_ENTRIES_MAX);
112
270 -}
113 + health_globals.config.health_log_entries_max = HEALTH_LOG_ENTRIES_MAX;
114 + config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries",
115 + (long)health_globals.config.health_log_entries_max);
116 + }
117
272 -static inline void unlink_alarm_notify_in_progress(ALARM_ENTRY *ae)
273 -{
274 - struct alarm_entry *prev = ae->prev_in_progress;
275 - struct alarm_entry *next = ae->next_in_progress;
118 + if (health_globals.config.health_log_history < HEALTH_LOG_MINIMUM_HISTORY) {
119 + nd_log(NDLS_DAEMON, NDLP_WARNING,
120 + "Health configuration has invalid health log history %u. Using minimum %d",
121 + health_globals.config.health_log_history, HEALTH_LOG_MINIMUM_HISTORY);
122
277 - if (NULL != prev) {
278 - prev->next_in_progress = next;
279 - }
280 - if (NULL != next) {
281 - next->prev_in_progress = prev;
282 - }
283 - if (ae == alarm_notifications_in_progress.head) {
284 - alarm_notifications_in_progress.head = next;
285 - }
286 - if (ae == alarm_notifications_in_progress.tail) {
287 - alarm_notifications_in_progress.tail = prev;
123 + health_globals.config.health_log_history = HEALTH_LOG_MINIMUM_HISTORY;
124 + config_set_number(CONFIG_SECTION_HEALTH, "health log history", health_globals.config.health_log_history);
125 }
126 +
127 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
128 + "Health log history is set to %u seconds (%u days)",
129 + health_globals.config.health_log_history, health_globals.config.health_log_history / 86400);
130 }
290 -// ----------------------------------------------------------------------------
291 -// health initialization
131
293 -/**
294 - * User Config directory
295 - *
296 - * Get the config directory for health and return it.
297 - *
298 - * @return a pointer to the user config directory
299 - */
132 inline char *health_user_config_dir(void) {
133 char buffer[FILENAME_MAX + 1];
134 snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_user_config_dir);
135 return config_get(CONFIG_SECTION_DIRECTORIES, "health config", buffer);
136 }
137
306 -/**
307 - * Stock Config Directory
308 - *
309 - * Get the Stock config directory and return it.
310 - *
311 - * @return a pointer to the stock config directory.
312 - */
138 inline char *health_stock_config_dir(void) {
139 char buffer[FILENAME_MAX + 1];
140 snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_stock_config_dir);
141 return config_get(CONFIG_SECTION_DIRECTORIES, "stock health config", buffer);
142 }
143
319 -/**
320 - * Silencers init
321 - *
322 - * Function used to initialize the silencer structure.
323 - */
324 -static void health_silencers_init(void) {
325 - FILE *fd = fopen(silencers_filename, "r");
326 - if (fd) {
327 - fseek(fd, 0 , SEEK_END);
328 - off_t length = (off_t) ftell(fd);
329 - fseek(fd, 0 , SEEK_SET);
330 -
331 - if (length > 0 && length < HEALTH_SILENCERS_MAX_FILE_LEN) {
332 - char *str = mallocz((length+1)* sizeof(char));
333 - if(str) {
334 - size_t copied;
335 - copied = fread(str, sizeof(char), length, fd);
336 - if (copied == (length* sizeof(char))) {
337 - str[length] = 0x00;
338 - json_parse(str, NULL, health_silencers_json_read_callback);
339 - netdata_log_info("Parsed health silencers file %s", silencers_filename);
340 - } else {
341 - netdata_log_error("Cannot read the data from health silencers file %s", silencers_filename);
342 - }
343 - freez(str);
344 - }
345 - } else {
346 - netdata_log_error("Health silencers file %s has the size %" PRId64 " that is out of range[ 1 , %d ]. Aborting read.",
347 - silencers_filename,
348 - (int64_t)length,
349 - HEALTH_SILENCERS_MAX_FILE_LEN);
350 - }
351 - fclose(fd);
352 - } else {
353 - netdata_log_info("Cannot open the file %s, so Netdata will work with the default health configuration.",
354 - silencers_filename);
355 - }
356 -}
357 -
358 -/**
359 - * Health Init
360 - *
361 - * Initialize the health thread.
362 - */
363 -void health_init(void) {
364 - netdata_log_debug(D_HEALTH, "Health configuration initializing");
365 -
366 - if(!(default_health_enabled = (unsigned int)config_get_boolean(CONFIG_SECTION_HEALTH, "enabled", default_health_enabled))) {
367 - netdata_log_debug(D_HEALTH, "Health is disabled.");
368 - return;
369 - }
370 -
371 - health_silencers_init();
372 -}
373 -
374 -// ----------------------------------------------------------------------------
375 -// re-load health configuration
376 -
377 -/**
378 - * Reload host
379 - *
380 - * Reload configuration for a specific host.
381 - *
382 - * @param host the structure of the host that the function will reload the configuration.
383 - */
384 -static void health_reload_host(RRDHOST *host) {
385 - if(unlikely(!host->health.health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH))
386 - return;
387 -
388 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
389 - "[%s]: Reloading health.",
390 - rrdhost_hostname(host));
391 -
392 - char *user_path = health_user_config_dir();
393 - char *stock_path = health_stock_config_dir();
394 -
395 - // free all running alarms
396 - rrdcalc_delete_all(host);
397 - rrdcalctemplate_delete_all(host);
398 -
399 - // invalidate all previous entries in the alarm log
400 - rw_spinlock_read_lock(&host->health_log.spinlock);
401 - ALARM_ENTRY *t;
402 - for(t = host->health_log.alarms ; t ; t = t->next) {
403 - if(t->new_status != RRDCALC_STATUS_REMOVED)
404 - t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
405 - }
406 - rw_spinlock_read_unlock(&host->health_log.spinlock);
407 -
408 - // reset all thresholds to all charts
409 - RRDSET *st;
410 - rrdset_foreach_read(st, host) {
411 - st->green = NAN;
412 - st->red = NAN;
413 - }
414 - rrdset_foreach_done(st);
415 -
416 - // load the new alarms
417 - health_readdir(host, user_path, stock_path, NULL);
418 -
419 - //Discard alarms with labels that do not apply to host
420 - rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host);
421 -
422 - // link the loaded alarms to their charts
423 - rrdset_foreach_write(st, host) {
424 - rrdcalc_link_matching_alerts_to_rrdset(st);
425 - rrdcalctemplate_link_matching_templates_to_rrdset(st);
426 - }
427 - rrdset_foreach_done(st);
428 -
429 -#ifdef ENABLE_ACLK
430 - if (netdata_cloud_enabled) {
431 - struct aclk_sync_cfg_t *wc = host->aclk_config;
432 - if (likely(wc)) {
433 - wc->alert_queue_removed = SEND_REMOVED_AFTER_HEALTH_LOOPS;
434 - }
435 - }
436 -#endif
437 -}
438 -
439 -/**
440 - * Reload
441 - *
442 - * Reload the host configuration for all hosts.
443 - */
444 -void health_reload(void) {
445 - sql_refresh_hashes();
446 -
447 - RRDHOST *host;
448 - dfe_start_reentrant(rrdhost_root_index, host){
449 - health_reload_host(host);
450 - }
451 - dfe_done(host);
452 -}
453 -
454 -// ----------------------------------------------------------------------------
455 -// health main thread and friends
456 -
457 -static inline RRDCALC_STATUS rrdcalc_value2status(NETDATA_DOUBLE n) {
458 - if(isnan(n) || isinf(n)) return RRDCALC_STATUS_UNDEFINED;
459 - if(n) return RRDCALC_STATUS_RAISED;
460 - return RRDCALC_STATUS_CLEAR;
461 -}
462 -
463 -#define ACTIVE_ALARMS_LIST_EXAMINE 500
464 -#define ACTIVE_ALARMS_LIST 15
465 -
466 -static inline int compare_active_alerts(const void * a, const void * b) {
467 - active_alerts_t *active_alerts_a = (active_alerts_t *)a;
468 - active_alerts_t *active_alerts_b = (active_alerts_t *)b;
469 -
470 - return (int) ( active_alerts_b->last_status_change - active_alerts_a->last_status_change );
471 -}
472 -
473 -static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
474 - ae->flags |= HEALTH_ENTRY_FLAG_PROCESSED;
475 -
476 - if(unlikely(ae->new_status < RRDCALC_STATUS_CLEAR)) {
477 - // do not send notifications for internal statuses
478 - netdata_log_debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (internal statuses)", ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
479 - goto done;
480 - }
481 -
482 - if(unlikely(ae->new_status <= RRDCALC_STATUS_CLEAR && (ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) {
483 - // do not send notifications for disabled statuses
484 -
485 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
486 - "[%s]: Health not sending notification for alarm '%s.%s' status %s (it has no-clear-notification enabled)",
487 - rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
488 -
489 - // mark it as run, so that we will send the same alarm if it happens again
490 - goto done;
491 - }
492 -
493 - // find the previous notification for the same alarm
494 - // which we have run the exec script
495 - // exception: alarms with HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION set
496 - RRDCALC_STATUS last_executed_status = -3;
497 - if(likely(!(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) {
498 - int ret = sql_health_get_last_executed_event(host, ae, &last_executed_status);
499 -
500 - if (likely(ret == 1)) {
501 - // we have executed this alarm notification in the past
502 - if(last_executed_status == ae->new_status && !(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING)) {
503 - // don't send the notification for the same status again
504 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
505 - "[%s]: Health not sending again notification for alarm '%s.%s' status %s",
506 - rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae),
507 - rrdcalc_status2string(ae->new_status));
508 - goto done;
509 - }
510 - }
511 - else {
512 - // we have not executed this alarm notification in the past
513 - // so, don't send CLEAR notifications
514 - if(unlikely(ae->new_status == RRDCALC_STATUS_CLEAR)) {
515 - if((!(ae->flags & HEALTH_ENTRY_RUN_ONCE)) || (ae->flags & HEALTH_ENTRY_RUN_ONCE && ae->old_status < RRDCALC_STATUS_RAISED) ) {
516 - netdata_log_debug(D_HEALTH, "Health not sending notification for first initialization of alarm '%s.%s' status %s"
517 - , ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
518 - goto done;
519 - }
520 - }
521 - }
522 - }
523 -
524 - // Check if alarm notifications are silenced
525 - if (ae->flags & HEALTH_ENTRY_FLAG_SILENCED) {
526 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
527 - "[%s]: Health not sending notification for alarm '%s.%s' status %s "
528 - "(command API has disabled notifications)",
529 - rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
530 - goto done;
531 - }
532 -
533 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
534 - "[%s]: Sending notification for alarm '%s.%s' status %s.",
535 - rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
536 -
537 - const char *exec = (ae->exec) ? ae_exec(ae) : string2str(host->health.health_default_exec);
538 - const char *recipient = (ae->recipient) ? ae_recipient(ae) : string2str(host->health.health_default_recipient);
539 -
540 - int n_warn=0, n_crit=0;
541 - RRDCALC *rc;
542 - EVAL_EXPRESSION *expr=NULL;
543 - BUFFER *warn_alarms, *crit_alarms;
544 - active_alerts_t *active_alerts = callocz(ACTIVE_ALARMS_LIST_EXAMINE, sizeof(active_alerts_t));
545 -
546 - warn_alarms = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE, &netdata_buffers_statistics.buffers_health);
547 - crit_alarms = buffer_create(NETDATA_WEB_RESPONSE_INITIAL_SIZE, &netdata_buffers_statistics.buffers_health);
548 -
549 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
550 - if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec))
551 - continue;
552 -
553 - if(unlikely((n_warn + n_crit) >= ACTIVE_ALARMS_LIST_EXAMINE))
554 - break;
555 -
556 - if (unlikely(rc->status == RRDCALC_STATUS_WARNING)) {
557 - if (likely(ae->alarm_id != rc->id) || likely(ae->alarm_event_id != rc->next_event_id - 1)) {
558 - active_alerts[n_warn+n_crit].name = (char *)rrdcalc_name(rc);
559 - active_alerts[n_warn+n_crit].last_status_change = rc->last_status_change;
560 - active_alerts[n_warn+n_crit].status = rc->status;
561 - n_warn++;
562 - } else if (ae->alarm_id == rc->id)
563 - expr = rc->warning;
564 - } else if (unlikely(rc->status == RRDCALC_STATUS_CRITICAL)) {
565 - if (likely(ae->alarm_id != rc->id) || likely(ae->alarm_event_id != rc->next_event_id - 1)) {
566 - active_alerts[n_warn+n_crit].name = (char *)rrdcalc_name(rc);
567 - active_alerts[n_warn+n_crit].last_status_change = rc->last_status_change;
568 - active_alerts[n_warn+n_crit].status = rc->status;
569 - n_crit++;
570 - } else if (ae->alarm_id == rc->id)
571 - expr = rc->critical;
572 - } else if (unlikely(rc->status == RRDCALC_STATUS_CLEAR)) {
573 - if (ae->alarm_id == rc->id)
574 - expr = rc->warning;
575 - }
576 - }
577 - foreach_rrdcalc_in_rrdhost_done(rc);
578 -
579 - if (n_warn+n_crit>1)
580 - qsort (active_alerts, n_warn+n_crit, sizeof(active_alerts_t), compare_active_alerts);
581 -
582 - int count_w = 0, count_c = 0;
583 - while (count_w + count_c < n_warn + n_crit && count_w + count_c < ACTIVE_ALARMS_LIST) {
584 - if (active_alerts[count_w+count_c].status == RRDCALC_STATUS_WARNING) {
585 - if (count_w)
586 - buffer_strcat(warn_alarms, ",");
587 - buffer_strcat(warn_alarms, active_alerts[count_w+count_c].name);
588 - buffer_strcat(warn_alarms, "=");
589 - buffer_snprintf(warn_alarms, 11, "%"PRId64"", (int64_t)active_alerts[count_w+count_c].last_status_change);
590 - count_w++;
591 - }
592 - else if (active_alerts[count_w+count_c].status == RRDCALC_STATUS_CRITICAL) {
593 - if (count_c)
594 - buffer_strcat(crit_alarms, ",");
595 - buffer_strcat(crit_alarms, active_alerts[count_w+count_c].name);
596 - buffer_strcat(crit_alarms, "=");
597 - buffer_snprintf(crit_alarms, 11, "%"PRId64"", (int64_t)active_alerts[count_w+count_c].last_status_change);
598 - count_c++;
599 - }
600 - }
601 -
602 - char *edit_command = ae->source ? health_edit_command_from_source(ae_source(ae)) : strdupz("UNKNOWN=0=UNKNOWN");
603 -
604 - BUFFER *wb = buffer_create(8192, &netdata_buffers_statistics.buffers_health);
605 - bool ok = prepare_command(wb,
606 - exec,
607 - recipient,
608 - rrdhost_registry_hostname(host),
609 - ae->unique_id,
610 - ae->alarm_id,
611 - ae->alarm_event_id,
612 - (unsigned long)ae->when,
613 - ae_name(ae),
614 - ae->chart?ae_chart_id(ae):"NOCHART",
615 - rrdcalc_status2string(ae->new_status),
616 - rrdcalc_status2string(ae->old_status),
617 - ae->new_value,
618 - ae->old_value,
619 - ae->source?ae_source(ae):"UNKNOWN",
620 - (uint32_t)ae->duration,
621 - (ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING && ae->new_status >= RRDCALC_STATUS_WARNING) ? (uint32_t)ae->duration : (uint32_t)ae->non_clear_duration,
622 - ae_units(ae),
623 - ae_info(ae),
624 - ae_new_value_string(ae),
625 - ae_old_value_string(ae),
626 - (expr && expr->source)?expr->source:"NOSOURCE",
627 - (expr && expr->error_msg)?buffer_tostring(expr->error_msg):"NOERRMSG",
628 - n_warn,
629 - n_crit,
630 - buffer_tostring(warn_alarms),
631 - buffer_tostring(crit_alarms),
632 - ae->classification?ae_classification(ae):"Unknown",
633 - edit_command,
634 - host->machine_guid,
635 - &ae->transition_id,
636 - host->health.use_summary_for_notifications && ae->summary?ae_summary(ae):ae_name(ae),
637 - string2str(ae->chart_context),
638 - string2str(ae->component),
639 - string2str(ae->type)
640 - );
641 -
642 - const char *command_to_run = buffer_tostring(wb);
643 - if (ok) {
644 - ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN;
645 - ae->exec_run_timestamp = now_realtime_sec(); /* will be updated by real time after spawning */
646 -
647 - netdata_log_debug(D_HEALTH, "executing command '%s'", command_to_run);
648 - ae->flags |= HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
649 - ae->exec_spawn_serial = spawn_enq_cmd(command_to_run);
650 - enqueue_alarm_notify_in_progress(ae);
651 - health_alarm_log_save(host, ae);
652 - } else {
653 - netdata_log_error("Failed to format command arguments");
654 - }
655 -
656 - buffer_free(wb);
657 - freez(edit_command);
658 - buffer_free(warn_alarms);
659 - buffer_free(crit_alarms);
660 - freez(active_alerts);
661 -
662 - return; //health_alarm_wait_for_execution
663 -done:
664 - health_alarm_log_save(host, ae);
665 -}
666 -
667 -static inline void health_alarm_wait_for_execution(ALARM_ENTRY *ae) {
668 - if (!(ae->flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS))
669 - return;
670 -
671 - spawn_wait_cmd(ae->exec_spawn_serial, &ae->exec_code, &ae->exec_run_timestamp);
672 - netdata_log_debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
673 - ae->flags &= ~HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
674 -
675 - if(ae->exec_code != 0)
676 - ae->flags |= HEALTH_ENTRY_FLAG_EXEC_FAILED;
677 -
678 - unlink_alarm_notify_in_progress(ae);
679 -}
680 -
681 -static inline void health_process_notifications(RRDHOST *host, ALARM_ENTRY *ae) {
682 - netdata_log_debug(D_HEALTH, "Health alarm '%s.%s' = " NETDATA_DOUBLE_FORMAT_AUTO " - changed status from %s to %s",
683 - ae->chart?ae_chart_id(ae):"NOCHART", ae_name(ae),
684 - ae->new_value,
685 - rrdcalc_status2string(ae->old_status),
686 - rrdcalc_status2string(ae->new_status)
687 - );
688 -
689 - health_alarm_execute(host, ae);
690 -}
691 -
692 -static inline void health_alarm_log_process(RRDHOST *host) {
693 - uint32_t first_waiting = (host->health_log.alarms)?host->health_log.alarms->unique_id:0;
694 - time_t now = now_realtime_sec();
695 -
696 - rw_spinlock_read_lock(&host->health_log.spinlock);
697 -
698 - ALARM_ENTRY *ae;
699 - for(ae = host->health_log.alarms; ae && ae->unique_id >= host->health_last_processed_id; ae = ae->next) {
700 - if(unlikely(
701 - !(ae->flags & HEALTH_ENTRY_FLAG_PROCESSED) &&
702 - !(ae->flags & HEALTH_ENTRY_FLAG_UPDATED)
703 - )) {
704 - if(unlikely(ae->unique_id < first_waiting))
705 - first_waiting = ae->unique_id;
706 -
707 - if(likely(now >= ae->delay_up_to_timestamp))
708 - health_process_notifications(host, ae);
709 - }
710 - }
711 -
712 - rw_spinlock_read_unlock(&host->health_log.spinlock);
713 -
714 - // remember this for the next iteration
715 - host->health_last_processed_id = first_waiting;
716 -
717 - //delete those that are updated, no in progress execution, and is not repeating
718 - rw_spinlock_write_lock(&host->health_log.spinlock);
719 -
720 - ALARM_ENTRY *prev = NULL, *next = NULL;
721 - for(ae = host->health_log.alarms; ae ; ae = next) {
722 - next = ae->next; // set it here, for the next iteration
723 -
724 - if((likely(!(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING)) &&
725 - (ae->flags & HEALTH_ENTRY_FLAG_UPDATED) &&
726 - (ae->flags & HEALTH_ENTRY_FLAG_SAVED) &&
727 - !(ae->flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS))
728 - ||
729 - ((ae->new_status == RRDCALC_STATUS_REMOVED) &&
730 - (ae->flags & HEALTH_ENTRY_FLAG_SAVED) &&
731 - (ae->when + 86400 < now_realtime_sec())))
732 - {
733 -
734 - if(host->health_log.alarms == ae) {
735 - host->health_log.alarms = next;
736 - // prev is also NULL here
737 - }
738 - else {
739 - prev->next = next;
740 - // prev should not be touched here - we need it for the next iteration
741 - // because we may have to also remove the next item
742 - }
743 -
744 - health_alarm_log_free_one_nochecks_nounlink(ae);
745 - }
746 - else
747 - prev = ae;
748 - }
749 -
750 - rw_spinlock_write_unlock(&host->health_log.spinlock);
751 -}
752 -
753 -static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
754 - if(unlikely(!rc->rrdset)) {
755 - netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rrdcalc_chart_name(rc), rrdcalc_name(rc));
756 - return 0;
757 - }
758 -
759 - if(unlikely(rc->next_update > now)) {
760 - if (unlikely(*next_run > rc->next_update)) {
761 - // update the next_run time of the main loop
762 - // to run this alarm precisely the time required
763 - *next_run = rc->next_update;
764 - }
765 -
766 - netdata_log_debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rrdcalc_chart_name(rc), rrdcalc_name(rc), (int) (rc->next_update - now));
767 - return 0;
768 - }
769 -
770 - if(unlikely(!rc->update_every)) {
771 - netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rrdcalc_chart_name(rc), rrdcalc_name(rc));
772 - return 0;
773 - }
774 -
775 - if(unlikely(rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE))) {
776 - netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as obsolete", rrdcalc_chart_name(rc), rrdcalc_name(rc));
777 - return 0;
778 - }
779 -
780 - if(unlikely(!rc->rrdset->last_collected_time.tv_sec || rc->rrdset->counter_done < 2)) {
781 - netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not fully collected yet.", rrdcalc_chart_name(rc), rrdcalc_name(rc));
782 - return 0;
783 - }
784 -
785 - int update_every = rc->rrdset->update_every;
786 - time_t first = rrdset_first_entry_s(rc->rrdset);
787 - time_t last = rrdset_last_entry_s(rc->rrdset);
788 -
789 - if(unlikely(now + update_every < first /* || now - update_every > last */)) {
790 - netdata_log_debug(D_HEALTH
791 - , "Health not examining alarm '%s.%s' yet (wanted time is out of bounds - we need %lu but got %lu - %lu)."
792 - , rrdcalc_chart_name(rc), rrdcalc_name(rc), (unsigned long) now, (unsigned long) first
793 - , (unsigned long) last);
794 - return 0;
795 - }
796 -
797 - if(RRDCALC_HAS_DB_LOOKUP(rc)) {
798 - time_t needed = now + rc->before + rc->after;
799 -
800 - if(needed + update_every < first || needed - update_every > last) {
801 - netdata_log_debug(D_HEALTH
802 - , "Health not examining alarm '%s.%s' yet (not enough data yet - we need %lu but got %lu - %lu)."
803 - , rrdcalc_chart_name(rc), rrdcalc_name(rc), (unsigned long) needed, (unsigned long) first
804 - , (unsigned long) last);
805 - return 0;
806 - }
807 - }
808 -
809 - return 1;
810 -}
811 -
812 -static inline int check_if_resumed_from_suspension(void) {
813 - static usec_t last_realtime = 0, last_monotonic = 0;
814 - usec_t realtime = now_realtime_usec(), monotonic = now_monotonic_usec();
815 - int ret = 0;
816 -
817 - // detect if monotonic and realtime have twice the difference
818 - // in which case we assume the system was just waken from hibernation
819 -
820 - if(last_realtime && last_monotonic && realtime - last_realtime > 2 * (monotonic - last_monotonic))
821 - ret = 1;
822 -
823 - last_realtime = realtime;
824 - last_monotonic = monotonic;
825 -
826 - return ret;
827 -}
828 -
829 -static void health_main_cleanup(void *ptr) {
830 - worker_unregister();
831 -
832 - struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
833 - static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
834 - netdata_log_info("cleaning up...");
835 - static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
836 -
837 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
838 - "Health thread ended.");
839 -}
840 -
841 -static void initialize_health(RRDHOST *host)
842 -{
843 - if(!host->health.health_enabled ||
844 - rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH) ||
845 - !service_running(SERVICE_HEALTH))
846 - return;
847 -
848 - rrdhost_flag_set(host, RRDHOST_FLAG_INITIALIZED_HEALTH);
849 -
850 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
851 - "[%s]: Initializing health.",
852 - rrdhost_hostname(host));
853 -
854 - host->health.health_default_warn_repeat_every = config_get_duration(CONFIG_SECTION_HEALTH, "default repeat warning", "never");
855 - host->health.health_default_crit_repeat_every = config_get_duration(CONFIG_SECTION_HEALTH, "default repeat critical", "never");
856 -
857 - host->health_log.next_log_id = 1;
858 - host->health_log.next_alarm_id = 1;
859 - host->health_log.max = 1000;
860 - host->health_log.next_log_id = (uint32_t)now_realtime_sec();
861 - host->health_log.next_alarm_id = 0;
862 -
863 - long n = config_get_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", host->health_log.max);
864 - if(n < 10) {
865 - nd_log(NDLS_DAEMON, NDLP_WARNING,
866 - "Host '%s': health configuration has invalid max log entries %ld. "
867 - "Using default %u",
868 - rrdhost_hostname(host), n, host->health_log.max);
869 -
870 - config_set_number(CONFIG_SECTION_HEALTH, "in memory max health log entries", (long)host->health_log.max);
871 - }
872 - else
873 - host->health_log.max = (unsigned int)n;
874 -
875 - uint32_t m = config_get_number(CONFIG_SECTION_HEALTH, "health log history", HEALTH_LOG_DEFAULT_HISTORY);
876 - if (m < HEALTH_LOG_MINIMUM_HISTORY) {
877 - nd_log(NDLS_DAEMON, NDLP_WARNING,
878 - "Host '%s': health configuration has invalid health log history %u. "
879 - "Using minimum %d",
880 - rrdhost_hostname(host), m, HEALTH_LOG_MINIMUM_HISTORY);
881 -
882 - config_set_number(CONFIG_SECTION_HEALTH, "health log history", HEALTH_LOG_MINIMUM_HISTORY);
883 - m = HEALTH_LOG_MINIMUM_HISTORY;
884 - }
885 -
886 - //default health log history is 5 days and not less than a day
887 - if (host->health_log.health_log_history) {
888 - if (host->health_log.health_log_history < HEALTH_LOG_MINIMUM_HISTORY)
889 - host->health_log.health_log_history = HEALTH_LOG_MINIMUM_HISTORY;
890 - } else
891 - host->health_log.health_log_history = m;
892 -
893 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
894 - "[%s]: Health log history is set to %u seconds (%u days)",
895 - rrdhost_hostname(host), host->health_log.health_log_history, host->health_log.health_log_history / 86400);
896 -
897 - conf_enabled_alarms = simple_pattern_create(config_get(CONFIG_SECTION_HEALTH, "enabled alarms", "*"), NULL,
898 - SIMPLE_PATTERN_EXACT, true);
899 -
900 - rw_spinlock_init(&host->health_log.spinlock);
901 -
902 - char filename[FILENAME_MAX + 1];
903 -
904 - snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_primary_plugins_dir);
905 - host->health.health_default_exec = string_strdupz(config_get(CONFIG_SECTION_HEALTH, "script to execute on alarm", filename));
906 - host->health.health_default_recipient = string_strdupz("root");
907 - host->health.use_summary_for_notifications = config_get_boolean(CONFIG_SECTION_HEALTH, "use summary for notifications", CONFIG_BOOLEAN_YES);
908 -
909 - sql_health_alarm_log_load(host);
910 -
911 - // ------------------------------------------------------------------------
912 - // load health configuration
913 -
914 - health_readdir(host, health_user_config_dir(), health_stock_config_dir(), NULL);
915 -
916 - // link the loaded alarms to their charts
917 - RRDSET *st;
918 - rrdset_foreach_reentrant(st, host) {
919 - rrdcalc_link_matching_alerts_to_rrdset(st);
920 - rrdcalctemplate_link_matching_templates_to_rrdset(st);
921 - }
922 - rrdset_foreach_done(st);
923 -
924 - //Discard alarms with labels that do not apply to host
925 - rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host);
926 -}
927 -
928 -static void health_sleep(time_t next_run, unsigned int loop __maybe_unused) {
929 - time_t now = now_realtime_sec();
930 - if(now < next_run) {
931 - worker_is_idle();
932 - netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now));
933 - while (now < next_run && service_running(SERVICE_HEALTH)) {
934 - sleep_usec(USEC_PER_SEC);
935 - now = now_realtime_sec();
936 - }
937 - }
938 - else {
939 - netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop);
940 - }
941 -}
144 +void health_plugin_init(void) {
145 + spinlock_lock(&health_globals.initialization.spinlock);
146
943 -static SILENCE_TYPE check_silenced(RRDCALC *rc, const char *host)
944 -{
945 - SILENCER *s;
147 + if(health_globals.initialization.done)
148 + goto cleanup;
149
947 - for (s = silencers->silencers; s!=NULL; s=s->next){
948 - if (
949 - (!s->alarms_pattern || (rc->name && s->alarms_pattern && simple_pattern_matches_string(s->alarms_pattern, rc->name))) &&
950 - (!s->contexts_pattern || (rc->rrdset && rc->rrdset->context && s->contexts_pattern && simple_pattern_matches_string(s->contexts_pattern, rc->rrdset->context))) &&
951 - (!s->hosts_pattern || (host && s->hosts_pattern && simple_pattern_matches(s->hosts_pattern, host))) &&
952 - (!s->charts_pattern || (rc->chart && s->charts_pattern && simple_pattern_matches_string(s->charts_pattern, rc->chart)))
953 - ) {
954 - netdata_log_debug(D_HEALTH, "Alarm matches command API silence entry %s:%s:%s:%s", s->alarms,s->charts, s->contexts, s->hosts);
955 - if (unlikely(silencers->stype == STYPE_NONE)) {
956 - netdata_log_debug(D_HEALTH, "Alarm %s matched a silence entry, but no SILENCE or DISABLE command was issued via the command API. The match has no effect.", rrdcalc_name(rc));
957 - } else {
958 - netdata_log_debug(D_HEALTH, "Alarm %s via the command API - name:%s context:%s chart:%s host:%s"
959 - , (silencers->stype == STYPE_DISABLE_ALARMS)?"Disabled":"Silenced"
960 - , rrdcalc_name(rc)
961 - , (rc->rrdset)?rrdset_context(rc->rrdset):""
962 - , rrdcalc_chart_name(rc)
963 - , host
964 - );
965 - }
966 - return silencers->stype;
967 - }
968 - }
969 - return STYPE_NONE;
970 -}
150 + health_globals.initialization.done = true;
151
972 -/**
973 - * Update Disabled Silenced
974 - *
975 - * Update the variable rrdcalc_flags of the structure RRDCALC according with the values of the host structure
976 - *
977 - * @param host structure that contains information about the host monitored.
978 - * @param rc structure with information about the alarm
979 - *
980 - * @return It returns 1 case rrdcalc_flags is DISABLED or 0 otherwise
981 - */
982 -static int update_disabled_silenced(RRDHOST *host, RRDCALC *rc) {
983 - uint32_t rrdcalc_flags_old = rc->run_flags;
984 - // Clear the flags
985 - rc->run_flags &= ~(RRDCALC_FLAG_DISABLED | RRDCALC_FLAG_SILENCED);
986 - if (unlikely(silencers->all_alarms)) {
987 - if (silencers->stype == STYPE_DISABLE_ALARMS) rc->run_flags |= RRDCALC_FLAG_DISABLED;
988 - else if (silencers->stype == STYPE_SILENCE_NOTIFICATIONS) rc->run_flags |= RRDCALC_FLAG_SILENCED;
989 - } else {
990 - SILENCE_TYPE st = check_silenced(rc, rrdhost_hostname(host));
991 - if (st == STYPE_DISABLE_ALARMS) rc->run_flags |= RRDCALC_FLAG_DISABLED;
992 - else if (st == STYPE_SILENCE_NOTIFICATIONS) rc->run_flags |= RRDCALC_FLAG_SILENCED;
993 - }
152 + health_init_prototypes();
153 + health_load_config_defaults();
154
995 - if (rrdcalc_flags_old != rc->run_flags) {
996 - netdata_log_info(
997 - "Alarm silencing changed for host '%s' alarm '%s': Disabled %s->%s Silenced %s->%s",
998 - rrdhost_hostname(host),
999 - rrdcalc_name(rc),
1000 - (rrdcalc_flags_old & RRDCALC_FLAG_DISABLED) ? "true" : "false",
1001 - (rc->run_flags & RRDCALC_FLAG_DISABLED) ? "true" : "false",
1002 - (rrdcalc_flags_old & RRDCALC_FLAG_SILENCED) ? "true" : "false",
1003 - (rc->run_flags & RRDCALC_FLAG_SILENCED) ? "true" : "false");
1004 - }
1005 - if (rc->run_flags & RRDCALC_FLAG_DISABLED)
1006 - return 1;
1007 - else
1008 - return 0;
1009 -}
155 + if(!health_plugin_enabled())
156 + goto cleanup;
157
1011 -static void sql_health_postpone_queue_removed(RRDHOST *host __maybe_unused) {
1012 -#ifdef ENABLE_ACLK
1013 - if (netdata_cloud_enabled) {
1014 - struct aclk_sync_cfg_t *wc = host->aclk_config;
1015 - if (unlikely(!wc)) {
1016 - return;
1017 - }
158 + health_reload_prototypes();
159 + health_silencers_init();
160
1019 - if (wc->alert_queue_removed >= 1) {
1020 - wc->alert_queue_removed+=6;
1021 - }
1022 - }
1023 -#endif
161 +cleanup:
162 + spinlock_unlock(&health_globals.initialization.spinlock);
163 }
164
1026 -static void health_execute_delayed_initializations(RRDHOST *host) {
1027 - RRDSET *st;
1028 - bool must_postpone = false;
1029 -
1030 - if (!rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION)) return;
1031 - rrdhost_flag_clear(host, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION);
1032 -
1033 - rrdset_foreach_reentrant(st, host) {
1034 - if(!rrdset_flag_check(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION)) continue;
1035 - rrdset_flag_clear(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION);
1036 -
1037 - worker_is_busy(WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET);
1038 -
1039 - rrdcalc_link_matching_alerts_to_rrdset(st);
1040 - rrdcalctemplate_link_matching_templates_to_rrdset(st);
1041 -
1042 - RRDDIM *rd;
1043 - rrddim_foreach_read(rd, st) {
1044 - if(!rrddim_flag_check(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION)) continue;
1045 - rrddim_flag_clear(rd, RRDDIM_FLAG_PENDING_HEALTH_INITIALIZATION);
1046 -
1047 - worker_is_busy(WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM);
1048 -
1049 - RRDCALCTEMPLATE *rt;
1050 - foreach_rrdcalctemplate_read(host, rt) {
1051 - if(!rt->foreach_dimension_pattern)
1052 - continue;
1053 -
1054 - if(rrdcalctemplate_check_rrdset_conditions(rt, st, host)) {
1055 - rrdcalctemplate_check_rrddim_conditions_and_link(rt, st, rd, host);
1056 - }
1057 - }
1058 - foreach_rrdcalctemplate_done(rt);
1059 -
1060 - if (health_variable_check(health_rrdvars, st, rd) || rrdset_flag_check(st, RRDSET_FLAG_HAS_RRDCALC_LINKED))
1061 - rrdvar_store_for_chart(host, st);
1062 - }
1063 - rrddim_foreach_done(rd);
1064 - must_postpone = true;
1065 - }
1066 - rrdset_foreach_done(st);
1067 - if (must_postpone)
1068 - sql_health_postpone_queue_removed(host);
165 +void health_plugin_destroy(void) {
166 + ;
167 }
168
1071 -/**
1072 - * Health Main
1073 - *
1074 - * The main thread of the health system. In this function all the alarms will be processed.
1075 - *
1076 - * @param ptr is a pointer to the netdata_static_thread structure.
1077 - *
1078 - * @return It always returns NULL
1079 - */
1080 -
1081 -void *health_main(void *ptr) {
1082 - worker_register("HEALTH");
1083 - worker_register_job_name(WORKER_HEALTH_JOB_RRD_LOCK, "rrd lock");
1084 - worker_register_job_name(WORKER_HEALTH_JOB_HOST_LOCK, "host lock");
1085 - worker_register_job_name(WORKER_HEALTH_JOB_DB_QUERY, "db lookup");
1086 - worker_register_job_name(WORKER_HEALTH_JOB_CALC_EVAL, "calc eval");
1087 - worker_register_job_name(WORKER_HEALTH_JOB_WARNING_EVAL, "warning eval");
1088 - worker_register_job_name(WORKER_HEALTH_JOB_CRITICAL_EVAL, "critical eval");
1089 - worker_register_job_name(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY, "alarm log entry");
1090 - worker_register_job_name(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS, "alarm log process");
1091 - worker_register_job_name(WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET, "rrdset init");
1092 - worker_register_job_name(WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM, "rrddim init");
1093 -
1094 - netdata_thread_cleanup_push(health_main_cleanup, ptr);
1095 -
1096 - int min_run_every = (int)config_get_number(CONFIG_SECTION_HEALTH, "run at least every seconds", 10);
1097 - if(min_run_every < 1) min_run_every = 1;
1098 -
1099 - time_t hibernation_delay = config_get_number(CONFIG_SECTION_HEALTH, "postpone alarms during hibernation for seconds", 60);
1100 -
1101 - bool health_running_logged = false;
1102 -
1103 - rrdcalc_delete_alerts_not_matching_host_labels_from_all_hosts();
1104 -
1105 - unsigned int loop = 0;
1106 -
1107 - while(service_running(SERVICE_HEALTH)) {
1108 - loop++;
1109 - netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
1110 -
1111 - time_t now = now_realtime_sec();
1112 - int runnable = 0, apply_hibernation_delay = 0;
1113 - time_t next_run = now + min_run_every;
1114 - RRDCALC *rc;
1115 - RRDHOST *host;
1116 -
1117 - if (unlikely(check_if_resumed_from_suspension())) {
1118 - apply_hibernation_delay = 1;
1119 -
1120 - nd_log(NDLS_DAEMON, NDLP_NOTICE,
1121 - "Postponing alarm checks for %"PRId64" seconds, "
1122 - "because it seems that the system was just resumed from suspension.",
1123 - (int64_t)hibernation_delay);
1124 - }
1125 -
1126 - if (unlikely(silencers->all_alarms && silencers->stype == STYPE_DISABLE_ALARMS)) {
1127 - static int logged=0;
1128 - if (!logged) {
1129 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1130 - "Skipping health checks, because all alarms are disabled via a %s command.",
1131 - HEALTH_CMDAPI_CMD_DISABLEALL);
1132 - logged = 1;
1133 - }
1134 - }
1135 -
1136 - worker_is_busy(WORKER_HEALTH_JOB_RRD_LOCK);
1137 - dfe_start_reentrant(rrdhost_root_index, host) {
1138 -
1139 - if(unlikely(!service_running(SERVICE_HEALTH)))
1140 - break;
1141 -
1142 - if (unlikely(!host->health.health_enabled))
1143 - continue;
1144 -
1145 - if (unlikely(!rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)))
1146 - initialize_health(host);
1147 -
1148 - health_execute_delayed_initializations(host);
1149 -
1150 - rrdcalc_delete_alerts_not_matching_host_labels_from_this_host(host);
1151 -
1152 - if (unlikely(apply_hibernation_delay)) {
1153 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1154 - "[%s]: Postponing health checks for %"PRId64" seconds.",
1155 - rrdhost_hostname(host),
1156 - (int64_t)hibernation_delay);
1157 -
1158 - host->health.health_delay_up_to = now + hibernation_delay;
1159 - }
1160 -
1161 - if (unlikely(host->health.health_delay_up_to)) {
1162 - if (unlikely(now < host->health.health_delay_up_to)) {
1163 - continue;
1164 - }
1165 -
1166 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1167 - "[%s]: Resuming health checks after delay.",
1168 - rrdhost_hostname(host));
1169 -
1170 - host->health.health_delay_up_to = 0;
1171 - }
1172 -
1173 - // wait until cleanup of obsolete charts on children is complete
1174 - if (host != localhost) {
1175 - if (unlikely(host->trigger_chart_obsoletion_check == 1)) {
1176 -
1177 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1178 - "[%s]: Waiting for chart obsoletion check.",
1179 - rrdhost_hostname(host));
1180 -
1181 - continue;
1182 - }
1183 - }
1184 -
1185 - if (!health_running_logged) {
1186 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1187 - "[%s]: Health is running.",
1188 - rrdhost_hostname(host));
1189 -
1190 - health_running_logged = true;
1191 - }
1192 -
1193 - worker_is_busy(WORKER_HEALTH_JOB_HOST_LOCK);
1194 -
1195 - // the first loop is to lookup values from the db
1196 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
1197 -
1198 - if(unlikely(!service_running(SERVICE_HEALTH)))
1199 - break;
1200 -
1201 - rrdcalc_update_info_using_rrdset_labels(rc);
1202 -
1203 - if (update_disabled_silenced(host, rc))
1204 - continue;
1205 -
1206 - // create an alert removed event if the chart is obsolete and
1207 - // has stopped being collected for 60 seconds
1208 - if (unlikely(rc->rrdset && rc->status != RRDCALC_STATUS_REMOVED &&
1209 - rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE) &&
1210 - now > (rc->rrdset->last_collected_time.tv_sec + 60))) {
1211 - if (!rrdcalc_isrepeating(rc)) {
1212 - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
1213 - time_t now = now_realtime_sec();
1214 -
1215 - ALARM_ENTRY *ae = health_create_alarm_entry(
1216 - host,
1217 - rc->id,
1218 - rc->next_event_id++,
1219 - rc->config_hash_id,
1220 - now,
1221 - rc->name,
1222 - rc->rrdset->id,
1223 - rc->rrdset->context,
1224 - rc->rrdset->name,
1225 - rc->classification,
1226 - rc->component,
1227 - rc->type,
1228 - rc->exec,
1229 - rc->recipient,
1230 - now - rc->last_status_change,
1231 - rc->value,
1232 - NAN,
1233 - rc->status,
1234 - RRDCALC_STATUS_REMOVED,
1235 - rc->source,
1236 - rc->units,
1237 - rc->summary,
1238 - rc->info,
1239 - 0,
1240 - rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0);
1241 -
1242 - if (ae) {
1243 - health_log_alert(host, ae);
1244 - health_alarm_log_add_entry(host, ae);
1245 - rc->old_status = rc->status;
1246 - rc->status = RRDCALC_STATUS_REMOVED;
1247 - rc->last_status_change = now;
1248 - rc->last_status_change_value = rc->value;
1249 - rc->last_updated = now;
1250 - rc->value = NAN;
1251 -
1252 -#ifdef ENABLE_ACLK
1253 - if (netdata_cloud_enabled)
1254 - sql_queue_alarm_to_aclk(host, ae, true);
1255 -#endif
1256 - }
1257 - }
1258 - }
1259 -
1260 - if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) {
1261 - if (unlikely(rc->run_flags & RRDCALC_FLAG_RUNNABLE))
1262 - rc->run_flags &= ~RRDCALC_FLAG_RUNNABLE;
1263 - continue;
1264 - }
1265 -
1266 - runnable++;
1267 - rc->old_value = rc->value;
1268 - rc->run_flags |= RRDCALC_FLAG_RUNNABLE;
1269 -
1270 - // ------------------------------------------------------------
1271 - // if there is database lookup, do it
1272 -
1273 - if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) {
1274 - worker_is_busy(WORKER_HEALTH_JOB_DB_QUERY);
1275 -
1276 - /* time_t old_db_timestamp = rc->db_before; */
1277 - int value_is_null = 0;
1278 -
1279 - int ret = rrdset2value_api_v1(rc->rrdset, NULL, &rc->value, rrdcalc_dimensions(rc), 1,
1280 - rc->after, rc->before, rc->group, NULL,
1281 - 0, rc->options | RRDR_OPTION_SELECTED_TIER,
1282 - &rc->db_after,&rc->db_before,
1283 - NULL, NULL, NULL,
1284 - &value_is_null, NULL, 0, 0,
1285 - QUERY_SOURCE_HEALTH, STORAGE_PRIORITY_LOW);
1286 -
1287 - if (unlikely(ret != 200)) {
1288 - // database lookup failed
1289 - rc->value = NAN;
1290 - rc->run_flags |= RRDCALC_FLAG_DB_ERROR;
1291 -
1292 - netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup returned error %d",
1293 - rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), ret
1294 - );
1295 - } else
1296 - rc->run_flags &= ~RRDCALC_FLAG_DB_ERROR;
1297 -
1298 - if (unlikely(value_is_null)) {
1299 - // collected value is null
1300 - rc->value = NAN;
1301 - rc->run_flags |= RRDCALC_FLAG_DB_NAN;
1302 -
1303 - netdata_log_debug(D_HEALTH,
1304 - "Health on host '%s', alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
1305 - rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc)
1306 - );
1307 - } else
1308 - rc->run_flags &= ~RRDCALC_FLAG_DB_NAN;
1309 -
1310 - netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup gave value " NETDATA_DOUBLE_FORMAT,
1311 - rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), rc->value
1312 - );
1313 - }
1314 -
1315 - // ------------------------------------------------------------
1316 - // if there is calculation expression, run it
1317 -
1318 - if (unlikely(rc->calculation)) {
1319 - worker_is_busy(WORKER_HEALTH_JOB_CALC_EVAL);
1320 -
1321 - if (unlikely(!expression_evaluate(rc->calculation))) {
1322 - // calculation failed
1323 - rc->value = NAN;
1324 - rc->run_flags |= RRDCALC_FLAG_CALC_ERROR;
1325 -
1326 - netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' failed: %s",
1327 - rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
1328 - rc->calculation->parsed_as, buffer_tostring(rc->calculation->error_msg)
1329 - );
1330 - } else {
1331 - rc->run_flags &= ~RRDCALC_FLAG_CALC_ERROR;
1332 -
1333 - netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' gave value "
1334 - NETDATA_DOUBLE_FORMAT
1335 - ": %s (source: %s)", rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
1336 - rc->calculation->parsed_as, rc->calculation->result,
1337 - buffer_tostring(rc->calculation->error_msg), rrdcalc_source(rc)
1338 - );
1339 -
1340 - rc->value = rc->calculation->result;
1341 - }
1342 - }
1343 - }
1344 - foreach_rrdcalc_in_rrdhost_done(rc);
1345 -
1346 - if (unlikely(runnable && service_running(SERVICE_HEALTH))) {
1347 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
1348 - if(unlikely(!service_running(SERVICE_HEALTH)))
1349 - break;
1350 -
1351 - if (unlikely(!(rc->run_flags & RRDCALC_FLAG_RUNNABLE)))
1352 - continue;
1353 -
1354 - if (rc->run_flags & RRDCALC_FLAG_DISABLED) {
1355 - continue;
1356 - }
1357 - RRDCALC_STATUS warning_status = RRDCALC_STATUS_UNDEFINED;
1358 - RRDCALC_STATUS critical_status = RRDCALC_STATUS_UNDEFINED;
1359 -
1360 - // --------------------------------------------------------
1361 - // check the warning expression
1362 -
1363 - if (likely(rc->warning)) {
1364 - worker_is_busy(WORKER_HEALTH_JOB_WARNING_EVAL);
1365 -
1366 - if (unlikely(!expression_evaluate(rc->warning))) {
1367 - // calculation failed
1368 - rc->run_flags |= RRDCALC_FLAG_WARN_ERROR;
1369 -
1370 - netdata_log_debug(D_HEALTH,
1371 - "Health on host '%s', alarm '%s.%s': warning expression failed with error: %s",
1372 - rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
1373 - buffer_tostring(rc->warning->error_msg)
1374 - );
1375 - } else {
1376 - rc->run_flags &= ~RRDCALC_FLAG_WARN_ERROR;
1377 - netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': warning expression gave value "
1378 - NETDATA_DOUBLE_FORMAT
1379 - ": %s (source: %s)", rrdhost_hostname(host), rrdcalc_chart_name(rc),
1380 - rrdcalc_name(rc), rc->warning->result, buffer_tostring(rc->warning->error_msg), rrdcalc_source(rc)
1381 - );
1382 - warning_status = rrdcalc_value2status(rc->warning->result);
1383 - }
1384 - }
1385 -
1386 - // --------------------------------------------------------
1387 - // check the critical expression
1388 -
1389 - if (likely(rc->critical)) {
1390 - worker_is_busy(WORKER_HEALTH_JOB_CRITICAL_EVAL);
1391 -
1392 - if (unlikely(!expression_evaluate(rc->critical))) {
1393 - // calculation failed
1394 - rc->run_flags |= RRDCALC_FLAG_CRIT_ERROR;
1395 -
1396 - netdata_log_debug(D_HEALTH,
1397 - "Health on host '%s', alarm '%s.%s': critical expression failed with error: %s",
1398 - rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
1399 - buffer_tostring(rc->critical->error_msg)
1400 - );
1401 - } else {
1402 - rc->run_flags &= ~RRDCALC_FLAG_CRIT_ERROR;
1403 - netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': critical expression gave value "
1404 - NETDATA_DOUBLE_FORMAT
1405 - ": %s (source: %s)", rrdhost_hostname(host), rrdcalc_chart_name(rc),
1406 - rrdcalc_name(rc), rc->critical->result, buffer_tostring(rc->critical->error_msg),
1407 - rrdcalc_source(rc)
1408 - );
1409 - critical_status = rrdcalc_value2status(rc->critical->result);
1410 - }
1411 - }
1412 -
1413 - // --------------------------------------------------------
1414 - // decide the final alarm status
1415 -
1416 - RRDCALC_STATUS status = RRDCALC_STATUS_UNDEFINED;
1417 -
1418 - switch (warning_status) {
1419 - case RRDCALC_STATUS_CLEAR:
1420 - status = RRDCALC_STATUS_CLEAR;
1421 - break;
1422 -
1423 - case RRDCALC_STATUS_RAISED:
1424 - status = RRDCALC_STATUS_WARNING;
1425 - break;
1426 -
1427 - default:
1428 - break;
1429 - }
1430 -
1431 - switch (critical_status) {
1432 - case RRDCALC_STATUS_CLEAR:
1433 - if (status == RRDCALC_STATUS_UNDEFINED)
1434 - status = RRDCALC_STATUS_CLEAR;
1435 - break;
1436 -
1437 - case RRDCALC_STATUS_RAISED:
1438 - status = RRDCALC_STATUS_CRITICAL;
1439 - break;
1440 -
1441 - default:
1442 - break;
1443 - }
1444 -
1445 - // --------------------------------------------------------
1446 - // check if the new status and the old differ
1447 -
1448 - if (status != rc->status) {
1449 -
1450 - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
1451 - int delay = 0;
1452 -
1453 - // apply trigger hysteresis
1454 -
1455 - if (now > rc->delay_up_to_timestamp) {
1456 - rc->delay_up_current = rc->delay_up_duration;
1457 - rc->delay_down_current = rc->delay_down_duration;
1458 - rc->delay_last = 0;
1459 - rc->delay_up_to_timestamp = 0;
1460 - } else {
1461 - rc->delay_up_current = (int) (rc->delay_up_current * rc->delay_multiplier);
1462 - if (rc->delay_up_current > rc->delay_max_duration)
1463 - rc->delay_up_current = rc->delay_max_duration;
1464 -
1465 - rc->delay_down_current = (int) (rc->delay_down_current * rc->delay_multiplier);
1466 - if (rc->delay_down_current > rc->delay_max_duration)
1467 - rc->delay_down_current = rc->delay_max_duration;
1468 - }
1469 -
1470 - if (status > rc->status)
1471 - delay = rc->delay_up_current;
1472 - else
1473 - delay = rc->delay_down_current;
1474 -
1475 - // COMMENTED: because we do need to send raising alarms
1476 - // if(now + delay < rc->delay_up_to_timestamp)
1477 - // delay = (int)(rc->delay_up_to_timestamp - now);
1478 -
1479 - rc->delay_last = delay;
1480 - rc->delay_up_to_timestamp = now + delay;
1481 -
1482 - ALARM_ENTRY *ae = health_create_alarm_entry(
1483 - host,
1484 - rc->id,
1485 - rc->next_event_id++,
1486 - rc->config_hash_id,
1487 - now,
1488 - rc->name,
1489 - rc->rrdset->id,
1490 - rc->rrdset->context,
1491 - rc->rrdset->name,
1492 - rc->classification,
1493 - rc->component,
1494 - rc->type,
1495 - rc->exec,
1496 - rc->recipient,
1497 - now - rc->last_status_change,
1498 - rc->old_value,
1499 - rc->value,
1500 - rc->status,
1501 - status,
1502 - rc->source,
1503 - rc->units,
1504 - rc->summary,
1505 - rc->info,
1506 - rc->delay_last,
1507 - (
1508 - ((rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) |
1509 - ((rc->run_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) |
1510 - (rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0)
1511 - )
1512 - );
1513 -
1514 - health_log_alert(host, ae);
1515 - health_alarm_log_add_entry(host, ae);
1516 -
1517 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1518 - "[%s]: Alert event for [%s.%s], value [%s], status [%s].",
1519 - rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), ae_new_value_string(ae),
1520 - rrdcalc_status2string(ae->new_status));
1521 -
1522 - rc->last_status_change_value = rc->value;
1523 - rc->last_status_change = now;
1524 - rc->old_status = rc->status;
1525 - rc->status = status;
1526 -
1527 - if(unlikely(rrdcalc_isrepeating(rc))) {
1528 - rc->last_repeat = now;
1529 - if (rc->status == RRDCALC_STATUS_CLEAR)
1530 - rc->run_flags |= RRDCALC_FLAG_RUN_ONCE;
1531 - }
1532 - }
1533 -
1534 - rc->last_updated = now;
1535 - rc->next_update = now + rc->update_every;
1536 -
1537 - if (next_run > rc->next_update)
1538 - next_run = rc->next_update;
1539 - }
1540 - foreach_rrdcalc_in_rrdhost_done(rc);
1541 -
1542 - // process repeating alarms
1543 - foreach_rrdcalc_in_rrdhost_read(host, rc) {
1544 - if(unlikely(!service_running(SERVICE_HEALTH)))
1545 - break;
1546 -
1547 - int repeat_every = 0;
1548 - if(unlikely(rrdcalc_isrepeating(rc) && rc->delay_up_to_timestamp <= now)) {
1549 - if(unlikely(rc->status == RRDCALC_STATUS_WARNING)) {
1550 - rc->run_flags &= ~RRDCALC_FLAG_RUN_ONCE;
1551 - repeat_every = rc->warn_repeat_every;
1552 - } else if(unlikely(rc->status == RRDCALC_STATUS_CRITICAL)) {
1553 - rc->run_flags &= ~RRDCALC_FLAG_RUN_ONCE;
1554 - repeat_every = rc->crit_repeat_every;
1555 - } else if(unlikely(rc->status == RRDCALC_STATUS_CLEAR)) {
1556 - if(!(rc->run_flags & RRDCALC_FLAG_RUN_ONCE)) {
1557 - if(rc->old_status == RRDCALC_STATUS_CRITICAL) {
1558 - repeat_every = 1;
1559 - } else if (rc->old_status == RRDCALC_STATUS_WARNING) {
1560 - repeat_every = 1;
1561 - }
1562 - }
1563 - }
1564 - } else {
1565 - continue;
1566 - }
1567 -
1568 - if(unlikely(repeat_every > 0 && (rc->last_repeat + repeat_every) <= now)) {
1569 - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
1570 - rc->last_repeat = now;
1571 - if (likely(rc->times_repeat < UINT32_MAX)) rc->times_repeat++;
1572 - ALARM_ENTRY *ae = health_create_alarm_entry(
1573 - host,
1574 - rc->id,
1575 - rc->next_event_id++,
1576 - rc->config_hash_id,
1577 - now,
1578 - rc->name,
1579 - rc->rrdset->id,
1580 - rc->rrdset->context,
1581 - rc->rrdset->name,
1582 - rc->classification,
1583 - rc->component,
1584 - rc->type,
1585 - rc->exec,
1586 - rc->recipient,
1587 - now - rc->last_status_change,
1588 - rc->old_value,
1589 - rc->value,
1590 - rc->old_status,
1591 - rc->status,
1592 - rc->source,
1593 - rc->units,
1594 - rc->summary,
1595 - rc->info,
1596 - rc->delay_last,
1597 - (
1598 - ((rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) |
1599 - ((rc->run_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) |
1600 - (rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0)
1601 - )
1602 - );
1603 -
1604 - health_log_alert(host, ae);
1605 - ae->last_repeat = rc->last_repeat;
1606 - if (!(rc->run_flags & RRDCALC_FLAG_RUN_ONCE) && rc->status == RRDCALC_STATUS_CLEAR) {
1607 - ae->flags |= HEALTH_ENTRY_RUN_ONCE;
1608 - }
1609 - rc->run_flags |= RRDCALC_FLAG_RUN_ONCE;
1610 - health_process_notifications(host, ae);
1611 - netdata_log_debug(D_HEALTH, "Notification sent for the repeating alarm %u.", ae->alarm_id);
1612 - health_alarm_wait_for_execution(ae);
1613 - health_alarm_log_free_one_nochecks_nounlink(ae);
1614 - }
1615 - }
1616 - foreach_rrdcalc_in_rrdhost_done(rc);
1617 - }
1618 -
1619 - if (unlikely(!service_running(SERVICE_HEALTH)))
1620 - break;
1621 -
1622 - // execute notifications
1623 - // and cleanup
1624 - worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS);
1625 - health_alarm_log_process(host);
1626 -
1627 - if (unlikely(!service_running(SERVICE_HEALTH))) {
1628 - // wait for all notifications to finish before allowing health to be cleaned up
1629 - ALARM_ENTRY *ae;
1630 - while (NULL != (ae = alarm_notifications_in_progress.head)) {
1631 - if(unlikely(!service_running(SERVICE_HEALTH)))
1632 - break;
1633 -
1634 - health_alarm_wait_for_execution(ae);
1635 - }
1636 - break;
1637 - }
1638 -#ifdef ENABLE_ACLK
1639 - if (netdata_cloud_enabled) {
1640 - struct aclk_sync_cfg_t *wc = host->aclk_config;
1641 - if (unlikely(!wc))
1642 - continue;
1643 -
1644 - if (wc->alert_queue_removed == 1) {
1645 - sql_queue_removed_alerts_to_aclk(host);
1646 - } else if (wc->alert_queue_removed > 1) {
1647 - wc->alert_queue_removed--;
1648 - }
1649 -
1650 - if (wc->alert_checkpoint_req == 1) {
1651 - aclk_push_alarm_checkpoint(host);
1652 - } else if (wc->alert_checkpoint_req > 1) {
1653 - wc->alert_checkpoint_req--;
1654 - }
1655 - }
1656 -#endif
1657 - }
1658 - dfe_done(host);
1659 -
1660 - // wait for all notifications to finish before allowing health to be cleaned up
1661 - ALARM_ENTRY *ae;
1662 - while (NULL != (ae = alarm_notifications_in_progress.head)) {
1663 - if(unlikely(!service_running(SERVICE_HEALTH)))
1664 - break;
1665 -
1666 - health_alarm_wait_for_execution(ae);
1667 - }
1668 -
1669 - if(unlikely(!service_running(SERVICE_HEALTH)))
1670 - break;
1671 -
1672 - health_sleep(next_run, loop);
1673 -
1674 - } // forever
1675 -
1676 - netdata_thread_cleanup_pop(1);
1677 - return NULL;
169 +void health_plugin_reload(void) {
170 + health_reload_prototypes();
171 + health_apply_prototypes_to_all_hosts();
172 }
health/health.d/adaptec_raid.conf
+2 -2
@@ -6,7 +6,7 @@
6 class: Errors
7 type: System
8 component: RAID
9 - lookup: max -10s foreach *
9 + lookup: max -10s
10 units: bool
11 every: 10s
12 crit: $this > 0
@@ -22,7 +22,7 @@ component: RAID
22 class: Errors
23 type: System
24 component: RAID
25 - lookup: max -10s foreach *
25 + lookup: max -10s
26 units: bool
27 every: 10s
28 crit: $this > 0
health/health.d/anomalies.conf
+21 -19
@@ -1,23 +1,25 @@
1 -# raise a warning alarm if an anomaly probability is consistently above 50%
1 +## raise a warning alarm if an anomaly probability is consistently above 50%
2
3 - template: anomalies_anomaly_probabilities
4 - on: anomalies.probability
5 - class: Errors
6 - type: Netdata
7 -component: ML
8 - lookup: average -2m foreach *
9 - every: 1m
10 - warn: $this > 50
11 - info: average anomaly probability over the last 2 minutes
3 +## "foreach" was removed, these alarms don't work anymore
4 +
5 +# template: anomalies_anomaly_probabilities
6 +# on: anomalies.probability
7 +# class: Errors
8 +# type: Netdata
9 +#component: ML
10 +# lookup: average -2m foreach *
11 +# every: 1m
12 +# warn: $this > 50
13 +# info: average anomaly probability over the last 2 minutes
14
15 # raise a warning alarm if an anomaly flag is consistently firing
16
15 - template: anomalies_anomaly_flags
16 - on: anomalies.anomaly
17 - class: Errors
18 - type: Netdata
19 -component: ML
20 - lookup: sum -2m foreach *
21 - every: 1m
22 - warn: $this > 10
23 - info: number of anomalies in the last 2 minutes
17 +# template: anomalies_anomaly_flags
18 +# on: anomalies.anomaly
19 +# class: Errors
20 +# type: Netdata
21 +#component: ML
22 +# lookup: sum -2m foreach *
23 +# every: 1m
24 +# warn: $this > 10
25 +# info: number of anomalies in the last 2 minutes
health/health.d/file_descriptors.conf
+1 -1
@@ -23,7 +23,7 @@ component: Process
23 os: linux
24 module: *
25 hosts: *
26 - lookup: max -10s unaligned foreach *
26 + lookup: max -10s unaligned
27 units: %
28 every: 10s
29 warn: $this > (($status >= $WARNING) ? (85) : (95))
health/health.d/megacli.conf
+3 -3
@@ -6,7 +6,7 @@
6 class: Errors
7 type: System
8 component: RAID
9 - lookup: max -10s foreach *
9 + lookup: max -10s
10 units: boolean
11 every: 10s
12 crit: $this > 0
@@ -22,7 +22,7 @@ component: RAID
22 class: Errors
23 type: System
24 component: RAID
25 - lookup: sum -10s foreach *
25 + lookup: sum -10s
26 units: predictive failures
27 every: 10s
28 warn: $this > 0
@@ -36,7 +36,7 @@ component: RAID
36 class: Errors
37 type: System
38 component: RAID
39 - lookup: sum -10s foreach *
39 + lookup: sum -10s
40 units: media errors
41 every: 10s
42 warn: $this > 0
health/health.d/redis.conf
+2 -1
@@ -20,7 +20,8 @@ component: Redis
20 type: KV Storage
21 component: Redis
22 every: 10s
23 - crit: $last_bgsave != nan AND $last_bgsave != 0
23 + calc: $last_bgsave != nan AND $last_bgsave != 0
24 + crit: $this
25 units: ok/failed
26 summary: Redis background save
27 info: Status of the last RDB save operation (0: ok, 1: error)
health/health.h
+14 -27
@@ -4,8 +4,7 @@
4 #define NETDATA_HEALTH_H 1
5
6 #include "daemon/common.h"
7 -
8 -extern unsigned int default_health_enabled;
7 +#include "rrdcalc.h"
8
9 typedef enum __attribute__((packed)) {
10 HEALTH_ENTRY_FLAG_PROCESSED = 0x00000001, // notifications engine has processed this
@@ -41,20 +40,17 @@ void health_entry_flags_to_json_array(BUFFER *wb, const char *key, HEALTH_ENTRY_
40
41 #define HEALTH_SILENCERS_MAX_FILE_LEN 10000
42
44 -extern char *silencers_filename;
45 -extern SIMPLE_PATTERN *conf_enabled_alarms;
46 -extern DICTIONARY *health_rrdvars;
47 -
48 -void health_init(void);
43 +void health_plugin_init(void);
44 +void health_plugin_destroy(void);
45
50 -void health_reload(void);
46 +void health_plugin_reload(void);
47
48 void health_aggregate_alarms(RRDHOST *host, BUFFER *wb, BUFFER* context, RRDCALC_STATUS status);
49 void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
50 void health_alert2json_conf(RRDHOST *host, BUFFER *wb, CONTEXTS_V2_OPTIONS all);
51 void health_alarms_values2json(RRDHOST *host, BUFFER *wb, int all);
52
57 -void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *buf);
53 +void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *wb);
54 void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf);
55
56 int health_alarm_log_open(RRDHOST *host);
@@ -63,34 +59,18 @@ void health_alarm_log_load(RRDHOST *host);
59
60 ALARM_ENTRY* health_create_alarm_entry(
61 RRDHOST *host,
66 - uint32_t alarm_id,
67 - uint32_t alarm_event_id,
68 - const uuid_t config_hash_id,
62 + RRDCALC *rc,
63 time_t when,
70 - STRING *name,
71 - STRING *chart,
72 - STRING *chart_context,
73 - STRING *chart_id,
74 - STRING *classification,
75 - STRING *component,
76 - STRING *type,
77 - STRING *exec,
78 - STRING *recipient,
64 time_t duration,
65 NETDATA_DOUBLE old_value,
66 NETDATA_DOUBLE new_value,
67 RRDCALC_STATUS old_status,
68 RRDCALC_STATUS new_status,
84 - STRING *source,
85 - STRING *units,
86 - STRING *summary,
87 - STRING *info,
69 int delay,
70 HEALTH_ENTRY_FLAGS flags);
71
72 void health_alarm_log_add_entry(RRDHOST *host, ALARM_ENTRY *ae);
73
93 -void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath);
74 char *health_user_config_dir(void);
75 char *health_stock_config_dir(void);
76 void health_alarm_log_free(RRDHOST *host);
@@ -100,7 +80,6 @@ void health_alarm_log_free_one_nochecks_nounlink(ALARM_ENTRY *ae);
80 void *health_cmdapi_thread(void *ptr);
81
82 char *health_edit_command_from_source(const char *source);
103 -void sql_refresh_hashes(void);
83
84 void health_string2json(BUFFER *wb, const char *prefix, const char *label, const char *value, const char *suffix);
85
@@ -108,4 +87,12 @@ void health_log_alert_transition_with_trace(RRDHOST *host, ALARM_ENTRY *ae, int
87 #define health_log_alert(host, ae) health_log_alert_transition_with_trace(host, ae, __LINE__, __FILE__, __FUNCTION__)
88 bool health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(RRDCALC *rc, usec_t *global_id, uuid_t *transitions_id);
89
90 +int alert_variable_lookup_trace(RRDHOST *host, RRDSET *st, const char *variable, BUFFER *wb);
91 +
92 +#include "health_prototypes.h"
93 +#include "health_silencers.h"
94 +
95 +typedef void (*prototype_metadata_cb_t)(void *data, STRING *type, STRING *component, STRING *classification, STRING *recipient);
96 +void health_prototype_metadata_foreach(void *data, prototype_metadata_cb_t cb);
97 +
98 #endif //NETDATA_HEALTH_H
health/health_config.c
+254 -945
@@ -1,38 +1,7 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "health.h"
4 -
5 -#define HEALTH_CONF_MAX_LINE 4096
6 -
7 -#define HEALTH_ALARM_KEY "alarm"
8 -#define HEALTH_TEMPLATE_KEY "template"
9 -#define HEALTH_ON_KEY "on"
10 -#define HEALTH_HOST_KEY "hosts"
11 -#define HEALTH_OS_KEY "os"
12 -#define HEALTH_PLUGIN_KEY "plugin"
13 -#define HEALTH_MODULE_KEY "module"
14 -#define HEALTH_CHARTS_KEY "charts"
15 -#define HEALTH_LOOKUP_KEY "lookup"
16 -#define HEALTH_CALC_KEY "calc"
17 -#define HEALTH_EVERY_KEY "every"
18 -#define HEALTH_GREEN_KEY "green"
19 -#define HEALTH_RED_KEY "red"
20 -#define HEALTH_WARN_KEY "warn"
21 -#define HEALTH_CRIT_KEY "crit"
22 -#define HEALTH_EXEC_KEY "exec"
23 -#define HEALTH_RECIPIENT_KEY "to"
24 -#define HEALTH_UNITS_KEY "units"
25 -#define HEALTH_SUMMARY_KEY "summary"
26 -#define HEALTH_INFO_KEY "info"
27 -#define HEALTH_CLASS_KEY "class"
28 -#define HEALTH_COMPONENT_KEY "component"
29 -#define HEALTH_TYPE_KEY "type"
30 -#define HEALTH_DELAY_KEY "delay"
31 -#define HEALTH_OPTIONS_KEY "options"
32 -#define HEALTH_REPEAT_KEY "repeat"
33 -#define HEALTH_HOST_LABEL_KEY "host labels"
34 -#define HEALTH_FOREACH_KEY "foreach"
35 -#define HEALTH_CHART_LABEL_KEY "chart labels"
4 +#include "health_internals.h"
5
6 static inline int health_parse_delay(
7 size_t line, const char *filename, char *string,
@@ -114,8 +83,8 @@ static inline int health_parse_delay(
83 return 1;
84 }
85
117 -static inline uint32_t health_parse_options(const char *s) {
118 - uint32_t options = 0;
86 +static inline ALERT_ACTION_OPTIONS health_parse_options(const char *s) {
87 + ALERT_ACTION_OPTIONS options = ALERT_ACTION_OPTION_NONE;
88 char buf[100+1] = "";
89
90 while(*s) {
@@ -134,7 +103,7 @@ static inline uint32_t health_parse_options(const char *s) {
103 buf[count] = '\0';
104
105 if(!strcasecmp(buf, "no-clear-notification") || !strcasecmp(buf, "no-clear"))
137 - options |= RRDCALC_OPTION_NO_CLEAR_NOTIFICATION;
106 + options |= ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION;
107 else
108 netdata_log_error("Ignoring unknown alarm option '%s'", buf);
109 }
@@ -193,131 +162,19 @@ static inline int isvariableterm(const char s) {
162 return 1;
163 }
164
196 -// If needed, add a prefix key to all possible values in the range
197 -static inline char *health_config_add_key_to_values(char *value) {
198 - BUFFER *wb = buffer_create(HEALTH_CONF_MAX_LINE + 1, NULL);
199 - char key[HEALTH_CONF_MAX_LINE + 1];
200 - char data[HEALTH_CONF_MAX_LINE + 1];
201 -
202 - char *s = value;
203 - size_t i = 0;
204 -
205 - key[0] = '\0';
206 - while(*s) {
207 - if (*s == '=') {
208 - //hold the key
209 - data[i]='\0';
210 - strncpyz(key, data, HEALTH_CONF_MAX_LINE);
211 - i=0;
212 - } else if (*s == ' ') {
213 - data[i]='\0';
214 - if (data[0]=='!')
215 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
216 - else
217 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
218 - i=0;
219 - } else {
220 - data[i++] = *s;
221 - }
222 - s++;
223 - }
224 -
225 - data[i]='\0';
226 - if (data[0]) {
227 - if (data[0]=='!')
228 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
229 - else
230 - buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
231 - }
232 -
233 - char *final = mallocz(HEALTH_CONF_MAX_LINE + 1);
234 - strncpyz(final, buffer_tostring(wb), HEALTH_CONF_MAX_LINE);
235 - buffer_free(wb);
236 -
237 - return final;
238 -}
239 -
240 -static inline void parse_variables_and_store_in_health_rrdvars(char *value, size_t len) {
241 - const char *s = value;
242 - char buffer[RRDVAR_MAX_LENGTH];
243 -
244 - // $
245 - while (*s) {
246 - if(*s == '$') {
247 - size_t i = 0;
248 - s++;
249 -
250 - if(*s == '{') {
251 - // ${variable_name}
252 -
253 - s++;
254 - while (*s && *s != '}' && i < len)
255 - buffer[i++] = *s++;
256 -
257 - if(*s == '}')
258 - s++;
259 - }
260 - else {
261 - // $variable_name
262 -
263 - while (*s && !isvariableterm(*s) && i < len)
264 - buffer[i++] = *s++;
265 - }
266 -
267 - buffer[i] = '\0';
268 -
269 - //TODO: check and try to store only variables
270 - STRING *name_string = rrdvar_name_to_string(buffer);
271 - rrdvar_add("health", health_rrdvars, name_string, RRDVAR_TYPE_CALCULATED, RRDVAR_FLAG_CONFIG_VAR, NULL);
272 - string_freez(name_string);
273 - } else
274 - s++;
275 - }
276 -}
277 -
278 -/**
279 - * Health pattern from Foreach
280 - *
281 - * Create a new simple pattern using the user input
282 - *
283 - * @param s the string that will be used to create the simple pattern.
284 - */
285 -
286 -static void dimension_remove_pipe_comma(char *str) {
287 - while(*str) {
288 - if(*str == '|' || *str == ',') *str = ' ';
289 - str++;
290 - }
291 -}
292 -
293 -static SIMPLE_PATTERN *health_pattern_from_foreach(const char *s) {
294 - char *convert= strdupz(s);
295 - SIMPLE_PATTERN *val = NULL;
296 -
297 - if(convert) {
298 - dimension_remove_pipe_comma(convert);
299 - val = simple_pattern_create(convert, NULL, SIMPLE_PATTERN_EXACT, true);
300 - freez(convert);
301 - }
302 -
303 - return val;
304 -}
305 -
165 static inline int health_parse_db_lookup(
166 size_t line, const char *filename, char *string,
167 RRDR_TIME_GROUPING *group_method, int *after, int *before, int *every,
309 - RRDCALC_OPTIONS *options, STRING **dimensions, STRING **foreachdim
168 + RRDR_OPTIONS *options, STRING **dimensions
169 ) {
170 netdata_log_debug(D_HEALTH, "Health configuration parsing database lookup %zu@%s: %s", line, filename, string);
171
172 if(*dimensions) string_freez(*dimensions);
314 - if(*foreachdim) string_freez(*foreachdim);
173 *dimensions = NULL;
316 - *foreachdim = NULL;
174 *after = 0;
175 *before = 0;
176 *every = 0;
320 - *options = (*options) & RRDCALC_ALL_OPTIONS_EXCLUDING_THE_RRDR_ONES; // preserve rrdcalc options
177 + *options = 0;
178
179 char *s = string, *key;
180
@@ -417,10 +274,6 @@ static inline int health_parse_db_lookup(
274 }
275 s = ++find;
276 }
420 - else if(!strcasecmp(key, HEALTH_FOREACH_KEY )) {
421 - *foreachdim = string_strdupz(s);
422 - break;
423 - }
277 else {
278 netdata_log_error("Health configuration at line %zu of file '%s': unknown keyword '%s'",
279 line, filename, key);
@@ -432,7 +285,7 @@ static inline int health_parse_db_lookup(
285
286 static inline STRING *health_source_file(size_t line, const char *file) {
287 char buffer[FILENAME_MAX + 1];
435 - snprintfz(buffer, FILENAME_MAX, "%zu@%s", line, file);
288 + snprintfz(buffer, FILENAME_MAX, "line=%zu,file=%s", line, file);
289 return string_strdupz(buffer);
290 }
291
@@ -441,25 +294,45 @@ char *health_edit_command_from_source(const char *source)
294 char buffer[FILENAME_MAX + 1];
295 char *temp = strdupz(source);
296 char *line_num = strchr(temp, '@');
297 + char *line_p = temp;
298 char *file_no_path = strrchr(temp, '/');
299
300 + // Check for the 'line=' format if '@' is not found
301 + if (!line_num) {
302 + line_num = strstr(temp, "line=");
303 + file_no_path = strstr(temp, "file=/");
304 + }
305 +
306 if (likely(file_no_path && line_num)) {
447 - *line_num = '\0';
307 + if (line_num == strchr(temp, '@')) {
308 + *line_num = '\0'; // Handle the old format
309 + } else {
310 + line_num += strlen("line=");
311 + file_no_path = strrchr(file_no_path + strlen("file="), '/');
312 + char *line_end = strchr(line_num, ',');
313 + if (line_end) {
314 + line_p = line_num;
315 + *line_end = '\0';
316 + }
317 + }
318 +
319 snprintfz(
320 buffer,
321 FILENAME_MAX,
322 "sudo %s/edit-config health.d/%s=%s=%s",
323 netdata_configured_user_config_dir,
324 file_no_path + 1,
454 - temp,
325 + line_p,
326 rrdhost_registry_hostname(localhost));
456 - } else
327 + } else {
328 buffer[0] = '\0';
329 + }
330
331 freez(temp);
332 return strdupz(buffer);
333 }
334
335 +
336 static inline void strip_quotes(char *s) {
337 while(*s) {
338 if(*s == '\'' || *s == '"') *s = ' ';
@@ -467,46 +340,38 @@ static inline void strip_quotes(char *s) {
340 }
341 }
342
470 -static inline void alert_config_free(struct alert_config *cfg)
471 -{
472 - string_freez(cfg->alarm);
473 - string_freez(cfg->template_key);
474 - string_freez(cfg->os);
475 - string_freez(cfg->host);
476 - string_freez(cfg->on);
477 - string_freez(cfg->plugin);
478 - string_freez(cfg->module);
479 - string_freez(cfg->charts);
480 - string_freez(cfg->lookup);
481 - string_freez(cfg->calc);
482 - string_freez(cfg->warn);
483 - string_freez(cfg->crit);
484 - string_freez(cfg->every);
485 - string_freez(cfg->green);
486 - string_freez(cfg->red);
487 - string_freez(cfg->exec);
488 - string_freez(cfg->to);
489 - string_freez(cfg->units);
490 - string_freez(cfg->summary);
491 - string_freez(cfg->info);
492 - string_freez(cfg->classification);
493 - string_freez(cfg->component);
494 - string_freez(cfg->type);
495 - string_freez(cfg->delay);
496 - string_freez(cfg->options);
497 - string_freez(cfg->repeat);
498 - string_freez(cfg->host_labels);
499 - string_freez(cfg->p_db_lookup_dimensions);
500 - string_freez(cfg->p_db_lookup_method);
501 - string_freez(cfg->chart_labels);
502 - string_freez(cfg->source);
503 - freez(cfg);
504 -}
505 -
506 -int sql_store_hashes = 1;
507 -static int health_readfile(const char *filename, void *data) {
508 - RRDHOST *host = (RRDHOST *)data;
509 -
343 +#define PARSE_HEALTH_CONFIG_DUPLICATE_STRING_MSG(ax, member) do { \
344 + if(strcmp(string2str(ax->member), value) != 0) \
345 + netdata_log_error( \
346 + "Health configuration at line %zu of file '%s' for alarm '%s' has key '%s' twice, " \
347 + "once with value '%s' and later with value '%s'. Using ('%s').", \
348 + line, filename, string2str(ac->name), key, \
349 + string2str(ax->member), value, value); \
350 +} while(0)
351 +
352 +#define PARSE_HEALTH_CONFIG_LINE_STRING(ax, member) do { \
353 + if(ax->member) { \
354 + PARSE_HEALTH_CONFIG_DUPLICATE_STRING_MSG(ax, member); \
355 + string_freez(ax->member); \
356 + } \
357 + ax->member = string_strdupz(value); \
358 +} while(0)
359 +
360 +#define PARSE_HEALTH_CONFIG_LINE_PATTERN(ax, member) do { \
361 + if(ax->member) { \
362 + PARSE_HEALTH_CONFIG_DUPLICATE_STRING_MSG(ax, member); \
363 + string_freez(ax->member); \
364 + } \
365 + if(value && strcmp(value, "*") == 0) \
366 + value = NULL; \
367 + else if(value && (strcmp(value, "!* *") == 0 || strcmp(value, "!*") == 0)) { \
368 + value = NULL; \
369 + ap->match.enabled = false; \
370 + } \
371 + ax->member = string_strdupz(value); \
372 +} while(0)
373 +
374 +int health_readfile(const char *filename, void *data __maybe_unused, bool stock_config) {
375 netdata_log_debug(D_HEALTH, "Health configuration reading file '%s'", filename);
376
377 static uint32_t
@@ -578,12 +443,10 @@ static int health_readfile(const char *filename, void *data) {
443 return 0;
444 }
445
581 - RRDCALC *rc = NULL;
582 - RRDCALCTEMPLATE *rt = NULL;
583 - struct alert_config *alert_cfg = NULL;
446 + RRD_ALERT_PROTOTYPE *ap = NULL;
447 + struct rrd_alert_config *ac = NULL;
448 + struct rrd_alert_match *am = NULL;
449
585 - int ignore_this = 0;
586 - bool filtered_config = false;
450 size_t line = 0, append = 0;
451 char *s;
452 while((s = fgets(&buffer[append], (int)(HEALTH_CONF_MAX_LINE - append), fp)) || append) {
@@ -598,18 +461,19 @@ static int health_readfile(const char *filename, void *data) {
461 append = &s[append] - buffer;
462 if(append < HEALTH_CONF_MAX_LINE)
463 continue;
601 - else {
602 - netdata_log_error("Health configuration has too long multi-line at line %zu of file '%s'.",
603 - line, filename);
604 - }
464 + else
465 + netdata_log_error(
466 + "Health configuration has too long multi-line at line %zu of file '%s'.",
467 + line, filename);
468 }
469 append = 0;
470
471 char *key = s;
472 while(*s && *s != ':') s++;
473 if(!*s) {
611 - netdata_log_error("Health configuration has invalid line %zu of file '%s'. It does not contain a ':'. Ignoring it.",
612 - line, filename);
474 + netdata_log_error(
475 + "Health configuration has invalid line %zu of file '%s'. It does not contain a ':'. Ignoring it.",
476 + line, filename);
477 continue;
478 }
479 *s = '\0';
@@ -620,769 +484,214 @@ static int health_readfile(const char *filename, void *data) {
484 value = trim_all(value);
485
486 if(!key) {
623 - netdata_log_error("Health configuration has invalid line %zu of file '%s'. Keyword is empty. Ignoring it.",
624 - line, filename);
487 + netdata_log_error(
488 + "Health configuration has invalid line %zu of file '%s'. Keyword is empty. Ignoring it.",
489 + line, filename);
490 +
491 continue;
492 }
493
494 if(!value) {
629 - netdata_log_error("Health configuration has invalid line %zu of file '%s'. value is empty. Ignoring it.",
630 - line, filename);
495 + netdata_log_error(
496 + "Health configuration has invalid line %zu of file '%s'. value is empty. Ignoring it.",
497 + line, filename);
498 continue;
499 }
500
501 uint32_t hash = simple_uhash(key);
502
636 - if(hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
637 - if(rc) {
638 - if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this)
639 - rrdcalc_free_unused_rrdcalc_loaded_from_config(rc);
640 - else
641 - rrdcalc_add_from_config(host, rc);
642 -
643 - // health_add_alarms_loop(host, rc, ignore_this) ;
644 - }
645 -
646 - if(rt) {
647 - if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this)
648 - rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt);
649 - else
650 - rrdcalctemplate_add_from_config(host, rt);
651 -
652 - rt = NULL;
503 + if((hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) || (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY))) {
504 + if(ap) {
505 + health_prototype_add(ap);
506 + freez(ap);
507 }
508
655 - if (simple_pattern_matches(conf_enabled_alarms, value)) {
656 - rc = callocz(1, sizeof(RRDCALC));
657 - rc->next_event_id = 1;
658 -
659 - {
660 - char *tmp = strdupz(value);
661 - if(rrdvar_fix_name(tmp))
662 - netdata_log_error("Health configuration renamed alarm '%s' to '%s'", value, tmp);
663 -
664 - rc->name = string_strdupz(tmp);
665 - freez(tmp);
666 - }
509 + ap = callocz(1, sizeof(*ap));
510 + am = &ap->match;
511 + ac = &ap->config;
512
668 - rc->source = health_source_file(line, filename);
669 - rc->green = NAN;
670 - rc->red = NAN;
671 - rc->value = NAN;
672 - rc->old_value = NAN;
673 - rc->delay_multiplier = 1.0;
674 - rc->old_status = RRDCALC_STATUS_UNINITIALIZED;
675 - rc->warn_repeat_every = host->health.health_default_warn_repeat_every;
676 - rc->crit_repeat_every = host->health.health_default_crit_repeat_every;
677 - if (alert_cfg)
678 - alert_config_free(alert_cfg);
679 - alert_cfg = callocz(1, sizeof(struct alert_config));
680 -
681 - alert_cfg->alarm = string_dup(rc->name);
682 - alert_cfg->source = health_source_file(line, filename);
683 - ignore_this = 0;
684 - filtered_config = false;
685 - } else {
686 - rc = NULL;
687 - filtered_config = true;
688 - }
689 - }
690 - else if(hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) {
691 - if(rc) {
692 -// health_add_alarms_loop(host, rc, ignore_this) ;
693 - if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this)
694 - rrdcalc_free_unused_rrdcalc_loaded_from_config(rc);
695 - else
696 - rrdcalc_add_from_config(host, rc);
697 -
698 - rc = NULL;
699 - }
513 + {
514 + char *tmp = strdupz(value);
515 + if(rrdvar_fix_name(tmp))
516 + netdata_log_error("Health configuration renamed alarm '%s' to '%s'", value, tmp);
517
701 - if(rt) {
702 - if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this)
703 - rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt);
704 - else
705 - rrdcalctemplate_add_from_config(host, rt);
518 + ap->config.name = string_strdupz(tmp);
519 + freez(tmp);
520 }
521
708 - if (simple_pattern_matches(conf_enabled_alarms, value)) {
709 - rt = callocz(1, sizeof(RRDCALCTEMPLATE));
710 -
711 - {
712 - char *tmp = strdupz(value);
713 - if(rrdvar_fix_name(tmp))
714 - netdata_log_error("Health configuration renamed template '%s' to '%s'", value, tmp);
715 -
716 - rt->name = string_strdupz(tmp);
717 - freez(tmp);
718 - }
719 -
720 - rt->source = health_source_file(line, filename);
721 - rt->green = NAN;
722 - rt->red = NAN;
723 - rt->delay_multiplier = (float)1.0;
724 - rt->warn_repeat_every = host->health.health_default_warn_repeat_every;
725 - rt->crit_repeat_every = host->health.health_default_crit_repeat_every;
726 - if (alert_cfg)
727 - alert_config_free(alert_cfg);
728 - alert_cfg = callocz(1, sizeof(struct alert_config));
729 -
730 - alert_cfg->template_key = string_dup(rt->name);
731 - alert_cfg->source = health_source_file(line, filename);
732 - ignore_this = 0;
733 - filtered_config = false;
734 - } else {
735 - rt = NULL;
736 - filtered_config = true;
737 - }
522 + ap->_internal.enabled = true;
523 + ap->match.enabled = true;
524 + ap->match.is_template = (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY));
525 + ap->config.source = health_source_file(line, filename);
526 + ap->config.source_type = stock_config ? DYNCFG_SOURCE_TYPE_STOCK : DYNCFG_SOURCE_TYPE_USER;
527 + ap->config.green = NAN;
528 + ap->config.red = NAN;
529 + ap->config.delay_multiplier = 1;
530 + ap->config.warn_repeat_every = health_globals.config.default_warn_repeat_every;
531 + ap->config.crit_repeat_every = health_globals.config.default_crit_repeat_every;
532 }
739 - else if(hash == hash_os && !strcasecmp(key, HEALTH_OS_KEY)) {
740 - char *os_match = value;
741 - if (alert_cfg) alert_cfg->os = string_strdupz(value);
742 - SIMPLE_PATTERN *os_pattern = simple_pattern_create(os_match, NULL, SIMPLE_PATTERN_EXACT, true);
743 -
744 - if(!simple_pattern_matches_string(os_pattern, host->os)) {
745 - if(rc)
746 - netdata_log_debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: host O/S does not match '%s'", rrdhost_hostname(host), rrdcalc_name(rc), line, filename, os_match);
747 -
748 - if(rt)
749 - netdata_log_debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: host O/S does not match '%s'", rrdhost_hostname(host), rrdcalctemplate_name(rt), line, filename, os_match);
750 -
751 - ignore_this = 1;
752 - }
753 -
754 - simple_pattern_free(os_pattern);
533 + else if(!am || !ac || !ap) {
534 + netdata_log_error(
535 + "Health configuration at line %zu of file '%s' has unknown key '%s'. "
536 + "Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.",
537 + line, filename, key);
538 }
756 - else if(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
757 - char *host_match = value;
758 - if (alert_cfg) alert_cfg->host = string_strdupz(value);
759 - SIMPLE_PATTERN *host_pattern = simple_pattern_create(host_match, NULL, SIMPLE_PATTERN_EXACT, true);
760 -
761 - if(!simple_pattern_matches_string(host_pattern, host->hostname)) {
762 - if(rc)
763 - netdata_log_debug(D_HEALTH, "HEALTH on '%s' ignoring alarm '%s' defined at %zu@%s: hostname does not match '%s'", rrdhost_hostname(host), rrdcalc_name(rc), line, filename, host_match);
764 -
765 - if(rt)
766 - netdata_log_debug(D_HEALTH, "HEALTH on '%s' ignoring template '%s' defined at %zu@%s: hostname does not match '%s'", rrdhost_hostname(host), rrdcalctemplate_name(rt), line, filename, host_match);
767 -
768 - ignore_this = 1;
769 - }
770 -
771 - simple_pattern_free(host_pattern);
539 + else if(!am->is_template && hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
540 + PARSE_HEALTH_CONFIG_LINE_STRING(am, on.chart);
541 }
773 - else if(rc) {
774 - if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
775 - alert_cfg->on = string_strdupz(value);
776 - if(rc->chart) {
777 - if(strcmp(rrdcalc_chart_name(rc), value) != 0)
778 - netdata_log_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').",
779 - line, filename, rrdcalc_name(rc), key, rrdcalc_chart_name(rc), value, value);
780 -
781 - string_freez(rc->chart);
782 - }
783 - rc->chart = string_strdupz(value);
784 - }
785 - else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) {
786 - strip_quotes(value);
787 -
788 - alert_cfg->classification = string_strdupz(value);
789 - if(rc->classification) {
790 - if(strcmp(rrdcalc_classification(rc), value) != 0)
791 - netdata_log_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').",
792 - line, filename, rrdcalc_name(rc), key, rrdcalc_classification(rc), value, value);
793 -
794 - string_freez(rc->classification);
795 - }
796 - rc->classification = string_strdupz(value);
797 - }
798 - else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) {
799 - strip_quotes(value);
800 -
801 - alert_cfg->component = string_strdupz(value);
802 - if(rc->component) {
803 - if(strcmp(rrdcalc_component(rc), value) != 0)
804 - netdata_log_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').",
805 - line, filename, rrdcalc_name(rc), key, rrdcalc_component(rc), value, value);
806 -
807 - string_freez(rc->component);
808 - }
809 - rc->component = string_strdupz(value);
810 - }
811 - else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) {
812 - strip_quotes(value);
813 -
814 - alert_cfg->type = string_strdupz(value);
815 - if(rc->type) {
816 - if(strcmp(rrdcalc_type(rc), value) != 0)
817 - netdata_log_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').",
818 - line, filename, rrdcalc_name(rc), key, rrdcalc_type(rc), value, value);
819 -
820 - string_freez(rc->type);
821 - }
822 - rc->type = string_strdupz(value);
823 - }
824 - else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
825 - alert_cfg->lookup = string_strdupz(value);
826 - health_parse_db_lookup(line, filename, value, &rc->group, &rc->after, &rc->before,
827 - &rc->update_every, &rc->options, &rc->dimensions, &rc->foreach_dimension);
828 -
829 - if(rc->foreach_dimension)
830 - rc->foreach_dimension_pattern = health_pattern_from_foreach(rrdcalc_foreachdim(rc));
831 -
832 - if (rc->after) {
833 - if (rc->dimensions)
834 - alert_cfg->p_db_lookup_dimensions = string_dup(rc->dimensions);
835 - if (rc->group)
836 - alert_cfg->p_db_lookup_method = string_strdupz(time_grouping_method2string(rc->group));
837 - alert_cfg->p_db_lookup_options = rc->options;
838 - alert_cfg->p_db_lookup_after = rc->after;
839 - alert_cfg->p_db_lookup_before = rc->before;
840 - alert_cfg->p_update_every = rc->update_every;
841 - }
842 - }
843 - else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
844 - alert_cfg->every = string_strdupz(value);
845 - if(!config_parse_duration(value, &rc->update_every))
846 - netdata_log_error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' cannot parse duration: '%s'.",
847 - line, filename, rrdcalc_name(rc), key, value);
848 - alert_cfg->p_update_every = rc->update_every;
849 - }
850 - else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
851 - alert_cfg->green = string_strdupz(value);
852 - char *e;
853 - rc->green = str2ndd(value, &e);
854 - if(e && *e) {
855 - netdata_log_error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
856 - line, filename, rrdcalc_name(rc), key, e);
857 - }
858 - }
859 - else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
860 - alert_cfg->red = string_strdupz(value);
861 - char *e;
862 - rc->red = str2ndd(value, &e);
863 - if(e && *e) {
864 - netdata_log_error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' leaves this string unmatched: '%s'.",
865 - line, filename, rrdcalc_name(rc), key, e);
866 - }
867 - }
868 - else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
869 - alert_cfg->calc = string_strdupz(value);
870 - const char *failed_at = NULL;
871 - int error = 0;
872 - rc->calculation = expression_parse(value, &failed_at, &error);
873 - if(!rc->calculation) {
874 - netdata_log_error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
875 - line, filename, rrdcalc_name(rc), key, value, expression_strerror(error), failed_at);
876 - }
877 - parse_variables_and_store_in_health_rrdvars(value, HEALTH_CONF_MAX_LINE);
878 - }
879 - else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
880 - alert_cfg->warn = string_strdupz(value);
881 - const char *failed_at = NULL;
882 - int error = 0;
883 - rc->warning = expression_parse(value, &failed_at, &error);
884 - if(!rc->warning) {
885 - netdata_log_error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
886 - line, filename, rrdcalc_name(rc), key, value, expression_strerror(error), failed_at);
887 - }
888 - parse_variables_and_store_in_health_rrdvars(value, HEALTH_CONF_MAX_LINE);
889 - }
890 - else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
891 - alert_cfg->crit = string_strdupz(value);
892 - const char *failed_at = NULL;
893 - int error = 0;
894 - rc->critical = expression_parse(value, &failed_at, &error);
895 - if(!rc->critical) {
896 - netdata_log_error("Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
897 - line, filename, rrdcalc_name(rc), key, value, expression_strerror(error), failed_at);
898 - }
899 - parse_variables_and_store_in_health_rrdvars(value, HEALTH_CONF_MAX_LINE);
900 - }
901 - else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
902 - alert_cfg->exec = string_strdupz(value);
903 - if(rc->exec) {
904 - if(strcmp(rrdcalc_exec(rc), value) != 0)
905 - netdata_log_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').",
906 - line, filename, rrdcalc_name(rc), key, rrdcalc_exec(rc), value, value);
907 -
908 - string_freez(rc->exec);
909 - }
910 - rc->exec = string_strdupz(value);
911 - }
912 - else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
913 - alert_cfg->to = string_strdupz(value);
914 - if(rc->recipient) {
915 - if(strcmp(rrdcalc_recipient(rc), value) != 0)
916 - netdata_log_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').",
917 - line, filename, rrdcalc_name(rc), key, rrdcalc_recipient(rc), value, value);
918 -
919 - string_freez(rc->recipient);
920 - }
921 - rc->recipient = string_strdupz(value);
922 - }
923 - else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
924 - strip_quotes(value);
925 -
926 - alert_cfg->units = string_strdupz(value);
927 - if(rc->units) {
928 - if(strcmp(rrdcalc_units(rc), value) != 0)
929 - netdata_log_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').",
930 - line, filename, rrdcalc_name(rc), key, rrdcalc_units(rc), value, value);
931 -
932 - string_freez(rc->units);
933 - }
934 - rc->units = string_strdupz(value);
935 - }
936 - else if(hash == hash_summary && !strcasecmp(key, HEALTH_SUMMARY_KEY)) {
937 - strip_quotes(value);
938 -
939 - alert_cfg->summary = string_strdupz(value);
940 - if(rc->summary) {
941 - if(strcmp(rrdcalc_summary(rc), value) != 0)
942 - netdata_log_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').",
943 - line, filename, rrdcalc_name(rc), key, rrdcalc_summary(rc), value, value);
944 -
945 - string_freez(rc->summary);
946 - string_freez(rc->original_summary);
947 - }
948 - rc->summary = string_strdupz(value);
949 - rc->original_summary = string_dup(rc->summary);
950 - }
951 - else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
952 - strip_quotes(value);
953 -
954 - alert_cfg->info = string_strdupz(value);
955 - if(rc->info) {
956 - if(strcmp(rrdcalc_info(rc), value) != 0)
957 - netdata_log_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').",
958 - line, filename, rrdcalc_name(rc), key, rrdcalc_info(rc), value, value);
959 -
960 - string_freez(rc->info);
961 - string_freez(rc->original_info);
962 - }
963 - rc->info = string_strdupz(value);
964 - rc->original_info = string_dup(rc->info);
965 - }
966 - else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
967 - alert_cfg->delay = string_strdupz(value);
968 - health_parse_delay(line, filename, value, &rc->delay_up_duration, &rc->delay_down_duration, &rc->delay_max_duration, &rc->delay_multiplier);
969 - }
970 - else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
971 - alert_cfg->options = string_strdupz(value);
972 - rc->options |= health_parse_options(value);
973 - }
974 - else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){
975 - alert_cfg->repeat = string_strdupz(value);
976 - health_parse_repeat(line, filename, value,
977 - &rc->warn_repeat_every,
978 - &rc->crit_repeat_every);
979 - }
980 - else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) {
981 - alert_cfg->host_labels = string_strdupz(value);
982 - if(rc->host_labels) {
983 - if(strcmp(rrdcalc_host_labels(rc), value) != 0)
984 - netdata_log_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'.",
985 - line, filename, rrdcalc_name(rc), key, value, value);
986 -
987 - string_freez(rc->host_labels);
988 - simple_pattern_free(rc->host_labels_pattern);
989 - }
990 -
991 - {
992 - char *tmp = simple_pattern_trim_around_equal(value);
993 - rc->host_labels = string_strdupz(tmp);
994 - freez(tmp);
995 - }
996 - rc->host_labels_pattern = simple_pattern_create(rrdcalc_host_labels(rc), NULL, SIMPLE_PATTERN_EXACT,
997 - true);
998 - }
999 - else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) {
1000 - alert_cfg->plugin = string_strdupz(value);
1001 - string_freez(rc->plugin_match);
1002 - simple_pattern_free(rc->plugin_pattern);
1003 -
1004 - rc->plugin_match = string_strdupz(value);
1005 - rc->plugin_pattern = simple_pattern_create(rrdcalc_plugin_match(rc), NULL, SIMPLE_PATTERN_EXACT, true);
1006 - }
1007 - else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) {
1008 - alert_cfg->module = string_strdupz(value);
1009 - string_freez(rc->module_match);
1010 - simple_pattern_free(rc->module_pattern);
1011 -
1012 - rc->module_match = string_strdupz(value);
1013 - rc->module_pattern = simple_pattern_create(rrdcalc_module_match(rc), NULL, SIMPLE_PATTERN_EXACT, true);
1014 - }
1015 - else if(hash == hash_chart_label && !strcasecmp(key, HEALTH_CHART_LABEL_KEY)) {
1016 - alert_cfg->chart_labels = string_strdupz(value);
1017 - if(rc->chart_labels) {
1018 - if(strcmp(rrdcalc_chart_labels(rc), value) != 0)
1019 - netdata_log_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'.",
1020 - line, filename, rrdcalc_name(rc), key, value, value);
1021 -
1022 - string_freez(rc->chart_labels);
1023 - simple_pattern_free(rc->chart_labels_pattern);
1024 - }
1025 -
1026 - {
1027 - char *tmp = simple_pattern_trim_around_equal(value);
1028 - char *tmp_2 = health_config_add_key_to_values(tmp);
1029 - rc->chart_labels = string_strdupz(tmp_2);
1030 - freez(tmp);
1031 - freez(tmp_2);
1032 - }
1033 - rc->chart_labels_pattern = simple_pattern_create(rrdcalc_chart_labels(rc), NULL, SIMPLE_PATTERN_EXACT,
1034 - true);
1035 - }
1036 - else {
1037 - // "families" has become obsolete and has been removed from standard alarms, but some still have it:
1038 - // alarms of obsolete collectors (e.g. fping, wmi).
1039 - if (strcmp(key, "families"))
1040 - netdata_log_error(
1041 - "Health configuration at line %zu of file '%s' for alarm '%s' has unknown key '%s'.",
1042 - line,
1043 - filename,
1044 - rrdcalc_name(rc),
1045 - key);
1046 - }
542 + else if(am->is_template && hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
543 + PARSE_HEALTH_CONFIG_LINE_STRING(am, on.context);
544 }
1048 - else if(rt) {
1049 - if(hash == hash_on && !strcasecmp(key, HEALTH_ON_KEY)) {
1050 - alert_cfg->on = string_strdupz(value);
1051 - if(rt->context) {
1052 - if(strcmp(string2str(rt->context), value) != 0)
1053 - netdata_log_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 - line, filename, rrdcalctemplate_name(rt), key, string2str(rt->context), value, value);
1055 -
1056 - string_freez(rt->context);
1057 - }
1058 - rt->context = string_strdupz(value);
1059 - }
1060 - else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) {
1061 - strip_quotes(value);
1062 -
1063 - alert_cfg->classification = string_strdupz(value);
1064 - if(rt->classification) {
1065 - if(strcmp(rrdcalctemplate_classification(rt), value) != 0)
1066 - netdata_log_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').",
1067 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_classification(rt), value, value);
1068 -
1069 - string_freez(rt->classification);
1070 - }
1071 - rt->classification = string_strdupz(value);
1072 - }
1073 - else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) {
1074 - strip_quotes(value);
1075 -
1076 - alert_cfg->component = string_strdupz(value);
1077 - if(rt->component) {
1078 - if(strcmp(rrdcalctemplate_component(rt), value) != 0)
1079 - netdata_log_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').",
1080 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_component(rt), value, value);
1081 -
1082 - string_freez(rt->component);
1083 - }
1084 - rt->component = string_strdupz(value);
1085 - }
1086 - else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) {
1087 - strip_quotes(value);
1088 -
1089 - alert_cfg->type = string_strdupz(value);
1090 - if(rt->type) {
1091 - if(strcmp(rrdcalctemplate_type(rt), value) != 0)
1092 - netdata_log_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').",
1093 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_type(rt), value, value);
1094 -
1095 - string_freez(rt->type);
1096 - }
1097 - rt->type = string_strdupz(value);
1098 - }
1099 - else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) {
1100 - alert_cfg->plugin = string_strdupz(value);
1101 - string_freez(rt->plugin_match);
1102 - simple_pattern_free(rt->plugin_pattern);
1103 -
1104 - rt->plugin_match = string_strdupz(value);
1105 - rt->plugin_pattern = simple_pattern_create(rrdcalctemplate_plugin_match(rt), NULL, SIMPLE_PATTERN_EXACT,
1106 - true);
1107 - }
1108 - else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) {
1109 - alert_cfg->module = string_strdupz(value);
1110 - string_freez(rt->module_match);
1111 - simple_pattern_free(rt->module_pattern);
1112 -
1113 - rt->module_match = string_strdupz(value);
1114 - rt->module_pattern = simple_pattern_create(rrdcalctemplate_module_match(rt), NULL, SIMPLE_PATTERN_EXACT,
1115 - true);
1116 - }
1117 - else if(hash == hash_charts && !strcasecmp(key, HEALTH_CHARTS_KEY)) {
1118 - alert_cfg->charts = string_strdupz(value);
1119 - string_freez(rt->charts_match);
1120 - simple_pattern_free(rt->charts_pattern);
1121 -
1122 - rt->charts_match = string_strdupz(value);
1123 - rt->charts_pattern = simple_pattern_create(rrdcalctemplate_charts_match(rt), NULL, SIMPLE_PATTERN_EXACT,
1124 - true);
1125 - }
1126 - else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
1127 - alert_cfg->lookup = string_strdupz(value);
1128 - health_parse_db_lookup(line, filename, value, &rt->group, &rt->after, &rt->before,
1129 - &rt->update_every, &rt->options, &rt->dimensions, &rt->foreach_dimension);
1130 -
1131 - if(rt->foreach_dimension)
1132 - rt->foreach_dimension_pattern = health_pattern_from_foreach(rrdcalctemplate_foreachdim(rt));
1133 -
1134 - if (rt->after) {
1135 - if (rt->dimensions)
1136 - alert_cfg->p_db_lookup_dimensions = string_dup(rt->dimensions);
1137 -
1138 - if (rt->group)
1139 - alert_cfg->p_db_lookup_method = string_strdupz(time_grouping_method2string(rt->group));
1140 -
1141 - alert_cfg->p_db_lookup_options = rt->options;
1142 - alert_cfg->p_db_lookup_after = rt->after;
1143 - alert_cfg->p_db_lookup_before = rt->before;
1144 - alert_cfg->p_update_every = rt->update_every;
1145 - }
1146 - }
1147 - else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
1148 - alert_cfg->every = string_strdupz(value);
1149 - if(!config_parse_duration(value, &rt->update_every))
1150 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' cannot parse duration: '%s'.",
1151 - line, filename, rrdcalctemplate_name(rt), key, value);
1152 - alert_cfg->p_update_every = rt->update_every;
1153 - }
1154 - else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
1155 - alert_cfg->green = string_strdupz(value);
1156 - char *e;
1157 - rt->green = str2ndd(value, &e);
1158 - if(e && *e) {
1159 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
1160 - line, filename, rrdcalctemplate_name(rt), key, e);
1161 - }
1162 - }
1163 - else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
1164 - alert_cfg->red = string_strdupz(value);
1165 - char *e;
1166 - rt->red = str2ndd(value, &e);
1167 - if(e && *e) {
1168 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' leaves this string unmatched: '%s'.",
1169 - line, filename, rrdcalctemplate_name(rt), key, e);
1170 - }
1171 - }
1172 - else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
1173 - alert_cfg->calc = string_strdupz(value);
1174 - const char *failed_at = NULL;
1175 - int error = 0;
1176 - rt->calculation = expression_parse(value, &failed_at, &error);
1177 - if(!rt->calculation) {
1178 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1179 - line, filename, rrdcalctemplate_name(rt), key, value, expression_strerror(error), failed_at);
1180 - }
1181 - parse_variables_and_store_in_health_rrdvars(value, HEALTH_CONF_MAX_LINE);
1182 - }
1183 - else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
1184 - alert_cfg->warn = string_strdupz(value);
1185 - const char *failed_at = NULL;
1186 - int error = 0;
1187 - rt->warning = expression_parse(value, &failed_at, &error);
1188 - if(!rt->warning) {
1189 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1190 - line, filename, rrdcalctemplate_name(rt), key, value, expression_strerror(error), failed_at);
1191 - }
1192 - parse_variables_and_store_in_health_rrdvars(value, HEALTH_CONF_MAX_LINE);
1193 - }
1194 - else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
1195 - alert_cfg->crit = string_strdupz(value);
1196 - const char *failed_at = NULL;
1197 - int error = 0;
1198 - rt->critical = expression_parse(value, &failed_at, &error);
1199 - if(!rt->critical) {
1200 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' at key '%s' has unparse-able expression '%s': %s at '%s'",
1201 - line, filename, rrdcalctemplate_name(rt), key, value, expression_strerror(error), failed_at);
1202 - }
1203 - parse_variables_and_store_in_health_rrdvars(value, HEALTH_CONF_MAX_LINE);
1204 - }
1205 - else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
1206 - alert_cfg->exec = string_strdupz(value);
1207 - if(rt->exec) {
1208 - if(strcmp(rrdcalctemplate_exec(rt), value) != 0)
1209 - netdata_log_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').",
1210 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_exec(rt), value, value);
1211 -
1212 - string_freez(rt->exec);
1213 - }
1214 - rt->exec = string_strdupz(value);
1215 - }
1216 - else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
1217 - alert_cfg->to = string_strdupz(value);
1218 - if(rt->recipient) {
1219 - if(strcmp(rrdcalctemplate_recipient(rt), value) != 0)
1220 - netdata_log_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').",
1221 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_recipient(rt), value, value);
1222 -
1223 - string_freez(rt->recipient);
1224 - }
1225 - rt->recipient = string_strdupz(value);
1226 - }
1227 - else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
1228 - strip_quotes(value);
1229 -
1230 - alert_cfg->units = string_strdupz(value);
1231 - if(rt->units) {
1232 - if(strcmp(rrdcalctemplate_units(rt), value) != 0)
1233 - netdata_log_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').",
1234 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_units(rt), value, value);
1235 -
1236 - string_freez(rt->units);
1237 - }
1238 - rt->units = string_strdupz(value);
1239 - }
1240 - else if(hash == hash_summary && !strcasecmp(key, HEALTH_SUMMARY_KEY)) {
1241 - strip_quotes(value);
1242 -
1243 - alert_cfg->summary = string_strdupz(value);
1244 - if(rt->summary) {
1245 - if(strcmp(rrdcalctemplate_summary(rt), value) != 0)
1246 - netdata_log_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').",
1247 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_summary(rt), value, value);
1248 -
1249 - string_freez(rt->summary);
1250 - }
1251 - rt->summary = string_strdupz(value);
1252 - }
1253 - else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
1254 - strip_quotes(value);
1255 -
1256 - alert_cfg->info = string_strdupz(value);
1257 - if(rt->info) {
1258 - if(strcmp(rrdcalctemplate_info(rt), value) != 0)
1259 - netdata_log_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').",
1260 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_info(rt), value, value);
1261 -
1262 - string_freez(rt->info);
1263 - }
1264 - rt->info = string_strdupz(value);
1265 - }
1266 - else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
1267 - alert_cfg->delay = string_strdupz(value);
1268 - health_parse_delay(line, filename, value, &rt->delay_up_duration, &rt->delay_down_duration, &rt->delay_max_duration, &rt->delay_multiplier);
1269 - }
1270 - else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
1271 - alert_cfg->options = string_strdupz(value);
1272 - rt->options |= health_parse_options(value);
1273 - }
1274 - else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){
1275 - alert_cfg->repeat = string_strdupz(value);
1276 - health_parse_repeat(line, filename, value,
1277 - &rt->warn_repeat_every,
1278 - &rt->crit_repeat_every);
1279 - }
1280 - else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) {
1281 - alert_cfg->host_labels = string_strdupz(value);
1282 - if(rt->host_labels) {
1283 - if(strcmp(rrdcalctemplate_host_labels(rt), value) != 0)
1284 - netdata_log_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').",
1285 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_host_labels(rt), value, value);
1286 -
1287 - string_freez(rt->host_labels);
1288 - simple_pattern_free(rt->host_labels_pattern);
1289 - }
1290 -
1291 - {
1292 - char *tmp = simple_pattern_trim_around_equal(value);
1293 - rt->host_labels = string_strdupz(tmp);
1294 - freez(tmp);
1295 - }
1296 -
1297 - rt->host_labels_pattern = simple_pattern_create(rrdcalctemplate_host_labels(rt), NULL,
1298 - SIMPLE_PATTERN_EXACT, true);
1299 - }
1300 - else if(hash == hash_chart_label && !strcasecmp(key, HEALTH_CHART_LABEL_KEY)) {
1301 - alert_cfg->chart_labels = string_strdupz(value);
1302 - if(rt->chart_labels) {
1303 - if(strcmp(rrdcalctemplate_chart_labels(rt), value) != 0)
1304 - netdata_log_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').",
1305 - line, filename, rrdcalctemplate_name(rt), key, rrdcalctemplate_chart_labels(rt), value, value);
1306 -
1307 - string_freez(rt->chart_labels);
1308 - simple_pattern_free(rt->chart_labels_pattern);
1309 - }
1310 -
1311 - {
1312 - char *tmp = simple_pattern_trim_around_equal(value);
1313 - char *tmp_2 = health_config_add_key_to_values(tmp);
1314 - rt->chart_labels = string_strdupz(tmp_2);
1315 - freez(tmp);
1316 - freez(tmp_2);
1317 - }
1318 - rt->chart_labels_pattern = simple_pattern_create(rrdcalctemplate_chart_labels(rt), NULL,
1319 - SIMPLE_PATTERN_EXACT, true);
1320 - }
1321 - else {
1322 - if (strcmp(key, "families") != 0)
1323 - netdata_log_error("Health configuration at line %zu of file '%s' for template '%s' has unknown key '%s'.",
1324 - line, filename, rrdcalctemplate_name(rt), key);
1325 - }
545 + else if(am->is_template && hash == hash_charts && !strcasecmp(key, HEALTH_CHARTS_KEY)) {
546 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, charts);
547 + }
548 + else if(hash == hash_os && !strcasecmp(key, HEALTH_OS_KEY)) {
549 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, os);
550 + }
551 + else if(hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
552 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, host);
553 + }
554 + else if(hash == hash_host_label && !strcasecmp(key, HEALTH_HOST_LABEL_KEY)) {
555 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, host_labels);
556 + }
557 + else if(hash == hash_plugin && !strcasecmp(key, HEALTH_PLUGIN_KEY)) {
558 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, plugin);
559 + }
560 + else if(hash == hash_module && !strcasecmp(key, HEALTH_MODULE_KEY)) {
561 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, module);
562 + }
563 + else if(hash == hash_chart_label && !strcasecmp(key, HEALTH_CHART_LABEL_KEY)) {
564 + PARSE_HEALTH_CONFIG_LINE_PATTERN(am, chart_labels);
565 + }
566 + else if(hash == hash_class && !strcasecmp(key, HEALTH_CLASS_KEY)) {
567 + strip_quotes(value);
568 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, classification);
569 + }
570 + else if(hash == hash_component && !strcasecmp(key, HEALTH_COMPONENT_KEY)) {
571 + strip_quotes(value);
572 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, component);
573 + }
574 + else if(hash == hash_type && !strcasecmp(key, HEALTH_TYPE_KEY)) {
575 + strip_quotes(value);
576 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, type);
577 + }
578 + else if(hash == hash_lookup && !strcasecmp(key, HEALTH_LOOKUP_KEY)) {
579 + ac->lookup = string_strdupz(value);
580 + health_parse_db_lookup(line, filename, value,
581 + &ac->group, &ac->after, &ac->before,
582 + &ac->update_every, &ac->options,
583 + &ac->dimensions);
584 + }
585 + else if(hash == hash_every && !strcasecmp(key, HEALTH_EVERY_KEY)) {
586 + if(!config_parse_duration(value, &ac->update_every))
587 + netdata_log_error(
588 + "Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' "
589 + "cannot parse duration: '%s'.",
590 + line, filename, string2str(ac->name), key, value);
591 + }
592 + else if(hash == hash_green && !strcasecmp(key, HEALTH_GREEN_KEY)) {
593 + char *e;
594 + ac->green = str2ndd(value, &e);
595 + if(e && *e) {
596 + netdata_log_error(
597 + "Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' "
598 + "leaves this string unmatched: '%s'.",
599 + line, filename, string2str(ac->name), key, e);
600 + }
601 + }
602 + else if(hash == hash_red && !strcasecmp(key, HEALTH_RED_KEY)) {
603 + char *e;
604 + ac->red = str2ndd(value, &e);
605 + if(e && *e) {
606 + netdata_log_error(
607 + "Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' "
608 + "leaves this string unmatched: '%s'.",
609 + line, filename, string2str(ac->name), key, e);
610 + }
611 + }
612 + else if(hash == hash_calc && !strcasecmp(key, HEALTH_CALC_KEY)) {
613 + const char *failed_at = NULL;
614 + int error = 0;
615 + ac->calculation = expression_parse(value, &failed_at, &error);
616 + if(!ac->calculation) {
617 + netdata_log_error(
618 + "Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' "
619 + "has non-parseable expression '%s': %s at '%s'",
620 + line, filename, string2str(ac->name), key, value, expression_strerror(error), failed_at);
621 + am->enabled = false;
622 + }
623 + }
624 + else if(hash == hash_warn && !strcasecmp(key, HEALTH_WARN_KEY)) {
625 + const char *failed_at = NULL;
626 + int error = 0;
627 + ac->warning = expression_parse(value, &failed_at, &error);
628 + if(!ac->warning) {
629 + netdata_log_error(
630 + "Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' "
631 + "has non-parseable expression '%s': %s at '%s'",
632 + line, filename, string2str(ac->name), key, value, expression_strerror(error), failed_at);
633 + am->enabled = false;
634 + }
635 + }
636 + else if(hash == hash_crit && !strcasecmp(key, HEALTH_CRIT_KEY)) {
637 + const char *failed_at = NULL;
638 + int error = 0;
639 + ac->critical = expression_parse(value, &failed_at, &error);
640 + if(!ac->critical) {
641 + netdata_log_error(
642 + "Health configuration at line %zu of file '%s' for alarm '%s' at key '%s' "
643 + "has non-parseable expression '%s': %s at '%s'",
644 + line, filename, string2str(ac->name), key, value, expression_strerror(error), failed_at);
645 + am->enabled = false;
646 + }
647 + }
648 + else if(hash == hash_exec && !strcasecmp(key, HEALTH_EXEC_KEY)) {
649 + strip_quotes(value);
650 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, exec);
651 + }
652 + else if(hash == hash_recipient && !strcasecmp(key, HEALTH_RECIPIENT_KEY)) {
653 + strip_quotes(value);
654 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, recipient);
655 + }
656 + else if(hash == hash_units && !strcasecmp(key, HEALTH_UNITS_KEY)) {
657 + strip_quotes(value);
658 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, units);
659 + }
660 + else if(hash == hash_summary && !strcasecmp(key, HEALTH_SUMMARY_KEY)) {
661 + strip_quotes(value);
662 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, summary);
663 + }
664 + else if(hash == hash_info && !strcasecmp(key, HEALTH_INFO_KEY)) {
665 + strip_quotes(value);
666 + PARSE_HEALTH_CONFIG_LINE_STRING(ac, info);
667 + }
668 + else if(hash == hash_delay && !strcasecmp(key, HEALTH_DELAY_KEY)) {
669 + health_parse_delay(line, filename, value,
670 + &ac->delay_up_duration, &ac->delay_down_duration,
671 + &ac->delay_max_duration, &ac->delay_multiplier);
672 + }
673 + else if(hash == hash_options && !strcasecmp(key, HEALTH_OPTIONS_KEY)) {
674 + ac->alert_action_options |= health_parse_options(value);
675 + }
676 + else if(hash == hash_repeat && !strcasecmp(key, HEALTH_REPEAT_KEY)){
677 + health_parse_repeat(line, filename, value,
678 + &ac->warn_repeat_every,
679 + &ac->crit_repeat_every);
680 + ac->has_custom_repeat_config = true;
681 }
682 else {
1328 - if (!filtered_config)
1329 - netdata_log_error("Health configuration at line %zu of file '%s' has unknown key '%s'. Expected either '" HEALTH_ALARM_KEY "' or '" HEALTH_TEMPLATE_KEY "'.", line, filename, key);
683 + if (strcmp(key, "families") != 0)
684 + netdata_log_error(
685 + "Health configuration at line %zu of file '%s' for alarm/template '%s' has unknown key '%s'.",
686 + line, filename, string2str(ac->name), key);
687 }
688 }
689
1333 - if(rc) {
1334 - //health_add_alarms_loop(host, rc, ignore_this) ;
1335 - if(!alert_hash_and_store_config(rc->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this)
1336 - rrdcalc_free_unused_rrdcalc_loaded_from_config(rc);
1337 - else
1338 - rrdcalc_add_from_config(host, rc);
690 + if(ap) {
691 + health_prototype_add(ap);
692 + freez(ap);
693 }
694
1341 - if(rt) {
1342 - if(!alert_hash_and_store_config(rt->config_hash_id, alert_cfg, sql_store_hashes) || ignore_this)
1343 - rrdcalctemplate_free_unused_rrdcalctemplate_loaded_from_config(rt);
1344 - else
1345 - rrdcalctemplate_add_from_config(host, rt);
1346 - }
1347 -
1348 - if (alert_cfg)
1349 - alert_config_free(alert_cfg);
1350 -
695 fclose(fp);
696 return 1;
697 }
1354 -
1355 -void sql_refresh_hashes(void)
1356 -{
1357 - sql_store_hashes = 1;
1358 -}
1359 -
1360 -void health_readdir(RRDHOST *host, const char *user_path, const char *stock_path, const char *subpath) {
1361 - if(unlikely((!host->health.health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)) ||
1362 - !service_running(SERVICE_HEALTH)) {
1363 - netdata_log_debug(D_HEALTH, "CONFIG health is not enabled for host '%s'", rrdhost_hostname(host));
1364 - return;
1365 - }
1366 -
1367 - int stock_enabled = (int)config_get_boolean(CONFIG_SECTION_HEALTH, "enable stock health configuration",
1368 - CONFIG_BOOLEAN_YES);
1369 -
1370 - if (!stock_enabled) {
1371 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1372 - "[%s]: Netdata will not load stock alarms.",
1373 - rrdhost_hostname(host));
1374 -
1375 - stock_path = user_path;
1376 - }
1377 -
1378 - if (!health_rrdvars)
1379 - health_rrdvars = health_rrdvariables_create();
1380 -
1381 - recursive_config_double_dir_load(user_path, stock_path, subpath, health_readfile, (void *) host, 0);
1382 -
1383 - nd_log(NDLS_DAEMON, NDLP_DEBUG,
1384 - "[%s]: Read health configuration.",
1385 - rrdhost_hostname(host));
1386 -
1387 - sql_store_hashes = 0;
1388 -}
health/health_dyncfg.c new
+603
@@ -0,0 +1,603 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "health_internals.h"
4 +
5 +#define DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX "health:alert:prototype"
6 +
7 +static void health_dyncfg_register_prototype(RRD_ALERT_PROTOTYPE *ap);
8 +
9 +// ---------------------------------------------------------------------------------------------------------------------
10 +// parse the json object of an alert definition
11 +
12 +static bool parse_match(json_object *jobj, const char *path, struct rrd_alert_match *match, BUFFER *error) {
13 + STRING *on = NULL;
14 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "on", on, error, true);
15 + if(match->is_template)
16 + match->on.context = on;
17 + else
18 + match->on.chart = on;
19 +
20 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "os", match->os, error);
21 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host", match->host, error);
22 +
23 + if(match->is_template)
24 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instances", match->charts, error);
25 +
26 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "plugin", match->plugin, error);
27 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "module", match->module, error);
28 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "host_labels", match->host_labels, error);
29 + JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, "instance_labels", match->chart_labels, error);
30 +
31 + return true;
32 +}
33 +
34 +static bool parse_config_value_database_lookup(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
35 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "after", config->after, error);
36 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "before", config->before, error);
37 + JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, "grouping", time_grouping_txt2id, config->group, error);
38 + JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", rrdr_options_parse_one, config->options, error);
39 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "dimensions", config->dimensions, error, true);
40 + return true;
41 +}
42 +static bool parse_config_value(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
43 + JSONC_PARSE_SUBOBJECT(jobj, path, "database_lookup", config, parse_config_value_database_lookup, error);
44 + JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "calculation", config->calculation, error);
45 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "units", config->units, error, true);
46 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "update_every", config->update_every, error);
47 + return true;
48 +}
49 +
50 +static bool parse_config_conditions(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
51 + JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "green", config->green, error);
52 + JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "red", config->red, error);
53 + JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "warning_condition", config->warning, error);
54 + JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, "critical_condition", config->critical, error);
55 + return true;
56 +}
57 +
58 +static bool parse_config_action_delay(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
59 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "up", config->delay_up_duration, error);
60 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "down", config->delay_down_duration, error);
61 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "max", config->delay_max_duration, error);
62 + JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, "multiplier", config->delay_multiplier, error);
63 + return true;
64 +}
65 +static bool parse_config_action_repeat(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
66 + JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, "enabled", config->has_custom_repeat_config, error);
67 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "warning", config->warn_repeat_every, error);
68 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "critical", config->crit_repeat_every, error);
69 + return true;
70 +}
71 +
72 +static bool parse_config_action(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
73 + JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, "options", alert_action_options_parse_one, config->alert_action_options, error);
74 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "execute", config->exec, error, true);
75 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "recipient", config->recipient, error, true);
76 + JSONC_PARSE_SUBOBJECT(jobj, path, "delay", config, parse_config_action_delay, error);
77 + JSONC_PARSE_SUBOBJECT(jobj, path, "repeat", config, parse_config_action_repeat, error);
78 + return true;
79 +}
80 +
81 +static bool parse_config(json_object *jobj, const char *path, struct rrd_alert_config *config, BUFFER *error) {
82 + // we shouldn't parse these from the payload - they are given to us via the function call
83 + // JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, "source_type", dyncfg_source_type2id, config->source_type);
84 + // JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, "source", config->source);
85 +
86 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "summary", config->summary, error, true);
87 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "info", config->info, error, true);
88 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "type", config->type, error, true);
89 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "component", config->component, error, true);
90 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "classification", config->classification, error, true);
91 +
92 + JSONC_PARSE_SUBOBJECT(jobj, path, "value", config, parse_config_value, error);
93 + JSONC_PARSE_SUBOBJECT(jobj, path, "conditions", config, parse_config_conditions, error);
94 + JSONC_PARSE_SUBOBJECT(jobj, path, "action", config, parse_config_action, error);
95 +
96 + return true;
97 +}
98 +
99 +static bool parse_prototype(json_object *jobj, const char *path, RRD_ALERT_PROTOTYPE *base, BUFFER *error) {
100 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, "name", base->config.name, error, false);
101 +
102 + int64_t version;
103 + JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, "format_version", version, error);
104 +
105 + json_object *rules;
106 + if (json_object_object_get_ex(jobj, "rules", &rules)) {
107 + size_t rules_len = json_object_array_length(rules);
108 +
109 + RRD_ALERT_PROTOTYPE *ap = base; // fill the first entry
110 + for (size_t i = 0; i < rules_len; i++) {
111 + if(!ap) {
112 + ap = callocz(1, sizeof(*base));
113 + ap->config.name = string_dup(base->config.name);
114 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(base->_internal.next, ap, _internal.prev, _internal.next);
115 + }
116 +
117 + json_object *rule = json_object_array_get_idx(rules, i);
118 +
119 + JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(rule, path, "enabled", ap->match.enabled, error);
120 +
121 + STRING *type = NULL;
122 + JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(rule, path, "type", type, error, true);
123 + if(string_strcmp(type, "template") == 0)
124 + ap->match.is_template = true;
125 + else if(string_strcmp(type, "instance") == 0)
126 + ap->match.is_template = false;
127 + else {
128 + buffer_sprintf(error, "type is '%s', but it can only be 'instance' or 'template'", string2str(type));
129 + return false;
130 + }
131 +
132 + JSONC_PARSE_SUBOBJECT(rule, path, "match", &ap->match, parse_match, error);
133 + JSONC_PARSE_SUBOBJECT(rule, path, "config", &ap->config, parse_config, error);
134 +
135 + ap = NULL; // so that we will create another one, if available
136 + }
137 + }
138 + else {
139 + buffer_sprintf(error, "the rules array is missing");
140 + return false;
141 + }
142 +
143 + return true;
144 +}
145 +
146 +static RRD_ALERT_PROTOTYPE *health_prototype_payload_parse(const char *payload, size_t payload_len, BUFFER *error, const char *name) {
147 + RRD_ALERT_PROTOTYPE *base = callocz(1, sizeof(*base));
148 + CLEAN_JSON_OBJECT *jobj = NULL;
149 +
150 + struct json_tokener *tokener = json_tokener_new();
151 + if (!tokener) {
152 + buffer_sprintf(error, "failed to allocate memory for json tokener");
153 + goto cleanup;
154 + }
155 +
156 + jobj = json_tokener_parse_ex(tokener, payload, (int)payload_len);
157 + if (json_tokener_get_error(tokener) != json_tokener_success) {
158 + const char *error_msg = json_tokener_error_desc(json_tokener_get_error(tokener));
159 + buffer_sprintf(error, "failed to parse json payload: %s", error_msg);
160 + json_tokener_free(tokener);
161 + goto cleanup;
162 + }
163 + json_tokener_free(tokener);
164 +
165 + if(!parse_prototype(jobj, "", base, error))
166 + goto cleanup;
167 +
168 + if(!base->config.name && name)
169 + base->config.name = string_strdupz(name);
170 +
171 + int i = 1;
172 + for(RRD_ALERT_PROTOTYPE *ap = base; ap; ap = ap->_internal.next, i++) {
173 + if(ap->config.name != base->config.name) {
174 + string_freez(ap->config.name);
175 + ap->config.name = string_dup(base->config.name);
176 + }
177 +
178 + if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation) {
179 + buffer_sprintf(error, "the rule No %d has neither database lookup nor calculation", i);
180 + goto cleanup;
181 + }
182 +
183 + if(ap->match.enabled)
184 + base->_internal.enabled = true;
185 + }
186 +
187 + if(string_strcmp(base->config.name, name) != 0) {
188 + buffer_sprintf(error,
189 + "name parsed ('%s') does not match the name of the alert prototype ('%s')",
190 + string2str(base->config.name), name);
191 + goto cleanup;
192 + }
193 +
194 + return base;
195 +
196 +cleanup:
197 + health_prototype_free(base);
198 + return NULL;
199 +}
200 +
201 +// ---------------------------------------------------------------------------------------------------------------------
202 +// generate the json object of an alert definition
203 +
204 +static inline void health_prototype_rule_to_json_array_member(BUFFER *wb, RRD_ALERT_PROTOTYPE *ap, bool for_hashing) {
205 + buffer_json_add_array_item_object(wb);
206 + {
207 + buffer_json_member_add_boolean(wb, "enabled", ap->match.enabled);
208 + buffer_json_member_add_string(wb, "type", ap->match.is_template ? "template" : "instance");
209 +
210 + buffer_json_member_add_object(wb, "match");
211 + {
212 + if(ap->match.is_template)
213 + buffer_json_member_add_string(wb, "on", string2str(ap->match.on.context));
214 + else
215 + buffer_json_member_add_string(wb, "on", string2str(ap->match.on.chart));
216 +
217 + buffer_json_member_add_string_or_empty(wb, "os", ap->match.os ? string2str(ap->match.os) : "*");
218 + buffer_json_member_add_string_or_empty(wb, "host", ap->match.host ? string2str(ap->match.host) : "*");
219 + buffer_json_member_add_string_or_empty(wb, "instances", ap->match.charts ? string2str(ap->match.charts) : "*");
220 + buffer_json_member_add_string_or_empty(wb, "plugin", ap->match.charts ? string2str(ap->match.plugin) : "*");
221 + buffer_json_member_add_string_or_empty(wb, "module", ap->match.module ? string2str(ap->match.module) : "*");
222 + buffer_json_member_add_string_or_empty(wb, "host_labels", ap->match.host_labels ? string2str(ap->match.host_labels) : "*");
223 + buffer_json_member_add_string_or_empty(wb, "instance_labels", ap->match.chart_labels ? string2str(ap->match.chart_labels) : "*");
224 + }
225 + buffer_json_object_close(wb); // match
226 +
227 + buffer_json_member_add_object(wb, "config");
228 + {
229 + if(!for_hashing) {
230 + buffer_json_member_add_uuid(wb, "hash", &ap->config.hash_id);
231 + buffer_json_member_add_string(wb, "source_type", dyncfg_id2source_type(ap->config.source_type));
232 + buffer_json_member_add_string(wb, "source", string2str(ap->config.source));
233 + }
234 +
235 + buffer_json_member_add_string(wb, "summary", string2str(ap->config.summary));
236 + buffer_json_member_add_string(wb, "info", string2str(ap->config.info));
237 +
238 + buffer_json_member_add_string(wb, "type", string2str(ap->config.type));
239 + buffer_json_member_add_string(wb, "component", string2str(ap->config.component));
240 + buffer_json_member_add_string(wb, "classification", string2str(ap->config.classification));
241 +
242 + buffer_json_member_add_object(wb, "value");
243 + {
244 + buffer_json_member_add_object(wb, "database_lookup");
245 + {
246 + buffer_json_member_add_int64(wb, "after", ap->config.after);
247 + buffer_json_member_add_int64(wb, "before", ap->config.before);
248 + buffer_json_member_add_string(wb, "grouping", time_grouping_id2txt(ap->config.group));
249 + rrdr_options_to_buffer_json_array(wb, "options", ap->config.options);
250 + buffer_json_member_add_string(wb, "dimensions", string2str(ap->config.dimensions));
251 + }
252 + buffer_json_object_close(wb); // database lookup
253 +
254 + buffer_json_member_add_string(wb, "calculation", expression_source(ap->config.calculation));
255 + buffer_json_member_add_string(wb, "units", string2str(ap->config.units));
256 + buffer_json_member_add_uint64(wb, "update_every", ap->config.update_every);
257 + }
258 + buffer_json_object_close(wb); // value
259 +
260 + buffer_json_member_add_object(wb, "conditions");
261 + {
262 + buffer_json_member_add_double(wb, "green", ap->config.green);
263 + buffer_json_member_add_double(wb, "red", ap->config.red);
264 + buffer_json_member_add_string(wb, "warning_condition", expression_source(ap->config.warning));
265 + buffer_json_member_add_string(wb, "critical_condition", expression_source(ap->config.critical));
266 + }
267 + buffer_json_object_close(wb); // conditions
268 +
269 + buffer_json_member_add_object(wb, "action");
270 + {
271 + alert_action_options_to_buffer_json_array(wb, "options", ap->config.alert_action_options);
272 + buffer_json_member_add_string(wb, "execute", string2str(ap->config.exec));
273 + buffer_json_member_add_string(wb, "recipient", string2str(ap->config.recipient));
274 +
275 + buffer_json_member_add_object(wb, "delay");
276 + {
277 + buffer_json_member_add_int64(wb, "up", ap->config.delay_up_duration);
278 + buffer_json_member_add_int64(wb, "down", ap->config.delay_down_duration);
279 + buffer_json_member_add_int64(wb, "max", ap->config.delay_max_duration);
280 + buffer_json_member_add_double(wb, "multiplier", ap->config.delay_multiplier);
281 + }
282 + buffer_json_object_close(wb); // delay
283 +
284 + buffer_json_member_add_object(wb, "repeat");
285 + {
286 + buffer_json_member_add_boolean(wb, "enabled", ap->config.has_custom_repeat_config);
287 + buffer_json_member_add_uint64(wb, "warning", ap->config.has_custom_repeat_config ? ap->config.warn_repeat_every : 0);
288 + buffer_json_member_add_uint64(wb, "critical", ap->config.has_custom_repeat_config ? ap->config.crit_repeat_every : 0);
289 + }
290 + buffer_json_object_close(wb); // repeat
291 + }
292 + buffer_json_object_close(wb); // action
293 + }
294 + buffer_json_object_close(wb); // match
295 + }
296 + buffer_json_object_close(wb); // array item
297 +}
298 +
299 +void health_prototype_to_json(BUFFER *wb, RRD_ALERT_PROTOTYPE *ap, bool for_hashing) {
300 + buffer_flush(wb);
301 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_MINIFY);
302 +
303 + buffer_json_member_add_uint64(wb, "format_version", 1);
304 + buffer_json_member_add_string(wb, "name", string2str(ap->config.name));
305 + buffer_json_member_add_array(wb, "rules");
306 + {
307 + for(RRD_ALERT_PROTOTYPE *t = ap; t ; t = t->_internal.next)
308 + health_prototype_rule_to_json_array_member(wb, t, for_hashing);
309 + }
310 + buffer_json_array_close(wb); // rules
311 + buffer_json_finalize(wb);
312 +}
313 +
314 +// ---------------------------------------------------------------------------------------------------------------------
315 +
316 +static size_t dyncfg_health_remove_all_rrdcalc_of_prototype(STRING *alert_name) {
317 + size_t removed = 0;
318 +
319 + RRDHOST *host;
320 + dfe_start_reentrant(rrdhost_root_index, host) {
321 + RRDCALC *rc;
322 + foreach_rrdcalc_in_rrdhost_read(host, rc) {
323 + if(rc->config.name != alert_name)
324 + continue;
325 +
326 + rrdcalc_unlink_and_delete(host, rc, false);
327 + removed++;
328 + }
329 + foreach_rrdcalc_in_rrdhost_done(rc);
330 + }
331 + dfe_done(host);
332 +
333 + return removed;
334 +}
335 +
336 +static void dyncfg_health_prototype_reapply(RRD_ALERT_PROTOTYPE *ap) {
337 + dyncfg_health_remove_all_rrdcalc_of_prototype(ap->config.name);
338 + health_prototype_apply_to_all_hosts(ap);
339 +}
340 +
341 +static int dyncfg_health_prototype_template_action(BUFFER *result, DYNCFG_CMDS cmd, const char *add_name, BUFFER *payload, const char *source __maybe_unused) {
342 + int code = HTTP_RESP_INTERNAL_SERVER_ERROR;
343 + switch(cmd) {
344 + case DYNCFG_CMD_ADD: {
345 + CLEAN_BUFFER *error = buffer_create(0, NULL);
346 + RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, add_name);
347 + if(!nap)
348 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
349 + else {
350 + nap->config.source_type = DYNCFG_SOURCE_TYPE_DYNCFG;
351 + bool added = health_prototype_add(nap); // this swaps ap <-> nap
352 +
353 + if(!added) {
354 + health_prototype_free(nap);
355 + return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "required attributes are missing");
356 + }
357 + else
358 + freez(nap);
359 +
360 + const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(health_globals.prototypes.dict, add_name);
361 + if(!item)
362 + return dyncfg_default_response(result, HTTP_RESP_INTERNAL_SERVER_ERROR, "added prototype is not found");
363 +
364 + RRD_ALERT_PROTOTYPE *ap = dictionary_acquired_item_value(item);
365 +
366 + dyncfg_health_prototype_reapply(ap);
367 + health_dyncfg_register_prototype(ap);
368 + code = ap->_internal.enabled ? DYNCFG_RESP_ACCEPTED : DYNCFG_RESP_ACCEPTED_DISABLED;
369 + dictionary_acquired_item_release(health_globals.prototypes.dict, item);
370 +
371 + code = dyncfg_default_response(result, code, "accepted");
372 + }
373 + }
374 + break;
375 +
376 + case DYNCFG_CMD_SCHEMA:
377 + code = dyncfg_default_response(result, HTTP_RESP_NOT_IMPLEMENTED, "schema not implemented yet for prototype templates");
378 + break;
379 +
380 + case DYNCFG_CMD_REMOVE:
381 + case DYNCFG_CMD_RESTART:
382 + case DYNCFG_CMD_DISABLE:
383 + case DYNCFG_CMD_ENABLE:
384 + case DYNCFG_CMD_UPDATE:
385 + case DYNCFG_CMD_TEST:
386 + case DYNCFG_CMD_GET:
387 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "action given is not supported for prototype templates");
388 + break;
389 +
390 + case DYNCFG_CMD_NONE:
391 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "invalid action received for prototype templates");
392 + break;
393 + }
394 +
395 + return code;
396 +}
397 +
398 +static int dyncfg_health_prototype_job_action(BUFFER *result, DYNCFG_CMDS cmd, BUFFER *payload, const char *source __maybe_unused, const char *alert_name) {
399 + const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(health_globals.prototypes.dict, alert_name);
400 + if(!item)
401 + return dyncfg_default_response(result, HTTP_RESP_NOT_FOUND, "no alert prototype is available by the name given");
402 +
403 + RRD_ALERT_PROTOTYPE *ap = dictionary_acquired_item_value(item);
404 +
405 + char alert_name_dyncfg[strlen(DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX) + strlen(alert_name) + 10];
406 + snprintfz(alert_name_dyncfg, sizeof(alert_name_dyncfg), DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX ":%s", alert_name);
407 +
408 + int code = HTTP_RESP_INTERNAL_SERVER_ERROR;
409 +
410 + switch(cmd) {
411 + case DYNCFG_CMD_SCHEMA:
412 + code = dyncfg_default_response(result, HTTP_RESP_NOT_IMPLEMENTED, "schema not implemented yet");
413 + break;
414 +
415 + case DYNCFG_CMD_GET:
416 + health_prototype_to_json(result, ap, false);
417 + code = HTTP_RESP_OK;
418 + break;
419 +
420 + case DYNCFG_CMD_DISABLE:
421 + if(ap->_internal.enabled) {
422 + ap->_internal.enabled = false;
423 + dyncfg_health_prototype_reapply(ap);
424 + dyncfg_status(localhost, alert_name_dyncfg, DYNCFG_STATUS_DISABLED);
425 + code = dyncfg_default_response(result, HTTP_RESP_OK, "disabled");
426 + }
427 + else
428 + code = dyncfg_default_response(result, HTTP_RESP_OK, "already disabled");
429 + break;
430 +
431 + case DYNCFG_CMD_ENABLE:
432 + if(ap->_internal.enabled)
433 + code = dyncfg_default_response(result, HTTP_RESP_OK, "already enabled");
434 + else {
435 + size_t matches_enabled = 0;
436 + spinlock_lock(&ap->_internal.spinlock);
437 + for(RRD_ALERT_PROTOTYPE *t = ap; t ;t = t->_internal.next)
438 + if(t->match.enabled)
439 + matches_enabled++;
440 + spinlock_unlock(&ap->_internal.spinlock);
441 +
442 + if(!matches_enabled) {
443 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "all rules in this alert are disabled, so enabling the alert has no effect");
444 + }
445 + else {
446 + ap->_internal.enabled = true;
447 + dyncfg_health_prototype_reapply(ap);
448 + dyncfg_status(localhost, alert_name_dyncfg, DYNCFG_STATUS_ACCEPTED);
449 + code = dyncfg_default_response(result, DYNCFG_RESP_ACCEPTED, "enabled");
450 + }
451 + }
452 + break;
453 +
454 + case DYNCFG_CMD_UPDATE: {
455 + CLEAN_BUFFER *error = buffer_create(0, NULL);
456 + RRD_ALERT_PROTOTYPE *nap = health_prototype_payload_parse(buffer_tostring(payload), buffer_strlen(payload), error, alert_name);
457 + if(!nap)
458 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, buffer_tostring(error));
459 + else {
460 + nap->config.source_type = DYNCFG_SOURCE_TYPE_DYNCFG;
461 + bool added = health_prototype_add(nap); // this swaps ap <-> nap
462 +
463 + if(!added) {
464 + health_prototype_free(nap);
465 + return dyncfg_default_response( result, HTTP_RESP_BAD_REQUEST, "required attributes are missing");
466 + }
467 + else
468 + freez(nap);
469 +
470 + dyncfg_health_prototype_reapply(ap);
471 + code = ap->_internal.enabled ? DYNCFG_RESP_ACCEPTED : DYNCFG_RESP_ACCEPTED_DISABLED;
472 + code = dyncfg_default_response(result, code, "updated");
473 + }
474 + }
475 + break;
476 +
477 + case DYNCFG_CMD_REMOVE:
478 + dyncfg_health_remove_all_rrdcalc_of_prototype(ap->config.name);
479 + dictionary_del(health_globals.prototypes.dict, dictionary_acquired_item_name(item));
480 + code = dyncfg_default_response(result, HTTP_RESP_OK, "deleted");
481 + dyncfg_del(localhost, alert_name_dyncfg);
482 + break;
483 +
484 + case DYNCFG_CMD_TEST:
485 + case DYNCFG_CMD_ADD:
486 + case DYNCFG_CMD_RESTART:
487 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "action given is not supported for the prototype job");
488 + break;
489 +
490 + case DYNCFG_CMD_NONE:
491 + code = dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "invalid action received");
492 + break;
493 + }
494 +
495 + dictionary_acquired_item_release(health_globals.prototypes.dict, item);
496 + return code;
497 +}
498 +
499 +int dyncfg_health_cb(const char *transaction __maybe_unused, const char *id, DYNCFG_CMDS cmd, const char *add_name,
500 + BUFFER *payload, usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused,
501 + BUFFER *result, const char *source, void *data __maybe_unused) {
502 +
503 + char buf[strlen(id) + 1];
504 + memcpy(buf, id, sizeof(buf));
505 +
506 + char *words[100] = { NULL };
507 + size_t num_words = quoted_strings_splitter_dyncfg_id(buf, words, 100);
508 + size_t i = 0;
509 + int code = HTTP_RESP_INTERNAL_SERVER_ERROR;
510 +
511 + char *health_prefix = get_word(words, num_words, i++);
512 + if(!health_prefix || !*health_prefix || strcmp(health_prefix, "health") != 0)
513 + return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "first component of id is not 'health'");
514 +
515 + char *alert_prefix = get_word(words, num_words, i++);
516 + if(!alert_prefix || !*alert_prefix || strcmp(alert_prefix, "alert") != 0)
517 + return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "second component of id is not 'alert'");
518 +
519 + char *type_prefix = get_word(words, num_words, i++);
520 + if(!type_prefix || !*type_prefix || strcmp(type_prefix, "prototype") != 0)
521 + return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "third component of id is not 'prototype'");
522 +
523 + char *alert_name = get_word(words, num_words, i++);
524 + if(!alert_name || !*alert_name) {
525 + // action on the prototype template
526 +
527 + code = dyncfg_health_prototype_template_action(result, cmd, add_name, payload, source);
528 + }
529 + else {
530 + // action on a specific alert prototype
531 +
532 + code = dyncfg_health_prototype_job_action(result, cmd, payload, source, alert_name);
533 + }
534 + return code;
535 +}
536 +
537 +void health_dyncfg_unregister_all_prototypes(void) {
538 + char key[HEALTH_CONF_MAX_LINE];
539 + RRD_ALERT_PROTOTYPE *ap;
540 +
541 + // remove dyncfg
542 + // it is ok if they are not added before
543 +
544 + dfe_start_read(health_globals.prototypes.dict, ap) {
545 + snprintfz(key, sizeof(key), DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX ":%s", string2str(ap->config.name));
546 + dyncfg_del(localhost, key);
547 + }
548 + dfe_done(ap);
549 + dyncfg_del(localhost, DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX);
550 +}
551 +
552 +static void health_dyncfg_register_prototype(RRD_ALERT_PROTOTYPE *ap) {
553 + char key[HEALTH_CONF_MAX_LINE];
554 +
555 +// bool trace = false;
556 +// if(string_strcmp(ap->config.name, "ram_available") == 0)
557 +// trace = true;
558 +
559 + snprintfz(key, sizeof(key), DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX ":%s", string2str(ap->config.name));
560 + dyncfg_add(localhost, key, "/health/alerts/prototypes",
561 + ap->_internal.enabled ? DYNCFG_STATUS_ACCEPTED : DYNCFG_STATUS_DISABLED, DYNCFG_TYPE_JOB,
562 + ap->config.source_type, string2str(ap->config.source),
563 + DYNCFG_CMD_SCHEMA | DYNCFG_CMD_GET | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE |
564 + DYNCFG_CMD_UPDATE | DYNCFG_CMD_TEST |
565 + (ap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG && !ap->_internal.is_on_disk ? DYNCFG_CMD_REMOVE : 0),
566 + dyncfg_health_cb, NULL);
567 +
568 +#ifdef NETDATA_TEST_HEALTH_PROTOTYPES_JSON_AND_PARSING
569 + {
570 + // make sure we can generate valid json, parse it back and come up to the same object
571 +
572 + CLEAN_BUFFER *original = buffer_create(0, NULL);
573 + CLEAN_BUFFER *parsed = buffer_create(0, NULL);
574 + CLEAN_BUFFER *error = buffer_create(0, NULL);
575 + health_prototype_to_json(original, ap, true);
576 + RRD_ALERT_PROTOTYPE *t = health_prototype_payload_parse(buffer_tostring(original), buffer_strlen(original), error, string2str(ap->config.name));
577 + if(!t)
578 + fatal("hey! cannot parse: %s", buffer_tostring(error));
579 +
580 + health_prototype_to_json(parsed, t, true);
581 +
582 + if(strcmp(buffer_tostring(original), buffer_tostring(parsed)) != 0)
583 + fatal("hey! they are different!");
584 + }
585 +#endif
586 +}
587 +
588 +void health_dyncfg_register_all_prototypes(void) {
589 + RRD_ALERT_PROTOTYPE *ap;
590 +
591 + dyncfg_add(localhost,
592 + DYNCFG_HEALTH_ALERT_PROTOTYPE_PREFIX, "/health/alerts/prototypes",
593 + DYNCFG_STATUS_ACCEPTED, DYNCFG_TYPE_TEMPLATE,
594 + DYNCFG_SOURCE_TYPE_INTERNAL, "internal",
595 + DYNCFG_CMD_SCHEMA | DYNCFG_CMD_ADD | DYNCFG_CMD_ENABLE | DYNCFG_CMD_DISABLE,
596 + dyncfg_health_cb, NULL);
597 +
598 + dfe_start_read(health_globals.prototypes.dict, ap) {
599 + if(ap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG)
600 + health_dyncfg_register_prototype(ap);
601 + }
602 + dfe_done(ap);
603 +}
health/health_event_loop.c new
+751
@@ -0,0 +1,751 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "health.h"
4 +#include "health_internals.h"
5 +
6 +#define WORKER_HEALTH_JOB_RRD_LOCK 0
7 +#define WORKER_HEALTH_JOB_HOST_LOCK 1
8 +#define WORKER_HEALTH_JOB_DB_QUERY 2
9 +#define WORKER_HEALTH_JOB_CALC_EVAL 3
10 +#define WORKER_HEALTH_JOB_WARNING_EVAL 4
11 +#define WORKER_HEALTH_JOB_CRITICAL_EVAL 5
12 +#define WORKER_HEALTH_JOB_ALARM_LOG_ENTRY 6
13 +#define WORKER_HEALTH_JOB_ALARM_LOG_PROCESS 7
14 +#define WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET 8
15 +#define WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM 9
16 +
17 +#if WORKER_UTILIZATION_MAX_JOB_TYPES < 10
18 +#error WORKER_UTILIZATION_MAX_JOB_TYPES has to be at least 10
19 +#endif
20 +
21 +// ----------------------------------------------------------------------------
22 +// health main thread and friends
23 +
24 +static inline RRDCALC_STATUS rrdcalc_value2status(NETDATA_DOUBLE n) {
25 + if(isnan(n) || isinf(n)) return RRDCALC_STATUS_UNDEFINED;
26 + if(n) return RRDCALC_STATUS_RAISED;
27 + return RRDCALC_STATUS_CLEAR;
28 +}
29 +
30 +static inline int rrdcalc_isrunnable(RRDCALC *rc, time_t now, time_t *next_run) {
31 + if(unlikely(!rc->rrdset)) {
32 + netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. It is not linked to a chart.", rrdcalc_chart_name(rc), rrdcalc_name(rc));
33 + return 0;
34 + }
35 +
36 + if(unlikely(rc->next_update > now)) {
37 + if (unlikely(*next_run > rc->next_update)) {
38 + // update the next_run time of the main loop
39 + // to run this alarm precisely the time required
40 + *next_run = rc->next_update;
41 + }
42 +
43 + netdata_log_debug(D_HEALTH, "Health not examining alarm '%s.%s' yet (will do in %d secs).", rrdcalc_chart_name(rc), rrdcalc_name(rc), (int) (rc->next_update - now));
44 + return 0;
45 + }
46 +
47 + if(unlikely(!rc->config.update_every)) {
48 + netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. It does not have an update frequency", rrdcalc_chart_name(rc), rrdcalc_name(rc));
49 + return 0;
50 + }
51 +
52 + if(unlikely(rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE))) {
53 + netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. The chart has been marked as obsolete", rrdcalc_chart_name(rc), rrdcalc_name(rc));
54 + return 0;
55 + }
56 +
57 + if(unlikely(!rc->rrdset->last_collected_time.tv_sec || rc->rrdset->counter_done < 2)) {
58 + netdata_log_debug(D_HEALTH, "Health not running alarm '%s.%s'. Chart is not fully collected yet.", rrdcalc_chart_name(rc), rrdcalc_name(rc));
59 + return 0;
60 + }
61 +
62 + int update_every = rc->rrdset->update_every;
63 + time_t first = rrdset_first_entry_s(rc->rrdset);
64 + time_t last = rrdset_last_entry_s(rc->rrdset);
65 +
66 + if(unlikely(now + update_every < first /* || now - update_every > last */)) {
67 + netdata_log_debug(D_HEALTH
68 + , "Health not examining alarm '%s.%s' yet (wanted time is out of bounds - we need %lu but got %lu - %lu)."
69 + , rrdcalc_chart_name(rc), rrdcalc_name(rc), (unsigned long) now, (unsigned long) first
70 + , (unsigned long) last);
71 + return 0;
72 + }
73 +
74 + if(RRDCALC_HAS_DB_LOOKUP(rc)) {
75 + time_t needed = now + rc->config.before + rc->config.after;
76 +
77 + if(needed + update_every < first || needed - update_every > last) {
78 + netdata_log_debug(D_HEALTH
79 + , "Health not examining alarm '%s.%s' yet (not enough data yet - we need %lu but got %lu - %lu)."
80 + , rrdcalc_chart_name(rc), rrdcalc_name(rc), (unsigned long) needed, (unsigned long) first
81 + , (unsigned long) last);
82 + return 0;
83 + }
84 + }
85 +
86 + return 1;
87 +}
88 +
89 +static void health_sleep(time_t next_run, unsigned int loop __maybe_unused) {
90 + time_t now = now_realtime_sec();
91 + if(now < next_run) {
92 + worker_is_idle();
93 + netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration in %d secs", loop, (int) (next_run - now));
94 + while (now < next_run && service_running(SERVICE_HEALTH)) {
95 + sleep_usec(USEC_PER_SEC);
96 + now = now_realtime_sec();
97 + }
98 + }
99 + else {
100 + netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u done. Next iteration now", loop);
101 + }
102 +}
103 +
104 +static void sql_health_postpone_queue_removed(RRDHOST *host __maybe_unused) {
105 +#ifdef ENABLE_ACLK
106 + if (netdata_cloud_enabled) {
107 + struct aclk_sync_cfg_t *wc = host->aclk_config;
108 + if (unlikely(!wc)) {
109 + return;
110 + }
111 +
112 + if (wc->alert_queue_removed >= 1) {
113 + wc->alert_queue_removed+=6;
114 + }
115 + }
116 +#endif
117 +}
118 +
119 +static void health_execute_delayed_initializations(RRDHOST *host) {
120 + health_plugin_init();
121 +
122 + RRDSET *st;
123 + bool must_postpone = false;
124 +
125 + if (!rrdhost_flag_check(host, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION)) return;
126 + rrdhost_flag_clear(host, RRDHOST_FLAG_PENDING_HEALTH_INITIALIZATION);
127 +
128 + rrdset_foreach_reentrant(st, host) {
129 + if(!rrdset_flag_check(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION)) continue;
130 + rrdset_flag_clear(st, RRDSET_FLAG_PENDING_HEALTH_INITIALIZATION);
131 +
132 + worker_is_busy(WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET);
133 + health_prototype_alerts_for_rrdset_incrementally(st);
134 + must_postpone = true;
135 + }
136 + rrdset_foreach_done(st);
137 + if (must_postpone)
138 + sql_health_postpone_queue_removed(host);
139 +}
140 +
141 +static void health_initialize_rrdhost(RRDHOST *host) {
142 + health_plugin_init();
143 +
144 + if(!host->health.health_enabled ||
145 + rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH) ||
146 + !service_running(SERVICE_HEALTH))
147 + return;
148 +
149 + rrdhost_flag_set(host, RRDHOST_FLAG_INITIALIZED_HEALTH);
150 +
151 + host->health.health_default_warn_repeat_every = health_globals.config.default_warn_repeat_every;
152 + host->health.health_default_crit_repeat_every = health_globals.config.default_crit_repeat_every;
153 + host->health_log.max = health_globals.config.health_log_entries_max;
154 + host->health_log.health_log_history = health_globals.config.health_log_history;
155 + host->health.health_default_exec = string_dup(health_globals.config.default_exec);
156 + host->health.health_default_recipient = string_dup(health_globals.config.default_recipient);
157 + host->health.use_summary_for_notifications = health_globals.config.use_summary_for_notifications;
158 +
159 + host->health_log.next_log_id = (uint32_t)now_realtime_sec();
160 + host->health_log.next_alarm_id = 0;
161 +
162 + rw_spinlock_init(&host->health_log.spinlock);
163 + sql_health_alarm_log_load(host);
164 + health_apply_prototypes_to_host(host);
165 +}
166 +
167 +static inline int check_if_resumed_from_suspension(void) {
168 + static usec_t last_realtime = 0, last_monotonic = 0;
169 + usec_t realtime = now_realtime_usec(), monotonic = now_monotonic_usec();
170 + int ret = 0;
171 +
172 + // detect if monotonic and realtime have twice the difference
173 + // in which case we assume the system was just waken from hibernation
174 +
175 + if(last_realtime && last_monotonic && realtime - last_realtime > 2 * (monotonic - last_monotonic))
176 + ret = 1;
177 +
178 + last_realtime = realtime;
179 + last_monotonic = monotonic;
180 +
181 + return ret;
182 +}
183 +
184 +static void health_event_loop(void) {
185 + bool health_running_logged = false;
186 +
187 + unsigned int loop = 0;
188 +
189 + while(service_running(SERVICE_HEALTH)) {
190 + loop++;
191 + netdata_log_debug(D_HEALTH, "Health monitoring iteration no %u started", loop);
192 +
193 + time_t now = now_realtime_sec();
194 + int runnable = 0, apply_hibernation_delay = 0;
195 + time_t next_run = now + health_globals.config.run_at_least_every_seconds;
196 + RRDCALC *rc;
197 + RRDHOST *host;
198 +
199 + if (unlikely(check_if_resumed_from_suspension())) {
200 + apply_hibernation_delay = 1;
201 +
202 + nd_log(NDLS_DAEMON, NDLP_NOTICE,
203 + "Postponing alarm checks for %"PRId32" seconds, "
204 + "because it seems that the system was just resumed from suspension.",
205 + (int32_t)health_globals.config.postpone_alarms_during_hibernation_for_seconds);
206 + }
207 +
208 + if (unlikely(silencers->all_alarms && silencers->stype == STYPE_DISABLE_ALARMS)) {
209 + static int logged=0;
210 + if (!logged) {
211 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
212 + "Skipping health checks, because all alarms are disabled via API command.");
213 + logged = 1;
214 + }
215 + }
216 +
217 + worker_is_busy(WORKER_HEALTH_JOB_RRD_LOCK);
218 + dfe_start_reentrant(rrdhost_root_index, host) {
219 +
220 + if(unlikely(!service_running(SERVICE_HEALTH)))
221 + break;
222 +
223 + if (unlikely(!host->health.health_enabled))
224 + continue;
225 +
226 + if (unlikely(!rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH)))
227 + health_initialize_rrdhost(host);
228 +
229 + health_execute_delayed_initializations(host);
230 +
231 + if (unlikely(apply_hibernation_delay)) {
232 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
233 + "[%s]: Postponing health checks for %"PRId32" seconds.",
234 + rrdhost_hostname(host),
235 + health_globals.config.postpone_alarms_during_hibernation_for_seconds);
236 +
237 + host->health.health_delay_up_to =
238 + now + health_globals.config.postpone_alarms_during_hibernation_for_seconds;
239 + }
240 +
241 + if (unlikely(host->health.health_delay_up_to)) {
242 + if (unlikely(now < host->health.health_delay_up_to)) {
243 + continue;
244 + }
245 +
246 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
247 + "[%s]: Resuming health checks after delay.",
248 + rrdhost_hostname(host));
249 +
250 + host->health.health_delay_up_to = 0;
251 + }
252 +
253 + // wait until cleanup of obsolete charts on children is complete
254 + if (host != localhost) {
255 + if (unlikely(host->trigger_chart_obsoletion_check == 1)) {
256 +
257 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
258 + "[%s]: Waiting for chart obsoletion check.",
259 + rrdhost_hostname(host));
260 +
261 + continue;
262 + }
263 + }
264 +
265 + if (!health_running_logged) {
266 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
267 + "[%s]: Health is running.",
268 + rrdhost_hostname(host));
269 +
270 + health_running_logged = true;
271 + }
272 +
273 + worker_is_busy(WORKER_HEALTH_JOB_HOST_LOCK);
274 +
275 + // the first loop is to lookup values from the db
276 + foreach_rrdcalc_in_rrdhost_read(host, rc) {
277 +
278 + if(unlikely(!service_running(SERVICE_HEALTH)))
279 + break;
280 +
281 + rrdcalc_update_info_using_rrdset_labels(rc);
282 +
283 + if (health_silencers_update_disabled_silenced(host, rc))
284 + continue;
285 +
286 + // create an alert removed event if the chart is obsolete and
287 + // has stopped being collected for 60 seconds
288 + if (unlikely(rc->rrdset && rc->status != RRDCALC_STATUS_REMOVED &&
289 + rrdset_flag_check(rc->rrdset, RRDSET_FLAG_OBSOLETE) &&
290 + now > (rc->rrdset->last_collected_time.tv_sec + 60))) {
291 + if (!rrdcalc_isrepeating(rc)) {
292 + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
293 + time_t now_tmp = now_realtime_sec();
294 +
295 + ALARM_ENTRY *ae =
296 + health_create_alarm_entry(
297 + host,
298 + rc,
299 + now_tmp,
300 + now_tmp - rc->last_status_change,
301 + rc->value,
302 + NAN,
303 + rc->status,
304 + RRDCALC_STATUS_REMOVED,
305 + 0,
306 + rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0);
307 +
308 + if (ae) {
309 + health_log_alert(host, ae);
310 + health_alarm_log_add_entry(host, ae);
311 + rc->old_status = rc->status;
312 + rc->status = RRDCALC_STATUS_REMOVED;
313 + rc->last_status_change = now_tmp;
314 + rc->last_status_change_value = rc->value;
315 + rc->last_updated = now_tmp;
316 + rc->value = NAN;
317 +
318 +#ifdef ENABLE_ACLK
319 + if (netdata_cloud_enabled)
320 + sql_queue_alarm_to_aclk(host, ae, true);
321 +#endif
322 + }
323 + }
324 + }
325 +
326 + if (unlikely(!rrdcalc_isrunnable(rc, now, &next_run))) {
327 + if (unlikely(rc->run_flags & RRDCALC_FLAG_RUNNABLE))
328 + rc->run_flags &= ~RRDCALC_FLAG_RUNNABLE;
329 + continue;
330 + }
331 +
332 + runnable++;
333 + rc->old_value = rc->value;
334 + rc->run_flags |= RRDCALC_FLAG_RUNNABLE;
335 +
336 + // ------------------------------------------------------------
337 + // if there is database lookup, do it
338 +
339 + if (unlikely(RRDCALC_HAS_DB_LOOKUP(rc))) {
340 + worker_is_busy(WORKER_HEALTH_JOB_DB_QUERY);
341 +
342 + /* time_t old_db_timestamp = rc->db_before; */
343 + int value_is_null = 0;
344 +
345 + int ret = rrdset2value_api_v1(rc->rrdset, NULL, &rc->value, rrdcalc_dimensions(rc), 1,
346 + rc->config.after, rc->config.before, rc->config.group, NULL,
347 + 0, rc->config.options | RRDR_OPTION_SELECTED_TIER,
348 + &rc->db_after,&rc->db_before,
349 + NULL, NULL, NULL,
350 + &value_is_null, NULL, 0, 0,
351 + QUERY_SOURCE_HEALTH, STORAGE_PRIORITY_SYNCHRONOUS);
352 +
353 + if (unlikely(ret != 200)) {
354 + // database lookup failed
355 + rc->value = NAN;
356 + rc->run_flags |= RRDCALC_FLAG_DB_ERROR;
357 +
358 + netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup returned error %d",
359 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), ret
360 + );
361 + } else
362 + rc->run_flags &= ~RRDCALC_FLAG_DB_ERROR;
363 +
364 + if (unlikely(value_is_null)) {
365 + // collected value is null
366 + rc->value = NAN;
367 + rc->run_flags |= RRDCALC_FLAG_DB_NAN;
368 +
369 + netdata_log_debug(D_HEALTH,
370 + "Health on host '%s', alarm '%s.%s': database lookup returned empty value (possibly value is not collected yet)",
371 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc)
372 + );
373 + } else
374 + rc->run_flags &= ~RRDCALC_FLAG_DB_NAN;
375 +
376 + netdata_log_debug(D_HEALTH, "Health on host '%s', alarm '%s.%s': database lookup gave value " NETDATA_DOUBLE_FORMAT,
377 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc), rc->value
378 + );
379 + }
380 +
381 + // ------------------------------------------------------------
382 + // if there is calculation expression, run it
383 +
384 + if (unlikely(rc->config.calculation)) {
385 + worker_is_busy(WORKER_HEALTH_JOB_CALC_EVAL);
386 +
387 + if (unlikely(!expression_evaluate(rc->config.calculation))) {
388 + // calculation failed
389 + rc->value = NAN;
390 + rc->run_flags |= RRDCALC_FLAG_CALC_ERROR;
391 +
392 + netdata_log_debug(
393 + D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' failed: %s",
394 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
395 + expression_parsed_as(rc->config.calculation), expression_error_msg(rc->config.calculation)
396 + );
397 + }
398 + else {
399 + rc->run_flags &= ~RRDCALC_FLAG_CALC_ERROR;
400 +
401 + netdata_log_debug(
402 + D_HEALTH, "Health on host '%s', alarm '%s.%s': expression '%s' gave value "
403 + NETDATA_DOUBLE_FORMAT": %s (source: %s)",
404 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
405 + expression_parsed_as(rc->config.calculation),
406 + expression_result(rc->config.calculation),
407 + expression_error_msg(rc->config.calculation),
408 + rrdcalc_source(rc)
409 + );
410 +
411 + rc->value = expression_result(rc->config.calculation);
412 + }
413 + }
414 + }
415 + foreach_rrdcalc_in_rrdhost_done(rc);
416 +
417 + struct health_raised_summary *hrm = alerts_raised_summary_create(host);
418 +
419 + if (unlikely(runnable && service_running(SERVICE_HEALTH))) {
420 + foreach_rrdcalc_in_rrdhost_read(host, rc) {
421 + if(unlikely(!service_running(SERVICE_HEALTH)))
422 + break;
423 +
424 + if (unlikely(!(rc->run_flags & RRDCALC_FLAG_RUNNABLE)))
425 + continue;
426 +
427 + if (rc->run_flags & RRDCALC_FLAG_DISABLED) {
428 + continue;
429 + }
430 + RRDCALC_STATUS warning_status = RRDCALC_STATUS_UNDEFINED;
431 + RRDCALC_STATUS critical_status = RRDCALC_STATUS_UNDEFINED;
432 +
433 + // --------------------------------------------------------
434 + // check the warning expression
435 +
436 + if (likely(rc->config.warning)) {
437 + worker_is_busy(WORKER_HEALTH_JOB_WARNING_EVAL);
438 +
439 + if (unlikely(!expression_evaluate(rc->config.warning))) {
440 + // calculation failed
441 + rc->run_flags |= RRDCALC_FLAG_WARN_ERROR;
442 +
443 + netdata_log_debug(D_HEALTH,
444 + "Health on host '%s', alarm '%s.%s': warning expression failed with error: %s",
445 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
446 + expression_error_msg(rc->config.warning)
447 + );
448 + } else {
449 + rc->run_flags &= ~RRDCALC_FLAG_WARN_ERROR;
450 + netdata_log_debug(D_HEALTH,
451 + "Health on host '%s', alarm '%s.%s': warning expression gave value "
452 + NETDATA_DOUBLE_FORMAT ": %s (source: %s)",
453 + rrdhost_hostname(host),
454 + rrdcalc_chart_name(rc),
455 + rrdcalc_name(rc),
456 + expression_result(rc->config.warning),
457 + expression_error_msg(rc->config.warning),
458 + rrdcalc_source(rc)
459 + );
460 + warning_status = rrdcalc_value2status(expression_result(rc->config.warning));
461 + }
462 + }
463 +
464 + // --------------------------------------------------------
465 + // check the critical expression
466 +
467 + if (likely(rc->config.critical)) {
468 + worker_is_busy(WORKER_HEALTH_JOB_CRITICAL_EVAL);
469 +
470 + if (unlikely(!expression_evaluate(rc->config.critical))) {
471 + // calculation failed
472 + rc->run_flags |= RRDCALC_FLAG_CRIT_ERROR;
473 +
474 + netdata_log_debug(D_HEALTH,
475 + "Health on host '%s', alarm '%s.%s': critical expression failed with error: %s",
476 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
477 + expression_error_msg(rc->config.critical)
478 + );
479 + } else {
480 + rc->run_flags &= ~RRDCALC_FLAG_CRIT_ERROR;
481 + netdata_log_debug(D_HEALTH,
482 + "Health on host '%s', alarm '%s.%s': critical expression gave value "
483 + NETDATA_DOUBLE_FORMAT ": %s (source: %s)",
484 + rrdhost_hostname(host), rrdcalc_chart_name(rc), rrdcalc_name(rc),
485 + expression_result(rc->config.critical),
486 + expression_error_msg(rc->config.critical),
487 + rrdcalc_source(rc)
488 + );
489 + critical_status = rrdcalc_value2status(expression_result(rc->config.critical));
490 + }
491 + }
492 +
493 + // --------------------------------------------------------
494 + // decide the final alarm status
495 +
496 + RRDCALC_STATUS status = RRDCALC_STATUS_UNDEFINED;
497 +
498 + switch (warning_status) {
499 + case RRDCALC_STATUS_CLEAR:
500 + status = RRDCALC_STATUS_CLEAR;
501 + break;
502 +
503 + case RRDCALC_STATUS_RAISED:
504 + status = RRDCALC_STATUS_WARNING;
505 + break;
506 +
507 + default:
508 + break;
509 + }
510 +
511 + switch (critical_status) {
512 + case RRDCALC_STATUS_CLEAR:
513 + if (status == RRDCALC_STATUS_UNDEFINED)
514 + status = RRDCALC_STATUS_CLEAR;
515 + break;
516 +
517 + case RRDCALC_STATUS_RAISED:
518 + status = RRDCALC_STATUS_CRITICAL;
519 + break;
520 +
521 + default:
522 + break;
523 + }
524 +
525 + // --------------------------------------------------------
526 + // check if the new status and the old differ
527 +
528 + if (status != rc->status) {
529 +
530 + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
531 + int delay;
532 +
533 + // apply trigger hysteresis
534 +
535 + if (now > rc->delay_up_to_timestamp) {
536 + rc->delay_up_current = rc->config.delay_up_duration;
537 + rc->delay_down_current = rc->config.delay_down_duration;
538 + rc->delay_last = 0;
539 + rc->delay_up_to_timestamp = 0;
540 + } else {
541 + rc->delay_up_current = (int)((float)rc->delay_up_current * rc->config.delay_multiplier);
542 + if (rc->delay_up_current > rc->config.delay_max_duration)
543 + rc->delay_up_current = rc->config.delay_max_duration;
544 +
545 + rc->delay_down_current = (int)((float)rc->delay_down_current * rc->config.delay_multiplier);
546 + if (rc->delay_down_current > rc->config.delay_max_duration)
547 + rc->delay_down_current = rc->config.delay_max_duration;
548 + }
549 +
550 + if (status > rc->status)
551 + delay = rc->delay_up_current;
552 + else
553 + delay = rc->delay_down_current;
554 +
555 + // COMMENTED: because we do need to send raising alarms
556 + // if (now + delay < rc->delay_up_to_timestamp)
557 + // delay = (int)(rc->delay_up_to_timestamp - now);
558 +
559 + rc->delay_last = delay;
560 + rc->delay_up_to_timestamp = now + delay;
561 +
562 + ALARM_ENTRY *ae =
563 + health_create_alarm_entry(
564 + host,
565 + rc,
566 + now,
567 + now - rc->last_status_change,
568 + rc->old_value,
569 + rc->value,
570 + rc->status,
571 + status,
572 + rc->delay_last,
573 + (
574 + ((rc->config.alert_action_options & ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) |
575 + ((rc->run_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) |
576 + (rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0)
577 + )
578 + );
579 +
580 + health_log_alert(host, ae);
581 + health_alarm_log_add_entry(host, ae);
582 +
583 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
584 + "[%s]: Alert event for [%s.%s], value [%s], status [%s].",
585 + rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), ae_new_value_string(ae),
586 + rrdcalc_status2string(ae->new_status));
587 +
588 + rc->last_status_change_value = rc->value;
589 + rc->last_status_change = now;
590 + rc->old_status = rc->status;
591 + rc->status = status;
592 +
593 + if(unlikely(rrdcalc_isrepeating(rc))) {
594 + rc->last_repeat = now;
595 + if (rc->status == RRDCALC_STATUS_CLEAR)
596 + rc->run_flags |= RRDCALC_FLAG_RUN_ONCE;
597 + }
598 + }
599 +
600 + rc->last_updated = now;
601 + rc->next_update = now + rc->config.update_every;
602 +
603 + if (next_run > rc->next_update)
604 + next_run = rc->next_update;
605 + }
606 + foreach_rrdcalc_in_rrdhost_done(rc);
607 +
608 + alerts_raised_summary_populate(hrm);
609 +
610 + // process repeating alarms
611 + foreach_rrdcalc_in_rrdhost_read(host, rc) {
612 + if(unlikely(!service_running(SERVICE_HEALTH)))
613 + break;
614 +
615 + int repeat_every = 0;
616 + if(unlikely(rrdcalc_isrepeating(rc) && rc->delay_up_to_timestamp <= now)) {
617 + if(unlikely(rc->status == RRDCALC_STATUS_WARNING)) {
618 + rc->run_flags &= ~RRDCALC_FLAG_RUN_ONCE;
619 + repeat_every = (int)rc->config.warn_repeat_every;
620 + }
621 + else if(unlikely(rc->status == RRDCALC_STATUS_CRITICAL)) {
622 + rc->run_flags &= ~RRDCALC_FLAG_RUN_ONCE;
623 + repeat_every = (int)rc->config.crit_repeat_every;
624 + }
625 + else if(unlikely(rc->status == RRDCALC_STATUS_CLEAR)) {
626 + if(!(rc->run_flags & RRDCALC_FLAG_RUN_ONCE) &&
627 + (rc->old_status == RRDCALC_STATUS_CRITICAL || rc->old_status == RRDCALC_STATUS_WARNING))
628 + repeat_every = 1;
629 + }
630 + }
631 + else
632 + continue;
633 +
634 + if(unlikely(repeat_every > 0 && (rc->last_repeat + repeat_every) <= now)) {
635 + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY);
636 + rc->last_repeat = now;
637 + if (likely(rc->times_repeat < UINT32_MAX)) rc->times_repeat++;
638 + ALARM_ENTRY *ae =
639 + health_create_alarm_entry(
640 + host,
641 + rc,
642 + now,
643 + now - rc->last_status_change,
644 + rc->old_value,
645 + rc->value,
646 + rc->old_status,
647 + rc->status,
648 + rc->delay_last,
649 + (
650 + ((rc->config.alert_action_options & ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION)? HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION : 0) |
651 + ((rc->run_flags & RRDCALC_FLAG_SILENCED)? HEALTH_ENTRY_FLAG_SILENCED : 0) |
652 + (rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0)
653 + )
654 + );
655 +
656 + health_log_alert(host, ae);
657 + ae->last_repeat = rc->last_repeat;
658 + if (!(rc->run_flags & RRDCALC_FLAG_RUN_ONCE) && rc->status == RRDCALC_STATUS_CLEAR) {
659 + ae->flags |= HEALTH_ENTRY_RUN_ONCE;
660 + }
661 + rc->run_flags |= RRDCALC_FLAG_RUN_ONCE;
662 + health_send_notification(host, ae, hrm);
663 + netdata_log_debug(D_HEALTH, "Notification sent for the repeating alarm %u.", ae->alarm_id);
664 + health_alarm_wait_for_execution(ae);
665 + health_alarm_log_free_one_nochecks_nounlink(ae);
666 + }
667 + }
668 + foreach_rrdcalc_in_rrdhost_done(rc);
669 + }
670 +
671 + if (unlikely(!service_running(SERVICE_HEALTH)))
672 + break;
673 +
674 + // execute notifications
675 + // and cleanup
676 +
677 + worker_is_busy(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS);
678 + health_alarm_log_process_to_send_notifications(host, hrm);
679 + alerts_raised_summary_free(hrm);
680 +
681 + if (unlikely(!service_running(SERVICE_HEALTH))) {
682 + // wait for all notifications to finish before allowing health to be cleaned up
683 + wait_for_all_notifications_to_finish_before_allowing_health_to_be_cleaned_up();
684 + break;
685 + }
686 +#ifdef ENABLE_ACLK
687 + if (netdata_cloud_enabled) {
688 + struct aclk_sync_cfg_t *wc = host->aclk_config;
689 + if (unlikely(!wc))
690 + continue;
691 +
692 + if (wc->alert_queue_removed == 1) {
693 + sql_queue_removed_alerts_to_aclk(host);
694 + } else if (wc->alert_queue_removed > 1) {
695 + wc->alert_queue_removed--;
696 + }
697 +
698 + if (wc->alert_checkpoint_req == 1) {
699 + aclk_push_alarm_checkpoint(host);
700 + } else if (wc->alert_checkpoint_req > 1) {
701 + wc->alert_checkpoint_req--;
702 + }
703 + }
704 +#endif
705 + }
706 + dfe_done(host);
707 +
708 + // wait for all notifications to finish before allowing health to be cleaned up
709 + wait_for_all_notifications_to_finish_before_allowing_health_to_be_cleaned_up();
710 +
711 + if(unlikely(!service_running(SERVICE_HEALTH)))
712 + break;
713 +
714 + health_sleep(next_run, loop);
715 +
716 + } // forever
717 +}
718 +
719 +
720 +static void health_main_cleanup(void *ptr) {
721 + worker_unregister();
722 +
723 + struct netdata_static_thread *static_thread = (struct netdata_static_thread *)ptr;
724 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
725 + netdata_log_info("cleaning up...");
726 + static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
727 +
728 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
729 + "Health thread ended.");
730 +}
731 +
732 +void *health_main(void *ptr) {
733 + worker_register("HEALTH");
734 + worker_register_job_name(WORKER_HEALTH_JOB_RRD_LOCK, "rrd lock");
735 + worker_register_job_name(WORKER_HEALTH_JOB_HOST_LOCK, "host lock");
736 + worker_register_job_name(WORKER_HEALTH_JOB_DB_QUERY, "db lookup");
737 + worker_register_job_name(WORKER_HEALTH_JOB_CALC_EVAL, "calc eval");
738 + worker_register_job_name(WORKER_HEALTH_JOB_WARNING_EVAL, "warning eval");
739 + worker_register_job_name(WORKER_HEALTH_JOB_CRITICAL_EVAL, "critical eval");
740 + worker_register_job_name(WORKER_HEALTH_JOB_ALARM_LOG_ENTRY, "alarm log entry");
741 + worker_register_job_name(WORKER_HEALTH_JOB_ALARM_LOG_PROCESS, "alarm log process");
742 + worker_register_job_name(WORKER_HEALTH_JOB_DELAYED_INIT_RRDSET, "rrdset init");
743 + worker_register_job_name(WORKER_HEALTH_JOB_DELAYED_INIT_RRDDIM, "rrddim init");
744 +
745 + netdata_thread_cleanup_push(health_main_cleanup, ptr);
746 + {
747 + health_event_loop();
748 + }
749 + netdata_thread_cleanup_pop(1);
750 + return NULL;
751 +}
health/health_internals.h new
+130
@@ -0,0 +1,130 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_HEALTH_INTERNALS_H
4 +#define NETDATA_HEALTH_INTERNALS_H
5 +
6 +#include "health.h"
7 +
8 +#define HEALTH_LOG_ENTRIES_DEFAULT 1000U
9 +#define HEALTH_LOG_ENTRIES_MAX 100000U
10 +#define HEALTH_LOG_ENTRIES_MIN 10U
11 +
12 +#define HEALTH_LOG_HISTORY_DEFAULT (5 * 86400)
13 +
14 +#define HEALTH_CONF_MAX_LINE 4096
15 +
16 +#define HEALTH_ALARM_KEY "alarm"
17 +#define HEALTH_TEMPLATE_KEY "template"
18 +#define HEALTH_CHART_KEY "chart"
19 +#define HEALTH_CONTEXT_KEY "context"
20 +#define HEALTH_ON_KEY "on"
21 +#define HEALTH_HOST_KEY "hosts"
22 +#define HEALTH_OS_KEY "os"
23 +#define HEALTH_PLUGIN_KEY "plugin"
24 +#define HEALTH_MODULE_KEY "module"
25 +#define HEALTH_CHARTS_KEY "charts"
26 +#define HEALTH_LOOKUP_KEY "lookup"
27 +#define HEALTH_CALC_KEY "calc"
28 +#define HEALTH_EVERY_KEY "every"
29 +#define HEALTH_GREEN_KEY "green"
30 +#define HEALTH_RED_KEY "red"
31 +#define HEALTH_WARN_KEY "warn"
32 +#define HEALTH_CRIT_KEY "crit"
33 +#define HEALTH_EXEC_KEY "exec"
34 +#define HEALTH_RECIPIENT_KEY "to"
35 +#define HEALTH_UNITS_KEY "units"
36 +#define HEALTH_SUMMARY_KEY "summary"
37 +#define HEALTH_INFO_KEY "info"
38 +#define HEALTH_CLASS_KEY "class"
39 +#define HEALTH_COMPONENT_KEY "component"
40 +#define HEALTH_TYPE_KEY "type"
41 +#define HEALTH_DELAY_KEY "delay"
42 +#define HEALTH_OPTIONS_KEY "options"
43 +#define HEALTH_REPEAT_KEY "repeat"
44 +#define HEALTH_HOST_LABEL_KEY "host labels"
45 +#define HEALTH_FOREACH_KEY "foreach"
46 +#define HEALTH_CHART_LABEL_KEY "chart labels"
47 +
48 +void alert_action_options_to_buffer_json_array(BUFFER *wb, const char *key, ALERT_ACTION_OPTIONS options);
49 +ALERT_ACTION_OPTIONS alert_action_options_parse(char *o);
50 +ALERT_ACTION_OPTIONS alert_action_options_parse_one(const char *o);
51 +
52 +typedef struct rrd_alert_prototype {
53 + struct rrd_alert_match match;
54 + struct rrd_alert_config config;
55 +
56 + struct {
57 + uint32_t uses;
58 + bool enabled;
59 + bool is_on_disk;
60 + SPINLOCK spinlock;
61 + struct rrd_alert_prototype *prev, *next;
62 + } _internal;
63 +} RRD_ALERT_PROTOTYPE;
64 +bool health_prototype_add(RRD_ALERT_PROTOTYPE *ap);
65 +void health_prototype_cleanup(RRD_ALERT_PROTOTYPE *ap);
66 +void health_prototype_free(RRD_ALERT_PROTOTYPE *ap);
67 +
68 +struct health_plugin_globals {
69 + struct {
70 + SPINLOCK spinlock;
71 + bool done;
72 + } initialization;
73 +
74 + struct {
75 + bool enabled;
76 + bool stock_enabled;
77 + bool use_summary_for_notifications;
78 +
79 + unsigned int health_log_entries_max;
80 + uint32_t health_log_history; // the health log history in seconds to be kept in db
81 +
82 + STRING *silencers_filename;
83 + STRING *default_exec;
84 + STRING *default_recipient;
85 +
86 + SIMPLE_PATTERN *enabled_alerts;
87 +
88 + uint32_t default_warn_repeat_every; // the default value for the interval between repeating warning notifications
89 + uint32_t default_crit_repeat_every; // the default value for the interval between repeating critical notifications
90 +
91 + int32_t run_at_least_every_seconds;
92 + int32_t postpone_alarms_during_hibernation_for_seconds;
93 + } config;
94 +
95 + struct {
96 + DICTIONARY *dict;
97 + } prototypes;
98 +};
99 +
100 +extern struct health_plugin_globals health_globals;
101 +
102 +int health_readfile(const char *filename, void *data, bool stock_config);
103 +void unlink_alarm_notify_in_progress(ALARM_ENTRY *ae);
104 +void wait_for_all_notifications_to_finish_before_allowing_health_to_be_cleaned_up(void);
105 +
106 +void health_alarm_wait_for_execution(ALARM_ENTRY *ae);
107 +
108 +bool rrdcalc_add_from_prototype(RRDHOST *host, RRDSET *st, RRD_ALERT_PROTOTYPE *ap);
109 +
110 +int dyncfg_health_cb(const char *transaction, const char *id, DYNCFG_CMDS cmd, const char *add_name,
111 + BUFFER *payload, usec_t *stop_monotonic_ut, bool *cancelled,
112 + BUFFER *result, const char *source, void *data);
113 +
114 +void health_dyncfg_unregister_all_prototypes(void);
115 +void health_dyncfg_register_all_prototypes(void);
116 +void health_prototype_to_json(BUFFER *wb, RRD_ALERT_PROTOTYPE *ap, bool for_hashing);
117 +
118 +bool alert_variable_lookup(STRING *variable, void *data, NETDATA_DOUBLE *result);
119 +
120 +struct health_raised_summary;
121 +struct health_raised_summary *alerts_raised_summary_create(RRDHOST *host);
122 +void alerts_raised_summary_populate(struct health_raised_summary *hrm);
123 +void alerts_raised_summary_free(struct health_raised_summary *hrm);
124 +void health_send_notification(RRDHOST *host, ALARM_ENTRY *ae, struct health_raised_summary *hrm);
125 +void health_alarm_log_process_to_send_notifications(RRDHOST *host, struct health_raised_summary *hrm);
126 +
127 +void health_apply_prototype_to_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap);
128 +void health_prototype_apply_to_all_hosts(RRD_ALERT_PROTOTYPE *ap);
129 +
130 +#endif //NETDATA_HEALTH_INTERNALS_H
health/health_json.c
+64 -43
@@ -41,7 +41,7 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC
41 format_value_and_unit(value_string, 100, rc->value, rrdcalc_units(rc), -1);
42
43 char hash_id[GUID_LEN + 1];
44 - uuid_unparse_lower(rc->config_hash_id, hash_id);
44 + uuid_unparse_lower(rc->config.hash_id, hash_id);
45
46 buffer_sprintf(wb,
47 "\t\t\"%s.%s\": {\n"
@@ -82,42 +82,42 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC
82 , hash_id
83 , rrdcalc_name(rc)
84 , rrdcalc_chart_name(rc)
85 - , rc->classification?rrdcalc_classification(rc):"Unknown"
86 - , rc->component?rrdcalc_component(rc):"Unknown"
87 - , rc->type?rrdcalc_type(rc):"Unknown"
85 + , rc->config.classification?rrdcalc_classification(rc):"Unknown"
86 + , rc->config.component?rrdcalc_component(rc):"Unknown"
87 + , rc->config.type?rrdcalc_type(rc):"Unknown"
88 , (rc->rrdset)?"true":"false"
89 , (rc->run_flags & RRDCALC_FLAG_DISABLED)?"true":"false"
90 , (rc->run_flags & RRDCALC_FLAG_SILENCED)?"true":"false"
91 - , rc->exec?rrdcalc_exec(rc):string2str(host->health.health_default_exec)
92 - , rc->recipient?rrdcalc_recipient(rc):string2str(host->health.health_default_recipient)
91 + , rc->config.exec?rrdcalc_exec(rc):string2str(host->health.health_default_exec)
92 + , rc->config.recipient?rrdcalc_recipient(rc):string2str(host->health.health_default_recipient)
93 , rrdcalc_source(rc)
94 , rrdcalc_units(rc)
95 - , rrdcalc_summary(rc)
96 - , rrdcalc_info(rc)
95 + , string2str(rc->summary)
96 + , string2str(rc->info)
97 , rrdcalc_status2string(rc->status)
98 , (unsigned long)rc->last_status_change
99 , (unsigned long)rc->last_updated
100 , (unsigned long)rc->next_update
101 - , rc->update_every
102 - , rc->delay_up_duration
103 - , rc->delay_down_duration
104 - , rc->delay_max_duration
105 - , rc->delay_multiplier
101 + , rc->config.update_every
102 + , rc->config.delay_up_duration
103 + , rc->config.delay_down_duration
104 + , rc->config.delay_max_duration
105 + , rc->config.delay_multiplier
106 , rc->delay_last
107 , (unsigned long)rc->delay_up_to_timestamp
108 - , rc->warn_repeat_every
109 - , rc->crit_repeat_every
108 + , rc->config.warn_repeat_every
109 + , rc->config.crit_repeat_every
110 , value_string
111 , (unsigned long)rc->last_repeat
112 , (unsigned long)rc->times_repeat
113 );
114
115 - if(unlikely(rc->options & RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)) {
115 + if(unlikely(rc->config.alert_action_options & ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION)) {
116 buffer_strcat(wb, "\t\t\t\"no_clear_notification\": true,\n");
117 }
118
119 if(RRDCALC_HAS_DB_LOOKUP(rc)) {
120 - if(rc->dimensions)
120 + if(rc->config.dimensions)
121 health_string2json(wb, "\t\t\t", "lookup_dimensions", rrdcalc_dimensions(rc), ",\n");
122
123 buffer_sprintf(wb,
@@ -129,35 +129,35 @@ static inline void health_rrdcalc2json_nolock(RRDHOST *host, BUFFER *wb, RRDCALC
129 "\t\t\t\"lookup_options\": \"",
130 (unsigned long) rc->db_after,
131 (unsigned long) rc->db_before,
132 - time_grouping_method2string(rc->group),
133 - rc->after,
134 - rc->before
132 + time_grouping_id2txt(rc->config.group),
133 + rc->config.after,
134 + rc->config.before
135 );
136 - buffer_data_options2string(wb, rc->options);
136 + buffer_data_options2string(wb, rc->config.options);
137 buffer_strcat(wb, "\",\n");
138 }
139
140 - if(rc->calculation) {
141 - health_string2json(wb, "\t\t\t", "calc", rc->calculation->source, ",\n");
142 - health_string2json(wb, "\t\t\t", "calc_parsed", rc->calculation->parsed_as, ",\n");
140 + if(rc->config.calculation) {
141 + health_string2json(wb, "\t\t\t", "calc", expression_source(rc->config.calculation), ",\n");
142 + health_string2json(wb, "\t\t\t", "calc_parsed", expression_parsed_as(rc->config.calculation), ",\n");
143 }
144
145 - if(rc->warning) {
146 - health_string2json(wb, "\t\t\t", "warn", rc->warning->source, ",\n");
147 - health_string2json(wb, "\t\t\t", "warn_parsed", rc->warning->parsed_as, ",\n");
145 + if(rc->config.warning) {
146 + health_string2json(wb, "\t\t\t", "warn", expression_source(rc->config.warning), ",\n");
147 + health_string2json(wb, "\t\t\t", "warn_parsed", expression_parsed_as(rc->config.warning), ",\n");
148 }
149
150 - if(rc->critical) {
151 - health_string2json(wb, "\t\t\t", "crit", rc->critical->source, ",\n");
152 - health_string2json(wb, "\t\t\t", "crit_parsed", rc->critical->parsed_as, ",\n");
150 + if(rc->config.critical) {
151 + health_string2json(wb, "\t\t\t", "crit", expression_source(rc->config.critical), ",\n");
152 + health_string2json(wb, "\t\t\t", "crit_parsed", expression_parsed_as(rc->config.critical), ",\n");
153 }
154
155 buffer_strcat(wb, "\t\t\t\"green\":");
156 - buffer_print_netdata_double(wb, rc->green);
156 + buffer_print_netdata_double(wb, rc->config.green);
157 buffer_strcat(wb, ",\n");
158
159 buffer_strcat(wb, "\t\t\t\"red\":");
160 - buffer_print_netdata_double(wb, rc->red);
160 + buffer_print_netdata_double(wb, rc->config.red);
161 buffer_strcat(wb, ",\n");
162
163 buffer_strcat(wb, "\t\t\t\"value\":");
@@ -236,20 +236,13 @@ void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
236 "\n\t\"status\": %s,"
237 "\n\t\"now\": %lu,"
238 "\n\t\"alarms\": {\n",
239 - rrdhost_hostname(host),
240 - (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0,
241 - host->health.health_enabled?"true":"false",
242 - (unsigned long)now_realtime_sec());
239 + rrdhost_hostname(host),
240 + (host->health_log.next_log_id > 0)?(host->health_log.next_log_id - 1):0,
241 + host->health.health_enabled?"true":"false",
242 + (unsigned long)now_realtime_sec());
243
244 health_alarms2json_fill_alarms(host, wb, all, health_rrdcalc2json_nolock);
245
246 -// rrdhost_rdlock(host);
247 -// buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
248 -// RRDCALCTEMPLATE *rt;
249 -// for(rt = host->templates; rt ; rt = rt->next)
250 -// health_rrdcalctemplate2json_nolock(wb, rt);
251 -// rrdhost_unlock(host);
252 -
246 buffer_strcat(wb, "\n\t}\n}\n");
247 }
248
@@ -263,3 +256,31 @@ void health_alarms_values2json(RRDHOST *host, BUFFER *wb, int all) {
256 buffer_strcat(wb, "\n\t}\n}\n");
257 }
258
259 +void health_entry_flags_to_json_array(BUFFER *wb, const char *key, HEALTH_ENTRY_FLAGS flags) {
260 + buffer_json_member_add_array(wb, key);
261 +
262 + if(flags & HEALTH_ENTRY_FLAG_PROCESSED)
263 + buffer_json_add_array_item_string(wb, "PROCESSED");
264 + if(flags & HEALTH_ENTRY_FLAG_UPDATED)
265 + buffer_json_add_array_item_string(wb, "UPDATED");
266 + if(flags & HEALTH_ENTRY_FLAG_EXEC_RUN)
267 + buffer_json_add_array_item_string(wb, "EXEC_RUN");
268 + if(flags & HEALTH_ENTRY_FLAG_EXEC_FAILED)
269 + buffer_json_add_array_item_string(wb, "EXEC_FAILED");
270 + if(flags & HEALTH_ENTRY_FLAG_SILENCED)
271 + buffer_json_add_array_item_string(wb, "SILENCED");
272 + if(flags & HEALTH_ENTRY_RUN_ONCE)
273 + buffer_json_add_array_item_string(wb, "RUN_ONCE");
274 + if(flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS)
275 + buffer_json_add_array_item_string(wb, "EXEC_IN_PROGRESS");
276 + if(flags & HEALTH_ENTRY_FLAG_IS_REPEATING)
277 + buffer_json_add_array_item_string(wb, "RECURRING");
278 + if(flags & HEALTH_ENTRY_FLAG_SAVED)
279 + buffer_json_add_array_item_string(wb, "SAVED");
280 + if(flags & HEALTH_ENTRY_FLAG_ACLK_QUEUED)
281 + buffer_json_add_array_item_string(wb, "ACLK_QUEUED");
282 + if(flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION)
283 + buffer_json_add_array_item_string(wb, "NO_CLEAR_NOTIFICATION");
284 +
285 + buffer_json_array_close(wb);
286 +}
health/health_log.c
+18 -17
@@ -86,31 +86,32 @@ void health_log_alert_transition_with_trace(RRDHOST *host, ALARM_ENTRY *ae, int
86
87 inline ALARM_ENTRY* health_create_alarm_entry(
88 RRDHOST *host,
89 - uint32_t alarm_id,
90 - uint32_t alarm_event_id,
91 - const uuid_t config_hash_id,
89 + RRDCALC *rc,
90 time_t when,
93 - STRING *name,
94 - STRING *chart,
95 - STRING *chart_context,
96 - STRING *chart_name,
97 - STRING *class,
98 - STRING *component,
99 - STRING *type,
100 - STRING *exec,
101 - STRING *recipient,
91 time_t duration,
92 NETDATA_DOUBLE old_value,
93 NETDATA_DOUBLE new_value,
94 RRDCALC_STATUS old_status,
95 RRDCALC_STATUS new_status,
107 - STRING *source,
108 - STRING *units,
109 - STRING *summary,
110 - STRING *info,
96 int delay,
97 HEALTH_ENTRY_FLAGS flags
98 ) {
99 + uint32_t alarm_id = rc->id;
100 + uint32_t alarm_event_id = rc->next_event_id++;
101 + STRING *name = rc->config.name;
102 + STRING *chart = rc->rrdset->id;
103 + STRING *chart_context = rc->rrdset->context;
104 + STRING *chart_name = rc->rrdset->name;
105 + STRING *class = rc->config.classification;
106 + STRING *component = rc->config.component;
107 + STRING *type = rc->config.type;
108 + STRING *exec = rc->config.exec;
109 + STRING *recipient = rc->config.recipient;
110 + STRING *source = rc->config.source;
111 + STRING *units = rc->config.units;
112 + STRING *summary = rc->summary;
113 + STRING *info = rc->info;
114 +
115 netdata_log_debug(D_HEALTH, "Health adding alarm log entry with id: %u", host->health_log.next_log_id);
116
117 ALARM_ENTRY *ae = callocz(1, sizeof(ALARM_ENTRY));
@@ -119,7 +120,7 @@ inline ALARM_ENTRY* health_create_alarm_entry(
120 ae->chart_context = string_dup(chart_context);
121 ae->chart_name = string_dup(chart_name);
122
122 - uuid_copy(ae->config_hash_id, *((uuid_t *) config_hash_id));
123 + uuid_copy(ae->config_hash_id, rc->config.hash_id);
124
125 uuid_generate_random(ae->transition_id);
126 ae->global_id = now_realtime_usec();
health/health_notifications.c new
+569
@@ -0,0 +1,569 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "health_internals.h"
4 +
5 +// the queue of executed alarm notifications that haven't been waited for yet
6 +static struct {
7 + ALARM_ENTRY *head; // oldest
8 + ALARM_ENTRY *tail; // latest
9 +} alarm_notifications_in_progress = {NULL, NULL};
10 +
11 +struct health_raised_summary {
12 + RRDHOST *host;
13 + DICTIONARY *rrdcalc_dict;
14 +
15 + struct {
16 + size_t size;
17 + size_t used;
18 + const DICTIONARY_ITEM **array;
19 + } active_alerts;
20 +};
21 +
22 +void health_alarm_wait_for_execution(ALARM_ENTRY *ae) {
23 + if (!(ae->flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS))
24 + return;
25 +
26 + spawn_wait_cmd(ae->exec_spawn_serial, &ae->exec_code, &ae->exec_run_timestamp);
27 + netdata_log_debug(D_HEALTH, "done executing command - returned with code %d", ae->exec_code);
28 + ae->flags &= ~HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
29 +
30 + if(ae->exec_code != 0)
31 + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_FAILED;
32 +
33 + unlink_alarm_notify_in_progress(ae);
34 +}
35 +
36 +void wait_for_all_notifications_to_finish_before_allowing_health_to_be_cleaned_up(void) {
37 + ALARM_ENTRY *ae;
38 + while (NULL != (ae = alarm_notifications_in_progress.head)) {
39 + if(unlikely(!service_running(SERVICE_HEALTH)))
40 + break;
41 +
42 + health_alarm_wait_for_execution(ae);
43 + }
44 +}
45 +
46 +void unlink_alarm_notify_in_progress(ALARM_ENTRY *ae)
47 +{
48 + struct alarm_entry *prev = ae->prev_in_progress;
49 + struct alarm_entry *next = ae->next_in_progress;
50 +
51 + if (NULL != prev) {
52 + prev->next_in_progress = next;
53 + }
54 + if (NULL != next) {
55 + next->prev_in_progress = prev;
56 + }
57 + if (ae == alarm_notifications_in_progress.head) {
58 + alarm_notifications_in_progress.head = next;
59 + }
60 + if (ae == alarm_notifications_in_progress.tail) {
61 + alarm_notifications_in_progress.tail = prev;
62 + }
63 +}
64 +
65 +static inline void enqueue_alarm_notify_in_progress(ALARM_ENTRY *ae)
66 +{
67 + ae->prev_in_progress = NULL;
68 + ae->next_in_progress = NULL;
69 +
70 + if (NULL != alarm_notifications_in_progress.tail) {
71 + ae->prev_in_progress = alarm_notifications_in_progress.tail;
72 + alarm_notifications_in_progress.tail->next_in_progress = ae;
73 + }
74 + if (NULL == alarm_notifications_in_progress.head) {
75 + alarm_notifications_in_progress.head = ae;
76 + }
77 + alarm_notifications_in_progress.tail = ae;
78 +
79 +}
80 +
81 +static bool prepare_command(BUFFER *wb,
82 + const char *exec,
83 + const char *recipient,
84 + const char *registry_hostname,
85 + uint32_t unique_id,
86 + uint32_t alarm_id,
87 + uint32_t alarm_event_id,
88 + uint32_t when,
89 + const char *alert_name,
90 + const char *alert_chart_name,
91 + const char *new_status,
92 + const char *old_status,
93 + NETDATA_DOUBLE new_value,
94 + NETDATA_DOUBLE old_value,
95 + const char *alert_source,
96 + uint32_t duration,
97 + uint32_t non_clear_duration,
98 + const char *alert_units,
99 + const char *alert_info,
100 + const char *new_value_string,
101 + const char *old_value_string,
102 + const char *source,
103 + const char *error_msg,
104 + int n_warn,
105 + int n_crit,
106 + const char *warn_alarms,
107 + const char *crit_alarms,
108 + const char *classification,
109 + const char *edit_command,
110 + const char *machine_guid,
111 + uuid_t *transition_id,
112 + const char *summary,
113 + const char *context,
114 + const char *component,
115 + const char *type
116 +) {
117 + char buf[8192];
118 + size_t n = sizeof(buf) - 1;
119 +
120 + buffer_strcat(wb, "exec");
121 +
122 + if (!sanitize_command_argument_string(buf, exec, n))
123 + return false;
124 + buffer_sprintf(wb, " '%s'", buf);
125 +
126 + if (!sanitize_command_argument_string(buf, recipient, n))
127 + return false;
128 + buffer_sprintf(wb, " '%s'", buf);
129 +
130 + if (!sanitize_command_argument_string(buf, registry_hostname, n))
131 + return false;
132 + buffer_sprintf(wb, " '%s'", buf);
133 +
134 + buffer_sprintf(wb, " '%u'", unique_id);
135 +
136 + buffer_sprintf(wb, " '%u'", alarm_id);
137 +
138 + buffer_sprintf(wb, " '%u'", alarm_event_id);
139 +
140 + buffer_sprintf(wb, " '%u'", when);
141 +
142 + if (!sanitize_command_argument_string(buf, alert_name, n))
143 + return false;
144 + buffer_sprintf(wb, " '%s'", buf);
145 +
146 + if (!sanitize_command_argument_string(buf, alert_chart_name, n))
147 + return false;
148 + buffer_sprintf(wb, " '%s'", buf);
149 +
150 + if (!sanitize_command_argument_string(buf, new_status, n))
151 + return false;
152 + buffer_sprintf(wb, " '%s'", buf);
153 +
154 + if (!sanitize_command_argument_string(buf, old_status, n))
155 + return false;
156 + buffer_sprintf(wb, " '%s'", buf);
157 +
158 + buffer_sprintf(wb, " '" NETDATA_DOUBLE_FORMAT_ZERO "'", new_value);
159 +
160 + buffer_sprintf(wb, " '" NETDATA_DOUBLE_FORMAT_ZERO "'", old_value);
161 +
162 + if (!sanitize_command_argument_string(buf, alert_source, n))
163 + return false;
164 + buffer_sprintf(wb, " '%s'", buf);
165 +
166 + buffer_sprintf(wb, " '%u'", duration);
167 +
168 + buffer_sprintf(wb, " '%u'", non_clear_duration);
169 +
170 + if (!sanitize_command_argument_string(buf, alert_units, n))
171 + return false;
172 + buffer_sprintf(wb, " '%s'", buf);
173 +
174 + if (!sanitize_command_argument_string(buf, alert_info, n))
175 + return false;
176 + buffer_sprintf(wb, " '%s'", buf);
177 +
178 + if (!sanitize_command_argument_string(buf, new_value_string, n))
179 + return false;
180 + buffer_sprintf(wb, " '%s'", buf);
181 +
182 + if (!sanitize_command_argument_string(buf, old_value_string, n))
183 + return false;
184 + buffer_sprintf(wb, " '%s'", buf);
185 +
186 + if (!sanitize_command_argument_string(buf, source, n))
187 + return false;
188 + buffer_sprintf(wb, " '%s'", buf);
189 +
190 + if (!sanitize_command_argument_string(buf, error_msg, n))
191 + return false;
192 + buffer_sprintf(wb, " '%s'", buf);
193 +
194 + buffer_sprintf(wb, " '%d'", n_warn);
195 +
196 + buffer_sprintf(wb, " '%d'", n_crit);
197 +
198 + if (!sanitize_command_argument_string(buf, warn_alarms, n))
199 + return false;
200 + buffer_sprintf(wb, " '%s'", buf);
201 +
202 + if (!sanitize_command_argument_string(buf, crit_alarms, n))
203 + return false;
204 + buffer_sprintf(wb, " '%s'", buf);
205 +
206 + if (!sanitize_command_argument_string(buf, classification, n))
207 + return false;
208 + buffer_sprintf(wb, " '%s'", buf);
209 +
210 + if (!sanitize_command_argument_string(buf, edit_command, n))
211 + return false;
212 + buffer_sprintf(wb, " '%s'", buf);
213 +
214 + if (!sanitize_command_argument_string(buf, machine_guid, n))
215 + return false;
216 + buffer_sprintf(wb, " '%s'", buf);
217 +
218 + char tr_id[UUID_STR_LEN];
219 + uuid_unparse_lower(*transition_id, tr_id);
220 + if (!sanitize_command_argument_string(buf, tr_id, n))
221 + return false;
222 + buffer_sprintf(wb, " '%s'", buf);
223 +
224 + if (!sanitize_command_argument_string(buf, summary, n))
225 + return false;
226 + buffer_sprintf(wb, " '%s'", buf);
227 +
228 + if (!sanitize_command_argument_string(buf, context, n))
229 + return false;
230 + buffer_sprintf(wb, " '%s'", buf);
231 +
232 + if (!sanitize_command_argument_string(buf, component, n))
233 + return false;
234 + buffer_sprintf(wb, " '%s'", buf);
235 +
236 + if (!sanitize_command_argument_string(buf, type, n))
237 + return false;
238 + buffer_sprintf(wb, " '%s'", buf);
239 +
240 + return true;
241 +}
242 +
243 +static inline int compare_raised_alerts(const void *a, const void *b) {
244 + const DICTIONARY_ITEM *item1 = *(const DICTIONARY_ITEM **)a;
245 + const DICTIONARY_ITEM *item2 = *(const DICTIONARY_ITEM **)b;
246 +
247 + RRDCALC *rc1 = dictionary_acquired_item_value(item1);
248 + RRDCALC *rc2 = dictionary_acquired_item_value(item2);
249 +
250 + return (int)(rc2->last_status_change - rc1->last_status_change);
251 +}
252 +
253 +static void health_raised_summary_add_alert(struct health_raised_summary *hrm, const DICTIONARY_ITEM *item) {
254 + if(hrm->active_alerts.used >= hrm->active_alerts.size) {
255 + if(hrm->active_alerts.size == 0)
256 + hrm->active_alerts.size = 2;
257 +
258 + hrm->active_alerts.size *= 2;
259 + hrm->active_alerts.array = reallocz(hrm->active_alerts.array, sizeof(const DICTIONARY_ITEM *) * hrm->active_alerts.size);
260 + }
261 +
262 + hrm->active_alerts.array[hrm->active_alerts.used++] = dictionary_acquired_item_dup(hrm->rrdcalc_dict, item);
263 +}
264 +
265 +void alerts_raised_summary_free(struct health_raised_summary *hrm) {
266 + for(size_t i = 0; i < hrm->active_alerts.used ;i++)
267 + dictionary_acquired_item_release(hrm->rrdcalc_dict, hrm->active_alerts.array[i]);
268 +
269 + freez(hrm->active_alerts.array);
270 + freez(hrm);
271 +}
272 +
273 +struct health_raised_summary *alerts_raised_summary_create(RRDHOST *host) {
274 + struct health_raised_summary *hrm = callocz(1, sizeof(*hrm));
275 + hrm->rrdcalc_dict = host->rrdcalc_root_index;
276 + hrm->host = host;
277 + return hrm;
278 +}
279 +
280 +void alerts_raised_summary_populate(struct health_raised_summary *hrm) {
281 + RRDCALC *rc;
282 + foreach_rrdcalc_in_rrdhost_read(hrm->host, rc) {
283 + if(unlikely(!rc->rrdset || !rc->rrdset->last_collected_time.tv_sec)) continue;
284 + health_raised_summary_add_alert(hrm, rc_dfe.item);
285 + }
286 + foreach_rrdcalc_in_rrdhost_done(rc);
287 +
288 + if (hrm->active_alerts.used > 1)
289 + qsort(hrm->active_alerts.array, hrm->active_alerts.used, sizeof(const DICTIONARY_ITEM *), compare_raised_alerts);
290 +}
291 +
292 +static size_t
293 +health_raised_summary_entries(struct health_raised_summary *hrm, BUFFER *dst, ALARM_ENTRY *ae, RRDCALC_STATUS status) {
294 + buffer_flush(dst);
295 +
296 + size_t count = 0;
297 + for(size_t i = 0; i < hrm->active_alerts.used ;i++) {
298 + RRDCALC *rc = dictionary_acquired_item_value(hrm->active_alerts.array[i]);
299 + if(rc->status != status) continue;
300 + if(rc->id == ae->alarm_id) continue;
301 +
302 + count++;
303 + if(buffer_strlen(dst)) buffer_putc(dst, ',');
304 + buffer_sprintf(dst, "%s=%" PRId64, string2str(rc->config.name), (int64_t)rc->last_status_change);
305 + }
306 +
307 + return count;
308 +}
309 +
310 +static const char *health_raised_summary_my_expression_source(struct health_raised_summary *hrm, ALARM_ENTRY *ae) {
311 + for(size_t i = 0; i < hrm->active_alerts.used ;i++) {
312 + RRDCALC *rc = dictionary_acquired_item_value(hrm->active_alerts.array[i]);
313 + if(rc->id != ae->alarm_id) continue;
314 +
315 + if(rc->status == RRDCALC_STATUS_CRITICAL)
316 + return expression_source(rc->config.critical);
317 + else
318 + return expression_source(rc->config.warning);
319 + }
320 +
321 + return "";
322 +}
323 +
324 +static const char *health_raised_summary_my_expression_error(struct health_raised_summary *hrm, ALARM_ENTRY *ae) {
325 + for(size_t i = 0; i < hrm->active_alerts.used ;i++) {
326 + RRDCALC *rc = dictionary_acquired_item_value(hrm->active_alerts.array[i]);
327 + if(rc->id != ae->alarm_id) continue;
328 +
329 + if(rc->status == RRDCALC_STATUS_CRITICAL)
330 + return expression_error_msg(rc->config.critical);
331 + else
332 + return expression_error_msg(rc->config.warning);
333 + }
334 +
335 + return "";
336 +}
337 +
338 +void health_send_notification(RRDHOST *host, ALARM_ENTRY *ae, struct health_raised_summary *hrm) {
339 + netdata_log_debug(D_HEALTH, "Health alarm '%s.%s' = " NETDATA_DOUBLE_FORMAT_AUTO " - changed status from %s to %s",
340 + ae->chart?ae_chart_id(ae):"NOCHART", ae_name(ae),
341 + ae->new_value,
342 + rrdcalc_status2string(ae->old_status),
343 + rrdcalc_status2string(ae->new_status)
344 + );
345 +
346 + ae->flags |= HEALTH_ENTRY_FLAG_PROCESSED;
347 +
348 + if(unlikely(ae->new_status < RRDCALC_STATUS_CLEAR)) {
349 + // do not send notifications for internal statuses
350 + netdata_log_debug(D_HEALTH, "Health not sending notification for alarm '%s.%s' status %s (internal statuses)", ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
351 + goto done;
352 + }
353 +
354 + if(unlikely(ae->new_status <= RRDCALC_STATUS_CLEAR && (ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) {
355 + // do not send notifications for disabled statuses
356 +
357 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
358 + "[%s]: Health not sending notification for alarm '%s.%s' status %s (it has no-clear-notification enabled)",
359 + rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
360 +
361 + // mark it as run, so that we will send the same alarm if it happens again
362 + goto done;
363 + }
364 +
365 + // find the previous notification for the same alarm
366 + // which we have run the exec script
367 + // exception: alarms with HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION set
368 + RRDCALC_STATUS last_executed_status = -3;
369 + if(likely(!(ae->flags & HEALTH_ENTRY_FLAG_NO_CLEAR_NOTIFICATION))) {
370 + int ret = sql_health_get_last_executed_event(host, ae, &last_executed_status);
371 +
372 + if (likely(ret == 1)) {
373 + // we have executed this alarm notification in the past
374 + if(last_executed_status == ae->new_status && !(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING)) {
375 + // don't send the notification for the same status again
376 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
377 + "[%s]: Health not sending again notification for alarm '%s.%s' status %s",
378 + rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae),
379 + rrdcalc_status2string(ae->new_status));
380 + goto done;
381 + }
382 + }
383 + else {
384 + // we have not executed this alarm notification in the past
385 + // so, don't send CLEAR notifications
386 + if(unlikely(ae->new_status == RRDCALC_STATUS_CLEAR)) {
387 + if((!(ae->flags & HEALTH_ENTRY_RUN_ONCE)) || (ae->flags & HEALTH_ENTRY_RUN_ONCE && ae->old_status < RRDCALC_STATUS_RAISED) ) {
388 + netdata_log_debug(D_HEALTH, "Health not sending notification for first initialization of alarm '%s.%s' status %s"
389 + , ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
390 + goto done;
391 + }
392 + }
393 + }
394 + }
395 +
396 + // Check if alarm notifications are silenced
397 + if (ae->flags & HEALTH_ENTRY_FLAG_SILENCED) {
398 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
399 + "[%s]: Health not sending notification for alarm '%s.%s' status %s "
400 + "(command API has disabled notifications)",
401 + rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
402 + goto done;
403 + }
404 +
405 + nd_log(NDLS_DAEMON, NDLP_DEBUG,
406 + "[%s]: Sending notification for alarm '%s.%s' status %s.",
407 + rrdhost_hostname(host), ae_chart_id(ae), ae_name(ae), rrdcalc_status2string(ae->new_status));
408 +
409 + const char *exec = (ae->exec) ? ae_exec(ae) : string2str(host->health.health_default_exec);
410 + const char *recipient = (ae->recipient) ? ae_recipient(ae) : string2str(host->health.health_default_recipient);
411 +
412 + char *edit_command = ae->source ? health_edit_command_from_source(ae_source(ae)) : strdupz("UNKNOWN=0=UNKNOWN");
413 +
414 + BUFFER *warn_alarms = buffer_create(1024, &netdata_buffers_statistics.buffers_health);
415 + BUFFER *crit_alarms = buffer_create(1024, &netdata_buffers_statistics.buffers_health);
416 +
417 + size_t n_warn = health_raised_summary_entries(hrm, warn_alarms, ae, RRDCALC_STATUS_WARNING);
418 + size_t n_crit = health_raised_summary_entries(hrm, crit_alarms, ae, RRDCALC_STATUS_CRITICAL);
419 +
420 + BUFFER *wb = buffer_create(8192, &netdata_buffers_statistics.buffers_health);
421 + bool ok = prepare_command(wb,
422 + exec,
423 + recipient,
424 + rrdhost_registry_hostname(host),
425 + ae->unique_id,
426 + ae->alarm_id,
427 + ae->alarm_event_id,
428 + (unsigned long)ae->when,
429 + ae_name(ae),
430 + ae->chart?ae_chart_id(ae):"NOCHART",
431 + rrdcalc_status2string(ae->new_status),
432 + rrdcalc_status2string(ae->old_status),
433 + ae->new_value,
434 + ae->old_value,
435 + ae->source?ae_source(ae):"UNKNOWN",
436 + (uint32_t)ae->duration,
437 + (ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING && ae->new_status >= RRDCALC_STATUS_WARNING) ? (uint32_t)ae->duration : (uint32_t)ae->non_clear_duration,
438 + ae_units(ae),
439 + ae_info(ae),
440 + ae_new_value_string(ae),
441 + ae_old_value_string(ae),
442 + health_raised_summary_my_expression_source(hrm, ae),
443 + health_raised_summary_my_expression_error(hrm, ae),
444 + n_warn,
445 + n_crit,
446 + buffer_tostring(warn_alarms),
447 + buffer_tostring(crit_alarms),
448 + ae->classification?ae_classification(ae):"Unknown",
449 + edit_command,
450 + host->machine_guid,
451 + &ae->transition_id,
452 + host->health.use_summary_for_notifications && ae->summary?ae_summary(ae):ae_name(ae),
453 + string2str(ae->chart_context),
454 + string2str(ae->component),
455 + string2str(ae->type)
456 + );
457 +
458 + const char *command_to_run = buffer_tostring(wb);
459 + if (ok) {
460 + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_RUN;
461 + ae->exec_run_timestamp = now_realtime_sec(); /* will be updated by real time after spawning */
462 +
463 + netdata_log_debug(D_HEALTH, "executing command '%s'", command_to_run);
464 + ae->flags |= HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS;
465 + ae->exec_spawn_serial = spawn_enq_cmd(command_to_run);
466 + enqueue_alarm_notify_in_progress(ae);
467 + health_alarm_log_save(host, ae);
468 + } else {
469 + netdata_log_error("Failed to format command arguments");
470 + }
471 +
472 + buffer_free(warn_alarms);
473 + buffer_free(crit_alarms);
474 + buffer_free(wb);
475 + freez(edit_command);
476 +
477 + return; //health_alarm_wait_for_execution
478 +done:
479 + health_alarm_log_save(host, ae);
480 +}
481 +
482 +bool health_alarm_log_get_global_id_and_transition_id_for_rrdcalc(RRDCALC *rc, usec_t *global_id, uuid_t *transitions_id) {
483 + if(!rc->rrdset)
484 + return false;
485 +
486 + RRDHOST *host = rc->rrdset->rrdhost;
487 +
488 + rw_spinlock_read_lock(&host->health_log.spinlock);
489 +
490 + ALARM_ENTRY *ae;
491 + for(ae = host->health_log.alarms; ae ; ae = ae->next) {
492 + if(unlikely(ae->alarm_id == rc->id))
493 + break;
494 + }
495 +
496 + if(ae) {
497 + *global_id = ae->global_id;
498 + uuid_copy(*transitions_id, ae->transition_id);
499 + }
500 + else {
501 + *global_id = 0;
502 + uuid_clear(*transitions_id);
503 + }
504 +
505 + rw_spinlock_read_unlock(&host->health_log.spinlock);
506 +
507 + return ae != NULL;
508 +}
509 +
510 +void health_alarm_log_process_to_send_notifications(RRDHOST *host, struct health_raised_summary *hrm) {
511 + uint32_t first_waiting = (host->health_log.alarms)?host->health_log.alarms->unique_id:0;
512 + time_t now = now_realtime_sec();
513 +
514 + rw_spinlock_read_lock(&host->health_log.spinlock);
515 +
516 + ALARM_ENTRY *ae;
517 + for(ae = host->health_log.alarms; ae && ae->unique_id >= host->health_last_processed_id; ae = ae->next) {
518 + if(unlikely(
519 + !(ae->flags & HEALTH_ENTRY_FLAG_PROCESSED) &&
520 + !(ae->flags & HEALTH_ENTRY_FLAG_UPDATED)
521 + )) {
522 + if(unlikely(ae->unique_id < first_waiting))
523 + first_waiting = ae->unique_id;
524 +
525 + if(likely(now >= ae->delay_up_to_timestamp))
526 + health_send_notification(host, ae, hrm);
527 + }
528 + }
529 +
530 + rw_spinlock_read_unlock(&host->health_log.spinlock);
531 +
532 + // remember this for the next iteration
533 + host->health_last_processed_id = first_waiting;
534 +
535 + //delete those that are updated, no in progress execution, and is not repeating
536 + rw_spinlock_write_lock(&host->health_log.spinlock);
537 +
538 + ALARM_ENTRY *prev = NULL, *next = NULL;
539 + for(ae = host->health_log.alarms; ae ; ae = next) {
540 + next = ae->next; // set it here, for the next iteration
541 +
542 + if((likely(!(ae->flags & HEALTH_ENTRY_FLAG_IS_REPEATING)) &&
543 + (ae->flags & HEALTH_ENTRY_FLAG_UPDATED) &&
544 + (ae->flags & HEALTH_ENTRY_FLAG_SAVED) &&
545 + !(ae->flags & HEALTH_ENTRY_FLAG_EXEC_IN_PROGRESS))
546 + ||
547 + ((ae->new_status == RRDCALC_STATUS_REMOVED) &&
548 + (ae->flags & HEALTH_ENTRY_FLAG_SAVED) &&
549 + (ae->when + 86400 < now_realtime_sec())))
550 + {
551 +
552 + if(host->health_log.alarms == ae) {
553 + host->health_log.alarms = next;
554 + // prev is also NULL here
555 + }
556 + else {
557 + prev->next = next;
558 + // prev should not be touched here - we need it for the next iteration
559 + // because we may have to also remove the next item
560 + }
561 +
562 + health_alarm_log_free_one_nochecks_nounlink(ae);
563 + }
564 + else
565 + prev = ae;
566 + }
567 +
568 + rw_spinlock_write_unlock(&host->health_log.spinlock);
569 +}
health/health_prototypes.c new
+616
@@ -0,0 +1,616 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "health_internals.h"
4 +
5 +// ---------------------------------------------------------------------------------------------------------------------
6 +
7 +static struct {
8 + const char *name;
9 + uint32_t hash;
10 + ALERT_ACTION_OPTIONS value;
11 +} alert_action_options[] = {
12 + { "no-clear-notification", 0 , ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION}
13 +
14 + // terminator
15 + , {NULL, 0, 0}
16 +};
17 +
18 +inline ALERT_ACTION_OPTIONS alert_action_options_parse_one(const char *o) {
19 + ALERT_ACTION_OPTIONS ret = 0;
20 +
21 + if(!o || !*o) return ret;
22 +
23 + uint32_t hash = simple_hash(o);
24 + int i;
25 + for(i = 0; alert_action_options[i].name ; i++) {
26 + if (unlikely(hash == alert_action_options[i].hash && !strcmp(o, alert_action_options[i].name))) {
27 + ret |= alert_action_options[i].value;
28 + break;
29 + }
30 + }
31 +
32 + return ret;
33 +}
34 +
35 +inline ALERT_ACTION_OPTIONS alert_action_options_parse(char *o) {
36 + ALERT_ACTION_OPTIONS ret = 0;
37 + char *tok;
38 +
39 + while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
40 + if(!*tok) continue;
41 + ret |= alert_action_options_parse_one(tok);
42 + }
43 +
44 + return ret;
45 +}
46 +
47 +void alert_action_options_to_buffer_json_array(BUFFER *wb, const char *key, ALERT_ACTION_OPTIONS options) {
48 + buffer_json_member_add_array(wb, key);
49 +
50 + RRDR_OPTIONS used = 0; // to prevent adding duplicates
51 + for(int i = 0; alert_action_options[i].name ; i++) {
52 + if (unlikely((alert_action_options[i].value & options) && !(alert_action_options[i].value & used))) {
53 + const char *name = alert_action_options[i].name;
54 + used |= alert_action_options[i].value;
55 +
56 + buffer_json_add_array_item_string(wb, name);
57 + }
58 + }
59 +
60 + buffer_json_array_close(wb);
61 +}
62 +
63 +static void alert_action_options_init(void) {
64 + for(int i = 0; alert_action_options[i].name ; i++)
65 + alert_action_options[i].hash = simple_hash(alert_action_options[i].name);
66 +}
67 +
68 +
69 +// ---------------------------------------------------------------------------------------------------------------------
70 +
71 +static void health_prototype_cleanup_one_unsafe(RRD_ALERT_PROTOTYPE *ap) {
72 + rrd_alert_match_cleanup(&ap->match);
73 + rrd_alert_config_cleanup(&ap->config);
74 +}
75 +
76 +void health_prototype_cleanup(RRD_ALERT_PROTOTYPE *ap) {
77 + spinlock_lock(&ap->_internal.spinlock);
78 +
79 + while(ap->_internal.next) {
80 + RRD_ALERT_PROTOTYPE *t = ap->_internal.next;
81 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(ap->_internal.next, t, _internal.prev, _internal.next);
82 + health_prototype_cleanup_one_unsafe(t);
83 + freez(t);
84 + }
85 +
86 + spinlock_unlock(&ap->_internal.spinlock);
87 +
88 + health_prototype_cleanup_one_unsafe(ap);
89 +}
90 +
91 +void health_prototype_free(RRD_ALERT_PROTOTYPE *ap) {
92 + if(!ap) return;
93 + health_prototype_cleanup(ap);
94 + freez(ap);
95 +}
96 +
97 +void health_prototype_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
98 + RRD_ALERT_PROTOTYPE *ap = value;
99 + spinlock_init(&ap->_internal.spinlock);
100 + if(ap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG)
101 + ap->_internal.is_on_disk = true;
102 +}
103 +
104 +bool health_prototype_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
105 + RRD_ALERT_PROTOTYPE *ap = old_value;
106 + RRD_ALERT_PROTOTYPE *nap = new_value;
107 +
108 + bool replace = nap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG;
109 +
110 + if(ap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG || nap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG)
111 + ap->_internal.is_on_disk = nap->_internal.is_on_disk = true;
112 +
113 + if(!replace) {
114 + if(ap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG) {
115 + // the existing is a dyncfg and the new one is read from the config
116 + health_prototype_cleanup(nap);
117 + memset(nap, 0, sizeof(*nap));
118 + }
119 + else {
120 + // alerts with the same name are appended to the existing one
121 + nap = callocz(1, sizeof(*nap));
122 + memcpy(nap, new_value, sizeof(*nap));
123 +
124 + spinlock_lock(&ap->_internal.spinlock);
125 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(ap->_internal.next, nap, _internal.prev, _internal.next);
126 + spinlock_unlock(&ap->_internal.spinlock);
127 +
128 + if(nap->_internal.enabled)
129 + ap->_internal.enabled = true;
130 + }
131 + }
132 + else {
133 + // alerts with the same name replace the existing one
134 + spinlock_init(&nap->_internal.spinlock);
135 + nap->_internal.uses = ap->_internal.uses;
136 +
137 + spinlock_lock(&nap->_internal.spinlock);
138 + spinlock_lock(&ap->_internal.spinlock);
139 + SWAP(*ap, *nap);
140 + spinlock_unlock(&ap->_internal.spinlock);
141 + spinlock_unlock(&nap->_internal.spinlock);
142 +
143 + health_prototype_cleanup(nap);
144 + memset(nap, 0, sizeof(*nap));
145 + }
146 +
147 + return true;
148 +}
149 +
150 +void health_prototype_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
151 + RRD_ALERT_PROTOTYPE *ap = value;
152 + health_prototype_cleanup(ap);
153 +}
154 +
155 +void health_init_prototypes(void) {
156 + if(health_globals.prototypes.dict)
157 + return;
158 +
159 + health_globals.prototypes.dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE);
160 + dictionary_register_insert_callback(health_globals.prototypes.dict, health_prototype_insert_cb, NULL);
161 + dictionary_register_conflict_callback(health_globals.prototypes.dict, health_prototype_conflict_cb, NULL);
162 + dictionary_register_delete_callback(health_globals.prototypes.dict, health_prototype_delete_cb, NULL);
163 +
164 + alert_action_options_init();
165 +}
166 +
167 +// ---------------------------------------------------------------------------------------------------------------------
168 +
169 +// If needed, add a prefix key to all possible values in the range
170 +static inline char *health_config_add_key_to_values(char *value) {
171 + BUFFER *wb = buffer_create(HEALTH_CONF_MAX_LINE + 1, NULL);
172 + char key[HEALTH_CONF_MAX_LINE + 1];
173 + char data[HEALTH_CONF_MAX_LINE + 1];
174 +
175 + char *s = value;
176 + size_t i = 0;
177 +
178 + key[0] = '\0';
179 + while(*s) {
180 + if (*s == '=') {
181 + //hold the key
182 + data[i]='\0';
183 + strncpyz(key, data, HEALTH_CONF_MAX_LINE);
184 + i=0;
185 + } else if (*s == ' ') {
186 + data[i]='\0';
187 + if (data[0]=='!')
188 + buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
189 + else
190 + buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
191 + i=0;
192 + } else {
193 + data[i++] = *s;
194 + }
195 + s++;
196 + }
197 +
198 + data[i]='\0';
199 + if (data[0]) {
200 + if (data[0]=='!')
201 + buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
202 + else
203 + buffer_snprintf(wb, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
204 + }
205 +
206 + char *final = strdupz(buffer_tostring(wb));
207 + buffer_free(wb);
208 +
209 + return final;
210 +}
211 +
212 +static void health_prototype_activate_match_patterns(struct rrd_alert_match *am) {
213 + if(am->os) {
214 + simple_pattern_free(am->os_pattern);
215 +
216 + char *tmp = simple_pattern_trim_around_equal(string2str(am->os));
217 + am->os_pattern = simple_pattern_create(
218 + tmp, NULL, SIMPLE_PATTERN_EXACT, true);
219 + freez(tmp);
220 + }
221 +
222 + if(am->host) {
223 + simple_pattern_free(am->host_pattern);
224 +
225 + char *tmp = simple_pattern_trim_around_equal(string2str(am->host));
226 + am->host_pattern = simple_pattern_create(
227 + tmp, NULL, SIMPLE_PATTERN_EXACT, true);
228 + freez(tmp);
229 + }
230 +
231 + if(am->charts) {
232 + simple_pattern_free(am->charts_pattern);
233 +
234 + char *tmp = simple_pattern_trim_around_equal(string2str(am->charts));
235 + am->charts_pattern = simple_pattern_create(
236 + tmp, NULL, SIMPLE_PATTERN_EXACT, true);
237 + freez(tmp);
238 + }
239 +
240 + if(am->plugin) {
241 + simple_pattern_free(am->plugin_pattern);
242 +
243 + char *tmp = simple_pattern_trim_around_equal(string2str(am->plugin));
244 + am->plugin_pattern = simple_pattern_create(
245 + tmp, NULL, SIMPLE_PATTERN_EXACT, true);
246 + freez(tmp);
247 + }
248 +
249 + if(am->module) {
250 + simple_pattern_free(am->module_pattern);
251 +
252 + char *tmp = simple_pattern_trim_around_equal(string2str(am->module));
253 + am->module_pattern = simple_pattern_create(
254 + tmp, NULL, SIMPLE_PATTERN_EXACT, true);
255 + freez(tmp);
256 + }
257 +
258 + if(am->host_labels) {
259 + simple_pattern_free(am->host_labels_pattern);
260 +
261 + char *tmp = simple_pattern_trim_around_equal(string2str(am->host_labels));
262 + am->host_labels_pattern = simple_pattern_create(
263 + tmp, NULL, SIMPLE_PATTERN_EXACT, true);
264 + freez(tmp);
265 + }
266 +
267 + if(am->chart_labels) {
268 + simple_pattern_free(am->chart_labels_pattern);
269 +
270 + char *tmp = simple_pattern_trim_around_equal(string2str(am->chart_labels));
271 + char *tmp2 = health_config_add_key_to_values(tmp);
272 + am->chart_labels_pattern = simple_pattern_create(
273 + tmp2, NULL, SIMPLE_PATTERN_EXACT, true);
274 + freez(tmp2);
275 + freez(tmp);
276 + }
277 +}
278 +
279 +void health_prototype_hash_id(RRD_ALERT_PROTOTYPE *ap) {
280 + CLEAN_BUFFER *wb = buffer_create(100, NULL);
281 + health_prototype_to_json(wb, ap, true);
282 + UUID uuid = UUID_generate_from_hash(buffer_tostring(wb), buffer_strlen(wb));
283 + uuid_copy(ap->config.hash_id, uuid.uuid);
284 +
285 + (void) sql_alert_store_config(ap);
286 +}
287 +
288 +bool health_prototype_add(RRD_ALERT_PROTOTYPE *ap) {
289 + if(!ap->match.is_template) {
290 + if(!ap->match.on.chart) {
291 + netdata_log_error(
292 + "HEALTH: alert '%s' does not define a instance (parameter 'on'). Source: %s",
293 + string2str(ap->config.name), string2str(ap->config.source));
294 + return false;
295 + }
296 + }
297 + else {
298 + if(!ap->match.on.context) {
299 + netdata_log_error(
300 + "HEALTH: alert '%s' does not define a context (parameter 'on'). Source: %s",
301 + string2str(ap->config.name), string2str(ap->config.source));
302 + return false;
303 + }
304 + }
305 +
306 + if(!ap->config.update_every) {
307 + netdata_log_error(
308 + "HEALTH: alert '%s' has no frequency (parameter 'every'). Source: %s",
309 + string2str(ap->config.name), string2str(ap->config.source));
310 + return false;
311 + }
312 +
313 + if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation && !ap->config.warning && !ap->config.critical) {
314 + netdata_log_error(
315 + "HEALTH: alert '%s' is useless (no db lookup, no calculation, no warning and no critical expressions). Source: %s",
316 + string2str(ap->config.name), string2str(ap->config.source));
317 + return false;
318 + }
319 +
320 + // activate the match patterns in it
321 + bool enabled = false;
322 + for(RRD_ALERT_PROTOTYPE *t = ap; t ;t = t->_internal.next) {
323 + // we need to generate config_hash_id for each instance included
324 + // so, let's break the linked list for this iteration
325 +
326 + RRD_ALERT_PROTOTYPE *prev = t->_internal.prev;
327 + RRD_ALERT_PROTOTYPE *next = t->_internal.next;
328 + t->_internal.prev = t;
329 + t->_internal.next = NULL;
330 +
331 + if(t->match.enabled)
332 + enabled = true;
333 +
334 + if(!t->config.name)
335 + t->config.name = string_dup(ap->config.name);
336 +
337 + health_prototype_hash_id(t);
338 +
339 + health_prototype_activate_match_patterns(&t->match);
340 +
341 + if (!t->config.exec)
342 + t->config.exec = string_dup(health_globals.config.default_exec);
343 +
344 + if (!t->config.recipient)
345 + t->config.recipient = string_dup(health_globals.config.default_recipient);
346 +
347 + // restore the linked list
348 + t->_internal.prev = prev;
349 + t->_internal.next = next;
350 + }
351 + ap->_internal.enabled = enabled;
352 +
353 + // add it to the prototypes
354 + dictionary_set_advanced(health_globals.prototypes.dict,
355 + string2str(ap->config.name), string_strlen(ap->config.name),
356 + ap, sizeof(*ap),
357 + NULL);
358 +
359 + return true;
360 +}
361 +
362 +// ---------------------------------------------------------------------------------------------------------------------
363 +
364 +void health_reload_prototypes(void) {
365 + // remove all dyncfg related to prototypes
366 + health_dyncfg_unregister_all_prototypes();
367 +
368 + // clear old prototypes from memory
369 + dictionary_flush(health_globals.prototypes.dict);
370 +
371 + // load the prototypes from disk
372 + recursive_config_double_dir_load(
373 + health_user_config_dir(),
374 + health_globals.config.stock_enabled ? health_stock_config_dir() : NULL,
375 + NULL,
376 + health_readfile,
377 + NULL, 0);
378 +
379 + // register all loaded prototypes
380 + health_dyncfg_register_all_prototypes();
381 +}
382 +
383 +// ---------------------------------------------------------------------------------------------------------------------
384 +
385 +static bool prototype_matches_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap) {
386 + if(health_globals.config.enabled_alerts &&
387 + !simple_pattern_matches(health_globals.config.enabled_alerts, string2str(ap->config.name)))
388 + return false;
389 +
390 + if(ap->match.os_pattern && !simple_pattern_matches_string(ap->match.os_pattern, host->os))
391 + return false;
392 +
393 + if(ap->match.host_pattern && !simple_pattern_matches_string(ap->match.host_pattern, host->hostname))
394 + return false;
395 +
396 + if(host->rrdlabels && ap->match.host_labels_pattern &&
397 + !rrdlabels_match_simple_pattern_parsed(
398 + host->rrdlabels, ap->match.host_labels_pattern, '=', NULL))
399 + return false;
400 +
401 + return true;
402 +}
403 +
404 +static bool prototype_matches_rrdset(RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
405 + // match the chart id
406 + if(!ap->match.is_template && ap->match.on.chart &&
407 + ap->match.on.chart != st->id && ap->match.on.chart != st->name)
408 + return false;
409 +
410 + // match the chart context
411 + if(ap->match.is_template && ap->match.on.context &&
412 + ap->match.on.context != st->context)
413 + return false;
414 +
415 + // match the chart pattern
416 + if(ap->match.is_template && ap->match.charts && ap->match.charts_pattern &&
417 + !simple_pattern_matches_string(ap->match.charts_pattern, st->id) &&
418 + !simple_pattern_matches_string(ap->match.charts_pattern, st->name))
419 + return false;
420 +
421 + // match the plugin pattern
422 + if(ap->match.plugin && ap->match.plugin_pattern &&
423 + !simple_pattern_matches_string(ap->match.plugin_pattern, st->plugin_name))
424 + return false;
425 +
426 + // match the module pattern
427 + if(ap->match.module && ap->match.module_pattern &&
428 + !simple_pattern_matches_string(ap->match.module_pattern, st->module_name))
429 + return false;
430 +
431 + if (st->rrdlabels && ap->match.chart_labels && ap->match.chart_labels_pattern &&
432 + !rrdlabels_match_simple_pattern_parsed(
433 + st->rrdlabels, ap->match.chart_labels_pattern, '=', NULL))
434 + return false;
435 +
436 + return true;
437 +}
438 +
439 +void health_prototype_copy_match_without_patterns(struct rrd_alert_match *dst, struct rrd_alert_match *src) {
440 + dst->enabled = src->enabled;
441 + dst->is_template = src->is_template;
442 +
443 + if(dst->is_template)
444 + dst->on.context = string_dup(src->on.context);
445 + else
446 + dst->on.chart = string_dup(src->on.chart);
447 +
448 + dst->os = string_dup(src->os);
449 + dst->host = string_dup(src->host);
450 + dst->charts = string_dup(src->charts);
451 + dst->plugin = string_dup(src->plugin);
452 + dst->module = string_dup(src->module);
453 + dst->host_labels = string_dup(src->host_labels);
454 + dst->chart_labels = string_dup(src->chart_labels);
455 +}
456 +
457 +void health_prototype_copy_config(struct rrd_alert_config *dst, struct rrd_alert_config *src) {
458 + uuid_copy(dst->hash_id, src->hash_id);
459 +
460 + dst->name = string_dup(src->name);
461 +
462 + dst->exec = string_dup(src->exec);
463 + dst->recipient = string_dup(src->recipient);
464 +
465 + dst->classification = string_dup(src->classification);
466 + dst->component = string_dup(src->component);
467 + dst->type = string_dup(src->type);
468 +
469 + dst->source_type = src->source_type;
470 + dst->source = string_dup(src->source);
471 + dst->units = string_dup(src->units);
472 + dst->summary = string_dup(src->summary);
473 + dst->info = string_dup(src->info);
474 +
475 + dst->update_every = src->update_every;
476 +
477 + dst->green = src->green;
478 + dst->red = src->red;
479 +
480 + dst->dimensions = string_dup(src->dimensions);
481 +
482 + dst->group = src->group;
483 + dst->before = src->before;
484 + dst->after = src->after;
485 + dst->options = src->options;
486 +
487 + const char *failed_at = NULL;
488 + int error = 0;
489 +
490 + dst->calculation = expression_parse(expression_source(src->calculation), &failed_at, &error);
491 + dst->warning = expression_parse(expression_source(src->warning), &failed_at, &error);
492 + dst->critical = expression_parse(expression_source(src->critical), &failed_at, &error);
493 +
494 + dst->delay_up_duration = src->delay_up_duration;
495 + dst->delay_down_duration = src->delay_down_duration;
496 + dst->delay_max_duration = src->delay_max_duration;
497 + dst->delay_multiplier = src->delay_multiplier;
498 +
499 + dst->has_custom_repeat_config = src->has_custom_repeat_config;
500 + dst->warn_repeat_every = src->warn_repeat_every;
501 + dst->crit_repeat_every = src->crit_repeat_every;
502 +}
503 +
504 +static void health_prototype_apply_to_rrdset(RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
505 + if(!ap->_internal.enabled)
506 + return;
507 +
508 + spinlock_lock(&ap->_internal.spinlock);
509 + for(RRD_ALERT_PROTOTYPE *t = ap; t ; t = t->_internal.next) {
510 + if(!t->match.enabled)
511 + continue;
512 +
513 + if(!prototype_matches_host(st->rrdhost, t))
514 + continue;
515 +
516 + if(!prototype_matches_rrdset(st, t))
517 + continue;
518 +
519 + if(rrdcalc_add_from_prototype(st->rrdhost, st, ap))
520 + ap->_internal.uses++;
521 + }
522 + spinlock_unlock(&ap->_internal.spinlock);
523 +}
524 +
525 +void health_prototype_alerts_for_rrdset_incrementally(RRDSET *st) {
526 + RRD_ALERT_PROTOTYPE *ap;
527 + dfe_start_read(health_globals.prototypes.dict, ap) {
528 + health_prototype_apply_to_rrdset(st, ap);
529 + }
530 + dfe_done(ap);
531 +}
532 +
533 +void health_prototype_reset_alerts_for_rrdset(RRDSET *st) {
534 + rrdcalc_unlink_and_delete_all_rrdset_alerts(st);
535 + health_prototype_alerts_for_rrdset_incrementally(st);
536 +}
537 +
538 +// ---------------------------------------------------------------------------------------------------------------------
539 +
540 +void health_apply_prototype_to_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap) {
541 + if(!ap->_internal.enabled)
542 + return;
543 +
544 + if(unlikely(!host->health.health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH))
545 + return;
546 +
547 + RRDSET *st;
548 + rrdset_foreach_read(st, host) {
549 + health_prototype_apply_to_rrdset(st, ap);
550 + }
551 + rrdset_foreach_done(st);
552 +}
553 +
554 +void health_prototype_apply_to_all_hosts(RRD_ALERT_PROTOTYPE *ap) {
555 + if(!ap->_internal.enabled)
556 + return;
557 +
558 + RRDHOST *host;
559 + dfe_start_reentrant(rrdhost_root_index, host){
560 + health_apply_prototype_to_host(host, ap);
561 + }
562 + dfe_done(host);
563 +}
564 +
565 +// ---------------------------------------------------------------------------------------------------------------------
566 +
567 +void health_apply_prototypes_to_host(RRDHOST *host) {
568 + if(unlikely(!host->health.health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH))
569 + return;
570 +
571 + // free all running alarms
572 + rrdcalc_delete_all(host);
573 +
574 + // invalidate all previous entries in the alarm log
575 + rw_spinlock_read_lock(&host->health_log.spinlock);
576 + ALARM_ENTRY *t;
577 + for(t = host->health_log.alarms ; t ; t = t->next) {
578 + if(t->new_status != RRDCALC_STATUS_REMOVED)
579 + t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
580 + }
581 + rw_spinlock_read_unlock(&host->health_log.spinlock);
582 +
583 + // apply all the prototypes for the charts of the host
584 + RRDSET *st;
585 + rrdset_foreach_read(st, host) {
586 + health_prototype_reset_alerts_for_rrdset(st);
587 + }
588 + rrdset_foreach_done(st);
589 +
590 +#ifdef ENABLE_ACLK
591 + if (netdata_cloud_enabled) {
592 + struct aclk_sync_cfg_t *wc = host->aclk_config;
593 + if (likely(wc)) {
594 + wc->alert_queue_removed = SEND_REMOVED_AFTER_HEALTH_LOOPS;
595 + }
596 + }
597 +#endif
598 +}
599 +
600 +void health_apply_prototypes_to_all_hosts(void) {
601 + RRDHOST *host;
602 + dfe_start_reentrant(rrdhost_root_index, host){
603 + health_apply_prototypes_to_host(host);
604 + }
605 + dfe_done(host);
606 +}
607 +
608 +// ---------------------------------------------------------------------------------------------------------------------
609 +
610 +void health_prototype_metadata_foreach(void *data, prototype_metadata_cb_t cb) {
611 + RRD_ALERT_PROTOTYPE *ap;
612 + dfe_start_read(health_globals.prototypes.dict, ap) {
613 + cb(data, ap->config.type, ap->config.component, ap->config.classification, ap->config.recipient);
614 + }
615 + dfe_done(ap);
616 +}
health/health_prototypes.h new
+120
@@ -0,0 +1,120 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_HEALTH_PROTOTYPES_H
4 +#define NETDATA_HEALTH_PROTOTYPES_H
5 +
6 +#include "../web/api/queries/rrdr.h"
7 +
8 +typedef enum __attribute__((packed)) {
9 + ALERT_ACTION_OPTION_NONE = 0,
10 + ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION = (1 << 0),
11 +} ALERT_ACTION_OPTIONS;
12 +
13 +struct rrd_alert_match {
14 + bool enabled;
15 +
16 + bool is_template;
17 + union {
18 + STRING *chart;
19 + STRING *context;
20 + } on;
21 +
22 + STRING *os;
23 + STRING *host;
24 + STRING *charts; // the charts that should be linked to (for templates)
25 + STRING *plugin; // the plugin name that should be linked to
26 + STRING *module; // the module name that should be linked to
27 + STRING *host_labels; // the label read from an alarm file
28 + STRING *chart_labels; // the chart label read from an alarm file
29 +
30 + SIMPLE_PATTERN *os_pattern;
31 + SIMPLE_PATTERN *host_pattern;
32 + SIMPLE_PATTERN *charts_pattern; // the simple pattern of charts
33 + SIMPLE_PATTERN *plugin_pattern; // the simple pattern of plugin
34 + SIMPLE_PATTERN *module_pattern; // the simple pattern of module
35 + SIMPLE_PATTERN *host_labels_pattern; // the simple pattern of labels
36 + SIMPLE_PATTERN *chart_labels_pattern; // the simple pattern of chart labels
37 +};
38 +void rrd_alert_match_cleanup(struct rrd_alert_match *am);
39 +
40 +struct rrd_alert_config {
41 + uuid_t hash_id;
42 +
43 + STRING *name; // the name of this alarm
44 +
45 + STRING *exec; // the command to execute when this alarm switches state
46 + STRING *recipient; // the recipient of the alarm (the first parameter to exec)
47 +
48 + STRING *classification; // the class that this alarm belongs
49 + STRING *component; // the component that this alarm refers to
50 + STRING *type; // type of the alarm
51 +
52 + DYNCFG_SOURCE_TYPE source_type;
53 + STRING *source; // the source of this alarm
54 + STRING *units; // the units of the alarm
55 + STRING *summary; // a short alert summary
56 + STRING *info; // a description of the alarm
57 + STRING *lookup; // the lookup field
58 +
59 + int update_every; // update frequency for the alarm
60 +
61 + ALERT_ACTION_OPTIONS alert_action_options;
62 +
63 + // the red and green threshold of this alarm (to be set to the chart)
64 + NETDATA_DOUBLE green;
65 + NETDATA_DOUBLE red;
66 +
67 + // ------------------------------------------------------------------------
68 + // database lookup settings
69 +
70 + STRING *dimensions; // the chart dimensions
71 + RRDR_TIME_GROUPING group; // grouping method: average, max, etc.
72 + int before; // ending point in time-series
73 + int after; // starting point in time-series
74 + RRDR_OPTIONS options; // configuration options
75 +
76 + // ------------------------------------------------------------------------
77 + // expressions related to the alarm
78 +
79 + EVAL_EXPRESSION *calculation; // expression to calculate the value of the alarm
80 + EVAL_EXPRESSION *warning; // expression to check the warning condition
81 + EVAL_EXPRESSION *critical; // expression to check the critical condition
82 +
83 + // ------------------------------------------------------------------------
84 + // notification delay settings
85 +
86 + int delay_up_duration; // duration to delay notifications when alarm raises
87 + int delay_down_duration; // duration to delay notifications when alarm lowers
88 + int delay_max_duration; // the absolute max delay to apply to this alarm
89 + float delay_multiplier; // multiplier for all delays when alarms switch status
90 + // while now < delay_up_to
91 +
92 + // ------------------------------------------------------------------------
93 + // notification repeat settings
94 +
95 + bool has_custom_repeat_config;
96 + uint32_t warn_repeat_every; // interval between repeating warning notifications
97 + uint32_t crit_repeat_every; // interval between repeating critical notifications
98 +};
99 +void rrd_alert_config_cleanup(struct rrd_alert_config *ac);
100 +
101 +#include "health.h"
102 +
103 +void health_init_prototypes(void);
104 +
105 +bool health_plugin_enabled(void);
106 +void health_plugin_disable(void);
107 +
108 +void health_reload_prototypes(void);
109 +void health_apply_prototypes_to_host(RRDHOST *host);
110 +void health_apply_prototypes_to_all_hosts(void);
111 +
112 +void health_prototype_alerts_for_rrdset_incrementally(RRDSET *st);
113 +
114 +struct rrd_alert_config;
115 +struct rrd_alert_match;
116 +void health_prototype_copy_config(struct rrd_alert_config *dst, struct rrd_alert_config *src);
117 +void health_prototype_copy_match_without_patterns(struct rrd_alert_match *dst, struct rrd_alert_match *src);
118 +void health_prototype_reset_alerts_for_rrdset(RRDSET *st);
119 +
120 +#endif //NETDATA_HEALTH_PROTOTYPES_H
health/health_silencers.c new
+495
@@ -0,0 +1,495 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "health_internals.h"
4 +
5 +#define HEALTH_CMDAPI_CMD_SILENCEALL "SILENCE ALL"
6 +#define HEALTH_CMDAPI_CMD_DISABLEALL "DISABLE ALL"
7 +#define HEALTH_CMDAPI_CMD_SILENCE "SILENCE"
8 +#define HEALTH_CMDAPI_CMD_DISABLE "DISABLE"
9 +#define HEALTH_CMDAPI_CMD_RESET "RESET"
10 +#define HEALTH_CMDAPI_CMD_LIST "LIST"
11 +
12 +#define HEALTH_CMDAPI_MSG_AUTHERROR "Auth Error\n"
13 +#define HEALTH_CMDAPI_MSG_SILENCEALL "All alarm notifications are silenced\n"
14 +#define HEALTH_CMDAPI_MSG_DISABLEALL "All health checks are disabled\n"
15 +#define HEALTH_CMDAPI_MSG_RESET "All health checks and notifications are enabled\n"
16 +#define HEALTH_CMDAPI_MSG_DISABLE "Health checks disabled for alarms matching the selectors\n"
17 +#define HEALTH_CMDAPI_MSG_SILENCE "Alarm notifications silenced for alarms matching the selectors\n"
18 +#define HEALTH_CMDAPI_MSG_ADDED "Alarm selector added\n"
19 +#define HEALTH_CMDAPI_MSG_STYPEWARNING "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command.\n"
20 +#define HEALTH_CMDAPI_MSG_NOSELECTORWARNING "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.\n"
21 +
22 +SILENCERS *silencers;
23 +
24 +/**
25 + * Create Silencer
26 + *
27 + * Allocate a new silencer to Netdata.
28 + *
29 + * @return It returns the address off the silencer on success and NULL otherwise
30 + */
31 +SILENCER *create_silencer(void) {
32 + SILENCER *t = callocz(1, sizeof(SILENCER));
33 + netdata_log_debug(D_HEALTH, "HEALTH command API: Created empty silencer");
34 +
35 + return t;
36 +}
37 +
38 +/**
39 + * Health Silencers add
40 + *
41 + * Add more one silencer to the list of silencers.
42 + *
43 + * @param silencer
44 + */
45 +void health_silencers_add(SILENCER *silencer) {
46 + // Add the created instance to the linked list in silencers
47 + silencer->next = silencers->silencers;
48 + silencers->silencers = silencer;
49 + netdata_log_debug(
50 + D_HEALTH,
51 + "HEALTH command API: Added silencer %s:%s:%s:%s",
52 + silencer->alarms,
53 + silencer->charts,
54 + silencer->contexts,
55 + silencer->hosts);
56 +}
57 +
58 +/**
59 + * Silencers Add Parameter
60 + *
61 + * Create a new silencer and adjust the variables
62 + *
63 + * @param silencer a pointer to the silencer that will be adjusted
64 + * @param key the key value sent by client
65 + * @param value the value sent to the key
66 + *
67 + * @return It returns the silencer configured on success and NULL otherwise
68 + */
69 +SILENCER *health_silencers_addparam(SILENCER *silencer, char *key, char *value) {
70 + static uint32_t
71 + hash_alarm = 0,
72 + hash_template = 0,
73 + hash_chart = 0,
74 + hash_context = 0,
75 + hash_host = 0;
76 +
77 + if (unlikely(!hash_alarm)) {
78 + hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
79 + hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
80 + hash_chart = simple_uhash(HEALTH_CHART_KEY);
81 + hash_context = simple_uhash(HEALTH_CONTEXT_KEY);
82 + hash_host = simple_uhash(HEALTH_HOST_KEY);
83 + }
84 +
85 + uint32_t hash = simple_uhash(key);
86 + if (unlikely(silencer == NULL)) {
87 + if (
88 + (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
89 + (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
90 + (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
91 + (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
92 + (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY))
93 + ) {
94 + silencer = create_silencer();
95 + }
96 + }
97 +
98 + if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
99 + silencer->alarms = strdupz(value);
100 + silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT, true);
101 + } else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
102 + silencer->charts = strdupz(value);
103 + silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT, true);
104 + } else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
105 + silencer->contexts = strdupz(value);
106 + silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT, true);
107 + } else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
108 + silencer->hosts = strdupz(value);
109 + silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT, true);
110 + }
111 +
112 + return silencer;
113 +}
114 +
115 +/**
116 + * JSON Read Callback
117 + *
118 + * Callback called by netdata to create the silencer.
119 + *
120 + * @param e the main json structure
121 + *
122 + * @return It always return 0.
123 + */
124 +int health_silencers_json_read_callback(JSON_ENTRY *e)
125 +{
126 + switch(e->type) {
127 + case JSON_OBJECT:
128 +#ifndef ENABLE_JSONC
129 + e->callback_function = health_silencers_json_read_callback;
130 + if(strcmp(e->name,"")) {
131 + // init silencer
132 + netdata_log_debug(D_HEALTH, "JSON: Got object with a name, initializing new silencer for %s",e->name);
133 +#endif
134 + e->callback_data = create_silencer();
135 + if(e->callback_data) {
136 + health_silencers_add(e->callback_data);
137 + }
138 +#ifndef ENABLE_JSONC
139 + }
140 +#endif
141 + break;
142 +
143 + case JSON_ARRAY:
144 + e->callback_function = health_silencers_json_read_callback;
145 + break;
146 +
147 + case JSON_STRING:
148 + if(!strcmp(e->name,"type")) {
149 + netdata_log_debug(D_HEALTH, "JSON: Processing type=%s",e->data.string);
150 + if (!strcmp(e->data.string,"SILENCE")) silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
151 + else if (!strcmp(e->data.string,"DISABLE")) silencers->stype = STYPE_DISABLE_ALARMS;
152 + } else {
153 + netdata_log_debug(D_HEALTH, "JSON: Adding %s=%s", e->name, e->data.string);
154 + if (e->callback_data)
155 + (void)health_silencers_addparam(e->callback_data, e->name, e->data.string);
156 + }
157 + break;
158 +
159 + case JSON_BOOLEAN:
160 + netdata_log_debug(D_HEALTH, "JSON: Processing all_alarms");
161 + silencers->all_alarms=e->data.boolean?1:0;
162 + break;
163 +
164 + case JSON_NUMBER:
165 + case JSON_NULL:
166 + break;
167 + }
168 +
169 + return 0;
170 +}
171 +
172 +/**
173 + * Initialize Global Silencers
174 + *
175 + * Initialize the silencer for the whole netdata system.
176 + *
177 + * @return It returns 0 on success and -1 otherwise
178 + */
179 +int health_initialize_global_silencers() {
180 + silencers = mallocz(sizeof(SILENCERS));
181 + silencers->all_alarms = 0;
182 + silencers->stype = STYPE_NONE;
183 + silencers->silencers = NULL;
184 +
185 + return 0;
186 +}
187 +
188 +// ----------------------------------------------------------------------------
189 +
190 +/**
191 + * Free Silencers
192 + *
193 + * Clean the silencer structure
194 + *
195 + * @param t is the structure that will be cleaned.
196 + */
197 +void free_silencers(SILENCER *t) {
198 + if (!t) return;
199 +
200 + while(t) {
201 + SILENCER *next = t->next;
202 +
203 + simple_pattern_free(t->alarms_pattern);
204 + simple_pattern_free(t->charts_pattern);
205 + simple_pattern_free(t->contexts_pattern);
206 + simple_pattern_free(t->hosts_pattern);
207 + freez(t->alarms);
208 + freez(t->charts);
209 + freez(t->contexts);
210 + freez(t->hosts);
211 + freez(t);
212 +
213 + t = next;
214 + }
215 +}
216 +
217 +/**
218 + * Silencers to JSON Entry
219 + *
220 + * Fill the buffer with the other values given.
221 + *
222 + * @param wb a pointer to the output buffer
223 + * @param var the json variable
224 + * @param val the json value
225 + * @param hasprev has it a previous value?
226 + *
227 + * @return
228 + */
229 +int health_silencers2json_entry(BUFFER *wb, char* var, char* val, int hasprev) {
230 + if (val) {
231 + buffer_sprintf(wb, "%s\n\t\t\t\"%s\": \"%s\"", (hasprev)?",":"", var, val);
232 + return 1;
233 + } else {
234 + return hasprev;
235 + }
236 +}
237 +
238 +/**
239 + * Silencer to JSON
240 + *
241 + * Write the silencer values using JSON format inside a buffer.
242 + *
243 + * @param wb is the buffer to write the silencers.
244 + */
245 +void health_silencers2json(BUFFER *wb) {
246 + buffer_sprintf(wb, "{\n\t\"all\": %s,"
247 + "\n\t\"type\": \"%s\","
248 + "\n\t\"silencers\": [",
249 + (silencers->all_alarms)?"true":"false",
250 + (silencers->stype == STYPE_NONE)?"None":((silencers->stype == STYPE_DISABLE_ALARMS)?"DISABLE":"SILENCE"));
251 +
252 + SILENCER *silencer;
253 + int i = 0, j = 0;
254 + for(silencer = silencers->silencers; silencer ; silencer = silencer->next) {
255 + if(likely(i)) buffer_strcat(wb, ",");
256 + buffer_strcat(wb, "\n\t\t{");
257 + j=health_silencers2json_entry(wb, HEALTH_ALARM_KEY, silencer->alarms, j);
258 + j=health_silencers2json_entry(wb, HEALTH_CHART_KEY, silencer->charts, j);
259 + j=health_silencers2json_entry(wb, HEALTH_CONTEXT_KEY, silencer->contexts, j);
260 + j=health_silencers2json_entry(wb, HEALTH_HOST_KEY, silencer->hosts, j);
261 + j=0;
262 + buffer_strcat(wb, "\n\t\t}");
263 + i++;
264 + }
265 + if(likely(i)) buffer_strcat(wb, "\n\t");
266 + buffer_strcat(wb, "]\n}\n");
267 +}
268 +
269 +
270 +/**
271 + * Silencer to FILE
272 + *
273 + * Write the silencer buffer to a file.
274 + * @param wb
275 + */
276 +void health_silencers2file(BUFFER *wb) {
277 + if (wb->len == 0) return;
278 +
279 + FILE *fd = fopen(health_silencers_filename(), "wb");
280 + if(fd) {
281 + size_t written = (size_t)fprintf(fd, "%s", wb->buffer) ;
282 + if (written == wb->len ) {
283 + netdata_log_info("Silencer changes written to %s", health_silencers_filename());
284 + }
285 + fclose(fd);
286 + return;
287 + }
288 + netdata_log_error("Silencer changes could not be written to %s. Error %s", health_silencers_filename(), strerror(errno));
289 +}
290 +
291 +/**
292 + * Request V1 MGMT Health
293 + *
294 + * Function called by api to management the health.
295 + *
296 + * @param host main structure with client information!
297 + * @param w is the structure with all information of the client request.
298 + * @param url is the url that netdata is working
299 + *
300 + * @return It returns 200 on success and another code otherwise.
301 + */
302 +int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url) {
303 + int ret;
304 + (void) host;
305 +
306 + BUFFER *wb = w->response.data;
307 + buffer_flush(wb);
308 + wb->content_type = CT_TEXT_PLAIN;
309 +
310 + buffer_flush(w->response.data);
311 +
312 + //Local instance of the silencer
313 + SILENCER *silencer = NULL;
314 + int config_changed = 1;
315 +
316 + if (!w->auth_bearer_token) {
317 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
318 + ret = HTTP_RESP_FORBIDDEN;
319 + } else {
320 + netdata_log_debug(D_HEALTH, "HEALTH command API: Comparing secret '%s' to '%s'", w->auth_bearer_token, api_secret);
321 + if (strcmp(w->auth_bearer_token, api_secret) != 0) {
322 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
323 + ret = HTTP_RESP_FORBIDDEN;
324 + } else {
325 + while (url) {
326 + char *value = strsep_skip_consecutive_separators(&url, "&");
327 + if (!value || !*value) continue;
328 +
329 + char *key = strsep_skip_consecutive_separators(&value, "=");
330 + if (!key || !*key) continue;
331 + if (!value || !*value) continue;
332 +
333 + netdata_log_debug(D_WEB_CLIENT, "%llu: API v1 health query param '%s' with value '%s'", w->id, key, value);
334 +
335 + // name and value are now the parameters
336 + if (!strcmp(key, "cmd")) {
337 + if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
338 + silencers->all_alarms = 1;
339 + silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
340 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCEALL);
341 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLEALL)) {
342 + silencers->all_alarms = 1;
343 + silencers->stype = STYPE_DISABLE_ALARMS;
344 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLEALL);
345 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCE)) {
346 + silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
347 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCE);
348 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLE)) {
349 + silencers->stype = STYPE_DISABLE_ALARMS;
350 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLE);
351 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_RESET)) {
352 + silencers->all_alarms = 0;
353 + silencers->stype = STYPE_NONE;
354 + free_silencers(silencers->silencers);
355 + silencers->silencers = NULL;
356 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
357 + } else if (!strcmp(value, HEALTH_CMDAPI_CMD_LIST)) {
358 + w->response.data->content_type = CT_APPLICATION_JSON;
359 + health_silencers2json(wb);
360 + config_changed=0;
361 + }
362 + } else {
363 + silencer = health_silencers_addparam(silencer, key, value);
364 + }
365 + }
366 +
367 + if (likely(silencer)) {
368 + health_silencers_add(silencer);
369 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_ADDED);
370 + if (silencers->stype == STYPE_NONE) {
371 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_STYPEWARNING);
372 + }
373 + }
374 + if (unlikely(silencers->stype != STYPE_NONE && !silencers->all_alarms && !silencers->silencers)) {
375 + buffer_strcat(wb, HEALTH_CMDAPI_MSG_NOSELECTORWARNING);
376 + }
377 + ret = HTTP_RESP_OK;
378 + }
379 + }
380 + w->response.data = wb;
381 + buffer_no_cacheable(w->response.data);
382 + if (ret == HTTP_RESP_OK && config_changed) {
383 + BUFFER *jsonb = buffer_create(200, &netdata_buffers_statistics.buffers_health);
384 + health_silencers2json(jsonb);
385 + health_silencers2file(jsonb);
386 + buffer_free(jsonb);
387 + }
388 +
389 + return ret;
390 +}
391 +
392 +// ----------------------------------------------------------------------------
393 +
394 +const char *health_silencers_filename(void) {
395 + return string2str(health_globals.config.silencers_filename);
396 +}
397 +
398 +void health_set_silencers_filename(void) {
399 + char filename[FILENAME_MAX + 1];
400 + snprintfz(filename, FILENAME_MAX, "%s/health.silencers.json", netdata_configured_varlib_dir);
401 +
402 + health_globals.config.silencers_filename =
403 + string_strdupz(config_get(CONFIG_SECTION_HEALTH, "silencers file", filename));
404 +}
405 +
406 +void health_silencers_init(void) {
407 + FILE *fd = fopen(health_silencers_filename(), "r");
408 + if (fd) {
409 + fseek(fd, 0 , SEEK_END);
410 + off_t length = (off_t) ftell(fd);
411 + fseek(fd, 0 , SEEK_SET);
412 +
413 + if (length > 0 && length < HEALTH_SILENCERS_MAX_FILE_LEN) {
414 + char *str = mallocz((length+1)* sizeof(char));
415 + if(str) {
416 + size_t copied;
417 + copied = fread(str, sizeof(char), length, fd);
418 + if (copied == (length* sizeof(char))) {
419 + str[length] = 0x00;
420 + json_parse(str, NULL, health_silencers_json_read_callback);
421 + netdata_log_info("Parsed health silencers file %s", health_silencers_filename());
422 + } else {
423 + netdata_log_error("Cannot read the data from health silencers file %s", health_silencers_filename());
424 + }
425 + freez(str);
426 + }
427 + } else {
428 + netdata_log_error("Health silencers file %s has the size %" PRId64 " that is out of range[ 1 , %d ]. Aborting read.",
429 + health_silencers_filename(),
430 + (int64_t)length,
431 + HEALTH_SILENCERS_MAX_FILE_LEN);
432 + }
433 + fclose(fd);
434 + } else {
435 + netdata_log_info("Cannot open the file %s, so Netdata will work with the default health configuration.",
436 + health_silencers_filename());
437 + }
438 +}
439 +
440 +SILENCE_TYPE health_silencers_check_silenced(RRDCALC *rc, const char *host) {
441 + SILENCER *s;
442 +
443 + for (s = silencers->silencers; s!=NULL; s=s->next){
444 + if (
445 + (!s->alarms_pattern || (rc->config.name && s->alarms_pattern && simple_pattern_matches_string(s->alarms_pattern, rc->config.name))) &&
446 + (!s->contexts_pattern || (rc->rrdset && rc->rrdset->context && s->contexts_pattern && simple_pattern_matches_string(s->contexts_pattern, rc->rrdset->context))) &&
447 + (!s->hosts_pattern || (host && s->hosts_pattern && simple_pattern_matches(s->hosts_pattern, host))) &&
448 + (!s->charts_pattern || (rc->chart && s->charts_pattern && simple_pattern_matches_string(s->charts_pattern, rc->chart)))
449 + ) {
450 + netdata_log_debug(D_HEALTH, "Alarm matches command API silence entry %s:%s:%s:%s", s->alarms,s->charts, s->contexts, s->hosts);
451 + if (unlikely(silencers->stype == STYPE_NONE)) {
452 + netdata_log_debug(D_HEALTH, "Alarm %s matched a silence entry, but no SILENCE or DISABLE command was issued via the command API. The match has no effect.", rrdcalc_name(rc));
453 + } else {
454 + netdata_log_debug(D_HEALTH, "Alarm %s via the command API - name:%s context:%s chart:%s host:%s"
455 + , (silencers->stype == STYPE_DISABLE_ALARMS)?"Disabled":"Silenced"
456 + , rrdcalc_name(rc)
457 + , (rc->rrdset)?rrdset_context(rc->rrdset):""
458 + , rrdcalc_chart_name(rc)
459 + , host
460 + );
461 + }
462 + return silencers->stype;
463 + }
464 + }
465 + return STYPE_NONE;
466 +}
467 +
468 +int health_silencers_update_disabled_silenced(RRDHOST *host, RRDCALC *rc) {
469 + uint32_t rrdcalc_flags_old = rc->run_flags;
470 + // Clear the flags
471 + rc->run_flags &= ~(RRDCALC_FLAG_DISABLED | RRDCALC_FLAG_SILENCED);
472 + if (unlikely(silencers->all_alarms)) {
473 + if (silencers->stype == STYPE_DISABLE_ALARMS) rc->run_flags |= RRDCALC_FLAG_DISABLED;
474 + else if (silencers->stype == STYPE_SILENCE_NOTIFICATIONS) rc->run_flags |= RRDCALC_FLAG_SILENCED;
475 + } else {
476 + SILENCE_TYPE st = health_silencers_check_silenced(rc, rrdhost_hostname(host));
477 + if (st == STYPE_DISABLE_ALARMS) rc->run_flags |= RRDCALC_FLAG_DISABLED;
478 + else if (st == STYPE_SILENCE_NOTIFICATIONS) rc->run_flags |= RRDCALC_FLAG_SILENCED;
479 + }
480 +
481 + if (rrdcalc_flags_old != rc->run_flags) {
482 + netdata_log_info(
483 + "Alarm silencing changed for host '%s' alarm '%s': Disabled %s->%s Silenced %s->%s",
484 + rrdhost_hostname(host),
485 + rrdcalc_name(rc),
486 + (rrdcalc_flags_old & RRDCALC_FLAG_DISABLED) ? "true" : "false",
487 + (rc->run_flags & RRDCALC_FLAG_DISABLED) ? "true" : "false",
488 + (rrdcalc_flags_old & RRDCALC_FLAG_SILENCED) ? "true" : "false",
489 + (rc->run_flags & RRDCALC_FLAG_SILENCED) ? "true" : "false");
490 + }
491 + if (rc->run_flags & RRDCALC_FLAG_DISABLED)
492 + return 1;
493 + else
494 + return 0;
495 +}
health/health_silencers.h renamed
+15 -13
@@ -1,18 +1,9 @@
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 -#ifndef NETDATA_HEALTH_LIB
4 -# define NETDATA_HEALTH_LIB 1
3 +#ifndef NETDATA_HEALTH_SILENCERS_H
4 +#define NETDATA_HEALTH_SILENCERS_H
5
6 -# include "../libnetdata.h"
7 -
8 -#define HEALTH_ALARM_KEY "alarm"
9 -#define HEALTH_TEMPLATE_KEY "template"
10 -#define HEALTH_CONTEXT_KEY "context"
11 -#define HEALTH_CHART_KEY "chart"
12 -#define HEALTH_HOST_KEY "hosts"
13 -#define HEALTH_OS_KEY "os"
14 -#define HEALTH_LOOKUP_KEY "lookup"
15 -#define HEALTH_CALC_KEY "calc"
6 +#include "health.h"
7
8 typedef struct silencer {
9 char *alarms;
@@ -50,4 +41,15 @@ void health_silencers_add(SILENCER *silencer);
41 SILENCER * health_silencers_addparam(SILENCER *silencer, char *key, char *value);
42 int health_initialize_global_silencers();
43
53 -#endif
44 +void free_silencers(SILENCER *t);
45 +
46 +struct web_client;
47 +int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url);
48 +
49 +const char *health_silencers_filename(void);
50 +void health_set_silencers_filename(void);
51 +void health_silencers_init(void);
52 +SILENCE_TYPE health_silencers_check_silenced(RRDCALC *rc, const char *host);
53 +int health_silencers_update_disabled_silenced(RRDHOST *host, RRDCALC *rc);
54 +
55 +#endif //NETDATA_HEALTH_SILENCERS_H
health/health_variable.c new
+486
@@ -0,0 +1,486 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "health.h"
4 +#include "health_internals.h"
5 +
6 +struct variable_lookup_score {
7 + RRDSET *st;
8 + const char *source;
9 + NETDATA_DOUBLE value;
10 + size_t score;
11 +};
12 +
13 +struct variable_lookup_job {
14 + RRDCALC *rc;
15 + RRDHOST *host;
16 + STRING *variable;
17 + STRING *dim;
18 + const char *dimension;
19 + size_t dimension_length;
20 + enum {
21 + DIM_SELECT_NORMAL,
22 + DIM_SELECT_RAW,
23 + DIM_SELECT_LAST_COLLECTED,
24 + } dimension_selection;
25 +
26 + struct {
27 + size_t size;
28 + size_t used;
29 + struct variable_lookup_score *array;
30 + } result;
31 +
32 + struct {
33 + RRDSET *last_rrdset;
34 + size_t last_score;
35 + } score;
36 +};
37 +
38 +static void variable_lookup_add_result_with_score(struct variable_lookup_job *vbd, NETDATA_DOUBLE n, RRDSET *st, const char *source __maybe_unused) {
39 + if(vbd->score.last_rrdset != st) {
40 + vbd->score.last_rrdset = st;
41 + vbd->score.last_score = rrdlabels_common_count(vbd->rc->rrdset->rrdlabels, st->rrdlabels);
42 + }
43 +
44 + if(vbd->result.used >= vbd->result.size) {
45 + if(!vbd->result.size)
46 + vbd->result.size = 1;
47 +
48 + vbd->result.size *= 2;
49 + vbd->result.array = reallocz(vbd->result.array, sizeof(struct variable_lookup_score) * vbd->result.size);
50 + }
51 +
52 + vbd->result.array[vbd->result.used++] = (struct variable_lookup_score) {
53 + .value = n,
54 + .score = vbd->score.last_score,
55 + .st = st,
56 + .source = source,
57 + };
58 +}
59 +
60 +static bool variable_lookup_in_chart(struct variable_lookup_job *vbd, RRDSET *st, bool stop_on_match) {
61 + bool found = false;
62 + const DICTIONARY_ITEM *item = NULL;
63 + RRDDIM *rd = NULL;
64 + dfe_start_read(st->rrddim_root_index, rd) {
65 + if(rd->id == vbd->dim || rd->name == vbd->dim) {
66 + item = dictionary_acquired_item_dup(st->rrddim_root_index, rd_dfe.item);
67 + break;
68 + }
69 + }
70 + dfe_done(rd);
71 +
72 + if (item) {
73 + switch (vbd->dimension_selection) {
74 + case DIM_SELECT_NORMAL:
75 + variable_lookup_add_result_with_score(vbd, (NETDATA_DOUBLE)rd->collector.last_stored_value, st, "last stored value of dimension");
76 + break;
77 + case DIM_SELECT_RAW:
78 + variable_lookup_add_result_with_score(vbd, (NETDATA_DOUBLE)rd->collector.last_collected_value, st, "last collected value of dimension");
79 + break;
80 + case DIM_SELECT_LAST_COLLECTED:
81 + variable_lookup_add_result_with_score(vbd, (NETDATA_DOUBLE)rd->collector.last_collected_time.tv_sec, st, "last collected time of dimension");
82 + break;
83 + }
84 +
85 + dictionary_acquired_item_release(st->rrddim_root_index, item);
86 + found = true;
87 + }
88 + if(found && stop_on_match) goto cleanup;
89 +
90 + // chart variable
91 + {
92 + NETDATA_DOUBLE n;
93 + if(rrdvar_get_custom_chart_variable_value(st, vbd->variable, &n)) {
94 + variable_lookup_add_result_with_score(vbd, n, st, "chart variable");
95 + found = true;
96 + }
97 + }
98 + if(found && stop_on_match) goto cleanup;
99 +
100 +cleanup:
101 + return found;
102 +}
103 +
104 +static int foreach_instance_in_context_cb(RRDSET *st, void *data) {
105 + struct variable_lookup_job *vbd = data;
106 + return variable_lookup_in_chart(vbd, st, false) ? 1 : 0;
107 +}
108 +
109 +static bool variable_lookup_context(struct variable_lookup_job *vbd, const char *chart_or_context, const char *dim_id_or_name) {
110 + struct variable_lookup_job vbd_back = *vbd;
111 +
112 + vbd->dimension = dim_id_or_name;
113 + vbd->dim = string_strdupz(vbd->dimension);
114 + vbd->dimension_length = string_strlen(vbd->dim);
115 + // vbd->dimension_selection = DIM_SELECT_NORMAL;
116 +
117 + bool found = false;
118 +
119 + // lookup chart in host
120 +
121 + RRDSET_ACQUIRED *rsa = rrdset_find_and_acquire(vbd->host, chart_or_context);
122 + if(rsa) {
123 + if(variable_lookup_in_chart(vbd, rrdset_acquired_to_rrdset(rsa), false))
124 + found = true;
125 + rrdset_acquired_release(rsa);
126 + }
127 +
128 + // lookup context in contexts, then foreach chart
129 +
130 + if(rrdcontext_foreach_instance_with_rrdset_in_context(vbd->host, chart_or_context, foreach_instance_in_context_cb, vbd) > 0)
131 + found = true;
132 +
133 + string_freez(vbd->dim);
134 +
135 + vbd->dimension = vbd_back.dimension;
136 + vbd->dim = vbd_back.dim;
137 + vbd->dimension_length = vbd_back.dimension_length;
138 + // vbd->dimension_selection = vbd_back.dimension_selection;
139 +
140 + return found;
141 +}
142 +
143 +bool alert_variable_from_running_alerts(struct variable_lookup_job *vbd) {
144 + bool found = false;
145 + RRDCALC *rc;
146 + foreach_rrdcalc_in_rrdhost_read(vbd->host, rc) {
147 + if(rc->config.name == vbd->variable) {
148 + variable_lookup_add_result_with_score(vbd, (NETDATA_DOUBLE)rc->value, rc->rrdset, "alarm value");
149 + found = true;
150 + }
151 + }
152 + foreach_rrdcalc_in_rrdhost_done(rc);
153 + return found;
154 +}
155 +
156 +bool alert_variable_lookup_internal(STRING *variable, void *data, NETDATA_DOUBLE *result, BUFFER *wb) {
157 + static STRING *this_string = NULL,
158 + *now_string = NULL,
159 + *after_string = NULL,
160 + *before_string = NULL,
161 + *status_string = NULL,
162 + *removed_string = NULL,
163 + *uninitialized_string = NULL,
164 + *undefined_string = NULL,
165 + *clear_string = NULL,
166 + *warning_string = NULL,
167 + *critical_string = NULL,
168 + *last_collected_t_string = NULL,
169 + *green_string = NULL,
170 + *red_string = NULL,
171 + *update_every_string = NULL;
172 +
173 +
174 + struct variable_lookup_job vbd = { 0 };
175 +
176 +// const char *v_name = string2str(variable);
177 +// bool trace_this = false;
178 +// if(strcmp(v_name, "btrfs_allocated") == 0)
179 +// trace_this = true;
180 +
181 + bool found = false;
182 +
183 + const char *source = NULL;
184 + RRDSET *source_st = NULL;
185 +
186 + RRDCALC *rc = data;
187 + RRDSET *st = rc->rrdset;
188 +
189 + if(!st)
190 + return false;
191 +
192 + if(unlikely(!last_collected_t_string)) {
193 + this_string = string_strdupz("this");
194 + now_string = string_strdupz("now");
195 + after_string = string_strdupz("after");
196 + before_string = string_strdupz("before");
197 + status_string = string_strdupz("status");
198 + removed_string = string_strdupz("REMOVED");
199 + undefined_string = string_strdupz("UNDEFINED");
200 + uninitialized_string = string_strdupz("UNINITIALIZED");
201 + clear_string = string_strdupz("CLEAR");
202 + warning_string = string_strdupz("WARNING");
203 + critical_string = string_strdupz("CRITICAL");
204 + last_collected_t_string = string_strdupz("last_collected_t");
205 + green_string = string_strdupz("green");
206 + red_string = string_strdupz("red");
207 + update_every_string = string_strdupz("update_every");
208 + }
209 +
210 + if(unlikely(variable == this_string)) {
211 + *result = (NETDATA_DOUBLE)rc->value;
212 + source = "current alert value";
213 + source_st = st;
214 + found = true;
215 + goto log;
216 + }
217 +
218 + if(unlikely(variable == after_string)) {
219 + *result = (NETDATA_DOUBLE)rc->db_after;
220 + source = "current alert query start time";
221 + source_st = st;
222 + found = true;
223 + goto log;
224 + }
225 +
226 + if(unlikely(variable == before_string)) {
227 + *result = (NETDATA_DOUBLE)rc->db_before;
228 + source = "current alert query end time";
229 + source_st = st;
230 + found = true;
231 + goto log;
232 + }
233 +
234 + if(unlikely(variable == now_string)) {
235 + *result = (NETDATA_DOUBLE)now_realtime_sec();
236 + source = "current wall-time clock timestamp";
237 + source_st = st;
238 + found = true;
239 + goto log;
240 + }
241 +
242 + if(unlikely(variable == status_string)) {
243 + *result = (NETDATA_DOUBLE)rc->status;
244 + source = "current alert status";
245 + source_st = st;
246 + found = true;
247 + goto log;
248 + }
249 +
250 + if(unlikely(variable == removed_string)) {
251 + *result = (NETDATA_DOUBLE)RRDCALC_STATUS_REMOVED;
252 + source = "removed status constant";
253 + source_st = st;
254 + found = true;
255 + goto log;
256 + }
257 +
258 + if(unlikely(variable == uninitialized_string)) {
259 + *result = (NETDATA_DOUBLE)RRDCALC_STATUS_UNINITIALIZED;
260 + source = "uninitialized status constant";
261 + source_st = st;
262 + found = true;
263 + goto log;
264 + }
265 +
266 + if(unlikely(variable == undefined_string)) {
267 + *result = (NETDATA_DOUBLE)RRDCALC_STATUS_UNDEFINED;
268 + source = "undefined status constant";
269 + source_st = st;
270 + found = true;
271 + goto log;
272 + }
273 +
274 + if(unlikely(variable == clear_string)) {
275 + *result = (NETDATA_DOUBLE)RRDCALC_STATUS_CLEAR;
276 + source = "clear status constant";
277 + source_st = st;
278 + found = true;
279 + goto log;
280 + }
281 +
282 + if(unlikely(variable == warning_string)) {
283 + *result = (NETDATA_DOUBLE)RRDCALC_STATUS_WARNING;
284 + source = "warning status constant";
285 + source_st = st;
286 + found = true;
287 + goto log;
288 + }
289 +
290 + if(unlikely(variable == critical_string)) {
291 + *result = (NETDATA_DOUBLE)RRDCALC_STATUS_CRITICAL;
292 + source = "critical status constant";
293 + source_st = st;
294 + found = true;
295 + goto log;
296 + }
297 +
298 + if(unlikely(variable == last_collected_t_string)) {
299 + *result = (NETDATA_DOUBLE)st->last_collected_time.tv_sec;
300 + source = "current instance last_collected_t";
301 + source_st = st;
302 + found = true;
303 + goto log;
304 + }
305 +
306 + if(unlikely(variable == update_every_string)) {
307 + *result = (NETDATA_DOUBLE)st->update_every;
308 + source = "current instance update_every";
309 + source_st = st;
310 + found = true;
311 + goto log;
312 + }
313 +
314 + if(unlikely(variable == green_string)) {
315 + *result = (NETDATA_DOUBLE)rc->config.green;
316 + source = "current alert green threshold";
317 + source_st = st;
318 + found = true;
319 + goto log;
320 + }
321 +
322 + if(unlikely(variable == red_string)) {
323 + *result = (NETDATA_DOUBLE)rc->config.red;
324 + source = "current alert red threshold";
325 + source_st = st;
326 + found = true;
327 + goto log;
328 + }
329 +
330 + // find the dimension id/name
331 +
332 + vbd = (struct variable_lookup_job){
333 + .rc = rc,
334 + .host = st->rrdhost,
335 + .variable = variable,
336 + .dimension = string2str(variable),
337 + .dimension_length = string_strlen(variable),
338 + .dimension_selection = DIM_SELECT_NORMAL,
339 + .dim = string_dup(variable),
340 + .result = { 0 },
341 + };
342 + if (strendswith_lengths(vbd.dimension, vbd.dimension_length, "_raw", 4)) {
343 + vbd.dimension_length -= 4;
344 + vbd.dimension_selection = DIM_SELECT_RAW;
345 + vbd.dim = string_strndupz(vbd.dimension, vbd.dimension_length);
346 + } else if (strendswith_lengths(vbd.dimension, vbd.dimension_length, "_last_collected_t", 17)) {
347 + vbd.dimension_length -= 17;
348 + vbd.dimension_selection = DIM_SELECT_LAST_COLLECTED;
349 + vbd.dim = string_strndupz(vbd.dimension, vbd.dimension_length);
350 + }
351 +
352 + if(variable_lookup_in_chart(&vbd, st, true)) {
353 + found = true;
354 + goto find_best_scored;
355 + }
356 +
357 + // host variables
358 + {
359 + NETDATA_DOUBLE n;
360 + found = rrdvar_get_custom_host_variable_value(vbd.host, vbd.variable, &n);
361 + if(found) {
362 + variable_lookup_add_result_with_score(&vbd, n, st, "host variable");
363 + goto find_best_scored;
364 + }
365 + }
366 +
367 + // alert names
368 + if(alert_variable_from_running_alerts(&vbd)) {
369 + found = true;
370 + goto find_best_scored;
371 + }
372 +
373 + // find the components of the variable
374 + {
375 + char id[string_strlen(vbd.dim) + 1];
376 + memcpy(id, string2str(vbd.dim), string_strlen(vbd.dim));
377 + id[string_strlen(vbd.dim)] = '\0';
378 +
379 + char *dot = strrchr(id, '.');
380 + while(dot) {
381 + *dot = '\0';
382 +
383 + if(strchr(id, '.') == NULL) break;
384 +
385 + if(variable_lookup_context(&vbd, id, dot + 1))
386 + found = true;
387 +
388 + char *dot2 = strrchr(id, '.');
389 + *dot = '.';
390 + dot = dot2;
391 + }
392 + }
393 +
394 +find_best_scored:
395 + if(found && vbd.result.array) {
396 + struct variable_lookup_score *best = &vbd.result.array[0];
397 + for (size_t i = 1; i < vbd.result.used; i++)
398 + if (vbd.result.array[i].score > best->score)
399 + best = &vbd.result.array[i];
400 +
401 + source = best->source;
402 + source_st = best->st;
403 + *result = best->value;
404 + freez(vbd.result.array);
405 + }
406 + else {
407 + found = false;
408 + *result = NAN;
409 + }
410 +
411 +log:
412 +#ifdef NETDATA_LOG_HEALTH_VARIABLES_LOOKUP
413 + if(found) {
414 + nd_log(NDLS_DAEMON, NDLP_INFO,
415 + "HEALTH_VARIABLE_LOOKUP: variable '%s' of alert '%s' of chart '%s', context '%s', host '%s' "
416 + "resolved with %s of chart '%s' and context '%s'",
417 + string2str(variable),
418 + string2str(rc->config.name),
419 + string2str(rc->rrdset->id),
420 + string2str(rc->rrdset->context),
421 + string2str(rc->rrdset->rrdhost->hostname),
422 + source,
423 + string2str(source_st->id),
424 + string2str(source_st->context)
425 + );
426 + }
427 + else {
428 + nd_log(NDLS_DAEMON, NDLP_INFO,
429 + "HEALTH_VARIABLE_LOOKUP: variable '%s' of alert '%s' of chart '%s', context '%s', host '%s' "
430 + "could not be resolved",
431 + string2str(variable),
432 + string2str(rc->config.name),
433 + string2str(rc->rrdset->id),
434 + string2str(rc->rrdset->context),
435 + string2str(rc->rrdset->rrdhost->hostname)
436 + );
437 + }
438 +#endif
439 +
440 + if(unlikely(wb)) {
441 + buffer_json_member_add_string(wb, "variable", string2str(variable));
442 + buffer_json_member_add_string(wb, "instance", string2str(st->id));
443 + buffer_json_member_add_string(wb, "context", string2str(st->context));
444 + buffer_json_member_add_boolean(wb, "found", found);
445 +
446 + if (found) {
447 + buffer_json_member_add_double(wb, "value", *result);
448 + buffer_json_member_add_object(wb, "source");
449 + {
450 + buffer_json_member_add_string(wb, "description", source);
451 + buffer_json_member_add_string(wb, "instance", string2str(source_st->id));
452 + buffer_json_member_add_string(wb, "context", string2str(source_st->context));
453 + buffer_json_member_add_uint64(wb, "candidates", vbd.result.used ? vbd.result.used : 1);
454 + }
455 + buffer_json_object_close(wb); // source
456 + }
457 + }
458 +
459 + string_freez(vbd.dim);
460 +
461 + return found;
462 +}
463 +
464 +bool alert_variable_lookup(STRING *variable, void *data, NETDATA_DOUBLE *result) {
465 + return alert_variable_lookup_internal(variable, data, result, NULL);
466 +}
467 +
468 +int alert_variable_lookup_trace(RRDHOST *host __maybe_unused, RRDSET *st, const char *variable, BUFFER *wb) {
469 + int code = HTTP_RESP_INTERNAL_SERVER_ERROR;
470 +
471 + buffer_flush(wb);
472 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
473 +
474 + STRING *v = string_strdupz(variable);
475 + RRDCALC rc = {
476 + .rrdset = st,
477 + };
478 +
479 + NETDATA_DOUBLE n;
480 + alert_variable_lookup_internal(v, &rc, &n, wb);
481 +
482 + string_freez(v);
483 +
484 + buffer_json_finalize(wb);
485 + return code;
486 +}
health/rrdcalc.c new
+539
@@ -0,0 +1,539 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "database/rrd.h"
4 +#include "health_internals.h"
5 +
6 +// ----------------------------------------------------------------------------
7 +// RRDCALC helpers
8 +
9 +void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags) {
10 + buffer_json_member_add_array(wb, key);
11 +
12 + if(flags & RRDCALC_FLAG_DB_ERROR)
13 + buffer_json_add_array_item_string(wb, "DB_ERROR");
14 + if(flags & RRDCALC_FLAG_DB_NAN)
15 + buffer_json_add_array_item_string(wb, "DB_NAN");
16 + if(flags & RRDCALC_FLAG_CALC_ERROR)
17 + buffer_json_add_array_item_string(wb, "CALC_ERROR");
18 + if(flags & RRDCALC_FLAG_WARN_ERROR)
19 + buffer_json_add_array_item_string(wb, "WARN_ERROR");
20 + if(flags & RRDCALC_FLAG_CRIT_ERROR)
21 + buffer_json_add_array_item_string(wb, "CRIT_ERROR");
22 + if(flags & RRDCALC_FLAG_RUNNABLE)
23 + buffer_json_add_array_item_string(wb, "RUNNABLE");
24 + if(flags & RRDCALC_FLAG_DISABLED)
25 + buffer_json_add_array_item_string(wb, "DISABLED");
26 + if(flags & RRDCALC_FLAG_SILENCED)
27 + buffer_json_add_array_item_string(wb, "SILENCED");
28 + if(flags & RRDCALC_FLAG_RUN_ONCE)
29 + buffer_json_add_array_item_string(wb, "RUN_ONCE");
30 +
31 + buffer_json_array_close(wb);
32 +}
33 +
34 +inline const char *rrdcalc_status2string(RRDCALC_STATUS status) {
35 + switch(status) {
36 + case RRDCALC_STATUS_REMOVED:
37 + return "REMOVED";
38 +
39 + case RRDCALC_STATUS_UNDEFINED:
40 + return "UNDEFINED";
41 +
42 + case RRDCALC_STATUS_UNINITIALIZED:
43 + return "UNINITIALIZED";
44 +
45 + case RRDCALC_STATUS_CLEAR:
46 + return "CLEAR";
47 +
48 + case RRDCALC_STATUS_RAISED:
49 + return "RAISED";
50 +
51 + case RRDCALC_STATUS_WARNING:
52 + return "WARNING";
53 +
54 + case RRDCALC_STATUS_CRITICAL:
55 + return "CRITICAL";
56 +
57 + default:
58 + netdata_log_error("Unknown alarm status %d", status);
59 + return "UNKNOWN";
60 + }
61 +}
62 +
63 +uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id) {
64 + rw_spinlock_read_lock(&host->health_log.spinlock);
65 +
66 + // re-use old IDs, by looking them up in the alarm log
67 + ALARM_ENTRY *ae = NULL;
68 + for(ae = host->health_log.alarms; ae ;ae = ae->next) {
69 + if(unlikely(name == ae->name && chart == ae->chart && !uuid_memcmp(&ae->config_hash_id, config_hash_id))) {
70 + if(next_event_id) *next_event_id = ae->alarm_event_id + 1;
71 + break;
72 + }
73 + }
74 +
75 + uint32_t alarm_id;
76 +
77 + if(ae)
78 + alarm_id = ae->alarm_id;
79 + else {
80 + alarm_id = sql_get_alarm_id(host, chart, name, next_event_id);
81 + if (!alarm_id) {
82 + if (unlikely(!host->health_log.next_alarm_id))
83 + host->health_log.next_alarm_id = (uint32_t)now_realtime_sec();
84 + alarm_id = host->health_log.next_alarm_id++;
85 + }
86 + }
87 +
88 + rw_spinlock_read_unlock(&host->health_log.spinlock);
89 + return alarm_id;
90 +}
91 +
92 +// ----------------------------------------------------------------------------
93 +// RRDCALC replacing info/summary text variables with RRDSET labels
94 +
95 +static STRING *rrdcalc_replace_variables_with_rrdset_labels(const char *line, RRDCALC *rc) {
96 + if (!line || !*line)
97 + return NULL;
98 +
99 + size_t pos = 0;
100 + char *temp = strdupz(line);
101 + char var[RRDCALC_VAR_MAX];
102 + char *m, *lbl_value = NULL;
103 +
104 + while ((m = strchr(temp + pos, '$')) && *(m+1) == '{') {
105 + int i = 0;
106 + char *e = m;
107 + while (*e) {
108 + var[i++] = *e;
109 +
110 + if (*e == '}' || i == RRDCALC_VAR_MAX - 1)
111 + break;
112 +
113 + e++;
114 + }
115 +
116 + var[i] = '\0';
117 + pos = m - temp + 1;
118 +
119 + if (!strcmp(var, RRDCALC_VAR_FAMILY)) {
120 + char *buf = find_and_replace(temp, var, (rc->rrdset && rc->rrdset->family) ? rrdset_family(rc->rrdset) : "", m);
121 + freez(temp);
122 + temp = buf;
123 + }
124 + else if (!strncmp(var, RRDCALC_VAR_LABEL, RRDCALC_VAR_LABEL_LEN)) {
125 + char label_val[RRDCALC_VAR_MAX + RRDCALC_VAR_LABEL_LEN + 1] = { 0 };
126 + strcpy(label_val, var+RRDCALC_VAR_LABEL_LEN);
127 + label_val[i - RRDCALC_VAR_LABEL_LEN - 1] = '\0';
128 +
129 + if(likely(rc->rrdset && rc->rrdset->rrdlabels)) {
130 + lbl_value = NULL;
131 + rrdlabels_get_value_strdup_or_null(rc->rrdset->rrdlabels, &lbl_value, label_val);
132 + if (lbl_value) {
133 + char *buf = find_and_replace(temp, var, lbl_value, m);
134 + freez(temp);
135 + temp = buf;
136 + freez(lbl_value);
137 + }
138 + }
139 + }
140 + }
141 +
142 + STRING *ret = string_strdupz(temp);
143 + freez(temp);
144 +
145 + return ret;
146 +}
147 +
148 +void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc) {
149 + if(rc->rrdset && rc->rrdset->rrdlabels) {
150 + size_t labels_version = rrdlabels_version(rc->rrdset->rrdlabels);
151 + if (rc->labels_version != labels_version) {
152 + STRING *old;
153 +
154 + old = rc->info;
155 + rc->info = rrdcalc_replace_variables_with_rrdset_labels(string2str(rc->config.info), rc);
156 + string_freez(old);
157 +
158 + old = rc->summary;
159 + rc->summary = rrdcalc_replace_variables_with_rrdset_labels(string2str(rc->config.summary), rc);
160 + string_freez(old);
161 +
162 + rc->labels_version = labels_version;
163 + }
164 + }
165 +
166 + if(!rc->summary)
167 + rc->summary = string_dup(rc->config.summary);
168 +
169 + if(!rc->info)
170 + rc->info = string_dup(rc->config.info);
171 +}
172 +
173 +// ----------------------------------------------------------------------------
174 +// RRDCALC index management for RRDSET
175 +
176 +// the dictionary requires a unique key for every item
177 +// we use {chart id}.{alert name} for both the RRDHOST and RRDSET alert indexes.
178 +
179 +#define RRDCALC_MAX_KEY_SIZE 1024
180 +static size_t rrdcalc_key(char *dst, size_t dst_len, const char *chart, const char *alert) {
181 + return snprintfz(dst, dst_len, "%s,on[%s]", alert, chart);
182 +}
183 +
184 +const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name) {
185 + char key[RRDCALC_MAX_KEY_SIZE + 1];
186 + size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_id(st), alert_name);
187 +
188 + const RRDCALC_ACQUIRED *rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)key_len);
189 +
190 + if(!rca) {
191 + key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE, rrdset_name(st), alert_name);
192 + rca = (const RRDCALC_ACQUIRED *)dictionary_get_and_acquire_item_advanced(st->rrdhost->rrdcalc_root_index, key, (ssize_t)key_len);
193 + }
194 +
195 + return rca;
196 +}
197 +
198 +void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca) {
199 + if(!rca) return;
200 +
201 + dictionary_acquired_item_release(st->rrdhost->rrdcalc_root_index, (const DICTIONARY_ITEM *)rca);
202 +}
203 +
204 +RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca) {
205 + if(rca)
206 + return dictionary_acquired_item_value((const DICTIONARY_ITEM *)rca);
207 +
208 + return NULL;
209 +}
210 +
211 +// ----------------------------------------------------------------------------
212 +// RRDCALC managing the linking with RRDSET
213 +
214 +static void rrdcalc_link_to_rrdset(RRDCALC *rc) {
215 + RRDSET *st = rc->rrdset;
216 + RRDHOST *host = st->rrdhost;
217 +
218 + rw_spinlock_write_lock(&st->alerts.spinlock);
219 + DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(st->alerts.base, rc, prev, next);
220 + rw_spinlock_write_unlock(&st->alerts.spinlock);
221 +
222 + char buf[RRDVAR_MAX_LENGTH + 1];
223 + snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_name(st), rrdcalc_name(rc));
224 + STRING *rrdset_name_rrdcalc_name = string_strdupz(buf);
225 + snprintfz(buf, RRDVAR_MAX_LENGTH, "%s.%s", rrdset_id(st), rrdcalc_name(rc));
226 + STRING *rrdset_id_rrdcalc_name = string_strdupz(buf);
227 +
228 + string_freez(rrdset_id_rrdcalc_name);
229 + string_freez(rrdset_name_rrdcalc_name);
230 +
231 + time_t now = now_realtime_sec();
232 + ALARM_ENTRY *ae = health_create_alarm_entry(
233 + host,
234 + rc,
235 + now,
236 + now - rc->last_status_change,
237 + rc->old_value,
238 + rc->value,
239 + RRDCALC_STATUS_REMOVED,
240 + rc->status,
241 + 0,
242 + rrdcalc_isrepeating(rc)?HEALTH_ENTRY_FLAG_IS_REPEATING:0);
243 +
244 + health_alarm_log_add_entry(host, ae);
245 + rrdset_flag_set(st, RRDSET_FLAG_HAS_RRDCALC_LINKED);
246 +
247 +}
248 +
249 +static void rrdcalc_unlink_from_rrdset(RRDCALC *rc, bool having_ll_wrlock) {
250 + RRDSET *st = rc->rrdset;
251 +
252 + if(!st) {
253 + netdata_log_error(
254 + "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET",
255 + rrdcalc_chart_name(rc), rrdcalc_name(rc));
256 + return;
257 + }
258 +
259 + RRDHOST *host = st->rrdhost;
260 +
261 + time_t now = now_realtime_sec();
262 +
263 + if (likely(rc->status != RRDCALC_STATUS_REMOVED)) {
264 + ALARM_ENTRY *ae = health_create_alarm_entry(
265 + host,
266 + rc,
267 + now,
268 + now - rc->last_status_change,
269 + rc->old_value,
270 + rc->value,
271 + rc->status,
272 + RRDCALC_STATUS_REMOVED,
273 + 0,
274 + 0);
275 +
276 + health_alarm_log_add_entry(host, ae);
277 + }
278 +
279 + // unlink it
280 +
281 + if(!having_ll_wrlock)
282 + rw_spinlock_write_lock(&st->alerts.spinlock);
283 +
284 + DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(st->alerts.base, rc, prev, next);
285 +
286 + if(!having_ll_wrlock)
287 + rw_spinlock_write_unlock(&st->alerts.spinlock);
288 +
289 + rc->rrdset = NULL;
290 +}
291 +
292 +static inline bool rrdcalc_check_if_it_matches_rrdset(RRDCALC *rc, RRDSET *st) {
293 + if ( (rc->chart != st->id)
294 + && (rc->chart != st->name))
295 + return false;
296 +
297 + if (rc->match.module_pattern && !simple_pattern_matches_string(rc->match.module_pattern, st->module_name))
298 + return false;
299 +
300 + if (rc->match.plugin_pattern && !simple_pattern_matches_string(rc->match.plugin_pattern, st->module_name))
301 + return false;
302 +
303 + if (st->rrdlabels && rc->match.chart_labels_pattern && !rrdlabels_match_simple_pattern_parsed(
304 + st->rrdlabels, rc->match.chart_labels_pattern, '=', NULL))
305 + return false;
306 +
307 + return true;
308 +}
309 +
310 +// ----------------------------------------------------------------------------
311 +// RRDCALC rrdhost index management - constructor
312 +
313 +struct rrdcalc_constructor {
314 + RRDSET *rrdset;
315 + RRD_ALERT_PROTOTYPE *ap;
316 +
317 + enum {
318 + RRDCALC_REACT_NONE,
319 + RRDCALC_REACT_NEW,
320 + } react_action;
321 +};
322 +
323 +static void rrdcalc_rrdhost_insert_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) {
324 + RRDCALC *rc = rrdcalc;
325 + struct rrdcalc_constructor *ctr = constructor_data;
326 + RRDSET *st = ctr->rrdset;
327 + RRDHOST *host = st->rrdhost;
328 + RRD_ALERT_PROTOTYPE *ap = ctr->ap;
329 +
330 + rc->key = string_strdupz(dictionary_acquired_item_name(item));
331 + rc->rrdset = st;
332 + rc->chart = string_dup(st->id);
333 +
334 + health_prototype_copy_config(&rc->config, &ap->config);
335 + health_prototype_copy_match_without_patterns(&rc->match, &ap->match);
336 +
337 + rc->next_event_id = 1;
338 + rc->value = NAN;
339 + rc->old_value = NAN;
340 + rc->last_repeat = 0;
341 + rc->times_repeat = 0;
342 + rc->last_status_change_value = rc->value;
343 + rc->last_status_change = now_realtime_sec();
344 +
345 + if(!rc->config.units)
346 + rc->config.units = string_dup(st->units);
347 +
348 + if(rc->config.update_every < rc->rrdset->update_every) {
349 + netdata_log_info(
350 + "HEALTH: alert '%s.%s' has update every %d, less than chart update every %d. "
351 + "Setting alarm update frequency to %d.",
352 + string2str(st->id), string2str(rc->config.name),
353 + rc->config.update_every, rc->rrdset->update_every, rc->rrdset->update_every);
354 +
355 + rc->config.update_every = st->update_every;
356 + }
357 +
358 + rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->config.name, &rc->next_event_id, &rc->config.hash_id);
359 +
360 + if(!isnan(rc->config.green) && isnan(st->green))
361 + st->green = rc->config.green;
362 +
363 + if(!isnan(rc->config.red) && isnan(st->red))
364 + st->red = rc->config.red;
365 +
366 + expression_set_variable_lookup_callback(rc->config.calculation, alert_variable_lookup, rc);
367 + expression_set_variable_lookup_callback(rc->config.warning, alert_variable_lookup, rc);
368 + expression_set_variable_lookup_callback(rc->config.critical, alert_variable_lookup, rc);
369 +
370 + rrdcalc_update_info_using_rrdset_labels(rc);
371 +
372 + ctr->react_action = RRDCALC_REACT_NEW;
373 +}
374 +
375 +static bool rrdcalc_rrdhost_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc __maybe_unused, void *rrdcalc_new __maybe_unused, void *constructor_data) {
376 + struct rrdcalc_constructor *ctr = constructor_data;
377 + ctr->react_action = RRDCALC_REACT_NONE;
378 + return false;
379 +}
380 +
381 +static void rrdcalc_rrdhost_react_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *constructor_data) {
382 + RRDCALC *rc = rrdcalc;
383 + struct rrdcalc_constructor *ctr = constructor_data;
384 +
385 + if(ctr->react_action == RRDCALC_REACT_NEW)
386 + rrdcalc_link_to_rrdset(rc);
387 +}
388 +
389 +// ----------------------------------------------------------------------------
390 +// RRDCALC rrdhost index management - destructor
391 +
392 +static void rrdcalc_free_internals(RRDCALC *rc) {
393 + if(unlikely(!rc)) return;
394 +
395 + rrd_alert_match_cleanup(&rc->match);
396 + rrd_alert_config_cleanup(&rc->config);
397 +
398 + string_freez(rc->key);
399 + string_freez(rc->chart);
400 +
401 + string_freez(rc->info);
402 + string_freez(rc->summary);
403 +}
404 +
405 +static void rrdcalc_rrdhost_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *rrdcalc, void *rrdhost __maybe_unused) {
406 + RRDCALC *rc = rrdcalc;
407 + //RRDHOST *host = rrdhost;
408 +
409 + if(unlikely(rc->rrdset))
410 + rrdcalc_unlink_from_rrdset(rc, false);
411 +
412 + // any destruction actions that require other locks
413 + // have to be placed in rrdcalc_del(), because the object is actually locked for deletion
414 +
415 + rrdcalc_free_internals(rc);
416 +}
417 +
418 +// ----------------------------------------------------------------------------
419 +// RRDCALC rrdhost index management - index API
420 +
421 +void rrdcalc_rrdhost_index_init(RRDHOST *host) {
422 + if(!host->rrdcalc_root_index) {
423 + host->rrdcalc_root_index = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
424 + &dictionary_stats_category_rrdhealth, sizeof(RRDCALC));
425 +
426 + dictionary_register_insert_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_insert_callback, NULL);
427 + dictionary_register_conflict_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_conflict_callback, NULL);
428 + dictionary_register_react_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_react_callback, NULL);
429 + dictionary_register_delete_callback(host->rrdcalc_root_index, rrdcalc_rrdhost_delete_callback, host);
430 + }
431 +}
432 +
433 +void rrdcalc_rrdhost_index_destroy(RRDHOST *host) {
434 + dictionary_destroy(host->rrdcalc_root_index);
435 + host->rrdcalc_root_index = NULL;
436 +}
437 +
438 +bool rrdcalc_add_from_prototype(RRDHOST *host, RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
439 + char key[RRDCALC_MAX_KEY_SIZE + 1];
440 + size_t key_len = rrdcalc_key(key, RRDCALC_MAX_KEY_SIZE,
441 + string2str(st->id), string2str(ap->config.name));
442 +
443 + struct rrdcalc_constructor tmp = {
444 + .ap = ap,
445 + .rrdset = st,
446 + .react_action = RRDCALC_REACT_NONE,
447 + };
448 +
449 + bool ret = true;
450 +
451 + dictionary_set_advanced(host->rrdcalc_root_index, key, (ssize_t)key_len,
452 + NULL, sizeof(RRDCALC), &tmp);
453 +
454 + if(tmp.react_action != RRDCALC_REACT_NEW)
455 + ret = false;
456 +
457 + return ret;
458 +}
459 +
460 +void rrdcalc_unlink_and_delete(RRDHOST *host, RRDCALC *rc, bool having_ll_wrlock) {
461 + if(rc->rrdset)
462 + rrdcalc_unlink_from_rrdset(rc, having_ll_wrlock);
463 +
464 + dictionary_del_advanced(host->rrdcalc_root_index, string2str(rc->key), (ssize_t)string_strlen(rc->key));
465 +}
466 +
467 +
468 +// ----------------------------------------------------------------------------
469 +// RRDCALC cleanup API functions
470 +
471 +void rrdcalc_unlink_and_delete_all_rrdset_alerts(RRDSET *st) {
472 + RRDCALC *rc, *last = NULL;
473 + rw_spinlock_write_lock(&st->alerts.spinlock);
474 + while((rc = st->alerts.base)) {
475 + if(last == rc) {
476 + netdata_log_error("RRDCALC: malformed list of alerts linked to chart - cannot cleanup - giving up.");
477 + break;
478 + }
479 + last = rc;
480 +
481 + rrdcalc_unlink_and_delete(st->rrdhost, rc, true);
482 + }
483 + rw_spinlock_write_unlock(&st->alerts.spinlock);
484 +}
485 +
486 +void rrdcalc_delete_all(RRDHOST *host) {
487 + dictionary_flush(host->rrdcalc_root_index);
488 +}
489 +
490 +void rrd_alert_match_cleanup(struct rrd_alert_match *am) {
491 + if(am->is_template)
492 + string_freez(am->on.context);
493 + else
494 + string_freez(am->on.chart);
495 +
496 + string_freez(am->os);
497 + simple_pattern_free(am->os_pattern);
498 +
499 + string_freez(am->host);
500 + simple_pattern_free(am->host_pattern);
501 +
502 + string_freez(am->plugin);
503 + simple_pattern_free(am->plugin_pattern);
504 +
505 + string_freez(am->module);
506 + simple_pattern_free(am->module_pattern);
507 +
508 + string_freez(am->charts);
509 + simple_pattern_free(am->charts_pattern);
510 +
511 + string_freez(am->host_labels);
512 + simple_pattern_free(am->host_labels_pattern);
513 +
514 + string_freez(am->chart_labels);
515 + simple_pattern_free(am->chart_labels_pattern);
516 +}
517 +
518 +void rrd_alert_config_cleanup(struct rrd_alert_config *ac) {
519 + string_freez(ac->name);
520 +
521 + string_freez(ac->exec);
522 + string_freez(ac->recipient);
523 +
524 + string_freez(ac->classification);
525 + string_freez(ac->component);
526 + string_freez(ac->type);
527 +
528 + string_freez(ac->source);
529 + string_freez(ac->units);
530 + string_freez(ac->summary);
531 + string_freez(ac->info);
532 + string_freez(ac->lookup);
533 +
534 + string_freez(ac->dimensions);
535 +
536 + expression_free(ac->calculation);
537 + expression_free(ac->warning);
538 + expression_free(ac->critical);
539 +}
health/rrdcalc.h new
+146
@@ -0,0 +1,146 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "../database/rrd.h"
4 +#include "../web/api/queries/rrdr.h"
5 +#include "health_prototypes.h"
6 +
7 +#ifndef NETDATA_RRDCALC_H
8 +#define NETDATA_RRDCALC_H 1
9 +
10 +// calculated variables (defined in health configuration)
11 +// These aggregate time-series data at fixed intervals
12 +// (defined in their update_every member below)
13 +// They increase the overhead of netdata.
14 +//
15 +// These calculations are stored under RRDHOST.
16 +// Then are also linked to RRDSET (of course only when a
17 +// matching chart is found).
18 +
19 +typedef enum rrdcalc_status {
20 + RRDCALC_STATUS_REMOVED = -2,
21 + RRDCALC_STATUS_UNDEFINED = -1,
22 + RRDCALC_STATUS_UNINITIALIZED = 0,
23 + RRDCALC_STATUS_CLEAR = 1,
24 + RRDCALC_STATUS_RAISED = 2, // DO NOT CHANGE THESE NUMBERS
25 + RRDCALC_STATUS_WARNING = 3, // DO NOT CHANGE THESE NUMBERS
26 + RRDCALC_STATUS_CRITICAL = 4, // DO NOT CHANGE THESE NUMBERS
27 +} RRDCALC_STATUS;
28 +
29 +typedef enum {
30 + RRDCALC_FLAG_DB_ERROR = (1 << 0),
31 + RRDCALC_FLAG_DB_NAN = (1 << 1),
32 + // RRDCALC_FLAG_DB_STALE = (1 << 2),
33 + RRDCALC_FLAG_CALC_ERROR = (1 << 3),
34 + RRDCALC_FLAG_WARN_ERROR = (1 << 4),
35 + RRDCALC_FLAG_CRIT_ERROR = (1 << 5),
36 + RRDCALC_FLAG_RUNNABLE = (1 << 6),
37 + RRDCALC_FLAG_DISABLED = (1 << 7),
38 + RRDCALC_FLAG_SILENCED = (1 << 8),
39 + RRDCALC_FLAG_RUN_ONCE = (1 << 9),
40 +} RRDCALC_FLAGS;
41 +void rrdcalc_flags_to_json_array(BUFFER *wb, const char *key, RRDCALC_FLAGS flags);
42 +
43 +#define RRDCALC_ALL_OPTIONS_EXCLUDING_THE_RRDR_ONES (RRDCALC_OPTION_NO_CLEAR_NOTIFICATION)
44 +
45 +struct rrdcalc {
46 + uint32_t id; // the unique id of this alarm
47 + uint32_t next_event_id; // the next event id that will be used for this alarm
48 +
49 + STRING *key; // the unique key in the host's rrdcalc_root_index
50 + STRING *chart; // the chart id this should be linked to
51 +
52 + struct rrd_alert_match match;
53 + struct rrd_alert_config config;
54 +
55 + // ------------------------------------------------------------------------
56 + // runtime information
57 +
58 + STRING *summary; // the original summary field before any variable replacement
59 + STRING *info; // the original info field before any variable replacement
60 +
61 + RRDCALC_STATUS old_status; // the old status of the alarm
62 + RRDCALC_STATUS status; // the current status of the alarm
63 +
64 + NETDATA_DOUBLE value; // the current value of the alarm
65 + NETDATA_DOUBLE old_value; // the previous value of the alarm
66 + NETDATA_DOUBLE last_status_change_value; // the value at the last status change
67 +
68 + RRDCALC_FLAGS run_flags; // check RRDCALC_FLAG_*
69 +
70 + time_t last_updated; // the last update timestamp of the alarm
71 + time_t next_update; // the next update timestamp of the alarm
72 + time_t last_status_change; // the timestamp of the last time this alarm changed status
73 + time_t last_repeat; // the last time the alarm got repeated
74 + uint32_t times_repeat; // number of times the alarm got repeated
75 +
76 + time_t db_after; // the first timestamp evaluated by the db lookup
77 + time_t db_before; // the last timestamp evaluated by the db lookup
78 +
79 + time_t delay_up_to_timestamp; // the timestamp up to which we should delay notifications
80 + int delay_up_current; // the current up notification delay duration
81 + int delay_down_current; // the current down notification delay duration
82 + int delay_last; // the last delay we used
83 +
84 + // ------------------------------------------------------------------------
85 + // the chart this alarm it is linked to
86 +
87 + size_t labels_version;
88 + struct rrdset *rrdset;
89 +
90 + struct rrdcalc *next;
91 + struct rrdcalc *prev;
92 +};
93 +
94 +#define rrdcalc_name(rc) string2str((rc)->config.name)
95 +#define rrdcalc_chart_name(rc) string2str((rc)->chart)
96 +#define rrdcalc_exec(rc) string2str((rc)->config.exec)
97 +#define rrdcalc_recipient(rc) string2str((rc)->config.recipient)
98 +#define rrdcalc_classification(rc) string2str((rc)->config.classification)
99 +#define rrdcalc_component(rc) string2str((rc)->config.component)
100 +#define rrdcalc_type(rc) string2str((rc)->config.type)
101 +#define rrdcalc_source(rc) string2str((rc)->config.source)
102 +#define rrdcalc_units(rc) string2str((rc)->config.units)
103 +#define rrdcalc_dimensions(rc) string2str((rc)->config.dimensions)
104 +
105 +#define foreach_rrdcalc_in_rrdhost_read(host, rc) \
106 + dfe_start_read((host)->rrdcalc_root_index, rc) \
107 +
108 +#define foreach_rrdcalc_in_rrdhost_reentrant(host, rc) \
109 + dfe_start_reentrant((host)->rrdcalc_root_index, rc)
110 +
111 +#define foreach_rrdcalc_in_rrdhost_done(rc) \
112 + dfe_done(rc)
113 +
114 +#define RRDCALC_HAS_DB_LOOKUP(rc) ((rc)->config.after)
115 +
116 +void rrdcalc_update_info_using_rrdset_labels(RRDCALC *rc);
117 +
118 +const RRDCALC_ACQUIRED *rrdcalc_from_rrdset_get(RRDSET *st, const char *alert_name);
119 +void rrdcalc_from_rrdset_release(RRDSET *st, const RRDCALC_ACQUIRED *rca);
120 +RRDCALC *rrdcalc_acquired_to_rrdcalc(const RRDCALC_ACQUIRED *rca);
121 +
122 +const char *rrdcalc_status2string(RRDCALC_STATUS status);
123 +
124 +uint32_t rrdcalc_get_unique_id(RRDHOST *host, STRING *chart, STRING *name, uint32_t *next_event_id, uuid_t *config_hash_id);
125 +
126 +static inline int rrdcalc_isrepeating(RRDCALC *rc) {
127 + if (unlikely(rc->config.warn_repeat_every > 0 || rc->config.crit_repeat_every > 0)) {
128 + return 1;
129 + }
130 + return 0;
131 +}
132 +
133 +void rrdcalc_unlink_and_delete_all_rrdset_alerts(RRDSET *st);
134 +void rrdcalc_delete_all(RRDHOST *host);
135 +
136 +void rrdcalc_rrdhost_index_init(RRDHOST *host);
137 +void rrdcalc_rrdhost_index_destroy(RRDHOST *host);
138 +
139 +void rrdcalc_unlink_and_delete(RRDHOST *host, RRDCALC *rc, bool having_ll_wrlock);
140 +
141 +#define RRDCALC_VAR_MAX 100
142 +#define RRDCALC_VAR_FAMILY "${family}"
143 +#define RRDCALC_VAR_LABEL "${label:"
144 +#define RRDCALC_VAR_LABEL_LEN (sizeof(RRDCALC_VAR_LABEL)-1)
145 +
146 +#endif //NETDATA_RRDCALC_H
health/rrdvar.c new
+342
@@ -0,0 +1,342 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#include "database/rrd.h"
4 +
5 +typedef struct rrdvar {
6 + NETDATA_DOUBLE value;
7 +} RRDVAR;
8 +
9 +// ----------------------------------------------------------------------------
10 +// RRDVAR management
11 +
12 +inline int rrdvar_fix_name(char *variable) {
13 + int fixed = 0;
14 + while(*variable) {
15 + if (!isalnum(*variable) && *variable != '.' && *variable != '_') {
16 + *variable++ = '_';
17 + fixed++;
18 + }
19 + else
20 + variable++;
21 + }
22 +
23 + return fixed;
24 +}
25 +
26 +inline STRING *rrdvar_name_to_string(const char *name) {
27 + char *variable = strdupz(name);
28 + rrdvar_fix_name(variable);
29 + STRING *name_string = string_strdupz(variable);
30 + freez(variable);
31 + return name_string;
32 +}
33 +
34 +static bool rrdvar_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
35 + RRDVAR *rv = old_value;
36 + RRDVAR *nrv = new_value;
37 +
38 + rv->value = nrv->value;
39 + return false;
40 +}
41 +
42 +DICTIONARY *rrdvariables_create(void) {
43 + DICTIONARY *dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE,
44 + &dictionary_stats_category_rrdhealth, sizeof(RRDVAR));
45 + dictionary_register_conflict_callback(dict, rrdvar_conflict_callback, NULL);
46 + return dict;
47 +}
48 +
49 +void rrdvariables_destroy(DICTIONARY *dict) {
50 + dictionary_destroy(dict);
51 +}
52 +
53 +static inline const RRDVAR_ACQUIRED *rrdvar_get_and_acquire(DICTIONARY *dict, STRING *name) {
54 + return (const RRDVAR_ACQUIRED *)dictionary_get_and_acquire_item_advanced(dict, string2str(name), (ssize_t)string_strlen(name));
55 +}
56 +
57 +inline const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(DICTIONARY *dict, STRING *name, NETDATA_DOUBLE value) {
58 + if(unlikely(!dict || !name)) return NULL;
59 + RRDVAR tmp = {
60 + .value = value,
61 + };
62 + return (const RRDVAR_ACQUIRED *)dictionary_set_and_acquire_item_advanced(
63 + dict, string2str(name), (ssize_t)string_strlen(name),
64 + &tmp, sizeof(tmp), NULL);
65 +}
66 +
67 +void rrdvar_delete_all(DICTIONARY *dict) {
68 + dictionary_flush(dict);
69 +}
70 +
71 +void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva) {
72 + if(unlikely(!dict || !rva)) return; // when health is not enabled
73 + dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
74 +}
75 +
76 +// ----------------------------------------------------------------------------
77 +// CUSTOM HOST VARIABLES
78 +
79 +inline int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data) {
80 + if(unlikely(!dict)) return 0; // when health is not enabled
81 + return dictionary_walkthrough_read(dict, callback, data);
82 +}
83 +
84 +const RRDVAR_ACQUIRED *rrdvar_host_variable_add_and_acquire(RRDHOST *host, const char *name) {
85 + if(unlikely(!host->rrdvars)) return NULL; // when health is not enabled
86 +
87 + STRING *name_string = rrdvar_name_to_string(name);
88 + const RRDVAR_ACQUIRED *rva = rrdvar_add_and_acquire(host->rrdvars, name_string, NAN);
89 +
90 + string_freez(name_string);
91 + return rva;
92 +}
93 +
94 +void rrdvar_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) {
95 + if(unlikely(!host->rrdvars || !rva)) return; // when health is not enabled
96 +
97 + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
98 + if(rv->value != value) {
99 + rv->value = value;
100 +
101 + // if the host is streaming, send this variable upstream immediately
102 + rrdpush_sender_send_this_host_variable_now(host, rva);
103 + }
104 +}
105 +
106 +// ----------------------------------------------------------------------------
107 +// CUSTOM CHART VARIABLES
108 +
109 +const RRDVAR_ACQUIRED *rrdvar_chart_variable_add_and_acquire(RRDSET *st, const char *name) {
110 + if(unlikely(!st->rrdvars)) return NULL;
111 +
112 + STRING *name_string = rrdvar_name_to_string(name);
113 + const RRDVAR_ACQUIRED *rs = rrdvar_add_and_acquire(st->rrdvars, name_string, NAN);
114 + string_freez(name_string);
115 + return rs;
116 +}
117 +
118 +void rrdvar_chart_variable_set(RRDSET *st, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value) {
119 + if(unlikely(!st->rrdvars || !rva)) return;
120 +
121 + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
122 + if(rv->value != value) {
123 + rv->value = value;
124 + rrdset_flag_set(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES);
125 + }
126 +}
127 +
128 +// ----------------------------------------------------------------------------
129 +// RRDVAR lookup
130 +
131 +NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva) {
132 + if(unlikely(!rva)) return NAN;
133 + RRDVAR *rv = dictionary_acquired_item_value((const DICTIONARY_ITEM *)rva);
134 + return rv->value;
135 +}
136 +
137 +static inline bool rrdvar_get_value(DICTIONARY *dict, STRING *variable, NETDATA_DOUBLE *result) {
138 + bool found = false;
139 +
140 + const RRDVAR_ACQUIRED *rva = rrdvar_get_and_acquire(dict, variable);
141 + if(rva) {
142 + *result = rrdvar2number(rva);
143 + found = true;
144 + dictionary_acquired_item_release(dict, (const DICTIONARY_ITEM *)rva);
145 + }
146 +
147 + return found;
148 +}
149 +
150 +bool rrdvar_get_custom_host_variable_value(RRDHOST *host, STRING *variable, NETDATA_DOUBLE *result) {
151 + return rrdvar_get_value(host->rrdvars, variable, result);
152 +}
153 +
154 +bool rrdvar_get_custom_chart_variable_value(RRDSET *st, STRING *variable, NETDATA_DOUBLE *result) {
155 + return rrdvar_get_value(st->rrdvars, variable, result);
156 +}
157 +
158 +// ----------------------------------------------------------------------------
159 +// RRDVAR to JSON
160 +
161 +void rrdvar_to_json_members(DICTIONARY *dict, BUFFER *wb) {
162 + RRDVAR *rv;
163 + dfe_start_read(dict, rv) {
164 + buffer_json_member_add_double(wb, rv_dfe.name, rv->value);
165 + }
166 + dfe_done(rv);
167 +}
168 +
169 +void health_api_v1_chart_custom_variables2json(RRDSET *st, BUFFER *buf) {
170 + rrdvar_to_json_members(st->rrdvars, buf);
171 +}
172 +
173 +void health_api_v1_chart_variables2json(RRDSET *st, BUFFER *wb) {
174 +
175 + // FIXME this list is incomplete
176 + // alerts can also access {context}.{dimension} from the entire host database
177 +
178 + RRDHOST *host = st->rrdhost;
179 +
180 + buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_DEFAULT);
181 +
182 + buffer_json_member_add_string(wb, "chart", rrdset_id(st));
183 + buffer_json_member_add_string(wb, "chart_name", rrdset_name(st));
184 + buffer_json_member_add_string(wb, "chart_context", rrdset_context(st));
185 + buffer_json_member_add_string(wb, "family", rrdset_family(st));
186 + buffer_json_member_add_string(wb, "host", rrdhost_hostname(host));
187 +
188 + time_t now = now_realtime_sec();
189 +
190 + buffer_json_member_add_object(wb, "current_alert_values");
191 + {
192 + buffer_json_member_add_double(wb, "this", NAN);
193 + buffer_json_member_add_double(wb, "after", (NETDATA_DOUBLE)now - 1);
194 + buffer_json_member_add_double(wb, "before", (NETDATA_DOUBLE)now);
195 + buffer_json_member_add_double(wb, "now", (NETDATA_DOUBLE)now);
196 + buffer_json_member_add_double(wb, "status", (NETDATA_DOUBLE)RRDCALC_STATUS_REMOVED);
197 + buffer_json_member_add_double(wb, "REMOVED", (NETDATA_DOUBLE)RRDCALC_STATUS_REMOVED);
198 + buffer_json_member_add_double(wb, "UNDEFINED", (NETDATA_DOUBLE)RRDCALC_STATUS_UNDEFINED);
199 + buffer_json_member_add_double(wb, "UNINITIALIZED", (NETDATA_DOUBLE)RRDCALC_STATUS_UNINITIALIZED);
200 + buffer_json_member_add_double(wb, "CLEAR", (NETDATA_DOUBLE)RRDCALC_STATUS_CLEAR);
201 + buffer_json_member_add_double(wb, "WARNING", (NETDATA_DOUBLE)RRDCALC_STATUS_WARNING);
202 + buffer_json_member_add_double(wb, "CRITICAL", (NETDATA_DOUBLE)RRDCALC_STATUS_CRITICAL);
203 + buffer_json_member_add_double(wb, "green", NAN);
204 + buffer_json_member_add_double(wb, "red", NAN);
205 + }
206 + buffer_json_object_close(wb);
207 +
208 + buffer_json_member_add_object(wb, "dimensions_last_stored_values");
209 + {
210 + RRDDIM *rd;
211 + dfe_start_read(st->rrddim_root_index, rd) {
212 + buffer_json_member_add_double(wb, string2str(rd->id), rd->collector.last_stored_value);
213 + if(rd->name != rd->id)
214 + buffer_json_member_add_double(wb, string2str(rd->name), rd->collector.last_stored_value);
215 + }
216 + dfe_done(rd);
217 + }
218 + buffer_json_object_close(wb);
219 +
220 + buffer_json_member_add_object(wb, "dimensions_last_collected_values");
221 + {
222 + char name[RRD_ID_LENGTH_MAX + 1 + 100];
223 + RRDDIM *rd;
224 + dfe_start_read(st->rrddim_root_index, rd) {
225 + snprintfz(name, sizeof(name), "%s_raw", string2str(rd->id));
226 + buffer_json_member_add_int64(wb, name, rd->collector.last_collected_value);
227 + if(rd->name != rd->id) {
228 + snprintfz(name, sizeof(name), "%s_raw", string2str(rd->name));
229 + buffer_json_member_add_int64(wb, name, rd->collector.last_collected_value);
230 + }
231 + }
232 + dfe_done(rd);
233 + }
234 + buffer_json_object_close(wb);
235 +
236 + buffer_json_member_add_object(wb, "dimensions_last_collected_time");
237 + {
238 + char name[RRD_ID_LENGTH_MAX + 1 + 100];
239 + RRDDIM *rd;
240 + dfe_start_read(st->rrddim_root_index, rd) {
241 + snprintfz(name, sizeof(name), "%s_last_collected_t", string2str(rd->id));
242 + buffer_json_member_add_int64(wb, name, rd->collector.last_collected_time.tv_sec);
243 + if(rd->name != rd->id) {
244 + snprintfz(name, sizeof(name), "%s_last_collected_t", string2str(rd->name));
245 + buffer_json_member_add_int64(wb, name, rd->collector.last_collected_time.tv_sec);
246 + }
247 + }
248 + dfe_done(rd);
249 + }
250 + buffer_json_object_close(wb);
251 +
252 + buffer_json_member_add_object(wb, "chart_variables");
253 + {
254 + buffer_json_member_add_int64(wb, "update_every", st->update_every);
255 + buffer_json_member_add_uint64(wb, "last_collected_t", st->last_collected_time.tv_sec);
256 +
257 + rrdvar_to_json_members(st->rrdvars, wb);
258 + }
259 + buffer_json_object_close(wb);
260 +
261 + buffer_json_member_add_object(wb, "host_variables");
262 + {
263 + rrdvar_to_json_members(st->rrdhost->rrdvars, wb);
264 + }
265 + buffer_json_object_close(wb);
266 +
267 + buffer_json_member_add_object(wb, "alerts");
268 + {
269 + struct scored {
270 + bool existing;
271 + STRING *chart;
272 + STRING *context;
273 + NETDATA_DOUBLE value;
274 + size_t score;
275 + } tmp, *z;
276 + DICTIONARY *dict = dictionary_create(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE);
277 +
278 + RRDCALC *rc;
279 + dfe_start_read(st->rrdhost->rrdcalc_root_index, rc) {
280 + tmp = (struct scored) {
281 + .existing = false,
282 + .chart = string_dup(rc->rrdset->id),
283 + .context = string_dup(rc->rrdset->context),
284 + .value = rc->value,
285 + .score = rrdlabels_common_count(rc->rrdset->rrdlabels, st->rrdlabels),
286 + };
287 + z = dictionary_set(dict, string2str(rc->config.name), &tmp, sizeof(tmp));
288 +
289 + if(z->existing) {
290 + if(tmp.score > z->score)
291 + SWAP(*z, tmp);
292 + z->existing = true;
293 + string_freez(tmp.chart);
294 + string_freez(tmp.context);
295 + }
296 + else
297 + z->existing = true;
298 + }
299 + dfe_done(rc);
300 +
301 + dfe_start_read(dict, z) {
302 + buffer_json_member_add_object(wb, z_dfe.name);
303 + {
304 + buffer_json_member_add_double(wb, "value", z->value);
305 + buffer_json_member_add_string(wb, "instance", string2str(z->chart));
306 + buffer_json_member_add_string(wb, "context", string2str(z->context));
307 + buffer_json_member_add_uint64(wb, "score", z->score);
308 + }
309 + buffer_json_object_close(wb);
310 +
311 + string_freez(z->chart);
312 + string_freez(z->context);
313 + }
314 + dfe_done(z);
315 +
316 + dictionary_destroy(dict);
317 + }
318 + buffer_json_object_close(wb);
319 +
320 + buffer_json_finalize(wb);
321 +}
322 +
323 +// ----------------------------------------------------------------------------
324 +// RRDVAR private members examination
325 +
326 +const char *rrdvar_name(const RRDVAR_ACQUIRED *rva) {
327 + return dictionary_acquired_item_name((const DICTIONARY_ITEM *)rva);
328 +}
329 +
330 +void rrdvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb) {
331 + rrdset_flag_clear(st, RRDSET_FLAG_UPSTREAM_SEND_VARIABLES);
332 +
333 + // send the chart local custom variables
334 + RRDVAR *rv;
335 + dfe_start_read(st->rrdvars, rv) {
336 + buffer_sprintf(wb
337 + , "VARIABLE CHART %s = " NETDATA_DOUBLE_FORMAT "\n"
338 + , rv_dfe.name, rv->value
339 + );
340 + }
341 + dfe_done(rv);
342 +}
health/rrdvar.h new
+44
@@ -0,0 +1,44 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_RRDVAR_H
4 +#define NETDATA_RRDVAR_H 1
5 +
6 +#include "libnetdata/libnetdata.h"
7 +
8 +#define RRDVAR_MAX_LENGTH 1024
9 +
10 +int rrdvar_fix_name(char *variable);
11 +
12 +#include "database/rrd.h"
13 +
14 +STRING *rrdvar_name_to_string(const char *name);
15 +
16 +const RRDVAR_ACQUIRED *rrdvar_host_variable_add_and_acquire(RRDHOST *host, const char *name);
17 +void rrdvar_host_variable_set(RRDHOST *host, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value);
18 +
19 +int rrdvar_walkthrough_read(DICTIONARY *dict, int (*callback)(const DICTIONARY_ITEM *item, void *rrdvar, void *data), void *data);
20 +
21 +#define rrdvar_host_variable_release(host, rva) rrdvar_release((host)->rrdvars, rva)
22 +#define rrdvar_chart_variable_release(st, rva) rrdvar_release((st)->rrdvars, rva)
23 +void rrdvar_release(DICTIONARY *dict, const RRDVAR_ACQUIRED *rva);
24 +
25 +NETDATA_DOUBLE rrdvar2number(const RRDVAR_ACQUIRED *rva);
26 +
27 +const RRDVAR_ACQUIRED *rrdvar_add_and_acquire(DICTIONARY *dict, STRING *name, NETDATA_DOUBLE value);
28 +
29 +DICTIONARY *rrdvariables_create(void);
30 +void rrdvariables_destroy(DICTIONARY *dict);
31 +
32 +void rrdvar_delete_all(DICTIONARY *dict);
33 +
34 +const char *rrdvar_name(const RRDVAR_ACQUIRED *rva);
35 +
36 +void rrdvar_print_to_streaming_custom_chart_variables(RRDSET *st, BUFFER *wb);
37 +
38 +const RRDVAR_ACQUIRED *rrdvar_chart_variable_add_and_acquire(RRDSET *st, const char *name);
39 +void rrdvar_chart_variable_set(RRDSET *st, const RRDVAR_ACQUIRED *rva, NETDATA_DOUBLE value);
40 +
41 +bool rrdvar_get_custom_host_variable_value(RRDHOST *host, STRING *variable, NETDATA_DOUBLE *result);
42 +bool rrdvar_get_custom_chart_variable_value(RRDSET *st, STRING *variable, NETDATA_DOUBLE *result);
43 +
44 +#endif //NETDATA_RRDVAR_H
health/schema.d/health:alert:prototype.json new
+509
@@ -0,0 +1,509 @@
1 +{
2 + "jsonSchema": {
3 + "$schema": "http://json-schema.org/draft-07/schema#",
4 + "definitions": {
5 + "matchInstance": {
6 + "type": "object",
7 + "title": "Matching rule for a specific instance",
8 + "description": "This rule will be applied to a specific instance on all nodes",
9 + "properties": {
10 + "on": {
11 + "type": "string",
12 + "default": "",
13 + "title": "The instance this rule should be applied to",
14 + "description": "You can find the instance names on all charts at the instances drop down menu. Do not include the host name in this field."
15 + },
16 + "plugin": {
17 + "type": "string",
18 + "default": "*",
19 + "title": "Match data collection plugins",
20 + "description": "A simple pattern to match the data collection plugins that are collecting the data this rule is to be applied to."
21 + },
22 + "module": {
23 + "type": "string",
24 + "default": "*",
25 + "title": "Match data collection plugin modules",
26 + "description": "A simple pattern to match the data collection plugin modules that are collecting the data this rule is to be applied to."
27 + },
28 + "instance_labels": {
29 + "type": "string",
30 + "default": "*",
31 + "title": "Match instance labels",
32 + "description": "A simple pattern to match the instance labels of the instances this rule is to be applied to."
33 + },
34 + "host_labels": {
35 + "type": "string",
36 + "default": "*",
37 + "title": "Match node labels",
38 + "description": "A simple pattern to match the node labels of the nodes this rule is to be applied to."
39 + },
40 + "os": {
41 + "type": "string",
42 + "default": "*",
43 + "title": "Match operating system",
44 + "description": "A simple pattern to match the operating system name of the nodes this rule is to be applied to."
45 + },
46 + "host": {
47 + "type": "string",
48 + "default": "*",
49 + "title": "Match node hostnames",
50 + "description": "A simple pattern to match the hostnames of the nodes this rule is to be applied to."
51 + }
52 + },
53 + "required": [
54 + "on",
55 + "os",
56 + "host",
57 + "plugin",
58 + "module",
59 + "host_labels",
60 + "instance_labels"
61 + ]
62 + },
63 + "matchTemplate": {
64 + "type": "object",
65 + "title": "Matching rule for applying the alert to multiple instances",
66 + "description": "This rule will applied to all instances on all nodes.",
67 + "properties": {
68 + "on": {
69 + "type": "string",
70 + "default": "",
71 + "title": "The context of the instances this rule should be applied to",
72 + "description": "You can find the context at the title bar of all charts in the Metrics dashboard, between the chart title and the units (like system.cpu, or disk.io, etc)."
73 + },
74 + "plugin": {
75 + "type": "string",
76 + "default": "*",
77 + "title": "Match data collection plugins",
78 + "description": "A simple pattern to match the data collection plugins that are collecting the data this rule is to be applied to."
79 + },
80 + "module": {
81 + "type": "string",
82 + "default": "*",
83 + "title": "Match data collection plugin modules",
84 + "description": "A simple pattern to match the data collection plugin modules that are collecting the data this rule is to be applied to."
85 + },
86 + "instance_labels": {
87 + "type": "string",
88 + "default": "*",
89 + "title": "Match instance labels",
90 + "description": "A simple pattern to match the instance labels of the instances this rule is to be applied to."
91 + },
92 + "instances": {
93 + "type": "string",
94 + "default": "*",
95 + "title": "Match instance names",
96 + "description": "A simple pattern to match the instance names of the instances this rule is to be applied to."
97 + },
98 + "host_labels": {
99 + "type": "string",
100 + "default": "*",
101 + "title": "Match node labels",
102 + "description": "A simple pattern to match the node labels of the nodes this rule is to be applied to."
103 + },
104 + "os": {
105 + "type": "string",
106 + "default": "*",
107 + "title": "Match operating system",
108 + "description": "A simple pattern to match the operating system name of the nodes this rule is to be applied to."
109 + },
110 + "host": {
111 + "type": "string",
112 + "default": "*",
113 + "title": "Match node hostnames",
114 + "description": "A simple pattern to match the hostnames of the nodes this rule is to be applied to."
115 + }
116 + },
117 + "required": [
118 + "on",
119 + "os",
120 + "host",
121 + "plugin",
122 + "module",
123 + "host_labels",
124 + "instances",
125 + "instance_labels"
126 + ]
127 + },
128 + "config": {
129 + "type": "object",
130 + "title": "Alert Configuration",
131 + "description": "The properties that control the value the alert will get, the conditions it will trigger, the back-off for notifications, the auto-repeating of notifications, etc.",
132 + "properties": {
133 + "summary": {
134 + "type": "string",
135 + "title": "Short description of the alert",
136 + "description": "This field is used in notification as a short description of the alert. Variables, like ${label:key}, are replaced with the value of instance label called 'key'."
137 + },
138 + "info": {
139 + "type": "string",
140 + "title": "Long description of the alert",
141 + "description": "This field is used to provide enough information about the type and nature of the alert. Variables, like ${label:key}, are replaced with the value of instance label called 'key'."
142 + },
143 + "type": {
144 + "type": "string",
145 + "title": "Alert Type",
146 + "description": "Use categories like: 'System', 'Containers', 'Web Servers', 'Message Brokers', etc."
147 + },
148 + "component": {
149 + "type": "string",
150 + "title": "Alert Component (sub-type)",
151 + "description": "Component is a sub-type of Alert Type. Examples: 'CPU', 'Memory', 'Network', 'Disk', 'Hardware', 'nginx', 'redis', 'postgresql', etc."
152 + },
153 + "classification": {
154 + "type": "string",
155 + "title": "Classification",
156 + "description": "Use 'Workload', 'Utilization', 'Latency', 'Availability', 'Errors', etc."
157 + },
158 + "value": {
159 + "type": "object",
160 + "title": "Alert Value",
161 + "description": "Each alert has a value. This section defines how this value is calculated.",
162 + "properties": {
163 + "database_lookup": {
164 + "type": "object",
165 + "properties": {
166 + "after": {
167 + "type": "integer",
168 + "default": 0,
169 + "title": "Time-Series Oldest Time",
170 + "description": "The oldest timestamp of the time-series data to be included in the query. Negative values define a duration in seconds in the past (so, -60 means a minute ago)."
171 + },
172 + "before": {
173 + "type": "integer",
174 + "default": 0,
175 + "title": "Time-Series Newest Time",
176 + "description": "The newest timestamp of the time-series data to be included in the query. Negative value define a duration in seconds in the past (so, -60 means a minute ago). Zero means now."
177 + },
178 + "grouping": {
179 + "type": "string",
180 + "enum": [
181 + "average",
182 + "median",
183 + "min",
184 + "max",
185 + "sum",
186 + "incremental_sum",
187 + "stddev",
188 + "cv",
189 + "trimmed-mean1",
190 + "trimmed-mean2",
191 + "trimmed-mean3",
192 + "trimmed-mean",
193 + "trimmed-mean10",
194 + "trimmed-mean15",
195 + "trimmed-mean20",
196 + "trimmed-mean25",
197 + "trimmed-median1",
198 + "trimmed-median2",
199 + "trimmed-median3",
200 + "trimmed-median",
201 + "trimmed-median10",
202 + "trimmed-median15",
203 + "trimmed-median20",
204 + "trimmed-median25",
205 + "percentile99",
206 + "percentile98",
207 + "percentile97",
208 + "percentile",
209 + "percentile90",
210 + "percentile80",
211 + "percentile75",
212 + "percentile50",
213 + "percentile25",
214 + "ses",
215 + "des",
216 + "countif"
217 + ],
218 + "enumNames": [
219 + "The mean (average) value",
220 + "The median value",
221 + "The minimum value",
222 + "The maximum value",
223 + "The sum of all the values",
224 + "The delta of the latest and oldest values",
225 + "The standard deviation of the values",
226 + "The standard deviation expresses as a % of the mean value",
227 + "The mean after trimming 1% of the extreme values",
228 + "The mean after trimming 2% of the extreme values",
229 + "The mean after trimming 3% of the extreme values",
230 + "The mean after trimming 5% of the extreme values",
231 + "The mean after trimming 10% of the extreme values",
232 + "The mean after trimming 15% of the extreme values",
233 + "The mean after trimming 20% of the extreme values",
234 + "The mean after trimming 25% of the extreme values",
235 + "The median after trimming 1% of the extreme values",
236 + "The median after trimming 2% of the extreme values",
237 + "The median after trimming 3% of the extreme values",
238 + "The median after trimming 5% of the extreme values",
239 + "The median after trimming 10% of the extreme values",
240 + "The median after trimming 15% of the extreme values",
241 + "The median after trimming 20% of the extreme values",
242 + "The median after trimming 25% of the extreme values",
243 + "The 99th percentile of the values",
244 + "The 98th percentile of the values",
245 + "The 97th percentile of the values",
246 + "The 95th percentile of the values",
247 + "The 90th percentile of the values",
248 + "The 80th percentile of the values",
249 + "The 75th percentile of the values",
250 + "The 50th percentile of the values",
251 + "The 25th percentile of the values",
252 + "Single Exponential Smoothing",
253 + "Double Exponential Smoothing",
254 + "Count If zero"
255 + ],
256 + "default": "average",
257 + "title": "Time Aggregation Function",
258 + "description": "When querying time-series data we need to come up with a single value. This function is used to aggregate all the values of the time-series data to a single value."
259 + },
260 + "options": {
261 + "type": "array",
262 + "title": "Time-Series Query options",
263 + "description": "Options affecting the way the value is calculated",
264 + "uniqueItems": true,
265 + "items": {
266 + "enum": [
267 + "unaligned",
268 + "abs",
269 + "min2max",
270 + "null2zero",
271 + "percentage",
272 + "anomaly-bit",
273 + "match_ids",
274 + "match_names"
275 + ],
276 + "enumNames": [
277 + "Do not shift the time-frame for visual presentation",
278 + "Make all values positive before using them",
279 + "Use the delta of the minimum to the maximum value",
280 + "Treat gaps in the time-series as a zero value",
281 + "Calculate the percentage of the selected dimensions over the sum of all dimensions",
282 + "Query the anomaly rate of the samples collected",
283 + "Match only dimension IDs, not Names",
284 + "Match only dimension Names, not IDs"
285 + ]
286 + },
287 + "default": [ "unaligned" ]
288 + },
289 + "dimensions": {
290 + "type": "string",
291 + "title": "Dimensions Selection Pattern",
292 + "description": "A simple pattern to match the dimensions that should be included in the query",
293 + "default": "*"
294 + }
295 + }
296 + },
297 + "calculation": {
298 + "type": "string",
299 + "title": "Calculation Expression",
300 + "description": "The database value is available as '$this'. This expression can utilize variables to transform the value of the alert."
301 + },
302 + "units": {
303 + "type": "string",
304 + "title": "Alert Unit of Measurement",
305 + "description": "The unit of measurement the alert value is expressed with. If unset, the units of the instance the alert is attached to will be used."
306 + }
307 + }
308 + },
309 + "conditions": {
310 + "type": "object",
311 + "properties": {
312 + "green": {
313 + "type": [
314 + "integer",
315 + "null"
316 + ],
317 + "title": "Healthy threshold ($green)",
318 + "description": "A threshold that indicates a healthy status. This threshold can be used as '$green' in the alert conditions."
319 + },
320 + "red": {
321 + "type": [
322 + "integer",
323 + "null"
324 + ],
325 + "title": "Critical threshold ($red)",
326 + "description": "A threshold that indicates a critical status. This threshold can be used as '$red' in the alert conditions."
327 + },
328 + "warning_condition": {
329 + "type": "string",
330 + "title": "Warning Expression",
331 + "description": "The alert value is available as '$this'. If this expression evaluates to a non-zero value, the alert is considered to be in warning level."
332 + },
333 + "critical_condition": {
334 + "type": "string",
335 + "title": "Critical Expression",
336 + "description": "The alert value is available as '$this'. If this expression evaluates to a non-zero value, the alert is considered to be in critical level."
337 + }
338 + }
339 + },
340 + "action": {
341 + "type": "object",
342 + "title": "Alert Action",
343 + "description": "The action the alert should take when it transitions states",
344 + "properties": {
345 + "execute": {
346 + "type": "string",
347 + "title": "Command to execute when the alert transitions states",
348 + "description": "Leave this empty to get the default alert notification"
349 + },
350 + "recipient": {
351 + "type": "string",
352 + "title": "Notification Recipients",
353 + "description": "A space separated list of the recipients of the alert notifications. The special recipient 'silent' prevents this alert from taking any action (i.e. sending notifications)."
354 + },
355 + "options": {
356 + "type": "array",
357 + "title": "Action Options",
358 + "description": "Options related to the actions this alert will take.",
359 + "uniqueItems": true,
360 + "items": {
361 + "enum": [
362 + "no-clear-notification"
363 + ],
364 + "enumNames": [
365 + "Do not perform any action when the alert is cleared"
366 + ]
367 + },
368 + "default": []
369 + },
370 + "delay": {
371 + "type": "object",
372 + "title": "Delay the action (notification)",
373 + "description": "Rules to postpone the action, to avoid multiple notifications on flapping alerts.",
374 + "properties": {
375 + "up": {
376 + "type": "integer",
377 + "title": "Delay when raising",
378 + "description": "Delay the action (notification) that many seconds, when the alert is rising."
379 + },
380 + "down": {
381 + "type": "integer",
382 + "title": "Delay when going Down",
383 + "description": "Delay the action (notification) that many seconds, when the alert is recovering."
384 + },
385 + "max": {
386 + "type": "integer",
387 + "title": "Max Acceptable Delay",
388 + "description": "The maximum acceptable delay in seconds, for taking the action (notification)."
389 + },
390 + "multiplier": {
391 + "type": "number",
392 + "title": "Back-Off on Transitions",
393 + "description": "Multiply the delay by this number, every time the alert transitions to a new state, while the action (notification) is being delayed."
394 + }
395 + }
396 + },
397 + "repeat": {
398 + "type": "object",
399 + "title": "Action Auto-Repeat",
400 + "description": "Repeat the action while the alert is raised.",
401 + "properties": {
402 + "enabled": {
403 + "type": "boolean"
404 + },
405 + "warning": {
406 + "type": "integer",
407 + "title": "Repeat on Warning",
408 + "description": "The number of seconds to repeat the action while the alert is in warning state"
409 + },
410 + "critical": {
411 + "type": "integer",
412 + "title": "Repeat on Critical",
413 + "description": "The number of seconds to repeat the action while the alert is in critical state"
414 + }
415 + }
416 + }
417 + }
418 + }
419 + },
420 + "required": [
421 + ]
422 + }
423 + },
424 + "type": "object",
425 + "properties": {
426 + "name": {
427 + "type": "string"
428 + },
429 + "format_version": {
430 + "type": "integer",
431 + "default": 1
432 + },
433 + "rules": {
434 + "type": "array",
435 + "items": {
436 + "type": "object",
437 + "properties": {
438 + "enabled": {
439 + "type": "boolean",
440 + "default": true,
441 + "title": "Enabled"
442 + },
443 + "type": {
444 + "type": "string",
445 + "enum": [
446 + "instance",
447 + "template"
448 + ],
449 + "enumNames": [
450 + "Apply this rule to a specific instance (deprecated)",
451 + "Apply this rule to all instances matching the rules"
452 + ],
453 + "default": "template",
454 + "title": "Type of rule",
455 + "description": "Rules can be configured to match a specific instance (like a specific disk), or match all the instances (like all the disks). All rules are always checked against all nodes streamed to this Netdata, so the matching rules include patterns to match both instances and nodes."
456 + }
457 + },
458 + "required": [ "type", "enabled" ],
459 + "if": {
460 + "properties": {
461 + "type": { "const": "instance" }
462 + }
463 + },
464 + "then": {
465 + "properties": {
466 + "match": { "$ref": "#/definitions/matchInstance" },
467 + "config": { "$ref": "#/definitions/config" }
468 + }
469 + },
470 + "else": {
471 + "properties": {
472 + "match": { "$ref": "#/definitions/matchTemplate" },
473 + "config": { "$ref": "#/definitions/config" }
474 + }
475 + }
476 + }
477 + }
478 + },
479 + "required": [
480 + "rules"
481 + ]
482 + },
483 + "uiSchema": {
484 + "format_version": {
485 + "ui:widget": "hidden"
486 + },
487 + "name": {
488 + "ui:widget": "hidden"
489 + },
490 + "rules": {
491 + "items": {
492 + "enabled": {
493 + "ui:widget": "checkbox"
494 + },
495 + "config": {
496 + "hash": {
497 + "ui:widget": "hidden"
498 + },
499 + "source_type": {
500 + "ui:widget": "hidden"
501 + },
502 + "source": {
503 + "ui:widget": "hidden"
504 + }
505 + }
506 + }
507 + }
508 + }
509 +}
libnetdata/clocks/clocks.c
+1 -1
@@ -60,7 +60,7 @@ static usec_t get_clock_resolution(clockid_t clock) {
60
61 if(clock_getres(clock, &ts) == 0) {
62 usec_t ret = (usec_t)ts.tv_sec * USEC_PER_SEC + (usec_t)ts.tv_nsec / NSEC_PER_USEC;
63 - if(!ret && ts.tv_nsec > 0 && ts.tv_nsec < NSEC_PER_USEC)
63 + if(!ret && ts.tv_nsec > 0 && ts.tv_nsec < (long int)NSEC_PER_USEC)
64 return (usec_t)1;
65
66 else if(ret > MAX_CLOCK_RESOLUTION_UT) {
libnetdata/config/dyncfg.c
+2 -1
@@ -263,6 +263,7 @@ int dyncfg_node_find_and_call(DICTIONARY *dyncfg_nodes, const char *transaction,
263
264 const char *id = get_word(words, num_words, 1);
265 const char *action = get_word(words, num_words, 2);
266 + const char *add_name = get_word(words, num_words, 3);
267
268 if(!id || !*id)
269 return dyncfg_default_response(result, HTTP_RESP_BAD_REQUEST, "dyncfg node: id is missing from the request");
@@ -283,7 +284,7 @@ int dyncfg_node_find_and_call(DICTIONARY *dyncfg_nodes, const char *transaction,
284 buffer_flush(result);
285 result->content_type = CT_APPLICATION_JSON;
286
286 - int code = df->cb(transaction, id, cmd, payload, stop_monotonic_ut, cancelled, result, source, df->data);
287 + int code = df->cb(transaction, id, cmd, add_name, payload, stop_monotonic_ut, cancelled, result, source, df->data);
288
289 if(!result->expires)
290 result->expires = now_realtime_sec();
libnetdata/config/dyncfg.h
+2 -1
@@ -8,6 +8,7 @@
8 #define DYNCFG_RESP_SUCCESS(code) (code >= 200 && code <= 299)
9 #define DYNCFG_RESP_RUNNING 200 // accepted and running
10 #define DYNCFG_RESP_ACCEPTED 202 // accepted, but not running yet
11 +#define DYNCFG_RESP_ACCEPTED_DISABLED 298 // accepted, but is disabled
12 #define DYNCFG_RESP_ACCEPTED_RESTART_REQUIRED 299 // accepted, but restart is required to apply it
13
14 typedef enum __attribute__((packed)) {
@@ -65,7 +66,7 @@ char *dyncfg_escape_id_for_filename(const char *id);
66 #include "../buffer/buffer.h"
67 #include "../dictionary/dictionary.h"
68
68 -typedef int (*dyncfg_cb_t)(const char *transaction, const char *id, DYNCFG_CMDS cmd, BUFFER *payload, usec_t *stop_monotonic_ut, bool *cancelled, BUFFER *result, const char *source, void *data);
69 +typedef int (*dyncfg_cb_t)(const char *transaction, const char *id, DYNCFG_CMDS cmd, const char *add_name, BUFFER *payload, usec_t *stop_monotonic_ut, bool *cancelled, BUFFER *result, const char *source, void *data);
70
71 struct dyncfg_node {
72 DYNCFG_TYPE type;
libnetdata/eval/eval.c
+67 -123
@@ -5,6 +5,11 @@
5 // ----------------------------------------------------------------------------
6 // data structures for storing the parsed expression in memory
7
8 +typedef struct eval_variable {
9 + STRING *name;
10 + struct eval_variable *next;
11 +} EVAL_VARIABLE;
12 +
13 typedef struct eval_value {
14 int type;
15
@@ -24,6 +29,21 @@ typedef struct eval_node {
29 EVAL_VALUE ops[];
30 } EVAL_NODE;
31
32 +struct eval_expression {
33 + STRING *source;
34 + STRING *parsed_as;
35 +
36 + NETDATA_DOUBLE result;
37 +
38 + int error;
39 + BUFFER *error_msg;
40 +
41 + EVAL_NODE *nodes;
42 +
43 + void *variable_lookup_cb_data;
44 + eval_expression_variable_lookup_t variable_lookup_cb;
45 +};
46 +
47 // these are used for EVAL_NODE.operator
48 // they are used as internal IDs to identify an operator
49 // THEY ARE NOT USED FOR PARSING OPERATORS LIKE THAT
@@ -62,124 +82,9 @@ static inline void print_parsed_as_constant(BUFFER *out, NETDATA_DOUBLE n);
82 // evaluation of expressions
83
84 static inline NETDATA_DOUBLE eval_variable(EVAL_EXPRESSION *exp, EVAL_VARIABLE *v, int *error) {
65 - static STRING
66 - *this_string = NULL,
67 - *now_string = NULL,
68 - *after_string = NULL,
69 - *before_string = NULL,
70 - *status_string = NULL,
71 - *removed_string = NULL,
72 - *uninitialized_string = NULL,
73 - *undefined_string = NULL,
74 - *clear_string = NULL,
75 - *warning_string = NULL,
76 - *critical_string = NULL;
77 -
85 NETDATA_DOUBLE n;
86
80 - if(unlikely(this_string == NULL)) {
81 - this_string = string_strdupz("this");
82 - now_string = string_strdupz("now");
83 - after_string = string_strdupz("after");
84 - before_string = string_strdupz("before");
85 - status_string = string_strdupz("status");
86 - removed_string = string_strdupz("REMOVED");
87 - uninitialized_string = string_strdupz("UNINITIALIZED");
88 - undefined_string = string_strdupz("UNDEFINED");
89 - clear_string = string_strdupz("CLEAR");
90 - warning_string = string_strdupz("WARNING");
91 - critical_string = string_strdupz("CRITICAL");
92 - }
93 -
94 - if(unlikely(v->name == this_string)) {
95 - n = (exp->myself)?*exp->myself:NAN;
96 - buffer_strcat(exp->error_msg, "[ $this = ");
97 - print_parsed_as_constant(exp->error_msg, n);
98 - buffer_strcat(exp->error_msg, " ] ");
99 - return n;
100 - }
101 -
102 - if(unlikely(v->name == after_string)) {
103 - n = (exp->after && *exp->after)?*exp->after:NAN;
104 - buffer_strcat(exp->error_msg, "[ $after = ");
105 - print_parsed_as_constant(exp->error_msg, n);
106 - buffer_strcat(exp->error_msg, " ] ");
107 - return n;
108 - }
109 -
110 - if(unlikely(v->name == before_string)) {
111 - n = (exp->before && *exp->before)?*exp->before:NAN;
112 - buffer_strcat(exp->error_msg, "[ $before = ");
113 - print_parsed_as_constant(exp->error_msg, n);
114 - buffer_strcat(exp->error_msg, " ] ");
115 - return n;
116 - }
117 -
118 - if(unlikely(v->name == now_string)) {
119 - n = (NETDATA_DOUBLE)now_realtime_sec();
120 - buffer_strcat(exp->error_msg, "[ $now = ");
121 - print_parsed_as_constant(exp->error_msg, n);
122 - buffer_strcat(exp->error_msg, " ] ");
123 - return n;
124 - }
125 -
126 - if(unlikely(v->name == status_string)) {
127 - n = (exp->status)?*exp->status:RRDCALC_STATUS_UNINITIALIZED;
128 - buffer_strcat(exp->error_msg, "[ $status = ");
129 - print_parsed_as_constant(exp->error_msg, n);
130 - buffer_strcat(exp->error_msg, " ] ");
131 - return n;
132 - }
133 -
134 - if(unlikely(v->name == removed_string)) {
135 - n = RRDCALC_STATUS_REMOVED;
136 - buffer_strcat(exp->error_msg, "[ $REMOVED = ");
137 - print_parsed_as_constant(exp->error_msg, n);
138 - buffer_strcat(exp->error_msg, " ] ");
139 - return n;
140 - }
141 -
142 - if(unlikely(v->name == uninitialized_string)) {
143 - n = RRDCALC_STATUS_UNINITIALIZED;
144 - buffer_strcat(exp->error_msg, "[ $UNINITIALIZED = ");
145 - print_parsed_as_constant(exp->error_msg, n);
146 - buffer_strcat(exp->error_msg, " ] ");
147 - return n;
148 - }
149 -
150 - if(unlikely(v->name == undefined_string)) {
151 - n = RRDCALC_STATUS_UNDEFINED;
152 - buffer_strcat(exp->error_msg, "[ $UNDEFINED = ");
153 - print_parsed_as_constant(exp->error_msg, n);
154 - buffer_strcat(exp->error_msg, " ] ");
155 - return n;
156 - }
157 -
158 - if(unlikely(v->name == clear_string)) {
159 - n = RRDCALC_STATUS_CLEAR;
160 - buffer_strcat(exp->error_msg, "[ $CLEAR = ");
161 - print_parsed_as_constant(exp->error_msg, n);
162 - buffer_strcat(exp->error_msg, " ] ");
163 - return n;
164 - }
165 -
166 - if(unlikely(v->name == warning_string)) {
167 - n = RRDCALC_STATUS_WARNING;
168 - buffer_strcat(exp->error_msg, "[ $WARNING = ");
169 - print_parsed_as_constant(exp->error_msg, n);
170 - buffer_strcat(exp->error_msg, " ] ");
171 - return n;
172 - }
173 -
174 - if(unlikely(v->name == critical_string)) {
175 - n = RRDCALC_STATUS_CRITICAL;
176 - buffer_strcat(exp->error_msg, "[ $CRITICAL = ");
177 - print_parsed_as_constant(exp->error_msg, n);
178 - buffer_strcat(exp->error_msg, " ] ");
179 - return n;
180 - }
181 -
182 - if(exp->rrdcalc && health_variable_lookup(v->name, exp->rrdcalc, &n)) {
87 + if(exp->variable_lookup_cb && exp->variable_lookup_cb(v->name, exp->variable_lookup_cb_data, &n)) {
88 buffer_sprintf(exp->error_msg, "[ ${%s} = ", string2str(v->name));
89 print_parsed_as_constant(exp->error_msg, n);
90 buffer_strcat(exp->error_msg, " ] ");
@@ -1074,7 +979,7 @@ int expression_evaluate(EVAL_EXPRESSION *expression) {
979 expression->error = EVAL_ERROR_OK;
980
981 buffer_reset(expression->error_msg);
1077 - expression->result = eval_node(expression, (EVAL_NODE *)expression->nodes, &expression->error);
982 + expression->result = eval_node(expression, expression->nodes, &expression->error);
983
984 if(unlikely(isnan(expression->result))) {
985 if(expression->error == EVAL_ERROR_OK)
@@ -1104,6 +1009,9 @@ int expression_evaluate(EVAL_EXPRESSION *expression) {
1009 }
1010
1011 EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, int *error) {
1012 + if(!string || !*string)
1013 + return NULL;
1014 +
1015 const char *s = string;
1016 int err = EVAL_ERROR_OK;
1017
@@ -1137,12 +1045,12 @@ EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, in
1045
1046 EVAL_EXPRESSION *exp = callocz(1, sizeof(EVAL_EXPRESSION));
1047
1140 - exp->source = strdupz(string);
1141 - exp->parsed_as = strdupz(buffer_tostring(out));
1048 + exp->source = string_strdupz(string);
1049 + exp->parsed_as = string_strdupz(buffer_tostring(out));
1050 buffer_free(out);
1051
1052 exp->error_msg = buffer_create(100, NULL);
1145 - exp->nodes = (void *)op;
1053 + exp->nodes = op;
1054
1055 return exp;
1056 }
@@ -1150,9 +1058,9 @@ EVAL_EXPRESSION *expression_parse(const char *string, const char **failed_at, in
1058 void expression_free(EVAL_EXPRESSION *expression) {
1059 if(!expression) return;
1060
1153 - if(expression->nodes) eval_node_free((EVAL_NODE *)expression->nodes);
1154 - freez((void *)expression->source);
1155 - freez((void *)expression->parsed_as);
1061 + if(expression->nodes) eval_node_free(expression->nodes);
1062 + string_freez((void *)expression->source);
1063 + string_freez((void *)expression->parsed_as);
1064 buffer_free(expression->error_msg);
1065 freez(expression);
1066 }
@@ -1199,3 +1107,39 @@ const char *expression_strerror(int error) {
1107 return "unknown error";
1108 }
1109 }
1110 +
1111 +const char *expression_source(EVAL_EXPRESSION *expression) {
1112 + if(!expression)
1113 + return string2str(NULL);
1114 +
1115 + return string2str(expression->source);
1116 +}
1117 +
1118 +const char *expression_parsed_as(EVAL_EXPRESSION *expression) {
1119 + if(!expression)
1120 + return string2str(NULL);
1121 +
1122 + return string2str(expression->parsed_as);
1123 +}
1124 +
1125 +const char *expression_error_msg(EVAL_EXPRESSION *expression) {
1126 + if(!expression || !expression->error_msg)
1127 + return "";
1128 +
1129 + return buffer_tostring(expression->error_msg);
1130 +}
1131 +
1132 +NETDATA_DOUBLE expression_result(EVAL_EXPRESSION *expression) {
1133 + if(!expression)
1134 + return NAN;
1135 +
1136 + return expression->result;
1137 +}
1138 +
1139 +void expression_set_variable_lookup_callback(EVAL_EXPRESSION *expression, eval_expression_variable_lookup_t cb, void *data) {
1140 + if(!expression)
1141 + return;
1142 +
1143 + expression->variable_lookup_cb = cb;
1144 + expression->variable_lookup_cb_data = data;
1145 +}
libnetdata/eval/eval.h
+8 -36
@@ -7,41 +7,9 @@
7
8 #define EVAL_MAX_VARIABLE_NAME_LENGTH 300
9
10 -typedef enum rrdcalc_status {
11 - RRDCALC_STATUS_REMOVED = -2,
12 - RRDCALC_STATUS_UNDEFINED = -1,
13 - RRDCALC_STATUS_UNINITIALIZED = 0,
14 - RRDCALC_STATUS_CLEAR = 1,
15 - RRDCALC_STATUS_RAISED = 2, // DO NOT CHANGE THESE NUMBERS
16 - RRDCALC_STATUS_WARNING = 3, // DO NOT CHANGE THESE NUMBERS
17 - RRDCALC_STATUS_CRITICAL = 4, // DO NOT CHANGE THESE NUMBERS
18 -} RRDCALC_STATUS;
19 -
20 -typedef struct eval_variable {
21 - STRING *name;
22 - struct eval_variable *next;
23 -} EVAL_VARIABLE;
24 -
25 -typedef struct eval_expression {
26 - const char *source;
27 - const char *parsed_as;
28 -
29 - RRDCALC_STATUS *status;
30 - NETDATA_DOUBLE *myself;
31 - time_t *after;
32 - time_t *before;
33 -
34 - NETDATA_DOUBLE result;
35 -
36 - int error;
37 - BUFFER *error_msg;
38 -
39 - // hidden EVAL_NODE *
40 - void *nodes;
41 -
42 - // custom data to be used for looking up variables
43 - struct rrdcalc *rrdcalc;
44 -} EVAL_EXPRESSION;
10 +struct eval_expression;
11 +typedef struct eval_expression EVAL_EXPRESSION;
12 +typedef bool (*eval_expression_variable_lookup_t)(STRING *variable, void *data, NETDATA_DOUBLE *result);
13
14 #define EVAL_VALUE_INVALID 0
15 #define EVAL_VALUE_NUMBER 1
@@ -82,6 +50,10 @@ const char *expression_strerror(int error);
50 // 2 = FAILED, the error message is in: buffer_tostring(expression->error_msg)
51 int expression_evaluate(EVAL_EXPRESSION *expression);
52
85 -int health_variable_lookup(STRING *variable, struct rrdcalc *rc, NETDATA_DOUBLE *result);
53 +const char *expression_source(EVAL_EXPRESSION *expression);
54 +const char *expression_parsed_as(EVAL_EXPRESSION *expression);
55 +const char *expression_error_msg(EVAL_EXPRESSION *expression);
56 +NETDATA_DOUBLE expression_result(EVAL_EXPRESSION *expression);
57 +void expression_set_variable_lookup_callback(EVAL_EXPRESSION *expression, eval_expression_variable_lookup_t cb, void *data);
58
59 #endif //NETDATA_EVAL_H
libnetdata/health/health.c deleted
-169
@@ -1,169 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#include "health.h"
4 -
5 -SILENCERS *silencers;
6 -
7 -/**
8 - * Create Silencer
9 - *
10 - * Allocate a new silencer to Netdata.
11 - *
12 - * @return It returns the address off the silencer on success and NULL otherwise
13 - */
14 -SILENCER *create_silencer(void) {
15 - SILENCER *t = callocz(1, sizeof(SILENCER));
16 - netdata_log_debug(D_HEALTH, "HEALTH command API: Created empty silencer");
17 -
18 - return t;
19 -}
20 -
21 -/**
22 - * Health Silencers add
23 - *
24 - * Add more one silencer to the list of silencers.
25 - *
26 - * @param silencer
27 - */
28 -void health_silencers_add(SILENCER *silencer) {
29 - // Add the created instance to the linked list in silencers
30 - silencer->next = silencers->silencers;
31 - silencers->silencers = silencer;
32 - netdata_log_debug(
33 - D_HEALTH,
34 - "HEALTH command API: Added silencer %s:%s:%s:%s",
35 - silencer->alarms,
36 - silencer->charts,
37 - silencer->contexts,
38 - silencer->hosts);
39 -}
40 -
41 -/**
42 - * Silencers Add Parameter
43 - *
44 - * Create a new silencer and adjust the variables
45 - *
46 - * @param silencer a pointer to the silencer that will be adjusted
47 - * @param key the key value sent by client
48 - * @param value the value sent to the key
49 - *
50 - * @return It returns the silencer configured on success and NULL otherwise
51 - */
52 -SILENCER *health_silencers_addparam(SILENCER *silencer, char *key, char *value) {
53 - static uint32_t
54 - hash_alarm = 0,
55 - hash_template = 0,
56 - hash_chart = 0,
57 - hash_context = 0,
58 - hash_host = 0;
59 -
60 - if (unlikely(!hash_alarm)) {
61 - hash_alarm = simple_uhash(HEALTH_ALARM_KEY);
62 - hash_template = simple_uhash(HEALTH_TEMPLATE_KEY);
63 - hash_chart = simple_uhash(HEALTH_CHART_KEY);
64 - hash_context = simple_uhash(HEALTH_CONTEXT_KEY);
65 - hash_host = simple_uhash(HEALTH_HOST_KEY);
66 - }
67 -
68 - uint32_t hash = simple_uhash(key);
69 - if (unlikely(silencer == NULL)) {
70 - if (
71 - (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) ||
72 - (hash == hash_template && !strcasecmp(key, HEALTH_TEMPLATE_KEY)) ||
73 - (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) ||
74 - (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) ||
75 - (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY))
76 - ) {
77 - silencer = create_silencer();
78 - }
79 - }
80 -
81 - if (hash == hash_alarm && !strcasecmp(key, HEALTH_ALARM_KEY)) {
82 - silencer->alarms = strdupz(value);
83 - silencer->alarms_pattern = simple_pattern_create(silencer->alarms, NULL, SIMPLE_PATTERN_EXACT, true);
84 - } else if (hash == hash_chart && !strcasecmp(key, HEALTH_CHART_KEY)) {
85 - silencer->charts = strdupz(value);
86 - silencer->charts_pattern = simple_pattern_create(silencer->charts, NULL, SIMPLE_PATTERN_EXACT, true);
87 - } else if (hash == hash_context && !strcasecmp(key, HEALTH_CONTEXT_KEY)) {
88 - silencer->contexts = strdupz(value);
89 - silencer->contexts_pattern = simple_pattern_create(silencer->contexts, NULL, SIMPLE_PATTERN_EXACT, true);
90 - } else if (hash == hash_host && !strcasecmp(key, HEALTH_HOST_KEY)) {
91 - silencer->hosts = strdupz(value);
92 - silencer->hosts_pattern = simple_pattern_create(silencer->hosts, NULL, SIMPLE_PATTERN_EXACT, true);
93 - }
94 -
95 - return silencer;
96 -}
97 -
98 -/**
99 - * JSON Read Callback
100 - *
101 - * Callback called by netdata to create the silencer.
102 - *
103 - * @param e the main json structure
104 - *
105 - * @return It always return 0.
106 - */
107 -int health_silencers_json_read_callback(JSON_ENTRY *e)
108 -{
109 - switch(e->type) {
110 - case JSON_OBJECT:
111 -#ifndef ENABLE_JSONC
112 - e->callback_function = health_silencers_json_read_callback;
113 - if(strcmp(e->name,"")) {
114 - // init silencer
115 - netdata_log_debug(D_HEALTH, "JSON: Got object with a name, initializing new silencer for %s",e->name);
116 -#endif
117 - e->callback_data = create_silencer();
118 - if(e->callback_data) {
119 - health_silencers_add(e->callback_data);
120 - }
121 -#ifndef ENABLE_JSONC
122 - }
123 -#endif
124 - break;
125 -
126 - case JSON_ARRAY:
127 - e->callback_function = health_silencers_json_read_callback;
128 - break;
129 -
130 - case JSON_STRING:
131 - if(!strcmp(e->name,"type")) {
132 - netdata_log_debug(D_HEALTH, "JSON: Processing type=%s",e->data.string);
133 - if (!strcmp(e->data.string,"SILENCE")) silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
134 - else if (!strcmp(e->data.string,"DISABLE")) silencers->stype = STYPE_DISABLE_ALARMS;
135 - } else {
136 - netdata_log_debug(D_HEALTH, "JSON: Adding %s=%s", e->name, e->data.string);
137 - if (e->callback_data)
138 - (void)health_silencers_addparam(e->callback_data, e->name, e->data.string);
139 - }
140 - break;
141 -
142 - case JSON_BOOLEAN:
143 - netdata_log_debug(D_HEALTH, "JSON: Processing all_alarms");
144 - silencers->all_alarms=e->data.boolean?1:0;
145 - break;
146 -
147 - case JSON_NUMBER:
148 - case JSON_NULL:
149 - break;
150 - }
151 -
152 - return 0;
153 -}
154 -
155 -/**
156 - * Initialize Global Silencers
157 - *
158 - * Initialize the silencer for the whole netdata system.
159 - *
160 - * @return It returns 0 on success and -1 otherwise
161 - */
162 -int health_initialize_global_silencers() {
163 - silencers = mallocz(sizeof(SILENCERS));
164 - silencers->all_alarms = 0;
165 - silencers->stype = STYPE_NONE;
166 - silencers->silencers = NULL;
167 -
168 - return 0;
169 -}
libnetdata/inlined.h
+10
@@ -633,4 +633,14 @@ static inline bool strendswith(const char *string, const char *suffix) {
633 return strcmp(string + string_len - suffix_len, suffix) == 0;
634 }
635
636 +static inline bool strendswith_lengths(const char *string, size_t string_len, const char *suffix, size_t suffix_len) {
637 + if (string == NULL || suffix == NULL)
638 + return false;
639 +
640 + if (suffix_len > string_len)
641 + return false;
642 +
643 + return strcmp(string + string_len - suffix_len, suffix) == 0;
644 +}
645 +
646 #endif //NETDATA_INLINED_H
libnetdata/json/json-c-parser-inline.h new
+154
@@ -0,0 +1,154 @@
1 +// SPDX-License-Identifier: GPL-3.0-or-later
2 +
3 +#ifndef NETDATA_JSON_C_PARSER_INLINE_H
4 +#define NETDATA_JSON_C_PARSER_INLINE_H
5 +
6 +#define JSONC_PARSE_BOOL_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
7 + json_object *_j; \
8 + if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_boolean)) \
9 + dst = json_object_get_boolean(_j); \
10 + else { \
11 + buffer_sprintf(error, "missing or invalid type for '%s.%s' boolean", path, member); \
12 + return false; \
13 + } \
14 +} while(0)
15 +
16 +#define JSONC_PARSE_TXT2STRING_OR_ERROR_AND_RETURN(jobj, path, member, dst, error, required) do { \
17 + json_object *_j; \
18 + if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
19 + string_freez(dst); \
20 + dst = string_strdupz(json_object_get_string(_j)); \
21 + } \
22 + else if(required) { \
23 + buffer_sprintf(error, "missing or invalid type for '%s.%s' string", path, member); \
24 + return false; \
25 + } \
26 +} while(0)
27 +
28 +#define JSONC_PARSE_TXT2PATTERN_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
29 + json_object *_j; \
30 + if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
31 + string_freez(dst); \
32 + const char *_v = json_object_get_string(_j); \
33 + if(strcmp(_v, "*") == 0) \
34 + dst = NULL; \
35 + else \
36 + dst = string_strdupz(_v); \
37 + } \
38 + else { \
39 + buffer_sprintf(error, "missing or invalid type for '%s.%s' string", path, member); \
40 + return false; \
41 + } \
42 +} while(0)
43 +
44 +#define JSONC_PARSE_TXT2EXPRESSION_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
45 + json_object *_j; \
46 + if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) { \
47 + const char *_t = json_object_get_string(_j); \
48 + if(_t && *_t && strcmp(_t, "*") != 0) { \
49 + const char *_failed_at = NULL; \
50 + int _err = 0; \
51 + expression_free(dst); \
52 + dst = expression_parse(_t, &_failed_at, &_err); \
53 + if(!dst) { \
54 + buffer_sprintf(error, "expression '%s.%s' has a non-parseable expression '%s': %s at '%s'", \
55 + path, member, _t, expression_strerror(_err), _failed_at); \
56 + return false; \
57 + } \
58 + } \
59 + } \
60 + else { \
61 + buffer_sprintf(error, "missing or invalid type for '%s.%s' expression", path, member); \
62 + return false; \
63 + } \
64 +} while(0)
65 +
66 +#define JSONC_PARSE_ARRAY_OF_TXT2BITMAP_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error) do { \
67 + json_object *_jarray; \
68 + if (json_object_object_get_ex(jobj, member, &_jarray) && json_object_is_type(_jarray, json_type_array)) { \
69 + size_t _num_options = json_object_array_length(_jarray); \
70 + dst = 0; \
71 + for (size_t _i = 0; _i < _num_options; ++_i) { \
72 + json_object *_joption = json_object_array_get_idx(_jarray, _i); \
73 + if (!json_object_is_type(_joption, json_type_string)) { \
74 + buffer_sprintf(error, "invalid type for '%s.%s' at index %zu", path, member, _i); \
75 + return false; \
76 + } \
77 + const char *_option_str = json_object_get_string(_joption); \
78 + typeof(dst) _bit = converter(_option_str); \
79 + if (_bit == 0) { \
80 + buffer_sprintf(error, "unknown option '%s' in '%s.%s' at index %zu", _option_str, path, member, _i); \
81 + return false; \
82 + } \
83 + dst |= _bit; \
84 + } \
85 + } else { \
86 + buffer_sprintf(error, "missing or invalid type for '%s.%s' array", path, member); \
87 + return false; \
88 + } \
89 +} while(0)
90 +
91 +
92 +#define JSONC_PARSE_TXT2ENUM_OR_ERROR_AND_RETURN(jobj, path, member, converter, dst, error) do { \
93 + json_object *_j; \
94 + if (json_object_object_get_ex(jobj, member, &_j) && json_object_is_type(_j, json_type_string)) \
95 + dst = converter(json_object_get_string(_j)); \
96 + else { \
97 + buffer_sprintf(error, "missing or invalid type (expected text value) for '%s.%s' enum", path, member); \
98 + return false; \
99 + } \
100 +} while(0)
101 +
102 +#define JSONC_PARSE_INT_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
103 + json_object *_j; \
104 + if (json_object_object_get_ex(jobj, member, &_j)) { \
105 + if (_j != NULL && json_object_is_type(_j, json_type_int)) \
106 + dst = json_object_get_int(_j); \
107 + else if (_j != NULL && json_object_is_type(_j, json_type_double)) \
108 + dst = (typeof(dst))json_object_get_double(_j); \
109 + else if (_j == NULL) \
110 + dst = 0; \
111 + else { \
112 + buffer_sprintf(error, "not supported type (expected int) for '%s.%s'", path, member); \
113 + return false; \
114 + } \
115 + } else { \
116 + buffer_sprintf(error, "missing or invalid type (expected double value or null) for '%s.%s'", path, member); \
117 + return false; \
118 + } \
119 +} while(0)
120 +
121 +#define JSONC_PARSE_DOUBLE_OR_ERROR_AND_RETURN(jobj, path, member, dst, error) do { \
122 + json_object *_j; \
123 + if (json_object_object_get_ex(jobj, member, &_j)) { \
124 + if (_j != NULL && json_object_is_type(_j, json_type_double)) \
125 + dst = json_object_get_double(_j); \
126 + else if (_j != NULL && json_object_is_type(_j, json_type_int)) \
127 + dst = (typeof(dst))json_object_get_int(_j); \
128 + else if (_j == NULL) \
129 + dst = NAN; \
130 + else { \
131 + buffer_sprintf(error, "not supported type (expected double) for '%s.%s'", path, member); \
132 + return false; \
133 + } \
134 + } else { \
135 + buffer_sprintf(error, "missing or invalid type (expected double value or null) for '%s.%s'", path, member); \
136 + return false; \
137 + } \
138 +} while(0)
139 +
140 +#define JSONC_PARSE_SUBOBJECT(jobj, path, member, dst, callback, error) do { \
141 + json_object *_j; \
142 + if (json_object_object_get_ex(jobj, member, &_j)) { \
143 + char _new_path[strlen(path) + strlen(member) + 2]; \
144 + snprintfz(_new_path, sizeof(_new_path), "%s%s%s", path, *path?".":"", member); \
145 + if (!callback(_j, _new_path, dst, error)) { \
146 + return false; \
147 + } \
148 + } else { \
149 + buffer_sprintf(error, "missing '%s.%s' object", path, member); \
150 + return false; \
151 + } \
152 +} while(0)
153 +
154 +#endif //NETDATA_JSON_C_PARSER_INLINE_H
libnetdata/libnetdata.c
+11 -8
@@ -1503,12 +1503,15 @@ int path_is_file(const char *path, const char *subpath) {
1503 return is_file;
1504 }
1505
1506 -void recursive_config_double_dir_load(const char *user_path, const char *stock_path, const char *subpath, int (*callback)(const char *filename, void *data), void *data, size_t depth) {
1506 +void recursive_config_double_dir_load(const char *user_path, const char *stock_path, const char *subpath, int (*callback)(const char *filename, void *data, bool stock_config), void *data, size_t depth) {
1507 if(depth > 3) {
1508 netdata_log_error("CONFIG: Max directory depth reached while reading user path '%s', stock path '%s', subpath '%s'", user_path, stock_path, subpath);
1509 return;
1510 }
1511
1512 + if(!stock_path)
1513 + stock_path = user_path;
1514 +
1515 char *udir = strdupz_path_subpath(user_path, subpath);
1516 char *sdir = strdupz_path_subpath(stock_path, subpath);
1517
@@ -1542,7 +1545,7 @@ void recursive_config_double_dir_load(const char *user_path, const char *stock_p
1545 len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
1546 char *filename = strdupz_path_subpath(udir, de->d_name);
1547 netdata_log_debug(D_HEALTH, "CONFIG calling callback for user file '%s'", filename);
1545 - callback(filename, data);
1548 + callback(filename, data, false);
1549 freez(filename);
1550 continue;
1551 }
@@ -1590,7 +1593,7 @@ void recursive_config_double_dir_load(const char *user_path, const char *stock_p
1593 len > 5 && !strcmp(&de->d_name[len - 5], ".conf")) {
1594 char *filename = strdupz_path_subpath(sdir, de->d_name);
1595 netdata_log_debug(D_HEALTH, "CONFIG calling callback for stock file '%s'", filename);
1593 - callback(filename, data);
1596 + callback(filename, data, true);
1597 freez(filename);
1598 continue;
1599 }
@@ -1997,7 +2000,7 @@ bool rrdr_relative_window_to_absolute(time_t *after, time_t *before, time_t now)
2000 }
2001
2002 // Returns 1 if an absolute period was requested or 0 if it was a relative period
2000 -bool rrdr_relative_window_to_absolute_query(time_t *after, time_t *before, time_t *now_ptr, bool unittest_running) {
2003 +bool rrdr_relative_window_to_absolute_query(time_t *after, time_t *before, time_t *now_ptr, bool unittest) {
2004 time_t now = now_realtime_sec() - 1;
2005
2006 if(now_ptr)
@@ -2011,16 +2014,16 @@ bool rrdr_relative_window_to_absolute_query(time_t *after, time_t *before, time_
2014 time_t absolute_minimum_time = now - (10 * 365 * 86400);
2015 time_t absolute_maximum_time = now + (1 * 365 * 86400);
2016
2014 - if (after_requested < absolute_minimum_time && !unittest_running)
2017 + if (after_requested < absolute_minimum_time && !unittest)
2018 after_requested = absolute_minimum_time;
2019
2017 - if (after_requested > absolute_maximum_time && !unittest_running)
2020 + if (after_requested > absolute_maximum_time && !unittest)
2021 after_requested = absolute_maximum_time;
2022
2020 - if (before_requested < absolute_minimum_time && !unittest_running)
2023 + if (before_requested < absolute_minimum_time && !unittest)
2024 before_requested = absolute_minimum_time;
2025
2023 - if (before_requested > absolute_maximum_time && !unittest_running)
2026 + if (before_requested > absolute_maximum_time && !unittest)
2027 before_requested = absolute_maximum_time;
2028
2029 *before = before_requested;
libnetdata/libnetdata.h
+3 -3
@@ -572,7 +572,7 @@ void recursive_config_double_dir_load(
572 const char *user_path
573 , const char *stock_path
574 , const char *subpath
575 - , int (*callback)(const char *filename, void *data)
575 + , int (*callback)(const char *filename, void *data, bool stock_config)
576 , void *data
577 , size_t depth
578 );
@@ -744,7 +744,7 @@ extern char *netdata_configured_host_prefix;
744 #include "adaptive_resortable_list/adaptive_resortable_list.h"
745 #include "url/url.h"
746 #include "json/json.h"
747 -#include "health/health.h"
747 +#include "json/json-c-parser-inline.h"
748 #include "string/utf8.h"
749 #include "libnetdata/aral/aral.h"
750 #include "onewayalloc/onewayalloc.h"
@@ -902,7 +902,7 @@ extern bool unittest_running;
902 #define API_RELATIVE_TIME_MAX (3 * 365 * 86400)
903
904 bool rrdr_relative_window_to_absolute(time_t *after, time_t *before, time_t now);
905 -bool rrdr_relative_window_to_absolute_query(time_t *after, time_t *before, time_t *now_ptr, bool unittest_running);
905 +bool rrdr_relative_window_to_absolute_query(time_t *after, time_t *before, time_t *now_ptr, bool unittest);
906
907 int netdata_base64_decode(const char *encoded, char *decoded, size_t decoded_size);
908
libnetdata/line_splitter/line_splitter.c
+9
@@ -56,14 +56,23 @@ inline int group_by_label_isspace(char c) {
56 return 0;
57 }
58
59 +inline int dyncfg_id_isspace(char c) {
60 + if(c == ':')
61 + return 1;
62 +
63 + return 0;
64 +}
65 +
66 bool isspace_map_pluginsd[256] = {};
67 bool isspace_map_config[256] = {};
68 bool isspace_map_group_by_label[256] = {};
69 +bool isspace_dyncfg_id_map[256] = {};
70
71 __attribute__((constructor)) void initialize_is_space_arrays(void) {
72 for(int c = 0; c < 256 ; c++) {
73 isspace_map_pluginsd[c] = pluginsd_isspace((char) c);
74 isspace_map_config[c] = config_isspace((char) c);
75 isspace_map_group_by_label[c] = group_by_label_isspace((char) c);
76 + isspace_dyncfg_id_map[c] = dyncfg_id_isspace((char)c);
77 }
78 }
libnetdata/line_splitter/line_splitter.h
+5
@@ -22,10 +22,12 @@ static inline void line_splitter_reset(struct line_splitter *line) {
22 int pluginsd_isspace(char c);
23 int config_isspace(char c);
24 int group_by_label_isspace(char c);
25 +int dyncfg_id_isspace(char c);
26
27 extern bool isspace_map_pluginsd[256];
28 extern bool isspace_map_config[256];
29 extern bool isspace_map_group_by_label[256];
30 +extern bool isspace_dyncfg_id_map[256];
31
32 static inline size_t quoted_strings_splitter(char *str, char **words, size_t max_words, bool *isspace_map) {
33 char *s = str, quote = 0;
@@ -110,6 +112,9 @@ static inline size_t quoted_strings_splitter(char *str, char **words, size_t max
112 #define quoted_strings_splitter_pluginsd(str, words, max_words) \
113 quoted_strings_splitter(str, words, max_words, isspace_map_pluginsd)
114
115 +#define quoted_strings_splitter_dyncfg_id(str, words, max_words) \
116 + quoted_strings_splitter(str, words, max_words, isspace_dyncfg_id_map)
117 +
118 static inline char *get_word(char **words, size_t num_words, size_t index) {
119 if (unlikely(index >= num_words))
120 return NULL;
libnetdata/required_dummies.h
-11
@@ -18,17 +18,6 @@ void signals_block(void){}
18 void signals_unblock(void){}
19 void signals_reset(void){}
20
21 -#ifndef UNIT_TESTING
22 -// callback required by eval()
23 -int health_variable_lookup(STRING *variable, struct rrdcalc *rc, NETDATA_DOUBLE *result)
24 -{
25 - (void)variable;
26 - (void)rc;
27 - (void)result;
28 - return 0;
29 -}
30 -#endif
31 -
21 void rrdset_thread_rda_free(void){}
22 void sender_thread_buffer_free(void){}
23 void query_target_free(void){}
libnetdata/simple_pattern/simple_pattern.c
+1 -1
@@ -396,7 +396,7 @@ extern int simple_pattern_is_potential_name(SIMPLE_PATTERN *p)
396 return (alpha || wildcards) && !colon;
397 }
398
399 -char *simple_pattern_trim_around_equal(char *src) {
399 +char *simple_pattern_trim_around_equal(const char *src) {
400 char *store = mallocz(strlen(src) + 1);
401
402 char *dst = store;
libnetdata/simple_pattern/simple_pattern.h
+1 -1
@@ -48,7 +48,7 @@ int simple_pattern_is_potential_name(SIMPLE_PATTERN *p) ;
48 char *simple_pattern_iterate(SIMPLE_PATTERN **p);
49
50 // Auxiliary function to create a pattern
51 -char *simple_pattern_trim_around_equal(char *src);
51 +char *simple_pattern_trim_around_equal(const char *src);
52
53 #define SIMPLE_PATTERN_DEFAULT_WEB_SEPARATORS ",|\t\r\n\f\v"
54
libnetdata/uuid/uuid.c
+20
@@ -2,6 +2,26 @@
2
3 #include "../libnetdata.h"
4
5 +
6 +UUID UUID_generate_from_hash(const void *payload, size_t payload_len) {
7 + assert(sizeof(XXH128_hash_t) == sizeof(UUID));
8 +
9 + UUID uuid;
10 + XXH128_hash_t *xxh3_128 = (XXH128_hash_t *)&uuid;
11 +
12 + // Hash the payload using XXH128
13 + // Assume xxh128_hash_function is your function to generate XXH128 hash
14 + *xxh3_128 = XXH3_128bits(payload, payload_len);
15 +
16 + // Set the UUID version (here, setting it to 4)
17 + uuid.uuid[6] = (uuid.uuid[6] & 0x0F) | 0x40; // Version 4
18 +
19 + // Set the UUID variant (standard variant for UUID)
20 + uuid.uuid[8] = (uuid.uuid[8] & 0x3F) | 0x80; // Variant is 10xxxxxx
21 +
22 + return uuid;
23 +}
24 +
25 void uuid_unparse_lower_compact(const uuid_t uuid, char *out) {
26 static const char *hex_chars = "0123456789abcdef";
27 for (int i = 0; i < 16; i++) {
libnetdata/uuid/uuid.h
+18
@@ -10,6 +10,24 @@ UUID_DEFINE(health_alert_transition_msgid, 0x9c, 0xe0, 0xcb, 0x58, 0xab, 0x8b, 0
10 // this is also defined in alarm-notify.sh.in
11 UUID_DEFINE(health_alert_notification_msgid, 0x6d, 0xb0, 0x01, 0x8e, 0x83, 0xe3, 0x43, 0x20, 0xae, 0x2a, 0x65, 0x9d, 0x78, 0x01, 0x9f, 0xb7);
12
13 +typedef struct {
14 + union {
15 + uuid_t uuid;
16 + struct {
17 + uint64_t hig64;
18 + uint64_t low64;
19 + } parts;
20 + };
21 +} UUID;
22 +UUID UUID_generate_from_hash(const void *payload, size_t payload_len);
23 +
24 +#define UUIDeq(a, b) ((a).parts.hig64 == (b).parts.hig64 && (a).parts.low64 == (b).parts.low64)
25 +
26 +static inline UUID uuid2UUID(uuid_t uu1) {
27 + UUID *ret = (UUID *)uu1;
28 + return *ret;
29 +}
30 +
31 #define UUID_COMPACT_STR_LEN 33
32 void uuid_unparse_lower_compact(const uuid_t uuid, char *out);
33 int uuid_parse_compact(const char *in, uuid_t uuid);
logsmanagement/db_api.c
+5 -5
@@ -754,9 +754,9 @@ int db_init() {
754 -1, &stmt_init_BLOBS_table, NULL);
755 do_sqlite_error_check(p_file_info, rc, SQLITE_OK);
756
757 - for(int i = 0; i < BLOB_MAX_FILES; i++){
757 + for(int t = 0; t < BLOB_MAX_FILES; t++){
758 char filename[FILENAME_MAX + 1];
759 - snprintfz(filename, FILENAME_MAX, BLOB_STORE_FILENAME "%d", i);
759 + snprintfz(filename, FILENAME_MAX, BLOB_STORE_FILENAME "%d", t);
760 if(unlikely(
761 SQLITE_OK != (rc = sqlite3_bind_text(stmt_init_BLOBS_table, 1, filename, -1, NULL)) ||
762 SQLITE_OK != (rc = sqlite3_bind_int64(stmt_init_BLOBS_table, 2, (sqlite3_int64) 0)) ||
@@ -919,10 +919,10 @@ int db_init() {
919 -1, &stmt_update_id, NULL);
920 do_sqlite_error_check(p_file_info, rc, SQLITE_OK);
921
922 - for (int i = 0; i < BLOB_MAX_FILES; i++){
922 + for (int t = 0; t < BLOB_MAX_FILES; t++){
923 if(unlikely(
924 - SQLITE_OK != (rc = sqlite3_bind_int(stmt_update_id, 1, i + 1)) ||
925 - SQLITE_OK != (rc = sqlite3_bind_int(stmt_update_id, 2, old_blobs_table_ids[i])) ||
924 + SQLITE_OK != (rc = sqlite3_bind_int(stmt_update_id, 1, t + 1)) ||
925 + SQLITE_OK != (rc = sqlite3_bind_int(stmt_update_id, 2, old_blobs_table_ids[t])) ||
926 SQLITE_DONE != (rc = sqlite3_step(stmt_update_id)) ||
927 SQLITE_OK != (rc = sqlite3_reset(stmt_update_id)))) {
928 throw_error(p_file_info->chartname, ERR_TYPE_SQLITE, rc, __LINE__, __FILE__, __FUNCTION__);
logsmanagement/flb_plugin.c
+3 -3
@@ -475,9 +475,9 @@ static int flb_collect_logs_cb(void *record, size_t size, void *data){
475 case FLB_KMSG:
476 {
477 if(unlikely(skip_kmsg_log_buffering)){
478 - static time_t netdata_start_time = 0;
479 - if (!netdata_start_time) netdata_start_time = now_boottime_sec();
480 - if(now_boottime_sec() - netdata_start_time < KERNEL_LOGS_COLLECT_INIT_WAIT)
478 + static time_t start_time = 0;
479 + if (!start_time) start_time = now_boottime_sec();
480 + if(now_boottime_sec() - start_time < KERNEL_LOGS_COLLECT_INIT_WAIT)
481 goto skip_collect_and_drop_logs;
482 else skip_kmsg_log_buffering = 0;
483 }
ml/ml.cc
+1 -2
@@ -1416,8 +1416,7 @@ void ml_host_get_info(RRDHOST *rh, BUFFER *wb)
1416
1417 buffer_json_member_add_double(wb, "dimension-anomaly-score-threshold", Cfg.dimension_anomaly_score_threshold);
1418
1419 - buffer_json_member_add_string(wb, "anomaly-detection-grouping-method",
1420 - time_grouping_method2string(Cfg.anomaly_detection_grouping_method));
1419 + buffer_json_member_add_string(wb, "anomaly-detection-grouping-method", time_grouping_id2txt(Cfg.anomaly_detection_grouping_method));
1420
1421 buffer_json_member_add_int64(wb, "anomaly-detection-query-duration", Cfg.anomaly_detection_query_duration);
1422
streaming/receiver.c
+1 -1
@@ -551,7 +551,7 @@ static void rrdpush_receive(struct receiver_state *rpt)
551 rpt->config.mode = default_rrd_memory_mode;
552 rpt->config.history = default_rrd_history_entries;
553
554 - rpt->config.health_enabled = (int)default_health_enabled;
554 + rpt->config.health_enabled = health_plugin_enabled();
555 rpt->config.alarms_delay = 60;
556 rpt->config.alarms_history = HEALTH_LOG_DEFAULT_HISTORY;
557
streaming/rrdpush.c
+3 -3
@@ -305,7 +305,7 @@ static inline bool rrdpush_send_chart_definition(BUFFER *wb, RRDSET *st) {
305 rrd_chart_functions_expose_rrdpush(st, wb);
306
307 // send the chart local custom variables
308 - rrdsetvar_print_to_streaming_custom_chart_variables(st, wb);
308 + rrdvar_print_to_streaming_custom_chart_variables(st, wb);
309
310 if (stream_has_capability(host->sender, STREAM_CAP_REPLICATION)) {
311 time_t db_first_time_t, db_last_time_t;
@@ -380,7 +380,7 @@ static void rrdpush_send_chart_metrics(BUFFER *wb, RRDSET *st, struct sender_sta
380 rrddim_foreach_done(rd);
381
382 if(unlikely(flags & RRDSET_FLAG_UPSTREAM_SEND_VARIABLES))
383 - rrdsetvar_print_to_streaming_custom_chart_variables(st, wb);
383 + rrdvar_print_to_streaming_custom_chart_variables(st, wb);
384
385 buffer_fast_strcat(wb, "END\n", 4);
386 }
@@ -475,7 +475,7 @@ void rrdset_push_metrics_finished(RRDSET_STREAM_BUFFER *rsb, RRDSET *st) {
475
476 if(rsb->v2 && rsb->begin_v2_added) {
477 if(unlikely(rsb->rrdset_flags & RRDSET_FLAG_UPSTREAM_SEND_VARIABLES))
478 - rrdsetvar_print_to_streaming_custom_chart_variables(st, rsb->wb);
478 + rrdvar_print_to_streaming_custom_chart_variables(st, rsb->wb);
479
480 buffer_fast_strcat(rsb->wb, PLUGINSD_KEYWORD_END_V2 "\n", sizeof(PLUGINSD_KEYWORD_END_V2) - 1 + 1);
481 }
streaming/sender.c
+2 -5
@@ -234,11 +234,8 @@ static int rrdpush_sender_thread_custom_host_variables_callback(const DICTIONARY
234 struct custom_host_variables_callback *tmp = struct_ptr;
235 BUFFER *wb = tmp->wb;
236
237 - if(unlikely(rrdvar_flags(rv) & RRDVAR_FLAG_CUSTOM_HOST_VAR && rrdvar_type(rv) == RRDVAR_TYPE_CALCULATED)) {
238 - rrdpush_sender_add_host_variable_to_buffer(wb, rv);
239 - return 1;
240 - }
241 - return 0;
237 + rrdpush_sender_add_host_variable_to_buffer(wb, rv);
238 + return 1;
239 }
240
241 static void rrdpush_sender_thread_send_custom_host_variables(RRDHOST *host) {
tests/profile/test-eval.c
+1 -1
@@ -10,7 +10,7 @@
10 #include "config.h"
11 #include "libnetdata/libnetdata.h"
12 #include "libnetdata/required_dummies.h"
13 -#include "database/rrdcalc.h"
13 +#include "health/rrdcalc.h"
14
15 /*
16 void indent(int level, int show) {
web/api/badges/web_buffer_svg.c
+3 -3
@@ -926,7 +926,7 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
926 group = time_grouping_parse(value, RRDR_GROUPING_AVERAGE);
927 }
928 else if(!strcmp(name, "options")) {
929 - options |= web_client_api_request_v1_data_options(value);
929 + options |= rrdr_options_parse(value);
930 }
931 else if(!strcmp(name, "label")) label = value;
932 else if(!strcmp(name, "units")) units = value;
@@ -996,7 +996,7 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
996 int refresh = 0;
997 if(refresh_str && *refresh_str) {
998 if(!strcmp(refresh_str, "auto")) {
999 - if(rc) refresh = rc->update_every;
999 + if(rc) refresh = rc->config.update_every;
1000 else if(options & RRDR_OPTION_NOT_ALIGNED)
1001 refresh = st->update_every;
1002 else {
@@ -1029,7 +1029,7 @@ int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *u
1029 }
1030 if(!units) {
1031 if(alarm) {
1032 - if(rc->units)
1032 + if(rc->config.units)
1033 units = rrdcalc_units(rc);
1034 else
1035 units = "";
web/api/formatters/json_wrapper.c
+12 -12
@@ -435,23 +435,23 @@ static void query_target_summary_dimensions_v12(BUFFER *wb, QUERY_TARGET *qt, co
435 qm = tqm;
436 }
437
438 - const char *key, *id, *name;
438 + const char *k, *id, *name;
439
440 if(v2) {
441 - key = rrdmetric_acquired_name(rma);
442 - id = key;
443 - name = key;
441 + k = rrdmetric_acquired_name(rma);
442 + id = k;
443 + name = k;
444 }
445 else {
446 snprintfz(buf, RRD_ID_LENGTH_MAX * 2 + 1, "%s:%s",
447 rrdmetric_acquired_id(rma),
448 rrdmetric_acquired_name(rma));
449 - key = buf;
449 + k = buf;
450 id = rrdmetric_acquired_id(rma);
451 name = rrdmetric_acquired_name(rma);
452 }
453
454 - z = dictionary_set(dict, key, NULL, sizeof(*z));
454 + z = dictionary_set(dict, k, NULL, sizeof(*z));
455 if(!z->id) {
456 z->id = id;
457 z->name = name;
@@ -618,7 +618,7 @@ static void query_target_summary_alerts_v2(BUFFER *wb, QUERY_TARGET *qt, const c
618 rw_spinlock_read_lock(&st->alerts.spinlock);
619 if (st->alerts.base) {
620 for (RRDCALC *rc = st->alerts.base; rc; rc = rc->next) {
621 - z = dictionary_set(dict, string2str(rc->name), NULL, sizeof(*z));
621 + z = dictionary_set(dict, string2str(rc->config.name), NULL, sizeof(*z));
622
623 switch(rc->status) {
624 case RRDCALC_STATUS_CLEAR:
@@ -887,7 +887,7 @@ void rrdr_json_wrapper_begin(RRDR *r, BUFFER *wb) {
887 buffer_json_member_add_time_t(wb, "after", r->view.after);
888 buffer_json_member_add_time_t(wb, "before", r->view.before);
889 buffer_json_member_add_string(wb, "group", time_grouping_tostring(qt->request.time_group_method));
890 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", options);
890 + rrdr_options_to_buffer_json_array(wb, "options", options);
891
892 if(!rrdr_dimension_names(wb, "dimension_names", r, options))
893 rows = 0;
@@ -939,10 +939,10 @@ static void rrdset_rrdcalc_entries_v2(BUFFER *wb, RRDINSTANCE_ACQUIRED *ria) {
939 if(rc->status < RRDCALC_STATUS_CLEAR)
940 continue;
941
942 - buffer_json_member_add_object(wb, string2str(rc->name));
942 + buffer_json_member_add_object(wb, string2str(rc->config.name));
943 buffer_json_member_add_string(wb, "st", rrdcalc_status2string(rc->status));
944 buffer_json_member_add_double(wb, "vl", rc->value);
945 - buffer_json_member_add_string(wb, "un", string2str(rc->units));
945 + buffer_json_member_add_string(wb, "un", string2str(rc->config.units));
946 buffer_json_object_close(wb);
947 }
948 buffer_json_object_close(wb);
@@ -1299,7 +1299,7 @@ void rrdr_json_wrapper_begin2(RRDR *r, BUFFER *wb) {
1299 buffer_json_member_add_object(wb, "request");
1300 {
1301 buffer_json_member_add_string(wb, "format", rrdr_format_to_string(qt->request.format));
1302 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", qt->request.options);
1302 + rrdr_options_to_buffer_json_array(wb, "options", qt->request.options);
1303
1304 buffer_json_member_add_object(wb, "scope");
1305 buffer_json_member_add_string(wb, "scope_nodes", qt->request.scope_nodes);
@@ -1538,7 +1538,7 @@ void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
1538
1539 if(options & RRDR_OPTION_DEBUG) {
1540 buffer_json_member_add_string(wb, "format", rrdr_format_to_string(format));
1541 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", options);
1541 + rrdr_options_to_buffer_json_array(wb, "options", options);
1542 buffer_json_member_add_string(wb, "time_group", time_grouping_tostring(qt->request.time_group_method));
1543 }
1544
web/api/formatters/rrdset2json.c
+1 -1
@@ -88,7 +88,7 @@ void rrdset2json(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memor
88 buffer_json_member_add_string_or_empty(wb, "id", rrdcalc_name(rc));
89 buffer_json_member_add_string_or_empty(wb, "status", rrdcalc_status2string(rc->status));
90 buffer_json_member_add_string_or_empty(wb, "units", rrdcalc_units(rc));
91 - buffer_json_member_add_int64(wb, "duration", (int64_t)rc->update_every);
91 + buffer_json_member_add_int64(wb, "duration", (int64_t)rc->config.update_every);
92 buffer_json_object_close(wb);
93 }
94 }
web/api/health/health_cmdapi.c deleted
-204
@@ -1,204 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -//
3 -// Created by Christopher on 11/12/18.
4 -//
5 -
6 -#include "health_cmdapi.h"
7 -
8 -/**
9 - * Free Silencers
10 - *
11 - * Clean the silencer structure
12 - *
13 - * @param t is the structure that will be cleaned.
14 - */
15 -void free_silencers(SILENCER *t) {
16 - if (!t) return;
17 - if (t->next) free_silencers(t->next);
18 - netdata_log_debug(D_HEALTH, "HEALTH command API: Freeing silencer %s:%s:%s:%s", t->alarms,
19 - t->charts, t->contexts, t->hosts);
20 - simple_pattern_free(t->alarms_pattern);
21 - simple_pattern_free(t->charts_pattern);
22 - simple_pattern_free(t->contexts_pattern);
23 - simple_pattern_free(t->hosts_pattern);
24 - freez(t->alarms);
25 - freez(t->charts);
26 - freez(t->contexts);
27 - freez(t->hosts);
28 - freez(t);
29 - return;
30 -}
31 -
32 -/**
33 - * Silencers to JSON Entry
34 - *
35 - * Fill the buffer with the other values given.
36 - *
37 - * @param wb a pointer to the output buffer
38 - * @param var the json variable
39 - * @param val the json value
40 - * @param hasprev has it a previous value?
41 - *
42 - * @return
43 - */
44 -int health_silencers2json_entry(BUFFER *wb, char* var, char* val, int hasprev) {
45 - if (val) {
46 - buffer_sprintf(wb, "%s\n\t\t\t\"%s\": \"%s\"", (hasprev)?",":"", var, val);
47 - return 1;
48 - } else {
49 - return hasprev;
50 - }
51 -}
52 -
53 -/**
54 - * Silencer to JSON
55 - *
56 - * Write the silencer values using JSON format inside a buffer.
57 - *
58 - * @param wb is the buffer to write the silencers.
59 - */
60 -void health_silencers2json(BUFFER *wb) {
61 - buffer_sprintf(wb, "{\n\t\"all\": %s,"
62 - "\n\t\"type\": \"%s\","
63 - "\n\t\"silencers\": [",
64 - (silencers->all_alarms)?"true":"false",
65 - (silencers->stype == STYPE_NONE)?"None":((silencers->stype == STYPE_DISABLE_ALARMS)?"DISABLE":"SILENCE"));
66 -
67 - SILENCER *silencer;
68 - int i = 0, j = 0;
69 - for(silencer = silencers->silencers; silencer ; silencer = silencer->next) {
70 - if(likely(i)) buffer_strcat(wb, ",");
71 - buffer_strcat(wb, "\n\t\t{");
72 - j=health_silencers2json_entry(wb, HEALTH_ALARM_KEY, silencer->alarms, j);
73 - j=health_silencers2json_entry(wb, HEALTH_CHART_KEY, silencer->charts, j);
74 - j=health_silencers2json_entry(wb, HEALTH_CONTEXT_KEY, silencer->contexts, j);
75 - j=health_silencers2json_entry(wb, HEALTH_HOST_KEY, silencer->hosts, j);
76 - j=0;
77 - buffer_strcat(wb, "\n\t\t}");
78 - i++;
79 - }
80 - if(likely(i)) buffer_strcat(wb, "\n\t");
81 - buffer_strcat(wb, "]\n}\n");
82 -}
83 -
84 -/**
85 - * Silencer to FILE
86 - *
87 - * Write the silencer buffer to a file.
88 - * @param wb
89 - */
90 -void health_silencers2file(BUFFER *wb) {
91 - if (wb->len == 0) return;
92 -
93 - FILE *fd = fopen(silencers_filename, "wb");
94 - if(fd) {
95 - size_t written = (size_t)fprintf(fd, "%s", wb->buffer) ;
96 - if (written == wb->len ) {
97 - netdata_log_info("Silencer changes written to %s", silencers_filename);
98 - }
99 - fclose(fd);
100 - return;
101 - }
102 - netdata_log_error("Silencer changes could not be written to %s. Error %s", silencers_filename, strerror(errno));
103 -}
104 -
105 -/**
106 - * Request V1 MGMT Health
107 - *
108 - * Function called by api to management the health.
109 - *
110 - * @param host main structure with client information!
111 - * @param w is the structure with all information of the client request.
112 - * @param url is the url that netdata is working
113 - *
114 - * @return It returns 200 on success and another code otherwise.
115 - */
116 -int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url) {
117 - int ret;
118 - (void) host;
119 -
120 - BUFFER *wb = w->response.data;
121 - buffer_flush(wb);
122 - wb->content_type = CT_TEXT_PLAIN;
123 -
124 - buffer_flush(w->response.data);
125 -
126 - //Local instance of the silencer
127 - SILENCER *silencer = NULL;
128 - int config_changed = 1;
129 -
130 - if (!w->auth_bearer_token) {
131 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
132 - ret = HTTP_RESP_FORBIDDEN;
133 - } else {
134 - netdata_log_debug(D_HEALTH, "HEALTH command API: Comparing secret '%s' to '%s'", w->auth_bearer_token, api_secret);
135 - if (strcmp(w->auth_bearer_token, api_secret)) {
136 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_AUTHERROR);
137 - ret = HTTP_RESP_FORBIDDEN;
138 - } else {
139 - while (url) {
140 - char *value = strsep_skip_consecutive_separators(&url, "&");
141 - if (!value || !*value) continue;
142 -
143 - char *key = strsep_skip_consecutive_separators(&value, "=");
144 - if (!key || !*key) continue;
145 - if (!value || !*value) continue;
146 -
147 - netdata_log_debug(D_WEB_CLIENT, "%llu: API v1 health query param '%s' with value '%s'", w->id, key, value);
148 -
149 - // name and value are now the parameters
150 - if (!strcmp(key, "cmd")) {
151 - if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCEALL)) {
152 - silencers->all_alarms = 1;
153 - silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
154 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCEALL);
155 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLEALL)) {
156 - silencers->all_alarms = 1;
157 - silencers->stype = STYPE_DISABLE_ALARMS;
158 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLEALL);
159 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_SILENCE)) {
160 - silencers->stype = STYPE_SILENCE_NOTIFICATIONS;
161 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_SILENCE);
162 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_DISABLE)) {
163 - silencers->stype = STYPE_DISABLE_ALARMS;
164 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_DISABLE);
165 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_RESET)) {
166 - silencers->all_alarms = 0;
167 - silencers->stype = STYPE_NONE;
168 - free_silencers(silencers->silencers);
169 - silencers->silencers = NULL;
170 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_RESET);
171 - } else if (!strcmp(value, HEALTH_CMDAPI_CMD_LIST)) {
172 - w->response.data->content_type = CT_APPLICATION_JSON;
173 - health_silencers2json(wb);
174 - config_changed=0;
175 - }
176 - } else {
177 - silencer = health_silencers_addparam(silencer, key, value);
178 - }
179 - }
180 -
181 - if (likely(silencer)) {
182 - health_silencers_add(silencer);
183 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_ADDED);
184 - if (silencers->stype == STYPE_NONE) {
185 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_STYPEWARNING);
186 - }
187 - }
188 - if (unlikely(silencers->stype != STYPE_NONE && !silencers->all_alarms && !silencers->silencers)) {
189 - buffer_strcat(wb, HEALTH_CMDAPI_MSG_NOSELECTORWARNING);
190 - }
191 - ret = HTTP_RESP_OK;
192 - }
193 - }
194 - w->response.data = wb;
195 - buffer_no_cacheable(w->response.data);
196 - if (ret == HTTP_RESP_OK && config_changed) {
197 - BUFFER *jsonb = buffer_create(200, &netdata_buffers_statistics.buffers_health);
198 - health_silencers2json(jsonb);
199 - health_silencers2file(jsonb);
200 - buffer_free(jsonb);
201 - }
202 -
203 - return ret;
204 -}
web/api/health/health_cmdapi.h deleted
-31
@@ -1,31 +0,0 @@
1 -// SPDX-License-Identifier: GPL-3.0-or-later
2 -
3 -#ifndef NETDATA_WEB_HEALTH_SVG_H
4 -#define NETDATA_WEB_HEALTH_SVG_H 1
5 -
6 -#include "libnetdata/libnetdata.h"
7 -#include "web/server/web_client.h"
8 -#include "health/health.h"
9 -
10 -#define HEALTH_CMDAPI_CMD_SILENCEALL "SILENCE ALL"
11 -#define HEALTH_CMDAPI_CMD_DISABLEALL "DISABLE ALL"
12 -#define HEALTH_CMDAPI_CMD_SILENCE "SILENCE"
13 -#define HEALTH_CMDAPI_CMD_DISABLE "DISABLE"
14 -#define HEALTH_CMDAPI_CMD_RESET "RESET"
15 -#define HEALTH_CMDAPI_CMD_LIST "LIST"
16 -
17 -#define HEALTH_CMDAPI_MSG_AUTHERROR "Auth Error\n"
18 -#define HEALTH_CMDAPI_MSG_SILENCEALL "All alarm notifications are silenced\n"
19 -#define HEALTH_CMDAPI_MSG_DISABLEALL "All health checks are disabled\n"
20 -#define HEALTH_CMDAPI_MSG_RESET "All health checks and notifications are enabled\n"
21 -#define HEALTH_CMDAPI_MSG_DISABLE "Health checks disabled for alarms matching the selectors\n"
22 -#define HEALTH_CMDAPI_MSG_SILENCE "Alarm notifications silenced for alarms matching the selectors\n"
23 -#define HEALTH_CMDAPI_MSG_ADDED "Alarm selector added\n"
24 -#define HEALTH_CMDAPI_MSG_STYPEWARNING "WARNING: Added alarm selector to silence/disable alarms without a SILENCE or DISABLE command.\n"
25 -#define HEALTH_CMDAPI_MSG_NOSELECTORWARNING "WARNING: SILENCE or DISABLE command is ineffective without defining any alarm selectors.\n"
26 -
27 -int web_client_api_request_v1_mgmt_health(RRDHOST *host, struct web_client *w, char *url);
28 -
29 -#include "web/api/web_api_v1.h"
30 -
31 -#endif /* NETDATA_WEB_HEALTH_SVG_H */
web/api/http_header.c
+9 -1
@@ -135,10 +135,18 @@ static void http_header_x_netdata_role(struct web_client *w, const char *v, size
135 strncpyz(buffer, v, (len < sizeof(buffer) - 1 ? len : sizeof(buffer) - 1));
136 if (strcasecmp(buffer, "admin") == 0)
137 w->access = HTTP_ACCESS_ADMIN;
138 + else if(strcasecmp(buffer, "manager") == 0)
139 + w->access = HTTP_ACCESS_MEMBER;
140 + else if(strcasecmp(buffer, "troubleshooter") == 0)
141 + w->access = HTTP_ACCESS_MEMBER;
142 + else if(strcasecmp(buffer, "observer") == 0)
143 + w->access = HTTP_ACCESS_MEMBER;
144 else if(strcasecmp(buffer, "member") == 0)
145 w->access = HTTP_ACCESS_MEMBER;
146 + else if(strcasecmp(buffer, "billing") == 0)
147 + w->access = HTTP_ACCESS_MEMBER;
148 else
141 - w->access = HTTP_ACCESS_ANY;
149 + w->access = HTTP_ACCESS_MEMBER;
150
151 web_client_flags_clear_auth(w);
152 web_client_flag_set(w, WEB_CLIENT_FLAG_AUTH_CLOUD);
web/api/queries/query.c
+14 -3
@@ -632,7 +632,7 @@ void time_grouping_init(void) {
632 }
633 }
634
635 -const char *time_grouping_method2string(RRDR_TIME_GROUPING group) {
635 +const char *time_grouping_id2txt(RRDR_TIME_GROUPING group) {
636 int i;
637
638 for(i = 0; api_v1_data_groups[i].name ; i++) {
@@ -641,7 +641,18 @@ const char *time_grouping_method2string(RRDR_TIME_GROUPING group) {
641 }
642 }
643
644 - return "unknown-group-method";
644 + return "average";
645 +}
646 +
647 +RRDR_TIME_GROUPING time_grouping_txt2id(const char *name) {
648 + int i;
649 +
650 + uint32_t hash = simple_hash(name);
651 + for(i = 0; api_v1_data_groups[i].name ; i++)
652 + if(unlikely(hash == api_v1_data_groups[i].hash && !strcmp(name, api_v1_data_groups[i].name)))
653 + return api_v1_data_groups[i].value;
654 +
655 + return RRDR_GROUPING_AVERAGE;
656 }
657
658 RRDR_TIME_GROUPING time_grouping_parse(const char *name, RRDR_TIME_GROUPING def) {
@@ -2045,7 +2056,7 @@ static void rrd2rrdr_log_request_response_metadata(RRDR *r
2056
2057 // grouping
2058 , (aligned) ? "aligned" : "unaligned"
2048 - , time_grouping_method2string(group_method)
2059 + , time_grouping_id2txt(group_method)
2060 , group
2061 , resampling_time
2062 , resampling_group
web/api/queries/query.h
+3 -1
@@ -47,7 +47,9 @@ typedef enum rrdr_time_grouping {
47 RRDR_GROUPING_COUNTIF,
48 } RRDR_TIME_GROUPING;
49
50 -const char *time_grouping_method2string(RRDR_TIME_GROUPING group);
50 +const char *time_grouping_id2txt(RRDR_TIME_GROUPING group);
51 +RRDR_TIME_GROUPING time_grouping_txt2id(const char *name);
52 +
53 void time_grouping_init(void);
54 RRDR_TIME_GROUPING time_grouping_parse(const char *name, RRDR_TIME_GROUPING def);
55 const char *time_grouping_tostring(RRDR_TIME_GROUPING group);
web/api/queries/rrdr.h
+1 -2
@@ -47,8 +47,7 @@ typedef enum rrdr_options {
47 RRDR_OPTION_GROUP_BY_LABELS = (1 << 26), // v2 returns flattened labels per dimension of the chart
48
49 // internal ones - not to be exposed to the API
50 - RRDR_OPTION_HEALTH_RSRVD1 = (1 << 30), // reserved for RRDCALC_OPTION_NO_CLEAR_NOTIFICATION
51 - RRDR_OPTION_INTERNAL_AR = (1 << 31), // internal use only, to let the formatters know we want to render the anomaly rate
50 + RRDR_OPTION_INTERNAL_AR = (1 << 31), // internal use only, to let the formatters know we want to render the anomaly rate
51 } RRDR_OPTIONS;
52
53 typedef enum context_v2_options {
web/api/queries/weights.c
+2 -2
@@ -158,7 +158,7 @@ static void results_header_to_json(DICTIONARY *results __maybe_unused, BUFFER *w
158
159 buffer_json_member_add_string(wb, "group", time_grouping_tostring(group));
160 buffer_json_member_add_string(wb, "method", weights_method_to_string(method));
161 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", options);
161 + rrdr_options_to_buffer_json_array(wb, "options", options);
162 }
163
164 static size_t registered_results_to_json_charts(DICTIONARY *results, BUFFER *wb,
@@ -372,7 +372,7 @@ static void results_header_to_json_v2(DICTIONARY *results __maybe_unused, BUFFER
372
373 buffer_json_member_add_object(wb, "request");
374 buffer_json_member_add_string(wb, "method", weights_method_to_string(method));
375 - web_client_api_request_v1_data_options_to_buffer_json_array(wb, "options", options);
375 + rrdr_options_to_buffer_json_array(wb, "options", options);
376
377 buffer_json_member_add_object(wb, "scope");
378 buffer_json_member_add_string(wb, "scope_nodes", qwd->qwr->scope_nodes ? qwd->qwr->scope_nodes : "*");
web/api/web_api.c
+1 -1
@@ -151,7 +151,7 @@ int web_client_api_request_weights(RRDHOST *host, struct web_client *w, char *ur
151 time_group_options = value;
152
153 else if(!strcmp(name, "options"))
154 - options |= web_client_api_request_v1_data_options(value);
154 + options |= rrdr_options_parse(value);
155
156 else if(!strcmp(name, "method"))
157 method = weights_string_to_method(value);
web/api/web_api.h
-1
@@ -9,7 +9,6 @@
9 #include "web/api/badges/web_buffer_svg.h"
10 #include "web/api/ilove/ilove.h"
11 #include "web/api/formatters/rrd2json.h"
12 -#include "web/api/health/health_cmdapi.h"
12 #include "web/api/queries/weights.h"
13
14 struct web_api_command {
web/api/web_api_v1.c
+80 -24
@@ -8,7 +8,7 @@ static struct {
8 const char *name;
9 uint32_t hash;
10 RRDR_OPTIONS value;
11 -} api_v1_data_options[] = {
11 +} rrdr_options[] = {
12 { "nonzero" , 0 , RRDR_OPTION_NONZERO}
13 , {"flip" , 0 , RRDR_OPTION_REVERSED}
14 , {"reversed" , 0 , RRDR_OPTION_REVERSED}
@@ -125,8 +125,8 @@ void web_client_api_v1_init(void) {
125 for(i = 0; contexts_v2_alert_status[i].name ; i++)
126 contexts_v2_alert_status[i].hash = simple_hash(contexts_v2_alert_status[i].name);
127
128 - for(i = 0; api_v1_data_options[i].name ; i++)
129 - api_v1_data_options[i].hash = simple_hash(api_v1_data_options[i].name);
128 + for(i = 0; rrdr_options[i].name ; i++)
129 + rrdr_options[i].hash = simple_hash(rrdr_options[i].name);
130
131 for(i = 0; contexts_v2_options[i].name ; i++)
132 contexts_v2_options[i].hash = simple_hash(contexts_v2_options[i].name);
@@ -211,21 +211,30 @@ void web_client_api_v1_management_init(void) {
211 api_secret = get_mgmt_api_key();
212 }
213
214 -inline RRDR_OPTIONS web_client_api_request_v1_data_options(char *o) {
214 +inline RRDR_OPTIONS rrdr_options_parse_one(const char *o) {
215 + RRDR_OPTIONS ret = 0;
216 +
217 + if(!o || !*o) return ret;
218 +
219 + uint32_t hash = simple_hash(o);
220 + int i;
221 + for(i = 0; rrdr_options[i].name ; i++) {
222 + if (unlikely(hash == rrdr_options[i].hash && !strcmp(o, rrdr_options[i].name))) {
223 + ret |= rrdr_options[i].value;
224 + break;
225 + }
226 + }
227 +
228 + return ret;
229 +}
230 +
231 +inline RRDR_OPTIONS rrdr_options_parse(char *o) {
232 RRDR_OPTIONS ret = 0;
233 char *tok;
234
235 while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
236 if(!*tok) continue;
220 -
221 - uint32_t hash = simple_hash(tok);
222 - int i;
223 - for(i = 0; api_v1_data_options[i].name ; i++) {
224 - if (unlikely(hash == api_v1_data_options[i].hash && !strcmp(tok, api_v1_data_options[i].name))) {
225 - ret |= api_v1_data_options[i].value;
226 - break;
227 - }
228 - }
237 + ret |= rrdr_options_parse_one(tok);
238 }
239
240 return ret;
@@ -303,14 +312,14 @@ void web_client_api_request_v2_contexts_options_to_buffer_json_array(BUFFER *wb,
312 buffer_json_array_close(wb);
313 }
314
306 -void web_client_api_request_v1_data_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options) {
315 +void rrdr_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options) {
316 buffer_json_member_add_array(wb, key);
317
318 RRDR_OPTIONS used = 0; // to prevent adding duplicates
310 - for(int i = 0; api_v1_data_options[i].name ; i++) {
311 - if (unlikely((api_v1_data_options[i].value & options) && !(api_v1_data_options[i].value & used))) {
312 - const char *name = api_v1_data_options[i].name;
313 - used |= api_v1_data_options[i].value;
319 + for(int i = 0; rrdr_options[i].name ; i++) {
320 + if (unlikely((rrdr_options[i].value & options) && !(rrdr_options[i].value & used))) {
321 + const char *name = rrdr_options[i].name;
322 + used |= rrdr_options[i].value;
323
324 buffer_json_add_array_item_string(wb, name);
325 }
@@ -325,10 +334,10 @@ void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RR
334
335 RRDR_OPTIONS used = 0; // to prevent adding duplicates
336 int added = 0;
328 - for(int i = 0; api_v1_data_options[i].name ; i++) {
329 - if (unlikely((api_v1_data_options[i].value & options) && !(api_v1_data_options[i].value & used))) {
330 - const char *name = api_v1_data_options[i].name;
331 - used |= api_v1_data_options[i].value;
337 + for(int i = 0; rrdr_options[i].name ; i++) {
338 + if (unlikely((rrdr_options[i].value & options) && !(rrdr_options[i].value & used))) {
339 + const char *name = rrdr_options[i].name;
340 + used |= rrdr_options[i].value;
341
342 if(added && write < end)
343 *write++ = ',';
@@ -514,6 +523,52 @@ inline int web_client_api_request_single_chart(RRDHOST *host, struct web_client
523 return ret;
524 }
525
526 +static inline int web_client_api_request_variable(RRDHOST *host, struct web_client *w, char *url) {
527 + int ret = HTTP_RESP_BAD_REQUEST;
528 + char *chart = NULL;
529 + char *variable = NULL;
530 +
531 + buffer_flush(w->response.data);
532 +
533 + while(url) {
534 + char *value = strsep_skip_consecutive_separators(&url, "&");
535 + if(!value || !*value) continue;
536 +
537 + char *name = strsep_skip_consecutive_separators(&value, "=");
538 + if(!name || !*name) continue;
539 + if(!value || !*value) continue;
540 +
541 + // name and value are now the parameters
542 + // they are not null and not empty
543 +
544 + if(!strcmp(name, "chart")) chart = value;
545 + else if(!strcmp(name, "variable")) variable = value;
546 + }
547 +
548 + if(!chart || !*chart || !variable || !*variable) {
549 + buffer_sprintf(w->response.data, "A chart= and a variable= are required.");
550 + goto cleanup;
551 + }
552 +
553 + RRDSET *st = rrdset_find(host, chart);
554 + if(!st) st = rrdset_find_byname(host, chart);
555 + if(!st) {
556 + buffer_strcat(w->response.data, "Chart is not found: ");
557 + buffer_strcat_htmlescape(w->response.data, chart);
558 + ret = HTTP_RESP_NOT_FOUND;
559 + goto cleanup;
560 + }
561 +
562 + w->response.data->content_type = CT_APPLICATION_JSON;
563 + st->last_accessed_time_s = now_realtime_sec();
564 + alert_variable_lookup_trace(host, st, variable, w->response.data);
565 +
566 + return HTTP_RESP_OK;
567 +
568 +cleanup:
569 + return ret;
570 +}
571 +
572 inline int web_client_api_request_v1_alarm_variables(RRDHOST *host, struct web_client *w, char *url) {
573 return web_client_api_request_single_chart(host, w, url, health_api_v1_chart_variables2json);
574 }
@@ -723,7 +778,7 @@ static inline int web_client_api_request_v1_data(RRDHOST *host, struct web_clien
778 format = web_client_api_request_v1_data_format(value);
779 }
780 else if(!strcmp(name, "options")) {
726 - options |= web_client_api_request_v1_data_options(value);
781 + options |= rrdr_options_parse(value);
782 }
783 else if(!strcmp(name, "callback")) {
784 responseHandler = value;
@@ -1655,7 +1710,7 @@ int web_client_api_request_v1_mgmt(RRDHOST *host, struct web_client *w, char *ur
1710 }
1711 needle += strlen(HLT_MGM);
1712 if (*needle != '\0') {
1658 - buffer_strcat(w->response.data, "Invalid management request. Curently only 'health' is supported.");
1713 + buffer_strcat(w->response.data, "Invalid management request. Currently only 'health' is supported.");
1714 return HTTP_RESP_NOT_FOUND;
1715 }
1716 return web_client_api_request_v1_mgmt_health(host, w, url);
@@ -1679,6 +1734,7 @@ static struct web_api_command api_commands_v1[] = {
1734 {"alarms_values", 0, HTTP_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarms_values, 0 },
1735 {"alarm_log", 0, HTTP_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarm_log, 0 },
1736 {"alarm_variables", 0, HTTP_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarm_variables, 0 },
1737 + {"variable", 0, HTTP_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_variable, 0 },
1738 {"alarm_count", 0, HTTP_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_alarm_count, 0 },
1739 {"allmetrics", 0, HTTP_ACL_DASHBOARD_ACLK_WEBRTC, web_client_api_request_v1_allmetrics, 0 },
1740
web/api/web_api_v1.h
+4 -2
@@ -12,8 +12,10 @@ CONTEXTS_V2_ALERT_STATUS web_client_api_request_v2_alert_status(char *o);
12 void web_client_api_request_v2_contexts_options_to_buffer_json_array(BUFFER *wb, const char *key, CONTEXTS_V2_OPTIONS options);
13 void web_client_api_request_v2_contexts_alerts_status_to_buffer_json_array(BUFFER *wb, const char *key, CONTEXTS_V2_ALERT_STATUS options);
14
15 -RRDR_OPTIONS web_client_api_request_v1_data_options(char *o);
16 -void web_client_api_request_v1_data_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options);
15 +RRDR_OPTIONS rrdr_options_parse(char *o);
16 +RRDR_OPTIONS rrdr_options_parse_one(const char *o);
17 +
18 +void rrdr_options_to_buffer_json_array(BUFFER *wb, const char *key, RRDR_OPTIONS options);
19 void web_client_api_request_v1_data_options_to_string(char *buf, size_t size, RRDR_OPTIONS options);
20
21 uint32_t web_client_api_request_v1_data_format(char *name);
web/api/web_api_v2.c
+1 -1
@@ -355,7 +355,7 @@ static int web_client_api_request_v2_data(RRDHOST *host __maybe_unused, struct w
355 aggregation_idx = MAX_QUERY_GROUP_BY_PASSES - 1;
356 }
357 else if(!strcmp(name, "format")) format = web_client_api_request_v1_data_format(value);
358 - else if(!strcmp(name, "options")) options |= web_client_api_request_v1_data_options(value);
358 + else if(!strcmp(name, "options")) options |= rrdr_options_parse(value);
359 else if(!strcmp(name, "time_group")) time_group = time_grouping_parse(value, RRDR_GROUPING_AVERAGE);
360 else if(!strcmp(name, "time_group_options")) time_group_options = value;
361 else if(!strcmp(name, "time_resampling")) resampling_time_str = value;
web/server/h2o/h2o_utils.c
+1 -1
@@ -25,7 +25,7 @@ h2o_iovec_pair_vector_t *parse_URL_params(h2o_mem_pool_t *pool, h2o_iovec_t para
25 if (params_vec->capacity == params_vec->size)
26 h2o_vector_reserve(pool, params_vec, params_vec->capacity + KEY_VAL_BUFFER_GROWTH_STEP);
27
28 - params_vec->entries[params_vec->size++] = param;
28 + params_vec->entries[params_vec->size++] = param;
29 }
30
31 return params_vec;
web/server/h2o/http_server.c
+5
@@ -4,8 +4,13 @@
4 #include "streaming/common.h"
5 #include "http_server.h"
6
7 +#pragma GCC diagnostic push
8 +#pragma GCC diagnostic ignored "-Wunused-parameter"
9 +#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
10 +#pragma GCC diagnostic ignored "-Wtype-limits"
11 #include "h2o.h"
12 #include "h2o/http1.h"
13 +#pragma GCC diagnostic pop
14
15 #include "streaming.h"
16 #include "h2o_utils.h"
web/server/h2o/streaming.h
+6
@@ -4,7 +4,13 @@
4 #define HTTPD_STREAMING_H
5
6 #include "aclk/mqtt_websockets/c-rbuf/cringbuffer.h"
7 +
8 +#pragma GCC diagnostic push
9 +#pragma GCC diagnostic ignored "-Wunused-parameter"
10 +#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
11 +#pragma GCC diagnostic ignored "-Wtype-limits"
12 #include "h2o.h"
13 +#pragma GCC diagnostic pop
14
15 typedef enum {
16 STREAM_X_HTTP_1_1 = 0,