Skip to content

Commit 4781b3f

Browse files
Srivastava, PiyushSrivastava, Piyush
authored andcommitted
feature/CSTACKEX-122: Per host Igroup changes
1 parent eace4ee commit 4781b3f

File tree

8 files changed

+210
-244
lines changed

8 files changed

+210
-244
lines changed

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

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.cloud.agent.api.to.DataTO;
2525
import com.cloud.exception.InvalidParameterValueException;
2626
import com.cloud.host.Host;
27+
import com.cloud.host.HostVO;
2728
import com.cloud.storage.Storage;
2829
import com.cloud.storage.StoragePool;
2930
import com.cloud.storage.Volume;
@@ -50,6 +51,7 @@
5051
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
5152
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
5253
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
54+
import org.apache.cloudstack.storage.feign.model.Igroup;
5355
import org.apache.cloudstack.storage.feign.model.Lun;
5456
import org.apache.cloudstack.storage.service.SANStrategy;
5557
import org.apache.cloudstack.storage.service.StorageStrategy;
@@ -63,8 +65,10 @@
6365
import org.apache.logging.log4j.Logger;
6466

6567
import javax.inject.Inject;
68+
import java.util.ArrayList;
6669
import java.util.Arrays;
6770
import java.util.HashMap;
71+
import java.util.List;
6872
import java.util.Map;
6973

