rrdcalc.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #define NETDATA_HEALTH_INTERNALS
  3. #include "rrd.h"
  4. // ----------------------------------------------------------------------------
  5. // RRDCALC management
  6. inline const char *rrdcalc_status2string(RRDCALC_STATUS status) {
  7. switch(status) {
  8. case RRDCALC_STATUS_REMOVED:
  9. return "REMOVED";
  10. case RRDCALC_STATUS_UNDEFINED:
  11. return "UNDEFINED";
  12. case RRDCALC_STATUS_UNINITIALIZED:
  13. return "UNINITIALIZED";
  14. case RRDCALC_STATUS_CLEAR:
  15. return "CLEAR";
  16. case RRDCALC_STATUS_RAISED:
  17. return "RAISED";
  18. case RRDCALC_STATUS_WARNING:
  19. return "WARNING";
  20. case RRDCALC_STATUS_CRITICAL:
  21. return "CRITICAL";
  22. default:
  23. error("Unknown alarm status %d", status);
  24. return "UNKNOWN";
  25. }
  26. }
  27. static void rrdsetcalc_link(RRDSET *st, RRDCALC *rc) {
  28. RRDHOST *host = st->rrdhost;
  29. debug(D_HEALTH, "Health linking alarm '%s.%s' to chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
  30. rc->last_status_change = now_realtime_sec();
  31. rc->rrdset = st;
  32. rc->rrdset_next = st->alarms;
  33. rc->rrdset_prev = NULL;
  34. if(rc->rrdset_next)
  35. rc->rrdset_next->rrdset_prev = rc;
  36. st->alarms = rc;
  37. if(rc->update_every < rc->rrdset->update_every) {
  38. error("Health alarm '%s.%s' has update every %d, less than chart update every %d. Setting alarm update frequency to %d.", rc->rrdset->id, rc->name, rc->update_every, rc->rrdset->update_every, rc->rrdset->update_every);
  39. rc->update_every = rc->rrdset->update_every;
  40. }
  41. if(!isnan(rc->green) && isnan(st->green)) {
  42. debug(D_HEALTH, "Health alarm '%s.%s' green threshold set from " CALCULATED_NUMBER_FORMAT_AUTO " to " CALCULATED_NUMBER_FORMAT_AUTO ".", rc->rrdset->id, rc->name, rc->rrdset->green, rc->green);
  43. st->green = rc->green;
  44. }
  45. if(!isnan(rc->red) && isnan(st->red)) {
  46. debug(D_HEALTH, "Health alarm '%s.%s' red threshold set from " CALCULATED_NUMBER_FORMAT_AUTO " to " CALCULATED_NUMBER_FORMAT_AUTO ".", rc->rrdset->id, rc->name, rc->rrdset->red, rc->red);
  47. st->red = rc->red;
  48. }
  49. rc->local = rrdvar_create_and_index("local", &st->rrdvar_root_index, rc->name, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_LOCAL_VAR, &rc->value);
  50. rc->family = rrdvar_create_and_index("family", &st->rrdfamily->rrdvar_root_index, rc->name, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_FAMILY_VAR, &rc->value);
  51. char fullname[RRDVAR_MAX_LENGTH + 1];
  52. snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->id, rc->name);
  53. rc->hostid = rrdvar_create_and_index("host", &host->rrdvar_root_index, fullname, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_HOST_CHARTID_VAR, &rc->value);
  54. snprintfz(fullname, RRDVAR_MAX_LENGTH, "%s.%s", st->name, rc->name);
  55. rc->hostname = rrdvar_create_and_index("host", &host->rrdvar_root_index, fullname, RRDVAR_TYPE_CALCULATED, RRDVAR_OPTION_RRDCALC_HOST_CHARTNAME_VAR, &rc->value);
  56. if(rc->hostid && !rc->hostname)
  57. rc->hostid->options |= RRDVAR_OPTION_RRDCALC_HOST_CHARTNAME_VAR;
  58. if(!rc->units) rc->units = strdupz(st->units);
  59. time_t now = now_realtime_sec();
  60. ALARM_ENTRY *ae = health_create_alarm_entry(
  61. host,
  62. rc->id,
  63. rc->next_event_id++,
  64. rc->config_hash_id,
  65. now,
  66. rc->name,
  67. rc->rrdset->id,
  68. rc->rrdset->family,
  69. rc->classification,
  70. rc->component,
  71. rc->type,
  72. rc->exec,
  73. rc->recipient,
  74. now - rc->last_status_change,
  75. rc->old_value,
  76. rc->value,
  77. rc->status,
  78. RRDCALC_STATUS_UNINITIALIZED,
  79. rc->source,
  80. rc->units,
  81. rc->info,
  82. 0,
  83. 0);
  84. health_alarm_log(host, ae);
  85. }
  86. static inline int rrdcalc_test_additional_restriction(RRDCALC *rc, RRDSET *st){
  87. if (rc->module_match && !simple_pattern_matches(rc->module_pattern, st->module_name))
  88. return 0;
  89. if (rc->plugin_match && !simple_pattern_matches(rc->plugin_pattern, st->plugin_name))
  90. return 0;
  91. if (rc->labels) {
  92. int labels_count=1;
  93. int labels_match=0;
  94. char *s = rc->labels;
  95. while (*s) {
  96. if (*s==' ')
  97. labels_count++;
  98. s++;
  99. }
  100. RRDHOST *host = st->rrdhost;
  101. char cmp[CONFIG_FILE_LINE_MAX+1];
  102. struct label *move = host->labels.head;
  103. while(move) {
  104. snprintf(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
  105. if (simple_pattern_matches(rc->splabels, move->key) ||
  106. simple_pattern_matches(rc->splabels, cmp)) {
  107. labels_match++;
  108. }
  109. move = move->next;
  110. }
  111. if (labels_match != labels_count)
  112. return 0;
  113. }
  114. return 1;
  115. }
  116. static inline int rrdcalc_is_matching_this_rrdset(RRDCALC *rc, RRDSET *st) {
  117. if(((rc->hash_chart == st->hash && !strcmp(rc->chart, st->id)) ||
  118. (rc->hash_chart == st->hash_name && !strcmp(rc->chart, st->name))) &&
  119. rrdcalc_test_additional_restriction(rc, st)) {
  120. return 1;
  121. }
  122. return 0;
  123. }
  124. // this has to be called while the RRDHOST is locked
  125. inline void rrdsetcalc_link_matching(RRDSET *st) {
  126. RRDHOST *host = st->rrdhost;
  127. // debug(D_HEALTH, "find matching alarms for chart '%s'", st->id);
  128. RRDCALC *rc;
  129. for(rc = host->alarms; rc ; rc = rc->next) {
  130. if(unlikely(rc->rrdset))
  131. continue;
  132. if(unlikely(rrdcalc_is_matching_this_rrdset(rc, st)))
  133. rrdsetcalc_link(st, rc);
  134. }
  135. }
  136. // this has to be called while the RRDHOST is locked
  137. inline void rrdsetcalc_unlink(RRDCALC *rc) {
  138. RRDSET *st = rc->rrdset;
  139. if(!st) {
  140. debug(D_HEALTH, "Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
  141. error("Requested to unlink RRDCALC '%s.%s' which is not linked to any RRDSET", rc->chart?rc->chart:"NOCHART", rc->name);
  142. return;
  143. }
  144. RRDHOST *host = st->rrdhost;
  145. time_t now = now_realtime_sec();
  146. ALARM_ENTRY *ae = health_create_alarm_entry(
  147. host,
  148. rc->id,
  149. rc->next_event_id++,
  150. rc->config_hash_id,
  151. now,
  152. rc->name,
  153. rc->rrdset->id,
  154. rc->rrdset->family,
  155. rc->classification,
  156. rc->component,
  157. rc->type,
  158. rc->exec,
  159. rc->recipient,
  160. now - rc->last_status_change,
  161. rc->old_value,
  162. rc->value,
  163. rc->status,
  164. RRDCALC_STATUS_REMOVED,
  165. rc->source,
  166. rc->units,
  167. rc->info,
  168. 0,
  169. 0);
  170. health_alarm_log(host, ae);
  171. debug(D_HEALTH, "Health unlinking alarm '%s.%s' from chart '%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, st->id, host->hostname);
  172. // unlink it
  173. if(rc->rrdset_prev)
  174. rc->rrdset_prev->rrdset_next = rc->rrdset_next;
  175. if(rc->rrdset_next)
  176. rc->rrdset_next->rrdset_prev = rc->rrdset_prev;
  177. if(st->alarms == rc)
  178. st->alarms = rc->rrdset_next;
  179. rc->rrdset_prev = rc->rrdset_next = NULL;
  180. rrdvar_free(host, &st->rrdvar_root_index, rc->local);
  181. rc->local = NULL;
  182. rrdvar_free(host, &st->rrdfamily->rrdvar_root_index, rc->family);
  183. rc->family = NULL;
  184. rrdvar_free(host, &host->rrdvar_root_index, rc->hostid);
  185. rc->hostid = NULL;
  186. rrdvar_free(host, &host->rrdvar_root_index, rc->hostname);
  187. rc->hostname = NULL;
  188. rc->rrdset = NULL;
  189. // RRDCALC will remain in RRDHOST
  190. // so that if the matching chart is found in the future
  191. // it will be applied automatically
  192. }
  193. RRDCALC *rrdcalc_find(RRDSET *st, const char *name) {
  194. RRDCALC *rc;
  195. uint32_t hash = simple_hash(name);
  196. for( rc = st->alarms; rc ; rc = rc->rrdset_next ) {
  197. if(unlikely(rc->hash == hash && !strcmp(rc->name, name)))
  198. return rc;
  199. }
  200. return NULL;
  201. }
  202. inline int rrdcalc_exists(RRDHOST *host, const char *chart, const char *name, uint32_t hash_chart, uint32_t hash_name) {
  203. RRDCALC *rc;
  204. if(unlikely(!chart)) {
  205. error("attempt to find RRDCALC '%s' without giving a chart name", name);
  206. return 1;
  207. }
  208. if(unlikely(!hash_chart)) hash_chart = simple_hash(chart);
  209. if(unlikely(!hash_name)) hash_name = simple_hash(name);
  210. // make sure it does not already exist
  211. for(rc = host->alarms; rc ; rc = rc->next) {
  212. if (unlikely(rc->chart && rc->hash == hash_name && rc->hash_chart == hash_chart && !strcmp(name, rc->name) && !strcmp(chart, rc->chart))) {
  213. debug(D_HEALTH, "Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
  214. info("Health alarm '%s.%s' already exists in host '%s'.", chart, name, host->hostname);
  215. return 1;
  216. }
  217. }
  218. return 0;
  219. }
  220. inline uint32_t rrdcalc_get_unique_id(RRDHOST *host, const char *chart, const char *name, uint32_t *next_event_id) {
  221. if(chart && name) {
  222. uint32_t hash_chart = simple_hash(chart);
  223. uint32_t hash_name = simple_hash(name);
  224. // re-use old IDs, by looking them up in the alarm log
  225. ALARM_ENTRY *ae;
  226. for(ae = host->health_log.alarms; ae ;ae = ae->next) {
  227. if(unlikely(ae->hash_name == hash_name && ae->hash_chart == hash_chart && !strcmp(name, ae->name) && !strcmp(chart, ae->chart))) {
  228. if(next_event_id) *next_event_id = ae->alarm_event_id + 1;
  229. return ae->alarm_id;
  230. }
  231. }
  232. }
  233. if (unlikely(!host->health_log.next_alarm_id))
  234. host->health_log.next_alarm_id = (uint32_t)now_realtime_sec();
  235. return host->health_log.next_alarm_id++;
  236. }
  237. /**
  238. * Alarm name with dimension
  239. *
  240. * Change the name of the current alarm appending a new diagram.
  241. *
  242. * @param name the alarm name
  243. * @param namelen is the length of the previous vector.
  244. * @param dim the dimension of the chart.
  245. * @param dimlen is the length of the previous vector.
  246. *
  247. * @return It returns the new name on success and the old otherwise
  248. */
  249. char *alarm_name_with_dim(char *name, size_t namelen, const char *dim, size_t dimlen) {
  250. char *newname,*move;
  251. newname = mallocz(namelen + dimlen + 2);
  252. move = newname;
  253. memcpy(move, name, namelen);
  254. move += namelen;
  255. *move++ = '_';
  256. memcpy(move, dim, dimlen);
  257. move += dimlen;
  258. *move = '\0';
  259. return newname;
  260. }
  261. /**
  262. * Remove pipe comma
  263. *
  264. * Remove the pipes and commas converting to space.
  265. *
  266. * @param str the string to change.
  267. */
  268. void dimension_remove_pipe_comma(char *str) {
  269. while(*str) {
  270. if(*str == '|' || *str == ',') *str = ' ';
  271. str++;
  272. }
  273. }
  274. inline void rrdcalc_add_to_host(RRDHOST *host, RRDCALC *rc) {
  275. rrdhost_check_rdlock(host);
  276. if(rc->calculation) {
  277. rc->calculation->status = &rc->status;
  278. rc->calculation->myself = &rc->value;
  279. rc->calculation->after = &rc->db_after;
  280. rc->calculation->before = &rc->db_before;
  281. rc->calculation->rrdcalc = rc;
  282. }
  283. if(rc->warning) {
  284. rc->warning->status = &rc->status;
  285. rc->warning->myself = &rc->value;
  286. rc->warning->after = &rc->db_after;
  287. rc->warning->before = &rc->db_before;
  288. rc->warning->rrdcalc = rc;
  289. }
  290. if(rc->critical) {
  291. rc->critical->status = &rc->status;
  292. rc->critical->myself = &rc->value;
  293. rc->critical->after = &rc->db_after;
  294. rc->critical->before = &rc->db_before;
  295. rc->critical->rrdcalc = rc;
  296. }
  297. if(!rc->foreachdim) {
  298. // link it to the host alarms list
  299. if(likely(host->alarms)) {
  300. // append it
  301. RRDCALC *t;
  302. for(t = host->alarms; t && t->next ; t = t->next) ;
  303. t->next = rc;
  304. }
  305. else {
  306. host->alarms = rc;
  307. }
  308. // link it to its chart
  309. RRDSET *st;
  310. rrdset_foreach_read(st, host) {
  311. if(rrdcalc_is_matching_this_rrdset(rc, st)) {
  312. rrdsetcalc_link(st, rc);
  313. break;
  314. }
  315. }
  316. } else {
  317. //link it case there is a foreach
  318. if(likely(host->alarms_with_foreach)) {
  319. // append it
  320. RRDCALC *t;
  321. for(t = host->alarms_with_foreach; t && t->next ; t = t->next) ;
  322. t->next = rc;
  323. }
  324. else {
  325. host->alarms_with_foreach = rc;
  326. }
  327. //I am not linking this alarm direct to the host here, this will be done when the children is created
  328. }
  329. }
  330. inline RRDCALC *rrdcalc_create_from_template(RRDHOST *host, RRDCALCTEMPLATE *rt, const char *chart) {
  331. debug(D_HEALTH, "Health creating dynamic alarm (from template) '%s.%s'", chart, rt->name);
  332. if(rrdcalc_exists(host, chart, rt->name, 0, 0))
  333. return NULL;
  334. RRDCALC *rc = callocz(1, sizeof(RRDCALC));
  335. rc->next_event_id = 1;
  336. rc->name = strdupz(rt->name);
  337. rc->hash = simple_hash(rc->name);
  338. rc->chart = strdupz(chart);
  339. rc->hash_chart = simple_hash(rc->chart);
  340. uuid_copy(rc->config_hash_id, rt->config_hash_id);
  341. rc->id = rrdcalc_get_unique_id(host, rc->chart, rc->name, &rc->next_event_id);
  342. if(rt->dimensions) rc->dimensions = strdupz(rt->dimensions);
  343. if(rt->foreachdim) {
  344. rc->foreachdim = strdupz(rt->foreachdim);
  345. rc->spdim = health_pattern_from_foreach(rc->foreachdim);
  346. }
  347. rc->foreachcounter = rt->foreachcounter;
  348. rc->green = rt->green;
  349. rc->red = rt->red;
  350. rc->value = NAN;
  351. rc->old_value = NAN;
  352. rc->delay_up_duration = rt->delay_up_duration;
  353. rc->delay_down_duration = rt->delay_down_duration;
  354. rc->delay_max_duration = rt->delay_max_duration;
  355. rc->delay_multiplier = rt->delay_multiplier;
  356. rc->last_repeat = 0;
  357. rc->times_repeat = 0;
  358. rc->warn_repeat_every = rt->warn_repeat_every;
  359. rc->crit_repeat_every = rt->crit_repeat_every;
  360. rc->group = rt->group;
  361. rc->after = rt->after;
  362. rc->before = rt->before;
  363. rc->update_every = rt->update_every;
  364. rc->options = rt->options;
  365. if(rt->exec) rc->exec = strdupz(rt->exec);
  366. if(rt->recipient) rc->recipient = strdupz(rt->recipient);
  367. if(rt->source) rc->source = strdupz(rt->source);
  368. if(rt->units) rc->units = strdupz(rt->units);
  369. if(rt->info) rc->info = strdupz(rt->info);
  370. if (rt->classification) rc->classification = strdupz(rt->classification);
  371. if (rt->component) rc->component = strdupz(rt->component);
  372. if (rt->type) rc->type = strdupz(rt->type);
  373. if(rt->calculation) {
  374. rc->calculation = expression_parse(rt->calculation->source, NULL, NULL);
  375. if(!rc->calculation)
  376. error("Health alarm '%s.%s': failed to parse calculation expression '%s'", chart, rt->name, rt->calculation->source);
  377. }
  378. if(rt->warning) {
  379. rc->warning = expression_parse(rt->warning->source, NULL, NULL);
  380. if(!rc->warning)
  381. error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", chart, rt->name, rt->warning->source);
  382. }
  383. if(rt->critical) {
  384. rc->critical = expression_parse(rt->critical->source, NULL, NULL);
  385. if(!rc->critical)
  386. error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", chart, rt->name, rt->critical->source);
  387. }
  388. debug(D_HEALTH, "Health runtime added alarm '%s.%s': exec '%s', recipient '%s', green " CALCULATED_NUMBER_FORMAT_AUTO ", red " CALCULATED_NUMBER_FORMAT_AUTO ", 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",
  389. (rc->chart)?rc->chart:"NOCHART",
  390. rc->name,
  391. (rc->exec)?rc->exec:"DEFAULT",
  392. (rc->recipient)?rc->recipient:"DEFAULT",
  393. rc->green,
  394. rc->red,
  395. (int)rc->group,
  396. rc->after,
  397. rc->before,
  398. rc->options,
  399. (rc->dimensions)?rc->dimensions:"NONE",
  400. (rc->foreachdim)?rc->foreachdim:"NONE",
  401. rc->update_every,
  402. (rc->calculation)?rc->calculation->parsed_as:"NONE",
  403. (rc->warning)?rc->warning->parsed_as:"NONE",
  404. (rc->critical)?rc->critical->parsed_as:"NONE",
  405. rc->source,
  406. rc->delay_up_duration,
  407. rc->delay_down_duration,
  408. rc->delay_max_duration,
  409. rc->delay_multiplier,
  410. rc->warn_repeat_every,
  411. rc->crit_repeat_every
  412. );
  413. rrdcalc_add_to_host(host, rc);
  414. if(!rt->foreachdim) {
  415. RRDCALC *rdcmp = (RRDCALC *) avl_insert_lock(&(host)->alarms_idx_health_log,(avl_t *)rc);
  416. if (rdcmp != rc) {
  417. error("Cannot insert the alarm index ID %s",rc->name);
  418. }
  419. }
  420. return rc;
  421. }
  422. /**
  423. * Create from RRDCALC
  424. *
  425. * Create a new alarm using another alarm as template.
  426. *
  427. * @param rc is the alarm that will be used as source
  428. * @param host is the host structure.
  429. * @param name is the newest chart name.
  430. * @param dimension is the current dimension
  431. * @param foreachdim the whole list of dimension
  432. *
  433. * @return it returns the new alarm changed.
  434. */
  435. inline RRDCALC *rrdcalc_create_from_rrdcalc(RRDCALC *rc, RRDHOST *host, const char *name, const char *dimension) {
  436. RRDCALC *newrc = callocz(1, sizeof(RRDCALC));
  437. newrc->next_event_id = 1;
  438. newrc->id = rrdcalc_get_unique_id(host, rc->chart, name, &rc->next_event_id);
  439. newrc->name = (char *)name;
  440. newrc->hash = simple_hash(newrc->name);
  441. newrc->chart = strdupz(rc->chart);
  442. newrc->hash_chart = simple_hash(rc->chart);
  443. uuid_copy(newrc->config_hash_id, *((uuid_t *) &rc->config_hash_id));
  444. newrc->dimensions = strdupz(dimension);
  445. newrc->foreachdim = NULL;
  446. rc->foreachcounter++;
  447. newrc->foreachcounter = rc->foreachcounter;
  448. newrc->green = rc->green;
  449. newrc->red = rc->red;
  450. newrc->value = NAN;
  451. newrc->old_value = NAN;
  452. newrc->delay_up_duration = rc->delay_up_duration;
  453. newrc->delay_down_duration = rc->delay_down_duration;
  454. newrc->delay_max_duration = rc->delay_max_duration;
  455. newrc->delay_multiplier = rc->delay_multiplier;
  456. newrc->last_repeat = 0;
  457. newrc->times_repeat = 0;
  458. newrc->warn_repeat_every = rc->warn_repeat_every;
  459. newrc->crit_repeat_every = rc->crit_repeat_every;
  460. newrc->group = rc->group;
  461. newrc->after = rc->after;
  462. newrc->before = rc->before;
  463. newrc->update_every = rc->update_every;
  464. newrc->options = rc->options;
  465. if(rc->exec) newrc->exec = strdupz(rc->exec);
  466. if(rc->recipient) newrc->recipient = strdupz(rc->recipient);
  467. if(rc->source) newrc->source = strdupz(rc->source);
  468. if(rc->units) newrc->units = strdupz(rc->units);
  469. if(rc->info) newrc->info = strdupz(rc->info);
  470. if (rc->classification) newrc->classification = strdupz(rc->classification);
  471. if (rc->component) newrc->component = strdupz(rc->component);
  472. if (rc->type) newrc->type = strdupz(rc->type);
  473. if(rc->calculation) {
  474. newrc->calculation = expression_parse(rc->calculation->source, NULL, NULL);
  475. if(!newrc->calculation)
  476. error("Health alarm '%s.%s': failed to parse calculation expression '%s'", rc->chart, rc->name, rc->calculation->source);
  477. }
  478. if(rc->warning) {
  479. newrc->warning = expression_parse(rc->warning->source, NULL, NULL);
  480. if(!newrc->warning)
  481. error("Health alarm '%s.%s': failed to re-parse warning expression '%s'", rc->chart, rc->name, rc->warning->source);
  482. }
  483. if(rc->critical) {
  484. newrc->critical = expression_parse(rc->critical->source, NULL, NULL);
  485. if(!newrc->critical)
  486. error("Health alarm '%s.%s': failed to re-parse critical expression '%s'", rc->chart, rc->name, rc->critical->source);
  487. }
  488. return newrc;
  489. }
  490. void rrdcalc_free(RRDCALC *rc) {
  491. if(unlikely(!rc)) return;
  492. expression_free(rc->calculation);
  493. expression_free(rc->warning);
  494. expression_free(rc->critical);
  495. freez(rc->name);
  496. freez(rc->chart);
  497. freez(rc->family);
  498. freez(rc->dimensions);
  499. freez(rc->foreachdim);
  500. freez(rc->exec);
  501. freez(rc->recipient);
  502. freez(rc->source);
  503. freez(rc->units);
  504. freez(rc->info);
  505. freez(rc->classification);
  506. freez(rc->component);
  507. freez(rc->type);
  508. simple_pattern_free(rc->spdim);
  509. freez(rc->labels);
  510. simple_pattern_free(rc->splabels);
  511. freez(rc->module_match);
  512. simple_pattern_free(rc->module_pattern);
  513. freez(rc->plugin_match);
  514. simple_pattern_free(rc->plugin_pattern);
  515. freez(rc);
  516. }
  517. void rrdcalc_unlink_and_free(RRDHOST *host, RRDCALC *rc) {
  518. if(unlikely(!rc)) return;
  519. debug(D_HEALTH, "Health removing alarm '%s.%s' of host '%s'", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
  520. // unlink it from RRDSET
  521. if(rc->rrdset) rrdsetcalc_unlink(rc);
  522. // unlink it from RRDHOST
  523. if(unlikely(rc == host->alarms))
  524. host->alarms = rc->next;
  525. else {
  526. RRDCALC *t;
  527. for(t = host->alarms; t && t->next != rc; t = t->next) ;
  528. if(t) {
  529. t->next = rc->next;
  530. rc->next = NULL;
  531. }
  532. else
  533. error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
  534. }
  535. RRDCALC *rdcmp = (RRDCALC *) avl_search_lock(&(host)->alarms_idx_health_log, (avl_t *)rc);
  536. if (rdcmp) {
  537. rdcmp = (RRDCALC *) avl_remove_lock(&(host)->alarms_idx_health_log, (avl_t *)rc);
  538. if (!rdcmp) {
  539. error("Cannot remove the health alarm index from health_log");
  540. }
  541. }
  542. rdcmp = (RRDCALC *) avl_search_lock(&(host)->alarms_idx_name, (avl_t *)rc);
  543. if (rdcmp) {
  544. rdcmp = (RRDCALC *) avl_remove_lock(&(host)->alarms_idx_name, (avl_t *)rc);
  545. if (!rdcmp) {
  546. error("Cannot remove the health alarm index from idx_name");
  547. }
  548. }
  549. rrdcalc_free(rc);
  550. }
  551. void rrdcalc_foreach_unlink_and_free(RRDHOST *host, RRDCALC *rc) {
  552. if(unlikely(rc == host->alarms_with_foreach))
  553. host->alarms_with_foreach = rc->next;
  554. else {
  555. RRDCALC *t;
  556. for(t = host->alarms_with_foreach; t && t->next != rc; t = t->next) ;
  557. if(t) {
  558. t->next = rc->next;
  559. rc->next = NULL;
  560. }
  561. else
  562. error("Cannot unlink alarm '%s.%s' from host '%s': not found", rc->chart?rc->chart:"NOCHART", rc->name, host->hostname);
  563. }
  564. rrdcalc_free(rc);
  565. }
  566. static void rrdcalc_labels_unlink_alarm_loop(RRDHOST *host, RRDCALC *alarms) {
  567. RRDCALC *rc = alarms;
  568. while (rc) {
  569. if (!rc->labels) {
  570. rc = rc->next;
  571. continue;
  572. }
  573. char cmp[CONFIG_FILE_LINE_MAX+1];
  574. struct label *move = host->labels.head;
  575. while(move) {
  576. snprintf(cmp, CONFIG_FILE_LINE_MAX, "%s=%s", move->key, move->value);
  577. if (simple_pattern_matches(rc->splabels, move->key) ||
  578. simple_pattern_matches(rc->splabels, cmp)) {
  579. break;
  580. }
  581. move = move->next;
  582. }
  583. RRDCALC *next = rc->next;
  584. if(!move) {
  585. info("Health configuration for alarm '%s' cannot be applied, because the host %s does not have the label(s) '%s'",
  586. rc->name,
  587. host->hostname,
  588. rc->labels);
  589. if(host->alarms == alarms) {
  590. rrdcalc_unlink_and_free(host, rc);
  591. } else
  592. rrdcalc_foreach_unlink_and_free(host, rc);
  593. }
  594. rc = next;
  595. }
  596. }
  597. void rrdcalc_labels_unlink_alarm_from_host(RRDHOST *host) {
  598. rrdhost_check_rdlock(host);
  599. netdata_rwlock_rdlock(&host->labels.labels_rwlock);
  600. rrdcalc_labels_unlink_alarm_loop(host, host->alarms);
  601. rrdcalc_labels_unlink_alarm_loop(host, host->alarms_with_foreach);
  602. netdata_rwlock_unlock(&host->labels.labels_rwlock);
  603. }
  604. void rrdcalc_labels_unlink() {
  605. rrd_rdlock();
  606. RRDHOST *host;
  607. rrdhost_foreach_read(host) {
  608. if (unlikely(!host->health_enabled))
  609. continue;
  610. if (host->labels.head) {
  611. rrdhost_wrlock(host);
  612. rrdcalc_labels_unlink_alarm_from_host(host);
  613. rrdhost_unlock(host);
  614. }
  615. }
  616. rrd_unlock();
  617. }
  618. // ----------------------------------------------------------------------------
  619. // Alarm
  620. /**
  621. * Alarm is repeating
  622. *
  623. * Is this alarm repeating ?
  624. *
  625. * @param host The structure that has the binary tree
  626. * @param alarm_id the id of the alarm to search
  627. *
  628. * @return It returns 1 case it is repeating and 0 otherwise
  629. */
  630. int alarm_isrepeating(RRDHOST *host, uint32_t alarm_id) {
  631. RRDCALC findme;
  632. findme.id = alarm_id;
  633. RRDCALC *rc = (RRDCALC *)avl_search_lock(&host->alarms_idx_health_log, (avl_t *)&findme);
  634. if (!rc) {
  635. return 0;
  636. }
  637. return rrdcalc_isrepeating(rc);
  638. }
  639. /**
  640. * Entry is repeating
  641. *
  642. * Check whether the id of alarm entry is yet present in the host structure
  643. *
  644. * @param host The structure that has the binary tree
  645. * @param ae the alarm entry
  646. *
  647. * @return It returns 1 case it is repeating and 0 otherwise
  648. */
  649. int alarm_entry_isrepeating(RRDHOST *host, ALARM_ENTRY *ae) {
  650. return alarm_isrepeating(host, ae->alarm_id);
  651. }
  652. /**
  653. * Max last repeat
  654. *
  655. * Check the maximum last_repeat for the alarms associated a host
  656. *
  657. * @param host The structure that has the binary tree
  658. *
  659. * @return It returns 1 case it is repeating and 0 otherwise
  660. */
  661. RRDCALC *alarm_max_last_repeat(RRDHOST *host, char *alarm_name,uint32_t hash) {
  662. RRDCALC findme;
  663. findme.name = alarm_name;
  664. findme.hash = hash;
  665. RRDCALC *rc = (RRDCALC *)avl_search_lock(&host->alarms_idx_name, (avl_t *)&findme);
  666. return rc;
  667. }