Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions plugins/storage/volume/ontap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The NetApp ONTAP Storage Plugin provides integration between Apache CloudStack a

### Minimum Volume Size

ONTAP requires a minimum volume size of **1.56 GB** (1,677,721,600 bytes). The plugin will automatically adjust requested sizes below this threshold.
ONTAP requires a minimum volume size of **20 MB** (20,971,520 bytes). Requests below this threshold are rejected.

## Configuration

Expand Down Expand Up @@ -116,7 +116,7 @@ username=admin;password=secretpass;svmName=svm1;protocol=ISCSI;managementLIF=192

3. **Capacity Errors**
- Check aggregate space availability
- Ensure requested volume size meets minimum requirements (1.56 GB)
- Ensure requested volume size meets minimum requirements (20 MB)

4. **Host Connection Issues**
- For iSCSI: Verify host IQN is properly configured in host's storage URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycl
@Inject private AlertManager _alertMgr;
private static final Logger logger = LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);

private static final long ONTAP_MIN_VOLUME_SIZE_IN_BYTES = 1677721600L;
private static final long ONTAP_MIN_VOLUME_SIZE_IN_BYTES = 20971520L;

/**
* Creates primary storage on NetApp storage
Expand Down Expand Up @@ -113,7 +113,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
@SuppressWarnings("unchecked")
Map<String, String> details = (Map<String, String>) dsInfos.get("details");

capacityBytes = validateInitializeInputs(capacityBytes, podId, clusterId, zoneId, storagePoolName, providerName, managed, details);
validateInitializeInputs(capacityBytes, podId, clusterId, zoneId, storagePoolName, providerName, managed, details);

PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
if (clusterId != null) {
Expand Down Expand Up @@ -212,16 +212,16 @@ public DataStore initialize(Map<String, Object> dsInfos) {
return _dataStoreHelper.createPrimaryDataStore(parameters);
}

private long validateInitializeInputs(Long capacityBytes, Long podId, Long clusterId, Long zoneId,
private void validateInitializeInputs(Long capacityBytes, Long podId, Long clusterId, Long zoneId,
String storagePoolName, String providerName, boolean managed, Map<String, String> details) {

// Validate and set capacity
if (capacityBytes == null || capacityBytes <= 0) {
logger.warn("capacityBytes not provided or invalid (" + capacityBytes + "), using ONTAP minimum size: " + ONTAP_MIN_VOLUME_SIZE_IN_BYTES);
capacityBytes = ONTAP_MIN_VOLUME_SIZE_IN_BYTES;
} else if (capacityBytes < ONTAP_MIN_VOLUME_SIZE_IN_BYTES) {
logger.warn("capacityBytes (" + capacityBytes + ") is below ONTAP minimum (" + ONTAP_MIN_VOLUME_SIZE_IN_BYTES + "), adjusting to minimum");
capacityBytes = ONTAP_MIN_VOLUME_SIZE_IN_BYTES;
throw new InvalidParameterValueException("Storage pool capacity is required for ONTAP primary storage and must be at least "
+ ONTAP_MIN_VOLUME_SIZE_IN_BYTES + " bytes (20 MB)");
}
if (capacityBytes < ONTAP_MIN_VOLUME_SIZE_IN_BYTES) {
throw new InvalidParameterValueException("Storage pool capacity " + capacityBytes + " bytes is below the ONTAP minimum volume size of "
+ ONTAP_MIN_VOLUME_SIZE_IN_BYTES + " bytes (20 MB)");
}

// Validate scope
Expand Down Expand Up @@ -278,8 +278,6 @@ private long validateInitializeInputs(Long capacityBytes, Long podId, Long clust
missing.removeAll(providedKeys);
throw new CloudRuntimeException("ONTAP primary storage creation failed, missing detail(s): " + missing);
}

return capacityBytes;
}

private void processDataLifSelection(Pair<String, String> lifResult, Map<String, String> details,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.mockito.quality.Strictness;
import org.apache.cloudstack.storage.feign.model.Volume;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.dc.ClusterVO;
import com.cloud.host.HostVO;
Expand Down Expand Up @@ -178,7 +179,7 @@ public void testInitialize_positive() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -212,7 +213,7 @@ public void testInitialize_missingRequiredDetailKey() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -249,7 +250,37 @@ public void testInitialize_invalidCapacityBytes() {

try (MockedStatic<StorageProviderFactory> storageProviderFactory = Mockito.mockStatic(StorageProviderFactory.class)) {
storageProviderFactory.when(() -> StorageProviderFactory.getStrategy(any())).thenReturn(storageStrategy);
ontapPrimaryDatastoreLifecycle.initialize(dsInfos);
Exception ex = assertThrows(InvalidParameterValueException.class, () -> ontapPrimaryDatastoreLifecycle.initialize(dsInfos));
assertTrue(ex.getMessage().contains("must be at least"));
}
}

@Test
public void testInitialize_capacityBelowOntapMinimum() {

HashMap<String, String> detailsMap = new HashMap<String, String>();
detailsMap.put(OntapStorageConstants.USERNAME, "testUser");
detailsMap.put(OntapStorageConstants.PASSWORD, "testPassword");
detailsMap.put(OntapStorageConstants.STORAGE_IP, "10.10.10.10");
detailsMap.put(OntapStorageConstants.SVM_NAME, "vs0");
detailsMap.put(OntapStorageConstants.PROTOCOL, "NFS3");

Map<String, Object> dsInfos = new HashMap<>();
dsInfos.put("zoneId",1L);
dsInfos.put("podId",1L);
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 20971519L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
dsInfos.put("details", detailsMap);

try (MockedStatic<StorageProviderFactory> storageProviderFactory = Mockito.mockStatic(StorageProviderFactory.class)) {
storageProviderFactory.when(() -> StorageProviderFactory.getStrategy(any())).thenReturn(storageStrategy);
Exception ex = assertThrows(InvalidParameterValueException.class, () -> ontapPrimaryDatastoreLifecycle.initialize(dsInfos));
assertTrue(ex.getMessage().contains("below the ONTAP minimum volume size"));
}
}

Expand All @@ -261,7 +292,7 @@ public void testInitialize_unmanagedStorage() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",false);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand All @@ -284,7 +315,7 @@ public void testInitialize_nullStoragePoolName() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", null);
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand All @@ -307,7 +338,7 @@ public void testInitialize_nullProviderName() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", null);
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand All @@ -330,7 +361,7 @@ public void testInitialize_nullPodAndClusterAndZone() {
dsInfos.put("clusterId", null);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -364,7 +395,7 @@ public void testInitialize_clusterNotKVM() {
dsInfos.put("clusterId", 2L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -396,7 +427,7 @@ public void testInitialize_unexpectedDetailKey() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes",200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed",true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -428,7 +459,7 @@ public void testInitialize_dataLifWithWarning() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -464,7 +495,7 @@ public void testInitialize_nullDataLif() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -495,7 +526,7 @@ public void testInitialize_emptyDataLif() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -526,7 +557,7 @@ public void testInitialize_getNetworkInterfaceException() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -558,7 +589,7 @@ public void testInitialize_volumeCreationFailure_nullVolume() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -589,7 +620,7 @@ public void testInitialize_volumeCreationException() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down Expand Up @@ -621,7 +652,7 @@ public void testInitialize_positiveWithDetailAssertions() {
dsInfos.put("clusterId", 1L);
dsInfos.put("name", "testStoragePool");
dsInfos.put("providerName", "testProvider");
dsInfos.put("capacityBytes", 200000L);
dsInfos.put("capacityBytes", 1073741824L);
dsInfos.put("managed", true);
dsInfos.put("tags", "testTag");
dsInfos.put("isTagARule", false);
Expand Down
Loading