ui: add Task Panels for dashboard
LongestTasks: grid that shows tasks sorted by duration in descending order RunningTasks: grid that shows all running tasks TaskSummary: an overview of backup,prune,gc and sync tasks (error/warning/ok) Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
4b12879289
commit
6f3146c08c
|
@ -19,6 +19,9 @@ JSSRC= \
|
|||
window/ACLEdit.js \
|
||||
window/DataStoreEdit.js \
|
||||
dashboard/DataStoreStatistics.js \
|
||||
dashboard/LongestTasks.js \
|
||||
dashboard/RunningTasks.js \
|
||||
dashboard/TaskSummary.js \
|
||||
Utils.js \
|
||||
LoginView.js \
|
||||
VersionInfo.js \
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
Ext.define('PBS.LongestTasks', {
|
||||
extend: 'Ext.grid.Panel',
|
||||
alias: 'widget.pbsLongestTasks',
|
||||
|
||||
title: gettext('Longest Tasks (last Month)'),
|
||||
|
||||
hideHeaders: true,
|
||||
rowLines: false,
|
||||
|
||||
emptyText: gettext('No Tasks'),
|
||||
|
||||
controller: {
|
||||
xclass: 'Ext.app.ViewController',
|
||||
|
||||
openTask: function(record) {
|
||||
let me = this;
|
||||
let view = me.getView();
|
||||
Ext.create('Proxmox.window.TaskViewer', {
|
||||
upid: record.data.upid,
|
||||
endtime: record.data.endtime,
|
||||
}).show();
|
||||
},
|
||||
|
||||
openTaskItemDblClick: function(grid, record) {
|
||||
this.openTask(record);
|
||||
},
|
||||
|
||||
openTaskActionColumn: function(grid, rowIndex) {
|
||||
this.openTask(grid.getStore().getAt(rowIndex));
|
||||
},
|
||||
|
||||
render_status: function(value) {
|
||||
let cls = 'times-circle critical';
|
||||
if (value === 'OK') {
|
||||
cls = 'check-circle good';
|
||||
} else if (value.startsWith('WARNINGS:')) {
|
||||
cls = 'exclamation-circle warning';
|
||||
} else if (value === 'unknown') {
|
||||
cls = 'question-circle faded';
|
||||
}
|
||||
|
||||
return `<i class="fa fa-${cls}"></i>`;
|
||||
},
|
||||
},
|
||||
|
||||
updateTasks: function(data) {
|
||||
let me = this;
|
||||
me.getStore().setData(data);
|
||||
},
|
||||
|
||||
listeners: {
|
||||
itemdblclick: 'openTaskItemDblClick',
|
||||
},
|
||||
|
||||
store: {
|
||||
type: 'diff',
|
||||
autoDestroy: true,
|
||||
autoDestroyRstore: true,
|
||||
sorters: {
|
||||
property: 'duration',
|
||||
direction: 'DESC',
|
||||
},
|
||||
rstore: {
|
||||
storeid: 'proxmox-tasks-dash',
|
||||
type: 'store',
|
||||
model: 'proxmox-tasks',
|
||||
proxy: {
|
||||
type: 'memory',
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
columns: [
|
||||
{
|
||||
text: gettext('Task'),
|
||||
dataIndex: 'upid',
|
||||
renderer: Proxmox.Utils.render_upid,
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
text: gettext('Duration'),
|
||||
dataIndex: 'duration',
|
||||
renderer: Proxmox.Utils.format_duration_human,
|
||||
},
|
||||
{
|
||||
text: gettext('Status'),
|
||||
align: 'center',
|
||||
width: 40,
|
||||
dataIndex: 'status',
|
||||
renderer: 'render_status',
|
||||
},
|
||||
{
|
||||
xtype: 'actioncolumn',
|
||||
width: 40,
|
||||
items: [
|
||||
{
|
||||
iconCls: 'fa fa-chevron-right',
|
||||
tooltip: gettext('Open Task'),
|
||||
handler: 'openTaskActionColumn',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
|
@ -0,0 +1,108 @@
|
|||
Ext.define('PBS.RunningTasks', {
|
||||
extend: 'Ext.grid.Panel',
|
||||
alias: 'widget.pbsRunningTasks',
|
||||
|
||||
title: gettext('Running Tasks'),
|
||||
emptyText: gettext('No running tasks'),
|
||||
|
||||
hideHeaders: true,
|
||||
rowLines: false,
|
||||
|
||||
controller: {
|
||||
xclass: 'Ext.app.ViewController',
|
||||
|
||||
openTask: function(record) {
|
||||
let me = this;
|
||||
let view = me.getView();
|
||||
Ext.create('Proxmox.window.TaskViewer', {
|
||||
upid: record.data.upid,
|
||||
endtime: record.data.endtime,
|
||||
}).show();
|
||||
},
|
||||
|
||||
openTaskItemDblClick: function(grid, record) {
|
||||
this.openTask(record);
|
||||
},
|
||||
|
||||
openTaskActionColumn: function(grid, rowIndex) {
|
||||
this.openTask(grid.getStore().getAt(rowIndex));
|
||||
},
|
||||
|
||||
render_status: function(value) {
|
||||
let cls = 'times-circle critical';
|
||||
if (value === 'OK') {
|
||||
cls = 'check-circle good';
|
||||
} else if (value.startsWith('WARNINGS:')) {
|
||||
cls = 'exclamation-circle warning';
|
||||
} else if (value === 'unknown') {
|
||||
cls = 'question-circle faded';
|
||||
}
|
||||
|
||||
return `<i class="fa fa-${cls}"></i>`;
|
||||
},
|
||||
},
|
||||
|
||||
updateTasks: function(data) {
|
||||
let me = this;
|
||||
me.getStore().setData(data);
|
||||
},
|
||||
|
||||
listeners: {
|
||||
itemdblclick: 'openTaskItemDblClick',
|
||||
},
|
||||
|
||||
store: {
|
||||
type: 'diff',
|
||||
autoDestroy: true,
|
||||
autoDestroyRstore: true,
|
||||
sorters: 'starttime',
|
||||
rstore: {
|
||||
type: 'update',
|
||||
autoStart: true,
|
||||
interval: 3000,
|
||||
storeid: 'pbs-running-tasks-dash',
|
||||
model: 'proxmox-tasks',
|
||||
proxy: {
|
||||
type: 'proxmox',
|
||||
// maybe separate api call?
|
||||
url: '/api2/json/nodes/localhost/tasks?running=1'
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
columns: [
|
||||
{
|
||||
text: 'Task',
|
||||
dataIndex: 'upid',
|
||||
renderer: Proxmox.Utils.render_upid,
|
||||
flex: 2,
|
||||
},
|
||||
{
|
||||
text: 'Starttime',
|
||||
dataIndex: 'starttime',
|
||||
renderer: function(value) {
|
||||
return Ext.Date.format(value, "Y-m-d H:i:s");
|
||||
},
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
text: 'Duration',
|
||||
dataIndex: 'duration',
|
||||
renderer: function(value, md, record) {
|
||||
return Proxmox.Utils.format_duration_human((Date.now() - record.data.starttime)/1000);
|
||||
}
|
||||
},
|
||||
{
|
||||
xtype: 'actioncolumn',
|
||||
width: 40,
|
||||
items: [
|
||||
{
|
||||
iconCls: 'fa fa-chevron-right',
|
||||
tooltip: gettext('Open Task'),
|
||||
handler: 'openTaskActionColumn',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
Ext.define('PBS.TaskSummary', {
|
||||
extend: 'Ext.panel.Panel',
|
||||
alias: 'widget.pbsTaskSummary',
|
||||
|
||||
title: gettext('Task Summary (last Month)'),
|
||||
|
||||
controller: {
|
||||
xclass: 'Ext.app.ViewController',
|
||||
|
||||
render_count: function(value, md, record, rowindex, colindex) {
|
||||
let cls = 'question';
|
||||
switch (colindex) {
|
||||
case 1: cls = "times-circle critical"; break;
|
||||
case 2: cls = "exclamation-circle warning"; break;
|
||||
case 3: cls = "check-circle good"; break;
|
||||
default: break;
|
||||
}
|
||||
return `<i class="fa fa-${cls}"></i> ${value}`;
|
||||
},
|
||||
},
|
||||
|
||||
updateTasks: function(data) {
|
||||
let me = this;
|
||||
data.backup.type = gettext('Backups');
|
||||
data.prune.type = gettext('Prunes');
|
||||
data.garbage_collection.type = gettext('Garbage collections');
|
||||
data.sync.type = gettext('Syncs');
|
||||
me.lookup('grid').getStore().setData([
|
||||
data.backup,
|
||||
data.prune,
|
||||
data.garbage_collection,
|
||||
data.sync,
|
||||
]);
|
||||
},
|
||||
|
||||
layout: 'fit',
|
||||
bodyPadding: 15,
|
||||
minHeight: 166,
|
||||
|
||||
// we have to wrap the grid in a panel to get the padding right
|
||||
items: [
|
||||
{
|
||||
xtype: 'grid',
|
||||
reference: 'grid',
|
||||
hideHeaders: true,
|
||||
border: false,
|
||||
bodyBorder: false,
|
||||
rowLines: false,
|
||||
viewConfig: {
|
||||
stripeRows: false,
|
||||
trackOver: false,
|
||||
},
|
||||
scrollable: false,
|
||||
disableSelection: true,
|
||||
|
||||
store: {
|
||||
data: []
|
||||
},
|
||||
|
||||
columns: [
|
||||
{
|
||||
dataIndex: 'type',
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
dataIndex: 'error',
|
||||
renderer: 'render_count',
|
||||
},
|
||||
{
|
||||
dataIndex: 'warning',
|
||||
renderer: 'render_count',
|
||||
},
|
||||
{
|
||||
dataIndex: 'ok',
|
||||
renderer: 'render_count',
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
|
||||
});
|
Loading…
Reference in New Issue