[pve-devel] [PATCH manager] fix #4068: expose fw_cfg through the GUI

Leo Nunner l.nunner at proxmox.com
Wed Mar 1 10:12:29 CET 2023


Introduces a new UI element to set/edit/delete any number of fw_cfg
arguments in a table-like manner.

Signed-off-by: Leo Nunner <l.nunner at proxmox.com>
---
 www/manager6/Makefile                |   1 +
 www/manager6/qemu/FirmwareCfgEdit.js | 224 +++++++++++++++++++++++++++
 www/manager6/qemu/Options.js         |   6 +
 3 files changed, 231 insertions(+)
 create mode 100644 www/manager6/qemu/FirmwareCfgEdit.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 05afeda4..bf3c8b20 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -217,6 +217,7 @@ JSSRC= 							\
 	qemu/Config.js					\
 	qemu/CreateWizard.js				\
 	qemu/DisplayEdit.js				\
+	qemu/FirmwareCfgEdit.js				\
 	qemu/HDEdit.js					\
 	qemu/HDEfi.js					\
 	qemu/HDTPM.js					\
diff --git a/www/manager6/qemu/FirmwareCfgEdit.js b/www/manager6/qemu/FirmwareCfgEdit.js
new file mode 100644
index 00000000..031042e3
--- /dev/null
+++ b/www/manager6/qemu/FirmwareCfgEdit.js
@@ -0,0 +1,224 @@
+Ext.define('pve-fw-cfg-option', {
+    extend: 'Ext.data.Model',
+    fields: [
+	{ name: 'opt', type: 'string' },
+	{ name: 'val', type: 'string' },
+    ],
+});
+
+Ext.define('PVE.qemu.FirmwareCfgPanel', {
+    extend: 'Ext.form.FieldContainer',
+    alias: 'widget.pveQemuFirmwareCfgPanel',
+
+    config: {}, // store loaded vm config
+    store: undefined,
+
+    inUpdate: false,
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	init: function(view) {
+	    let me = this;
+	    let grid = me.lookup('grid');
+
+	    view.store = Ext.create('Ext.data.Store', {
+		model: 'pve-fw-cfg-option',
+	    });
+
+	    grid.setStore(view.store);
+	},
+
+	addOption: function() {
+	    let me = this;
+	    me.lookup('grid').getStore().add({});
+	},
+
+	removeOption: function(field) {
+	    let me = this;
+	    let record = field.getWidgetRecord();
+	    me.lookup('grid').getStore().remove(record);
+	    me.setMarkerValue();
+	},
+
+	onUpdate: function(record, property, value) {
+	    let me = this;
+
+	    if (record === undefined) {
+		return;
+	    }
+
+	    record.set(property, value);
+	    record.commit();
+	    me.setMarkerValue();
+	},
+
+	onUpdateOption: function(field, value) {
+	    let me = this;
+	    let record = field.getWidgetRecord();
+
+	    me.onUpdate(record, "opt", value);
+	},
+
+	onUpdateValue: function(field, value) {
+	    let me = this;
+	    let record = field.getWidgetRecord();
+
+	    me.onUpdate(record, "val", value);
+	},
+
+	setMarkerValue() {
+	    let me = this;
+	    let view = me.getView();
+
+	    view.inUpdate = true;
+	    me.lookup('marker').setValue(view.calculateValue());
+	    view.inUpdate = false;
+	},
+
+	control: {
+	    "grid textfield[dest=opt]": {
+		change: "onUpdateOption",
+	    },
+	    "grid textfield[dest=val]": {
+		change: "onUpdateValue",
+	    },
+	},
+    },
+
+    loadConfig: function(config) {
+	let me = this;
+	let marker = me.lookup('marker');
+	let list = PVE.Parser.parsePropertyString(config.fw_cfg);
+	let options = [];
+
+	me.config = config;
+	me.store.removeAll();
+
+	for (const [key, value] of Object.entries(list)) {
+	    options.push({
+		opt: key,
+		val: value,
+	    });
+	}
+
+	marker.originalValue = config.fw_cfg;
+	marker.value = config.fw_cfg;
+	me.store.setData(options);
+    },
+
+    calculateValue: function() {
+	let me = this;
+	let ret = [];
+	me.store.each((record) => {
+	    ret.push(record.data.opt + "=" + record.data.val);
+	});
+	return ret.join(",");
+    },
+
+    items: [
+	{
+	    xtype: 'grid',
+	    reference: 'grid',
+	    margin: '0 0 5 0',
+	    height: 150,
+	    defaults: {
+		sortable: false,
+		hideable: false,
+		draggable: false,
+	    },
+	    columns: [
+		{
+		    xtype: 'widgetcolumn',
+		    text: gettext('Option'),
+		    dataIndex: 'opt',
+		    flex: 1,
+		    isFormField: false,
+		    widget: {
+			xtype: 'textfield',
+			allowBlank: false,
+			dest: 'opt',
+			emptyText: 'opt/...',
+		    },
+		},
+		{
+		    xtype: 'widgetcolumn',
+		    text: gettext('Value'),
+		    dataIndex: 'val',
+		    flex: 1,
+		    isFormField: false,
+		    widget: {
+			xtype: 'textfield',
+			allowBlank: false,
+			dest: 'val',
+		    },
+		},
+		{
+		    xtype: 'widgetcolumn',
+		    width: 40,
+		    widget: {
+			xtype: 'button',
+			iconCls: 'fa fa-trash-o',
+			handler: 'removeOption',
+		    },
+		},
+	    ],
+	},
+	{
+	    // for dirty marking and 'reset' function
+	    xtype: 'hiddenfield',
+	    reference: 'marker',
+	    name: 'fw_cfg',
+	    setValue: function(value) {
+		let me = this;
+		let panel = me.up('pveQemuFirmwareCfgPanel');
+
+		// Reset
+		if (!panel.inUpdate) {
+		    panel.loadConfig(panel.config);
+		}
+
+		me.value = value;
+		me.checkDirty();
+	    },
+	    getValue: function() {
+		return this.value;
+	    },
+	    getSubmitValue: function() {
+		return this.value;
+	    },
+	},
+	{
+	    xtype: 'button',
+	    text: gettext('Add'),
+	    iconCls: 'fa fa-plus-circle',
+	    handler: 'addOption',
+	},
+    ],
+});
+
+Ext.define('PVE.qemu.FirmwareCfgEdit', {
+    extend: 'Proxmox.window.Edit',
+
+    items: [{
+	xtype: 'pveQemuFirmwareCfgPanel',
+	itemId: 'inputpanel',
+    }],
+
+    subject: gettext('Firmware Configuration'),
+    onlineHelp: 'qm_fw_cfg',
+    width: 640,
+
+    getValues: function() {
+	let me = this;
+	let values = me.callParent();
+	return values.fw_cfg ? values : { 'delete': 'fw_cfg' };
+    },
+
+    initComponent: function() {
+	let me = this;
+	me.callParent();
+	me.load({
+	    success: ({ result }) => me.down('#inputpanel').loadConfig(result.data),
+	});
+    },
+});
diff --git a/www/manager6/qemu/Options.js b/www/manager6/qemu/Options.js
index 7b112400..80407010 100644
--- a/www/manager6/qemu/Options.js
+++ b/www/manager6/qemu/Options.js
@@ -338,6 +338,12 @@ Ext.define('PVE.qemu.Options', {
 		    },
 		} : undefined,
 	    },
+	    fw_cfg: {
+		header: gettext('QEMU Firmware Configuration'),
+		defaultValue: '',
+		renderer: val => val || Proxmox.Utils.noneText,
+		editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.FirmwareCfgEdit' : undefined,
+	    },
 	    hookscript: {
 		header: gettext('Hookscript'),
 	    },
-- 
2.30.2






More information about the pve-devel mailing list