From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 741951FF0AF for ; Fri, 06 Nov 2026 01:52:33 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id E11B52188E; Fri, 06 Nov 2026 01:51:00 +0100 (CET) From: Thomas Lamprecht To: pve-devel@lists.proxmox.com Subject: [PATCH v3 storage 32/76] iscsi: share the initiator identity reader Date: Fri, 6 Nov 2026 00:31:07 +0100 Message-ID: <20261105233336.2178064-33-t.lamprecht@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261105233336.2178064-1-t.lamprecht@proxmox.com> References: <20261105233336.2178064-1-t.lamprecht@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1793926179489 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.328 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: 7GUSK57IAMRWKTORNKJ7O5FGN7VGRZA6 X-Message-ID-Hash: 7GUSK57IAMRWKTORNKJ7O5FGN7VGRZA6 X-MailFrom: t.lamprecht@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: The node API and multipath status publication need the same initiator identity. Share the open-iscsi configuration reader through the plugin so both consumers interpret its name and alias consistently. Bound the read to 64 KiB: a valid identity file is small, and an oversized or unreadable file must not stall reporting or produce a truncated identity. Missing names remain unavailable rather than being invented. Signed-off-by: Thomas Lamprecht --- src/PVE/API2/ISCSI.pm | 22 ++--------------- src/PVE/Storage/ISCSIPlugin.pm | 16 +++++++++++++ src/test/run_multipath_api_tests.pl | 37 +++++++++++++++++++++++++++++ src/test/run_san_api_tests.pl | 21 ++++++++++++++++ 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/PVE/API2/ISCSI.pm b/src/PVE/API2/ISCSI.pm index a3d55845..0f7bde83 100644 --- a/src/PVE/API2/ISCSI.pm +++ b/src/PVE/API2/ISCSI.pm @@ -7,13 +7,10 @@ use PVE::JSONSchema qw(get_standard_option); use PVE::RESTHandler; use PVE::Storage; use PVE::Storage::ISCSIPlugin; -use PVE::Tools qw(run_command file_get_contents file_read_firstline); +use PVE::Tools qw(file_read_firstline); use base qw(PVE::RESTHandler); -my $ISCSIADM = '/usr/bin/iscsiadm'; -my $INITIATOR_NAME_FILE = '/etc/iscsi/initiatorname.iscsi'; - my $session_id_schema = { description => "Kernel iSCSI session id.", type => 'integer', @@ -166,21 +163,6 @@ my $session_params_return = { }, }; -my sub read_initiator_info { - my $content = eval { file_get_contents($INITIATOR_NAME_FILE) }; - return undef if !defined($content); - - my $res = {}; - for my $line (split(/\n/, $content)) { - if ($line =~ m/^\s*InitiatorName\s*=\s*(\S+)\s*$/) { - $res->{name} = $1; - } elsif ($line =~ m/^\s*InitiatorAlias\s*=\s*(.+?)\s*$/) { - $res->{alias} = $1; - } - } - return defined($res->{name}) ? $res : undef; -} - my sub session_health { my ($kernel_state) = @_; @@ -299,7 +281,7 @@ __PACKAGE__->register_method({ code => sub { my $res = { supported => PVE::Storage::ISCSIPlugin::iscsi_supported() }; if ($res->{supported}) { - my $initiator = read_initiator_info(); + my $initiator = PVE::Storage::ISCSIPlugin::read_initiator_info(); if ($initiator) { $res->{'initiator-name'} = $initiator->{name} if defined($initiator->{name}); $res->{'initiator-alias'} = $initiator->{alias} if defined($initiator->{alias}); diff --git a/src/PVE/Storage/ISCSIPlugin.pm b/src/PVE/Storage/ISCSIPlugin.pm index 0e3b6df3..4831d2d4 100644 --- a/src/PVE/Storage/ISCSIPlugin.pm +++ b/src/PVE/Storage/ISCSIPlugin.pm @@ -24,9 +24,25 @@ use base qw(PVE::Storage::Plugin); # iscsi helper function my $ISCSIADM = '/usr/bin/iscsiadm'; +our $INITIATOR_NAME_FILE = '/etc/iscsi/initiatorname.iscsi'; our $SYSFS = '/sys'; my $MULTIPATHD_EXECUTABLES = { '/usr/sbin/multipathd' => 1, '/sbin/multipathd' => 1 }; +sub read_initiator_info { + my $content = eval { PVE::Tools::file_get_contents($INITIATOR_NAME_FILE, 64 * 1024) }; + return undef if !defined($content); + + my $res = {}; + for my $line (split(/\n/, $content)) { + if ($line =~ m/^\s*InitiatorName\s*=\s*(\S+)\s*$/) { + $res->{name} = $1; + } elsif ($line =~ m/^\s*InitiatorAlias\s*=\s*(.+?)\s*$/) { + $res->{alias} = $1; + } + } + return defined($res->{name}) ? $res : undef; +} + my $found_iscsi_adm_exe; my sub assert_iscsi_support { my ($noerr) = @_; diff --git a/src/test/run_multipath_api_tests.pl b/src/test/run_multipath_api_tests.pl index b013ba38..fdd9ec39 100755 --- a/src/test/run_multipath_api_tests.pl +++ b/src/test/run_multipath_api_tests.pl @@ -99,6 +99,43 @@ subtest 'enabling the multipath option needs every node to know it' => sub { ); }; +subtest 'the iSCSI initiator identity reader' => sub { + # the initiator identity reader with open-iscsi configuration files + require File::Temp; + my $file = File::Temp->new(); + local $PVE::Storage::ISCSIPlugin::INITIATOR_NAME_FILE = $file->filename; + my $read = sub { + my ($text) = @_; + open(my $fh, '>', $file->filename) or die "open failed - $!\n"; + print $fh $text; + close($fh); + return PVE::Storage::ISCSIPlugin::read_initiator_info(); + }; + is_deeply( + $read->( + "## a comment\nInitiatorName=iqn.2026-10.test.lab:n1\nInitiatorAlias= node 1 \n"), + { name => 'iqn.2026-10.test.lab:n1', alias => 'node 1' }, + 'the reader takes the name and the alias', + ); + is( + $read->("InitiatorName=iqn.2026-10.test.lab:n2\n")->{name}, + 'iqn.2026-10.test.lab:n2', + 'and reads the file again', + ); + ok(!defined($read->("## only a comment\n")), 'a file without a name gives none'); + ok( + !defined($read->('InitiatorName=iqn.2026-10.test.lab:' . ('x' x 70000))), + 'an oversized file gives none rather than a cut name', + ); + { + local $PVE::Storage::ISCSIPlugin::INITIATOR_NAME_FILE = $file->filename . '.missing'; + ok( + !defined(PVE::Storage::ISCSIPlugin::read_initiator_info()), + 'a missing file gives none', + ); + } +}; + subtest 'an entry stays allow-listed while its map is in use' => sub { my $mp = Test::MockModule->new('PVE::Multipath'); my $map = { wwid => 'w1', sysfs => 'dm-999999', 'paths-active' => 2 }; diff --git a/src/test/run_san_api_tests.pl b/src/test/run_san_api_tests.pl index cf382a11..94ee74f0 100755 --- a/src/test/run_san_api_tests.pl +++ b/src/test/run_san_api_tests.pl @@ -11,9 +11,30 @@ use PVE::API2::ISCSI; use PVE::API2::Storage::Scan; subtest 'iSCSI status fields' => sub { + my $plugin = Test::MockModule->new('PVE::Storage::ISCSIPlugin'); + my $identity = { name => 'iqn.test', alias => 'test' }; + $plugin->mock(read_initiator_info => sub { $identity }); my $method = PVE::API2::ISCSI->map_method_by_name('status'); ok($method->{returns}->{properties}->{'initiator-name'}, 'name schema'); ok($method->{returns}->{properties}->{'initiator-alias'}, 'alias schema'); + SKIP: { + skip 'iscsiadm not installed', 2 if !-x '/usr/bin/iscsiadm'; + is_deeply( + $method->{code}->({ node => 'test' }), + { + supported => 1, + 'initiator-name' => 'iqn.test', + 'initiator-alias' => 'test', + }, + 'flat identity', + ); + $identity = undef; + is_deeply( + $method->{code}->({ node => 'test' }), + { supported => 1 }, + 'missing identity omitted', + ); + } my $enum = PVE::API2::ISCSI->map_method_by_name('targets')->{returns}->{items}->{properties}->{health} ->{enum}; -- 2.47.3