2021-02-02 13:00:38 +00:00
|
|
|
Ext.define('PBS.TapeManagement.TapeRestoreWindow', {
|
|
|
|
extend: 'Proxmox.window.Edit',
|
2021-03-25 10:04:40 +00:00
|
|
|
alias: 'widget.pbsTapeRestoreWindow',
|
2021-02-02 13:00:38 +00:00
|
|
|
mixins: ['Proxmox.Mixin.CBind'],
|
|
|
|
|
2021-03-25 10:04:40 +00:00
|
|
|
width: 800,
|
2021-02-02 13:00:38 +00:00
|
|
|
title: gettext('Restore Media Set'),
|
2021-05-12 12:08:12 +00:00
|
|
|
submitText: gettext('Restore'),
|
2021-02-02 13:00:38 +00:00
|
|
|
url: '/api2/extjs/tape/restore',
|
|
|
|
method: 'POST',
|
|
|
|
showTaskViewer: true,
|
|
|
|
isCreate: true,
|
|
|
|
|
2021-05-11 10:50:07 +00:00
|
|
|
cbindData: function(config) {
|
|
|
|
let me = this;
|
|
|
|
me.isSingle = false;
|
|
|
|
me.listText = "";
|
|
|
|
if (me.list !== undefined) {
|
|
|
|
me.isSingle = true;
|
|
|
|
me.listText = me.list.join('<br>');
|
|
|
|
me.title = gettext('Restore Snapshot');
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
},
|
|
|
|
|
2021-02-02 13:00:38 +00:00
|
|
|
defaults: {
|
|
|
|
labelWidth: 120,
|
|
|
|
},
|
|
|
|
|
2021-03-25 10:04:40 +00:00
|
|
|
referenceHolder: true,
|
|
|
|
|
2021-02-02 13:00:38 +00:00
|
|
|
items: [
|
|
|
|
{
|
2021-03-25 10:04:40 +00:00
|
|
|
xtype: 'inputpanel',
|
|
|
|
|
|
|
|
onGetValues: function(values) {
|
|
|
|
let me = this;
|
|
|
|
let datastores = [];
|
2021-03-29 11:25:37 +00:00
|
|
|
if (values.store.toString() !== "") {
|
2021-03-25 10:04:40 +00:00
|
|
|
datastores.push(values.store);
|
|
|
|
delete values.store;
|
|
|
|
}
|
|
|
|
|
2021-03-29 11:25:37 +00:00
|
|
|
if (values.mapping.toString() !== "") {
|
2021-03-25 10:04:40 +00:00
|
|
|
datastores.push(values.mapping);
|
|
|
|
}
|
2021-03-29 11:25:36 +00:00
|
|
|
delete values.mapping;
|
2021-03-25 10:04:40 +00:00
|
|
|
|
2021-05-11 10:50:07 +00:00
|
|
|
if (me.up('window').list !== undefined) {
|
|
|
|
values.snapshots = me.up('window').list;
|
|
|
|
}
|
|
|
|
|
2021-03-25 10:04:40 +00:00
|
|
|
values.store = datastores.join(',');
|
|
|
|
|
|
|
|
return values;
|
2021-02-02 13:00:38 +00:00
|
|
|
},
|
2021-03-25 10:04:40 +00:00
|
|
|
|
|
|
|
column1: [
|
|
|
|
{
|
|
|
|
xtype: 'displayfield',
|
|
|
|
fieldLabel: gettext('Media Set'),
|
|
|
|
cbind: {
|
|
|
|
value: '{mediaset}',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xtype: 'displayfield',
|
|
|
|
fieldLabel: gettext('Media Set UUID'),
|
|
|
|
name: 'media-set',
|
|
|
|
submitValue: true,
|
|
|
|
cbind: {
|
|
|
|
value: '{uuid}',
|
|
|
|
},
|
|
|
|
},
|
2021-05-11 10:50:07 +00:00
|
|
|
{
|
|
|
|
xtype: 'displayfield',
|
|
|
|
fieldLabel: gettext('Snapshot(s)'),
|
|
|
|
submitValue: false,
|
|
|
|
cbind: {
|
|
|
|
hidden: '{!isSingle}',
|
|
|
|
value: '{listText}',
|
|
|
|
},
|
|
|
|
},
|
2021-03-25 10:04:40 +00:00
|
|
|
{
|
|
|
|
xtype: 'pbsDriveSelector',
|
|
|
|
fieldLabel: gettext('Drive'),
|
|
|
|
name: 'drive',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
column2: [
|
|
|
|
{
|
|
|
|
xtype: 'pbsUserSelector',
|
|
|
|
name: 'notify-user',
|
|
|
|
fieldLabel: gettext('Notify User'),
|
|
|
|
emptyText: gettext('Current User'),
|
|
|
|
value: null,
|
|
|
|
allowBlank: true,
|
|
|
|
skipEmptyText: true,
|
|
|
|
renderer: Ext.String.htmlEncode,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xtype: 'pbsUserSelector',
|
|
|
|
name: 'owner',
|
|
|
|
fieldLabel: gettext('Owner'),
|
|
|
|
emptyText: gettext('Current User'),
|
|
|
|
value: null,
|
|
|
|
allowBlank: true,
|
|
|
|
skipEmptyText: true,
|
|
|
|
renderer: Ext.String.htmlEncode,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xtype: 'pbsDataStoreSelector',
|
2021-05-10 11:19:46 +00:00
|
|
|
fieldLabel: gettext('Target Datastore'),
|
2021-03-25 10:04:40 +00:00
|
|
|
reference: 'defaultDatastore',
|
|
|
|
name: 'store',
|
|
|
|
listeners: {
|
|
|
|
change: function(field, value) {
|
|
|
|
let me = this;
|
|
|
|
let grid = me.up('window').lookup('mappingGrid');
|
|
|
|
grid.setNeedStores(!value);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
columnB: [
|
|
|
|
{
|
|
|
|
fieldLabel: gettext('Datastore Mapping'),
|
|
|
|
labelWidth: 200,
|
|
|
|
hidden: true,
|
|
|
|
reference: 'mappingLabel',
|
|
|
|
xtype: 'displayfield',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
xtype: 'pbsDataStoreMappingField',
|
|
|
|
reference: 'mappingGrid',
|
|
|
|
name: 'mapping',
|
|
|
|
defaultBindProperty: 'value',
|
|
|
|
hidden: true,
|
|
|
|
},
|
|
|
|
],
|
2021-02-02 13:00:38 +00:00
|
|
|
},
|
2021-03-25 10:04:40 +00:00
|
|
|
],
|
|
|
|
|
|
|
|
setDataStores: function(datastores) {
|
|
|
|
let me = this;
|
|
|
|
|
|
|
|
let label = me.lookup('mappingLabel');
|
|
|
|
let grid = me.lookup('mappingGrid');
|
|
|
|
let defaultField = me.lookup('defaultDatastore');
|
|
|
|
|
|
|
|
if (!datastores || datastores.length <= 1) {
|
|
|
|
label.setVisible(false);
|
|
|
|
grid.setVisible(false);
|
2021-05-10 11:19:46 +00:00
|
|
|
defaultField.setFieldLabel(gettext('Target Datastore'));
|
2021-03-25 10:04:40 +00:00
|
|
|
defaultField.setAllowBlank(false);
|
|
|
|
defaultField.setEmptyText("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
label.setVisible(true);
|
|
|
|
defaultField.setFieldLabel(gettext('Default Datastore'));
|
|
|
|
defaultField.setAllowBlank(true);
|
|
|
|
defaultField.setEmptyText(Proxmox.Utils.NoneText);
|
|
|
|
|
|
|
|
grid.setDataStores(datastores);
|
|
|
|
grid.setVisible(true);
|
|
|
|
},
|
|
|
|
|
|
|
|
initComponent: function() {
|
|
|
|
let me = this;
|
|
|
|
|
|
|
|
me.callParent();
|
|
|
|
if (me.datastores) {
|
|
|
|
me.setDataStores(me.datastores);
|
|
|
|
} else {
|
|
|
|
// use timeout so that the window is rendered already
|
|
|
|
// for correct masking
|
|
|
|
setTimeout(function() {
|
|
|
|
Proxmox.Utils.API2Request({
|
|
|
|
waitMsgTarget: me,
|
|
|
|
url: `/tape/media/content?media-set=${me.uuid}`,
|
|
|
|
success: function(response, opt) {
|
|
|
|
let datastores = {};
|
|
|
|
for (const content of response.result.data) {
|
|
|
|
datastores[content.store] = true;
|
|
|
|
}
|
|
|
|
me.setDataStores(Object.keys(datastores));
|
|
|
|
},
|
|
|
|
failure: function() {
|
|
|
|
// ignore failing api call, maybe catalog is missing
|
|
|
|
me.setDataStores();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}, 10);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
Ext.define('PBS.TapeManagement.DataStoreMappingGrid', {
|
|
|
|
extend: 'Ext.grid.Panel',
|
|
|
|
alias: 'widget.pbsDataStoreMappingField',
|
|
|
|
mixins: ['Ext.form.field.Field'],
|
|
|
|
|
2021-05-21 10:20:18 +00:00
|
|
|
scrollable: true,
|
|
|
|
|
2021-03-25 10:04:40 +00:00
|
|
|
getValue: function() {
|
|
|
|
let me = this;
|
|
|
|
let datastores = [];
|
|
|
|
me.getStore().each((rec) => {
|
|
|
|
let source = rec.data.source;
|
|
|
|
let target = rec.data.target;
|
|
|
|
if (target && target !== "") {
|
|
|
|
datastores.push(`${source}=${target}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return datastores.join(',');
|
|
|
|
},
|
|
|
|
|
|
|
|
// this determines if we need at least one valid mapping
|
|
|
|
needStores: false,
|
|
|
|
|
|
|
|
setNeedStores: function(needStores) {
|
|
|
|
let me = this;
|
|
|
|
me.needStores = needStores;
|
|
|
|
me.checkChange();
|
|
|
|
me.validate();
|
|
|
|
},
|
|
|
|
|
|
|
|
setValue: function(value) {
|
|
|
|
let me = this;
|
|
|
|
me.setDataStores(value);
|
|
|
|
return me;
|
|
|
|
},
|
|
|
|
|
|
|
|
getErrors: function(value) {
|
|
|
|
let me = this;
|
|
|
|
let error = false;
|
|
|
|
|
|
|
|
if (me.needStores) {
|
|
|
|
error = true;
|
|
|
|
me.getStore().each((rec) => {
|
|
|
|
if (rec.data.target) {
|
|
|
|
error = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-05-21 10:20:18 +00:00
|
|
|
let el = me.getActionEl();
|
2021-03-25 10:04:40 +00:00
|
|
|
if (error) {
|
|
|
|
me.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
|
|
|
|
let errorMsg = gettext("Need at least one mapping");
|
2021-05-21 10:20:18 +00:00
|
|
|
if (el) {
|
|
|
|
el.dom.setAttribute('data-errorqtip', errorMsg);
|
|
|
|
}
|
2021-03-25 10:04:40 +00:00
|
|
|
|
|
|
|
return [errorMsg];
|
|
|
|
}
|
|
|
|
me.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
|
2021-05-21 10:20:18 +00:00
|
|
|
if (el) {
|
|
|
|
el.dom.setAttribute('data-errorqtip', "");
|
|
|
|
}
|
2021-03-25 10:04:40 +00:00
|
|
|
return [];
|
|
|
|
},
|
|
|
|
|
|
|
|
setDataStores: function(datastores) {
|
|
|
|
let me = this;
|
|
|
|
let store = me.getStore();
|
|
|
|
let data = [];
|
|
|
|
|
|
|
|
for (const datastore of datastores) {
|
|
|
|
data.push({
|
|
|
|
source: datastore,
|
|
|
|
target: '',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
store.setData(data);
|
|
|
|
},
|
|
|
|
|
|
|
|
viewConfig: {
|
|
|
|
markDirty: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
store: { data: [] },
|
|
|
|
|
|
|
|
columns: [
|
2021-02-02 13:00:38 +00:00
|
|
|
{
|
2021-03-25 10:04:40 +00:00
|
|
|
text: gettext('Source Datastore'),
|
|
|
|
dataIndex: 'source',
|
|
|
|
flex: 1,
|
2021-03-05 08:45:32 +00:00
|
|
|
},
|
2021-03-12 10:28:41 +00:00
|
|
|
{
|
2021-03-25 10:04:40 +00:00
|
|
|
text: gettext('Target Datastore'),
|
|
|
|
xtype: 'widgetcolumn',
|
|
|
|
dataIndex: 'target',
|
|
|
|
flex: 1,
|
|
|
|
widget: {
|
|
|
|
xtype: 'pbsDataStoreSelector',
|
|
|
|
allowBlank: true,
|
|
|
|
emptyText: Proxmox.Utils.NoneText,
|
|
|
|
listeners: {
|
|
|
|
change: function(selector, value) {
|
|
|
|
let me = this;
|
|
|
|
let rec = me.getWidgetRecord();
|
|
|
|
if (!rec) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rec.set('target', value);
|
|
|
|
me.up('grid').checkChange();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-03-12 10:28:41 +00:00
|
|
|
},
|
2021-02-02 13:00:38 +00:00
|
|
|
],
|
|
|
|
});
|
2021-05-14 12:59:21 +00:00
|
|
|
|
|
|
|
Ext.define('PBS.TapeManagement.SnapshotGrid', {
|
|
|
|
extend: 'Ext.grid.Panel',
|
|
|
|
alias: 'widget.pbsTapeSnapshotGrid',
|
|
|
|
mixins: ['Ext.form.field.Field'],
|
|
|
|
|
|
|
|
getValue: function() {
|
|
|
|
let me = this;
|
|
|
|
let snapshots = [];
|
|
|
|
|
|
|
|
me.getSelection().forEach((rec) => {
|
|
|
|
let id = rec.get('id');
|
|
|
|
let store = rec.data.store;
|
|
|
|
let snap = rec.data.snapshot;
|
|
|
|
// only add if not filtered
|
|
|
|
if (me.store.findExact('id', id) !== -1) {
|
|
|
|
snapshots.push(`${store}:${snap}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2021-05-21 10:20:19 +00:00
|
|
|
// getSource returns null if data is not filtered
|
|
|
|
let originalData = me.store.getData().getSource() || me.store.getData();
|
|
|
|
|
|
|
|
if (snapshots.length === originalData.length) {
|
|
|
|
return "all";
|
|
|
|
}
|
|
|
|
|
2021-05-14 12:59:21 +00:00
|
|
|
return snapshots;
|
|
|
|
},
|
|
|
|
|
|
|
|
setValue: function(value) {
|
|
|
|
let me = this;
|
|
|
|
// not implemented
|
|
|
|
return me;
|
|
|
|
},
|
|
|
|
|
|
|
|
getErrors: function(value) {
|
|
|
|
let me = this;
|
2021-05-21 10:20:19 +00:00
|
|
|
if (me.getSelection().length < 1) {
|
2021-05-14 12:59:21 +00:00
|
|
|
me.addCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
|
|
|
|
let errorMsg = gettext("Need at least one snapshot");
|
2021-05-21 10:20:19 +00:00
|
|
|
let el = me.getActionEl();
|
|
|
|
if (el) {
|
|
|
|
el.dom.setAttribute('data-errorqtip', errorMsg);
|
|
|
|
}
|
2021-05-14 12:59:21 +00:00
|
|
|
|
|
|
|
return [errorMsg];
|
|
|
|
}
|
|
|
|
me.removeCls(['x-form-trigger-wrap-default', 'x-form-trigger-wrap-invalid']);
|
2021-05-21 10:20:19 +00:00
|
|
|
let el = me.getActionEl();
|
|
|
|
if (el) {
|
|
|
|
el.dom.setAttribute('data-errorqtip', "");
|
|
|
|
}
|
2021-05-14 12:59:21 +00:00
|
|
|
return [];
|
|
|
|
},
|
|
|
|
|
|
|
|
scrollable: true,
|
|
|
|
plugins: 'gridfilters',
|
|
|
|
|
|
|
|
viewConfig: {
|
|
|
|
emptyText: gettext('No Snapshots'),
|
|
|
|
markDirty: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
selModel: 'checkboxmodel',
|
|
|
|
store: {
|
|
|
|
sorters: ['store', 'snapshot'],
|
|
|
|
data: [],
|
|
|
|
filters: [],
|
|
|
|
},
|
|
|
|
|
|
|
|
listeners: {
|
|
|
|
selectionchange: function() {
|
|
|
|
// to trigger validity and error checks
|
|
|
|
this.checkChange();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
checkChangeEvents: [
|
|
|
|
'selectionchange',
|
|
|
|
'change',
|
|
|
|
],
|
|
|
|
|
|
|
|
columns: [
|
|
|
|
{
|
|
|
|
text: gettext('Source Datastore'),
|
|
|
|
dataIndex: 'store',
|
|
|
|
filter: {
|
|
|
|
type: 'list',
|
|
|
|
},
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: gettext('Snapshot'),
|
|
|
|
dataIndex: 'snapshot',
|
|
|
|
filter: {
|
|
|
|
type: 'string',
|
|
|
|
},
|
|
|
|
flex: 2,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
initComponent: function() {
|
|
|
|
let me = this;
|
|
|
|
me.callParent();
|
|
|
|
if (me.prefilter !== undefined) {
|
|
|
|
me.store.filters.add(
|
|
|
|
{
|
|
|
|
id: 'x-gridfilter-store',
|
|
|
|
property: 'store',
|
|
|
|
operator: 'in',
|
|
|
|
value: [me.prefilter.store],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'x-gridfilter-snapshot',
|
|
|
|
property: 'snapshot',
|
|
|
|
value: me.prefilter.snapshot,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2021-05-21 10:20:19 +00:00
|
|
|
|
|
|
|
me.mon(me.store, 'filterchange', () => me.checkChange());
|
2021-05-14 12:59:21 +00:00
|
|
|
},
|
|
|
|
});
|