]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/qemu/HardwareView.js
rework lxc DNS Edit
[pve-manager.git] / www / manager6 / qemu / HardwareView.js
CommitLineData
f97670f5 1Ext.define('PVE.qemu.HardwareView', {
bc0eaf58 2 extend: 'Proxmox.grid.PendingObjectGrid',
f97670f5
DM
3 alias: ['widget.PVE.qemu.HardwareView'],
4
ba93a9c6
DC
5 onlineHelp: 'qm_virtual_machines_settings',
6
f97670f5
DM
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,
72408db9 48 defaultValue: '512',
f97670f5
DM
49 tdCls: 'pve-itype-icon-memory',
50 renderer: function(value, metaData, record) {
51 var balloon = me.getObjectValue('balloon');
52 if (balloon) {
e7ade592
DC
53 return Proxmox.Utils.format_size(balloon*1024*1024) + "/" +
54 Proxmox.Utils.format_size(value*1024*1024);
f97670f5
DM
55
56 }
e7ade592 57 return Proxmox.Utils.format_size(value*1024*1024);
f97670f5
DM
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',
72408db9 66 defaultValue: '1',
f97670f5
DM
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
72408db9
EK
78 var res = Ext.String.format('{0} ({1} sockets, {2} cores)',
79 sockets*cores, sockets, cores);
80
f97670f5
DM
81 if (model) {
82 res += ' [' + model + ']';
83 }
84
85 if (numa) {
86 res += ' [numa=' + numa +']';
87 }
88
89 if (vcpus) {
90 res += ' [vcpus=' + vcpus +']';
91 }
92
93 if (cpulimit) {
94 res += ' [cpulimit=' + cpulimit +']';
95 }
96
97 if (cpuunits) {
98 res += ' [cpuunits=' + cpuunits +']';
99 }
100
101 return res;
102 }
103 },
104 keyboard: {
105 header: gettext('Keyboard Layout'),
106 never_delete: true,
107 editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.KeyboardEdit' : undefined,
108 tdCls: 'pve-itype-icon-keyboard',
109 defaultValue: '',
110 renderer: PVE.Utils.render_kvm_language
111 },
112 vga: {
113 header: gettext('Display'),
114 editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
115 never_delete: true,
116 tdCls: 'pve-itype-icon-display',
117 defaultValue: '',
118 renderer: PVE.Utils.render_kvm_vga_driver
119 },
120 cores: {
121 visible: false
122 },
123 cpu: {
124 visible: false
125 },
126 numa: {
127 visible: false
128 },
129 balloon: {
130 visible: false
131 },
132 hotplug: {
133 visible: false
134 },
135 vcpus: {
136 visible: false
137 },
138 cpuunits: {
139 visible: false
140 },
141 cpulimit: {
142 visible: false
3b37ab6d
DC
143 },
144 bios: {
145 visible: false
f97670f5
DM
146 }
147
148 };
149
150 for (i = 0; i < 4; i++) {
72408db9 151 confid = "ide" + i.toString();
f97670f5
DM
152 rows[confid] = {
153 group: 1,
154 tdCls: 'pve-itype-icon-storage',
155 editor: 'PVE.qemu.HDEdit',
156 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
157 header: gettext('Hard Disk') + ' (' + confid +')',
158 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
159 };
160 }
161 for (i = 0; i < 6; i++) {
72408db9 162 confid = "sata" + i.toString();
f97670f5
DM
163 rows[confid] = {
164 group: 1,
165 tdCls: 'pve-itype-icon-storage',
166 editor: 'PVE.qemu.HDEdit',
167 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
168 header: gettext('Hard Disk') + ' (' + confid +')',
169 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
170 };
171 }
172 for (i = 0; i < 16; i++) {
72408db9 173 confid = "scsi" + i.toString();
f97670f5
DM
174 rows[confid] = {
175 group: 1,
176 tdCls: 'pve-itype-icon-storage',
177 editor: 'PVE.qemu.HDEdit',
178 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
179 header: gettext('Hard Disk') + ' (' + confid +')',
180 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
181 };
182 }
183 for (i = 0; i < 16; i++) {
72408db9 184 confid = "virtio" + i.toString();
f97670f5
DM
185 rows[confid] = {
186 group: 1,
187 tdCls: 'pve-itype-icon-storage',
188 editor: 'PVE.qemu.HDEdit',
189 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
190 header: gettext('Hard Disk') + ' (' + confid +')',
191 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
192 };
193 }
194 for (i = 0; i < 32; i++) {
72408db9 195 confid = "net" + i.toString();
f97670f5
DM
196 rows[confid] = {
197 group: 2,
198 tdCls: 'pve-itype-icon-network',
199 editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
200 never_delete: caps.vms['VM.Config.Network'] ? false : true,
201 header: gettext('Network Device') + ' (' + confid +')'
202 };
203 }
194c9b09
DC
204 rows.efidisk0 = {
205 group: 3,
206 tdCls: 'pve-itype-icon-storage',
207 editor: null,
208 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
209 header: gettext('EFI Disk')
210 };
fa522e48 211 for (i = 0; i < 5; i++) {
72408db9 212 confid = "usb" + i.toString();
fa522e48
DC
213 rows[confid] = {
214 group: 4,
215 tdCls: 'pve-itype-icon-usb',
2668ba64
DC
216 editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined,
217 never_delete: caps.nodes['Sys.Console'] ? false : true,
fa522e48
DC
218 header: gettext('USB Device') + ' (' + confid + ')'
219 };
220 }
ec3d8372 221 for (i = 0; i < 4; i++) {
72408db9 222 confid = "hostpci" + i.toString();
ec3d8372
DC
223 rows[confid] = {
224 group: 5,
225 tdCls: 'pve-itype-icon-pci',
226 never_delete: caps.nodes['Sys.Console'] ? false : true,
227 header: gettext('PCI Device') + ' (' + confid + ')'
228 };
229 }
a488da8c
DC
230 for (i = 0; i < 4; i++) {
231 confid = "serial" + i.toString();
232 rows[confid] = {
233 group: 6,
234 tdCls: 'pve-itype-icon-serial',
235 never_delete: caps.nodes['Sys.Console'] ? false : true,
236 header: gettext('Serial Port') + ' (' + confid + ')'
237 };
238 }
f97670f5 239 for (i = 0; i < 8; i++) {
72408db9 240 rows["unused" + i.toString()] = {
cd86ab24 241 group: 99,
f97670f5
DM
242 tdCls: 'pve-itype-icon-storage',
243 editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
72408db9 244 header: gettext('Unused Disk') + ' ' + i.toString()
f97670f5
DM
245 };
246 }
247
248 var sorterFn = function(rec1, rec2) {
249 var v1 = rec1.data.key;
250 var v2 = rec2.data.key;
251 var g1 = rows[v1].group || 0;
252 var g2 = rows[v2].group || 0;
253
254 return (g1 !== g2) ?
255 (g1 > g2 ? 1 : -1) : (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
256 };
257
258 var reload = function() {
259 me.rstore.load();
260 };
261
262 var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config';
263
264 var sm = Ext.create('Ext.selection.RowModel', {});
265
266 var run_editor = function() {
267 var rec = sm.getSelection()[0];
268 if (!rec) {
269 return;
270 }
271
272 var rowdef = rows[rec.data.key];
273 if (!rowdef.editor) {
274 return;
275 }
276
277 var editor = rowdef.editor;
278 if (rowdef.tdCls == 'pve-itype-icon-storage') {
279 var value = me.getObjectValue(rec.data.key, '', true);
280 if (value.match(/media=cdrom/)) {
281 editor = 'PVE.qemu.CDEdit';
282 }
283 }
284
285 var win;
286
287 if (Ext.isString(editor)) {
288 win = Ext.create(editor, {
289 pveSelNode: me.pveSelNode,
290 confid: rec.data.key,
291 url: '/api2/extjs/' + baseurl
292 });
293 } else {
294 var config = Ext.apply({
295 pveSelNode: me.pveSelNode,
296 confid: rec.data.key,
297 url: '/api2/extjs/' + baseurl
298 }, rowdef.editor);
299 win = Ext.createWidget(rowdef.editor.xtype, config);
300 win.load();
301 }
302
303 win.show();
304 win.on('destroy', reload);
305 };
306
307 var run_diskthrottle = function() {
308 var rec = sm.getSelection()[0];
309 if (!rec) {
310 return;
311 }
312
313 var win = Ext.create('PVE.qemu.HDThrottle', {
314 pveSelNode: me.pveSelNode,
315 confid: rec.data.key,
316 url: '/api2/extjs/' + baseurl
317 });
318
319 win.show();
320 win.on('destroy', reload);
321 };
322
323 var run_resize = function() {
324 var rec = sm.getSelection()[0];
325 if (!rec) {
326 return;
327 }
328
329 var win = Ext.create('PVE.window.HDResize', {
330 disk: rec.data.key,
331 nodename: nodename,
332 vmid: vmid
333 });
334
335 win.show();
336
337 win.on('destroy', reload);
338 };
339
340 var run_cpuoptions = function() {
341 var sockets = me.getObjectValue('sockets', 1);
342 var cores = me.getObjectValue('cores', 1);
343
344 var win = Ext.create('PVE.qemu.CPUOptions', {
345 maxvcpus: sockets * cores,
346 vmid: vmid,
347 pveSelNode: me.pveSelNode,
348 url: '/api2/extjs/' + baseurl
349 });
350
351 win.show();
352
353 win.on('destroy', reload);
354 };
355
356 var run_move = function() {
357 var rec = sm.getSelection()[0];
358 if (!rec) {
359 return;
360 }
361
362 var win = Ext.create('PVE.window.HDMove', {
363 disk: rec.data.key,
364 nodename: nodename,
365 vmid: vmid
366 });
367
368 win.show();
369
370 win.on('destroy', reload);
371 };
372
5720fafa 373 var edit_btn = new Proxmox.button.Button({
f97670f5
DM
374 text: gettext('Edit'),
375 selModel: sm,
376 disabled: true,
377 handler: run_editor
378 });
379
5720fafa 380 var resize_btn = new Proxmox.button.Button({
f97670f5
DM
381 text: gettext('Resize disk'),
382 selModel: sm,
383 disabled: true,
384 handler: run_resize
385 });
386
5720fafa 387 var move_btn = new Proxmox.button.Button({
f97670f5
DM
388 text: gettext('Move disk'),
389 selModel: sm,
390 disabled: true,
391 handler: run_move
392 });
393
5720fafa 394 var diskthrottle_btn = new Proxmox.button.Button({
f97670f5
DM
395 text: gettext('Disk Throttle'),
396 selModel: sm,
397 disabled: true,
398 handler: run_diskthrottle
399 });
400
401 var cpuoptions_btn = new Ext.Button({
402 text: gettext('CPU options'),
403 handler: run_cpuoptions
404 });
405
5720fafa 406 var remove_btn = new Proxmox.button.Button({
f97670f5 407 text: gettext('Remove'),
877fce54
EK
408 defaultText: gettext('Remove'),
409 altText: gettext('Detach'),
f97670f5
DM
410 selModel: sm,
411 disabled: true,
412 dangerous: true,
413 confirmMsg: function(rec) {
ff5a8a9b
EK
414 var warn = gettext('Are you sure you want to remove entry {0}');
415 if (this.text === this.altText) {
416 warn = gettext('Are you sure you want to detach entry {0}');
417 }
418
419 var entry = rec.data.key;
420 var msg = Ext.String.format(warn, "'"
421 + me.renderKey(entry, {}, rec) + "'");
422
423 if (entry.match(/^unused\d+$/)) {
16152937 424 msg += " " + gettext('This will permanently erase all data.');
f97670f5
DM
425 }
426
427 return msg;
428 },
429 handler: function(b, e, rec) {
e7ade592 430 Proxmox.Utils.API2Request({
f97670f5
DM
431 url: '/api2/extjs/' + baseurl,
432 waitMsgTarget: me,
433 method: 'PUT',
434 params: {
435 'delete': rec.data.key
436 },
437 callback: function() {
438 reload();
439 },
440 failure: function (response, opts) {
441 Ext.Msg.alert('Error', response.htmlStatus);
442 }
443 });
877fce54
EK
444 },
445 listeners: {
446 render: function(btn) {
447 // hack: calculate an optimal button width on first display
448 // to prevent the whole toolbar to move when we switch
449 // between the "Remove" and "Detach" labels
450 var def = btn.getSize().width;
451
452 btn.setText(btn.altText);
453 var alt = btn.getSize().width;
454
455 btn.setText(btn.defaultText);
456
457 var optimal = alt > def ? alt : def;
458 btn.setSize({ width: optimal });
459 }
f97670f5
DM
460 }
461 });
462
5720fafa 463 var revert_btn = new Proxmox.button.Button({
f97670f5
DM
464 text: gettext('Revert'),
465 selModel: sm,
466 disabled: true,
467 handler: function(b, e, rec) {
468 var rowdef = me.rows[rec.data.key] || {};
469 var keys = rowdef.multiKey || [ rec.data.key ];
470 var revert = keys.join(',');
e7ade592 471 Proxmox.Utils.API2Request({
f97670f5
DM
472 url: '/api2/extjs/' + baseurl,
473 waitMsgTarget: me,
474 method: 'PUT',
475 params: {
476 'revert': revert
477 },
478 callback: function() {
479 reload();
480 },
481 failure: function (response, opts) {
482 Ext.Msg.alert('Error',response.htmlStatus);
483 }
484 });
485 }
486 });
487
3b37ab6d
DC
488 var efidisk_menuitem = Ext.create('Ext.menu.Item',{
489 text: gettext('EFI Disk'),
490 iconCls: 'pve-itype-icon-storage',
491 disabled: !caps.vms['VM.Config.Disk'],
492 handler: function() {
1535df62
DC
493
494 var rstoredata = me.rstore.getData().map;
495 // check if ovmf is configured
496 if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') {
497 var win = Ext.create('PVE.qemu.EFIDiskEdit', {
498 url: '/api2/extjs/' + baseurl,
499 pveSelNode: me.pveSelNode
500 });
501 win.on('destroy', reload);
502 win.show();
503 } else {
504 Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
505 }
506
3b37ab6d
DC
507 }
508 });
509
f97670f5
DM
510 var set_button_status = function() {
511 var sm = me.getSelectionModel();
512 var rec = sm.getSelection()[0];
513
1535df62
DC
514 // disable button when we have an efidisk already
515 // disable is ok in this case, because you can instantly
516 // see that there is already one
517 efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined);
2668ba64
DC
518 // en/disable usb add button
519 var count = 0;
520 me.rstore.getData().items.forEach(function(item){
521 if (/^usb\d+/.test(item.id)) {
522 count++;
523 }
524 });
525 me.down('#addusb').setDisabled((count >= 5));
3b37ab6d 526
f97670f5
DM
527 if (!rec) {
528 remove_btn.disable();
529 edit_btn.disable();
530 resize_btn.disable();
531 move_btn.disable();
532 diskthrottle_btn.disable();
533 revert_btn.disable();
534 return;
535 }
536 var key = rec.data.key;
537 var value = rec.data.value;
538 var rowdef = rows[key];
539
540 var pending = rec.data['delete'] || me.hasPendingChanges(key);
877fce54 541 var isUsedDisk = !key.match(/^unused\d+/) &&
25e0bc2d
DC
542 rowdef.tdCls == 'pve-itype-icon-storage' &&
543 (value && !value.match(/media=cdrom/));
f97670f5 544
3b37ab6d
DC
545 var isEfi = (key === 'efidisk0');
546
f97670f5 547 remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true));
877fce54 548 remove_btn.setText(isUsedDisk ? remove_btn.altText : remove_btn.defaultText);
f97670f5
DM
549
550 edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor);
551
877fce54 552 resize_btn.setDisabled(pending || !isUsedDisk);
f97670f5 553
877fce54 554 move_btn.setDisabled(pending || !isUsedDisk);
f97670f5 555
877fce54 556 diskthrottle_btn.setDisabled(pending || !isUsedDisk || isEfi);
f97670f5
DM
557
558 revert_btn.setDisabled(!pending);
559
560 };
561
f09dd6c1 562 Ext.apply(me, {
f97670f5
DM
563 url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending',
564 interval: 5000,
565 selModel: sm,
f97670f5
DM
566 tbar: [
567 {
568 text: gettext('Add'),
569 menu: new Ext.menu.Menu({
570 items: [
571 {
572 text: gettext('Hard Disk'),
573 iconCls: 'pve-itype-icon-storage',
574 disabled: !caps.vms['VM.Config.Disk'],
575 handler: function() {
576 var win = Ext.create('PVE.qemu.HDEdit', {
577 url: '/api2/extjs/' + baseurl,
578 pveSelNode: me.pveSelNode
579 });
580 win.on('destroy', reload);
581 win.show();
582 }
583 },
584 {
585 text: gettext('CD/DVD Drive'),
586 iconCls: 'pve-itype-icon-cdrom',
587 disabled: !caps.vms['VM.Config.Disk'],
588 handler: function() {
589 var win = Ext.create('PVE.qemu.CDEdit', {
590 url: '/api2/extjs/' + baseurl,
591 pveSelNode: me.pveSelNode
592 });
593 win.on('destroy', reload);
594 win.show();
595 }
596 },
597 {
598 text: gettext('Network Device'),
599 iconCls: 'pve-itype-icon-network',
600 disabled: !caps.vms['VM.Config.Network'],
601 handler: function() {
602 var win = Ext.create('PVE.qemu.NetworkEdit', {
603 url: '/api2/extjs/' + baseurl,
604 pveSelNode: me.pveSelNode
605 });
606 win.on('destroy', reload);
607 win.show();
608 }
3b37ab6d 609 },
2668ba64
DC
610 efidisk_menuitem,
611 {
612 text: gettext('USB Device'),
613 itemId: 'addusb',
614 iconCls: 'pve-itype-icon-usb',
615 disabled: !caps.nodes['Sys.Console'],
616 handler: function() {
617 var win = Ext.create('PVE.qemu.USBEdit', {
618 url: '/api2/extjs/' + baseurl,
619 pveSelNode: me.pveSelNode
620 });
621 win.on('destroy', reload);
622 win.show();
623 }
624 }
f97670f5
DM
625 ]
626 })
fa522e48 627 },
f97670f5
DM
628 remove_btn,
629 edit_btn,
630 resize_btn,
631 move_btn,
632 diskthrottle_btn,
633 cpuoptions_btn,
634 revert_btn
635 ],
636 rows: rows,
637 sorterFn: sorterFn,
638 listeners: {
639 itemdblclick: run_editor,
640 selectionchange: set_button_status
641 }
642 });
643
644 me.callParent();
645
f09dd6c1 646 me.on('activate', me.rstore.startUpdate);
f97670f5
DM
647 me.on('destroy', me.rstore.stopUpdate);
648
f09dd6c1 649 me.mon(me.rstore, 'refresh', function() {
f97670f5
DM
650 set_button_status();
651 });
652 }
653});