1 /*jslint confusion: true*/
2 Ext
.define('PVE.window.Migrate', {
3 extend
: 'Ext.window.Window',
17 onlineHelp
: 'qm_migration',
21 onlineHelp
: 'pct_migration',
27 'with-local-disks': 0,
29 allowedNodes
: undefined,
30 overwriteLocalResourceCheck
: false,
31 hasLocalResources
: false
37 setMigrationMode: function(get) {
39 if (get('vmtype') === 'qemu') {
40 return gettext('Online');
42 return gettext('Restart Mode');
45 return gettext('Offline');
48 setStorageselectorHidden: function(get) {
49 if (get('migration.with-local-disks') && get('running')) {
55 setLocalResourceCheckboxHidden: function(get) {
56 if (get('running') || !get('migration.hasLocalResources') ||
57 Proxmox
.UserName
!== 'root@pam') {
67 xclass
: 'Ext.app.ViewController',
69 'panel[reference=formPanel]': {
70 validityChange: function(panel
, isValid
) {
71 this.getViewModel().set('migration.possible', isValid
);
72 this.checkMigratePreconditions();
77 init: function(view
) {
79 vm
= view
.getViewModel();
82 throw "missing custom view config: nodename";
84 vm
.set('nodename', view
.nodename
);
87 throw "missing custom view config: vmid";
89 vm
.set('vmid', view
.vmid
);
92 throw "missing custom view config: vmtype";
94 vm
.set('vmtype', view
.vmtype
);
98 Ext
.String
.format('{0} {1} {2}', gettext('Migrate'), vm
.get(view
.vmtype
).commonName
, view
.vmid
)
100 me
.lookup('proxmoxHelpButton').setHelpConfig({
101 onlineHelp
: vm
.get(view
.vmtype
).onlineHelp
103 me
.checkMigratePreconditions();
104 me
.lookup('formPanel').isValid();
108 onTargetChange: function (nodeSelector
) {
109 //Always display the storages of the currently seleceted migration target
110 this.lookup('pveDiskStorageSelector').setNodename(nodeSelector
.value
);
111 this.checkMigratePreconditions();
114 startMigration: function() {
117 vm
= me
.getViewModel();
119 var values
= me
.lookup('formPanel').getValues();
121 target
: values
.target
124 if (vm
.get('migration.mode')) {
125 params
[vm
.get('migration.mode')] = 1;
127 if (vm
.get('migration.with-local-disks')) {
128 params
['with-local-disks'] = 1;
130 //only submit targetstorage if vm is running, storage migration to different storage is only possible online
131 if (vm
.get('migration.with-local-disks') && vm
.get('running')) {
132 params
.targetstorage
= values
.targetstorage
;
135 if (vm
.get('migration.overwriteLocalResourceCheck')) {
139 Proxmox
.Utils
.API2Request({
141 url
: '/nodes/' + vm
.get('nodename') + '/' + vm
.get('vmtype') + '/' + vm
.get('vmid') + '/migrate',
144 failure: function(response
, opts
) {
145 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
147 success: function(response
, options
) {
148 var upid
= response
.result
.data
;
149 var extraTitle
= Ext
.String
.format(' ({0} ---> {1})', vm
.get('nodename'), params
.target
);
151 Ext
.create('Proxmox.window.TaskViewer', {
153 extraTitle
: extraTitle
162 checkMigratePreconditions: function(resetMigrationPossible
) {
164 vm
= me
.getViewModel();
166 var vmrec
= PVE
.data
.ResourceStore
.findRecord('vmid', vm
.get('vmid'),
167 0, false, false, true);
168 if (vmrec
&& vmrec
.data
&& vmrec
.data
.running
) {
169 vm
.set('running', true);
172 if (vm
.get('vmtype') === 'qemu') {
173 me
.checkQemuPreconditions(resetMigrationPossible
);
175 me
.checkLxcPreconditions(resetMigrationPossible
);
177 me
.lookup('pveNodeSelector').disallowedNodes
= [vm
.get('nodename')];
179 // Only allow nodes where the local storage is available in case of offline migration
180 // where storage migration is not possible
181 me
.lookup('pveNodeSelector').allowedNodes
= vm
.get('migration.allowedNodes');
183 me
.lookup('formPanel').isValid();
187 checkQemuPreconditions: function(resetMigrationPossible
) {
189 vm
= me
.getViewModel(),
192 if (vm
.get('running')) {
193 vm
.set('migration.mode', 'online');
196 Proxmox
.Utils
.API2Request({
197 url
: '/nodes/' + vm
.get('nodename') + '/' + vm
.get('vmtype') + '/' + vm
.get('vmid') + '/migrate',
199 failure: function(response
, opts
) {
200 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
202 success: function(response
, options
) {
203 migrateStats
= response
.result
.data
;
204 if (migrateStats
.running
) {
205 vm
.set('running', true);
207 // Get migration object from viewmodel to prevent
208 // to many bind callbacks
209 var migration
= vm
.get('migration');
210 if (resetMigrationPossible
) migration
.possible
= true;
211 migration
.preconditions
= [];
213 if (migrateStats
.allowed_nodes
) {
214 migration
.allowedNodes
= migrateStats
.allowed_nodes
;
215 var target
= me
.lookup('pveNodeSelector').value
;
216 if (target
.length
&& !migrateStats
.allowed_nodes
.includes(target
)) {
217 let disallowed
= migrateStats
.not_allowed_nodes
[target
];
218 let missing_storages
= disallowed
.unavailable_storages
.join(', ');
220 migration
.possible
= false;
221 migration
.preconditions
.push({
222 text
: 'Storage (' + missing_storages
+ ') not available on selected target. ' +
223 'Start VM to use live storage migration or select other target node',
229 if (migrateStats
.local_resources
.length
) {
230 migration
.hasLocalResources
= true;
231 if(!migration
.overwriteLocalResourceCheck
|| vm
.get('running')){
232 migration
.possible
= false;
233 migration
.preconditions
.push({
234 text
: Ext
.String
.format('Can\'t migrate VM with local resources: {0}',
235 migrateStats
.local_resources
.join(', ')),
239 migration
.preconditions
.push({
240 text
: Ext
.String
.format('Migrate VM with local resources: {0}. ' +
241 'This might fail if resources aren\'t available on the target node.',
242 migrateStats
.local_resources
.join(', ')),
248 if (migrateStats
.local_disks
.length
) {
250 migrateStats
.local_disks
.forEach(function (disk
) {
251 if (disk
.cdrom
&& disk
.cdrom
=== 1) {
252 if (disk
.volid
.includes('vm-'+vm
.get('vmid')+'-cloudinit')) {
253 if (migrateStats
.running
) {
254 migration
.possible
= false;
255 migration
.preconditions
.push({
256 text
: "Can't live migrate VM with local cloudinit disk, use shared storage instead",
263 migration
.possible
= false;
264 migration
.preconditions
.push({
265 text
: "Can't migrate VM with local CD/DVD",
270 migration
['with-local-disks'] = 1;
271 migration
.preconditions
.push({
272 text
:'Migration with local disk might take long: ' + disk
.volid
273 +' (' + PVE
.Utils
.render_size(disk
.size
) + ')',
281 vm
.set('migration', migration
);
286 checkLxcPreconditions: function(resetMigrationPossible
) {
288 vm
= me
.getViewModel();
289 if (vm
.get('running')) {
290 vm
.set('migration.mode', 'restart');
307 reference
: 'formPanel',
318 xtype
: 'displayfield',
320 fieldLabel
: gettext('Source node'),
326 xtype
: 'displayfield',
327 reference
: 'migrationMode',
328 fieldLabel
: gettext('Mode'),
330 value
: '{setMigrationMode}'
338 xtype
: 'pveNodeSelector',
339 reference
: 'pveNodeSelector',
341 fieldLabel
: gettext('Target node'),
343 disallowedNodes
: undefined,
344 onlineValidator
: true,
346 change
: 'onTargetChange'
350 xtype
: 'pveStorageSelector',
351 reference
: 'pveDiskStorageSelector',
352 name
: 'targetstorage',
353 fieldLabel
: gettext('Target storage'),
354 storageContent
: 'images',
356 hidden
: '{setStorageselectorHidden}'
360 xtype
: 'proxmoxcheckbox',
361 name
: 'overwriteLocalResourceCheck',
362 fieldLabel
: gettext('Force'),
365 'data-qtip': 'Overwrite local resources unavailable check'
368 hidden
: '{setLocalResourceCheckboxHidden}',
369 value
: '{migration.overwriteLocalResourceCheck}'
372 change
: {fn
: 'checkMigratePreconditions', extraArg
: true}
380 reference
: 'preconditionGrid',
385 dataIndex
: 'severity',
386 renderer: function(v
) {
389 return '<i class="fa fa-exclamation-triangle warning"></i> ';
391 return '<i class="fa fa-times critical"></i>';
405 hidden
: '{!migration.preconditions.length}',
407 fields
: ['severity','text'],
408 data
: '{migration.preconditions}'
416 xtype
: 'proxmoxHelpButton',
417 reference
: 'proxmoxHelpButton',
418 onlineHelp
: 'pct_migration',
419 listenToGlobalEvent
: false,
425 reference
: 'submitButton',
426 text
: gettext('Migrate'),
427 handler
: 'startMigration',
429 disabled
: '{!migration.possible}'