charts2json.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "charts2json.h"
  3. // generate JSON for the /api/v1/charts API call
  4. const char* get_release_channel() {
  5. static int use_stable = -1;
  6. if (use_stable == -1) {
  7. char filename[FILENAME_MAX + 1];
  8. snprintfz(filename, FILENAME_MAX, "%s/.environment", netdata_configured_user_config_dir);
  9. procfile *ff = procfile_open(filename, "=", PROCFILE_FLAG_DEFAULT);
  10. if (ff) {
  11. procfile_set_quotes(ff, "'\"");
  12. ff = procfile_readall(ff);
  13. if (ff) {
  14. unsigned int i;
  15. for (i = 0; i < procfile_lines(ff); i++) {
  16. if (!procfile_linewords(ff, i))
  17. continue;
  18. if (!strcmp(procfile_lineword(ff, i, 0), "RELEASE_CHANNEL")) {
  19. if (!strcmp(procfile_lineword(ff, i, 1), "stable"))
  20. use_stable = 1;
  21. else if (!strcmp(procfile_lineword(ff, i, 1), "nightly"))
  22. use_stable = 0;
  23. break;
  24. }
  25. }
  26. procfile_close(ff);
  27. }
  28. }
  29. if (use_stable == -1)
  30. use_stable = strchr(program_version, '-') ? 0 : 1;
  31. }
  32. return (use_stable)?"stable":"nightly";
  33. }
  34. void charts2json(RRDHOST *host, BUFFER *wb, int skip_volatile, int show_archived) {
  35. static char *custom_dashboard_info_js_filename = NULL;
  36. size_t c, dimensions = 0, memory = 0, alarms = 0;
  37. RRDSET *st;
  38. time_t now = now_realtime_sec();
  39. if(unlikely(!custom_dashboard_info_js_filename))
  40. custom_dashboard_info_js_filename = config_get(CONFIG_SECTION_WEB, "custom dashboard_info.js", "");
  41. buffer_sprintf(wb, "{\n"
  42. "\t\"hostname\": \"%s\""
  43. ",\n\t\"version\": \"%s\""
  44. ",\n\t\"release_channel\": \"%s\""
  45. ",\n\t\"os\": \"%s\""
  46. ",\n\t\"timezone\": \"%s\""
  47. ",\n\t\"update_every\": %d"
  48. ",\n\t\"history\": %ld"
  49. ",\n\t\"memory_mode\": \"%s\""
  50. ",\n\t\"custom_info\": \"%s\""
  51. ",\n\t\"charts\": {"
  52. , host->hostname
  53. , host->program_version
  54. , get_release_channel()
  55. , host->os
  56. , host->timezone
  57. , host->rrd_update_every
  58. , host->rrd_history_entries
  59. , rrd_memory_mode_name(host->rrd_memory_mode)
  60. , custom_dashboard_info_js_filename
  61. );
  62. c = 0;
  63. rrdhost_rdlock(host);
  64. rrdset_foreach_read(st, host) {
  65. if ((!show_archived && rrdset_is_available_for_viewers(st)) || (show_archived && rrdset_is_archived(st))) {
  66. if(c) buffer_strcat(wb, ",");
  67. buffer_strcat(wb, "\n\t\t\"");
  68. buffer_strcat(wb, st->id);
  69. buffer_strcat(wb, "\": ");
  70. rrdset2json(st, wb, &dimensions, &memory, skip_volatile);
  71. c++;
  72. st->last_accessed_time = now;
  73. }
  74. }
  75. RRDCALC *rc;
  76. for(rc = host->alarms; rc ; rc = rc->next) {
  77. if(rc->rrdset)
  78. alarms++;
  79. }
  80. rrdhost_unlock(host);
  81. buffer_sprintf(wb
  82. , "\n\t}"
  83. ",\n\t\"charts_count\": %zu"
  84. ",\n\t\"dimensions_count\": %zu"
  85. ",\n\t\"alarms_count\": %zu"
  86. ",\n\t\"rrd_memory_bytes\": %zu"
  87. ",\n\t\"hosts_count\": %zu"
  88. ",\n\t\"hosts\": ["
  89. , c
  90. , dimensions
  91. , alarms
  92. , memory
  93. , rrd_hosts_available
  94. );
  95. if(unlikely(rrd_hosts_available > 1)) {
  96. rrd_rdlock();
  97. size_t found = 0;
  98. RRDHOST *h;
  99. rrdhost_foreach_read(h) {
  100. if(!rrdhost_should_be_removed(h, host, now) && !rrdhost_flag_check(h, RRDHOST_FLAG_ARCHIVED)) {
  101. buffer_sprintf(wb
  102. , "%s\n\t\t{"
  103. "\n\t\t\t\"hostname\": \"%s\""
  104. "\n\t\t}"
  105. , (found > 0) ? "," : ""
  106. , h->hostname
  107. );
  108. found++;
  109. }
  110. }
  111. rrd_unlock();
  112. }
  113. else {
  114. buffer_sprintf(wb
  115. , "\n\t\t{"
  116. "\n\t\t\t\"hostname\": \"%s\""
  117. "\n\t\t}"
  118. , host->hostname
  119. );
  120. }
  121. buffer_sprintf(wb, "\n\t]\n}\n");
  122. }
  123. // generate collectors list for the api/v1/info call
  124. struct collector {
  125. char *plugin;
  126. char *module;
  127. };
  128. struct array_printer {
  129. int c;
  130. BUFFER *wb;
  131. };
  132. static int print_collector_callback(const char *name, void *entry, void *data) {
  133. (void)name;
  134. struct array_printer *ap = (struct array_printer *)data;
  135. BUFFER *wb = ap->wb;
  136. struct collector *col=(struct collector *) entry;
  137. if(ap->c) buffer_strcat(wb, ",");
  138. buffer_strcat(wb, "\n\t\t{\n\t\t\t\"plugin\": \"");
  139. buffer_strcat(wb, col->plugin);
  140. buffer_strcat(wb, "\",\n\t\t\t\"module\": \"");
  141. buffer_strcat(wb, col->module);
  142. buffer_strcat(wb, "\"\n\t\t}");
  143. (ap->c)++;
  144. return 0;
  145. }
  146. void chartcollectors2json(RRDHOST *host, BUFFER *wb) {
  147. DICTIONARY *dict = dictionary_create(DICTIONARY_FLAG_SINGLE_THREADED);
  148. RRDSET *st;
  149. char name[500];
  150. time_t now = now_realtime_sec();
  151. rrdhost_rdlock(host);
  152. rrdset_foreach_read(st, host) {
  153. if (rrdset_is_available_for_viewers(st)) {
  154. struct collector col = {
  155. .plugin = st->plugin_name ? st->plugin_name : "",
  156. .module = st->module_name ? st->module_name : ""
  157. };
  158. sprintf(name, "%s:%s", col.plugin, col.module);
  159. dictionary_set(dict, name, &col, sizeof(struct collector));
  160. st->last_accessed_time = now;
  161. }
  162. }
  163. rrdhost_unlock(host);
  164. struct array_printer ap = {
  165. .c = 0,
  166. .wb = wb
  167. };
  168. dictionary_walkthrough_read(dict, print_collector_callback, &ap);
  169. dictionary_destroy(dict);
  170. }