Skip to content

Commit 09caf90

Browse files
Srivastava, PiyushSrivastava, Piyush
authored andcommitted
creatacessgroup for NFS impl
1 parent e3c44ae commit 09caf90

File tree

4 files changed

+129
-58
lines changed

4 files changed

+129
-58
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ void createFile(@Param("authHeader") String authHeader,
5858
// Export Policy Operations
5959
@RequestLine("POST /")
6060
@Headers({"Authorization: {authHeader}"})
61-
ExportPolicy createExportPolicy(@Param("authHeader") String authHeader,
61+
void createExportPolicy(@Param("authHeader") String authHeader,
6262
ExportPolicy exportPolicy);
6363

6464
@RequestLine("GET /")
6565
@Headers({"Authorization: {authHeader}"})
66-
OntapResponse<ExportPolicy> getExportPolicyResponse(@Param("authHeader") String authHeader);
66+
ExportPolicy getExportPolicyResponse(@Param("authHeader") String authHeader);
6767

6868
@RequestLine("GET /{id}")
6969
@Headers({"Authorization: {authHeader}"})
70-
OntapResponse<ExportPolicy> getExportPolicyById(@Param("authHeader") String authHeader,
70+
ExportPolicy getExportPolicyById(@Param("authHeader") String authHeader,
7171
@Param("id") String id);
7272

7373
@RequestLine("DELETE /{id}")

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ public static ProtocolsEnum fromValue(String text) {
7676
@JsonProperty("protocols")
7777
private List<ProtocolsEnum> protocols = null;
7878

79+
@JsonProperty("ro_rule")
80+
private List<String> roRule = null;
81+
82+
@JsonProperty("rw_rule")
83+
private List<String> rwRule = null;
84+
85+
7986
public ExportRule anonymousUser(String anonymousUser) {
8087
this.anonymousUser = anonymousUser;
8188
return this;
@@ -140,6 +147,22 @@ public void setMatch (String match) {
140147
}
141148
}
142149

150+
public List<String> getRwRule() {
151+
return rwRule;
152+
}
153+
154+
public void setRwRule(List<String> rwRule) {
155+
this.rwRule = rwRule;
156+
}
157+
158+
public List<String> getRoRule() {
159+
return roRule;
160+
}
161+
162+
public void setRoRule(List<String> roRule) {
163+
this.roRule = roRule;
164+
}
165+
143166
@Override
144167
public String toString() {
145168
StringBuilder sb = new StringBuilder();

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.apache.cloudstack.storage.datastore.lifecycle.BasePrimaryDataStoreLifeCycleImpl;
4343
import org.apache.cloudstack.storage.feign.model.ExportPolicy;
4444
import org.apache.cloudstack.storage.feign.model.OntapStorage;
45-
import org.apache.cloudstack.storage.feign.model.Svm;
4645
import org.apache.cloudstack.storage.feign.model.Volume;
4746
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
4847
import org.apache.cloudstack.storage.service.StorageStrategy;
@@ -271,13 +270,11 @@ public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
271270

272271
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
273272
StorageStrategy strategy = Utility.getStrategyByStoragePoolDetails(details);
274-
Svm svm = new Svm();
275-
svm.setName(details.get(Constants.SVM_NAME));
276273
ExportPolicy exportPolicy = new ExportPolicy();
277-
exportPolicy.setSvm(svm);
278274
AccessGroup accessGroupRequest = new AccessGroup();
279275
accessGroupRequest.setHostsToConnect(hostsToConnect);
280276
accessGroupRequest.setScope(scope);
277+
primaryStore.setDetails(details);// setting details as it does not come from cloudstack
281278
accessGroupRequest.setPrimaryDataStoreInfo(primaryStore);
282279
accessGroupRequest.setPolicy(exportPolicy);
283280
strategy.createAccessGroup(accessGroupRequest);
@@ -308,13 +305,17 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.Hyper
308305
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), Hypervisor.HypervisorType.KVM);
309306
logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
310307

311-
Map<String, String> details = primaryStore.getDetails(); // TODO check while testing , if it is populated we can remove below db call
308+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
312309
StorageStrategy strategy = Utility.getStrategyByStoragePoolDetails(details);
310+
ExportPolicy exportPolicy = new ExportPolicy();
313311
AccessGroup accessGroupRequest = new AccessGroup();
314312
accessGroupRequest.setHostsToConnect(hostsToConnect);
315313
accessGroupRequest.setScope(scope);
314+
primaryStore.setDetails(details); // setting details as it does not come from cloudstack
316315
accessGroupRequest.setPrimaryDataStoreInfo(primaryStore);
316+
accessGroupRequest.setPolicy(exportPolicy);
317317
strategy.createAccessGroup(accessGroupRequest);
318+
318319
for (HostVO host : hostsToConnect) {
319320
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
320321
try {

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

Lines changed: 97 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,20 @@
2222
import com.cloud.host.HostVO;
2323
import com.cloud.utils.exception.CloudRuntimeException;
2424
import feign.FeignException;
25+
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
2526
import org.apache.cloudstack.storage.feign.FeignClientFactory;
27+
import org.apache.cloudstack.storage.feign.client.JobFeignClient;
2628
import org.apache.cloudstack.storage.feign.client.NASFeignClient;
2729
import org.apache.cloudstack.storage.feign.client.VolumeFeignClient;
2830
import org.apache.cloudstack.storage.feign.model.ExportPolicy;
2931
import org.apache.cloudstack.storage.feign.model.ExportRule;
3032
import org.apache.cloudstack.storage.feign.model.FileInfo;
33+
import org.apache.cloudstack.storage.feign.model.Job;
3134
import org.apache.cloudstack.storage.feign.model.Nas;
3235
import org.apache.cloudstack.storage.feign.model.OntapStorage;
3336
import org.apache.cloudstack.storage.feign.model.Svm;
3437
import org.apache.cloudstack.storage.feign.model.Volume;
38+
import org.apache.cloudstack.storage.feign.model.response.JobResponse;
3539
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
3640
import org.apache.cloudstack.storage.service.model.AccessGroup;
3741
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
@@ -40,6 +44,7 @@
4044
import org.apache.logging.log4j.LogManager;
4145
import org.apache.logging.log4j.Logger;
4246

47+
import javax.inject.Inject;
4348
import java.util.ArrayList;
4449
import java.util.List;
4550
import java.util.Map;
@@ -50,6 +55,9 @@ public class UnifiedNASStrategy extends NASStrategy {
5055
private final FeignClientFactory feignClientFactory;
5156
private final NASFeignClient nasFeignClient;
5257
private final VolumeFeignClient volumeFeignClient;
58+
private final JobFeignClient jobFeignClient;
59+
@Inject
60+
private StoragePoolDetailsDao storagePoolDetailsDao;
5361

5462
public UnifiedNASStrategy(OntapStorage ontapStorage) {
5563
super(ontapStorage);
@@ -58,6 +66,7 @@ public UnifiedNASStrategy(OntapStorage ontapStorage) {
5866
this.feignClientFactory = new FeignClientFactory();
5967
this.nasFeignClient = feignClientFactory.createClient(NASFeignClient.class, baseURL);
6068
this.volumeFeignClient = feignClientFactory.createClient(VolumeFeignClient.class, baseURL);
69+
this.jobFeignClient = feignClientFactory.createClient(JobFeignClient.class, baseURL);
6170
}
6271

6372
public void setOntapStorage(OntapStorage ontapStorage) {
@@ -99,35 +108,54 @@ CloudStackVolume getCloudStackVolume(CloudStackVolume cloudstackVolume) {
99108
@Override
100109
public AccessGroup createAccessGroup(AccessGroup accessGroup) {
101110

102-
103111
// Create the export policy
104-
String svmName = accessGroup.getPolicy().getSvm().getName();
105-
String exportPolicyName = "export-" + svmName + "-" + accessGroup.getPrimaryDataStoreInfo().getName();
112+
Map<String, String> details = accessGroup.getPrimaryDataStoreInfo().getDetails();
113+
String svmName = details.get(Constants.SVM_NAME);
114+
String volumeUUID = details.get(Constants.VOLUME_UUID);
115+
String volumeName = details.get(Constants.VOLUME_NAME);
116+
String exportPolicyName = "export-" + svmName + "-" + volumeName;// TODO move this to util
106117

107118
ExportPolicy exportPolicy = new ExportPolicy();
108-
exportPolicy.setName(exportPolicyName);
109-
110-
Svm svm = new Svm();
111-
svm.setName(svmName);
112-
exportPolicy.setSvm(svm);
113119

114120
List<ExportRule> rules = new ArrayList<>();
115121
ExportRule exportRule = new ExportRule();
116122

123+
List<ExportRule.ExportClient> exportClients = new ArrayList<>();
117124
List<HostVO> hosts = accessGroup.getHostsToConnect();
118125
for (HostVO host : hosts) {
119-
host.getStorageIpAddress()
126+
String hostStorageIp = host.getStorageIpAddress();
127+
String ip = (hostStorageIp != null && !hostStorageIp.isEmpty())
128+
? hostStorageIp
129+
: host.getPrivateIpAddress();
130+
String ipToUse = ip + "/32";
131+
ExportRule.ExportClient exportClient = new ExportRule.ExportClient();
132+
exportClient.setMatch(ipToUse);
133+
exportClients.add(exportClient);
120134
}
135+
exportRule.setClients(exportClients);
136+
exportRule.setProtocols(List.of(ExportRule.ProtocolsEnum.any));
137+
exportRule.setRoRule(List.of("any"));
138+
exportRule.setRwRule(List.of("any"));
139+
rules.add(exportRule);
121140

122-
141+
Svm svm = new Svm();
142+
svm.setName(svmName);
143+
exportPolicy.setSvm(svm);
123144
exportPolicy.setRules(rules);
124-
ExportPolicy createExportPolicy = createExportPolicy(svmName, exportPolicy);
125-
126-
127-
128-
129-
// attach export policy to volume of storage pool
130-
return null;
145+
exportPolicy.setName(exportPolicyName);
146+
try {
147+
createExportPolicy(svmName, exportPolicy);
148+
s_logger.info("ExportPolicy created: {}, now attaching this policy to storage pool volume", exportPolicy.getName());
149+
150+
// attach export policy to volume of storage pool
151+
assignExportPolicyToVolume(volumeUUID,exportPolicy.getName());
152+
s_logger.info("Successfully assigned exportPolicy {} to volume {}", exportPolicy.getName(), volumeName);
153+
accessGroup.setPolicy(exportPolicy);
154+
return accessGroup;
155+
}catch (Exception e){
156+
s_logger.error("Exception occurred while creating access group: " + e);
157+
throw new CloudRuntimeException("Failed to create access group: " + e);
158+
}
131159
}
132160

133161
@Override
@@ -158,31 +186,13 @@ void disableLogicalAccess(Map<String, String> values) {
158186
}
159187

160188

161-
private ExportPolicy createExportPolicy(String svmName, ExportPolicy policy) {
189+
private void createExportPolicy(String svmName, ExportPolicy policy) {
162190
s_logger.info("Creating export policy: {} for SVM: {}", policy, svmName);
163191

164192
try {
165193
String authHeader = Utility.generateAuthHeader(storage.getUsername(), storage.getPassword());
166-
167-
// // Create ExportPolicy object
168-
// ExportPolicy exportPolicy = new ExportPolicy();
169-
// exportPolicy.setName(policyName);
170-
//
171-
// // Set SVM
172-
// Svm svm = new Svm();
173-
// svm.setName(svmName);
174-
// exportPolicy.setSvm(svm);
175-
176-
// Create export policy
177-
ExportPolicy createdPolicy = nasFeignClient.createExportPolicy(authHeader, policy);
178-
179-
if (createdPolicy != null && createdPolicy.getId() != null) {
180-
s_logger.info("Export policy created successfully with ID: {}", createdPolicy.getId());
181-
return createdPolicy;
182-
} else {
183-
throw new CloudRuntimeException("Failed to create export policy: " + policy);
184-
}
185-
194+
nasFeignClient.createExportPolicy(authHeader, policy);
195+
s_logger.info("Export policy created successfully with name {}", policy.getName());
186196
} catch (FeignException e) {
187197
s_logger.error("Failed to create export policy: {}", policy, e);
188198
throw new CloudRuntimeException("Failed to create export policy: " + e.getMessage());
@@ -192,17 +202,16 @@ private ExportPolicy createExportPolicy(String svmName, ExportPolicy policy) {
192202
}
193203
}
194204

195-
196205
private void deleteExportPolicy(String svmName, String policyName) {
197206
try {
198207
String authHeader = Utility.generateAuthHeader(storage.getUsername(), storage.getPassword());
199-
OntapResponse<ExportPolicy> policiesResponse = nasFeignClient.getExportPolicyResponse(authHeader);
208+
ExportPolicy policiesResponse = nasFeignClient.getExportPolicyResponse(authHeader);
200209

201-
if (policiesResponse.getRecords() == null || policiesResponse.getRecords().isEmpty()) {
210+
if (policiesResponse == null ) {
202211
s_logger.warn("Export policy not found for deletion: {}", policyName);
203212
throw new CloudRuntimeException("Export policy not found : " + policyName);
204213
}
205-
String policyId = policiesResponse.getRecords().get(0).getId().toString();
214+
String policyId = policiesResponse.getId().toString();
206215
nasFeignClient.deleteExportPolicyById(authHeader, policyId);
207216
s_logger.info("Export policy deleted successfully: {}", policyName);
208217
} catch (Exception e) {
@@ -216,26 +225,64 @@ private String addExportRule(String policyName, String clientMatch, String[] pro
216225
return "";
217226
}
218227

219-
private String assignExportPolicyToVolume(String volumeUuid, String policyName) {
228+
private void assignExportPolicyToVolume(String volumeUuid, String policyName) {
220229
s_logger.info("Assigning export policy: {} to volume: {}", policyName, volumeUuid);
221230

222231
try {
223232
String authHeader = Utility.generateAuthHeader(storage.getUsername(), storage.getPassword());
224-
OntapResponse<ExportPolicy> policiesResponse = nasFeignClient.getExportPolicyResponse(authHeader);
225-
if (policiesResponse.getRecords() == null || policiesResponse.getRecords().isEmpty()) {
233+
ExportPolicy policiesResponse = nasFeignClient.getExportPolicyResponse(authHeader);
234+
if (policiesResponse == null) {
226235
throw new CloudRuntimeException("Export policy not found: " + policyName);
227236
}
228-
ExportPolicy exportPolicy = policiesResponse.getRecords().get(0);
229237
// Create Volume update object with NAS configuration
230238
Volume volumeUpdate = new Volume();
231239
Nas nas = new Nas();
232-
nas.setExportPolicy(exportPolicy);
240+
ExportPolicy policy = new ExportPolicy();
241+
policy.setName(policiesResponse.getName());
242+
nas.setExportPolicy(policy);
233243
volumeUpdate.setNas(nas);
234244

235-
volumeFeignClient.updateVolumeRebalancing(authHeader, volumeUuid, volumeUpdate);
236-
s_logger.info("Export policy successfully assigned to volume: {}", volumeUuid);
237-
return "Export policy " + policyName + " assigned to volume " + volumeUuid;
245+
try {
246+
/*
247+
ONTAP created a default rule of 0.0.0.0 if no export rule are defined while creating volume
248+
and since in storage pool creation, cloudstack is not aware of the host , we can either create default or
249+
permissive rule and later update it as part of attachCluster or attachZone implementation
250+
*/
251+
JobResponse jobResponse = volumeFeignClient.updateVolumeRebalancing(authHeader, volumeUuid, volumeUpdate);
252+
if (jobResponse == null || jobResponse.getJob() == null) {
253+
throw new CloudRuntimeException("Failed to attach policy " + policiesResponse.getName() + "to volume " + volumeUuid);
254+
}
255+
String jobUUID = jobResponse.getJob().getUuid();
256+
257+
//Create URI for GET Job API
258+
int jobRetryCount = 0;
259+
Job createVolumeJob = null;
260+
while(createVolumeJob == null || !createVolumeJob.getState().equals(Constants.JOB_SUCCESS)) {
261+
if(jobRetryCount >= Constants.JOB_MAX_RETRIES) {
262+
s_logger.error("Job to update volume " + volumeUuid + " did not complete within expected time.");
263+
throw new CloudRuntimeException("Job to update volume " + volumeUuid + " did not complete within expected time.");
264+
}
265+
266+
try {
267+
createVolumeJob = jobFeignClient.getJobByUUID(authHeader, jobUUID);
268+
if (createVolumeJob == null) {
269+
s_logger.warn("Job with UUID " + jobUUID + " not found. Retrying...");
270+
} else if (createVolumeJob.getState().equals(Constants.JOB_FAILURE)) {
271+
throw new CloudRuntimeException("Job to update volume " + volumeUuid + " failed with error: " + createVolumeJob.getMessage());
272+
}
273+
} catch (FeignException.FeignClientException e) {
274+
throw new CloudRuntimeException("Failed to fetch job status: " + e.getMessage());
275+
}
276+
277+
jobRetryCount++;
278+
Thread.sleep(Constants.CREATE_VOLUME_CHECK_SLEEP_TIME); // Sleep for 2 seconds before polling again
279+
}
280+
} catch (Exception e) {
281+
s_logger.error("Exception while updating volume: ", e);
282+
throw new CloudRuntimeException("Failed to update volume: " + e.getMessage());
283+
}
238284

285+
s_logger.info("Export policy successfully assigned to volume: {}", volumeUuid);
239286
} catch (FeignException e) {
240287
s_logger.error("Failed to assign export policy to volume: {}", volumeUuid, e);
241288
throw new CloudRuntimeException("Failed to assign export policy: " + e.getMessage());

0 commit comments

Comments
 (0)