Skip to content

Commit 0244569

Browse files
Gupta, SuryaGupta, Surya
authored andcommitted
CSTACKEX-36 Attach Cluster and Grant Access
1 parent 6c4b24e commit 0244569

File tree

8 files changed

+444
-83
lines changed

8 files changed

+444
-83
lines changed

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

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
import com.cloud.exception.InvalidParameterValueException;
2626
import com.cloud.host.Host;
2727
import com.cloud.storage.Storage;
28-
import com.cloud.storage.StoragePool;
28+
import com.cloud.storage.ScopeType;
29+
import com.cloud.storage.VolumeVO;
2930
import com.cloud.storage.Volume;
31+
import com.cloud.storage.StoragePool;
32+
import com.cloud.storage.dao.VolumeDao;
3033
import com.cloud.utils.Pair;
3134
import com.cloud.utils.exception.CloudRuntimeException;
3235
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
@@ -45,6 +48,7 @@
4548
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
4649
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
4750
import org.apache.cloudstack.storage.service.StorageStrategy;
51+
import org.apache.cloudstack.storage.service.model.AccessGroup;
4852
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
4953
import org.apache.cloudstack.storage.service.model.ProtocolType;
5054
import org.apache.cloudstack.storage.utils.Constants;
@@ -56,13 +60,14 @@
5660
import java.util.HashMap;
5761
import java.util.Map;
5862

