mirror of
https://github.com/kolesa-team/pve-purestorage-plugin.git
synced 2026-08-12 21:53:06 -06:00
+264
-287
@@ -124,129 +124,155 @@ sub exec_command {
|
||||
}
|
||||
}
|
||||
|
||||
sub prepare_api_params {
|
||||
my ( $parms ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::prepare_api_params\n" if $DEBUG;
|
||||
|
||||
return $parms unless ref( $parms ) eq 'HASH';
|
||||
|
||||
my @temp;
|
||||
my $ref;
|
||||
my @ands;
|
||||
my $or;
|
||||
while ( my ( $key, $value ) = each( %$parms ) ) {
|
||||
$ref = ref $value;
|
||||
if ( $ref eq 'HASH' ) {
|
||||
@temp = ();
|
||||
while ( my ( $fname, $fvalue ) = each( %$value ) ) {
|
||||
$ref = ref $fvalue;
|
||||
if ( $ref eq '' ) {
|
||||
$fvalue = [ split( ',', $fvalue ) ];
|
||||
} else {
|
||||
die "Error :: Unsupported condition type: $ref" if $ref ne 'ARRAY';
|
||||
}
|
||||
$or = $#$fvalue > 0;
|
||||
$fvalue = join( ' or ', map { "$fname='$_'" } @$fvalue );
|
||||
$fvalue = '(' . $fvalue . ')' if $or;
|
||||
push @temp, $fvalue;
|
||||
}
|
||||
$value = join( ' and ', @temp );
|
||||
} else {
|
||||
$value = join( ',', @$value ) if $ref eq 'ARRAY';
|
||||
}
|
||||
push @ands, uri_escape( $key ) . '=' . uri_escape( $value );
|
||||
}
|
||||
|
||||
return join( '&', @ands );
|
||||
}
|
||||
|
||||
sub fix_snap_name {
|
||||
my ( $snap_name ) = @_;
|
||||
|
||||
return 'snap-' . $snap_name;
|
||||
}
|
||||
|
||||
### BLOCK: Local multipath => PVE::Storage::Custom::PureStoragePlugin::sub::s
|
||||
|
||||
sub purestorage_request {
|
||||
my ( $class, $scfg, $type, $method, $params, $body ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_request\n" if $DEBUG;
|
||||
my $psfa_api = "2.26";
|
||||
|
||||
my $api = "2.26";
|
||||
my $url = $scfg->{ address };
|
||||
my $check_ssl = $scfg->{ check_ssl } ? 1 : 0;
|
||||
sub purestorage_api_request {
|
||||
my ( $scfg, $action ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_api_request\n" if $DEBUG;
|
||||
|
||||
$url .= "/api/$api/$type";
|
||||
my $type = $action->{ type };
|
||||
my $url = $scfg->{ address } . '/api/' . $psfa_api . '/' . $type;
|
||||
|
||||
my $params = prepare_api_params( $action->{ params } );
|
||||
$url .= "?$params" if $params;
|
||||
|
||||
my $token =
|
||||
$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"
|
||||
);
|
||||
my $headers = HTTP::Headers->new( 'Content-Type' => 'application/json' );
|
||||
|
||||
if ( $scfg->{ x_request_id } ) {
|
||||
$headers->header( "X-Request-ID" => $scfg->{ x_request_id } );
|
||||
my $login = $type eq 'login';
|
||||
if ( $login ) {
|
||||
$headers->header( 'api-token' => $scfg->{ token } );
|
||||
} else {
|
||||
if ( $scfg->{ x_auth_token } ) {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_auth_token::cached\n" if $DEBUG;
|
||||
} else {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_auth_token\n" if $DEBUG;
|
||||
purestorage_api_request( $scfg, { name => 'Authentication', type => 'login', method => 'POST' } );
|
||||
}
|
||||
$headers->header( 'x-auth-token' => $scfg->{ x_auth_token } );
|
||||
}
|
||||
$headers->header( 'X-Request-ID' => $scfg->{ x_request_id } ) if $scfg->{ x_request_id };
|
||||
|
||||
my $ua = LWP::UserAgent->new;
|
||||
$ua->ssl_opts(
|
||||
verify_hostname => 0,
|
||||
SSL_verify_mode => 0x00
|
||||
) if !$check_ssl;
|
||||
my $request = HTTP::Request->new( $method, $url, $headers, $body ? encode_json( $body ) : undef );
|
||||
) unless $scfg->{ check_ssl };
|
||||
|
||||
my $request = HTTP::Request->new( $action->{ method }, $url, $headers, $action->{ body } ? encode_json( $action->{ 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 ""
|
||||
defined $content_type && $content_type =~ /application\/json/ && $response->content ne ''
|
||||
? decode_json( $response->content )
|
||||
: $response->decoded_content;
|
||||
|
||||
return {
|
||||
content => $content,
|
||||
headers => $response->headers,
|
||||
error => $response->is_success ? undef : $response->code,
|
||||
};
|
||||
}
|
||||
$content = {} if $content eq '';
|
||||
|
||||
sub purestorage_get_auth_token {
|
||||
my ( $class, $scfg ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_auth_token\n" if $DEBUG;
|
||||
|
||||
if ( !$scfg->{ x_auth_token } ) {
|
||||
my $response = $class->purestorage_request( $scfg, "login", "POST" );
|
||||
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Authentication failed.\n" . "=> Trace:\n" . "==> Code: " . $response->{ error } . "\n" . $response->{ content }
|
||||
? "==> Message: " . Dumper( $response->{ content } )
|
||||
: "";
|
||||
}
|
||||
|
||||
$scfg->{ x_auth_token } = $response->{ headers }->header( "x-auth-token" ) || die "Header 'x-auth-token' missing.";
|
||||
if ( $response->{ headers }->header( "x-request-id" ) ) {
|
||||
$scfg->{ x_request_id } = $response->{ headers }->header( "x-request-id" );
|
||||
my $action_name = $action->{ name } || "Action '$type' (method '" . $action->{ method } . "')";
|
||||
my $success = $response->is_success;
|
||||
if ( $success ) {
|
||||
if ( $login ) {
|
||||
$headers = $response->headers;
|
||||
$scfg->{ x_auth_token } = $headers->header( 'x-auth-token' ) || die "Error :: Header 'x-auth-token' is missing.";
|
||||
$scfg->{ x_request_id } = $headers->header( 'x-request-id' );
|
||||
}
|
||||
} else {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_get_auth_token::cached\n" if $DEBUG;
|
||||
my $ignore_errors = $action->{ ignore };
|
||||
if ( defined( $ignore_errors ) ) {
|
||||
$ignore_errors = [$ignore_errors] if ref( $ignore_errors ) eq '';
|
||||
my $first = $content->{ errors }->[0]->{ message };
|
||||
$success = 1 if grep { $_ eq $first } @$ignore_errors;
|
||||
}
|
||||
|
||||
if ( !$success ) {
|
||||
my $message = substr( $action_name, 0, 1 );
|
||||
$message = $message eq uc( $message ) ? $action_name . ' failed' : 'Failed to ' . $action_name;
|
||||
die "Error :: PureStorage API :: $message.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->code . "\n"
|
||||
. ( $content ? "==> Message: " . Dumper( $content ) : '' );
|
||||
}
|
||||
}
|
||||
return $scfg->{ x_auth_token };
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
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 } ||= {};
|
||||
$scfg->{ cache }->{ volume_info } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ last_update } ||= 0;
|
||||
$scfg->{ cache } ||= {};
|
||||
$scfg->{ cache }->{ volume_info } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ $vgname } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ $vgname }->{ $volname } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ $vgname }->{ $volname }->{ last_update } ||= 0;
|
||||
|
||||
my $current_time = gettimeofday();
|
||||
|
||||
if ( $current_time - $scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ last_update } >= 60 ) {
|
||||
my $volume = $scfg->{ cache }->{ volume_info }->{ $vgname }->{ $volname };
|
||||
|
||||
my $filter = "name='$vgname/$volname'";
|
||||
my $response = $class->purestorage_request( $scfg, "volumes", "GET", "filter=" . uri_escape( $filter ) );
|
||||
if ( $current_time - $volume->{ last_update } >= 60 ) {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_volume_info\n" if $DEBUG;
|
||||
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Get volume \"$vgname/$volname\" info failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
$volume = $class->purestorage_get_existing_volume_info( $scfg, $volname );
|
||||
die "Error :: PureStorage API :: No volume data found for \"$vgname/$volname\".\n" unless $volume;
|
||||
|
||||
my $volumes = $response->{ content }->{ items };
|
||||
unless ( ref( $volumes ) eq 'ARRAY' && @$volumes ) {
|
||||
die "Error :: PureStorage API :: No volume data found for \"$vgname/$volname\".\n";
|
||||
}
|
||||
|
||||
my $volume = $volumes->[0];
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" } = {
|
||||
size => $volume->{ provisioned } || 0,
|
||||
used => $volume->{ space }->{ total_used } || 0,
|
||||
last_update => $current_time,
|
||||
};
|
||||
|
||||
print "Debug :: curtime: " . $current_time . " " . $scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ last_update } . "\n";
|
||||
print "Debug :: Provisioned: "
|
||||
. $scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ size }
|
||||
. ", Used: "
|
||||
. $scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ used } . "\n"
|
||||
if $DEBUG;
|
||||
|
||||
print "Debug :: Provisioned: " . $volume->{ provisioned } . ", Used: " . $volume->{ space }->{ total_used } . "\n"
|
||||
if $DEBUG;
|
||||
$volume->{ last_update } = $current_time;
|
||||
$scfg->{ cache }->{ volume_info }->{ $vgname }->{ $volname } = $volume;
|
||||
} else {
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_volume_info::cached\n" if $DEBUG;
|
||||
}
|
||||
|
||||
return (
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ size },
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" }->{ used }
|
||||
);
|
||||
print "Debug :: curtime: " . $current_time . " " . $current_time . "\n" if $DEBUG;
|
||||
print "Debug :: Provisioned: " . $volume->{ size } . ", Used: " . $volume->{ used } . "\n" if $DEBUG;
|
||||
|
||||
return ( $volume->{ size }, $volume->{ used } );
|
||||
}
|
||||
|
||||
sub purestorage_list_volumes {
|
||||
@@ -262,23 +288,17 @@ sub purestorage_get_volumes {
|
||||
my ( $class, $scfg, $names, $storeid, $destroyed ) = @_;
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
|
||||
my @names_list = map { "name='$vgname/$_'" } split( ',', $names );
|
||||
my $filter = { name => [ map { "$vgname/$_" } split( ',', $names ) ] };
|
||||
$filter->{ destroyed } = $destroyed ? 'true' : 'false' if defined $destroyed;
|
||||
|
||||
my $filter = join( ' or ', @names_list );
|
||||
my $action = {
|
||||
name => $names =~ m/[*,]/ ? 'list volumes' : 'get volume information',
|
||||
type => 'volumes',
|
||||
method => 'GET',
|
||||
params => { filter => $filter }
|
||||
};
|
||||
|
||||
if ( defined( $destroyed ) ) {
|
||||
$filter = '(' . $filter . ')' if $#names_list > 0;
|
||||
$filter .= " and destroyed='" . ( $destroyed ? "true" : "false" ) . "'";
|
||||
}
|
||||
|
||||
my $response = $class->purestorage_request( $scfg, "volumes", "GET", "filter=" . uri_escape( $filter ) );
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: List volumes status failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
my $response = purestorage_api_request( $scfg, $action );
|
||||
|
||||
my @volumes = map {
|
||||
my $volname = $_->{ name };
|
||||
@@ -292,12 +312,13 @@ sub purestorage_get_volumes {
|
||||
name => $volname,
|
||||
vmid => $volvm,
|
||||
serial => $_->{ serial },
|
||||
size => $_->{ provisioned },
|
||||
size => $_->{ provisioned } || 0,
|
||||
used => $_->{ space }->{ total_used } || 0,
|
||||
ctime => $ctime,
|
||||
volid => $storeid ? "$storeid:$volname" : $volname,
|
||||
format => 'raw'
|
||||
}
|
||||
} @{ $response->{ content }->{ items } };
|
||||
} @{ $response->{ items } };
|
||||
|
||||
return \@volumes;
|
||||
}
|
||||
@@ -379,11 +400,10 @@ sub purestorage_cleanup_diskmap {
|
||||
}
|
||||
|
||||
sub purestorage_volume_connection {
|
||||
my ( $class, $scfg, $volname, $action ) = @_;
|
||||
my ( $class, $scfg, $volname, $mode ) = @_;
|
||||
|
||||
$action //= 'POST';
|
||||
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_volume_connection :: $action\n" if $DEBUG;
|
||||
my $method = $mode ? 'POST' : 'DELETE';
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_volume_connection :: $method\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";
|
||||
@@ -392,32 +412,31 @@ sub purestorage_volume_connection {
|
||||
my $hgsuffix = $scfg->{ hgsuffix } // $default_hgsuffix;
|
||||
$hname .= "-" . $hgsuffix if $hgsuffix ne "";
|
||||
|
||||
my $params = "host_names=$hname&volume_names=$vgname/$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." ) {
|
||||
$message = '' if $action eq 'DELETE';
|
||||
}
|
||||
if ( $message ne '' ) {
|
||||
$Data::Dumper::Indent = 0;
|
||||
die "Error :: PureStorage API :: Failed to modify connection.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
$message = 'was already';
|
||||
my $name;
|
||||
my $ignore;
|
||||
if ( $mode ) {
|
||||
$name = 'create volume connection';
|
||||
$ignore = 'Connection already exists.';
|
||||
} else {
|
||||
$message = 'is';
|
||||
$name = 'delete volume connection';
|
||||
$ignore = [ 'Volume has been destroyed.', 'Connection does not exist.' ];
|
||||
}
|
||||
|
||||
$message .= ' ' . ( $action eq 'DELETE' ? 'removed from' : 'added to' );
|
||||
print "Info :: Volume \"$vgname/$volname\" $message host \"$hname\".\n";
|
||||
my $action = {
|
||||
name => $name,
|
||||
type => 'connections',
|
||||
method => $method,
|
||||
ignore => $ignore,
|
||||
params => {
|
||||
host_names => $hname,
|
||||
volume_names => "$vgname/$volname"
|
||||
}
|
||||
};
|
||||
|
||||
my $response = purestorage_api_request( $scfg, $action );
|
||||
|
||||
my $message = ( $response->{ errors } ? 'already ' : '' ) . ( $mode ? 'connected to' : 'disconnected from' );
|
||||
print "Info :: Volume \"$vgname/$volname\" is $message host \"$hname\".\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -428,20 +447,18 @@ 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 $volparams = { "provisioned" => $size };
|
||||
my $action = {
|
||||
name => 'create volume',
|
||||
type => 'volumes',
|
||||
method => 'POST',
|
||||
params => { names => "$vgname/$volname" },
|
||||
body => { provisioned => $size }
|
||||
};
|
||||
|
||||
my $response = $class->purestorage_request( $scfg, "volumes", "POST", $params, $volparams );
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Create volume failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
my $response = purestorage_api_request( $scfg, $action );
|
||||
|
||||
my $serial = $response->{ content }->{ items }->[0]->{ serial } || die "Error :: Failed to retrieve volume serial";
|
||||
print "Info :: Volume \"$vgname/$volname\" created (serial=$serial).\n";
|
||||
my $serial = $response->{ items }->[0]->{ serial } or die "Error :: Failed to retrieve volume serial";
|
||||
print "Info :: Volume \"$vgname/$volname\" is created (serial=$serial).\n";
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -459,41 +476,32 @@ 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 $params = { names => "$vgname/$volname" };
|
||||
my $action = {
|
||||
name => 'destroy volume',
|
||||
type => 'volumes',
|
||||
method => 'PATCH',
|
||||
ignore => 'Volume has been deleted.',
|
||||
params => $params,
|
||||
body => { destroyed => \1 }
|
||||
};
|
||||
|
||||
my $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $body );
|
||||
if ( $response->{ error } ) {
|
||||
if ( $response->{ content }->{ errors }->[0]->{ message } eq "Volume has been deleted." ) {
|
||||
warn "Warning :: PureStorage API :: Destroy volume failed :: Nothing to remove.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
} else {
|
||||
$Data::Dumper::Indent = 0;
|
||||
die "Error :: PureStorage API :: Destroy volume \"$vgname/$volname\" failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
} else {
|
||||
print "Info :: Volume \"$vgname/$volname\" destroyed.\n";
|
||||
}
|
||||
my $response = purestorage_api_request( $scfg, $action );
|
||||
|
||||
my $message = ( $response->{ errors } ? 'already ' : '' ) . 'destroyed';
|
||||
print "Info :: Volume \"$vgname/$volname\" is $message.\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"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
} else {
|
||||
print "Info :: Volume \"$vgname/$volname\" eradicated.\n";
|
||||
}
|
||||
$action = {
|
||||
name => 'eradicate volume',
|
||||
type => 'volumes',
|
||||
method => 'DELETE',
|
||||
params => $params,
|
||||
};
|
||||
|
||||
purestorage_api_request( $scfg, $action );
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" is eradicated.\n";
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -529,16 +537,15 @@ sub purestorage_resize_volume {
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" } ||= {};
|
||||
$scfg->{ cache }->{ volume_info }->{ "$vgname" }->{ "$volname" } = {};
|
||||
|
||||
my $params = "names=$vgname/$volname";
|
||||
my $volparams = { "provisioned" => $size };
|
||||
my $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $volparams );
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Resize volume failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
my $action = {
|
||||
name => 'resize volume',
|
||||
type => 'volumes',
|
||||
method => 'PATCH',
|
||||
params => { names => "$vgname/$volname" },
|
||||
body => { provisioned => $size }
|
||||
};
|
||||
|
||||
purestorage_api_request( $scfg, $action );
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" resized.\n";
|
||||
|
||||
@@ -577,21 +584,20 @@ 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 $response = $class->purestorage_request( $scfg, "volumes", "PATCH", $params, $volparams );
|
||||
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";
|
||||
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Rename volume failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
my $action = {
|
||||
name => 'rename volume',
|
||||
type => 'volumes',
|
||||
method => 'PATCH',
|
||||
params => { names => "$vgname/$source_volname" },
|
||||
body => { name => "$vgname/$target_volname" }
|
||||
};
|
||||
|
||||
print "Info :: Volume \"$vgname/$source_volname\" renamed to \"$vgname/$target_volname\".\n";
|
||||
purestorage_api_request( $scfg, $action );
|
||||
|
||||
print "Info :: Volume \"$vgname/$source_volname\" is renamed to \"$vgname/$target_volname\".\n";
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -601,22 +607,20 @@ 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 $action = {
|
||||
name => 'create volume snapshot',
|
||||
type => 'volume-snapshots',
|
||||
method => 'POST',
|
||||
params => {
|
||||
source_names => "$vgname/$volname",
|
||||
suffix => fix_snap_name( $snap_name )
|
||||
}
|
||||
};
|
||||
|
||||
$response = $class->purestorage_request( $scfg, "volume-snapshots", "POST", $params );
|
||||
purestorage_api_request( $scfg, $action );
|
||||
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Snapshot volume failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" created.\n";
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" is created.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -625,28 +629,25 @@ 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 = {
|
||||
source => {
|
||||
name => "$vgname/$volname.snap-$snap_name"
|
||||
my $action = {
|
||||
name => 'restore volume snapshot',
|
||||
type => 'volumes',
|
||||
method => 'POST',
|
||||
params => {
|
||||
names => "$vgname/$volname",
|
||||
overwrite => 'true'
|
||||
},
|
||||
body => {
|
||||
source => {
|
||||
name => "$vgname/$volname." . fix_snap_name( $snap_name )
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$response = $class->purestorage_request( $scfg, "volumes", "POST", $params, $body );
|
||||
purestorage_api_request( $scfg, $action );
|
||||
|
||||
if ( $response->{ error } ) {
|
||||
die "Error :: PureStorage API :: Restore volume snapshot failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" restored.\n";
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" is restored.\n";
|
||||
return $volname;
|
||||
}
|
||||
|
||||
@@ -655,52 +656,35 @@ 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;
|
||||
my $params = { names => "$vgname/$volname." . fix_snap_name( $snap_name ) };
|
||||
my $action = {
|
||||
name => 'destroy volume snapshot',
|
||||
type => 'volume-snapshots',
|
||||
method => 'PATCH',
|
||||
ignore =>
|
||||
[ 'Volume snapshot has been destroyed. It can be recovered by purevol recover and eradicated by purevol eradicate.', 'No such volume or snapshot.' ],
|
||||
params => $params,
|
||||
body => { destroyed => \1 }
|
||||
};
|
||||
my $response = purestorage_api_request( $scfg, $action );
|
||||
|
||||
$params = "names=$vgname/$volname.snap-$snap_name";
|
||||
$body = { destroyed => \1 };
|
||||
my $message = ( $response->{ errors } ? 'already ' : '' ) . 'destroyed';
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" is $message.\n";
|
||||
|
||||
$response = $class->purestorage_request( $scfg, "volume-snapshots", "PATCH", $params, $body );
|
||||
#FIXME: Pure FA API states that replication_snapshot is query (not body) parameter
|
||||
$action = {
|
||||
name => 'eradicate volume snapshot',
|
||||
type => 'volume-snapshots',
|
||||
method => 'DELETE',
|
||||
ignore => 'No such volume or snapshot.',
|
||||
params => $params,
|
||||
body => { replication_snapshot => \1 }
|
||||
};
|
||||
$response = purestorage_api_request( $scfg, $action );
|
||||
|
||||
if ( $response->{ error } ) {
|
||||
my @valid_errors =
|
||||
( "Volume snapshot has been destroyed. It can be recovered by purevol recover and eradicated by purevol eradicate.", "No such volume or snapshot." );
|
||||
if ( grep { $_ eq $response->{ content }->{ errors }->[0]->{ message } } @valid_errors ) {
|
||||
warn "Warning :: PureStorage API :: Destroy snapshot failed :: Nothing to destoy.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
} else {
|
||||
$Data::Dumper::Indent = 0;
|
||||
die "Error :: PureStorage API :: Destroy volume snapshot failed.\n"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
}
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" destroyed.\n";
|
||||
|
||||
$params = "names=$vgname/$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"
|
||||
. "=> Trace:\n"
|
||||
. "==> Code: "
|
||||
. $response->{ error } . "\n"
|
||||
. ( $response->{ content } ? "==> Message: " . Dumper( $response->{ content } ) : "" );
|
||||
}
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" eradicated.\n";
|
||||
$message = ( $response->{ errors } ? 'already ' : '' ) . 'eradicated';
|
||||
print "Info :: Volume \"$vgname/$volname\" snapshot \"$snap_name\" is $message.\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -826,13 +810,13 @@ sub status {
|
||||
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" );
|
||||
my $response = purestorage_api_request( $scfg, { name => 'get array space', type => 'arrays/space', method => '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->{ total } = $response->{ items }->[0]->{ capacity };
|
||||
$cache->{ used } = $response->{ items }->[0]->{ space }->{ total_physical };
|
||||
|
||||
# $cache->{ used } = $response->{ content }->{ items }->[0]->{ space }->{ total_used }; # Do not know what is correct
|
||||
# $cache->{ used } = $response->{ items }->[0]->{ space }->{ total_used }; # Do not know what is correct
|
||||
|
||||
$cache->{ last_update } = $current_time;
|
||||
} else {
|
||||
@@ -852,6 +836,8 @@ sub status {
|
||||
sub activate_storage {
|
||||
my ( $class, $storeid, $scfg, $cache ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::activate_storage\n" if $DEBUG;
|
||||
|
||||
#FIXME: Why is this needed?
|
||||
$class->purestorage_cleanup_diskmap();
|
||||
|
||||
return 1;
|
||||
@@ -870,7 +856,7 @@ sub volume_size_info {
|
||||
|
||||
my ( $size, $used ) = $class->purestorage_volume_info( $scfg, $volname );
|
||||
|
||||
return wantarray ? ( $size, "raw", $used, undef ) : $size;
|
||||
return wantarray ? ( $size, 'raw', $used, undef ) : $size;
|
||||
}
|
||||
|
||||
sub map_volume {
|
||||
@@ -967,7 +953,7 @@ sub activate_volume {
|
||||
my ( $class, $storeid, $scfg, $volname, $snapname, $cache ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::activate_volume\n" if $DEBUG;
|
||||
|
||||
$class->purestorage_volume_connection( $scfg, $volname );
|
||||
$class->purestorage_volume_connection( $scfg, $volname, 1 );
|
||||
|
||||
$class->map_volume( $storeid, $scfg, $volname, $snapname );
|
||||
return 1;
|
||||
@@ -981,7 +967,7 @@ sub deactivate_volume {
|
||||
|
||||
$class->unmap_volume( $storeid, $scfg, $volname, $snapname );
|
||||
|
||||
$class->purestorage_volume_connection( $scfg, $volname, 'DELETE' );
|
||||
$class->purestorage_volume_connection( $scfg, $volname, 0 );
|
||||
|
||||
print "Info :: Volume \"$vgname/$volname\" deactivated.\n";
|
||||
|
||||
@@ -1035,11 +1021,8 @@ sub volume_snapshot {
|
||||
my ( $class, $scfg, $storeid, $volname, $snap ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::volume_snapshot\n" if $DEBUG;
|
||||
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
$class->purestorage_snap_volume_create( $scfg, $snap, $volname );
|
||||
|
||||
if ( !$class->purestorage_snap_volume_create( $scfg, $snap, $volname ) ) {
|
||||
die "Error :: Failed to snapshot volume \"$vgname/$volname\".\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1047,11 +1030,8 @@ sub volume_snapshot_rollback {
|
||||
my ( $class, $scfg, $storeid, $volname, $snap ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::volume_snapshot_rollback\n" if $DEBUG;
|
||||
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
$class->purestorage_snap_volume_rollback( $scfg, $snap, $volname );
|
||||
|
||||
if ( !$class->purestorage_snap_volume_rollback( $scfg, $snap, $volname ) ) {
|
||||
die "Error :: Failed to rollback snapshot volume \"$vgname/$volname\".\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1059,11 +1039,8 @@ sub volume_snapshot_delete {
|
||||
my ( $class, $scfg, $storeid, $volname, $snap ) = @_;
|
||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::volume_snapshot_delete\n" if $DEBUG;
|
||||
|
||||
my $vgname = $scfg->{ vgname } || die "Error :: Volume group name is not defined.\n";
|
||||
$class->purestorage_snap_volume_delete( $scfg, $snap, $volname );
|
||||
|
||||
if ( !$class->purestorage_snap_volume_delete( $scfg, $snap, $volname ) ) {
|
||||
die "Error :: Failed to snapshot volume \"$vgname/$volname\".\n";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user