* [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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox