Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.cloudstack.storage.feign.client;

import feign.QueryMap;
import org.apache.cloudstack.storage.feign.model.Igroup;
import org.apache.cloudstack.storage.feign.model.Lun;
import org.apache.cloudstack.storage.feign.model.LunMap;
Expand All @@ -26,6 +27,7 @@
import feign.Param;
import feign.RequestLine;
import java.net.URI;
import java.util.Map;

//TODO: Proper URLs should be added in the RequestLine annotations below
public interface SANFeignClient {
Expand Down Expand Up @@ -54,15 +56,14 @@ OntapResponse<Lun> createLun(@Param("authHeader") String authHeader,
void deleteLun(@Param("authHeader") String authHeader, @Param("uuid") String uuid);

// iGroup Operation APIs
@RequestLine("POST /")
@RequestLine("POST /api/protocols/san/igroups")
@Headers({"Authorization: {authHeader}", "return_records: {returnRecords}"})
OntapResponse<Igroup> createIgroup(@Param("authHeader") String authHeader,
@Param("returnRecords") boolean returnRecords,
Igroup igroupRequest);
@Param("returnRecords") boolean returnRecords, Igroup igroupRequest);

@RequestLine("GET /")
@Headers({"Authorization: {authHeader}"}) // TODO: Check this again, uuid should be part of the path?
OntapResponse<Igroup> getIgroupResponse(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
@RequestLine("GET /api/protocols/san/igroups")
@Headers({"Authorization: {authHeader}"})
OntapResponse<Igroup> getIgroupResponse(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryMap);

@RequestLine("GET /{uuid}")
@Headers({"Authorization: {authHeader}"})
Expand All @@ -73,17 +74,18 @@ OntapResponse<Igroup> createIgroup(@Param("authHeader") String authHeader,
void deleteIgroup(@Param("baseUri") URI baseUri, @Param("authHeader") String authHeader, @Param("uuid") String uuid);

// LUN Maps Operation APIs
@RequestLine("POST /")
@Headers({"Authorization: {authHeader}"})
OntapResponse<LunMap> createLunMap(@Param("authHeader") String authHeader, LunMap lunMap);
@RequestLine("POST /api/protocols/san/lun-maps")
@Headers({"Authorization: {authHeader}", "return_records: {returnRecords}"})
OntapResponse<LunMap> createLunMap(@Param("authHeader") String authHeader, @Param("returnRecords") boolean returnRecords, LunMap lunMap);


@RequestLine("GET /")
@Headers({"Authorization: {authHeader}"})
OntapResponse<LunMap> getLunMapResponse(@Param("authHeader") String authHeader);

@RequestLine("DELETE /{lunUuid}/{igroupUuid}")
@RequestLine("DELETE /api/protocols/san/lun-maps/{lunUuid}/{igroupUuid}")
@Headers({"Authorization: {authHeader}"})
void deleteLunMap(@Param("authHeader") String authHeader,
@Param("lunUuid") String lunUuid,
@Param("igroupUuid") String igroupUuid);
@Param("lunUuid") String lunUUID,
@Param("igroupUuid") String igroupUUID);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.HostVO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.resource.ResourceManager;
Expand All @@ -38,17 +39,23 @@
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.lifecycle.BasePrimaryDataStoreLifeCycleImpl;
import org.apache.cloudstack.storage.feign.model.OntapStorage;
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
import org.apache.cloudstack.storage.service.StorageStrategy;
import org.apache.cloudstack.storage.service.model.AccessGroup;
import org.apache.cloudstack.storage.service.model.ProtocolType;
import org.apache.cloudstack.storage.utils.Constants;
import org.apache.cloudstack.storage.utils.Utility;
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -59,6 +66,8 @@ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycl
@Inject private StorageManager _storageMgr;
@Inject private ResourceManager _resourceMgr;
@Inject private PrimaryDataStoreHelper _dataStoreHelper;
@Inject private PrimaryDataStoreDao storagePoolDao;
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
private static final Logger s_logger = LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);

// ONTAP minimum volume size is 1.56 GB (1677721600 bytes)
Expand Down Expand Up @@ -241,16 +250,42 @@ public DataStore initialize(Map<String, Object> dsInfos) {
@Override
public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
logger.debug("In attachCluster for ONTAP primary storage");
PrimaryDataStoreInfo primarystore = (PrimaryDataStoreInfo)dataStore;
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInClusterForStorageConnection(primarystore);
if (dataStore == null) {
throw new InvalidParameterValueException("attachCluster: dataStore should not be null");
}
if (scope == null) {
throw new InvalidParameterValueException("attachCluster: scope should not be null");
}
List<String> hostsIdentifier = new ArrayList<>();
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
if(storagePool == null) {
s_logger.error("attachCluster : Storage Pool not found for id: " + dataStore.getId());
throw new CloudRuntimeException("attachCluster : Storage Pool not found for id: " + dataStore.getId());
}
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)dataStore;
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInClusterForStorageConnection(primaryStore);
// TODO- need to check if no host to connect then throw exception or just continue
logger.debug("attachCluster: Eligible Up and Enabled hosts: {} in cluster {}", hostsToConnect, primaryStore.getClusterId());

logger.debug(String.format("Attaching the pool to each of the hosts %s in the cluster: %s", hostsToConnect, primarystore.getClusterId()));
Map<String, String> details = primaryStore.getDetails();
StorageStrategy strategy = Utility.getStrategyByStoragePoolDetails(details);
ProtocolType protocol = ProtocolType.valueOf(details.get(Constants.PROTOCOL));
//TODO- Check if we have to handle heterogeneous host within the cluster
if (!isProtocolSupportedByAllHosts(hostsToConnect, protocol, hostsIdentifier)) {
s_logger.error("attachCluster: Not all hosts in the cluster support the protocol: " + protocol.name());
throw new CloudRuntimeException("attachCluster: Not all hosts in the cluster support the protocol: " + protocol.name());
}
//TODO - check if no host to connect then also need to create access group without initiators
if (hostsIdentifier != null && hostsIdentifier.size() > 0) {
AccessGroup accessGroupRequest = Utility.createAccessGroupRequestByProtocol(storagePool, scope.getScopeId(), details, hostsIdentifier);
strategy.createAccessGroup(accessGroupRequest);
}
logger.debug("attachCluster: Attaching the pool to each of the host in the cluster: {}", primaryStore.getClusterId());
for (HostVO host : hostsToConnect) {
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
try {
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
} catch (Exception e) {
logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
logger.warn("attachCluster: Unable to establish a connection between " + host + " and " + dataStore, e);
}
}
_dataStoreHelper.attachCluster(dataStore);
Expand All @@ -265,11 +300,35 @@ public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo exis
@Override
public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.HypervisorType hypervisorType) {
logger.debug("In attachZone for ONTAP primary storage");
if (dataStore == null) {
throw new InvalidParameterValueException("attachZone: dataStore should not be null");
}
if (scope == null) {
throw new InvalidParameterValueException("attachZone: scope should not be null");
}
List<String> hostsIdentifier = new ArrayList<>();
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
if(storagePool == null) {
s_logger.error("attachZone : Storage Pool not found for id: " + dataStore.getId());
throw new CloudRuntimeException("attachZone : Storage Pool not found for id: " + dataStore.getId());
}
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), Hypervisor.HypervisorType.KVM);
// TODO- need to check if no host to connect then throw exception or just continue
logger.debug("attachZone: Eligible Up and Enabled hosts: {}", hostsToConnect);

logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(dataStore.getId());
StorageStrategy strategy = Utility.getStrategyByStoragePoolDetails(details);
ProtocolType protocol = ProtocolType.valueOf(details.get(Constants.PROTOCOL));
//TODO- Check if we have to handle heterogeneous host within the zone
if (!isProtocolSupportedByAllHosts(hostsToConnect, protocol, hostsIdentifier)) {
s_logger.error("attachZone: Not all hosts in the cluster support the protocol: " + protocol.name());
throw new CloudRuntimeException("attachZone: Not all hosts in the zone support the protocol: " + protocol.name());
}
if (hostsIdentifier != null && !hostsIdentifier.isEmpty()) {
AccessGroup accessGroupRequest = Utility.createAccessGroupRequestByProtocol(storagePool, scope.getScopeId(), details, hostsIdentifier);
strategy.createAccessGroup(accessGroupRequest);
}
for (HostVO host : hostsToConnect) {
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
try {
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
} catch (Exception e) {
Expand All @@ -280,6 +339,24 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.Hyper
return true;
}

private boolean isProtocolSupportedByAllHosts(List<HostVO> hosts, ProtocolType protocolType, List<String> hostIdentifiers) {
switch (protocolType) {
case ISCSI:
String protocolPrefix = Constants.IQN;
for (HostVO host : hosts) {
if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().isEmpty()
|| !host.getStorageUrl().startsWith(protocolPrefix)) {
return false;
}
hostIdentifiers.add(host.getStorageUrl());
}
break;
default:
throw new CloudRuntimeException("isProtocolSupportedByAllHosts : Unsupported protocol: " + protocolType.name());
}
return true;
}

