public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Filip Schauer <f.schauer@proxmox.com>
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	[thread overview]
Message-ID: <20260223130706.90972-9-f.schauer@proxmox.com> (raw)
In-Reply-To: <20260223130706.90972-1-f.schauer@proxmox.com>

Signed-off-by: Filip Schauer <f.schauer@proxmox.com>
---
 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





      parent reply	other threads:[~2026-02-23 13:10 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-23 13:04 [PATCH common/container/manager 0/8] implement per-mountpoint uid/gid mapping Filip Schauer
2026-02-23 13:04 ` [PATCH common 1/8] tools: export O_CLOEXEC constant Filip Schauer
2026-02-23 13:04 ` [PATCH common 2/8] syscall: add missing mount attribute constants Filip Schauer
2026-02-23 13:04 ` [PATCH common 3/8] tools: add mount_setattr syscall Filip Schauer
2026-02-23 13:04 ` [PATCH container 4/8] namespaces: relax prototype of run_in_userns Filip Schauer
2026-02-23 13:04 ` [PATCH container 5/8] namespaces: refactor run_in_userns Filip Schauer
2026-02-23 13:04 ` [PATCH container 6/8] namespaces: add helper to create user namespace from idmap Filip Schauer
2026-02-23 13:04 ` [PATCH container 7/8] implement per-mountpoint uid/gid mapping Filip Schauer
2026-02-23 13:04 ` Filip Schauer [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=20260223130706.90972-9-f.schauer@proxmox.com \
    --to=f.schauer@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