]> arthur.barton.de Git - netdata.git/commitdiff
basic health monitoring information is now rendered on the dashboard
authorCosta Tsaousis <costa@tsaousis.gr>
Thu, 25 Aug 2016 21:44:02 +0000 (00:44 +0300)
committerCosta Tsaousis <costa@tsaousis.gr>
Thu, 25 Aug 2016 21:44:02 +0000 (00:44 +0300)
src/health.c
src/health.h
src/web_buffer_svg.c
src/web_client.c
web/dashboard.js
web/index.html

index 827fd3de0b8a6015c0baee7c148afef70ac12d10..89fad3f8e99b8c3914dce91ca3242686ba4ba5af 100644 (file)
@@ -1563,28 +1563,31 @@ static inline void health_rrdcalc2json_nolock(BUFFER *wb, RRDCALC *rc) {
 //
 //}
 
-void health_alarms2json(RRDHOST *host, BUFFER *wb) {
+void health_alarms2json(RRDHOST *host, BUFFER *wb, int all) {
     int i;
     rrdhost_rdlock(&localhost);
 
     buffer_strcat(wb, "{\n\t\"alarms\": {\n");
     RRDCALC *rc;
-    for(i = 0, rc = host->alarms; rc ; rc = rc->next, i++) {
-        if(!rc->rrdset) continue;
+    for(i = 0, rc = host->alarms; rc ; rc = rc->next) {
+        if(!rc->rrdset)
+            continue;
+
+        if(!all && !(rc->status == RRDCALC_STATUS_WARNING || rc->status == RRDCALC_STATUS_CRITICAL))
+            continue;
 
         if(likely(i)) buffer_strcat(wb, ",\n");
         health_rrdcalc2json_nolock(wb, rc);
+        i++;
     }
 
-    buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
+//    buffer_strcat(wb, "\n\t},\n\t\"templates\": {");
 
 //    RRDCALCTEMPLATE *rt;
 //    for(rt = host->templates; rt ; rt = rt->next)
 //        health_rrdcalctemplate2json_nolock(wb, rt);
 
-    buffer_strcat(wb, "\n\t}");
-    buffer_sprintf(wb, ",\n\t\"now\": %lu", (unsigned long)time(NULL));
-    buffer_strcat(wb, "\n}\n");
+    buffer_sprintf(wb, "\n\t},\n\t\"now\": %lu\n}\n", (unsigned long)time(NULL));
     rrdhost_unlock(&localhost);
 }
 
index b6b890911859d809587f2ce828ba522193affbc8..7399660a68ec19e7343dbf5929fa12b1806b3c74 100644 (file)
@@ -270,7 +270,7 @@ extern void *health_main(void *ptr);
 extern void health_reload(void);
 
 extern int health_variable_lookup(const char *variable, uint32_t hash, RRDCALC *rc, calculated_number *result);
-extern void health_alarms2json(RRDHOST *host, BUFFER *wb);
+extern void health_alarms2json(RRDHOST *host, BUFFER *wb, int all);
 extern void health_alarm_log2json(RRDHOST *host, BUFFER *wb);
 
 #endif //NETDATA_HEALTH_H
index daabee3df759e6bc9ac82aa4b8e2d4c523e672b3..9900cc398a9c976ec4431d6b9abdf8f1566fb89e 100644 (file)
@@ -1,7 +1,7 @@
 #include "common.h"
 
 #define BADGE_HORIZONTAL_PADDING 4
-#define VERDANA_KERNING 0.5
+#define VERDANA_KERNING 0.0
 
 /*
  * verdana11_widths[] has been generated with this method:
index 29d633b2fb39be6f03881c4f4086c0d37fcfe4c0..69f61a422082953198c2dcf98ff5e4c01bf8c568 100644 (file)
@@ -662,11 +662,19 @@ int web_client_api_request_v1_data_group(char *name, int def)
 
 int web_client_api_request_v1_alarms(struct web_client *w, char *url)
 {
-    (void)url;
+    int all = 0;
+
+    while(url) {
+        char *value = mystrsep(&url, "?&[]");
+        if (!value || !*value) continue;
+
+        if(!strcmp(value, "all")) all = 1;
+        else if(!strcmp(value, "active")) all = 0;
+    }
 
     buffer_flush(w->response.data);
     w->response.data->contenttype = CT_APPLICATION_JSON;
-    health_alarms2json(&localhost, w->response.data);
+    health_alarms2json(&localhost, w->response.data, all);
     return 200;
 }
 
@@ -737,6 +745,8 @@ cleanup:
 }
 
 int web_client_api_request_v1_badge(struct web_client *w, char *url) {
+    char buf[100 + 1];
+
     int ret = 400;
     buffer_flush(w->response.data);
 
@@ -851,8 +861,15 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
     }
 
     if(!label) {
-        if(alarm)
-            label = alarm;
+        if(alarm) {
+            snprintfz(buf, 100, "%s%s%s", (rc->rrdset)?rc->rrdset->family:"", (rc->rrdset)?": ":"", rc->name);
+            char *s = buf;
+            while(*s) {
+                if(*s == '_') *s = ' ';
+                s++;
+            }
+            label = buf;
+        }
         else if(dimensions) {
             const char *dim = buffer_tostring(dimensions);
             if(*dim == '|') dim++;
@@ -864,6 +881,8 @@ int web_client_api_request_v1_badge(struct web_client *w, char *url) {
     if(!units) {
         if(options & RRDR_OPTION_PERCENTAGE)
             units="%";
+        else if(alarm && rc && rc->calculation)
+            units = "";
         else
             units = st->units;
     }
index 6bc35031701f65c5ea6c723518a605a8b7d8edb8..f686d3deaa09b93d5faaa5addf814850951030a4 100644 (file)
@@ -16,6 +16,7 @@
 // var netdataRegistryCallback = null;  // Callback function that will be invoked with one param,
 //                                         the URLs from the registry
 // var netdataShowHelp = true;          // enable/disable help
+// var netdataShowAlarms = true;        // enable/disable help
 //
 // You can also set the default netdata server, using the following.
 // When this variable is not set, we assume the page is hosted on your
     if(typeof netdataShowHelp === 'undefined')
         netdataShowHelp = true;
 
+    if(typeof netdataShowAlarms === 'undefined')
+        netdataShowAlarms = true;
+
     NETDATA.colors = NETDATA.themes.current.colors;
 
     // these are the colors Google Charts are using
         // console.log('onscroll');
 
         NETDATA.options.last_page_scroll = new Date().getTime();
+        NETDATA.options.auto_refresher_stop_until = 0;
+
         if(NETDATA.options.targets === null) return;
 
         // when the user scrolls he sees that we have
         // the charts back to visible quickly
         var targets = NETDATA.options.targets;
         var len = targets.length;
-        while(len--) targets[len].isVisible();
+        while(len--) {
+            if(targets[len]._updating === true) {
+                if (typeof targets[len].xhr !== 'undefined') {
+                    targets[len].xhr.abort();
+                    targets[len].running = false;
+                    targets[len]._updating = false;
+                }
+                targets[len].isVisible();
+            }
+        }
     };
 
     window.onresize = NETDATA.onresize;
         411: { message: "Netdata registry server send invalid response to DELETE ", alert: false },
         412: { message: "Netdata registry DELETE failed", alert: false },
         413: { message: "Netdata registry server send invalid response to SWITCH ", alert: false },
-        414: { message: "Netdata registry SWITCH failed", alert: false }
+        414: { message: "Netdata registry SWITCH failed", alert: false },
+        415: { message: "Netdata alarms download failed", alert: false },
+        416: { message: "Netdata alarms log download failed", alert: false }
     };
     NETDATA.errorLast = {
         code: 0,
                 async: true,
                 xhrFields: { withCredentials: true } // required for the cookie
             })
-            .success(function(data) {
+            .done(function(data) {
+                that.xhr = undefined;
+
                 if(that.debug === true)
                     that.log('data received. updating chart.');
 
                 that.updateChartWithData(data);
             })
-            .fail(function() {
-                error('data download failed for url: ' + that.data_url);
+            .fail(function(msg) {
+                that.xhr = undefined;
+
+                if(msg.statusText !== 'abort')
+                    error('data download failed for url: ' + that.data_url);
             })
             .always(function() {
+                that.xhr = undefined;
+
                 NETDATA.statistics.refreshes_active--;
                 that._updating = false;
                 if(typeof callback === 'function') callback();
     // the first set will be executed in parallel
     // the second will be given to NETDATA.chartRefresher_uninitialized()
     NETDATA.chartRefresher = function() {
+        // console.log('auto-refresher...');
+
         if(NETDATA.options.pause === true) {
             // console.log('auto-refresher is paused');
             setTimeout(NETDATA.chartRefresher,
         }
 
         if(NETDATA.options.current.parallel_refresher === false) {
+            // console.log('auto-refresher is calling chartRefresherNoParallel(0)');
             NETDATA.chartRefresherNoParallel(0);
             return;
         }
         if(NETDATA.options.updated_dom === true) {
             // the dom has been updated
             // get the dom parts again
+            // console.log('auto-refresher is calling parseDom()');
             NETDATA.parseDom(NETDATA.chartRefresher);
             return;
         }
         }
 
         if(parallel.length > 0) {
+            // console.log('auto-refresher executing in parallel for ' + parallel.length.toString() + ' charts');
             // this will execute the jobs in parallel
             $(parallel).each(function() {
                 this.autoRefresh();
             })
         }
+        else {
+            console.log('auto-refresher nothing to do');
+        }
 
         // run the next refresh iteration
         setTimeout(NETDATA.chartRefresher,
 
         NETDATA.parseDom(NETDATA.chartRefresher);
 
+        // Alarms initialization
+        if(netdataShowAlarms === true)
+            setTimeout(NETDATA.alarms.init, 1000);
+
         // Registry initialization
-        setTimeout(NETDATA.registry.init, 1000);
+        setTimeout(NETDATA.registry.init, 1500);
     };
 
     // ----------------------------------------------------------------------------------------------------------------
     };
 
 
+    // ----------------------------------------------------------------------------------------------------------------
+    // Registry of netdata hosts
+
+    NETDATA.alarms = {
+        current: null,
+        callback: null,
+
+        get: function(what, callback) {
+            $.ajax({
+                url: NETDATA.serverDefault + '/api/v1/alarms?' + what.toString(),
+                async: true,
+                cache: false,
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+                .done(function(data) {
+                    if(typeof callback === 'function')
+                        callback(data);
+                })
+                .fail(function() {
+                    NETDATA.error(415, host);
+
+                    if(typeof callback === 'function')
+                        callback(null);
+                });
+        },
+
+        update_forever: function() {
+            NETDATA.alarms.get('active', function(data) {
+                if(data !== null) {
+                    NETDATA.alarms.current = data;
+
+                    if (typeof NETDATA.alarms.callback === 'function') {
+                        NETDATA.alarms.callback(data);
+                    }
+                }
+
+                setTimeout(NETDATA.alarms.update_forever, 10000);
+            });
+        },
+
+        get_log: function(callback) {
+            $.ajax({
+                url: NETDATA.serverDefault + '/api/v1/alarm_log',
+                async: true,
+                cache: false,
+                xhrFields: { withCredentials: true } // required for the cookie
+            })
+                .done(function(data) {
+                    if(typeof callback === 'function')
+                        callback(data);
+                })
+                .fail(function() {
+                    NETDATA.error(416, host);
+
+                    if(typeof callback === 'function')
+                        callback(null);
+                });
+        },
+
+        init: function() {
+            NETDATA.alarms.update_forever();
+        }
+    };
+
     // ----------------------------------------------------------------------------------------------------------------
     // Registry of netdata hosts
 
index 942ea592120de0f4525e2117bf9fda32021a4d16..117ce54410edbb077572ad3cf837e109999979bc 100644 (file)
                     <span class="icon-bar"></span>
                     <span class="icon-bar"></span>
                 </button>
-                <a href="/" class="navbar-brand" id="hostname">netdata</a>
+                <a href="/" class="navbar-brand" id="hostname" title="reload the dashboard">netdata</a>
             </div>
             <nav class="collapse navbar-collapse navbar-right" role="navigation">
                 <ul class="nav navbar-nav">
-                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
-                    <li class="hidden-sm"><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
-                    <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fa fa-cloud-download"></i> update</a></li>
-                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
+                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal" title="alarms"><i class="fa fa-bell"></i><span id="alarms_count_badge" class="badge"></span></a></li>
+                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal" title="dashboard settings"><i class="fa fa-cog"></i></a></li>
+                    <li class="hidden-sm"><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank" title="netdata community"><i class="fa fa-github"></i></a></li>
+                    <li class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal" title="check for update"><i class="fa fa-cloud-download"></i><span id="update_badge" class="badge"></span></a></li>
+                    <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal" title="dashboard help"><i class="fa fa-question-circle"></i></a></li>
                     <li class="dropdown hidden-md hidden-lg hidden-xs">
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown" id="current_view">Menu <strong class="caret"></strong></a>
                         <ul class="dropdown-menu scrollable-menu inpagemenu" role="menu">
+                            <li><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fa fa-cog"></i> alarms</a></li>
                             <li><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fa fa-cog"></i> settings</a></li>
                             <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank"><i class="fa fa-github"></i> community</a></li>
                             <li><a href="#" class="btn" data-toggle="modal" data-target="#helpModal"><i class="fa fa-question-circle"></i> help</a></li>
         </div>
     </div>
 
+    <div class="modal fade" id="alarmsModal" tabindex="-1" role="dialog" aria-labelledby="alarmsModalLabel">
+        <div class="modal-dialog modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                    <h4 class="modal-title" id="alarmsModalLabel">netdata alarms</h4>
+                </div>
+                <div class="modal-body">
+                    <!-- Nav tabs -->
+                    <ul class="nav nav-tabs" role="tablist">
+                        <li role="presentation" class="active"><a href="#alarms_active" aria-controls="alarms_active" role="tab" data-toggle="tab">Active</a></li>
+                        <li role="presentation"><a href="#alarms_all" aria-controls="alarms_all" role="tab" data-toggle="tab">All</a></li>
+                        <li role="presentation"><a href="#alarms_log" aria-controls="alarms_log" role="tab" data-toggle="tab">Log</a></li>
+                    </ul>
+
+                    <!-- Tab panes -->
+                    <div class="tab-content">
+                        <div role="tabpanel" class="tab-pane active" id="alarms_active">
+                            loading...
+                        </div>
+                        <div role="tabpanel" class="tab-pane" id="alarms_all">
+                            loading...
+                        </div>
+                        <div role="tabpanel" class="tab-pane" id="alarms_log">
+                            loading...
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <!-- <a href="#" onclick="alarmsUpdateModal(); return false;" type="button" class="btn btn-default">Update</a> -->
+                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
     <div class="modal fade" id="optionsModal" tabindex="-1" role="dialog" aria-labelledby="optionsModalLabel">
         <div class="modal-dialog modal-lg" role="document">
             <div class="modal-content">
@@ -2363,6 +2401,92 @@ function renderChartsAndMenu(data) {
     renderPage(menus, data);
 }
 
+function alarmsUpdateModal() {
+    var active = '<h3>Raised Alarms</h3>';
+    var all = '<h3>All Running Alarms</h3>';
+    var log = '<h3>Alarm Log</h3>';
+    var footer = '<hr/>These are <a href="https://github.com/firehol/netdata/wiki/Generating-Badges" target="_blank">netdata badges</a>. You can copy and paste their URLs to embed them in any web page. Their color indicates the state of the alarm: <b>red</b> is critical, <b>orange</b> is warning, <b>bright green</b> is ok, <b>light grey</b> is undefined (i.e. no data or no status), <b>black</b> is not initialized.';
+
+    NETDATA.alarms.get('all', function(data) {
+        if(data === null) {
+            document.getElementById('alarms_active').innerHTML =
+                    document.getElementById('alarms_all').innerHTML =
+                            document.getElementById('alarms_log').innerHTML =
+                                    'failed to load alarm data!';
+            return;
+        }
+
+        var now = new Date().getTime();
+        var x;
+        var count_active = 0;
+        var count_all = 0;
+        var count_log = 0;
+        var families = {};
+        for(x in data.alarms) {
+            var alarm = data.alarms[x];
+
+            if(typeof families[alarm.family] === 'undefined')
+                families[alarm.family] = new Array();
+
+            families[alarm.family].push(alarm);
+        }
+
+        for(x in families) {
+            var active_family_added = false;
+            var all_family_added = false;
+
+            var arr = families[x];
+            var c = arr.length;
+            while(c--) {
+                var alarm = arr[c];
+                if(alarm.status === 'WARNING' || alarm.status === 'CRITICAL') {
+                    if(!active_family_added) {
+                        active_family_added = true;
+                        active += '<h4>' + x + '</h4>';
+                    }
+                    active += '<embed><img src="' + NETDATA.serverDefault + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto&_=' + now.toString()+ '"></img></embed><br/>';
+                    count_active++;
+                }
+
+                if(!all_family_added) {
+                    all_family_added = true;
+                    all += '<h4>' + x + '</h4>';
+                }
+                all += '<embed><img src="' + NETDATA.serverDefault + '/api/v1/badge.svg?chart=' + alarm.chart + '&alarm=' + alarm.name + '&refresh=auto&_=' + now.toString()+ '"></img></embed><br/>';
+                count_all++;
+            }
+        }
+
+        if(!count_active)
+            active += "<h4>Everything is normal. No raised alarms.</h4>";
+        else
+            active += footer;
+
+        if(!count_all)
+            all += "<h4>No alarms are running in this system.</h4>";
+        else
+            all += footer;
+
+        if(!count_log)
+            log += "<h4>No alarms have been logged in this system.</h4>";
+
+        document.getElementById('alarms_active').innerHTML = active;
+        document.getElementById('alarms_all').innerHTML = all;
+        document.getElementById('alarms_log').innerHTML = log;
+    });
+}
+
+function alarmsCallback(data) {
+    var count = 0;
+    for(x in data.alarms)
+        count++;
+
+    if(count > 0)
+        document.getElementById('alarms_count_badge').innerHTML = count.toString();
+    else
+        document.getElementById('alarms_count_badge').innerHTML = '';
+}
+
 function downloadAllCharts(netdata_url) {
     if(typeof netdata_url === 'undefined' || netdata_url === null)
         netdata_url = NETDATA.serverDefault;
@@ -2371,6 +2495,8 @@ function downloadAllCharts(netdata_url) {
         $("#loadOverlay").css("display","none");
         $("#loadOverlay").css("display","none");
 
+        NETDATA.alarms.callback = alarmsCallback;
+
         // download all the charts the server knows
         NETDATA.chartRegistry.downloadAll(netdata_url, function(data) {
 
@@ -2453,7 +2579,6 @@ function checkForUpdate(callback) {
     return null;
 }
 
-var updateBlinkCounter = 0;
 function notifyForUpdate(force) {
     versionLog('<p>checking for updates...</p>');
 
@@ -2494,16 +2619,7 @@ function notifyForUpdate(force) {
 
             versionLog('<p><big><strong>New version of netdata available!</strong></big></p><p>Latest version: ' + sha2.toString() + '</p><p><a href="' + compare + '" target="_blank">Click here for the changes log</a> since your installed version, and<br/><a href="https://github.com/firehol/netdata/wiki/Updating-Netdata" target="_blank">click here for directions on updating</a> your netdata installation.</p><p>We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.<br/>Keeping your netdata updated, is generally a good idea.</p>');
 
-            function updateButtonBlink() {
-                updateBlinkCounter--;
-                if(updateBlinkCounter > 0)
-                    $('#updateButton').fadeOut(500).fadeIn(500, updateButtonBlink);
-            }
-
-            if(updateBlinkCounter === 0) {
-                updateBlinkCounter = 300;
-                updateButtonBlink();
-            }
+            document.getElementById('update_badge').innerHTML = '!';
         }
 
         if(save)
@@ -2772,6 +2888,17 @@ function finalizePage() {
         notifyForUpdate(true);
     });
 
+    $('#alarmsModal').on('shown.bs.modal', function() {
+        alarmsUpdateModal();
+    });
+
+    $('#alarmsModal').on('hidden.bs.modal', function() {
+        document.getElementById('alarms_active').innerHTML =
+                document.getElementById('alarms_all').innerHTML =
+                document.getElementById('alarms_log').innerHTML =
+                        'loading...';
+    });
+
     $('#deleteRegistryModal').on('hidden.bs.modal', function() {
         deleteRegistryGuid = null;
     });