Skip to content

Commit 61f8105

Browse files
Merge branch 'main' into feature/CSTACKEX-200
2 parents 605a878 + b8c2971 commit 61f8105

14 files changed

Lines changed: 268 additions & 38 deletions

File tree

plugins/storage/volume/ontap/pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@
164164
<version>${maven-surefire-plugin.version}</version>
165165
<configuration>
166166
<skipTests>false</skipTests>
167-
<argLine>-javaagent:${settings.localRepository}/net/bytebuddy/byte-buddy-agent/${byte-buddy-agent.version}/byte-buddy-agent-${byte-buddy-agent.version}.jar</argLine>
168167
<includes>
169168
<include>**/*Test.java</include>
170169
</includes>

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriver.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore
451451
private void grantAccessIscsi(Host host, VolumeVO volumeVO, Map<String, String> details, String svmName, StoragePoolVO storagePool) {
452452
String cloudStackVolumeName = volumeDetailsDao.findDetail(volumeVO.getId(), OntapStorageConstants.LUN_DOT_NAME).getValue();
453453
UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) OntapStorageUtils.getStrategyByStoragePoolDetails(details);
454-
String accessGroupName = OntapStorageUtils.getIgroupName(svmName, host.getName());
454+
String accessGroupName = OntapStorageUtils.getIgroupName(svmName, host.getUuid());
455455

456456
// Validate if Igroup exist ONTAP for this host as we may be using delete_on_unmap= true and igroup may be deleted by ONTAP automatically
457457
Map<String, String> getAccessGroupMap = Map.of(
@@ -544,7 +544,7 @@ private void revokeAccessForVolume(StoragePoolVO storagePool, VolumeVO volumeVO,
544544
String svmName = details.get(OntapStorageConstants.SVM_NAME);
545545

546546
if (ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(OntapStorageConstants.PROTOCOL))) {
547-
String accessGroupName = OntapStorageUtils.getIgroupName(svmName, host.getName());
547+
String accessGroupName = OntapStorageUtils.getIgroupName(svmName, host.getUuid());
548548

549549
// Retrieve LUN name from volume details; if missing, volume may not have been fully created
550550
VolumeDetailVO lunDetail = volumeDetailsDao.findDetail(volumeVO.getId(), OntapStorageConstants.LUN_DOT_NAME);

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/Volume.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919

2020
package org.apache.cloudstack.storage.feign.model;
2121

22+
import com.fasterxml.jackson.annotation.JsonCreator;
2223
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2324
import com.fasterxml.jackson.annotation.JsonInclude;
2425
import com.fasterxml.jackson.annotation.JsonProperty;
26+
import com.fasterxml.jackson.annotation.JsonValue;
2527

2628
import java.util.List;
2729
import java.util.Objects;
@@ -50,6 +52,9 @@ public class Volume {
5052
@JsonProperty("space")
5153
private VolumeSpace space;
5254

55+
@JsonProperty("guarantee")
56+
private Guarantee guarantee;
57+
5358
@JsonProperty("anti_ransomware")
5459
private AntiRansomware antiRansomware;
5560

@@ -112,6 +117,14 @@ public void setSpace(VolumeSpace space) {
112117
this.space = space;
113118
}
114119

120+
public Guarantee getGuarantee() {
121+
return guarantee;
122+
}
123+
124+
public void setGuarantee(Guarantee guarantee) {
125+
this.guarantee = guarantee;
126+
}
127+
115128
public AntiRansomware getAntiRansomware() {
116129
return antiRansomware;
117130
}
@@ -139,4 +152,66 @@ public boolean equals(Object o) {
139152
public int hashCode() {
140153
return Objects.hashCode(uuid);
141154
}
155+
156+
public static class Guarantee {
157+
158+
/**
159+
* ONTAP FlexVolume space guarantee (provisioning) type.
160+
* <ul>
161+
* <li>{@link #NONE} - thin provisioning (space is not reserved up front)</li>
162+
* <li>{@link #VOLUME} - thick provisioning (full volume size is reserved on the aggregate)</li>
163+
* </ul>
164+
*/
165+
public enum TypeEnum {
166+
NONE("none"),
167+
168+
VOLUME("volume");
169+
170+
private String value;
171+
172+
TypeEnum(String value) {
173+
this.value = value;
174+
}
175+
176+
@JsonValue
177+
public String getValue() {
178+
return value;
179+
}
180+
181+
@Override
182+
public String toString() {
183+
return String.valueOf(value);
184+
}
185+
186+
@JsonCreator
187+
public static TypeEnum fromValue(String text) {
188+
if (text == null) return null;
189+
for (TypeEnum b : TypeEnum.values()) {
190+
if (text.equalsIgnoreCase(b.value)) {
191+
return b;
192+
}
193+
}
194+
return null;
195+
}
196+
}
197+
198+
@JsonProperty("type")
199+
private TypeEnum type;
200+
201+
public Guarantee() {
202+
}
203+
204+
public Guarantee(TypeEnum type) {
205+
this.type = type;
206+
}
207+
208+
public TypeEnum getType() {
209+
return type;
210+
}
211+
212+
public void setType(TypeEnum type) {
213+
this.type = type;
214+
}
215+
}
216+
142217
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/lifecycle/OntapPrimaryDatastoreLifecycle.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,16 +238,6 @@ private long validateInitializeInputs(Long capacityBytes, Long podId, Long clust
238238
throw new CloudRuntimeException("Provider name is null or empty, cannot create primary storage");
239239
}
240240

