]> arthur.barton.de Git - netdata.git/blobdiff - src/rrd2json.c
remove user-specified zero dimensions with nonzero option
[netdata.git] / src / rrd2json.c
index bbf48a2965c44e06bb0fa34a779a5c59439fa2c1..568d04e8d9275e20291d2532d77c96973d88cdde 100644 (file)
@@ -1,9 +1,6 @@
 #include "common.h"
 
-#define HOSTNAME_MAX 1024
-char *hostname = "unknown";
-
-void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
+void rrd_stats_api_v1_chart_with_data(RRDSET *st, BUFFER *wb, size_t *dimensions_count, size_t *memory_used)
 {
     pthread_rwlock_rdlock(&st->rwlock);
 
@@ -44,7 +41,7 @@ void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
 
     unsigned long memory = st->memsize;
 
-    int c = 0;
+    size_t dimensions = 0;
     RRDDIM *rd;
     for(rd = st->dimensions; rd ; rd = rd->next) {
         if(rd->flags & RRDDIM_FLAG_HIDDEN) continue;
@@ -54,25 +51,36 @@ void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb)
         buffer_sprintf(wb,
             "%s"
             "\t\t\t\t\"%s\": { \"name\": \"%s\" }"
-            , c?",\n":""
+            , dimensions?",\n":""
             , rd->id
             , rd->name
             );
 
-        c++;
+        dimensions++;
     }
 
+    if(dimensions_count) *dimensions_count += dimensions;
+    if(memory_used) *memory_used += memory;
+
+    buffer_strcat(wb, "\n\t\t\t},\n\t\t\t\"green\": ");
+    buffer_rrd_value(wb, st->green);
+    buffer_strcat(wb, ",\n\t\t\t\"red\": ");
+    buffer_rrd_value(wb, st->red);
+
     buffer_sprintf(wb,
-        "\n\t\t\t}\n"
-        "\t\t}"
+        "\n\t\t}"
         );
 
     pthread_rwlock_unlock(&st->rwlock);
 }
 
+void rrd_stats_api_v1_chart(RRDSET *st, BUFFER *wb) {
+    rrd_stats_api_v1_chart_with_data(st, wb, NULL, NULL);
+}
+
 void rrd_stats_api_v1_charts(BUFFER *wb)
 {
-    long c;
+    size_t c, dimensions = 0, memory = 0, alarms = 0;
     RRDSET *st;
 
     buffer_sprintf(wb, "{\n"
@@ -80,7 +88,7 @@ void rrd_stats_api_v1_charts(BUFFER *wb)
         ",\n\t\"update_every\": %d"
         ",\n\t\"history\": %d"
         ",\n\t\"charts\": {"
-        , hostname
+        , localhost.hostname
         , rrd_update_every
         , rrd_default_history_entries
         );
@@ -92,19 +100,202 @@ void rrd_stats_api_v1_charts(BUFFER *wb)
             buffer_strcat(wb, "\n\t\t\"");
             buffer_strcat(wb, st->id);
             buffer_strcat(wb, "\": ");
-            rrd_stats_api_v1_chart(st, wb);
+            rrd_stats_api_v1_chart_with_data(st, wb, &dimensions, &memory);
             c++;
         }
     }
