[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