From 4f3f2d081d0e8f06489cab9e4bc9900de66fb68b Mon Sep 17 00:00:00 2001 From: NojuHD Date: Wed, 5 Feb 2025 19:40:18 -0500 Subject: [PATCH 1/5] Added support for SCSI (Fibre Channel) - Added subroutines - added sub::scsi_scan_new - added sub::scsi_rescan_device - Added protocol selection --- PureStoragePlugin.pm | 93 +++++++++++++++++++++++++++++++++++++++++--- README.md | 1 + 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/PureStoragePlugin.pm b/PureStoragePlugin.pm index aaef57d..d2b9088 100644 --- a/PureStoragePlugin.pm +++ b/PureStoragePlugin.pm @@ -8,6 +8,7 @@ use Data::Dumper qw( Dumper ); # DEBUG use IO::File (); use Net::IP (); use File::Path (); +use File::Spec (); use PVE::JSONSchema (); use PVE::Network (); @@ -37,9 +38,10 @@ my $default_hgsuffix = ""; my $DEBUG = 0; my $cmd = { - iscsiadm => '/usr/bin/iscsiadm', - multipath => '/sbin/multipath', - blockdev => '/usr/sbin/blockdev' + iscsiadm => '/usr/bin/iscsiadm', + multipath => '/sbin/multipath', + multipathd => '/sbin/multipathd', + blockdev => '/usr/sbin/blockdev' }; ### BLOCK: Configuration @@ -99,6 +101,11 @@ sub properties { type => "boolean", default => "no" }, + protocol => { + description => "Set storage protocol (1 = iscsi | 2 = scsi | 3 = nvme)", + type => "integer", + default => 1 + }, }; } @@ -112,6 +119,7 @@ sub options { podname => { optional => 1 }, vnprefix => { optional => 1 }, check_ssl => { optional => 1 }, + protocol => { optional => 1 }, nodes => { optional => 1 }, disable => { optional => 1 }, content => { optional => 1 }, @@ -134,6 +142,62 @@ sub exec_command { } } +### Block: SCSI (Fibre Channel) subroutines + +sub scsi_scan_new { + print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::scsi_scan_new\n" if $DEBUG; + my $fc_base = '/sys/class/fc_host'; + my @fc_hosts = glob("$fc_base/*"); + + die "Error :: sub::scsi_scan_new did not find fibre channel hosts.\n" unless @fc_hosts; + + foreach my $fc_host (@fc_hosts) { + next unless ($fc_host =~ m/^(\/sys\/class\/fc_host\/\w+)$/); + my $adapter = basename($1); + my $scsi_host = File::Spec->catfile("/sys/class/scsi_host/", $adapter); + + if(-d $scsi_host) { + open my $fh, '>', File::Spec->catfile($scsi_host, "scan") or die "Error :: Cannot open file: $!"; + print $fh "- - -\n"; + close $fh; + } else { + warn "Warning :: SCSI host path $scsi_host does not exist.\n"; + } + } +} + +sub scsi_rescan_device { + my ($wwid) = @_; + print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::scsi_rescan_device\n" if $DEBUG; + die "Error :: sub::scsi_rescan_device did not recive a wwid.\n" unless $wwid; + + foreach my $device (glob('/sys/class/scsi_device/*')) { + next unless ($device =~ m/^(\/sys\/class\/scsi_device\/[\d\:\\]+)$/); + my $tmppath = $1; + + my $wwid_file = File::Spec->catfile($tmppath, "device/wwid"); + next unless -f $wwid_file; + + open(my $wwid_fh, '<', $wwid_file) or die "Error :: Cannot open file: $!"; + my $tmpwwid = <$wwid_fh>; + close($wwid_fh); + + $tmpwwid =~ s/^naa\.//; + $tmpwwid = "3" . lc($tmpwwid); + chomp($tmpwwid); + + if ($tmpwwid eq $wwid) { + open my $rescan, '>', File::Spec->catfile($tmppath, "device/rescan") or die "Error :: Cannot open file: $!"; + print $rescan "1\n"; + close $rescan; + } + } + my $param = qq{ -k"resize map $wwid"}; + exec_command( [ $cmd->{ multipathd } . $param ], 1 ); +} + +### Block: Pure Storage subroutines + sub prepare_api_params { my ( $parms ) = @_; print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::prepare_api_params\n" if $DEBUG; @@ -580,7 +644,17 @@ sub purestorage_resize_volume { my ( $path, $wwid ) = $class->purestorage_get_wwn( $scfg, $volname ); - exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1 ); + my $protocol = $scfg->{ protocol }; + if ($protocol == 1) { + exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1 ); + } elsif ($protocol == 2) { + scsi_rescan_device($wwid); + } elsif ($protocol == 3) { + die qq{"Error :: Protocol: "$protocol" isn't implemented yet.\n}; + } else { + die qq{Error :: Protocol: "$protocol" isn't a valid protocol.\n}; + } + # FIXME: wwid is probably ignored exec_command( [ $cmd->{ multipath }, '-r', $wwid ], 1 ); @@ -904,7 +978,16 @@ sub map_volume { exec_command( [ $cmd->{ multipath }, '-a', $wwid ], 1 ); - exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 ); + my $protocol = $scfg->{ protocol }; + if ($protocol == 1) { + exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 ); + } elsif ($protocol == 2) { + scsi_scan_new(); + } elsif ($protocol == 3) { + die qq{"Error :: Protocol: "$protocol" isn't implemented yet.\n}; + } else { + die qq{Error :: Protocol: "$protocol" isn't a valid protocol.\n}; + } # Wait for the device to apear my $iteration = 0; diff --git a/README.md b/README.md index 9c3489e..171e1f3 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ purestorage: | vnprefix | (`optional`) The prefix to prepend to name of virtual disks. | | hgsuffix | (`optional`) A suffix that is appended to the hostname when the plugin interacts with the Pure Storage array. This can help differentiate hosts if necessary. | | content | Specifies the types of content that can be stored. For virtual machine disk images, use images. | +| protocol | (`optional`, default is `iscsi`) Specifies the storage protocol (1 = iscsi, 2 = scsi) | > **_NOTE:_** Ensure that the token and other sensitive information are kept secure and not exposed publicly. From 7bb26c2a7bd57c3029bdd2aecca9528dde86bb44 Mon Sep 17 00:00:00 2001 From: NojuHD Date: Fri, 7 Feb 2025 13:57:24 +0100 Subject: [PATCH 2/5] Remove multipathd (use existing multipath) --- PureStoragePlugin.pm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/PureStoragePlugin.pm b/PureStoragePlugin.pm index d2b9088..142564c 100644 --- a/PureStoragePlugin.pm +++ b/PureStoragePlugin.pm @@ -40,7 +40,6 @@ my $DEBUG = 0; my $cmd = { iscsiadm => '/usr/bin/iscsiadm', multipath => '/sbin/multipath', - multipathd => '/sbin/multipathd', blockdev => '/usr/sbin/blockdev' }; @@ -192,8 +191,7 @@ sub scsi_rescan_device { close $rescan; } } - my $param = qq{ -k"resize map $wwid"}; - exec_command( [ $cmd->{ multipathd } . $param ], 1 ); + exec_command( [ $cmd->{ multipath }, '-r', $wwid ], 1 ); } ### Block: Pure Storage subroutines From e6abf236a0bdce963158ad370023c9da515ec2a3 Mon Sep 17 00:00:00 2001 From: NojuHD Date: Fri, 7 Feb 2025 12:29:05 -0500 Subject: [PATCH 3/5] Correct formatting (perltidy) --- PureStoragePlugin.pm | 59 ++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/PureStoragePlugin.pm b/PureStoragePlugin.pm index 142564c..6ddb05f 100644 --- a/PureStoragePlugin.pm +++ b/PureStoragePlugin.pm @@ -38,9 +38,9 @@ my $default_hgsuffix = ""; my $DEBUG = 0; my $cmd = { - iscsiadm => '/usr/bin/iscsiadm', - multipath => '/sbin/multipath', - blockdev => '/usr/sbin/blockdev' + iscsiadm => '/usr/bin/iscsiadm', + multipath => '/sbin/multipath', + blockdev => '/usr/sbin/blockdev' }; ### BLOCK: Configuration @@ -145,18 +145,18 @@ sub exec_command { sub scsi_scan_new { print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::scsi_scan_new\n" if $DEBUG; - my $fc_base = '/sys/class/fc_host'; - my @fc_hosts = glob("$fc_base/*"); + my $fc_base = '/sys/class/fc_host'; + my @fc_hosts = glob( "$fc_base/*" ); die "Error :: sub::scsi_scan_new did not find fibre channel hosts.\n" unless @fc_hosts; - foreach my $fc_host (@fc_hosts) { - next unless ($fc_host =~ m/^(\/sys\/class\/fc_host\/\w+)$/); - my $adapter = basename($1); - my $scsi_host = File::Spec->catfile("/sys/class/scsi_host/", $adapter); + foreach my $fc_host ( @fc_hosts ) { + next unless ( $fc_host =~ m/^(\/sys\/class\/fc_host\/\w+)$/ ); + my $adapter = basename( $1 ); + my $scsi_host = File::Spec->catfile( "/sys/class/scsi_host/", $adapter ); - if(-d $scsi_host) { - open my $fh, '>', File::Spec->catfile($scsi_host, "scan") or die "Error :: Cannot open file: $!"; + if ( -d $scsi_host ) { + open my $fh, '>', File::Spec->catfile( $scsi_host, "scan" ) or die "Error :: Cannot open file: $!"; print $fh "- - -\n"; close $fh; } else { @@ -166,27 +166,27 @@ sub scsi_scan_new { } sub scsi_rescan_device { - my ($wwid) = @_; + my ( $wwid ) = @_; print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::scsi_rescan_device\n" if $DEBUG; - die "Error :: sub::scsi_rescan_device did not recive a wwid.\n" unless $wwid; + die "Error :: sub::scsi_rescan_device did not recive a wwid.\n" unless $wwid; - foreach my $device (glob('/sys/class/scsi_device/*')) { - next unless ($device =~ m/^(\/sys\/class\/scsi_device\/[\d\:\\]+)$/); + foreach my $device ( glob( '/sys/class/scsi_device/*' ) ) { + next unless ( $device =~ m/^(\/sys\/class\/scsi_device\/[\d\:\\]+)$/ ); my $tmppath = $1; - my $wwid_file = File::Spec->catfile($tmppath, "device/wwid"); + my $wwid_file = File::Spec->catfile( $tmppath, "device/wwid" ); next unless -f $wwid_file; - open(my $wwid_fh, '<', $wwid_file) or die "Error :: Cannot open file: $!"; + open( my $wwid_fh, '<', $wwid_file ) or die "Error :: Cannot open file: $!"; my $tmpwwid = <$wwid_fh>; - close($wwid_fh); + close( $wwid_fh ); $tmpwwid =~ s/^naa\.//; - $tmpwwid = "3" . lc($tmpwwid); - chomp($tmpwwid); + $tmpwwid = "3" . lc( $tmpwwid ); + chomp( $tmpwwid ); - if ($tmpwwid eq $wwid) { - open my $rescan, '>', File::Spec->catfile($tmppath, "device/rescan") or die "Error :: Cannot open file: $!"; + if ( $tmpwwid eq $wwid ) { + open my $rescan, '>', File::Spec->catfile( $tmppath, "device/rescan" ) or die "Error :: Cannot open file: $!"; print $rescan "1\n"; close $rescan; } @@ -643,16 +643,15 @@ sub purestorage_resize_volume { my ( $path, $wwid ) = $class->purestorage_get_wwn( $scfg, $volname ); my $protocol = $scfg->{ protocol }; - if ($protocol == 1) { + if ( $protocol == 1 ) { exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1 ); - } elsif ($protocol == 2) { - scsi_rescan_device($wwid); - } elsif ($protocol == 3) { + } elsif ( $protocol == 2 ) { + scsi_rescan_device( $wwid ); + } elsif ( $protocol == 3 ) { die qq{"Error :: Protocol: "$protocol" isn't implemented yet.\n}; } else { die qq{Error :: Protocol: "$protocol" isn't a valid protocol.\n}; } - # FIXME: wwid is probably ignored exec_command( [ $cmd->{ multipath }, '-r', $wwid ], 1 ); @@ -977,11 +976,11 @@ sub map_volume { exec_command( [ $cmd->{ multipath }, '-a', $wwid ], 1 ); my $protocol = $scfg->{ protocol }; - if ($protocol == 1) { + if ( $protocol == 1 ) { exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 ); - } elsif ($protocol == 2) { + } elsif ( $protocol == 2 ) { scsi_scan_new(); - } elsif ($protocol == 3) { + } elsif ( $protocol == 3 ) { die qq{"Error :: Protocol: "$protocol" isn't implemented yet.\n}; } else { die qq{Error :: Protocol: "$protocol" isn't a valid protocol.\n}; From 44e53e7e2037dbd4af5e85c5a60d7ae7d3126247 Mon Sep 17 00:00:00 2001 From: NojuHD Date: Fri, 7 Feb 2025 21:41:14 -0500 Subject: [PATCH 4/5] Fix typos & change name scheme --- PureStoragePlugin.pm | 22 +++++++++++----------- README.md | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/PureStoragePlugin.pm b/PureStoragePlugin.pm index 6ddb05f..09bda4a 100644 --- a/PureStoragePlugin.pm +++ b/PureStoragePlugin.pm @@ -101,9 +101,9 @@ sub properties { default => "no" }, protocol => { - description => "Set storage protocol (1 = iscsi | 2 = scsi | 3 = nvme)", - type => "integer", - default => 1 + description => "Set storage protocol ( iscsi | fc | nvme )", + type => "string", + default => "iscsi" }, }; } @@ -643,12 +643,12 @@ sub purestorage_resize_volume { my ( $path, $wwid ) = $class->purestorage_get_wwn( $scfg, $volname ); my $protocol = $scfg->{ protocol }; - if ( $protocol == 1 ) { + if ( $protocol eq 'iscsi' ) { exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1 ); - } elsif ( $protocol == 2 ) { + } elsif ( $protocol eq 'fc' ) { scsi_rescan_device( $wwid ); - } elsif ( $protocol == 3 ) { - die qq{"Error :: Protocol: "$protocol" isn't implemented yet.\n}; + } elsif ( $protocol eq 'nvme' ) { + die qq{Error :: Protocol: "$protocol" isn't implemented yet.\n}; } else { die qq{Error :: Protocol: "$protocol" isn't a valid protocol.\n}; } @@ -976,12 +976,12 @@ sub map_volume { exec_command( [ $cmd->{ multipath }, '-a', $wwid ], 1 ); my $protocol = $scfg->{ protocol }; - if ( $protocol == 1 ) { + if ( $protocol eq 'iscsi' ) { exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 ); - } elsif ( $protocol == 2 ) { + } elsif ( $protocol eq 'fc' ) { scsi_scan_new(); - } elsif ( $protocol == 3 ) { - die qq{"Error :: Protocol: "$protocol" isn't implemented yet.\n}; + } elsif ( $protocol eq 'nvme' ) { + die qq{Error :: Protocol: "$protocol" isn't implemented yet.\n}; } else { die qq{Error :: Protocol: "$protocol" isn't a valid protocol.\n}; } diff --git a/README.md b/README.md index 171e1f3..d599045 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ purestorage: | vnprefix | (`optional`) The prefix to prepend to name of virtual disks. | | hgsuffix | (`optional`) A suffix that is appended to the hostname when the plugin interacts with the Pure Storage array. This can help differentiate hosts if necessary. | | content | Specifies the types of content that can be stored. For virtual machine disk images, use images. | -| protocol | (`optional`, default is `iscsi`) Specifies the storage protocol (1 = iscsi, 2 = scsi) | +| protocol | (`optional`, default is `iscsi`) Specifies the storage protocol (iscsi, fc) | > **_NOTE:_** Ensure that the token and other sensitive information are kept secure and not exposed publicly. From 32ed77d1558f3b31829e90be7e9685017439569b Mon Sep 17 00:00:00 2001 From: NojuHD Date: Sat, 8 Feb 2025 00:06:34 -0500 Subject: [PATCH 5/5] Set protocol default via "$default_protocol" --- PureStoragePlugin.pm | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/PureStoragePlugin.pm b/PureStoragePlugin.pm index 09bda4a..60b7a9d 100644 --- a/PureStoragePlugin.pm +++ b/PureStoragePlugin.pm @@ -32,8 +32,9 @@ $Data::Dumper::Terse = 1; # Removes `$VAR1 =` in output $Data::Dumper::Indent = 1; # Outputs everything in one line $Data::Dumper::Useqq = 1; # Uses quotes for strings -my $purestorage_wwn_prefix = "624a9370"; -my $default_hgsuffix = ""; +my $purestorage_wwn_prefix = '624a9370'; +my $default_hgsuffix = ''; +my $default_protocol = 'iscsi'; my $DEBUG = 0; @@ -76,34 +77,34 @@ sub properties { return { hgsuffix => { description => "Host group suffx.", - type => "string", + type => 'string', default => $default_hgsuffix }, address => { description => "PureStorage Management IP address or DNS name.", - type => "string" + type => 'string' }, token => { description => "Storage API token.", - type => "string" + type => 'string' }, podname => { - description => 'PureStorage pod name', + description => "PureStorage pod name", type => 'string' }, vnprefix => { - description => 'Prefix to add to volume name before sending it to PureStorage array', + description => "Prefix to add to volume name before sending it to PureStorage array", type => 'string' }, check_ssl => { description => "Verify the server's TLS certificate", - type => "boolean", - default => "no" + type => 'boolean', + default => 'no' }, protocol => { description => "Set storage protocol ( iscsi | fc | nvme )", - type => "string", - default => "iscsi" + type => 'string', + default => $default_protocol }, }; } @@ -642,7 +643,7 @@ sub purestorage_resize_volume { my ( $path, $wwid ) = $class->purestorage_get_wwn( $scfg, $volname ); - my $protocol = $scfg->{ protocol }; + my $protocol = $scfg->{ protocol } // $default_protocol; if ( $protocol eq 'iscsi' ) { exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1 ); } elsif ( $protocol eq 'fc' ) { @@ -975,7 +976,7 @@ sub map_volume { exec_command( [ $cmd->{ multipath }, '-a', $wwid ], 1 ); - my $protocol = $scfg->{ protocol }; + my $protocol = $scfg->{ protocol } // $default_protocol; if ( $protocol eq 'iscsi' ) { exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 ); } elsif ( $protocol eq 'fc' ) {