+
+    RRDCALC *rc;
+    for(rc = localhost.alarms; rc ; rc = rc->next) {
+        if(rc->rrdset)
+            alarms++;
+    }
+    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+
+    buffer_sprintf(wb, "\n\t}"
+                    ",\n\t\"charts_count\": %zu"
+                    ",\n\t\"dimensions_count\": %zu"
+                    ",\n\t\"alarms_count\": %zu"
+                    ",\n\t\"rrd_memory_bytes\": %zu"
+                    "\n}\n"
+                   , c
+                   , dimensions
+                   , alarms
+                   , memory
+    );
+}
+
+// ----------------------------------------------------------------------------
+// PROMETHEUS
+// /api/v1/allmetrics?format=prometheus
+
+static inline size_t prometheus_name_copy(char *d, const char *s, size_t usable) {
+    size_t n;
+
+    for(n = 0; *s && n < usable ; d++, s++, n++) {
+        register char c = *s;
+
+        if(unlikely(!isalnum(c))) *d = '_';
+        else *d = c;
+    }
+    *d = '\0';
+
+    return n;
+}
+
+#define PROMETHEUS_ELEMENT_MAX 256
+
+void rrd_stats_api_v1_charts_allmetrics_prometheus(BUFFER *wb)
+{
+    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+
+    char host[PROMETHEUS_ELEMENT_MAX + 1];
+    prometheus_name_copy(host, config_get("global", "hostname", "localhost"), PROMETHEUS_ELEMENT_MAX);
+
+    // for each chart
+    RRDSET *st;
+    for(st = localhost.rrdset_root; st ; st = st->next) {
+        char chart[PROMETHEUS_ELEMENT_MAX + 1];
+        prometheus_name_copy(chart, st->id, PROMETHEUS_ELEMENT_MAX);
+
+        buffer_strcat(wb, "\n");
+        if(st->enabled && st->dimensions) {
+            pthread_rwlock_rdlock(&st->rwlock);
+
+            // for each dimension
+            RRDDIM *rd;
+            for(rd = st->dimensions; rd ; rd = rd->next) {
+                if(rd->counter) {
+                    char dimension[PROMETHEUS_ELEMENT_MAX + 1];
+                    prometheus_name_copy(dimension, rd->id, PROMETHEUS_ELEMENT_MAX);
+
+                    // buffer_sprintf(wb, "# HELP %s.%s %s\n", st->id, rd->id, st->units);
+
+                    switch(rd->algorithm) {
+                        case RRDDIM_INCREMENTAL:
+                        case RRDDIM_PCENT_OVER_DIFF_TOTAL:
+                            buffer_sprintf(wb, "# TYPE %s_%s counter\n", chart, dimension);
+                            break;
+
+                        default:
+                            buffer_sprintf(wb, "# TYPE %s_%s gauge\n", chart, dimension);
+                            break;
+                    }
+
+                    // calculated_number n = (calculated_number)rd->last_collected_value * (calculated_number)(abs(rd->multiplier)) / (calculated_number)(abs(rd->divisor));
+                    // buffer_sprintf(wb, "%s.%s " CALCULATED_NUMBER_FORMAT " %llu\n", st->id, rd->id, n,
+                    //        (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
+
+                    buffer_sprintf(wb, "%s_%s{instance=\"%s\"} " COLLECTED_NUMBER_FORMAT " %llu\n",
+                            chart, dimension, host, rd->last_collected_value,
+                            (unsigned long long)((rd->last_collected_time.tv_sec * 1000) + (rd->last_collected_time.tv_usec / 1000)));
+
+                }
+            }
+
+            pthread_rwlock_unlock(&st->rwlock);
+        }
+    }
+
     pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+}
+
+// ----------------------------------------------------------------------------
+// BASH
+// /api/v1/allmetrics?format=bash
 
-    buffer_strcat(wb, "\n\t}\n}\n");
+static inline size_t shell_name_copy(char *d, const char *s, size_t usable) {
+    size_t n;
+
+    for(n = 0; *s && n < usable ; d++, s++, n++) {
+        register char c = *s;
+
+        if(unlikely(!isalnum(c))) *d = '_';
+        else *d = (char)toupper(c);
+    }
+    *d = '\0';
+
+    return n;
 }
 
