Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
*/
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.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,15 +41,30 @@
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.OntapStorage;
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
import org.apache.cloudstack.storage.service.StorageStrategy;
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

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

public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets remove this type cast

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!


@Inject private Utility utils;
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
@Inject private PrimaryDataStoreDao storagePoolDao;
@Override
public Map<String, String> getCapabilities() {
s_logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
Expand All @@ -68,9 +87,71 @@ 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 {
s_logger.info("createAsync: Volume creation starting for data store [{}] and data object [{}] of type [{}]",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change the logger

dataStore, dataObject, dataObject.getType());
if (dataObject.getType() == DataObjectType.VOLUME) {
path = createCloudStackVolumeForTypeVolume(dataStore, dataObject);
createCmdResult = new CreateCmdResult(path, new Answer(null, true, null));
} else {
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
s_logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
} catch (Exception e) {
errMsg = e.getMessage();
s_logger.error("createAsync: Volume creation failed for dataObject [{}]: {}", dataObject, errMsg);
createCmdResult = new CreateCmdResult(null, new Answer(null, false, errMsg));
createCmdResult.setResult(e.toString());
} finally {
callback.complete(createCmdResult);
}
}

s_logger.trace("OntapPrimaryDatastoreDriver: createAsync: Store: "+store+", data: "+data);
private String createCloudStackVolumeForTypeVolume(DataStore dataStore, DataObject dataObject) {
StoragePoolVO storagePool = storagePoolDao.findById(dataStore.getId());
if(storagePool == null) {
throw new CloudRuntimeException("createCloudStackVolume : Storage Pool not found for id: " + dataStore.getId());
}
Map<String, String> details = storagePoolDetailsDao.listDetailsKeyPairs(dataStore.getId());
if(details == null || details.isEmpty()) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

create util method for ontap connection

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

throw new CloudRuntimeException("createCloudStackVolume : Storage Details not found for id: " + dataStore.getId());
}
String protocol = details.get(Constants.PROTOCOL);
OntapStorage ontapStorage = new OntapStorage(details.get(Constants.USERNAME), details.get(Constants.PASSWORD),
details.get(Constants.MANAGEMENT_LIF), details.get(Constants.SVM_NAME), ProtocolType.valueOf(protocol),
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED)));
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
boolean isValid = storageStrategy.connect();
if (isValid) {
s_logger.info("createCloudStackVolumeForTypeVolume: Connection to Ontap SVM [{}] successful, preparing CloudStackVolumeRequest", details.get(Constants.SVM_NAME));
CloudStackVolume cloudStackVolumeRequest = utils.createCloudStackVolumeRequestByProtocol(storagePool, details, dataObject);
CloudStackVolume cloudStackVolume = storageStrategy.createCloudStackVolume(cloudStackVolumeRequest);
if (ProtocolType.ISCSI.name().equalsIgnoreCase(protocol) && cloudStackVolume.getLun() != null && cloudStackVolume.getLun().getName() != null) {
return cloudStackVolume.getLun().getName();
} else {
String errMsg = "createCloudStackVolumeForTypeVolume: Volume creation failed. Lun or Lun Path is null for dataObject: " + dataObject;
s_logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
} else {
String errMsg = "createCloudStackVolumeForTypeVolume: Connection to Ontap SVM [" + details.get(Constants.SVM_NAME) + "] failed";
s_logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public abstract class StorageStrategy {
@Inject
private JobFeignClient jobFeignClient;

private final OntapStorage storage;
protected final OntapStorage storage;

/**
* Presents aggregate object for the unified storage, not eligible for disaggregated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,59 @@

package org.apache.cloudstack.storage.service;

import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.storage.feign.client.SANFeignClient;
import org.apache.cloudstack.storage.feign.model.Lun;
import org.apache.cloudstack.storage.feign.model.OntapStorage;
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
import org.apache.cloudstack.storage.service.model.AccessGroup;
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
import org.apache.cloudstack.storage.utils.Constants;
import org.apache.cloudstack.storage.utils.Utility;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.inject.Inject;
import java.net.URI;
import java.util.Map;

public class UnifiedSANStrategy extends SANStrategy{
public class UnifiedSANStrategy extends SANStrategy {

private static final Logger s_logger = (Logger) LogManager.getLogger(UnifiedSANStrategy.class);
@Inject private Utility utils;
@Inject private SANFeignClient sanFeignClient;
public UnifiedSANStrategy(OntapStorage ontapStorage) {
super(ontapStorage);
}

@Override
public CloudStackVolume createCloudStackVolume(CloudStackVolume cloudstackVolume) {
//TODO
return null;
s_logger.info("createCloudStackVolume : Creating Lun with cloudstackVolume request {} ", cloudstackVolume);
if (cloudstackVolume == null || cloudstackVolume.getLun() == null) {
s_logger.error("createCloudStackVolume: LUN creation failed. Invalid cloudstackVolume request: {}", cloudstackVolume);
throw new CloudRuntimeException("createCloudStackVolume : Failed to create Lun, invalid cloudstackVolume request");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change invalid cloudstackVolume request logger change

}
try {
// Get AuthHeader
String authHeader = utils.generateAuthHeader(storage.getUsername(), storage.getPassword());
// Create URI for lun creation
URI url = utils.generateURI(Constants.CREATE_LUN);
OntapResponse<Lun> createdLun = sanFeignClient.createLun(url, authHeader, true, cloudstackVolume.getLun());
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add TODO, based on testing need to covert this createLun to Async

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

if (createdLun == null || createdLun.getRecords() == null || createdLun.getRecords().size() == 0) {
s_logger.error("createCloudStackVolume: LUN creation failed for Lun {}", cloudstackVolume.getLun().getName());
throw new CloudRuntimeException("Failed to create Lun: " + cloudstackVolume.getLun().getName());
}
Lun lun = createdLun.getRecords().get(0);
s_logger.debug("createCloudStackVolume: LUN created successfully. Lun: {}", lun);
s_logger.info("createCloudStackVolume: LUN created successfully. LunName: {}", lun.getName());

CloudStackVolume createdCloudStackVolume = new CloudStackVolume();
createdCloudStackVolume.setLun(lun);
return createdCloudStackVolume;
} catch (Exception e) {
s_logger.error("Exception occurred while creating LUN: {}. Exception: {}", cloudstackVolume.getLun().getName(), e.getMessage());
throw new CloudRuntimeException("Failed to create Lun: " + e.getMessage());
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,16 @@ public class Constants {
public static final int JOB_MAX_RETRIES = 100;
public static final int CREATE_VOLUME_CHECK_SLEEP_TIME = 2000;

public static final String PATH_SEPARATOR = "/";

public static final String VOLUME_PATH_PREFIX = "/vol/";

public static final String KVM = "KVM";

public static final String HTTPS = "https://";
public static final String GET_SVMs = "/api/svm/svms";
public static final String CREATE_VOLUME = "/api/storage/volumes";
public static final String GET_JOB_BY_UUID = "/api/cluster/jobs";
public static final String CREATE_LUN = "/api/storage/luns";

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,89 @@
package org.apache.cloudstack.storage.utils;

import com.cloud.utils.StringUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
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.feign.model.LunSpace;
import org.apache.cloudstack.storage.feign.model.OntapStorage;
import org.apache.cloudstack.storage.feign.model.Svm;
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
import org.apache.cloudstack.storage.service.model.ProtocolType;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.inject.Inject;
import java.net.URI;
import java.util.Map;

@Component
public class Utility {
@Inject
OntapStorage ontapStorage;

private static final Logger s_logger = (Logger) LogManager.getLogger(Utility.class);
@Inject private OntapStorage ontapStorage;
@Inject private PrimaryDataStoreDao storagePoolDao;
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;

private static final String BASIC = "Basic";
private static final String AUTH_HEADER_COLON = ":";

/**
* Method generates authentication headers using storage backend credentials passed as normal string
* @param username -->> username of the storage backend
* @param password -->> normal decoded password of the storage backend
*
* @param username -->> username of the storage backend
* @param password -->> normal decoded password of the storage backend
* @return
*/
public String generateAuthHeader(String username, String password) {
public String generateAuthHeader (String username, String password) {
byte[] encodedBytes = Base64Utils.encode((username + AUTH_HEADER_COLON + password).getBytes());
return BASIC + StringUtils.SPACE + new String(encodedBytes);
}

public URI generateURI(String path) {
public URI generateURI (String path) {
String uriString = Constants.HTTPS + ontapStorage.getManagementLIF() + path;
return URI.create(uriString);
}

public CloudStackVolume createCloudStackVolumeRequestByProtocol(StoragePoolVO storagePool, Map<String, String> details, DataObject dataObject) {
CloudStackVolume cloudStackVolumeRequest = null;

String protocol = details.get(Constants.PROTOCOL);
if (ProtocolType.ISCSI.name().equalsIgnoreCase(protocol)) {
cloudStackVolumeRequest = new CloudStackVolume();
Lun lunRequest = new Lun();
Svm svm = new Svm();
svm.setName(details.get(Constants.SVM_NAME));
lunRequest.setSvm(svm);

LunSpace lunSpace = new LunSpace();
lunSpace.setSize(dataObject.getSize());
lunRequest.setSpace(lunSpace);

String lunFullName = Constants.VOLUME_PATH_PREFIX + storagePool.getName() + Constants.PATH_SEPARATOR + dataObject.getName();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add example for lun full name

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

lunRequest.setName(lunFullName);

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";
s_logger.error(errMsg);
throw new CloudRuntimeException(errMsg);
}
lunRequest.setOsType(Lun.OsTypeEnum.valueOf(osType));

cloudStackVolumeRequest.setLun(lunRequest);
return cloudStackVolumeRequest;
} else {
throw new CloudRuntimeException("createCloudStackVolumeRequestByProtocol: Unsupported protocol " + protocol);
}
}
}
Loading