flasharray: authenticate via REST 2.x api-token and discover API version#13060
flasharray: authenticate via REST 2.x api-token and discover API version#13060genegr wants to merge 2 commits intoapache:mainfrom
Conversation
The FlashArray adapter previously always made an initial call to the deprecated Purity REST 1.x session endpoint using a username and password to obtain a long-lived api_token, then exchanged that token for the REST 2.x x-auth-token session key. Purity 1.x is being removed from the array, so this path has an expiration date, and storing the username and password as pool details is not what the Purity documentation recommends. Accept a pre-minted api_token in the pool details (ProviderAdapter.API_TOKEN_KEY, already reserved in the base interface) and go straight to the REST 2.x /login endpoint. The api_token is long-lived and is created on the array via the Purity GUI (Users -> API Tokens) or CLI (pureadmin create --api-token). If api_token is not set, fall back to the legacy username/password flow and emit a deprecation warning so existing deployments keep working during the transition. The fallback path will be removed in a later release. While here, resolve the API version dynamically by calling the unauthenticated GET /api/api_version endpoint the first time a login happens on a pool, unless the operator pinned a specific version via API_VERSION. This makes the adapter pick up newer Purity releases automatically instead of being stuck on the hard-coded 2.23 default. Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
|
@blueorangutan package |
|
@winterhazel a [SL] Jenkins job has been kicked to build packages. It will be bundled with no SystemVM templates. I'll keep you posted as I make progress. |
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 17577 |
|
|
||
| apiVersion = connectionDetails.get(FlashArrayAdapter.API_VERSION); | ||
| boolean apiVersionExplicit = apiVersion != null; | ||
| if (apiVersion == null) { |
There was a problem hiding this comment.
| if (apiVersion == null) { | |
| if (!apiVersionExplicit) { |
| if (apiVersion == null) { | ||
| apiVersion = queryParms.get(FlashArrayAdapter.API_VERSION); | ||
| apiVersionExplicit = apiVersion != null; | ||
| if (apiVersion == null) { |
There was a problem hiding this comment.
| if (apiVersion == null) { | |
| if (!apiVersionExplicit) { |
There was a problem hiding this comment.
Pull request overview
Updates the FlashArray managed-storage adapter to support Pure’s REST 2.x preferred authentication model by accepting a long-lived api_token and dynamically discovering the array’s supported REST API version, while keeping a deprecated username/password fallback for existing deployments.
Changes:
- Add support for authenticating directly to REST 2.x
/loginusing a pre-mintedapi_tokenpool detail, with a deprecation warning when falling back to REST 1.x username/password. - Discover the latest supported Purity REST API version via unauthenticated
GET /api/api_versionwhenapi_versionis not explicitly configured. - Improve some login error handling/messages around token-based auth.
Comments suppressed due to low confidence (1)
plugins/storage/volume/flasharray/src/main/java/org/apache/cloudstack/storage/datastore/adapter/flasharray/FlashArrayAdapter.java:603
apiVersionis re-derived fromconnectionDetails/query params on everylogin(). Whenapi_versionis not explicitly configured, this causes/api_versiondiscovery to run on every session refresh, not just the first login for a pool. Consider caching the discovered version in a field (e.g., only resolve whenapiVersionis null/uninitialized, or add anapiVersionResolvedflag) so subsequent logins reuse it without another discovery call.
apiVersion = connectionDetails.get(FlashArrayAdapter.API_VERSION);
boolean apiVersionExplicit = apiVersion != null;
if (apiVersion == null) {
apiVersion = queryParms.get(FlashArrayAdapter.API_VERSION);
apiVersionExplicit = apiVersion != null;
if (apiVersion == null) {
apiVersion = API_VERSION_DEFAULT;
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| + "/api_version, falling back to default " + API_VERSION_DEFAULT, e); | ||
| } finally { | ||
| if (vResp != null) { | ||
| vResp.close(); |
There was a problem hiding this comment.
vResp.close() is called in the inner finally without handling IOException. A close failure would currently bubble up and fail the entire login even though the version discovery response was already processed. Wrap the close in its own try/catch and log at debug (similar to the outer response close) to avoid spurious login failures.
| vResp.close(); | |
| try { | |
| vResp.close(); | |
| } catch (IOException e) { | |
| logger.debug("Unable to close /api_version response", e); | |
| } |
| response.close(); | ||
| response = null; |
There was a problem hiding this comment.
response.close() in the legacy username/password branch is not protected by a try/catch. If closing the response throws, login() will fail even after a successful auth/token exchange. Close it in a try/catch (or rely on the existing outer finally) to avoid turning close errors into authentication failures.
| response.close(); | |
| response = null; | |
| try { | |
| response.close(); | |
| } catch (IOException e) { | |
| logger.warn("Failed to close legacy authentication response from FlashArray [" + url + "]", e); | |
| } finally { | |
| response = null; | |
| } |
Codecov Report✅ All modified and coverable lines are covered by tests.
Additional details and impacted files@@ Coverage Diff @@
## main #13060 +/- ##
=============================================
- Coverage 18.01% 3.52% -14.50%
=============================================
Files 6029 464 -5565
Lines 542160 40137 -502023
Branches 66451 7555 -58896
=============================================
- Hits 97682 1415 -96267
+ Misses 433461 38534 -394927
+ Partials 11017 188 -10829
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- Replace nested null-check of apiVersion with !apiVersionExplicit for clarity (sureshanaparti). - Wrap vResp.close() in the api_version-discovery finally with its own try/catch; log any IOException at debug so a failed close does not mask a successful discovery. - Wrap the explicit response.close() after the legacy username/password auth with try/catch for the same reason. Signed-off-by: Eugenio Grosso <eugenio.grosso@gmail.com>
|
Pushed
Smoke-tested locally: login via |
The FlashArray adapter previously always made an initial call to the deprecated Purity REST 1.x session endpoint using a username and password to obtain a long-lived api_token, then exchanged that token for the REST 2.x x-auth-token session key. Purity 1.x is being removed from the array, so this path has an expiration date, and storing the username and password as pool details is not what the Purity documentation recommends.
Accept a pre-minted api_token in the pool details (ProviderAdapter.API_TOKEN_KEY, already reserved in the base interface) and go straight to the REST 2.x /login endpoint. The api_token is long-lived and is created on the array via the Purity GUI (Users -> API Tokens) or CLI (pureadmin create --api-token).
If api_token is not set, fall back to the legacy username/password flow and emit a deprecation warning so existing deployments keep working during the transition. The fallback path will be removed in a later release.
While here, resolve the API version dynamically by calling the unauthenticated GET /api/api_version endpoint the first time a login happens on a pool, unless the operator pinned a specific version via API_VERSION. This makes the adapter pick up newer Purity releases automatically instead of being stuck on the hard-coded 2.23 default.
Description
The FlashArray adapter currently performs two calls to log in: a POST to the deprecated Purity REST 1.x
auth/apitokenendpoint with username/password to obtain a long-livedapi_token, then a POST to/api/<ver>/loginwithapi-token:header to get the sessionx-auth-token. Pure Storage is phasing out REST 1.x, and storing username/password as pool details is not the recommended pattern.This PR:
api_tokenas a pool detail (ProviderAdapter.API_TOKEN_KEY, already reserved in the base interface). When present, the adapter skips the 1.x call entirely and goes straight toPOST /api/<ver>/loginwithapi-token:header.api_tokenis not set, logging a deprecation warning. No existing deployments break; removal of the fallback can happen in a later release.GET /api/api_versionon first login (unless an explicitapi_versionpool detail pins it). Replaces the hardcoded2.23default, so the adapter tracks newer Purity releases automatically.Types of changes
How Has This Been Tested?
Validated on a 4.23-SNAPSHOT lab against a Purity 6.7.7 FlashArray:
details[api_token]=<long-lived-token>to an existing pool and restarted mgmt → adapter resumes without the deprecation warning, capacity polling and volume create continue working./api/1.19/auth/apitoken, still working./api/api_versiondiscovery resolves to2.36on Purity 6.7.7 without manual configuration.How to Get an API Token on the Array
Purity GUI: Settings → Users → <user> → API Tokens → Create API Token. CLI:
ssh pureuser@<array>; pureadmin create --api-token <user>.