public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 6/6] ui: add panel/Tasks and use it for the node tasks
Date: Fri, 30 Oct 2020 15:02:15 +0100	[thread overview]
Message-ID: <20201030140215.13329-8-d.csapak@proxmox.com> (raw)
In-Reply-To: <20201030140215.13329-1-d.csapak@proxmox.com>

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>
---
 www/Makefile                |   1 +
 www/ServerAdministration.js |   2 +-
 www/panel/Tasks.js          | 362 ++++++++++++++++++++++++++++++++++++
 3 files changed, 364 insertions(+), 1 deletion(-)
 create mode 100644 www/panel/Tasks.js

diff --git a/www/Makefile b/www/Makefile
index 99ea578e..affeb6a9 100644
--- a/www/Makefile
+++ b/www/Makefile
@@ -36,6 +36,7 @@ JSSRC=							\
 	dashboard/LongestTasks.js			\
 	dashboard/RunningTasks.js			\
 	dashboard/TaskSummary.js			\
+	panel/Tasks.js					\
 	Utils.js					\
 	AccessControlPanel.js				\
 	ZFSList.js					\
diff --git a/www/ServerAdministration.js b/www/ServerAdministration.js
index 936d8a07..aa963638 100644
--- a/www/ServerAdministration.js
+++ b/www/ServerAdministration.js
@@ -55,7 +55,7 @@ Ext.define('PBS.ServerAdministration', {
 	    url: "/api2/extjs/nodes/localhost/journal",
 	},
 	{
-	    xtype: 'proxmoxNodeTasks',
+	    xtype: 'pbsNodeTasks',
 	    itemId: 'tasks',
 	    title: gettext('Tasks'),
 	    height: 'auto',
diff --git a/www/panel/Tasks.js b/www/panel/Tasks.js
new file mode 100644
index 00000000..65f0b31c
--- /dev/null
+++ b/www/panel/Tasks.js
@@ -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;
+		}
+	    },
+	},
+    ],
+});
-- 
2.20.1





  parent reply	other threads:[~2020-10-30 14:02 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-30 14:02 [pbs-devel] [PATCH widget-toolkit/proxmox-backup] improve Administration/Task panel Dominik Csapak
2020-10-30 14:02 ` [pbs-devel] [PATCH widget-toolkit 1/1] add form/TaskTypeSelector Dominik Csapak
2020-10-30 17:19   ` [pbs-devel] applied: " Thomas Lamprecht
2020-10-30 14:02 ` [pbs-devel] [PATCH proxmox-backup 1/6] server/worker_task: add tasktype to return the api type of a taskstate Dominik Csapak
2020-10-30 14:02 ` [pbs-devel] [PATCH proxmox-backup 2/6] api2/node/tasks: change limit behaviour when it is 0 Dominik Csapak
2020-10-30 14:02 ` [pbs-devel] [PATCH proxmox-backup 3/6] api2/node/tasks: add optional since/typefilter/statusfilter Dominik Csapak
2020-10-30 14:02 ` [pbs-devel] [PATCH proxmox-backup 4/6] api2/status: remove list_task api call Dominik Csapak
2020-10-30 14:02 ` [pbs-devel] [PATCH proxmox-backup 5/6] api2/node/tasks: add optional until filter Dominik Csapak
2020-10-30 14:02 ` Dominik Csapak [this message]
2020-11-03 13:52 ` [pbs-devel] applied-series: [PATCH widget-toolkit/proxmox-backup] improve Administration/Task panel Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201030140215.13329-8-d.csapak@proxmox.com \
    --to=d.csapak@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal