Skip to content

Commit eb35e8c

Browse files
Locharla, SandeepLocharla, Sandeep
authored andcommitted
CSTACKEX-50: Disable, Re-Enable, Delete Storage pool and Enter, Exit Storage pool workflows
1 parent 1b0c7f7 commit eb35e8c

File tree

10 files changed

+305
-83
lines changed

10 files changed

+305
-83
lines changed

plugins/storage/volume/ontap/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<spring-cloud.version>2021.0.7</spring-cloud.version>
3232
<openfeign.version>11.0</openfeign.version>
3333
<json.version>20230227</json.version>
34-
<jackson-databind.version>2.15.2</jackson-databind.version>
34+
<jackson-databind.version>2.13.4</jackson-databind.version>
3535
<httpclient.version>4.5.14</httpclient.version>
3636
<swagger-annotations.version>1.6.2</swagger-annotations.version>
3737
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ private StorageStrategy getStrategyByStoragePoolDetails(Map<String, String> deta
277277
}
278278
String protocol = details.get(Constants.PROTOCOL);
279279
OntapStorage ontapStorage = new OntapStorage(details.get(Constants.USERNAME), details.get(Constants.PASSWORD),
280-
details.get(Constants.MANAGEMENT_LIF), details.get(Constants.SVM_NAME), ProtocolType.valueOf(protocol),
280+
details.get(Constants.MANAGEMENT_LIF), details.get(Constants.SVM_NAME), Long.parseLong(details.get(Constants.SIZE)), ProtocolType.valueOf(protocol),
281281
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED)));
282282
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
283283
boolean isValid = storageStrategy.connect();

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/FeignConfiguration.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
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+
120
package org.apache.cloudstack.storage.feign;
221

