* [PATCH manager v3 0/2] fix #6855: ui: storage: add mount options field for nfs, cifs, cephfs
@ 2026-07-30 9:40 Elias Huhsovitz
2026-07-30 9:40 ` [PATCH manager v3 1/2] fix #6855: ui: storage: add mount options field for nfs amd smb/cifs Elias Huhsovitz
2026-07-30 9:40 ` [PATCH manager v3 2/2] ui: storage: add mount options field for cephfs Elias Huhsovitz
0 siblings, 2 replies; 3+ messages in thread
From: Elias Huhsovitz @ 2026-07-30 9:40 UTC (permalink / raw)
To: pve-devel; +Cc: Elias Huhsovitz
Setting custom mount options for NFS, CIFS and CephFS storage
requires users to use the CLI or edit `/etc/pve/storage.cfg`.
This series adds a new `Mount Options` text field allowing users to
add custom mount options directly via the web interface.
The field is located in the `Advanced` section of the edit window.
Bugzilla: https://bugzilla.proxmox.com/show_bug.cgi?id=6855
v2: https://lore.proxmox.com/pve-devel/20260723150316.168440-1-e.huhsovitz@proxmox.com/
v1: https://lore.proxmox.com/pve-devel/20260721121334.115027-1-e.huhsovitz@proxmox.com/
This version extends the UI update to CephFS for the sake of
completion, as the backend API already exposes the options
parameter for it. Additionally, it fixes a logic flaw where the
remount warning hint was incorrectly displayed when adding a
new storage.
Patch 1/2 add mount options field for NFS and CIFS.
Patch 2/2 add mount options field for CephFS.
This was not mentioned in the original bug report, but users have
encountered this issue before:
https://forum.proxmox.com/threads/storage-cephfs-and-multiple-file-systems-passing-options.55018/
Changes v2 -> v3:
- Extend the mount options UI field to CephFS for consistency,
as the backend API already supports this parameter.
- Fix the dynamic hint logic to only display when editing an
existing storage and the configuration is actively changed.
This prevents the hint from showing unnecessarily during
initial creation.
- Fix a typo in the commit message and harmonize formatting.
Changes v1 -> v2:
- Add a dynamic UI hint when a user changes the mount config
or NFS version during an edit, informing them that a remount
is required for changes to take effect.
- Update emptyText placeholders to align with existing Proxmox
VE UI conventions.
- Add filtering of exact duplicate options (for example,
"rdma, seal, rdma" becomes "rdma, seal"). Deep semantic
deduplication (for example, "mount_timeout=60, mount_timeout=70")
is omitted to preserve opaque mount options where such
configurations might be desired by the underlying system.
Note: This was already implemented in the v2 patch, but was
accidentally omitted from the v1 -> v2 changelog.
Comments that have not been implemented:
- Automatic re-mount:
Automatically triggering a remount could introduce
unnecessary complexity and risk disrupting active I/O
operations if a user modifies options while the storage is
currently in use by running guests or backup jobs. If users
need this functionality, it can be added in a separate,
dedicated patch.
Big Thanks to Max for reviewing the previous versions!
Elias Huhsovitz (2):
fix #6855: ui: storage: add mount options field for nfs amd smb/cifs
ui: storage: add mount options field for cephfs
www/manager6/storage/CIFSEdit.js | 69 ++++++++++++++++--
www/manager6/storage/CephFSEdit.js | 59 ++++++++++++++-
www/manager6/storage/NFSEdit.js | 112 +++++++++++++++++++++++------
3 files changed, 210 insertions(+), 30 deletions(-)
--
2.47.3
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH manager v3 1/2] fix #6855: ui: storage: add mount options field for nfs amd smb/cifs
2026-07-30 9:40 [PATCH manager v3 0/2] fix #6855: ui: storage: add mount options field for nfs, cifs, cephfs Elias Huhsovitz
@ 2026-07-30 9:40 ` Elias Huhsovitz
2026-07-30 9:40 ` [PATCH manager v3 2/2] ui: storage: add mount options field for cephfs Elias Huhsovitz
1 sibling, 0 replies; 3+ messages in thread
From: Elias Huhsovitz @ 2026-07-30 9:40 UTC (permalink / raw)
To: pve-devel; +Cc: Elias Huhsovitz
Add a text field for custom mount options to the NFS and CIFS
storage configuration dialogs. This allows users to configure
advanced mount parameters (like `nconnect`, `rdma`, or `seal`)
directly via the web interface, without needing to use the CLI
or manually edit storage.cfg.
When nfs version or mount option is being changed:
Display a hint to the user that changes will require
remounting the storage to take effect.
Reduce the usage of legacy JS practices, such as `var` & `+` string
concatination in affected functions.
Remove unused class property `options`.
Signed-off-by: Elias Huhsovitz <e.huhsovitz@proxmox.com>
---
www/manager6/storage/CIFSEdit.js | 69 +++++++++++++++++--
www/manager6/storage/NFSEdit.js | 112 ++++++++++++++++++++++++-------
2 files changed, 152 insertions(+), 29 deletions(-)
diff --git a/www/manager6/storage/CIFSEdit.js b/www/manager6/storage/CIFSEdit.js
index 5fe3fefe..47e2118a 100644
--- a/www/manager6/storage/CIFSEdit.js
+++ b/www/manager6/storage/CIFSEdit.js
@@ -121,7 +121,7 @@ Ext.define('PVE.storage.CIFSInputPanel', {
onlineHelp: 'storage_cifs',
onGetValues: function (values) {
- let me = this;
+ const me = this;
if (values.password?.length === 0) {
delete values.password;
@@ -133,11 +133,39 @@ Ext.define('PVE.storage.CIFSInputPanel', {
delete values.subdir;
}
+ if (values.options) {
+ values.options = values.options
+ .split(',')
+ .map((opt) => opt.trim())
+ .filter((opt, index, self) => opt !== '' && self.indexOf(opt) === index)
+ .join(',');
+ }
+
+ if (!values.options) {
+ delete values.options;
+ if (!me.isCreate) {
+ values.delete = values.delete ? `${values.delete},options` : 'options';
+ }
+ }
+
return me.callParent([values]);
},
+ setValues: function (values) {
+ const me = this;
+
+ me.originalOptions = values.options || '';
+
+ me.callParent([values]);
+
+ const hint = me.down('#mountOptionsHint');
+ if (hint) {
+ hint.setHidden(true);
+ }
+ },
+
initComponent: function () {
- var me = this;
+ const me = this;
me.column1 = [
{
@@ -149,7 +177,7 @@ Ext.define('PVE.storage.CIFSInputPanel', {
listeners: {
change: function (f, value) {
if (me.isCreate) {
- let exportField = me.down('field[name=share]');
+ const exportField = me.down('field[name=share]');
exportField.setServer(value);
}
},
@@ -166,7 +194,7 @@ Ext.define('PVE.storage.CIFSInputPanel', {
if (!me.isCreate) {
return;
}
- var exportField = me.down('field[name=share]');
+ const exportField = me.down('field[name=share]');
exportField.setUsername(value);
},
},
@@ -181,7 +209,7 @@ Ext.define('PVE.storage.CIFSInputPanel', {
minLength: 1,
listeners: {
change: function (f, value) {
- let exportField = me.down('field[name=share]');
+ const exportField = me.down('field[name=share]');
exportField.setPassword(value);
},
},
@@ -213,7 +241,7 @@ Ext.define('PVE.storage.CIFSInputPanel', {
listeners: {
change: function (f, value) {
if (me.isCreate) {
- let exportField = me.down('field[name=share]');
+ const exportField = me.down('field[name=share]');
exportField.setDomain(value);
}
},
@@ -229,6 +257,35 @@ Ext.define('PVE.storage.CIFSInputPanel', {
},
];
+ me.advancedColumn2 = [
+ {
+ xtype: 'textfield',
+ name: 'options',
+ fieldLabel: gettext('Mount Options'),
+ emptyText: `${gettext('Example')}: rdma, seal`,
+ allowBlank: true,
+ listeners: {
+ change: function (field, newValue) {
+ const hint = me.down('#mountOptionsHint');
+ if (hint) {
+ hint.setHidden(me.isCreate || (newValue || '') === me.originalOptions);
+ }
+ },
+ },
+ },
+ ];
+
+ me.advancedColumnB = me.advancedColumnB || [];
+ me.advancedColumnB.unshift({
+ xtype: 'displayfield',
+ itemId: 'mountOptionsHint',
+ userCls: 'pmx-hint',
+ hidden: true,
+ value: gettext(
+ 'Changes to mount options require remounting the storage to take effect.',
+ ),
+ });
+
me.callParent();
},
});
diff --git a/www/manager6/storage/NFSEdit.js b/www/manager6/storage/NFSEdit.js
index 72db156f..68f4a8ce 100644
--- a/www/manager6/storage/NFSEdit.js
+++ b/www/manager6/storage/NFSEdit.js
@@ -57,34 +57,37 @@ Ext.define('PVE.storage.NFSScan', {
me.callParent();
},
});
-
Ext.define('PVE.storage.NFSInputPanel', {
extend: 'PVE.panel.StorageBase',
onlineHelp: 'storage_nfs',
- options: [],
-
onGetValues: function (values) {
- var me = this;
-
- var i;
- var res = [];
- for (i = 0; i < me.options.length; i++) {
- let item = me.options[i];
- if (!item.match(/^vers=(.*)$/)) {
- res.push(item);
- }
+ const me = this;
+ const res = [];
+
+ if (values.options && values.options.trim() !== '') {
+ const opts = values.options.split(',');
+ opts.forEach((opt) => {
+ const cleanOpt = opt.trim();
+ if (cleanOpt !== '' && !cleanOpt.match(/^vers=(.*)$/) && !res.includes(cleanOpt)) {
+ res.push(cleanOpt);
+ }
+ });
}
+
if (values.nfsversion && values.nfsversion !== '__default__') {
- res.push('vers=' + values.nfsversion);
+ res.push(`vers=${values.nfsversion}`);
}
+
delete values.nfsversion;
- values.options = res.join(',');
- if (values.options === '') {
+
+ if (res.length > 0) {
+ values.options = res.join(',');
+ } else {
delete values.options;
if (!me.isCreate) {
- values.delete = 'options';
+ values.delete = values.delete ? `${values.delete},options` : 'options';
}
}
@@ -92,21 +95,42 @@ Ext.define('PVE.storage.NFSInputPanel', {
},
setValues: function (values) {
- var me = this;
+ const me = this;
+
if (values.options) {
- me.options = values.options.split(',');
- me.options.forEach(function (item) {
- var match = item.match(/^vers=(.*)$/);
+ const opts = values.options.split(',').map((item) => item.trim());
+
+ opts.forEach((item) => {
+ const match = item.match(/^vers=(.*)$/);
if (match) {
values.nfsversion = match[1];
}
});
+
+ // Filter out "vers=..." and empty strings
+ const otherOptions = opts.filter(
+ (item, index, self) => item !== '' && !item.match(/^vers=(.*)$/),
+ );
+ values.options = otherOptions.join(',');
+
+ if (values.options === '') {
+ delete values.options;
+ }
+ }
+
+ me.originalOptions = values.options || '';
+ me.originalNfsVersion = values.nfsversion || '__default__';
+
+ me.callParent([values]);
+
+ const hint = me.down('#mountOptionsHint');
+ if (hint) {
+ hint.setHidden(true);
}
- return me.callParent([values]);
},
initComponent: function () {
- var me = this;
+ const me = this;
me.column1 = [
{
@@ -118,7 +142,7 @@ Ext.define('PVE.storage.NFSInputPanel', {
listeners: {
change: function (f, value) {
if (me.isCreate) {
- let exportField = me.down('field[name=export]');
+ const exportField = me.down('field[name=export]');
exportField.setServer(value);
exportField.setValue('');
}
@@ -143,6 +167,25 @@ Ext.define('PVE.storage.NFSInputPanel', {
];
me.advancedColumn2 = [
+ {
+ xtype: 'textfield',
+ name: 'options',
+ fieldLabel: gettext('Mount Options'),
+ emptyText: `${gettext('Example')}: nconnect=4, soft`,
+ allowBlank: true,
+ listeners: {
+ change: function (field, newValue) {
+ const hint = me.down('#mountOptionsHint');
+ if (hint) {
+ const currentVersion =
+ me.down('field[name=nfsversion]')?.getValue() || '__default__';
+ const isOptionsChanged = (newValue || '') !== me.originalOptions;
+ const isVersionChanged = currentVersion !== me.originalNfsVersion;
+ hint.setHidden(me.isCreate || !(isOptionsChanged || isVersionChanged));
+ }
+ },
+ },
+ },
{
xtype: 'proxmoxKVComboBox',
fieldLabel: gettext('NFS Version'),
@@ -156,9 +199,32 @@ Ext.define('PVE.storage.NFSInputPanel', {
['4.1', '4.1'],
['4.2', '4.2'],
],
+ listeners: {
+ change: function (field, newValue) {
+ const hint = me.down('#mountOptionsHint');
+ if (hint) {
+ const currentOptions = me.down('field[name=options]')?.getValue() || '';
+ const isOptionsChanged = currentOptions !== me.originalOptions;
+ const isVersionChanged =
+ (newValue || '__default__') !== me.originalNfsVersion;
+ hint.setHidden(me.isCreate || !(isOptionsChanged || isVersionChanged));
+ }
+ },
+ },
},
];
+ me.advancedColumnB = me.advancedColumnB || [];
+ me.advancedColumnB.unshift({
+ xtype: 'displayfield',
+ itemId: 'mountOptionsHint',
+ userCls: 'pmx-hint',
+ hidden: true,
+ value: gettext(
+ 'Changes to mount options require remounting the storage to take effect.',
+ ),
+ });
+
me.callParent();
},
});
--
2.47.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH manager v3 2/2] ui: storage: add mount options field for cephfs
2026-07-30 9:40 [PATCH manager v3 0/2] fix #6855: ui: storage: add mount options field for nfs, cifs, cephfs Elias Huhsovitz
2026-07-30 9:40 ` [PATCH manager v3 1/2] fix #6855: ui: storage: add mount options field for nfs amd smb/cifs Elias Huhsovitz
@ 2026-07-30 9:40 ` Elias Huhsovitz
1 sibling, 0 replies; 3+ messages in thread
From: Elias Huhsovitz @ 2026-07-30 9:40 UTC (permalink / raw)
To: pve-devel; +Cc: Elias Huhsovitz
Add a text field for custom mount options to the CephFS
storage configuration dialog. This allows users to configure
advanced mount parameters (like 'ms_mode' or 'mount_timeout')
directly via the web interface, without needing to use the CLI
or manually edit storage.cfg.
When mount options are being changed:
Display a hint to the user that changes will require
remounting the storage to take effect.
Reduce the usage of `var` keyword in CephFSEdit.js.
Signed-off-by: Elias Huhsovitz <e.huhsovitz@proxmox.com>
---
www/manager6/storage/CephFSEdit.js | 59 +++++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/www/manager6/storage/CephFSEdit.js b/www/manager6/storage/CephFSEdit.js
index db54df87..1c7a9bd9 100644
--- a/www/manager6/storage/CephFSEdit.js
+++ b/www/manager6/storage/CephFSEdit.js
@@ -8,17 +8,46 @@ Ext.define('PVE.storage.CephFSInputPanel', {
type: 'cephstorage',
},
+ onGetValues: function (values) {
+ const me = this;
+
+ if (values.options) {
+ values.options = values.options
+ .split(',')
+ .map((opt) => opt.trim())
+ .filter((opt, index, self) => opt !== '' && self.indexOf(opt) === index)
+ .join(',');
+ }
+
+ if (!values.options) {
+ delete values.options;
+ if (!me.isCreate) {
+ values.delete = values.delete ? `${values.delete},options` : 'options';
+ }
+ }
+
+ return me.callParent([values]);
+ },
+
setValues: function (values) {
if (values.monhost) {
this.viewModel.set('pveceph', false);
this.lookupReference('pvecephRef').setValue(false);
this.lookupReference('pvecephRef').resetOriginalValue();
}
+
+ this.originalOptions = values.options || '';
+
this.callParent([values]);
+
+ const hint = this.down('#mountOptionsHint');
+ if (hint) {
+ hint.setHidden(true);
+ }
},
initComponent: function () {
- var me = this;
+ const me = this;
if (!me.nodename) {
me.nodename = 'localhost';
@@ -132,6 +161,34 @@ Ext.define('PVE.storage.CephFSInputPanel', {
},
];
+ me.advancedColumnB = me.advancedColumnB || [];
+
+ me.advancedColumnB.push({
+ xtype: 'textfield',
+ name: 'options',
+ fieldLabel: gettext('Mount Options'),
+ emptyText: `${gettext('Example')}: ms_mode=secure, mount_timeout=60`,
+ allowBlank: true,
+ listeners: {
+ change: function (field, newValue) {
+ const hint = me.down('#mountOptionsHint');
+ if (hint) {
+ hint.setHidden(me.isCreate || (newValue || '') === me.originalOptions);
+ }
+ },
+ },
+ });
+
+ me.advancedColumnB.push({
+ xtype: 'displayfield',
+ itemId: 'mountOptionsHint',
+ userCls: 'pmx-hint',
+ hidden: true,
+ value: gettext(
+ 'Changes to mount options require remounting the storage to take effect.',
+ ),
+ });
+
me.callParent();
},
});
--
2.47.3
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-30 9:41 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-30 9:40 [PATCH manager v3 0/2] fix #6855: ui: storage: add mount options field for nfs, cifs, cephfs Elias Huhsovitz
2026-07-30 9:40 ` [PATCH manager v3 1/2] fix #6855: ui: storage: add mount options field for nfs amd smb/cifs Elias Huhsovitz
2026-07-30 9:40 ` [PATCH manager v3 2/2] ui: storage: add mount options field for cephfs Elias Huhsovitz
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.