Skip to content
Next Next commit
CSTACKEX-29 Cluster, SVM and Aggr Feign Client
  • Loading branch information
Gupta, Surya Gupta, Surya
Gupta, Surya authored and Gupta, Surya committed Oct 14, 2025
commit 951d8eada772128f2fc255494772e8fd89f20bbb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.apache.cloudstack.storage.feign.client;

import org.apache.cloudstack.storage.feign.model.AggregateResponse;
import org.apache.cloudstack.storage.feign.model.Aggregate;
import org.apache.cloudstack.storage.feign.FeignConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.net.URI;

@Lazy

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

To instantiate only when required, we can use this across all the feign clients?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

some feign client we need upfront like Cluster, SVM for others we will use @lazy

@FeignClient(name="AggregateClient", url="https://{clusterIP}/api/storage/aggregates", configuration = FeignConfiguration.class)
public interface AggregateFeignClient {
@RequestMapping(method=RequestMethod.GET)
AggregateResponse getAllAggregates(URI baseURL,@RequestHeader("Authorization") String header);

@RequestMapping(method=RequestMethod.GET, value="/{uuid}")
Aggregate getAggregateByUUID(URI baseURL,@RequestHeader("Authorization") String header, @PathVariable(name = "uuid", required = true) String uuid);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.apache.cloudstack.storage.feign.client;

import org.apache.cloudstack.storage.feign.FeignConfiguration;
import org.apache.cloudstack.storage.feign.model.Cluster;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.net.URI;

@FeignClient(name="ClusterClient", url="https://{clusterIP}/api/cluster", configuration = FeignConfiguration.class)
public interface ClusterFeignClient {
@RequestMapping(method= RequestMethod.GET)
Cluster getCluster(URI baseURL, @RequestHeader("Authorization") String header, @RequestHeader("return_records") boolean value);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.apache.cloudstack.storage.feign.client;

import org.apache.cloudstack.storage.feign.FeignConfiguration;
import org.apache.cloudstack.storage.feign.model.SvmResponse;
import org.apache.cloudstack.storage.feign.model.Svm;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.net.URI;

@FeignClient(name = "SvmClient", url = "https://{clusterIP}/api/svm/svms", configuration = FeignConfiguration.class)
public interface SvmFeignClient {
@RequestMapping(method = RequestMethod.GET)
SvmResponse getAllSvms(URI baseURL, @RequestHeader("Authorization") String header);
@RequestMapping(method = RequestMethod.GET, value = "/{uuid}")
Svm getSvmByUUID(URI baseURL, @RequestHeader("Authorization") String header);

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@


import org.apache.cloudstack.storage.feign.FeignConfiguration;
import org.apache.cloudstack.storage.feign.model.request.VolumeRequestDTO;
import org.apache.cloudstack.storage.feign.model.response.JobResponseDTO;
import org.apache.cloudstack.storage.feign.model.response.VolumeDetailsResponseDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -34,27 +37,20 @@
public interface VolumeFeignClient {

@DeleteMapping("/storage/volumes/{id}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

/api is missing

void deleteVolume(@RequestHeader("Authorization") String authHeader,
@PathVariable("id") String volumeId);
void deleteVolume(@RequestHeader("Authorization") String authHeader, @PathVariable("id") String volumeId);

@PostMapping("/api/storage/volumes")
org.apache.cloudstack.storage.feign.model.response.JobResponseDTO createVolumeWithJob(
@RequestHeader("Authorization") String authHeader,
@RequestBody org.apache.cloudstack.storage.feign.model.request.VolumeRequestDTO request
JobResponseDTO createVolumeWithJob(@RequestHeader("Authorization") String authHeader, @RequestBody VolumeRequestDTO request
);

@GetMapping("/api/storage/volumes/{uuid}")
org.apache.cloudstack.storage.feign.model.response.VolumeDetailsResponseDTO getVolumeDetails(
@RequestHeader("Authorization") String authHeader,
@PathVariable("uuid") String uuid
VolumeDetailsResponseDTO getVolumeDetails(@RequestHeader("Authorization") String authHeader, @PathVariable("uuid") String uuid
);

@PatchMapping("/api/storage/volumes/{uuid}")
org.apache.cloudstack.storage.feign.model.response.JobResponseDTO updateVolumeRebalancing(
@RequestHeader("accept") String acceptHeader,
org.apache.cloudstack.storage.feign.model.response.JobResponseDTO updateVolumeRebalancing(@RequestHeader("accept") String acceptHeader,
@PathVariable("uuid") String uuid,
@RequestBody org.apache.cloudstack.storage.feign.model.request.VolumeRequestDTO request
);


}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can't we reuse this in VolumeRequestDTO, instead of AggregateDTO?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

As discussed, will remove the request DTOs and use the same ontap model with null annotation

Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,24 @@
package org.apache.cloudstack.storage.feign.model;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;
import io.swagger.annotations.ApiModelProperty;

import java.util.Objects;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Aggregate {

@SerializedName("name")
@JsonProperty("name")
private String name = null;

@Override
public int hashCode() {
return Objects.hash(getName(), getUuid());
}

@SerializedName("uuid")
@JsonProperty("uuid")
private String uuid = null;

public Aggregate name(String name) {
Expand All @@ -48,6 +50,7 @@ public Aggregate name(String name) {
*
* @return name
**/
@ApiModelProperty(value = "Name of aggregate")
public String getName() {
return name;
}
Expand All @@ -66,6 +69,7 @@ public Aggregate uuid(String uuid) {
*
* @return uuid
**/
@ApiModelProperty(value = "UUID of aggregate")
public String getUuid() {
return uuid;
}
Expand Down

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This file can be shifted to model/response

@suryag1201 suryag1201 Oct 15, 2025 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

As discussed, will create one generic ontap response class

Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* ONTAP REST API
* ONTAP adds support for an expansive RESTful API. The documentation below provides information about the types of API calls available to you, as well as details about using each API endpoint. You can learn more about the ONTAP REST API and ONTAP in the ONTAP 9 Documentation Center: http://docs.netapp.com/ontap-9/topic/com.netapp.doc.dot-rest-api/home.html. NetApp welcomes your comments and suggestions about the ONTAP REST API and the documentation for its use.</br> **Using the ONTAP REST API online documentation** Each API method includes usage examples, as well as a model that displays all the required and optional properties supported by the method. Click the _Model_ link, available with each API method, to see all the required and optional properties supported by each method.
*
* OpenAPI spec version: v1
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/


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

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.SerializedName;
import io.swagger.annotations.ApiModelProperty;

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

/**
* AggregateResponse
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class AggregateResponse {
@JsonProperty("error")
private Error error = null;
@JsonProperty("num_records")
private Integer numRecords = null;
@JsonProperty("records")
private List<Aggregate> records = null;

public AggregateResponse error(Error error) {
this.error = error;
return this;
}

/**
* Get error
* @return error
**/
@ApiModelProperty(value = "")
public Error getError() {
return error;
}

public void setError(Error error) {
this.error = error;
}

public AggregateResponse numRecords(Integer numRecords) {
this.numRecords = numRecords;
return this;
}

/**
* Number of records
* @return numRecords
**/
@ApiModelProperty(value = "Number of records")
public Integer getNumRecords() {
return numRecords;
}

public void setNumRecords(Integer numRecords) {
this.numRecords = numRecords;
}

public AggregateResponse records(List<Aggregate> records) {
this.records = records;
return this;
}

public AggregateResponse addRecordsItem(Aggregate recordsItem) {
if (this.records == null) {
this.records = new ArrayList<Aggregate>();
}
this.records.add(recordsItem);
return this;
}

/**
* Get records
* @return records
**/
@ApiModelProperty(value = "")
public List<Aggregate> getRecords() {
return records;
}

public void setRecords(List<Aggregate> records) {
this.records = records;
}


@Override

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

for response object classes, we need not to have equals, hashcode and testing methods.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

removed all responses and created one generic OnTapResponse for all object which has numRecord or records

public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AggregateResponse aggregateResponse = (AggregateResponse) o;
return
Objects.equals(this.error, aggregateResponse.error) &&
Objects.equals(this.numRecords, aggregateResponse.numRecords) &&
Objects.equals(this.records, aggregateResponse.records);
}

@Override
public int hashCode() {
return Objects.hash(error, numRecords, records);
}


@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class AggregateResponse {\n");

sb.append(" error: ").append(toIndentedString(error)).append("\n");
sb.append(" numRecords: ").append(toIndentedString(numRecords)).append("\n");
sb.append(" records: ").append(toIndentedString(records)).append("\n");
sb.append("}");
return sb.toString();
}

/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}

}

Loading