api_v1.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "internal.h"
  3. static void rrd_flags_to_buffer_json_array_items(RRD_FLAGS flags, BUFFER *wb) {
  4. if(flags & RRD_FLAG_QUEUED_FOR_HUB)
  5. buffer_json_add_array_item_string(wb, "QUEUED");
  6. if(flags & RRD_FLAG_DELETED)
  7. buffer_json_add_array_item_string(wb, "DELETED");
  8. if(flags & RRD_FLAG_COLLECTED)
  9. buffer_json_add_array_item_string(wb, "COLLECTED");
  10. if(flags & RRD_FLAG_UPDATED)
  11. buffer_json_add_array_item_string(wb, "UPDATED");
  12. if(flags & RRD_FLAG_ARCHIVED)
  13. buffer_json_add_array_item_string(wb, "ARCHIVED");
  14. if(flags & RRD_FLAG_OWN_LABELS)
  15. buffer_json_add_array_item_string(wb, "OWN_LABELS");
  16. if(flags & RRD_FLAG_LIVE_RETENTION)
  17. buffer_json_add_array_item_string(wb, "LIVE_RETENTION");
  18. if(flags & RRD_FLAG_HIDDEN)
  19. buffer_json_add_array_item_string(wb, "HIDDEN");
  20. if(flags & RRD_FLAG_QUEUED_FOR_PP)
  21. buffer_json_add_array_item_string(wb, "PENDING_UPDATES");
  22. }
  23. // ----------------------------------------------------------------------------
  24. // /api/v1/context(s) API
  25. struct rrdcontext_to_json {
  26. BUFFER *wb;
  27. RRDCONTEXT_TO_JSON_OPTIONS options;
  28. time_t after;
  29. time_t before;
  30. SIMPLE_PATTERN *chart_label_key;
  31. SIMPLE_PATTERN *chart_labels_filter;
  32. SIMPLE_PATTERN *chart_dimensions;
  33. size_t written;
  34. time_t now;
  35. time_t combined_first_time_s;
  36. time_t combined_last_time_s;
  37. RRD_FLAGS combined_flags;
  38. };
  39. static inline int rrdmetric_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
  40. const char *id = dictionary_acquired_item_name(item);
  41. struct rrdcontext_to_json * t = data;
  42. RRDMETRIC *rm = value;
  43. BUFFER *wb = t->wb;
  44. RRDCONTEXT_TO_JSON_OPTIONS options = t->options;
  45. time_t after = t->after;
  46. time_t before = t->before;
  47. if(unlikely(rrd_flag_is_deleted(rm) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
  48. return 0;
  49. if(after && (!rm->last_time_s || after > rm->last_time_s))
  50. return 0;
  51. if(before && (!rm->first_time_s || before < rm->first_time_s))
  52. return 0;
  53. if(t->chart_dimensions
  54. && !simple_pattern_matches_string(t->chart_dimensions, rm->id)
  55. && rm->name != rm->id
  56. && !simple_pattern_matches_string(t->chart_dimensions, rm->name))
  57. return 0;
  58. if(t->written) {
  59. t->combined_first_time_s = MIN(t->combined_first_time_s, rm->first_time_s);
  60. t->combined_last_time_s = MAX(t->combined_last_time_s, rm->last_time_s);
  61. t->combined_flags |= rrd_flags_get(rm);
  62. }
  63. else {
  64. t->combined_first_time_s = rm->first_time_s;
  65. t->combined_last_time_s = rm->last_time_s;
  66. t->combined_flags = rrd_flags_get(rm);
  67. }
  68. buffer_json_member_add_object(wb, id);
  69. if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
  70. char uuid[UUID_STR_LEN];
  71. uuid_unparse(rm->uuid, uuid);
  72. buffer_json_member_add_string(wb, "uuid", uuid);
  73. }
  74. buffer_json_member_add_string(wb, "name", string2str(rm->name));
  75. buffer_json_member_add_time_t(wb, "first_time_t", rm->first_time_s);
  76. buffer_json_member_add_time_t(wb, "last_time_t", rrd_flag_is_collected(rm) ? (long long)t->now : (long long)rm->last_time_s);
  77. buffer_json_member_add_boolean(wb, "collected", rrd_flag_is_collected(rm));
  78. if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
  79. buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(rm));
  80. if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
  81. buffer_json_member_add_array(wb, "flags");
  82. rrd_flags_to_buffer_json_array_items(rrd_flags_get(rm), wb);
  83. buffer_json_array_close(wb);
  84. }
  85. buffer_json_object_close(wb);
  86. t->written++;
  87. return 1;
  88. }
  89. static inline int rrdinstance_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
  90. const char *id = dictionary_acquired_item_name(item);
  91. struct rrdcontext_to_json *t_parent = data;
  92. RRDINSTANCE *ri = value;
  93. BUFFER *wb = t_parent->wb;
  94. RRDCONTEXT_TO_JSON_OPTIONS options = t_parent->options;
  95. time_t after = t_parent->after;
  96. time_t before = t_parent->before;
  97. bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions;
  98. if(unlikely(rrd_flag_is_deleted(ri) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
  99. return 0;
  100. if(after && (!ri->last_time_s || after > ri->last_time_s))
  101. return 0;
  102. if(before && (!ri->first_time_s || before < ri->first_time_s))
  103. return 0;
  104. if(t_parent->chart_label_key && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels, t_parent->chart_label_key,
  105. '\0', NULL))
  106. return 0;
  107. if(t_parent->chart_labels_filter && !rrdlabels_match_simple_pattern_parsed(ri->rrdlabels,
  108. t_parent->chart_labels_filter, ':',
  109. NULL))
  110. return 0;
  111. time_t first_time_s = ri->first_time_s;
  112. time_t last_time_s = ri->last_time_s;
  113. RRD_FLAGS flags = rrd_flags_get(ri);
  114. BUFFER *wb_metrics = NULL;
  115. if(options & RRDCONTEXT_OPTION_SHOW_METRICS || t_parent->chart_dimensions) {
  116. wb_metrics = buffer_create(4096, &netdata_buffers_statistics.buffers_api);
  117. buffer_json_initialize(wb_metrics, "\"", "\"", wb->json.depth + 2, false, false);
  118. struct rrdcontext_to_json t_metrics = {
  119. .wb = wb_metrics,
  120. .options = options,
  121. .chart_label_key = t_parent->chart_label_key,
  122. .chart_labels_filter = t_parent->chart_labels_filter,
  123. .chart_dimensions = t_parent->chart_dimensions,
  124. .after = after,
  125. .before = before,
  126. .written = 0,
  127. .now = t_parent->now,
  128. };
  129. dictionary_walkthrough_read(ri->rrdmetrics, rrdmetric_to_json_callback, &t_metrics);
  130. if(has_filter && !t_metrics.written) {
  131. buffer_free(wb_metrics);
  132. return 0;
  133. }
  134. first_time_s = t_metrics.combined_first_time_s;
  135. last_time_s = t_metrics.combined_last_time_s;
  136. flags = t_metrics.combined_flags;
  137. }
  138. if(t_parent->written) {
  139. t_parent->combined_first_time_s = MIN(t_parent->combined_first_time_s, first_time_s);
  140. t_parent->combined_last_time_s = MAX(t_parent->combined_last_time_s, last_time_s);
  141. t_parent->combined_flags |= flags;
  142. }
  143. else {
  144. t_parent->combined_first_time_s = first_time_s;
  145. t_parent->combined_last_time_s = last_time_s;
  146. t_parent->combined_flags = flags;
  147. }
  148. buffer_json_member_add_object(wb, id);
  149. if(options & RRDCONTEXT_OPTION_SHOW_UUIDS) {
  150. char uuid[UUID_STR_LEN];
  151. uuid_unparse(ri->uuid, uuid);
  152. buffer_json_member_add_string(wb, "uuid", uuid);
  153. }
  154. buffer_json_member_add_string(wb, "name", string2str(ri->name));
  155. buffer_json_member_add_string(wb, "context", string2str(ri->rc->id));
  156. buffer_json_member_add_string(wb, "title", string2str(ri->title));
  157. buffer_json_member_add_string(wb, "units", string2str(ri->units));
  158. buffer_json_member_add_string(wb, "family", string2str(ri->family));
  159. buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(ri->chart_type));
  160. buffer_json_member_add_uint64(wb, "priority", ri->priority);
  161. buffer_json_member_add_time_t(wb, "update_every", ri->update_every_s);
  162. buffer_json_member_add_time_t(wb, "first_time_t", first_time_s);
  163. buffer_json_member_add_time_t(wb, "last_time_t", (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_s);
  164. buffer_json_member_add_boolean(wb, "collected", flags & RRD_FLAG_COLLECTED);
  165. if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
  166. buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(ri));
  167. if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
  168. buffer_json_member_add_array(wb, "flags");
  169. rrd_flags_to_buffer_json_array_items(rrd_flags_get(ri), wb);
  170. buffer_json_array_close(wb);
  171. }
  172. if(options & RRDCONTEXT_OPTION_SHOW_LABELS && ri->rrdlabels && dictionary_entries(ri->rrdlabels)) {
  173. buffer_json_member_add_object(wb, "labels");
  174. rrdlabels_to_buffer_json_members(ri->rrdlabels, wb);
  175. buffer_json_object_close(wb);
  176. }
  177. if(wb_metrics) {
  178. buffer_json_member_add_object(wb, "dimensions");
  179. buffer_fast_strcat(wb, buffer_tostring(wb_metrics), buffer_strlen(wb_metrics));
  180. buffer_json_object_close(wb);
  181. buffer_free(wb_metrics);
  182. }
  183. buffer_json_object_close(wb);
  184. t_parent->written++;
  185. return 1;
  186. }
  187. static inline int rrdcontext_to_json_callback(const DICTIONARY_ITEM *item, void *value, void *data) {
  188. const char *id = dictionary_acquired_item_name(item);
  189. struct rrdcontext_to_json *t_parent = data;
  190. RRDCONTEXT *rc = value;
  191. BUFFER *wb = t_parent->wb;
  192. RRDCONTEXT_TO_JSON_OPTIONS options = t_parent->options;
  193. time_t after = t_parent->after;
  194. time_t before = t_parent->before;
  195. bool has_filter = t_parent->chart_label_key || t_parent->chart_labels_filter || t_parent->chart_dimensions;
  196. if(unlikely(rrd_flag_check(rc, RRD_FLAG_HIDDEN) && !(options & RRDCONTEXT_OPTION_SHOW_HIDDEN)))
  197. return 0;
  198. if(unlikely(rrd_flag_is_deleted(rc) && !(options & RRDCONTEXT_OPTION_SHOW_DELETED)))
  199. return 0;
  200. if(options & RRDCONTEXT_OPTION_DEEPSCAN)
  201. rrdcontext_recalculate_context_retention(rc, RRD_FLAG_NONE, false);
  202. if(after && (!rc->last_time_s || after > rc->last_time_s))
  203. return 0;
  204. if(before && (!rc->first_time_s || before < rc->first_time_s))
  205. return 0;
  206. time_t first_time_s = rc->first_time_s;
  207. time_t last_time_s = rc->last_time_s;
  208. RRD_FLAGS flags = rrd_flags_get(rc);
  209. BUFFER *wb_instances = NULL;
  210. if((options & (RRDCONTEXT_OPTION_SHOW_LABELS|RRDCONTEXT_OPTION_SHOW_INSTANCES|RRDCONTEXT_OPTION_SHOW_METRICS))
  211. || t_parent->chart_label_key
  212. || t_parent->chart_labels_filter
  213. || t_parent->chart_dimensions) {
  214. wb_instances = buffer_create(4096, &netdata_buffers_statistics.buffers_api);
  215. buffer_json_initialize(wb_instances, "\"", "\"", wb->json.depth + 2, false, false);
  216. struct rrdcontext_to_json t_instances = {
  217. .wb = wb_instances,
  218. .options = options,
  219. .chart_label_key = t_parent->chart_label_key,
  220. .chart_labels_filter = t_parent->chart_labels_filter,
  221. .chart_dimensions = t_parent->chart_dimensions,
  222. .after = after,
  223. .before = before,
  224. .written = 0,
  225. .now = t_parent->now,
  226. };
  227. dictionary_walkthrough_read(rc->rrdinstances, rrdinstance_to_json_callback, &t_instances);
  228. if(has_filter && !t_instances.written) {
  229. buffer_free(wb_instances);
  230. return 0;
  231. }
  232. first_time_s = t_instances.combined_first_time_s;
  233. last_time_s = t_instances.combined_last_time_s;
  234. flags = t_instances.combined_flags;
  235. }
  236. if(!(options & RRDCONTEXT_OPTION_SKIP_ID))
  237. buffer_json_member_add_object(wb, id);
  238. rrdcontext_lock(rc);
  239. buffer_json_member_add_string(wb, "title", string2str(rc->title));
  240. buffer_json_member_add_string(wb, "units", string2str(rc->units));
  241. buffer_json_member_add_string(wb, "family", string2str(rc->family));
  242. buffer_json_member_add_string(wb, "chart_type", rrdset_type_name(rc->chart_type));
  243. buffer_json_member_add_uint64(wb, "priority", rc->priority);
  244. buffer_json_member_add_time_t(wb, "first_time_t", first_time_s);
  245. buffer_json_member_add_time_t(wb, "last_time_t", (flags & RRD_FLAG_COLLECTED) ? (long long)t_parent->now : (long long)last_time_s);
  246. buffer_json_member_add_boolean(wb, "collected", (flags & RRD_FLAG_COLLECTED));
  247. if(options & RRDCONTEXT_OPTION_SHOW_DELETED)
  248. buffer_json_member_add_boolean(wb, "deleted", rrd_flag_is_deleted(rc));
  249. if(options & RRDCONTEXT_OPTION_SHOW_FLAGS) {
  250. buffer_json_member_add_array(wb, "flags");
  251. rrd_flags_to_buffer_json_array_items(rrd_flags_get(rc), wb);
  252. buffer_json_array_close(wb);
  253. }
  254. if(options & RRDCONTEXT_OPTION_SHOW_QUEUED) {
  255. buffer_json_member_add_array(wb, "queued_reasons");
  256. rrd_reasons_to_buffer_json_array_items(rc->queue.queued_flags, wb);
  257. buffer_json_array_close(wb);
  258. buffer_json_member_add_time_t(wb, "last_queued", (time_t)(rc->queue.queued_ut / USEC_PER_SEC));
  259. buffer_json_member_add_time_t(wb, "scheduled_dispatch", (time_t)(rc->queue.scheduled_dispatch_ut / USEC_PER_SEC));
  260. buffer_json_member_add_time_t(wb, "last_dequeued", (time_t)(rc->queue.dequeued_ut / USEC_PER_SEC));
  261. buffer_json_member_add_uint64(wb, "dispatches", rc->queue.dispatches);
  262. buffer_json_member_add_uint64(wb, "hub_version", rc->hub.version);
  263. buffer_json_member_add_uint64(wb, "version", rc->version);
  264. buffer_json_member_add_array(wb, "pp_reasons");
  265. rrd_reasons_to_buffer_json_array_items(rc->pp.queued_flags, wb);
  266. buffer_json_array_close(wb);
  267. buffer_json_member_add_time_t(wb, "pp_last_queued", (time_t)(rc->pp.queued_ut / USEC_PER_SEC));
  268. buffer_json_member_add_time_t(wb, "pp_last_dequeued", (time_t)(rc->pp.dequeued_ut / USEC_PER_SEC));
  269. buffer_json_member_add_uint64(wb, "pp_executed", rc->pp.executions);
  270. }
  271. rrdcontext_unlock(rc);
  272. if(wb_instances) {
  273. buffer_json_member_add_object(wb, "charts");
  274. buffer_fast_strcat(wb, buffer_tostring(wb_instances), buffer_strlen(wb_instances));
  275. buffer_json_object_close(wb);
  276. buffer_free(wb_instances);
  277. }
  278. if(!(options & RRDCONTEXT_OPTION_SKIP_ID))
  279. buffer_json_object_close(wb);
  280. t_parent->written++;
  281. return 1;
  282. }
  283. int rrdcontext_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, const char *context, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) {
  284. if(!host->rrdctx.contexts) {
  285. netdata_log_error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host));
  286. return HTTP_RESP_NOT_FOUND;
  287. }
  288. RRDCONTEXT_ACQUIRED *rca = (RRDCONTEXT_ACQUIRED *)dictionary_get_and_acquire_item(host->rrdctx.contexts, context);
  289. if(!rca) return HTTP_RESP_NOT_FOUND;
  290. RRDCONTEXT *rc = rrdcontext_acquired_value(rca);
  291. if(after != 0 && before != 0)
  292. rrdr_relative_window_to_absolute(&after, &before, NULL);
  293. buffer_json_initialize(wb, "\"", "\"", 0, true, false);
  294. struct rrdcontext_to_json t_contexts = {
  295. .wb = wb,
  296. .options = options|RRDCONTEXT_OPTION_SKIP_ID,
  297. .chart_label_key = chart_label_key,
  298. .chart_labels_filter = chart_labels_filter,
  299. .chart_dimensions = chart_dimensions,
  300. .after = after,
  301. .before = before,
  302. .written = 0,
  303. .now = now_realtime_sec(),
  304. };
  305. rrdcontext_to_json_callback((DICTIONARY_ITEM *)rca, rc, &t_contexts);
  306. buffer_json_finalize(wb);
  307. rrdcontext_release(rca);
  308. if(!t_contexts.written)
  309. return HTTP_RESP_NOT_FOUND;
  310. return HTTP_RESP_OK;
  311. }
  312. int rrdcontexts_to_json(RRDHOST *host, BUFFER *wb, time_t after, time_t before, RRDCONTEXT_TO_JSON_OPTIONS options, SIMPLE_PATTERN *chart_label_key, SIMPLE_PATTERN *chart_labels_filter, SIMPLE_PATTERN *chart_dimensions) {
  313. if(!host->rrdctx.contexts) {
  314. netdata_log_error("%s(): request for host '%s' that does not have rrdcontexts initialized.", __FUNCTION__, rrdhost_hostname(host));
  315. return HTTP_RESP_NOT_FOUND;
  316. }
  317. char node_uuid[UUID_STR_LEN] = "";
  318. if(host->node_id)
  319. uuid_unparse(*host->node_id, node_uuid);
  320. if(after != 0 && before != 0)
  321. rrdr_relative_window_to_absolute(&after, &before, NULL);
  322. buffer_json_initialize(wb, "\"", "\"", 0, true, false);
  323. buffer_json_member_add_string(wb, "hostname", rrdhost_hostname(host));
  324. buffer_json_member_add_string(wb, "machine_guid", host->machine_guid);
  325. buffer_json_member_add_string(wb, "node_id", node_uuid);
  326. buffer_json_member_add_string(wb, "claim_id", host->aclk_state.claimed_id ? host->aclk_state.claimed_id : "");
  327. if(options & RRDCONTEXT_OPTION_SHOW_LABELS) {
  328. buffer_json_member_add_object(wb, "host_labels");
  329. rrdlabels_to_buffer_json_members(host->rrdlabels, wb);
  330. buffer_json_object_close(wb);
  331. }
  332. buffer_json_member_add_object(wb, "contexts");
  333. struct rrdcontext_to_json t_contexts = {
  334. .wb = wb,
  335. .options = options,
  336. .chart_label_key = chart_label_key,
  337. .chart_labels_filter = chart_labels_filter,
  338. .chart_dimensions = chart_dimensions,
  339. .after = after,
  340. .before = before,
  341. .written = 0,
  342. .now = now_realtime_sec(),
  343. };
  344. dictionary_walkthrough_read(host->rrdctx.contexts, rrdcontext_to_json_callback, &t_contexts);
  345. buffer_json_object_close(wb);
  346. buffer_json_finalize(wb);
  347. return HTTP_RESP_OK;
  348. }