mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-06-03 06:23:30 +00:00

This allows to add the components in each column in the order they appear in the Clone Window. References from child components are kept in an object in the parent component so it should be the same performance-wise (contrary to to Ext.ComponentQuery which is doing DOM parsing)
323 lines
6.9 KiB
JavaScript
323 lines
6.9 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());
|
|
}
|
|
},
|
|
|
|
// if set to true, will display an extra snapshot selector combobox
|
|
hasSnapshots: false,
|
|
|
|
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 storagesel = me.lookupReference('storagesel');
|
|
var formatsel = me.lookupReference('formatsel');
|
|
|
|
var clonemode = me.lookupReference('clonemodesel').getValue();
|
|
var storage = storagesel.getValue();
|
|
var rec = storagesel.store.getById(storage);
|
|
|
|
storagesel.setDisabled(clonemode === 'clone');
|
|
|
|
if (!rec || clonemode === 'clone') {
|
|
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'
|
|
) {
|
|
formatsel.setValue('raw');
|
|
formatsel.setDisabled(true);
|
|
} else {
|
|
formatsel.setValue('qcow2');
|
|
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.lookupReference('snapshotsel').getValue();
|
|
var clonemode = me.lookupReference('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.lookupReference('targetsel').allowedNodes = res.nodes;
|
|
me.lookupReference('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 titletext = me.isTemplate ? "Template" : "VM";
|
|
me.title = "Clone " + titletext + " " + me.vmid;
|
|
|
|
var col1 = [];
|
|
var col2 = [];
|
|
|
|
col1.push({
|
|
xtype: 'pveNodeSelector',
|
|
name: 'target',
|
|
reference: 'targetsel',
|
|
fieldLabel: gettext('Target node'),
|
|
selectCurNode: true,
|
|
allowBlank: false,
|
|
onlineValidator: true,
|
|
listeners: {
|
|
change: function(f, value) {
|
|
me.lookupReference('storagesel').setTargetNode(value);
|
|
}
|
|
}
|
|
});
|
|
|
|
var modelist = [['copy', gettext('Full Clone')]];
|
|
if (me.isTemplate) {
|
|
modelist.push(['clone', gettext('Linked Clone')]);
|
|
}
|
|
|
|
col1.push({
|
|
xtype: 'pveGuestIDSelector',
|
|
name: 'newvmid',
|
|
guestType: 'qemu',
|
|
value: '',
|
|
loadNextFreeID: true,
|
|
validateExists: false
|
|
},
|
|
{
|
|
xtype: 'textfield',
|
|
name: 'name',
|
|
allowBlank: true,
|
|
fieldLabel: gettext('Name')
|
|
},
|
|
{
|
|
xtype: 'pvePoolSelector',
|
|
fieldLabel: gettext('Resource Pool'),
|
|
name: 'pool',
|
|
value: '',
|
|
allowBlank: true
|
|
}
|
|
);
|
|
|
|
col2.push({
|
|
xtype: 'pveKVComboBox',
|
|
fieldLabel: gettext('Mode'),
|
|
name: 'clonemode',
|
|
reference: 'clonemodesel',
|
|
allowBlank: false,
|
|
hidden: !me.isTemplate,
|
|
value: me.isTemplate ? 'clone' : 'copy',
|
|
comboItems: modelist,
|
|
listeners: {
|
|
change: function(t, value) {
|
|
me.updateVisibility();
|
|
me.verifyFeature();
|
|
}
|
|
}
|
|
},
|
|
{
|
|
xtype: 'PVE.form.SnapshotSelector',
|
|
name: 'snapname',
|
|
reference: 'snapshotsel',
|
|
fieldLabel: gettext('Snapshot'),
|
|
nodename: me.nodename,
|
|
vmid: me.vmid,
|
|
hidden: me.isTemplate || !me.hasSnapshots ? true : false,
|
|
disabled: false,
|
|
allowBlank: false,
|
|
value : me.snapname,
|
|
listeners: {
|
|
change: function(f, value) {
|
|
me.verifyFeature();
|
|
}
|
|
}
|
|
},
|
|
{
|
|
xtype: 'pveStorageSelector',
|
|
name: 'storage',
|
|
reference: 'storagesel',
|
|
nodename: me.nodename,
|
|
fieldLabel: gettext('Target Storage'),
|
|
storageContent: 'images',
|
|
autoSelect: true,
|
|
allowBlank: true,
|
|
disabled: me.isTemplate ? true : false, // because default mode is clone for templates
|
|
hidden: false,
|
|
listeners: {
|
|
change: function(f, value) {
|
|
me.updateVisibility();
|
|
}
|
|
}
|
|
},
|
|
{
|
|
xtype: 'pveDiskFormatSelector',
|
|
name: 'diskformat',
|
|
reference: 'formatsel',
|
|
fieldLabel: gettext('Format'),
|
|
value: 'raw',
|
|
disabled: true,
|
|
hidden: false,
|
|
allowBlank: false
|
|
});
|
|
|
|
var 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
|
|
}
|
|
]
|
|
});
|
|
|
|
Ext.apply(me, {
|
|
modal: true,
|
|
width: 600,
|
|
height: 250,
|
|
border: false,
|
|
layout: 'fit',
|
|
buttons: [ {
|
|
xtype: 'pveHelpButton',
|
|
listenToGlobalEvent: false,
|
|
hidden: false,
|
|
onlineHelp: me.onlineHelp
|
|
},
|
|
'->',
|
|
{
|
|
reference: 'submitBtn',
|
|
text: gettext('Clone'),
|
|
disabled: true,
|
|
handler: function() {
|
|
var cloneForm = me.lookupReference('cloneform');
|
|
if (cloneForm.isValid()) {
|
|
me.create_clone(cloneForm.getValues());
|
|
}
|
|
}
|
|
} ],
|
|
items: [ formPanel ]
|
|
});
|
|
|
|
me.callParent();
|
|
|
|
me.verifyFeature();
|
|
}
|
|
});
|