Skip to content

Commit 997d03b

Browse files
Locharla, SandeepLocharla, Sandeep
authored andcommitted
CSTACKEX-50: Changes for selecting aggregate, retrieving ip interface and setting host and path
1 parent eb35e8c commit 997d03b

File tree

5 files changed

+246
-5
lines changed

5 files changed

+246
-5
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.apache.cloudstack.storage.feign.client;
2+
3+
import feign.Headers;
4+
import feign.Param;
5+
import feign.QueryMap;
6+
import feign.RequestLine;
7+
import org.apache.cloudstack.storage.feign.model.IpInterface;
8+
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;
9+
10+
import java.util.Map;
11+
12+
public interface NetworkFeignClient {
13+
@RequestLine("GET /api/network/ip/interfaces")
14+
@Headers({"Authorization: {authHeader}"})
15+
OntapResponse<IpInterface> getNetworkIpInterfaces(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryParams);
16+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
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;
21+
22+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
23+
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
26+
import java.util.List;
27+
import java.util.Objects;
28+
29+
@JsonIgnoreProperties(ignoreUnknown = true)
30+
@JsonInclude(JsonInclude.Include.NON_NULL)
31+
public class IpInterface {
32+
@JsonProperty("uuid")
33+
private String uuid;
34+
35+
@JsonProperty("name")
36+
private String name;
37+
38+
@JsonProperty("ip")
39+
private IpInfo ip;
40+
41+
@JsonProperty("svm")
42+
private Svm svm;
43+
44+
@JsonProperty("services")
45+
private List<String> services;
46+
47+
// Getters and setters
48+
public String getUuid() {
49+
return uuid;
50+
}
51+
52+
public void setUuid(String uuid) {
53+
this.uuid = uuid;
54+
}
55+
56+
public String getName() {
57+
return name;
58+
}
59+
60+
public void setName(String name) {
61+
this.name = name;
62+
}
63+
64+
public IpInfo getIp() {
65+
return ip;
66+
}
67+
68+
public void setIp(IpInfo ip) {
69+
this.ip = ip;
70+
}
71+
72+
public Svm getSvm() {
73+
return svm;
74+
}
75+
76+
public void setSvm(Svm svm) {
77+
this.svm = svm;
78+
}
79+
80+
public List<String> getServices() {
81+
return services;
82+
}
83+
84+
public void setServices(List<String> services) {
85+
this.services = services;
86+
}
87+
88+
@Override
89+
public boolean equals(Object o) {
90+
if (this == o) {
91+
return true;
92+
}
93+
if (o == null || getClass() != o.getClass()) {
94+
return false;
95+
}
96+
IpInterface that = (IpInterface) o;
97+
return Objects.equals(uuid, that.uuid) &&
98+
Objects.equals(name, that.name) &&
99+
Objects.equals(ip, that.ip) &&
100+
Objects.equals(svm, that.svm) &&
101+
Objects.equals(services, that.services);
102+
}
103+
104+
@Override
105+
public int hashCode() {
106+
return Objects.hash(uuid, name, ip, svm, services);
107+
}
108+
109+
@Override
110+
public String toString() {
111+
return "IpInterface{" +
112+
"uuid='" + uuid + '\'' +
113+
", name='" + name + '\'' +
114+
", ip=" + ip +
115+
", svm=" + svm +
116+
", services=" + services +
117+
'}';
118+
}
119+
120+
// Nested class for IP information
121+
@JsonIgnoreProperties(ignoreUnknown = true)
122+
@JsonInclude(JsonInclude.Include.NON_NULL)
123+
public static class IpInfo {
124+
@JsonProperty("address")
125+
private String address;
126+
127+
public String getAddress() {
128+
return address;
129+
}
130+
131+
public void setAddress(String address) {
132+
this.address = address;
133+
}
134+
135+
@Override
136+
public boolean equals(Object o) {
137+
if (this == o) return true;
138+
if (o == null || getClass() != o.getClass()) return false;
139+
IpInfo ipInfo = (IpInfo) o;
140+
return Objects.equals(address, ipInfo.address);
141+
}
142+
143+
@Override
144+
public int hashCode() {
145+
return Objects.hash(address);
146+
}
147+
148+
@Override
149+
public String toString() {
150+
return "IpInfo{" +
151+
"address='" + address + '\'' +
152+
'}';
153+
}
154+
}
155+
}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,12 @@ public DataStore initialize(Map<String, Object> dsInfos) {
191191
switch (protocol) {
192192
case NFS3:
193193
parameters.setType(Storage.StoragePoolType.NetworkFilesystem);
194-
path = details.get(Constants.MANAGEMENT_LIF) + ":/" + storagePoolName;
194+
path = Constants.PATH_SEPARATOR + storagePoolName;
195195
s_logger.info("Setting NFS path for storage pool: " + path);
196196
break;
197197
case ISCSI:
198198
parameters.setType(Storage.StoragePoolType.Iscsi);
199-
path = "iqn.1992-08.com.netapp:" + details.get(Constants.SVM_NAME) + "." + storagePoolName;
199+
path = Constants.PATH_SEPARATOR;
200200
s_logger.info("Setting iSCSI path for storage pool: " + path);
201201
break;
202202
default:
@@ -227,11 +227,19 @@ public DataStore initialize(Map<String, Object> dsInfos) {
227227
throw new CloudRuntimeException("ONTAP details validation failed, cannot create primary storage");
228228
}
229229

230+
// Get the DataLIF for data access
231+
String dataLIF = storageStrategy.getNetworkInterface();
232+
if (dataLIF == null || dataLIF.isEmpty()) {
233+
throw new CloudRuntimeException("Failed to retrieve Data LIF from ONTAP, cannot create primary storage");
234+
}
235+
s_logger.info("Using Data LIF for storage access: " + dataLIF);
236+
details.put(Constants.DATA_LIF, dataLIF);
237+
230238
// Set parameters for primary data store
231-
parameters.setHost(details.get(Constants.MANAGEMENT_LIF));
239+
parameters.setHost(details.get(Constants.DATA_LIF));
232240
parameters.setPort(Constants.ONTAP_PORT);
233241
parameters.setPath(path);
234-
parameters.setTags(tags != null ? tags : "");
242+
parameters.setTags(tags);
235243
parameters.setIsTagARule(isTagARule != null ? isTagARule : Boolean.FALSE);
236244
parameters.setDetails(details);
237245
parameters.setUuid(UUID.randomUUID().toString());

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
import org.apache.cloudstack.storage.feign.FeignClientFactory;
2525
import org.apache.cloudstack.storage.feign.client.AggregateFeignClient;
2626
import org.apache.cloudstack.storage.feign.client.JobFeignClient;
27+
import org.apache.cloudstack.storage.feign.client.NetworkFeignClient;
2728
import org.apache.cloudstack.storage.feign.client.SvmFeignClient;
2829
import org.apache.cloudstack.storage.feign.client.VolumeFeignClient;
2930
import org.apache.cloudstack.storage.feign.model.Aggregate;
31+
import org.apache.cloudstack.storage.feign.model.IpInterface;
3032
import org.apache.cloudstack.storage.feign.model.Job;
3133
import org.apache.cloudstack.storage.feign.model.OntapStorage;
3234
import org.apache.cloudstack.storage.feign.model.Svm;
@@ -58,6 +60,7 @@ public abstract class StorageStrategy {
5860
private final VolumeFeignClient volumeFeignClient;
5961
private final SvmFeignClient svmFeignClient;
6062
private final JobFeignClient jobFeignClient;
63+
private final NetworkFeignClient networkFeignClient;
6164

6265
protected OntapStorage storage;
6366

@@ -78,6 +81,7 @@ public StorageStrategy(OntapStorage ontapStorage) {
7881
this.volumeFeignClient = feignClientFactory.createClient(VolumeFeignClient.class, baseURL);
7982
this.svmFeignClient = feignClientFactory.createClient(SvmFeignClient.class, baseURL);
8083
this.jobFeignClient = feignClientFactory.createClient(JobFeignClient.class, baseURL);
84+
this.networkFeignClient = feignClientFactory.createClient(NetworkFeignClient.class, baseURL);
8185
}
8286

8387
// Connect method to validate ONTAP cluster, credentials, protocol, and SVM
@@ -174,7 +178,10 @@ public Volume createStorageVolume(String volumeName, Long size) {
174178

175179
volumeRequest.setName(volumeName);
176180
volumeRequest.setSvm(svm);
177-
volumeRequest.setAggregates(aggregates);
181+
Aggregate aggr = new Aggregate();
182+
aggr.setName(aggregates.get(0).getName());
183+
aggr.setUuid(aggregates.get(0).getUuid());
184+
volumeRequest.setAggregates(List.of(aggr));
178185
volumeRequest.setSize(size);
179186
// Make the POST API call to create the volume
180187
try {
@@ -270,6 +277,49 @@ public Volume getStorageVolume(Volume volume) {
270277
return null;
271278
}
272279

280+
/**
281+
* Get the network ip interface
282+
*
283+
* @return the network interface ip as a String
284+
*/
285+
286+
public String getNetworkInterface() {
287+
// Feign call to get network interfaces
288+
String authHeader = Utility.generateAuthHeader(storage.getUsername(), storage.getPassword());
289+
try {
290+
Map<String, Object> queryParams = Map.of(Constants.SVMNAME, storage.getSvmName());
291+
if (storage.getProtocol() != null) {
292+
switch (storage.getProtocol()) {
293+
case NFS3:
294+
queryParams = Map.of(Constants.SERVICES, Constants.DATA_NFS);
295+
break;
296+
case ISCSI:
297+
queryParams = Map.of(Constants.SERVICES, Constants.DATA_ISCSI);
298+
break;
299+
default:
300+
s_logger.error("Unsupported protocol: " + storage.getProtocol());
301+
throw new CloudRuntimeException("Unsupported protocol: " + storage.getProtocol());
302+
}
303+
}
304+
queryParams.put(Constants.FIELDS, Constants.IP_ADDRESS);
305+
queryParams.put(Constants.RETURN_RECORDS, Constants.TRUE);
306+
OntapResponse<IpInterface> response =
307+
networkFeignClient.getNetworkIpInterfaces(authHeader, queryParams);
308+
if (response != null && response.getRecords() != null && !response.getRecords().isEmpty()) {
309+
// For simplicity, return the first interface's name
310+
IpInterface ipInterface = response.getRecords().get(0);
311+
s_logger.info("Retrieved network interface: " + ipInterface.getIp().getAddress());
312+
return ipInterface.getIp().getAddress();
313+
} else {
314+
throw new CloudRuntimeException("No network interfaces found for SVM " + storage.getSvmName() +
315+
" for protocol " + storage.getProtocol());
316+
}
317+
} catch (FeignException.FeignClientException e) {
318+
s_logger.error("Exception while retrieving network interfaces: ", e);
319+
throw new CloudRuntimeException("Failed to retrieve network interfaces: " + e.getMessage());
320+
}
321+
}
322+
273323
/**
274324
* Method encapsulates the behavior based on the opted protocol in subclasses.
275325
* it is going to mimic

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

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

22+
import org.opensaml.xml.encryption.Public;
23+
2224
public class Constants {
2325

2426
public static final String NFS = "nfs";
@@ -28,6 +30,7 @@ public class Constants {
2830
public static final String SVM_NAME = "svmName";
2931
public static final String USERNAME = "username";
3032
public static final String PASSWORD = "password";
33+
public static final String DATA_LIF = "dataLIF";
3134
public static final String MANAGEMENT_LIF = "managementLIF";
3235
public static final String VOLUME_NAME = "volumeName";
3336
public static final String VOLUME_UUID = "volumeUUID";
@@ -42,11 +45,20 @@ public class Constants {
4245
public static final String JOB_FAILURE = "failure";
4346
public static final String JOB_SUCCESS = "success";
4447

48+
public static final String TRUE = "true";
49+
public static final String FALSE = "false";
50+
4551
// Query params
4652
public static final String NAME = "name";
4753
public static final String FIELDS = "fields";
4854
public static final String AGGREGATES = "aggregates";
4955
public static final String STATE = "state";
56+
public static final String SVMNAME = "svm.name";
57+
public static final String DATA_NFS = "data_nfs";
58+
public static final String DATA_ISCSI = "data_iscsi";
59+
public static final String IP_ADDRESS = "ip.address";
60+
public static final String SERVICES = "services";
61+
public static final String RETURN_RECORDS = "return_records";
5062

5163
public static final int JOB_MAX_RETRIES = 100;
5264
public static final int CREATE_VOLUME_CHECK_SLEEP_TIME = 2000;

0 commit comments

Comments
 (0)