241-
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
242-
if (clusterId != null) {
243-
ClusterVO clusterVO = _clusterDao.findById(clusterId);
244-
Preconditions.checkNotNull(clusterVO, "Unable to locate the specified cluster");
245-
if (clusterVO.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
246-
throw new CloudRuntimeException("ONTAP primary storage is supported only for KVM hypervisor");
247-
}
248-
parameters.setHypervisorType(clusterVO.getHypervisorType());
249-
}
250-
251241
logger.debug("ONTAP primary storage will be created as " + (managed ? "managed" : "unmanaged"));
252242
if (!managed) {
253243
throw new CloudRuntimeException("ONTAP primary storage must be managed");
@@ -350,9 +340,16 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.Hyper
350340
logger.error("attachZone : Storage Pool not found for id: " + dataStore.getId());
351341
throw new CloudRuntimeException("Storage Pool not found for id: " + dataStore.getId());
352342
}
343+
if (!Hypervisor.HypervisorType.KVM.equals(hypervisorType)){
344+
logger.error("attachZone : ONTAP primary storage is supported only for KVM hypervisor");
345+
throw new CloudRuntimeException("ONTAP primary storage is supported only for KVM hypervisor");
346+
}
347+
storagePool.setHypervisor(hypervisorType);
348+
storagePoolDao.update(storagePool.getId(),storagePool);
349+
logger.debug("attachZone : Set Hypervisor type for storage pool {} to {}", storagePool.getName(), hypervisorType);
353350

354351
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)dataStore;
355-
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), Hypervisor.HypervisorType.KVM);
352+
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), hypervisorType);
356353
logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
357354

358355
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
@@ -405,8 +402,8 @@ private boolean validateProtocolSupportAndFetchHostsIdentifier(List<HostVO> host
405402
} else {
406403
ip = ip.isEmpty() ? host.getPrivateIpAddress().trim() : ip;
407404
}
405+
hostIdentifiers.add(ip);
408406
}
409-
hostIdentifiers.add(ip);
410407
}
411408
break;
412409
default:

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/StorageStrategy.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.apache.logging.log4j.Logger;
5050

5151
import java.util.HashMap;
52+
import java.util.ArrayList;
5253
import java.util.List;
5354
import java.util.Map;
5455
import java.util.Objects;
@@ -154,6 +155,8 @@ public boolean connect(boolean validateAggregatesForVolumeCreation) {
154155
} else {
155156
logger.debug("Skipping aggregate capacity validation — not required for existing-volume operations");
156157
}
158+
this.aggregates = eligibleAggregates;
159+
logger.info("Found " + eligibleAggregates.size() + " online aggregate(s) on SVM " + svmName + " for volume operations.");
157160

