Skip to content

Commit a20dc4e

Browse files
Locharla, SandeepLocharla, Sandeep
authored andcommitted
# This is a combination of 11 commits.� This is a combination of 10 commits.
# This is the 1st commit message: CSTACKEX-25: Basic class structure # This is the commit message #2: Add PrimaryStoragePool base code # This is the commit message #3: CSTACKEX-25: Create Volume code basic code added # This is the commit message #4: CSTACKEX-25: additional logic for Primary storage pool creation # This is the commit message #5: CSTACKEX-29 Cluster, SVM and Aggr Feign Client # This is the commit message #6: CSTACKEX-29 Added License Info # This is the commit message #7: CSTACKEX-29 Resolve Review Comments # This is the commit message #8: CSTACKEX-29 Resolve Style check issues � This is the commit message #9: CSTACKEX-29 Resolve Style check issues � This is the commit message #10: CSTACKEX-29 Resolve Precommits Issues # This is the commit message #11: CSTACKEX-29 Resolve Precommits Issues
1 parent d8b98ed commit a20dc4e

File tree

10 files changed

+510
-12
lines changed

10 files changed

+510
-12
lines changed

plugins/storage/volume/ontap/pom.xml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
under the License.
1818
-->
1919
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2121
<modelVersion>4.0.0</modelVersion>
2222
<artifactId>cloud-plugin-storage-volume-ontap</artifactId>
2323
<name>Apache CloudStack Plugin - Storage Volume ONTAP Provider</name>
@@ -29,6 +29,7 @@
2929
</parent>
3030
<properties>
3131
<spring-cloud.version>2021.0.7</spring-cloud.version>
32+
<spring-boot.version>2.7.10</spring-boot.version>
3233
<openfeign.version>11.0</openfeign.version>
3334
<json.version>20230227</json.version>
3435
<swagger-annotations.version>1.6.2</swagger-annotations.version>
@@ -86,6 +87,15 @@
8687
<artifactId>swagger-annotations</artifactId>
8788
<version>${swagger-annotations.version}</version>
8889
</dependency>
90+
<!-- <dependency>-->
91+
<!-- <groupId>org.springframework</groupId>-->
92+
<!-- <artifactId>spring-web</artifactId>-->
93+
<!-- </dependency>-->
94+
<!-- <dependency>-->
95+
<!-- <groupId>org.springframework.boot</groupId>-->
96+
<!-- <artifactId>spring-boot-starter</artifactId>-->
97+
<!-- <version>${spring-boot.version}</version>-->
98+
<!-- </dependency>-->
8999
</dependencies>
90100
<build>
91101
<plugins>
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.cloudstack.storage.feign.model.response;
21+
22+
import com.fasterxml.jackson.annotation.JsonInclude;
23+
import com.fasterxml.jackson.annotation.JsonProperty;
24+
import java.util.List;
25+
26+
/**
27+
* OnTapResponse
28+
*/
29+
@JsonInclude(JsonInclude.Include.NON_NULL)
30+
public class OnTapResponse<T> {
31+
@JsonProperty("num_records")
32+
private Integer numRecords;
33+
34+
@JsonProperty("records")
35+
private List<T> records;
36+
37+
public OnTapResponse() {
38+
// Default constructor
39+
}
40+
41+
public OnTapResponse(List<T> records) {
42+
this.records = records;
43+
this.numRecords = (records != null) ? records.size() : 0;
44+
}
45+
46+
public Integer getNumRecords() {
47+
return numRecords;
48+
}
49+
50+
public void setNumRecords(Integer numRecords) {
51+
this.numRecords = numRecords;
52+
}
53+
54+
public List<T> getRecords() {
55+
return records;
56+
}
57+
58+
public void setRecords(List<T> records) {
59+
this.records = records;
60+
this.numRecords = (records != null) ? records.size() : 0;
61+
}
62+
}

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/model/response/OntapResponse.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
* OnTapResponse
2828
*/
2929
@JsonInclude(JsonInclude.Include.NON_NULL)
30-
public class OntapResponse<T> {
30+
public class OnTapResponse<T> {
3131
@JsonProperty("num_records")
3232
private Integer numRecords;
3333

3434
@JsonProperty("records")
3535
private List<T> records;
3636

37-
public OntapResponse () {
37+
public OnTapResponse() {
3838
// Default constructor
3939
}
4040

41-
public OntapResponse (List<T> records) {
41+
public OnTapResponse(List<T> records) {
4242
this.records = records;
4343
this.numRecords = (records != null) ? records.size() : 0;
4444
}
@@ -60,3 +60,5 @@ public void setRecords(List<T> records) {
6060
this.numRecords = (records != null) ? records.size() : 0;
6161
}
6262
}
63+
64+

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

Lines changed: 126 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,40 @@
2121

2222

2323
import com.cloud.agent.api.StoragePoolInfo;
24+
import com.cloud.dc.ClusterVO;
25+
import com.cloud.dc.dao.ClusterDao;
26+
import com.cloud.host.HostVO;
2427
import com.cloud.hypervisor.Hypervisor;
28+
import com.cloud.resource.ResourceManager;
29+
import com.cloud.storage.Storage;
30+
import com.cloud.storage.StorageManager;
2531
import com.cloud.storage.StoragePool;
32+
import com.cloud.utils.exception.CloudRuntimeException;
33+
import com.google.common.base.Preconditions;
2634
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
2735
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
2836
import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
37+
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
2938
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
39+
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
3040
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
41+
import org.apache.cloudstack.storage.datastore.lifecycle.BasePrimaryDataStoreLifeCycleImpl;
42+
import org.apache.cloudstack.storage.provider.StorageProviderManager;
43+
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
3144
import org.apache.logging.log4j.LogManager;
3245
import org.apache.logging.log4j.Logger;
33-
import java.util.Map;
3446

35-
public class OntapPrimaryDatastoreLifecycle implements PrimaryDataStoreLifeCycle {
47+
import javax.inject.Inject;
48+
import java.util.ArrayList;
49+
import java.util.List;
50+
import java.util.Map;
51+
import java.util.UUID;
3652

53+
public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycleImpl implements PrimaryDataStoreLifeCycle {
54+
@Inject private ClusterDao _clusterDao;
55+
@Inject private StorageManager _storageMgr;
56+
@Inject private ResourceManager _resourceMgr;
57+
@Inject private PrimaryDataStoreHelper _dataStoreHelper;
3758
private static final Logger s_logger = (Logger)LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);
3859

3960
/**
@@ -43,14 +64,94 @@ public class OntapPrimaryDatastoreLifecycle implements PrimaryDataStoreLifeCycle
4364
*/
4465
@Override
4566
public DataStore initialize(Map<String, Object> dsInfos) {
46-
47-
return null;
48-
67+
String url = dsInfos.get("url").toString();
68+
Long zoneId = (Long) dsInfos.get("zoneId");
69+
Long podId = (Long)dsInfos.get("podId");
70+
Long clusterId = (Long)dsInfos.get("clusterId");
71+
String storagePoolName = dsInfos.get("name").toString();
72+
String providerName = dsInfos.get("providerName").toString();
73+
String tags = dsInfos.get("tags").toString();
74+
Boolean isTagARule = (Boolean) dsInfos.get("isTagARule");
75+
String scheme = dsInfos.get("scheme").toString();
76+
77+
// Additional details requested for ONTAP primary storage pool creation
78+
@SuppressWarnings("unchecked")
79+
Map<String, String> details = (Map<String, String>)dsInfos.get("details");
80+
// Validations
81+
if (podId != null && clusterId == null) {
82+
s_logger.error("Cluster Id is null, cannot create primary storage");
83+
return null;
84+
} else if (podId == null && clusterId != null) {
85+
s_logger.error("Pod Id is null, cannot create primary storage");
86+
return null;
87+
}
88+
89+
if (podId == null && clusterId == null) {
90+
if (zoneId != null) {
91+
s_logger.info("Both Pod Id and Cluster Id are null, Primary storage pool will be associated with a Zone");
92+
} else {
93+
throw new CloudRuntimeException("Pod Id, Cluster Id and Zone Id are all null, cannot create primary storage");
94+
}
95+
}
96+
97+
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
98+
if (clusterId != null) {
99+
ClusterVO clusterVO = _clusterDao.findById(clusterId);
100+
Preconditions.checkNotNull(clusterVO, "Unable to locate the specified cluster");
101+
if (clusterVO.getHypervisorType() != Hypervisor.HypervisorType.KVM) {
102+
throw new CloudRuntimeException("ONTAP primary storage is not supported for KVM hypervisor");
103+
}
104+
parameters.setHypervisorType(clusterVO.getHypervisorType());
105+
}
106+
// Validate the ONTAP details
107+
StorageProviderManager storageProviderManager = new StorageProviderManager(details, scheme);
108+
boolean isValid = storageProviderManager.connect(details);
109+
if (isValid) {
110+
// String volumeName = storagePoolName + "_vol"; //TODO: Figure out a better naming convention
111+
storageProviderManager.createVolume(storagePoolName, Integer.parseInt((details.get("size")))); // TODO: size should be in bytes, so see if conversion is needed
112+
} else {
113+
throw new CloudRuntimeException("ONTAP details validation failed, cannot create primary storage");
114+
}
115+
116+
// TODO: While testing need to check what does this actually do and if the fields corresponding to each protocol should also be set
117+
if (scheme.equalsIgnoreCase("nfs")) {
118+
parameters.setType(Storage.StoragePoolType.NetworkFilesystem);
119+
} else if (scheme.equalsIgnoreCase("iscsi")) {
120+
parameters.setType(Storage.StoragePoolType.Iscsi);
121+
} else {
122+
throw new CloudRuntimeException("Unsupported protocol: " + scheme + ", cannot create primary storage");
123+
}
124+
125+
parameters.setTags(tags);
126+
parameters.setIsTagARule(isTagARule);
127+
parameters.setDetails(details);
128+
parameters.setUuid(UUID.randomUUID().toString());
129+
parameters.setZoneId(zoneId);
130+
parameters.setPodId(podId);
131+
parameters.setClusterId(clusterId);
132+
parameters.setName(storagePoolName);
133+
parameters.setProviderName(providerName);
134+
parameters.setManaged(true);
135+
136+
return _dataStoreHelper.createPrimaryDataStore(parameters);
49137
}
50138

51139
@Override
52-
public boolean attachCluster(DataStore store, ClusterScope scope) {
53-
return false;
140+
public boolean attachCluster(DataStore dataStore, ClusterScope scope) {
141+
PrimaryDataStoreInfo primarystore = (PrimaryDataStoreInfo)dataStore;
142+
List<HostVO> hostsToConnect = _resourceMgr.getEligibleUpAndEnabledHostsInClusterForStorageConnection(primarystore);
143+
144+
logger.debug(String.format("Attaching the pool to each of the hosts %s in the cluster: %s", hostsToConnect, primarystore.getClusterId()));
145+
for (HostVO host : hostsToConnect) {
146+
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
147+
try {
148+
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
149+
} catch (Exception e) {
150+
logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
151+
}
152+
}
153+
_dataStoreHelper.attachCluster(dataStore);
154+
return true;
54155
}
55156

56157
@Override
@@ -60,7 +161,24 @@ public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo exis
60161

61162
@Override
62163
public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.HypervisorType hypervisorType) {
63-
return false;
164+
List<HostVO> hostsToConnect = new ArrayList<>();
165+
Hypervisor.HypervisorType[] hypervisorTypes = {Hypervisor.HypervisorType.XenServer, Hypervisor.HypervisorType.VMware, Hypervisor.HypervisorType.KVM};
166+
167+
for (Hypervisor.HypervisorType type : hypervisorTypes) {
168+
hostsToConnect.addAll(_resourceMgr.getEligibleUpAndEnabledHostsInZoneForStorageConnection(dataStore, scope.getScopeId(), type));
169+
}
170+
171+
logger.debug(String.format("In createPool. Attaching the pool to each of the hosts in %s.", hostsToConnect));
172+
for (HostVO host : hostsToConnect) {
173+
// TODO: Fetch the host IQN and add to the initiator group on ONTAP cluster
174+
try {
175+
_storageMgr.connectHostToSharedPool(host, dataStore.getId());
176+
} catch (Exception e) {
177+
logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
178+
}
179+
}
180+
_dataStoreHelper.attachZone(dataStore);
181+
return true;
64182
}
65183

66184
@Override

0 commit comments

Comments
 (0)