]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/qemu/HardwareView.js
1 Ext
.define('PVE.qemu.HardwareView', {
2 extend
: 'Proxmox.grid.PendingObjectGrid',
3 alias
: ['widget.PVE.qemu.HardwareView'],
5 onlineHelp
: 'qm_virtual_machines_settings',
7 renderKey: function(key
, metaData
, rec
, rowIndex
, colIndex
, store
) {
10 var rowdef
= rows
[key
] || {};
11 var iconCls
= rowdef
.iconCls
;
13 var txt
= (rowdef
.header
|| key
);
15 metaData
.tdAttr
= "valign=middle";
18 metaData
.tdCls
= rowdef
.tdCls
;
19 if (rowdef
.tdCls
== 'pve-itype-icon-storage') {
20 var value
= me
.getObjectValue(key
, '', false);
22 value
= me
.getObjectValue(key
, '', true);
24 if (value
.match(/vm-.*-cloudinit/)) {
25 metaData
.tdCls
= 'pve-itype-icon-cloud';
26 return rowdef
.cloudheader
;
27 } else if (value
.match(/media=cdrom/)) {
28 metaData
.tdCls
= 'pve-itype-icon-cdrom';
29 return rowdef
.cdheader
;
33 icon
= "<i class='pve-grid-fa fa fa-fw fa-" + iconCls
+ "'></i>";
34 metaData
.tdCls
+= " pve-itype-fa";
39 initComponent : function() {
43 var nodename
= me
.pveSelNode
.data
.node
;
45 throw "no node name specified";
48 var vmid
= me
.pveSelNode
.data
.vmid
;
50 throw "no VM ID specified";
53 var caps
= Ext
.state
.Manager
.get('GuiCap');
54 var diskCap
= caps
.vms
['VM.Config.Disk'];
56 /*jslint confusion: true */
59 header
: gettext('Memory'),
60 editor
: caps
.vms
['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined,
63 tdCls
: 'pve-itype-icon-memory',
65 multiKey
: ['memory', 'balloon', 'shares'],
66 renderer: function(value
, metaData
, record
, ri
, ci
, store
, pending
) {
69 var max
= me
.getObjectValue('memory', 512, pending
);
70 var balloon
= me
.getObjectValue('balloon', undefined, pending
);
71 var shares
= me
.getObjectValue('shares', undefined, pending
);
73 res
= Proxmox
.Utils
.format_size(max
*1024*1024);
75 if (balloon
!== undefined && balloon
> 0) {
76 res
= Proxmox
.Utils
.format_size(balloon
*1024*1024) + "/" + res
;
79 res
+= ' [shares=' + shares
+']';
81 } else if (balloon
=== 0) {
82 res
+= ' [balloon=0]';
88 header
: gettext('Processors'),
90 editor
: (caps
.vms
['VM.Config.CPU'] || caps
.vms
['VM.Config.HWType']) ?
91 'PVE.qemu.ProcessorEdit' : undefined,
92 tdCls
: 'pve-itype-icon-processor',
95 multiKey
: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'],
96 renderer: function(value
, metaData
, record
, rowIndex
, colIndex
, store
, pending
) {
98 var sockets
= me
.getObjectValue('sockets', 1, pending
);
99 var model
= me
.getObjectValue('cpu', undefined, pending
);
100 var cores
= me
.getObjectValue('cores', 1, pending
);
101 var numa
= me
.getObjectValue('numa', undefined, pending
);
102 var vcpus
= me
.getObjectValue('vcpus', undefined, pending
);
103 var cpulimit
= me
.getObjectValue('cpulimit', undefined, pending
);
104 var cpuunits
= me
.getObjectValue('cpuunits', undefined, pending
);
106 var res
= Ext
.String
.format('{0} ({1} sockets, {2} cores)',
107 sockets
*cores
, sockets
, cores
);
110 res
+= ' [' + model
+ ']';
114 res
+= ' [numa=' + numa
+']';
118 res
+= ' [vcpus=' + vcpus
+']';
122 res
+= ' [cpulimit=' + cpulimit
+']';
126 res
+= ' [cpuunits=' + cpuunits
+']';
136 editor
: caps
.vms
['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined,
138 iconCls
: 'microchip',
139 renderer
: PVE
.Utils
.render_qemu_bios
142 header
: gettext('Display'),
143 editor
: caps
.vms
['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
145 tdCls
: 'pve-itype-icon-display',
148 renderer
: PVE
.Utils
.render_kvm_vga_driver
151 header
: gettext('Machine'),
152 editor
: caps
.vms
['VM.Config.HWType'] ? {
153 xtype
: 'proxmoxWindowEdit',
154 subject
: gettext('Machine'),
157 xtype
: 'proxmoxKVComboBox',
159 value
: '__default__',
160 fieldLabel
: gettext('Machine'),
162 ['__default__', PVE
.Utils
.render_qemu_machine('')],
170 renderer
: PVE
.Utils
.render_qemu_machine
173 header
: gettext('SCSI Controller'),
175 editor
: caps
.vms
['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined,
176 renderer
: PVE
.Utils
.render_scsihw
,
209 /*jslint confusion: false */
211 PVE
.Utils
.forEachBus(undefined, function(type
, id
) {
212 var confid
= type
+ id
;
215 tdCls
: 'pve-itype-icon-storage',
216 editor
: 'PVE.qemu.HDEdit',
217 never_delete
: caps
.vms
['VM.Config.Disk'] ? false : true,
218 header
: gettext('Hard Disk') + ' (' + confid
+')',
219 cdheader
: gettext('CD/DVD Drive') + ' (' + confid
+')',
220 cloudheader
: gettext('CloudInit Drive') + ' (' + confid
+ ')'
223 for (i
= 0; i
< 32; i
++) {
224 confid
= "net" + i
.toString();
228 tdCls
: 'pve-itype-icon-network',
229 editor
: caps
.vms
['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
230 never_delete
: caps
.vms
['VM.Config.Network'] ? false : true,
231 header
: gettext('Network Device') + ' (' + confid
+')'
236 tdCls
: 'pve-itype-icon-storage',
238 never_delete
: caps
.vms
['VM.Config.Disk'] ? false : true,
239 header
: gettext('EFI Disk')
241 for (i
= 0; i
< 5; i
++) {
242 confid
= "usb" + i
.toString();
246 tdCls
: 'pve-itype-icon-usb',
247 editor
: caps
.nodes
['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined,
248 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
249 header
: gettext('USB Device') + ' (' + confid
+ ')'
252 for (i
= 0; i
< 4; i
++) {
253 confid
= "hostpci" + i
.toString();
257 tdCls
: 'pve-itype-icon-pci',
258 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
259 editor
: caps
.nodes
['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined,
260 header
: gettext('PCI Device') + ' (' + confid
+ ')'
263 for (i
= 0; i
< 4; i
++) {
264 confid
= "serial" + i
.toString();
268 tdCls
: 'pve-itype-icon-serial',
269 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
270 header
: gettext('Serial Port') + ' (' + confid
+ ')'
273 for (i
= 0; i
< 256; i
++) {
274 rows
["unused" + i
.toString()] = {
277 tdCls
: 'pve-itype-icon-storage',
278 editor
: caps
.vms
['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
279 header
: gettext('Unused Disk') + ' ' + i
.toString()
283 var sorterFn = function(rec1
, rec2
) {
284 var v1
= rec1
.data
.key
;
285 var v2
= rec2
.data
.key
;
286 var g1
= rows
[v1
].group
|| 0;
287 var g2
= rows
[v2
].group
|| 0;
288 var order1
= rows
[v1
].order
|| 0;
289 var order2
= rows
[v2
].order
|| 0;
291 if ((g1
- g2
) !== 0) {
295 if ((order1
- order2
) !== 0) {
296 return order1
- order2
;
301 } else if (v1
< v2
) {
308 var reload = function() {
312 var baseurl
= 'nodes/' + nodename
+ '/qemu/' + vmid
+ '/config';
314 var sm
= Ext
.create('Ext.selection.RowModel', {});
316 var run_editor = function() {
317 var rec
= sm
.getSelection()[0];
322 var rowdef
= rows
[rec
.data
.key
];
323 if (!rowdef
.editor
) {
327 var editor
= rowdef
.editor
;
328 if (rowdef
.tdCls
== 'pve-itype-icon-storage') {
332 var value
= me
.getObjectValue(rec
.data
.key
, '', true);
333 if (value
.match(/vm-.*-cloudinit/)) {
335 } else if (value
.match(/media=cdrom/)) {
336 editor
= 'PVE.qemu.CDEdit';
342 if (Ext
.isString(editor
)) {
343 win
= Ext
.create(editor
, {
344 pveSelNode
: me
.pveSelNode
,
345 confid
: rec
.data
.key
,
346 url
: '/api2/extjs/' + baseurl
349 var config
= Ext
.apply({
350 pveSelNode
: me
.pveSelNode
,
351 confid
: rec
.data
.key
,
352 url
: '/api2/extjs/' + baseurl
354 win
= Ext
.createWidget(rowdef
.editor
.xtype
, config
);
359 win
.on('destroy', reload
);
362 var run_resize = function() {
363 var rec
= sm
.getSelection()[0];
368 var win
= Ext
.create('PVE.window.HDResize', {
376 win
.on('destroy', reload
);
379 var run_move = function() {
380 var rec
= sm
.getSelection()[0];
385 var win
= Ext
.create('PVE.window.HDMove', {
393 win
.on('destroy', reload
);
396 var edit_btn
= new Proxmox
.button
.Button({
397 text
: gettext('Edit'),
403 var resize_btn
= new Proxmox
.button
.Button({
404 text
: gettext('Resize disk'),
410 var move_btn
= new Proxmox
.button
.Button({
411 text
: gettext('Move disk'),
417 var remove_btn
= new Proxmox
.button
.Button({
418 text
: gettext('Remove'),
419 defaultText
: gettext('Remove'),
420 altText
: gettext('Detach'),
425 confirmMsg: function(rec
) {
426 var warn
= gettext('Are you sure you want to remove entry {0}');
427 if (this.text
=== this.altText
) {
428 warn
= gettext('Are you sure you want to detach entry {0}');
431 var entry
= rec
.data
.key
;
432 var msg
= Ext
.String
.format(warn
, "'"
433 + me
.renderKey(entry
, {}, rec
) + "'");
435 if (entry
.match(/^unused\d+$/)) {
436 msg
+= " " + gettext('This will permanently erase all data.');
441 handler: function(b
, e
, rec
) {
442 Proxmox
.Utils
.API2Request({
443 url
: '/api2/extjs/' + baseurl
,
445 method
: b
.RESTMethod
,
447 'delete': rec
.data
.key
449 callback: function() {
452 failure: function (response
, opts
) {
453 Ext
.Msg
.alert('Error', response
.htmlStatus
);
455 success: function(response
, options
) {
456 if (b
.RESTMethod
=== 'POST') {
457 var upid
= response
.result
.data
;
458 var win
= Ext
.create('Proxmox.window.TaskProgress', {
461 destroy: function () {
472 render: function(btn
) {
473 // hack: calculate an optimal button width on first display
474 // to prevent the whole toolbar to move when we switch
475 // between the "Remove" and "Detach" labels
476 var def
= btn
.getSize().width
;
478 btn
.setText(btn
.altText
);
479 var alt
= btn
.getSize().width
;
481 btn
.setText(btn
.defaultText
);
483 var optimal
= alt
> def
? alt
: def
;
484 btn
.setSize({ width
: optimal
});
489 var revert_btn
= new Proxmox
.button
.Button({
490 text
: gettext('Revert'),
493 handler: function(b
, e
, rec
) {
494 var rowdef
= me
.rows
[rec
.data
.key
] || {};
495 var keys
= rowdef
.multiKey
|| [ rec
.data
.key
];
496 var revert
= keys
.join(',');
497 Proxmox
.Utils
.API2Request({
498 url
: '/api2/extjs/' + baseurl
,
504 callback: function() {
507 failure: function (response
, opts
) {
508 Ext
.Msg
.alert('Error',response
.htmlStatus
);
514 var efidisk_menuitem
= Ext
.create('Ext.menu.Item',{
515 text
: gettext('EFI Disk'),
516 iconCls
: 'pve-itype-icon-storage',
517 disabled
: !caps
.vms
['VM.Config.Disk'],
518 handler: function() {
520 var rstoredata
= me
.rstore
.getData().map
;
521 // check if ovmf is configured
522 if (rstoredata
.bios
&& rstoredata
.bios
.data
.value
=== 'ovmf') {
523 var win
= Ext
.create('PVE.qemu.EFIDiskEdit', {
524 url
: '/api2/extjs/' + baseurl
,
525 pveSelNode
: me
.pveSelNode
527 win
.on('destroy', reload
);
530 Ext
.Msg
.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
536 var set_button_status = function() {
537 var sm
= me
.getSelectionModel();
538 var rec
= sm
.getSelection()[0];
540 // disable button when we have an efidisk already
541 // disable is ok in this case, because you can instantly
542 // see that there is already one
543 efidisk_menuitem
.setDisabled(me
.rstore
.getData().map
.efidisk0
!== undefined);
544 // en/disable usb add button
547 var hasCloudInit
= false;
548 me
.rstore
.getData().items
.forEach(function(item
){
549 if (/^usb\d+/.test(item
.id
)) {
551 } else if (/^hostpci\d+/.test(item
.id
)) {
554 if (!hasCloudInit
&& /vm-.*-cloudinit/.test(item
.data
.value
)) {
559 // heuristic only for disabling some stuff, the backend has the final word.
560 var noSysConsolePerm
= !caps
.nodes
['Sys.Console'];
562 me
.down('#addusb').setDisabled(noSysConsolePerm
|| (usbcount
>= 5));
563 me
.down('#addpci').setDisabled(noSysConsolePerm
|| (pcicount
>= 4));
564 me
.down('#addci').setDisabled(noSysConsolePerm
|| hasCloudInit
);
567 remove_btn
.disable();
569 resize_btn
.disable();
571 revert_btn
.disable();
574 var key
= rec
.data
.key
;
575 var value
= rec
.data
.value
;
576 var rowdef
= rows
[key
];
578 var pending
= rec
.data
['delete'] || me
.hasPendingChanges(key
);
579 var isUnusedDisk
= key
.match(/^unused\d+/);
580 var isUsedDisk
= !isUnusedDisk
&&
581 rowdef
.tdCls
== 'pve-itype-icon-storage' &&
582 (value
&& !value
.match(/media=cdrom/));
584 var isCloudInit
= (value
&& value
.toString().match(/vm-.*-cloudinit/));
586 var isEfi
= (key
=== 'efidisk0');
588 remove_btn
.setDisabled(rec
.data
['delete'] || (rowdef
.never_delete
=== true) || (isUnusedDisk
&& !diskCap
));
589 remove_btn
.setText((isUsedDisk
&& !isCloudInit
) ? remove_btn
.altText
: remove_btn
.defaultText
);
590 remove_btn
.RESTMethod
= isUnusedDisk
? 'POST':'PUT';
592 edit_btn
.setDisabled(rec
.data
['delete'] || !rowdef
.editor
|| isCloudInit
|| !diskCap
);
594 resize_btn
.setDisabled(pending
|| !isUsedDisk
|| !diskCap
);
596 move_btn
.setDisabled(pending
|| !isUsedDisk
|| !diskCap
);
598 revert_btn
.setDisabled(!pending
);
603 url
: '/api2/json/' + 'nodes/' + nodename
+ '/qemu/' + vmid
+ '/pending',
606 run_editor
: run_editor
,
609 text
: gettext('Add'),
610 menu
: new Ext
.menu
.Menu({
613 text
: gettext('Hard Disk'),
614 iconCls
: 'pve-itype-icon-storage',
615 disabled
: !caps
.vms
['VM.Config.Disk'],
616 handler: function() {
617 var win
= Ext
.create('PVE.qemu.HDEdit', {
618 url
: '/api2/extjs/' + baseurl
,
619 pveSelNode
: me
.pveSelNode
621 win
.on('destroy', reload
);
626 text
: gettext('CD/DVD Drive'),
627 iconCls
: 'pve-itype-icon-cdrom',
628 disabled
: !caps
.vms
['VM.Config.Disk'],
629 handler: function() {
630 var win
= Ext
.create('PVE.qemu.CDEdit', {
631 url
: '/api2/extjs/' + baseurl
,
632 pveSelNode
: me
.pveSelNode
634 win
.on('destroy', reload
);
639 text
: gettext('Network Device'),
640 iconCls
: 'pve-itype-icon-network',
641 disabled
: !caps
.vms
['VM.Config.Network'],
642 handler: function() {
643 var win
= Ext
.create('PVE.qemu.NetworkEdit', {
644 url
: '/api2/extjs/' + baseurl
,
645 pveSelNode
: me
.pveSelNode
,
648 win
.on('destroy', reload
);
654 text
: gettext('USB Device'),
656 iconCls
: 'pve-itype-icon-usb',
657 disabled
: !caps
.nodes
['Sys.Console'],
658 handler: function() {
659 var win
= Ext
.create('PVE.qemu.USBEdit', {
660 url
: '/api2/extjs/' + baseurl
,
661 pveSelNode
: me
.pveSelNode
663 win
.on('destroy', reload
);
668 text
: gettext('PCI Device'),
670 iconCls
: 'pve-itype-icon-pci',
671 disabled
: !caps
.nodes
['Sys.Console'],
672 handler: function() {
673 var win
= Ext
.create('PVE.qemu.PCIEdit', {
674 url
: '/api2/extjs/' + baseurl
,
675 pveSelNode
: me
.pveSelNode
677 win
.on('destroy', reload
);
682 text
: gettext('Serial Port'),
684 iconCls
: 'pve-itype-icon-serial',
685 disabled
: !caps
.vms
['VM.Config.Options'],
686 handler: function() {
687 var win
= Ext
.create('PVE.qemu.SerialEdit', {
688 url
: '/api2/extjs/' + baseurl
690 win
.on('destroy', reload
);
695 text
: gettext('CloudInit Drive'),
697 iconCls
: 'pve-itype-icon-cloud',
698 disabled
: !caps
.nodes
['Sys.Console'],
699 handler: function() {
700 var win
= Ext
.create('PVE.qemu.CIDriveEdit', {
701 url
: '/api2/extjs/' + baseurl
,
702 pveSelNode
: me
.pveSelNode
704 win
.on('destroy', reload
);
720 itemdblclick
: run_editor
,
721 selectionchange
: set_button_status
727 me
.on('activate', me
.rstore
.startUpdate
);
728 me
.on('destroy', me
.rstore
.stopUpdate
);
730 me
.mon(me
.rstore
, 'refresh', function() {