[pve-devel] [RFC pve-storage 28/36] plugin: iscsi: factor helper functions into common module
Max Carrara
m.carrara at proxmox.com
Wed Jul 17 11:40:26 CEST 2024
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 <m.carrara at proxmox.com>
---
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<PVE::Storage::Common::ISCSI>
+
+Subroutines that provide ISCSI-related functionalities.
+
=item C<PVE::Storage::Common::LVM>
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<undef> instead
+of raising an exception.
+
+ISCSI operations are considered to be supported if C</usr/bin/iscsiadm> 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<discover|/iscsi_discovery>
+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</var/run/pve-iscsi-rescan.lock>. 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
More information about the pve-devel
mailing list