Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion plugins/storage/volume/ontap/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<spring-cloud.version>2021.0.7</spring-cloud.version>
<openfeign.version>11.0</openfeign.version>
<json.version>20230227</json.version>
<jackson-databind.version>2.15.2</jackson-databind.version>
<jackson-databind.version>2.13.4</jackson-databind.version>
Copy link

Copilot AI Nov 10, 2025

Choose a reason for hiding this comment

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

Downgrading jackson-databind from 2.15.2 to 2.13.4 may introduce known security vulnerabilities. Version 2.13.4 was released in September 2022 and several CVEs have been fixed in later versions. Consider using a more recent version or document the reason for this downgrade.

Suggested change
<jackson-databind.version>2.13.4</jackson-databind.version>
<jackson-databind.version>2.15.2</jackson-databind.version>

Copilot uses AI. Check for mistakes.
<httpclient.version>4.5.14</httpclient.version>
<swagger-annotations.version>1.6.2</swagger-annotations.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ private StorageStrategy getStrategyByStoragePoolDetails(Map<String, String> deta
}
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),
details.get(Constants.MANAGEMENT_LIF), details.get(Constants.SVM_NAME), Long.parseLong(details.get(Constants.SIZE)), ProtocolType.valueOf(protocol),
Boolean.parseBoolean(details.get(Constants.IS_DISAGGREGATED)));
StorageStrategy storageStrategy = StorageProviderFactory.getStrategy(ontapStorage);
boolean isValid = storageStrategy.connect();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.cloudstack.storage.feign;

