mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-07-23 05:41:39 +00:00

especially on chromium based browser (e.g. chrome, edge) it can happen, depending on the zoom level, that the last column does not fit next to the other columns and is moved below the other columns. This results in an ugly looking UI and in the worst case makes it unusable. This can also be triggered if the monitor is set to a higher scaling / different DPI settings. I was able to have the same problem in Edge when setting the scaling in the windows display settings to 125% (Clone VM). Changing the layout from columns with 0.5 width to extjs HBOXes with flex 1 works as expected. Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
313 lines
6.9 KiB
JavaScript
313 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());
|
|
}
|
|
},
|
|
|
|
statics: {
|
|
// display a snapshot selector only if needed
|
|
wrap: function(nodename, vmid, isTemplate, guestType) {
|
|
Proxmox.Utils.API2Request({
|
|
url: '/nodes/' + nodename + '/' + guestType + '/' + vmid +'/snapshot',
|
|
failure: function(response, opts) {
|
|
Ext.Msg.alert('Error', response.htmlStatus);
|
|
},
|
|
success: function(response, opts) {
|
|
var snapshotList = response.result.data;
|
|
var hasSnapshots = snapshotList.length === 1 &&
|
|
snapshotList[0].name === 'current' ? false : true;
|
|
|
|
Ext.create('PVE.window.Clone', {
|
|
nodename: nodename,
|
|
guestType: guestType,
|
|
vmid: vmid,
|
|
isTemplate: isTemplate,
|
|
hasSnapshots: hasSnapshots
|
|
}).show();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
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) {
|
|
if (me.guestType === 'lxc') {
|
|
params.hostname = values.name;
|
|
} else {
|
|
params.name = values.name;
|
|
}
|
|
}
|
|
|
|
if (values.target) {
|
|
params.target = values.target;
|
|
}
|
|
|
|
if (values.clonemode === 'copy') {
|
|
params.full = 1;
|
|
if (values.hdstorage) {
|
|
params.storage = values.hdstorage;
|
|
if (values.diskformat && me.guestType !== 'lxc') {
|
|
params.format = values.diskformat;
|
|
}
|
|
}
|
|
}
|
|
|
|
Proxmox.Utils.API2Request({
|
|
params: params,
|
|
url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + 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
|
|
updateVisibility: function() {
|
|
var me = this;
|
|
var clonemode = me.lookupReference('clonemodesel').getValue();
|
|
var disksel = me.lookup('diskselector');
|
|
disksel.setDisabled(clonemode === 'clone');
|
|
},
|
|
|
|
// 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;
|
|
}
|
|
|
|
Proxmox.Utils.API2Request({
|
|
waitMsgTarget: me,
|
|
url: '/nodes/' + me.nodename + '/' + me.guestType + '/' + me.vmid + '/feature',
|
|
params: params,
|
|
method: 'GET',
|
|
failure: function(response, opts) {
|
|
me.lookupReference('submitBtn').setDisabled(true);
|
|
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';
|
|
}
|
|
|
|
if (!me.guestType) {
|
|
throw "no Guest Type specified";
|
|
}
|
|
|
|
var titletext = me.guestType === 'lxc' ? 'CT' : 'VM';
|
|
if (me.isTemplate) {
|
|
titletext += ' Template';
|
|
}
|
|
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('hdstorage').setTargetNode(value);
|
|
}
|
|
}
|
|
});
|
|
|
|
var modelist = [['copy', gettext('Full Clone')]];
|
|
if (me.isTemplate) {
|
|
modelist.push(['clone', gettext('Linked Clone')]);
|
|
}
|
|
|
|
col1.push({
|
|
xtype: 'pveGuestIDSelector',
|
|
name: 'newvmid',
|
|
guestType: me.guestType,
|
|
value: '',
|
|
loadNextFreeID: true,
|
|
validateExists: false
|
|
},
|
|
{
|
|
xtype: 'textfield',
|
|
name: 'name',
|
|
allowBlank: true,
|
|
fieldLabel: me.guestType === 'lxc' ? gettext('Hostname') : gettext('Name')
|
|
},
|
|
{
|
|
xtype: 'pvePoolSelector',
|
|
fieldLabel: gettext('Resource Pool'),
|
|
name: 'pool',
|
|
value: '',
|
|
allowBlank: true
|
|
}
|
|
);
|
|
|
|
col2.push({
|
|
xtype: 'proxmoxKVComboBox',
|
|
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,
|
|
guestType: me.guestType,
|
|
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: 'pveDiskStorageSelector',
|
|
reference: 'diskselector',
|
|
nodename: me.nodename,
|
|
autoSelect: false,
|
|
hideSize: true,
|
|
hideSelection: true,
|
|
storageLabel: gettext('Target Storage'),
|
|
allowBlank: true,
|
|
storageContent: me.guestType === 'qemu' ? 'images' : 'rootdir',
|
|
emptyText: gettext('Same as source'),
|
|
disabled: me.isTemplate ? true : false // because default mode is clone for templates
|
|
});
|
|
|
|
var formPanel = Ext.create('Ext.form.Panel', {
|
|
bodyPadding: 10,
|
|
reference: 'cloneform',
|
|
border: false,
|
|
layout: 'hbox',
|
|
defaultType: 'container',
|
|
fieldDefaults: {
|
|
labelWidth: 100,
|
|
anchor: '100%'
|
|
},
|
|
items: [
|
|
{
|
|
flex: 1,
|
|
padding: '0 10 0 0',
|
|
layout: 'anchor',
|
|
items: col1
|
|
},
|
|
{
|
|
flex: 1,
|
|
padding: '0 0 0 10',
|
|
layout: 'anchor',
|
|
items: col2
|
|
}
|
|
]
|
|
});
|
|
|
|
Ext.apply(me, {
|
|
modal: true,
|
|
width: 600,
|
|
height: 250,
|
|
border: false,
|
|
layout: 'fit',
|
|
buttons: [ {
|
|
xtype: 'proxmoxHelpButton',
|
|
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();
|
|
}
|
|
});
|