59-
public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {
63+
public abstract class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {
6064

6165
private static final Logger s_logger = LogManager.getLogger(OntapPrimaryDatastoreDriver.class);
6266

6367
@Inject private Utility utils;
6468
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
6569
@Inject private PrimaryDataStoreDao storagePoolDao;
70+
@Inject private VolumeDao volumeDao;
6671
@Override
6772
public Map<String, String> getCapabilities() {
6873
s_logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
@@ -99,10 +104,16 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
99104
throw new InvalidParameterValueException("createAsync: callback should not be null");
100105
}
101106
try {
102-
s_logger.info("createAsync: Started for data store [{}] and data object [{}] of type [{}]",
103-
dataStore, dataObject, dataObject.getType());
107+
s_logger.info("createAsync: Started for data store [{}] and data object [{}] of type [{}]", dataStore, dataObject, dataObject.getType());
108+
109+
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
110+
if(storagePool == null) {
111+
s_logger.error("createCloudStackVolume : Storage Pool not found for id: " + dataStore.getId());
112+
throw new CloudRuntimeException("createCloudStackVolume : Storage Pool not found for id: " + dataStore.getId());
113+
}
114+
104115
if (dataObject.getType() == DataObjectType.VOLUME) {
105-
path = createCloudStackVolumeForTypeVolume(dataStore, dataObject);
116+
path = createCloudStackVolumeForTypeVolume(storagePool, dataObject);
106117
createCmdResult = new CreateCmdResult(path, new Answer(null, true, null));
107118
} else {
108119
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
@@ -119,13 +130,8 @@ public void createAsync(DataStore dataStore, DataObject dataObject, AsyncComplet
119130
}
120131
}
121132

122-
private String createCloudStackVolumeForTypeVolume(DataStore dataStore, DataObject dataObject) {
123-
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
124-
if(storagePool == null) {
125-
s_logger.error("createCloudStackVolume : Storage Pool not found for id: " + dataStore.getId());
126-
throw new CloudRuntimeException("createCloudStackVolume : Storage Pool not found for id: " + dataStore.getId());
127-
}
128-
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(dataStore.getId());
133+
private String createCloudStackVolumeForTypeVolume(StoragePoolVO storagePool, DataObject dataObject) {
134+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
129135
StorageStrategy storageStrategy = utils.getStrategyByStoragePoolDetails(details);
130136
s_logger.info("createCloudStackVolumeForTypeVolume: Connection to Ontap SVM [{}] successful, preparing CloudStackVolumeRequest", details.get(Constants.SVM_NAME));
131137
CloudStackVolume cloudStackVolumeRequest = utils.createCloudStackVolumeRequestByProtocol(storagePool, details, dataObject);
@@ -171,9 +177,83 @@ public ChapInfo getChapInfo(DataObject dataObject) {
171177

172178
@Override
173179
public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) {
180+
if (dataStore == null) {
181+
throw new InvalidParameterValueException("grantAccess: dataStore should not be null");
182+
}
183+
if (dataObject == null) {
184+
throw new InvalidParameterValueException("grantAccess: dataObject should not be null");
185+
}
186+
if (host == null) {
187+
throw new InvalidParameterValueException("grantAccess: host should not be null");
188+
}
189+
try {
190+
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
191+
if(storagePool == null) {
192+
s_logger.error("grantAccess : Storage Pool not found for id: " + dataStore.getId());
193+
throw new CloudRuntimeException("grantAccess : Storage Pool not found for id: " + dataStore.getId());
194+
}
195+
if (storagePool.getScope() != ScopeType.CLUSTER || storagePool.getScope() != ScopeType.ZONE) {
196+
s_logger.error("grantAccess: Only Cluster and ZONE scoped primary storage is supported. Storage Pool: " + storagePool.getName());
197+
throw new CloudRuntimeException("grantAccess: Only Cluster and ZONE scoped primary storage is supported. Storage Pool: " + storagePool.getName());
198+
}
199+
200+
VolumeVO volumeVO = volumeDao.findById(dataObject.getId());
201+
if(volumeVO == null) {
202+
s_logger.error("grantAccess : Cloud Stack Volume not found for id: " + dataObject.getId());
203+
throw new CloudRuntimeException("grantAccess : Cloud Stack Volume not found for id: " + dataObject.getId());
204+
}
205+
206+
if (dataObject.getType() == DataObjectType.VOLUME) {
207+
grantAccessForVolume(storagePool, volumeVO, host);
208+
} else {
209+
s_logger.error("Invalid DataObjectType (" + dataObject.getType() + ") passed to grantAccess");
210+
throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to grantAccess");
211+
}
212+
} catch(Exception e){
213+
s_logger.error("grantAccess: Failed for dataObject [{}]: {}", dataObject, e.getMessage());
214+
throw new CloudRuntimeException("grantAccess: Failed with error :" + e.getMessage());
215+
}
174216
return true;
175217
}
176218

219+
private void grantAccessForVolume(StoragePoolVO storagePool, VolumeVO volumeVO, Host host) {
220+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(storagePool.getId());
221+
StorageStrategy storageStrategy = utils.getStrategyByStoragePoolDetails(details);
222+
String svmName = details.get(Constants.SVM_NAME);
223+
224+
if(ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(Constants.PROTOCOL))) {
225+
Map<String, String> getCloudStackVolumeMap = new HashMap<>();
226+
getCloudStackVolumeMap.put(Constants.NAME, volumeVO.getPath());
227+
getCloudStackVolumeMap.put(Constants.SVM_DOT_NAME, svmName);
228+
CloudStackVolume cloudStackVolume = storageStrategy.getCloudStackVolume(getCloudStackVolumeMap);
229+
if(cloudStackVolume == null ||cloudStackVolume.getLun() == null || cloudStackVolume.getLun().getName() == null) {
230+
s_logger.error("grantAccess: Failed to get LUN details [{}]", volumeVO.getName());
231+
throw new CloudRuntimeException("grantAccess: Failed to get LUN [" + volumeVO.getName() + "]");
232+
}
233+
234+
long scopeId = (storagePool.getScope() == ScopeType.CLUSTER) ? host.getClusterId() : host.getDataCenterId();
235+
String igroupName = utils.getIgroupName(storagePool.getName(), scopeId);
236+
Map<String, String> getAccessGroupMap = new HashMap<>();
237+
getAccessGroupMap.put(Constants.NAME, igroupName);
238+
getAccessGroupMap.put(Constants.SVM_DOT_NAME, svmName);
239+
AccessGroup accessGroup = storageStrategy.getAccessGroup(getAccessGroupMap);
240+
if (accessGroup == null || accessGroup.getIgroup() == null || accessGroup.getIgroup().getName() == null) {
241+
s_logger.error("grantAccess: Failed to get iGroup details for host [{}]", host.getName());
242+
throw new CloudRuntimeException("grantAccess: Failed to get iGroup details for host [" + host.getName() + "]");
243+
}
244+
if(!accessGroup.getIgroup().getInitiators().contains(host.getStorageUrl())) {
245+
s_logger.error("grantAccess: initiator [{}] is not present in iGroup [{}]", host.getStorageUrl(), igroupName);
246+
throw new CloudRuntimeException("grantAccess: initiator [" + host.getStorageUrl() + "] is not present in iGroup [" + igroupName);
247+
}
248+
249+
Map<String, String> enableLogicalAccessMap = new HashMap<>();
250+
enableLogicalAccessMap.put(Constants.LUN_DOT_NAME, volumeVO.getPath());
251+
enableLogicalAccessMap.put(Constants.SVM_DOT_NAME, svmName);
252+
enableLogicalAccessMap.put(Constants.IGROUP_DOT_NAME, igroupName);
253+
storageStrategy.enableLogicalAccess(enableLogicalAccessMap);
254+
}
255+
}
256+
177257
@Override
178258
public void revokeAccess(DataObject dataObject, Host host, DataStore dataStore) {
179259

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,17 @@ OntapResponse<Igroup> createIgroup(URI uri, @RequestHeader("Authorization") Stri
6565

6666
//this method to get all igroups and also filtered igroups based on query params as a part of URL
6767
@RequestMapping(method = RequestMethod.GET)
68-
OntapResponse<Igroup> getIgroupResponse(URI baseURL, @RequestHeader("Authorization") String header, @PathVariable(name = "uuid", required = true) String uuid);
68+
OntapResponse<Igroup> getIgroupResponse(URI baseURL, @RequestHeader("Authorization") String header);
6969
@RequestMapping(method = RequestMethod.GET, value = "/{uuid}")
7070
Igroup getIgroupByUUID(URI baseURL, @RequestHeader("Authorization") String header, @PathVariable(name = "uuid", required = true) String uuid);
7171
@RequestMapping(method = RequestMethod.DELETE, value = "/{uuid}")
7272
void deleteIgroup(URI baseUri, @RequestHeader("Authorization") String authHeader, @PathVariable(name = "uuid", required = true) String uuid);
7373

74-
@RequestMapping(method = RequestMethod.POST, value = "/{uuid}/igroups")
75-
OntapResponse<Igroup> addNestedIgroups(URI uri, @RequestHeader("Authorization") String header, @PathVariable(name = "uuid", required = true) String uuid,
76-
@RequestBody Igroup igroupNestedRequest, @RequestHeader(value="return_records", defaultValue = "true") boolean value);
77-
78-
7974
//Lun Maps Operation APIs
8075

8176
@RequestMapping(method = RequestMethod.POST)
82-
OntapResponse<LunMap> createLunMap(URI baseURL, @RequestHeader("Authorization") String authHeader, @RequestBody LunMap lunMap);
77+
OntapResponse<LunMap> createLunMap(URI baseURL, @RequestHeader("Authorization") String authHeader, @RequestHeader("return_records") boolean value,
78+
@RequestBody LunMap lunMap);
8379

8480
@RequestMapping(method = RequestMethod.GET)
8581
OntapResponse<LunMap> getLunMapResponse(URI baseURL, @RequestHeader("Authorization") String authHeader);

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

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.cloud.agent.api.StoragePoolInfo;
2424
import com.cloud.dc.ClusterVO;
2525
import com.cloud.dc.dao.ClusterDao;
26+
import com.cloud.exception.InvalidParameterValueException;
2627
import com.cloud.host.HostVO;
2728
import com.cloud.hypervisor.Hypervisor;
2829
import com.cloud.resource.ResourceManager;
@@ -38,17 +39,23 @@
3839
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
3940
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
4041
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
42+
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
43+
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
44+
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
4145
import org.apache.cloudstack.storage.datastore.lifecycle.BasePrimaryDataStoreLifeCycleImpl;
4246
import org.apache.cloudstack.storage.feign.model.OntapStorage;
4347
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
4448
import org.apache.cloudstack.storage.service.StorageStrategy;
49+
import org.apache.cloudstack.storage.service.model.AccessGroup;
4550
import org.apache.cloudstack.storage.service.model.ProtocolType;
4651
import org.apache.cloudstack.storage.utils.Constants;
52+
import org.apache.cloudstack.storage.utils.Utility;
4753
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
4854
import org.apache.logging.log4j.LogManager;
4955
import org.apache.logging.log4j.Logger;
5056

5157
import javax.inject.Inject;
58+
import java.util.ArrayList;
5259
import java.util.List;
5360
import java.util.Map;
5461
import java.util.UUID;
@@ -58,7 +65,10 @@ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycl
5865
@Inject private StorageManager _storageMgr;
5966
@Inject private ResourceManager _resourceMgr;
6067
@Inject private PrimaryDataStoreHelper _dataStoreHelper;
61-
private static final Logger s_logger = (Logger)LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);
68+
@Inject private Utility utils;
69+
@Inject private PrimaryDataStoreDao storagePoolDao;
70+
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
71+
private static final Logger s_logger = LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);
6272

6373
/**
6474
* Creates primary storage on NetApp storage
@@ -167,12 +177,34 @@ public DataStore initialize(Map<String, Object> dsInfos) {
167177
@Override
168178
public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
169179
logger.debug("In attachCluster for ONTAP primary storage");
180+
if (dataStore == null) {
181+
throw new InvalidParameterValueException("attachCluster: dataStore should not be null");
182+
}
183+
if (scope == null) {
184+
throw new InvalidParameterValueException("attachCluster: clusterScope should not be null");
185+
}
186+
List<String> hostsIdentifier = new ArrayList<>();
187+
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
188+
if(storagePool == null) {
189+
s_logger.error("attachCluster : Storage Pool not found for id: " + dataStore.getId());
190+
throw new CloudRuntimeException("attachCluster : Storage Pool not found for id: " + dataStore.getId());
191+
}
170192
PrimaryDataStoreInfo primarystore = (PrimaryDataStoreInfo)dataStore;
171193
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInClusterForStorageConnection(primarystore);
172-
194+
// TODO- need to check if no host to connect then throw exception or just continue
173195
logger.debug(String.format("Attaching the pool to each of the hosts %s in the cluster: %s", hostsToConnect, primarystore.getClusterId()));
196+
197+
Map<String, String> details = primarystore.getDetails();
198+
StorageStrategy strategy = utils.getStrategyByStoragePoolDetails(details);
199+
ProtocolType protocol = ProtocolType.valueOf(details.get(Constants.PROTOCOL));
200+
if (!isProtocolSupportedByAllHosts(hostsToConnect, protocol, hostsIdentifier)) {
201+
throw new CloudRuntimeException("Not all hosts in the cluster support the protocol: " + protocol.toString());
202+
}
203+
if (hostsIdentifier != null && hostsIdentifier.size() > 0) {
204+
AccessGroup accessGroupRequest = utils.createAccessGroupRequestByProtocol(storagePool, scope.getScopeId(), details, hostsIdentifier);
205+
strategy.createAccessGroup(accessGroupRequest);
206+
}
174207
for (HostVO host : hostsToConnect) {
175-
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
176208
try {
177209
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
178210
} catch (Exception e) {
@@ -183,6 +215,25 @@ public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
183215
return true;
184216
}
185217

218+
private boolean isProtocolSupportedByAllHosts(List<HostVO> hosts, ProtocolType protocolType, List<String> hostIdentifiers) {
219+
String protocolPrefix;
220+
switch (protocolType) {
221+
case ISCSI:
222+
protocolPrefix = Constants.IQN;
223+
for (HostVO host : hosts) {
224+
if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().isEmpty()
225+
|| !host.getStorageUrl().startsWith(protocolPrefix)) {
226+
return false;
227+
}
228+
hostIdentifiers.add(host.getStorageUrl());
229+
}
230+
break;
231+
default:
232+
throw new CloudRuntimeException("Unsupported protocol: " + protocolType.toString());
233+
}
234+
return true;
235+
}
236+
186237
@Override
187238
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
188239
return false;
@@ -191,9 +242,32 @@ public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo exis
191242
@Override
192243
public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.HypervisorType hypervisorType) {
193244
logger.debug("In attachZone for ONTAP primary storage");
245+
if (dataStore == null) {
246+
throw new InvalidParameterValueException("attachZone: dataStore should not be null");
247+
}
248+
if (scope == null) {
249+
throw new InvalidParameterValueException("attachZone: clusterScope should not be null");
250+
}
251+
List<String> hostsIdentifier = new ArrayList<>();
252+
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
253+
if(storagePool == null) {
254+
s_logger.error("attachCluster : Storage Pool not found for id: " + dataStore.getId());
255+
throw new CloudRuntimeException("attachCluster : Storage Pool not found for id: " + dataStore.getId());
256+
}
194257
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), Hypervisor.HypervisorType.KVM);
195-
258+
// TODO- need to check if no host to connect then throw exception or just continue
196259
logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
260+
261+
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(dataStore.getId());
262+
StorageStrategy strategy = utils.getStrategyByStoragePoolDetails(details);
263+
ProtocolType protocol = ProtocolType.valueOf(details.get(Constants.PROTOCOL));
264+
if (!isProtocolSupportedByAllHosts(hostsToConnect, protocol, hostsIdentifier)) {
265+
throw new CloudRuntimeException("Not all hosts in the zone support the protocol: " + protocol.toString());
266+
}
267+
if (hostsIdentifier != null && !hostsIdentifier.isEmpty()) {
268+
AccessGroup accessGroupRequest = utils.createAccessGroupRequestByProtocol(storagePool, scope.getScopeId(), details, hostsIdentifier);
269+
strategy.createAccessGroup(accessGroupRequest);
270+
}
197271
for (HostVO host : hostsToConnect) {
198272
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
199273
try {

0 commit comments

Comments
 (0)