Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
*/
package org.apache.cloudstack.storage.driver;

import com.cloud.agent.api.Answer;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.host.Host;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.Storage;
import com.cloud.storage.StoragePool;
import com.cloud.storage.Volume;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
Expand All @@ -37,18 +42,38 @@
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.feign.model.Lun;
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
import org.apache.cloudstack.storage.service.SANStrategy;
import org.apache.cloudstack.storage.service.StorageStrategy;
import org.apache.cloudstack.storage.feign.model.OntapStorage;
import org.apache.cloudstack.storage.utils.Constants;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.inject.Inject;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.cloud.hypervisor.Hypervisor.HypervisorType.KVM;
import static com.cloud.hypervisor.Hypervisor.HypervisorType.VMware;

public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {

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

@Inject
private StoragePoolDetailsDao storagePoolDetailsDao;
@Inject private PrimaryDataStoreDao storagePoolDao;

@Override
public Map<String, String> getCapabilities() {
s_logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
Map<String, String> mapCapabilities = new HashMap<>();

mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString());
Expand All @@ -68,9 +93,88 @@ public DataStoreTO getStoreTO(DataStore store) {
}

@Override
public void createAsync(DataStore store, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) {
CreateCmdResult createCmdResult = null;
String path = null;
String errMsg = null;
if (dataStore == null) {
throw new InvalidParameterValueException("createAsync: dataStore should not be null");
}
if (dataObject == null) {
throw new InvalidParameterValueException("createAsync: dataObject should not be null");
}
if (callback == null) {
throw new InvalidParameterValueException("createAsync: callback should not be null");
}

try {
logger.info("createAsync starting for data store [{}] and data object [{}] of type [{}]",
dataStore, dataObject, dataObject.getType());
if (dataObject.getType() == DataObjectType.VOLUME) {
path = createCloudStackVolume(dataStore.getId(), dataObject);
createCmdResult = new CreateCmdResult(path, new Answer(null, true, null));
} else {
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
} catch (Exception e) {
errMsg = e.getMessage();
logger.error("createAsync failed for dataObject [{}]: {}", dataObject, errMsg);
createCmdResult = new CreateCmdResult(null, new Answer(null, false, errMsg));
createCmdResult.setResult(e.toString());
} finally {
logger.info("createAsync completed");
callback.complete(createCmdResult);
}
}

s_logger.trace("OntapPrimaryDatastoreDriver: createAsync: Store: "+store+", data: "+data);
private String createCloudStackVolume(long storagePoolId, DataObject dataObject) {
logger.info("createCloudStackVolume starting for storagePoolId {} and data object [{}]", storagePoolId, dataObject);
String path = null;
StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
if(storagePool == null) {
throw new CloudRuntimeException("createCloudStackVolume : Storage Pool not found for id: " + storagePoolId);
}
List<String> keyList = Arrays.asList(Constants.USERNAME, Constants.PROTOCOL, Constants.IS_DISAGGREGATED, Constants.PASSWORD, Constants.MANAGEMENT_LIF,
Constants.SVM_NAME, Constants.NAME);
Map<String, String> storagePoolDetailMap = storagePoolDetailsDao.listDetailsKeyPairs(storagePoolId, keyList);
OntapStorage ontapStorage = new OntapStorage(storagePoolDetailMap.get(Constants.USERNAME), storagePoolDetailMap.get(Constants.PASSWORD),
storagePoolDetailMap.get(Constants.MANAGEMENT_LIF), storagePoolDetailMap.get(Constants.SVM_NAME), Constants.ProtocolType.valueOf(storagePoolDetailMap.get(Constants.PROTOCOL)),
Boolean.parseBoolean(storagePoolDetailMap.get(Constants.IS_DISAGGREGATED)));
StorageStrategy storageStrategy = StorageProviderFactory.createStrategy(ontapStorage);
boolean isValid = storageStrategy.connect();
if (isValid) {
String svmName = ontapStorage.getSvmName();
String lunOrFileName = dataObject.getName();
Long size = dataObject.getSize();
String volName = storagePool.getName();
String hypervisorType = storagePool.getHypervisor().name();
String osType = null;
switch (hypervisorType) {
case Constants.KVM:
osType = Lun.OsTypeEnum.LINUX.getValue();
break;
default:
String errMsg = "createCloudStackVolume : Unsupported hypervisor type " + hypervisorType + " for ONTAP storage";
logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}

// Create LUN based on protocol
if (ontapStorage.getProtocol().equals(Constants.ISCSI)) {
SANStrategy sanStrategy = StorageProviderFactory.getSANStrategy(ontapStorage);
Lun lun = sanStrategy.createLUN(svmName, volName, lunOrFileName, size , osType);
if(lun.getName() == null || lun.getName().isEmpty()) {
throw new CloudRuntimeException("createCloudStackVolume : LUN Name is invalid");
}
path = lun.getName();
}
} else {
throw new CloudRuntimeException("createCloudStackVolume : ONTAP details validation failed, cannot connect to ONTAP cluster");
}
logger.info("createCloudStackVolume completed with path {}", path);
return path;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.net.URI;
import java.util.Map;

@FeignClient(name = "SvmClient", url = "https://{clusterIP}/api/svm/svms", configuration = FeignConfiguration.class)
public interface SvmFeignClient {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,67 +22,67 @@
import org.apache.cloudstack.storage.utils.Constants.ProtocolType;

public class OntapStorage {
public static String _username;
public static String _password;
public static String _managementLIF;
public static String _svmName;
public static ProtocolType _protocolType;
public static Boolean _isDisaggregated;
public static String username;
public static String password;
public static String managementLIF;
public static String svmName;
public static ProtocolType protocolType;
public static Boolean isDisaggregated;

public OntapStorage(String username, String password, String managementLIF, String svmName, ProtocolType protocolType, Boolean isDisaggregated) {
_username = username;
_password = password;
_managementLIF = managementLIF;
_svmName = svmName;
_protocolType = protocolType;
_isDisaggregated = isDisaggregated;
this.username = username;
this.password = password;
this.managementLIF = managementLIF;
this.svmName = svmName;
this.protocolType = protocolType;
this.isDisaggregated = isDisaggregated;
}

public String getUsername() {
return _username;
return username;
}

public void setUsername(String username) {
_username = username;
username = username;
}

public String getPassword() {
return _password;
return password;
}

public void setPassword(String password) {
_password = password;
password = password;
}

public String getManagementLIF() {
return _managementLIF;
return managementLIF;
}

public void setManagementLIF(String managementLIF) {
_managementLIF = managementLIF;
managementLIF = managementLIF;
}

public String getSvmName() {
return _svmName;
return svmName;
}

public void setSvmName(String svmName) {
_svmName = svmName;
svmName = svmName;
}

public ProtocolType getProtocol() {
return _protocolType;
return protocolType;
}

public void setProtocol(ProtocolType protocolType) {
_protocolType = protocolType;
protocolType = protocolType;
}

public Boolean getIsDisaggregated() {
return _isDisaggregated;
return isDisaggregated;
}

public void setIsDisaggregated(Boolean isDisaggregated) {
_isDisaggregated = isDisaggregated;
isDisaggregated = isDisaggregated;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public DataStore initialize(Map<String, Object> dsInfos) {
OntapStorage ontapStorage = new OntapStorage(details.get(Constants.USERNAME), details.get(Constants.PASSWORD),
details.get(Constants.MANAGEMENT_LIF), details.get(Constants.SVM_NAME), protocol,
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED)));
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
StorageStrategy storageStrategy = StorageProviderFactory.createStrategy(ontapStorage);
boolean isValid = storageStrategy.connect();
if (isValid) {
// String volumeName = storagePoolName + "_vol"; //TODO: Figure out a better naming convention
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,45 @@
import org.apache.cloudstack.storage.service.StorageStrategy;
import org.apache.cloudstack.storage.service.UnifiedNASStrategy;
import org.apache.cloudstack.storage.service.UnifiedSANStrategy;
import org.apache.cloudstack.storage.utils.Constants;
import org.apache.cloudstack.storage.service.SANStrategy;
import org.apache.cloudstack.storage.service.NASStrategy;
import org.apache.cloudstack.storage.utils.Constants.ProtocolType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

@Component
public class StorageProviderFactory {
private final StorageStrategy storageStrategy;
private static final Logger s_logger = (Logger) LogManager.getLogger(StorageProviderFactory.class);

private StorageProviderFactory(OntapStorage ontapStorage) {
private StorageProviderFactory() {}

public static StorageStrategy createStrategy(OntapStorage ontapStorage) {
ProtocolType protocol = ontapStorage.getProtocol();
s_logger.info("Initializing StorageProviderFactory with protocol: " + protocol);
s_logger.info("Initializing StorageProviderFactory with protocol: {}", protocol);
switch (protocol) {
case NFS:
if(!ontapStorage.getIsDisaggregated()) {
this.storageStrategy = new UnifiedNASStrategy(ontapStorage);
return new UnifiedNASStrategy(ontapStorage);
} else {
throw new CloudRuntimeException("Unsupported configuration: Disaggregated ONTAP is not supported.");
}
break;
case ISCSI:
if (!ontapStorage.getIsDisaggregated()) {
this.storageStrategy = new UnifiedSANStrategy(ontapStorage);
return new UnifiedSANStrategy(ontapStorage);
} else {
throw new CloudRuntimeException("Unsupported configuration: Disaggregated ONTAP is not supported.");
}
break;
default:
this.storageStrategy = null;
throw new CloudRuntimeException("Unsupported protocol: " + protocol);
throw new CloudRuntimeException("Unsupported configuration: " + protocol);
}
}

public static StorageStrategy getStrategy(OntapStorage ontapStorage) {
return new StorageProviderFactory(ontapStorage).storageStrategy;
public static SANStrategy getSANStrategy(OntapStorage ontapStorage) {
return (SANStrategy) createStrategy(ontapStorage);
}

public static NASStrategy getNASStrategy(OntapStorage ontapStorage) {
return (NASStrategy) createStrategy(ontapStorage);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@

package org.apache.cloudstack.storage.service;

import org.apache.cloudstack.storage.feign.model.Lun;
import org.apache.cloudstack.storage.feign.model.OntapStorage;

public abstract class SANStrategy extends StorageStrategy {
public SANStrategy(OntapStorage ontapStorage) {
super(ontapStorage);
}

public abstract String createLUN(String svmName, String volumeName, String lunName, long sizeBytes, String osType);
public abstract Lun createLUN(String svmName, String volumeName, String lunName, long sizeBytes, String osType);
public abstract String createIgroup(String svmName, String igroupName, String[] initiators);
public abstract String mapLUNToIgroup(String lunName, String igroupName);
public abstract String enableISCSI(String svmUuid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import org.apache.logging.log4j.Logger;

import javax.inject.Inject;
import java.util.Map;
import java.net.URI;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -102,7 +101,7 @@ public boolean connect() {
this.aggregates = aggrs;
s_logger.info("Successfully connected to ONTAP cluster and validated ONTAP details provided");
} catch (Exception e) {
throw new CloudRuntimeException("Failed to connect to ONTAP cluster: " + e.getMessage());
throw new CloudRuntimeException("Failed to connect to ONTAP cluster: " + e.getMessage());
}
return true;
}
Expand Down
Loading
Loading