-/*jslint confusion: true*/
Ext.define('PVE.window.Migrate', {
extend: 'Ext.window.Window',
vmtype: undefined,
nodename: undefined,
vmid: undefined,
+ maxHeight: 450,
viewModel: {
data: {
running: false,
qemu: {
onlineHelp: 'qm_migration',
- commonName: 'VM'
+ commonName: 'VM',
},
lxc: {
onlineHelp: 'pct_migration',
- commonName: 'CT'
+ commonName: 'CT',
},
migration: {
possible: true,
mode: undefined,
allowedNodes: undefined,
overwriteLocalResourceCheck: false,
- hasLocalResources: false
- }
+ hasLocalResources: false,
+ },
},
formulas: {
setMigrationMode: function(get) {
- if (get('running')){
+ if (get('running')) {
if (get('vmtype') === 'qemu') {
return gettext('Online');
} else {
} else {
return false;
}
- }
- }
+ },
+ },
},
controller: {
validityChange: function(panel, isValid) {
this.getViewModel().set('migration.possible', isValid);
this.checkMigratePreconditions();
- }
- }
+ },
+ },
},
init: function(view) {
}
vm.set('vmtype', view.vmtype);
-
view.setTitle(
- Ext.String.format('{0} {1} {2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid)
+ Ext.String.format('{0} {1} {2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid),
);
me.lookup('proxmoxHelpButton').setHelpConfig({
- onlineHelp: vm.get(view.vmtype).onlineHelp
+ onlineHelp: vm.get(view.vmtype).onlineHelp,
});
- me.checkMigratePreconditions();
me.lookup('formPanel').isValid();
-
},
- onTargetChange: function (nodeSelector) {
- //Always display the storages of the currently seleceted migration target
+ onTargetChange: function(nodeSelector) {
+ // Always display the storages of the currently seleceted migration target
this.lookup('pveDiskStorageSelector').setNodename(nodeSelector.value);
this.checkMigratePreconditions();
},
var values = me.lookup('formPanel').getValues();
var params = {
- target: values.target
+ target: values.target,
};
if (vm.get('migration.mode')) {
if (vm.get('migration.with-local-disks')) {
params['with-local-disks'] = 1;
}
- //only submit targetstorage if vm is running, storage migration to different storage is only possible online
- if (vm.get('migration.with-local-disks') && vm.get('running')) {
+ //offline migration to a different storage currently might fail at a late stage
+ //(i.e. after some disks have been moved), so don't expose it yet in the GUI
+ if (vm.get('migration.with-local-disks') && vm.get('running') && values.targetstorage) {
params.targetstorage = values.targetstorage;
}
if (vm.get('migration.overwriteLocalResourceCheck')) {
- params['force'] = 1;
+ params.force = 1;
}
Proxmox.Utils.API2Request({
Ext.create('Proxmox.window.TaskViewer', {
upid: upid,
- extraTitle: extraTitle
+ extraTitle: extraTitle,
}).show();
view.close();
- }
+ },
});
-
},
checkMigratePreconditions: function(resetMigrationPossible) {
me.lookup('pveNodeSelector').allowedNodes = vm.get('migration.allowedNodes');
me.lookup('formPanel').isValid();
-
},
- checkQemuPreconditions: function(resetMigrationPossible) {
- var me = this,
+ checkQemuPreconditions: async function(resetMigrationPossible) {
+ let me = this,
vm = me.getViewModel(),
migrateStats;
vm.set('migration.mode', 'online');
}
- Proxmox.Utils.API2Request({
- url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate',
- method: 'GET',
- failure: function(response, opts) {
- Ext.Msg.alert(gettext('Error'), response.htmlStatus);
- },
- success: function(response, options) {
- migrateStats = response.result.data;
- if (migrateStats.running) {
- vm.set('running', true);
- }
- // Get migration object from viewmodel to prevent
- // to many bind callbacks
- var migration = vm.get('migration');
- if (resetMigrationPossible) migration.possible = true;
- migration.preconditions = [];
-
- if (migrateStats.allowed_nodes) {
- migration.allowedNodes = migrateStats.allowed_nodes;
- var target = me.lookup('pveNodeSelector').value;
- if (target.length && !migrateStats.allowed_nodes.includes(target)) {
- let disallowed = migrateStats.not_allowed_nodes[target];
- let missing_storages = disallowed.unavailable_storages.join(', ');
+ try {
+ if (me.fetchingNodeMigrateInfo && me.fetchingNodeMigrateInfo === vm.get('nodename')) {
+ return;
+ }
+ me.fetchingNodeMigrateInfo = vm.get('nodename');
+ let { result } = await Proxmox.Async.api2({
+ url: `/nodes/${vm.get('nodename')}/${vm.get('vmtype')}/${vm.get('vmid')}/migrate`,
+ method: 'GET',
+ });
+ migrateStats = result.data;
+ me.fetchingNodeMigrateInfo = false;
+ } catch (error) {
+ Ext.Msg.alert(gettext('Error'), error.htmlStatus);
+ return;
+ }
- migration.possible = false;
- migration.preconditions.push({
- text: 'Storage (' + missing_storages + ') not available on selected target. ' +
- 'Start VM to use live storage migration or select other target node',
- severity: 'error'
- });
- }
- }
+ if (migrateStats.running) {
+ vm.set('running', true);
+ }
+ // Get migration object from viewmodel to prevent to many bind callbacks
+ let migration = vm.get('migration');
+ if (resetMigrationPossible) {
+ migration.possible = true;
+ }
+ migration.preconditions = [];
+
+ if (migrateStats.allowed_nodes) {
+ migration.allowedNodes = migrateStats.allowed_nodes;
+ let target = me.lookup('pveNodeSelector').value;
+ if (target.length && !migrateStats.allowed_nodes.includes(target)) {
+ let disallowed = migrateStats.not_allowed_nodes[target];
+ let missingStorages = disallowed.unavailable_storages.join(', ');
+
+ migration.possible = false;
+ migration.preconditions.push({
+ text: 'Storage (' + missingStorages + ') not available on selected target. ' +
+ 'Start VM to use live storage migration or select other target node',
+ severity: 'error',
+ });
+ }
+ }
+
+ if (migrateStats.local_resources.length) {
+ migration.hasLocalResources = true;
+ if (!migration.overwriteLocalResourceCheck || vm.get('running')) {
+ migration.possible = false;
+ migration.preconditions.push({
+ text: Ext.String.format('Can\'t migrate VM with local resources: {0}',
+ migrateStats.local_resources.join(', ')),
+ severity: 'error',
+ });
+ } else {
+ migration.preconditions.push({
+ text: Ext.String.format('Migrate VM with local resources: {0}. ' +
+ 'This might fail if resources aren\'t available on the target node.',
+ migrateStats.local_resources.join(', ')),
+ severity: 'warning',
+ });
+ }
+ }
- if (migrateStats.local_resources.length) {
- migration.hasLocalResources = true;
- if(!migration.overwriteLocalResourceCheck || vm.get('running')){
+ if (migrateStats.local_disks.length) {
+ migrateStats.local_disks.forEach(function(disk) {
+ if (disk.cdrom && disk.cdrom === 1) {
+ if (!disk.volid.includes('vm-' + vm.get('vmid') + '-cloudinit')) {
migration.possible = false;
migration.preconditions.push({
- text: Ext.String.format('Can\'t migrate VM with local resources: {0}',
- migrateStats.local_resources.join(', ')),
- severity: 'error'
- });
- } else {
- migration.preconditions.push({
- text: Ext.String.format('Migrate VM with local resources: {0}. ' +
- 'This might fail if resources aren\'t available on the target node.',
- migrateStats.local_resources.join(', ')),
- severity: 'warning'
+ text: "Can't migrate VM with local CD/DVD",
+ severity: 'error',
});
}
- }
-
- if (migrateStats.local_disks.length) {
-
- migrateStats.local_disks.forEach(function (disk) {
- if (disk.cdrom && disk.cdrom === 1) {
- if (disk.volid.includes('vm-'+vm.get('vmid')+'-cloudinit')) {
- if (migrateStats.running) {
- migration.possible = false;
- migration.preconditions.push({
- text: "Can't live migrate VM with local cloudinit disk, use shared storage instead",
- severity: 'error'
- });
- } else {
- return;
- }
- } else {
- migration.possible = false;
- migration.preconditions.push({
- text: "Can't migrate VM with local CD/DVD",
- severity: 'error'
- });
- }
-
- } else if (!disk.referenced_in_config) {
- migration.possible = false;
- migration.preconditions.push({
- text: 'Found not referenced/unused disk via storage: '+ disk.volid,
- severity: 'error'
- });
- } else {
- migration['with-local-disks'] = 1;
- migration.preconditions.push({
- text:'Migration with local disk might take long: ' + disk.volid
- +' (' + PVE.Utils.render_size(disk.size) + ')',
- severity: 'warning'
- });
- }
+ } else {
+ let size = disk.size ? '(' + Proxmox.Utils.render_size(disk.size) + ')' : '';
+ migration['with-local-disks'] = 1;
+ migration.preconditions.push({
+ text: Ext.String.format('Migration with local disk might take long: {0} {1}', disk.volid, size),
+ severity: 'warning',
});
-
}
+ });
+ }
- vm.set('migration', migration);
-
- }
- });
+ vm.set('migration', migration);
},
checkLxcPreconditions: function(resetMigrationPossible) {
- var me = this,
- vm = me.getViewModel();
+ let vm = this.getViewModel();
if (vm.get('running')) {
vm.set('migration.mode', 'restart');
}
- }
-
-
+ },
},
width: 600,
modal: true,
layout: {
type: 'vbox',
- align: 'stretch'
+ align: 'stretch',
},
border: false,
items: [
reference: 'formPanel',
bodyPadding: 10,
border: false,
- layout: {
- type: 'column'
- },
+ layout: 'hbox',
items: [
{
xtype: 'container',
- columnWidth: 0.5,
+ flex: 1,
items: [{
xtype: 'displayfield',
name: 'source',
fieldLabel: gettext('Source node'),
bind: {
- value: '{nodename}'
- }
+ value: '{nodename}',
+ },
},
{
xtype: 'displayfield',
reference: 'migrationMode',
fieldLabel: gettext('Mode'),
bind: {
- value: '{setMigrationMode}'
- }
- }]
+ value: '{setMigrationMode}',
+ },
+ }],
},
{
xtype: 'container',
- columnWidth: 0.5,
+ flex: 1,
items: [{
xtype: 'pveNodeSelector',
reference: 'pveNodeSelector',
disallowedNodes: undefined,
onlineValidator: true,
listeners: {
- change: 'onTargetChange'
- }
+ change: 'onTargetChange',
+ },
},
{
xtype: 'pveStorageSelector',
name: 'targetstorage',
fieldLabel: gettext('Target storage'),
storageContent: 'images',
+ allowBlank: true,
+ autoSelect: false,
+ emptyText: gettext('Current layout'),
bind: {
- hidden: '{setStorageselectorHidden}'
- }
+ hidden: '{setStorageselectorHidden}',
+ },
},
{
xtype: 'proxmoxcheckbox',
fieldLabel: gettext('Force'),
autoEl: {
tag: 'div',
- 'data-qtip': 'Overwrite local resources unavailable check'
+ 'data-qtip': 'Overwrite local resources unavailable check',
},
bind: {
hidden: '{setLocalResourceCheckboxHidden}',
- value: '{migration.overwriteLocalResourceCheck}'
+ value: '{migration.overwriteLocalResourceCheck}',
},
listeners: {
- change: {fn: 'checkMigratePreconditions', extraArg: true}
- }
- }]
- }
- ]
+ change: {
+ fn: 'checkMigratePreconditions',
+ extraArg: true,
+ },
+ },
+ }],
+ },
+ ],
},
{
xtype: 'gridpanel',
return v;
}
},
- width: 35
+ width: 35,
},
{
text: 'Info',
dataIndex: 'text',
cellWrap: true,
- flex: 1
+ flex: 1,
}],
bind: {
hidden: '{!migration.preconditions.length}',
store: {
- fields: ['severity','text'],
- data: '{migration.preconditions}'
- }
- }
- }
+ fields: ['severity', 'text'],
+ data: '{migration.preconditions}',
+ sorters: 'text',
+ },
+ },
+ },
],
buttons: [
reference: 'proxmoxHelpButton',
onlineHelp: 'pct_migration',
listenToGlobalEvent: false,
- hidden: false
+ hidden: false,
},
'->',
{
text: gettext('Migrate'),
handler: 'startMigration',
bind: {
- disabled: '{!migration.possible}'
- }
- }
- ]
+ disabled: '{!migration.possible}',
+ },
+ },
+ ],
});