all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH manager v3 0/3] ui: add container mount point idmapping
@ 2026-05-12 14:01 Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 1/3] ui: lxc/MPEdit: remove duplicate "mp" assignment Filip Schauer
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Filip Schauer @ 2026-05-12 14:01 UTC (permalink / raw)
  To: pve-devel

Integrate UID/GID mapping for container mount points into the web UI.

Changes since v2:
* Replace segmented None/Passthrough/Custom button
  with passthrough checkbox
* Replace segmented UID/GID button with combobox
* Factor the idmap grid out into its own component

Filip Schauer (3):
  ui: lxc/MPEdit: remove duplicate "mp" assignment
  d/control: bump versiond dependency for pve-container
  ui: lxc/MPEdit: add "idmap" option

 debian/control                 |   2 +-
 www/manager6/Makefile          |   1 +
 www/manager6/lxc/IdMapField.js | 187 +++++++++++++++++++++++++++++++++
 www/manager6/lxc/MPEdit.js     |   9 +-
 4 files changed, 197 insertions(+), 2 deletions(-)
 create mode 100644 www/manager6/lxc/IdMapField.js

-- 
2.47.3





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH manager v3 1/3] ui: lxc/MPEdit: remove duplicate "mp" assignment
  2026-05-12 14:01 [PATCH manager v3 0/3] ui: add container mount point idmapping Filip Schauer
@ 2026-05-12 14:01 ` Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 2/3] d/control: bump versioned dependency for pve-container Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option Filip Schauer
  2 siblings, 0 replies; 6+ messages in thread
From: Filip Schauer @ 2026-05-12 14:01 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 www/manager6/lxc/MPEdit.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/www/manager6/lxc/MPEdit.js b/www/manager6/lxc/MPEdit.js
index 4ed2d07b..b1f67741 100644
--- a/www/manager6/lxc/MPEdit.js
+++ b/www/manager6/lxc/MPEdit.js
@@ -41,7 +41,6 @@ Ext.define('PVE.lxc.MountPointInputPanel', {
         setMPOpt('mp', values.mp);
         let mountOpts = (values.mountoptions || []).join(';');
         setMPOpt('mountoptions', values.mountoptions, mountOpts);
-        setMPOpt('mp', values.mp);
         setMPOpt('backup', values.backup);
         setMPOpt('quota', values.quota);
         setMPOpt('ro', values.ro);
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH manager v3 2/3] d/control: bump versioned dependency for pve-container
  2026-05-12 14:01 [PATCH manager v3 0/3] ui: add container mount point idmapping Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 1/3] ui: lxc/MPEdit: remove duplicate "mp" assignment Filip Schauer
@ 2026-05-12 14:01 ` Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option Filip Schauer
  2 siblings, 0 replies; 6+ messages in thread
From: Filip Schauer @ 2026-05-12 14:01 UTC (permalink / raw)
  To: pve-devel

This is needed for UI integration of container mount point id mapping.

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 debian/control | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/debian/control b/debian/control
index 5a6aec2a..447fbec8 100644
--- a/debian/control
+++ b/debian/control
@@ -86,7 +86,7 @@ Depends: apt (>= 1.5~),
          proxmox-termproxy (>= 2.1.0~),
          proxmox-widget-toolkit (>= 5.1.1),
          pve-cluster (>= 9.0.1),
