]>
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
] || {};
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(/vm-.*-cloudinit/)) {
19 metaData
.tdCls
= 'pve-itype-icon-cloud';
20 return rowdef
.cloudheader
;
21 } else if (value
.match(/media=cdrom/)) {
22 metaData
.tdCls
= 'pve-itype-icon-cdrom';
23 return rowdef
.cdheader
;
27 return rowdef
.header
|| key
;
30 initComponent : function() {
34 var nodename
= me
.pveSelNode
.data
.node
;
36 throw "no node name specified";
39 var vmid
= me
.pveSelNode
.data
.vmid
;
41 throw "no VM ID specified";
44 var caps
= Ext
.state
.Manager
.get('GuiCap');
48 header
: gettext('Memory'),
49 editor
: caps
.vms
['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined,
52 tdCls
: 'pve-itype-icon-memory',
53 multiKey
: ['memory', 'balloon', 'shares'],
54 renderer: function(value
, metaData
, record
, ri
, ci
, store
, pending
) {
57 var max
= me
.getObjectValue('memory', 512, pending
);
58 var balloon
= me
.getObjectValue('balloon', undefined, pending
);
59 var shares
= me
.getObjectValue('shares', undefined, pending
);
61 res
= Proxmox
.Utils
.format_size(max
*1024*1024);
63 if (balloon
!== undefined && balloon
> 0) {
64 res
= Proxmox
.Utils
.format_size(balloon
*1024*1024) + "/" + res
;
67 res
+= ' [shares=' + shares
+']';
69 } else if (balloon
=== 0) {
70 res
+= ' [balloon=0]';
76 header
: gettext('Processors'),
78 editor
: (caps
.vms
['VM.Config.CPU'] || caps
.vms
['VM.Config.HWType']) ?
79 'PVE.qemu.ProcessorEdit' : undefined,
80 tdCls
: 'pve-itype-icon-processor',
82 multiKey
: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'],
83 renderer: function(value
, metaData
, record
, rowIndex
, colIndex
, store
, pending
) {
85 var sockets
= me
.getObjectValue('sockets', 1, pending
);
86 var model
= me
.getObjectValue('cpu', undefined, pending
);
87 var cores
= me
.getObjectValue('cores', 1, pending
);
88 var numa
= me
.getObjectValue('numa', undefined, pending
);
89 var vcpus
= me
.getObjectValue('vcpus', undefined, pending
);
90 var cpulimit
= me
.getObjectValue('cpulimit', undefined, pending
);
91 var cpuunits
= me
.getObjectValue('cpuunits', undefined, pending
);
93 var res
= Ext
.String
.format('{0} ({1} sockets, {2} cores)',
94 sockets
*cores
, sockets
, cores
);
97 res
+= ' [' + model
+ ']';
101 res
+= ' [numa=' + numa
+']';
105 res
+= ' [vcpus=' + vcpus
+']';
109 res
+= ' [cpulimit=' + cpulimit
+']';
113 res
+= ' [cpuunits=' + cpuunits
+']';
120 header
: gettext('Keyboard Layout'),
122 editor
: caps
.vms
['VM.Config.Options'] ? 'PVE.qemu.KeyboardEdit' : undefined,
123 tdCls
: 'pve-itype-icon-keyboard',
125 renderer
: PVE
.Utils
.render_kvm_language
128 header
: gettext('Display'),
129 editor
: caps
.vms
['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
131 tdCls
: 'pve-itype-icon-display',
133 renderer
: PVE
.Utils
.render_kvm_vga_driver
167 PVE
.Utils
.forEachBus(undefined, function(type
, id
) {
168 var confid
= type
+ id
;
171 tdCls
: 'pve-itype-icon-storage',
172 editor
: 'PVE.qemu.HDEdit',
173 never_delete
: caps
.vms
['VM.Config.Disk'] ? false : true,
174 header
: gettext('Hard Disk') + ' (' + confid
+')',
175 cdheader
: gettext('CD/DVD Drive') + ' (' + confid
+')',
176 cloudheader
: gettext('CloudInit Drive') + ' (' + confid
+ ')'
179 for (i
= 0; i
< 32; i
++) {
180 confid
= "net" + i
.toString();
183 tdCls
: 'pve-itype-icon-network',
184 editor
: caps
.vms
['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
185 never_delete
: caps
.vms
['VM.Config.Network'] ? false : true,
186 header
: gettext('Network Device') + ' (' + confid
+')'
191 tdCls
: 'pve-itype-icon-storage',
193 never_delete
: caps
.vms
['VM.Config.Disk'] ? false : true,
194 header
: gettext('EFI Disk')
196 for (i
= 0; i
< 5; i
++) {
197 confid
= "usb" + i
.toString();
200 tdCls
: 'pve-itype-icon-usb',
201 editor
: caps
.nodes
['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined,
202 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
203 header
: gettext('USB Device') + ' (' + confid
+ ')'
206 for (i
= 0; i
< 4; i
++) {
207 confid
= "hostpci" + i
.toString();
210 tdCls
: 'pve-itype-icon-pci',
211 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
212 header
: gettext('PCI Device') + ' (' + confid
+ ')'
215 for (i
= 0; i
< 4; i
++) {
216 confid
= "serial" + i
.toString();
219 tdCls
: 'pve-itype-icon-serial',
220 never_delete
: caps
.nodes
['Sys.Console'] ? false : true,
221 header
: gettext('Serial Port') + ' (' + confid
+ ')'
224 for (i
= 0; i
< 8; i
++) {
225 rows
["unused" + i
.toString()] = {
227 tdCls
: 'pve-itype-icon-storage',
228 editor
: caps
.vms
['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
229 header
: gettext('Unused Disk') + ' ' + i
.toString()
233 var sorterFn = function(rec1
, rec2
) {
234 var v1
= rec1
.data
.key
;
235 var v2
= rec2
.data
.key
;
236 var g1
= rows
[v1
].group
|| 0;
237 var g2
= rows
[v2
].group
|| 0;
240 (g1
> g2
? 1 : -1) : (v1
> v2
? 1 : (v1
< v2
? -1 : 0));
243 var reload = function() {
247 var baseurl
= 'nodes/' + nodename
+ '/qemu/' + vmid
+ '/config';
249 var sm
= Ext
.create('Ext.selection.RowModel', {});
251 var run_editor = function() {
252 var rec
= sm
.getSelection()[0];
257 var rowdef
= rows
[rec
.data
.key
];
258 if (!rowdef
.editor
) {
262 var editor
= rowdef
.editor
;
263 if (rowdef
.tdCls
== 'pve-itype-icon-storage') {
264 var value
= me
.getObjectValue(rec
.data
.key
, '', true);
265 if (value
.match(/vm-.*-cloudinit/)) {
267 } else if (value
.match(/media=cdrom/)) {
268 editor
= 'PVE.qemu.CDEdit';
274 if (Ext
.isString(editor
)) {
275 win
= Ext
.create(editor
, {
276 pveSelNode
: me
.pveSelNode
,
277 confid
: rec
.data
.key
,
278 url
: '/api2/extjs/' + baseurl
281 var config
= Ext
.apply({
282 pveSelNode
: me
.pveSelNode
,
283 confid
: rec
.data
.key
,
284 url
: '/api2/extjs/' + baseurl
286 win
= Ext
.createWidget(rowdef
.editor
.xtype
, config
);
291 win
.on('destroy', reload
);
294 var run_resize = function() {
295 var rec
= sm
.getSelection()[0];
300 var win
= Ext
.create('PVE.window.HDResize', {
308 win
.on('destroy', reload
);
311 var run_move = function() {
312 var rec
= sm
.getSelection()[0];
317 var win
= Ext
.create('PVE.window.HDMove', {
325 win
.on('destroy', reload
);
328 var edit_btn
= new Proxmox
.button
.Button({
329 text
: gettext('Edit'),
335 var resize_btn
= new Proxmox
.button
.Button({
336 text
: gettext('Resize disk'),
342 var move_btn
= new Proxmox
.button
.Button({
343 text
: gettext('Move disk'),
349 var remove_btn
= new Proxmox
.button
.Button({
350 text
: gettext('Remove'),
351 defaultText
: gettext('Remove'),
352 altText
: gettext('Detach'),
356 confirmMsg: function(rec
) {
357 var warn
= gettext('Are you sure you want to remove entry {0}');
358 if (this.text
=== this.altText
) {
359 warn
= gettext('Are you sure you want to detach entry {0}');
362 var entry
= rec
.data
.key
;
363 var msg
= Ext
.String
.format(warn
, "'"
364 + me
.renderKey(entry
, {}, rec
) + "'");
366 if (entry
.match(/^unused\d+$/)) {
367 msg
+= " " + gettext('This will permanently erase all data.');
372 handler: function(b
, e
, rec
) {
373 Proxmox
.Utils
.API2Request({
374 url
: '/api2/extjs/' + baseurl
,
378 'delete': rec
.data
.key
380 callback: function() {
383 failure: function (response
, opts
) {
384 Ext
.Msg
.alert('Error', response
.htmlStatus
);
389 render: function(btn
) {
390 // hack: calculate an optimal button width on first display
391 // to prevent the whole toolbar to move when we switch
392 // between the "Remove" and "Detach" labels
393 var def
= btn
.getSize().width
;
395 btn
.setText(btn
.altText
);
396 var alt
= btn
.getSize().width
;
398 btn
.setText(btn
.defaultText
);
400 var optimal
= alt
> def
? alt
: def
;
401 btn
.setSize({ width
: optimal
});
406 var revert_btn
= new Proxmox
.button
.Button({
407 text
: gettext('Revert'),
410 handler: function(b
, e
, rec
) {
411 var rowdef
= me
.rows
[rec
.data
.key
] || {};
412 var keys
= rowdef
.multiKey
|| [ rec
.data
.key
];
413 var revert
= keys
.join(',');
414 Proxmox
.Utils
.API2Request({
415 url
: '/api2/extjs/' + baseurl
,
421 callback: function() {
424 failure: function (response
, opts
) {
425 Ext
.Msg
.alert('Error',response
.htmlStatus
);
431 var efidisk_menuitem
= Ext
.create('Ext.menu.Item',{
432 text
: gettext('EFI Disk'),
433 iconCls
: 'pve-itype-icon-storage',
434 disabled
: !caps
.vms
['VM.Config.Disk'],
435 handler: function() {
437 var rstoredata
= me
.rstore
.getData().map
;
438 // check if ovmf is configured
439 if (rstoredata
.bios
&& rstoredata
.bios
.data
.value
=== 'ovmf') {
440 var win
= Ext
.create('PVE.qemu.EFIDiskEdit', {
441 url
: '/api2/extjs/' + baseurl
,
442 pveSelNode
: me
.pveSelNode
444 win
.on('destroy', reload
);
447 Ext
.Msg
.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
453 var set_button_status = function() {
454 var sm
= me
.getSelectionModel();
455 var rec
= sm
.getSelection()[0];
457 // disable button when we have an efidisk already
458 // disable is ok in this case, because you can instantly
459 // see that there is already one
460 efidisk_menuitem
.setDisabled(me
.rstore
.getData().map
.efidisk0
!== undefined);
461 // en/disable usb add button
463 var hasCloudInit
= false;
464 me
.rstore
.getData().items
.forEach(function(item
){
465 if (/^usb\d+/.test(item
.id
)) {
468 if (!hasCloudInit
&& /vm-.*-cloudinit/.test(item
.data
.value
)) {
472 me
.down('#addusb').setDisabled((count
>= 5));
473 me
.down('#addci').setDisabled(hasCloudInit
);
476 remove_btn
.disable();
478 resize_btn
.disable();
480 revert_btn
.disable();
483 var key
= rec
.data
.key
;
484 var value
= rec
.data
.value
;
485 var rowdef
= rows
[key
];
487 var pending
= rec
.data
['delete'] || me
.hasPendingChanges(key
);
488 var isUsedDisk
= !key
.match(/^unused\d+/) &&
489 rowdef
.tdCls
== 'pve-itype-icon-storage' &&
490 (value
&& !value
.match(/media=cdrom/));
492 var isCloudInit
= (value
&& value
.toString().match(/vm-.*-cloudinit/));
494 var isEfi
= (key
=== 'efidisk0');
496 remove_btn
.setDisabled(rec
.data
['delete'] || (rowdef
.never_delete
=== true));
497 remove_btn
.setText((isUsedDisk
&& !isCloudInit
) ? remove_btn
.altText
: remove_btn
.defaultText
);
499 edit_btn
.setDisabled(rec
.data
['delete'] || !rowdef
.editor
|| isCloudInit
);
501 resize_btn
.setDisabled(pending
|| !isUsedDisk
);
503 move_btn
.setDisabled(pending
|| !isUsedDisk
);
505 revert_btn
.setDisabled(!pending
);
510 url
: '/api2/json/' + 'nodes/' + nodename
+ '/qemu/' + vmid
+ '/pending',
513 run_editor
: run_editor
,
516 text
: gettext('Add'),
517 menu
: new Ext
.menu
.Menu({
520 text
: gettext('Hard Disk'),
521 iconCls
: 'pve-itype-icon-storage',
522 disabled
: !caps
.vms
['VM.Config.Disk'],
523 handler: function() {
524 var win
= Ext
.create('PVE.qemu.HDEdit', {
525 url
: '/api2/extjs/' + baseurl
,
526 pveSelNode
: me
.pveSelNode
528 win
.on('destroy', reload
);
533 text
: gettext('CD/DVD Drive'),
534 iconCls
: 'pve-itype-icon-cdrom',
535 disabled
: !caps
.vms
['VM.Config.Disk'],
536 handler: function() {
537 var win
= Ext
.create('PVE.qemu.CDEdit', {
538 url
: '/api2/extjs/' + baseurl
,
539 pveSelNode
: me
.pveSelNode
541 win
.on('destroy', reload
);
546 text
: gettext('Network Device'),
547 iconCls
: 'pve-itype-icon-network',
548 disabled
: !caps
.vms
['VM.Config.Network'],
549 handler: function() {
550 var win
= Ext
.create('PVE.qemu.NetworkEdit', {
551 url
: '/api2/extjs/' + baseurl
,
552 pveSelNode
: me
.pveSelNode
554 win
.on('destroy', reload
);
560 text
: gettext('USB Device'),
562 iconCls
: 'pve-itype-icon-usb',
563 disabled
: !caps
.nodes
['Sys.Console'],
564 handler: function() {
565 var win
= Ext
.create('PVE.qemu.USBEdit', {
566 url
: '/api2/extjs/' + baseurl
,
567 pveSelNode
: me
.pveSelNode
569 win
.on('destroy', reload
);
574 text
: gettext('Serial Port'),
576 iconCls
: 'pve-itype-icon-serial',
577 disabled
: !caps
.vms
['VM.Config.Options'],
578 handler: function() {
579 var win
= Ext
.create('PVE.qemu.SerialEdit', {
580 url
: '/api2/extjs/' + baseurl
582 win
.on('destroy', reload
);
587 text
: gettext('CloudInit Drive'),
589 iconCls
: 'pve-itype-icon-cloud',
590 disabled
: !caps
.nodes
['Sys.Console'],
591 handler: function() {
592 var win
= Ext
.create('PVE.qemu.CIDriveEdit', {
593 url
: '/api2/extjs/' + baseurl
,
594 pveSelNode
: me
.pveSelNode
596 win
.on('destroy', reload
);
612 itemdblclick
: run_editor
,
613 selectionchange
: set_button_status
619 me
.on('activate', me
.rstore
.startUpdate
);
620 me
.on('destroy', me
.rstore
.stopUpdate
);
622 me
.mon(me
.rstore
, 'refresh', function() {