2 Commits

Author SHA1 Message Date
timansky a0c90b99b3 Fix readme wwid incorrect 2025-01-25 02:53:39 +05:00
timansky 2995222f74 Adds ability to configure volume group or pod namespace (#29) 2025-01-25 02:53:22 +05:00
2 changed files with 121 additions and 78 deletions
+114 -76
View File
@@ -32,7 +32,8 @@ $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 $default_hgsuffix = "";
my $default_vgsuffix = "/";
my $DEBUG = 0;
@@ -78,6 +79,11 @@ sub properties {
type => "string",
default => $default_hgsuffix
},
vgsuffix => {
description => "Delimeter defines volumes group or pod.",
type => "string",
default => $default_vgsuffix
},
address => {
description => "PureStorage Management IP address or DNS name.",
type => "string"
@@ -96,10 +102,10 @@ sub properties {
sub options {
return {
address => { fixed => 1 },
token => { fixed => 1 },
address => { fixed => 1 },
token => { fixed => 1 },
hgsuffix => { optional => 1 },
vgsuffix => { optional => 1 },
vgname => { fixed => 1 },
check_ssl => { optional => 1 },
nodes => { optional => 1 },
@@ -196,6 +202,7 @@ sub purestorage_get_auth_token {
sub purestorage_volume_info {
my ( $class, $scfg, $volname ) = @_;
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_volume_info\n" if $DEBUG;
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
$scfg->{ cache } ||= {};
@@ -203,15 +210,21 @@ sub purestorage_volume_info {
$scfg->{ cache }->{ volume_info }->{ "$vgname" } ||= {};
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" } ||= {};
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ last_update } ||= 0;
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $current_time = gettimeofday();
if ( $current_time - $scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ last_update } >= 60 ) {
my $filter = "name='$vgname/$volname'";
my $filter = "name='$vgname$vgsuffix$volname'";
my $response = $class->purestorage_request( $scfg, "volumes", "GET", "filter=" . uri_escape( $filter ) );
if ( $response->{ error } ) {
die "Error :: PureStorage API :: Get volume \"$vgname/$volname\" info failed.\n"
die "Error :: PureStorage API :: Get volume \""
. $vgname
. $vgsuffix
. $volname
. "\" info failed.\n"
. "=> Trace:\n"
. "==> Code: "
. $response->{ error } . "\n"
@@ -220,7 +233,7 @@ sub purestorage_volume_info {
my $volumes = $response->{ content }->{ items };
unless ( ref( $volumes ) eq 'ARRAY' && @$volumes ) {
die "Error :: PureStorage API :: No volume data found for \"$vgname/$volname\".\n";
die "Error :: PureStorage API :: No volume data found for \"$vgname$vgsuffix$volname\".\n";
}
my $volume = $volumes->[0];
@@ -253,18 +266,20 @@ 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_get_volumes( $scfg, $names, $storeid, $destroyed );
}
sub purestorage_get_volumes {
my ( $class, $scfg, $names, $storeid, $destroyed ) = @_;
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_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 );
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my @names_list = map { "name='$vgname$vgsuffix$_'" } split( ',', $names );
my $filter = join( ' or ', @names_list );
if ( defined( $destroyed ) ) {
$filter = '(' . $filter . ')' if $#names_list > 0;
@@ -283,7 +298,7 @@ sub purestorage_get_volumes {
my @volumes = map {
my $volname = $_->{ name };
$volname =~ s/^$scfg->{vgname}\///;
$volname =~ s/^$vgname\Q$vgsuffix\E//;
my ( undef, undef, $volvm ) = $class->parse_volname( $volname );
@@ -326,6 +341,7 @@ sub purestorage_get_wwn {
my $volume = $class->purestorage_get_existing_volume_info( $scfg, $volname );
if ( $volume ) {
# 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 } );
@@ -387,19 +403,21 @@ 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;
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
$hname .= "-" . $hgsuffix if $hgsuffix ne "";
my $params = "host_names=$hname&volume_names=$vgname/$volname";
my $params = "host_names=$hname&volume_names=$vgname$vgsuffix$volname";
my $response = $class->purestorage_request( $scfg, "connections", $action, $params );
my $message;
if ( $response->{ error } ) {
$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 '' ) {
@@ -415,8 +433,8 @@ sub purestorage_volume_connection {
$message = 'is';
}
$message .= ' ' . ($action eq 'DELETE' ? 'removed from' : 'added to');
print "Info :: Volume \"$vgname/$volname\" $message host \"$hname\".\n";
$message .= ' ' . ( $action eq 'DELETE' ? 'removed from' : 'added to' );
print "Info :: Volume \"$vgname$vgsuffix$volname\" $message host \"$hname\".\n";
return 1;
}
@@ -427,7 +445,8 @@ sub purestorage_create_volume {
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 $params = "names=$vgname/$volname";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $params = "names=$vgname$vgsuffix$volname";
my $volparams = { "provisioned" => $size };
my $response = $class->purestorage_request( $scfg, "volumes", "POST", $params, $volparams );
@@ -440,7 +459,7 @@ sub purestorage_create_volume {
}
my $serial = $response->{ content }->{ items }->[0]->{ serial } || die "Error :: Failed to retrieve volume serial";
print "Info :: Volume \"$vgname/$volname\" created (serial=$serial).\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" created (serial=$serial).\n";
return 1;
}
@@ -458,8 +477,10 @@ sub purestorage_remove_volume {
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 $params = "names=$vgname/$volname";
my $body = { destroyed => \1 };
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $params = "names=$vgname$vgsuffix$volname";
my $body = { destroyed => \1 };
my $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $body );
if ( $response->{ error } ) {
@@ -471,27 +492,27 @@ sub purestorage_remove_volume {
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
} else {
$Data::Dumper::Indent = 0;
die "Error :: PureStorage API :: Destroy volume \"$vgname/$volname\" failed.\n"
die "Error :: PureStorage API :: Destroy volume \"$vgname$vgsuffix$volname\" failed.\n"
. "=> Trace:\n"
. "==> Code: "
. $response->{ error } . "\n"
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
}
} else {
print "Info :: Volume \"$vgname/$volname\" destroyed.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" destroyed.\n";
}
if ( $eradicate ) {
$response = $class->purestorage_request( $scfg, "volumes", "DELETE", $params );
if ( $response->{ error } ) {
$Data::Dumper::Indent = 0;
die "Error :: PureStorage API :: Eradicate volume \"$vgname/$volname\" failed.\n"
die "Error :: PureStorage API :: Eradicate volume \"$vgname$vgsuffix$volname\" failed.\n"
. "=> Trace:\n"
. "==> Code: "
. $response->{ error } . "\n"
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
} else {
print "Info :: Volume \"$vgname/$volname\" eradicated.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" eradicated.\n";
}
}
@@ -503,7 +524,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;
@@ -521,12 +544,14 @@ sub purestorage_resize_volume {
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 $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
$scfg->{ cache } ||= {};
$scfg->{ cache }->{ volume_info } ||= {};
$scfg->{ cache }->{ volume_info }->{ "$vgname" } ||= {};
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" } = {};
my $params = "names=$vgname/$volname";
my $params = "names=$vgname$vgsuffix$volname";
my $volparams = { "provisioned" => $size };
my $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $volparams );
if ( $response->{ error } ) {
@@ -537,11 +562,11 @@ sub purestorage_resize_volume {
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
}
print "Info :: Volume \"$vgname/$volname\" resized.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" resized.\n";
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 );
@@ -555,11 +580,11 @@ sub purestorage_resize_volume {
print "Debug :: Expected size = $size\n" if $DEBUG;
while ( $iteration < $max_attempts ) {
print "Info :: Waiting (" . $iteration . "s) for size update for volume \"$vgname/$volname\"...\n";
print "Info :: Waiting (" . $iteration . "s) for size update for volume \"$vgname$vgsuffix$volname\"...\n";
$new_size = $class->purestorage_get_device_size( $path );
if ( $new_size >= $size ) {
print "Info :: New size detected for volume \"$vgname/$volname\": $new_size bytes.\n";
print "Info :: New size detected for volume \"$vgname$vgsuffix$volname\": $new_size bytes.\n";
return $new_size;
}
@@ -567,17 +592,20 @@ sub purestorage_resize_volume {
++$iteration;
}
die "Error :: Timeout while waiting for updated size of volume \"$vgname/$volname\".\n";
die "Error :: Timeout while waiting for updated size of volume \"$vgname$vgsuffix$volname\".\n";
}
sub purestorage_rename_volume {
my ( $class, $scfg, $source_volname, $target_volname ) = @_;
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_rename_volume\n" if $DEBUG;
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 $params = "names=$vgname/$source_volname";
my $volparams = { "name" => "$vgname/$target_volname" };
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 $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $params = "names=$vgname$vgsuffix$source_volname";
my $volparams = { "name" => "$vgname$vgsuffix$target_volname" };
my $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $volparams );
if ( $response->{ error } ) {
@@ -588,7 +616,7 @@ sub purestorage_rename_volume {
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
}
print "Info :: Volume \"$vgname/$source_volname\" renamed to \"$vgname/$target_volname\".\n";
print "Info :: Volume \"$vgname$vgsuffix$source_volname\" renamed to \"$vgname$vgsuffix$target_volname\".\n";
return 1;
}
@@ -598,12 +626,11 @@ sub purestorage_snap_volume_create {
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_snap_volume_create\n" if $DEBUG;
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $params;
my $response;
$params = "source_names=$vgname/$volname&suffix=snap-$snap_name";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
$response = $class->purestorage_request( $scfg, "volume-snapshots", "POST", $params );
my $params = "source_names=$vgname$vgsuffix$volname&suffix=snap-$snap_name";
my $response = $class->purestorage_request( $scfg, "volume-snapshots", "POST", $params );
if ( $response->{ error } ) {
die "Error :: PureStorage API :: Snapshot volume failed.\n"
@@ -613,7 +640,7 @@ sub purestorage_snap_volume_create {
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
}
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" created.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" snapshot \"$snap_name\" created.\n";
return 1;
}
@@ -622,18 +649,16 @@ sub purestorage_snap_volume_rollback {
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_snap_volume_rollback\n" if $DEBUG;
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $params;
my $response;
my $body;
$params = "names=$vgname/$volname&overwrite=true";
$body = {
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $params = "names=$vgname$vgsuffix$volname&overwrite=true";
my $body = {
source => {
name => "$vgname/$volname.snap-$snap_name"
name => "$vgname$vgsuffix$volname.snap-$snap_name"
}
};
$response = $class->purestorage_request( $scfg, "volumes", "POST", $params, $body );
my $response = $class->purestorage_request( $scfg, "volumes", "POST", $params, $body );
if ( $response->{ error } ) {
die "Error :: PureStorage API :: Restore volume snapshot failed.\n"
@@ -643,7 +668,7 @@ sub purestorage_snap_volume_rollback {
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
}
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" restored.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" snapshot \"$snap_name\" restored.\n";
return $volname;
}
@@ -652,15 +677,11 @@ sub purestorage_snap_volume_delete {
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_snap_volume_delete\n" if $DEBUG;
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $params;
my $response;
my $body;
$params = "names=$vgname/$volname.snap-$snap_name";
$body = { destroyed => \1 };
$response = $class->purestorage_request( $scfg, "volume-snapshots", "PATCH", $params, $body );
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $params = "names=$vgname$vgsuffix$volname.snap-$snap_name";
my $body = { destroyed => \1 };
my $response = $class->purestorage_request( $scfg, "volume-snapshots", "PATCH", $params, $body );
if ( $response->{ error } ) {
my @valid_errors =
@@ -681,23 +702,23 @@ sub purestorage_snap_volume_delete {
}
}
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" destroyed.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" snapshot \"$snap_name\" destroyed.\n";
$params = "names=$vgname/$volname.snap-$snap_name";
$params = "names=$vgname$vgsuffix$volname.snap-$snap_name";
$body = { replication_snapshot => \1 };
$response = $class->purestorage_request( $scfg, "volume-snapshots", "DELETE", $params, $body );
if ( $response->{ error } ) {
$Data::Dumper::Indent = 0;
die "Error :: PureStorage API :: Eradicate volume \"$vgname/$volname\" snapshot \"$snap_name\" failed.\n"
die "Error :: PureStorage API :: Eradicate volume \"$vgname$vgsuffix$volname\" snapshot \"$snap_name\" failed.\n"
. "=> Trace:\n"
. "==> Code: "
. $response->{ error } . "\n"
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
}
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" eradicated.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" snapshot \"$snap_name\" eradicated.\n";
return 1;
}
@@ -715,7 +736,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;
}
@@ -752,8 +773,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 );
}
@@ -764,6 +785,8 @@ sub alloc_image {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
# Check for supported format (only 'raw' is allowed)
die "Error :: Unsupported format ($fmt).\n" if $fmt ne 'raw';
@@ -773,7 +796,7 @@ 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;
}
@@ -782,7 +805,7 @@ sub alloc_image {
my $sizeB = $size * 1024; # KB => B
if ( !$class->purestorage_create_volume( $scfg, $name, $sizeB, $storeid ) ) {
die "Error :: Failed to create volume \"$vgname/$name\".\n";
die "Error :: Failed to create volume \"$vgname$vgsuffix$name\".\n";
}
return $name;
@@ -828,6 +851,7 @@ sub status {
# 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;
@@ -876,11 +900,13 @@ sub map_volume {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
print "Info :: Mapping volume \"$vgname/$volname\" with WWN: " . uc( $wwid ) . ".\n" if $DEBUG;
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
print "Info :: Mapping volume \"$vgname$vgsuffix$volname\" with WWN: " . uc( $wwid ) . ".\n" if $DEBUG;
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;
@@ -917,9 +943,9 @@ 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 = ();
my @slaves = ();
if ( -d $slaves_path ) {
opendir( my $dh, $slaves_path ) or die "Cannot open directory: $!";
@slaves = grep { !/^\.\.?$/ } readdir( $dh );
@@ -975,11 +1001,13 @@ sub deactivate_volume {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
$class->unmap_volume( $storeid, $scfg, $volname, $snapname );
$class->purestorage_volume_connection( $scfg, $volname, 'DELETE' );
print "Info :: Volume \"$vgname/$volname\" deactivated.\n";
print "Info :: Volume \"$vgname$vgsuffix$volname\" deactivated.\n";
return 1;
}
@@ -991,7 +1019,10 @@ sub volume_resize {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $new_size = $class->purestorage_resize_volume( $scfg, $volname, $size ) or die "Error :: Failed to resize volume \"$vgname/$volname\".\n";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
my $new_size = $class->purestorage_resize_volume( $scfg, $volname, $size )
or die "Error :: Failed to resize volume \"$vgname$vgsuffix$volname\".\n";
return $new_size;
}
@@ -1003,6 +1034,7 @@ sub rename_volume {
die "Error :: not implemented in storage plugin \"$class\".\n" if $class->can( 'api' ) && $class->api() < 10;
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;
@@ -1032,8 +1064,10 @@ sub volume_snapshot {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
if ( !$class->purestorage_snap_volume_create( $scfg, $snap, $volname ) ) {
die "Error :: Failed to snapshot volume \"$vgname/$volname\".\n";
die "Error :: Failed to snapshot volume \"$vgname$vgsuffix$volname\".\n";
}
return 1;
}
@@ -1044,8 +1078,10 @@ sub volume_snapshot_rollback {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
if ( !$class->purestorage_snap_volume_rollback( $scfg, $snap, $volname ) ) {
die "Error :: Failed to rollback snapshot volume \"$vgname/$volname\".\n";
die "Error :: Failed to rollback snapshot volume \"$vgname$vgsuffix$volname\".\n";
}
return 1;
}
@@ -1056,8 +1092,10 @@ sub volume_snapshot_delete {
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
my $vgsuffix = $scfg->{ vgsuffix } // $default_vgsuffix;
if ( !$class->purestorage_snap_volume_delete( $scfg, $snap, $volname ) ) {
die "Error :: Failed to snapshot volume \"$vgname/$volname\".\n";
die "Error :: Failed to snapshot volume \"$vgname$vgsuffix$volname\".\n";
}
return 1;
}
+7 -2
View File
@@ -56,7 +56,7 @@ blacklist {
}
blacklist_exceptions {
wwid "624a9370.*"
wwid "3624a9370.*"
device {
vendor "PURE"
}
@@ -99,21 +99,24 @@ sudo apt install ./libpve-storage-purestorage-perl.deb
After installing the plugin, you need to configure Proxmox VE to use it. Since Proxmox VE does not currently support adding custom storage plugins via the GUI, you will need to manually edit the storage configuration file `/etc/pve/storage.cfg`.
```
purestorage: pure
purestorage: <proxmox_storage_id>
nodes: <proxmox_node_list>
address https://<purestorage_fqdn_or_ip>
token <purestorage_api_token>
vgname <purestorage_volume_group_name>
vgsuffix <purestorage_volumegroup_or_pod_delimeter>
hgsuffix <purestorage_host_suffix>
content images
```
| Parameter | Description |
| --------- | ----------- |
| proxmox_storage_id | Storage name in proxmox. |
| nodes | (`optional`) A comma-separated list of Proxmox node names. Use this parameter to limit the plugin to specific nodes in your cluster. If omitted, the storage is available to all nodes. |
| address | The URL or IP address of the Pure Storage API endpoint. Ensure that the Proxmox VE nodes can reach this address over the network. |
| token | The API token used for authentication with the Pure Storage array. This token must have sufficient permissions to create and manage volumes. |
| vgname | The name of the volume group where new virtual disks will be created. This should match the configuration on your Pure Storage array. |
| vgsuffix | (`optional`) (`default: /`)Delimeter to define Volume Group or POD storage variant. Can be `/` for Volume Group or `::` for POD |
| 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. |
@@ -142,11 +145,13 @@ 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.