]> git.proxmox.com Git - proxmox-backup.git/commitdiff
ui: add DataStoreSummary and move Statistics into it
authorDominik Csapak <d.csapak@proxmox.com>
Tue, 27 Oct 2020 15:20:07 +0000 (16:20 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Tue, 27 Oct 2020 16:41:30 +0000 (17:41 +0100)
this adds a 'Summary' panel to the datastores, similar to what we have
for PVE's nodes/guests/storages

contains an info panel with useful information, a comment field, and
the charts from the statistics panel (which can be deleted since it is
not necessary any more)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
www/DataStoreNotes.js [new file with mode: 0644]
www/DataStorePanel.js
www/DataStoreStatistic.js [deleted file]
www/DataStoreSummary.js [new file with mode: 0644]
www/Makefile

diff --git a/www/DataStoreNotes.js b/www/DataStoreNotes.js
new file mode 100644 (file)
index 0000000..2146280
--- /dev/null
@@ -0,0 +1,104 @@
+Ext.define('PBS.DataStoreNotes', {
+    extend: 'Ext.panel.Panel',
+    xtype: 'pbsDataStoreNotes',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    title: gettext("Comment"),
+    bodyStyle: 'white-space:pre',
+    bodyPadding: 10,
+    scrollable: true,
+    animCollapse: false,
+
+    cbindData: function(initalConfig) {
+       let me = this;
+       me.url = `/api2/extjs/config/datastore/${me.datastore}`;
+       return { };
+    },
+
+    run_editor: function() {
+       let me = this;
+       let win = Ext.create('Proxmox.window.Edit', {
+           title: gettext('Comment'),
+           width: 600,
+           resizable: true,
+           layout: 'fit',
+           defaultButton: undefined,
+           items: {
+               xtype: 'textfield',
+               name: 'comment',
+               value: '',
+               hideLabel: true,
+           },
+           url: me.url,
+           listeners: {
+               destroy: function() {
+                   me.load();
+               },
+           },
+       }).show();
+       win.load();
+    },
+
+    setNotes: function(value) {
+       let me = this;
+       var data = value || '';
+       me.update(Ext.htmlEncode(data));
+
+       if (me.collapsible && me.collapseMode === 'auto') {
+           me.setCollapsed(data === '');
+       }
+    },
+
+    load: function() {
+       var me = this;
+
+       Proxmox.Utils.API2Request({
+           url: me.url,
+           waitMsgTarget: me,
+           failure: function(response, opts) {
+               me.update(gettext('Error') + " " + response.htmlStatus);
+               me.setCollapsed(false);
+           },
+           success: function(response, opts) {
+               me.setNotes(response.result.data.comment);
+           },
+       });
+    },
+
+    listeners: {
+       render: function(c) {
+           var me = this;
+           me.getEl().on('dblclick', me.run_editor, me);
+       },
+       afterlayout: function() {
+           let me = this;
+           if (me.collapsible && !me.getCollapsed() && me.collapseMode === 'always') {
+               me.setCollapsed(true);
+               me.collapseMode = ''; // only once, on initial load!
+           }
+       },
+    },
+
+    tools: [{
+       type: 'gear',
+       handler: function() {
+           this.up('panel').run_editor();
+       },
+    }],
+
+    collapsible: true,
+    collapseDirection: 'right',
+
+    initComponent: function() {
+       var me = this;
+
+       me.callParent();
+
+       let sp = Ext.state.Manager.getProvider();
+       me.collapseMode = sp.get('notes-collapse', 'never');
+
+       if (me.collapseMode === 'auto') {
+           me.setCollapsed(true);
+       }
+    },
+});
index 88ef02a87cd82558d123aff9445ccd2a8d80a4d7..a00ccd476938bc7951cdbd353a07c413df2375ce 100644 (file)
@@ -17,22 +17,24 @@ Ext.define('PBS.DataStorePanel', {
 
     items: [
        {
-           xtype: 'pbsDataStoreContent',
-           itemId: 'content',
+           xtype: 'pbsDataStoreSummary',
+           title: gettext('Summary'),
+           itemId: 'summary',
            cbind: {
                datastore: '{datastore}',
            },
        },
        {
-           title: gettext('Prune & Garbage collection'),
-           xtype: 'pbsDataStorePruneAndGC',
-           itemId: 'prunegc',
+           xtype: 'pbsDataStoreContent',
+           itemId: 'content',
            cbind: {
                datastore: '{datastore}',
            },
        },
        {
-           xtype: 'pbsDataStoreStatistic',
+           title: gettext('Prune & Garbage collection'),
+           xtype: 'pbsDataStorePruneAndGC',
+           itemId: 'prunegc',
            cbind: {
                datastore: '{datastore}',
            },
diff --git a/www/DataStoreStatistic.js b/www/DataStoreStatistic.js
deleted file mode 100644 (file)
index c22640e..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-Ext.define('pve-rrd-datastore', {
-    extend: 'Ext.data.Model',
-    fields: [
-       'used',
-       'total',
-       'read_ios',
-       'read_bytes',
-       'write_ios',
-       'write_bytes',
-        'io_ticks',
-       {
-           name: 'io_delay', calculate: function(data) {
-               let ios = 0;
-               if (data.read_ios !== undefined) { ios += data.read_ios; }
-               if (data.write_ios !== undefined) { ios += data.write_ios; }
-               if (data.io_ticks === undefined) {
-                   return undefined;
-               } else if (ios === 0) {
-                   return 0;
-               }
-               return (data.io_ticks*1000.0)/ios;
-           },
-       },
-       { type: 'date', dateFormat: 'timestamp', name: 'time' },
-    ],
-});
-
-Ext.define('PBS.DataStoreStatistic', {
-    extend: 'Ext.panel.Panel',
-    alias: 'widget.pbsDataStoreStatistic',
-
-    title: gettext('Statistics'),
-
-    scrollable: true,
-
-    initComponent: function() {
-        var me = this;
-
-       if (!me.datastore) {
-           throw "no datastore specified";
-       }
-
-       me.tbar = ['->', { xtype: 'proxmoxRRDTypeSelector' }];
-
-       var rrdstore = Ext.create('Proxmox.data.RRDStore', {
-           rrdurl: "/api2/json/admin/datastore/" + me.datastore + "/rrd",
-           model: 'pve-rrd-datastore',
-       });
-
-       me.items = {
-           xtype: 'container',
-           itemId: 'itemcontainer',
-           layout: 'column',
-           minWidth: 700,
-           defaults: {
-               minHeight: 320,
-               padding: 5,
-               columnWidth: 1,
-           },
-           items: [
-               {
-                   xtype: 'proxmoxRRDChart',
-                   title: gettext('Storage usage (bytes)'),
-                   fields: ['total', 'used'],
-                   fieldTitles: [gettext('Total'), gettext('Storage usage')],
-                   store: rrdstore,
-               },
-               {
-                   xtype: 'proxmoxRRDChart',
-                   title: gettext('Transfer Rate (bytes/second)'),
-                   fields: ['read_bytes', 'write_bytes'],
-                   fieldTitles: [gettext('Read'), gettext('Write')],
-                   store: rrdstore,
-               },
-               {
-                   xtype: 'proxmoxRRDChart',
-                   title: gettext('Input/Output Operations per Second (IOPS)'),
-                   fields: ['read_ios', 'write_ios'],
-                   fieldTitles: [gettext('Read'), gettext('Write')],
-                   store: rrdstore,
-               },
-               {
-                   xtype: 'proxmoxRRDChart',
-                   title: gettext('IO Delay (ms)'),
-                   fields: ['io_delay'],
-                   fieldTitles: [gettext('IO Delay')],
-                   store: rrdstore,
-               },
-           ],
-       };
-
-       me.listeners = {
-           activate: function() {
-               rrdstore.startUpdate();
-           },
-           destroy: function() {
-               rrdstore.stopUpdate();
-           },
-       };
-
-       me.callParent();
-    },
-
-});
diff --git a/www/DataStoreSummary.js b/www/DataStoreSummary.js
new file mode 100644 (file)
index 0000000..539075a
--- /dev/null
@@ -0,0 +1,296 @@
+Ext.define('pve-rrd-datastore', {
+    extend: 'Ext.data.Model',
+    fields: [
+        'used',
+        'total',
+        'read_ios',
+        'read_bytes',
+        'write_ios',
+        'write_bytes',
+        'io_ticks',
+        {
+            name: 'io_delay', calculate: function(data) {
+                let ios = 0;
+                if (data.read_ios !== undefined) { ios += data.read_ios; }
+                if (data.write_ios !== undefined) { ios += data.write_ios; }
+                if (data.io_ticks === undefined) {
+                    return undefined;
+                } else if (ios === 0) {
+                    return 0;
+                }
+                return (data.io_ticks*1000.0)/ios;
+            },
+        },
+        { type: 'date', dateFormat: 'timestamp', name: 'time' },
+    ],
+});
+
+Ext.define('PBS.DataStoreInfo', {
+    extend: 'Ext.panel.Panel',
+    alias: 'widget.pbsDataStoreInfo',
+
+    viewModel: {
+       data: {
+           countstext: '',
+           usage: {},
+           stillbad: 0,
+           removedbytes: 0,
+           mountpoint: "",
+       },
+    },
+
+    controller: {
+       xclass: 'Ext.app.ViewController',
+
+       onLoad: function(store, data, success) {
+           if (!success) return;
+           let me = this;
+           let vm = me.getViewModel();
+
+           let counts = store.getById('counts').data.value;
+           let storage = store.getById('storage').data.value;
+
+           let used = Proxmox.Utils.format_size(storage.used);
+           let total = Proxmox.Utils.format_size(storage.total);
+           let percent = 100*storage.used/storage.total;
+           if (storage.total === 0) {
+               percent = 0;
+           }
+           let used_percent = `${percent.toFixed(2)}%`;
+
+           let usage = used_percent + ' (' +
+               Ext.String.format(gettext('{0} of {1}'),
+                                 used, total) + ')';
+           vm.set('usagetext', usage);
+           vm.set('usage', storage.used/storage.total);
+
+           let gcstatus = store.getById('gc-status').data.value;
+
+           let dedup = (gcstatus['index-data-bytes'] || 0)/
+                       (gcstatus['disk-bytes'] || Infinity);
+
+           let countstext = function(count) {
+               return `${count[0]} ${gettext('Groups')}, ${count[1]} ${gettext('Snapshots')}`;
+           };
+
+           vm.set('ctcount', countstext(counts.ct || [0, 0]));
+           vm.set('vmcount', countstext(counts.vm || [0, 0]));
+           vm.set('hostcount', countstext(counts.host || [0, 0]));
+           vm.set('deduplication', dedup.toFixed(2));
+           vm.set('stillbad', gcstatus['still-bad']);
+           vm.set('removedbytes', Proxmox.Utils.format_size(gcstatus['removed-bytes']));
+       },
+
+       startStore: function() { this.store.startUpdate(); },
+       stopStore: function() { this.store.stopUpdate(); },
+
+       init: function(view) {
+           let me = this;
+           let datastore = encodeURIComponent(view.datastore);
+           me.store = Ext.create('Proxmox.data.ObjectStore', {
+               interval: 5*1000,
+               url: `/api2/json/admin/datastore/${datastore}/status`,
+           });
+           me.store.on('load', me.onLoad, me);
+       },
+    },
+
+    listeners: {
+       activate: 'startStore',
+       destroy: 'stopStore',
+       deactivate: 'stopStore',
+    },
+
+    defaults: {
+       xtype: 'pmxInfoWidget',
+    },
+
+    bodyPadding: 20,
+
+    items: [
+       {
+           iconCls: 'fa fa-hdd-o',
+           title: gettext('Usage'),
+           bind: {
+               data: {
+                   usage: '{usage}',
+                   text: '{usagetext}',
+               },
+           },
+       },
+       {
+           xtype: 'box',
+           html: `<b>${gettext('Backup Count')}</b>`,
+           padding: '10 0 5 0',
+       },
+       {
+           iconCls: 'fa fa-cube',
+           title: gettext('CT'),
+           printBar: false,
+           bind: {
+               data: {
+                   text: '{ctcount}',
+               },
+           },
+       },
+       {
+           iconCls: 'fa fa-building',
+           title: gettext('Host'),
+           printBar: false,
+           bind: {
+               data: {
+                   text: '{hostcount}',
+               },
+           },
+       },
+       {
+           iconCls: 'fa fa-desktop',
+           title: gettext('VM'),
+           printBar: false,
+           bind: {
+               data: {
+                   text: '{vmcount}',
+               },
+           },
+       },
+       {
+           xtype: 'box',
+           html: `<b>${gettext('Stats from last Garbage Collection')}</b>`,
+           padding: '10 0 5 0',
+       },
+       {
+           iconCls: 'fa fa-compress',
+           title: gettext('Deduplication'),
+           printBar: false,
+           bind: {
+               data: {
+                   text: '{deduplication}',
+               },
+           },
+       },
+       {
+           iconCls: 'fa fa-trash-o',
+           title: gettext('Removed Bytes'),
+           printBar: false,
+           bind: {
+               data: {
+                   text: '{removedbytes}',
+               },
+           },
+       },
+       {
+           iconCls: 'fa critical fa-exclamation-triangle',
+           title: gettext('Bad Chunks'),
+           printBar: false,
+           bind: {
+               data: {
+                   text: '{stillbad}',
+               },
+               visible: '{stillbad}',
+           },
+       },
+    ],
+});
+
+Ext.define('PBS.DataStoreSummary', {
+    extend: 'Ext.panel.Panel',
+    alias: 'widget.pbsDataStoreSummary',
+    mixins: ['Proxmox.Mixin.CBind'],
+
+    layout: 'column',
+    scrollable: true,
+
+    bodyPadding: 5,
+    defaults: {
+       columnWidth: 1,
+       padding: 5,
+    },
+
+    tbar: ['->', { xtype: 'proxmoxRRDTypeSelector' }],
+
+    items: [
+       {
+           xtype: 'container',
+           height: 300,
+           layout: {
+               type: 'hbox',
+               align: 'stretch',
+           },
+           items: [
+               {
+                   xtype: 'pbsDataStoreInfo',
+                   flex: 1,
+                   padding: '0 10 0 0',
+                   cbind: {
+                       title: '{datastore}',
+                       datastore: '{datastore}',
+                   },
+               },
+               {
+                   xtype: 'pbsDataStoreNotes',
+                   flex: 1,
+                   cbind: {
+                       datastore: '{datastore}',
+                   },
+               },
+           ],
+       },
+       {
+           xtype: 'proxmoxRRDChart',
+           title: gettext('Storage usage (bytes)'),
+           fields: ['total', 'used'],
+           fieldTitles: [gettext('Total'), gettext('Storage usage')],
+       },
+       {
+           xtype: 'proxmoxRRDChart',
+           title: gettext('Transfer Rate (bytes/second)'),
+           fields: ['read_bytes', 'write_bytes'],
+           fieldTitles: [gettext('Read'), gettext('Write')],
+       },
+       {
+           xtype: 'proxmoxRRDChart',
+           title: gettext('Input/Output Operations per Second (IOPS)'),
+           fields: ['read_ios', 'write_ios'],
+           fieldTitles: [gettext('Read'), gettext('Write')],
+       },
+       {
+           xtype: 'proxmoxRRDChart',
+           title: gettext('IO Delay (ms)'),
+           fields: ['io_delay'],
+           fieldTitles: [gettext('IO Delay')],
+       },
+    ],
+
+    listeners: {
+       activate: function() { this.rrdstore.startUpdate(); },
+       deactivate: function() { this.rrdstore.stopUpdate(); },
+       destroy: function() { this.rrdstore.stopUpdate(); },
+    },
+
+    initComponent: function() {
+       let me = this;
+
+       me.rrdstore = Ext.create('Proxmox.data.RRDStore', {
+           rrdurl: "/api2/json/admin/datastore/" + me.datastore + "/rrd",
+           model: 'pve-rrd-datastore',
+       });
+
+       me.callParent();
+
+       Proxmox.Utils.API2Request({
+           url: `/config/datastore/${me.datastore}`,
+           waitMsgTarget: me.down('pbsDataStoreInfo'),
+           success: function(response) {
+               let path = Ext.htmlEncode(response.result.data.path);
+               me.down('pbsDataStoreInfo').setTitle(`${me.datastore} (${path})`);
+               me.down('pbsDataStoreNotes').setNotes(response.result.data.comment);
+           },
+       });
+
+       me.query('proxmoxRRDChart').forEach((chart) => {
+           chart.setStore(me.rrdstore);
+       });
+
+       me.down('pbsDataStoreInfo').relayEvents(me, ['activate', 'deactivate']);
+    },
+});
index afc240c5b5163012ff648d86017362c14251b1fb..97b9b848d76f86adec3864e060f99179a90824eb 100644 (file)
@@ -40,9 +40,10 @@ JSSRC=                                                       \
        VersionInfo.js                                  \
        SystemConfiguration.js                          \
        Subscription.js                                 \
+       DataStoreSummary.js                             \
+       DataStoreNotes.js                               \
        DataStorePruneAndGC.js                          \
        DataStorePrune.js                               \
-       DataStoreStatistic.js                           \
        DataStoreContent.js                             \
        DataStorePanel.js                               \
        ServerStatus.js                                 \