incomplete support for saving / loading dashboard snapshots as JSON
Costa Tsaousis (ktsaou) committed
Nov 12, 2017 at 03:31 UTC
32650ce68f1ddcf1d04442940cc10708ea80f81a
2 files changed
+271
-26
web/dashboard.js
+9
-5
@@ -4114,7 +4114,7 @@ var NETDATA = window.NETDATA || {};
4114
this.log('I am already updating...');
4115
4116
if(typeof callback === 'function')
4117
- return callback();
4117
+ return callback(false, 'already running');
4118
4119
return;
4120
}
@@ -4126,14 +4126,14 @@ var NETDATA = window.NETDATA || {};
4126
this.log('I am not enabled');
4127
4128
if(typeof callback === 'function')
4129
- return callback();
4129
+ return callback(false, 'not enabled');
4130
4131
return;
4132
}
4133
4134
if(canBeRendered() === false) {
4135
if(typeof callback === 'function')
4136
- return callback();
4136
+ return callback(false, 'cannot be rendered');
4137
4138
return;
4139
}
@@ -4156,7 +4156,7 @@ var NETDATA = window.NETDATA || {};
4156
error('chart library "' + this.library_name + '" is not available.');
4157
4158
if(typeof callback === 'function')
4159
- return callback();
4159
+ return callback(false, 'library not available');
4160
4161
return;
4162
}
@@ -4176,6 +4176,8 @@ var NETDATA = window.NETDATA || {};
4176
4177
this.fetching_data = true;
4178
4179
+ var ok = false;
4180
+
4181
this.xhr = $.ajax( {
4182
url: this.data_url,
4183
cache: false,
@@ -4190,6 +4192,8 @@ var NETDATA = window.NETDATA || {};
4192
that.xhr = undefined;
4193
that.retries_on_data_failures = 0;
4194
4195
+ ok = true;
4196
+
4197
if(that.debug === true)
4198
that.log('data received. updating chart.');
4199
@@ -4218,7 +4222,7 @@ var NETDATA = window.NETDATA || {};
4222
that.fetching_data = false;
4223
4224
if(typeof callback === 'function')
4221
- return callback();
4225
+ return callback(ok, 'download');
4226
});
4227
};
4228
web/index.html
+262
-21
@@ -2675,19 +2675,20 @@
2675
}
2676
2677
2678
- function exportPreflight(mode) {
2678
+ function printPreflight() {
2679
var url = document.location.origin + document.location.search + '#' + urlOptions.genHash() + ';mode=print';
2680
var width = 990;
2681
var height = screen.height * 90 / 100;
2682
console.log(url);
2683
console.log(document.location);
2684
- window.open(url, '', 'width=' + width.toString() + ',height=' + height.toString() + ',menubar=no,location=no,resizable=no,scrollbars=yes,status=no');
2684
+ 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');
2685
+ $('#printPreflightModal').modal('hide');
2686
}
2687
2688
function printPage() {
2689
var print_is_rendering = true;
2690
2690
- $('#exportModal').on('hide.bs.modal', function(e) {
2691
+ $('#printModal').on('hide.bs.modal', function(e) {
2692
if(print_is_rendering === true) {
2693
e.preventDefault();
2694
return false;
@@ -2696,7 +2697,7 @@
2697
return true;
2698
});
2699
2699
- $('#exportModal').on('show.bs.modal', function() {
2700
+ $('#printModal').on('show.bs.modal', function() {
2701
var print_options = {
2702
stop_updates_when_focus_is_lost: false,
2703
update_only_visible: false,
@@ -2722,8 +2723,8 @@
2723
NETDATA.globalPanAndZoom.setMaster(NETDATA.options.targets[0], urlOptions.after, urlOptions.before);
2724
// NETDATA.onresize();
2725
2725
- var el = document.getElementById('exportModalProgressBar');
2726
- var eltxt = document.getElementById('exportModalProgressBarText');
2726
+ var el = document.getElementById('printModalProgressBar');
2727
+ var eltxt = document.getElementById('printModalProgressBarText');
2728
2729
function update_chart(idx) {
2730
var state = NETDATA.options.targets[--idx];
@@ -2741,9 +2742,9 @@
2742
}
2743
else {
2744
print_is_rendering = false;
2744
- $('#exportModal').modal('hide');
2745
+ $('#printModal').modal('hide');
2746
window.print();
2746
- // window.close();
2747
+ window.close();
2748
}
2749
})
2750
}, 0);
@@ -2753,7 +2754,178 @@
2754
update_chart(NETDATA.options.targets.length);
2755
});
2756
2756
- $('#exportModal').modal('show');
2757
+ $('#printModal').modal('show');
2758
+ }
2759
+
2760
+ function saveObject(data, filename){
2761
+ var blob = new Blob( [ JSON.stringify(data) ], {
2762
+ type: 'application/octet-stream'
2763
+ });
2764
+
2765
+ var url = URL.createObjectURL( blob );
2766
+ var link = document.createElement( 'a' );
2767
+ link.setAttribute( 'href', url );
2768
+ link.setAttribute( 'download', filename );
2769
+ link.click();
2770
+ }
2771
+
2772
+ function loadPage() {
2773
+ var files = document.getElementById('loadJSONSelectFiles').files;
2774
+ if (files.length <= 0) {
2775
+ document.getElementById('loadJSONStatus').className = "alert alert-danger";
2776
+ document.getElementById('loadJSONStatus').innerHTML = "No file selected";
2777
+ return;
2778
+ }
2779
+
2780
+ document.getElementById('loadJSONStatus').className = "alert alert-info";
2781
+ document.getElementById('loadJSONStatus').innerHTML = "Loading file...";
2782
+
2783
+ var fr = new FileReader();
2784
+ fr.onload = function(e) {
2785
+ console.log(e);
2786
+
2787
+ document.getElementById('loadJSONFilename').innerHTML = files.item(0).name;
2788
+ var result = null;
2789
+ try {
2790
+ result = JSON.parse(e.target.result);
2791
+ console.log(result);
2792
+ document.getElementById('loadJSONHostname').innerHTML = result.charts.hostname;
2793
+ document.getElementById('loadJSONNetdataVersion').innerHTML = result.charts.version;
2794
+ document.getElementById('loadJSONCharts').innerHTML = result.charts.charts_count.toString() + ' charts / ' + result.charts.dimensions_count.toString() + ' dimensions';
2795
+
2796
+ document.getElementById('loadJSONStatus').className = "alert alert-success";
2797
+ document.getElementById('loadJSONStatus').innerHTML = "File loaded, click <b>Import</b> to render it!";
2798
+ $('#loadJSONImport').prop('disabled', false);
2799
+ $('#loadJSONImport').removeAttr('disabled');
2800
+ }
2801
+ catch(e) {
2802
+ document.getElementById('loadJSONStatus').className = "alert alert-danger";
2803
+ document.getElementById('loadJSONStatus').innerHTML = "Failed to parse this file!";
2804
+ }
2805
+ }
2806
+
2807
+ fr.readAsText(files.item(0));
2808
+ }
2809
+
2810
+ var savePageJSON = '';
2811
+ var savePageStop = false;
2812
+ function savePageCancel() {
2813
+ savePageStop = true;
2814
+ }
2815
+
2816
+ function savePageStart() {
2817
+ savePageStop = false;
2818
+ savePageJSON = '';
2819
+
2820
+ var save_options = {
2821
+ stop_updates_when_focus_is_lost: false,
2822
+ update_only_visible: false,
2823
+ sync_selection: false,
2824
+ eliminate_zero_dimensions: true,
2825
+ pan_and_zoom_data_padding: false,
2826
+ show_help: false,
2827
+ legend_toolbox: false,
2828
+ resize_charts: false,
2829
+ pixels_per_point: 1
2830
+ };
2831
+ var backedup_options = {};
2832
+
2833
+ var x;
2834
+ for(x in save_options) {
2835
+ if (save_options.hasOwnProperty(x)) {
2836
+ backedup_options[x] = NETDATA.options.current[x];
2837
+ NETDATA.options.current[x] = save_options[x];
2838
+ }
2839
+ }
2840
+
2841
+ var el = document.getElementById('saveModalProgressBar');
2842
+ var eltxt = document.getElementById('saveModalProgressBarText');
2843
+
2844
+ var saveJSON = {
2845
+ charts: {},
2846
+ data: {}
2847
+ }
2848
+ var charts_count = 0;
2849
+ var charts_fetched = 0;
2850
+ var charts_failed = 0;
2851
+
2852
+ function savePageRestore() {
2853
+ $('#saveModal').modal('hide');
2854
+
2855
+ // restore the options
2856
+ var x;
2857
+ for(x in backedup_options) {
2858
+ if (backedup_options.hasOwnProperty(x))
2859
+ NETDATA.options.current[x] = backedup_options[x];
2860
+ }
2861
+
2862
+ $(el).css('width', '0%').attr('aria-valuenow', 0);
2863
+ eltxt.innerText = '0%';
2864
+
2865
+ NETDATA.unpause();
2866
+ }
2867
+
2868
+ NETDATA.pause(function() {
2869
+ NETDATA.globalSelectionSync.stop();
2870
+
2871
+ function update_chart(idx) {
2872
+ if(savePageStop === true) {
2873
+ savePageRestore();
2874
+ return;
2875
+ }
2876
+
2877
+ var state = NETDATA.options.targets[--idx];
2878
+
2879
+ var pcent = (NETDATA.options.targets.length - idx) * 100 / NETDATA.options.targets.length;
2880
+ $(el).css('width', pcent+'%').attr('aria-valuenow', pcent);
2881
+ eltxt.innerText = Math.round(pcent).toString() + '%, ' + state.id;
2882
+
2883
+ setTimeout(function() {
2884
+ charts_count++;
2885
+ state.isVisible(true);
2886
+
2887
+ state.updateChart(function(status, reason) {
2888
+
2889
+ if(status === true) {
2890
+ charts_fetched++;
2891
+ // state.log('ok');
2892
+ }
2893
+ else {
2894
+ charts_failed++;
2895
+ state.log('failed to be updated: ' + reason);
2896
+ }
2897
+
2898
+ // saveJSON.charts[state.id] = state.chart;
2899
+ if(state.library_name !== null) {
2900
+ var d = state.data;
2901
+ state.data = null;
2902
+ d.state = undefined;
2903
+
2904
+ if (typeof saveJSON.data[state.library_name] === 'undefined')
2905
+ saveJSON.data[state.library_name] = {};
2906
+
2907
+ saveJSON.data[state.library_name][state.id] = d;
2908
+ }
2909
+
2910
+ if (idx > 0) {
2911
+ update_chart(idx);
2912
+ }
2913
+ else {
2914
+ // FIXME save it
2915
+ saveJSON.charts = options.data;
2916
+ saveJSON.charts.charts_by_name = undefined;
2917
+
2918
+ console.log(saveJSON);
2919
+ saveObject(saveJSON, 'netdata-dashboard.json');
2920
+
2921
+ savePageRestore();
2922
+ }
2923
+ })
2924
+ }, 0);
2925
+ }
2926
+
2927
+ update_chart(NETDATA.options.targets.length);
2928
+ });
2929
}
2930
2931
function finalizePage() {
@@ -2959,6 +3131,9 @@
3131
//else console.log('el.find(): not found');
3132
});
3133
3134
+ $('#saveModal').on('shown.bs.modal', savePageStart);
3135
+ $('#saveModal').on('hide.bs.modal', savePageCancel);
3136
+
3137
showPageFooter();
3138
3139
var update_options_modal = function() {
@@ -3329,8 +3504,9 @@
3504
<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>
3505
<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>
3506
<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>
3332
- <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#importPreflightModal" title="import a saved dashboard"><i class="fa fa-upload"></i> <span class="hidden-sm hidden-md">Load</span></a></li>
3333
- <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#exportPreflightModal" title="export this dashboard"><i class="fa fa-download"></i> <span class="hidden-sm hidden-md">Save</span></a></li>
3507
+ <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> <span class="hidden-sm hidden-md">Load</span></a></li>
3508
+ <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> <span class="hidden-sm hidden-md">Save</span></a></li>
3509
+ <li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#printPreflightModal" title="print this dashboard"><i class="fa fa-print"></i> <span class="hidden-sm hidden-md">Print</span></a></li>
3510
<li class="hidden-sm"><a href="#" class="btn" data-toggle="modal" data-target="#helpModal" title="dashboard help"><i class="fa fa-question-circle"></i> <span class="hidden-sm hidden-md">Help</span></a></li>
3511
<li class="dropdown hidden-sm hidden-md hidden-lg">
3512
<a href="#" class="dropdown-toggle" data-toggle="dropdown">my-netdata <strong class="caret"></strong></a>
@@ -3431,21 +3607,21 @@
3607
</div>
3608
</div>
3609
3434
- <div class="modal fade" id="exportPreflightModal" tabindex="-1" role="dialog" aria-labelledby="exportPreflightModalLabel">
3610
+ <div class="modal fade" id="printPreflightModal" tabindex="-1" role="dialog" aria-labelledby="printPreflightModalLabel">
3611
<div class="modal-dialog modal-lg" role="document">
3612
<div class="modal-content">
3613
<div class="modal-header">
3614
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
3439
- <h4 class="modal-title" id="exportPreflightModalLabel">Export this dashboard?</h4>
3615
+ <h4 class="modal-title" id="printPreflightModalLabel">Export this dashboard?</h4>
3616
</div>
3617
<div class="modal-body">
3618
<p>
3443
- <b>Print to printer (i.e. PDF) or save data (i.e. JSON)?</b>
3619
+ <b>Print to printer (i.e. PDF)?</b>
3620
</p>
3621
<p>
3622
netdata dashboards cannot be captured, since we are lazy loading and hiding all but the visible charts.
3623
<br/>
3448
- To capture the whole page with the charts rendered, a new browser window will pop-up that will render all the charts at once.
3624
+ To capture the whole page with all the charts rendered, a new browser window will pop-up that will render all the charts at once.
3625
The new browser window will maintain the current pan and zoom settings of the charts. So, align the charts before proceeding.
3626
</p>
3627
<p>
@@ -3457,26 +3633,25 @@
3633
</p>
3634
</div>
3635
<div class="modal-footer">
3460
- <a href="#" onclick="exportPreflight('print'); return false;" type="button" class="btn btn-default">Print (PDF)</a>
3461
- <a href="#" onclick="exportPreflight('save'); return false;" type="button" class="btn btn-default">Save (JSON)</a>
3636
+ <a href="#" onclick="printPreflight(); return false;" type="button" class="btn btn-default">Print</a>
3637
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
3638
</div>
3639
</div>
3640
</div>
3641
</div>
3642
3468
- <div class="modal fade" id="exportModal" tabindex="-1" role="dialog" aria-labelledby="exportModalLabel" data-keyboard="false" data-backdrop="static">
3643
+ <div class="modal fade" id="printModal" tabindex="-1" role="dialog" aria-labelledby="printModalLabel" data-keyboard="false" data-backdrop="static">
3644
<div class="modal-dialog modal-lg" role="document">
3645
<div class="modal-content">
3646
<div class="modal-header">
3647
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
3473
- <h4 class="modal-title" id="exportModalLabel">Preparing dashboard for printing...</h4>
3648
+ <h4 class="modal-title" id="printModalLabel">Preparing dashboard for printing...</h4>
3649
</div>
3650
<div class="modal-body">
3651
Please wait while we initialize and render all the charts on the dashboard.
3652
<div class="progress progress-striped active" style="height: 2em !important;">
3478
- <div id="exportModalProgressBar" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em;">
3479
- <span id="exportModalProgressBarText" style="padding-top: 4px; font-size: 1.2em; text-align: center; width: 100%; position: absolute; display: block; color: black;"></span>
3653
+ <div id="printModalProgressBar" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em;">
3654
+ <span id="printModalProgressBarText" style="padding-top: 4px; font-size: 1.2em; text-align: center; width: 100%; position: absolute; display: block; color: black;"></span>
3655
</div>
3656
</div>
3657
The print dialog will appear as soon as we finish rendering the page.
@@ -3487,6 +3662,72 @@
3662
</div>
3663
</div>
3664
3665
+ <div class="modal fade" id="loadModal" tabindex="-1" role="dialog" aria-labelledby="loadModalLabel">
3666
+ <div class="modal-dialog modal-lg" role="document">
3667
+ <div class="modal-content">
3668
+ <div class="modal-header">
3669
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
3670
+ <h4 class="modal-title" id="loadModalLabel">Select JSON file to load...</h4>
3671
+ </div>
3672
+ <div class="modal-body">
3673
+ <p>
3674
+ netdata can load JSON snapshots of netdata dashboards.<br/>
3675
+ Any netdata can load a saved snapshot of the dashboard of any other netdata.
3676
+ </p>
3677
+ <p>
3678
+ <label class="btn btn-default">
3679
+ Select a netdata saved JSON file to load
3680
+ <input type="file" id="loadJSONSelectFiles" value="Import" style="display: none;"
3681
+ onchange="loadPage();">
3682
+ </label>
3683
+ </p>
3684
+ <div id="loadJSONStatus" class="alert alert-info" role="alert">
3685
+ Click the button above to select the file to load, then click <b>Import</b> to render it.
3686
+ </div>
3687
+ <p>
3688
+ <table class="table">
3689
+ <tbody>
3690
+ <tr><th>Filename</th><td id="loadJSONFilename"></td></tr>
3691
+ <tr><th>Hostname</th><td id="loadJSONHostname"></td></tr>
3692
+ <tr><th>Netdata Version</th><td id="loadJSONNetdataVersion"></td></tr>
3693
+ <tr><th>DateTime From</th><td></td id="loadJSONDateTimeFrom"></tr>
3694
+ <tr><th>DateTime To</th><td></td id="loadJSONDateTimeTo"></tr>
3695
+ <tr><th>Charts</th><td id="loadJSONCharts"></td></tr>
3696
+ <tr><th>Data Resolution</th><td id="loadJSONDataResolution"></td></tr>
3697
+ </tbody>
3698
+ </table>
3699
+ </p>
3700
+ </div>
3701
+ <div class="modal-footer">
3702
+ <button id="loadJSONImport" type="button" class="btn btn-success btn-disabled" disabled>Import</button>
3703
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
3704
+ </div>
3705
+ </div>
3706
+ </div>
3707
+ </div>
3708
+
3709
+ <div class="modal fade" id="saveModal" tabindex="-1" role="dialog" aria-labelledby="saveModalLabel" data-keyboard="false" data-backdrop="static">
3710
+ <div class="modal-dialog modal-lg" role="document">
3711
+ <div class="modal-content">
3712
+ <div class="modal-header">
3713
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
3714
+ <h4 class="modal-title" id="saveModalLabel">Preparing save file...</h4>
3715
+ </div>
3716
+ <div class="modal-body">
3717
+ Please wait while we collect all the dashboard data...
3718
+ <div class="progress progress-striped active" style="height: 2em !important;">
3719
+ <div id="saveModalProgressBar" class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 2em;">
3720
+ <span id="saveModalProgressBarText" style="padding-top: 4px; font-size: 1.2em; text-align: center; width: 100%; position: absolute; display: block;"></span>
3721
+ </div>
3722
+ </div>
3723
+ The save dialog will appear once we have collected all data.
3724
+ </div>
3725
+ <div class="modal-footer">
3726
+ </div>
3727
+ </div>
3728
+ </div>
3729
+ </div>
3730
+
3731
<div class="modal fade" id="welcomeModal" tabindex="-1" role="dialog" aria-labelledby="welcomeModalLabel">
3732
<div class="modal-dialog modal-lg" role="document">
3733
<div class="modal-content">