158161
logger.info("Successfully connected to ONTAP cluster and validated ONTAP details provided");
159162
} catch (CloudRuntimeException e) {
@@ -246,7 +249,7 @@ public Volume createStorageVolume(String volumeName, Long size) {
246249

247250
if (aggrResp == null) {
248251
logger.warn("Aggregate details response is null for aggregate " + aggr.getName() + ". Skipping.");
249-
break;
252+
continue;
250253
}
251254

252255
if (!Objects.equals(aggrResp.getState(), Aggregate.StateEnum.ONLINE)) {
@@ -286,6 +289,7 @@ public Volume createStorageVolume(String volumeName, Long size) {
286289
volumeRequest.setAggregates(List.of(aggr));
287290
volumeRequest.setSize(size);
288291
volumeRequest.setNas(nas);
292+
volumeRequest.setGuarantee(new Volume.Guarantee(Volume.Guarantee.TypeEnum.NONE));
289293
try {
290294
JobResponse jobResponse = volumeFeignClient.createVolumeWithJob(authHeader, volumeRequest);
291295
if (jobResponse == null || jobResponse.getJob() == null) {

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedNASStrategy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,6 @@ public CloudStackVolume getCloudStackVolume(Map<String, String> cloudStackVolume
127127
cloudStackVolume = new CloudStackVolume();
128128
cloudStackVolume.setFlexVolumeUuid(cloudStackVolumeMap.get(OntapStorageConstants.VOLUME_UUID));
129129
cloudStackVolume.setFile(fileInfo);
130-
} else {
131-
logger.warn("getCloudStackVolume: File not found for volume UUID: {} and file path: {}", cloudStackVolumeMap.get(OntapStorageConstants.VOLUME_UUID), cloudStackVolumeMap.get(OntapStorageConstants.FILE_PATH));
132130
}
133131

134132
return cloudStackVolume;

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/service/UnifiedSANStrategy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public AccessGroup createAccessGroup(AccessGroup accessGroup) {
207207
igroupRequest.setOsType(Igroup.OsTypeEnum.Linux);
208208

209209
for (HostVO host : accessGroup.getHostsToConnect()) {
210-
igroupName = OntapStorageUtils.getIgroupName(svmName, host.getName());
210+
igroupName = OntapStorageUtils.getIgroupName(svmName, host.getUuid());
211211
igroupRequest.setName(igroupName);
212212

213213
List<Initiator> initiators = new ArrayList<>();
@@ -271,7 +271,7 @@ public void deleteAccessGroup(AccessGroup accessGroup) {
271271
//Get iGroup name per host
272272
if(!CollectionUtils.isEmpty(accessGroup.getHostsToConnect())) {
273273
for (HostVO host : accessGroup.getHostsToConnect()) {
274-
String igroupName = OntapStorageUtils.getIgroupName(svmName, host.getName());
274+
String igroupName = OntapStorageUtils.getIgroupName(svmName, host.getUuid());
275275
logger.info("deleteAccessGroup: iGroup name '{}'", igroupName);
276276

277277
// Get the iGroup to retrieve its UUID

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/OntapStorageConstants.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public class OntapStorageConstants {
9797
public static final String IGROUP_DOT_UUID = "igroup.uuid";
9898
public static final String UNDERSCORE = "_";
9999
public static final String CS = "cs";
100+
public static final int IGROUP_NAME_MAX_LENGTH = 96;
100101
public static final String SRC_CS_VOLUME_ID = "src_cs_volume_id";
101102
public static final String BASE_ONTAP_FV_ID = "base_ontap_fv_id";
102103
public static final String ONTAP_SNAP_ID = "ontap_snap_id";
@@ -105,7 +106,7 @@ public class OntapStorageConstants {
105106
public static final String PRIMARY_POOL_ID = "primary_pool_id";
106107
public static final String ONTAP_SNAP_SIZE = "ontap_snap_size";
107108
public static final String FILE_PATH = "file_path";
108-
public static final int MAX_SNAPSHOT_NAME_LENGTH = 64;
109+
public static final int MAX_SNAPSHOT_NAME_LENGTH = 255;
109110
public static final String ONTAP_TEMP_CG_PREFIX = "cs-temp-cg-";
110111
/** ONTAP CG API: action required when referencing existing FlexVols in a consistency group. */
111112
public static final String CG_VOLUME_PROVISIONING_ACTION_ADD = "add";

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/OntapStorageUtils.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,15 @@ public static StorageStrategy getStrategyByStoragePoolDetails(Map<String, String
148148
}
149149
}
150150

151-
public static String getIgroupName(String svmName, String hostName) {
152-
//Igroup name format: cs_svmName_hostName
153-
String sanitizedHostName = hostName.split("\\.")[0].replaceAll("[^a-zA-Z0-9_-]", "_");
154-
return OntapStorageConstants.CS + OntapStorageConstants.UNDERSCORE + svmName + OntapStorageConstants.UNDERSCORE + sanitizedHostName;
151+
public static String getIgroupName(String svmName, String hostUuid) {
152+
//Igroup name format: cs_svmName_hostUuid
153+
String sanitizedHostUuid = hostUuid.replaceAll("[^a-zA-Z0-9_-]", "_");
154+
String igroupName = OntapStorageConstants.CS + OntapStorageConstants.UNDERSCORE + svmName + OntapStorageConstants.UNDERSCORE + sanitizedHostUuid;
155+
// ONTAP igroup names are limited to 96 characters; truncate if longer.
156+
if (igroupName.length() > OntapStorageConstants.IGROUP_NAME_MAX_LENGTH) {
157+
igroupName = igroupName.substring(0, OntapStorageConstants.IGROUP_NAME_MAX_LENGTH);
158+
}
159+
return igroupName;
155160
}
156161

157162
public static String generateExportPolicyName(String svmName, String volumeName){

plugins/storage/volume/ontap/src/test/java/org/apache/cloudstack/storage/driver/OntapPrimaryDatastoreDriverTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ void testGrantAccess_ClusterScope_Success() {
349349
when(volumeVO.getId()).thenReturn(100L);
350350

351351
when(host.getName()).thenReturn("host1");
352+
when(host.getUuid()).thenReturn("host-uuid-1");
352353

353354
VolumeDetailVO lunNameDetail = new VolumeDetailVO(100L, OntapStorageConstants.LUN_DOT_NAME, "/vol/vol1/lun1", false);
354355
when(volumeDetailsDao.findDetail(100L, OntapStorageConstants.LUN_DOT_NAME)).thenReturn(lunNameDetail);
@@ -385,6 +386,7 @@ void testGrantAccess_IgroupNotFound_CreatesNewIgroup() {
385386
// Setup - use HostVO mock since production code casts Host to HostVO
386387
HostVO hostVO = mock(HostVO.class);
387388
when(hostVO.getName()).thenReturn("host1");
389+
when(hostVO.getUuid()).thenReturn("host-uuid-1");
388390

389391
when(dataStore.getId()).thenReturn(1L);
390392
when(volumeInfo.getType()).thenReturn(VOLUME);
@@ -478,6 +480,7 @@ void testRevokeAccess_ISCSIVolume_Success() {
478480

479481
when(host.getStorageUrl()).thenReturn("iqn.1993-08.org.debian:01:host1");
480482
when(host.getName()).thenReturn("host1");
483+
when(host.getUuid()).thenReturn("host-uuid-1");
481484

482485
VolumeDetailVO lunNameDetail = new VolumeDetailVO(100L, OntapStorageConstants.LUN_DOT_NAME, "/vol/vol1/lun1", false);
483486
when(volumeDetailsDao.findDetail(100L, OntapStorageConstants.LUN_DOT_NAME)).thenReturn(lunNameDetail);

0 commit comments

Comments
 (0)