defaultFocus: undefined,
- initComponent : function() {
+ initComponent: function() {
var me = this;
me.isCreate = !me.jobid;
}
var vmidField = Ext.create('Ext.form.field.Hidden', {
- name: 'vmid'
+ name: 'vmid',
});
- /*jslint confusion: true*/
// 'value' can be assigned a string or an array
- var selModeField = Ext.create('Proxmox.form.KVComboBox', {
+ var selModeField = Ext.create('Proxmox.form.KVComboBox', {
xtype: 'proxmoxKVComboBox',
comboItems: [
['include', gettext('Include selected VMs')],
['all', gettext('All')],
- ['exclude', gettext('Exclude selected VMs')]
+ ['exclude', gettext('Exclude selected VMs')],
+ ['pool', gettext('Pool based')],
],
fieldLabel: gettext('Selection mode'),
name: 'selMode',
- value: ''
+ value: '',
});
var sm = Ext.create('Ext.selection.CheckboxModel', {
vmidField.suspendEvent('change');
vmidField.setValue(sel);
vmidField.resumeEvent('change');
- }
- }
+ },
+ },
});
var storagesel = Ext.create('PVE.form.StorageSelector', {
nodename: 'localhost',
storageContent: 'backup',
allowBlank: false,
- name: 'storage'
+ name: 'storage',
+ listeners: {
+ change: function(f, v) {
+ let store = f.getStore();
+ let rec = store.findRecord('storage', v, 0, false, true, true);
+ let compressionSelector = me.down('pveCompressionSelector');
+
+ if (rec && rec.data && rec.data.type === 'pbs') {
+ compressionSelector.setValue('zstd');
+ compressionSelector.setDisabled(true);
+ } else if (!compressionSelector.getEditable()) {
+ compressionSelector.setDisabled(false);
+ }
+ },
+ },
});
var store = new Ext.data.Store({
model: 'PVEResources',
sorters: {
property: 'vmid',
- order: 'ASC'
- }
+ order: 'ASC',
+ },
});
var vmgrid = Ext.createWidget('grid', {
{
header: 'ID',
dataIndex: 'vmid',
- width: 60
+ width: 60,
},
{
header: gettext('Node'),
- dataIndex: 'node'
+ dataIndex: 'node',
},
{
header: gettext('Status'),
} else {
return Proxmox.Utils.stoppedText;
}
- }
+ },
},
{
header: gettext('Name'),
dataIndex: 'name',
- flex: 1
+ flex: 1,
},
{
header: gettext('Type'),
- dataIndex: 'type'
- }
- ]
+ dataIndex: 'type',
+ },
+ ],
+ });
+
+ var selectPoolMembers = function(poolid) {
+ if (!poolid) {
+ return;
+ }
+ sm.deselectAll(true);
+ store.filter([
+ {
+ id: 'poolFilter',
+ property: 'pool',
+ value: poolid,
+ },
+ ]);
+ sm.selectAll(true);
+ };
+
+ var selPool = Ext.create('PVE.form.PoolSelector', {
+ fieldLabel: gettext('Pool to backup'),
+ hidden: true,
+ allowBlank: true,
+ name: 'pool',
+ listeners: {
+ change: function(selpool, newValue, oldValue) {
+ selectPoolMembers(newValue);
+ },
+ },
});
var nodesel = Ext.create('PVE.form.NodeSelector', {
var mode = selModeField.getValue();
store.clearFilter();
store.filterBy(function(rec) {
- return (!value || rec.get('node') === value);
+ return !value || rec.get('node') === value;
});
if (mode === 'all') {
sm.selectAll(true);
}
- }
- }
+
+ if (mode === 'pool') {
+ selectPoolMembers(selPool.value);
+ }
+ },
+ },
});
var column1 = [
fieldLabel: gettext('Day of week'),
multiSelect: true,
value: ['sat'],
- allowBlank: false
+ allowBlank: false,
},
{
xtype: 'timefield',
format: 'H:i',
formatText: 'HH:MM',
value: '00:00',
- allowBlank: false
+ allowBlank: false,
},
- selModeField
+ selModeField,
+ selPool,
];
var column2 = [
{
xtype: 'textfield',
fieldLabel: gettext('Send email to'),
- name: 'mailto'
+ name: 'mailto',
},
{
xtype: 'pveEmailNotificationSelector',
fieldLabel: gettext('Email notification'),
name: 'mailnotification',
- deleteEmpty: me.isCreate ? false : true,
- value: me.isCreate ? 'always' : ''
+ deleteEmpty: !me.isCreate,
+ value: me.isCreate ? 'always' : '',
},
{
xtype: 'pveCompressionSelector',
fieldLabel: gettext('Compression'),
name: 'compress',
- deleteEmpty: me.isCreate ? false : true,
- value: 'lzo'
+ deleteEmpty: !me.isCreate,
+ value: 'zstd',
},
{
xtype: 'pveBackupModeSelector',
fieldLabel: gettext('Mode'),
value: 'snapshot',
- name: 'mode'
+ name: 'mode',
},
{
xtype: 'proxmoxcheckbox',
name: 'enabled',
uncheckedValue: 0,
defaultValue: 1,
- checked: true
+ checked: true,
},
- vmidField
+ vmidField,
];
- /*jslint confusion: false*/
var ipanel = Ext.create('Proxmox.panel.InputPanel', {
onlineHelp: 'chapter_vzdump',
column1: column1,
- column2: column2,
+ column2: column2,
onGetValues: function(values) {
if (!values.node) {
if (!me.isCreate) {
values.all = 1;
values.exclude = values.vmid;
delete values.vmid;
+ } else if (selMode === 'pool') {
+ delete values.vmid;
+ }
+
+ if (selMode !== 'pool') {
+ delete values.pool;
}
return values;
- }
+ },
});
var update_vmid_selection = function(list, mode) {
- if (mode !== 'all') {
+ if (mode !== 'all' && mode !== 'pool') {
sm.deselectAll(true);
if (list) {
Ext.Array.each(list.split(','), function(vmid) {
- var rec = store.findRecord('vmid', vmid);
+ var rec = store.findRecord('vmid', vmid, 0, false, true, true);
if (rec) {
sm.select(rec, true);
}
});
selModeField.on('change', function(f, value, oldValue) {
+ if (oldValue === 'pool') {
+ store.removeFilter('poolFilter');
+ }
+
+ if (oldValue === 'all') {
+ sm.deselectAll(true);
+ vmidField.setValue('');
+ }
+
if (value === 'all') {
sm.selectAll(true);
vmgrid.setDisabled(true);
} else {
vmgrid.setDisabled(false);
}
- if (oldValue === 'all') {
- sm.deselectAll(true);
+
+ if (value === 'pool') {
+ vmgrid.setDisabled(true);
vmidField.setValue('');
+ selPool.setVisible(true);
+ selPool.allowBlank = false;
+ selectPoolMembers(selPool.value);
+ } else {
+ selPool.setVisible(false);
+ selPool.allowBlank = true;
}
var list = vmidField.getValue();
update_vmid_selection(list, value);
var node = nodesel.getValue();
store.clearFilter();
store.filterBy(function(rec) {
- return (!node || node.length === 0 || rec.get('node') === node);
+ return !node || node.length === 0 || rec.get('node') === node;
});
var list = vmidField.getValue();
var mode = selModeField.getValue();
if (mode === 'all') {
sm.selectAll(true);
+ } else if (mode === 'pool') {
+ selectPoolMembers(selPool.value);
} else {
update_vmid_selection(list, mode);
}
- }
+ },
});
};
subject: gettext("Backup Job"),
url: url,
method: method,
- items: [ ipanel, vmgrid ]
+ items: [ipanel, vmgrid],
});
me.callParent();
data.vmid = '';
data.selMode = 'all';
}
+ } else if (data.pool) {
+ data.selMode = 'pool';
+ data.selPool = data.pool;
} else {
data.selMode = 'include';
}
me.setValues(data);
- }
+ },
});
}
reload();
- }
+ },
+});
+
+
+Ext.define('PVE.dc.BackupDiskTree', {
+ extend: 'Ext.tree.Panel',
+ alias: 'widget.pveBackupDiskTree',
+
+ folderSort: true,
+ rootVisible: false,
+
+ store: {
+ sorters: 'id',
+ data: {},
+ },
+
+ tools: [
+ {
+ type: 'expand',
+ tooltip: gettext('Expand All'),
+ scope: this,
+ callback: function(panel) {
+ panel.expandAll();
+ },
+ },
+ {
+ type: 'collapse',
+ tooltip: gettext('Collapse All'),
+ scope: this,
+ callback: function(panel) {
+ panel.collapseAll();
+ },
+ },
+ ],
+
+ columns: [
+ {
+ xtype: 'treecolumn',
+ text: gettext('Guest Image'),
+ renderer: function(value, meta, record) {
+ if (record.data.type) {
+ // guest level
+ let ret = value;
+ if (record.data.name) {
+ ret += " (" + record.data.name + ")";
+ }
+ return ret;
+ } else {
+ // volume level
+ // extJS needs unique IDs but we only want to show the
+ // volumes key from "vmid:key"
+ return value.split(':')[1] + " - " + record.data.name;
+ }
+ },
+ dataIndex: 'id',
+ flex: 6,
+ },
+ {
+ text: gettext('Type'),
+ dataIndex: 'type',
+ flex: 1,
+ },
+ {
+ text: gettext('Backup Job'),
+ renderer: PVE.Utils.render_backup_status,
+ dataIndex: 'included',
+ flex: 3,
+ },
+ ],
+
+ reload: function() {
+ var me = this;
+ var sm = me.getSelectionModel();
+
+ Proxmox.Utils.API2Request({
+ url: "/cluster/backup/" + me.jobid + "/included_volumes",
+ waitMsgTarget: me,
+ method: 'GET',
+ failure: function(response, opts) {
+ Proxmox.Utils.setErrorMask(me, response.htmlStatus);
+ },
+ success: function(response, opts) {
+ sm.deselectAll();
+ me.setRootNode(response.result.data);
+ me.expandAll();
+ },
+ });
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.jobid) {
+ throw "no job id specified";
+ }
+
+ var sm = Ext.create('Ext.selection.TreeModel', {});
+
+ Ext.apply(me, {
+ selModel: sm,
+ fields: ['id', 'type',
+ {
+ type: 'string',
+ name: 'iconCls',
+ calculate: function(data) {
+ var txt = 'fa x-fa-tree fa-';
+ if (data.leaf && !data.type) {
+ return txt + 'hdd-o';
+ } else if (data.type === 'qemu') {
+ return txt + 'desktop';
+ } else if (data.type === 'lxc') {
+ return txt + 'cube';
+ } else {
+ return txt + 'question-circle';
+ }
+ },
+ },
+ ],
+ header: {
+ items: [{
+ xtype: 'textfield',
+ fieldLabel: gettext('Search'),
+ labelWidth: 50,
+ emptyText: 'Name, VMID, Type',
+ width: 200,
+ padding: '0 5 0 0',
+ enableKeyEvents: true,
+ listeners: {
+ buffer: 500,
+ keyup: function(field) {
+ let searchValue = field.getValue();
+ searchValue = searchValue.toLowerCase();
+
+ me.store.clearFilter(true);
+ me.store.filterBy(function(record) {
+ let match = false;
+
+ let data = '';
+ if (record.data.depth == 0) {
+ return true;
+ } else if (record.data.depth == 1) {
+ data = record.data;
+ } else if (record.data.depth == 2) {
+ data = record.parentNode.data;
+ }
+
+ Ext.each(['name', 'id', 'type'], function(property) {
+ if (data[property] === null) {
+ return;
+ }
+
+ let v = data[property].toString();
+ if (v !== undefined) {
+ v = v.toLowerCase();
+ if (v.includes(searchValue)) {
+ match = true;
+ return;
+ }
+ }
+ });
+ return match;
+ });
+ },
+ },
+ },
+ ]
+},
+ });
+
+ me.callParent();
+
+ me.reload();
+ },
+});
+
+Ext.define('PVE.dc.BackupInfo', {
+ extend: 'Proxmox.panel.InputPanel',
+ alias: 'widget.pveBackupInfo',
+
+ padding: '5 0 5 10',
+
+ column1: [
+ {
+ name: 'node',
+ fieldLabel: gettext('Node'),
+ xtype: 'displayfield',
+ renderer: function(value) {
+ if (!value) {
+ return '-- ' + gettext('All') + ' --';
+ } else {
+ return value;
+ }
+ },
+ },
+ {
+ name: 'storage',
+ fieldLabel: gettext('Storage'),
+ xtype: 'displayfield',
+ },
+ {
+ name: 'dow',
+ fieldLabel: gettext('Day of week'),
+ xtype: 'displayfield',
+ renderer: PVE.Utils.render_backup_days_of_week,
+ },
+ {
+ name: 'starttime',
+ fieldLabel: gettext('Start Time'),
+ xtype: 'displayfield',
+ },
+ {
+ name: 'selMode',
+ fieldLabel: gettext('Selection mode'),
+ xtype: 'displayfield',
+ },
+ {
+ name: 'pool',
+ fieldLabel: gettext('Pool to backup'),
+ xtype: 'displayfield',
+ },
+ ],
+ column2: [
+ {
+ name: 'mailto',
+ fieldLabel: gettext('Send email to'),
+ xtype: 'displayfield',
+ },
+ {
+ name: 'mailnotification',
+ fieldLabel: gettext('Email notification'),
+ xtype: 'displayfield',
+ renderer: function(value) {
+ let msg;
+ switch (value) {
+ case 'always':
+ msg = gettext('Always');
+ break;
+ case 'failure':
+ msg = gettext('On failure only');
+ break;
+ }
+ return msg;
+ },
+ },
+ {
+ name: 'compress',
+ fieldLabel: gettext('Compression'),
+ xtype: 'displayfield',
+ },
+ {
+ name: 'mode',
+ fieldLabel: gettext('Mode'),
+ xtype: 'displayfield',
+ renderer: function(value) {
+ let msg;
+ switch (value) {
+ case 'snapshot':
+ msg = gettext('Snapshot');
+ break;
+ case 'suspend':
+ msg = gettext('Suspend');
+ break;
+ case 'stop':
+ msg = gettext('Stop');
+ break;
+ }
+ return msg;
+ },
+ },
+ {
+ name: 'enabled',
+ fieldLabel: gettext('Enabled'),
+ xtype: 'displayfield',
+ renderer: function(value) {
+ if (PVE.Parser.parseBoolean(value.toString())) {
+ return gettext('Yes');
+ } else {
+ return gettext('No');
+ }
+ },
+ },
+ ],
+
+ setValues: function(values) {
+ var me = this;
+
+ Ext.iterate(values, function(fieldId, val) {
+ let field = me.query('[isFormField][name=' + fieldId + ']')[0];
+ if (field) {
+ field.setValue(val);
+ }
+ });
+
+ // selection Mode depends on the presence/absence of several keys
+ let selModeField = me.query('[isFormField][name=selMode]')[0];
+ let selMode = 'none';
+ if (values.vmid) {
+ selMode = gettext('Include selected VMs');
+ }
+ if (values.all) {
+ selMode = gettext('All');
+ }
+ if (values.exclude) {
+ selMode = gettext('Exclude selected VMs');
+ }
+ if (values.pool) {
+ selMode = gettext('Pool based');
+ }
+ selModeField.setValue(selMode);
+
+ if (!values.pool) {
+ let poolField = me.query('[isFormField][name=pool]')[0];
+ poolField.setVisible(0);
+ }
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.record) {
+ throw "no data provided";
+ }
+ me.callParent();
+
+ me.setValues(me.record);
+ },
});
+Ext.define('PVE.dc.BackedGuests', {
+ extend: 'Ext.grid.GridPanel',
+ alias: 'widget.pveBackedGuests',
+
+ textfilter: '',
+
+ columns: [
+ {
+ header: gettext('Type'),
+ dataIndex: "type",
+ renderer: PVE.Utils.render_resource_type,
+ flex: 1,
+ sortable: true,
+ },
+ {
+ header: gettext('VMID'),
+ dataIndex: 'vmid',
+ flex: 1,
+ sortable: true,
+ },
+ {
+ header: gettext('Name'),
+ dataIndex: 'name',
+ flex: 2,
+ sortable: true,
+ },
+ ],
+
+ initComponent: function() {
+ let me = this;
+
+ me.store.clearFilter(true);
+
+ Ext.apply(me, {
+ stateful: true,
+ stateId: 'grid-dc-backed-guests',
+ tbar: [
+ '->',
+ gettext('Search') + ':', ' ',
+ {
+ xtype: 'textfield',
+ width: 200,
+ emptyText: 'Name, VMID, Type',
+ enableKeyEvents: true,
+ listeners: {
+ buffer: 500,
+ keyup: function(field) {
+ let searchValue = field.getValue();
+ searchValue = searchValue.toLowerCase();
+
+ me.store.clearFilter(true);
+ me.store.filterBy(function(record) {
+ let match = false;
+
+ Ext.each(['name', 'vmid', 'type'], function(property) {
+ if (record.data[property] == null) {
+ return;
+ }
+
+ let v = record.data[property].toString();
+ if (v !== undefined) {
+ v = v.toLowerCase();
+ if (v.includes(searchValue)) {
+ match = true;
+ return;
+ }
+ }
+ });
+ return match;
+ });
+ },
+ },
+ },
+ ],
+ viewConfig: {
+ stripeRows: true,
+ trackOver: false,
+ },
+ });
+ me.callParent();
+ },
+});
+
Ext.define('PVE.dc.BackupView', {
extend: 'Ext.grid.GridPanel',
onlineHelp: 'chapter_vzdump',
allText: '-- ' + gettext('All') + ' --',
- allExceptText: gettext('All except {0}'),
- initComponent : function() {
+ initComponent: function() {
var me = this;
var store = new Ext.data.Store({
model: 'pve-cluster-backup',
proxy: {
type: 'proxmox',
- url: "/api2/json/cluster/backup"
- }
+ url: "/api2/json/cluster/backup",
+ },
+ });
+
+ var not_backed_store = new Ext.data.Store({
+ sorters: 'vmid',
+ proxy: {
+ type: 'proxmox',
+ url: 'api2/json/cluster/backupinfo/not_backed_up',
+ },
});
var reload = function() {
store.load();
+ not_backed_store.load({
+ callback: function(records, operation, success) {
+ if (records.length) {
+ not_backed_warning.setVisible(true);
+ not_backed_btn.setVisible(true);
+ } else {
+ not_backed_warning.setVisible(false);
+ not_backed_btn.setVisible(false);
+ }
+ },
+ });
};
var sm = Ext.create('Ext.selection.RowModel', {});
return;
}
- var win = Ext.create('PVE.dc.BackupEdit',{
- jobid: rec.data.id
- });
- win.on('destroy', reload);
- win.show();
+ var win = Ext.create('PVE.dc.BackupEdit', {
+ jobid: rec.data.id,
+ });
+ win.on('destroy', reload);
+ win.show();
+ };
+
+ var run_detail = function() {
+ let me = this;
+ let record = sm.getSelection()[0];
+ if (!record) {
+ return;
+ }
+ let infoview = Ext.create('PVE.dc.BackupInfo', {
+ flex: 0,
+ layout: 'fit',
+ record: record.data,
+ });
+ let disktree = Ext.create('PVE.dc.BackupDiskTree', {
+ title: gettext('Included disks'),
+ flex: 1,
+ jobid: record.data.id,
+ });
+
+ Ext.create('Ext.window.Window', {
+ modal: true,
+ width: 800,
+ height: 600,
+ stateful: true,
+ stateId: 'backup-detail-view',
+ resizable: true,
+ layout: 'fit',
+ title: gettext('Backup Details'),
+
+ items: [{
+ xtype: 'panel',
+ region: 'center',
+ layout: {
+ type: 'vbox',
+ align: 'stretch',
+ },
+ items: [infoview, disktree],
+ }],
+ }).show();
+ };
+
+ var run_backup_now = function(job) {
+ job = Ext.clone(job);
+
+ let jobNode = job.node;
+ // Remove properties related to scheduling
+ delete job.enabled;
+ delete job.starttime;
+ delete job.dow;
+ delete job.id;
+ delete job.node;
+ job.all = job.all === true ? 1 : 0;
+
+ let allNodes = PVE.data.ResourceStore.getNodes();
+ let nodes = allNodes.filter(node => node.status === 'online').map(node => node.node);
+ let errors = [];
+
+ if (jobNode !== undefined) {
+ if (!nodes.includes(jobNode)) {
+ Ext.Msg.alert('Error', "Node '"+ jobNode +"' from backup job isn't online!");
+ return;
+ }
+ nodes = [jobNode];
+ } else {
+ let unkownNodes = allNodes.filter(node => node.status !== 'online');
+ if (unkownNodes.length > 0)
+ errors.push(unkownNodes.map(node => node.node + ": " + gettext("Node is offline")));
+ }
+ let jobTotalCount = nodes.length, jobsStarted = 0;
+
+ Ext.Msg.show({
+ title: gettext('Please wait...'),
+ closable: false,
+ progress: true,
+ progressText: '0/' + jobTotalCount,
+ });
+
+ let postRequest = function() {
+ jobsStarted++;
+ Ext.Msg.updateProgress(jobsStarted / jobTotalCount, jobsStarted + '/' + jobTotalCount);
+
+ if (jobsStarted == jobTotalCount) {
+ Ext.Msg.hide();
+ if (errors.length > 0) {
+ Ext.Msg.alert('Error', 'Some errors have been encountered:<br />' + errors.join('<br />'));
+ }
+ }
+ };
+
+ nodes.forEach(node => Proxmox.Utils.API2Request({
+ url: '/nodes/' + node + '/vzdump',
+ method: 'POST',
+ params: job,
+ failure: function(response, opts) {
+ errors.push(node + ': ' + response.htmlStatus);
+ postRequest();
+ },
+ success: postRequest,
+ }));
+ };
+
+ var run_show_not_backed = function() {
+ var me = this;
+ var backedinfo = Ext.create('PVE.dc.BackedGuests', {
+ flex: 1,
+ layout: 'fit',
+ store: not_backed_store,
+ });
+
+ var win = Ext.create('Ext.window.Window', {
+ modal: true,
+ width: 600,
+ height: 500,
+ resizable: true,
+ layout: 'fit',
+ title: gettext('Guests without backup job'),
+
+ items: [{
+ xtype: 'panel',
+ region: 'center',
+ layout: {
+ type: 'vbox',
+ align: 'stretch',
+ },
+ items: [backedinfo],
+ }],
+ }).show();
};
var edit_btn = new Proxmox.button.Button({
text: gettext('Edit'),
disabled: true,
selModel: sm,
- handler: run_editor
+ handler: run_editor,
+ });
+
+ var run_btn = new Proxmox.button.Button({
+ text: gettext('Run now'),
+ disabled: true,
+ selModel: sm,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ if (!rec) {
+ return;
+ }
+
+ Ext.Msg.show({
+ title: gettext('Confirm'),
+ icon: Ext.Msg.QUESTION,
+ msg: gettext('Start the selected backup job now?'),
+ buttons: Ext.Msg.YESNO,
+ callback: function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+ run_backup_now(rec.data);
+ },
+ });
+ },
});
var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', {
baseurl: '/cluster/backup',
callback: function() {
reload();
- }
+ },
+ });
+
+ var detail_btn = new Proxmox.button.Button({
+ text: gettext('Job Detail'),
+ disabled: true,
+ tooltip: gettext('Show job details and which guests and volumes are affected by the backup job'),
+ selModel: sm,
+ handler: run_detail,
+ });
+
+ var not_backed_warning = Ext.create('Ext.toolbar.TextItem', {
+ html: '<i class="fa fa-fw fa-exclamation-circle"></i>' + gettext('Some guests are not covered by any backup job.'),
+ hidden: true,
+ });
+
+ var not_backed_btn = new Proxmox.button.Button({
+ text: gettext('Show'),
+ hidden: true,
+ handler: run_show_not_backed,
});
Proxmox.Utils.monStoreErrors(me, store);
stateful: true,
stateId: 'grid-dc-backup',
viewConfig: {
- trackOver: false
+ trackOver: false,
},
tbar: [
{
text: gettext('Add'),
handler: function() {
- var win = Ext.create('PVE.dc.BackupEdit',{});
+ var win = Ext.create('PVE.dc.BackupEdit', {});
win.on('destroy', reload);
win.show();
- }
+ },
},
+ '-',
remove_btn,
- edit_btn
+ edit_btn,
+ detail_btn,
+ '-',
+ run_btn,
+ '->',
+ not_backed_warning,
+ not_backed_btn,
],
columns: [
{
sortable: true,
disabled: true,
disabledCls: 'x-item-enabled',
- stopSelection: false
+ stopSelection: false,
},
{
header: gettext('Node'),
return value;
}
return me.allText;
- }
+ },
},
{
header: gettext('Day of week'),
width: 200,
sortable: false,
dataIndex: 'dow',
- renderer: function(val) {
- var dows = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
- var selected = [];
- var cur = -1;
- val.split(',').forEach(function(day){
- cur++;
- var dow = (dows.indexOf(day)+6)%7;
- if (cur === dow) {
- if (selected.length === 0 || selected[selected.length-1] === 0) {
- selected.push(1);
- } else {
- selected[selected.length-1]++;
- }
- } else {
- while (cur < dow) {
- cur++;
- selected.push(0);
- }
- selected.push(1);
- }
- });
-
- cur = -1;
- var days = [];
- selected.forEach(function(item) {
- cur++;
- if (item > 2) {
- days.push(Ext.Date.dayNames[(cur+1)] + '-' + Ext.Date.dayNames[(cur+item)%7]);
- cur += item-1;
- } else if (item == 2) {
- days.push(Ext.Date.dayNames[cur+1]);
- days.push(Ext.Date.dayNames[(cur+2)%7]);
- cur++;
- } else if (item == 1) {
- days.push(Ext.Date.dayNames[(cur+1)%7]);
- }
- });
- return days.join(', ');
- }
+ renderer: PVE.Utils.render_backup_days_of_week,
},
{
header: gettext('Start Time'),
width: 60,
sortable: true,
- dataIndex: 'starttime'
+ dataIndex: 'starttime',
},
{
header: gettext('Storage'),
width: 100,
sortable: true,
- dataIndex: 'storage'
+ dataIndex: 'storage',
},
{
header: gettext('Selection'),
flex: 1,
sortable: false,
dataIndex: 'vmid',
- renderer: function(value, metaData, record) {
- /*jslint confusion: true */
- if (record.data.all) {
- if (record.data.exclude) {
- return Ext.String.format(me.allExceptText, record.data.exclude);
- }
- return me.allText;
- }
- if (record.data.vmid) {
- return record.data.vmid;
- }
-
- return "-";
- }
- }
+ renderer: PVE.Utils.render_backup_selection,
+ },
],
listeners: {
activate: reload,
- itemdblclick: run_editor
- }
+ itemdblclick: run_editor,
+ },
});
me.callParent();
- }
+ },
}, function() {
-
Ext.define('pve-cluster-backup', {
extend: 'Ext.data.Model',
fields: [
'id', 'starttime', 'dow',
'storage', 'node', 'vmid', 'exclude',
- 'mailto',
+ 'mailto', 'pool', 'compress', 'mode',
{ name: 'enabled', type: 'boolean' },
{ name: 'all', type: 'boolean' },
- { name: 'snapshot', type: 'boolean' },
- { name: 'stop', type: 'boolean' },
- { name: 'suspend', type: 'boolean' },
- { name: 'compress', type: 'boolean' }
- ]
+ ],
});
});