ui: add panel/Tasks and use it for the node tasks
this is a panel that is heavily inspired from widget-toolkits node/Tasks panel, but is adapted to use the extended api calls of pbs (e.g. since/until filter) has 'filter' panel (like pmgs log tracker gui), but it is collapsible if we extend the api calls of the other projects, we can merge this again into the widget-toolkit one and use that Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
parent
c1fa057cce
commit
185dab7678
@ -36,6 +36,7 @@ JSSRC= \
|
||||
dashboard/LongestTasks.js \
|
||||
dashboard/RunningTasks.js \
|
||||
dashboard/TaskSummary.js \
|
||||
panel/Tasks.js \
|
||||
Utils.js \
|
||||
AccessControlPanel.js \
|
||||
ZFSList.js \
|
||||
|
@ -59,7 +59,7 @@ Ext.define('PBS.ServerAdministration', {
|
||||
url: "/api2/extjs/nodes/localhost/journal",
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxNodeTasks',
|
||||
xtype: 'pbsNodeTasks',
|
||||
itemId: 'tasks',
|
||||
iconCls: 'fa fa-list-alt',
|
||||
title: gettext('Tasks'),
|
||||
|
362
www/panel/Tasks.js
Normal file
362
www/panel/Tasks.js
Normal file
@ -0,0 +1,362 @@
|
||||
Ext.define('PBS.node.Tasks', {
|
||||
extend: 'Ext.grid.GridPanel',
|
||||
|
||||
alias: 'widget.pbsNodeTasks',
|
||||
|
||||
stateful: true,
|
||||
stateId: 'pbs-grid-node-tasks',
|
||||
|
||||
loadMask: true,
|
||||
sortableColumns: false,
|
||||
|
||||
controller: {
|
||||
xclass: 'Ext.app.ViewController',
|
||||
|
||||
showTaskLog: function() {
|
||||
let me = this;
|
||||
let selection = me.getView().getSelection();
|
||||
if (selection.length < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let rec = selection[0];
|
||||
|
||||
Ext.create('Proxmox.window.TaskViewer', {
|
||||
upid: rec.data.upid,
|
||||
endtime: rec.data.endtime,
|
||||
}).show();
|
||||
},
|
||||
|
||||
updateLayout: function() {
|
||||
let me = this;
|
||||
// we want to update the scrollbar on every store load
|
||||
// since the total count might be different
|
||||
// the buffered grid plugin does this only on scrolling itself
|
||||
// and even reduces the scrollheight again when scrolling up
|
||||
me.getView().updateLayout();
|
||||
},
|
||||
|
||||
sinceChange: function(field, newval) {
|
||||
let me = this;
|
||||
let vm = me.getViewModel();
|
||||
|
||||
vm.set('since', newval);
|
||||
},
|
||||
|
||||
untilChange: function(field, newval, oldval) {
|
||||
let me = this;
|
||||
let vm = me.getViewModel();
|
||||
|
||||
vm.set('until', newval);
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
let me = this;
|
||||
let view = me.getView();
|
||||
view.getStore().load();
|
||||
},
|
||||
|
||||
showFilter: function(btn, pressed) {
|
||||
let me = this;
|
||||
let vm = me.getViewModel();
|
||||
vm.set('showFilter', pressed);
|
||||
},
|
||||
|
||||
init: function(view) {
|
||||
let me = this;
|
||||
Proxmox.Utils.monStoreErrors(view, view.getStore(), true);
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
listeners: {
|
||||
itemdblclick: 'showTaskLog',
|
||||
},
|
||||
|
||||
viewModel: {
|
||||
data: {
|
||||
typefilter: '',
|
||||
statusfilter: '',
|
||||
datastore: '',
|
||||
showFilter: false,
|
||||
since: null,
|
||||
until: null,
|
||||
},
|
||||
|
||||
formulas: {
|
||||
extraParams: function(get) {
|
||||
let me = this;
|
||||
let params = {};
|
||||
if (get('typefilter')) {
|
||||
params.typefilter = get('typefilter');
|
||||
}
|
||||
if (get('statusfilter')) {
|
||||
params.statusfilter = get('statusfilter');
|
||||
}
|
||||
if (get('datastore')) {
|
||||
params.store = get('datastore');
|
||||
}
|
||||
|
||||
if (get('since')) {
|
||||
params.since = get('since').valueOf()/1000;
|
||||
}
|
||||
|
||||
if (get('until')) {
|
||||
let until = new Date(get('until').getTime()); // copy object
|
||||
until.setDate(until.getDate() + 1); // end of the day
|
||||
params.until = until.valueOf()/1000;
|
||||
}
|
||||
|
||||
me.getView().getStore().load();
|
||||
|
||||
return params;
|
||||
},
|
||||
},
|
||||
|
||||
stores: {
|
||||
bufferedstore: {
|
||||
type: 'buffered',
|
||||
pageSize: 500,
|
||||
autoLoad: true,
|
||||
remoteFilter: true,
|
||||
model: 'proxmox-tasks',
|
||||
proxy: {
|
||||
type: 'proxmox',
|
||||
startParam: 'start',
|
||||
limitParam: 'limit',
|
||||
extraParams: '{extraParams}',
|
||||
url: "/api2/json/nodes/localhost/tasks",
|
||||
},
|
||||
listeners: {
|
||||
prefetch: 'updateLayout',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
bind: {
|
||||
store: '{bufferedstore}',
|
||||
},
|
||||
|
||||
|
||||
dockedItems: [
|
||||
{
|
||||
xtype: 'toolbar',
|
||||
items: [
|
||||
{
|
||||
xtype: 'proxmoxButton',
|
||||
text: gettext('View'),
|
||||
disabled: true,
|
||||
handler: 'showTaskLog',
|
||||
},
|
||||
{
|
||||
xtype: 'button',
|
||||
text: gettext('Reload'),
|
||||
handler: 'reload',
|
||||
},
|
||||
'->',
|
||||
{
|
||||
xtype: 'button',
|
||||
enableToggle: true,
|
||||
text: gettext('Filter'),
|
||||
stateful: true,
|
||||
stateId: 'task-showfilter',
|
||||
stateEvents: ['toggle'],
|
||||
applyState: function(state) {
|
||||
if (state.pressed !== undefined) {
|
||||
this.setPressed(state.pressed);
|
||||
}
|
||||
},
|
||||
getState: function() {
|
||||
return {
|
||||
pressed: this.pressed,
|
||||
};
|
||||
},
|
||||
listeners: {
|
||||
toggle: 'showFilter',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
xtype: 'toolbar',
|
||||
dock: 'top',
|
||||
layout: {
|
||||
type: 'hbox',
|
||||
align: 'top',
|
||||
},
|
||||
bind: {
|
||||
hidden: '{!showFilter}',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'container',
|
||||
padding: 10,
|
||||
layout: {
|
||||
type: 'vbox',
|
||||
align: 'stretch',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
fieldLabel: gettext('Type'),
|
||||
bind: {
|
||||
value: '{typefilter}',
|
||||
},
|
||||
xtype: 'pmxTaskTypeSelector',
|
||||
},
|
||||
{
|
||||
fieldLabel: gettext('Datastore'),
|
||||
xtype: 'pbsDataStoreSelector',
|
||||
emptyText: gettext('All'),
|
||||
bind: {
|
||||
value: '{datastore}',
|
||||
},
|
||||
allowBlank: true,
|
||||
},
|
||||
{
|
||||
fieldLabel: gettext('States'),
|
||||
xtype: 'combobox',
|
||||
multiSelect: true,
|
||||
emptyText: gettext('All'),
|
||||
store: [
|
||||
['ok', gettext('OK'), ],
|
||||
['unknown', Proxmox.Utils.unknownText, ],
|
||||
['warning', gettext('Warnings') ],
|
||||
['error', gettext('Errors') ],
|
||||
],
|
||||
bind: {
|
||||
value: '{statusfilter}',
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
padding: 10,
|
||||
layout: {
|
||||
type: 'vbox',
|
||||
align: 'stretch',
|
||||
},
|
||||
items: [
|
||||
// we cannot bind the values directly,
|
||||
// since it then changes also on blur,
|
||||
// causing wrong reloads of the store
|
||||
{
|
||||
fieldLabel: gettext('Since'),
|
||||
xtype: 'datefield',
|
||||
format: 'Y-m-d',
|
||||
bind: {
|
||||
maxValue: '{until}',
|
||||
},
|
||||
listeners: {
|
||||
change: 'sinceChange',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldLabel: gettext('Until'),
|
||||
xtype: 'datefield',
|
||||
format: 'Y-m-d',
|
||||
bind: {
|
||||
minValue: '{since}',
|
||||
},
|
||||
listeners: {
|
||||
change: 'untilChange',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
],
|
||||
|
||||
viewConfig: {
|
||||
trackOver: false,
|
||||
stripeRows: false, // does not work with getRowClass()
|
||||
emptyText: gettext('No Tasks found'),
|
||||
|
||||
getRowClass: function(record, index) {
|
||||
let status = record.get('status');
|
||||
|
||||
if (status) {
|
||||
let parsed = Proxmox.Utils.parse_task_status(status);
|
||||
if (parsed === 'error') {
|
||||
return "proxmox-invalid-row";
|
||||
} else if (parsed === 'warning') {
|
||||
return "proxmox-warning-row";
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
|
||||
columns: [
|
||||
{
|
||||
header: gettext("Start Time"),
|
||||
dataIndex: 'starttime',
|
||||
width: 130,
|
||||
renderer: function(value) {
|
||||
return Ext.Date.format(value, "M d H:i:s");
|
||||
},
|
||||
},
|
||||
{
|
||||
header: gettext("End Time"),
|
||||
dataIndex: 'endtime',
|
||||
width: 130,
|
||||
renderer: function(value, metaData, record) {
|
||||
if (!value) {
|
||||
metaData.tdCls = "x-grid-row-loading";
|
||||
return '';
|
||||
}
|
||||
return Ext.Date.format(value, "M d H:i:s");
|
||||
},
|
||||
},
|
||||
{
|
||||
header: gettext("Duration"),
|
||||
hidden: true,
|
||||
width: 80,
|
||||
renderer: function(value, metaData, record) {
|
||||
let start = record.data.starttime;
|
||||
if (start) {
|
||||
let end = record.data.endtime || Date.now();
|
||||
let duration = end - start;
|
||||
if (duration > 0) {
|
||||
duration /= 1000;
|
||||
}
|
||||
return Proxmox.Utils.format_duration_human(duration);
|
||||
}
|
||||
return Proxmox.Utils.unknownText;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: gettext("User name"),
|
||||
dataIndex: 'user',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
header: gettext("Description"),
|
||||
dataIndex: 'upid',
|
||||
flex: 1,
|
||||
renderer: Proxmox.Utils.render_upid,
|
||||
},
|
||||
{
|
||||
header: gettext("Status"),
|
||||
dataIndex: 'status',
|
||||
width: 200,
|
||||
renderer: function(value, metaData, record) {
|
||||
if (value === undefined && !record.data.endtime) {
|
||||
metaData.tdCls = "x-grid-row-loading";
|
||||
return '';
|
||||
}
|
||||
|
||||
let parsed = Proxmox.Utils.parse_task_status(value);
|
||||
switch (parsed) {
|
||||
case 'unknown': return Proxmox.Utils.unknownText;
|
||||
case 'error': return Proxmox.Utils.errorText + ': ' + value;
|
||||
case 'ok': // fall-through
|
||||
case 'warning': // fall-through
|
||||
default: return value;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
Loading…
Reference in New Issue
Block a user