@cryptotaxi247 / netdata-1 / commits / 32650ce68

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>&nbsp;<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>&nbsp;<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>&nbsp;<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>&nbsp;<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>&nbsp;<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>&nbsp;<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">&times;</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">&times;</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">&times;</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">&times;</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">