Skip to content

Commit 63e0f2f

Browse files
Srivastava, PiyushSrivastava, Piyush
authored andcommitted
Pilot commit for NAS support
1 parent e27a2fd commit 63e0f2f

File tree

5 files changed

+196
-23
lines changed

5 files changed

+196
-23
lines changed

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/client/SvmFeignClient.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
import org.apache.cloudstack.storage.feign.model.Svm;
2424
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
2525
import org.springframework.cloud.openfeign.FeignClient;
26-
import org.springframework.web.bind.annotation.RequestHeader;
27-
import org.springframework.web.bind.annotation.RequestMapping;
28-
import org.springframework.web.bind.annotation.RequestMethod;
26+
import org.springframework.web.bind.annotation.*;
2927

3028
import java.net.URI;
3129

@@ -37,6 +35,9 @@ public interface SvmFeignClient {
3735
OntapResponse<Svm> getSvms(URI baseURL, @RequestHeader("Authorization") String header);
3836

3937
@RequestMapping(method = RequestMethod.GET, value = "/{uuid}")
40-
Svm getSvmByUUID(URI baseURL, @RequestHeader("Authorization") String header);
38+
Svm getSvmByUUID(URI baseURL, @RequestHeader("Authorization") String header, @PathVariable("uuid") String uuid);
39+
40+
@RequestMapping(method = RequestMethod.PATCH, value = "/{uuid}")
41+
Svm updateSVM(URI baseURL, @RequestHeader("Authorization") String header, @RequestBody Svm svm);
4142

4243
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycl
6666
*/
6767
@Override
6868
public DataStore initialize(Map<String, Object> dsInfos) {
69+
s_logger.info("initialize {}", dsInfos);
6970
if (dsInfos == null) {
7071
throw new CloudRuntimeException("Datastore info map is null, cannot create primary storage");
7172
}
@@ -94,7 +95,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
9495
return null;
9596
}
9697

97-
if (podId == null && clusterId == null) {
98+
if (podId == null) {
9899
if (zoneId != null) {
99100
s_logger.info("Both Pod Id and Cluster Id are null, Primary storage pool will be associated with a Zone");
100101
} else {
@@ -115,7 +116,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
115116
ClusterVO clusterVO = _clusterDao.findById(clusterId);
116117
Preconditions.checkNotNull(clusterVO, "Unable to locate the specified cluster");
117118
if (clusterVO.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
118-
throw new CloudRuntimeException("ONTAP primary storage is not supported for KVM hypervisor");
119+
throw new CloudRuntimeException("ONTAP primary storage does not support" + clusterVO.getHypervisorType() + "hypervisor currently");
119120
}
120121
parameters.setHypervisorType(clusterVO.getHypervisorType());
121122
}
@@ -144,8 +145,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
144145
OntapStorage ontapStorage = new OntapStorage(details.get(Constants.USERNAME), details.get(Constants.PASSWORD),
145146
details.get(Constants.MANAGEMENTLIF), details.get(Constants.SVMNAME), details.get(Constants.PROTOCOL),
146147
Boolean.parseBoolean(details.get(Constants.ISDISAGGREGATED)));
147-
StorageProviderFactory storageProviderManager = new StorageProviderFactory(ontapStorage);
148-
StorageStrategy storageStrategy = storageProviderManager.getStrategy();
148+
StorageStrategy storageStrategy = StorageProviderFactory.createStrategy(ontapStorage);
149149
boolean isValid = storageStrategy.connect();
150150
if (isValid) {
151151
// String volumeName = storagePoolName + "_vol"; //TODO: Figure out a better naming convention
@@ -154,6 +154,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
154154
throw new CloudRuntimeException("ONTAP details validation failed, cannot create primary storage");
155155
}
156156

157+
String storagePath = url + ":/" + storagePoolName;
157158
parameters.setTags(tags);
158159
parameters.setIsTagARule(isTagARule);
159160
parameters.setDetails(details);
@@ -164,6 +165,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
164165
parameters.setName(storagePoolName);
165166
parameters.setProviderName(providerName);
166167
parameters.setManaged(true);
168+
parameters.setPath(storagePath);
167169

168170
return _dataStoreHelper.createPrimaryDataStore(parameters);
169171
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/provider/StorageProviderFactory.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,29 @@
2929
import org.apache.logging.log4j.Logger;
3030
import org.springframework.stereotype.Component;
3131

32-
@Component
3332
public class StorageProviderFactory {
34-
private final StorageStrategy storageStrategy;
3533
private static final Logger s_logger = (Logger) LogManager.getLogger(StorageProviderFactory.class);
3634

37-
public StorageProviderFactory(OntapStorage ontapStorage) {
35+
public static StorageStrategy createStrategy(OntapStorage ontapStorage) {
3836
String protocol = ontapStorage.getProtocol();
3937
s_logger.info("Initializing StorageProviderFactory with protocol: " + protocol);
4038
switch (protocol.toLowerCase()) {
4139
case Constants.NFS:
4240
if(!ontapStorage.getIsDisaggregated()) {
43-
this.storageStrategy = new UnifiedNASStrategy(ontapStorage);
41+
return new UnifiedNASStrategy(ontapStorage);
4442
} else {
4543
throw new CloudRuntimeException("Unsupported configuration: Disaggregated ONTAP is not supported.");
4644
}
47-
break;
4845
case Constants.ISCSI:
4946
if (!ontapStorage.getIsDisaggregated()) {
50-
this.storageStrategy = new UnifiedSANStrategy(ontapStorage);
47+
return new UnifiedSANStrategy(ontapStorage);
5148
} else {
5249
throw new CloudRuntimeException("Unsupported configuration: Disaggregated ONTAP is not supported.");
5350
}
54-
break;
5551
default:
56-
this.storageStrategy = null;
52+
5753
throw new CloudRuntimeException("Unsupported protocol: " + protocol);
5854
}
5955
}
6056

61-
public StorageStrategy getStrategy() {
62-
return storageStrategy;
63-
}
6457
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public NASStrategy(OntapStorage ontapStorage) {
2727
}
2828

2929
public abstract String createExportPolicy(String svmName, String policyName);
30+
public abstract boolean deleteExportPolicy(String svmName, String policyName);
31+
public abstract boolean exportPolicyExists(String svmName, String policyName);
32+
3033
public abstract String addExportRule(String policyName, String clientMatch, String[] protocols, String[] roRule, String[] rwRule);
3134
public abstract String assignExportPolicyToVolume(String volumeUuid, String policyName);
3235
public abstract String enableNFS(String svmUuid);

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

Lines changed: 178 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,126 @@
1919

2020
package org.apache.cloudstack.storage.service;
2121

22-
import org.apache.cloudstack.storage.feign.model.OntapStorage;
22+
import com.cloud.utils.exception.CloudRuntimeException;
23+
import feign.FeignException;
24+
import org.apache.cloudstack.storage.feign.client.NASFeignClient;
25+
import org.apache.cloudstack.storage.feign.client.SvmFeignClient;
26+
import org.apache.cloudstack.storage.feign.client.VolumeFeignClient;
27+
import org.apache.cloudstack.storage.feign.model.*;
28+
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
29+
import org.apache.cloudstack.storage.utils.Constants;
30+
import org.apache.cloudstack.storage.utils.Utility;
31+
import org.apache.logging.log4j.LogManager;
32+
import org.apache.logging.log4j.Logger;
33+
34+
import javax.inject.Inject;
35+
import java.net.URI;
2336

2437
public class UnifiedNASStrategy extends NASStrategy{
38+
39+
@Inject
40+
private Utility utils;
41+
42+
@Inject
43+
private NASFeignClient nasFeignClient;
44+
45+
@Inject
46+
private SvmFeignClient svmFeignClient;
47+
48+
@Inject
49+
private VolumeFeignClient volumeFeignClient;
50+
51+
private static final Logger s_logger = LogManager.getLogger(NASStrategy.class);
2552
public UnifiedNASStrategy(OntapStorage ontapStorage) {
2653
super(ontapStorage);
2754
}
2855

2956
@Override
3057
public String createExportPolicy(String svmName, String policyName) {
31-
return "";
58+
s_logger.info("Creating export policy: {} for SVM: {}", policyName, svmName);
59+
60+
try {
61+
// Get AuthHeader
62+
String authHeader = utils.generateAuthHeader(OntapStorage.Username, OntapStorage.Password); // TODO change these once ontapStorage is made singleton
63+
64+
// Create ExportPolicy object
65+
ExportPolicy exportPolicy = new ExportPolicy();
66+
exportPolicy.setName(policyName);
67+
68+
// Set SVM
69+
Svm svm = new Svm();
70+
svm.setName(svmName);
71+
exportPolicy.setSvm(svm);
72+
73+
// Create URI for export policy creation
74+
URI url = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF + "/api/protocols/nfs/export-policies"); // TODO move this to constants ?
75+
76+
// Create export policy
77+
ExportPolicy createdPolicy = nasFeignClient.createExportPolicy(url, authHeader, true, exportPolicy);
78+
79+
if (createdPolicy != null && createdPolicy.getId() != null) {
80+
s_logger.info("Export policy created successfully with ID: {}", createdPolicy.getId());
81+
return createdPolicy.getId().toString();
82+
} else {
83+
throw new CloudRuntimeException("Failed to create export policy: " + policyName);
84+
}
85+
86+
} catch (FeignException e) {
87+
s_logger.error("Failed to create export policy: {}", policyName, e);
88+
throw new CloudRuntimeException("Failed to create export policy: " + e.getMessage());
89+
} catch (Exception e) {
90+
s_logger.error("Exception while creating export policy: {}", policyName, e);
91+
throw new CloudRuntimeException("Failed to create export policy: " + e.getMessage());
92+
}
93+
}
94+
95+
@Override
96+
public boolean deleteExportPolicy(String svmName, String policyName) {
97+
try {
98+
String authHeader = utils.generateAuthHeader(OntapStorage.Username, OntapStorage.Password);
99+
100+
// Get policy ID first
101+
URI getUrl = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF +
102+
"/api/protocols/nfs/export-policies?name=" + policyName + "&svm.name=" + svmName); // TODO move this to constants and how to dynamic pass params later ?
103+
104+
OntapResponse<ExportPolicy> policiesResponse = nasFeignClient.getExportPolicyResponse(getUrl, authHeader);
105+
106+
if (policiesResponse.getRecords() == null || policiesResponse.getRecords().isEmpty()) {
107+
s_logger.warn("Export policy not found for deletion: {}", policyName);
108+
return false;
109+
}
110+
111+
String policyId = policiesResponse.getRecords().get(0).getId().toString();
112+
113+
// Delete the policy
114+
URI deleteUrl = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF +
115+
"/api/protocols/nfs/export-policies/" + policyId);
116+
117+
nasFeignClient.deleteExportPolicyById(deleteUrl, authHeader, policyId);
118+
119+
s_logger.info("Export policy deleted successfully: {}", policyName);
120+
return true;
121+
122+
} catch (Exception e) {
123+
s_logger.error("Failed to delete export policy: {}", policyName, e);
124+
return false;
125+
}
126+
}
127+
128+
@Override
129+
public boolean exportPolicyExists(String svmName, String policyName) {
130+
try {
131+
String authHeader = utils.generateAuthHeader(OntapStorage.Username, OntapStorage.Password);
132+
URI url = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF +
133+
"/api/protocols/nfs/export-policies?name=" + policyName + "&svm.name=" + svmName);
134+
135+
OntapResponse<ExportPolicy> response = nasFeignClient.getExportPolicyResponse(url, authHeader);
136+
return response.getRecords() != null && !response.getRecords().isEmpty();
137+
138+
} catch (Exception e) {
139+
s_logger.warn("Error checking export policy existence: {}", e.getMessage());
140+
return false;
141+
}
32142
}
33143

34144
@Override
@@ -38,11 +148,75 @@ public String addExportRule(String policyName, String clientMatch, String[] prot
38148

39149
@Override
40150
public String assignExportPolicyToVolume(String volumeUuid, String policyName) {
41-
return "";
151+
s_logger.info("Assigning export policy: {} to volume: {}", policyName, volumeUuid);
152+
153+
try {
154+
// Get AuthHeader
155+
String authHeader = utils.generateAuthHeader(OntapStorage.Username, OntapStorage.Password);
156+
157+
// First, get the export policy by name
158+
URI getPolicyUrl = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF +
159+
"/api/protocols/nfs/export-policies?name=" + policyName + "&svm.name=" + OntapStorage.Svm);
160+
161+
OntapResponse<ExportPolicy> policiesResponse = nasFeignClient.getExportPolicyResponse(getPolicyUrl, authHeader);
162+
163+
if (policiesResponse.getRecords() == null || policiesResponse.getRecords().isEmpty()) {
164+
throw new CloudRuntimeException("Export policy not found: " + policyName);
165+
}
166+
167+
ExportPolicy exportPolicy = policiesResponse.getRecords().get(0);
168+
169+
// Create Volume update object with NAS configuration
170+
Volume volumeUpdate = new Volume();
171+
Nas nas = new Nas();
172+
nas.setExportPolicy(exportPolicy);
173+
volumeUpdate.setNas(nas);
174+
175+
// Update the volume
176+
URI updateVolumeUrl = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF +
177+
"/api/storage/volumes/" + volumeUuid);
178+
179+
volumeFeignClient.updateVolumeRebalancing(updateVolumeUrl, authHeader, volumeUuid, volumeUpdate);
180+
181+
s_logger.info("Export policy successfully assigned to volume: {}", volumeUuid);
182+
return "Export policy " + policyName + " assigned to volume " + volumeUuid;
183+
184+
} catch (FeignException e) {
185+
s_logger.error("Failed to assign export policy to volume: {}", volumeUuid, e);
186+
throw new CloudRuntimeException("Failed to assign export policy: " + e.getMessage());
187+
} catch (Exception e) {
188+
s_logger.error("Exception while assigning export policy to volume: {}", volumeUuid, e);
189+
throw new CloudRuntimeException("Failed to assign export policy: " + e.getMessage());
190+
}
42191
}
43192

44193
@Override
45194
public String enableNFS(String svmUuid) {
46-
return "";
195+
s_logger.info("Enabling NFS on SVM: {}", svmUuid);
196+
197+
try {
198+
// Get AuthHeader
199+
String authHeader = utils.generateAuthHeader(OntapStorage.Username, OntapStorage.Password);
200+
201+
// Create SVM update object to enable NFS
202+
Svm svmUpdate = new Svm();
203+
svmUpdate.setNfsEnabled(true);
204+
205+
// Update the SVM to enable NFS
206+
URI updateSvmUrl = URI.create(Constants.HTTPS + OntapStorage.ManagementLIF +
207+
"/api/svm/svms/" + svmUuid);
208+
209+
svmFeignClient.updateSVM(updateSvmUrl, authHeader, svmUpdate);
210+
211+
s_logger.info("NFS successfully enabled on SVM: {}", svmUuid);
212+
return "NFS enabled on SVM: " + svmUuid;
213+
214+
} catch (FeignException e) {
215+
s_logger.error("Failed to enable NFS on SVM: {}", svmUuid, e);
216+
throw new CloudRuntimeException("Failed to enable NFS: " + e.getMessage());
217+
} catch (Exception e) {
218+
s_logger.error("Exception while enabling NFS on SVM: {}", svmUuid, e);
219+
throw new CloudRuntimeException("Failed to enable NFS: " + e.getMessage());
220+
}
47221
}
48222
}

0 commit comments

Comments
 (0)