22+
import com.fasterxml.jackson.databind.ObjectMapper;
323
import feign.RequestInterceptor;
424
import feign.Retryer;
525
import feign.Client;
@@ -11,7 +31,6 @@
1131
import feign.codec.EncodeException;
1232
import com.fasterxml.jackson.core.JsonProcessingException;
1333
import com.fasterxml.jackson.databind.DeserializationFeature;
14-
import com.fasterxml.jackson.databind.json.JsonMapper;
1534
import org.apache.http.conn.ConnectionKeepAliveStrategy;
1635
import org.apache.http.conn.ssl.NoopHostnameVerifier;
1736
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
@@ -36,13 +55,11 @@ public class FeignConfiguration {
3655
private final int retryMaxInterval = 5;
3756
private final String ontapFeignMaxConnection = "80";
3857
private final String ontapFeignMaxConnectionPerRoute = "20";
39-
private final JsonMapper jsonMapper;
58+
private final ObjectMapper objectMapper;
4059

4160
public FeignConfiguration() {
42-
this.jsonMapper = JsonMapper.builder()
43-
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
44-
.findAndAddModules()
45-
.build();
61+
this.objectMapper = new ObjectMapper();
62+
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
4663
}
4764

4865
public Client createClient() {
@@ -105,7 +122,7 @@ public void encode(Object object, Type bodyType, feign.RequestTemplate template)
105122
return;
106123
}
107124
try {
108-
byte[] jsonBytes = jsonMapper.writeValueAsBytes(object);
125+
byte[] jsonBytes = objectMapper.writeValueAsBytes(object);
109126
template.body(jsonBytes, StandardCharsets.UTF_8);
110127
template.header("Content-Type", "application/json");
111128
} catch (JsonProcessingException e) {
@@ -126,7 +143,7 @@ public Object decode(Response response, Type type) throws IOException, DecodeExc
126143
try (InputStream bodyStream = response.body().asInputStream()) {
127144
json = new String(bodyStream.readAllBytes(), StandardCharsets.UTF_8);
128145
logger.debug("Decoding JSON response: {}", json);
129-
return jsonMapper.readValue(json, jsonMapper.getTypeFactory().constructType(type));
146+
return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type));
130147
} catch (IOException e) {
131148
logger.error("Error decoding JSON response. Status: {}, Raw body: {}", response.status(), json, e);
132149
throw new DecodeException(response.status(), "Error decoding JSON response", response.request(), e);

plugins/storage/volume/ontap/src/main/java/org/apache/cloudstack/storage/feign/client/VolumeFeignClient.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,30 @@
1818
*/
1919
package org.apache.cloudstack.storage.feign.client;
2020

21+
import feign.QueryMap;
2122
import org.apache.cloudstack.storage.feign.model.Volume;
2223
import org.apache.cloudstack.storage.feign.model.response.JobResponse;
2324
import feign.Headers;
2425
import feign.Param;
2526
import feign.RequestLine;
27+
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
28+
29+
import java.util.Map;
2630

2731
public interface VolumeFeignClient {
2832

2933
@RequestLine("DELETE /api/storage/volumes/{uuid}")
3034
@Headers({"Authorization: {authHeader}"})
31-
void deleteVolume(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
35+
JobResponse deleteVolume(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
3236

3337
@RequestLine("POST /api/storage/volumes")
3438
@Headers({"Authorization: {authHeader}"})
3539
JobResponse createVolumeWithJob(@Param("authHeader") String authHeader, Volume volumeRequest);
3640

41+
@RequestLine("GET /api/storage/volumes")
42+
@Headers({"Authorization: {authHeader}"})
43+
OntapResponse<Volume> getAllVolumes(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryParams);
44+
3745
@RequestLine("GET /api/storage/volumes/{uuid}")
3846
@Headers({"Authorization: {authHeader}"})
3947
Volume getVolumeByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,43 @@
2222
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
2323
import com.fasterxml.jackson.annotation.JsonInclude;
2424
import com.fasterxml.jackson.annotation.JsonProperty;
25+
import com.fasterxml.jackson.annotation.JsonCreator;
26+
import com.fasterxml.jackson.annotation.JsonValue;
2527

2628
import java.util.Objects;
2729

2830
@JsonIgnoreProperties(ignoreUnknown = true)
2931
@JsonInclude(JsonInclude.Include.NON_NULL)
3032
public class Aggregate {
33+
// Replace previous enum with case-insensitive mapping
34+
public enum StateEnum {
35+
ONLINE("online");
36+
private final String value;
37+
38+
StateEnum(String value) {
39+
this.value = value;
40+
}
41+
42+
@JsonValue
43+
public String getValue() {
44+
return value;
45+
}
46+
47+
@Override
48+
public String toString() {
49+
return String.valueOf(value);
50+
}
51+
52+
@JsonCreator
53+
public static StateEnum fromValue(String text) {
54+
for (StateEnum b : StateEnum.values()) {
55+
if (String.valueOf(b.value).equals(text)) {
56+
return b;
57+
}
58+
}
59+
return null;
60+
}
61+
}
3162

3263
@JsonProperty("name")
3364
private String name = null;
@@ -40,6 +71,13 @@ public int hashCode() {
4071
@JsonProperty("uuid")
4172
private String uuid = null;
4273

74+
@JsonProperty("state")
75+
private StateEnum state = null;
76+
77+
@JsonProperty("space")
78+
private AggregateSpace space = null;
79+
80+
4381
public Aggregate name(String name) {
4482
this.name = name;
4583
return this;
@@ -65,6 +103,21 @@ public void setUuid(String uuid) {
65103
this.uuid = uuid;
66104
}
67105

106+
public StateEnum getState() {
107+
return state;
108+
}
109+
110+
public AggregateSpace getSpace() {
111+
return space;
112+
}
113+
114+
public Double getAvailableBlockStorageSpace() {
115+
if (space != null && space.blockStorage != null) {
116+
return space.blockStorage.available;
117+
}
118+
return null;
119+
}
120+
68121

69122
@Override
70123
public boolean equals(java.lang.Object o) {
@@ -95,4 +148,18 @@ public String toString() {
95148
return "DiskAggregates [name=" + name + ", uuid=" + uuid + "]";
96149
}
97150

151+
public static class AggregateSpace {
152+
@JsonProperty("block_storage")
153+
private AggregateSpaceBlockStorage blockStorage = null;
154+
}
155+
156+
public static class AggregateSpaceBlockStorage {
157+
@JsonProperty("available")
158+
private Double available = null;
159+
@JsonProperty("size")
160+
private Double size = null;
161+
@JsonProperty("used")
162+
private Double used = null;
163+
}
164+
98165
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ public String toString() {
8787
}
8888

8989
public static class Links {
90-
@JsonProperty("message")
90+
@JsonProperty("self")
9191
private Self self;
9292
public Self getSelf() { return self; }
9393
public void setSelf(Self self) { this.self = self; }
9494
}
9595

9696
public static class Self {
97-
@JsonProperty("message")
97+
@JsonProperty("href")
9898
private String href;
9999
public String getHref() { return href; }
100100
public void setHref(String href) { this.href = href; }

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ public class OntapStorage {
2626
private final String password;
2727
private final String managementLIF;
2828
private final String svmName;
29+
private final Long size;
2930
private final ProtocolType protocolType;
3031
private final Boolean isDisaggregated;
3132

32-
public OntapStorage(String username, String password, String managementLIF, String svmName, ProtocolType protocolType, Boolean isDisaggregated) {
33+
public OntapStorage(String username, String password, String managementLIF, String svmName, Long size, ProtocolType protocolType, Boolean isDisaggregated) {
3334
this.username = username;
3435
this.password = password;
3536
this.managementLIF = managementLIF;
3637
this.svmName = svmName;
38+
this.size = size;
3739
this.protocolType = protocolType;
3840
this.isDisaggregated = isDisaggregated;
3941
}
@@ -54,6 +56,10 @@ public String getSvmName() {
5456
return svmName;
5557
}
5658

59+
public Long getSize() {
60+
return size;
61+
}
62+
5763
public ProtocolType getProtocol() {
5864
return protocolType;
5965
}

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

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.cloud.storage.Storage;
3030
import com.cloud.storage.StorageManager;
3131
import com.cloud.storage.StoragePool;
32+
import com.cloud.storage.StoragePoolAutomation;
3233
import com.cloud.utils.exception.CloudRuntimeException;
3334
import com.google.common.base.Preconditions;
3435
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
@@ -38,8 +39,10 @@
3839
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
3940
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
4041
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
42+
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDetailsDao;
4143
import org.apache.cloudstack.storage.datastore.lifecycle.BasePrimaryDataStoreLifeCycleImpl;
4244
import org.apache.cloudstack.storage.feign.model.OntapStorage;
45+
import org.apache.cloudstack.storage.feign.model.Volume;
4346
import org.apache.cloudstack.storage.provider.StorageProviderFactory;
4447
import org.apache.cloudstack.storage.service.StorageStrategy;
4548
import org.apache.cloudstack.storage.service.model.ProtocolType;
@@ -59,6 +62,8 @@ public class OntapPrimaryDatastoreLifecycle extends BasePrimaryDataStoreLifeCycl
5962
@Inject private StorageManager _storageMgr;
6063
@Inject private ResourceManager _resourceMgr;
6164
@Inject private PrimaryDataStoreHelper _dataStoreHelper;
65+
@Inject private PrimaryDataStoreDetailsDao _datastoreDetailsDao;
66+
@Inject private StoragePoolAutomation _storagePoolAutomation;
6267
private static final Logger s_logger = LogManager.getLogger(OntapPrimaryDatastoreLifecycle.class);
6368

6469
// ONTAP minimum volume size is 1.56 GB (1677721600 bytes)
@@ -199,21 +204,25 @@ public DataStore initialize(Map<String, Object> dsInfos) {
199204
}
200205

201206
// Connect to ONTAP and create volume
207+
long volumeSize = Long.parseLong(details.get(Constants.SIZE));
202208
OntapStorage ontapStorage = new OntapStorage(
203209
details.get(Constants.USERNAME),
204210
details.get(Constants.PASSWORD),
205211
details.get(Constants.MANAGEMENT_LIF),
206212
details.get(Constants.SVM_NAME),
213+
volumeSize,
207214
protocol,
208215
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED).toLowerCase()));
209216

210217
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
211218
boolean isValid = storageStrategy.connect();
212219
if (isValid) {
213-
long volumeSize = Long.parseLong(details.get(Constants.SIZE));
214220
s_logger.info("Creating ONTAP volume '" + storagePoolName + "' with size: " + volumeSize + " bytes (" +
215221
(volumeSize / (1024 * 1024 * 1024)) + " GB)");
216-
storageStrategy.createStorageVolume(storagePoolName, volumeSize);
222+
Volume volume = storageStrategy.createStorageVolume(storagePoolName, volumeSize);
223+
s_logger.info("ONTAP volume created successfully: " + volume.getName());
224+
details.put(Constants.VOLUME_NAME, volume.getName());
225+
details.put(Constants.VOLUME_UUID, volume.getUuid());
217226
} else {
218227
throw new CloudRuntimeException("ONTAP details validation failed, cannot create primary storage");
219228
}
@@ -282,17 +291,49 @@ public boolean attachZone(DataStore dataStore, ZoneScope scope, Hypervisor.Hyper
282291

283292
@Override
284293
public boolean maintain(DataStore store) {
285-
return true;
294+
_storagePoolAutomation.maintain(store);
295+
return _dataStoreHelper.maintain(store);
286296
}
287297

288298
@Override
289299
public boolean cancelMaintain(DataStore store) {
290-
return true;
300+
_storagePoolAutomation.cancelMaintain(store);
301+
return _dataStoreHelper.cancelMaintain(store);
291302
}
292303

293304
@Override
294305
public boolean deleteDataStore(DataStore store) {
295-
return true;
306+
// Deletion of underlying ONTAP volume
307+
long storagePoolId = store.getId();
308+
// Get the StoragePool details
309+
StoragePool storagePool = _storageMgr.getStoragePool(storagePoolId);
310+
if (storagePool == null) {
311+
s_logger.warn("Storage pool not found for id: " + storagePoolId + ", cannot delete underlying ONTAP volume");
312+
return false;// TODO: As the CS entity is not present, should we return true here?
313+
}
314+
Map<String, String> details = _datastoreDetailsDao.listDetailsKeyPairs(storagePoolId);
315+
// Set the Volume object for deletion
316+
Volume volume = new Volume();
317+
volume.setName(details.get(Constants.VOLUME_NAME));
318+
volume.setUuid(details.get(Constants.VOLUME_UUID));
319+
// Call Volume deletion through StorageStrategy
320+
OntapStorage ontapStorage = new OntapStorage(
321+
details.get(Constants.USERNAME),
322+
details.get(Constants.PASSWORD),
323+
details.get(Constants.MANAGEMENT_LIF),
324+
details.get(Constants.SVM_NAME),
325+
Long.parseLong(details.get(Constants.SIZE)),
326+
ProtocolType.valueOf(details.get(Constants.PROTOCOL)),
327+
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED).toLowerCase())
328+
);
329+
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
330+
boolean isValid = storageStrategy.connect();
331+
if (isValid) {
332+
s_logger.info("Deleting ONTAP volume '" + volume.getName() + "' for storage pool id: " + storagePoolId);
333+
storageStrategy.deleteStorageVolume(volume);
334+
}
335+
336+
return _dataStoreHelper.deletePrimaryDataStore(store);
296337
}
297338

298339
@Override
@@ -307,12 +348,12 @@ public void updateStoragePool(StoragePool storagePool, Map<String, String> detai
307348

308349
@Override
309350
public void enableStoragePool(DataStore store) {
310-
351+
_dataStoreHelper.enable(store);
311352
}
312353

313354
@Override
314355
public void disableStoragePool(DataStore store) {
315-
356+
_dataStoreHelper.disable(store);
316357
}
317358

318359
@Override

0 commit comments

Comments
 (0)