]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/qemu/HardwareView.js
give error instead of disabling efidisk button
[pve-manager.git] / www / manager6 / qemu / HardwareView.js
1 // fixme: howto avoid jslint type confusion?
2 /*jslint confusion: true */
3 Ext.define('PVE.qemu.HardwareView', {
4 extend: 'PVE.grid.PendingObjectGrid',
5 alias: ['widget.PVE.qemu.HardwareView'],
6
7 renderKey: function(key, metaData, rec, rowIndex, colIndex, store) {
8 var me = this;
9 var rows = me.rows;
10 var rowdef = rows[key] || {};
11
12 metaData.tdAttr = "valign=middle";
13
14 if (rowdef.tdCls) {
15 metaData.tdCls = rowdef.tdCls;
16 if (rowdef.tdCls == 'pve-itype-icon-storage') {
17 var value = me.getObjectValue(key, '', true);
18 if (value.match(/media=cdrom/)) {
19 metaData.tdCls = 'pve-itype-icon-cdrom';
20 return rowdef.cdheader;
21 }
22 }
23 }
24 return rowdef.header || key;
25 },
26
27 initComponent : function() {
28 var me = this;
29 var i, confid;
30
31 var nodename = me.pveSelNode.data.node;
32 if (!nodename) {
33 throw "no node name specified";
34 }
35
36 var vmid = me.pveSelNode.data.vmid;
37 if (!vmid) {
38 throw "no VM ID specified";
39 }
40
41 var caps = Ext.state.Manager.get('GuiCap');
42
43 var rows = {
44 memory: {
45 header: gettext('Memory'),
46 editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined,
47 never_delete: true,
48 defaultValue: 512,
49 tdCls: 'pve-itype-icon-memory',
50 renderer: function(value, metaData, record) {
51 var balloon = me.getObjectValue('balloon');
52 if (balloon) {
53 return PVE.Utils.format_size(balloon*1024*1024) + "/" +
54 PVE.Utils.format_size(value*1024*1024);
55
56 }
57 return PVE.Utils.format_size(value*1024*1024);
58 }
59 },
60 sockets: {
61 header: gettext('Processors'),
62 never_delete: true,
63 editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ?
64 'PVE.qemu.ProcessorEdit' : undefined,
65 tdCls: 'pve-itype-icon-processor',
66 defaultValue: 1,
67 multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'],
68 renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) {
69
70 var sockets = me.getObjectValue('sockets', 1, pending);
71 var model = me.getObjectValue('cpu', undefined, pending);
72 var cores = me.getObjectValue('cores', 1, pending);
73 var numa = me.getObjectValue('numa', undefined, pending);
74 var vcpus = me.getObjectValue('vcpus', undefined, pending);
75 var cpulimit = me.getObjectValue('cpulimit', undefined, pending);
76 var cpuunits = me.getObjectValue('cpuunits', undefined, pending);
77
78 var res = (sockets*cores) + ' (' + sockets + ' sockets, ' + cores + ' cores)';
79
80 if (model) {
81 res += ' [' + model + ']';
82 }
83
84 if (numa) {
85 res += ' [numa=' + numa +']';
86 }
87
88 if (vcpus) {
89 res += ' [vcpus=' + vcpus +']';
90 }
91
92 if (cpulimit) {
93 res += ' [cpulimit=' + cpulimit +']';
94 }
95
96 if (cpuunits) {
97 res += ' [cpuunits=' + cpuunits +']';
98 }
99
100 return res;
101 }
102 },
103 keyboard: {
104 header: gettext('Keyboard Layout'),
105 never_delete: true,
106 editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.KeyboardEdit' : undefined,
107 tdCls: 'pve-itype-icon-keyboard',
108 defaultValue: '',
109 renderer: PVE.Utils.render_kvm_language
110 },
111 vga: {
112 header: gettext('Display'),
113 editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
114 never_delete: true,
115 tdCls: 'pve-itype-icon-display',
116 defaultValue: '',
117 renderer: PVE.Utils.render_kvm_vga_driver
118 },
119 cores: {
120 visible: false
121 },
122 cpu: {
123 visible: false
124 },
125 numa: {
126 visible: false
127 },
128 balloon: {
129 visible: false
130 },
131 hotplug: {
132 visible: false
133 },
134 vcpus: {
135 visible: false
136 },
137 cpuunits: {
138 visible: false
139 },
140 cpulimit: {
141 visible: false
142 },
143 bios: {
144 visible: false
145 }
146
147 };
148
149 for (i = 0; i < 4; i++) {
150 confid = "ide" + i;
151 rows[confid] = {
152 group: 1,
153 tdCls: 'pve-itype-icon-storage',
154 editor: 'PVE.qemu.HDEdit',
155 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
156 header: gettext('Hard Disk') + ' (' + confid +')',
157 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
158 };
159 }
160 for (i = 0; i < 6; i++) {
161 confid = "sata" + i;
162 rows[confid] = {
163 group: 1,
164 tdCls: 'pve-itype-icon-storage',
165 editor: 'PVE.qemu.HDEdit',
166 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
167 header: gettext('Hard Disk') + ' (' + confid +')',
168 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
169 };
170 }
171 for (i = 0; i < 16; i++) {
172 confid = "scsi" + i;
173 rows[confid] = {
174 group: 1,
175 tdCls: 'pve-itype-icon-storage',
176 editor: 'PVE.qemu.HDEdit',
177 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
178 header: gettext('Hard Disk') + ' (' + confid +')',
179 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
180 };
181 }
182 for (i = 0; i < 16; i++) {
183 confid = "virtio" + i;
184 rows[confid] = {
185 group: 1,
186 tdCls: 'pve-itype-icon-storage',
187 editor: 'PVE.qemu.HDEdit',
188 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
189 header: gettext('Hard Disk') + ' (' + confid +')',
190 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
191 };
192 }
193 for (i = 0; i < 32; i++) {
194 confid = "net" + i;
195 rows[confid] = {
196 group: 2,
197 tdCls: 'pve-itype-icon-network',
198 editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
199 never_delete: caps.vms['VM.Config.Network'] ? false : true,
200 header: gettext('Network Device') + ' (' + confid +')'
201 };
202 }
203 rows.efidisk0 = {
204 group: 3,
205 tdCls: 'pve-itype-icon-storage',
206 editor: null,
207 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
208 header: gettext('EFI Disk')
209 };
210 for (i = 0; i < 8; i++) {
211 rows["unused" + i] = {
212 group: 4,
213 tdCls: 'pve-itype-icon-storage',
214 editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
215 header: gettext('Unused Disk') + ' ' + i
216 };
217 }
218
219 var sorterFn = function(rec1, rec2) {
220 var v1 = rec1.data.key;
221 var v2 = rec2.data.key;
222 var g1 = rows[v1].group || 0;
223 var g2 = rows[v2].group || 0;
224
225 return (g1 !== g2) ?
226 (g1 > g2 ? 1 : -1) : (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
227 };
228
229 var reload = function() {
230 me.rstore.load();
231 };
232
233 var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config';
234
235 var sm = Ext.create('Ext.selection.RowModel', {});
236
237 var run_editor = function() {
238 var rec = sm.getSelection()[0];
239 if (!rec) {
240 return;
241 }
242
243 var rowdef = rows[rec.data.key];
244 if (!rowdef.editor) {
245 return;
246 }
247
248 var editor = rowdef.editor;
249 if (rowdef.tdCls == 'pve-itype-icon-storage') {
250 var value = me.getObjectValue(rec.data.key, '', true);
251 if (value.match(/media=cdrom/)) {
252 editor = 'PVE.qemu.CDEdit';
253 }
254 }
255
256 var win;
257
258 if (Ext.isString(editor)) {
259 win = Ext.create(editor, {
260 pveSelNode: me.pveSelNode,
261 confid: rec.data.key,
262 url: '/api2/extjs/' + baseurl
263 });
264 } else {
265 var config = Ext.apply({
266 pveSelNode: me.pveSelNode,
267 confid: rec.data.key,
268 url: '/api2/extjs/' + baseurl
269 }, rowdef.editor);
270 win = Ext.createWidget(rowdef.editor.xtype, config);
271 win.load();
272 }
273
274 win.show();
275 win.on('destroy', reload);
276 };
277
278 var run_diskthrottle = function() {
279 var rec = sm.getSelection()[0];
280 if (!rec) {
281 return;
282 }
283
284 var win = Ext.create('PVE.qemu.HDThrottle', {
285 pveSelNode: me.pveSelNode,
286 confid: rec.data.key,
287 url: '/api2/extjs/' + baseurl
288 });
289
290 win.show();
291 win.on('destroy', reload);
292 };
293
294 var run_resize = function() {
295 var rec = sm.getSelection()[0];
296 if (!rec) {
297 return;
298 }
299
300 var win = Ext.create('PVE.window.HDResize', {
301 disk: rec.data.key,
302 nodename: nodename,
303 vmid: vmid
304 });
305
306 win.show();
307
308 win.on('destroy', reload);
309 };
310
311 var run_cpuoptions = function() {
312 var sockets = me.getObjectValue('sockets', 1);
313 var cores = me.getObjectValue('cores', 1);
314
315 var win = Ext.create('PVE.qemu.CPUOptions', {
316 maxvcpus: sockets * cores,
317 vmid: vmid,
318 pveSelNode: me.pveSelNode,
319 url: '/api2/extjs/' + baseurl
320 });
321
322 win.show();
323
324 win.on('destroy', reload);
325 };
326
327 var run_move = function() {
328 var rec = sm.getSelection()[0];
329 if (!rec) {
330 return;
331 }
332
333 var win = Ext.create('PVE.window.HDMove', {
334 disk: rec.data.key,
335 nodename: nodename,
336 vmid: vmid
337 });
338
339 win.show();
340
341 win.on('destroy', reload);
342 };
343
344 var edit_btn = new PVE.button.Button({
345 text: gettext('Edit'),
346 selModel: sm,
347 disabled: true,
348 handler: run_editor
349 });
350
351 var resize_btn = new PVE.button.Button({
352 text: gettext('Resize disk'),
353 selModel: sm,
354 disabled: true,
355 handler: run_resize
356 });
357
358 var move_btn = new PVE.button.Button({
359 text: gettext('Move disk'),
360 selModel: sm,
361 disabled: true,
362 handler: run_move
363 });
364
365 var diskthrottle_btn = new PVE.button.Button({
366 text: gettext('Disk Throttle'),
367 selModel: sm,
368 disabled: true,
369 handler: run_diskthrottle
370 });
371
372 var cpuoptions_btn = new Ext.Button({
373 text: gettext('CPU options'),
374 handler: run_cpuoptions
375 });
376
377 var remove_btn = new PVE.button.Button({
378 text: gettext('Remove'),
379 selModel: sm,
380 disabled: true,
381 dangerous: true,
382 confirmMsg: function(rec) {
383 var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
384 "'" + me.renderKey(rec.data.key, {}, rec) + "'");
385 if (rec.data.key.match(/^unused\d+$/)) {
386 msg += " " + gettext('This will permanently erase all data.');
387 }
388
389 return msg;
390 },
391 handler: function(b, e, rec) {
392 PVE.Utils.API2Request({
393 url: '/api2/extjs/' + baseurl,
394 waitMsgTarget: me,
395 method: 'PUT',
396 params: {
397 'delete': rec.data.key
398 },
399 callback: function() {
400 reload();
401 },
402 failure: function (response, opts) {
403 Ext.Msg.alert('Error', response.htmlStatus);
404 }
405 });
406 }
407 });
408
409 var revert_btn = new PVE.button.Button({
410 text: gettext('Revert'),
411 selModel: sm,
412 disabled: true,
413 handler: function(b, e, rec) {
414 var rowdef = me.rows[rec.data.key] || {};
415 var keys = rowdef.multiKey || [ rec.data.key ];
416 var revert = keys.join(',');
417 PVE.Utils.API2Request({
418 url: '/api2/extjs/' + baseurl,
419 waitMsgTarget: me,
420 method: 'PUT',
421 params: {
422 'revert': revert
423 },
424 callback: function() {
425 reload();
426 },
427 failure: function (response, opts) {
428 Ext.Msg.alert('Error',response.htmlStatus);
429 }
430 });
431 }
432 });
433
434 var efidisk_menuitem = Ext.create('Ext.menu.Item',{
435 text: gettext('EFI Disk'),
436 iconCls: 'pve-itype-icon-storage',
437 disabled: !caps.vms['VM.Config.Disk'],
438 handler: function() {
439
440 var rstoredata = me.rstore.getData().map;
441 // check if ovmf is configured
442 if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') {
443 var win = Ext.create('PVE.qemu.EFIDiskEdit', {
444 url: '/api2/extjs/' + baseurl,
445 pveSelNode: me.pveSelNode
446 });
447 win.on('destroy', reload);
448 win.show();
449 } else {
450 Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
451 }
452
453 }
454 });
455
456 var set_button_status = function() {
457 var sm = me.getSelectionModel();
458 var rec = sm.getSelection()[0];
459
460 // disable button when we have an efidisk already
461 // disable is ok in this case, because you can instantly
462 // see that there is already one
463 efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined);
464
465 if (!rec) {
466 remove_btn.disable();
467 edit_btn.disable();
468 resize_btn.disable();
469 move_btn.disable();
470 diskthrottle_btn.disable();
471 revert_btn.disable();
472 return;
473 }
474 var key = rec.data.key;
475 var value = rec.data.value;
476 var rowdef = rows[key];
477
478 var pending = rec.data['delete'] || me.hasPendingChanges(key);
479 var isDisk = !key.match(/^unused\d+/) &&
480 rowdef.tdCls == 'pve-itype-icon-storage' &&
481 (value && !value.match(/media=cdrom/));
482
483 var isEfi = (key === 'efidisk0');
484
485
486 remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true));
487
488 edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor);
489
490 resize_btn.setDisabled(pending || !isDisk);
491
492 move_btn.setDisabled(pending || !isDisk);
493
494 diskthrottle_btn.setDisabled(pending || !isDisk || isEfi);
495
496 revert_btn.setDisabled(!pending);
497
498 };
499
500 Ext.apply(me, {
501 url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending',
502 interval: 5000,
503 selModel: sm,
504 tbar: [
505 {
506 text: gettext('Add'),
507 menu: new Ext.menu.Menu({
508 items: [
509 {
510 text: gettext('Hard Disk'),
511 iconCls: 'pve-itype-icon-storage',
512 disabled: !caps.vms['VM.Config.Disk'],
513 handler: function() {
514 var win = Ext.create('PVE.qemu.HDEdit', {
515 url: '/api2/extjs/' + baseurl,
516 pveSelNode: me.pveSelNode
517 });
518 win.on('destroy', reload);
519 win.show();
520 }
521 },
522 {
523 text: gettext('CD/DVD Drive'),
524 iconCls: 'pve-itype-icon-cdrom',
525 disabled: !caps.vms['VM.Config.Disk'],
526 handler: function() {
527 var win = Ext.create('PVE.qemu.CDEdit', {
528 url: '/api2/extjs/' + baseurl,
529 pveSelNode: me.pveSelNode
530 });
531 win.on('destroy', reload);
532 win.show();
533 }
534 },
535 {
536 text: gettext('Network Device'),
537 iconCls: 'pve-itype-icon-network',
538 disabled: !caps.vms['VM.Config.Network'],
539 handler: function() {
540 var win = Ext.create('PVE.qemu.NetworkEdit', {
541 url: '/api2/extjs/' + baseurl,
542 pveSelNode: me.pveSelNode
543 });
544 win.on('destroy', reload);
545 win.show();
546 }
547 },
548 efidisk_menuitem
549 ]
550 })
551 },
552 remove_btn,
553 edit_btn,
554 resize_btn,
555 move_btn,
556 diskthrottle_btn,
557 cpuoptions_btn,
558 revert_btn
559 ],
560 rows: rows,
561 sorterFn: sorterFn,
562 listeners: {
563 itemdblclick: run_editor,
564 selectionchange: set_button_status
565 }
566 });
567
568 me.callParent();
569
570 me.on('activate', me.rstore.startUpdate);
571 me.on('destroy', me.rstore.stopUpdate);
572
573 me.mon(me.rstore, 'refresh', function() {
574 set_button_status();
575 });
576 }
577 });