@Override
public boolean maintain(DataStore store) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,10 @@ public Volume getStorageVolume(Volume volume)
* getLun for iSCSI, FC protocols
* getFile for NFS3.0 and NFS4.1 protocols
* getNameSpace for Nvme/TCP and Nvme/FC protocol
* @param cloudstackVolume the CloudStack volume to retrieve
* @param cloudStackVolumeMap the CloudStack volume to retrieve
* @return the retrieved CloudStackVolume object
*/
abstract CloudStackVolume getCloudStackVolume(CloudStackVolume cloudstackVolume);
abstract public CloudStackVolume getCloudStackVolume(Map<String, String> cloudStackVolumeMap);

/**
* Method encapsulates the behavior based on the opted protocol in subclasses
Expand All @@ -287,7 +287,7 @@ public Volume getStorageVolume(Volume volume)
* @param accessGroup the access group to create
* @return the created AccessGroup object
*/
abstract AccessGroup createAccessGroup(AccessGroup accessGroup);
abstract public AccessGroup createAccessGroup(AccessGroup accessGroup);

/**
* Method encapsulates the behavior based on the opted protocol in subclasses
Expand All @@ -310,27 +310,25 @@ public Volume getStorageVolume(Volume volume)

/**
* Method encapsulates the behavior based on the opted protocol in subclasses
* getiGroup for iSCSI and FC protocols
* getExportPolicy for NFS 3.0 and NFS 4.1 protocols
* getNameSpace for Nvme/TCP and Nvme/FC protocols
* @param accessGroup the access group to retrieve
* @return the retrieved AccessGroup object
@@ -306,22 +306,22 @@ public Volume getStorageVolume(Volume volume)
* getNameSpace for Nvme/TCP and Nvme/FC protocols
* @param values
*/
abstract AccessGroup getAccessGroup(AccessGroup accessGroup);
abstract public AccessGroup getAccessGroup(Map<String, String> values);

/**
* Method encapsulates the behavior based on the opted protocol in subclasses
* lunMap for iSCSI and FC protocols
* //TODO for Nvme/TCP and Nvme/FC protocols
* @param values
*/
abstract void enableLogicalAccess(Map<String,String> values);
abstract public void enableLogicalAccess(Map<String,String> values);

/**
* Method encapsulates the behavior based on the opted protocol in subclasses
* lunUnmap for iSCSI and FC protocols
* //TODO for Nvme/TCP and Nvme/FC protocols
* @param values
*/
abstract void disableLogicalAccess(Map<String,String> values);
abstract public void disableLogicalAccess(Map<String,String> values);
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void deleteCloudStackVolume(CloudStackVolume cloudstackVolume) {
}

@Override
CloudStackVolume getCloudStackVolume(CloudStackVolume cloudstackVolume) {
public CloudStackVolume getCloudStackVolume(Map<String, String> cloudStackVolumeMap) {
//TODO
return null;
}
Expand All @@ -90,18 +90,18 @@ public AccessGroup updateAccessGroup(AccessGroup accessGroup) {
}

@Override
public AccessGroup getAccessGroup(AccessGroup accessGroup) {
public AccessGroup getAccessGroup(Map<String, String> values) {
//TODO
return null;
}

@Override
void enableLogicalAccess(Map<String, String> values) {
public void enableLogicalAccess(Map<String, String> values) {
//TODO
}

@Override
void disableLogicalAccess(Map<String, String> values) {
public void disableLogicalAccess(Map<String, String> values) {
//TODO
}
}
Loading
Loading