Copilot commented on code in PR #13060:
URL: https://github.com/apache/cloudstack/pull/13060#discussion_r3153273727


##########
plugins/storage/volume/flasharray/src/main/java/org/apache/cloudstack/storage/datastore/adapter/flasharray/FlashArrayAdapter.java:
##########
@@ -661,72 +664,125 @@ private void login() {
             skipTlsValidation = true;
         }
 
+        // Resolve the long-lived API token. Prefer a pre-minted api_token 
(Purity REST 2.x flow);
+        // fall back to legacy username/password auth via Purity REST 1.x for 
backward compatibility.
+        String apiToken = connectionDetails.get(ProviderAdapter.API_TOKEN_KEY);
+        if (apiToken != null && apiToken.isEmpty()) {
+            apiToken = null;
+        }
+        boolean usingLegacyUserPass = apiToken == null;
+        if (usingLegacyUserPass && (username == null || password == null)) {
+            throw new CloudRuntimeException("FlashArray adapter requires 
either " + ProviderAdapter.API_TOKEN_KEY
+                    + " (preferred) or both " + 
ProviderAdapter.API_USERNAME_KEY + " and "
+                    + ProviderAdapter.API_PASSWORD_KEY + " in the connection 
details");
+        }
+
+        CloseableHttpClient client = getClient();
         CloseableHttpResponse response = null;
         try {
-            HttpPost request = new HttpPost(url + "/" + apiLoginVersion + 
"/auth/apitoken");
-            // request.addHeader("Content-Type", "application/json");
-            // request.addHeader("Accept", "application/json");
-            ArrayList<NameValuePair> postParms = new 
ArrayList<NameValuePair>();
-            postParms.add(new BasicNameValuePair("username", username));
-            postParms.add(new BasicNameValuePair("password", password));
-            request.setEntity(new UrlEncodedFormEntity(postParms, "UTF-8"));
-            CloseableHttpClient client = getClient();
-            response = (CloseableHttpResponse) client.execute(request);
+            // Discover the latest supported API version from the array unless 
one was explicitly configured.
+            // GET /api/api_version is unauthenticated and returns 
{"version":["1.0",...,"2.36"]}.
+            if (!apiVersionExplicit) {
+                HttpGet vReq = new HttpGet(url + "/api_version");
+                CloseableHttpResponse vResp = null;
+                try {
+                    vResp = (CloseableHttpResponse) client.execute(vReq);
+                    if (vResp.getStatusLine().getStatusCode() == 200) {
+                        JsonNode root = 
mapper.readTree(vResp.getEntity().getContent());
+                        JsonNode versions = root.get("version");
+                        if (versions != null && versions.isArray() && 
versions.size() > 0) {
+                            apiVersion = versions.get(versions.size() - 
1).asText();
+                        }
+                    } else {
+                        logger.warn("Unexpected HTTP " + 
vResp.getStatusLine().getStatusCode()
+                                + " from FlashArray [" + url + "] 
/api_version, falling back to default "
+                                + API_VERSION_DEFAULT);
+                    }
+                } catch (Exception e) {
+                    logger.warn("Failed to discover Purity REST API version 
from " + url
+                            + "/api_version, falling back to default " + 
API_VERSION_DEFAULT, e);
+                } finally {
+                    if (vResp != null) {
+                        try {
+                            vResp.close();
+                        } catch (IOException e) {
+                            logger.debug("Error closing /api/api_version 
response from FlashArray [" + url + "]", e);
+                        }
+                    }
+                }
+            }
 
-            int statusCode = response.getStatusLine().getStatusCode();
-            FlashArrayApiToken apitoken = null;
-            if (statusCode == 200 | statusCode == 201) {
-                apitoken = mapper.readValue(response.getEntity().getContent(), 
FlashArrayApiToken.class);
-                if (apitoken == null) {
+            if (usingLegacyUserPass) {
+                logger.warn("FlashArray adapter at [" + url + "] is using 
deprecated username/password "
+                        + "login against Purity REST 1.x. Replace with a 
pre-minted "
+                        + ProviderAdapter.API_TOKEN_KEY + " detail; the 
username/password code path will be "
+                        + "removed in a future release.");

Review Comment:
   The deprecation warning for legacy username/password auth is emitted on 
every login/refresh. With the default key TTL (~14 minutes), this can spam logs 
for each configured FlashArray pool. Consider rate-limiting (e.g., log once per 
adapter instance/endpoint) or downgrading subsequent messages to DEBUG after 
the first WARN.



##########
plugins/storage/volume/flasharray/src/main/java/org/apache/cloudstack/storage/datastore/adapter/flasharray/FlashArrayAdapter.java:
##########
@@ -661,72 +664,125 @@ private void login() {
             skipTlsValidation = true;
         }
 
+        // Resolve the long-lived API token. Prefer a pre-minted api_token 
(Purity REST 2.x flow);
+        // fall back to legacy username/password auth via Purity REST 1.x for 
backward compatibility.
+        String apiToken = connectionDetails.get(ProviderAdapter.API_TOKEN_KEY);
+        if (apiToken != null && apiToken.isEmpty()) {
+            apiToken = null;
+        }
+        boolean usingLegacyUserPass = apiToken == null;
+        if (usingLegacyUserPass && (username == null || password == null)) {
+            throw new CloudRuntimeException("FlashArray adapter requires 
either " + ProviderAdapter.API_TOKEN_KEY
+                    + " (preferred) or both " + 
ProviderAdapter.API_USERNAME_KEY + " and "
+                    + ProviderAdapter.API_PASSWORD_KEY + " in the connection 
details");
+        }
+
+        CloseableHttpClient client = getClient();
         CloseableHttpResponse response = null;
         try {
-            HttpPost request = new HttpPost(url + "/" + apiLoginVersion + 
"/auth/apitoken");
-            // request.addHeader("Content-Type", "application/json");
-            // request.addHeader("Accept", "application/json");
-            ArrayList<NameValuePair> postParms = new 
ArrayList<NameValuePair>();
-            postParms.add(new BasicNameValuePair("username", username));
-            postParms.add(new BasicNameValuePair("password", password));
-            request.setEntity(new UrlEncodedFormEntity(postParms, "UTF-8"));
-            CloseableHttpClient client = getClient();
-            response = (CloseableHttpResponse) client.execute(request);
+            // Discover the latest supported API version from the array unless 
one was explicitly configured.
+            // GET /api/api_version is unauthenticated and returns 
{"version":["1.0",...,"2.36"]}.
+            if (!apiVersionExplicit) {
+                HttpGet vReq = new HttpGet(url + "/api_version");
+                CloseableHttpResponse vResp = null;
+                try {
+                    vResp = (CloseableHttpResponse) client.execute(vReq);
+                    if (vResp.getStatusLine().getStatusCode() == 200) {
+                        JsonNode root = 
mapper.readTree(vResp.getEntity().getContent());
+                        JsonNode versions = root.get("version");
+                        if (versions != null && versions.isArray() && 
versions.size() > 0) {
+                            apiVersion = versions.get(versions.size() - 
1).asText();
+                        }
+                    } else {
+                        logger.warn("Unexpected HTTP " + 
vResp.getStatusLine().getStatusCode()
+                                + " from FlashArray [" + url + "] 
/api_version, falling back to default "
+                                + API_VERSION_DEFAULT);
+                    }
+                } catch (Exception e) {
+                    logger.warn("Failed to discover Purity REST API version 
from " + url
+                            + "/api_version, falling back to default " + 
API_VERSION_DEFAULT, e);
+                } finally {
+                    if (vResp != null) {
+                        try {
+                            vResp.close();
+                        } catch (IOException e) {
+                            logger.debug("Error closing /api/api_version 
response from FlashArray [" + url + "]", e);

Review Comment:
   The request is sent to `.../api_version` (relative to `url`), but the debug 
message says "Error closing /api/api_version response...". Align the log text 
with the actual endpoint/path to avoid confusion during troubleshooting.
   ```suggestion
                               logger.debug("Error closing /api_version 
response from FlashArray [" + url + "]", e);
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to