import com.fasterxml.jackson.databind.ObjectMapper;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.Client;
Expand All @@ -11,7 +31,6 @@
import feign.codec.EncodeException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
Expand All @@ -36,13 +55,11 @@ public class FeignConfiguration {
private final int retryMaxInterval = 5;
private final String ontapFeignMaxConnection = "80";
private final String ontapFeignMaxConnectionPerRoute = "20";
private final JsonMapper jsonMapper;
private final ObjectMapper objectMapper;

public FeignConfiguration() {
this.jsonMapper = JsonMapper.builder()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.findAndAddModules()
.build();
this.objectMapper = new ObjectMapper();
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

public Client createClient() {
Expand Down Expand Up @@ -105,7 +122,7 @@ public void encode(Object object, Type bodyType, feign.RequestTemplate template)
return;
}
try {
byte[] jsonBytes = jsonMapper.writeValueAsBytes(object);
byte[] jsonBytes = objectMapper.writeValueAsBytes(object);
template.body(jsonBytes, StandardCharsets.UTF_8);
template.header("Content-Type", "application/json");
} catch (JsonProcessingException e) {
Expand All @@ -126,7 +143,7 @@ public Object decode(Response response, Type type) throws IOException, DecodeExc
try (InputStream bodyStream = response.body().asInputStream()) {
json = new String(bodyStream.readAllBytes(), StandardCharsets.UTF_8);
logger.debug("Decoding JSON response: {}", json);
return jsonMapper.readValue(json, jsonMapper.getTypeFactory().constructType(type));
return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type));
} catch (IOException e) {
logger.error("Error decoding JSON response. Status: {}, Raw body: {}", response.status(), json, e);
throw new DecodeException(response.status(), "Error decoding JSON response", response.request(), e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.apache.cloudstack.storage.feign.client;
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Missing Apache license header. All source files should include the standard Apache Software Foundation license header at the top of the file.

Copilot uses AI. Check for mistakes.

import feign.Headers;
import feign.Param;
import feign.QueryMap;
import feign.RequestLine;
import org.apache.cloudstack.storage.feign.model.IpInterface;
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;

import java.util.Map;

public interface NetworkFeignClient {
@RequestLine("GET /api/network/ip/interfaces")
@Headers({"Authorization: {authHeader}"})
OntapResponse<IpInterface> getNetworkIpInterfaces(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryParams);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,30 @@
*/
package org.apache.cloudstack.storage.feign.client;

import feign.QueryMap;
import org.apache.cloudstack.storage.feign.model.Volume;
import org.apache.cloudstack.storage.feign.model.response.JobResponse;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import org.apache.cloudstack.storage.feign.model.response.OntapResponse;

import java.util.Map;

public interface VolumeFeignClient {

@RequestLine("DELETE /api/storage/volumes/{uuid}")
@Headers({"Authorization: {authHeader}"})
void deleteVolume(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
JobResponse deleteVolume(@Param("authHeader") String authHeader, @Param("uuid") String uuid);

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

@RequestLine("GET /api/storage/volumes")
@Headers({"Authorization: {authHeader}"})
OntapResponse<Volume> getAllVolumes(@Param("authHeader") String authHeader, @QueryMap Map<String, Object> queryParams);

@RequestLine("GET /api/storage/volumes/{uuid}")
@Headers({"Authorization: {authHeader}"})
Volume getVolumeByUUID(@Param("authHeader") String authHeader, @Param("uuid") String uuid);
Expand All @@ -42,4 +50,3 @@ public interface VolumeFeignClient {
@Headers({"Accept: {acceptHeader}", "Authorization: {authHeader}"})
JobResponse updateVolumeRebalancing(@Param("acceptHeader") String acceptHeader, @Param("uuid") String uuid, Volume volumeRequest);
}

Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,43 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Objects;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Aggregate {
// Replace previous enum with case-insensitive mapping
public enum StateEnum {
ONLINE("online");
private final String value;

StateEnum(String value) {
this.value = value;
}

@JsonValue
public String getValue() {
return value;
}

@Override
public String toString() {
return String.valueOf(value);
}

@JsonCreator
public static StateEnum fromValue(String text) {
for (StateEnum b : StateEnum.values()) {
if (String.valueOf(b.value).equals(text)) {
return b;
}
}
return null;
}
}

@JsonProperty("name")
private String name = null;
Expand All @@ -40,6 +71,13 @@ public int hashCode() {
@JsonProperty("uuid")
private String uuid = null;

@JsonProperty("state")
private StateEnum state = null;

@JsonProperty("space")
private AggregateSpace space = null;


public Aggregate name(String name) {
this.name = name;
return this;
Expand All @@ -65,6 +103,21 @@ public void setUuid(String uuid) {
this.uuid = uuid;
}

public StateEnum getState() {
return state;
}

public AggregateSpace getSpace() {
return space;
}

public Double getAvailableBlockStorageSpace() {
if (space != null && space.blockStorage != null) {
return space.blockStorage.available;
}
return null;
}


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

public static class AggregateSpace {
@JsonProperty("block_storage")
private AggregateSpaceBlockStorage blockStorage = null;
}

public static class AggregateSpaceBlockStorage {
@JsonProperty("available")
private Double available = null;
@JsonProperty("size")
private Double size = null;
@JsonProperty("used")
private Double used = null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.cloudstack.storage.feign.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.List;
import java.util.Objects;

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class IpInterface {
@JsonProperty("uuid")
private String uuid;

@JsonProperty("name")
private String name;

@JsonProperty("ip")
private IpInfo ip;

@JsonProperty("svm")
private Svm svm;

@JsonProperty("services")
private List<String> services;

// Getters and setters
public String getUuid() {
return uuid;
}

public void setUuid(String uuid) {
this.uuid = uuid;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public IpInfo getIp() {
return ip;
}

public void setIp(IpInfo ip) {
this.ip = ip;
}

public Svm getSvm() {
return svm;
}

public void setSvm(Svm svm) {
this.svm = svm;
}

public List<String> getServices() {
return services;
}

public void setServices(List<String> services) {
this.services = services;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
IpInterface that = (IpInterface) o;
return Objects.equals(uuid, that.uuid) &&
Objects.equals(name, that.name) &&
Objects.equals(ip, that.ip) &&
Objects.equals(svm, that.svm) &&
Objects.equals(services, that.services);
}

@Override
public int hashCode() {
return Objects.hash(uuid, name, ip, svm, services);
}

@Override
public String toString() {
return "IpInterface{" +
"uuid='" + uuid + '\'' +
", name='" + name + '\'' +
", ip=" + ip +
", svm=" + svm +
", services=" + services +
'}';
}

// Nested class for IP information
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class IpInfo {
@JsonProperty("address")
private String address;

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IpInfo ipInfo = (IpInfo) o;
return Objects.equals(address, ipInfo.address);
}

@Override
public int hashCode() {
return Objects.hash(address);
}

@Override
public String toString() {
return "IpInfo{" +
"address='" + address + '\'' +
'}';
}
}
}
Loading
Loading