+#define SHELL_ELEMENT_MAX 100
+
+void rrd_stats_api_v1_charts_allmetrics_shell(BUFFER *wb)
+{
+    pthread_rwlock_rdlock(&localhost.rrdset_root_rwlock);
+
+    char host[SHELL_ELEMENT_MAX + 1];
+    shell_name_copy(host, config_get("global", "hostname", "localhost"), SHELL_ELEMENT_MAX);
+
+    // for each chart
+    RRDSET *st;
+    for(st = localhost.rrdset_root; st ; st = st->next) {
+        calculated_number total = 0.0;
+        char chart[SHELL_ELEMENT_MAX + 1];
+        shell_name_copy(chart, st->id, SHELL_ELEMENT_MAX);
+
+        buffer_sprintf(wb, "\n# chart: %s (name: %s)\n", st->id, st->name);
+        if(st->enabled && st->dimensions) {
+            pthread_rwlock_rdlock(&st->rwlock);
+
+            // for each dimension
+            RRDDIM *rd;
+            for(rd = st->dimensions; rd ; rd = rd->next) {
+                if(rd->counter) {
+                    char dimension[SHELL_ELEMENT_MAX + 1];
+                    shell_name_copy(dimension, rd->id, SHELL_ELEMENT_MAX);
+
+                    calculated_number n = rd->last_stored_value;
+
+                    if(isnan(n) || isinf(n))
+                        buffer_sprintf(wb, "NETDATA_%s_%s=\"\"      # %s\n", chart, dimension, st->units);
+                    else {
+                        if(rd->multiplier < 0 || rd->divisor < 0) n = -n;
+                        n = roundl(n);
+                        if(!(rd->flags & RRDDIM_FLAG_HIDDEN)) total += n;
+                        buffer_sprintf(wb, "NETDATA_%s_%s=\"%0.0Lf\"      # %s\n", chart, dimension, n, st->units);
+                    }
+                }
+            }
+
+            total = roundl(total);
+            buffer_sprintf(wb, "NETDATA_%s_VISIBLETOTAL=\"%0.0Lf\"      # %s\n", chart, total, st->units);
+            pthread_rwlock_unlock(&st->rwlock);
+        }
+    }
+
+    buffer_strcat(wb, "\n# NETDATA ALARMS RUNNING\n");
+
+    RRDCALC *rc;
+    for(rc = localhost.alarms; rc ;rc = rc->next) {
+        if(!rc->rrdset) continue;
+
+        char chart[SHELL_ELEMENT_MAX + 1];
+        shell_name_copy(chart, rc->rrdset->id, SHELL_ELEMENT_MAX);
+
+        char alarm[SHELL_ELEMENT_MAX + 1];
+        shell_name_copy(alarm, rc->name, SHELL_ELEMENT_MAX);
+
+        calculated_number n = rc->value;
+
+        if(isnan(n) || isinf(n))
+            buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"\"      # %s\n", chart, alarm, rc->units);
+        else {
+            n = roundl(n);
+            buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_VALUE=\"%0.0Lf\"      # %s\n", chart, alarm, n, rc->units);
+        }
+
+        buffer_sprintf(wb, "NETDATA_ALARM_%s_%s_STATUS=\"%s\"\n", chart, alarm, rrdcalc_status2string(rc->status));
+    }
+
+    pthread_rwlock_unlock(&localhost.rrdset_root_rwlock);
+}
+
+// ----------------------------------------------------------------------------
 
 unsigned long rrd_stats_one_json(RRDSET *st, char *options, BUFFER *wb)
 {
-    time_t now = time(NULL);
+    time_t now = now_realtime_sec();
 
     pthread_rwlock_rdlock(&st->rwlock);
 
@@ -242,7 +433,7 @@ void rrd_stats_all_json(BUFFER *wb)
         "\t\"history\": %d,\n"
         "\t\"memory\": %lu\n"
         "}\n"
-        , hostname
+        , localhost.hostname
         , rrd_update_every
         , rrd_default_history_entries
         , memory
@@ -350,7 +541,7 @@ static void rrdr_dump(RRDR *r)
 }
 */
 
-void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
+void rrdr_disable_not_selected_dimensions(RRDR *r, uint32_t options, const char *dims)
 {
     char b[strlen(dims) + 1];
     char *o = b, *tok;
@@ -376,7 +567,8 @@ void rrdr_disable_not_selected_dimensions(RRDR *r, const char *dims)
                 // since the user needs this dimension
                 // make it appear as NONZERO, to return it
                 // even if the dimension has only zeros
-                r->od[c] |= RRDR_NONZERO;
+                // unless option non_zero is set
+                if (!(options & RRDR_OPTION_NONZERO)) r->od[c] |= RRDR_NONZERO;
             }
         }
     }
