summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorThomas Anderson <tnyeanderson@users.noreply.github.com>2019-09-04 12:48:01 -0400
committerChris Akritidis <43294513+cakrit@users.noreply.github.com>2019-09-04 18:48:01 +0200
commit1eecab0ed87994cf64e230ad6ff53b8a5c26ffa2 (patch)
tree5438bbd9a2aa258d269ce2b830e6b329babea496 /web
parent6b0a4713cdfd040868a46955044c71fefcc72e8e (diff)
dash.html (#6603)
Multi-Host custom dashboard: dash.html is a single-file solution to show graphs, charts, and alarms from all streamed hosts on one page. It takes one defined set of graphs and charts and replicates it for each host. It is quite simple, but quite effective.
Diffstat (limited to 'web')
-rw-r--r--web/gui/Makefile.am1
-rw-r--r--web/gui/custom/README.md58
-rw-r--r--web/gui/dash-example.html1015
3 files changed, 1072 insertions, 2 deletions
diff --git a/web/gui/Makefile.am b/web/gui/Makefile.am
index ef8aa05fd6..84ef6a92b5 100644
--- a/web/gui/Makefile.am
+++ b/web/gui/Makefile.am
@@ -67,6 +67,7 @@ dist_web_DATA = \
refresh-badges.js \
sitemap.xml \
tv.html \
+ dash-example.html \
version.txt \
$(NULL)
diff --git a/web/gui/custom/README.md b/web/gui/custom/README.md
index 68d8bbb56a..69649a3432 100644
--- a/web/gui/custom/README.md
+++ b/web/gui/custom/README.md
@@ -11,12 +11,13 @@ Netdata charts can also be added to existing web pages.
Check this **[very simple working example of a custom dashboard](http://netdata.firehol.org/demo.html)**, and its **[html source](../demo.html)**.
-You should also look at the **[custom dashboard template](https://my-netdata.io/dashboard.html)**, that contains samples of all supported charts. The code is [here](../dashboard.html).
+You should also look at the **[custom dashboard template](https://my-netdata.io/dashboard.html)**, that contains samples of all supported charts. The code is [here](../dashboard.html).
If you plan to put the dashboard on TV, check **[tv.html](../tv.html)**. This is a screenshot of it, monitoring 2 servers on the same page:
![image](https://cloud.githubusercontent.com/assets/2662304/14252187/d8d5f78e-fa8e-11e5-990d-99821d38c874.png)
---
+
+--
## Web directory
@@ -51,6 +52,59 @@ If you need to create a new dashboard on an empty page, we suggest the following
</html>
```
+## Dash (Multi-Host Dashboard)
+
+`dash-example.html` is an all-in-one page that automatically fetches graphs from all your hosts. Just add your graphs and charts (or use the defaults) one time using the `dash-*` syntax and it will be automatically replicated for all of your hosts; showing alarms and graphs for all your hosts on **one page!**
+
+### Configure Dash
+
+First, rename the file so it doesn't get overwritten. For instance, with a webroot at `/usr/share/netdata/web`:
+```
+cp /usr/share/netdata/web/dash-example.html /usr/share/netdata/web/dash.html
+```
+
+Change the following line in `dash.html` to reflect your URLs. The second URL is used if you access your netdata dashboard from a reverse proxy. The reverse proxy URL is optional, if it is not set then both will use the netdata host URL.
+
+```js
+/*
+* TUTORIAL: Change this to the URL of your netdata host
+* If you use netdata behind a reverse proxy, add a second parameter for the reverse proxy url like so:
+* new Dash('http://localhost:19999', 'https://my-domain.com/stats');
+*/
+var dash = new Dash('http://localhost:19999');
+```
+
+If you want to change the graphs or styling to fit your needs, just add an element to the page as shown. child divs will be generated to create your graph/chart:
+```
+<div class="dash-graph" <---- Use class dash-graph for line graphs, etc
+ data-dash-netdata="system.cpu" <---- REQUIRED: Use data-dash-netdata to set the data source
+ data-dygraph-valuerange="[0, 100]"> <---- OPTIONAL: This overrides the default config. Any other data-* attributes will
+</div> be added to the generated div, so you can set any desired options here
+
+<div class="dash-chart" <---- Use class dash-chart for pie charts, etc. CHARTS ARE SQUARE
+ data-dash-netdata="system.io" <---- REQUIRED: Use data-dash-netdata to set the data source
+ data-dimensions="in" <---- Use this to override or append default options
+ data-title="Disk Read" <---- Use this to override or append default options
+ data-common-units="dash.io"> <---- Use this to override or append default options
+</div>
+```
+
+To change the sizes of graphs and charts, find the `Dash.options` object in `dash.html` and set your preferences:
+```js
+/*
+* TUTORIAL: Change your graph/chart dimensions here. Host columns will automatically adjust.
+* Charts are square! Their width is the same as their height.
+*/
+this.options = {
+ graph_width: '40em',
+ graph_height: '20em',
+ chart_width: '10em' // Charts are square
+};
+```
+
+To change the display order of your hosts, which is saved in localStorage, click the settings gear in the lower right corner
+
+
## dashboard.js
To add Netdata charts to any web page (dedicated to Netdata or not), you need to include the `/dashboard.js` file of a Netdata server.
diff --git a/web/gui/dash-example.html b/web/gui/dash-example.html
new file mode 100644
index 0000000000..55cd6400c3
--- /dev/null
+++ b/web/gui/dash-example.html
@@ -0,0 +1,1015 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
+</head>
+<body>
+ <div id="alarms" class="collapsed">
+ <div class="alarm-collapse-button" onclick="dash.toggle_alarm_collapse()">&rsaquo;</div>
+ <span class="alarm-count"></span>
+ <h1>Alarms</h1>
+ <div class="alarm-host-list"></div>
+ <div class="settings-button" onclick="dash.reorder_hosts()">&#9881;</div>
+ </div>
+ <div id="dash">
+ <div class="netdata-host-stats-container template">
+ <div class="drag-anchor"></div>
+ <div class="netdata-host-name">host</div>
+ <div class="netdata-host-stats">
+ <div class="dash-graph"
+ data-dash-netdata="system.cpu"
+ data-dygraph-valuerange="[0, 100]">
+ </div>
+ <div class="dash-graph"
+ data-dash-netdata="system.load">
+ </div>
+ <div class="dash-graph"
+ data-dash-netdata="system.ram">
+ </div>
+ <div class="dash-graph"
+ data-dash-netdata="system.net">
+ </div>
+ <div class="dash-graph"
+ data-dash-netdata="system.processes">
+ </div>
+ <div class="dash-graph"
+ data-dash-netdata="apps.cpu">
+ </div>
+ <div class="dash-graph"
+ data-dash-netdata="apps.mem">
+ </div>
+ <div class="dash-charts">
+ <div class="dash-chart"
+ data-dash-netdata="system.io"
+ data-dimensions="in"
+ data-title="Disk Read"
+ data-common-units="dash.io">
+ </div>
+ <div class="dash-chart"
+ data-dash-netdata="system.io"
+ data-dimensions="out"
+ data-title="Disk Write"
+ data-common-units="dash.io">
+ </div>
+ <div class="dash-chart"
+ data-dash-netdata="system.cpu"
+ data-chart-library="gauge"
+ data-title="CPU"
+ data-units="%"
+ data-gauge-max-value="100"
+ data-colors="#22AA99"
+ data-gauge-stroke-color="#373B40">
+ </div>
+ <div class="dash-chart"
+ data-dash-netdata="system.net"
+ data-dimensions="received"
+ data-title="Net Inbound"
+ data-common-units="dash.net">
+ </div>
+ <div class="dash-chart"
+ data-dash-netdata="system.net"
+ data-dimensions="sent"
+ data-title="Net Outbound"
+ data-common-units="dash.net">
+ </div>
+ <div class="dash-chart"
+ data-dash-netdata="system.ram"
+ data-dimensions="used|buffers|active|wired"
+ data-append-options="percentage"
+ data-title="Used RAM"
+ data-units="%"
+ data-easypiechart-max-value="100"
+ data-colors="#EE9911">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</body>
+<script
+ src="https://code.jquery.com/jquery-3.4.1.min.js"
+ integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
+ crossorigin="anonymous">
+</script>
+<script type="text/javascript">
+class PickNSort {
+ // PickNSort.js
+
+ constructor (dark) {
+ this.items = [];
+ this.callback = function (output, disabled) { console.log(output, disabled) };
+ this.reset = function () { this.items = [] };
+ this.last_output = {
+ enabled: [],
+ disabled: []
+ }
+
+ this.add_css_to_page(dark);
+ }
+
+ create_modal () {
+ $("<div></div>", {
+ id: "picknsort-container"
+ }).appendTo('body');
+
+ $("<div></div>", {
+ id: 'picknsort-window'
+ }).appendTo('#picknsort-container');
+
+ $("<div></div>", {
+ id: "picknsort-close-button",
+ text: '\u2573'
+ }).appendTo('#picknsort-window');
+
+ $("<div></div>", {
+ id: "picknsort-item-list"
+ }).appendTo('#picknsort-window')
+
+ $("<div></div>", {
+ text: "Apply",
+ id: "picknsort-apply-button"
+ }).appendTo('#picknsort-window');
+
+ $("<div></div>", {
+ text: "Reset",
+ id: "picknsort-reset-button"
+ }).appendTo('#picknsort-window');
+
+ $('#picknsort-close-button').click(function () {
+ picknsort.destroy_modal();
+ picknsort.callback(null, null);
+ });
+
+ $('#picknsort-apply-button').click(function () {
+ picknsort.apply();
+ });
+
+ $('#picknsort-reset-button').click(function () {
+ picknsort.reset();
+ });
+ }
+
+ destroy_modal () {
+ $('#picknsort-container').remove();
+ }
+
+ populate_list () {
+ this.clear_list();
+
+ for (var i=0, len=this.items.length; i<len; i++) {
+ this.draw_item(i);
+ }
+ }
+
+ draw_item (index) {
+ var item = this.items[index];
+
+ var $item = $("<div></div>").addClass("picknsort-item");
+
+ var $checkbox= $("<div></div>").addClass('picknsort-item-checkbox-wrapper');
+
+ $("<input>", {
+ type: "checkbox",
+ checked: item.enabled
+ }).addClass('picknsort-item-checkbox')
+ .appendTo($checkbox);
+
+ var $value = $("<div></div>", {
+ text: item.value
+ }).addClass("picknsort-item-value");
+
+ var $nav = $("<div></div>").addClass("picknsort-item-nav")
+ .append("<div class='picknsort-item-down' data-index='" + index + "' onclick='picknsort.shift_down(this)'>&#8595;</div>")
+ .append("<div class='picknsort-item-up' data-index='" + index + "' onclick='picknsort.shift_up(this)'>&#8593;</div>");
+
+ $item.append($checkbox).append($value).append($nav).appendTo('#picknsort-item-list');
+ }
+
+ clear_list () {
+ $('#picknsort-item-list').html('');
+ }
+
+ popup (callback, reset, options={}) {
+ this.callback = callback || this.callback;
+ this.reset = reset || this.reset;
+ if (!options.enabled) {
+ options.enabled = this.last_output.enabled;
+ }
+ if (!options.disabled) {
+ options.disabled = this.last_output.disabled;
+ }
+
+ this.parse_items(options.enabled, options.disabled);
+ if (this.items.length) {
+ this.create_modal();
+ this.populate_list();
+ }
+
+ }
+
+ parse_items (enabled, disabled) {
+ this.items = [];
+
+ for (var i=0, len=enabled.length; i<len; i++) {
+ this.items.push({
+ value: enabled[i],
+ enabled: true
+ });
+ }
+
+ for (var i=0, len=disabled.length; i<len; i++) {
+ this.items.push({
+ value: disabled[i],
+ enabled: false
+ });
+ }
+ }
+
+ shift_down (el) {
+ var index = $(el).data('index');
+ if (index === this.items.length - 1) {
+ return;
+ }
+ var temp = this.items[index];
+ this.items[index] = this.items[index + 1];
+ this.items[index + 1] = temp;
+ this.move_element_down(index);
+ }
+
+ shift_up (el) {
+ var index = $(el).data('index');
+ if (index === 0) {
+ return;
+ }
+ var temp = this.items[index];
+ this.items[index] = this.items[index - 1];
+ this.items[index - 1] = temp;
+ this.move_element_up(index);
+ }
+
+ move_element_up (index) {
+ var $src = $('.picknsort-item:eq(' + index + ')');
+ var $dest = $('.picknsort-item:eq(' + (index-1) + ')');
+ $src.insertBefore($dest);
+
+ // Update data-index
+ $src.find('.picknsort-item-down, .picknsort-item-up').data("index", index-1);
+ $dest.find('.picknsort-item-down, .picknsort-item-up').data("index", index);
+ }
+
+ move_element_down (index) {
+ var $src = $('.picknsort-item:eq(' + index + ')');
+ var $dest = $('.picknsort-item:eq(' + (index+1) + ')');
+ $src.insertAfter($dest);
+
+ // Update data-index
+ $src.find('.picknsort-item-down, .picknsort-item-up').data("index", index+1);
+ $dest.find('.picknsort-item-down, .picknsort-item-up').data("index", index);
+ }
+
+ apply () {
+ var out = [];
+ for (var i=0, len=this.items.length; i<len; i++) {
+ out.push(this.items[i].value);
+ }
+ var disabled = [];
+ $('input.picknsort-item-checkbox:not(:checked)').each(function (elindex) {
+ var itemindex = $('input.picknsort-item-checkbox').index($(this));
+ // Adjust for deleted elements
+ var spliceindex = itemindex - (1 * elindex);
+ var del = out.splice(spliceindex, 1);
+ disabled.push(del[0]);
+ });
+ this.callback(out, disabled);
+ this.last_output = {
+ enabled: out,
+ disabled: disabled
+ }
+ this.destroy_modal();
+ }
+
+ add_css_to_page (dark) {
+ var light_theme = {
+ windowbg: "#FFF",
+ itembg: "#F4F4F4",
+ itemcolor: "#535353"
+ }
+ var dark_theme = {
+ windowbg: "#444",
+ itembg: "#333",
+ itemcolor: "#DBDBDB"
+ }
+ var current_theme = (dark) ? dark_theme : light_theme;
+ $("<style>")
+ .prop("type", "text/css")
+ .html(`
+#picknsort-container {
+ position: fixed;
+ z-index: 9999 !important;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: rgba(0,0,0,0.5);
+}
+#picknsort-window {
+ max-height: 90vh;
+ width: 40em;
+ background: ${current_theme.windowbg};
+ position: absolute;
+ top: 5em;
+ left: calc(50% - 20em);
+ padding: 2em;
+ padding-top: 4em;
+}
+#picknsort-close-button {
+ position: absolute;
+ top: 1em;
+ font-size: 1.5em;
+ color: #666;
+ right: 1em;
+ cursor: pointer;
+}
+#picknsort-item-list {
+ max-height: 60vh;
+ overflow-y: scroll;
+ margin-bottom: 1em;
+}
+#picknsort-apply-button, #picknsort-reset-button {
+ text-align: center;
+ font-size: 1.5em;
+ padding: 1em;
+ background: rgb(106, 232, 165);
+ font-weight: bold;
+ color: #FFF;
+ cursor: pointer;
+ margin-top: 1em;
+}
+#picknsort-reset-button {
+ background: #CCC;
+}
+.picknsort-item {
+ padding: 1em;
+ border-top: solid 1px #CCC;
+ margin-bottom: 1em;
+ position: relative;
+ background: ${current_theme.itembg};
+ color: ${current_theme.itemcolor}
+}
+.picknsort-item-checkbox-wrapper {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 3em;
+ text-align: center;
+}
+.picknsort-item-checkbox {
+ transform: scale(1.5);
+ margin-top: 1.3em !important;
+}
+.picknsort-item-value {
+ margin-left: 3em;
+}
+.picknsort-item-nav {
+ font-size: 2em;
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ color: #CCC;
+ cursor: pointer;
+}
+.picknsort-item-down, .picknsort-item-up {
+ display: inline-block;
+ padding: 0em 0.5em;
+ height: calc(100% - 0.2em);
+}
+ `).appendTo("head");
+ }
+}
+</script>
+<script type="text/javascript">
+ // Dash JS
+
+ function picknsort_setup () {
+ if (localStorage.getItem('netdata_ordered_hosts')) {
+ picknsort.last_output = JSON.parse(localStorage.getItem('netdata_ordered_hosts'));
+ } else {
+ picknsort.last_output = {
+ enabled: dash.netdata_info.mirrored_hosts,
+ disabled: []
+ }
+ }
+ }
+
+ class Dash {
+ constructor (base_url, link_base_url) {
+ this.base_url = base_url; // URL of netdata host, with port
+ this.link_base_url = link_base_url || base_url; // Reverse proxy URL (Optional)
+ this.link_base_url;
+ this.netdata_info;
+ this.current_alarms = {};
+ this.new_alarms = {};
+ this.first_build = true;
+ /*
+ * TUTORIAL: Change your graph/chart dimensions here. Host columns will automatically adjust.
+ * Charts are square! Their width is the same as their height.
+ */
+ this.options = {
+ graph_width: '40em',
+ graph_height: '20em',
+ chart_width: '10em' // Charts are square
+ };
+
+ this.init();
+ }
+
+ reorder_hosts () {
+ picknsort.popup(dash.update_ordered_hosts, dash.find_new_hosts);
+ }
+
+ get_enabled_hosts () {
+ try {
+ return JSON.parse(localStorage.getItem('netdata_ordered_hosts')).enabled
+ } catch (e) {
+ return this.netdata_info.mirrored_hosts;
+ }
+ }
+
+ get_ordered_hosts () {
+ try {
+ return JSON.parse(localStorage.getItem('netdata_ordered_hosts'));
+ } catch (e) {
+ return null;
+ }
+ }
+
+ set_ordered_hosts (hosts) {
+ localStorage.setItem('netdata_ordered_hosts', JSON.stringify(hosts));
+ location.reload();
+ }
+
+ update_ordered_hosts (enabled, disabled) {
+ if (enabled === null && disabled === null) {
+ return;
+ }
+ dash.set_ordered_hosts({
+ enabled: enabled,
+ disabled: disabled
+ });
+ }
+
+ find_new_hosts () {
+ var newhosts = dash.netdata_info.mirrored_hosts.slice();
+ var currenthosts = dash.get_ordered_hosts();
+
+ for (var i=0,len=currenthosts.enabled.length; i<len; i++) {
+ var found = newhosts.indexOf(currenthosts.enabled[i]);
+
+ if (found > -1) {
+ newhosts.splice(found, 1);
+ } else {
+ currenthosts.enabled.splice(i, 1);
+ }
+ }
+
+ for (var i=0,len=currenthosts.disabled.length; i<len; i++) {
+ var found = newhosts.indexOf(currenthosts.disabled[i]);
+
+ if (found > -1) {
+ newhosts.splice(found, 1);
+ } else {
+ currenthosts.enabled.splice(i, 1);
+ }
+ }
+
+ for (var i=0,len=newhosts.length; i<len; i++) {
+ currenthosts.enabled.push(newhosts[i]);
+ }
+
+ dash.set_ordered_hosts(currenthosts);
+ }
+
+ get_host_url (hostname, link) {
+ var base = ( link ) ? this.link_base_url : this.base_url;
+ return (hostname) ? base + '/host/' + hostname : base;
+ }
+
+ get_api_url (hostname) {
+ return this.get_host_url(hostname) + '/api/v1'
+ }
+
+
+ init () {
+ var that = this;
+ $.get(this.get_api_url() + '/info', function (data) {
+ that.netdata_info = data;
+ picknsort_setup()
+ that.build();
+ });
+ }
+
+ digest () {
+ this.fetch_active_alarms();
+ this.fix_layout_errors();
+ }
+
+ build () {
+ // Fix vertically misaligned stats on load error
+ $('.dash-graph').css({
+ height: this.options.graph_height,
+ width: this.options.graph_width
+ });
+ $('.dash-chart').css({
+ height: this.options.chart_width, // Charts are square
+ width: this.options.chart_width
+ });
+
+ // Fix chart alignment
+ $('.netdata-host-stats-container').css({
+ width: 'calc(' + this.options.graph_width + ' + (2 * ' + $('.netdata-host-stats-container').css('padding-left') + '))'
+ });
+
+ var $template = $('.netdata-host-stats-container').first();
+
+ var hosts = this.get_enabled_hosts();
+
+ for (var i=0, len=hosts.length; i<len; i++) {
+ var hostname = hosts[i];
+ $('#alarms .alarm-host-list').append('<div class="host-alarms ' + hostname + '"><a target="_blank" href="' + this.get_host_url(hostname, true) + '/"><h2>' + hostname + '</h2></a></div>');
+
+ $template.clone().removeClass('template').appendTo('#dash');
+ var $newest = $('.netdata-host-stats-container').last();
+ this.build_stats($newest, hostname);
+ }
+
+
+ if (this.first_build === true) {
+ this.remove_template_elements();
+ this.first_build = false;
+ }
+ }
+
+ build_stats ($hoststats, hostname) {
+ var that = this;
+
+ $hoststats.find('.netdata-host-stats .dash-graph').each(function () {
+ that.build_graph($(this), hostname);
+ });
+
+ $hoststats.find('.netdata-host-stats .dash-chart').each(function () {
+ that.build_chart($(this), hostname);
+ });
+
+ $hoststats.find('.netdata-host-name').html('<a target="_blank" href="' + that.get_host_url(hostname, true) + '/">' + hostname + '</a>');
+ }
+
+ build_graph ($wrapper, hostname) {
+ var that = this;
+ var $graph = $('<div>', {
+ // Defaults
+ 'data-netdata': $wrapper.attr('data-dash-netdata'),
+ 'data-host': that.get_host_url(hostname),
+ 'data-before': '0',
+ 'data-after': '-540',
+ 'role': 'application',
+ 'data-width': that.options.graph_width,
+ 'data-height': that.options.graph_height
+ });
+
+ $.each($wrapper[0].attributes, function (key, node) {
+ if ( node.name.match(/^data-(?!dash).*/ ) ) {
+ $graph.attr(node.name, node.value);
+ }
+ });
+
+ $graph.appendTo($wrapper);
+ }
+
+ build_chart ($wrapper, hostname) {
+ var that = this;
+ var $graph = $('<div>', {
+ // Defaults
+ 'data-netdata': $wrapper.attr('data-dash-netdata'),
+ 'data-host': that.get_host_url(hostname),
+ 'data-before': '0',
+ 'data-after': '-540',
+ 'data-points': '540',
+ 'role': 'application',
+ 'data-width': that.options.chart_width,
+ 'data-title': ' ',
+ 'data-easypiechart-trackcolor': "#373B40",
+ 'data-chart-library': 'easypiechart'
+
+ })
+
+ $.each($wrapper[0].attributes, function (key, node) {
+ if ( node.name.match(/^data-(?!dash).*/ ) ) {
+ $graph.attr(node.name, node.value);
+ }
+ });
+
+ $graph.appendTo($wrapper);
+ }
+
+ all_alarms_are_warnings () {
+ var out = true;
+ $.each(this.current_alarms, function (host, alarms) {
+ if (out === false) {
+ return false; // Maximum efficiency
+ }
+ $.each(alarms, function (name, data) {
+ if (data.status != "WARNING") {
+ out = false;
+ return false;
+ }
+ });
+ });
+ return out;
+ }
+
+ update_alarm_count () {
+ console.log(this.all_alarms_are_warnings());
+ var count = $('img.alarm-badge').length;
+ $('.alarm-count').text($('img.alarm-badge').length);
+ $('.alarm-count').removeClass('no-alarms warnings-only');
+ if ( count === 0 ) {
+ $('.alarm-count').addClass('no-alarms');
+ return
+ }
+ if (this.all_alarms_are_warnings()) {
+ $('.alarm-count').addClass('warnings-only');
+ }
+ }
+
+ fetch_active_alarms () {
+ var that = this;
+ console.log("Getting alarms...");
+ var hosts = this.get_enabled_hosts();
+ for (var i=0, len=hosts.length; i<len; i++) {
+ var hostname = hosts[i];
+
+ $.get(this.get_api_url(hostname) + '/alarms', function (data) {
+ that.new_alarms[ data.hostname ] = data.alarms;
+ if ( Object.keys(that.new_alarms).length === len ) {
+ // All responses received
+ if ( that.check_new_current_alarms() ) {
+ console.log("No alarm changes detected... Refreshing images.");
+ that.new_alarms = {};
+ that.refresh_alarm_images();
+ return;
+ }
+
+ that.current_alarms = that.new_alarms;
+ that.new_alarms = {};
+ that.draw_alarms();
+ that.update_alarm_count();
+ }
+ });
+ }
+ }
+
+ toggle_alarm_collapse () {
+ $('#alarms').toggleClass('collapsed');
+ }
+
+ check_new_current_alarms () {
+ // Create arrays of property names
+ var a = this.current_alarms;
+ var b = this.new_alarms;
+ var aProps = Object.keys(a);
+ var bProps = Object.keys(b);
+
+ // If number of properties is different,
+ // objects are not equivalent
+ if (aProps.length != bProps.length) {
+ return false;
+ }
+
+ for (var i = 0; i < aProps.length; i++) {
+ var propName = aProps[i];
+ var aa = a[propName]
+ var aaProps = Object.keys(aa);
+ var bb = b[propName];
+ var bbProps = Object.keys(bb);
+
+ if (aaProps.length != bbProps.length) {
+ return false;
+ }
+
+ for (var n = 0, len=aaProps.length; i < len; i++) {
+ if ( bb[aaProps[n]] === undefined ) {
+ return false;
+ }
+ }
+ }
+
+ // If we made it this far, objects
+ // are considered equivalent
+ return true;
+ }
+
+ refresh_alarm_images () {
+ // Use Math.random() to "reload" the image
+ $('.alarm-badge').each(function () {
+ var old_src = $(this).attr('src').replace(/&rand=.*/g, "");
+ $(this).attr('src', old_src + "&rand=" + Math.random());
+ });
+ }
+
+ draw_alarms () {
+ var that = this;
+ console.log("Drawing...", that.current_alarms);
+ $.each(that.current_alarms, function(hostname, alarms) {
+ $('#alarms .host-alarms.' + hostname + ' .alarm-badge').remove();
+ $.each(alarms, function (index, alarm) {
+ that.draw_alarm(hostname, alarm);
+ })
+ });
+ }
+
+ draw_alarm (hostname, alarm) {
+ var queryStr = '/badge.svg?alarm=' + alarm.name + '&chart=' + alarm.chart
+ $('<img />', {
+ src: this.get_api_url(hostname) + queryStr,
+ class: 'alarm-badge'
+ }).appendTo($('#alarms .host-alarms.' + hostname));
+ }
+
+ remove_template_elements () {
+ $('.netdata-host-stats-container').first().remove();
+ }
+
+ fix_layout_errors () {
+ $('.dash-graph:contains("chart not found")').html('<div class="loading-error">Not found</div>');
+ $('.dash-chart:contains("chart not found")').html('<div class="loading-error">Not found</div>');
+ }
+ }
+
+ /*
+ * TUTORIAL: Change this to the URL of your netdata host
+ * If you use netdata behind a reverse proxy, add a second parameter for the reverse proxy url like so:
+ * new Dash('http://localhost:19999', 'https://my-domain.com/stats');
+ */
+ var dash = new Dash('http://localhost:19999');
+
+ var picknsort = new PickNSort(true);
+
+ // Import dashboard.js
+ $.getScript(dash.base_url + '/dashboard.js', function() {
+ console.log("Loaded dashboard.js");
+ setTimeout(function () {
+ $('#alarms').css("visibility", "visible");
+ }, 400);
+ });
+
+ setInterval(function () {
+ dash.digest();
+ }, 6 * 1000);
+
+</script>
+<style type="text/css">
+ body {
+ background-color: #272b30;
+ }
+
+ a, a:hover {
+ color: #AAA !important;
+ }
+
+ #dash {
+ overflow: scroll;
+ width: 100vw;
+ white-space: nowrap;
+ height: 100vh;
+
+ }
+
+ #alarms {
+ display: block;
+ z-index: 9999;
+ position: fixed;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: #111;
+ width: 23em;
+ padding: 1em;
+ color: #AAA;
+ box-shadow: 0 0 3em #060606;
+ border: solid 1px #2d2d2d;
+ visibility: hidden;
+ }
+
+
+
+ #alarms h1 {
+ text-align: center;
+ margin: 0;
+ margin-bottom: 0.5em;
+ margin-top: -0.1em;
+ font-size: 2em;
+ }
+
+ #alarms h2 {
+ font-size: 1.5em;
+ }
+
+ #alarms .alarm-collapse-button {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 0.9em;
+ height: 0.9em;
+ font-size: 4em;
+ line-height: 0.8;
+ background: #111;
+ text-align: center;
+ cursor: default;
+ }
+
+ #alarms .alarm-collapse-button:hover {
+ background: #000;
+ }
+
+ #alarms.collapsed {
+ width: 4em;
+ }
+
+ #alarms.collapsed .alarm-collapse-button {
+ position: fixed;
+ left: auto;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 4em;
+ height: 100vh;
+ opacity: 0;
+ z-index: 9999;
+ }
+
+ #alarms.collapsed h1 {
+ transform: rotate(-90deg);
+ position: absolute;
+ left: -9.1em;
+ top: 4em;
+ width: 20em;
+ }
+
+ #alarms .alarm-host-list {
+ margin-top: 1em;
+ height: 100%;
+ overflow-y: scroll;
+ scrollbar-width: thin;
+ }
+
+ #alarms.collapsed .alarm-host-list {
+ display: none;
+ }
+
+
+
+ #alarms .host-alarms {
+ background: #1d1d1d;
+ margin-bottom: 1em;
+ padding: 0.2em 1em;
+ padding-bottom: 1.5em;
+ border-top: 1px solid;
+ position: relative;
+ }
+
+ .host-alarms a:last-child:after {
+ position: absolute;
+ display: block;
+ content: 'no alarms';
+ width: 6em;
+ height: 1em;
+ right: 0;
+ top: 2em;
+ color: #646464;
+ }
+
+ #alarms .host-alarms img.alarm-badge {
+ margin: .25em;
+ height: 1.2em;
+ }
+
+ #alarms .alarm-count {
+ width: 1.5em;
+ height: 1.5em;
+ text-align: center;
+ position: absolute;
+ right: 0.5em;
+ top: 0.5em;
+ font-size: 1.5em;
+ background: #6f1515;
+ border-radius: 50%;
+ }
+
+ #alarms .alarm-count:empty {
+ background: #333;
+ }
+
+ #alarms .no-alarms {
+ background: #156f15;
+ color: rgba(0,0,0,0);
+ }
+
+ #alarms .warnings-only {
+ background: #f48041;
+ color: #EEE;
+ }
+
+ .settings-button {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ font-size: 3em;
+ width: 1.3em;
+ height: 1.3em;
+ z-index: 9999;
+ text-align: center;
+ line-height: 1.2;
+ cursor: default;
+ }
+
+ .settings-button:hover {
+ background: #424242;
+ }
+
+ .drag-anchor {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 2em;
+ background: #111;
+
+ }
+
+ .netdata-host-name {
+ font-size: 3em;
+ color: #FFF;
+ text-align: center;
+ white-space: nowrap;
+ }
+
+ .netdata-host-stats-container {
+ position: relative;
+ margin: 0 1em;
+ padding: 3em 2em;
+ display: inline-block;
+ text-align: center;
+ color: #CCC;
+ background: #232323;
+ }
+
+ .netdata-host-stats-container:last-of-type {
+ margin-right: 27em;
+ }
+
+ .netdata-host-stats {
+
+ }
+
+ .netdata-message {
+ }
+
+ .dash-graph {
+ margin-bottom: 1em;
+ }
+
+ .netdata-legend-resize-handler {
+ display: none
+ }
+
+ .dash-charts {
+ white-space: normal;
+ margin: 2em 0;
+ }
+
+ .dash-chart {
+ display: inline-block;
+ vertical-align: top;
+ }