pmg-gui/js/Dashboard.js
Fabian Ebner 11eae1a0e7 dashboard: add node info panel
Essentially the same as in PBS, but needed to use 'rootfs' instead of
'root' for the disk usage, avoid 'cpu' as an itemId (already used by
the cluster resources), and get rid of the fingerprint button. Also
added the link to the repositories panel like in Dominik's pending
patch for PBS.

Rename the existing resources panel to clarify what it actually is.

Signed-off-by: Fabian Ebner <f.ebner@proxmox.com>
2021-07-13 14:40:08 +02:00

493 lines
9.9 KiB
JavaScript

Ext.define('PMG.Dashboard', {
extend: 'Ext.panel.Panel',
xtype: 'pmgDashboard',
controller: {
xclass: 'Ext.app.ViewController',
openDashboardOptions: function() {
var me = this;
var viewModel = me.getViewModel();
Ext.create('Ext.window.Window', {
modal: true,
width: 300,
title: gettext('Dashboard Options'),
layout: {
type: 'auto',
},
items: [{
xtype: 'form',
bodyPadding: '10 10 10 10',
defaultButton: 'savebutton',
items: [{
xtype: 'proxmoxintegerfield',
itemId: 'hours',
labelWidth: 100,
anchor: '100%',
allowBlank: false,
minValue: 1,
maxValue: 24,
value: viewModel.get('hours'),
fieldLabel: gettext('Hours to show'),
}],
buttons: [{
text: gettext('Save'),
reference: 'loginButton',
formBind: true,
handler: function() {
var win = this.up('window');
var hours = win.down('#hours').getValue();
me.setHours(hours, true);
win.close();
},
}],
}],
}).show();
},
setHours: function(hours, setState) {
var me = this;
var viewModel = me.getViewModel();
viewModel.set('hours', hours);
viewModel.notify();
Ext.Array.forEach(['recentmails', 'receivers'], function(item) {
viewModel.getStore(item).reload();
});
if (setState) {
var sp = Ext.state.Manager.getProvider();
sp.set('dashboard-hours', hours);
}
},
updateMailStats: function(store, records, success) {
if (!success) {
return;
}
var me = this;
var viewModel = me.getViewModel();
var count = 0;
var bytes_in = 0;
var bytes_out = 0;
var ptime = 0;
var avg_ptime = 'N/A';
records.forEach(function(item) {
bytes_in += item.data.bytes_in;
bytes_out += item.data.bytes_out;
// unnormalize
count += (item.data.count*item.data.timespan)/60;
ptime += item.data.ptimesum;
});
if (count) {
avg_ptime = (ptime/count).toFixed(2) + " s";
}
viewModel.set('bytes_in', Proxmox.Utils.format_size(bytes_in));
viewModel.set('bytes_out', Proxmox.Utils.format_size(bytes_out));
viewModel.set('avg_ptime', avg_ptime);
},
updateClusterStats: function(store, records, success) {
if (!success) {
return;
}
var me = this;
var viewmodel = me.getViewModel();
var subStatus = 2; // 2 = all good, 1 = different leves, 0 = none
var subLevel = "";
var cpu = 0;
var mem = 0;
var hd = 0;
var count = records.length;
var errors = [];
records.forEach(function(item) {
// subscription level check
if (subStatus && item.data.level) {
if (subLevel !== "" && subLevel !== item.data.level) {
subStatus = 1;
} else if (subLevel === "") {
subLevel = item.data.level;
}
} else {
subStatus = 0;
}
// resources count
cpu += item.data.cpu || 0;
var memory = item.data.memory || { used: 0, total: 1 };
mem += memory.used/memory.total;
var rootfs = item.data.rootfs || { used: 0, total: 1 };
hd += rootfs.used/rootfs.total;
if (item.data.conn_error && count > 1) {
count--;
errors.push({
name: item.data.name,
msg: item.data.conn_error,
});
}
});
var subscriptionPanel = me.lookup('subscription');
subscriptionPanel.setSubStatus(subStatus);
me.lookup('nodeInfo').setSubscriptionStatus(subStatus);
cpu = cpu/count;
mem = mem/count;
hd = hd/count;
var cpuPanel = me.lookup('cpu');
cpuPanel.updateValue(cpu);
var memPanel = me.lookup('mem');
memPanel.updateValue(mem);
var hdPanel = me.lookup('hd');
hdPanel.updateValue(hd);
if (errors.length && !viewmodel.get('error_shown')) {
var text = "";
errors.forEach(function(error) {
text += error.name + ':<br>' + error.msg + '<br>';
});
Ext.Msg.alert(gettext('Error'), text);
viewmodel.set('error_shown', true);
}
},
updateRepositoryStatus: function(store, records, success) {
if (!success) {
return;
}
let me = this;
me.lookup('nodeInfo').setRepositoryInfo(records[0].data['standard-repos']);
},
init: function(view) {
var me = this;
var sp = Ext.state.Manager.getProvider();
var hours = sp.get('dashboard-hours') || 12;
me.setHours(hours, false);
},
},
viewModel: {
data: {
timespan: 300, // in seconds
hours: 12, // in hours
error_shown: false,
'bytes_in': 0,
'bytes_out': 0,
'avg_ptime': 0.0,
},
stores: {
cluster: {
storeid: 'dash-cluster',
type: 'update',
interval: 5000,
autoStart: true,
autoDestroy: true,
proxy: {
extraParams: { list_single_node: 1 },
type: 'proxmox',
url: '/api2/json/config/cluster/status',
},
listeners: {
load: 'updateClusterStats',
},
},
recentmails: {
storeid: 'dash-recent',
interval: 5000,
type: 'update',
autoStart: true,
autoDestroy: true,
proxy: {
type: 'proxmox',
url: '/api2/json/statistics/recent',
extraParams: {
hours: '{hours}',
timespan: '{timespan}',
},
},
fields: [
{
type: 'number', name: 'count',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'count_in',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'count_out',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'spam',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'spam_in',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'spam_out',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'virus',
convert: PMG.Utils.convert_field_to_per_min,
},
{
type: 'number', name: 'virus_in',
convert: PMG.Utils.convert_field_to_per_min,
},
{ type: 'integer', name: 'virus_out' },
{ type: 'integer', name: 'bytes_in' },
{ type: 'integer', name: 'bytes_out' },
{ type: 'number', name: 'ptimesum' },
{ type: 'date', dateFormat: 'timestamp', name: 'time' },
],
listeners: {
load: 'updateMailStats',
},
},
receivers: {
storeid: 'dash-receivers',
interval: 10000,
type: 'update',
autoStart: true,
autoDestroy: true,
proxy: {
type: 'proxmox',
url: '/api2/json/statistics/recentreceivers',
extraParams: {
hours: '{hours}',
},
},
fields: [
{ type: 'integer', name: 'count' },
{ type: 'string', name: 'receiver' },
],
},
repositories: {
storeid: 'dash-repositories',
type: 'update',
interval: 15000,
autoStart: true,
autoLoad: true,
autoDestroy: true,
proxy: {
type: 'proxmox',
url: '/api2/json/nodes/localhost/apt/repositories',
},
listeners: {
load: 'updateRepositoryStatus',
},
},
},
},
bind: {
title: gettext('Dashboard') + ' (' +
Ext.String.format(gettext('{0} hours'), '{hours}') + ')',
},
layout: {
type: 'column',
},
border: false,
bodyPadding: '20 0 0 20',
defaults: {
columnWidth: 0.5,
xtype: 'panel',
margin: '0 20 20 0',
},
tools: [
{
type: 'gear',
handler: 'openDashboardOptions',
},
],
scrollable: true,
items: [
{
height: 300,
flex: 1,
iconCls: 'fa fa-tachometer',
title: gettext('E-Mail Volume'),
layout: {
type: 'vbox',
align: 'stretch',
},
defaults: {
xtype: 'pmgMiniGraph',
bind: {
store: '{recentmails}',
},
},
items: [
{
fields: ['count'],
fieldTitles: [gettext('Mails / min')],
seriesConfig: {
colors: ['#00617F'],
style: {
opacity: 0.60,
lineWidth: 1,
},
highlightCfg: {
opacity: 1,
scaling: 1,
},
},
},
{
fields: ['spam'],
fieldTitles: [gettext('Spam / min')],
seriesConfig: {
colors: ['#E67300'],
style: {
opacity: 0.60,
lineWidth: 1,
},
highlightCfg: {
opacity: 1,
scaling: 1,
},
},
},
],
},
{
xtype: 'container',
height: 300,
layout: {
type: 'vbox',
align: 'stretch',
},
items: [
{
xtype: 'pmgMailProcessing',
title: gettext('E-Mail Processing'),
iconCls: 'fa fa-hourglass-half',
height: 180,
bind: {
data: {
'bytes_in': '{bytes_in}',
'bytes_out': '{bytes_out}',
'avg_ptime': '{avg_ptime}',
},
},
},
{
iconCls: 'fa fa-ticket',
title: 'Subscription',
reference: 'subscription',
xtype: 'pmgSubscriptionInfo',
margin: '10 0 0 0',
height: 110,
},
],
},
{
height: 250,
iconCls: 'fa fa-tasks',
title: gettext('Cluster Resources (average)'),
bodyPadding: '0 20 0 20',
layout: {
type: 'hbox',
align: 'center',
},
defaults: {
xtype: 'proxmoxGauge',
spriteFontSize: '20px',
flex: 1,
},
items: [
{
title: gettext('CPU'),
reference: 'cpu',
},
{
title: gettext('Memory'),
reference: 'mem',
},
{
title: gettext('Storage'),
reference: 'hd',
},
],
},
{
xtype: 'pmgNodeInfoPanel',
reference: 'nodeInfo',
height: 250,
bodyPadding: '10 5 10 5',
iconCls: 'fa fa-tasks',
},
{
height: 250,
iconCls: 'fa fa-list',
title: gettext('Top Receivers'),
bodyPadding: '20 20 20 20',
layout: {
type: 'vbox',
pack: 'center',
align: 'stretch',
},
items: [{
xtype: 'grid',
bind: {
store: '{receivers}',
},
emptyText: gettext('No data in database'),
// remove all borders/lines/headers
border: false,
bodyBorder: false,
hideHeaders: true,
header: false,
columnLines: false,
rowLines: false,
viewConfig: {
stripeRows: false,
},
columns: [
{
dataIndex: 'receiver',
flex: 1,
text: gettext('Receiver'),
},
{
dataIndex: 'count',
align: 'right',
text: gettext('Count'),
},
],
}],
},
],
});