From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 013CA1FF2C8 for ; Wed, 17 Jul 2024 11:43:14 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 0372E38A45; Wed, 17 Jul 2024 11:43:04 +0200 (CEST) From: Max Carrara To: pve-devel@lists.proxmox.com Date: Wed, 17 Jul 2024 11:40:26 +0200 Message-Id: <20240717094034.124857-29-m.carrara@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240717094034.124857-1-m.carrara@proxmox.com> References: <20240717094034.124857-1-m.carrara@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.030 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pve-devel] [RFC pve-storage 28/36] plugin: iscsi: factor helper functions into common module X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" Because the `iscsi_discovery` subroutine is used by `PVE::Storage` directly, move it and all other helpers independent from the plugin into the new `PVE::Storage::Common::ISCSI` module. As the name suggests, this new module collects ISCSI-related functionalities and utils. Due to the `iscsi_discovery` sub being the only subroutine that was actually used publicly, keep its original definition and let it wrap its "new" version from the common module. Also add a deprecation warning for any potential users. The code of all moved subroutines stays the same, though the subs themselves are now also documented and use prototypes. Signed-off-by: Max Carrara --- src/PVE/Storage.pm | 4 +- src/PVE/Storage/Common.pm | 4 + src/PVE/Storage/Common/ISCSI.pm | 475 ++++++++++++++++++++++++++++++++ src/PVE/Storage/Common/Makefile | 1 + src/PVE/Storage/ISCSIPlugin.pm | 255 +---------------- 5 files changed, 498 insertions(+), 241 deletions(-) create mode 100644 src/PVE/Storage/Common/ISCSI.pm diff --git a/src/PVE/Storage.pm b/src/PVE/Storage.pm index 57b2038..3865b44 100755 --- a/src/PVE/Storage.pm +++ b/src/PVE/Storage.pm @@ -24,6 +24,8 @@ use PVE::RPCEnvironment; use PVE::SSHInfo; use PVE::RESTEnvironment qw(log_warn); +use PVE::Storage::Common::ISCSI; + use PVE::Storage::Plugin; use PVE::Storage::DirPlugin; use PVE::Storage::LVMPlugin; @@ -1457,7 +1459,7 @@ sub scan_iscsi { die "unable to parse/resolve portal address '${portal_in}'\n"; } - return PVE::Storage::ISCSIPlugin::iscsi_discovery([ $portal ]); + return PVE::Storage::Common::ISCSI::iscsi_discovery([ $portal ]); } sub storage_default_format { diff --git a/src/PVE/Storage/Common.pm b/src/PVE/Storage/Common.pm index 3cc3c37..8a52498 100644 --- a/src/PVE/Storage/Common.pm +++ b/src/PVE/Storage/Common.pm @@ -40,6 +40,10 @@ be grouped in a submodule can also be found here. =over +=item C + +Subroutines that provide ISCSI-related functionalities. + =item C Utilities concerned with LVM, such as manipulating logical volumes. diff --git a/src/PVE/Storage/Common/ISCSI.pm b/src/PVE/Storage/Common/ISCSI.pm new file mode 100644 index 0000000..2eb7f65 --- /dev/null +++ b/src/PVE/Storage/Common/ISCSI.pm @@ -0,0 +1,475 @@ +package PVE::Storage::Common::ISCSI; + +use strict; +use warnings; + +use File::stat; +use IO::Dir; +use IO::File; + +use PVE::Network; +use PVE::Tools qw( + $IPV4RE + $IPV6RE + dir_glob_foreach + dir_glob_regex + file_read_firstline + run_command +); + +use parent qw(Exporter); + +our @EXPORT_OK = qw( + assert_iscsi_support + iscsi_device_list + iscsi_discovery + iscsi_login + iscsi_logout + iscsi_portals + iscsi_session_list + iscsi_session_rescan + iscsi_test_portal +); + +=pod + +=head1 NAME + +PVE::Storage::Common::ISCSI - Provides helper subroutines that wrap commonly used ISCSI commands + +=head1 FUNCTIONS + +=cut + +my $ISCSIADM = '/usr/bin/iscsiadm'; +my $found_iscsi_adm_exe; + +=pod + +=head3 assert_iscsi_support + + $is_supported = assert_iscsi_support($noerr) + +Asserts whether ISCSI operations are supported by the host, raising an exception +if they are not. + +Optionally, C<$noerr> may be set to C<1> in order to return C instead +of raising an exception. + +ISCSI operations are considered to be supported if C exists +and is executable by the current user. + +=cut + +sub assert_iscsi_support : prototype(;$) { + my ($noerr) = @_; + return $found_iscsi_adm_exe if $found_iscsi_adm_exe; # assume it won't be removed if ever found + + $found_iscsi_adm_exe = -x $ISCSIADM; + + if (!$found_iscsi_adm_exe) { + die "error: no iscsi support - please install open-iscsi\n" if !$noerr; + warn "warning: no iscsi support - please install open-iscsi\n"; + } + return $found_iscsi_adm_exe; +} + +# Example: 192.168.122.252:3260,1 iqn.2003-01.org.linux-iscsi.proxmox-nfs.x8664:sn.00567885ba8f +my $ISCSI_TARGET_RE = qr/^((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s*$/; + +=pod + +=head3 iscsi_session_list + + $active_sessions = iscsi_session_list() + +Scans for active ISCSI sessions and returns a hash that maps each ISCSI target +to a list of hashes describing the session. + +Asserts whether ISCSI is supported on the host beforehand. + +The returned hash has the following structure: + + { + 'iqn.1998-01.example.hostname.iscsi:name1' => [ + { + session_id => ..., + portal => ..., + }, + ... + ], + 'iqn.1998-01.example.hostname.iscsi:name1001' => [ + { + session_id => ..., + portal => ..., + }, + { + session_id => ..., + portal => ..., + }, + ... + ], + ... + } + +=cut + +sub iscsi_session_list : prototype() { + assert_iscsi_support(); + + my $cmd = [$ISCSIADM, '--mode', 'session']; + + my $res = {}; + eval { + run_command($cmd, errmsg => 'iscsi session scan failed', outfunc => sub { + my $line = shift; + # example: tcp: [1] 192.168.122.252:3260,1 iqn.2003-01.org.linux-iscsi.proxmox-nfs.x8664:sn.00567885ba8f (non-flash) + if ($line =~ m/^tcp:\s+\[(\S+)\]\s+((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s+\S+?\s*$/) { + my ($session_id, $portal, $target) = ($1, $2, $3); + # there can be several sessions per target (multipath) + push @{$res->{$target}}, { session_id => $session_id, portal => $portal }; + } + }); + }; + if (my $err = $@) { + die $err if $err !~ m/: No active sessions.$/i; + } + + return $res; +} + +=pod + +=head3 iscsi_test_portal + + $is_available = iscsi_test_portal($portal) + +Tests whether the given ISCSI C<$portal> can be reached by pinging it. + +=cut + +sub iscsi_test_portal : prototype($) { + my ($portal) = @_; + + my ($server, $port) = PVE::Tools::parse_host_and_port($portal); + return 0 if !$server; + return PVE::Network::tcp_ping($server, $port || 3260, 2); +} + +=pod + +=head3 iscsi_portals + + $portals = iscsi_portals($target, $portal_in) + +Lists all available ISCSI portals whose target equals C<$target>. + +Should the lookup fail, a warning with the error message is emitted and a list +only containing C<$portal_in> is returned as a fallback. + +=cut + +sub iscsi_portals : prototype($$) { + my ($target, $portal_in) = @_; + + assert_iscsi_support(); + + my $res = []; + my $cmd = [$ISCSIADM, '--mode', 'node']; + eval { + run_command($cmd, outfunc => sub { + my $line = shift; + + if ($line =~ $ISCSI_TARGET_RE) { + my ($portal, $portal_target) = ($1, $2); + if ($portal_target eq $target) { + push @{$res}, $portal; + } + } + }); + }; + + if ($@) { + warn $@; + return [ $portal_in ]; + } + + return $res; +} + +=pod + +=head3 iscsi_discovery + + $discovered_targets = iscsi_discovery($portals) + +Scans each portal in C<$portals> for available ISCSI targets and returns a hash +that maps each target to a list of portals. + +Asserts whether ISCSI is supported on the host beforehand. + +The returned hash has the following structure: + + { + 'iqn.1998-01.example.hostname.iscsi:name1' => [ + '172.16.64.177:3260', + ... + ], + 'iqn.1998-01.example.hostname.iscsi:name1001' => [ + '172.16.64.178:3260', + '172.16.64.179:3260', + ... + ], + ... + } + +=cut + +sub iscsi_discovery : prototype($) { + my ($portals) = @_; + + assert_iscsi_support(); + + my $res = {}; + for my $portal ($portals->@*) { + next if !iscsi_test_portal($portal); # fixme: raise exception here? + + my $cmd = [$ISCSIADM, '--mode', 'discovery', '--type', 'sendtargets', '--portal', $portal]; + eval { + run_command($cmd, outfunc => sub { + my $line = shift; + + if ($line =~ $ISCSI_TARGET_RE) { + my ($portal, $target) = ($1, $2); + # one target can have more than one portal (multipath) + # and sendtargets should return all of them in single call + push @{$res->{$target}}, $portal; + } + }); + }; + + # In case of multipath we can stop after receiving targets from any available portal + last if scalar(keys %$res) > 0; + } + + return $res; +} + +=pod + +=head3 iscsi_login + + iscsi_login($target, $portals) + +Logs in to the given ISCSI C<$target>. Will try to L +any targets for all C<$portals> beforehand. + +Asserts whether ISCSI is supported on the host beforehand. + +=cut + +sub iscsi_login : prototype($$) { + my ($target, $portals) = @_; + + assert_iscsi_support(); + + eval { iscsi_discovery($portals); }; + warn $@ if $@; + + run_command([$ISCSIADM, '--mode', 'node', '--targetname', $target, '--login']); +} + +=pod + +=head3 iscsi_logout + + iscsi_logout($target) + +Logs out of the given ISCSI C<$target>. + +Asserts whether ISCSI is supported on the host beforehand. + +=cut + +sub iscsi_logout : prototype($) { + my ($target) = @_; + + assert_iscsi_support(); + + run_command([$ISCSIADM, '--mode', 'node', '--targetname', $target, '--logout']); +} + +=pod + +=head3 iscsi_session_rescan + + iscsi_session_rescan($session_list) + +Rescans each ISCSI session in C<$session_list>. + +Asserts whether ISCSI is supported on the host beforehand. + +Frequent rescans are prevented by using a lock file located at +C. The access time of the file is used to +determine when the last rescan was performed. + +=cut + +my $rescan_filename = "/var/run/pve-iscsi-rescan.lock"; + +sub iscsi_session_rescan : prototype($) { + my $session_list = shift; + + assert_iscsi_support(); + + my $rstat = stat($rescan_filename); + + if (!$rstat) { + if (my $fh = IO::File->new($rescan_filename, "a")) { + utime undef, undef, $fh; + close($fh); + } + } else { + my $atime = $rstat->atime; + my $tdiff = time() - $atime; + # avoid frequent rescans + return if !($tdiff < 0 || $tdiff > 10); + utime undef, undef, $rescan_filename; + } + + foreach my $session (@$session_list) { + my $cmd = [$ISCSIADM, '--mode', 'session', '--sid', $session->{session_id}, '--rescan']; + eval { run_command($cmd, outfunc => sub {}); }; + warn $@ if $@; + } +} + +my sub load_stable_scsi_paths { + + my $stable_paths = {}; + + my $stabledir = "/dev/disk/by-id"; + + if (my $dh = IO::Dir->new($stabledir)) { + foreach my $tmp (sort $dh->read) { + # exclude filenames with part in name (same disk but partitions) + # use only filenames with scsi(with multipath i have the same device + # with dm-uuid-mpath , dm-name and scsi in name) + if($tmp !~ m/-part\d+$/ && ($tmp =~ m/^scsi-/ || $tmp =~ m/^dm-uuid-mpath-/)) { + my $path = "$stabledir/$tmp"; + my $bdevdest = readlink($path); + if ($bdevdest && $bdevdest =~ m|^../../([^/]+)|) { + $stable_paths->{$1}=$tmp; + } + } + } + $dh->close; + } + return $stable_paths; +} + +=pod + +=head3 iscsi_device_list + + $device_list = iscsi_device_list() + +Reads and parses the metadata of all ISCSI devices connected to the host, +returning a nested hash that maps ISCSI targets to the parsed device data. + +Devices that cannot be parsed are silently ignored. + +The returned hash has the following structure: + + { + 'iqn.2024-07.tld.example.hostname:lun01' => { + '0.0.1.scsi-360000000000000000e00000000010001' => { + 'channel' => 0, + 'format' => 'raw', + 'id' => 0, + 'lun' => 1, + 'size' => '17179869184', + 'vmid' => 0 + }, + ... + }, + 'iqn.2024-07.tld.example.hostname.lun02' => { + '0.0.1.scsi-360000000000000000e00000000020001' => { + 'channel' => 0, + 'format' => 'raw', + 'id' => 0, + 'lun' => 1, + 'size' => '17179869184', + 'vmid' => 0 + }, + ... + }, + ... + } + +=cut + +sub iscsi_device_list : prototype() { + + my $res = {}; + + my $dirname = '/sys/class/iscsi_session'; + + my $stable_paths = load_stable_scsi_paths(); + + dir_glob_foreach($dirname, 'session(\d+)', sub { + my ($ent, $session) = @_; + + my $target = file_read_firstline("$dirname/$ent/targetname"); + return if !$target; + + my (undef, $host) = dir_glob_regex("$dirname/$ent/device", 'target(\d+):.*'); + return if !defined($host); + + dir_glob_foreach("/sys/bus/scsi/devices", "$host:" . '(\d+):(\d+):(\d+)', sub { + my ($tmp, $channel, $id, $lun) = @_; + + my $type = file_read_firstline("/sys/bus/scsi/devices/$tmp/type"); + return if !defined($type) || $type ne '0'; # list disks only + + my $bdev; + if (-d "/sys/bus/scsi/devices/$tmp/block") { # newer kernels + (undef, $bdev) = dir_glob_regex("/sys/bus/scsi/devices/$tmp/block/", '([A-Za-z]\S*)'); + } else { + (undef, $bdev) = dir_glob_regex("/sys/bus/scsi/devices/$tmp", 'block:(\S+)'); + } + return if !$bdev; + + #check multipath + if (-d "/sys/block/$bdev/holders") { + my $multipathdev = dir_glob_regex("/sys/block/$bdev/holders", '[A-Za-z]\S*'); + $bdev = $multipathdev if $multipathdev; + } + + my $blockdev = $stable_paths->{$bdev}; + return if !$blockdev; + + my $size = file_read_firstline("/sys/block/$bdev/size"); + return if !$size; + + my $volid = "$channel.$id.$lun.$blockdev"; + + $res->{$target}->{$volid} = { + 'format' => 'raw', + 'size' => int($size * 512), + 'vmid' => 0, # not assigned to any vm + 'channel' => int($channel), + 'id' => int($id), + 'lun' => int($lun), + }; + + #print "TEST: $target $session $host,$bus,$tg,$lun $blockdev\n"; + }); + + }); + + return $res; +} + + +1; diff --git a/src/PVE/Storage/Common/Makefile b/src/PVE/Storage/Common/Makefile index 863f7c7..e15a47c 100644 --- a/src/PVE/Storage/Common/Makefile +++ b/src/PVE/Storage/Common/Makefile @@ -1,4 +1,5 @@ SOURCES = \ + ISCSI.pm \ LVM.pm \ Path.pm \ diff --git a/src/PVE/Storage/ISCSIPlugin.pm b/src/PVE/Storage/ISCSIPlugin.pm index 2bdd9a2..8a56cfe 100644 --- a/src/PVE/Storage/ISCSIPlugin.pm +++ b/src/PVE/Storage/ISCSIPlugin.pm @@ -8,254 +8,29 @@ use IO::Dir; use IO::File; use PVE::JSONSchema qw(get_standard_option); +use PVE::Storage::Common qw(get_deprecation_warning); +use PVE::Storage::Common::ISCSI qw( + assert_iscsi_support + iscsi_device_list + iscsi_login + iscsi_logout + iscsi_portals + iscsi_session_list + iscsi_session_rescan + iscsi_test_portal +); use PVE::Storage::Plugin; -use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach $IPV4RE $IPV6RE); use base qw(PVE::Storage::Plugin); -# iscsi helper function - -my $ISCSIADM = '/usr/bin/iscsiadm'; - -my $found_iscsi_adm_exe; -my sub assert_iscsi_support { - my ($noerr) = @_; - return $found_iscsi_adm_exe if $found_iscsi_adm_exe; # assume it won't be removed if ever found - - $found_iscsi_adm_exe = -x $ISCSIADM; - - if (!$found_iscsi_adm_exe) { - die "error: no iscsi support - please install open-iscsi\n" if !$noerr; - warn "warning: no iscsi support - please install open-iscsi\n"; - } - return $found_iscsi_adm_exe; -} - -# Example: 192.168.122.252:3260,1 iqn.2003-01.org.linux-iscsi.proxmox-nfs.x8664:sn.00567885ba8f -my $ISCSI_TARGET_RE = qr/^((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s*$/; - -sub iscsi_session_list { - assert_iscsi_support(); - - my $cmd = [$ISCSIADM, '--mode', 'session']; - - my $res = {}; - eval { - run_command($cmd, errmsg => 'iscsi session scan failed', outfunc => sub { - my $line = shift; - # example: tcp: [1] 192.168.122.252:3260,1 iqn.2003-01.org.linux-iscsi.proxmox-nfs.x8664:sn.00567885ba8f (non-flash) - if ($line =~ m/^tcp:\s+\[(\S+)\]\s+((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s+\S+?\s*$/) { - my ($session_id, $portal, $target) = ($1, $2, $3); - # there can be several sessions per target (multipath) - push @{$res->{$target}}, { session_id => $session_id, portal => $portal }; - } - }); - }; - if (my $err = $@) { - die $err if $err !~ m/: No active sessions.$/i; - } - - return $res; -} - -sub iscsi_test_portal { - my ($portal) = @_; - - my ($server, $port) = PVE::Tools::parse_host_and_port($portal); - return 0 if !$server; - return PVE::Network::tcp_ping($server, $port || 3260, 2); -} - -sub iscsi_portals { - my ($target, $portal_in) = @_; - - assert_iscsi_support(); - - my $res = []; - my $cmd = [$ISCSIADM, '--mode', 'node']; - eval { - run_command($cmd, outfunc => sub { - my $line = shift; - - if ($line =~ $ISCSI_TARGET_RE) { - my ($portal, $portal_target) = ($1, $2); - if ($portal_target eq $target) { - push @{$res}, $portal; - } - } - }); - }; - - if ($@) { - warn $@; - return [ $portal_in ]; - } - - return $res; -} - sub iscsi_discovery { my ($portals) = @_; - assert_iscsi_support(); - - my $res = {}; - for my $portal ($portals->@*) { - next if !iscsi_test_portal($portal); # fixme: raise exception here? - - my $cmd = [$ISCSIADM, '--mode', 'discovery', '--type', 'sendtargets', '--portal', $portal]; - eval { - run_command($cmd, outfunc => sub { - my $line = shift; - - if ($line =~ $ISCSI_TARGET_RE) { - my ($portal, $target) = ($1, $2); - # one target can have more than one portal (multipath) - # and sendtargets should return all of them in single call - push @{$res->{$target}}, $portal; - } - }); - }; - - # In case of multipath we can stop after receiving targets from any available portal - last if scalar(keys %$res) > 0; - } - - return $res; -} - -sub iscsi_login { - my ($target, $portals) = @_; - - assert_iscsi_support(); - - eval { iscsi_discovery($portals); }; - warn $@ if $@; + warn get_deprecation_warning( + "PVE::Storage::Common::ISCSI::iscsi_discovery" + ); - run_command([$ISCSIADM, '--mode', 'node', '--targetname', $target, '--login']); -} - -sub iscsi_logout { - my ($target) = @_; - - assert_iscsi_support(); - - run_command([$ISCSIADM, '--mode', 'node', '--targetname', $target, '--logout']); -} - -my $rescan_filename = "/var/run/pve-iscsi-rescan.lock"; - -sub iscsi_session_rescan { - my $session_list = shift; - - assert_iscsi_support(); - - my $rstat = stat($rescan_filename); - - if (!$rstat) { - if (my $fh = IO::File->new($rescan_filename, "a")) { - utime undef, undef, $fh; - close($fh); - } - } else { - my $atime = $rstat->atime; - my $tdiff = time() - $atime; - # avoid frequent rescans - return if !($tdiff < 0 || $tdiff > 10); - utime undef, undef, $rescan_filename; - } - - foreach my $session (@$session_list) { - my $cmd = [$ISCSIADM, '--mode', 'session', '--sid', $session->{session_id}, '--rescan']; - eval { run_command($cmd, outfunc => sub {}); }; - warn $@ if $@; - } -} - -sub load_stable_scsi_paths { - - my $stable_paths = {}; - - my $stabledir = "/dev/disk/by-id"; - - if (my $dh = IO::Dir->new($stabledir)) { - foreach my $tmp (sort $dh->read) { - # exclude filenames with part in name (same disk but partitions) - # use only filenames with scsi(with multipath i have the same device - # with dm-uuid-mpath , dm-name and scsi in name) - if($tmp !~ m/-part\d+$/ && ($tmp =~ m/^scsi-/ || $tmp =~ m/^dm-uuid-mpath-/)) { - my $path = "$stabledir/$tmp"; - my $bdevdest = readlink($path); - if ($bdevdest && $bdevdest =~ m|^../../([^/]+)|) { - $stable_paths->{$1}=$tmp; - } - } - } - $dh->close; - } - return $stable_paths; -} - -sub iscsi_device_list { - - my $res = {}; - - my $dirname = '/sys/class/iscsi_session'; - - my $stable_paths = load_stable_scsi_paths(); - - dir_glob_foreach($dirname, 'session(\d+)', sub { - my ($ent, $session) = @_; - - my $target = file_read_firstline("$dirname/$ent/targetname"); - return if !$target; - - my (undef, $host) = dir_glob_regex("$dirname/$ent/device", 'target(\d+):.*'); - return if !defined($host); - - dir_glob_foreach("/sys/bus/scsi/devices", "$host:" . '(\d+):(\d+):(\d+)', sub { - my ($tmp, $channel, $id, $lun) = @_; - - my $type = file_read_firstline("/sys/bus/scsi/devices/$tmp/type"); - return if !defined($type) || $type ne '0'; # list disks only - - my $bdev; - if (-d "/sys/bus/scsi/devices/$tmp/block") { # newer kernels - (undef, $bdev) = dir_glob_regex("/sys/bus/scsi/devices/$tmp/block/", '([A-Za-z]\S*)'); - } else { - (undef, $bdev) = dir_glob_regex("/sys/bus/scsi/devices/$tmp", 'block:(\S+)'); - } - return if !$bdev; - - #check multipath - if (-d "/sys/block/$bdev/holders") { - my $multipathdev = dir_glob_regex("/sys/block/$bdev/holders", '[A-Za-z]\S*'); - $bdev = $multipathdev if $multipathdev; - } - - my $blockdev = $stable_paths->{$bdev}; - return if !$blockdev; - - my $size = file_read_firstline("/sys/block/$bdev/size"); - return if !$size; - - my $volid = "$channel.$id.$lun.$blockdev"; - - $res->{$target}->{$volid} = { - 'format' => 'raw', - 'size' => int($size * 512), - 'vmid' => 0, # not assigned to any vm - 'channel' => int($channel), - 'id' => int($id), - 'lun' => int($lun), - }; - - #print "TEST: $target $session $host,$bus,$tg,$lun $blockdev\n"; - }); - - }); - - return $res; + return PVE::Storage::Common::ISCSI::iscsi_discovery($portals); } # Configuration -- 2.39.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel