]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/Migrate.js
ui: eslint: fix trailing comma and comma related whitespaces errors
[pve-manager.git] / www / manager6 / window / Migrate.js
CommitLineData
04c32bed
DM
1Ext.define('PVE.window.Migrate', {
2 extend: 'Ext.window.Window',
3
9706707d
TM
4 vmtype: undefined,
5 nodename: undefined,
6 vmid: undefined,
8b4987e5 7 maxHeight: 450,
9706707d
TM
8
9 viewModel: {
10 data: {
11 vmid: undefined,
12 nodename: undefined,
13 vmtype: undefined,
14 running: false,
15 qemu: {
16 onlineHelp: 'qm_migration',
f6710aac 17 commonName: 'VM',
9706707d
TM
18 },
19 lxc: {
20 onlineHelp: 'pct_migration',
f6710aac 21 commonName: 'CT',
9706707d
TM
22 },
23 migration: {
24 possible: true,
25 preconditions: [],
26 'with-local-disks': 0,
27 mode: undefined,
4ddaf290
TM
28 allowedNodes: undefined,
29 overwriteLocalResourceCheck: false,
f6710aac
TL
30 hasLocalResources: false,
31 },
9706707d
TM
32
33 },
34
35 formulas: {
36 setMigrationMode: function(get) {
37 if (get('running')){
38 if (get('vmtype') === 'qemu') {
39 return gettext('Online');
40 } else {
41 return gettext('Restart Mode');
42 }
43 } else {
44 return gettext('Offline');
45 }
46 },
47 setStorageselectorHidden: function(get) {
48 if (get('migration.with-local-disks') && get('running')) {
49 return false;
50 } else {
51 return true;
52 }
4ddaf290
TM
53 },
54 setLocalResourceCheckboxHidden: function(get) {
55 if (get('running') || !get('migration.hasLocalResources') ||
56 Proxmox.UserName !== 'root@pam') {
57 return true;
58 } else {
59 return false;
60 }
f6710aac
TL
61 },
62 },
2df7ea9f 63 },
2df7ea9f
EK
64
65 controller: {
66 xclass: 'Ext.app.ViewController',
67 control: {
68 'panel[reference=formPanel]': {
69 validityChange: function(panel, isValid) {
9706707d
TM
70 this.getViewModel().set('migration.possible', isValid);
71 this.checkMigratePreconditions();
f6710aac
TL
72 },
73 },
9706707d
TM
74 },
75
76 init: function(view) {
77 var me = this,
78 vm = view.getViewModel();
79
80 if (!view.nodename) {
81 throw "missing custom view config: nodename";
82 }
83 vm.set('nodename', view.nodename);
84
85 if (!view.vmid) {
86 throw "missing custom view config: vmid";
87 }
88 vm.set('vmid', view.vmid);
89
90 if (!view.vmtype) {
91 throw "missing custom view config: vmtype";
92 }
93 vm.set('vmtype', view.vmtype);
94
95
96 view.setTitle(
f6710aac 97 Ext.String.format('{0} {1} {2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid),
9706707d
TM
98 );
99 me.lookup('proxmoxHelpButton').setHelpConfig({
f6710aac 100 onlineHelp: vm.get(view.vmtype).onlineHelp,
9706707d
TM
101 });
102 me.checkMigratePreconditions();
103 me.lookup('formPanel').isValid();
104
105 },
106
107 onTargetChange: function (nodeSelector) {
108 //Always display the storages of the currently seleceted migration target
109 this.lookup('pveDiskStorageSelector').setNodename(nodeSelector.value);
110 this.checkMigratePreconditions();
111 },
112
113 startMigration: function() {
114 var me = this,
115 view = me.getView(),
116 vm = me.getViewModel();
117
118 var values = me.lookup('formPanel').getValues();
119 var params = {
f6710aac 120 target: values.target,
9706707d
TM
121 };
122
123 if (vm.get('migration.mode')) {
124 params[vm.get('migration.mode')] = 1;
125 }
126 if (vm.get('migration.with-local-disks')) {
127 params['with-local-disks'] = 1;
128 }
11c17a01
FE
129 //offline migration to a different storage currently might fail at a late stage
130 //(i.e. after some disks have been moved), so don't expose it yet in the GUI
131 if (vm.get('migration.with-local-disks') && vm.get('running') && values.targetstorage) {
9706707d
TM
132 params.targetstorage = values.targetstorage;
133 }
134
4ddaf290
TM
135 if (vm.get('migration.overwriteLocalResourceCheck')) {
136 params['force'] = 1;
137 }
138
9706707d
TM
139 Proxmox.Utils.API2Request({
140 params: params,
141 url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate',
142 waitMsgTarget: view,
143 method: 'POST',
144 failure: function(response, opts) {
145 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
146 },
147 success: function(response, options) {
148 var upid = response.result.data;
149 var extraTitle = Ext.String.format(' ({0} ---> {1})', vm.get('nodename'), params.target);
150
151 Ext.create('Proxmox.window.TaskViewer', {
152 upid: upid,
f6710aac 153 extraTitle: extraTitle,
9706707d
TM
154 }).show();
155
156 view.close();
f6710aac 157 },
9706707d
TM
158 });
159
160 },
161
4ddaf290 162 checkMigratePreconditions: function(resetMigrationPossible) {
9706707d
TM
163 var me = this,
164 vm = me.getViewModel();
165
9706707d
TM
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);
170 }
171
172 if (vm.get('vmtype') === 'qemu') {
4ddaf290 173 me.checkQemuPreconditions(resetMigrationPossible);
9706707d 174 } else {
4ddaf290 175 me.checkLxcPreconditions(resetMigrationPossible);
9706707d
TM
176 }
177 me.lookup('pveNodeSelector').disallowedNodes = [vm.get('nodename')];
178
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');
182
183 me.lookup('formPanel').isValid();
184
185 },
186
4ddaf290 187 checkQemuPreconditions: function(resetMigrationPossible) {
9706707d
TM
188 var me = this,
189 vm = me.getViewModel(),
190 migrateStats;
191
192 if (vm.get('running')) {
193 vm.set('migration.mode', 'online');
194 }
195
196 Proxmox.Utils.API2Request({
197 url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate',
198 method: 'GET',
199 failure: function(response, opts) {
200 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
201 },
202 success: function(response, options) {
203 migrateStats = response.result.data;
204 if (migrateStats.running) {
205 vm.set('running', true);
2df7ea9f 206 }
9706707d
TM
207 // Get migration object from viewmodel to prevent
208 // to many bind callbacks
209 var migration = vm.get('migration');
4ddaf290 210 if (resetMigrationPossible) migration.possible = true;
9706707d
TM
211 migration.preconditions = [];
212
213 if (migrateStats.allowed_nodes) {
214 migration.allowedNodes = migrateStats.allowed_nodes;
c54f9a75
TM
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(', ');
2df7ea9f 219
9706707d
TM
220 migration.possible = false;
221 migration.preconditions.push({
c54f9a75
TM
222 text: 'Storage (' + missing_storages + ') not available on selected target. ' +
223 'Start VM to use live storage migration or select other target node',
f6710aac 224 severity: 'error',
9706707d 225 });
2df7ea9f 226 }
9706707d
TM
227 }
228
229 if (migrateStats.local_resources.length) {
4ddaf290
TM
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(', ')),
f6710aac 236 severity: 'error',
4ddaf290
TM
237 });
238 } else {
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(', ')),
f6710aac 243 severity: 'warning',
4ddaf290
TM
244 });
245 }
9706707d
TM
246 }
247
248 if (migrateStats.local_disks.length) {
249
250 migrateStats.local_disks.forEach(function (disk) {
251 if (disk.cdrom && disk.cdrom === 1) {
cea7382b
ML
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",
f6710aac 257 severity: 'error',
cea7382b
ML
258 });
259 } else {
260 return;
261 }
262 } else {
263 migration.possible = false;
264 migration.preconditions.push({
265 text: "Can't migrate VM with local CD/DVD",
f6710aac 266 severity: 'error',
cea7382b
ML
267 });
268 }
9706707d 269 } else {
0411c2c0 270 var size_string = disk.size ? '(' + PVE.Utils.render_size(disk.size) + ')' : '';
9706707d
TM
271 migration['with-local-disks'] = 1;
272 migration.preconditions.push({
0411c2c0
FE
273 text: Ext.String.format('Migration with local disk might take long: {0} {1}',
274 disk.volid, size_string),
f6710aac 275 severity: 'warning',
9706707d
TM
276 });
277 }
278 });
279
280 }
281
282 vm.set('migration', migration);
283
f6710aac 284 },
9706707d
TM
285 });
286 },
4ddaf290 287 checkLxcPreconditions: function(resetMigrationPossible) {
9706707d
TM
288 var me = this,
289 vm = me.getViewModel();
290 if (vm.get('running')) {
291 vm.set('migration.mode', 'restart');
04c32bed 292 }
f6710aac 293 },
9706707d
TM
294
295
2df7ea9f 296 },
04c32bed 297
3188b0b7 298 width: 600,
2df7ea9f 299 modal: true,
9706707d
TM
300 layout: {
301 type: 'vbox',
f6710aac 302 align: 'stretch',
9706707d 303 },
2df7ea9f 304 border: false,
2df7ea9f
EK
305 items: [
306 {
307 xtype: 'form',
308 reference: 'formPanel',
04c32bed
DM
309 bodyPadding: 10,
310 border: false,
d27a44a6 311 layout: 'hbox',
04c32bed
DM
312 items: [
313 {
9706707d 314 xtype: 'container',
d27a44a6 315 flex: 1,
9706707d 316 items: [{
090dff2f
TL
317 xtype: 'displayfield',
318 name: 'source',
319 fieldLabel: gettext('Source node'),
320 bind: {
f6710aac
TL
321 value: '{nodename}',
322 },
9706707d
TM
323 },
324 {
325 xtype: 'displayfield',
326 reference: 'migrationMode',
327 fieldLabel: gettext('Mode'),
328 bind: {
f6710aac
TL
329 value: '{setMigrationMode}',
330 },
331 }],
04c32bed
DM
332 },
333 {
9706707d 334 xtype: 'container',
d27a44a6 335 flex: 1,
090dff2f
TL
336 items: [{
337 xtype: 'pveNodeSelector',
338 reference: 'pveNodeSelector',
339 name: 'target',
340 fieldLabel: gettext('Target node'),
341 allowBlank: false,
342 disallowedNodes: undefined,
343 onlineValidator: true,
344 listeners: {
f6710aac
TL
345 change: 'onTargetChange',
346 },
090dff2f
TL
347 },
348 {
349 xtype: 'pveStorageSelector',
350 reference: 'pveDiskStorageSelector',
351 name: 'targetstorage',
352 fieldLabel: gettext('Target storage'),
353 storageContent: 'images',
11c17a01
FE
354 allowBlank: true,
355 autoSelect: false,
cd338fe1 356 emptyText: gettext('Current layout'),
090dff2f 357 bind: {
f6710aac
TL
358 hidden: '{setStorageselectorHidden}',
359 },
4ddaf290
TM
360 },
361 {
362 xtype: 'proxmoxcheckbox',
363 name: 'overwriteLocalResourceCheck',
364 fieldLabel: gettext('Force'),
365 autoEl: {
366 tag: 'div',
f6710aac 367 'data-qtip': 'Overwrite local resources unavailable check',
4ddaf290
TM
368 },
369 bind: {
370 hidden: '{setLocalResourceCheckboxHidden}',
f6710aac 371 value: '{migration.overwriteLocalResourceCheck}',
4ddaf290
TM
372 },
373 listeners: {
f6710aac
TL
374 change: {fn: 'checkMigratePreconditions', extraArg: true},
375 },
376 }],
377 },
378 ],
9706707d
TM
379 },
380 {
381 xtype: 'gridpanel',
382 reference: 'preconditionGrid',
b4533390 383 selectable: false,
9706707d 384 flex: 1,
b4533390
TL
385 columns: [{
386 text: '',
387 dataIndex: 'severity',
388 renderer: function(v) {
389 switch (v) {
390 case 'warning':
391 return '<i class="fa fa-exclamation-triangle warning"></i> ';
392 case 'error':
393 return '<i class="fa fa-times critical"></i>';
394 default:
395 return v;
396 }
397 },
f6710aac 398 width: 35,
b4533390
TL
399 },
400 {
401 text: 'Info',
402 dataIndex: 'text',
403 cellWrap: true,
f6710aac 404 flex: 1,
b4533390 405 }],
9706707d
TM
406 bind: {
407 hidden: '{!migration.preconditions.length}',
408 store: {
cd338fe1
TL
409 fields: ['severity', 'text'],
410 data: '{migration.preconditions}',
411 sorters: 'text',
f6710aac
TL
412 },
413 },
414 },
9706707d 415
2df7ea9f
EK
416 ],
417 buttons: [
418 {
672a6270
TL
419 xtype: 'proxmoxHelpButton',
420 reference: 'proxmoxHelpButton',
2df7ea9f
EK
421 onlineHelp: 'pct_migration',
422 listenToGlobalEvent: false,
f6710aac 423 hidden: false,
2df7ea9f
EK
424 },
425 '->',
426 {
427 xtype: 'button',
428 reference: 'submitButton',
9706707d
TM
429 text: gettext('Migrate'),
430 handler: 'startMigration',
431 bind: {
f6710aac
TL
432 disabled: '{!migration.possible}',
433 },
434 },
435 ],
3188b0b7 436});