@@ -433,17 +625,21 @@ void rrdr_buffer_print_format(BUFFER *wb, uint32_t format)
 
 uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
 {
+    (void)dims;
+
     if(options & RRDR_OPTION_NONZERO) {
         long i;
 
-        if(dims && *dims) {
+        // commented due to #1514
+
+        //if(dims && *dims) {
             // the caller wants specific dimensions
             // disable NONZERO option
             // to make sure we don't accidentally prevent
             // the specific dimensions from being returned
-            i = 0;
-        }
-        else {
+            // i = 0;
+        //}
+        //else {
             // find how many dimensions are not zero
             long c;
             RRDDIM *rd;
@@ -452,7 +648,7 @@ uint32_t rrdr_check_options(RRDR *r, uint32_t options, const char *dims)
                 if(unlikely(!(r->od[c] & RRDR_NONZERO))) continue;
                 i++;
             }
-        }
+        //}
 
         // if with nonzero we get i = 0 (no dimensions will be returned)
         // disable nonzero to show all dimensions
@@ -1035,12 +1231,12 @@ inline static calculated_number rrdr2value(RRDR *r, long i, uint32_t options, in
     }
 
     if(unlikely(all_null)) {
-        if(likely(*all_values_are_null))
+        if(likely(all_values_are_null))
             *all_values_are_null = 1;
         return 0;
     }
     else {
-        if(likely(*all_values_are_null))
+        if(likely(all_values_are_null))
             *all_values_are_null = 0;
     }
 
@@ -1157,7 +1353,7 @@ inline static void rrdr_free(RRDR *r)
     freez(r);
 }
 
