pluginsd_parser.c 76 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "pluginsd_parser.h"
  3. #define LOG_FUNCTIONS false
  4. static int send_to_plugin(const char *txt, void *data) {
  5. PARSER *parser = data;
  6. if(!txt || !*txt)
  7. return 0;
  8. #ifdef ENABLE_HTTPS
  9. struct netdata_ssl *ssl = parser->ssl_output;
  10. if(ssl) {
  11. if(ssl->conn && ssl->flags == NETDATA_SSL_HANDSHAKE_COMPLETE)
  12. return (int)netdata_ssl_write(ssl->conn, (void *)txt, strlen(txt));
  13. error("PLUGINSD: cannot send command (SSL)");
  14. return -1;
  15. }
  16. #endif
  17. if(parser->fp_output) {
  18. int bytes = fprintf(parser->fp_output, "%s", txt);
  19. if(bytes <= 0) {
  20. error("PLUGINSD: cannot send command (FILE)");
  21. return -2;
  22. }
  23. fflush(parser->fp_output);
  24. return bytes;
  25. }
  26. if(parser->fd != -1) {
  27. size_t bytes = 0;
  28. size_t total = strlen(txt);
  29. ssize_t sent;
  30. do {
  31. sent = write(parser->fd, &txt[bytes], total - bytes);
  32. if(sent <= 0) {
  33. error("PLUGINSD: cannot send command (fd)");
  34. return -3;
  35. }
  36. bytes += sent;
  37. }
  38. while(bytes < total);
  39. return (int)bytes;
  40. }
  41. error("PLUGINSD: cannot send command (no output socket/pipe/file given to plugins.d parser)");
  42. return -4;
  43. }
  44. static inline RRDHOST *pluginsd_require_host_from_parent(void *user, const char *cmd) {
  45. RRDHOST *host = ((PARSER_USER_OBJECT *) user)->host;
  46. if(unlikely(!host))
  47. error("PLUGINSD: command %s requires a host, but is not set.", cmd);
  48. return host;
  49. }
  50. static inline RRDSET *pluginsd_require_chart_from_parent(void *user, const char *cmd, const char *parent_cmd) {
  51. RRDSET *st = ((PARSER_USER_OBJECT *) user)->st;
  52. if(unlikely(!st))
  53. error("PLUGINSD: command %s requires a chart defined via command %s, but is not set.", cmd, parent_cmd);
  54. return st;
  55. }
  56. static inline RRDSET *pluginsd_get_chart_from_parent(void *user) {
  57. return ((PARSER_USER_OBJECT *) user)->st;
  58. }
  59. static inline void pluginsd_lock_rrdset_data_collection(void *user) {
  60. PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
  61. if(u->st && !u->v2.locked_data_collection) {
  62. netdata_spinlock_lock(&u->st->data_collection_lock);
  63. u->v2.locked_data_collection = true;
  64. }
  65. }
  66. static inline bool pluginsd_unlock_rrdset_data_collection(void *user) {
  67. PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
  68. if(u->st && u->v2.locked_data_collection) {
  69. netdata_spinlock_unlock(&u->st->data_collection_lock);
  70. u->v2.locked_data_collection = false;
  71. return true;
  72. }
  73. return false;
  74. }
  75. void pluginsd_rrdset_cleanup(RRDSET *st) {
  76. for(size_t i = 0; i < st->pluginsd.used ; i++) {
  77. if (st->pluginsd.rda[i]) {
  78. rrddim_acquired_release(st->pluginsd.rda[i]);
  79. st->pluginsd.rda[i] = NULL;
  80. }
  81. }
  82. freez(st->pluginsd.rda);
  83. st->pluginsd.rda = NULL;
  84. st->pluginsd.size = 0;
  85. st->pluginsd.used = 0;
  86. st->pluginsd.pos = 0;
  87. }
  88. static inline void pluginsd_set_chart_from_parent(void *user, RRDSET *st, const char *keyword) {
  89. PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
  90. if(unlikely(pluginsd_unlock_rrdset_data_collection(user))) {
  91. error("PLUGINSD: 'host:%s/chart:%s/' stale data collection lock found during %s; it has been unlocked",
  92. rrdhost_hostname(u->st->rrdhost), rrdset_id(u->st), keyword);
  93. }
  94. if(unlikely(u->v2.ml_locked)) {
  95. ml_chart_update_end(u->st);
  96. u->v2.ml_locked = false;
  97. error("PLUGINSD: 'host:%s/chart:%s/' stale ML lock found during %s, it has been unlocked",
  98. rrdhost_hostname(u->st->rrdhost), rrdset_id(u->st), keyword);
  99. }
  100. if(st) {
  101. size_t dims = dictionary_entries(st->rrddim_root_index);
  102. if(unlikely(st->pluginsd.size < dims)) {
  103. st->pluginsd.rda = reallocz(st->pluginsd.rda, dims * sizeof(RRDDIM_ACQUIRED *));
  104. st->pluginsd.size = dims;
  105. }
  106. if(st->pluginsd.pos > st->pluginsd.used && st->pluginsd.pos <= st->pluginsd.size)
  107. st->pluginsd.used = st->pluginsd.pos;
  108. st->pluginsd.pos = 0;
  109. }
  110. u->st = st;
  111. }
  112. static inline RRDDIM *pluginsd_acquire_dimension(RRDHOST *host, RRDSET *st, const char *dimension, const char *cmd) {
  113. if (unlikely(!dimension || !*dimension)) {
  114. error("PLUGINSD: 'host:%s/chart:%s' got a %s, without a dimension.",
  115. rrdhost_hostname(host), rrdset_id(st), cmd);
  116. return NULL;
  117. }
  118. RRDDIM_ACQUIRED *rda;
  119. if(likely(st->pluginsd.pos < st->pluginsd.used)) {
  120. rda = st->pluginsd.rda[st->pluginsd.pos];
  121. RRDDIM *rd = rrddim_acquired_to_rrddim(rda);
  122. if (likely(rd && string_strcmp(rd->id, dimension) == 0)) {
  123. st->pluginsd.pos++;
  124. return rd;
  125. }
  126. else {
  127. rrddim_acquired_release(rda);
  128. st->pluginsd.rda[st->pluginsd.pos] = NULL;
  129. }
  130. }
  131. rda = rrddim_find_and_acquire(st, dimension);
  132. if (unlikely(!rda)) {
  133. error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s but dimension does not exist.",
  134. rrdhost_hostname(host), rrdset_id(st), dimension, cmd);
  135. return NULL;
  136. }
  137. if(likely(st->pluginsd.pos < st->pluginsd.size))
  138. st->pluginsd.rda[st->pluginsd.pos++] = rda;
  139. return rrddim_acquired_to_rrddim(rda);
  140. }
  141. static inline RRDSET *pluginsd_find_chart(RRDHOST *host, const char *chart, const char *cmd) {
  142. if (unlikely(!chart || !*chart)) {
  143. error("PLUGINSD: 'host:%s' got a %s without a chart id.",
  144. rrdhost_hostname(host), cmd);
  145. return NULL;
  146. }
  147. RRDSET *st = rrdset_find(host, chart);
  148. if (unlikely(!st))
  149. error("PLUGINSD: 'host:%s/chart:%s' got a %s but chart does not exist.",
  150. rrdhost_hostname(host), chart, cmd);
  151. return st;
  152. }
  153. static inline PARSER_RC PLUGINSD_DISABLE_PLUGIN(void *user, const char *keyword, const char *msg) {
  154. ((PARSER_USER_OBJECT *) user)->enabled = 0;
  155. if(keyword && msg) {
  156. error_limit_static_global_var(erl, 1, 0);
  157. error_limit(&erl, "PLUGINSD: keyword %s: %s", keyword, msg);
  158. }
  159. return PARSER_RC_ERROR;
  160. }
  161. PARSER_RC pluginsd_set(char **words, size_t num_words, void *user)
  162. {
  163. char *dimension = get_word(words, num_words, 1);
  164. char *value = get_word(words, num_words, 2);
  165. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_SET);
  166. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  167. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_SET, PLUGINSD_KEYWORD_CHART);
  168. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  169. RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_SET);
  170. if(!rd) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  171. if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
  172. debug(D_PLUGINSD, "PLUGINSD: 'host:%s/chart:%s/dim:%s' SET is setting value to '%s'",
  173. rrdhost_hostname(host), rrdset_id(st), dimension, value && *value ? value : "UNSET");
  174. if (value && *value)
  175. rrddim_set_by_pointer(st, rd, str2ll_encoded(value));
  176. return PARSER_RC_OK;
  177. }
  178. PARSER_RC pluginsd_begin(char **words, size_t num_words, void *user)
  179. {
  180. char *id = get_word(words, num_words, 1);
  181. char *microseconds_txt = get_word(words, num_words, 2);
  182. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_BEGIN);
  183. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  184. RRDSET *st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_BEGIN);
  185. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  186. pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_BEGIN);
  187. usec_t microseconds = 0;
  188. if (microseconds_txt && *microseconds_txt) {
  189. long long t = str2ll(microseconds_txt, NULL);
  190. if(t >= 0)
  191. microseconds = t;
  192. }
  193. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  194. if(st->replay.log_next_data_collection) {
  195. st->replay.log_next_data_collection = false;
  196. internal_error(true,
  197. "REPLAY: 'host:%s/chart:%s' first BEGIN after replication, last collected %llu, last updated %llu, microseconds %llu",
  198. rrdhost_hostname(host), rrdset_id(st),
  199. st->last_collected_time.tv_sec * USEC_PER_SEC + st->last_collected_time.tv_usec,
  200. st->last_updated.tv_sec * USEC_PER_SEC + st->last_updated.tv_usec,
  201. microseconds
  202. );
  203. }
  204. #endif
  205. if (likely(st->counter_done)) {
  206. if (likely(microseconds)) {
  207. if (((PARSER_USER_OBJECT *)user)->trust_durations)
  208. rrdset_next_usec_unfiltered(st, microseconds);
  209. else
  210. rrdset_next_usec(st, microseconds);
  211. }
  212. else
  213. rrdset_next(st);
  214. }
  215. return PARSER_RC_OK;
  216. }
  217. PARSER_RC pluginsd_end(char **words, size_t num_words, void *user)
  218. {
  219. UNUSED(words);
  220. UNUSED(num_words);
  221. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_END);
  222. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  223. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_END, PLUGINSD_KEYWORD_BEGIN);
  224. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  225. if (unlikely(rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
  226. debug(D_PLUGINSD, "requested an END on chart '%s'", rrdset_id(st));
  227. pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_END);
  228. ((PARSER_USER_OBJECT *) user)->data_collections_count++;
  229. struct timeval now;
  230. now_realtime_timeval(&now);
  231. rrdset_timed_done(st, now, /* pending_rrdset_next = */ false);
  232. return PARSER_RC_OK;
  233. }
  234. static void pluginsd_host_define_cleanup(void *user) {
  235. PARSER_USER_OBJECT *u = user;
  236. string_freez(u->host_define.hostname);
  237. dictionary_destroy(u->host_define.rrdlabels);
  238. u->host_define.hostname = NULL;
  239. u->host_define.rrdlabels = NULL;
  240. u->host_define.parsing_host = false;
  241. }
  242. static inline bool pluginsd_validate_machine_guid(const char *guid, uuid_t *uuid, char *output) {
  243. if(uuid_parse(guid, *uuid))
  244. return false;
  245. uuid_unparse_lower(*uuid, output);
  246. return true;
  247. }
  248. static PARSER_RC pluginsd_host_define(char **words, size_t num_words, void *user) {
  249. PARSER_USER_OBJECT *u = user;
  250. char *guid = get_word(words, num_words, 1);
  251. char *hostname = get_word(words, num_words, 2);
  252. if(unlikely(!guid || !*guid || !hostname || !*hostname))
  253. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE, "missing parameters");
  254. if(unlikely(u->host_define.parsing_host))
  255. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE,
  256. "another host definition is already open - did you send " PLUGINSD_KEYWORD_HOST_DEFINE_END "?");
  257. if(!pluginsd_validate_machine_guid(guid, &u->host_define.machine_guid, u->host_define.machine_guid_str))
  258. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE, "cannot parse MACHINE_GUID - is it a valid UUID?");
  259. u->host_define.hostname = string_strdupz(hostname);
  260. u->host_define.rrdlabels = rrdlabels_create();
  261. u->host_define.parsing_host = true;
  262. return PARSER_RC_OK;
  263. }
  264. static inline PARSER_RC pluginsd_host_dictionary(char **words, size_t num_words, void *user, DICTIONARY *dict, const char *keyword) {
  265. PARSER_USER_OBJECT *u = user;
  266. char *name = get_word(words, num_words, 1);
  267. char *value = get_word(words, num_words, 2);
  268. if(!name || !*name || !value)
  269. return PLUGINSD_DISABLE_PLUGIN(user, keyword, "missing parameters");
  270. if(!u->host_define.parsing_host || !dict)
  271. return PLUGINSD_DISABLE_PLUGIN(user, keyword, "host is not defined, send " PLUGINSD_KEYWORD_HOST_DEFINE " before this");
  272. rrdlabels_add(dict, name, value, RRDLABEL_SRC_CONFIG);
  273. return PARSER_RC_OK;
  274. }
  275. static PARSER_RC pluginsd_host_labels(char **words, size_t num_words, void *user) {
  276. PARSER_USER_OBJECT *u = user;
  277. return pluginsd_host_dictionary(words, num_words, user, u->host_define.rrdlabels, PLUGINSD_KEYWORD_HOST_LABEL);
  278. }
  279. static PARSER_RC pluginsd_host_define_end(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) {
  280. PARSER_USER_OBJECT *u = user;
  281. if(!u->host_define.parsing_host)
  282. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST_DEFINE_END, "missing initialization, send " PLUGINSD_KEYWORD_HOST_DEFINE " before this");
  283. RRDHOST *host = rrdhost_find_or_create(
  284. string2str(u->host_define.hostname),
  285. string2str(u->host_define.hostname),
  286. u->host_define.machine_guid_str,
  287. "Netdata Virtual Host 1.0",
  288. netdata_configured_timezone,
  289. netdata_configured_abbrev_timezone,
  290. netdata_configured_utc_offset,
  291. NULL,
  292. program_name,
  293. program_version,
  294. default_rrd_update_every,
  295. default_rrd_history_entries,
  296. default_rrd_memory_mode,
  297. default_health_enabled,
  298. default_rrdpush_enabled,
  299. default_rrdpush_destination,
  300. default_rrdpush_api_key,
  301. default_rrdpush_send_charts_matching,
  302. default_rrdpush_enable_replication,
  303. default_rrdpush_seconds_to_replicate,
  304. default_rrdpush_replication_step,
  305. rrdhost_labels_to_system_info(u->host_define.rrdlabels),
  306. false
  307. );
  308. if(host->rrdlabels) {
  309. rrdlabels_migrate_to_these(host->rrdlabels, u->host_define.rrdlabels);
  310. }
  311. else {
  312. host->rrdlabels = u->host_define.rrdlabels;
  313. u->host_define.rrdlabels = NULL;
  314. }
  315. pluginsd_host_define_cleanup(user);
  316. u->host = host;
  317. pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_HOST_DEFINE_END);
  318. rrdhost_flag_clear(host, RRDHOST_FLAG_ORPHAN);
  319. rrdcontext_host_child_connected(host);
  320. schedule_node_info_update(host);
  321. return PARSER_RC_OK;
  322. }
  323. static PARSER_RC pluginsd_host(char **words, size_t num_words, void *user) {
  324. PARSER_USER_OBJECT *u = user;
  325. char *guid = get_word(words, num_words, 1);
  326. if(!guid || !*guid || strcmp(guid, "localhost") == 0) {
  327. u->host = localhost;
  328. return PARSER_RC_OK;
  329. }
  330. uuid_t uuid;
  331. char uuid_str[UUID_STR_LEN];
  332. if(!pluginsd_validate_machine_guid(guid, &uuid, uuid_str))
  333. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST, "cannot parse MACHINE_GUID - is it a valid UUID?");
  334. RRDHOST *host = rrdhost_find_by_guid(uuid_str);
  335. if(unlikely(!host))
  336. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_HOST, "cannot find a host with this machine guid - have you created it?");
  337. u->host = host;
  338. return PARSER_RC_OK;
  339. }
  340. PARSER_RC pluginsd_chart(char **words, size_t num_words, void *user)
  341. {
  342. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART);
  343. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  344. char *type = get_word(words, num_words, 1);
  345. char *name = get_word(words, num_words, 2);
  346. char *title = get_word(words, num_words, 3);
  347. char *units = get_word(words, num_words, 4);
  348. char *family = get_word(words, num_words, 5);
  349. char *context = get_word(words, num_words, 6);
  350. char *chart = get_word(words, num_words, 7);
  351. char *priority_s = get_word(words, num_words, 8);
  352. char *update_every_s = get_word(words, num_words, 9);
  353. char *options = get_word(words, num_words, 10);
  354. char *plugin = get_word(words, num_words, 11);
  355. char *module = get_word(words, num_words, 12);
  356. // parse the id from type
  357. char *id = NULL;
  358. if (likely(type && (id = strchr(type, '.')))) {
  359. *id = '\0';
  360. id++;
  361. }
  362. // make sure we have the required variables
  363. if (unlikely((!type || !*type || !id || !*id)))
  364. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_CHART, "missing parameters");
  365. // parse the name, and make sure it does not include 'type.'
  366. if (unlikely(name && *name)) {
  367. // when data are streamed from child nodes
  368. // name will be type.name
  369. // so, we have to remove 'type.' from name too
  370. size_t len = strlen(type);
  371. if (strncmp(type, name, len) == 0 && name[len] == '.')
  372. name = &name[len + 1];
  373. // if the name is the same with the id,
  374. // or is just 'NULL', clear it.
  375. if (unlikely(strcmp(name, id) == 0 || strcasecmp(name, "NULL") == 0 || strcasecmp(name, "(NULL)") == 0))
  376. name = NULL;
  377. }
  378. int priority = 1000;
  379. if (likely(priority_s && *priority_s))
  380. priority = str2i(priority_s);
  381. int update_every = ((PARSER_USER_OBJECT *) user)->cd->update_every;
  382. if (likely(update_every_s && *update_every_s))
  383. update_every = str2i(update_every_s);
  384. if (unlikely(!update_every))
  385. update_every = ((PARSER_USER_OBJECT *) user)->cd->update_every;
  386. RRDSET_TYPE chart_type = RRDSET_TYPE_LINE;
  387. if (unlikely(chart))
  388. chart_type = rrdset_type_id(chart);
  389. if (unlikely(name && !*name))
  390. name = NULL;
  391. if (unlikely(family && !*family))
  392. family = NULL;
  393. if (unlikely(context && !*context))
  394. context = NULL;
  395. if (unlikely(!title))
  396. title = "";
  397. if (unlikely(!units))
  398. units = "unknown";
  399. debug(
  400. D_PLUGINSD,
  401. "creating chart type='%s', id='%s', name='%s', family='%s', context='%s', chart='%s', priority=%d, update_every=%d",
  402. type, id, name ? name : "", family ? family : "", context ? context : "", rrdset_type_name(chart_type),
  403. priority, update_every);
  404. RRDSET *st = NULL;
  405. st = rrdset_create(
  406. host, type, id, name, family, context, title, units,
  407. (plugin && *plugin) ? plugin : ((PARSER_USER_OBJECT *)user)->cd->filename,
  408. module, priority, update_every,
  409. chart_type);
  410. if (likely(st)) {
  411. if (options && *options) {
  412. if (strstr(options, "obsolete"))
  413. rrdset_is_obsolete(st);
  414. else
  415. rrdset_isnot_obsolete(st);
  416. if (strstr(options, "detail"))
  417. rrdset_flag_set(st, RRDSET_FLAG_DETAIL);
  418. else
  419. rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
  420. if (strstr(options, "hidden"))
  421. rrdset_flag_set(st, RRDSET_FLAG_HIDDEN);
  422. else
  423. rrdset_flag_clear(st, RRDSET_FLAG_HIDDEN);
  424. if (strstr(options, "store_first"))
  425. rrdset_flag_set(st, RRDSET_FLAG_STORE_FIRST);
  426. else
  427. rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
  428. } else {
  429. rrdset_isnot_obsolete(st);
  430. rrdset_flag_clear(st, RRDSET_FLAG_DETAIL);
  431. rrdset_flag_clear(st, RRDSET_FLAG_STORE_FIRST);
  432. }
  433. }
  434. pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_CHART);
  435. return PARSER_RC_OK;
  436. }
  437. PARSER_RC pluginsd_chart_definition_end(char **words, size_t num_words, void *user)
  438. {
  439. const char *first_entry_txt = get_word(words, num_words, 1);
  440. const char *last_entry_txt = get_word(words, num_words, 2);
  441. const char *wall_clock_time_txt = get_word(words, num_words, 3);
  442. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CHART_DEFINITION_END);
  443. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  444. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_CHART_DEFINITION_END, PLUGINSD_KEYWORD_CHART);
  445. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  446. time_t first_entry_child = (first_entry_txt && *first_entry_txt) ? (time_t)str2ul(first_entry_txt) : 0;
  447. time_t last_entry_child = (last_entry_txt && *last_entry_txt) ? (time_t)str2ul(last_entry_txt) : 0;
  448. time_t child_wall_clock_time = (wall_clock_time_txt && *wall_clock_time_txt) ? (time_t)str2ul(wall_clock_time_txt) : now_realtime_sec();
  449. bool ok = true;
  450. if(!rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) {
  451. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  452. st->replay.start_streaming = false;
  453. st->replay.after = 0;
  454. st->replay.before = 0;
  455. #endif
  456. rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
  457. rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
  458. rrdhost_receiver_replicating_charts_plus_one(st->rrdhost);
  459. PARSER *parser = ((PARSER_USER_OBJECT *)user)->parser;
  460. ok = replicate_chart_request(send_to_plugin, parser, host, st,
  461. first_entry_child, last_entry_child, child_wall_clock_time,
  462. 0, 0);
  463. }
  464. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  465. else {
  466. internal_error(true, "REPLAY: 'host:%s/chart:%s' not sending duplicate replication request",
  467. rrdhost_hostname(st->rrdhost), rrdset_id(st));
  468. }
  469. #endif
  470. return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
  471. }
  472. PARSER_RC pluginsd_dimension(char **words, size_t num_words, void *user)
  473. {
  474. char *id = get_word(words, num_words, 1);
  475. char *name = get_word(words, num_words, 2);
  476. char *algorithm = get_word(words, num_words, 3);
  477. char *multiplier_s = get_word(words, num_words, 4);
  478. char *divisor_s = get_word(words, num_words, 5);
  479. char *options = get_word(words, num_words, 6);
  480. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_DIMENSION);
  481. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  482. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_DIMENSION, PLUGINSD_KEYWORD_CHART);
  483. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  484. if (unlikely(!id))
  485. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_DIMENSION, "missing dimension id");
  486. long multiplier = 1;
  487. if (multiplier_s && *multiplier_s) {
  488. multiplier = str2ll_encoded(multiplier_s);
  489. if (unlikely(!multiplier))
  490. multiplier = 1;
  491. }
  492. long divisor = 1;
  493. if (likely(divisor_s && *divisor_s)) {
  494. divisor = str2ll_encoded(divisor_s);
  495. if (unlikely(!divisor))
  496. divisor = 1;
  497. }
  498. if (unlikely(!algorithm || !*algorithm))
  499. algorithm = "absolute";
  500. if (unlikely(st && rrdset_flag_check(st, RRDSET_FLAG_DEBUG)))
  501. debug(
  502. D_PLUGINSD,
  503. "creating dimension in chart %s, id='%s', name='%s', algorithm='%s', multiplier=%ld, divisor=%ld, hidden='%s'",
  504. rrdset_id(st), id, name ? name : "", rrd_algorithm_name(rrd_algorithm_id(algorithm)), multiplier, divisor,
  505. options ? options : "");
  506. RRDDIM *rd = rrddim_add(st, id, name, multiplier, divisor, rrd_algorithm_id(algorithm));
  507. int unhide_dimension = 1;
  508. rrddim_option_clear(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS);
  509. if (options && *options) {
  510. if (strstr(options, "obsolete") != NULL)
  511. rrddim_is_obsolete(st, rd);
  512. else
  513. rrddim_isnot_obsolete(st, rd);
  514. unhide_dimension = !strstr(options, "hidden");
  515. if (strstr(options, "noreset") != NULL)
  516. rrddim_option_set(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS);
  517. if (strstr(options, "nooverflow") != NULL)
  518. rrddim_option_set(rd, RRDDIM_OPTION_DONT_DETECT_RESETS_OR_OVERFLOWS);
  519. } else
  520. rrddim_isnot_obsolete(st, rd);
  521. bool should_update_dimension = false;
  522. if (likely(unhide_dimension)) {
  523. rrddim_option_clear(rd, RRDDIM_OPTION_HIDDEN);
  524. should_update_dimension = rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN);
  525. }
  526. else {
  527. rrddim_option_set(rd, RRDDIM_OPTION_HIDDEN);
  528. should_update_dimension = !rrddim_flag_check(rd, RRDDIM_FLAG_META_HIDDEN);
  529. }
  530. if (should_update_dimension) {
  531. rrddim_flag_set(rd, RRDDIM_FLAG_METADATA_UPDATE);
  532. rrdhost_flag_set(rd->rrdset->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
  533. }
  534. return PARSER_RC_OK;
  535. }
  536. // ----------------------------------------------------------------------------
  537. // execution of functions
  538. struct inflight_function {
  539. int code;
  540. int timeout;
  541. BUFFER *destination_wb;
  542. STRING *function;
  543. void (*callback)(BUFFER *wb, int code, void *callback_data);
  544. void *callback_data;
  545. usec_t timeout_ut;
  546. usec_t started_ut;
  547. usec_t sent_ut;
  548. };
  549. static void inflight_functions_insert_callback(const DICTIONARY_ITEM *item, void *func, void *parser_ptr) {
  550. struct inflight_function *pf = func;
  551. PARSER *parser = parser_ptr;
  552. // leave this code as default, so that when the dictionary is destroyed this will be sent back to the caller
  553. pf->code = HTTP_RESP_GATEWAY_TIMEOUT;
  554. char buffer[2048 + 1];
  555. snprintfz(buffer, 2048, "FUNCTION %s %d \"%s\"\n",
  556. dictionary_acquired_item_name(item),
  557. pf->timeout,
  558. string2str(pf->function));
  559. // send the command to the plugin
  560. int ret = send_to_plugin(buffer, parser);
  561. pf->sent_ut = now_realtime_usec();
  562. if(ret < 0) {
  563. error("FUNCTION: failed to send function to plugin, error %d", ret);
  564. rrd_call_function_error(pf->destination_wb, "Failed to communicate with collector", HTTP_RESP_BACKEND_FETCH_FAILED);
  565. }
  566. else {
  567. internal_error(LOG_FUNCTIONS,
  568. "FUNCTION '%s' with transaction '%s' sent to collector (%d bytes, in %llu usec)",
  569. string2str(pf->function), dictionary_acquired_item_name(item), ret,
  570. pf->sent_ut - pf->started_ut);
  571. }
  572. }
  573. static bool inflight_functions_conflict_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func __maybe_unused, void *new_func, void *parser_ptr __maybe_unused) {
  574. struct inflight_function *pf = new_func;
  575. error("PLUGINSD_PARSER: duplicate UUID on pending function '%s' detected. Ignoring the second one.", string2str(pf->function));
  576. pf->code = rrd_call_function_error(pf->destination_wb, "This request is already in progress", HTTP_RESP_BAD_REQUEST);
  577. pf->callback(pf->destination_wb, pf->code, pf->callback_data);
  578. string_freez(pf->function);
  579. return false;
  580. }
  581. static void inflight_functions_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, void *func, void *parser_ptr __maybe_unused) {
  582. struct inflight_function *pf = func;
  583. internal_error(LOG_FUNCTIONS,
  584. "FUNCTION '%s' result of transaction '%s' received from collector (%zu bytes, request %llu usec, response %llu usec)",
  585. string2str(pf->function), dictionary_acquired_item_name(item),
  586. buffer_strlen(pf->destination_wb), pf->sent_ut - pf->started_ut, now_realtime_usec() - pf->sent_ut);
  587. pf->callback(pf->destination_wb, pf->code, pf->callback_data);
  588. string_freez(pf->function);
  589. }
  590. void inflight_functions_init(PARSER *parser) {
  591. parser->inflight.functions = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE, &dictionary_stats_category_functions, 0);
  592. dictionary_register_insert_callback(parser->inflight.functions, inflight_functions_insert_callback, parser);
  593. dictionary_register_delete_callback(parser->inflight.functions, inflight_functions_delete_callback, parser);
  594. dictionary_register_conflict_callback(parser->inflight.functions, inflight_functions_conflict_callback, parser);
  595. }
  596. static void inflight_functions_garbage_collect(PARSER *parser, usec_t now) {
  597. parser->inflight.smaller_timeout = 0;
  598. struct inflight_function *pf;
  599. dfe_start_write(parser->inflight.functions, pf) {
  600. if (pf->timeout_ut < now) {
  601. internal_error(true,
  602. "FUNCTION '%s' removing expired transaction '%s', after %llu usec.",
  603. string2str(pf->function), pf_dfe.name, now - pf->started_ut);
  604. if(!buffer_strlen(pf->destination_wb) || pf->code == HTTP_RESP_OK)
  605. pf->code = rrd_call_function_error(pf->destination_wb,
  606. "Timeout waiting for collector response.",
  607. HTTP_RESP_GATEWAY_TIMEOUT);
  608. dictionary_del(parser->inflight.functions, pf_dfe.name);
  609. }
  610. else if(!parser->inflight.smaller_timeout || pf->timeout_ut < parser->inflight.smaller_timeout)
  611. parser->inflight.smaller_timeout = pf->timeout_ut;
  612. }
  613. dfe_done(pf);
  614. }
  615. // this is the function that is called from
  616. // rrd_call_function_and_wait() and rrd_call_function_async()
  617. static int pluginsd_execute_function_callback(BUFFER *destination_wb, int timeout, const char *function, void *collector_data, void (*callback)(BUFFER *wb, int code, void *callback_data), void *callback_data) {
  618. PARSER *parser = collector_data;
  619. usec_t now = now_realtime_usec();
  620. struct inflight_function tmp = {
  621. .started_ut = now,
  622. .timeout_ut = now + timeout * USEC_PER_SEC,
  623. .destination_wb = destination_wb,
  624. .timeout = timeout,
  625. .function = string_strdupz(function),
  626. .callback = callback,
  627. .callback_data = callback_data,
  628. };
  629. uuid_t uuid;
  630. uuid_generate_time(uuid);
  631. char key[UUID_STR_LEN];
  632. uuid_unparse_lower(uuid, key);
  633. dictionary_write_lock(parser->inflight.functions);
  634. // if there is any error, our dictionary callbacks will call the caller callback to notify
  635. // the caller about the error - no need for error handling here.
  636. dictionary_set(parser->inflight.functions, key, &tmp, sizeof(struct inflight_function));
  637. if(!parser->inflight.smaller_timeout || tmp.timeout_ut < parser->inflight.smaller_timeout)
  638. parser->inflight.smaller_timeout = tmp.timeout_ut;
  639. // garbage collect stale inflight functions
  640. if(parser->inflight.smaller_timeout < now)
  641. inflight_functions_garbage_collect(parser, now);
  642. dictionary_write_unlock(parser->inflight.functions);
  643. return HTTP_RESP_OK;
  644. }
  645. PARSER_RC pluginsd_function(char **words, size_t num_words, void *user)
  646. {
  647. bool global = false;
  648. size_t i = 1;
  649. if(num_words >= 2 && strcmp(get_word(words, num_words, 1), "GLOBAL") == 0) {
  650. i++;
  651. global = true;
  652. }
  653. char *name = get_word(words, num_words, i++);
  654. char *timeout_s = get_word(words, num_words, i++);
  655. char *help = get_word(words, num_words, i++);
  656. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_FUNCTION);
  657. if(!host) return PARSER_RC_ERROR;
  658. RRDSET *st = (global)?NULL:pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_FUNCTION, PLUGINSD_KEYWORD_CHART);
  659. if(!st) global = true;
  660. if (unlikely(!timeout_s || !name || !help || (!global && !st))) {
  661. error("PLUGINSD: 'host:%s/chart:%s' got a FUNCTION, without providing the required data (global = '%s', name = '%s', timeout = '%s', help = '%s'). Ignoring it.",
  662. rrdhost_hostname(host),
  663. st?rrdset_id(st):"(unset)",
  664. global?"yes":"no",
  665. name?name:"(unset)",
  666. timeout_s?timeout_s:"(unset)",
  667. help?help:"(unset)"
  668. );
  669. return PARSER_RC_ERROR;
  670. }
  671. int timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT;
  672. if (timeout_s && *timeout_s) {
  673. timeout = str2i(timeout_s);
  674. if (unlikely(timeout <= 0))
  675. timeout = PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT;
  676. }
  677. PARSER *parser = ((PARSER_USER_OBJECT *) user)->parser;
  678. rrd_collector_add_function(host, st, name, timeout, help, false, pluginsd_execute_function_callback, parser);
  679. return PARSER_RC_OK;
  680. }
  681. static void pluginsd_function_result_end(struct parser *parser, void *action_data) {
  682. STRING *key = action_data;
  683. if(key)
  684. dictionary_del(parser->inflight.functions, string2str(key));
  685. string_freez(key);
  686. }
  687. PARSER_RC pluginsd_function_result_begin(char **words, size_t num_words, void *user)
  688. {
  689. char *key = get_word(words, num_words, 1);
  690. char *status = get_word(words, num_words, 2);
  691. char *format = get_word(words, num_words, 3);
  692. char *expires = get_word(words, num_words, 4);
  693. if (unlikely(!key || !*key || !status || !*status || !format || !*format || !expires || !*expires)) {
  694. error("got a " PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " without providing the required data (key = '%s', status = '%s', format = '%s', expires = '%s')."
  695. , key ? key : "(unset)"
  696. , status ? status : "(unset)"
  697. , format ? format : "(unset)"
  698. , expires ? expires : "(unset)"
  699. );
  700. }
  701. int code = (status && *status) ? str2i(status) : 0;
  702. if (code <= 0)
  703. code = HTTP_RESP_BACKEND_RESPONSE_INVALID;
  704. time_t expiration = (expires && *expires) ? str2l(expires) : 0;
  705. PARSER *parser = ((PARSER_USER_OBJECT *) user)->parser;
  706. struct inflight_function *pf = NULL;
  707. if(key && *key)
  708. pf = (struct inflight_function *)dictionary_get(parser->inflight.functions, key);
  709. if(!pf) {
  710. error("got a " PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN " for transaction '%s', but the transaction is not found.", key?key:"(unset)");
  711. }
  712. else {
  713. if(format && *format)
  714. pf->destination_wb->content_type = functions_format_to_content_type(format);
  715. pf->code = code;
  716. pf->destination_wb->expires = expiration;
  717. if(expiration <= now_realtime_sec())
  718. buffer_no_cacheable(pf->destination_wb);
  719. else
  720. buffer_cacheable(pf->destination_wb);
  721. }
  722. parser->defer.response = (pf) ? pf->destination_wb : NULL;
  723. parser->defer.end_keyword = PLUGINSD_KEYWORD_FUNCTION_RESULT_END;
  724. parser->defer.action = pluginsd_function_result_end;
  725. parser->defer.action_data = string_strdupz(key); // it is ok is key is NULL
  726. parser->flags |= PARSER_DEFER_UNTIL_KEYWORD;
  727. return PARSER_RC_OK;
  728. }
  729. // ----------------------------------------------------------------------------
  730. PARSER_RC pluginsd_variable(char **words, size_t num_words, void *user)
  731. {
  732. char *name = get_word(words, num_words, 1);
  733. char *value = get_word(words, num_words, 2);
  734. NETDATA_DOUBLE v;
  735. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_VARIABLE);
  736. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  737. RRDSET *st = pluginsd_get_chart_from_parent(user);
  738. int global = (st) ? 0 : 1;
  739. if (name && *name) {
  740. if ((strcmp(name, "GLOBAL") == 0 || strcmp(name, "HOST") == 0)) {
  741. global = 1;
  742. name = get_word(words, num_words, 2);
  743. value = get_word(words, num_words, 3);
  744. } else if ((strcmp(name, "LOCAL") == 0 || strcmp(name, "CHART") == 0)) {
  745. global = 0;
  746. name = get_word(words, num_words, 2);
  747. value = get_word(words, num_words, 3);
  748. }
  749. }
  750. if (unlikely(!name || !*name))
  751. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_VARIABLE, "missing variable name");
  752. if (unlikely(!value || !*value))
  753. value = NULL;
  754. if (unlikely(!value)) {
  755. error("PLUGINSD: 'host:%s/chart:%s' cannot set %s VARIABLE '%s' to an empty value",
  756. rrdhost_hostname(host),
  757. st ? rrdset_id(st):"UNSET",
  758. (global) ? "HOST" : "CHART",
  759. name);
  760. return PARSER_RC_OK;
  761. }
  762. if (!global && !st)
  763. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_VARIABLE, "no chart is defined and no GLOBAL is given");
  764. char *endptr = NULL;
  765. v = (NETDATA_DOUBLE) str2ndd_encoded(value, &endptr);
  766. if (unlikely(endptr && *endptr)) {
  767. if (endptr == value)
  768. error("PLUGINSD: 'host:%s/chart:%s' the value '%s' of VARIABLE '%s' cannot be parsed as a number",
  769. rrdhost_hostname(host),
  770. st ? rrdset_id(st):"UNSET",
  771. value,
  772. name);
  773. else
  774. error("PLUGINSD: 'host:%s/chart:%s' the value '%s' of VARIABLE '%s' has leftovers: '%s'",
  775. rrdhost_hostname(host),
  776. st ? rrdset_id(st):"UNSET",
  777. value,
  778. name,
  779. endptr);
  780. }
  781. if (global) {
  782. const RRDVAR_ACQUIRED *rva = rrdvar_custom_host_variable_add_and_acquire(host, name);
  783. if (rva) {
  784. rrdvar_custom_host_variable_set(host, rva, v);
  785. rrdvar_custom_host_variable_release(host, rva);
  786. }
  787. else
  788. error("PLUGINSD: 'host:%s' cannot find/create HOST VARIABLE '%s'",
  789. rrdhost_hostname(host),
  790. name);
  791. } else {
  792. const RRDSETVAR_ACQUIRED *rsa = rrdsetvar_custom_chart_variable_add_and_acquire(st, name);
  793. if (rsa) {
  794. rrdsetvar_custom_chart_variable_set(st, rsa, v);
  795. rrdsetvar_custom_chart_variable_release(st, rsa);
  796. }
  797. else
  798. error("PLUGINSD: 'host:%s/chart:%s' cannot find/create CHART VARIABLE '%s'",
  799. rrdhost_hostname(host), rrdset_id(st), name);
  800. }
  801. return PARSER_RC_OK;
  802. }
  803. PARSER_RC pluginsd_flush(char **words __maybe_unused, size_t num_words __maybe_unused, void *user)
  804. {
  805. debug(D_PLUGINSD, "requested a " PLUGINSD_KEYWORD_FLUSH);
  806. pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_FLUSH);
  807. ((PARSER_USER_OBJECT *) user)->replay.start_time = 0;
  808. ((PARSER_USER_OBJECT *) user)->replay.end_time = 0;
  809. ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0;
  810. ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = 0;
  811. return PARSER_RC_OK;
  812. }
  813. PARSER_RC pluginsd_disable(char **words __maybe_unused, size_t num_words __maybe_unused, void *user __maybe_unused)
  814. {
  815. info("PLUGINSD: plugin called DISABLE. Disabling it.");
  816. ((PARSER_USER_OBJECT *) user)->enabled = 0;
  817. return PARSER_RC_STOP;
  818. }
  819. PARSER_RC pluginsd_label(char **words, size_t num_words, void *user)
  820. {
  821. const char *name = get_word(words, num_words, 1);
  822. const char *label_source = get_word(words, num_words, 2);
  823. const char *value = get_word(words, num_words, 3);
  824. if (!name || !label_source || !value)
  825. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_LABEL, "missing parameters");
  826. char *store = (char *)value;
  827. bool allocated_store = false;
  828. if(unlikely(num_words > 4)) {
  829. allocated_store = true;
  830. store = mallocz(PLUGINSD_LINE_MAX + 1);
  831. size_t remaining = PLUGINSD_LINE_MAX;
  832. char *move = store;
  833. char *word;
  834. for(size_t i = 3; i < num_words && remaining > 2 && (word = get_word(words, num_words, i)) ;i++) {
  835. if(i > 3) {
  836. *move++ = ' ';
  837. *move = '\0';
  838. remaining--;
  839. }
  840. size_t length = strlen(word);
  841. if (length > remaining)
  842. length = remaining;
  843. remaining -= length;
  844. memcpy(move, word, length);
  845. move += length;
  846. *move = '\0';
  847. }
  848. }
  849. if(unlikely(!((PARSER_USER_OBJECT *) user)->new_host_labels))
  850. ((PARSER_USER_OBJECT *) user)->new_host_labels = rrdlabels_create();
  851. rrdlabels_add(((PARSER_USER_OBJECT *)user)->new_host_labels,
  852. name,
  853. store,
  854. str2l(label_source));
  855. if (allocated_store)
  856. freez(store);
  857. return PARSER_RC_OK;
  858. }
  859. PARSER_RC pluginsd_overwrite(char **words __maybe_unused, size_t num_words __maybe_unused, void *user)
  860. {
  861. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_OVERWRITE);
  862. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  863. debug(D_PLUGINSD, "requested to OVERWRITE host labels");
  864. if(unlikely(!host->rrdlabels))
  865. host->rrdlabels = rrdlabels_create();
  866. rrdlabels_migrate_to_these(host->rrdlabels, (DICTIONARY *) (((PARSER_USER_OBJECT *)user)->new_host_labels));
  867. rrdhost_flag_set(host, RRDHOST_FLAG_METADATA_LABELS | RRDHOST_FLAG_METADATA_UPDATE);
  868. rrdlabels_destroy(((PARSER_USER_OBJECT *)user)->new_host_labels);
  869. ((PARSER_USER_OBJECT *)user)->new_host_labels = NULL;
  870. return PARSER_RC_OK;
  871. }
  872. PARSER_RC pluginsd_clabel(char **words, size_t num_words, void *user)
  873. {
  874. const char *name = get_word(words, num_words, 1);
  875. const char *value = get_word(words, num_words, 2);
  876. const char *label_source = get_word(words, num_words, 3);
  877. if (!name || !value || !*label_source) {
  878. error("Ignoring malformed or empty CHART LABEL command.");
  879. return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  880. }
  881. if(unlikely(!((PARSER_USER_OBJECT *) user)->chart_rrdlabels_linked_temporarily)) {
  882. RRDSET *st = pluginsd_get_chart_from_parent(user);
  883. ((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily = st->rrdlabels;
  884. rrdlabels_unmark_all(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily);
  885. }
  886. rrdlabels_add(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily,
  887. name, value, str2l(label_source));
  888. return PARSER_RC_OK;
  889. }
  890. PARSER_RC pluginsd_clabel_commit(char **words __maybe_unused, size_t num_words __maybe_unused, void *user)
  891. {
  892. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_CLABEL_COMMIT);
  893. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  894. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_CLABEL_COMMIT, PLUGINSD_KEYWORD_BEGIN);
  895. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  896. debug(D_PLUGINSD, "requested to commit chart labels");
  897. if(!((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily) {
  898. error("PLUGINSD: 'host:%s' got CLABEL_COMMIT, without a CHART or BEGIN. Ignoring it.",
  899. rrdhost_hostname(host));
  900. return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  901. }
  902. rrdlabels_remove_all_unmarked(((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily);
  903. rrdset_flag_set(st, RRDSET_FLAG_METADATA_UPDATE);
  904. rrdhost_flag_set(st->rrdhost, RRDHOST_FLAG_METADATA_UPDATE);
  905. ((PARSER_USER_OBJECT *)user)->chart_rrdlabels_linked_temporarily = NULL;
  906. return PARSER_RC_OK;
  907. }
  908. PARSER_RC pluginsd_replay_begin(char **words, size_t num_words, void *user) {
  909. char *id = get_word(words, num_words, 1);
  910. char *start_time_str = get_word(words, num_words, 2);
  911. char *end_time_str = get_word(words, num_words, 3);
  912. char *child_now_str = get_word(words, num_words, 4);
  913. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  914. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  915. RRDSET *st;
  916. if (likely(!id || !*id))
  917. st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_BEGIN, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  918. else
  919. st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  920. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  921. pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  922. if(start_time_str && end_time_str) {
  923. time_t start_time = (time_t) str2ull_encoded(start_time_str);
  924. time_t end_time = (time_t) str2ull_encoded(end_time_str);
  925. time_t wall_clock_time = 0, tolerance;
  926. bool wall_clock_comes_from_child; (void)wall_clock_comes_from_child;
  927. if(child_now_str) {
  928. wall_clock_time = (time_t) str2ull_encoded(child_now_str);
  929. tolerance = st->update_every + 1;
  930. wall_clock_comes_from_child = true;
  931. }
  932. if(wall_clock_time <= 0) {
  933. wall_clock_time = now_realtime_sec();
  934. tolerance = st->update_every + 5;
  935. wall_clock_comes_from_child = false;
  936. }
  937. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  938. internal_error(
  939. (!st->replay.start_streaming && (end_time < st->replay.after || start_time > st->replay.before)),
  940. "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, which does not match our request (%ld to %ld).",
  941. rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time, st->replay.after, st->replay.before);
  942. internal_error(
  943. true,
  944. "REPLAY: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN " from %ld to %ld, child wall clock is %ld (%s), had requested %ld to %ld",
  945. rrdhost_hostname(st->rrdhost), rrdset_id(st),
  946. start_time, end_time, wall_clock_time, wall_clock_comes_from_child ? "from child" : "parent time",
  947. st->replay.after, st->replay.before);
  948. #endif
  949. if(start_time && end_time && start_time < wall_clock_time + tolerance && end_time < wall_clock_time + tolerance && start_time < end_time) {
  950. if (unlikely(end_time - start_time != st->update_every))
  951. rrdset_set_update_every_s(st, end_time - start_time);
  952. st->last_collected_time.tv_sec = end_time;
  953. st->last_collected_time.tv_usec = 0;
  954. st->last_updated.tv_sec = end_time;
  955. st->last_updated.tv_usec = 0;
  956. st->counter++;
  957. st->counter_done++;
  958. // these are only needed for db mode RAM, SAVE, MAP, ALLOC
  959. st->current_entry++;
  960. if(st->current_entry >= st->entries)
  961. st->current_entry -= st->entries;
  962. ((PARSER_USER_OBJECT *) user)->replay.start_time = start_time;
  963. ((PARSER_USER_OBJECT *) user)->replay.end_time = end_time;
  964. ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = (usec_t) start_time * USEC_PER_SEC;
  965. ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = (usec_t) end_time * USEC_PER_SEC;
  966. ((PARSER_USER_OBJECT *) user)->replay.wall_clock_time = wall_clock_time;
  967. ((PARSER_USER_OBJECT *) user)->replay.rset_enabled = true;
  968. return PARSER_RC_OK;
  969. }
  970. error("PLUGINSD REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_BEGIN
  971. " from %ld to %ld, but timestamps are invalid "
  972. "(now is %ld [%s], tolerance %ld). Ignoring " PLUGINSD_KEYWORD_REPLAY_SET,
  973. rrdhost_hostname(st->rrdhost), rrdset_id(st), start_time, end_time,
  974. wall_clock_time, wall_clock_comes_from_child ? "child wall clock" : "parent wall clock", tolerance);
  975. }
  976. // the child sends an RBEGIN without any parameters initially
  977. // setting rset_enabled to false, means the RSET should not store any metrics
  978. // to store metrics, the RBEGIN needs to have timestamps
  979. ((PARSER_USER_OBJECT *) user)->replay.start_time = 0;
  980. ((PARSER_USER_OBJECT *) user)->replay.end_time = 0;
  981. ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0;
  982. ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = 0;
  983. ((PARSER_USER_OBJECT *) user)->replay.wall_clock_time = 0;
  984. ((PARSER_USER_OBJECT *) user)->replay.rset_enabled = false;
  985. return PARSER_RC_OK;
  986. }
  987. static inline SN_FLAGS pluginsd_parse_storage_number_flags(const char *flags_str) {
  988. SN_FLAGS flags = SN_FLAG_NONE;
  989. char c;
  990. while ((c = *flags_str++)) {
  991. switch (c) {
  992. case 'A':
  993. flags |= SN_FLAG_NOT_ANOMALOUS;
  994. break;
  995. case 'R':
  996. flags |= SN_FLAG_RESET;
  997. break;
  998. case 'E':
  999. flags = SN_EMPTY_SLOT;
  1000. return flags;
  1001. default:
  1002. internal_error(true, "Unknown SN_FLAGS flag '%c'", c);
  1003. break;
  1004. }
  1005. }
  1006. return flags;
  1007. }
  1008. PARSER_RC pluginsd_replay_set(char **words, size_t num_words, void *user)
  1009. {
  1010. char *dimension = get_word(words, num_words, 1);
  1011. char *value_str = get_word(words, num_words, 2);
  1012. char *flags_str = get_word(words, num_words, 3);
  1013. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_SET);
  1014. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1015. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  1016. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1017. PARSER_USER_OBJECT *u = user;
  1018. if(!u->replay.rset_enabled) {
  1019. error_limit_static_thread_var(erl, 1, 0);
  1020. error_limit(&erl, "PLUGINSD: 'host:%s/chart:%s' got a %s but it is disabled by %s errors",
  1021. rrdhost_hostname(host), rrdset_id(st), PLUGINSD_KEYWORD_REPLAY_SET, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  1022. // we have to return OK here
  1023. return PARSER_RC_OK;
  1024. }
  1025. RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_SET);
  1026. if(!rd) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1027. if (unlikely(!u->replay.start_time || !u->replay.end_time)) {
  1028. error("PLUGINSD: 'host:%s/chart:%s/dim:%s' got a %s with invalid timestamps %ld to %ld from a %s. Disabling it.",
  1029. rrdhost_hostname(host),
  1030. rrdset_id(st),
  1031. dimension,
  1032. PLUGINSD_KEYWORD_REPLAY_SET,
  1033. u->replay.start_time,
  1034. u->replay.end_time,
  1035. PLUGINSD_KEYWORD_REPLAY_BEGIN);
  1036. return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1037. }
  1038. if (unlikely(!value_str || !*value_str))
  1039. value_str = "NAN";
  1040. if(unlikely(!flags_str))
  1041. flags_str = "";
  1042. if (likely(value_str)) {
  1043. RRDDIM_FLAGS rd_flags = rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE | RRDDIM_FLAG_ARCHIVED);
  1044. if(!(rd_flags & RRDDIM_FLAG_ARCHIVED)) {
  1045. NETDATA_DOUBLE value = str2ndd_encoded(value_str, NULL);
  1046. SN_FLAGS flags = pluginsd_parse_storage_number_flags(flags_str);
  1047. if (!netdata_double_isnumber(value) || (flags == SN_EMPTY_SLOT)) {
  1048. value = NAN;
  1049. flags = SN_EMPTY_SLOT;
  1050. }
  1051. rrddim_store_metric(rd, u->replay.end_time_ut, value, flags);
  1052. rd->last_collected_time.tv_sec = u->replay.end_time;
  1053. rd->last_collected_time.tv_usec = 0;
  1054. rd->collections_counter++;
  1055. }
  1056. else {
  1057. error_limit_static_global_var(erl, 1, 0);
  1058. error_limit(&erl, "PLUGINSD: 'host:%s/chart:%s/dim:%s' has the ARCHIVED flag set, but it is replicated. Ignoring data.",
  1059. rrdhost_hostname(st->rrdhost), rrdset_id(st), rrddim_name(rd));
  1060. }
  1061. }
  1062. return PARSER_RC_OK;
  1063. }
  1064. PARSER_RC pluginsd_replay_rrddim_collection_state(char **words, size_t num_words, void *user)
  1065. {
  1066. if(((PARSER_USER_OBJECT *) user)->replay.rset_enabled == false)
  1067. return PARSER_RC_OK;
  1068. char *dimension = get_word(words, num_words, 1);
  1069. char *last_collected_ut_str = get_word(words, num_words, 2);
  1070. char *last_collected_value_str = get_word(words, num_words, 3);
  1071. char *last_calculated_value_str = get_word(words, num_words, 4);
  1072. char *last_stored_value_str = get_word(words, num_words, 5);
  1073. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
  1074. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1075. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  1076. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1077. RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE);
  1078. if(!rd) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1079. usec_t dim_last_collected_ut = (usec_t)rd->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)rd->last_collected_time.tv_usec;
  1080. usec_t last_collected_ut = last_collected_ut_str ? str2ull_encoded(last_collected_ut_str) : 0;
  1081. if(last_collected_ut > dim_last_collected_ut) {
  1082. rd->last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
  1083. rd->last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
  1084. }
  1085. rd->last_collected_value = last_collected_value_str ? str2ll_encoded(last_collected_value_str) : 0;
  1086. rd->last_calculated_value = last_calculated_value_str ? str2ndd_encoded(last_calculated_value_str, NULL) : 0;
  1087. rd->last_stored_value = last_stored_value_str ? str2ndd_encoded(last_stored_value_str, NULL) : 0.0;
  1088. return PARSER_RC_OK;
  1089. }
  1090. PARSER_RC pluginsd_replay_rrdset_collection_state(char **words, size_t num_words, void *user)
  1091. {
  1092. if(((PARSER_USER_OBJECT *) user)->replay.rset_enabled == false)
  1093. return PARSER_RC_OK;
  1094. char *last_collected_ut_str = get_word(words, num_words, 1);
  1095. char *last_updated_ut_str = get_word(words, num_words, 2);
  1096. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE);
  1097. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1098. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  1099. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1100. usec_t chart_last_collected_ut = (usec_t)st->last_collected_time.tv_sec * USEC_PER_SEC + (usec_t)st->last_collected_time.tv_usec;
  1101. usec_t last_collected_ut = last_collected_ut_str ? str2ull_encoded(last_collected_ut_str) : 0;
  1102. if(last_collected_ut > chart_last_collected_ut) {
  1103. st->last_collected_time.tv_sec = (time_t)(last_collected_ut / USEC_PER_SEC);
  1104. st->last_collected_time.tv_usec = (last_collected_ut % USEC_PER_SEC);
  1105. }
  1106. usec_t chart_last_updated_ut = (usec_t)st->last_updated.tv_sec * USEC_PER_SEC + (usec_t)st->last_updated.tv_usec;
  1107. usec_t last_updated_ut = last_updated_ut_str ? str2ull_encoded(last_updated_ut_str) : 0;
  1108. if(last_updated_ut > chart_last_updated_ut) {
  1109. st->last_updated.tv_sec = (time_t)(last_updated_ut / USEC_PER_SEC);
  1110. st->last_updated.tv_usec = (last_updated_ut % USEC_PER_SEC);
  1111. }
  1112. st->counter++;
  1113. st->counter_done++;
  1114. return PARSER_RC_OK;
  1115. }
  1116. PARSER_RC pluginsd_replay_end(char **words, size_t num_words, void *user)
  1117. {
  1118. if (num_words < 7) { // accepts 7, but the 7th is optional
  1119. error("REPLAY: malformed " PLUGINSD_KEYWORD_REPLAY_END " command");
  1120. return PARSER_RC_ERROR;
  1121. }
  1122. const char *update_every_child_txt = get_word(words, num_words, 1);
  1123. const char *first_entry_child_txt = get_word(words, num_words, 2);
  1124. const char *last_entry_child_txt = get_word(words, num_words, 3);
  1125. const char *start_streaming_txt = get_word(words, num_words, 4);
  1126. const char *first_entry_requested_txt = get_word(words, num_words, 5);
  1127. const char *last_entry_requested_txt = get_word(words, num_words, 6);
  1128. const char *child_world_time_txt = get_word(words, num_words, 7); // optional
  1129. time_t update_every_child = (time_t) str2ull_encoded(update_every_child_txt);
  1130. time_t first_entry_child = (time_t) str2ull_encoded(first_entry_child_txt);
  1131. time_t last_entry_child = (time_t) str2ull_encoded(last_entry_child_txt);
  1132. bool start_streaming = (strcmp(start_streaming_txt, "true") == 0);
  1133. time_t first_entry_requested = (time_t) str2ull_encoded(first_entry_requested_txt);
  1134. time_t last_entry_requested = (time_t) str2ull_encoded(last_entry_requested_txt);
  1135. // the optional child world time
  1136. time_t child_world_time = (child_world_time_txt && *child_world_time_txt) ? (time_t) str2ull_encoded(
  1137. child_world_time_txt) : now_realtime_sec();
  1138. PARSER_USER_OBJECT *user_object = user;
  1139. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_REPLAY_END);
  1140. if(!host) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1141. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_REPLAY_END, PLUGINSD_KEYWORD_REPLAY_BEGIN);
  1142. if(!st) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1143. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  1144. internal_error(true,
  1145. "PLUGINSD REPLAY: 'host:%s/chart:%s': got a " PLUGINSD_KEYWORD_REPLAY_END " child db from %llu to %llu, start_streaming %s, had requested from %llu to %llu, wall clock %llu",
  1146. rrdhost_hostname(host), rrdset_id(st),
  1147. (unsigned long long)first_entry_child, (unsigned long long)last_entry_child,
  1148. start_streaming?"true":"false",
  1149. (unsigned long long)first_entry_requested, (unsigned long long)last_entry_requested,
  1150. (unsigned long long)child_world_time
  1151. );
  1152. #endif
  1153. ((PARSER_USER_OBJECT *) user)->data_collections_count++;
  1154. if(((PARSER_USER_OBJECT *) user)->replay.rset_enabled && st->rrdhost->receiver) {
  1155. time_t now = now_realtime_sec();
  1156. time_t started = st->rrdhost->receiver->replication_first_time_t;
  1157. time_t current = ((PARSER_USER_OBJECT *) user)->replay.end_time;
  1158. if(started && current > started)
  1159. worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION,
  1160. (NETDATA_DOUBLE)(current - started) * 100.0 / (NETDATA_DOUBLE)(now - started));
  1161. }
  1162. ((PARSER_USER_OBJECT *) user)->replay.start_time = 0;
  1163. ((PARSER_USER_OBJECT *) user)->replay.end_time = 0;
  1164. ((PARSER_USER_OBJECT *) user)->replay.start_time_ut = 0;
  1165. ((PARSER_USER_OBJECT *) user)->replay.end_time_ut = 0;
  1166. ((PARSER_USER_OBJECT *) user)->replay.wall_clock_time = 0;
  1167. ((PARSER_USER_OBJECT *) user)->replay.rset_enabled = false;
  1168. st->counter++;
  1169. st->counter_done++;
  1170. store_metric_collection_completed();
  1171. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  1172. st->replay.start_streaming = false;
  1173. st->replay.after = 0;
  1174. st->replay.before = 0;
  1175. if(start_streaming)
  1176. st->replay.log_next_data_collection = true;
  1177. #endif
  1178. if (start_streaming) {
  1179. if (st->update_every != update_every_child)
  1180. rrdset_set_update_every_s(st, update_every_child);
  1181. if(rrdset_flag_check(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS)) {
  1182. rrdset_flag_set(st, RRDSET_FLAG_RECEIVER_REPLICATION_FINISHED);
  1183. rrdset_flag_clear(st, RRDSET_FLAG_RECEIVER_REPLICATION_IN_PROGRESS);
  1184. rrdset_flag_clear(st, RRDSET_FLAG_SYNC_CLOCK);
  1185. rrdhost_receiver_replicating_charts_minus_one(st->rrdhost);
  1186. }
  1187. #ifdef NETDATA_LOG_REPLICATION_REQUESTS
  1188. else
  1189. internal_error(true, "REPLAY ERROR: 'host:%s/chart:%s' got a " PLUGINSD_KEYWORD_REPLAY_END " with enable_streaming = true, but there is no replication in progress for this chart.",
  1190. rrdhost_hostname(host), rrdset_id(st));
  1191. #endif
  1192. pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_REPLAY_END);
  1193. worker_set_metric(WORKER_RECEIVER_JOB_REPLICATION_COMPLETION, 100.0);
  1194. return PARSER_RC_OK;
  1195. }
  1196. pluginsd_set_chart_from_parent(user, NULL, PLUGINSD_KEYWORD_REPLAY_END);
  1197. rrdcontext_updated_retention_rrdset(st);
  1198. bool ok = replicate_chart_request(send_to_plugin, user_object->parser, host, st,
  1199. first_entry_child, last_entry_child, child_world_time,
  1200. first_entry_requested, last_entry_requested);
  1201. return ok ? PARSER_RC_OK : PARSER_RC_ERROR;
  1202. }
  1203. PARSER_RC pluginsd_begin_v2(char **words, size_t num_words, void *user) {
  1204. timing_init();
  1205. char *id = get_word(words, num_words, 1);
  1206. char *update_every_str = get_word(words, num_words, 2);
  1207. char *end_time_str = get_word(words, num_words, 3);
  1208. char *wall_clock_time_str = get_word(words, num_words, 4);
  1209. if(unlikely(!id || !update_every_str || !end_time_str || !wall_clock_time_str))
  1210. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_BEGIN_V2, "missing parameters");
  1211. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_BEGIN_V2);
  1212. if(unlikely(!host)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1213. timing_step(TIMING_STEP_BEGIN2_PREPARE);
  1214. RRDSET *st = pluginsd_find_chart(host, id, PLUGINSD_KEYWORD_BEGIN_V2);
  1215. if(unlikely(!st)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1216. pluginsd_set_chart_from_parent(user, st, PLUGINSD_KEYWORD_BEGIN_V2);
  1217. if(unlikely(rrdset_flag_check(st, RRDSET_FLAG_OBSOLETE | RRDSET_FLAG_ARCHIVED)))
  1218. rrdset_isnot_obsolete(st);
  1219. timing_step(TIMING_STEP_BEGIN2_FIND_CHART);
  1220. // ------------------------------------------------------------------------
  1221. // parse the parameters
  1222. time_t update_every = (time_t) str2ull_encoded(update_every_str);
  1223. time_t end_time = (time_t) str2ull_encoded(end_time_str);
  1224. time_t wall_clock_time;
  1225. if(likely(*wall_clock_time_str == '#'))
  1226. wall_clock_time = end_time;
  1227. else
  1228. wall_clock_time = (time_t) str2ull_encoded(wall_clock_time_str);
  1229. if (unlikely(update_every != st->update_every))
  1230. rrdset_set_update_every_s(st, update_every);
  1231. timing_step(TIMING_STEP_BEGIN2_PARSE);
  1232. // ------------------------------------------------------------------------
  1233. // prepare our state
  1234. pluginsd_lock_rrdset_data_collection(user);
  1235. PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
  1236. u->v2.update_every = update_every;
  1237. u->v2.end_time = end_time;
  1238. u->v2.wall_clock_time = wall_clock_time;
  1239. u->v2.ml_locked = ml_chart_update_begin(st);
  1240. timing_step(TIMING_STEP_BEGIN2_ML);
  1241. // ------------------------------------------------------------------------
  1242. // propagate it forward in v2
  1243. if(!u->v2.stream_buffer.wb && rrdhost_has_rrdpush_sender_enabled(st->rrdhost))
  1244. u->v2.stream_buffer = rrdset_push_metric_initialize(u->st, wall_clock_time);
  1245. if(u->v2.stream_buffer.v2 && u->v2.stream_buffer.wb) {
  1246. // check if receiver and sender have the same number parsing capabilities
  1247. bool can_copy = stream_has_capability(u, STREAM_CAP_IEEE754) == stream_has_capability(&u->v2.stream_buffer, STREAM_CAP_IEEE754);
  1248. NUMBER_ENCODING encoding = stream_has_capability(&u->v2.stream_buffer, STREAM_CAP_IEEE754) ? NUMBER_ENCODING_BASE64 : NUMBER_ENCODING_HEX;
  1249. BUFFER *wb = u->v2.stream_buffer.wb;
  1250. buffer_need_bytes(wb, 1024);
  1251. if(unlikely(u->v2.stream_buffer.begin_v2_added))
  1252. buffer_fast_strcat(wb, PLUGINSD_KEYWORD_END_V2 "\n", sizeof(PLUGINSD_KEYWORD_END_V2) - 1 + 1);
  1253. buffer_fast_strcat(wb, PLUGINSD_KEYWORD_BEGIN_V2 " '", sizeof(PLUGINSD_KEYWORD_BEGIN_V2) - 1 + 2);
  1254. buffer_fast_strcat(wb, rrdset_id(st), string_strlen(st->id));
  1255. buffer_fast_strcat(wb, "' ", 2);
  1256. if(can_copy)
  1257. buffer_strcat(wb, update_every_str);
  1258. else
  1259. buffer_print_uint64_encoded(wb, encoding, update_every);
  1260. buffer_fast_strcat(wb, " ", 1);
  1261. if(can_copy)
  1262. buffer_strcat(wb, end_time_str);
  1263. else
  1264. buffer_print_uint64_encoded(wb, encoding, end_time);
  1265. buffer_fast_strcat(wb, " ", 1);
  1266. if(can_copy)
  1267. buffer_strcat(wb, wall_clock_time_str);
  1268. else
  1269. buffer_print_uint64_encoded(wb, encoding, wall_clock_time);
  1270. buffer_fast_strcat(wb, "\n", 1);
  1271. u->v2.stream_buffer.last_point_end_time_s = end_time;
  1272. u->v2.stream_buffer.begin_v2_added = true;
  1273. }
  1274. timing_step(TIMING_STEP_BEGIN2_PROPAGATE);
  1275. // ------------------------------------------------------------------------
  1276. // store it
  1277. st->last_collected_time.tv_sec = end_time;
  1278. st->last_collected_time.tv_usec = 0;
  1279. st->last_updated.tv_sec = end_time;
  1280. st->last_updated.tv_usec = 0;
  1281. st->counter++;
  1282. st->counter_done++;
  1283. // these are only needed for db mode RAM, SAVE, MAP, ALLOC
  1284. st->current_entry++;
  1285. if(st->current_entry >= st->entries)
  1286. st->current_entry -= st->entries;
  1287. timing_step(TIMING_STEP_BEGIN2_STORE);
  1288. return PARSER_RC_OK;
  1289. }
  1290. PARSER_RC pluginsd_set_v2(char **words, size_t num_words, void *user) {
  1291. timing_init();
  1292. char *dimension = get_word(words, num_words, 1);
  1293. char *collected_str = get_word(words, num_words, 2);
  1294. char *value_str = get_word(words, num_words, 3);
  1295. char *flags_str = get_word(words, num_words, 4);
  1296. if(unlikely(!dimension || !collected_str || !value_str || !flags_str))
  1297. return PLUGINSD_DISABLE_PLUGIN(user, PLUGINSD_KEYWORD_SET_V2, "missing parameters");
  1298. PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
  1299. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_SET_V2);
  1300. if(unlikely(!host)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1301. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_SET_V2, PLUGINSD_KEYWORD_BEGIN_V2);
  1302. if(unlikely(!st)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1303. timing_step(TIMING_STEP_SET2_PREPARE);
  1304. RRDDIM *rd = pluginsd_acquire_dimension(host, st, dimension, PLUGINSD_KEYWORD_SET_V2);
  1305. if(unlikely(!rd)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1306. if(unlikely(rrddim_flag_check(rd, RRDDIM_FLAG_OBSOLETE | RRDDIM_FLAG_ARCHIVED)))
  1307. rrddim_isnot_obsolete(st, rd);
  1308. timing_step(TIMING_STEP_SET2_LOOKUP_DIMENSION);
  1309. // ------------------------------------------------------------------------
  1310. // parse the parameters
  1311. collected_number collected_value = (collected_number) str2ll_encoded(collected_str);
  1312. NETDATA_DOUBLE value;
  1313. if(*value_str == '#')
  1314. value = (NETDATA_DOUBLE)collected_value;
  1315. else
  1316. value = str2ndd_encoded(value_str, NULL);
  1317. SN_FLAGS flags = pluginsd_parse_storage_number_flags(flags_str);
  1318. timing_step(TIMING_STEP_SET2_PARSE);
  1319. // ------------------------------------------------------------------------
  1320. // check value and ML
  1321. if (unlikely(!netdata_double_isnumber(value) || (flags == SN_EMPTY_SLOT))) {
  1322. value = NAN;
  1323. flags = SN_EMPTY_SLOT;
  1324. if(u->v2.ml_locked)
  1325. ml_dimension_is_anomalous(rd, u->v2.end_time, 0, false);
  1326. }
  1327. else if(u->v2.ml_locked) {
  1328. if (ml_dimension_is_anomalous(rd, u->v2.end_time, value, true)) {
  1329. // clear anomaly bit: 0 -> is anomalous, 1 -> not anomalous
  1330. flags &= ~((storage_number) SN_FLAG_NOT_ANOMALOUS);
  1331. }
  1332. else
  1333. flags |= SN_FLAG_NOT_ANOMALOUS;
  1334. }
  1335. timing_step(TIMING_STEP_SET2_ML);
  1336. // ------------------------------------------------------------------------
  1337. // propagate it forward in v2
  1338. if(u->v2.stream_buffer.v2 && u->v2.stream_buffer.begin_v2_added && u->v2.stream_buffer.wb) {
  1339. // check if receiver and sender have the same number parsing capabilities
  1340. bool can_copy = stream_has_capability(u, STREAM_CAP_IEEE754) == stream_has_capability(&u->v2.stream_buffer, STREAM_CAP_IEEE754);
  1341. NUMBER_ENCODING integer_encoding = stream_has_capability(&u->v2.stream_buffer, STREAM_CAP_IEEE754) ? NUMBER_ENCODING_BASE64 : NUMBER_ENCODING_HEX;
  1342. NUMBER_ENCODING doubles_encoding = stream_has_capability(&u->v2.stream_buffer, STREAM_CAP_IEEE754) ? NUMBER_ENCODING_BASE64 : NUMBER_ENCODING_DECIMAL;
  1343. BUFFER *wb = u->v2.stream_buffer.wb;
  1344. buffer_need_bytes(wb, 1024);
  1345. buffer_fast_strcat(wb, PLUGINSD_KEYWORD_SET_V2 " '", sizeof(PLUGINSD_KEYWORD_SET_V2) - 1 + 2);
  1346. buffer_fast_strcat(wb, rrddim_id(rd), string_strlen(rd->id));
  1347. buffer_fast_strcat(wb, "' ", 2);
  1348. if(can_copy)
  1349. buffer_strcat(wb, collected_str);
  1350. else
  1351. buffer_print_int64_encoded(wb, integer_encoding, collected_value); // original v2 had hex
  1352. buffer_fast_strcat(wb, " ", 1);
  1353. if(can_copy)
  1354. buffer_strcat(wb, value_str);
  1355. else
  1356. buffer_print_netdata_double_encoded(wb, doubles_encoding, value); // original v2 had decimal
  1357. buffer_fast_strcat(wb, " ", 1);
  1358. buffer_print_sn_flags(wb, flags, true);
  1359. buffer_fast_strcat(wb, "\n", 1);
  1360. }
  1361. timing_step(TIMING_STEP_SET2_PROPAGATE);
  1362. // ------------------------------------------------------------------------
  1363. // store it
  1364. rrddim_store_metric(rd, u->v2.end_time * USEC_PER_SEC, value, flags);
  1365. rd->last_collected_time.tv_sec = u->v2.end_time;
  1366. rd->last_collected_time.tv_usec = 0;
  1367. rd->last_collected_value = collected_value;
  1368. rd->last_stored_value = value;
  1369. rd->last_calculated_value = value;
  1370. rd->collections_counter++;
  1371. rd->updated = true;
  1372. timing_step(TIMING_STEP_SET2_STORE);
  1373. return PARSER_RC_OK;
  1374. }
  1375. void pluginsd_cleanup_v2(void *user) {
  1376. // this is called when the thread is stopped while processing
  1377. pluginsd_set_chart_from_parent(user, NULL, "THREAD CLEANUP");
  1378. }
  1379. PARSER_RC pluginsd_end_v2(char **words __maybe_unused, size_t num_words __maybe_unused, void *user) {
  1380. timing_init();
  1381. RRDHOST *host = pluginsd_require_host_from_parent(user, PLUGINSD_KEYWORD_END_V2);
  1382. if(unlikely(!host)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1383. RRDSET *st = pluginsd_require_chart_from_parent(user, PLUGINSD_KEYWORD_END_V2, PLUGINSD_KEYWORD_BEGIN_V2);
  1384. if(unlikely(!st)) return PLUGINSD_DISABLE_PLUGIN(user, NULL, NULL);
  1385. PARSER_USER_OBJECT *u = (PARSER_USER_OBJECT *) user;
  1386. u->data_collections_count++;
  1387. timing_step(TIMING_STEP_END2_PREPARE);
  1388. // ------------------------------------------------------------------------
  1389. // propagate the whole chart update in v1
  1390. if(unlikely(!u->v2.stream_buffer.v2 && !u->v2.stream_buffer.begin_v2_added && u->v2.stream_buffer.wb))
  1391. rrdset_push_metrics_v1(&u->v2.stream_buffer, st);
  1392. timing_step(TIMING_STEP_END2_PUSH_V1);
  1393. // ------------------------------------------------------------------------
  1394. // unblock data collection
  1395. ml_chart_update_end(st);
  1396. u->v2.ml_locked = false;
  1397. timing_step(TIMING_STEP_END2_ML);
  1398. pluginsd_unlock_rrdset_data_collection(user);
  1399. rrdcontext_collected_rrdset(st);
  1400. store_metric_collection_completed();
  1401. timing_step(TIMING_STEP_END2_RRDSET);
  1402. // ------------------------------------------------------------------------
  1403. // propagate it forward
  1404. rrdset_push_metrics_finished(&u->v2.stream_buffer, st);
  1405. timing_step(TIMING_STEP_END2_PROPAGATE);
  1406. // ------------------------------------------------------------------------
  1407. // cleanup RRDSET / RRDDIM
  1408. RRDDIM *rd;
  1409. rrddim_foreach_read(rd, st) {
  1410. rd->calculated_value = 0;
  1411. rd->collected_value = 0;
  1412. rd->updated = false;
  1413. }
  1414. rrddim_foreach_done(rd);
  1415. // ------------------------------------------------------------------------
  1416. // reset state
  1417. u->v2 = (struct parser_user_object_v2){ 0 };
  1418. timing_step(TIMING_STEP_END2_STORE);
  1419. timing_report();
  1420. return PARSER_RC_OK;
  1421. }
  1422. static void pluginsd_process_thread_cleanup(void *ptr) {
  1423. PARSER *parser = (PARSER *)ptr;
  1424. pluginsd_cleanup_v2(parser->user);
  1425. pluginsd_host_define_cleanup(parser->user);
  1426. rrd_collector_finished();
  1427. parser_destroy(parser);
  1428. }
  1429. // New plugins.d parser
  1430. inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp_plugin_input, FILE *fp_plugin_output, int trust_durations)
  1431. {
  1432. int enabled = cd->unsafe.enabled;
  1433. if (!fp_plugin_input || !fp_plugin_output || !enabled) {
  1434. cd->unsafe.enabled = 0;
  1435. return 0;
  1436. }
  1437. if (unlikely(fileno(fp_plugin_input) == -1)) {
  1438. error("input file descriptor given is not a valid stream");
  1439. cd->serial_failures++;
  1440. return 0;
  1441. }
  1442. if (unlikely(fileno(fp_plugin_output) == -1)) {
  1443. error("output file descriptor given is not a valid stream");
  1444. cd->serial_failures++;
  1445. return 0;
  1446. }
  1447. clearerr(fp_plugin_input);
  1448. clearerr(fp_plugin_output);
  1449. PARSER_USER_OBJECT user = {
  1450. .enabled = cd->unsafe.enabled,
  1451. .host = host,
  1452. .cd = cd,
  1453. .trust_durations = trust_durations
  1454. };
  1455. // fp_plugin_output = our input; fp_plugin_input = our output
  1456. PARSER *parser = parser_init(&user, fp_plugin_output, fp_plugin_input, -1,
  1457. PARSER_INPUT_SPLIT, NULL);
  1458. pluginsd_keywords_init(parser, PARSER_INIT_PLUGINSD);
  1459. rrd_collector_started();
  1460. // this keeps the parser with its current value
  1461. // so, parser needs to be allocated before pushing it
  1462. netdata_thread_cleanup_push(pluginsd_process_thread_cleanup, parser);
  1463. user.parser = parser;
  1464. char buffer[PLUGINSD_LINE_MAX + 1];
  1465. while (likely(!parser_next(parser, buffer, PLUGINSD_LINE_MAX))) {
  1466. if (unlikely(!service_running(SERVICE_COLLECTORS) || parser_action(parser, buffer)))
  1467. break;
  1468. }
  1469. // free parser with the pop function
  1470. netdata_thread_cleanup_pop(1);
  1471. cd->unsafe.enabled = user.enabled;
  1472. size_t count = user.data_collections_count;
  1473. if (likely(count)) {
  1474. cd->successful_collections += count;
  1475. cd->serial_failures = 0;
  1476. }
  1477. else
  1478. cd->serial_failures++;
  1479. return count;
  1480. }
  1481. PARSER_RC pluginsd_exit(char **words __maybe_unused, size_t num_words __maybe_unused, void *user __maybe_unused)
  1482. {
  1483. info("PLUGINSD: plugin called EXIT.");
  1484. return PARSER_RC_STOP;
  1485. }
  1486. static void pluginsd_keywords_init_internal(PARSER *parser, PLUGINSD_KEYWORDS types, void (*add_func)(PARSER *parser, char *keyword, keyword_function func)) {
  1487. if (types & PARSER_INIT_PLUGINSD) {
  1488. add_func(parser, PLUGINSD_KEYWORD_FLUSH, pluginsd_flush);
  1489. add_func(parser, PLUGINSD_KEYWORD_DISABLE, pluginsd_disable);
  1490. add_func(parser, PLUGINSD_KEYWORD_HOST_DEFINE, pluginsd_host_define);
  1491. add_func(parser, PLUGINSD_KEYWORD_HOST_DEFINE_END, pluginsd_host_define_end);
  1492. add_func(parser, PLUGINSD_KEYWORD_HOST_LABEL, pluginsd_host_labels);
  1493. add_func(parser, PLUGINSD_KEYWORD_HOST, pluginsd_host);
  1494. add_func(parser, PLUGINSD_KEYWORD_EXIT, pluginsd_exit);
  1495. }
  1496. if (types & (PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING)) {
  1497. // plugins.d plugins and streaming
  1498. add_func(parser, PLUGINSD_KEYWORD_CHART, pluginsd_chart);
  1499. add_func(parser, PLUGINSD_KEYWORD_DIMENSION, pluginsd_dimension);
  1500. add_func(parser, PLUGINSD_KEYWORD_VARIABLE, pluginsd_variable);
  1501. add_func(parser, PLUGINSD_KEYWORD_LABEL, pluginsd_label);
  1502. add_func(parser, PLUGINSD_KEYWORD_OVERWRITE, pluginsd_overwrite);
  1503. add_func(parser, PLUGINSD_KEYWORD_CLABEL_COMMIT, pluginsd_clabel_commit);
  1504. add_func(parser, PLUGINSD_KEYWORD_CLABEL, pluginsd_clabel);
  1505. add_func(parser, PLUGINSD_KEYWORD_FUNCTION, pluginsd_function);
  1506. add_func(parser, PLUGINSD_KEYWORD_FUNCTION_RESULT_BEGIN, pluginsd_function_result_begin);
  1507. add_func(parser, PLUGINSD_KEYWORD_BEGIN, pluginsd_begin);
  1508. add_func(parser, PLUGINSD_KEYWORD_SET, pluginsd_set);
  1509. add_func(parser, PLUGINSD_KEYWORD_END, pluginsd_end);
  1510. inflight_functions_init(parser);
  1511. }
  1512. if (types & PARSER_INIT_STREAMING) {
  1513. add_func(parser, PLUGINSD_KEYWORD_CHART_DEFINITION_END, pluginsd_chart_definition_end);
  1514. // replication
  1515. add_func(parser, PLUGINSD_KEYWORD_REPLAY_BEGIN, pluginsd_replay_begin);
  1516. add_func(parser, PLUGINSD_KEYWORD_REPLAY_SET, pluginsd_replay_set);
  1517. add_func(parser, PLUGINSD_KEYWORD_REPLAY_RRDDIM_STATE, pluginsd_replay_rrddim_collection_state);
  1518. add_func(parser, PLUGINSD_KEYWORD_REPLAY_RRDSET_STATE, pluginsd_replay_rrdset_collection_state);
  1519. add_func(parser, PLUGINSD_KEYWORD_REPLAY_END, pluginsd_replay_end);
  1520. // streaming metrics v2
  1521. add_func(parser, PLUGINSD_KEYWORD_BEGIN_V2, pluginsd_begin_v2);
  1522. add_func(parser, PLUGINSD_KEYWORD_SET_V2, pluginsd_set_v2);
  1523. add_func(parser, PLUGINSD_KEYWORD_END_V2, pluginsd_end_v2);
  1524. }
  1525. }
  1526. void pluginsd_keywords_init(PARSER *parser, PLUGINSD_KEYWORDS types) {
  1527. pluginsd_keywords_init_internal(parser, types, parser_add_keyword);
  1528. }
  1529. struct pluginsd_user_unittest {
  1530. size_t size;
  1531. const char **hashtable;
  1532. uint32_t (*hash)(const char *s);
  1533. size_t collisions;
  1534. };
  1535. void pluginsd_keyword_collision_check(PARSER *parser, char *keyword, keyword_function func __maybe_unused) {
  1536. struct pluginsd_user_unittest *u = parser->user;
  1537. uint32_t hash = u->hash(keyword);
  1538. uint32_t slot = hash % u->size;
  1539. if(u->hashtable[slot])
  1540. u->collisions++;
  1541. u->hashtable[slot] = keyword;
  1542. }
  1543. static struct {
  1544. const char *name;
  1545. uint32_t (*hash)(const char *s);
  1546. size_t slots_needed;
  1547. } hashers[] = {
  1548. { .name = "djb2_hash32(s)", djb2_hash32, .slots_needed = 0, },
  1549. { .name = "fnv1_hash32(s)", fnv1_hash32, .slots_needed = 0, },
  1550. { .name = "fnv1a_hash32(s)", fnv1a_hash32, .slots_needed = 0, },
  1551. { .name = "larson_hash32(s)", larson_hash32, .slots_needed = 0, },
  1552. { .name = "pluginsd_parser_hash32(s)", pluginsd_parser_hash32, .slots_needed = 0, },
  1553. // terminator
  1554. { .name = NULL, NULL, .slots_needed = 0, },
  1555. };
  1556. int pluginsd_parser_unittest(void) {
  1557. PARSER *p;
  1558. size_t slots_to_check = 1000;
  1559. size_t i, h;
  1560. // check for hashtable collisions
  1561. for(h = 0; hashers[h].name ;h++) {
  1562. hashers[h].slots_needed = slots_to_check * 1000000;
  1563. for (i = 10; i < slots_to_check; i++) {
  1564. struct pluginsd_user_unittest user = {
  1565. .hash = hashers[h].hash,
  1566. .size = i,
  1567. .hashtable = callocz(i, sizeof(const char *)),
  1568. .collisions = 0,
  1569. };
  1570. p = parser_init(&user, NULL, NULL, -1, PARSER_INPUT_SPLIT, NULL);
  1571. pluginsd_keywords_init_internal(p, PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING,
  1572. pluginsd_keyword_collision_check);
  1573. parser_destroy(p);
  1574. freez(user.hashtable);
  1575. if (!user.collisions) {
  1576. hashers[h].slots_needed = i;
  1577. break;
  1578. }
  1579. }
  1580. }
  1581. for(h = 0; hashers[h].name ;h++) {
  1582. if(hashers[h].slots_needed > 1000)
  1583. info("PARSER: hash function '%s' cannot be used without collisions under %zu slots", hashers[h].name, slots_to_check);
  1584. else
  1585. info("PARSER: hash function '%s' needs PARSER_KEYWORDS_HASHTABLE_SIZE (in parser.h) set to %zu", hashers[h].name, hashers[h].slots_needed);
  1586. }
  1587. p = parser_init(NULL, NULL, NULL, -1, PARSER_INPUT_SPLIT, NULL);
  1588. pluginsd_keywords_init(p, PARSER_INIT_PLUGINSD | PARSER_INIT_STREAMING);
  1589. parser_destroy(p);
  1590. return 0;
  1591. }