X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=www%2Fmanager6%2Fqemu%2FBootOrderEdit.js;h=c1f24ff4c9494d359d7c11478a1109dcfe37e245;hb=refs%2Fheads%2Fmaster;hp=9601313eacad11fb0ace51a221e550b0c9bf759d;hpb=638a57e735aa0fde2a40f13e37cc246f7c8ec2a0;p=pve-manager.git diff --git a/www/manager6/qemu/BootOrderEdit.js b/www/manager6/qemu/BootOrderEdit.js index 9601313e..c1f24ff4 100644 --- a/www/manager6/qemu/BootOrderEdit.js +++ b/www/manager6/qemu/BootOrderEdit.js @@ -1,194 +1,273 @@ -Ext.define('PVE.qemu.BootOrderPanel', { - extend: 'PVE.panel.InputPanel', - - vmconfig: {}, // store loaded vm config +Ext.define('pve-boot-order-entry', { + extend: 'Ext.data.Model', + fields: [ + { name: 'name', type: 'string' }, + { name: 'enabled', type: 'bool' }, + { name: 'desc', type: 'string' }, + ], +}); - bootdisk: undefined, - curSel1: '', - curSel2: '', - curSel3: '', +Ext.define('PVE.qemu.BootOrderPanel', { + extend: 'Proxmox.panel.InputPanel', + alias: 'widget.pveQemuBootOrderPanel', - onGetValues: function(values) { - var me = this; + onlineHelp: 'qm_bootorder', - var order = ''; + vmconfig: {}, // store loaded vm config + store: undefined, + + inUpdate: false, + controller: { + xclass: 'Ext.app.ViewController', + + init: function(view) { + let me = this; + + let grid = me.lookup('grid'); + let marker = me.lookup('marker'); + let emptyWarning = me.lookup('emptyWarning'); + + marker.originalValue = undefined; + + view.store = Ext.create('Ext.data.Store', { + model: 'pve-boot-order-entry', + listeners: { + update: function() { + this.commitChanges(); + let val = view.calculateValue(); + if (marker.originalValue === undefined) { + marker.originalValue = val; + } + view.inUpdate = true; + marker.setValue(val); + view.inUpdate = false; + marker.checkDirty(); + emptyWarning.setHidden(val !== ''); + grid.getView().refresh(); + }, + }, + }); + grid.setStore(view.store); + }, + }, - if (me.curSel1) { - order = order + me.curSel1; - } - if (me.curSel2) { - order = order + me.curSel2; - } - if (me.curSel3) { - order = order + me.curSel3; - } + isCloudinit: (v) => v.match(/media=cdrom/) && v.match(/[:/]vm-\d+-cloudinit/), - var res = { boot: order }; - if (me.bootdisk && (me.curSel1 === 'c' || me.curSel2 === 'c' || me.curSel3 === 'c') ) { - res.bootdisk = me.bootdisk; - } else { - res['delete'] = 'bootdisk'; - } + isDisk: function(value) { + return PVE.Utils.bus_match.test(value); + }, - return res; + isBootdev: function(dev, value) { + return (this.isDisk(dev) && !this.isCloudinit(value)) || + (/^net\d+/).test(dev) || + (/^hostpci\d+/).test(dev) || + ((/^usb\d+/).test(dev) && !(/spice/).test(value)); }, setVMConfig: function(vmconfig) { - var me = this; - + let me = this; me.vmconfig = vmconfig; - var order = me.vmconfig.boot || 'cdn'; - me.bootdisk = me.vmconfig.bootdisk; - if (!me.vmconfig[me.bootdisk]) { - me.bootdisk = undefined; - } - me.curSel1 = order.substring(0, 1) || ''; - me.curSel2 = order.substring(1, 2) || ''; - me.curSel3 = order.substring(2, 3) || ''; - - me.compute_sel1(); - - me.kv1.resetOriginalValue(); - me.kv2.resetOriginalValue(); - me.kv3.resetOriginalValue(); - }, - - genList: function(includeNone, sel1, sel2) { - var me = this; - var list = []; - - if (sel1 !== 'c' && (sel2 !== 'c')) { - Ext.Object.each(me.vmconfig, function(key, value) { - if ((/^(ide|sata|scsi|virtio)\d+$/).test(key) && - !(/media=cdrom/).test(value)) { - list.push([key, "Disk '" + key + "'"]); + me.store.removeAll(); + + let boot = PVE.Parser.parsePropertyString(me.vmconfig.boot, "legacy"); + + let bootorder = []; + if (boot.order) { + bootorder = boot.order.split(';').map(dev => ({ name: dev, enabled: true })); + } else if (!(/^\s*$/).test(me.vmconfig.boot)) { + // legacy style, transform to new bootorder + let order = boot.legacy || 'cdn'; + let bootdisk = me.vmconfig.bootdisk || undefined; + + // get the first 4 characters (acdn) + // ignore the rest (there should never be more than 4) + let orderList = order.split('').slice(0, 4); + + // build bootdev list + for (let i = 0; i < orderList.length; i++) { + let list = []; + if (orderList[i] === 'c') { + if (bootdisk !== undefined && me.vmconfig[bootdisk]) { + list.push(bootdisk); + } + } else if (orderList[i] === 'd') { + Ext.Object.each(me.vmconfig, function(key, value) { + if (me.isDisk(key) && value.match(/media=cdrom/) && !me.isCloudinit(value)) { + list.push(key); + } + }); + } else if (orderList[i] === 'n') { + Ext.Object.each(me.vmconfig, function(key, value) { + if ((/^net\d+/).test(key)) { + list.push(key); + } + }); } - }); - } - if (sel1 !== 'd' && (sel2 !== 'd')) { - list.push(['d', 'CD-ROM']); - } - if (sel1 !== 'n' && (sel2 !== 'n')) { - list.push(['n', gettext('Network')]); - } - //if (sel1 !== 'a' && (sel2 !== 'a')) { - // list.push(['a', 'Floppy']); - //} - - if (includeNone) { - list.push(['', PVE.Utils.noneText]); + // Object.each iterates in random order, sort alphabetically + list.sort(); + list.forEach(dev => bootorder.push({ name: dev, enabled: true })); + } } - return list; - }, - - compute_sel3: function() { - var me = this; - var list = me.genList(true, me.curSel1, me.curSel2); - me.kv3.store.loadData(list); - me.kv3.setValue((me.curSel3 === 'c') ? me.bootdisk : me.curSel3); - }, - - compute_sel2: function() { - var me = this; - var list = me.genList(true, me.curSel1); - me.kv2.store.loadData(list); - me.kv2.setValue((me.curSel2 === 'c') ? me.bootdisk : me.curSel2); - me.compute_sel3(); - }, - - compute_sel1: function() { - var me = this; - var list = me.genList(false); - me.kv1.store.loadData(list); - me.kv1.setValue((me.curSel1 === 'c') ? me.bootdisk : me.curSel1); - me.compute_sel2(); - }, - - initComponent : function() { - var me = this; - - me.kv1 = Ext.create('PVE.form.KVComboBox', { - fieldLabel: gettext('Boot device') + " 1", - labelWidth: 120, - name: 'bd1', - allowBlank: false, - data: [] + // add disabled devices as well + let disabled = []; + Ext.Object.each(me.vmconfig, function(key, value) { + if (me.isBootdev(key, value) && + !Ext.Array.some(bootorder, x => x.name === key)) { + disabled.push(key); + } }); + disabled.sort(); + disabled.forEach(dev => bootorder.push({ name: dev, enabled: false })); - me.kv2 = Ext.create('PVE.form.KVComboBox', { - fieldLabel: gettext('Boot device') + " 2", - labelWidth: 120, - name: 'bd2', - allowBlank: false, - data: [] + // add descriptions + bootorder.forEach(entry => { + entry.desc = me.vmconfig[entry.name]; }); - me.kv3 = Ext.create('PVE.form.KVComboBox', { - fieldLabel: gettext('Boot device') + " 3", - labelWidth: 120, - name: 'bd3', - allowBlank: false, - data: [] - }); + me.store.insert(0, bootorder); + me.store.fireEvent("update"); + }, - me.mon(me.kv1, 'change', function(t, value) { - if ((/^(ide|sata|scsi|virtio)\d+$/).test(value)) { - me.curSel1 = 'c'; - me.bootdisk = value; - } else { - me.curSel1 = value; - } - me.compute_sel2(); - }); + calculateValue: function() { + let me = this; + return me.store.getData().items + .filter(x => x.data.enabled) + .map(x => x.data.name) + .join(';'); + }, - me.mon(me.kv2, 'change', function(t, value) { - if ((/^(ide|sata|scsi|virtio)\d+$/).test(value)) { - me.curSel2 = 'c'; - me.bootdisk = value; - } else { - me.curSel2 = value; - } - me.compute_sel3(); - }); + onGetValues: function() { + let me = this; + // Note: we allow an empty value, so no 'delete' option + let val = { order: me.calculateValue() }; + let res = { boot: PVE.Parser.printPropertyString(val) }; + return res; + }, - me.mon(me.kv3, 'change', function(t, value) { - if ((/^(ide|sata|scsi|virtio)\d+$/).test(value)) { - me.curSel3 = 'c'; - me.bootdisk = value; - } else { - me.curSel3 = value; - } - }); + items: [ + { + xtype: 'grid', + reference: 'grid', + margin: '0 0 5 0', + minHeight: 150, + defaults: { + sortable: false, + hideable: false, + draggable: false, + }, + columns: [ + { + header: '#', + flex: 4, + renderer: (value, metaData, record, rowIndex) => { + let dragHandle = ""; + let idx = (rowIndex + 1).toString(); + if (record.get('enabled')) { + return dragHandle + idx; + } else { + return dragHandle + "" + idx + ""; + } + }, + }, + { + xtype: 'checkcolumn', + header: gettext('Enabled'), + dataIndex: 'enabled', + flex: 4, + }, + { + header: gettext('Device'), + dataIndex: 'name', + flex: 6, + renderer: (value, metaData, record, rowIndex) => { + let desc = record.get('desc'); + + let icon = '', iconCls; + if (value.match(/^net\d+$/)) { + iconCls = 'exchange'; + } else if (desc.match(/media=cdrom/)) { + metaData.tdCls = 'pve-itype-icon-cdrom'; + } else { + iconCls = 'hdd-o'; + } + if (iconCls !== undefined) { + metaData.tdCls += 'pve-itype-fa'; + icon = ``; + } + + return icon + value; + }, + }, + { + header: gettext('Description'), + dataIndex: 'desc', + flex: 20, + }, + ], + viewConfig: { + plugins: { + ptype: 'gridviewdragdrop', + dragText: gettext('Drag and drop to reorder'), + }, + }, + listeners: { + drop: function() { + // doesn't fire automatically on reorder + this.getStore().fireEvent("update"); + }, + }, + }, + { + xtype: 'component', + html: gettext('Drag and drop to reorder'), + }, + { + xtype: 'displayfield', + reference: 'emptyWarning', + userCls: 'pmx-hint', + value: gettext('Warning: No devices selected, the VM will probably not boot!'), + }, + { + // for dirty marking and 'reset' function + xtype: 'field', + reference: 'marker', + hidden: true, + setValue: function(val) { + let me = this; + let panel = me.up('pveQemuBootOrderPanel'); + + // on form reset, go back to original state + if (!panel.inUpdate) { + panel.setVMConfig(panel.vmconfig); + } - Ext.apply(me, { - items: [ me.kv1, me.kv2, me.kv3 ] - }); - - me.callParent(); - } + // not a subclass, so no callParent; just do it manually + me.setRawValue(me.valueToRaw(val)); + return me.mixins.field.setValue.call(me, val); + }, + }, + ], }); Ext.define('PVE.qemu.BootOrderEdit', { - extend: 'PVE.window.Edit', + extend: 'Proxmox.window.Edit', - initComponent : function() { - var me = this; - - var ipanel = Ext.create('PVE.qemu.BootOrderPanel', {}); + items: [{ + xtype: 'pveQemuBootOrderPanel', + itemId: 'inputpanel', + }], - me.items = [ ipanel ]; - - me.subject = gettext('Boot order'); + subject: gettext('Boot Order'), + width: 640, + initComponent: function() { + let me = this; me.callParent(); - me.load({ - success: function(response, options) { - ipanel.setVMConfig(response.result.data); - } + success: ({ result }) => me.down('#inputpanel').setVMConfig(result.data), }); - } + }, });