@cryptotaxi247 / netdata-1 / commits / 8bd1e8d54

fully functional web snapshots

Costa Tsaousis (ktsaou) committed Nov 12, 2017 at 20:45 UTC 8bd1e8d54a2e0f73ff3eaa13227d4a9752aac69d
2 files changed +353 -182
web/dashboard.js
+168 -57
@@ -202,6 +202,9 @@ var NETDATA = window.NETDATA || {};
202 // (blue) (red) (orange) (green) (pink) (brown) (purple) (yellow) (gray)
203 //NETDATA.colors = [ '#5DA5DA', '#F15854', '#FAA43A', '#60BD68', '#F17CB0', '#B2912F', '#B276B2', '#DECF3F', '#4D4D4D' ];
204
205 + if(typeof netdataSnapshotData === 'undefined')
206 + netdataSnapshotData = null;
207 +
208 if(typeof netdataShowHelp === 'undefined')
209 netdataShowHelp = true;
210
@@ -999,6 +1002,11 @@ var NETDATA = window.NETDATA || {};
1002 keys: {},
1003 latest: {},
1004
1005 + globalReset: function() {
1006 + this.keys = {};
1007 + this.latest = {};
1008 + },
1009 +
1010 get: function(state) {
1011 if(typeof state.tmp.__commonMin === 'undefined') {
1012 // get the commonMin setting
@@ -1053,6 +1061,11 @@ var NETDATA = window.NETDATA || {};
1061 keys: {},
1062 latest: {},
1063
1064 + globalReset: function() {
1065 + this.keys = {};
1066 + this.latest = {};
1067 + },
1068 +
1069 get: function(state) {
1070 if(typeof state.tmp.__commonMax === 'undefined') {
1071 // get the commonMax setting
@@ -1123,6 +1136,10 @@ var NETDATA = window.NETDATA || {};
1136 NETDATA.chartRegistry = {
1137 charts: {},
1138
1139 + globalReset: function() {
1140 + this.charts = {};
1141 + },
1142 +
1143 fixid: function(id) {
1144 return id.replace(/:/g, "_").replace(/\//g, "_");
1145 },
@@ -1157,13 +1174,7 @@ var NETDATA = window.NETDATA || {};
1174
1175 var self = this;
1176
1160 - $.ajax({
1161 - url: host + '/api/v1/charts',
1162 - async: true,
1163 - cache: false,
1164 - xhrFields: { withCredentials: true } // required for the cookie
1165 - })
1166 - .done(function(data) {
1177 + function fix_data(data, callback) {
1178 if(data !== null) {
1179 var h = NETDATA.chartRegistry.fixid(host);
1180 self.charts[h] = data.charts;
@@ -1175,14 +1186,29 @@ var NETDATA = window.NETDATA || {};
1186 else NETDATA.error(406, host + '/api/v1/charts');
1187
1188 if(typeof callback === 'function')
1178 - return callback(data);
1179 - })
1180 - .fail(function() {
1181 - NETDATA.error(405, host + '/api/v1/charts');
1189 + callback(data);
1190 + }
1191
1183 - if(typeof callback === 'function')
1184 - return callback(null);
1185 - });
1192 + if(netdataSnapshotData !== null) {
1193 + fix_data(netdataSnapshotData.charts, callback);
1194 + }
1195 + else {
1196 + $.ajax({
1197 + url: host + '/api/v1/charts',
1198 + async: true,
1199 + cache: false,
1200 + xhrFields: {withCredentials: true} // required for the cookie
1201 + })
1202 + .done(function (data) {
1203 + fix_data(data, callback);
1204 + })
1205 + .fail(function () {
1206 + NETDATA.error(405, host + '/api/v1/charts');
1207 +
1208 + if (typeof callback === 'function')
1209 + callback(null);
1210 + });
1211 + }
1212 }
1213 };
1214
@@ -1209,6 +1235,15 @@ var NETDATA = window.NETDATA || {};
1235
1236 callback: null,
1237
1238 + globalReset: function() {
1239 + this.clearMaster();
1240 + this.seq = 0;
1241 + this.master = null;
1242 + this.force_after_ms = null;
1243 + this.force_before_ms = null;
1244 + this.callback = null;
1245 + },
1246 +
1247 delay: function() {
1248 if(NETDATA.options.debug.globalPanAndZoom === true)
1249 console.log('globalPanAndZoom.delay()');
@@ -1619,6 +1654,11 @@ var NETDATA = window.NETDATA || {};
1654 keys: {}, // keys for data-common-units
1655 latest: {}, // latest selected units for data-common-units
1656
1657 + globalReset: function() {
1658 + this.keys = {};
1659 + this.latest = {};
1660 + },
1661 +
1662 scalableUnits: {
1663 'kilobits/s': {
1664 'bits/s': 1 / 1000,
@@ -1910,6 +1950,15 @@ var NETDATA = window.NETDATA || {};
1950 slaves: [],
1951 timeout_id: null,
1952
1953 + globalReset: function() {
1954 + this.stop();
1955 + this.state = null;
1956 + this.dont_sync_before = 0;
1957 + this.last_t = 0;
1958 + this.slaves = [];
1959 + this.timeout_id = null;
1960 + },
1961 +
1962 active: function() {
1963 return (this.state !== null);
1964 },
@@ -3908,37 +3957,62 @@ var NETDATA = window.NETDATA || {};
3957 return ret;
3958 };
3959
3911 - this.chartURL = function() {
3912 - var after, before, points_multiplier = 1;
3913 - if(NETDATA.globalPanAndZoom.isActive() && NETDATA.globalPanAndZoom.isMaster(this) === false) {
3914 - this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
3960 + this.chartDataUniqueID = function() {
3961 + return this.id + ',' + this.library_name + ',' + this.dimensions + ',' + this.chartURLOptions();
3962 + };
3963
3916 - after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
3917 - before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
3918 - this.view_after = after * 1000;
3919 - this.view_before = before * 1000;
3964 + this.chartURLOptions = function() {
3965 + var ret = '';
3966
3921 - this.requested_padding = null;
3922 - points_multiplier = 1;
3923 - }
3924 - else if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
3925 - this.tm.pan_and_zoom_seq = 0;
3967 + if(this.override_options !== null)
3968 + ret = this.override_options.toString();
3969 + else
3970 + ret = this.library.options(this);
3971
3927 - before = Math.round(this.current.force_before_ms / 1000);
3928 - after = Math.round(this.current.force_after_ms / 1000);
3929 - this.view_after = after * 1000;
3930 - this.view_before = before * 1000;
3972 + if(this.append_options !== null)
3973 + ret += '|' + this.append_options.toString();
3974
3932 - if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
3933 - this.requested_padding = Math.round((before - after) / 2);
3934 - after -= this.requested_padding;
3935 - before += this.requested_padding;
3936 - this.requested_padding *= 1000;
3937 - points_multiplier = 2;
3975 + ret += '|jsonwrap';
3976 +
3977 + if(NETDATA.options.current.eliminate_zero_dimensions === true)
3978 + ret += '|nonzero';
3979 +
3980 + return ret;
3981 + };
3982 +
3983 + this.chartURL = function() {
3984 + var after, before, points_multiplier = 1;
3985 + if(NETDATA.globalPanAndZoom.isActive()) {
3986 + if(this.current.force_before_ms !== null && this.current.force_after_ms !== null) {
3987 + this.tm.pan_and_zoom_seq = 0;
3988 +
3989 + before = Math.round(this.current.force_before_ms / 1000);
3990 + after = Math.round(this.current.force_after_ms / 1000);
3991 + this.view_after = after * 1000;
3992 + this.view_before = before * 1000;
3993 +
3994 + if(NETDATA.options.current.pan_and_zoom_data_padding === true) {
3995 + this.requested_padding = Math.round((before - after) / 2);
3996 + after -= this.requested_padding;
3997 + before += this.requested_padding;
3998 + this.requested_padding *= 1000;
3999 + points_multiplier = 2;
4000 + }
4001 +
4002 + this.current.force_before_ms = null;
4003 + this.current.force_after_ms = null;
4004 }
4005 + else {
4006 + this.tm.pan_and_zoom_seq = NETDATA.globalPanAndZoom.seq;
4007 +
4008 + after = Math.round(NETDATA.globalPanAndZoom.force_after_ms / 1000);
4009 + before = Math.round(NETDATA.globalPanAndZoom.force_before_ms / 1000);
4010 + this.view_after = after * 1000;
4011 + this.view_before = before * 1000;
4012
3940 - this.current.force_before_ms = null;
3941 - this.current.force_after_ms = null;
4013 + this.requested_padding = null;
4014 + points_multiplier = 1;
4015 + }
4016 }
4017 else {
4018 this.tm.pan_and_zoom_seq = 0;
@@ -3970,19 +4044,7 @@ var NETDATA = window.NETDATA || {};
4044 this.data_url += "&format=" + this.library.format();
4045 this.data_url += "&points=" + (data_points).toString();
4046 this.data_url += "&group=" + this.method;
3973 -
3974 - if(this.override_options !== null)
3975 - this.data_url += "&options=" + this.override_options.toString();
3976 - else
3977 - this.data_url += "&options=" + this.library.options(this);
3978 -
3979 - this.data_url += '|jsonwrap';
3980 -
3981 - if(NETDATA.options.current.eliminate_zero_dimensions === true)
3982 - this.data_url += '|nonzero';
3983 -
3984 - if(this.append_options !== null)
3985 - this.data_url += '|' + this.append_options.toString();
4047 + this.data_url += "&options=" + this.chartURLOptions();
4048
4049 if(after)
4050 this.data_url += "&after=" + after.toString();
@@ -4182,18 +4244,41 @@ var NETDATA = window.NETDATA || {};
4244 this.clearSelection();
4245 this.chartURL();
4246
4185 - if(this.debug === true)
4186 - this.log('updating from ' + this.data_url);
4187 -
4247 NETDATA.statistics.refreshes_total++;
4248 NETDATA.statistics.refreshes_active++;
4249
4250 if(NETDATA.statistics.refreshes_active > NETDATA.statistics.refreshes_active_max)
4251 NETDATA.statistics.refreshes_active_max = NETDATA.statistics.refreshes_active;
4252
4253 + var ok = false;
4254 this.fetching_data = true;
4255
4196 - var ok = false;
4256 + if(netdataSnapshotData !== null) {
4257 + var key = this.chartDataUniqueID();
4258 +
4259 + if(this.debug === true)
4260 + this.log('updating from snapshot: ' + key);
4261 +
4262 + if(typeof netdataSnapshotData.data[key] === 'string') {
4263 + ok = true;
4264 + this.updateChartWithData(JSON.parse(netdataSnapshotData.data[key]));
4265 + }
4266 + else {
4267 + ok = false;
4268 + error('data not found in snapshot for key: ' + key);
4269 + }
4270 +
4271 + NETDATA.statistics.refreshes_active--;
4272 + this.fetching_data = false;
4273 +
4274 + if(typeof callback === 'function')
4275 + callback(ok, 'snapshot');
4276 +
4277 + return;
4278 + }
4279 +
4280 + if(this.debug === true)
4281 + this.log('updating from ' + this.data_url);
4282
4283 this.xhr = $.ajax( {
4284 url: this.data_url,
@@ -4208,7 +4293,6 @@ var NETDATA = window.NETDATA || {};
4293 .done(function(data) {
4294 that.xhr = undefined;
4295 that.retries_on_data_failures = 0;
4211 -
4296 ok = true;
4297
4298 if(that.debug === true)
@@ -4898,6 +4982,18 @@ var NETDATA = window.NETDATA || {};
4982 netdataCallback();
4983 };
4984
4985 + NETDATA.globalReset = function() {
4986 + NETDATA.globalSelectionSync.globalReset();
4987 + NETDATA.globalPanAndZoom.globalReset();
4988 + NETDATA.chartRegistry.globalReset();
4989 + NETDATA.commonMin.globalReset();
4990 + NETDATA.commonMax.globalReset();
4991 + NETDATA.unitsConversion.globalReset();
4992 + NETDATA.options.targets = null;
4993 + NETDATA.parseDom();
4994 + NETDATA.unpause();
4995 + };
4996 +
4997 // ----------------------------------------------------------------------------------------------------------------
4998 // peity
4999
@@ -5265,6 +5361,12 @@ var NETDATA = window.NETDATA || {};
5361 }
5362 }
5363
5364 + if(netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() === true && NETDATA.globalPanAndZoom.isMaster(state) === false) {
5365 + // pan and zoom on snapshots
5366 + options.dateWindow = [ NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms ];
5367 + options.isZoomedIgnoreProgrammaticZoom = true;
5368 + }
5369 +
5370 dygraph.updateOptions(options);
5371
5372 var redraw = false;
@@ -5793,6 +5895,12 @@ var NETDATA = window.NETDATA || {};
5895 }
5896 else state.tmp.dygraph_smooth_eligible = false;
5897
5898 + if(netdataSnapshotData !== null && NETDATA.globalPanAndZoom.isActive() === true && NETDATA.globalPanAndZoom.isMaster(state) === false) {
5899 + // pan and zoom on snapshots
5900 + state.tmp.dygraph_options.dateWindow = [ NETDATA.globalPanAndZoom.force_after_ms, NETDATA.globalPanAndZoom.force_before_ms ];
5901 + state.tmp.dygraph_options.isZoomedIgnoreProgrammaticZoom = true;
5902 + }
5903 +
5904 state.tmp.dygraph_instance = new Dygraph(state.element_chart,
5905 data.result.data, state.tmp.dygraph_options);
5906
@@ -7457,6 +7565,9 @@ var NETDATA = window.NETDATA || {};
7565 },
7566
7567 update_forever: function() {
7568 + if(netdataShowAlarms !== true || netdataSnapshotData !== null)
7569 + return;
7570 +
7571 NETDATA.alarms.get('active', function(data) {
7572 if(data !== null) {
7573 NETDATA.alarms.current = data;
web/index.html
+185 -125
@@ -471,6 +471,9 @@
471
472 <!-- check which theme to use -->
473 <script type="text/javascript">
474 + // netdata snapshot data
475 + var netdataSnapshotData = null;
476 +
477 // enable alarms checking and notifications
478 var netdataShowAlarms = true;
479
@@ -487,7 +490,7 @@
490 hash: '#',
491 theme: null,
492 help: null,
490 - mode: 'live', // 'live', 'print', 'save', 'load'
493 + mode: 'live', // 'live', 'print'
494 update_always: false,
495 pan_and_zoom: false,
496 after: 0,
@@ -553,6 +556,12 @@
556 urlOptions[booleans[len]] = false;
557 }
558
559 + if(typeof urlOptions.after === 'string')
560 + urlOptions.after = parseInt(urlOptions.after);
561 +
562 + if(typeof urlOptions.before === 'string')
563 + urlOptions.before = parseInt(urlOptions.before);
564 +
565 if(urlOptions.before > 0 && urlOptions.after > 0) {
566 urlOptions.pan_and_zoom = true;
567 urlOptions.nowelcome = true;
@@ -561,34 +570,29 @@
570 urlOptions.pan_and_zoom = false;
571
572 switch(urlOptions.mode) {
564 - case 'live':
573 case 'print':
566 - case 'load':
567 - case 'save':
574 + urlOptions.theme = 'white';
575 + urlOptions.welcome = false;
576 + urlOptions.help = false;
577 + urlOptions.show_alarms = false;
578 +
579 + if(urlOptions.pan_and_zoom === false) {
580 + urlOptions.pan_and_zoom = true;
581 + urlOptions.before = Date.now();
582 + urlOptions.after = urlOptions.before - 600000;
583 + }
584 +
585 + netdataShowAlarms = false;
586 + netdataRegistry = false;
587 + this_is_demo = false;
588 break;
589
590 + case 'live':
591 default:
592 urlOptions.mode = 'live';
593 break;
594 }
595
575 - if(urlOptions.mode === 'print') {
576 - urlOptions.theme = 'white';
577 - urlOptions.welcome = false;
578 - urlOptions.help = false;
579 - urlOptions.show_alarms = false;
580 -
581 - if(urlOptions.pan_and_zoom === false) {
582 - urlOptions.pan_and_zoom = true;
583 - urlOptions.before = Date.now();
584 - urlOptions.after = urlOptions.before - 600000;
585 - }
586 -
587 - netdataShowAlarms = false;
588 - netdataRegistry = false;
589 - this_is_demo = false;
590 - }
591 -
596 // console.log(urlOptions);
597 },
598
@@ -623,6 +627,7 @@
627 localStorageTested = true;
628 }
629 catch (e) {
630 + console.log(e);
631 localStorageTested = false;
632 }
633 }
@@ -638,8 +643,13 @@
643 try {
644 if(localStorageTest() === true)
645 ret = localStorage.getItem(name);
646 + else
647 + console.log('localStorage is not available');
648 + }
649 + catch(error) {
650 + console.log(error);
651 + return null;
652 }
642 - catch(error) {}
653
654 if(typeof ret === 'undefined' || ret === null)
655 return null;
@@ -657,7 +667,9 @@
667 return true;
668 }
669 }
660 - catch(error) {}
670 + catch(error) {
671 + console.log(error);
672 + }
673
674 return false;
675 }
@@ -735,6 +747,25 @@
747 return aa.length - bb.length;
748 }
749
750 + // --------------------------------------------------------------------
751 + // saving files to client
752 +
753 + function saveTextToClient(data, filename) {
754 + var blob = new Blob( [ data ], {
755 + type: 'application/octet-stream'
756 + });
757 +
758 + var url = URL.createObjectURL( blob );
759 + var link = document.createElement( 'a' );
760 + link.setAttribute( 'href', url );
761 + link.setAttribute( 'download', filename );
762 + link.click();
763 + }
764 +
765 + function saveObjectToClient(data, filename) {
766 + saveTextToClient(JSON.stringify(data), filename);
767 + }
768 +
769 // --------------------------------------------------------------------
770 // registry call back to render my-netdata menu
771
@@ -1064,10 +1095,6 @@
1095 data: null,
1096 hostname: 'netdata_server', // will be overwritten by the netdata server
1097 version: 'unknown',
1067 - categories: [],
1068 - categories_idx: {},
1069 - families: [],
1070 - families_idx: {},
1098 hosts: [],
1099
1100 duration: 0, // the default duration of the charts
@@ -1075,7 +1102,7 @@
1102
1103 chartsPerRow: 0,
1104 // chartsMinWidth: 1450,
1078 - chartsHeight: 180
1105 + chartsHeight: 180,
1106 };
1107
1108 function chartsPerRow(total) {
@@ -1728,6 +1755,9 @@
1755 }
1756
1757 function renderChartsAndMenu(data) {
1758 + options.menus = {};
1759 + options.submenu_names = {};
1760 +
1761 var menus = options.menus;
1762 var charts = data.charts;
1763 var m, menu_key;
@@ -2675,6 +2705,7 @@
2705 }
2706
2707 // ----------------------------------------------------------------------------
2708 + // printing dashboards
2709
2710 function showPageFooter() {
2711 document.getElementById('footer').style.display = 'block';
@@ -2685,8 +2716,8 @@
2716 var url = document.location.origin + document.location.search + '#' + urlOptions.genHash() + ';mode=print';
2717 var width = 990;
2718 var height = screen.height * 90 / 100;
2688 - console.log(url);
2689 - console.log(document.location);
2719 + //console.log(url);
2720 + //console.log(document.location);
2721 window.open(url, '', 'width=' + width.toString() + ',height=' + height.toString() + ',menubar=no,toolbar=no,personalbar=no,location=no,resizable=no,scrollbars=yes,status=no,chrome=yes,centerscreen=yes,attention=yes,dialog=yes');
2722 $('#printPreflightModal').modal('hide');
2723 }
@@ -2763,73 +2794,86 @@
2794 $('#printModal').modal('show');
2795 }
2796
2766 - function saveObject(data, filename){
2767 - var blob = new Blob( [ JSON.stringify(data) ], {
2768 - type: 'application/octet-stream'
2769 - });
2797 + // --------------------------------------------------------------------
2798 + // loading snapshots
2799
2771 - var url = URL.createObjectURL( blob );
2772 - var link = document.createElement( 'a' );
2773 - link.setAttribute( 'href', url );
2774 - link.setAttribute( 'download', filename );
2775 - link.click();
2800 + function loadSnapshotModalLog(priority, msg) {
2801 + document.getElementById('loadSnapshotStatus').className = "alert alert-" + priority;
2802 + document.getElementById('loadSnapshotStatus').innerHTML = msg;
2803 }
2804
2778 - function loadPage() {
2779 - var files = document.getElementById('loadJSONSelectFiles').files;
2805 + var tmpSnapshotData = null;
2806 + function loadSnapshot() {
2807 + if(tmpSnapshotData === null) {
2808 + loadSnapshotModalLog('danger', 'no data have been loaded');
2809 + return;
2810 + }
2811 + loadSnapshotModalLog('info', 'Please wait, activating snapshot...');
2812 +
2813 + netdataShowAlarms === false;
2814 + netdataRegistry = false;
2815 +
2816 + document.getElementById('charts_div').innerHTML = '';
2817 + document.getElementById('sidebar').innerHTML = '';
2818 + NETDATA.globalReset();
2819 +
2820 + netdataSnapshotData = tmpSnapshotData;
2821 + initializeDynamicDashboard(netdataSnapshotData.url);
2822 +
2823 + $('#loadSnapshotModal').modal('hide');
2824 + }
2825 +
2826 + function loadSnapshotPreflight() {
2827 + var files = document.getElementById('loadSnapshotSelectFiles').files;
2828 if (files.length <= 0) {
2781 - document.getElementById('loadJSONStatus').className = "alert alert-danger";
2782 - document.getElementById('loadJSONStatus').innerHTML = "No file selected";
2829 + loadSnapshotModalLog('danger', 'No file selected');
2830 return;
2831 }
2832
2786 - document.getElementById('loadJSONStatus').className = "alert alert-info";
2787 - document.getElementById('loadJSONStatus').innerHTML = "Loading file...";
2833 + loadSnapshotModalLog('info', 'Loading file...');
2834
2835 var fr = new FileReader();
2836 fr.onload = function(e) {
2791 - console.log(e);
2837 + // console.log(e);
2838
2793 - document.getElementById('loadJSONFilename').innerHTML = files.item(0).name;
2839 + document.getElementById('loadSnapshotFilename').innerHTML = files.item(0).name;
2840 var result = null;
2841 try {
2842 result = JSON.parse(e.target.result);
2797 - console.log(result);
2798 - document.getElementById('loadJSONHostname').innerHTML = result.charts.hostname;
2799 - document.getElementById('loadJSONNetdataVersion').innerHTML = result.charts.version;
2800 - document.getElementById('loadJSONCharts').innerHTML = result.charts.charts_count.toString() + ' charts / ' + result.charts.dimensions_count.toString() + ' dimensions';
2801 -
2843 + //console.log(result);
2844 var date_after = new Date(result.after_ms);
2845 var date_before = new Date(result.before_ms);
2804 - console.log(date_after);
2805 - console.log(date_before);
2806 - document.getElementById('loadJSONTimeRange').innerHTML = NETDATA.dateTime.localeDateString(date_after) + ' ' + NETDATA.dateTime.localeTimeString(date_after) + ' to ' + NETDATA.dateTime.localeDateString(date_before) + ' ' + NETDATA.dateTime.localeTimeString(date_before);
2807 - document.getElementById('loadJSONDataResolution').innerHTML = Math.round(result.duration_ms / result.data_points).toString() + ' ms per point, ' + result.data_points.toString() + ' points per dimension';
2808 -
2809 - document.getElementById('loadJSONStatus').className = "alert alert-success";
2810 - document.getElementById('loadJSONStatus').innerHTML = "File loaded, click <b>Import</b> to render it!";
2811 - $('#loadJSONImport').prop('disabled', false);
2812 - $('#loadJSONImport').removeAttr('disabled');
2846 + document.getElementById('loadSnapshotHostname').innerHTML = '<b>' + result.hostname + '</b>, netdata version <b>' + result.version + '</b>';
2847 + document.getElementById('loadSnapshotURL').innerHTML = result.url;
2848 + document.getElementById('loadSnapshotCharts').innerHTML = result.charts.charts_count.toString() + ' charts, ' + result.charts.dimensions_count.toString() + ' dimensions, ' + result.data_points.toString() + ' points per dimension, ' + Math.round(result.duration_ms / result.data_points).toString() + ' ms per point';
2849 + document.getElementById('loadSnapshotTimeRange').innerHTML = '<b>' + NETDATA.dateTime.localeDateString(date_after) + ' ' + NETDATA.dateTime.localeTimeString(date_after) + '</b> to <b>' + NETDATA.dateTime.localeDateString(date_before) + ' ' + NETDATA.dateTime.localeTimeString(date_before) + '</b>';
2850 + loadSnapshotModalLog('success', 'File loaded, click <b>Import</b> to render it!');
2851 + $('#loadSnapshotImport').prop('disabled', false);
2852 + $('#loadSnapshotImport').removeAttr('disabled');
2853 +
2854 + tmpSnapshotData = result;
2855 }
2856 catch(e) {
2857 console.log(e);
2816 - document.getElementById('loadJSONStatus').className = "alert alert-danger";
2817 - document.getElementById('loadJSONStatus').innerHTML = "Failed to parse this file!";
2858 + document.getElementById('loadSnapshotStatus').className = "alert alert-danger";
2859 + document.getElementById('loadSnapshotStatus').innerHTML = "Failed to parse this file!";
2860 }
2861 }
2862
2863 + //console.log(files.item(0));
2864 fr.readAsText(files.item(0));
2865 }
2866
2824 - var savePageJSON = '';
2825 - var savePageStop = false;
2826 - function savePageCancel() {
2827 - savePageStop = true;
2867 + // --------------------------------------------------------------------
2868 + // saving snapshots
2869 +
2870 + var saveSnapshotStop = false;
2871 + function saveSnapshotCancel() {
2872 + saveSnapshotStop = true;
2873 }
2874
2830 - function savePageStart() {
2831 - savePageStop = false;
2832 - savePageJSON = '';
2875 + function saveSnapshotStart() {
2876 + saveSnapshotStop = false;
2877
2878 var save_options = {
2879 stop_updates_when_focus_is_lost: false,
@@ -2852,36 +2896,56 @@
2896 }
2897 }
2898
2855 - var el = document.getElementById('saveModalProgressBar');
2856 - var eltxt = document.getElementById('saveModalProgressBarText');
2899 + var el = document.getElementById('saveSnapshotModalProgressBar');
2900 + var eltxt = document.getElementById('saveSnapshotModalProgressBarText');
2901
2858 - var saveJSON = {
2902 + options.data.charts_by_name = null;
2903 +
2904 + var saveData = {
2905 + hostname: options.hostname,
2906 + version: options.data.version,
2907 after_ms: Date.now() - options.duration * 1000,
2908 before_ms: Date.now(),
2909 duration_ms: options.duration * 1000,
2910 update_every_ms: options.update_every * 1000,
2911 data_points: 0,
2864 - charts: {},
2912 + url: document.location.origin.toString() + document.location.search.toString(),
2913 + charts: options.data,
2914 data: {}
2915 + };
2916 +
2917 + function pack_api1_v1_chart_data(state) {
2918 + if(state.library_name === null || state.data === null)
2919 + return;
2920 +
2921 + var data = state.data;
2922 + state.data = null;
2923 + data.state = null;
2924 +
2925 + saveData.data[state.chartDataUniqueID()] = JSON.stringify(data);
2926 }
2927
2928 var clearPanAndZoom = false;
2929 if(NETDATA.globalPanAndZoom.isActive() === false) {
2870 - NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], saveJSON.after_ms, saveJSON.before_ms);
2930 + NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], saveData.after_ms, saveData.before_ms);
2931 clearPanAndZoom = true;
2932 }
2933
2874 - saveJSON.after_ms = NETDATA.globalPanAndZoom.force_after_ms;
2875 - saveJSON.before_ms = NETDATA.globalPanAndZoom.force_before_ms;
2876 - saveJSON.duration_ms = saveJSON.before_ms - saveJSON.after_ms;
2877 - saveJSON.data_points = Math.round((saveJSON.before_ms - saveJSON.after_ms) / saveJSON.update_every_ms);
2934 + saveData.after_ms = NETDATA.globalPanAndZoom.force_after_ms;
2935 + saveData.before_ms = NETDATA.globalPanAndZoom.force_before_ms;
2936 + saveData.duration_ms = saveData.before_ms - saveData.after_ms;
2937 + saveData.data_points = Math.round((saveData.before_ms - saveData.after_ms) / saveData.update_every_ms);
2938 + if(saveData.data_points > 10000) {
2939 + console.log('data points ' + saveData.data_points.toString() + ' is too big - limiting it to 10000');
2940 + saveData.data_points = 10000;
2941 + }
2942
2943 var charts_count = 0;
2944 var charts_fetched = 0;
2945 var charts_failed = 0;
2946
2883 - function savePageRestore() {
2884 - $('#saveModal').modal('hide');
2947 + function saveSnapshotRestore() {
2948 + $('#saveSnapshotModal').modal('hide');
2949
2950 // restore the options
2951 var x;
@@ -2904,12 +2968,12 @@
2968
2969 NETDATA.pause(function() {
2970 NETDATA.globalSelectionSync.stop();
2907 - NETDATA.options.force_data_points = saveJSON.data_points;
2971 + NETDATA.options.force_data_points = saveData.data_points;
2972 NETDATA.options.fake_chart_rendering = true;
2973
2974 function update_chart(idx) {
2911 - if(savePageStop === true) {
2912 - savePageRestore();
2975 + if(saveSnapshotStop === true) {
2976 + saveSnapshotRestore();
2977 return;
2978 }
2979
@@ -2922,8 +2986,12 @@
2986 setTimeout(function() {
2987 charts_count++;
2988 state.isVisible(true);
2989 + state.current.force_after_ms = saveData.after_ms;
2990 + state.current.force_before_ms = saveData.before_ms;
2991
2992 state.updateChart(function(status, reason) {
2993 + state.current.force_after_ms = null;
2994 + state.current.force_before_ms = null;
2995
2996 if(status === true) {
2997 charts_fetched++;
@@ -2934,30 +3002,18 @@
3002 state.log('failed to be updated: ' + reason);
3003 }
3004
2937 - // saveJSON.charts[state.id] = state.chart;
2938 - if(state.library_name !== null) {
2939 - var d = state.data;
2940 - state.data = null;
2941 - d.state = undefined;
2942 -
2943 - if (typeof saveJSON.data[state.library_name] === 'undefined')
2944 - saveJSON.data[state.library_name] = {};
2945 -
2946 - saveJSON.data[state.library_name][state.id] = d;
2947 - }
3005 + pack_api1_v1_chart_data(state);
3006
3007 if (idx > 0) {
3008 update_chart(idx);
3009 }
3010 else {
2953 - // FIXME save it
2954 - saveJSON.charts = options.data;
2955 - saveJSON.charts.charts_by_name = undefined;
3011 + // save it
3012 + // console.log(saveData);
3013 + saveObjectToClient(saveData, 'netdata-dashboard-' + saveData.hostname.toString() + '-' + saveData.after_ms.toString() + '-' + saveData.before_ms.toString() + '.json');
3014
2957 - console.log(saveJSON);
2958 - saveObject(saveJSON, 'netdata-dashboard.json');
2959 -
2960 - savePageRestore();
3015 + saveSnapshotRestore();
3016 + saveData = null;
3017 }
3018 })
3019 }, 0);
@@ -2967,6 +3023,9 @@
3023 });
3024 }
3025
3026 + // --------------------------------------------------------------------
3027 + // activate netdata on the page
3028 +
3029 function finalizePage() {
3030 // resize all charts - without starting the background thread
3031 // this has to be done while NETDATA is paused
@@ -3170,8 +3229,8 @@
3229 //else console.log('el.find(): not found');
3230 });
3231
3173 - $('#saveModal').on('shown.bs.modal', savePageStart);
3174 - $('#saveModal').on('hide.bs.modal', savePageCancel);
3232 + $('#saveSnapshotModal').on('shown.bs.modal', saveSnapshotStart);
3233 + $('#saveSnapshotModal').on('hide.bs.modal', saveSnapshotCancel);
3234
3235 showPageFooter();
3236
@@ -3490,6 +3549,7 @@
3549
3550 // our entry point
3551 // var netdataStarted = performance.now();
3552 +
3553 var netdataCallback = initializeDynamicDashboard;
3554
3555 </script>
@@ -3543,8 +3603,8 @@
3603 <li><a href="https://github.com/firehol/netdata/wiki" class="btn" target="_blank" title="netdata on github"><i class="fa fa-github"></i></a></li>
3604 <li><a href="https://twitter.com/linuxnetdata" class="btn" target="_blank" title="netdata on twitter"><i class="fa fa-twitter" aria-hidden="true"></i></a></li>
3605 <li><a href="https://www.facebook.com/linuxnetdata/" class="btn" target="_blank" title="netdata on facebook"><i class="fa fa-facebook-official" aria-hidden="true"></i></a></li>
3546 - <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#loadModal" title="load a saved dashboard"><i class="fa fa-upload"></i>&nbsp;<span class="hidden-sm hidden-md">Load</span></a></li>
3547 - <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#saveModal" title="save this dashboard as JSON"><i class="fa fa-download"></i>&nbsp;<span class="hidden-sm hidden-md">Save</span></a></li>
3606 + <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#loadSnapshotModal" title="load a saved dashboard"><i class="fa fa-upload"></i>&nbsp;<span class="hidden-sm hidden-md">Load</span></a></li>
3607 + <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#saveSnapshotModal" title="save this dashboard as JSON"><i class="fa fa-download"></i>&nbsp;<span class="hidden-sm hidden-md">Save</span></a></li>
3608 <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#printPreflightModal" title="print this dashboard"><i class="fa fa-print"></i>&nbsp;<span class="hidden-sm hidden-md">Print</span></a></li>
3609 <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal" title="dashboard help"><i class="fa fa-question-circle"></i>&nbsp;<span class="hidden-sm hidden-md">Help</span></a></li>
3610 <li class="dropdown hidden-sm hidden-md hidden-lg">
@@ -3690,7 +3750,7 @@
3750 Please wait while we initialize and render all the charts on the dashboard.
3751 <div class="progress progress-striped active" style="height: 2em !important;">
3752 <div id="printModalProgressBar" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em;">
3693 - <span id="printModalProgressBarText" style="padding-top: 4px; font-size: 1.2em; text-align: center; width: 100%; position: absolute; display: block; color: black;"></span>
3753 + <span id="printModalProgressBarText" style="padding-left: 10px; padding-top: 4px; font-size: 1.2em; text-align: left; width: 100%; position: absolute; display: block; color: black;"></span>
3754 </div>
3755 </div>
3756 The print dialog will appear as soon as we finish rendering the page.
@@ -3701,66 +3761,66 @@
3761 </div>
3762 </div>
3763
3704 - <div class="modal fade" id="loadModal" tabindex="-1" role="dialog" aria-labelledby="loadModalLabel">
3764 + <div class="modal fade" id="loadSnapshotModal" tabindex="-1" role="dialog" aria-labelledby="loadSnapshotModalLabel">
3765 <div class="modal-dialog modal-lg" role="document">
3766 <div class="modal-content">
3767 <div class="modal-header">
3768 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
3709 - <h4 class="modal-title" id="loadModalLabel">Load a netdata dashboard snapshot...</h4>
3769 + <h4 class="modal-title" id="loadSnapshotModalLabel">Load a netdata snapshot</h4>
3770 </div>
3771 <div class="modal-body">
3772 <p>
3713 - netdata can load JSON snapshots of netdata dashboards.<br/>
3773 + netdata can save and load JSON snapshots of netdata dashboards.<br/>
3774 Any netdata can load a saved snapshot of the dashboard of any other netdata.
3775 </p>
3776 <p>
3777 <label class="btn btn-default">
3778 Select a netdata saved JSON file to load
3719 - <input type="file" id="loadJSONSelectFiles" value="Import" style="display: none;"
3720 - onchange="loadPage();">
3779 + <input type="file" id="loadSnapshotSelectFiles" value="Import" style="display: none;"
3780 + onchange="loadSnapshotPreflight();">
3781 </label>
3782 </p>
3723 - <div id="loadJSONStatus" class="alert alert-info" role="alert">
3783 + <div id="loadSnapshotStatus" class="alert alert-info" role="alert">
3784 Click the button above to select the file to load, then click <b>Import</b> to render it.
3785 </div>
3786 <p>
3787 <table class="table">
3788 <tbody>
3729 - <tr><th>Filename</th><td id="loadJSONFilename"></td></tr>
3730 - <tr><th>Hostname</th><td id="loadJSONHostname"></td></tr>
3731 - <tr><th>Netdata Version</th><td id="loadJSONNetdataVersion"></td></tr>
3732 - <tr><th>Time Range</th><td id="loadJSONTimeRange"></td></tr>
3733 - <tr><th>Charts</th><td id="loadJSONCharts"></td></tr>
3734 - <tr><th>Data Resolution</th><td id="loadJSONDataResolution"></td></tr>
3789 + <tr><th>Filename</th><td id="loadSnapshotFilename"></td></tr>
3790 + <tr><th>Hostname</th><td id="loadSnapshotHostname"></td></tr>
3791 + <tr><th>Origin URL</th><td id="loadSnapshotURL"></td></tr>
3792 + <tr><th>Time Range</th><td id="loadSnapshotTimeRange"></td></tr>
3793 + <tr><th>Charts</th><td id="loadSnapshotCharts"></td></tr>
3794 </tbody>
3795 </table>
3796 </p>
3797 </div>
3798 <div class="modal-footer">
3740 - <button id="loadJSONImport" type="button" class="btn btn-success btn-disabled" disabled>Import</button>
3799 + <a id="loadSnapshotImport" href="#" onclick="loadSnapshot(); return false;" type="button" class="btn btn-success btn-disabled" disabled>Import</a>
3800 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
3801 </div>
3802 </div>
3803 </div>
3804 </div>
3805
3747 - <div class="modal fade" id="saveModal" tabindex="-1" role="dialog" aria-labelledby="saveModalLabel" data-keyboard="false" data-backdrop="static">
3806 + <div class="modal fade" id="saveSnapshotModal" tabindex="-1" role="dialog" aria-labelledby="saveSnapshotModalLabel" data-keyboard="false" data-backdrop="static">
3807 <div class="modal-dialog modal-lg" role="document">
3808 <div class="modal-content">
3809 <div class="modal-header">
3810 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
3752 - <h4 class="modal-title" id="saveModalLabel">Preparing save file...</h4>
3811 + <h4 class="modal-title" id="saveSnapshotModalLabel">Preparing netdata dashboard snapshot...</h4>
3812 </div>
3813 <div class="modal-body">
3814 Please wait while we collect all the dashboard data...
3815 <div class="progress progress-striped active" style="height: 2em !important;">
3757 - <div id="saveModalProgressBar" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em;">
3758 - <span id="saveModalProgressBarText" style="padding-top: 4px; font-size: 1.2em; text-align: center; width: 100%; position: absolute; display: block;"></span>
3816 + <div id="saveSnapshotModalProgressBar" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em;">
3817 + <span id="saveSnapshotModalProgressBarText" style="padding-left: 10px; padding-top: 4px; font-size: 1.2em; text-align: left; width: 100%; position: absolute; display: block;"></span>
3818 </div>
3819 </div>
3820 The save dialog will appear once we have collected all data.
3821 </div>
3822 <div class="modal-footer">
3823 + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
3824 </div>
3825 </div>
3826 </div>
@@ -4635,6 +4695,6 @@
4695 </div>
4696 </div>
4697 </div>
4638 - <script type="text/javascript" src="dashboard.js?v20171112-13"></script>
4698 + <script type="text/javascript" src="dashboard.js?v20171112-20"></script>
4699 </body>
4700 </html>