7074
/**
@@ -158,17 +162,26 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
158162
volumeVO.setFolder(created.getLun().getUuid());
159163
}
160164

161-
// Create LUN-to-igroup mapping and retrieve the assigned LUN ID
162-
UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) Utility.getStrategyByStoragePoolDetails(details);
163-
String accessGroupName = Utility.getIgroupName(svmName, storagePoolUuid);
164-
String lunNumber = sanStrategy.ensureLunMapped(svmName, lunName, accessGroupName);
165-
166-
// Construct iSCSI path: /<iqn>/<lun_id> format for KVM/libvirt attachment
167-
String iscsiPath = Constants.SLASH + storagePool.getPath() + Constants.SLASH + lunNumber;
168-
volumeVO.set_iScsiName(iscsiPath);
169-
volumeVO.setPath(iscsiPath);
170-
s_logger.info("createAsync: Volume [{}] iSCSI path set to {}", volumeVO.getId(), iscsiPath);
171-
createCmdResult = new CreateCmdResult(null, new Answer(null, true, null));
165+
s_logger.info("createAsync: Created LUN [{}] for volume [{}]. LUN mapping will occur during grantAccess() to per-host igroup.",
166+
lunName, volumeVO.getId());
167+
168+
// Path will be set during grantAccess when LUN is mapped and we get the LUN ID
169+
// Return LUN name as identifier for CloudStack tracking
170+
volumeVO.set_iScsiName(null);
171+
volumeVO.setPath(null);
172+
createCmdResult = new CreateCmdResult(lunName, new Answer(null, true, null));
173+
174+
// // Create LUN-to-igroup mapping and retrieve the assigned LUN ID
175+
// UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) Utility.getStrategyByStoragePoolDetails(details);
176+
// String accessGroupName = Utility.getIgroupName(svmName, storagePoolUuid);
177+
// String lunNumber = sanStrategy.ensureLunMapped(svmName, lunName, accessGroupName);
178+
//
179+
// // Construct iSCSI path: /<iqn>/<lun_id> format for KVM/libvirt attachment
180+
// String iscsiPath = Constants.SLASH + storagePool.getPath() + Constants.SLASH + lunNumber;
181+
// volumeVO.set_iScsiName(iscsiPath);
182+
// volumeVO.setPath(iscsiPath);
183+
// s_logger.info("createAsync: Volume [{}] iSCSI path set to {}", volumeVO.getId(), iscsiPath);
184+
// createCmdResult = new CreateCmdResult(null, new Answer(null, true, null));
172185

173186
} else if (ProtocolType.NFS3.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
174187
createCmdResult = new CreateCmdResult(volInfo.getUuid(), new Answer(null, true, null));
@@ -319,12 +332,32 @@ public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore
319332
// Only retrieve LUN name for iSCSI volumes
320333
String cloudStackVolumeName = volumeDetailsDao.findDetail(volumeVO.getId(), Constants.LUN_DOT_NAME).getValue();
321334
UnifiedSANStrategy sanStrategy = (UnifiedSANStrategy) Utility.getStrategyByStoragePoolDetails(details);
322-
String accessGroupName = Utility.getIgroupName(svmName, storagePoolUuid);
323-
324-
// Verify host initiator is registered in the igroup before allowing access
325-
if (!sanStrategy.validateInitiatorInAccessGroup(host.getStorageUrl(), svmName, accessGroupName)) {
326-
throw new CloudRuntimeException("grantAccess: Host initiator [" + host.getStorageUrl() +
327-
"] is not present in iGroup [" + accessGroupName + "]");
335+
String accessGroupName = Utility.getIgroupName(svmName, host.getName());
336+
337+
// 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
338+
Map<String, String> getAccessGroupMap = Map.of(
339+
Constants.NAME, accessGroupName,
340+
Constants.SVM_DOT_NAME, svmName
341+
);
342+
Igroup igroup = new Igroup();
343+
AccessGroup accessGroup = sanStrategy.getAccessGroup(getAccessGroupMap);
344+
if(accessGroup == null || accessGroup.getIgroup() == null) {
345+
s_logger.info("grantAccess: Igroup does not exist for the host: Need to create Igroup " + host.getName());
346+
// create the igroup for the host and perform lun-mapping
347+
accessGroup = new AccessGroup();
348+
List<HostVO> hosts = new ArrayList<>();
349+
hosts.add((HostVO) host);
350+
accessGroup.setHostsToConnect(hosts);
351+
accessGroup.setStoragePoolId(storagePool.getId());
352+
accessGroup = sanStrategy.createAccessGroup(accessGroup);
353+
}else{
354+
s_logger.info("grantAccess: Igroup {} already exist for the host {}: ", accessGroup.getIgroup().getName() ,host.getName());
355+
igroup = accessGroup.getIgroup();
356+
// Verify host initiator is registered in the igroup before allowing access
357+
// if (sanStrategy.validateInitiatorInAccessGroup(host.getStorageUrl(), svmName, accessGroup.getIgroup())) {
358+
// // add host initiator to the igroup ? or fail here ?
359+
// }
360+
// Use the existing igroup and perform lun-mapping
328361
}
329362

330363
// Create or retrieve existing LUN mapping
@@ -453,7 +486,7 @@ private void revokeAccessForVolume(StoragePoolVO storagePool, VolumeVO volumeVO,
453486

454487
// Verify host initiator is in the igroup before attempting to remove mapping
455488
SANStrategy sanStrategy = (UnifiedSANStrategy) storageStrategy;
456-
if (!sanStrategy.validateInitiatorInAccessGroup(host.getStorageUrl(), svmName, accessGroup.getIgroup().getName())) {
489+
if (!sanStrategy.validateInitiatorInAccessGroup(host.getStorageUrl(), svmName, accessGroup.getIgroup())) {
457490
s_logger.warn("revokeAccessForVolume: Initiator [{}] is not in iGroup [{}], skipping revoke",
458491
host.getStorageUrl(), accessGroupName);
459492
return;
@@ -524,7 +557,6 @@ public long getUsedIops(StoragePool storagePool) {
524557

525558
@Override
526559
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
527-
528560
}
529561

530562
@Override
@@ -569,7 +601,6 @@ public boolean isVmInfoNeeded() {
569601

570602
@Override
571603
public void provideVmInfo(long vmId, long volumeId) {
572-
573604
}
574605

575606
@Override

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

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,11 @@ public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
281281
}
282282
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)dataStore;
283283
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInClusterForStorageConnection(primaryStore);
284-
// TODO- need to check if no host to connect then throw exception or just continue?
285284
logger.debug("attachCluster: Eligible Up and Enabled hosts: {} in cluster {}", hostsToConnect, primaryStore.getClusterId());
285+
if(hostsToConnect.isEmpty()) {
286+
s_logger.info("attachCluster: No hosts found for primary storage");
287+
throw new CloudRuntimeException("attachCluster: No hosts found for primary storage");
288+
}
286289

287290
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
288291
StorageStrategy strategy = Utility.getStrategyByStoragePoolDetails(details);
@@ -294,22 +297,24 @@ public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
294297
s_logger.error(errMsg);
295298
throw new CloudRuntimeException(errMsg);
296299
}
297-
298300
logger.debug("attachCluster: Attaching the pool to each of the host in the cluster: {}", primaryStore.getClusterId());
299-
//TODO - check if no host to connect then also need to create access group without initiators
300-
if (hostsIdentifier != null && hostsIdentifier.size() > 0) {
301-
try {
302-
AccessGroup accessGroupRequest = new AccessGroup();
303-
accessGroupRequest.setHostsToConnect(hostsToConnect);
304-
accessGroupRequest.setScope(scope);
305-
primaryStore.setDetails(details);// setting details as it does not come from cloudstack
306-
accessGroupRequest.setPrimaryDataStoreInfo(primaryStore);
307-
strategy.createAccessGroup(accessGroupRequest);
308-
} catch (Exception e) {
309-
s_logger.error("attachCluster: Failed to create access group on storage system for cluster: " + primaryStore.getClusterId() + ". Exception: " + e.getMessage());
310-
throw new CloudRuntimeException("attachCluster: Failed to create access group on storage system for cluster: " + primaryStore.getClusterId() + ". Exception: " + e.getMessage());
301+
// We need to create export policy at pool level and igroup at host level(in grantAccess)
302+
if (ProtocolType.NFS3.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
303+
if (!hostsIdentifier.isEmpty()) {
304+
try {
305+
AccessGroup accessGroupRequest = new AccessGroup();
306+
accessGroupRequest.setHostsToConnect(hostsToConnect);
307+
accessGroupRequest.setScope(scope);
308+
primaryStore.setDetails(details);// setting details as it does not come from cloudstack
309+
accessGroupRequest.setStoragePoolId(storagePool.getId());
310+
strategy.createAccessGroup(accessGroupRequest);
311+
} catch (Exception e) {
312+
s_logger.error("attachCluster: Failed to create access group on storage system for cluster: " + primaryStore.getClusterId() + ". Exception: " + e.getMessage());
313+
throw new CloudRuntimeException("attachCluster: Failed to create access group on storage system for cluster: " + primaryStore.getClusterId() + ". Exception: " + e.getMessage());
314+
}
311315
}
312316
}
317+
313318
logger.debug("attachCluster: Attaching the pool to each of the host in the cluster: {}", primaryStore.getClusterId());
314319
for (HostVO host : hostsToConnect) {
315320
try {
@@ -347,30 +352,36 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.Hyper
347352
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)dataStore;
348353
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), Hypervisor.HypervisorType.KVM);
349354
logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
355+
if(hostsToConnect.isEmpty()) {
356+
s_logger.info("attachCluster: No hosts found for primary storage");
357+
throw new CloudRuntimeException("attachCluster: No hosts found for primary storage");
358+
}
350359

351360
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(primaryStore.getId());
352361
StorageStrategy strategy = Utility.getStrategyByStoragePoolDetails(details);
353362

354-
// TODO- need to check if no host to connect then throw exception or just continue
355-
logger.debug("attachZone: Eligible Up and Enabled hosts: {}", hostsToConnect);
356363
ProtocolType protocol = ProtocolType.valueOf(details.get(Constants.PROTOCOL));
357364
//TODO- Check if we have to handle heterogeneous host within the zone
358365
if (!validateProtocolSupportAndFetchHostsIdentifier(hostsToConnect, protocol, hostsIdentifier)) {
359366
String errMsg = "attachZone: Not all hosts in the zone support the protocol: " + protocol.name();
360367
s_logger.error(errMsg);
361368
throw new CloudRuntimeException(errMsg);
362369
}
363-
if (hostsIdentifier != null && !hostsIdentifier.isEmpty()) {
364-
try {
365-
AccessGroup accessGroupRequest = new AccessGroup();
366-
accessGroupRequest.setHostsToConnect(hostsToConnect);
367-
accessGroupRequest.setScope(scope);
368-
primaryStore.setDetails(details); // setting details as it does not come from cloudstack
369-
accessGroupRequest.setPrimaryDataStoreInfo(primaryStore);
370-
strategy.createAccessGroup(accessGroupRequest);
371-
} catch (Exception e) {
372-
s_logger.error("attachZone: Failed to create access group on storage system for zone with Exception: " + e.getMessage());
373-
throw new CloudRuntimeException("attachZone: Failed to create access group on storage system for zone with Exception: " + e.getMessage());
370+
371+
// We need to create export policy at pool level and igroup at host level
372+
if (ProtocolType.NFS3.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
373+
if (!hostsIdentifier.isEmpty()) {
374+
try {
375+
AccessGroup accessGroupRequest = new AccessGroup();
376+
accessGroupRequest.setHostsToConnect(hostsToConnect);
377+
accessGroupRequest.setScope(scope);
378+
primaryStore.setDetails(details); // setting details as it does not come from cloudstack
379+
accessGroupRequest.setStoragePoolId(storagePool.getId());
380+
strategy.createAccessGroup(accessGroupRequest);
381+
} catch (Exception e) {
382+
s_logger.error("attachZone: Failed to create access group on storage system for zone with Exception: " + e.getMessage());
383+
throw new CloudRuntimeException("attachZone: Failed to create access group on storage system for zone with Exception: " + e.getMessage());
384+
}
374385
}
375386
}
376387
for (HostVO host : hostsToConnect) {
@@ -485,7 +496,7 @@ public boolean deleteDataStore(DataStore store) {
485496
storagePoolId, e.getMessage(), e);
486497
}
487498
AccessGroup accessGroup = new AccessGroup();
488-
accessGroup.setPrimaryDataStoreInfo(primaryDataStoreInfo);
499+
accessGroup.setStoragePoolId(storagePoolId);
489500
// Delete access groups associated with this storage pool
490501
storageStrategy.deleteAccessGroup(accessGroup);
491502
s_logger.info("deleteDataStore: Successfully deleted access groups for storage pool '{}'", storagePool.getName());

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@
1919

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

22+
import org.apache.cloudstack.storage.feign.model.Igroup;
23+
import org.apache.cloudstack.storage.feign.model.Initiator;
2224
import org.apache.cloudstack.storage.feign.model.OntapStorage;
25+
import org.apache.logging.log4j.LogManager;
26+
import org.apache.logging.log4j.Logger;
2327

2428
public abstract class SANStrategy extends StorageStrategy {
29+
private static final Logger s_logger = LogManager.getLogger(SANStrategy.class);
2530
public SANStrategy(OntapStorage ontapStorage) {
2631
super(ontapStorage);
2732
}
@@ -46,5 +51,22 @@ public SANStrategy(OntapStorage ontapStorage) {
4651
* @param accessGroupName the igroup name
4752
* @return true if the initiator is found in the igroup, false otherwise
4853
*/
49-
public abstract boolean validateInitiatorInAccessGroup(String hostInitiator, String svmName, String accessGroupName);
54+
public boolean validateInitiatorInAccessGroup(String hostInitiator, String svmName, Igroup igroup) {
55+
s_logger.info("validateInitiatorInAccessGroup: Validating initiator [{}] is in igroup [{}] on SVM [{}]", hostInitiator, igroup, svmName);
56+
57+
if (hostInitiator == null || hostInitiator.isEmpty()) {
58+
s_logger.warn("validateInitiatorInAccessGroup: host initiator is null or empty");
59+
return false;
60+
}
61+
if (igroup.getInitiators() != null) {
62+
for (Initiator initiator : igroup.getInitiators()) {
63+
if (initiator.getName().equalsIgnoreCase(hostInitiator)) {
64+
s_logger.info("validateInitiatorInAccessGroup: Initiator [{}] validated successfully in igroup [{}]", hostInitiator, igroup);
65+
return true;
66+
}
67+
}
68+
}
69+
s_logger.warn("validateInitiatorInAccessGroup: Initiator [{}] NOT found in igroup [{}]", hostInitiator, igroup);
70+
return false;
71+
}
5072
}

0 commit comments

Comments
 (0)