mirror of
https://git.proxmox.com/git/proxmox-backup
synced 2025-07-04 16:01:07 +00:00
fix #3217: ui: global prune and gc job view
In the global datastore view, extend the prune view to display gc job status as a table. Use the same widget in the local view and dispaly gc job status as a single row. The local PruneAndGC view is parameterized (cbind) with the datastore. At initialization the only row is selected. This allows the rest of the grid to act on selected rows and it requires far less special casing if the datastore is set on the view or not. Having a single row always selected and therefore highlighted, is visually not appealing. Therefore, highlighting of selected rows is disabled in the local view. Moved GCView to different file and enhanced it with last, next run, status and duration. Added button to show task log. Changed `render_task_status()` to also take in account upids stored in other 'columns'. Signed-off-by: Stefan Lendl <s.lendl@proxmox.com> [LW: include ref to bugzilla in commit message] Signed-off-by: Lukas Wagner <l.wagner@proxmox.com> Originally-by: Gabriel Goller <g.goller@proxmox.com> Tested-by: Gabriel Goller <g.goller@proxmox.com> Reviewd-by: Gabriel Goller <g.goller@proxmox.com> Tested-by: Lukas Wagner <l.wagner@proxmox.com> Reviewed-by: Lukas Wagner <l.wagner@proxmox.com>
This commit is contained in:
parent
fe1d34d2e4
commit
db3fd2132d
@ -63,6 +63,7 @@ JSSRC= \
|
|||||||
config/SyncView.js \
|
config/SyncView.js \
|
||||||
config/VerifyView.js \
|
config/VerifyView.js \
|
||||||
config/PruneView.js \
|
config/PruneView.js \
|
||||||
|
config/GCView.js \
|
||||||
config/WebauthnView.js \
|
config/WebauthnView.js \
|
||||||
config/CertificateView.js \
|
config/CertificateView.js \
|
||||||
config/NodeOptionView.js \
|
config/NodeOptionView.js \
|
||||||
@ -79,6 +80,7 @@ JSSRC= \
|
|||||||
window/NotifyOptions.js \
|
window/NotifyOptions.js \
|
||||||
window/SyncJobEdit.js \
|
window/SyncJobEdit.js \
|
||||||
window/PruneJobEdit.js \
|
window/PruneJobEdit.js \
|
||||||
|
window/GCJobEdit.js \
|
||||||
window/UserEdit.js \
|
window/UserEdit.js \
|
||||||
window/Settings.js \
|
window/Settings.js \
|
||||||
window/TokenEdit.js \
|
window/TokenEdit.js \
|
||||||
|
@ -199,12 +199,12 @@ Ext.define('PBS.Utils', {
|
|||||||
return fingerprint.substring(0, 23);
|
return fingerprint.substring(0, 23);
|
||||||
},
|
},
|
||||||
|
|
||||||
render_task_status: function(value, metadata, record) {
|
render_task_status: function(value, metadata, record, rowIndex, colIndex, store) {
|
||||||
if (!record.data['last-run-upid']) {
|
if (!record.data['last-run-upid'] && !store.getById('last-run-upid')?.data.value) {
|
||||||
return '-';
|
return '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!record.data['last-run-endtime']) {
|
if (!record.data['last-run-endtime'] && !store.getById('last-run-endtime')?.data.value) {
|
||||||
metadata.tdCls = 'x-grid-row-loading';
|
metadata.tdCls = 'x-grid-row-loading';
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
207
www/config/GCView.js
Normal file
207
www/config/GCView.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
Ext.define('pbs-gc-jobs-status', {
|
||||||
|
extend: 'Ext.data.Model',
|
||||||
|
fields: [
|
||||||
|
'store', 'last-run-upid', 'removed-chunks', 'pending-chunks', 'schedule',
|
||||||
|
'next-run', 'last-run-endtime', 'last-run-state',
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
calculate: function(data) {
|
||||||
|
let endtime = data['last-run-endtime'];
|
||||||
|
if (!endtime) return undefined;
|
||||||
|
let task = Proxmox.Utils.parse_task_upid(data['last-run-upid']);
|
||||||
|
return endtime - task.starttime;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
idProperty: 'store',
|
||||||
|
proxy: {
|
||||||
|
type: 'proxmox',
|
||||||
|
url: '/api2/json/admin/gc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.define('PBS.config.GCJobView', {
|
||||||
|
extend: 'Ext.grid.GridPanel',
|
||||||
|
alias: 'widget.pbsGCJobView',
|
||||||
|
|
||||||
|
stateful: true,
|
||||||
|
stateId: 'grid-gc-jobs-v1',
|
||||||
|
allowDeselect: false,
|
||||||
|
|
||||||
|
title: gettext('Garbage Collect Jobs'),
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
init: function(view) {
|
||||||
|
let params = {};
|
||||||
|
let store = view.getStore();
|
||||||
|
let proxy = store.rstore.getProxy();
|
||||||
|
if (view.datastore) {
|
||||||
|
params.store = view.datastore;
|
||||||
|
|
||||||
|
// after the store is loaded, select the row to enable the Edit,.. buttons
|
||||||
|
store.rstore.proxy.on({
|
||||||
|
'afterload': {
|
||||||
|
fn: () => view.getSelectionModel().select(0),
|
||||||
|
single: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// do not highlight the selected row
|
||||||
|
view.items.items[0].selectedItemCls = '';
|
||||||
|
view.items.items[0].overItemCls = '';
|
||||||
|
}
|
||||||
|
proxy.setExtraParams(params);
|
||||||
|
Proxmox.Utils.monStoreErrors(view, store.rstore);
|
||||||
|
},
|
||||||
|
|
||||||
|
getDatastoreName: function() {
|
||||||
|
return this.getView().getSelection()[0]?.data.store;
|
||||||
|
},
|
||||||
|
|
||||||
|
getData: function() {
|
||||||
|
let view = this.getView();
|
||||||
|
let datastore = this.getDatastoreName();
|
||||||
|
return view.getStore().getById(datastore).data;
|
||||||
|
},
|
||||||
|
|
||||||
|
editGCJob: function() {
|
||||||
|
let data = this.getData();
|
||||||
|
Ext.create('PBS.window.GCJobEdit', {
|
||||||
|
datastore: data.store,
|
||||||
|
id: data.store,
|
||||||
|
schedule: data.schedule,
|
||||||
|
listeners: {
|
||||||
|
destroy: () => this.reload(),
|
||||||
|
},
|
||||||
|
}).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
garbageCollect: function() {
|
||||||
|
let datastore = this.getDatastoreName();
|
||||||
|
Proxmox.Utils.API2Request({
|
||||||
|
url: `/admin/datastore/${datastore}/gc`,
|
||||||
|
method: 'POST',
|
||||||
|
failure: function(response) {
|
||||||
|
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
|
||||||
|
},
|
||||||
|
success: function(response, options) {
|
||||||
|
Ext.create('Proxmox.window.TaskViewer', {
|
||||||
|
upid: response.result.data,
|
||||||
|
}).show();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showTaskLog: function() {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
let upid = this.getData()['last-run-upid'];
|
||||||
|
if (!upid) return;
|
||||||
|
|
||||||
|
Ext.create('Proxmox.window.TaskViewer', { upid }).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
startStore: function() { this.getView().getStore().rstore.startUpdate(); },
|
||||||
|
stopStore: function() { this.getView().getStore().rstore.stopUpdate(); },
|
||||||
|
reload: function() { this.getView().getStore().rstore.load(); },
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
activate: 'startStore',
|
||||||
|
destroy: 'stopStore',
|
||||||
|
deactivate: 'stopStore',
|
||||||
|
itemdblclick: 'editGCJob',
|
||||||
|
},
|
||||||
|
|
||||||
|
store: {
|
||||||
|
type: 'diff',
|
||||||
|
autoDestroy: true,
|
||||||
|
autoDestroyRstore: true,
|
||||||
|
sorters: 'store',
|
||||||
|
rstore: {
|
||||||
|
type: 'update',
|
||||||
|
storeid: 'pbs-gc-jobs-status',
|
||||||
|
model: 'pbs-gc-jobs-status',
|
||||||
|
interval: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
tbar: [
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxButton',
|
||||||
|
text: gettext('Edit'),
|
||||||
|
handler: 'editGCJob',
|
||||||
|
enableFn: (rec) => !!rec,
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
'-',
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxButton',
|
||||||
|
text: gettext('Show Log'),
|
||||||
|
handler: 'showTaskLog',
|
||||||
|
enableFn: (rec) => !!rec.data["last-run-upid"],
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'proxmoxButton',
|
||||||
|
text: gettext('Run now'),
|
||||||
|
handler: 'garbageCollect',
|
||||||
|
enableFn: (rec) => !!rec,
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
header: gettext('Datastore'),
|
||||||
|
dataIndex: 'store',
|
||||||
|
renderer: Ext.String.htmlEncode,
|
||||||
|
width: 120,
|
||||||
|
sortable: true,
|
||||||
|
hideable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Schedule'),
|
||||||
|
dataIndex: 'schedule',
|
||||||
|
maxWidth: 220,
|
||||||
|
minWidth: 80,
|
||||||
|
flex: 1,
|
||||||
|
sortable: false,
|
||||||
|
hideable: false,
|
||||||
|
renderer: (value) => value ? value : Proxmox.Utils.NoneText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Last GC'),
|
||||||
|
dataIndex: 'last-run-endtime',
|
||||||
|
renderer: PBS.Utils.render_optional_timestamp,
|
||||||
|
minWidth: 150,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: gettext('Duration'),
|
||||||
|
dataIndex: 'duration',
|
||||||
|
renderer: Proxmox.Utils.render_duration,
|
||||||
|
sortable: false,
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Last Status'),
|
||||||
|
dataIndex: 'last-run-state',
|
||||||
|
renderer: PBS.Utils.render_task_status,
|
||||||
|
sortable: true,
|
||||||
|
flex: 3,
|
||||||
|
maxWidth: 100,
|
||||||
|
minWidth: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: gettext('Next Run'),
|
||||||
|
dataIndex: 'next-run',
|
||||||
|
renderer: PBS.Utils.render_next_task_run,
|
||||||
|
width: 150,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
@ -239,8 +239,8 @@ Ext.define('PBS.datastore.DataStores', {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
iconCls: 'fa fa-trash-o',
|
iconCls: 'fa fa-trash-o',
|
||||||
itemId: 'prunejobs',
|
itemId: 'prunegc',
|
||||||
xtype: 'pbsPruneJobView',
|
xtype: 'pbsDatastorePruneAndGC',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
iconCls: 'fa fa-check-circle',
|
iconCls: 'fa fa-check-circle',
|
||||||
|
@ -58,7 +58,6 @@ Ext.define('PBS.DataStorePanel', {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: gettext('Prune & GC'),
|
|
||||||
xtype: 'pbsDatastorePruneAndGC',
|
xtype: 'pbsDatastorePruneAndGC',
|
||||||
itemId: 'prunegc',
|
itemId: 'prunegc',
|
||||||
iconCls: 'fa fa-trash-o',
|
iconCls: 'fa fa-trash-o',
|
||||||
|
@ -1,91 +1,8 @@
|
|||||||
Ext.define('PBS.Datastore.GCOptions', {
|
|
||||||
extend: 'Proxmox.grid.ObjectGrid',
|
|
||||||
alias: 'widget.pbsDatastoreGCOpts',
|
|
||||||
mixins: ['Proxmox.Mixin.CBind'],
|
|
||||||
|
|
||||||
onlineHelp: 'maintenance_pruning',
|
|
||||||
|
|
||||||
cbindData: function(initial) {
|
|
||||||
let me = this;
|
|
||||||
|
|
||||||
me.datastore = encodeURIComponent(me.datastore);
|
|
||||||
me.url = `/api2/json/config/datastore/${me.datastore}`;
|
|
||||||
me.editorConfig = {
|
|
||||||
url: `/api2/extjs/config/datastore/${me.datastore}`,
|
|
||||||
};
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
controller: {
|
|
||||||
xclass: 'Ext.app.ViewController',
|
|
||||||
|
|
||||||
edit: function() { this.getView().run_editor(); },
|
|
||||||
|
|
||||||
garbageCollect: function() {
|
|
||||||
let me = this;
|
|
||||||
let view = me.getView();
|
|
||||||
Proxmox.Utils.API2Request({
|
|
||||||
url: `/admin/datastore/${view.datastore}/gc`,
|
|
||||||
method: 'POST',
|
|
||||||
failure: function(response) {
|
|
||||||
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
|
|
||||||
},
|
|
||||||
success: function(response, options) {
|
|
||||||
Ext.create('Proxmox.window.TaskViewer', {
|
|
||||||
upid: response.result.data,
|
|
||||||
}).show();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
tbar: [
|
|
||||||
{
|
|
||||||
xtype: 'proxmoxButton',
|
|
||||||
text: gettext('Edit'),
|
|
||||||
disabled: true,
|
|
||||||
handler: 'edit',
|
|
||||||
},
|
|
||||||
'-',
|
|
||||||
{
|
|
||||||
xtype: 'proxmoxButton',
|
|
||||||
text: gettext('Start Garbage Collection'),
|
|
||||||
selModel: null,
|
|
||||||
handler: 'garbageCollect',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
listeners: {
|
|
||||||
activate: function() { this.rstore.startUpdate(); },
|
|
||||||
destroy: function() { this.rstore.stopUpdate(); },
|
|
||||||
deactivate: function() { this.rstore.stopUpdate(); },
|
|
||||||
itemdblclick: 'edit',
|
|
||||||
},
|
|
||||||
|
|
||||||
rows: {
|
|
||||||
"gc-schedule": {
|
|
||||||
required: true,
|
|
||||||
defaultValue: Proxmox.Utils.NoneText,
|
|
||||||
header: gettext('Garbage Collection Schedule'),
|
|
||||||
editor: {
|
|
||||||
xtype: 'proxmoxWindowEdit',
|
|
||||||
title: gettext('GC Schedule'),
|
|
||||||
onlineHelp: 'maintenance_gc',
|
|
||||||
items: {
|
|
||||||
xtype: 'pbsCalendarEvent',
|
|
||||||
name: 'gc-schedule',
|
|
||||||
fieldLabel: gettext("GC Schedule"),
|
|
||||||
emptyText: Proxmox.Utils.noneText,
|
|
||||||
deleteEmpty: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Ext.define('PBS.Datastore.PruneAndGC', {
|
Ext.define('PBS.Datastore.PruneAndGC', {
|
||||||
extend: 'Ext.panel.Panel',
|
extend: 'Ext.panel.Panel',
|
||||||
alias: 'widget.pbsDatastorePruneAndGC',
|
alias: 'widget.pbsDatastorePruneAndGC',
|
||||||
|
title: gettext('Prune & GC Jobs'),
|
||||||
|
|
||||||
mixins: ['Proxmox.Mixin.CBind'],
|
mixins: ['Proxmox.Mixin.CBind'],
|
||||||
|
|
||||||
layout: {
|
layout: {
|
||||||
@ -99,9 +16,8 @@ Ext.define('PBS.Datastore.PruneAndGC', {
|
|||||||
},
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
xtype: 'pbsDatastoreGCOpts',
|
xtype: 'pbsGCJobView',
|
||||||
title: gettext('Garbage Collection'),
|
itemId: 'gcjobs',
|
||||||
itemId: 'datastore-gc',
|
|
||||||
nodename: 'localhost',
|
nodename: 'localhost',
|
||||||
cbind: {
|
cbind: {
|
||||||
datastore: '{datastore}',
|
datastore: '{datastore}',
|
||||||
@ -110,9 +26,7 @@ Ext.define('PBS.Datastore.PruneAndGC', {
|
|||||||
{
|
{
|
||||||
xtype: 'pbsPruneJobView',
|
xtype: 'pbsPruneJobView',
|
||||||
nodename: 'localhost',
|
nodename: 'localhost',
|
||||||
itemId: 'datastore-prune-jobs',
|
itemId: 'prunejobs',
|
||||||
flex: 1,
|
|
||||||
minHeight: 200,
|
|
||||||
cbind: {
|
cbind: {
|
||||||
datastore: '{datastore}',
|
datastore: '{datastore}',
|
||||||
},
|
},
|
||||||
@ -130,4 +44,9 @@ Ext.define('PBS.Datastore.PruneAndGC', {
|
|||||||
component.relayEvents(me, ['activate', 'deactivate', 'destroy']);
|
component.relayEvents(me, ['activate', 'deactivate', 'destroy']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cbindData: function(initalConfig) {
|
||||||
|
let me = this;
|
||||||
|
me.datastore = initalConfig.datastore ? initalConfig.datastore : undefined;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
28
www/window/GCJobEdit.js
Normal file
28
www/window/GCJobEdit.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Ext.define('PBS.window.GCJobEdit', {
|
||||||
|
extend: 'Proxmox.window.Edit',
|
||||||
|
alias: 'widget.pbsGCJobEdit',
|
||||||
|
mixins: ['Proxmox.Mixin.CBind'],
|
||||||
|
|
||||||
|
userid: undefined,
|
||||||
|
onlineHelp: 'maintenance_gc',
|
||||||
|
isAdd: false,
|
||||||
|
|
||||||
|
subject: gettext('Garbage Collect Schedule'),
|
||||||
|
|
||||||
|
cbindData: function(initial) {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
me.datastore = encodeURIComponent(me.datastore);
|
||||||
|
me.url = `/api2/extjs/config/datastore/${me.datastore}`;
|
||||||
|
me.method = 'PUT';
|
||||||
|
me.autoLoad = true;
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
|
||||||
|
items: {
|
||||||
|
xtype: 'pbsCalendarEvent',
|
||||||
|
name: 'gc-schedule',
|
||||||
|
fieldLabel: gettext("GC Schedule"),
|
||||||
|
emptyText: gettext(Proxmox.Utils.NoneText + " (disabled)"),
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user