]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/Migrate.js
ui: migrate: set window width to 600
[pve-manager.git] / www / manager6 / window / Migrate.js
CommitLineData
9706707d 1/*jslint confusion: true*/
04c32bed
DM
2Ext.define('PVE.window.Migrate', {
3 extend: 'Ext.window.Window',
4
9706707d
TM
5 vmtype: undefined,
6 nodename: undefined,
7 vmid: undefined,
8
9 viewModel: {
10 data: {
11 vmid: undefined,
12 nodename: undefined,
13 vmtype: undefined,
14 running: false,
15 qemu: {
16 onlineHelp: 'qm_migration',
17 commonName: 'VM'
18 },
19 lxc: {
20 onlineHelp: 'pct_migration',
21 commonName: 'CT'
22 },
23 migration: {
24 possible: true,
25 preconditions: [],
26 'with-local-disks': 0,
27 mode: undefined,
28 allowedNodes: undefined
29 }
30
31 },
32
33 formulas: {
34 setMigrationMode: function(get) {
35 if (get('running')){
36 if (get('vmtype') === 'qemu') {
37 return gettext('Online');
38 } else {
39 return gettext('Restart Mode');
40 }
41 } else {
42 return gettext('Offline');
43 }
44 },
45 setStorageselectorHidden: function(get) {
46 if (get('migration.with-local-disks') && get('running')) {
47 return false;
48 } else {
49 return true;
50 }
51 }
52 }
2df7ea9f 53 },
2df7ea9f
EK
54
55 controller: {
56 xclass: 'Ext.app.ViewController',
57 control: {
58 'panel[reference=formPanel]': {
59 validityChange: function(panel, isValid) {
9706707d
TM
60 this.getViewModel().set('migration.possible', isValid);
61 this.checkMigratePreconditions();
2df7ea9f 62 }
9706707d
TM
63 }
64 },
65
66 init: function(view) {
67 var me = this,
68 vm = view.getViewModel();
69
70 if (!view.nodename) {
71 throw "missing custom view config: nodename";
72 }
73 vm.set('nodename', view.nodename);
74
75 if (!view.vmid) {
76 throw "missing custom view config: vmid";
77 }
78 vm.set('vmid', view.vmid);
79
80 if (!view.vmtype) {
81 throw "missing custom view config: vmtype";
82 }
83 vm.set('vmtype', view.vmtype);
84
85
86 view.setTitle(
87 Ext.String.format('{0} {1}{2}', gettext('Migrate'), vm.get(view.vmtype).commonName, view.vmid)
88 );
89 me.lookup('proxmoxHelpButton').setHelpConfig({
90 onlineHelp: vm.get(view.vmtype).onlineHelp
91 });
92 me.checkMigratePreconditions();
93 me.lookup('formPanel').isValid();
94
95 },
96
97 onTargetChange: function (nodeSelector) {
98 //Always display the storages of the currently seleceted migration target
99 this.lookup('pveDiskStorageSelector').setNodename(nodeSelector.value);
100 this.checkMigratePreconditions();
101 },
102
103 startMigration: function() {
104 var me = this,
105 view = me.getView(),
106 vm = me.getViewModel();
107
108 var values = me.lookup('formPanel').getValues();
109 var params = {
110 target: values.target
111 };
112
113 if (vm.get('migration.mode')) {
114 params[vm.get('migration.mode')] = 1;
115 }
116 if (vm.get('migration.with-local-disks')) {
117 params['with-local-disks'] = 1;
118 }
119 //only submit targetstorage if vm is running, storage migration to different storage is only possible online
120 if (vm.get('migration.with-local-disks') && vm.get('running')) {
121 params.targetstorage = values.targetstorage;
122 }
123
124 Proxmox.Utils.API2Request({
125 params: params,
126 url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate',
127 waitMsgTarget: view,
128 method: 'POST',
129 failure: function(response, opts) {
130 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
131 },
132 success: function(response, options) {
133 var upid = response.result.data;
134 var extraTitle = Ext.String.format(' ({0} ---> {1})', vm.get('nodename'), params.target);
135
136 Ext.create('Proxmox.window.TaskViewer', {
137 upid: upid,
138 extraTitle: extraTitle
139 }).show();
140
141 view.close();
142 }
143 });
144
145 },
146
147 checkMigratePreconditions: function() {
148 var me = this,
149 vm = me.getViewModel();
150
151
152 var vmrec = PVE.data.ResourceStore.findRecord('vmid', vm.get('vmid'),
153 0, false, false, true);
154 if (vmrec && vmrec.data && vmrec.data.running) {
155 vm.set('running', true);
156 }
157
158 if (vm.get('vmtype') === 'qemu') {
159 me.checkQemuPreconditions();
160 } else {
161 me.checkLxcPreconditions();
162 }
163 me.lookup('pveNodeSelector').disallowedNodes = [vm.get('nodename')];
164
165 // Only allow nodes where the local storage is available in case of offline migration
166 // where storage migration is not possible
167 me.lookup('pveNodeSelector').allowedNodes = vm.get('migration.allowedNodes');
168
169 me.lookup('formPanel').isValid();
170
171 },
172
173 checkQemuPreconditions: function() {
174 var me = this,
175 vm = me.getViewModel(),
176 migrateStats;
177
178 if (vm.get('running')) {
179 vm.set('migration.mode', 'online');
180 }
181
182 Proxmox.Utils.API2Request({
183 url: '/nodes/' + vm.get('nodename') + '/' + vm.get('vmtype') + '/' + vm.get('vmid') + '/migrate',
184 method: 'GET',
185 failure: function(response, opts) {
186 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
187 },
188 success: function(response, options) {
189 migrateStats = response.result.data;
190 if (migrateStats.running) {
191 vm.set('running', true);
2df7ea9f 192 }
9706707d
TM
193 // Get migration object from viewmodel to prevent
194 // to many bind callbacks
195 var migration = vm.get('migration');
196 migration.preconditions = [];
197
198 if (migrateStats.allowed_nodes) {
199 migration.allowedNodes = migrateStats.allowed_nodes;
2df7ea9f 200
9706707d
TM
201 if (!migrateStats.allowed_nodes.includes(me.lookup('pveNodeSelector').value)) {
202 migration.possible = false;
203 migration.preconditions.push({
204 text: 'Local storage not available on selected Node, start VM to use live storage migration or select other target node',
205 icon: '<i class="fa fa-times critical"></i>'
206 });
2df7ea9f 207 }
9706707d
TM
208 }
209
210 if (migrateStats.local_resources.length) {
211 migration.possible = false;
212 migration.preconditions.push({
213 text: 'Can\'t migrate VM with local resources: '+ migrateStats.local_resources.join(', '),
214 icon: '<i class="fa fa-times critical"></i>'
215 });
216 }
217
218 if (migrateStats.local_disks.length) {
219
220 migrateStats.local_disks.forEach(function (disk) {
221 if (disk.cdrom && disk.cdrom === 1) {
222 migration.possible = false;
223 migration.preconditions.push({
224 text:'Can\'t migrate VM with local CD/DVD',
225 icon: '<i class="fa fa-times critical"></i>'
226 });
227
228 } else if (!disk.referenced_in_config) {
229 migration.possible = false;
230 migration.preconditions.push({
231 text: 'Found not referenced/unused disk via storage: '+ disk.volid,
232 icon: '<i class="fa fa-times critical"></i>'
233 });
234 } else {
235 migration['with-local-disks'] = 1;
236 migration.preconditions.push({
237 text:'Migration with local disk might take long: '+ disk.volid,
238 icon: '<i class="fa fa-exclamation-triangle warning"></i>'
239 });
240 }
241 });
242
243 }
244
245 vm.set('migration', migration);
246
2df7ea9f 247 }
9706707d
TM
248 });
249 },
250 checkLxcPreconditions: function() {
251 var me = this,
252 vm = me.getViewModel();
253 if (vm.get('running')) {
254 vm.set('migration.mode', 'restart');
04c32bed 255 }
04c32bed 256 }
9706707d
TM
257
258
2df7ea9f 259 },
04c32bed 260
3188b0b7 261 width: 600,
2df7ea9f 262 modal: true,
9706707d
TM
263 layout: {
264 type: 'vbox',
265 align: 'stretch'
266 },
2df7ea9f 267 border: false,
2df7ea9f
EK
268 items: [
269 {
270 xtype: 'form',
271 reference: 'formPanel',
04c32bed
DM
272 bodyPadding: 10,
273 border: false,
9706707d
TM
274 layout: {
275 type: 'column'
04c32bed
DM
276 },
277 items: [
278 {
9706707d
TM
279 xtype: 'container',
280 columnWidth: 0.5,
281 items: [{
282 xtype: 'pveNodeSelector',
283 reference: 'pveNodeSelector',
284 name: 'target',
285 fieldLabel: gettext('Target node'),
286 allowBlank: false,
287 disallowedNodes: undefined,
288 onlineValidator: true,
289 listeners: {
290 change: 'onTargetChange'
291 }
292 },
293 {
294 xtype: 'displayfield',
295 reference: 'migrationMode',
296 fieldLabel: gettext('Mode'),
297 bind: {
298 value: '{setMigrationMode}'
299 }
300 }
301 ]
04c32bed
DM
302 },
303 {
9706707d
TM
304 xtype: 'container',
305 columnWidth: 0.5,
306 items: [
307 {
308 xtype: 'pveStorageSelector',
309 reference: 'pveDiskStorageSelector',
310 name: 'targetstorage',
311 fieldLabel: gettext('Target Storage'),
312 storageContent: 'images',
313 bind: {
314 hidden: '{setStorageselectorHidden}'
315 }
316 }
317 ]
318 }
319 ]
320 },
321 {
322 xtype: 'gridpanel',
323 reference: 'preconditionGrid',
324 flex: 1,
325 columns: [
326 {text: 'Severity', dataIndex: 'icon', width: 80},
327 {text: 'Info', dataIndex: 'text', flex: 1}
328 ],
329 bind: {
330 hidden: '{!migration.preconditions.length}',
331 store: {
332 fields: ['icon','text'],
333 data: '{migration.preconditions}'
04c32bed 334 }
9706707d 335 }
2df7ea9f 336 }
9706707d 337
2df7ea9f
EK
338 ],
339 buttons: [
340 {
672a6270
TL
341 xtype: 'proxmoxHelpButton',
342 reference: 'proxmoxHelpButton',
2df7ea9f
EK
343 onlineHelp: 'pct_migration',
344 listenToGlobalEvent: false,
345 hidden: false
346 },
347 '->',
348 {
349 xtype: 'button',
350 reference: 'submitButton',
9706707d
TM
351 text: gettext('Migrate'),
352 handler: 'startMigration',
353 bind: {
354 disabled: '{!migration.possible}'
1c62fde2
EK
355 }
356 }
9706707d 357 ]
3188b0b7 358});