From: Dietmar Maurer <dietmar@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [RFC pve-manager 19/27] ui: storage wizard: add iSCSI support
Date: Fri, 31 Jul 2026 12:21:48 +0200 [thread overview]
Message-ID: <20260731102156.3947857-20-dietmar@proxmox.com> (raw)
In-Reply-To: <20260731102156.3947857-1-dietmar@proxmox.com>
Using an iSCSI SAN properly usually means two storage entries: an
iSCSI storage for the connection and a shared LVM storage on top of a
LUN, with the backend creating the volume group automatically. The
existing dialogs leave this setup to the user; the wizard asks how the
SAN space should be used and defaults to the shared LVM setup, with
direct LUN usage as alternative.
For the LVM setup the submit chain first creates the iSCSI storage,
because LUNs can only be listed through an existing storage entry.
When the target provides more than one LUN, a small prompt asks which
one to use. If a later step fails, the wizard stays open and explains
what was already created; retrying skips the completed steps. Removing
the already created iSCSI storage again is intentionally not attempted.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
---
www/manager6/Makefile | 1 +
www/manager6/storage/wizard/IScsi.js | 351 +++++++++++++++++++++++++++
2 files changed, 352 insertions(+)
create mode 100644 www/manager6/storage/wizard/IScsi.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 3020ca05..e0a11e5e 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -381,6 +381,7 @@ JSSRC= \
storage/wizard/CommonSettings.js \
storage/wizard/NFS.js \
storage/wizard/CIFS.js \
+ storage/wizard/IScsi.js \
Workspace.js \
# end of JSSRC list
diff --git a/www/manager6/storage/wizard/IScsi.js b/www/manager6/storage/wizard/IScsi.js
new file mode 100644
index 00000000..9ffb5005
--- /dev/null
+++ b/www/manager6/storage/wizard/IScsi.js
@@ -0,0 +1,351 @@
+Ext.define('PVE.storage.wizard.IScsiTarget', {
+ extend: 'Proxmox.panel.InputPanel',
+ xtype: 'pveStorageWizardIScsiTarget',
+
+ onlineHelp: 'storage_open_iscsi',
+
+ initComponent: function () {
+ let me = this;
+
+ me.column1 = [
+ {
+ xtype: 'textfield',
+ name: 'portal',
+ fieldLabel: 'Portal',
+ allowBlank: false,
+ listeners: {
+ change: {
+ fn: function (f, value) {
+ let targetField = me.down('field[name=target]');
+ targetField.setDisabled(!value);
+ targetField.setPortal(value);
+ targetField.setValue('');
+ },
+ buffer: 500,
+ },
+ },
+ },
+ {
+ xtype: 'pveIScsiScan',
+ name: 'target',
+ fieldLabel: gettext('Target'),
+ disabled: true,
+ showNodeSelector: false,
+ },
+ ];
+
+ me.column2 = [];
+ if (!PVE.Utils.isStandaloneNode()) {
+ me.column2.push({
+ xtype: 'pveStorageScanNodeSelector',
+ listeners: {
+ change: function (f, value) {
+ me.down('field[name=target]').setNodeName(value);
+ let wizard = me.up('window');
+ wizard.scanNode = value;
+ wizard.down('field[name=nodes]').setValue(value);
+ },
+ },
+ });
+ }
+
+ me.callParent();
+ },
+});
+
+Ext.define('PVE.storage.wizard.IScsiUsage', {
+ extend: 'Proxmox.panel.InputPanel',
+ xtype: 'pveStorageWizardIScsiUsage',
+
+ onlineHelp: 'storage_open_iscsi',
+
+ items: [
+ {
+ xtype: 'displayfield',
+ value: gettext('How should the SAN space be used?'),
+ },
+ {
+ xtype: 'radiogroup',
+ columns: 1,
+ vertical: true,
+ items: [
+ {
+ boxLabel:
+ `<b>${gettext('Shared LVM on a LUN (recommended)')}</b> - ` +
+ gettext(
+ 'Create an LVM volume group on a LUN. Disk images are allocated as logical volumes, usable from all nodes.',
+ ),
+ name: 'usage',
+ inputValue: 'lvm',
+ checked: true,
+ margin: '0 0 10 0',
+ },
+ {
+ boxLabel:
+ `<b>${gettext('Use LUNs directly')}</b> - ` +
+ gettext(
+ 'Each existing LUN is used one-to-one as a disk image. LUN management stays on the SAN.',
+ ),
+ name: 'usage',
+ inputValue: 'direct',
+ },
+ ],
+ listeners: {
+ change: function (rg, value) {
+ rg.up('window').getViewModel().set('usage', value.usage);
+ },
+ },
+ },
+ {
+ xtype: 'displayfield',
+ userCls: 'pmx-hint',
+ value: gettext(
+ 'This creates two storage entries: an iSCSI storage for the SAN connection and an LVM storage on the selected LUN.',
+ ),
+ bind: {
+ hidden: '{!isLvmUsage}',
+ },
+ },
+ ],
+});
+
+Ext.define('PVE.storage.wizard.LunPrompt', {
+ extend: 'Ext.window.Window',
+
+ modal: true,
+ resizable: false,
+ width: 500,
+ layout: 'anchor',
+ bodyPadding: 10,
+
+ title: gettext('Select Base Volume (LUN)'),
+
+ // config: nodename, storage, callback(volid)
+
+ initComponent: function () {
+ let me = this;
+
+ me.items = [
+ {
+ xtype: 'displayfield',
+ userCls: 'pmx-hint',
+ anchor: '100%',
+ value: gettext(
+ 'The iSCSI target provides more than one LUN, select the one to use for the LVM volume group.',
+ ),
+ },
+ {
+ xtype: 'pveStorageLunSelector',
+ itemId: 'lunSelector',
+ fieldLabel: gettext('Base Volume'),
+ anchor: '100%',
+ nodename: me.nodename,
+ listeners: {
+ // setting the storage after render triggers the load,
+ // passing it as config would not
+ afterrender: function (f) {
+ f.setStorage(me.storage);
+ },
+ change: function (f, value) {
+ me.down('#okButton').setDisabled(!value);
+ },
+ },
+ },
+ ];
+
+ me.buttons = [
+ {
+ text: gettext('OK'),
+ itemId: 'okButton',
+ disabled: true,
+ handler: function () {
+ let volid = me.down('#lunSelector').getValue();
+ me.close();
+ me.callback(volid);
+ },
+ },
+ {
+ text: gettext('Cancel'),
+ handler: () => me.close(),
+ },
+ ];
+
+ me.callParent();
+ },
+});
+
+PVE.storage.wizard.types.iscsi = {
+ text: 'iSCSI',
+ description: gettext('Block storage (LUNs) on a SAN, accessed over the network.'),
+ group: 'san',
+ apiType: 'iscsi',
+ viewModel: {
+ data: {
+ usage: 'lvm',
+ },
+ formulas: {
+ isLvmUsage: (get) => get('usage') === 'lvm',
+ },
+ },
+ steps: () => [
+ {
+ xtype: 'pveStorageWizardIScsiTarget',
+ title: gettext('Target'),
+ },
+ {
+ xtype: 'pveStorageWizardIScsiUsage',
+ title: gettext('Usage'),
+ },
+ ],
+ settings: {
+ onlineHelp: 'storage_open_iscsi',
+ cts: ['images', 'rootdir'],
+ defaultContent: ['images', 'rootdir'],
+ contentFieldConfig: {
+ bind: {
+ disabled: '{!isLvmUsage}',
+ hidden: '{!isLvmUsage}',
+ },
+ },
+ advancedColumn1: [
+ {
+ xtype: 'textfield',
+ name: 'base-storage',
+ fieldLabel: gettext('iSCSI storage ID'),
+ emptyText: gettext('ID') + '-base',
+ vtype: 'StorageId',
+ allowBlank: true,
+ bind: {
+ disabled: '{!isLvmUsage}',
+ hidden: '{!isLvmUsage}',
+ },
+ },
+ ],
+ },
+ summaryNotes: function (values) {
+ if (values.usage !== 'lvm') {
+ return [];
+ }
+ let baseId = values['base-storage'] || `${values.storage}-base`;
+ return [
+ Ext.String.format(
+ gettext(
+ 'Two storage entries will be created: iSCSI storage "{0}" and LVM storage "{1}".',
+ ),
+ baseId,
+ values.storage,
+ ),
+ gettext(
+ 'The selected LUN is initialized as an LVM volume group. Existing data on the LUN will be destroyed!',
+ ),
+ gettext(
+ 'If the target provides more than one LUN, you will be asked to select one after the iSCSI storage was created.',
+ ),
+ ];
+ },
+ submit: function (wizard, values) {
+ let usage = values.usage;
+ delete values.usage;
+
+ if (usage === 'direct') {
+ delete values['base-storage'];
+ values.type = 'iscsi';
+ values.content = 'images';
+ wizard.runSubmitChain([(next) => wizard.createStorage(values, next)]);
+ return;
+ }
+
+ let baseId = values['base-storage'] || `${values.storage}-base`;
+ delete values['base-storage'];
+
+ let nodesList = values.nodes ? [].concat(values.nodes) : [];
+ let contentNode = wizard.scanNode || nodesList[0] || Proxmox.NodeName;
+
+ let baseCreatedHint = Ext.String.format(
+ gettext(
+ 'The iSCSI storage "{0}" was already created. You can complete the setup later by adding an LVM storage with a base volume via Add -> LVM.',
+ ),
+ Ext.htmlEncode(baseId),
+ );
+
+ wizard.runSubmitChain([
+ (next) => {
+ if (wizard.createdBaseStorage) {
+ next();
+ return;
+ }
+ wizard.createStorage(
+ {
+ storage: baseId,
+ type: 'iscsi',
+ portal: values.portal,
+ target: values.target,
+ content: 'none',
+ nodes: values.nodes,
+ },
+ () => {
+ wizard.createdBaseStorage = true;
+ next();
+ },
+ );
+ },
+ (next) => {
+ if (wizard.baseVolume) {
+ next();
+ return;
+ }
+ Proxmox.Utils.API2Request({
+ url: `/nodes/${contentNode}/storage/${baseId}/content`,
+ method: 'GET',
+ params: { content: 'images' },
+ waitMsgTarget: wizard,
+ failure: (response) =>
+ Ext.Msg.alert(
+ gettext('Error'),
+ `${baseCreatedHint}<br><br>${response.htmlStatus}`,
+ ),
+ success: function (response) {
+ let luns = response.result.data || [];
+ if (luns.length === 0) {
+ Ext.Msg.alert(
+ gettext('Error'),
+ gettext('No LUN found on the iSCSI target.') +
+ `<br><br>${baseCreatedHint}`,
+ );
+ return;
+ }
+ if (luns.length === 1) {
+ wizard.baseVolume = luns[0].volid;
+ next();
+ return;
+ }
+ Ext.create('PVE.storage.wizard.LunPrompt', {
+ autoShow: true,
+ nodename: contentNode,
+ storage: baseId,
+ callback: function (volid) {
+ wizard.baseVolume = volid;
+ next();
+ },
+ });
+ },
+ });
+ },
+ (next) =>
+ wizard.createStorage(
+ {
+ storage: values.storage,
+ type: 'lvm',
+ vgname: values.storage,
+ base: wizard.baseVolume,
+ shared: 1,
+ content: values.content,
+ nodes: values.nodes,
+ disable: values.disable,
+ },
+ next,
+ baseCreatedHint,
+ ),
+ ]);
+ },
+};
--
2.47.3
next prev parent reply other threads:[~2026-07-31 10:24 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-31 10:21 [RFC pve-storage/proxmox-widget-toolkit/pve-manager 00/27] add guided remote storage setup and SAN visibility Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 01/27] diskmanage: collect disk transport type from lsblk Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 02/27] diskmanage: add helper to list multipath devices Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 03/27] diskmanage: qualify NVMe over fabrics transport Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 04/27] disks: list: add include-remote parameter Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 05/27] diskmanage: include iSCSI session devices in disk enumeration Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 06/27] diskmanage: link multipath member disks to their map device Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 07/27] iscsi: factor out session device map from device list Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 08/27] api: scan: add san-luns method listing SAN LUN candidates Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 09/27] disks: lvm: allow creating volume groups on multipath devices Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 10/27] diskmanage: add helper querying multipath path state Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 11/27] diskmanage: add helper querying NVMe native " Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 12/27] api: scan: san-luns: report " Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 13/27] iscsi plugin: list sessions of all transports and capture transport Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-storage 14/27] api: add node-level iSCSI initiator target and session API Dietmar Maurer
2026-07-31 10:21 ` [RFC proxmox-widget-toolkit 15/27] disk selectors: allow opting into remote devices Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 16/27] ui: storage: allow switching the scan node of the NFS/CIFS scan combos Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 17/27] ui: storage: add guided remote storage wizard with NFS support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 18/27] ui: storage wizard: add SMB/CIFS support Dietmar Maurer
2026-07-31 10:21 ` Dietmar Maurer [this message]
2026-07-31 10:21 ` [RFC pve-manager 20/27] ui: storage wizard: add FC-attached SAN (shared LVM) support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 21/27] ui: storage wizard: add ZFS over iSCSI support Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 22/27] ui: dc: storage: add remote storage wizard entry to the add menu Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 23/27] ui: node: add SAN LUNs panel Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 24/27] ui: san luns: show multipath path state Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 25/27] api: nodes: add iSCSI initiator API endpoint Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 26/27] pvenode: add iscsi commands Dietmar Maurer
2026-07-31 10:21 ` [RFC pve-manager 27/27] ui: san luns: show iSCSI targets and sessions Dietmar Maurer
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=20260731102156.3947857-20-dietmar@proxmox.com \
--to=dietmar@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