mirror of
https://github.com/kolesa-team/pve-purestorage-plugin.git
synced 2026-08-12 21:53:06 -06:00
+117
-76
@@ -226,103 +226,144 @@ sub purestorage_name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
### BLOCK: Local multipath => PVE::Storage::Custom::PureStoragePlugin::sub::s
|
### BLOCK: Local multipath => PVE::Storage::Custom::PureStoragePlugin::sub::s
|
||||||
|
my $PSFA_API = "2.26";
|
||||||
my $psfa_api = "2.26";
|
|
||||||
|
|
||||||
sub purestorage_api_request {
|
sub purestorage_api_request {
|
||||||
my ( $scfg, $action ) = @_;
|
my ( $scfg, $action, $all ) = @_;
|
||||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_api_request\n" if $DEBUG;
|
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_api_request\n" if $DEBUG;
|
||||||
|
|
||||||
my $url = $scfg->{ address } or die "Error :: Pure Storage host address is not defined.\n";
|
$all //= 0;
|
||||||
|
|
||||||
my $type = $action->{ type };
|
my $ua = LWP::UserAgent->new( timeout => 15 );
|
||||||
$url .= '/api/' . $psfa_api . '/' . $type;
|
|
||||||
|
|
||||||
my $params = prepare_api_params( $action->{ params } );
|
|
||||||
$url .= "?$params" if length( $params );
|
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new;
|
|
||||||
$ua->ssl_opts(
|
$ua->ssl_opts(
|
||||||
verify_hostname => 0,
|
verify_hostname => 0,
|
||||||
SSL_verify_mode => 0x00
|
SSL_verify_mode => 0x00
|
||||||
) unless $scfg->{ check_ssl };
|
) unless $scfg->{ check_ssl };
|
||||||
|
|
||||||
my $body = $action->{ body } ? encode_json( $action->{ body } ) : undef;
|
my $type = $action->{ type };
|
||||||
my $headers = HTTP::Headers->new( 'Content-Type' => 'application/json' );
|
my $login = $type eq 'login' ? 1 : 0;
|
||||||
|
|
||||||
my $token_status;
|
my $params = prepare_api_params( $action->{ params } );
|
||||||
if ( $type eq 'login' ) {
|
my $path = $type;
|
||||||
$token_status = 0; # login request
|
$path .= '?' . $params if length( $params );
|
||||||
$headers->header( 'api-token' => $scfg->{ token } );
|
|
||||||
} elsif ( $scfg->{ x_auth_token } ) {
|
|
||||||
$token_status = 1; # have cached token
|
|
||||||
} else {
|
|
||||||
$token_status = 2; # need token
|
|
||||||
}
|
|
||||||
|
|
||||||
my $success;
|
my $method = $action->{ method };
|
||||||
my $response;
|
|
||||||
while ( 1 ) {
|
my $body = $action->{ body };
|
||||||
if ( $token_status > 0 ) {
|
|
||||||
if ( $token_status == 1 ) {
|
my $error;
|
||||||
print "Debug :: Using existing session token\n" if $DEBUG;
|
my $content;
|
||||||
} else {
|
my $url;
|
||||||
print "Debug :: Requesting new session token\n" if $DEBUG;
|
|
||||||
purestorage_api_request( $scfg, { name => 'Authentication', type => 'login', method => 'POST' } );
|
my @urls = split( ',', $scfg->{ address } // '' );
|
||||||
|
my @tokens = split( ',', $scfg->{ token } // '' );
|
||||||
|
|
||||||
|
foreach my $i ( 0, 1 ) {
|
||||||
|
$url = $urls[$i] // '';
|
||||||
|
my $token = $tokens[$i] // '';
|
||||||
|
next if $i && $url eq '' && $token eq '';
|
||||||
|
|
||||||
|
my $cf = $url eq '' ? 'address' : $token eq '' ? 'token' : '';
|
||||||
|
die "Error :: Pure Storage \"$cf\" parameter" . ( $i == 0 ? '' : ' for second array' ) . " is not defined.\n" unless $cf eq '';
|
||||||
|
|
||||||
|
my $config = {
|
||||||
|
ua => $ua,
|
||||||
|
url => $url,
|
||||||
|
token => $token,
|
||||||
|
auth_token => $scfg->{ '_auth_token' . $i },
|
||||||
|
request_id => $scfg->{ '_request_id' . $i }
|
||||||
|
};
|
||||||
|
|
||||||
|
( $error, $content ) = purestorage_api_request1( $config, $path, $method, $login, $body );
|
||||||
|
if ( $error == -1 ) {
|
||||||
|
$scfg->{ '_auth_token' . $i } = $config->{ auth_token };
|
||||||
|
$scfg->{ '_request_id' . $i } = $config->{ request_id };
|
||||||
|
} elsif ( $error == 1 ) {
|
||||||
|
my $ignore = $action->{ ignore };
|
||||||
|
if ( defined( $ignore ) ) {
|
||||||
|
$ignore = [$ignore] if ref( $ignore ) eq '';
|
||||||
|
my $first = $content->{ errors }->[0]->{ message };
|
||||||
|
$error = 0 if grep { $_ eq $first } @$ignore;
|
||||||
}
|
}
|
||||||
$headers->header( 'x-auth-token' => $scfg->{ x_auth_token } );
|
|
||||||
}
|
|
||||||
$headers->header( 'X-Request-ID' => $scfg->{ x_request_id } ) if $scfg->{ x_request_id };
|
|
||||||
|
|
||||||
my $request = HTTP::Request->new( $action->{ method }, $url, $headers, $body );
|
|
||||||
$response = $ua->request( $request );
|
|
||||||
|
|
||||||
$success = $response->is_success;
|
|
||||||
if ( !$success && $token_status == 1 && $response->code == 401 ) {
|
|
||||||
print "Debug :: Session token expired\n";
|
|
||||||
$token_status = 2;
|
|
||||||
next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
last;
|
last if $error == 1 || $error <= 0 && !$all;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $content_type = $response->header( "Content-Type" );
|
if ( $error > 0 ) {
|
||||||
my $content =
|
my $message = $error == 3 ? 'Authentication' : $action->{ name } || "Action '$type' (method '$method')";
|
||||||
defined $content_type && $content_type =~ /application\/json/ && $response->content ne ''
|
$message = substr( $message, 0, 1 ) eq uc( substr( $message, 0, 1 ) ) ? $message . ' failed' : 'Failed to ' . $message;
|
||||||
? decode_json( $response->content )
|
$message = 'PureStorage API :: ' . $message if $error == 1;
|
||||||
: $response->decoded_content;
|
die "Error :: $message.\n" . "=> Trace:\n" . "==> address: " . $url . "\n" . ( $content ? "==> Message: " . Dumper( $content ) : '' );
|
||||||
|
|
||||||
$content = {} if $content eq '';
|
|
||||||
|
|
||||||
if ( $success ) {
|
|
||||||
if ( $token_status == 0 ) {
|
|
||||||
$headers = $response->headers;
|
|
||||||
$scfg->{ x_auth_token } = $headers->header( 'x-auth-token' ) or die "Error :: Header 'x-auth-token' is missing.\n";
|
|
||||||
$scfg->{ x_request_id } = $headers->header( 'x-request-id' );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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 = $action->{ name } || "Action '$type' (method '" . $action->{ method } . "')";
|
|
||||||
$message = substr( $message, 0, 1 ) eq uc( substr( $message, 0, 1 ) ) ? $message . ' failed' : 'Failed to ' . $message;
|
|
||||||
die "Error :: PureStorage API :: $message.\n"
|
|
||||||
. "=> Trace:\n"
|
|
||||||
. "==> Code: "
|
|
||||||
. $response->code . "\n"
|
|
||||||
. ( $content ? "==> Message: " . Dumper( $content ) : '' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub purestorage_api_request1 {
|
||||||
|
my ( $config, $path, $method, $login, $body ) = @_;
|
||||||
|
|
||||||
|
my $headers = HTTP::Headers->new( 'Content-Type' => 'application/json' );
|
||||||
|
|
||||||
|
my $token_state;
|
||||||
|
if ( $login ) {
|
||||||
|
$token_state = 0; # login request
|
||||||
|
$headers->header( 'api-token' => $config->{ token } );
|
||||||
|
} elsif ( $config->{ auth_token } ) {
|
||||||
|
$token_state = 2; # have cached token
|
||||||
|
} else {
|
||||||
|
$token_state = 1; # need token
|
||||||
|
}
|
||||||
|
|
||||||
|
my $error;
|
||||||
|
my $response;
|
||||||
|
my $content;
|
||||||
|
while ( 1 ) {
|
||||||
|
if ( $token_state > 0 ) {
|
||||||
|
if ( $token_state == 1 ) {
|
||||||
|
print "Debug :: Requesting new session token\n" if $DEBUG;
|
||||||
|
( $error, $content ) = purestorage_api_request1( $config, 'login', 'POST', 1 );
|
||||||
|
return ( $error, $content ) if $error > 0;
|
||||||
|
} else {
|
||||||
|
print "Debug :: Using existing session token\n" if $DEBUG;
|
||||||
|
}
|
||||||
|
$headers->header( 'x-auth-token' => $config->{ auth_token } );
|
||||||
|
}
|
||||||
|
$headers->header( 'X-Request-ID' => $config->{ request_id } ) if $config->{ request_id };
|
||||||
|
|
||||||
|
my $request = HTTP::Request->new( $method, $config->{ url } . '/api/' . $PSFA_API . '/' . $path, $headers, length( $body ) ? encode_json( $body ) : undef );
|
||||||
|
$response = $config->{ ua }->request( $request );
|
||||||
|
|
||||||
|
$error = $response->is_success ? 0 : 1;
|
||||||
|
if ( $error && $token_state == 2 && $response->code == 401 ) {
|
||||||
|
print "Debug :: Session token expired\n";
|
||||||
|
$token_state = 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers = $response->headers;
|
||||||
|
if ( $error == 0 ) {
|
||||||
|
if ( $token_state == 0 ) {
|
||||||
|
$config->{ auth_token } = $headers->header( 'x-auth-token' ) or die "Error :: PureStorage API :: Header 'x-auth-token' is missing.\n";
|
||||||
|
$config->{ request_id } = $headers->header( 'x-request-id' );
|
||||||
|
}
|
||||||
|
$error = -1 if $token_state < 2; # auth_token was updated
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = $response->decoded_content;
|
||||||
|
my $content_type = $headers->header( 'Content-Type' ) // '';
|
||||||
|
if ( $content_type =~ /application\/json/ ) {
|
||||||
|
$content = decode_json( $content );
|
||||||
|
} else {
|
||||||
|
$error = $login ? 3 : 2 if $error == 1; # non-API error (connectivity, etc.)
|
||||||
|
$content = { response => $content };
|
||||||
|
}
|
||||||
|
|
||||||
|
return ( $error, $content );
|
||||||
|
}
|
||||||
|
|
||||||
sub purestorage_list_volumes {
|
sub purestorage_list_volumes {
|
||||||
my ( $class, $scfg, $vmid, $storeid, $destroyed ) = @_;
|
my ( $class, $scfg, $vmid, $storeid, $destroyed ) = @_;
|
||||||
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_list_volumes\n" if $DEBUG;
|
print "Debug :: PVE::Storage::Custom::PureStoragePlugin::sub::purestorage_list_volumes\n" if $DEBUG;
|
||||||
@@ -476,7 +517,7 @@ sub purestorage_volume_connection {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
my $response = purestorage_api_request( $scfg, $action );
|
my $response = purestorage_api_request( $scfg, $action, 1 );
|
||||||
|
|
||||||
my $message = ( $response->{ errors } ? 'already ' : '' ) . ( $mode ? 'connected to' : 'disconnected from' );
|
my $message = ( $response->{ errors } ? 'already ' : '' ) . ( $mode ? 'connected to' : 'disconnected from' );
|
||||||
print "Info :: Volume \"$volname\" is $message host \"$hname\".\n";
|
print "Info :: Volume \"$volname\" is $message host \"$hname\".\n";
|
||||||
|
|||||||
Reference in New Issue
Block a user