]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/qemu/HardwareView.js
gui: qemu/HardwareView: add machine to HardwareView
[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] || {};
6a040799
DC
11 var iconCls = rowdef.iconCls;
12 var icon = '';
13 var txt = (rowdef.header || key);
f97670f5
DM
14
15 metaData.tdAttr = "valign=middle";
16
17 if (rowdef.tdCls) {
18 metaData.tdCls = rowdef.tdCls;
19 if (rowdef.tdCls == 'pve-itype-icon-storage') {
cdf5bec2
DL
20 var value = me.getObjectValue(key, '', false);
21 if (value === '') {
22 value = me.getObjectValue(key, '', true);
23 }
b38d0810
DC
24 if (value.match(/vm-.*-cloudinit/)) {
25 metaData.tdCls = 'pve-itype-icon-cloud';
26 return rowdef.cloudheader;
27 } else if (value.match(/media=cdrom/)) {
f97670f5
DM
28 metaData.tdCls = 'pve-itype-icon-cdrom';
29 return rowdef.cdheader;
30 }
31 }
6a040799
DC
32 } else if (iconCls) {
33 icon = "<i class='pve-grid-fa fa fa-fw fa-" + iconCls + "'></i>";
34 metaData.tdCls += " pve-itype-fa";
f97670f5 35 }
6a040799 36 return icon + txt;
f97670f5
DM
37 },
38
39 initComponent : function() {
40 var me = this;
41 var i, confid;
42
43 var nodename = me.pveSelNode.data.node;
44 if (!nodename) {
45 throw "no node name specified";
46 }
47
48 var vmid = me.pveSelNode.data.vmid;
49 if (!vmid) {
50 throw "no VM ID specified";
51 }
52
53 var caps = Ext.state.Manager.get('GuiCap');
54
aae2273c 55 /*jslint confusion: true */
f97670f5
DM
56 var rows = {
57 memory: {
58 header: gettext('Memory'),
59 editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined,
60 never_delete: true,
72408db9 61 defaultValue: '512',
f97670f5 62 tdCls: 'pve-itype-icon-memory',
cbe0b4b8 63 group: 2,
1d902252
DC
64 multiKey: ['memory', 'balloon', 'shares'],
65 renderer: function(value, metaData, record, ri, ci, store, pending) {
66 var res = '';
67
68 var max = me.getObjectValue('memory', 512, pending);
69 var balloon = me.getObjectValue('balloon', undefined, pending);
70 var shares = me.getObjectValue('shares', undefined, pending);
71
72 res = Proxmox.Utils.format_size(max*1024*1024);
73
74 if (balloon !== undefined && balloon > 0) {
75 res = Proxmox.Utils.format_size(balloon*1024*1024) + "/" + res;
76
77 if (shares) {
78 res += ' [shares=' + shares +']';
79 }
80 } else if (balloon === 0) {
81 res += ' [balloon=0]';
82 }
83 return res;
f97670f5
DM
84 }
85 },
86 sockets: {
87 header: gettext('Processors'),
88 never_delete: true,
89 editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ?
90 'PVE.qemu.ProcessorEdit' : undefined,
91 tdCls: 'pve-itype-icon-processor',
cbe0b4b8 92 group: 3,
72408db9 93 defaultValue: '1',
f97670f5
DM
94 multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'],
95 renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) {
96
97 var sockets = me.getObjectValue('sockets', 1, pending);
98 var model = me.getObjectValue('cpu', undefined, pending);
99 var cores = me.getObjectValue('cores', 1, pending);
100 var numa = me.getObjectValue('numa', undefined, pending);
101 var vcpus = me.getObjectValue('vcpus', undefined, pending);
102 var cpulimit = me.getObjectValue('cpulimit', undefined, pending);
103 var cpuunits = me.getObjectValue('cpuunits', undefined, pending);
104
72408db9
EK
105 var res = Ext.String.format('{0} ({1} sockets, {2} cores)',
106 sockets*cores, sockets, cores);
107
f97670f5
DM
108 if (model) {
109 res += ' [' + model + ']';
110 }
111
112 if (numa) {
113 res += ' [numa=' + numa +']';
114 }
115
116 if (vcpus) {
117 res += ' [vcpus=' + vcpus +']';
118 }
119
120 if (cpulimit) {
121 res += ' [cpulimit=' + cpulimit +']';
122 }
123
124 if (cpuunits) {
125 res += ' [cpuunits=' + cpuunits +']';
126 }
127
128 return res;
129 }
130 },
439d7821
DC
131 bios: {
132 header: 'BIOS',
133 group: 4,
134 never_delete: true,
135 editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined,
136 defaultValue: '',
137 iconCls: 'microchip',
138 renderer: PVE.Utils.render_qemu_bios
139 },
f97670f5
DM
140 vga: {
141 header: gettext('Display'),
142 editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
143 never_delete: true,
144 tdCls: 'pve-itype-icon-display',
439d7821 145 group:5,
f97670f5
DM
146 defaultValue: '',
147 renderer: PVE.Utils.render_kvm_vga_driver
148 },
aae2273c
DC
149 machine: {
150 header: gettext('Machine'),
151 editor: caps.vms['VM.Config.HWType'] ? {
152 xtype: 'proxmoxWindowEdit',
153 subject: gettext('Machine'),
154 width: 350,
155 items: [{
156 xtype: 'proxmoxKVComboBox',
157 name: 'machine',
158 value: '__default__',
159 fieldLabel: gettext('Machine'),
160 comboItems: [
161 ['__default__', PVE.Utils.render_qemu_machine('')],
162 ['q35', 'q35']
163 ]
164 }]} : undefined,
165 iconCls: 'cogs',
166 never_delete: true,
167 group: 6,
168 defaultValue: '',
169 renderer: PVE.Utils.render_qemu_machine
170 },
439d7821
DC
171 scsihw: {
172 header: gettext('SCSI Controller'),
173 iconCls: 'database',
174 editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.ScsiHwEdit' : undefined,
175 renderer: PVE.Utils.render_scsihw,
176 group: 7,
177 never_delete: true,
178 defaultValue: ''
179 },
f97670f5
DM
180 cores: {
181 visible: false
182 },
183 cpu: {
184 visible: false
185 },
186 numa: {
187 visible: false
188 },
189 balloon: {
190 visible: false
191 },
192 hotplug: {
193 visible: false
194 },
195 vcpus: {
196 visible: false
197 },
198 cpuunits: {
199 visible: false
200 },
201 cpulimit: {
202 visible: false
3b37ab6d 203 },
1d902252
DC
204 shares: {
205 visible: false
f97670f5 206 }
f97670f5 207 };
aae2273c 208 /*jslint confusion: false */
f97670f5 209
abe824aa
DC
210 PVE.Utils.forEachBus(undefined, function(type, id) {
211 var confid = type + id;
f97670f5 212 rows[confid] = {
8d554444 213 group: 10,
f97670f5
DM
214 tdCls: 'pve-itype-icon-storage',
215 editor: 'PVE.qemu.HDEdit',
216 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
217 header: gettext('Hard Disk') + ' (' + confid +')',
b38d0810
DC
218 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')',
219 cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')'
f97670f5 220 };
abe824aa 221 });
f97670f5 222 for (i = 0; i < 32; i++) {
72408db9 223 confid = "net" + i.toString();
f97670f5 224 rows[confid] = {
8d554444 225 group: 15,
cbe0b4b8 226 order: i,
f97670f5
DM
227 tdCls: 'pve-itype-icon-network',
228 editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
229 never_delete: caps.vms['VM.Config.Network'] ? false : true,
230 header: gettext('Network Device') + ' (' + confid +')'
231 };
232 }
194c9b09 233 rows.efidisk0 = {
8d554444 234 group: 20,
194c9b09
DC
235 tdCls: 'pve-itype-icon-storage',
236 editor: null,
237 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
238 header: gettext('EFI Disk')
239 };
fa522e48 240 for (i = 0; i < 5; i++) {
72408db9 241 confid = "usb" + i.toString();
fa522e48 242 rows[confid] = {
8d554444 243 group: 25,
cbe0b4b8 244 order: i,
fa522e48 245 tdCls: 'pve-itype-icon-usb',
2668ba64
DC
246 editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined,
247 never_delete: caps.nodes['Sys.Console'] ? false : true,
fa522e48
DC
248 header: gettext('USB Device') + ' (' + confid + ')'
249 };
250 }
ec3d8372 251 for (i = 0; i < 4; i++) {
72408db9 252 confid = "hostpci" + i.toString();
ec3d8372 253 rows[confid] = {
8d554444 254 group: 30,
cbe0b4b8 255 order: i,
ec3d8372
DC
256 tdCls: 'pve-itype-icon-pci',
257 never_delete: caps.nodes['Sys.Console'] ? false : true,
25a1063a 258 editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined,
ec3d8372
DC
259 header: gettext('PCI Device') + ' (' + confid + ')'
260 };
261 }
a488da8c
DC
262 for (i = 0; i < 4; i++) {
263 confid = "serial" + i.toString();
264 rows[confid] = {
8d554444 265 group: 35,
cbe0b4b8 266 order: i,
a488da8c
DC
267 tdCls: 'pve-itype-icon-serial',
268 never_delete: caps.nodes['Sys.Console'] ? false : true,
269 header: gettext('Serial Port') + ' (' + confid + ')'
270 };
271 }
afa19975 272 for (i = 0; i < 256; i++) {
72408db9 273 rows["unused" + i.toString()] = {
cd86ab24 274 group: 99,
cbe0b4b8 275 order: i,
f97670f5
DM
276 tdCls: 'pve-itype-icon-storage',
277 editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
72408db9 278 header: gettext('Unused Disk') + ' ' + i.toString()
f97670f5
DM
279 };
280 }
281
282 var sorterFn = function(rec1, rec2) {
283 var v1 = rec1.data.key;
284 var v2 = rec2.data.key;
285 var g1 = rows[v1].group || 0;
286 var g2 = rows[v2].group || 0;
cbe0b4b8
DL
287 var order1 = rows[v1].order || 0;
288 var order2 = rows[v2].order || 0;
289
290 if ((g1 - g2) !== 0) {
291 return g1 - g2;
292 }
f97670f5 293
cbe0b4b8
DL
294 if ((order1 - order2) !== 0) {
295 return order1 - order2;
296 }
297
298 if (v1 > v2) {
299 return 1;
300 } else if (v1 < v2) {
301 return -1;
302 } else {
303 return 0;
304 }
f97670f5
DM
305 };
306
307 var reload = function() {
308 me.rstore.load();
309 };
310
311 var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config';
312
313 var sm = Ext.create('Ext.selection.RowModel', {});
314
315 var run_editor = function() {
316 var rec = sm.getSelection()[0];
317 if (!rec) {
318 return;
319 }
320
321 var rowdef = rows[rec.data.key];
322 if (!rowdef.editor) {
323 return;
324 }
325
326 var editor = rowdef.editor;
327 if (rowdef.tdCls == 'pve-itype-icon-storage') {
328 var value = me.getObjectValue(rec.data.key, '', true);
ab1ee352
DC
329 if (value.match(/vm-.*-cloudinit/)) {
330 return;
331 } else if (value.match(/media=cdrom/)) {
f97670f5
DM
332 editor = 'PVE.qemu.CDEdit';
333 }
334 }
335
336 var win;
337
338 if (Ext.isString(editor)) {
339 win = Ext.create(editor, {
340 pveSelNode: me.pveSelNode,
341 confid: rec.data.key,
342 url: '/api2/extjs/' + baseurl
343 });
344 } else {
345 var config = Ext.apply({
346 pveSelNode: me.pveSelNode,
347 confid: rec.data.key,
348 url: '/api2/extjs/' + baseurl
349 }, rowdef.editor);
350 win = Ext.createWidget(rowdef.editor.xtype, config);
351 win.load();
352 }
353
354 win.show();
355 win.on('destroy', reload);
356 };
357
f97670f5
DM
358 var run_resize = function() {
359 var rec = sm.getSelection()[0];
360 if (!rec) {
361 return;
362 }
363
364 var win = Ext.create('PVE.window.HDResize', {
365 disk: rec.data.key,
366 nodename: nodename,
367 vmid: vmid
368 });
369
370 win.show();
371
372 win.on('destroy', reload);
373 };
374
f97670f5
DM
375 var run_move = function() {
376 var rec = sm.getSelection()[0];
377 if (!rec) {
378 return;
379 }
380
381 var win = Ext.create('PVE.window.HDMove', {
382 disk: rec.data.key,
383 nodename: nodename,
384 vmid: vmid
385 });
386
387 win.show();
388
389 win.on('destroy', reload);
390 };
391
5720fafa 392 var edit_btn = new Proxmox.button.Button({
f97670f5
DM
393 text: gettext('Edit'),
394 selModel: sm,
395 disabled: true,
396 handler: run_editor
397 });
398
5720fafa 399 var resize_btn = new Proxmox.button.Button({
f97670f5
DM
400 text: gettext('Resize disk'),
401 selModel: sm,
402 disabled: true,
403 handler: run_resize
404 });
405
5720fafa 406 var move_btn = new Proxmox.button.Button({
f97670f5
DM
407 text: gettext('Move disk'),
408 selModel: sm,
409 disabled: true,
410 handler: run_move
411 });
412
5720fafa 413 var remove_btn = new Proxmox.button.Button({
f97670f5 414 text: gettext('Remove'),
877fce54
EK
415 defaultText: gettext('Remove'),
416 altText: gettext('Detach'),
f97670f5
DM
417 selModel: sm,
418 disabled: true,
419 dangerous: true,
3b2e557f 420 RESTMethod: 'PUT',
f97670f5 421 confirmMsg: function(rec) {
ff5a8a9b
EK
422 var warn = gettext('Are you sure you want to remove entry {0}');
423 if (this.text === this.altText) {
424 warn = gettext('Are you sure you want to detach entry {0}');
425 }
426
427 var entry = rec.data.key;
428 var msg = Ext.String.format(warn, "'"
429 + me.renderKey(entry, {}, rec) + "'");
430
431 if (entry.match(/^unused\d+$/)) {
16152937 432 msg += " " + gettext('This will permanently erase all data.');
f97670f5
DM
433 }
434
435 return msg;
436 },
437 handler: function(b, e, rec) {
e7ade592 438 Proxmox.Utils.API2Request({
f97670f5
DM
439 url: '/api2/extjs/' + baseurl,
440 waitMsgTarget: me,
3b2e557f 441 method: b.RESTMethod,
f97670f5
DM
442 params: {
443 'delete': rec.data.key
444 },
445 callback: function() {
446 reload();
447 },
448 failure: function (response, opts) {
449 Ext.Msg.alert('Error', response.htmlStatus);
3b2e557f
TM
450 },
451 success: function(response, options) {
452 if (b.RESTMethod === 'POST') {
453 var upid = response.result.data;
454 var win = Ext.create('Proxmox.window.TaskProgress', {
455 upid: upid,
456 listeners: {
457 destroy: function () {
458 me.reload();
459 }
460 }
461 });
462 win.show();
463 }
f97670f5
DM
464 }
465 });
877fce54
EK
466 },
467 listeners: {
468 render: function(btn) {
469 // hack: calculate an optimal button width on first display
470 // to prevent the whole toolbar to move when we switch
471 // between the "Remove" and "Detach" labels
472 var def = btn.getSize().width;
473
474 btn.setText(btn.altText);
475 var alt = btn.getSize().width;
476
477 btn.setText(btn.defaultText);
478
479 var optimal = alt > def ? alt : def;
480 btn.setSize({ width: optimal });
481 }
f97670f5
DM
482 }
483 });
484
5720fafa 485 var revert_btn = new Proxmox.button.Button({
f97670f5
DM
486 text: gettext('Revert'),
487 selModel: sm,
488 disabled: true,
489 handler: function(b, e, rec) {
490 var rowdef = me.rows[rec.data.key] || {};
491 var keys = rowdef.multiKey || [ rec.data.key ];
492 var revert = keys.join(',');
e7ade592 493 Proxmox.Utils.API2Request({
f97670f5
DM
494 url: '/api2/extjs/' + baseurl,
495 waitMsgTarget: me,
496 method: 'PUT',
497 params: {
498 'revert': revert
499 },
500 callback: function() {
501 reload();
502 },
503 failure: function (response, opts) {
504 Ext.Msg.alert('Error',response.htmlStatus);
505 }
506 });
507 }
508 });
509
3b37ab6d
DC
510 var efidisk_menuitem = Ext.create('Ext.menu.Item',{
511 text: gettext('EFI Disk'),
512 iconCls: 'pve-itype-icon-storage',
513 disabled: !caps.vms['VM.Config.Disk'],
514 handler: function() {
1535df62
DC
515
516 var rstoredata = me.rstore.getData().map;
517 // check if ovmf is configured
518 if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') {
519 var win = Ext.create('PVE.qemu.EFIDiskEdit', {
520 url: '/api2/extjs/' + baseurl,
521 pveSelNode: me.pveSelNode
522 });
523 win.on('destroy', reload);
524 win.show();
525 } else {
526 Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
527 }
528
3b37ab6d
DC
529 }
530 });
531
f97670f5
DM
532 var set_button_status = function() {
533 var sm = me.getSelectionModel();
534 var rec = sm.getSelection()[0];
535
1535df62
DC
536 // disable button when we have an efidisk already
537 // disable is ok in this case, because you can instantly
538 // see that there is already one
539 efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined);
2668ba64 540 // en/disable usb add button
25a1063a
DC
541 var usbcount = 0;
542 var pcicount = 0;
b38d0810 543 var hasCloudInit = false;
2668ba64
DC
544 me.rstore.getData().items.forEach(function(item){
545 if (/^usb\d+/.test(item.id)) {
25a1063a
DC
546 usbcount++;
547 } else if (/^hostpci\d+/.test(item.id)) {
548 pcicount++;
2668ba64 549 }
b38d0810
DC
550 if (!hasCloudInit && /vm-.*-cloudinit/.test(item.data.value)) {
551 hasCloudInit = true;
552 }
2668ba64 553 });
3097170d
TL
554
555 // heuristic only for disabling some stuff, the backend has the final word.
556 var noSysConsolePerm = !caps.nodes['Sys.Console'];
557
25a1063a
DC
558 me.down('#addusb').setDisabled(noSysConsolePerm || (usbcount >= 5));
559 me.down('#addpci').setDisabled(noSysConsolePerm || (pcicount >= 4));
3097170d 560 me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit);
3b37ab6d 561
f97670f5
DM
562 if (!rec) {
563 remove_btn.disable();
564 edit_btn.disable();
565 resize_btn.disable();
566 move_btn.disable();
f97670f5
DM
567 revert_btn.disable();
568 return;
569 }
570 var key = rec.data.key;
571 var value = rec.data.value;
572 var rowdef = rows[key];
573
574 var pending = rec.data['delete'] || me.hasPendingChanges(key);
3b2e557f
TM
575 var isUnusedDisk = key.match(/^unused\d+/);
576 var isUsedDisk = !isUnusedDisk &&
25e0bc2d
DC
577 rowdef.tdCls == 'pve-itype-icon-storage' &&
578 (value && !value.match(/media=cdrom/));
f97670f5 579
ab1ee352 580 var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/));
b38d0810 581
3b37ab6d
DC
582 var isEfi = (key === 'efidisk0');
583
f97670f5 584 remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true));
ab1ee352 585 remove_btn.setText((isUsedDisk && !isCloudInit) ? remove_btn.altText : remove_btn.defaultText);
3b2e557f 586 remove_btn.RESTMethod = isUnusedDisk ? 'POST':'PUT';
f97670f5 587
b38d0810 588 edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor || isCloudInit);
f97670f5 589
877fce54 590 resize_btn.setDisabled(pending || !isUsedDisk);
f97670f5 591
877fce54 592 move_btn.setDisabled(pending || !isUsedDisk);
f97670f5 593
f97670f5
DM
594 revert_btn.setDisabled(!pending);
595
596 };
597
f09dd6c1 598 Ext.apply(me, {
f97670f5
DM
599 url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending',
600 interval: 5000,
601 selModel: sm,
b08ae044 602 run_editor: run_editor,
f97670f5
DM
603 tbar: [
604 {
605 text: gettext('Add'),
606 menu: new Ext.menu.Menu({
607 items: [
608 {
609 text: gettext('Hard Disk'),
610 iconCls: 'pve-itype-icon-storage',
611 disabled: !caps.vms['VM.Config.Disk'],
612 handler: function() {
613 var win = Ext.create('PVE.qemu.HDEdit', {
614 url: '/api2/extjs/' + baseurl,
615 pveSelNode: me.pveSelNode
616 });
617 win.on('destroy', reload);
618 win.show();
619 }
620 },
621 {
622 text: gettext('CD/DVD Drive'),
623 iconCls: 'pve-itype-icon-cdrom',
624 disabled: !caps.vms['VM.Config.Disk'],
625 handler: function() {
626 var win = Ext.create('PVE.qemu.CDEdit', {
627 url: '/api2/extjs/' + baseurl,
628 pveSelNode: me.pveSelNode
629 });
630 win.on('destroy', reload);
631 win.show();
632 }
633 },
634 {
635 text: gettext('Network Device'),
636 iconCls: 'pve-itype-icon-network',
637 disabled: !caps.vms['VM.Config.Network'],
638 handler: function() {
639 var win = Ext.create('PVE.qemu.NetworkEdit', {
640 url: '/api2/extjs/' + baseurl,
641 pveSelNode: me.pveSelNode
642 });
643 win.on('destroy', reload);
644 win.show();
645 }
3b37ab6d 646 },
2668ba64
DC
647 efidisk_menuitem,
648 {
649 text: gettext('USB Device'),
650 itemId: 'addusb',
651 iconCls: 'pve-itype-icon-usb',
652 disabled: !caps.nodes['Sys.Console'],
653 handler: function() {
654 var win = Ext.create('PVE.qemu.USBEdit', {
655 url: '/api2/extjs/' + baseurl,
656 pveSelNode: me.pveSelNode
657 });
658 win.on('destroy', reload);
659 win.show();
660 }
b38d0810 661 },
25a1063a
DC
662 {
663 text: gettext('PCI Device'),
664 itemId: 'addpci',
665 iconCls: 'pve-itype-icon-pci',
666 disabled: !caps.nodes['Sys.Console'],
667 handler: function() {
668 var win = Ext.create('PVE.qemu.PCIEdit', {
669 url: '/api2/extjs/' + baseurl,
670 pveSelNode: me.pveSelNode
671 });
672 win.on('destroy', reload);
673 win.show();
674 }
675 },
37977fa5
TL
676 {
677 text: gettext('Serial Port'),
678 itemId: 'addserial',
679 iconCls: 'pve-itype-icon-serial',
680 disabled: !caps.vms['VM.Config.Options'],
681 handler: function() {
682 var win = Ext.create('PVE.qemu.SerialEdit', {
683 url: '/api2/extjs/' + baseurl
684 });
685 win.on('destroy', reload);
686 win.show();
687 }
688 },
b38d0810
DC
689 {
690 text: gettext('CloudInit Drive'),
691 itemId: 'addci',
692 iconCls: 'pve-itype-icon-cloud',
693 disabled: !caps.nodes['Sys.Console'],
694 handler: function() {
695 var win = Ext.create('PVE.qemu.CIDriveEdit', {
696 url: '/api2/extjs/' + baseurl,
697 pveSelNode: me.pveSelNode
698 });
699 win.on('destroy', reload);
700 win.show();
701 }
2668ba64 702 }
f97670f5
DM
703 ]
704 })
fa522e48 705 },
f97670f5
DM
706 remove_btn,
707 edit_btn,
708 resize_btn,
709 move_btn,
f97670f5
DM
710 revert_btn
711 ],
712 rows: rows,
713 sorterFn: sorterFn,
714 listeners: {
715 itemdblclick: run_editor,
716 selectionchange: set_button_status
717 }
718 });
719
720 me.callParent();
721
f09dd6c1 722 me.on('activate', me.rstore.startUpdate);
f97670f5
DM
723 me.on('destroy', me.rstore.stopUpdate);
724
f09dd6c1 725 me.mon(me.rstore, 'refresh', function() {
f97670f5
DM
726 set_button_status();
727 });
728 }
729});