pve-manager/www/manager6/qemu/Clone.js
Emmanuel Kasper 26693bb937 Disable the submit button based on whole form validity
Until now we disabled the submit button, based on
the feature API call where we tested if the selected VM, and its snapshot
can be used as the source of a clone.

This had the following problems:
 * the feature test for copy clone / linked clone always returned true,
 since the passed paramaters were already filtered before user input
 (ie we only allowed a linked clone to be selected if the source VM is
 a template, or a snasphot to be selected if existed on source VM)
 * the guest ID input field was not validated

With the current patch, the validation is now made on each of the form
fields. verifyFeature() is still used to populate a list of valid nodes
passed to the Node Selector.
2017-01-12 10:40:22 +01:00

321 lines
7.2 KiB
JavaScript

Ext.define('PVE.window.Clone', {
extend: 'Ext.window.Window',
resizable: false,
isTemplate: false,
onlineHelp: 'qm_copy_and_clone',
controller: {
xclass: 'Ext.app.ViewController',
control: {
'panel[reference=cloneform]': {
validitychange: 'disableSubmit'
}
},
disableSubmit: function(form) {
this.lookupReference('submitBtn').setDisabled(!form.isValid());
}
},
create_clone: function(values) {
var me = this;
var params = { newid: values.newvmid };
if (values.snapname && values.snapname !== 'current') {
params.snapname = values.snapname;
}
if (values.pool) {
params.pool = values.pool;
}
if (values.name) {
params.name = values.name;
}
if (values.target) {
params.target = values.target;
}
if (values.clonemode === 'copy') {
params.full = 1;
if (values.storage) {
params.storage = values.storage;
if (values.diskformat) {
params.format = values.diskformat;
}
}
}
PVE.Utils.API2Request({
params: params,
url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/clone',
waitMsgTarget: me,
method: 'POST',
failure: function(response, opts) {
Ext.Msg.alert('Error', response.htmlStatus);
},
success: function(response, options) {
me.close();
}
});
},
// disable the Storage selector when clone mode is linked clone
// disable the disk Format selector when
// A) clone mode is linked clone
// B) clone mode is full clone and storage is block device
updateVisibility: function() {
var me = this;
var clonemode = me.cloneModeSel.getValue();
var storage = me.hdstoragesel.getValue();
var rec = me.hdstoragesel.store.getById(storage);
me.hdstoragesel.setDisabled(clonemode === 'clone');
if (!rec || clonemode === 'clone') {
me.formatsel.setDisabled(true);
return;
}
if (rec.data.type === 'lvm' ||
rec.data.type === 'lvmthin' ||
rec.data.type === 'rbd' ||
rec.data.type === 'iscsi' ||
rec.data.type === 'sheepdog' ||
rec.data.type === 'zfs' ||
rec.data.type === 'zfspool'
) {
me.formatsel.setValue('raw');
me.formatsel.setDisabled(true);
} else {
me.formatsel.setDisabled(false);
}
},
// add to the list of valid nodes each node where
// all the VM disks are available
verifyFeature: function() {
var me = this;
var snapname = me.snapshotSel.getValue();
var clonemode = me.cloneModeSel.getValue();
var params = { feature: clonemode };
if (snapname !== 'current') {
params.snapname = snapname;
}
PVE.Utils.API2Request({
waitMsgTarget: me,
url: '/nodes/' + me.nodename + '/qemu/' + me.vmid + '/feature',
params: params,
method: 'GET',
failure: function(response, opts) {
me.submitBtn.setDisabled(false);
Ext.Msg.alert('Error', response.htmlStatus);
},
success: function(response, options) {
var res = response.result.data;
me.targetSel.allowedNodes = res.nodes;
me.targetSel.validate();
}
});
},
initComponent : function() {
var me = this;
if (!me.nodename) {
throw "no node name specified";
}
if (!me.vmid) {
throw "no VM ID specified";
}
if (!me.snapname) {
me.snapname = 'current';
}
var col1 = [];
var col2 = [];
me.targetSel = Ext.create('PVE.form.NodeSelector', {
name: 'target',
fieldLabel: gettext('Target node'),
selectCurNode: true,
allowBlank: false,
onlineValidator: true
});
col1.push(me.targetSel);
var modelist = [['copy', gettext('Full Clone')]];
if (me.isTemplate) {
modelist.push(['clone', gettext('Linked Clone')]);
}
me.cloneModeSel = Ext.create('PVE.form.KVComboBox', {
fieldLabel: gettext('Mode'),
name: 'clonemode',
allowBlank: false,
value: me.isTemplate ? 'clone' : 'copy',
comboItems: modelist
});
me.mon(me.cloneModeSel, 'change', function(t, value) {
me.updateVisibility();
me.verifyFeature();
});
col2.push(me.cloneModeSel);
me.snapshotSel = Ext.create('PVE.form.SnapshotSelector', {
name: 'snapname',
fieldLabel: gettext('Snapshot'),
nodename: me.nodename,
vmid: me.vmid,
hidden: me.isTemplate ? true : false,
disabled: false,
allowBlank: false,
value : me.snapname,
listeners: {
change: function(f, value) {
// current selected snaphshot has maybe local disks
me.verifyFeature();
}
}
});
col2.push(me.snapshotSel);
col1.push(
{
xtype: 'pveGuestIDSelector',
name: 'newvmid',
guestType: 'qemu',
value: '',
loadNextFreeID: true,
validateExists: false
},
{
xtype: 'textfield',
name: 'name',
allowBlank: true,
fieldLabel: gettext('Name')
}
);
me.hdstoragesel = Ext.create('PVE.form.StorageSelector', {
name: 'storage',
nodename: me.nodename,
fieldLabel: gettext('Target Storage'),
storageContent: 'images',
autoSelect: me.insideWizard,
allowBlank: true,
disabled: me.isTemplate ? true : false, // because default mode is clone for templates
hidden: false,
listeners: {
change: function(f, value) {
me.updateVisibility();
}
}
});
me.targetSel.on('change', function(f, value) {
me.hdstoragesel.setTargetNode(value);
});
me.formatsel = Ext.create('PVE.form.DiskFormatSelector', {
name: 'diskformat',
fieldLabel: gettext('Format'),
value: 'raw',
disabled: true,
hidden: false,
allowBlank: false
});
col2.push({
xtype: 'pvePoolSelector',
fieldLabel: gettext('Resource Pool'),
name: 'pool',
value: '',
allowBlank: true
});
col2.push(me.hdstoragesel);
col2.push(me.formatsel);
me.formPanel = Ext.create('Ext.form.Panel', {
bodyPadding: 10,
reference: 'cloneform',
border: false,
layout: 'column',
defaultType: 'container',
columns: 2,
fieldDefaults: {
labelWidth: 100,
anchor: '100%'
},
items: [
{
columnWidth: 0.5,
padding: '0 10 0 0',
layout: 'anchor',
items: col1
},
{
columnWidth: 0.5,
padding: '0 0 0 10',
layout: 'anchor',
items: col2
}
]
});
var form = me.formPanel.getForm();
var titletext = me.isTemplate ? "Template" : "VM";
me.title = "Clone " + titletext + " " + me.vmid;
me.submitBtn = Ext.create('Ext.Button', {
reference: 'submitBtn',
text: gettext('Clone'),
disabled: true,
handler: function() {
if (form.isValid()) {
var values = form.getValues();
me.create_clone(values);
}
}
});
var helpButton = Ext.create('PVE.button.Help', {
listenToGlobalEvent: false,
hidden: false,
onlineHelp: me.onlineHelp});
Ext.apply(me, {
modal: true,
width: 600,
height: 250,
border: false,
layout: 'fit',
buttons: [ helpButton, '->', me.submitBtn ],
items: [ me.formPanel ]
});
me.callParent();
me.verifyFeature();
}
});