From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 6897B1FF136 for ; Mon, 23 Feb 2026 14:10:18 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id E1A47C213; Mon, 23 Feb 2026 14:11:12 +0100 (CET) From: Filip Schauer To: pve-devel@lists.proxmox.com Subject: [PATCH manager 8/8] ui: lxc/MPEdit: add "idmap" option Date: Mon, 23 Feb 2026 14:04:54 +0100 Message-ID: <20260223130706.90972-9-f.schauer@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260223130706.90972-1-f.schauer@proxmox.com> References: <20260223130706.90972-1-f.schauer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1771852254209 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.002 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: OFBX3IAGXYMLDLEK67BRJACT3WCI2KQZ X-Message-ID-Hash: OFBX3IAGXYMLDLEK67BRJACT3WCI2KQZ X-MailFrom: f.schauer@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Signed-off-by: Filip Schauer --- www/manager6/lxc/MPEdit.js | 203 +++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/www/manager6/lxc/MPEdit.js b/www/manager6/lxc/MPEdit.js index 4ed2d07b..b07f8882 100644 --- a/www/manager6/lxc/MPEdit.js +++ b/www/manager6/lxc/MPEdit.js @@ -48,6 +48,7 @@ Ext.define('PVE.lxc.MountPointInputPanel', { setMPOpt('acl', values.acl); setMPOpt('replicate', values.replicate); setMPOpt('keepattrs', values.keepattrs); + setMPOpt('idmap', values.idmap); let res = {}; res[confid] = PVE.Parser.printLxcMountPoint(me.mp); @@ -132,6 +133,12 @@ Ext.define('PVE.lxc.MountPointInputPanel', { me.getViewModel().set('type', rec.data.type); }, }, + 'grid proxmoxintegerfield': { + change: 'idmapChanged', + }, + 'field[name=idmap]': { + change: 'setGridData', + }, }, init: function (view) { let _me = this; @@ -154,6 +161,90 @@ Ext.define('PVE.lxc.MountPointInputPanel', { view.setVMConfig(view.vmconfig); } }, + idmapChanged: function (field, value) { + let me = this; + if (value === null) { + return; + } + let record = field.getWidgetRecord(); + if (record === undefined) { + return; + } + let col = field.getWidgetColumn(); + record.set(col.dataIndex, value); + record.commit(); + + me.updateIDMapField(); + }, + idmapTypeChanged: function (button, newValue) { + let me = this; + let record = button.getWidgetRecord(); + if (record === undefined || record.get('type') === newValue) { + return; + } + record.set('type', newValue); + record.commit(); + me.updateIDMapField(); + }, + addIDMap: function () { + let me = this; + me.lookup('idmaps').getStore().add({ + type: 'u', + ct: '', + host: '', + length: '', + }); + + me.updateIDMapField(); + }, + removeIDMap: function (field) { + let me = this; + let record = field.getWidgetRecord(); + if (record === undefined) { + return; + } + + me.lookup('idmaps').getStore().remove(record); + me.updateIDMapField(); + }, + updateIDMapField: function () { + let me = this; + + let idmaps = []; + me.lookup('idmaps') + .getStore() + .each((rec) => { + let { type, ct, host, length } = rec.data; + idmaps.push(`${type}:${ct}:${host}:${length}`); + }); + + let field = me.lookup('idmap'); + field.suspendEvent('change'); + field.setValue(idmaps.join(' ')); + field.resumeEvent('change'); + }, + parseIDMap: function (idmap) { + let [type, ct, host, length] = idmap.split(':'); + + let record = { + type, + ct, + host, + length, + }; + + return record; + }, + setGridData: function (field, value) { + let me = this; + if (!value) { + return; + } + + value = value.split(' '); + let records = value.map((idmap) => me.parseIDMap(idmap)); + me.lookup('idmaps').getStore().setData(records); + }, }, viewModel: { @@ -354,6 +445,118 @@ Ext.define('PVE.lxc.MountPointInputPanel', { }, }, ], + + advancedColumnB: [ + { + xtype: 'displayfield', + fieldLabel: gettext('ID Mapping'), + }, + { + xtype: 'fieldcontainer', + items: [ + { + xtype: 'grid', + height: 200, + scrollable: true, + reference: 'idmaps', + viewConfig: { + emptyText: gettext('No ID maps configured'), + }, + store: { + fields: ['type', 'ct', 'host', 'length'], + data: [], + }, + columns: [ + { + text: gettext('ID Type'), + xtype: 'widgetcolumn', + dataIndex: 'type', + widget: { + xtype: 'segmentedbutton', + items: [ + { + text: 'UID', + value: 'u', + }, + { + text: 'GID', + value: 'g', + }, + ], + listeners: { + change: 'idmapTypeChanged', + }, + }, + flex: 1, + }, + { + text: gettext('Container'), + xtype: 'widgetcolumn', + dataIndex: 'ct', + widget: { + xtype: 'proxmoxintegerfield', + margin: '4 0', + emptyText: gettext('Container'), + isFormField: false, + allowBlank: false, + minValue: 0, + }, + flex: 1, + }, + { + text: gettext('Host'), + xtype: 'widgetcolumn', + dataIndex: 'host', + widget: { + xtype: 'proxmoxintegerfield', + margin: '4 0', + emptyText: gettext('Host'), + isFormField: false, + allowBlank: false, + minValue: 0, + }, + flex: 1, + }, + { + text: gettext('Length'), + xtype: 'widgetcolumn', + dataIndex: 'length', + widget: { + xtype: 'proxmoxintegerfield', + margin: '4 0', + emptyText: gettext('Length'), + isFormField: false, + allowBlank: false, + minValue: 1, + }, + flex: 1, + }, + { + xtype: 'widgetcolumn', + width: 40, + widget: { + xtype: 'button', + margin: '4 0', + iconCls: 'fa fa-trash-o', + handler: 'removeIDMap', + }, + }, + ], + }, + ], + }, + { + xtype: 'button', + text: gettext('Add'), + iconCls: 'fa fa-plus-circle', + handler: 'addIDMap', + }, + { + xtype: 'hidden', + reference: 'idmap', + name: 'idmap', + }, + ], }); Ext.define('PVE.lxc.MountPointEdit', { -- 2.47.3