Skip to content

Conversation

@sandeeplocharla
Copy link
Collaborator

@sandeeplocharla sandeeplocharla commented Nov 10, 2025

…Storage pool workflows

Description

This PR has the following changes implemented:

  1. Pick a single aggregate instead of passing the entire array of aggregates while creating a storage pool
  2. Disable and re-enable storage pool
  3. Enter and exit maintenance mode
  4. Delete primary storage pool and the underlying igroup

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?

  1. Create Primary storage pool of type iscsi (Cluster scoped)
Screenshot 2025-12-05 at 2 25 49 PM

Corresponding igroup created on ontap
Screenshot 2025-12-05 at 2 27 47 PM

StoragePool details
Screenshot 2025-12-05 at 2 26 16 PM
  1. Disable StoragePool
Screenshot 2025-12-05 at 2 26 45 PM
  1. Enable StoragePool
Screenshot 2025-12-05 at 2 26 56 PM
  1. Enter Maintenance mode
Screenshot 2025-12-05 at 2 27 14 PM
  1. Cancel Maintenance mode
Screenshot 2025-12-05 at 2 27 29 PM
  1. Enter Maintenance mode and Delete StoragePool
Screenshot 2025-12-05 at 2 28 18 PM
 Igroups on Ontap
Screenshot 2025-12-05 at 2 28 35 PM
  1. Create Primary StoragePool of type iscsi (Zone scoped)
Screenshot 2025-12-05 at 2 36 49 PM Screenshot 2025-12-05 at 2 37 00 PM Screenshot 2025-12-05 at 2 37 10 PM

Avg time taken for each operation:

Note: An average of 3 trials has been considered and the system tested on has both Management Server, Cloudstack Agent and host running on the same machine, though on different network interfaces.

  • cluster scoped SP => 5s 48ms
  • zone scoped SP => 5s 79ms
  • disable cluster scoped SP => ~ 54ms
  • disable zone scoped SP => ~ 56ms
  • enable cluster scoped SP => ~ 50ms
  • enable zone scoped SP => ~ 40ms
  • enable maintenance mode on cluster scoped SP => 3s 58ms
  • enable maintenance mode on zone scoped SP => 3s 52ms
  • cancel maintenance mode on cluster scoped SP => 3s 59ms
  • cancel maintenance mode on zone scoped SP => 3s 50ms
  • delete cluster scoped SP => 3s
  • delete zone scoped SP => 2s 85ms

How did you try to break this feature and the system with this change?

Copilot AI review requested due to automatic review settings November 10, 2025 13:25
@sandeeplocharla sandeeplocharla self-assigned this Nov 10, 2025
Copy link

Copilot AI left a comment

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 implements storage pool lifecycle management operations for ONTAP integration, enabling users to manage storage pools through disable/re-enable, maintenance mode, and deletion workflows.

Key Changes:

  • Enhanced aggregate selection logic to choose a single suitable aggregate based on online state and available space
  • Implemented storage pool lifecycle operations: enable, disable, maintenance mode (enter/exit), and deletion
  • Added underlying ONTAP volume deletion when removing storage pools

Reviewed Changes

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

Show a summary per file
File Description
Constants.java Added constants for volume name and UUID tracking
StorageStrategy.java Implemented aggregate selection, volume creation/deletion, and job polling logic
OntapPrimaryDatastoreLifecycle.java Implemented lifecycle methods for enable, disable, maintain, cancelMaintain, and deleteDataStore
OntapStorage.java Added size field to OntapStorage model
Job.java Fixed JSON property annotations for Links and Self classes
Aggregate.java Added state enum, space tracking, and methods to check aggregate availability
VolumeFeignClient.java Added getAllVolumes method and updated deleteVolume return type
FeignConfiguration.java Switched from JsonMapper to ObjectMapper and added Apache license header
OntapPrimaryDatastoreDriver.java Updated OntapStorage constructor call to include size parameter
pom.xml Downgraded jackson-databind version from 2.15.2 to 2.13.4

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

continue;
}
s_logger.info("Selected aggregate: " + aggr.getName() + " for volume operations.");
this.aggregates = List.of(aggr);
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

The space comparison uses <= which will reject aggregates that have exactly the required size. This should use < instead to allow aggregates with space equal to the required size.

