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