-         pve-container (>= 5.2.5),
+         pve-container (>= 6.1.6),
          pve-docs (>= 9.0.5),
          pve-firewall,
          pve-ha-manager (>= 5.0.3),
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option
  2026-05-12 14:01 [PATCH manager v3 0/3] ui: add container mount point idmapping Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 1/3] ui: lxc/MPEdit: remove duplicate "mp" assignment Filip Schauer
  2026-05-12 14:01 ` [PATCH manager v3 2/3] d/control: bump versioned dependency for pve-container Filip Schauer
@ 2026-05-12 14:01 ` Filip Schauer
  2026-05-12 14:57   ` Wolfgang Bumiller
  2 siblings, 1 reply; 6+ messages in thread
From: Filip Schauer @ 2026-05-12 14:01 UTC (permalink / raw)
  To: pve-devel

Integrate UID/GID mapping for container mount points into the web UI.

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 www/manager6/Makefile          |   1 +
 www/manager6/lxc/IdMapField.js | 187 +++++++++++++++++++++++++++++++++
 www/manager6/lxc/MPEdit.js     |   8 ++
 3 files changed, 196 insertions(+)
 create mode 100644 www/manager6/lxc/IdMapField.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index f63437d6..85d973fe 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -210,6 +210,7 @@ JSSRC= 							\
 	lxc/DNS.js					\
 	lxc/FeaturesEdit.js				\
 	lxc/EnvEdit.js				\
+	lxc/IdMapField.js				\
 	lxc/MPEdit.js					\
 	lxc/MPResize.js					\
 	lxc/Network.js					\
diff --git a/www/manager6/lxc/IdMapField.js b/www/manager6/lxc/IdMapField.js
new file mode 100644
index 00000000..93731ae2
--- /dev/null
+++ b/www/manager6/lxc/IdMapField.js
@@ -0,0 +1,187 @@
+Ext.define('PVE.lxc.IdMapField', {
+    extend: 'Ext.form.FieldContainer',
+    xtype: 'pveLxcIdMapField',
+
+    layout: { type: 'vbox', align: 'stretch' },
+
+    controller: {
+        xclass: 'Ext.app.ViewController',
+
+        control: {
+            'grid proxmoxintegerfield,grid proxmoxKVComboBox': {
+                change: function (widget, value) {
+                    let me = this;
+                    let record = widget.getWidgetRecord();
+                    let column = widget.getWidgetColumn();
+                    if (!record || !column) {
+                        return;
+                    }
+                    record.set(column.dataIndex, value);
+                    record.commit();
+                    me.updateIdMapField();
+                },
+            },
+        },
+
+        onIdMapFieldChange: function (field, value) {
+            let me = this;
+            let passthrough = value === 'passthrough';
+            let checkbox = me.lookup('passthrough');
+            checkbox.suspendEvent('change');
+            checkbox.setValue(passthrough);
+            checkbox.resumeEvent('change');
+            me.lookup('idmaps').setVisible(!passthrough);
+            me.lookup('addIdmapButton').setVisible(!passthrough);
+
+            let store = me.lookup('idmaps').getStore();
+            if (!passthrough && value) {
+                store.setData(
+                    value.split(';').map((v) => {
+                        let [type, ct, host, length] = v.split(':');
+                        return { type, ct, host, length };
+                    }),
+                );
+            } else {
+                store.removeAll();
+            }
+        },
+
+        onPassthroughCheckboxChange: function (checkbox, checked) {
+            let me = this;
+            me.lookup('idmap').setValue(checked ? 'passthrough' : '');
+        },
+
+        addIdMap: function () {
+            let me = this;
+            me.lookup('idmaps').getStore().add({ type: 'u', ct: '', host: '', length: '' });
+            me.updateIdMapField();
+        },
+
+        removeIdMap: function (button) {
+            let me = this;
+            me.lookup('idmaps').getStore().remove(button.getWidgetRecord());
+            me.updateIdMapField();
+        },
+
+        updateIdMapField: function () {
+            let me = this;
+            let value = me
+                .lookup('idmaps')
+                .getStore()
+                .getRange()
+                .map(({ data: { type, ct, host, length } }) => `${type}:${ct}:${host}:${length}`)
+                .join(';');
+            let field = me.lookup('idmap');
+            field.suspendEvent('change');
+            field.setValue(value);
+            field.resumeEvent('change');
+        },
+    },
+
+    items: [
+        {
+            xtype: 'proxmoxcheckbox',
+            reference: 'passthrough',
+            fieldLabel: gettext('ID Mapping'),
+            boxLabel: gettext('Passthrough'),
+            isFormField: false,
+            listeners: {
+                change: 'onPassthroughCheckboxChange',
+            },
+        },
+        {
+            xtype: 'grid',
+            height: 170,
+            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: 'proxmoxKVComboBox',
+                        margin: '4 0',
+                        allowBlank: false,
+                        comboItems: [
+                            ['u', 'UID'],
+                            ['g', 'GID'],
+                        ],
+                    },
+                    flex: 1,
+                },
+                {
+                    text: gettext('Container'),
+                    xtype: 'widgetcolumn',
+                    dataIndex: 'ct',
+                    widget: {
+                        xtype: 'proxmoxintegerfield',
+                        margin: '4 0',
+                        emptyText: gettext('Container'),
+                        allowBlank: false,
+                        minValue: 0,
+                    },
+                    flex: 1,
+                },
+                {
+                    text: gettext('Host'),
+                    xtype: 'widgetcolumn',
+                    dataIndex: 'host',
+                    widget: {
+                        xtype: 'proxmoxintegerfield',
+                        margin: '4 0',
+                        emptyText: gettext('Host'),
+                        allowBlank: false,
+                        minValue: 0,
+                    },
+                    flex: 1,
+                },
+                {
+                    text: gettext('Range Size'),
+                    xtype: 'widgetcolumn',
+                    dataIndex: 'length',
+                    widget: {
+                        xtype: 'proxmoxintegerfield',
+                        margin: '4 0',
+                        emptyText: gettext('Range Size'),
+                        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',
+            reference: 'addIdmapButton',
+            text: gettext('Add'),
+            iconCls: 'fa fa-plus-circle',
+            handler: 'addIdMap',
+        },
+        {
+            xtype: 'hidden',
+            reference: 'idmap',
+            name: 'idmap',
+            listeners: {
+                change: 'onIdMapFieldChange',
+            },
+        },
+    ],
+});
diff --git a/www/manager6/lxc/MPEdit.js b/www/manager6/lxc/MPEdit.js
index b1f67741..b193ff89 100644
--- a/www/manager6/lxc/MPEdit.js
+++ b/www/manager6/lxc/MPEdit.js
@@ -47,6 +47,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);
@@ -353,6 +354,13 @@ Ext.define('PVE.lxc.MountPointInputPanel', {
             },
         },
     ],
+
+    advancedColumnB: [
+        {
+            xtype: 'pveLxcIdMapField',
+            name: 'idmap',
+        },
+    ],
 });
 
 Ext.define('PVE.lxc.MountPointEdit', {
-- 
2.47.3





^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option
  2026-05-12 14:01 ` [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option Filip Schauer
@ 2026-05-12 14:57   ` Wolfgang Bumiller
  2026-05-13  9:33     ` superseded: " Filip Schauer
  0 siblings, 1 reply; 6+ messages in thread
From: Wolfgang Bumiller @ 2026-05-12 14:57 UTC (permalink / raw)
  To: Filip Schauer; +Cc: pve-devel

On Tue, May 12, 2026 at 04:01:21PM +0200, Filip Schauer wrote:
> Integrate UID/GID mapping for container mount points into the web UI.

Seems better than v1. Some things to consider, though:

Checking and then unchecking `passthrough` wipes the list.
I wonder if the last state of the list should be kept around as long as
the window is open?
On the other hand, at least it's a quick and easy way clear and get rid
of id mapping, but users would probably be looking for an explicit clear
button instead. Then again the list cannot be huge anyway, so it's not
really important. (Although the kernel has raised the limit from 5 to
340 lines long ago...)

> 
> Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
> ---
>  www/manager6/Makefile          |   1 +
>  www/manager6/lxc/IdMapField.js | 187 +++++++++++++++++++++++++++++++++
>  www/manager6/lxc/MPEdit.js     |   8 ++
>  3 files changed, 196 insertions(+)
>  create mode 100644 www/manager6/lxc/IdMapField.js
> 
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index f63437d6..85d973fe 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -210,6 +210,7 @@ JSSRC= 							\
>  	lxc/DNS.js					\
>  	lxc/FeaturesEdit.js				\
>  	lxc/EnvEdit.js				\
> +	lxc/IdMapField.js				\
>  	lxc/MPEdit.js					\
>  	lxc/MPResize.js					\
>  	lxc/Network.js					\
> diff --git a/www/manager6/lxc/IdMapField.js b/www/manager6/lxc/IdMapField.js
> new file mode 100644
> index 00000000..93731ae2
> --- /dev/null
> +++ b/www/manager6/lxc/IdMapField.js
> @@ -0,0 +1,187 @@
> +Ext.define('PVE.lxc.IdMapField', {
> +    extend: 'Ext.form.FieldContainer',
> +    xtype: 'pveLxcIdMapField',
> +
> +    layout: { type: 'vbox', align: 'stretch' },
> +
> +    controller: {
> +        xclass: 'Ext.app.ViewController',
> +
> +        control: {
> +            'grid proxmoxintegerfield,grid proxmoxKVComboBox': {
> +                change: function (widget, value) {
> +                    let me = this;
> +                    let record = widget.getWidgetRecord();
> +                    let column = widget.getWidgetColumn();
> +                    if (!record || !column) {
> +                        return;
> +                    }
> +                    record.set(column.dataIndex, value);
> +                    record.commit();
> +                    me.updateIdMapField();
> +                },
> +            },
> +        },
> +
> +        onIdMapFieldChange: function (field, value) {
> +            let me = this;
> +            let passthrough = value === 'passthrough';
> +            let checkbox = me.lookup('passthrough');
> +            checkbox.suspendEvent('change');
> +            checkbox.setValue(passthrough);
> +            checkbox.resumeEvent('change');
> +            me.lookup('idmaps').setVisible(!passthrough);
> +            me.lookup('addIdmapButton').setVisible(!passthrough);
> +
> +            let store = me.lookup('idmaps').getStore();
> +            if (!passthrough && value) {
> +                store.setData(
> +                    value.split(';').map((v) => {
> +                        let [type, ct, host, length] = v.split(':');
> +                        return { type, ct, host, length };
> +                    }),
> +                );
> +            } else {
> +                store.removeAll();
> +            }
> +        },
> +
> +        onPassthroughCheckboxChange: function (checkbox, checked) {
> +            let me = this;
> +            me.lookup('idmap').setValue(checked ? 'passthrough' : '');
> +        },
> +
> +        addIdMap: function () {
> +            let me = this;
> +            me.lookup('idmaps').getStore().add({ type: 'u', ct: '', host: '', length: '' });
> +            me.updateIdMapField();
> +        },
> +
> +        removeIdMap: function (button) {
> +            let me = this;
> +            me.lookup('idmaps').getStore().remove(button.getWidgetRecord());
> +            me.updateIdMapField();
> +        },
> +
> +        updateIdMapField: function () {
> +            let me = this;
> +            let value = me
> +                .lookup('idmaps')
> +                .getStore()
> +                .getRange()
> +                .map(({ data: { type, ct, host, length } }) => `${type}:${ct}:${host}:${length}`)
> +                .join(';');
> +            let field = me.lookup('idmap');
> +            field.suspendEvent('change');
> +            field.setValue(value);
> +            field.resumeEvent('change');
> +        },
> +    },
> +
> +    items: [
> +        {
> +            xtype: 'proxmoxcheckbox',
> +            reference: 'passthrough',
> +            fieldLabel: gettext('ID Mapping'),
> +            boxLabel: gettext('Passthrough'),
> +            isFormField: false,
> +            listeners: {
> +                change: 'onPassthroughCheckboxChange',
> +            },
> +        },
> +        {
> +            xtype: 'grid',
> +            height: 170,
> +            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: 'proxmoxKVComboBox',
> +                        margin: '4 0',
> +                        allowBlank: false,
> +                        comboItems: [
> +                            ['u', 'UID'],
> +                            ['g', 'GID'],
> +                        ],
> +                    },
> +                    flex: 1,
> +                },
> +                {
> +                    text: gettext('Container'),
> +                    xtype: 'widgetcolumn',
> +                    dataIndex: 'ct',
> +                    widget: {
> +                        xtype: 'proxmoxintegerfield',
> +                        margin: '4 0',
> +                        emptyText: gettext('Container'),
> +                        allowBlank: false,
> +                        minValue: 0,
> +                    },
> +                    flex: 1,
> +                },
> +                {
> +                    text: gettext('Host'),
> +                    xtype: 'widgetcolumn',
> +                    dataIndex: 'host',
> +                    widget: {
> +                        xtype: 'proxmoxintegerfield',
> +                        margin: '4 0',
> +                        emptyText: gettext('Host'),
> +                        allowBlank: false,
> +                        minValue: 0,
> +                    },
> +                    flex: 1,
> +                },
> +                {
> +                    text: gettext('Range Size'),
> +                    xtype: 'widgetcolumn',
> +                    dataIndex: 'length',
> +                    widget: {
> +                        xtype: 'proxmoxintegerfield',
> +                        margin: '4 0',
> +                        emptyText: gettext('Range Size'),
> +                        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',
> +            reference: 'addIdmapButton',
> +            text: gettext('Add'),
> +            iconCls: 'fa fa-plus-circle',
> +            handler: 'addIdMap',
> +        },
> +        {
> +            xtype: 'hidden',
> +            reference: 'idmap',
> +            name: 'idmap',
> +            listeners: {
> +                change: 'onIdMapFieldChange',
> +            },
> +        },
> +    ],
> +});
> diff --git a/www/manager6/lxc/MPEdit.js b/www/manager6/lxc/MPEdit.js
> index b1f67741..b193ff89 100644
> --- a/www/manager6/lxc/MPEdit.js
> +++ b/www/manager6/lxc/MPEdit.js
> @@ -47,6 +47,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);
> @@ -353,6 +354,13 @@ Ext.define('PVE.lxc.MountPointInputPanel', {
>              },
>          },
>      ],
> +
> +    advancedColumnB: [
> +        {
> +            xtype: 'pveLxcIdMapField',
> +            name: 'idmap',
> +        },
> +    ],
>  });
>  
>  Ext.define('PVE.lxc.MountPointEdit', {
> -- 
> 2.47.3




^ permalink raw reply	[flat|nested] 6+ messages in thread

* superseded: Re: [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option
  2026-05-12 14:57   ` Wolfgang Bumiller
@ 2026-05-13  9:33     ` Filip Schauer
  0 siblings, 0 replies; 6+ messages in thread
From: Filip Schauer @ 2026-05-13  9:33 UTC (permalink / raw)
  To: Wolfgang Bumiller; +Cc: pve-devel

On 12/05/2026 16:55, Wolfgang Bumiller wrote:
> Seems better than v1. Some things to consider, though:
>
> Checking and then unchecking `passthrough` wipes the list.
> I wonder if the last state of the list should be kept around as long as
> the window is open?
> On the other hand, at least it's a quick and easy way clear and get rid
> of id mapping, but users would probably be looking for an explicit clear
> button instead. Then again the list cannot be huge anyway, so it's not
> really important. (Although the kernel has raised the limit from 5 to
> 340 lines long ago...)

Added a "Clear" button next to "Add" and the "Passthrough" checkbox no
longer wipes the list in v4:

https://lore.proxmox.com/pve-devel/20260513092830.47167-1-f.schauer@proxmox.com





^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-05-13  9:33 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 14:01 [PATCH manager v3 0/3] ui: add container mount point idmapping Filip Schauer
2026-05-12 14:01 ` [PATCH manager v3 1/3] ui: lxc/MPEdit: remove duplicate "mp" assignment Filip Schauer
2026-05-12 14:01 ` [PATCH manager v3 2/3] d/control: bump versioned dependency for pve-container Filip Schauer
2026-05-12 14:01 ` [PATCH manager v3 3/3] ui: lxc/MPEdit: add "idmap" option Filip Schauer
2026-05-12 14:57   ` Wolfgang Bumiller
2026-05-13  9:33     ` superseded: " Filip Schauer

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal