]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/qemu/HardwareView.js
1 Ext
.define('PVE.qemu.HardwareView', {
2 extend
: 'PVE.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
] || {};
12 metaData
.tdAttr
= "valign=middle";
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
;
24 return rowdef
.header
|| key
;
27 initComponent : function() {
31 var nodename
= me
.pveSelNode
.data
.node
;
33 throw "no node name specified";
36 var vmid
= me
.pveSelNode
.data
.vmid
;
38 throw "no VM ID specified";
41 var caps
= Ext
.state
.Manager
.get('GuiCap');
45 header
: gettext('Memory'),
46 editor
: caps
.vms
['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined,
49 tdCls
: 'pve-itype-icon-memory',
50 renderer: function(value
, metaData
, record
) {
51 var balloon
= me
.getObjectValue('balloon');
53 return PVE
.Utils
.format_size(balloon
*1024*1024) + "/" +
54 PVE
.Utils
.format_size(value
*1024*1024);
57 return PVE
.Utils
.format_size(value
*1024*1024);
61 header
: gettext('Processors'),
63 editor
: (caps
.vms
['VM.Config.CPU'] || caps
.vms
['VM.Config.HWType']) ?
64 'PVE.qemu.ProcessorEdit' : undefined,
65 tdCls
: 'pve-itype-icon-processor',
67 multiKey
: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'],
68 renderer: function(value
, metaData
, record
, rowIndex
, colIndex
, store
, pending
) {
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
);
78 var res
= Ext
.String
.format('{0} ({1} sockets, {2} cores)',
79 sockets
*cores
, sockets
, cores
);
82 res
+= ' [' + model
+ ']';
86 res
+= ' [numa=' + numa
+']';
90 res
+= ' [vcpus=' + vcpus
+']';
94 res
+= ' [cpulimit=' + cpulimit
+']';
98 res
+= ' [cpuunits=' + cpuunits
+']';
105 header
: gettext('Keyboard Layout'),
107 editor
: caps
.vms
['VM.Config.Options'] ? 'PVE.qemu.KeyboardEdit' : undefined,
108 tdCls
: 'pve-itype-icon-keyboard',
110 renderer
: PVE
.Utils
.render_kvm_language
113 header
: gettext('Display'),
114 editor
: caps
.vms
['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
116 tdCls
: 'pve-itype-icon-display',
118 renderer
: PVE
.Utils
.render_kvm_vga_driver
150 for (i
= 0; i
< 4; i
++) {
151 confid
= "ide" + i
.toString();
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
+')'
161 for (i
= 0; i
< 6; i
++) {
162 confid
= "sata" + i
.toString();
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
+')'
172 for (i
= 0; i
< 16; i
++) {
173 confid
= "scsi" + i
.toString();
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
+')'
183 for (i
= 0; i
< 16; i
++) {
184 confid
= "virtio" + i
.toString();
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
+')'
194 for (i
= 0; i
< 32; i
++) {
195 confid
= "net" + i
.toString();
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
+')'
206 tdCls
: 'pve-itype-icon-storage',
208 never_delete
: caps
.vms
['VM.Config.Disk'] ? false : true,
209 header
: gettext('EFI Disk')
211 for (i
= 0; i
< 5; i
++) {
212 confid
= "usb" + i
.toString();
215 tdCls
: 'pve-itype-icon-usb',
216 editor
: caps
.nodes
['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined,
217 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
218 header
: gettext('USB Device') + ' (' + confid
+ ')'
221 for (i
= 0; i
< 4; i
++) {
222 confid
= "hostpci" + i
.toString();
225 tdCls
: 'pve-itype-icon-pci',
226 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
227 header
: gettext('PCI Device') + ' (' + confid
+ ')'
230 for (i
= 0; i
< 8; i
++) {
231 rows
["unused" + i
.toString()] = {
233 tdCls
: 'pve-itype-icon-storage',
234 editor
: caps
.vms
['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
235 header
: gettext('Unused Disk') + ' ' + i
.toString()
239 var sorterFn = function(rec1
, rec2
) {
240 var v1
= rec1
.data
.key
;
241 var v2
= rec2
.data
.key
;
242 var g1
= rows
[v1
].group
|| 0;
243 var g2
= rows
[v2
].group
|| 0;
246 (g1
> g2
? 1 : -1) : (v1
> v2
? 1 : (v1
< v2
? -1 : 0));
249 var reload = function() {
253 var baseurl
= 'nodes/' + nodename
+ '/qemu/' + vmid
+ '/config';
255 var sm
= Ext
.create('Ext.selection.RowModel', {});
257 var run_editor = function() {
258 var rec
= sm
.getSelection()[0];
263 var rowdef
= rows
[rec
.data
.key
];
264 if (!rowdef
.editor
) {
268 var editor
= rowdef
.editor
;
269 if (rowdef
.tdCls
== 'pve-itype-icon-storage') {
270 var value
= me
.getObjectValue(rec
.data
.key
, '', true);
271 if (value
.match(/media=cdrom/)) {
272 editor
= 'PVE.qemu.CDEdit';
278 if (Ext
.isString(editor
)) {
279 win
= Ext
.create(editor
, {
280 pveSelNode
: me
.pveSelNode
,
281 confid
: rec
.data
.key
,
282 url
: '/api2/extjs/' + baseurl
285 var config
= Ext
.apply({
286 pveSelNode
: me
.pveSelNode
,
287 confid
: rec
.data
.key
,
288 url
: '/api2/extjs/' + baseurl
290 win
= Ext
.createWidget(rowdef
.editor
.xtype
, config
);
295 win
.on('destroy', reload
);
298 var run_diskthrottle = function() {
299 var rec
= sm
.getSelection()[0];
304 var win
= Ext
.create('PVE.qemu.HDThrottle', {
305 pveSelNode
: me
.pveSelNode
,
306 confid
: rec
.data
.key
,
307 url
: '/api2/extjs/' + baseurl
311 win
.on('destroy', reload
);
314 var run_resize = function() {
315 var rec
= sm
.getSelection()[0];
320 var win
= Ext
.create('PVE.window.HDResize', {
328 win
.on('destroy', reload
);
331 var run_cpuoptions = function() {
332 var sockets
= me
.getObjectValue('sockets', 1);
333 var cores
= me
.getObjectValue('cores', 1);
335 var win
= Ext
.create('PVE.qemu.CPUOptions', {
336 maxvcpus
: sockets
* cores
,
338 pveSelNode
: me
.pveSelNode
,
339 url
: '/api2/extjs/' + baseurl
344 win
.on('destroy', reload
);
347 var run_move = function() {
348 var rec
= sm
.getSelection()[0];
353 var win
= Ext
.create('PVE.window.HDMove', {
361 win
.on('destroy', reload
);
364 var edit_btn
= new PVE
.button
.Button({
365 text
: gettext('Edit'),
371 var resize_btn
= new PVE
.button
.Button({
372 text
: gettext('Resize disk'),
378 var move_btn
= new PVE
.button
.Button({
379 text
: gettext('Move disk'),
385 var diskthrottle_btn
= new PVE
.button
.Button({
386 text
: gettext('Disk Throttle'),
389 handler
: run_diskthrottle
392 var cpuoptions_btn
= new Ext
.Button({
393 text
: gettext('CPU options'),
394 handler
: run_cpuoptions
397 var remove_btn
= new PVE
.button
.Button({
398 text
: gettext('Remove'),
399 defaultText
: gettext('Remove'),
400 altText
: gettext('Detach'),
404 confirmMsg: function(rec
) {
405 var warn
= gettext('Are you sure you want to remove entry {0}');
406 if (this.text
=== this.altText
) {
407 warn
= gettext('Are you sure you want to detach entry {0}');
410 var entry
= rec
.data
.key
;
411 var msg
= Ext
.String
.format(warn
, "'"
412 + me
.renderKey(entry
, {}, rec
) + "'");
414 if (entry
.match(/^unused\d+$/)) {
415 msg
+= " " + gettext('This will permanently erase all data.');
420 handler: function(b
, e
, rec
) {
421 PVE
.Utils
.API2Request({
422 url
: '/api2/extjs/' + baseurl
,
426 'delete': rec
.data
.key
428 callback: function() {
431 failure: function (response
, opts
) {
432 Ext
.Msg
.alert('Error', response
.htmlStatus
);
437 render: function(btn
) {
438 // hack: calculate an optimal button width on first display
439 // to prevent the whole toolbar to move when we switch
440 // between the "Remove" and "Detach" labels
441 var def
= btn
.getSize().width
;
443 btn
.setText(btn
.altText
);
444 var alt
= btn
.getSize().width
;
446 btn
.setText(btn
.defaultText
);
448 var optimal
= alt
> def
? alt
: def
;
449 btn
.setSize({ width
: optimal
});
454 var revert_btn
= new PVE
.button
.Button({
455 text
: gettext('Revert'),
458 handler: function(b
, e
, rec
) {
459 var rowdef
= me
.rows
[rec
.data
.key
] || {};
460 var keys
= rowdef
.multiKey
|| [ rec
.data
.key
];
461 var revert
= keys
.join(',');
462 PVE
.Utils
.API2Request({
463 url
: '/api2/extjs/' + baseurl
,
469 callback: function() {
472 failure: function (response
, opts
) {
473 Ext
.Msg
.alert('Error',response
.htmlStatus
);
479 var efidisk_menuitem
= Ext
.create('Ext.menu.Item',{
480 text
: gettext('EFI Disk'),
481 iconCls
: 'pve-itype-icon-storage',
482 disabled
: !caps
.vms
['VM.Config.Disk'],
483 handler: function() {
485 var rstoredata
= me
.rstore
.getData().map
;
486 // check if ovmf is configured
487 if (rstoredata
.bios
&& rstoredata
.bios
.data
.value
=== 'ovmf') {
488 var win
= Ext
.create('PVE.qemu.EFIDiskEdit', {
489 url
: '/api2/extjs/' + baseurl
,
490 pveSelNode
: me
.pveSelNode
492 win
.on('destroy', reload
);
495 Ext
.Msg
.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
501 var set_button_status = function() {
502 var sm
= me
.getSelectionModel();
503 var rec
= sm
.getSelection()[0];
505 // disable button when we have an efidisk already
506 // disable is ok in this case, because you can instantly
507 // see that there is already one
508 efidisk_menuitem
.setDisabled(me
.rstore
.getData().map
.efidisk0
!== undefined);
509 // en/disable usb add button
511 me
.rstore
.getData().items
.forEach(function(item
){
512 if (/^usb\d+/.test(item
.id
)) {
516 me
.down('#addusb').setDisabled((count
>= 5));
519 remove_btn
.disable();
521 resize_btn
.disable();
523 diskthrottle_btn
.disable();
524 revert_btn
.disable();
527 var key
= rec
.data
.key
;
528 var value
= rec
.data
.value
;
529 var rowdef
= rows
[key
];
531 var pending
= rec
.data
['delete'] || me
.hasPendingChanges(key
);
532 var isUsedDisk
= !key
.match(/^unused\d+/) &&
533 rowdef
.tdCls
== 'pve-itype-icon-storage' &&
534 (value
&& !value
.match(/media=cdrom/));
536 var isEfi
= (key
=== 'efidisk0');
538 remove_btn
.setDisabled(rec
.data
['delete'] || (rowdef
.never_delete
=== true));
539 remove_btn
.setText(isUsedDisk
? remove_btn
.altText
: remove_btn
.defaultText
);
541 edit_btn
.setDisabled(rec
.data
['delete'] || !rowdef
.editor
);
543 resize_btn
.setDisabled(pending
|| !isUsedDisk
);
545 move_btn
.setDisabled(pending
|| !isUsedDisk
);
547 diskthrottle_btn
.setDisabled(pending
|| !isUsedDisk
|| isEfi
);
549 revert_btn
.setDisabled(!pending
);
554 url
: '/api2/json/' + 'nodes/' + nodename
+ '/qemu/' + vmid
+ '/pending',
559 text
: gettext('Add'),
560 menu
: new Ext
.menu
.Menu({
563 text
: gettext('Hard Disk'),
564 iconCls
: 'pve-itype-icon-storage',
565 disabled
: !caps
.vms
['VM.Config.Disk'],
566 handler: function() {
567 var win
= Ext
.create('PVE.qemu.HDEdit', {
568 url
: '/api2/extjs/' + baseurl
,
569 pveSelNode
: me
.pveSelNode
571 win
.on('destroy', reload
);
576 text
: gettext('CD/DVD Drive'),
577 iconCls
: 'pve-itype-icon-cdrom',
578 disabled
: !caps
.vms
['VM.Config.Disk'],
579 handler: function() {
580 var win
= Ext
.create('PVE.qemu.CDEdit', {
581 url
: '/api2/extjs/' + baseurl
,
582 pveSelNode
: me
.pveSelNode
584 win
.on('destroy', reload
);
589 text
: gettext('Network Device'),
590 iconCls
: 'pve-itype-icon-network',
591 disabled
: !caps
.vms
['VM.Config.Network'],
592 handler: function() {
593 var win
= Ext
.create('PVE.qemu.NetworkEdit', {
594 url
: '/api2/extjs/' + baseurl
,
595 pveSelNode
: me
.pveSelNode
597 win
.on('destroy', reload
);
603 text
: gettext('USB Device'),
605 iconCls
: 'pve-itype-icon-usb',
606 disabled
: !caps
.nodes
['Sys.Console'],
607 handler: function() {
608 var win
= Ext
.create('PVE.qemu.USBEdit', {
609 url
: '/api2/extjs/' + baseurl
,
610 pveSelNode
: me
.pveSelNode
612 win
.on('destroy', reload
);
630 itemdblclick
: run_editor
,
631 selectionchange
: set_button_status
637 me
.on('activate', me
.rstore
.startUpdate
);
638 me
.on('destroy', me
.rstore
.stopUpdate
);
640 me
.mon(me
.rstore
, 'refresh', function() {