-inline void rrdr_done(RRDR *r)
+static inline void rrdr_done(RRDR *r)
 {
     r->rows = r->c + 1;
     r->c = 0;
@@ -1208,19 +1404,32 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
     time_t last_entry_t  = rrdset_last_entry_t(st);
 
     if(before == 0 && after == 0) {
+        // dump the all the data
         before = last_entry_t;
         after = first_entry_t;
         absolute_period_requested = 0;
     }
 
-    // allow relative for before and after
-    if(((before < 0)?-before:before) <= (st->update_every * st->entries)) {
-        before = last_entry_t + before;
+    // allow relative for before (smaller than API_RELATIVE_TIME_MAX)
+    if(((before < 0)?-before:before) <= API_RELATIVE_TIME_MAX) {
+        if(abs(before) % st->update_every) {
+            // make sure it is multiple of st->update_every
+            if(before < 0) before = before - st->update_every - before % st->update_every;
+            else           before = before + st->update_every - before % st->update_every;
+        }
+        if(before > 0) before = first_entry_t + before;
+        else           before = last_entry_t  + before;
         absolute_period_requested = 0;
     }
 
-    if(((after < 0)?-after:after) <= (st->update_every * st->entries)) {
+    // allow relative for after (smaller than API_RELATIVE_TIME_MAX)
+    if(((after < 0)?-after:after) <= API_RELATIVE_TIME_MAX) {
         if(after == 0) after = -st->update_every;
+        if(abs(after) % st->update_every) {
+            // make sure it is multiple of st->update_every
+            if(after < 0) after = after - st->update_every - after % st->update_every;
+            else          after = after + st->update_every - after % st->update_every;
+        }
         after = before + after;
         absolute_period_requested = 0;
     }
@@ -1366,7 +1575,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
     long c;
     for( rd = st->dimensions, c = 0 ; rd && c < dimensions ; rd = rd->next, c++) {
         last_values[c] = 0;
-        group_values[c] = 0;
+        group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
         group_counts[c] = 0;
         group_options[c] = 0;
         found_non_zero[c] = 0;
@@ -1445,14 +1654,22 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
                 group_options[c] |= RRDR_RESET;
 
             switch(group_method) {
+                case GROUP_MIN:
+                    if(unlikely(isnan(group_values[c])) ||
+                            fabsl(value) < fabsl(group_values[c]))
+                        group_values[c] = value;
+                    break;
+
                 case GROUP_MAX:
-                    if(unlikely(fabsl(value) > fabsl(group_values[c])))
+                    if(unlikely(isnan(group_values[c])) ||
+                            fabsl(value) > fabsl(group_values[c]))
                         group_values[c] = value;
                     break;
 
                 default:
                 case GROUP_SUM:
                 case GROUP_AVERAGE:
+                case GROUP_UNDEFINED:
                     group_values[c] += value;
                     break;
 
@@ -1487,23 +1704,39 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
                 if(unlikely(group_counts[c] == 0)) {
                     cn[c] = 0.0;
                     co[c] |= RRDR_EMPTY;
-                }
-                else if(unlikely(group_method == GROUP_AVERAGE)) {
-                    // GROUP_AVERAGE
-                    cn[c] = group_values[c] / group_counts[c];
+                    group_values[c] = (group_method == GROUP_MAX || group_method == GROUP_MIN)?NAN:0;
                 }
                 else {
-                    // GROUP_SUM
-                    // GROUP_MAX
-                    // GROUP_INCREMENTAL_SUM
-                    cn[c] = group_values[c];
-                }
+                    switch(group_method) {
+                        case GROUP_MIN:
+                        case GROUP_MAX:
+                            if(unlikely(isnan(group_values[c])))
+                                cn[c] = 0;
+                            else {
+                                cn[c] = group_values[c];
+                                group_values[c] = NAN;
+                            }
+                            break;
+
+                        case GROUP_SUM:
+                        case GROUP_INCREMENTAL_SUM:
+                            cn[c] = group_values[c];
+                            group_values[c] = 0;
+                            break;
+
+                        default:
+                        case GROUP_AVERAGE:
+                        case GROUP_UNDEFINED:
+                            cn[c] = group_values[c] / group_counts[c];
+                            group_values[c] = 0;
+                            break;
+                    }
 
-                if(cn[c] < r->min) r->min = cn[c];
-                if(cn[c] > r->max) r->max = cn[c];
+                    if(cn[c] < r->min) r->min = cn[c];
+                    if(cn[c] > r->max) r->max = cn[c];
+                }
 
-                // reset them for the next loop
-                group_values[c] = 0;
+                // reset for the next loop
                 group_counts[c] = 0;
                 group_options[c] = 0;
             }
@@ -1520,7 +1753,7 @@ RRDR *rrd2rrdr(RRDSET *st, long points, long long after, long long before, int g
     return r;
 }
 
-int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *latest_timestamp, int *value_is_null)
+int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, const char *dimensions, long points, long long after, long long before, int group_method, uint32_t options, time_t *db_after, time_t *db_before, int *value_is_null)
 {
     RRDR *r = rrd2rrdr(st, points, after, before, group_method, !(options & RRDR_OPTION_NOT_ALIGNED));
     if(!r) {
@@ -1530,22 +1763,26 @@ int rrd2value(RRDSET *st, BUFFER *wb, calculated_number *n, BUFFER *dimensions,
 
     if(rrdr_rows(r) == 0) {
         rrdr_free(r);
+
+        if(db_after)  *db_after  = 0;
+        if(db_before) *db_before = 0;
         if(value_is_null) *value_is_null = 1;
+
         return 400;
     }
 
     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
-        wb->options |= WB_CONTENT_NO_CACHEABLE;
+        buffer_no_cacheable(wb);
     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
-        wb->options |= WB_CONTENT_CACHEABLE;
+        buffer_cacheable(wb);
 
-    options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
+    options = rrdr_check_options(r, options, dimensions);
 
     if(dimensions)
-        rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
+        rrdr_disable_not_selected_dimensions(r, options, dimensions);
 
-    if(latest_timestamp)
-        *latest_timestamp = r->before;
+    if(db_after)  *db_after  = r->after;
+    if(db_before) *db_before = r->before;
 
     long i = (options & RRDR_OPTION_REVERSED)?rrdr_rows(r) - 1:0;
     *n = rrdr2value(r, i, options, value_is_null);
@@ -1563,14 +1800,14 @@ int rrd2format(RRDSET *st, BUFFER *wb, BUFFER *dimensions, uint32_t format, long
     }
 
     if(r->result_options & RRDR_RESULT_OPTION_RELATIVE)
-        wb->options |= WB_CONTENT_NO_CACHEABLE;
+        buffer_no_cacheable(wb);
     else if(r->result_options & RRDR_RESULT_OPTION_ABSOLUTE)
-        wb->options |= WB_CONTENT_CACHEABLE;
+        buffer_cacheable(wb);
 
     options = rrdr_check_options(r, options, (dimensions)?buffer_tostring(dimensions):NULL);
 
     if(dimensions)
-        rrdr_disable_not_selected_dimensions(r, buffer_tostring(dimensions));
+        rrdr_disable_not_selected_dimensions(r, options, buffer_tostring(dimensions));
 
     if(latest_timestamp && rrdr_rows(r) > 0)
         *latest_timestamp = r->before;