public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Leo Nunner <l.nunner@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH manager] fix #4068: expose fw_cfg through the GUI
Date: Wed,  1 Mar 2023 10:12:29 +0100	[thread overview]
Message-ID: <20230301091229.53505-5-l.nunner@proxmox.com> (raw)
In-Reply-To: <20230301091229.53505-1-l.nunner@proxmox.com>

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@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





      parent reply	other threads:[~2023-03-01  9:12 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-01  9:12 [pve-devel] [PATCH qemu-server docs manager] Implement support for fw_cfg Leo Nunner
2023-03-01  9:12 ` [pve-devel] [PATCH qemu-server 1/2] fix #4068: implement " Leo Nunner
2023-03-15 14:05   ` Wolfgang Bumiller
2023-03-24  9:43     ` Leo Nunner
2023-03-01  9:12 ` [pve-devel] [PATCH qemu-server 2/2] test: add cfg2cmd tests " Leo Nunner
2023-03-01  9:12 ` [pve-devel] [PATCH docs] fix #4068: document fw_cfg parameter Leo Nunner
2023-03-01  9:12 ` Leo Nunner [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230301091229.53505-5-l.nunner@proxmox.com \
    --to=l.nunner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal