@@ -524,6 +524,91 @@ private String getAccessToken() {
524524 return accessToken ;
525525 }
526526
527+ /**
528+ * Discover the latest supported Purity REST API version by hitting the unauthenticated
529+ * {@code /api/api_version} endpoint (returns {@code {"version":["1.0",...,"2.36"]}}).
530+ * The discovered version is stored on {@link #apiVersion}; on failure the caller-configured
531+ * default remains in place.
532+ */
533+ private void fetchApiVersionFromPurity (CloseableHttpClient client ) {
534+ HttpGet vReq = new HttpGet (url + "/api_version" );
535+ CloseableHttpResponse vResp = null ;
536+ try {
537+ vResp = client .execute (vReq );
538+ if (vResp .getStatusLine ().getStatusCode () == 200 ) {
539+ JsonNode root = mapper .readTree (vResp .getEntity ().getContent ());
540+ JsonNode versions = root .get ("version" );
541+ if (versions != null && versions .isArray () && versions .size () > 0 ) {
542+ apiVersion = versions .get (versions .size () - 1 ).asText ();
543+ }
544+ } else {
545+ logger .warn ("Unexpected HTTP " + vResp .getStatusLine ().getStatusCode ()
546+ + " from FlashArray [" + url + "] /api_version, falling back to default "
547+ + API_VERSION_DEFAULT );
548+ }
549+ } catch (Exception e ) {
550+ logger .warn ("Failed to discover Purity REST API version from " + url
551+ + "/api_version, falling back to default " + API_VERSION_DEFAULT , e );
552+ } finally {
553+ if (vResp != null ) {
554+ try {
555+ vResp .close ();
556+ } catch (IOException e ) {
557+ logger .debug ("Error closing /api_version response from FlashArray [" + url + "]" , e );
558+ }
559+ }
560+ }
561+ }
562+
563+ /**
564+ * Exchange the operator-configured username/password for a long-lived Purity api-token
565+ * via REST 1.x {@code /auth/apitoken}. Emits the once-per-URL deprecation WARN.
566+ * @return the api-token to feed into the REST 2.x /login exchange.
567+ */
568+ private String getApiTokenUsingUserPass (CloseableHttpClient client ) throws IOException {
569+ if (WARNED_LEGACY_URLS .add (url )) {
570+ logger .warn ("FlashArray adapter at [" + url + "] is using deprecated username/password "
571+ + "login against Purity REST 1.x. Replace with a pre-minted "
572+ + ProviderAdapter .API_TOKEN_KEY + " detail; the username/password code path will be "
573+ + "removed in a future release." );
574+ }
575+ HttpPost request = new HttpPost (url + "/" + apiLoginVersion + "/auth/apitoken" );
576+ ArrayList <NameValuePair > postParms = new ArrayList <NameValuePair >();
577+ postParms .add (new BasicNameValuePair ("username" , username ));
578+ postParms .add (new BasicNameValuePair ("password" , password ));
579+ request .setEntity (new UrlEncodedFormEntity (postParms , "UTF-8" ));
580+ CloseableHttpResponse response = null ;
581+ try {
582+ response = client .execute (request );
583+ int statusCode = response .getStatusLine ().getStatusCode ();
584+ if (statusCode == 200 || statusCode == 201 ) {
585+ FlashArrayApiToken legacyToken = mapper .readValue (response .getEntity ().getContent (),
586+ FlashArrayApiToken .class );
587+ if (legacyToken == null || legacyToken .getApiToken () == null ) {
588+ throw new CloudRuntimeException (
589+ "Authentication responded successfully but no api token was returned" );
590+ }
591+ return legacyToken .getApiToken ();
592+ } else if (statusCode == 401 || statusCode == 403 ) {
593+ throw new CloudRuntimeException (
594+ "Authentication or Authorization to FlashArray [" + url + "] with user [" + username
595+ + "] failed, unable to retrieve session token" );
596+ } else {
597+ throw new CloudRuntimeException (
598+ "Unexpected HTTP response code from FlashArray [" + url + "] - [" + statusCode
599+ + "] - " + response .getStatusLine ().getReasonPhrase ());
600+ }
601+ } finally {
602+ if (response != null ) {
603+ try {
604+ response .close ();
605+ } catch (IOException e ) {
606+ logger .debug ("Error closing legacy auth/apitoken response from FlashArray [" + url + "]" , e );
607+ }
608+ }
609+ }
610+ }
611+
527612 private synchronized void refreshSession (boolean force ) {
528613 try {
529614 if (force || keyExpiration < System .currentTimeMillis ()) {
@@ -688,78 +773,17 @@ private void login() {
688773 // Discover the latest supported API version from the array unless one was explicitly configured.
689774 // GET /api/api_version is unauthenticated and returns {"version":["1.0",...,"2.36"]}.
690775 if (!apiVersionExplicit ) {
691- HttpGet vReq = new HttpGet (url + "/api_version" );
692- CloseableHttpResponse vResp = null ;
693- try {
694- vResp = (CloseableHttpResponse ) client .execute (vReq );
695- if (vResp .getStatusLine ().getStatusCode () == 200 ) {
696- JsonNode root = mapper .readTree (vResp .getEntity ().getContent ());
697- JsonNode versions = root .get ("version" );
698- if (versions != null && versions .isArray () && versions .size () > 0 ) {
699- apiVersion = versions .get (versions .size () - 1 ).asText ();
700- }
701- } else {
702- logger .warn ("Unexpected HTTP " + vResp .getStatusLine ().getStatusCode ()
703- + " from FlashArray [" + url + "] /api_version, falling back to default "
704- + API_VERSION_DEFAULT );
705- }
706- } catch (Exception e ) {
707- logger .warn ("Failed to discover Purity REST API version from " + url
708- + "/api_version, falling back to default " + API_VERSION_DEFAULT , e );
709- } finally {
710- if (vResp != null ) {
711- try {
712- vResp .close ();
713- } catch (IOException e ) {
714- logger .debug ("Error closing /api_version response from FlashArray [" + url + "]" , e );
715- }
716- }
717- }
776+ fetchApiVersionFromPurity (client );
718777 }
719778
720779 if (usingLegacyUserPass ) {
721- if (WARNED_LEGACY_URLS .add (url )) {
722- logger .warn ("FlashArray adapter at [" + url + "] is using deprecated username/password "
723- + "login against Purity REST 1.x. Replace with a pre-minted "
724- + ProviderAdapter .API_TOKEN_KEY + " detail; the username/password code path will be "
725- + "removed in a future release." );
726- }
727- HttpPost request = new HttpPost (url + "/" + apiLoginVersion + "/auth/apitoken" );
728- ArrayList <NameValuePair > postParms = new ArrayList <NameValuePair >();
729- postParms .add (new BasicNameValuePair ("username" , username ));
730- postParms .add (new BasicNameValuePair ("password" , password ));
731- request .setEntity (new UrlEncodedFormEntity (postParms , "UTF-8" ));
732- response = (CloseableHttpResponse ) client .execute (request );
733- int statusCode = response .getStatusLine ().getStatusCode ();
734- if (statusCode == 200 || statusCode == 201 ) {
735- FlashArrayApiToken legacyToken = mapper .readValue (response .getEntity ().getContent (),
736- FlashArrayApiToken .class );
737- if (legacyToken == null || legacyToken .getApiToken () == null ) {
738- throw new CloudRuntimeException (
739- "Authentication responded successfully but no api token was returned" );
740- }
741- apiToken = legacyToken .getApiToken ();
742- } else if (statusCode == 401 || statusCode == 403 ) {
743- throw new CloudRuntimeException (
744- "Authentication or Authorization to FlashArray [" + url + "] with user [" + username
745- + "] failed, unable to retrieve session token" );
746- } else {
747- throw new CloudRuntimeException (
748- "Unexpected HTTP response code from FlashArray [" + url + "] - [" + statusCode
749- + "] - " + response .getStatusLine ().getReasonPhrase ());
750- }
751- try {
752- response .close ();
753- } catch (IOException e ) {
754- logger .debug ("Error closing legacy auth/apitoken response from FlashArray [" + url + "]" , e );
755- }
756- response = null ;
780+ apiToken = getApiTokenUsingUserPass (client );
757781 }
758782
759783 // Exchange the long-lived api-token for a short-lived x-auth-token (REST 2.x).
760784 HttpPost request = new HttpPost (url + "/" + apiVersion + "/login" );
761785 request .addHeader ("api-token" , apiToken );
762- response = ( CloseableHttpResponse ) client .execute (request );
786+ response = client .execute (request );
763787 int statusCode = response .getStatusLine ().getStatusCode ();
764788 if (statusCode == 200 || statusCode == 201 ) {
765789 Header [] headers = response .getHeaders ("x-auth-token" );
@@ -957,7 +981,7 @@ private <T> T PATCH(String path, Object input, final TypeReference<T> type) {
957981 request .setEntity (new StringEntity (data ));
958982
959983 CloseableHttpClient client = getClient ();
960- response = ( CloseableHttpResponse ) client .execute (request );
984+ response = client .execute (request );
961985
962986 final int statusCode = response .getStatusLine ().getStatusCode ();
963987 if (statusCode == 200 || statusCode == 201 ) {
@@ -1012,7 +1036,7 @@ private <T> T GET(String path, final TypeReference<T> type) {
10121036 request .addHeader ("X-auth-token" , getAccessToken ());
10131037
10141038 CloseableHttpClient client = getClient ();
1015- response = ( CloseableHttpResponse ) client .execute (request );
1039+ response = client .execute (request );
10161040 final int statusCode = response .getStatusLine ().getStatusCode ();
10171041 if (statusCode == 200 ) {
10181042 try {
@@ -1054,7 +1078,7 @@ private void DELETE(String path) {
10541078 request .addHeader ("X-auth-token" , getAccessToken ());
10551079
10561080 CloseableHttpClient client = getClient ();
1057- response = ( CloseableHttpResponse ) client .execute (request );
1081+ response = client .execute (request );
10581082 final int statusCode = response .getStatusLine ().getStatusCode ();
10591083 if (statusCode == 200 || statusCode == 404 || statusCode == 400 ) {
10601084 // this means the volume was deleted successfully, or doesn't exist (effective
0 commit comments