mirror of
https://github.com/kolesa-team/pve-purestorage-plugin.git
synced 2026-08-12 21:53:06 -06:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4214cf9ff5 | |||
| 918baa8a14 | |||
| 78a46db7c4 | |||
| 3a76010d96 | |||
| fc989677bb | |||
| d9018eec0f | |||
| c6770fc4cd | |||
| 6474161796 | |||
| 829cf63e1f | |||
| 660d9bff76 | |||
| e9342bee43 | |||
| d97f93c340 |
@@ -50,7 +50,7 @@ jobs:
|
||||
tag_name: ${{ github.ref_name }}
|
||||
generate_release_notes: true
|
||||
make_latest: true
|
||||
prerelease: true
|
||||
prerelease: false
|
||||
files: |
|
||||
*.deb
|
||||
sha256sums
|
||||
|
||||
+86
-33
@@ -32,7 +32,7 @@ $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 = "pve";
|
||||
my $default_hgsuffix = "";
|
||||
|
||||
my $DEBUG = 0;
|
||||
|
||||
@@ -127,12 +127,15 @@ sub exec_command {
|
||||
### BLOCK: Local multipath => PVE::Storage::Custom::PureStoragePlugin::sub::s
|
||||
|
||||
sub purestorage_request {
|
||||
my ( $class, $scfg, $type, $method, $params, $body ) = @_;
|
||||
my ( $class, $scfg, $type, $method, $params, $body, $attempt ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_request\n" if $DEBUG;
|
||||
|
||||
my $api = "2.26";
|
||||
my $url = $scfg->{ address };
|
||||
my $check_ssl = $scfg->{ check_ssl } ? 1 : 0;
|
||||
my $api = "2.26";
|
||||
my $url = $scfg->{ address };
|
||||
my $check_ssl = $scfg->{ check_ssl } ? 1 : 0;
|
||||
my $max_attempts = 5;
|
||||
my $interval = 1;
|
||||
$attempt //= 1; # Initialize the attempt counter to 1 if not provided
|
||||
|
||||
$url .= "/api/$api/$type";
|
||||
$url .= "?$params" if $params;
|
||||
@@ -141,6 +144,7 @@ sub purestorage_request {
|
||||
$type eq "login"
|
||||
? $scfg->{ token }
|
||||
: $class->purestorage_get_auth_token( $scfg );
|
||||
|
||||
my $headers = HTTP::Headers->new(
|
||||
( $type eq "login" ? "api-token" : "x-auth-token" ) => $token,
|
||||
"Content-Type" => "application/json"
|
||||
@@ -155,14 +159,31 @@ sub purestorage_request {
|
||||
verify_hostname => 0,
|
||||
SSL_verify_mode => 0x00
|
||||
) if !$check_ssl;
|
||||
my $request = HTTP::Request->new( $method, $url, $headers, $body ? encode_json( $body ) : undef );
|
||||
my $response = $ua->request( $request );
|
||||
|
||||
my $request = HTTP::Request->new( $method, $url, $headers, $body ? encode_json( $body ) : undef );
|
||||
my $response = $ua->request( $request );
|
||||
|
||||
my $content_type = $response->header( "Content-Type" );
|
||||
my $content =
|
||||
defined $content_type && $content_type =~ /application\/json/ && $response->content ne ""
|
||||
? decode_json( $response->content )
|
||||
: $response->decoded_content;
|
||||
|
||||
if ( !$response->is_success ) {
|
||||
if ( $response->code == 401 && $attempt < $max_attempts ) {
|
||||
$attempt++;
|
||||
print "Error :: Invalid session. Retrying... Attempt: " . ( $attempt ) . "\n";
|
||||
|
||||
# Reset the token cache
|
||||
$scfg->{ x_auth_token } = 0;
|
||||
|
||||
sleep $interval;
|
||||
|
||||
# Recursively call the function with an incremented attempt counter
|
||||
return $class->purestorage_request( $scfg, $type, $method, $params, $body, $attempt );
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content => $content,
|
||||
headers => $response->headers,
|
||||
@@ -251,19 +272,19 @@ sub purestorage_volume_info {
|
||||
|
||||
sub purestorage_list_volumes {
|
||||
my ( $class, $scfg, $vmid, $storeid, $destroyed ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_list_volumes\n" if $DEBUG;
|
||||
|
||||
my $names = defined ($vmid) ? "vm-$vmid-disk-*,vm-$vmid-cloudinit,vm-$vmid-state-*" : "*";
|
||||
my $names = defined( $vmid ) ? "vm-$vmid-disk-*,vm-$vmid-cloudinit,vm-$vmid-state-*" : "*";
|
||||
|
||||
return $class->purestorage_list_volumes2( $scfg, $names, $storeid, $destroyed );
|
||||
return $class->purestorage_get_volumes( $scfg, $names, $storeid, $destroyed );
|
||||
}
|
||||
|
||||
sub purestorage_list_volumes2 {
|
||||
sub purestorage_get_volumes {
|
||||
my ( $class, $scfg, $names, $storeid, $destroyed ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_list_volumes\n" if $DEBUG;
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
|
||||
my @names_list = map { "name='$vgname/$_'" } split( ',', $names );
|
||||
|
||||
|
||||
my $filter = join( ' or ', @names_list );
|
||||
|
||||
if ( defined( $destroyed ) ) {
|
||||
@@ -302,13 +323,31 @@ sub purestorage_list_volumes2 {
|
||||
return \@volumes;
|
||||
}
|
||||
|
||||
sub purestorage_get_volume_info {
|
||||
my ( $class, $scfg, $volname, $storeid, $destroyed ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_volume_info\n" if $DEBUG;
|
||||
|
||||
my $volumes = $class->purestorage_get_volumes( $scfg, $volname, $storeid, $destroyed );
|
||||
foreach my $volume ( @$volumes ) {
|
||||
return $volume;
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub purestorage_get_existing_volume_info {
|
||||
my ( $class, $scfg, $volname, $storeid ) = @_;
|
||||
|
||||
return $class->purestorage_get_volume_info( $scfg, $volname, $storeid, 0 );
|
||||
}
|
||||
|
||||
sub purestorage_get_wwn {
|
||||
my ( $class, $scfg, $volname ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_wwn\n" if $DEBUG;
|
||||
|
||||
my $volumes = $class->purestorage_list_volumes2( $scfg, $volname, undef, 0 );
|
||||
my $volume = $class->purestorage_get_existing_volume_info( $scfg, $volname );
|
||||
if ( $volume ) {
|
||||
|
||||
foreach my $volume ( @$volumes ) {
|
||||
# Construct the WWN path
|
||||
my $path = lc( "/dev/disk/by-id/wwn-0x" . $purestorage_wwn_prefix . $volume->{ serial } );
|
||||
my $wwn = lc( "3" . $purestorage_wwn_prefix . $volume->{ serial } );
|
||||
@@ -370,7 +409,7 @@ sub purestorage_volume_connection {
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
my $url = $scfg->{ address } || die "Error :: Pure Storage host is not defined.\n";
|
||||
|
||||
my $hname = PVE::INotify::nodename();
|
||||
my $hname = PVE::INotify::nodename();
|
||||
my $hgsuffix = $scfg->{ hgsuffix } // $default_hgsuffix;
|
||||
$hname .= "-" . $hgsuffix if $hgsuffix ne "";
|
||||
|
||||
@@ -382,7 +421,7 @@ sub purestorage_volume_connection {
|
||||
$message = $response->{ content }->{ errors }->[0]->{ message } || '*';
|
||||
if ( $message eq "Connection already exists." ) {
|
||||
$message = '' if $action eq 'POST';
|
||||
} elsif ( $message eq "Volume has been destroyed." || $message eq "Connection does not exist.") {
|
||||
} elsif ( $message eq "Volume has been destroyed." || $message eq "Connection does not exist." ) {
|
||||
$message = '' if $action eq 'DELETE';
|
||||
}
|
||||
if ( $message ne '' ) {
|
||||
@@ -398,7 +437,7 @@ sub purestorage_volume_connection {
|
||||
$message = 'is';
|
||||
}
|
||||
|
||||
$message .= ' ' . ($action eq 'DELETE' ? 'removed from' : 'added to');
|
||||
$message .= ' ' . ( $action eq 'DELETE' ? 'removed from' : 'added to' );
|
||||
print "Info :: Volume \"$vgname/$volname\" $message host \"$hname\".\n";
|
||||
return 1;
|
||||
}
|
||||
@@ -442,7 +481,7 @@ sub purestorage_remove_volume {
|
||||
my $url = $scfg->{ address } || die "Error :: Pure Storage host is not defined.\n";
|
||||
|
||||
my $params = "names=$vgname/$volname";
|
||||
my $body = { destroyed => \1 };
|
||||
my $body = { destroyed => \1 };
|
||||
|
||||
my $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $body );
|
||||
if ( $response->{ error } ) {
|
||||
@@ -486,7 +525,9 @@ sub purestorage_get_device_size {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_device_size\n" if $DEBUG;
|
||||
my $size = 0;
|
||||
|
||||
exec_command( [ $cmd->{ blockdev }, '--getsize64', $path ], 1,
|
||||
exec_command(
|
||||
[ $cmd->{ blockdev }, '--getsize64', $path ],
|
||||
1,
|
||||
outfunc => sub {
|
||||
$size = $_[0];
|
||||
chomp $size;
|
||||
@@ -524,7 +565,7 @@ sub purestorage_resize_volume {
|
||||
|
||||
my ( $path, undef, undef, $wwid ) = $class->filesystem_path( $scfg, $volname );
|
||||
|
||||
exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1);
|
||||
exec_command( [ $cmd->{ iscsiadm }, '--mode', 'node', '--rescan' ], 1 );
|
||||
|
||||
# FIXME: wwid is probably ignored
|
||||
exec_command( [ $cmd->{ multipath }, '-r', $wwid ], 1 );
|
||||
@@ -545,7 +586,7 @@ sub purestorage_resize_volume {
|
||||
print "Info :: New size detected for volume \"$vgname/$volname\": $new_size bytes.\n";
|
||||
return $new_size;
|
||||
}
|
||||
|
||||
|
||||
sleep $interval;
|
||||
++$iteration;
|
||||
}
|
||||
@@ -698,7 +739,7 @@ sub parse_volname {
|
||||
# ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format)
|
||||
return ( $vtype, $name, $vmid, undef, undef, undef, 'raw' );
|
||||
}
|
||||
|
||||
|
||||
die "Error :: Invalid volume name ($volname).\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -735,8 +776,8 @@ sub find_free_diskname {
|
||||
my ( $class, $storeid, $scfg, $vmid, $fmt, $add_fmt_suffix ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::find_free_diskname\n" if $DEBUG;
|
||||
|
||||
my $volumes = $class->purestorage_list_volumes( $scfg, $vmid, $storeid );
|
||||
my @disk_list = map { $_->{ name } } @$volumes;
|
||||
my $volumes = $class->purestorage_list_volumes( $scfg, $vmid, $storeid );
|
||||
my @disk_list = map { $_->{ name } } @$volumes;
|
||||
|
||||
return PVE::Storage::Plugin::get_next_vm_diskname( \@disk_list, $storeid, $vmid, undef, $scfg );
|
||||
}
|
||||
@@ -756,11 +797,11 @@ sub alloc_image {
|
||||
$name = $class->find_free_diskname( $storeid, $scfg, $vmid ) if !$name;
|
||||
|
||||
# Check size (must be between 1MB and 4PB)
|
||||
if ($size < 1024) {
|
||||
if ( $size < 1024 ) {
|
||||
print "Info :: Size is too small ($size kb), adjusting to 1024 kb\n";
|
||||
$size = 1024;
|
||||
}
|
||||
|
||||
|
||||
# Convert size from KB to bytes
|
||||
my $sizeB = $size * 1024; # KB => B
|
||||
|
||||
@@ -805,14 +846,15 @@ sub status {
|
||||
my $current_time = gettimeofday();
|
||||
if ( $current_time - $cache->{ last_update } >= 60 ) {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::status\n" if $DEBUG;
|
||||
|
||||
|
||||
my $response = $class->purestorage_request( $scfg, "arrays/space", "GET" );
|
||||
|
||||
# Get storage capacity and used space from the response
|
||||
$cache->{ total } = $response->{ content }->{ items }->[0]->{ capacity };
|
||||
$cache->{ used } = $response->{ content }->{ items }->[0]->{ space }->{ total_physical };
|
||||
|
||||
# $cache->{ used } = $response->{ content }->{ items }->[0]->{ space }->{ total_used }; # Do not know what is correct
|
||||
|
||||
|
||||
$cache->{ last_update } = $current_time;
|
||||
} else {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::status::cached\n" if $DEBUG;
|
||||
@@ -863,7 +905,7 @@ sub map_volume {
|
||||
|
||||
exec_command( [ $cmd->{ multipath }, '-a', $wwid ], 1 );
|
||||
|
||||
exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 );
|
||||
exec_command( [ $cmd->{ iscsiadm }, '--mode', 'session', '--rescan' ], 1 );
|
||||
|
||||
# Wait for the device to apear
|
||||
my $iteration = 0;
|
||||
@@ -900,7 +942,7 @@ sub unmap_volume {
|
||||
exec_command( [ $cmd->{ blockdev }, '--flushbufs', $path ] );
|
||||
|
||||
my $device_name = basename( $device_path );
|
||||
my $slaves_path = "/sys/block/$device_name/slaves";
|
||||
my $slaves_path = "/sys/block/$device_name/slaves";
|
||||
|
||||
my @slaves = ();
|
||||
if ( -d $slaves_path ) {
|
||||
@@ -938,7 +980,7 @@ sub unmap_volume {
|
||||
print "Info :: Device \"$device_name\" removed from system.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -959,7 +1001,7 @@ sub deactivate_volume {
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
|
||||
$class->unmap_volume( $storeid, $scfg, $volname, $snapname );
|
||||
|
||||
|
||||
$class->purestorage_volume_connection( $scfg, $volname, 'DELETE' );
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" deactivated.\n";
|
||||
@@ -982,9 +1024,20 @@ sub volume_resize {
|
||||
sub rename_volume {
|
||||
my ( $class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::rename_volume\n" if $DEBUG;
|
||||
|
||||
die "Error :: not implemented in storage plugin \"$class\".\n" if $class->can( 'api' ) && $class->api() < 10;
|
||||
|
||||
$target_volname = $class->find_free_diskname( $storeid, $scfg, $target_vmid ) if !$target_volname;
|
||||
if ( $target_volname ) {
|
||||
|
||||
# See RBDPlugin.pm (note, currently PVE does not supply $target_volname parameter)
|
||||
my $volume = $class->purestorage_get_volume_info( $scfg, $target_volname, $storeid );
|
||||
die "target volume '$target_volname' already exists\n" if $volume;
|
||||
} else {
|
||||
$target_volname = $class->find_free_diskname( $storeid, $scfg, $target_vmid );
|
||||
}
|
||||
|
||||
# we need to unmap source volume (see RBDPlugin.pm)
|
||||
$class->unmap_volume( $storeid, $scfg, $source_volname );
|
||||
|
||||
$class->purestorage_rename_volume( $scfg, $source_volname, $target_volname );
|
||||
|
||||
|
||||
@@ -141,6 +141,12 @@ sudo systemctl restart pve-cluster.service pvedaemon.service pvestatd.service pv
|
||||
|
||||
- Verify Network Connectivity: Ensure that the Proxmox VE nodes can reach the Pure Storage array over the network. Check for firewall rules or network issues that might be blocking communication.
|
||||
- Review Logs: Check the Proxmox VE logs for any error messages related to storage or the plugin. Logs are typically found in /var/log/pve.
|
||||
These commands are helpful for troubleshooting:
|
||||
```bash
|
||||
multipath -ll -v3 #diagnose issues with the multipath service
|
||||
iscsiadm -m node #list what iscsi nodes are mounted
|
||||
ls -l /dev/mapper/3624a9370* #list wwids of Pure mapped devices on the system
|
||||
```
|
||||
- Multipath Configuration: Verify that your multipath.conf is correctly configured and that multipath devices are recognized. Use multipath -ll to list the current multipath devices.
|
||||
- API Token Permissions: Ensure that the API token used has the necessary permissions to create and manage volumes on the Pure Storage array.
|
||||
- Plugin Updates: Ensure you are using the latest version of the plugin. Check the GitHub repository for updates.
|
||||
|
||||
Reference in New Issue
Block a user