health_prototypes.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "health_internals.h"
  3. // ---------------------------------------------------------------------------------------------------------------------
  4. static struct {
  5. ALERT_LOOKUP_DIMS_GROUPING group;
  6. const char *name;
  7. } dims_grouping[] = {
  8. { .group = ALERT_LOOKUP_DIMS_SUM, .name = "sum" },
  9. { .group = ALERT_LOOKUP_DIMS_MIN, .name = "min" },
  10. { .group = ALERT_LOOKUP_DIMS_MAX, .name = "max" },
  11. { .group = ALERT_LOOKUP_DIMS_AVERAGE, .name = "average" },
  12. { .group = ALERT_LOOKUP_DIMS_MIN2MAX, .name = "min2max" },
  13. // terminator
  14. { .group = 0, .name = NULL },
  15. };
  16. ALERT_LOOKUP_DIMS_GROUPING alerts_dims_grouping2id(const char *group) {
  17. if(!group || !*group)
  18. return dims_grouping[0].group;
  19. for(size_t i = 0; dims_grouping[i].name ;i++) {
  20. if(strcmp(dims_grouping[i].name, group) == 0)
  21. return dims_grouping[i].group;
  22. }
  23. nd_log(NDLS_DAEMON, NDLP_WARNING, "Alert lookup dimensions grouping '%s' is not valid", group);
  24. return dims_grouping[0].group;
  25. }
  26. const char *alerts_dims_grouping_id2group(ALERT_LOOKUP_DIMS_GROUPING grouping) {
  27. for(size_t i = 0; dims_grouping[i].name ;i++) {
  28. if(grouping == dims_grouping[i].group)
  29. return dims_grouping[i].name;
  30. }
  31. nd_log(NDLS_DAEMON, NDLP_WARNING, "Alert lookup dimensions grouping %d is not valid", grouping);
  32. return dims_grouping[0].name;
  33. }
  34. // ---------------------------------------------------------------------------------------------------------------------
  35. static struct {
  36. ALERT_LOOKUP_DATA_SOURCE source;
  37. const char *name;
  38. } data_sources[] = {
  39. { .source = ALERT_LOOKUP_DATA_SOURCE_SAMPLES, .name = "samples" },
  40. { .source = ALERT_LOOKUP_DATA_SOURCE_PERCENTAGES, .name = "percentages" },
  41. { .source = ALERT_LOOKUP_DATA_SOURCE_ANOMALIES, .name = "anomalies" },
  42. // terminator
  43. { .source = 0, .name = NULL },
  44. };
  45. ALERT_LOOKUP_DATA_SOURCE alerts_data_sources2id(const char *source) {
  46. if(!source || !*source)
  47. return data_sources[0].source;
  48. for(size_t i = 0; data_sources[i].name ;i++) {
  49. if(strcmp(data_sources[i].name, source) == 0)
  50. return data_sources[i].source;
  51. }
  52. nd_log(NDLS_DAEMON, NDLP_WARNING, "Alert data source '%s' is not valid", source);
  53. return data_sources[0].source;
  54. }
  55. const char *alerts_data_source_id2source(ALERT_LOOKUP_DATA_SOURCE source) {
  56. for(size_t i = 0; data_sources[i].name ;i++) {
  57. if(source == data_sources[i].source)
  58. return data_sources[i].name;
  59. }
  60. nd_log(NDLS_DAEMON, NDLP_WARNING, "Alert data source %d is not valid", source);
  61. return data_sources[0].name;
  62. }
  63. // ---------------------------------------------------------------------------------------------------------------------
  64. static struct {
  65. ALERT_LOOKUP_TIME_GROUP_CONDITION condition;
  66. const char *name;
  67. } group_conditions[] = {
  68. { .condition = ALERT_LOOKUP_TIME_GROUP_CONDITION_EQUAL, .name = "=" },
  69. { .condition = ALERT_LOOKUP_TIME_GROUP_CONDITION_NOT_EQUAL, .name = "!=" },
  70. { .condition = ALERT_LOOKUP_TIME_GROUP_CONDITION_GREATER, .name = ">" },
  71. { .condition = ALERT_LOOKUP_TIME_GROUP_CONDITION_GREATER_EQUAL, .name = ">=" },
  72. { .condition = ALERT_LOOKUP_TIME_GROUP_CONDITION_LESS, .name = "<" },
  73. { .condition = ALERT_LOOKUP_TIME_GROUP_CONDITION_LESS_EQUAL, .name = "<=" },
  74. // terminator
  75. { .condition = 0, .name = NULL },
  76. };
  77. ALERT_LOOKUP_TIME_GROUP_CONDITION alerts_group_condition2id(const char *source) {
  78. if(!source || !*source)
  79. return group_conditions[0].condition;
  80. for(size_t i = 0; group_conditions[i].name ;i++) {
  81. if(strcmp(group_conditions[i].name, source) == 0)
  82. return group_conditions[i].condition;
  83. }
  84. nd_log(NDLS_DAEMON, NDLP_WARNING, "Alert data source '%s' is not valid", source);
  85. return group_conditions[0].condition;
  86. }
  87. const char *alerts_group_conditions_id2txt(ALERT_LOOKUP_TIME_GROUP_CONDITION source) {
  88. for(size_t i = 0; group_conditions[i].name ;i++) {
  89. if(source == group_conditions[i].condition)
  90. return group_conditions[i].name;
  91. }
  92. nd_log(NDLS_DAEMON, NDLP_WARNING, "Alert data source %d is not valid", source);
  93. return group_conditions[0].name;
  94. }
  95. // ---------------------------------------------------------------------------------------------------------------------
  96. static struct {
  97. const char *name;
  98. uint32_t hash;
  99. ALERT_ACTION_OPTIONS value;
  100. } alert_action_options[] = {
  101. { "no-clear-notification", 0 , ALERT_ACTION_OPTION_NO_CLEAR_NOTIFICATION}
  102. // terminator
  103. , {NULL, 0, 0}
  104. };
  105. inline ALERT_ACTION_OPTIONS alert_action_options_parse_one(const char *o) {
  106. ALERT_ACTION_OPTIONS ret = 0;
  107. if(!o || !*o) return ret;
  108. uint32_t hash = simple_hash(o);
  109. int i;
  110. for(i = 0; alert_action_options[i].name ; i++) {
  111. if (unlikely(hash == alert_action_options[i].hash && !strcmp(o, alert_action_options[i].name))) {
  112. ret |= alert_action_options[i].value;
  113. break;
  114. }
  115. }
  116. return ret;
  117. }
  118. inline ALERT_ACTION_OPTIONS alert_action_options_parse(char *o) {
  119. ALERT_ACTION_OPTIONS ret = 0;
  120. char *tok;
  121. while(o && *o && (tok = strsep_skip_consecutive_separators(&o, ", |"))) {
  122. if(!*tok) continue;
  123. ret |= alert_action_options_parse_one(tok);
  124. }
  125. return ret;
  126. }
  127. void alert_action_options_to_buffer_json_array(BUFFER *wb, const char *key, ALERT_ACTION_OPTIONS options) {
  128. buffer_json_member_add_array(wb, key);
  129. RRDR_OPTIONS used = 0; // to prevent adding duplicates
  130. for(int i = 0; alert_action_options[i].name ; i++) {
  131. if (unlikely((alert_action_options[i].value & options) && !(alert_action_options[i].value & used))) {
  132. const char *name = alert_action_options[i].name;
  133. used |= alert_action_options[i].value;
  134. buffer_json_add_array_item_string(wb, name);
  135. }
  136. }
  137. buffer_json_array_close(wb);
  138. }
  139. static void alert_action_options_init(void) {
  140. for(int i = 0; alert_action_options[i].name ; i++)
  141. alert_action_options[i].hash = simple_hash(alert_action_options[i].name);
  142. }
  143. // ---------------------------------------------------------------------------------------------------------------------
  144. static void health_prototype_cleanup_one_unsafe(RRD_ALERT_PROTOTYPE *ap) {
  145. rrd_alert_match_cleanup(&ap->match);
  146. rrd_alert_config_cleanup(&ap->config);
  147. }
  148. void health_prototype_cleanup(RRD_ALERT_PROTOTYPE *ap) {
  149. spinlock_lock(&ap->_internal.spinlock);
  150. while(ap->_internal.next) {
  151. RRD_ALERT_PROTOTYPE *t = ap->_internal.next;
  152. DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(ap->_internal.next, t, _internal.prev, _internal.next);
  153. health_prototype_cleanup_one_unsafe(t);
  154. freez(t);
  155. }
  156. spinlock_unlock(&ap->_internal.spinlock);
  157. health_prototype_cleanup_one_unsafe(ap);
  158. }
  159. void health_prototype_free(RRD_ALERT_PROTOTYPE *ap) {
  160. if(!ap) return;
  161. health_prototype_cleanup(ap);
  162. freez(ap);
  163. }
  164. void health_prototype_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
  165. RRD_ALERT_PROTOTYPE *ap = value;
  166. spinlock_init(&ap->_internal.spinlock);
  167. if(ap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG)
  168. ap->_internal.is_on_disk = true;
  169. }
  170. bool health_prototype_conflict_cb(const DICTIONARY_ITEM *item __maybe_unused, void *old_value, void *new_value, void *data __maybe_unused) {
  171. RRD_ALERT_PROTOTYPE *ap = old_value;
  172. RRD_ALERT_PROTOTYPE *nap = new_value;
  173. bool replace = nap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG;
  174. if(ap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG || nap->config.source_type != DYNCFG_SOURCE_TYPE_DYNCFG)
  175. ap->_internal.is_on_disk = nap->_internal.is_on_disk = true;
  176. if(!replace) {
  177. if(ap->config.source_type == DYNCFG_SOURCE_TYPE_DYNCFG) {
  178. // the existing is a dyncfg and the new one is read from the config
  179. health_prototype_cleanup(nap);
  180. memset(nap, 0, sizeof(*nap));
  181. }
  182. else {
  183. // alerts with the same name are appended to the existing one
  184. nap = callocz(1, sizeof(*nap));
  185. memcpy(nap, new_value, sizeof(*nap));
  186. spinlock_lock(&ap->_internal.spinlock);
  187. DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(ap->_internal.next, nap, _internal.prev, _internal.next);
  188. spinlock_unlock(&ap->_internal.spinlock);
  189. if(nap->_internal.enabled)
  190. ap->_internal.enabled = true;
  191. }
  192. }
  193. else {
  194. // alerts with the same name replace the existing one
  195. spinlock_init(&nap->_internal.spinlock);
  196. nap->_internal.uses = ap->_internal.uses;
  197. spinlock_lock(&nap->_internal.spinlock);
  198. spinlock_lock(&ap->_internal.spinlock);
  199. SWAP(*ap, *nap);
  200. spinlock_unlock(&ap->_internal.spinlock);
  201. spinlock_unlock(&nap->_internal.spinlock);
  202. health_prototype_cleanup(nap);
  203. memset(nap, 0, sizeof(*nap));
  204. }
  205. return true;
  206. }
  207. void health_prototype_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
  208. RRD_ALERT_PROTOTYPE *ap = value;
  209. health_prototype_cleanup(ap);
  210. }
  211. void health_init_prototypes(void) {
  212. if(health_globals.prototypes.dict)
  213. return;
  214. health_globals.prototypes.dict = dictionary_create(DICT_OPTION_DONT_OVERWRITE_VALUE);
  215. dictionary_register_insert_callback(health_globals.prototypes.dict, health_prototype_insert_cb, NULL);
  216. dictionary_register_conflict_callback(health_globals.prototypes.dict, health_prototype_conflict_cb, NULL);
  217. dictionary_register_delete_callback(health_globals.prototypes.dict, health_prototype_delete_cb, NULL);
  218. alert_action_options_init();
  219. }
  220. // ---------------------------------------------------------------------------------------------------------------------
  221. static inline struct pattern_array *health_config_add_key_to_values(struct pattern_array *pa, const char *input_key, char *value)
  222. {
  223. char key[HEALTH_CONF_MAX_LINE + 1];
  224. char data[HEALTH_CONF_MAX_LINE + 1];
  225. char *s = value;
  226. size_t i = 0;
  227. char pair[HEALTH_CONF_MAX_LINE + 1];
  228. if (input_key)
  229. strncpyz(key, input_key, HEALTH_CONF_MAX_LINE);
  230. else
  231. key[0] = '\0';
  232. while(*s) {
  233. if (*s == '=') {
  234. //hold the key
  235. data[i]='\0';
  236. strncpyz(key, data, HEALTH_CONF_MAX_LINE);
  237. i=0;
  238. } else if (*s == ' ') {
  239. data[i]='\0';
  240. if (data[0]=='!')
  241. snprintfz(pair, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
  242. else
  243. snprintfz(pair, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
  244. pa = pattern_array_add_key_simple_pattern(pa, key, simple_pattern_create(pair, NULL, SIMPLE_PATTERN_EXACT, true));
  245. i=0;
  246. } else {
  247. data[i++] = *s;
  248. }
  249. s++;
  250. }
  251. data[i]='\0';
  252. if (data[0]) {
  253. if (data[0]=='!')
  254. snprintfz(pair, HEALTH_CONF_MAX_LINE, "!%s=%s ", key, data + 1);
  255. else
  256. snprintfz(pair, HEALTH_CONF_MAX_LINE, "%s=%s ", key, data);
  257. pa = pattern_array_add_key_simple_pattern(pa, key, simple_pattern_create(pair, NULL, SIMPLE_PATTERN_EXACT, true));
  258. }
  259. return pa;
  260. }
  261. static char *simple_pattern_trim_around_equal(const char *src) {
  262. char *store = mallocz(strlen(src) + 1);
  263. char *dst = store;
  264. while (*src) {
  265. if (*src == '=') {
  266. if (*(dst -1) == ' ')
  267. dst--;
  268. *dst++ = *src++;
  269. if (*src == ' ')
  270. src++;
  271. }
  272. *dst++ = *src++;
  273. }
  274. *dst = 0x00;
  275. return store;
  276. }
  277. static struct pattern_array *trim_and_add_key_to_values(struct pattern_array *pa, const char *key, STRING *input) {
  278. char *tmp = simple_pattern_trim_around_equal(string2str(input));
  279. pa = health_config_add_key_to_values(pa, key, tmp);
  280. freez(tmp);
  281. return pa;
  282. }
  283. static void health_prototype_activate_match_patterns(struct rrd_alert_match *am) {
  284. if(am->host_labels) {
  285. pattern_array_free(am->host_labels_pattern);
  286. am->host_labels_pattern = NULL;
  287. am->host_labels_pattern = trim_and_add_key_to_values(am->host_labels_pattern, NULL, am->host_labels);
  288. }
  289. if(am->chart_labels) {
  290. pattern_array_free(am->chart_labels_pattern);
  291. am->chart_labels_pattern = NULL;
  292. am->chart_labels_pattern = trim_and_add_key_to_values(am->chart_labels_pattern, NULL, am->chart_labels);
  293. }
  294. }
  295. void health_prototype_hash_id(RRD_ALERT_PROTOTYPE *ap) {
  296. CLEAN_BUFFER *wb = buffer_create(100, NULL);
  297. health_prototype_to_json(wb, ap, true);
  298. UUID uuid = UUID_generate_from_hash(buffer_tostring(wb), buffer_strlen(wb));
  299. uuid_copy(ap->config.hash_id, uuid.uuid);
  300. (void) sql_alert_store_config(ap);
  301. }
  302. bool health_prototype_add(RRD_ALERT_PROTOTYPE *ap) {
  303. if(!ap->match.is_template) {
  304. if(!ap->match.on.chart) {
  305. netdata_log_error(
  306. "HEALTH: alert '%s' does not define a instance (parameter 'on'). Source: %s",
  307. string2str(ap->config.name), string2str(ap->config.source));
  308. return false;
  309. }
  310. }
  311. else {
  312. if(!ap->match.on.context) {
  313. netdata_log_error(
  314. "HEALTH: alert '%s' does not define a context (parameter 'on'). Source: %s",
  315. string2str(ap->config.name), string2str(ap->config.source));
  316. return false;
  317. }
  318. }
  319. if(!ap->config.update_every) {
  320. netdata_log_error(
  321. "HEALTH: alert '%s' has no frequency (parameter 'every'). Source: %s",
  322. string2str(ap->config.name), string2str(ap->config.source));
  323. return false;
  324. }
  325. if(!RRDCALC_HAS_DB_LOOKUP(ap) && !ap->config.calculation && !ap->config.warning && !ap->config.critical) {
  326. netdata_log_error(
  327. "HEALTH: alert '%s' is useless (no db lookup, no calculation, no warning and no critical expressions). Source: %s",
  328. string2str(ap->config.name), string2str(ap->config.source));
  329. return false;
  330. }
  331. // activate the match patterns in it
  332. bool enabled = false;
  333. for(RRD_ALERT_PROTOTYPE *t = ap; t ;t = t->_internal.next) {
  334. // we need to generate config_hash_id for each instance included
  335. // so, let's break the linked list for this iteration
  336. RRD_ALERT_PROTOTYPE *prev = t->_internal.prev;
  337. RRD_ALERT_PROTOTYPE *next = t->_internal.next;
  338. t->_internal.prev = t;
  339. t->_internal.next = NULL;
  340. if(t->match.enabled)
  341. enabled = true;
  342. if(!t->config.name)
  343. t->config.name = string_dup(ap->config.name);
  344. health_prototype_hash_id(t);
  345. health_prototype_activate_match_patterns(&t->match);
  346. if (!t->config.exec)
  347. t->config.exec = string_dup(health_globals.config.default_exec);
  348. if (!t->config.recipient)
  349. t->config.recipient = string_dup(health_globals.config.default_recipient);
  350. // restore the linked list
  351. t->_internal.prev = prev;
  352. t->_internal.next = next;
  353. }
  354. ap->_internal.enabled = enabled;
  355. // add it to the prototypes
  356. dictionary_set_advanced(health_globals.prototypes.dict,
  357. string2str(ap->config.name), string_strlen(ap->config.name),
  358. ap, sizeof(*ap),
  359. NULL);
  360. return true;
  361. }
  362. // ---------------------------------------------------------------------------------------------------------------------
  363. void health_reload_prototypes(void) {
  364. // remove all dyncfg related to prototypes
  365. health_dyncfg_unregister_all_prototypes();
  366. // clear old prototypes from memory
  367. dictionary_flush(health_globals.prototypes.dict);
  368. // load the prototypes from disk
  369. recursive_config_double_dir_load(
  370. health_user_config_dir(),
  371. health_globals.config.stock_enabled ? health_stock_config_dir() : NULL,
  372. NULL,
  373. health_readfile,
  374. NULL, 0);
  375. // register all loaded prototypes
  376. health_dyncfg_register_all_prototypes();
  377. }
  378. // ---------------------------------------------------------------------------------------------------------------------
  379. static bool prototype_matches_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap) {
  380. if(health_globals.config.enabled_alerts &&
  381. !simple_pattern_matches(health_globals.config.enabled_alerts, string2str(ap->config.name)))
  382. return false;
  383. if (host->rrdlabels && ap->match.host_labels_pattern &&
  384. !pattern_array_label_match(ap->match.host_labels_pattern, host->rrdlabels, '=', NULL, rrdlabels_match_simple_pattern_parsed))
  385. return false;
  386. return true;
  387. }
  388. static bool prototype_matches_rrdset(RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
  389. // match the chart id
  390. if(!ap->match.is_template && ap->match.on.chart &&
  391. ap->match.on.chart != st->id && ap->match.on.chart != st->name)
  392. return false;
  393. // match the chart context
  394. if(ap->match.is_template && ap->match.on.context &&
  395. ap->match.on.context != st->context)
  396. return false;
  397. if (st->rrdlabels && ap->match.chart_labels_pattern &&
  398. !pattern_array_label_match(ap->match.chart_labels_pattern, st->rrdlabels, '=', NULL, rrdlabels_match_simple_pattern_parsed))
  399. return false;
  400. return true;
  401. }
  402. void health_prototype_copy_match_without_patterns(struct rrd_alert_match *dst, struct rrd_alert_match *src) {
  403. dst->enabled = src->enabled;
  404. dst->is_template = src->is_template;
  405. if(dst->is_template)
  406. dst->on.context = string_dup(src->on.context);
  407. else
  408. dst->on.chart = string_dup(src->on.chart);
  409. dst->host_labels = string_dup(src->host_labels);
  410. dst->chart_labels = string_dup(src->chart_labels);
  411. }
  412. void health_prototype_copy_config(struct rrd_alert_config *dst, struct rrd_alert_config *src) {
  413. uuid_copy(dst->hash_id, src->hash_id);
  414. dst->name = string_dup(src->name);
  415. dst->exec = string_dup(src->exec);
  416. dst->recipient = string_dup(src->recipient);
  417. dst->classification = string_dup(src->classification);
  418. dst->component = string_dup(src->component);
  419. dst->type = string_dup(src->type);
  420. dst->source_type = src->source_type;
  421. dst->source = string_dup(src->source);
  422. dst->units = string_dup(src->units);
  423. dst->summary = string_dup(src->summary);
  424. dst->info = string_dup(src->info);
  425. dst->update_every = src->update_every;
  426. dst->dimensions = string_dup(src->dimensions);
  427. dst->time_group = src->time_group;
  428. dst->before = src->before;
  429. dst->after = src->after;
  430. dst->options = src->options;
  431. const char *failed_at = NULL;
  432. int error = 0;
  433. dst->calculation = expression_parse(expression_source(src->calculation), &failed_at, &error);
  434. dst->warning = expression_parse(expression_source(src->warning), &failed_at, &error);
  435. dst->critical = expression_parse(expression_source(src->critical), &failed_at, &error);
  436. dst->delay_up_duration = src->delay_up_duration;
  437. dst->delay_down_duration = src->delay_down_duration;
  438. dst->delay_max_duration = src->delay_max_duration;
  439. dst->delay_multiplier = src->delay_multiplier;
  440. dst->has_custom_repeat_config = src->has_custom_repeat_config;
  441. dst->warn_repeat_every = src->warn_repeat_every;
  442. dst->crit_repeat_every = src->crit_repeat_every;
  443. }
  444. static void health_prototype_apply_to_rrdset(RRDSET *st, RRD_ALERT_PROTOTYPE *ap) {
  445. if(!ap->_internal.enabled)
  446. return;
  447. spinlock_lock(&ap->_internal.spinlock);
  448. for(RRD_ALERT_PROTOTYPE *t = ap; t ; t = t->_internal.next) {
  449. if(!t->match.enabled)
  450. continue;
  451. if(!prototype_matches_host(st->rrdhost, t))
  452. continue;
  453. if(!prototype_matches_rrdset(st, t))
  454. continue;
  455. if(rrdcalc_add_from_prototype(st->rrdhost, st, t))
  456. ap->_internal.uses++;
  457. }
  458. spinlock_unlock(&ap->_internal.spinlock);
  459. }
  460. void health_prototype_alerts_for_rrdset_incrementally(RRDSET *st) {
  461. RRD_ALERT_PROTOTYPE *ap;
  462. dfe_start_read(health_globals.prototypes.dict, ap) {
  463. health_prototype_apply_to_rrdset(st, ap);
  464. }
  465. dfe_done(ap);
  466. }
  467. void health_prototype_reset_alerts_for_rrdset(RRDSET *st) {
  468. rrdcalc_unlink_and_delete_all_rrdset_alerts(st);
  469. health_prototype_alerts_for_rrdset_incrementally(st);
  470. }
  471. // ---------------------------------------------------------------------------------------------------------------------
  472. void health_apply_prototype_to_host(RRDHOST *host, RRD_ALERT_PROTOTYPE *ap) {
  473. if(!ap->_internal.enabled)
  474. return;
  475. if(unlikely(!host->health.health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH))
  476. return;
  477. RRDSET *st;
  478. rrdset_foreach_read(st, host) {
  479. health_prototype_apply_to_rrdset(st, ap);
  480. }
  481. rrdset_foreach_done(st);
  482. }
  483. void health_prototype_apply_to_all_hosts(RRD_ALERT_PROTOTYPE *ap) {
  484. if(!ap->_internal.enabled)
  485. return;
  486. RRDHOST *host;
  487. dfe_start_reentrant(rrdhost_root_index, host){
  488. health_apply_prototype_to_host(host, ap);
  489. }
  490. dfe_done(host);
  491. }
  492. // ---------------------------------------------------------------------------------------------------------------------
  493. void health_apply_prototypes_to_host(RRDHOST *host) {
  494. if(unlikely(!host->health.health_enabled) && !rrdhost_flag_check(host, RRDHOST_FLAG_INITIALIZED_HEALTH))
  495. return;
  496. // free all running alarms
  497. rrdcalc_delete_all(host);
  498. // invalidate all previous entries in the alarm log
  499. rw_spinlock_read_lock(&host->health_log.spinlock);
  500. ALARM_ENTRY *t;
  501. for(t = host->health_log.alarms ; t ; t = t->next) {
  502. if(t->new_status != RRDCALC_STATUS_REMOVED)
  503. t->flags |= HEALTH_ENTRY_FLAG_UPDATED;
  504. }
  505. rw_spinlock_read_unlock(&host->health_log.spinlock);
  506. // apply all the prototypes for the charts of the host
  507. RRDSET *st;
  508. rrdset_foreach_read(st, host) {
  509. health_prototype_reset_alerts_for_rrdset(st);
  510. }
  511. rrdset_foreach_done(st);
  512. #ifdef ENABLE_ACLK
  513. if (netdata_cloud_enabled) {
  514. struct aclk_sync_cfg_t *wc = host->aclk_config;
  515. if (likely(wc)) {
  516. wc->alert_queue_removed = SEND_REMOVED_AFTER_HEALTH_LOOPS;
  517. }
  518. }
  519. #endif
  520. }
  521. void health_apply_prototypes_to_all_hosts(void) {
  522. RRDHOST *host;
  523. dfe_start_reentrant(rrdhost_root_index, host){
  524. health_apply_prototypes_to_host(host);
  525. }
  526. dfe_done(host);
  527. }
  528. // ---------------------------------------------------------------------------------------------------------------------
  529. void health_prototype_metadata_foreach(void *data, prototype_metadata_cb_t cb) {
  530. RRD_ALERT_PROTOTYPE *ap;
  531. dfe_start_read(health_globals.prototypes.dict, ap) {
  532. cb(data, ap->config.type, ap->config.component, ap->config.classification, ap->config.recipient);
  533. }
  534. dfe_done(ap);
  535. }