Skip to content

Commit 6c4b24e

Browse files
suryag1201Gupta, Surya
andauthored
CSTACKEX-35 Create Async (#14)
* CSTACKEX-35 Create Async * CSTACKEX-35 Added Null and empty check * CSTACKEX-35 Resolved review comments * CSTACKEX-35 Removed Type Casting for logger --------- Co-authored-by: Gupta, Surya <[email protected]>
1 parent 618f957 commit 6c4b24e

File tree

5 files changed

+204
-13
lines changed

5 files changed

+204
-13
lines changed

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

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
*/
1919
package org.apache.cloudstack.storage.driver;
2020

21+
import com.cloud.agent.api.Answer;
22+
import com.cloud.agent.api.to.DataObjectType;
2123
import com.cloud.agent.api.to.DataStoreTO;
2224
import com.cloud.agent.api.to.DataTO;
25+
import com.cloud.exception.InvalidParameterValueException;
2326
import com.cloud.host.Host;
2427
import com.cloud.storage.Storage;
2528
import com.cloud.storage.StoragePool;
2629
import com.cloud.storage.Volume;
2730
import com.cloud.utils.Pair;
31+
import com.cloud.utils.exception.CloudRuntimeException;
2832
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
2933
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
3034
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
@@ -37,15 +41,28 @@
3741
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
3842
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
3943
import org.apache.cloudstack.storage.command.CommandResult;
44+
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
45+
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
46+
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
47+
import org.apache.cloudstack.storage.service.StorageStrategy;
48+
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
49+
import org.apache.cloudstack.storage.service.model.ProtocolType;
50+
import org.apache.cloudstack.storage.utils.Constants;
51+
import org.apache.cloudstack.storage.utils.Utility;
4052
import org.apache.logging.log4j.LogManager;
4153
import org.apache.logging.log4j.Logger;
4254

55+
import javax.inject.Inject;
4356
import java.util.HashMap;
4457
import java.util.Map;
4558

4659
public class OntapPrimaryDatastoreDriver implements PrimaryDataStoreDriver {
4760

48-
private static final Logger s_logger = (Logger)LogManager.getLogger(OntapPrimaryDatastoreDriver.class);
61+
private static final Logger s_logger = LogManager.getLogger(OntapPrimaryDatastoreDriver.class);
62+
63+
@Inject private Utility utils;
64+
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
65+
@Inject private PrimaryDataStoreDao storagePoolDao;
4966
@Override
5067
public Map<String, String> getCapabilities() {
5168
s_logger.trace("OntapPrimaryDatastoreDriver: getCapabilities: Called");
@@ -68,9 +85,58 @@ public DataStoreTO getStoreTO(DataStore store) {
6885
}
6986

7087
@Override
71-
public void createAsync(DataStore store, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
88+
public void createAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CreateCmdResult> callback) {
89+
CreateCmdResult createCmdResult = null;
90+
String path = null;
91+
String errMsg = null;
92+
if (dataStore == null) {
93+
throw new InvalidParameterValueException("createAsync: dataStore should not be null");
94+
}
95+
if (dataObject == null) {
96+
throw new InvalidParameterValueException("createAsync: dataObject should not be null");
97+
}
98+
if (callback == null) {
99+
throw new InvalidParameterValueException("createAsync: callback should not be null");
100+
}
101+
try {
102+
s_logger.info("createAsync: Started for data store [{}] and data object [{}] of type [{}]",
103+
dataStore, dataObject, dataObject.getType());
104+
if (dataObject.getType() == DataObjectType.VOLUME) {
105+
path = createCloudStackVolumeForTypeVolume(dataStore, dataObject);
106+
createCmdResult = new CreateCmdResult(path, new Answer(null, true, null));
107+
} else {
108+
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
109+
s_logger.error(errMsg);
110+
throw new CloudRuntimeException(errMsg);
111+
}
112+
} catch (Exception e) {
113+
errMsg = e.getMessage();
114+
s_logger.error("createAsync: Failed for dataObject [{}]: {}", dataObject, errMsg);
115+
createCmdResult = new CreateCmdResult(null, new Answer(null, false, errMsg));
116+
createCmdResult.setResult(e.toString());
117+
} finally {
118+
callback.complete(createCmdResult);
119+
}
120+
}
72121

73-
s_logger.trace("OntapPrimaryDatastoreDriver: createAsync: Store: "+store+", data: "+data);
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());
129+
StorageStrategy storageStrategy = utils.getStrategyByStoragePoolDetails(details);
130+
s_logger.info("createCloudStackVolumeForTypeVolume: Connection to Ontap SVM [{}] successful, preparing CloudStackVolumeRequest", details.get(Constants.SVM_NAME));
131+
CloudStackVolume cloudStackVolumeRequest = utils.createCloudStackVolumeRequestByProtocol(storagePool, details, dataObject);
132+
CloudStackVolume cloudStackVolume = storageStrategy.createCloudStackVolume(cloudStackVolumeRequest);
133+
if (ProtocolType.ISCSI.name().equalsIgnoreCase(details.get(Constants.PROTOCOL)) && cloudStackVolume.getLun() != null && cloudStackVolume.getLun().getName() != null) {
134+
return cloudStackVolume.getLun().getName();
135+
} else {
136+
String errMsg = "createCloudStackVolumeForTypeVolume: Volume creation failed. Lun or Lun Path is null for dataObject: " + dataObject;
137+
s_logger.error(errMsg);
138+
throw new CloudRuntimeException(errMsg);
139+
}
74140
}
75141

76142
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public abstract class StorageStrategy {
6464
@Inject
6565
private JobFeignClient jobFeignClient;
6666

67-
private final OntapStorage storage;
67+
protected final OntapStorage storage;
6868

6969
/**
7070
* Presents aggregate object for the unified storage, not eligible for disaggregated

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

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,60 @@
1919

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

22+
import com.cloud.utils.exception.CloudRuntimeException;
23+
import org.apache.cloudstack.storage.feign.client.SANFeignClient;
24+
import org.apache.cloudstack.storage.feign.model.Lun;
2225
import org.apache.cloudstack.storage.feign.model.OntapStorage;
26+
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
2327
import org.apache.cloudstack.storage.service.model.AccessGroup;
2428
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
29+
import org.apache.cloudstack.storage.utils.Constants;
30+
import org.apache.cloudstack.storage.utils.Utility;
31+
import org.apache.logging.log4j.LogManager;
32+
import org.apache.logging.log4j.Logger;
2533

34+
import javax.inject.Inject;
35+
import java.net.URI;
2636
import java.util.Map;
2737

28-
public class UnifiedSANStrategy extends SANStrategy{
38+
public class UnifiedSANStrategy extends SANStrategy {
39+
40+
private static final Logger s_logger = LogManager.getLogger(UnifiedSANStrategy.class);
41+
@Inject private Utility utils;
42+
@Inject private SANFeignClient sanFeignClient;
2943
public UnifiedSANStrategy(OntapStorage ontapStorage) {
3044
super(ontapStorage);
3145
}
3246

3347
@Override
3448
public CloudStackVolume createCloudStackVolume(CloudStackVolume cloudstackVolume) {
35-
//TODO
36-
return null;
49+
s_logger.info("createCloudStackVolume : Creating Lun with cloudstackVolume request {} ", cloudstackVolume);
50+
if (cloudstackVolume == null || cloudstackVolume.getLun() == null) {
51+
s_logger.error("createCloudStackVolume: LUN creation failed. Invalid request: {}", cloudstackVolume);
52+
throw new CloudRuntimeException("createCloudStackVolume : Failed to create Lun, invalid request");
53+
}
54+
try {
55+
// Get AuthHeader
56+
String authHeader = utils.generateAuthHeader(storage.getUsername(), storage.getPassword());
57+
// Create URI for lun creation
58+
URI url = utils.generateURI(Constants.CREATE_LUN);
59+
//TODO: It is possible that Lun creation will take time and we may need to handle through async job.
60+
OntapResponse<Lun> createdLun = sanFeignClient.createLun(url, authHeader, true, cloudstackVolume.getLun());
61+
if (createdLun == null || createdLun.getRecords() == null || createdLun.getRecords().size() == 0) {
62+
s_logger.error("createCloudStackVolume: LUN creation failed for Lun {}", cloudstackVolume.getLun().getName());
63+
throw new CloudRuntimeException("Failed to create Lun: " + cloudstackVolume.getLun().getName());
64+
}
65+
Lun lun = createdLun.getRecords().get(0);
66+
s_logger.debug("createCloudStackVolume: LUN created successfully. Lun: {}", lun);
67+
s_logger.info("createCloudStackVolume: LUN created successfully. LunName: {}", lun.getName());
68+
69+
CloudStackVolume createdCloudStackVolume = new CloudStackVolume();
70+
createdCloudStackVolume.setLun(lun);
71+
return createdCloudStackVolume;
72+
} catch (Exception e) {
73+
s_logger.error("Exception occurred while creating LUN: {}. Exception: {}", cloudstackVolume.getLun().getName(), e.getMessage());
74+
throw new CloudRuntimeException("Failed to create Lun: " + e.getMessage());
75+
}
3776
}
3877

3978
@Override

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/Constants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,16 @@ public class Constants {
4040
public static final int JOB_MAX_RETRIES = 100;
4141
public static final int CREATE_VOLUME_CHECK_SLEEP_TIME = 2000;
4242

43+
public static final String PATH_SEPARATOR = "/";
44+
45+
public static final String VOLUME_PATH_PREFIX = "/vol/";
46+
47+
public static final String KVM = "KVM";
48+
4349
public static final String HTTPS = "https://";
4450
public static final String GET_SVMs = "/api/svm/svms";
4551
public static final String CREATE_VOLUME = "/api/storage/volumes";
4652
public static final String GET_JOB_BY_UUID = "/api/cluster/jobs";
53+
public static final String CREATE_LUN = "/api/storage/luns";
54+
4755
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/utils/Utility.java

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,111 @@
2020
package org.apache.cloudstack.storage.utils;
2121

2222
import com.cloud.utils.StringUtils;
23+
import com.cloud.utils.exception.CloudRuntimeException;
24+
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
25+
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
26+
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
27+
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
28+
import org.apache.cloudstack.storage.feign.model.Lun;
29+
import org.apache.cloudstack.storage.feign.model.LunSpace;
2330
import org.apache.cloudstack.storage.feign.model.OntapStorage;
31+
import org.apache.cloudstack.storage.feign.model.Svm;
32+
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
33+
import org.apache.cloudstack.storage.service.StorageStrategy;
34+
import org.apache.cloudstack.storage.service.model.CloudStackVolume;
35+
import org.apache.cloudstack.storage.service.model.ProtocolType;
36+
import org.apache.logging.log4j.LogManager;
37+
import org.apache.logging.log4j.Logger;
2438
import org.springframework.stereotype.Component;
2539
import org.springframework.util.Base64Utils;
2640

2741
import javax.inject.Inject;
2842
import java.net.URI;
43+
import java.util.Map;
2944

3045
@Component
3146
public class Utility {
32-
@Inject
33-
OntapStorage ontapStorage;
47+
48+
private static final Logger s_logger = LogManager.getLogger(Utility.class);
49+
@Inject private OntapStorage ontapStorage;
50+
@Inject private PrimaryDataStoreDao storagePoolDao;
51+
@Inject private StoragePoolDetailsDao storagePoolDetailsDao;
3452

3553
private static final String BASIC = "Basic";
3654
private static final String AUTH_HEADER_COLON = ":";
55+
3756
/**
3857
* Method generates authentication headers using storage backend credentials passed as normal string
39-
* @param username -->> username of the storage backend
40-
* @param password -->> normal decoded password of the storage backend
58+
*
59+
* @param username -->> username of the storage backend
60+
* @param password -->> normal decoded password of the storage backend
4161
* @return
4262
*/
43-
public String generateAuthHeader(String username, String password) {
63+
public String generateAuthHeader (String username, String password) {
4464
byte[] encodedBytes = Base64Utils.encode((username + AUTH_HEADER_COLON + password).getBytes());
4565
return BASIC + StringUtils.SPACE + new String(encodedBytes);
4666
}
4767

48-
public URI generateURI(String path) {
68+
public URI generateURI (String path) {
4969
String uriString = Constants.HTTPS + ontapStorage.getManagementLIF() + path;
5070
return URI.create(uriString);
5171
}
72+
73+
public CloudStackVolume createCloudStackVolumeRequestByProtocol(StoragePoolVO storagePool, Map<String, String> details, DataObject dataObject) {
74+
CloudStackVolume cloudStackVolumeRequest = null;
75+
76+
String protocol = details.get(Constants.PROTOCOL);
77+
if (ProtocolType.ISCSI.name().equalsIgnoreCase(protocol)) {
78+
cloudStackVolumeRequest = new CloudStackVolume();
79+
Lun lunRequest = new Lun();
80+
Svm svm = new Svm();
81+
svm.setName(details.get(Constants.SVM_NAME));
82+
lunRequest.setSvm(svm);
83+
84+
LunSpace lunSpace = new LunSpace();
85+
lunSpace.setSize(dataObject.getSize());
86+
lunRequest.setSpace(lunSpace);
87+
//Lun name is full path like in unified "/vol/VolumeName/LunName"
88+
String lunFullName = Constants.VOLUME_PATH_PREFIX + storagePool.getName() + Constants.PATH_SEPARATOR + dataObject.getName();
89+
lunRequest.setName(lunFullName);
90+
91+
String hypervisorType = storagePool.getHypervisor().name();
92+
String osType = null;
93+
switch (hypervisorType) {
94+
case Constants.KVM:
95+
osType = Lun.OsTypeEnum.LINUX.getValue();
96+
break;
97+
default:
98+
String errMsg = "createCloudStackVolume : Unsupported hypervisor type " + hypervisorType + " for ONTAP storage";
99+
s_logger.error(errMsg);
100+
throw new CloudRuntimeException(errMsg);
101+
}
102+
lunRequest.setOsType(Lun.OsTypeEnum.valueOf(osType));
103+
104+
cloudStackVolumeRequest.setLun(lunRequest);
105+
return cloudStackVolumeRequest;
106+
} else {
107+
throw new CloudRuntimeException("createCloudStackVolumeRequestByProtocol: Unsupported protocol " + protocol);
108+
}
109+
}
110+
111+
public StorageStrategy getStrategyByStoragePoolDetails(Map<String, String> details) {
112+
if (details == null || details.isEmpty()) {
113+
s_logger.error("getStrategyByStoragePoolDetails: Storage pool details are null or empty");
114+
throw new CloudRuntimeException("getStrategyByStoragePoolDetails: Storage pool details are null or empty");
115+
}
116+
String protocol = details.get(Constants.PROTOCOL);
117+
OntapStorage ontapStorage = new OntapStorage(details.get(Constants.USERNAME), details.get(Constants.PASSWORD),
118+
details.get(Constants.MANAGEMENT_LIF), details.get(Constants.SVM_NAME), ProtocolType.valueOf(protocol),
119+
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED)));
120+
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
121+
boolean isValid = storageStrategy.connect();
122+
if (isValid) {
123+
s_logger.info("Connection to Ontap SVM [{}] successful", details.get(Constants.SVM_NAME));
124+
return storageStrategy;
125+
} else {
126+
s_logger.error("getStrategyByStoragePoolDetails: Connection to Ontap SVM [" + details.get(Constants.SVM_NAME) + "] failed");
127+
throw new CloudRuntimeException("getStrategyByStoragePoolDetails: Connection to Ontap SVM [" + details.get(Constants.SVM_NAME) + "] failed");
128+
}
129+
}
52130
}

0 commit comments

Comments
 (0)