Copilot uses AI. Check for mistakes.
StoragePool storagePool = _storageMgr.getStoragePool(storagePoolId);
if (storagePool == null) {
s_logger.warn("Storage pool not found for id: " + storagePoolId + ", cannot delete underlying ONTAP volume");
return false;// TODO: As the CS entity is not present, should we return true here?
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

[nitpick] Remove the TODO comment or move it to a separate line. Inline TODO comments can make the code harder to read and should be placed on their own line above the code they reference.

Suggested change
return false;// TODO: As the CS entity is not present, should we return true here?
// TODO: As the CS entity is not present, should we return true here?
return false;

Copilot uses AI. Check for mistakes.
<openfeign.version>11.0</openfeign.version>
<json.version>20230227</json.version>
<jackson-databind.version>2.15.2</jackson-databind.version>
<jackson-databind.version>2.13.4</jackson-databind.version>
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

Downgrading jackson-databind from 2.15.2 to 2.13.4 may introduce known security vulnerabilities. Version 2.13.4 was released in September 2022 and several CVEs have been fixed in later versions. Consider using a more recent version or document the reason for this downgrade.

Suggested change
<jackson-databind.version>2.13.4</jackson-databind.version>
<jackson-databind.version>2.15.2</jackson-databind.version>

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings November 18, 2025 06:02
Copy link

Copilot AI left a comment

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 12 out of 12 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

if (storage.getProtocol() != null) {
switch (storage.getProtocol()) {
case NFS3:
queryParams .put(Constants.SERVICES, Constants.DATA_NFS);
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Extra space before the dot operator in queryParams .put. Should be queryParams.put.

Copilot uses AI. Check for mistakes.
StoragePool storagePool = _storageMgr.getStoragePool(storagePoolId);
if (storagePool == null) {
s_logger.warn("Storage pool not found for id: " + storagePoolId + ", cannot delete underlying ONTAP volume");
return false;// TODO: As the CS entity is not present, should we return true here?
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Missing space between false; and the comment. Should be return false; // TODO:.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,16 @@
package org.apache.cloudstack.storage.feign.client;
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Missing Apache license header. All source files should include the standard Apache Software Foundation license header at the top of the file.

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings November 18, 2025 08:54
Copy link

Copilot AI left a comment

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 12 out of 13 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

StoragePool storagePool = _storageMgr.getStoragePool(storagePoolId);
if (storagePool == null) {
s_logger.warn("Storage pool not found for id: " + storagePoolId + ", cannot delete underlying ONTAP volume");
return false;// TODO: As the CS entity is not present, should we return true here?
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

The inline TODO comment creates ambiguity about the correct return value. Move this TODO to a separate line above the return statement or resolve the question before merging.

Suggested change
return false;// TODO: As the CS entity is not present, should we return true here?
// TODO: As the CS entity is not present, should we return true here?
return false;

Copilot uses AI. Check for mistakes.
s_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

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

The space comparison is inverted. An aggregate should be skipped if available space is less than required size, but the condition uses <= which will skip aggregates with exactly enough space. Change to < or better yet < storage.getSize().doubleValue() to allow exact matches.

Suggested change
aggrResp.getAvailableBlockStorageSpace() <= storage.getSize().doubleValue()) {
aggrResp.getAvailableBlockStorageSpace() < storage.getSize().doubleValue()) {

Copilot uses AI. Check for mistakes.
continue;
}
s_logger.info("Selected aggregate: " + aggr.getName() + " for volume operations.");
this.aggregates = List.of(aggr);
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

The loop continues after finding the first suitable aggregate but the assignment inside the loop overwrites this.aggregates each time. Either add a break statement after line 140, or restructure the logic to set aggregates only once outside the loop after finding a suitable one.

Suggested change
this.aggregates = List.of(aggr);
this.aggregates = List.of(aggr);
break;

Copilot uses AI. Check for mistakes.
OntapResponse<IpInterface> response =
networkFeignClient.getNetworkIpInterfaces(authHeader, queryParams);
if (response != null && response.getRecords() != null && !response.getRecords().isEmpty()) {
// For simplicity, return the first interface's name
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Comment says 'return the first interface's name' but the code returns the IP address, not the name. Update comment to 'return the first interface's IP address'.

Suggested change
// For simplicity, return the first interface's name
// For simplicity, return the first interface's IP address

Copilot uses AI. Check for mistakes.

Choose a reason for hiding this comment

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

same changes are available in Piyush's PR also. whoever is merging second, do ensure to take latest and remove redundant changes before merge

Choose a reason for hiding this comment

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

same as earlier comment

Choose a reason for hiding this comment

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

file did not load

Choose a reason for hiding this comment

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

File is still not loading

Choose a reason for hiding this comment

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

file did not load

Copy link

@rajiv-jain-netapp rajiv-jain-netapp left a comment

Choose a reason for hiding this comment

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

Some of your files are not appearing for review. Let’s plan an online review during our upcoming scrum to cover the missing files.
In the test results you shared, I noticed references to cloudstack-volume. This seems incorrect—you are actually referring to the storage pool. Please update that accordingly.
Also, make sure to capture the time taken for each operation in your results.
I’m approving your changes for now, considering them as a partial implementation.

return false;
}
IpInterface that = (IpInterface) o;
return Objects.equals(uuid, that.uuid) &&

Choose a reason for hiding this comment

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

this can be optimized by using only UUID feild instead using all the fields


@Override
public int hashCode() {
return Objects.hash(uuid, name, ip, svm, services);

Choose a reason for hiding this comment

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

this method would also change as per the updates doenf rot he equals method.

Choose a reason for hiding this comment

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

File is still not loading


@Override
public boolean hostRemoved(long hostId, long clusterId) {
logger.info("hostRemoved: Host {} removed from cluster {}", hostId, clusterId);

Choose a reason for hiding this comment

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

Ithese loggers are added for now to debug the workflow, lets keep them as debug

String path;
int port;
switch (protocol) {
case NFS3:

Choose a reason for hiding this comment

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

I noticed that we have replaced NFS3 with NFS in some places. Keep them the same. I am inclined to keep these as NFS3, though.

@sandeeplocharla sandeeplocharla merged commit b23ac40 into main Dec 8, 2025
11 of 16 checks passed
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.

3 participants