CSTACKEX-127: Primary storage-pool is getting created even if desired data LIFs are not reachable#76
CSTACKEX-127: Primary storage-pool is getting created even if desired data LIFs are not reachable#76sandeeplocharla wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the ONTAP primary storage workflow to select a reachable/usable data LIF based on operational status and node affinity (aggregate home node vs. failover vs. cross-node fallback), and surfaces warnings via pool details/alerts when selection is degraded.
Changes:
- Adds node-aware, status-aware LIF selection in
StorageStrategy.getNetworkInterface()and returns both the chosen LIF IP + an optional warning. - Captures the chosen aggregate’s node during volume creation to bias LIF selection toward optimal locality.
- Updates lifecycle + tests to handle the new
(lifIp, warning)result and emits storage alerts when degraded selection occurs.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/service/StorageStrategyTest.java | Expands unit tests for LIF status and node-affinity tiers; avoids mocking issues on newer JDKs. |
| plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/lifecycle/OntapPrimaryDatastoreLifecycleTest.java | Updates mocks for new Pair<String,String> LIF return type and scope behavior. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/OntapStorageUtils.java | Adds helper to send storage alerts for degraded LIF selection. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/OntapStorageConstants.java | Adds constants for aggregate node/space fields and LIF state/location/warning keys. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java | Implements tiered LIF selection and tracks chosenAggregateNode from aggregate selection. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/lifecycle/OntapPrimaryDatastoreLifecycle.java | Persists selected LIF + warning and emits an alert during pool initialization. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/IpInterface.java | Extends model with state, enabled, and location to support selection logic. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/Aggregate.java | Extends model with node and setters needed for reading/constructing detailed aggregate info. |
| plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/client/AggregateFeignClient.java | Adds @QueryMap to request specific aggregate fields (node/space/state). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Pair<String, String> lifResult; | ||
| try { | ||
| lifResult = storageStrategy.getNetworkInterface(); | ||
| } catch (Exception e) { | ||
| logger.error("Exception occurred while retrieving network interface for pool: " + storagePoolName, e); | ||
| throw new CloudRuntimeException("Failed to retrieve Data LIF from ONTAP: " + e.getMessage(), e); | ||
| } |
There was a problem hiding this comment.
this comment has value, lets fix this
| if (aggrChosen.getNode() != null) { | ||
| this.chosenAggregateNode = aggrChosen.getNode().getName(); | ||
| } |
| for (IpInterface iface : response.getRecords()) { | ||
| if (!Boolean.TRUE.equals(iface.getEnabled()) || !OntapStorageConstants.LIF_STATE_UP.equals(iface.getState())) { | ||
| continue; | ||
| } | ||
| if (!isIPv4Address(iface.getIp().getAddress())) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
address this comment as well
| } catch (Exception e) { | ||
| logger.error("Exception while retrieving network interfaces: ", e); | ||
| throw new CloudRuntimeException("Failed to retrieve network interfaces: " + e.getMessage()); | ||
| } |
|
This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch. |
| Pair<String, String> lifResult; | ||
| try { | ||
| lifResult = storageStrategy.getNetworkInterface(); | ||
| } catch (Exception e) { | ||
| logger.error("Exception occurred while retrieving network interface for pool: " + storagePoolName, e); | ||
| throw new CloudRuntimeException("Failed to retrieve Data LIF from ONTAP: " + e.getMessage(), e); | ||
| } |
There was a problem hiding this comment.
this comment has value, lets fix this
| continue; | ||
| } | ||
|
|
||
| if (aggrResp.getSpace() == null || aggrResp.getAvailableBlockStorageSpace() == null) { |
There was a problem hiding this comment.
you are not fetcing availableblockStorageSpace now? would this work?
| OntapStorageConstants.IP_ADDRESS + OntapStorageConstants.COMMA | ||
| + OntapStorageConstants.STATE + OntapStorageConstants.COMMA | ||
| + OntapStorageConstants.LIF_ENABLED + OntapStorageConstants.COMMA | ||
| + OntapStorageConstants.LIF_LOCATION_HOME_NODE + OntapStorageConstants.COMMA |
There was a problem hiding this comment.
what is the diff between home_node and node values?
| for (IpInterface iface : response.getRecords()) { | ||
| if (!Boolean.TRUE.equals(iface.getEnabled()) || !OntapStorageConstants.LIF_STATE_UP.equals(iface.getState())) { | ||
| continue; | ||
| } | ||
| if (!isIPv4Address(iface.getIp().getAddress())) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
address this comment as well
| if (!isIPv4Address(iface.getIp().getAddress())) { | ||
| continue; | ||
| } | ||
| if (chosenAggregateNode != null) { |
There was a problem hiding this comment.
add code comment for the behaviour, you are trying to implement here
Choosing IpInterface based on its status and affinity to the chosen aggregate
Description
This PR...
Types of changes
Feature/Enhancement Scale or Bug Severity
Feature/Enhancement Scale
Bug Severity
Screenshots (if appropriate):
How Has This Been Tested?
Note: The following images have been captured for NFS3, the same would be the case for iSCSI.
Clearly, by the virtue of free space available, the plugin would choose
sti246_vsim_ocvs040d_aggr1by default.Scenario-1 [pool_P1]: Happy path; No LIFs were down.



The first best available LIF with current node and home node matching with the chosen node has been picked.
Scenario-2 [pool_P2_1]: LIFs on




040dnode were down; with one LIF whose current node:040d, while its home node:040cScenario-3 [pool_P3]: None of the




040dnode LIFs are UP. First best available LIF is picked from040c.