[pbs-devel] [PATCH proxmox-backup 4/8] ui: add DataStoreSummary and move Statistics into it
Dominik Csapak
d.csapak at proxmox.com
Tue Oct 27 16:20:07 CET 2020
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 at proxmox.com>
---
www/DataStoreNotes.js | 104 ++++++++++++++
www/DataStorePanel.js | 14 +-
www/DataStoreStatistic.js | 104 --------------
www/DataStoreSummary.js | 296 ++++++++++++++++++++++++++++++++++++++
www/Makefile | 3 +-
5 files changed, 410 insertions(+), 111 deletions(-)
create mode 100644 www/DataStoreNotes.js
delete mode 100644 www/DataStoreStatistic.js
create mode 100644 www/DataStoreSummary.js
diff --git a/www/DataStoreNotes.js b/www/DataStoreNotes.js
new file mode 100644
index 00000000..21462805
--- /dev/null
+++ b/www/DataStoreNotes.js
@@ -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);
+ }
+ },
+});
diff --git a/www/DataStorePanel.js b/www/DataStorePanel.js
index 88ef02a8..a00ccd47 100644
--- a/www/DataStorePanel.js
+++ b/www/DataStorePanel.js
@@ -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
index c22640e4..00000000
--- a/www/DataStoreStatistic.js
+++ /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
index 00000000..539075a1
--- /dev/null
+++ b/www/DataStoreSummary.js
@@ -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']);
+ },
+});
diff --git a/www/Makefile b/www/Makefile
index afc240c5..97b9b848 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -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 \
--
2.20.1
More information about the pbs-devel
mailing list