]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/qemu/HardwareView.js
ui: eslint: fix trailing comma and comma related whitespaces errors
[pve-manager.git] / www / manager6 / qemu / HardwareView.js
index 3ae207b4b0cabeb4a223c3ec9668fe2cece4945f..0bc913b4fc43e8af9dcd33f28b66b8662f3672b0 100644 (file)
@@ -14,21 +14,22 @@ Ext.define('PVE.qemu.HardwareView', {
 
        metaData.tdAttr = "valign=middle";
 
+       if (rowdef.isOnStorageBus) {
+           var value = me.getObjectValue(key, '', false);
+           if (value === '') {
+               value = me.getObjectValue(key, '', true);
+           }
+           if (value.match(/vm-.*-cloudinit/)) {
+               iconCls = 'cloud';
+               txt = rowdef.cloudheader;
+           } else if (value.match(/media=cdrom/)) {
+               metaData.tdCls = 'pve-itype-icon-cdrom';
+               return rowdef.cdheader;
+           }
+       }
+
        if (rowdef.tdCls) {
            metaData.tdCls = rowdef.tdCls;
-           if (rowdef.tdCls == 'pve-itype-icon-storage') { 
-               var value = me.getObjectValue(key, '', false);
-               if (value === '') {
-                   value = me.getObjectValue(key, '', true);
-               }
-               if (value.match(/vm-.*-cloudinit/)) {
-                   metaData.tdCls = 'pve-itype-icon-cloud';
-                   return rowdef.cloudheader;
-               } else if (value.match(/media=cdrom/)) {
-                   metaData.tdCls = 'pve-itype-icon-cdrom';
-                   return rowdef.cdheader;
-               }
-           }
        } else if (iconCls) {
            icon = "<i class='pve-grid-fa fa fa-fw fa-" + iconCls + "'></i>";
            metaData.tdCls += " pve-itype-fa";
@@ -47,7 +48,7 @@ Ext.define('PVE.qemu.HardwareView', {
        var i, confid;
 
        var nodename = me.pveSelNode.data.node;
-       if (!nodename) { 
+       if (!nodename) {
            throw "no node name specified";
        }
 
@@ -59,7 +60,6 @@ Ext.define('PVE.qemu.HardwareView', {
        var caps = Ext.state.Manager.get('GuiCap');
        var diskCap = caps.vms['VM.Config.Disk'];
 
-       /*jslint confusion: true */
        var rows = {
            memory: {
                header: gettext('Memory'),
@@ -88,12 +88,12 @@ Ext.define('PVE.qemu.HardwareView', {
                        res += ' [balloon=0]';
                    }
                    return res;
-               }
+               },
            },
            sockets: {
                header: gettext('Processors'),
                never_delete: true,
-               editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ? 
+               editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ?
                    'PVE.qemu.ProcessorEdit' : undefined,
                tdCls: 'pve-itype-icon-processor',
                group: 3,
@@ -133,7 +133,7 @@ Ext.define('PVE.qemu.HardwareView', {
                    }
 
                    return res;
-               }
+               },
            },
            bios: {
                header: 'BIOS',
@@ -142,16 +142,16 @@ Ext.define('PVE.qemu.HardwareView', {
                editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.BiosEdit' : undefined,
                defaultValue: '',
                iconCls: 'microchip',
-               renderer: PVE.Utils.render_qemu_bios
+               renderer: PVE.Utils.render_qemu_bios,
            },
            vga: {
                header: gettext('Display'),
                editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
                never_delete: true,
-               tdCls: 'pve-itype-icon-display',
+               iconCls: 'desktop',
                group:5,
                defaultValue: '',
-               renderer: PVE.Utils.render_kvm_vga_driver               
+               renderer: PVE.Utils.render_kvm_vga_driver,
            },
            machine: {
                header: gettext('Machine'),
@@ -166,14 +166,14 @@ Ext.define('PVE.qemu.HardwareView', {
                        fieldLabel: gettext('Machine'),
                        comboItems: [
                            ['__default__', PVE.Utils.render_qemu_machine('')],
-                           ['q35', 'q35']
-                       ]
+                           ['q35', 'q35'],
+                       ],
                    }]} : undefined,
                iconCls: 'cogs',
                never_delete: true,
                group: 6,
                defaultValue: '',
-               renderer: PVE.Utils.render_qemu_machine
+               renderer: PVE.Utils.render_qemu_machine,
            },
            scsihw: {
                header: gettext('SCSI Controller'),
@@ -182,80 +182,86 @@ Ext.define('PVE.qemu.HardwareView', {
                renderer: PVE.Utils.render_scsihw,
                group: 7,
                never_delete: true,
-               defaultValue: ''
+               defaultValue: '',
+           },
+           vmstate: {
+               header: gettext('Hibernation VM State'),
+               iconCls: 'download',
+               del_extra_msg: gettext('The saved VM state will be permanently lost.'),
+               group: 100,
            },
            cores: {
-               visible: false
+               visible: false,
            },
            cpu: {
-               visible: false
+               visible: false,
            },
            numa: {
-               visible: false
+               visible: false,
            },
            balloon: {
-               visible: false
+               visible: false,
            },
            hotplug: {
-               visible: false
+               visible: false,
            },
            vcpus: {
-               visible: false
+               visible: false,
            },
            cpuunits: {
-               visible: false
+               visible: false,
            },
            cpulimit: {
-               visible: false
+               visible: false,
            },
            shares: {
-               visible: false
-           }
+               visible: false,
+           },
        };
-       /*jslint confusion: false */
 
        PVE.Utils.forEachBus(undefined, function(type, id) {
            var confid = type + id;
            rows[confid] = {
                group: 10,
-               tdCls: 'pve-itype-icon-storage',
+               iconCls: 'hdd-o',
                editor: 'PVE.qemu.HDEdit',
                never_delete: caps.vms['VM.Config.Disk'] ? false : true,
+               isOnStorageBus: true,
                header: gettext('Hard Disk') + ' (' + confid +')',
                cdheader: gettext('CD/DVD Drive') + ' (' + confid +')',
-               cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')'
+               cloudheader: gettext('CloudInit Drive') + ' (' + confid + ')',
            };
        });
-       for (i = 0; i < 32; i++) {
+       for (i = 0; i < PVE.Utils.hardware_counts.net; i++) {
            confid = "net" + i.toString();
            rows[confid] = {
                group: 15,
                order: i,
-               tdCls: 'pve-itype-icon-network',
+               iconCls: 'exchange',
                editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
                never_delete: caps.vms['VM.Config.Network'] ? false : true,
-               header: gettext('Network Device') + ' (' + confid +')'
+               header: gettext('Network Device') + ' (' + confid +')',
            };
        }
        rows.efidisk0 = {
            group: 20,
-           tdCls: 'pve-itype-icon-storage',
+           iconCls: 'hdd-o',
            editor: null,
            never_delete: caps.vms['VM.Config.Disk'] ? false : true,
-           header: gettext('EFI Disk')
+           header: gettext('EFI Disk'),
        };
-       for (i = 0; i < 5; i++) {
+       for (i = 0; i < PVE.Utils.hardware_counts.usb; i++) {
            confid = "usb" + i.toString();
            rows[confid] = {
                group: 25,
                order: i,
-               tdCls: 'pve-itype-icon-usb',
+               iconCls: 'usb',
                editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.USBEdit' : undefined,
                never_delete: caps.nodes['Sys.Console'] ? false : true,
-               header: gettext('USB Device') + ' (' + confid + ')'
+               header: gettext('USB Device') + ' (' + confid + ')',
            };
        }
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < PVE.Utils.hardware_counts.hostpci; i++) {
            confid = "hostpci" + i.toString();
            rows[confid] = {
                group: 30,
@@ -263,28 +269,43 @@ Ext.define('PVE.qemu.HardwareView', {
                tdCls: 'pve-itype-icon-pci',
                never_delete: caps.nodes['Sys.Console'] ? false : true,
                editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.PCIEdit' : undefined,
-               header: gettext('PCI Device') + ' (' + confid + ')'
+               header: gettext('PCI Device') + ' (' + confid + ')',
            };
        }
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < PVE.Utils.hardware_counts.serial; i++) {
            confid = "serial" + i.toString();
            rows[confid] = {
                group: 35,
                order: i,
                tdCls: 'pve-itype-icon-serial',
                never_delete: caps.nodes['Sys.Console'] ? false : true,
-               header: gettext('Serial Port') + ' (' + confid + ')'
+               header: gettext('Serial Port') + ' (' + confid + ')',
            };
        }
+       rows.audio0 = {
+           group: 40,
+           iconCls: 'volume-up',
+           editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.AudioEdit' : undefined,
+           never_delete: caps.vms['VM.Config.HWType'] ? false : true,
+           header: gettext('Audio Device'),
+       };
        for (i = 0; i < 256; i++) {
            rows["unused" + i.toString()] = {
                group: 99,
                order: i,
-               tdCls: 'pve-itype-icon-storage',
+               iconCls: 'hdd-o',
+               del_extra_msg: gettext('This will permanently erase all data.'),
                editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
-               header: gettext('Unused Disk') + ' ' + i.toString()
+               header: gettext('Unused Disk') + ' ' + i.toString(),
            };
        }
+       rows.rng0 = {
+           group: 45,
+           tdCls: 'pve-itype-icon-die',
+           editor: caps.nodes['Sys.Console'] ? 'PVE.qemu.RNGEdit' : undefined,
+           never_delete: caps.nodes['Sys.Console'] ? false : true,
+           header: gettext("VirtIO RNG"),
+       };
 
        var sorterFn = function(rec1, rec2) {
            var v1 = rec1.data.key;
@@ -297,7 +318,7 @@ Ext.define('PVE.qemu.HardwareView', {
            if ((g1 - g2) !== 0) {
                return g1 - g2;
            }
-           
+
            if ((order1 - order2) !== 0) {
                return order1 - order2;
            }
@@ -311,10 +332,6 @@ Ext.define('PVE.qemu.HardwareView', {
            }
        };
 
-       var reload = function() {
-           me.rstore.load();
-       };
-
        var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config';
 
        var sm = Ext.create('Ext.selection.RowModel', {});
@@ -331,8 +348,8 @@ Ext.define('PVE.qemu.HardwareView', {
            }
 
            var editor = rowdef.editor;
-           if (rowdef.tdCls == 'pve-itype-icon-storage') {
-               var value = me.getObjectValue(rec.data.key, '', true); 
+           if (rowdef.isOnStorageBus) {
+               var value = me.getObjectValue(rec.data.key, '', true);
                if (value.match(/vm-.*-cloudinit/)) {
                    return;
                } else if (value.match(/media=cdrom/)) {
@@ -348,20 +365,20 @@ Ext.define('PVE.qemu.HardwareView', {
                win = Ext.create(editor, {
                    pveSelNode: me.pveSelNode,
                    confid: rec.data.key,
-                   url: '/api2/extjs/' + baseurl
+                   url: '/api2/extjs/' + baseurl,
                });
            } else {
                var config = Ext.apply({
                    pveSelNode: me.pveSelNode,
                    confid: rec.data.key,
-                   url: '/api2/extjs/' + baseurl
+                   url: '/api2/extjs/' + baseurl,
                }, rowdef.editor);
                win = Ext.createWidget(rowdef.editor.xtype, config);
                win.load();
            }
 
            win.show();
-           win.on('destroy', reload);
+           win.on('destroy', me.reload, me);
        };
 
        var run_resize = function() {
@@ -373,12 +390,12 @@ Ext.define('PVE.qemu.HardwareView', {
            var win = Ext.create('PVE.window.HDResize', {
                disk: rec.data.key,
                nodename: nodename,
-               vmid: vmid
+               vmid: vmid,
            });
 
            win.show();
 
-           win.on('destroy', reload);
+           win.on('destroy', me.reload, me);
        };
 
        var run_move = function() {
@@ -390,33 +407,33 @@ Ext.define('PVE.qemu.HardwareView', {
            var win = Ext.create('PVE.window.HDMove', {
                disk: rec.data.key,
                nodename: nodename,
-               vmid: vmid
+               vmid: vmid,
            });
 
            win.show();
 
-           win.on('destroy', reload);
+           win.on('destroy', me.reload, me);
        };
 
        var edit_btn = new Proxmox.button.Button({
            text: gettext('Edit'),
            selModel: sm,
            disabled: true,
-           handler: run_editor
+           handler: run_editor,
         });
 
        var resize_btn = new Proxmox.button.Button({
            text: gettext('Resize disk'),
            selModel: sm,
            disabled: true,
-           handler: run_resize
+           handler: run_resize,
        });
 
        var move_btn = new Proxmox.button.Button({
            text: gettext('Move disk'),
            selModel: sm,
            disabled: true,
-           handler: run_move
+           handler: run_move,
        });
 
        var remove_btn = new Proxmox.button.Button({
@@ -432,13 +449,14 @@ Ext.define('PVE.qemu.HardwareView', {
                if (this.text === this.altText) {
                    warn = gettext('Are you sure you want to detach entry {0}');
                }
+               var key = rec.data.key;
+               var entry = rows[key];
 
-               var entry = rec.data.key;
-               var rendered = me.renderKey(entry, {}, rec);
+               var rendered = me.renderKey(key, {}, rec);
                var msg = Ext.String.format(warn, "'" + rendered + "'");
 
-               if (entry.match(/^unused\d+$/)) {
-                   msg += " " + gettext('This will permanently erase all data.');
+               if (entry.del_extra_msg) {
+                   msg += '<br>' + entry.del_extra_msg;
                }
 
                return msg;
@@ -449,11 +467,9 @@ Ext.define('PVE.qemu.HardwareView', {
                    waitMsgTarget: me,
                    method: b.RESTMethod,
                    params: {
-                       'delete': rec.data.key
-                   },
-                   callback: function() {
-                       reload();
+                       'delete': rec.data.key,
                    },
+                   callback: () => me.reload(),
                    failure: function (response, opts) {
                        Ext.Msg.alert('Error', response.htmlStatus);
                    },
@@ -463,14 +479,12 @@ Ext.define('PVE.qemu.HardwareView', {
                            var win = Ext.create('Proxmox.window.TaskProgress', {
                                upid: upid,
                                listeners: {
-                                   destroy: function () {
-                                       me.reload();
-                                   }
-                               }
+                                   destroy: () => me.reload(),
+                               },
                            });
                            win.show();
                        }
-                   }
+                   },
                });
            },
            listeners: {
@@ -487,85 +501,75 @@ Ext.define('PVE.qemu.HardwareView', {
 
                    var optimal = alt > def ? alt : def;
                    btn.setSize({ width: optimal });
-               }
-           }
+               },
+           },
        });
 
-       var revert_btn = new Proxmox.button.Button({
-           text: gettext('Revert'),
-           selModel: sm,
-           disabled: true,
-           handler: function(b, e, rec) {
-               var rowdef = me.rows[rec.data.key] || {};
-               var keys = rowdef.multiKey ||  [ rec.data.key ];
-               var revert = keys.join(',');
-               Proxmox.Utils.API2Request({
-                   url: '/api2/extjs/' + baseurl,
-                   waitMsgTarget: me,
-                   method: 'PUT',
-                   params: {
-                       'revert': revert
-                   },
-                   callback: function() {
-                       reload();
-                   },
-                   failure: function (response, opts) {
-                       Ext.Msg.alert('Error',response.htmlStatus);
-                   }
-               });
-           }
+       var revert_btn = new PVE.button.PendingRevert({
+           apiurl: '/api2/extjs/' + baseurl,
        });
 
-       var efidisk_menuitem = Ext.create('Ext.menu.Item',{
+       var efidisk_menuitem = Ext.create('Ext.menu.Item', {
            text: gettext('EFI Disk'),
-           iconCls: 'pve-itype-icon-storage',
+           iconCls: 'fa fa-fw fa-hdd-o black',
            disabled: !caps.vms['VM.Config.Disk'],
            handler: function() {
+               let bios = me.rstore.getData().map.bios;
+               let usesEFI = bios && (bios.data.value === 'ovmf' || bios.data.pending === 'ovmf');
 
-               var rstoredata = me.rstore.getData().map;
-               // check if ovmf is configured
-               if (rstoredata.bios && rstoredata.bios.data.value === 'ovmf') {
-                   var win = Ext.create('PVE.qemu.EFIDiskEdit', {
-                       url: '/api2/extjs/' + baseurl,
-                       pveSelNode: me.pveSelNode
-                   });
-                   win.on('destroy', reload);
-                   win.show();
-               } else {
-                   Ext.Msg.alert('Error',gettext('Please select OVMF(UEFI) as BIOS first.'));
-               }
-
-           }
+               var win = Ext.create('PVE.qemu.EFIDiskEdit', {
+                   url: '/api2/extjs/' + baseurl,
+                   pveSelNode: me.pveSelNode,
+                   usesEFI: usesEFI,
+               });
+               win.on('destroy', me.reload, me);
+               win.show();
+           },
        });
 
+       let counts = {};
+       let isAtLimit = (type) => (counts[type] >= PVE.Utils.hardware_counts[type]);
+
        var set_button_status = function() {
            var sm = me.getSelectionModel();
            var rec = sm.getSelection()[0];
 
-           // disable button when we have an efidisk already
-           // disable is ok in this case, because you can instantly
-           // see that there is already one
-           efidisk_menuitem.setDisabled(me.rstore.getData().map.efidisk0 !== undefined);
-           // en/disable usb add button
-           var usbcount = 0;
-           var pcicount = 0;
+           // en/disable hardwarebuttons
+           counts = {};
            var hasCloudInit = false;
            me.rstore.getData().items.forEach(function(item){
-               if (/^usb\d+/.test(item.id)) {
-                   usbcount++;
-               } else if (/^hostpci\d+/.test(item.id)) {
-                   pcicount++;
-               }
-               if (!hasCloudInit && /vm-.*-cloudinit/.test(item.data.value)) {
+               if (!hasCloudInit && (
+                   /vm-.*-cloudinit/.test(item.data.value) ||
+                   /vm-.*-cloudinit/.test(item.data.pending)
+               )) {
                    hasCloudInit = true;
+                   return;
+               }
+
+               let match = item.id.match(/^([^\d]+)\d+$/);
+               let type;
+               if (match && PVE.Utils.hardware_counts[match[1]] !== undefined) {
+                   type = match[1];
+               } else {
+                   return;
                }
+
+               counts[type] = (counts[type] || 0) + 1;
            });
 
            // heuristic only for disabling some stuff, the backend has the final word.
            var noSysConsolePerm = !caps.nodes['Sys.Console'];
+           var noVMConfigHWTypePerm = !caps.vms['VM.Config.HWType'];
+           var noVMConfigNetPerm = !caps.vms['VM.Config.Network'];
+
 
-           me.down('#addusb').setDisabled(noSysConsolePerm || (usbcount >= 5));
-           me.down('#addpci').setDisabled(noSysConsolePerm || (pcicount >= 4));
+           me.down('#addusb').setDisabled(noSysConsolePerm || isAtLimit('usb'));
+           me.down('#addpci').setDisabled(noSysConsolePerm || isAtLimit('hostpci'));
+           me.down('#addaudio').setDisabled(noVMConfigHWTypePerm || isAtLimit('audio'));
+           me.down('#addserial').setDisabled(noVMConfigHWTypePerm || isAtLimit('serial'));
+           me.down('#addnet').setDisabled(noVMConfigNetPerm || isAtLimit('net'));
+           me.down('#addrng').setDisabled(noSysConsolePerm || isAtLimit('rng'));
+           efidisk_menuitem.setDisabled(isAtLimit('efidisk'));
            me.down('#addci').setDisabled(noSysConsolePerm || hasCloudInit);
 
            if (!rec) {
@@ -583,9 +587,7 @@ Ext.define('PVE.qemu.HardwareView', {
            var pending = rec.data['delete'] || me.hasPendingChanges(key);
            var isCDRom = (value && !!value.toString().match(/media=cdrom/));
            var isUnusedDisk = key.match(/^unused\d+/);
-           var isUsedDisk = !isUnusedDisk &&
-               rowdef.tdCls == 'pve-itype-icon-storage' &&
-               !isCDRom;
+           var isUsedDisk = !isUnusedDisk && rowdef.isOnStorageBus && !isCDRom;
 
            var isCloudInit = (value && value.toString().match(/vm-.*-cloudinit/));
 
@@ -599,7 +601,7 @@ Ext.define('PVE.qemu.HardwareView', {
 
            resize_btn.setDisabled(pending || !isUsedDisk || !diskCap);
 
-           move_btn.setDisabled(pending || !isUsedDisk || !diskCap);
+           move_btn.setDisabled(pending || !(isUsedDisk || isEfi) || !diskCap);
 
            revert_btn.setDisabled(!pending);
 
@@ -610,23 +612,24 @@ Ext.define('PVE.qemu.HardwareView', {
            interval: 5000,
            selModel: sm,
            run_editor: run_editor,
-           tbar: [ 
+           tbar: [
                {
                    text: gettext('Add'),
                    menu: new Ext.menu.Menu({
+                       cls: 'pve-add-hw-menu',
                        items: [
                            {
                                text: gettext('Hard Disk'),
-                               iconCls: 'pve-itype-icon-storage',
+                               iconCls: 'fa fa-fw fa-hdd-o black',
                                disabled: !caps.vms['VM.Config.Disk'],
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.HDEdit', {
                                        url: '/api2/extjs/' + baseurl,
-                                       pveSelNode: me.pveSelNode
+                                       pveSelNode: me.pveSelNode,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
+                               },
                            },
                            {
                                text: gettext('CD/DVD Drive'),
@@ -635,40 +638,41 @@ Ext.define('PVE.qemu.HardwareView', {
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.CDEdit', {
                                        url: '/api2/extjs/' + baseurl,
-                                       pveSelNode: me.pveSelNode
+                                       pveSelNode: me.pveSelNode,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
+                               },
                            },
                            {
                                text: gettext('Network Device'),
-                               iconCls: 'pve-itype-icon-network',
+                               itemId: 'addnet',
+                               iconCls: 'fa fa-fw fa-exchange black',
                                disabled: !caps.vms['VM.Config.Network'],
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.NetworkEdit', {
                                        url: '/api2/extjs/' + baseurl,
                                        pveSelNode: me.pveSelNode,
-                                       isCreate: true
+                                       isCreate: true,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
+                               },
                            },
                            efidisk_menuitem,
                            {
                                text: gettext('USB Device'),
                                itemId: 'addusb',
-                               iconCls: 'pve-itype-icon-usb',
+                               iconCls: 'fa fa-fw fa-usb black',
                                disabled: !caps.nodes['Sys.Console'],
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.USBEdit', {
                                        url: '/api2/extjs/' + baseurl,
-                                       pveSelNode: me.pveSelNode
+                                       pveSelNode: me.pveSelNode,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
+                               },
                            },
                            {
                                text: gettext('PCI Device'),
@@ -678,11 +682,11 @@ Ext.define('PVE.qemu.HardwareView', {
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.PCIEdit', {
                                        url: '/api2/extjs/' + baseurl,
-                                       pveSelNode: me.pveSelNode
+                                       pveSelNode: me.pveSelNode,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
+                               },
                            },
                            {
                                text: gettext('Serial Port'),
@@ -691,50 +695,78 @@ Ext.define('PVE.qemu.HardwareView', {
                                disabled: !caps.vms['VM.Config.Options'],
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.SerialEdit', {
-                                       url: '/api2/extjs/' + baseurl
+                                       url: '/api2/extjs/' + baseurl,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
+                               },
                            },
                            {
                                text: gettext('CloudInit Drive'),
                                itemId: 'addci',
-                               iconCls: 'pve-itype-icon-cloud',
+                               iconCls: 'fa fa-fw fa-cloud black',
                                disabled: !caps.nodes['Sys.Console'],
                                handler: function() {
                                    var win = Ext.create('PVE.qemu.CIDriveEdit', {
                                        url: '/api2/extjs/' + baseurl,
-                                       pveSelNode: me.pveSelNode
+                                       pveSelNode: me.pveSelNode,
                                    });
-                                   win.on('destroy', reload);
+                                   win.on('destroy', me.reload, me);
                                    win.show();
-                               }
-                           }
-                       ]
-                   })
+                               },
+                           },
+                           {
+                               text: gettext('Audio Device'),
+                               itemId: 'addaudio',
+                               iconCls: 'fa fa-fw fa-volume-up black',
+                               disabled: !caps.vms['VM.Config.HWType'],
+                               handler: function() {
+                                   var win = Ext.create('PVE.qemu.AudioEdit', {
+                                       url: '/api2/extjs/' + baseurl,
+                                       isCreate: true,
+                                       isAdd: true,
+                                   });
+                                   win.on('destroy', me.reload, me);
+                                   win.show();
+                               },
+                           },
+                           {
+                               text: gettext("VirtIO RNG"),
+                               itemId: 'addrng',
+                               iconCls: 'pve-itype-icon-die',
+                               disabled: !caps.nodes['Sys.Console'],
+                               handler: function() {
+                                   var win = Ext.create('PVE.qemu.RNGEdit', {
+                                       url: '/api2/extjs/' + baseurl,
+                                       isCreate: true,
+                                       isAdd: true,
+                                   });
+                                   win.on('destroy', me.reload, me);
+                                   win.show();
+                               },
+                           },
+                       ],
+                   }),
                },
                remove_btn,
                edit_btn,
                resize_btn,
                move_btn,
-               revert_btn
+               revert_btn,
            ],
            rows: rows,
            sorterFn: sorterFn,
            listeners: {
                itemdblclick: run_editor,
-               selectionchange: set_button_status
-           }
+               selectionchange: set_button_status,
+           },
        });
 
        me.callParent();
 
-       me.on('activate', me.rstore.startUpdate);
-       me.on('destroy', me.rstore.stopUpdate); 
+       me.on('activate', me.rstore.startUpdate, me.rstore);
+       me.on('destroy', me.rstore.stopUpdate, me.rstore);
 
-       me.mon(me.rstore, 'refresh', function() {
-           set_button_status();
-       });
-    }
+       me.mon(me.getStore(), 'datachanged', set_button_status, me);
+    },
 });