Skip to content

CSTACKEX-127: Primary storage-pool is getting created even if desired data LIFs are not reachable#76

Open
sandeeplocharla wants to merge 3 commits into
mainfrom
feature/CSTACKEX-127
Open

CSTACKEX-127: Primary storage-pool is getting created even if desired data LIFs are not reachable#76
sandeeplocharla wants to merge 3 commits into
mainfrom
feature/CSTACKEX-127

Conversation

@sandeeplocharla

@sandeeplocharla sandeeplocharla commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Choosing IpInterface based on its status and affinity to the chosen aggregate

Description

This PR...

  1. Fixed an issue in picking an unavailable IP while creating the storage pool.
  2. When CloudStack creates an ONTAP primary storage pool, it now picks the best available network interface (LIF) using a priority-based selection:
    • Best case: Uses a LIF homed on the same node as the storage aggregate — optimal I/O, no warning
    • Degraded case: All home-node LIFs are down but a failover LIF is running on that node — pool is created, admin is warned
    • Fallback case: No LIF at all on the aggregate's node, pool is created using a LIF from a different node, admin is warned with a latency note
    • Failure case: No usable LIF anywhere, pool creation fails with a clear error

Types of changes

  • Breaking change (fix or feature that would cause existing functionality to change)
  • New feature (non-breaking change which adds functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (improves an existing feature and functionality)
  • Cleanup (Code refactoring and cleanup, that may add test cases)
  • Build/CI
  • Test (unit or integration test code)

Feature/Enhancement Scale or Bug Severity

Feature/Enhancement Scale

  • Major
  • Minor

Bug Severity

  • BLOCKER
  • Critical
  • Major
  • Minor
  • Trivial

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.

Screenshot 2026-07-08 at 6 55 32 AM

Clearly, by the virtue of free space available, the plugin would choose sti246_vsim_ocvs040d_aggr1 by default.

Scenario-1 [pool_P1]: Happy path; No LIFs were down.
Screenshot 2026-07-07 at 10 26 34 PM
Screenshot 2026-07-07 at 10 26 49 PM
Screenshot 2026-07-07 at 10 27 48 PM
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 040d node were down; with one LIF whose current node: 040d, while its home node: 040c
Screenshot 2026-07-07 at 10 28 38 PM
Screenshot 2026-07-07 at 10 28 50 PM
Screenshot 2026-07-07 at 11 05 22 PM
Screenshot 2026-07-07 at 11 05 43 PM

Scenario-3 [pool_P3]: None of the 040d node LIFs are UP. First best available LIF is picked from 040c.
Screenshot 2026-07-07 at 11 07 00 PM
Screenshot 2026-07-07 at 11 07 22 PM
Screenshot 2026-07-07 at 11 08 30 PM
Screenshot 2026-07-07 at 11 08 54 PM

@sandeeplocharla sandeeplocharla self-assigned this Jul 8, 2026
Copilot AI review requested due to automatic review settings July 8, 2026 01:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions

Copy link
Copy Markdown

This pull request has merge conflicts. Dear author, please fix the conflicts and sync your branch with the base branch.

Copilot AI review requested due to automatic review settings July 14, 2026 06:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Copilot AI review requested due to automatic review settings July 15, 2026 05:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

IpInterface currentNodeInterface = null;
IpInterface fallbackInterface = null;

for (IpInterface iface : response.getRecords()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use proper var name instead iface

* @return DataStore instance
*/
@Override
public DataStore initialize(Map<String, Object> dsInfos) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is becoming very big, can we have some private method instead?

logger.warn("Aggregate " + aggr.getName() + " is not in online state. Skipping this aggregate.");
continue;
} else if (aggrResp.getSpace() == null || aggrResp.getAvailableBlockStorageSpace() == null ||
aggrResp.getAvailableBlockStorageSpace() <= storage.getSize().doubleValue()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this check validating? How does it help determine that there isn't enough free space available in the aggregate?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its skipping aggregates with available space less than the requested capacity for storage pool.

}

private void validateAndSelectAggregatesForVolumeCreation(String authHeader, String svmName, List<Aggregate> aggrs) {
if (aggrs == null || aggrs.isEmpty()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a question on the flow here. This method is invoked during the connect workflow, where we apply a set of filters/validations on the aggregates. As soon as the workflow finds the first aggregate that satisfies the criteria, it breaks out of the loop and stores that aggregate in a class-level variable.

Later, during volume creation, we appear to be using this class-level variable with the assumption that it contains all eligible aggregates. However, based on the current flow, it seems to contain only the single aggregate that was selected before the loop exited. The code path in the createVolume() method (and the related changes in this PR) seems to rely on that variable.

Is this the intended behaviour?

My understanding was that we would retain all aggregates that pass the filtering criteria and, during volume creation, iterate through the eligible aggregates to select the most appropriate one. That would also allow us to place volumes on aggregates with more available capacity and achieve better load balancing of storage consumption instead of always using the first matching aggregate.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The break should not be there. So, its been corrected and tested. Was waiting for more comments to raise a diff. 'break' would be removed in the next commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants