Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2026-01-22 16:43:05 +05:30
parent a30eb280e5
commit f52b114c8d
10 changed files with 669 additions and 21 deletions

View File

@ -0,0 +1,123 @@
// 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.veeam.api;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.cloudstack.veeam.RouteHandler;
import org.apache.cloudstack.veeam.VeeamControlServlet;
import org.apache.cloudstack.veeam.api.converter.ClusterVOToClusterConverter;
import org.apache.cloudstack.veeam.api.dto.Cluster;
import org.apache.cloudstack.veeam.api.dto.Clusters;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
public class ClustersRouteHandler extends ManagerBase implements RouteHandler {
public static final String BASE_ROUTE = "/api/clusters";
@Inject
ClusterDao clusterDao;
@Inject
DataCenterDao dataCenterDao;
@Override
public boolean start() {
return true;
}
@Override
public int priority() {
return 5;
}
@Override
public boolean canHandle(String method, String path) {
return getSanitizedPath(path).startsWith(BASE_ROUTE);
}
@Override
public void handle(HttpServletRequest req, HttpServletResponse resp, String path, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final String method = req.getMethod();
if (!"GET".equalsIgnoreCase(method)) {
io.methodNotAllowed(resp, "GET", outFormat);
return;
}
final String sanitizedPath = getSanitizedPath(path);
if (sanitizedPath.equals(BASE_ROUTE)) {
handleGet(req, resp, outFormat, io);
return;
}
Pair<String, String> idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE);
if (idAndSubPath != null) {
// /api/disks/{id}
if (idAndSubPath.first() != null) {
if (idAndSubPath.second() == null) {
handleGetById(idAndSubPath.first(), resp, outFormat, io);
return;
}
}
}
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not found");
}
public void handleGet(final HttpServletRequest req, final HttpServletResponse resp,
Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException {
final List<Cluster> result = ClusterVOToClusterConverter.toClusterList(listClusters(), this::getZoneById);
final Clusters response = new Clusters(result);
io.getWriter().write(resp, 200, response, outFormat);
}
protected List<ClusterVO> listClusters() {
return clusterDao.listAll();
}
public void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat,
final VeeamControlServlet io) throws IOException {
final ClusterVO vo = clusterDao.findByUuid(id);
if (vo == null) {
io.notFound(resp, "DataCenter not found: " + id, outFormat);
return;
}
Cluster response = ClusterVOToClusterConverter.toCluster(vo, this::getZoneById);
io.getWriter().write(resp, 200, response, outFormat);
}
private DataCenterVO getZoneById(Long zoneId) {
if (zoneId == null) {
return null;
}
return dataCenterDao.findById(zoneId);
}
}

View File

@ -41,8 +41,10 @@ import org.apache.cloudstack.veeam.api.response.VmEntityResponse;
import org.apache.cloudstack.veeam.utils.Negotiation;
import org.apache.cloudstack.veeam.utils.PathUtil;
import com.cloud.api.query.dao.HostJoinDao;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.dao.VolumeJoinDao;
import com.cloud.api.query.vo.HostJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ManagerBase;
@ -56,6 +58,9 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
@Inject
UserVmJoinDao userVmJoinDao;
@Inject
HostJoinDao hostJoinDao;
@Inject
VolumeJoinDao volumeJoinDao;
@ -143,7 +148,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
return;
}
final List<Vm> result = UserVmJoinVOToVmConverter.toVmList(listUserVms());
final List<Vm> result = UserVmJoinVOToVmConverter.toVmList(listUserVms(), this::getHostById);
final VmCollectionResponse response = new VmCollectionResponse(result);
io.getWriter().write(resp, 200, response, outFormat);
@ -178,7 +183,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
io.notFound(resp, "VM not found: " + id, outFormat);
return;
}
VmEntityResponse response = new VmEntityResponse(UserVmJoinVOToVmConverter.toVm(userVmJoinVO));
VmEntityResponse response = new VmEntityResponse(UserVmJoinVOToVmConverter.toVm(userVmJoinVO, this::getHostById));
io.getWriter().write(resp, 200, response, outFormat);
}
@ -196,4 +201,12 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler {
io.getWriter().write(resp, 200, response, outFormat);
}
private HostJoinVO getHostById(Long hostId) {
if (hostId == null) {
return null;
}
return hostJoinDao.findById(hostId);
}
}

View File

@ -0,0 +1,170 @@
// 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.veeam.api.converter;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.api.ClustersRouteHandler;
import org.apache.cloudstack.veeam.api.DataCentersRouteHandler;
import org.apache.cloudstack.veeam.api.dto.Actions;
import org.apache.cloudstack.veeam.api.dto.Cluster;
import org.apache.cloudstack.veeam.api.dto.Link;
import org.apache.cloudstack.veeam.api.dto.Ref;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
public class ClusterVOToClusterConverter {
public static Cluster toCluster(final ClusterVO vo, final Function<Long, DataCenterVO> dataCenterResolver) {
final Cluster c = new Cluster();
final String basePath = VeeamControlService.ContextPath.value();
// NOTE: oVirt uses UUIDs. If your ClusterVO id is numeric, generate a stable UUID:
// - Prefer: store a UUID in details table and reuse it
// - Fallback: name-based UUID from "cluster:<id>"
final String clusterId = vo.getUuid();
c.id = clusterId;
c.href = basePath + ClustersRouteHandler.BASE_ROUTE + "/" + clusterId;
c.name = vo.getName();
c.description = vo.getName();
c.comment = "";
// --- sensible defaults (match your sample)
c.ballooningEnabled = "true";
c.biosType = "q35_ovmf"; // or "q35_secure_boot" if you want to align with VM BIOS you saw
c.fipsMode = "disabled";
c.firewallType = "firewalld";
c.glusterService = "false";
c.haReservation = "false";
c.switchType = "legacy";
c.threadsAsCores = "false";
c.trustedService = "false";
c.tunnelMigration = "false";
c.upgradeInProgress = "false";
c.upgradePercentComplete = "0";
c.virtService = "true";
c.vncEncryption = "false";
c.logMaxMemoryUsedThreshold = "95";
c.logMaxMemoryUsedThresholdType = "percentage";
// --- cpu (best-effort defaults)
final Cluster.ClusterCpu cpu = new Cluster.ClusterCpu();
cpu.architecture = "x86_64";
cpu.type = "x86_64"; // replace if you can detect host cpu model
c.cpu = cpu;
// --- version (ovirt engine version; keep fixed unless you want to expose something else)
final Cluster.Version ver = new Cluster.Version();
ver.major = "4";
ver.minor = "8";
c.version = ver;
// --- ksm / memory policy (defaults)
c.ksm = new Cluster.Ksm();
c.ksm.enabled = "true";
c.ksm.mergeAcrossNodes = "true";
c.memoryPolicy = new Cluster.MemoryPolicy();
c.memoryPolicy.overCommit = new Cluster.OverCommit();
c.memoryPolicy.overCommit.percent = "100";
c.memoryPolicy.transparentHugepages = new Cluster.TransparentHugepages();
c.memoryPolicy.transparentHugepages.enabled = "true";
// --- migration defaults
c.migration = new Cluster.Migration();
c.migration.autoConverge = "inherit";
c.migration.bandwidth = new Cluster.Bandwidth();
c.migration.bandwidth.assignmentMethod = "auto";
c.migration.compressed = "inherit";
c.migration.encrypted = "inherit";
c.migration.parallelMigrationsPolicy = "disabled";
// policy ref (dummy but valid shape)
c.migration.policy = Ref.of(basePath + "/migrationpolicies/" + stableUuid("migrationpolicy:default"),
stableUuid("migrationpolicy:default")
);
// --- rng sources
c.requiredRngSources = new Cluster.RequiredRngSources();
c.requiredRngSources.requiredRngSource = Collections.singletonList("urandom");
// --- error handling
c.errorHandling = new Cluster.ErrorHandling();
c.errorHandling.onError = "migrate";
// --- fencing policy defaults
c.fencingPolicy = new Cluster.FencingPolicy();
c.fencingPolicy.enabled = "true";
c.fencingPolicy.skipIfConnectivityBroken = new Cluster.SkipIfConnectivityBroken();
c.fencingPolicy.skipIfConnectivityBroken.enabled = "false";
c.fencingPolicy.skipIfConnectivityBroken.threshold = "50";
c.fencingPolicy.skipIfGlusterBricksUp = "false";
c.fencingPolicy.skipIfGlusterQuorumNotMet = "false";
c.fencingPolicy.skipIfSdActive = new Cluster.SkipIfSdActive();
c.fencingPolicy.skipIfSdActive.enabled = "false";
// --- scheduling policy props (optional; dummy ok)
c.customSchedulingPolicyProperties = new Cluster.CustomSchedulingPolicyProperties();
final Cluster.Property p1 = new Cluster.Property(); p1.name = "HighUtilization"; p1.value = "80";
final Cluster.Property p2 = new Cluster.Property(); p2.name = "CpuOverCommitDurationMinutes"; p2.value = "2";
c.customSchedulingPolicyProperties.property = List.of(p1, p2);
// --- data_center ref mapping (CloudStack cluster -> pod -> zone)
if (dataCenterResolver != null) {
final DataCenterVO zone = dataCenterResolver.apply(vo.getDataCenterId());
if (zone != null) {
c.dataCenter = Ref.of(basePath + DataCentersRouteHandler.BASE_ROUTE + "/" + zone.getUuid(), zone.getUuid());
}
}
// --- mac pool & scheduling policy refs (dummy but consistent)
c.macPool = Ref.of(basePath + "/macpools/" + stableUuid("macpool:default"),
stableUuid("macpool:default"));
c.schedulingPolicy = Ref.of(basePath + "/schedulingpolicies/" + stableUuid("schedpolicy:default"),
stableUuid("schedpolicy:default"));
// --- actions.links (can be omitted; but Veeam sometimes expects actions to exist)
final Actions actions = new Actions();
actions.link = Collections.emptyList();
c.actions = actions;
// --- related links (optional)
c.link = List.of(
new Link("networks", c.href + "/networks")
);
return c;
}
public static List<Cluster> toClusterList(final List<ClusterVO> voList,
final Function<Long, DataCenterVO> dataCenterResolver) {
return voList.stream()
.map(vo -> toCluster(vo, dataCenterResolver))
.collect(Collectors.toList());
}
private static String stableUuid(final String key) {
// deterministic UUID, so the same ClusterVO maps to same "ovirt id" every time
return UUID.nameUUIDFromBytes(key.getBytes()).toString();
}
}

View File

@ -36,7 +36,7 @@ public class DataCenterVOToDataCenterConverter {
public static DataCenter toDataCenter(final DataCenterVO zone) {
final String id = zone.getUuid();
final String basePath = VeeamControlService.ContextPath.value();
final String href = basePath + DataCentersRouteHandler.BASE_ROUTE + "/datacenters/" + id;
final String href = basePath + DataCentersRouteHandler.BASE_ROUTE + DataCentersRouteHandler.BASE_ROUTE + "/" + id;
final DataCenter dc = new DataCenter();

View File

@ -77,7 +77,7 @@ public class StoreVOToStorageDomainConverter {
// dc attachment
String dcId = pool.getZoneUuid();
DataCenter dc = new DataCenter();
dc.href = href(basePath, DataCentersRouteHandler.BASE_ROUTE + dcId);
dc.href = href(basePath, DataCentersRouteHandler.BASE_ROUTE + "/" + dcId);
dc.id = dcId;
sd.dataCenters = new DataCenters(List.of(dc));
@ -132,7 +132,7 @@ public class StoreVOToStorageDomainConverter {
// Optionally include dc attachment (your first object had it; second didnt)
String dcId = store.getZoneUuid();
DataCenter dc = new DataCenter();
dc.href = href(basePath, DataCentersRouteHandler.BASE_ROUTE + dcId);
dc.href = href(basePath, DataCentersRouteHandler.BASE_ROUTE + "/" + dcId);
dc.id = dcId;
sd.dataCenters = new DataCenters(List.of(dc));

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.veeam.api.converter;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.cloudstack.veeam.VeeamControlService;
@ -34,6 +35,7 @@ import org.apache.cloudstack.veeam.api.dto.Topology;
import org.apache.cloudstack.veeam.api.dto.Vm;
import org.apache.commons.lang3.StringUtils;
import com.cloud.api.query.vo.HostJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.vm.VirtualMachine;
@ -47,7 +49,7 @@ public final class UserVmJoinVOToVmConverter {
*
* @param src UserVmJoinVO
*/
public static Vm toVm(final UserVmJoinVO src) {
public static Vm toVm(final UserVmJoinVO src, final Function<Long, HostJoinVO> hostResolver) {
if (src == null) {
return null;
}
@ -71,14 +73,32 @@ public final class UserVmJoinVOToVmConverter {
);
dst.template = template;
dst.originalTemplate = template;
dst.host = buildRef(
basePath + ApiService.BASE_ROUTE,
"hosts",
src.getHostUuid());
dst.cluster = buildRef(
basePath + ApiService.BASE_ROUTE,
"clusters",
src.getHostUuid());
if (StringUtils.isNotBlank(src.getHostUuid())) {
dst.host = buildRef(
basePath + ApiService.BASE_ROUTE,
"hosts",
src.getHostUuid());
}
if (hostResolver != null) {
HostJoinVO hostVo = hostResolver.apply(src.getHostId() == null ? src.getLastHostId() : src.getHostId());
if (hostVo != null) {
dst.host = buildRef(
basePath + ApiService.BASE_ROUTE,
"hosts",
hostVo.getUuid());
dst.cluster = buildRef(
basePath + ApiService.BASE_ROUTE,
"clusters",
hostVo.getClusterUuid());
}
}
Long hostId = src.getHostId() != null ? src.getHostId() : src.getLastHostId();
if (hostId != null) {
// I want to get Host data from hostJoinDao but this is a static method without dao access.
}
dst.memory = src.getRamSize() * 1024L * 1024L;
dst.cpu = new Cpu(src.getArch(), new Topology(src.getCpu(), src.getCpu(), 1));
@ -102,9 +122,9 @@ public final class UserVmJoinVOToVmConverter {
return dst;
}
public static List<Vm> toVmList(final List<UserVmJoinVO> srcList) {
public static List<Vm> toVmList(final List<UserVmJoinVO> srcList, final Function<Long, HostJoinVO> hostResolver) {
return srcList.stream()
.map(UserVmJoinVOToVmConverter::toVm)
.map(v -> toVm(v, hostResolver))
.collect(Collectors.toList());
}

View File

@ -23,6 +23,7 @@ import java.util.stream.Collectors;
import org.apache.cloudstack.veeam.VeeamControlService;
import org.apache.cloudstack.veeam.api.ApiService;
import org.apache.cloudstack.veeam.api.DisksRouteHandler;
import org.apache.cloudstack.veeam.api.dto.Actions;
import org.apache.cloudstack.veeam.api.dto.Disk;
import org.apache.cloudstack.veeam.api.dto.DiskAttachment;
@ -38,10 +39,10 @@ import com.cloud.storage.VolumeStats;
public class VolumeJoinVOToDiskConverter {
public static Disk toDisk(final VolumeJoinVO vol) {
final Disk disk = new Disk();
final String apiBase = VeeamControlService.ContextPath.value() + ApiService.BASE_ROUTE;
final String basePath = VeeamControlService.ContextPath.value() + ApiService.BASE_ROUTE;
final String diskId = vol.getUuid();
final String diskHref = apiBase + "/disks/" + diskId;
final String diskHref = basePath + DisksRouteHandler.BASE_ROUTE + "/" + diskId;
disk.id = diskId;
disk.href = diskHref;
@ -49,7 +50,7 @@ public class VolumeJoinVOToDiskConverter {
// Names
disk.name = vol.getName();
disk.alias = vol.getName();
disk.description = "";
disk.description = vol.getName();
// Sizes (bytes)
final long size = vol.getSize();
@ -96,7 +97,7 @@ public class VolumeJoinVOToDiskConverter {
// Disk profile (optional)
disk.diskProfile = Ref.of(
apiBase + "/diskprofiles/" + vol.getDiskOfferingId(),
basePath + "/diskprofiles/" + vol.getDiskOfferingId(),
String.valueOf(vol.getDiskOfferingId())
);
@ -105,7 +106,7 @@ public class VolumeJoinVOToDiskConverter {
Disk.StorageDomains sds = new Disk.StorageDomains();
sds.storageDomain = List.of(
Ref.of(
apiBase + "/storagedomains/" + vol.getPoolUuid(),
basePath + "/storagedomains/" + vol.getPoolUuid(),
vol.getPoolUuid()
)
);

View File

@ -0,0 +1,280 @@
// 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JacksonXmlRootElement(localName = "cluster")
public final class Cluster {
// --- common identity
public String href;
public String id;
public String name;
public String description;
public String comment;
// --- oVirt-ish knobs (strings in oVirt JSON)
@JsonProperty("ballooning_enabled")
@JacksonXmlProperty(localName = "ballooning_enabled")
public String ballooningEnabled; // "true"/"false"
@JsonProperty("bios_type")
@JacksonXmlProperty(localName = "bios_type")
public String biosType; // e.g. "q35_ovmf"
public ClusterCpu cpu;
@JsonProperty("custom_scheduling_policy_properties")
@JacksonXmlProperty(localName = "custom_scheduling_policy_properties")
public CustomSchedulingPolicyProperties customSchedulingPolicyProperties;
@JsonProperty("error_handling")
@JacksonXmlProperty(localName = "error_handling")
public ErrorHandling errorHandling;
@JsonProperty("fencing_policy")
@JacksonXmlProperty(localName = "fencing_policy")
public FencingPolicy fencingPolicy;
@JsonProperty("fips_mode")
@JacksonXmlProperty(localName = "fips_mode")
public String fipsMode; // "disabled"
@JsonProperty("firewall_type")
@JacksonXmlProperty(localName = "firewall_type")
public String firewallType; // "firewalld"
@JsonProperty("gluster_service")
@JacksonXmlProperty(localName = "gluster_service")
public String glusterService;
@JsonProperty("ha_reservation")
@JacksonXmlProperty(localName = "ha_reservation")
public String haReservation;
public Ksm ksm;
@JsonProperty("log_max_memory_used_threshold")
@JacksonXmlProperty(localName = "log_max_memory_used_threshold")
public String logMaxMemoryUsedThreshold;
@JsonProperty("log_max_memory_used_threshold_type")
@JacksonXmlProperty(localName = "log_max_memory_used_threshold_type")
public String logMaxMemoryUsedThresholdType;
@JsonProperty("memory_policy")
@JacksonXmlProperty(localName = "memory_policy")
public MemoryPolicy memoryPolicy;
public Migration migration;
@JsonProperty("required_rng_sources")
@JacksonXmlProperty(localName = "required_rng_sources")
public RequiredRngSources requiredRngSources;
@JsonProperty("switch_type")
@JacksonXmlProperty(localName = "switch_type")
public String switchType;
@JsonProperty("threads_as_cores")
@JacksonXmlProperty(localName = "threads_as_cores")
public String threadsAsCores;
@JsonProperty("trusted_service")
@JacksonXmlProperty(localName = "trusted_service")
public String trustedService;
@JsonProperty("tunnel_migration")
@JacksonXmlProperty(localName = "tunnel_migration")
public String tunnelMigration;
@JsonProperty("upgrade_in_progress")
@JacksonXmlProperty(localName = "upgrade_in_progress")
public String upgradeInProgress;
@JsonProperty("upgrade_percent_complete")
@JacksonXmlProperty(localName = "upgrade_percent_complete")
public String upgradePercentComplete;
public Version version;
@JsonProperty("virt_service")
@JacksonXmlProperty(localName = "virt_service")
public String virtService;
@JsonProperty("vnc_encryption")
@JacksonXmlProperty(localName = "vnc_encryption")
public String vncEncryption;
// --- references
@JsonProperty("data_center")
@JacksonXmlProperty(localName = "data_center")
public Ref dataCenter;
@JsonProperty("mac_pool")
@JacksonXmlProperty(localName = "mac_pool")
public Ref macPool;
@JsonProperty("scheduling_policy")
@JacksonXmlProperty(localName = "scheduling_policy")
public Ref schedulingPolicy;
// --- actions + links
public Actions actions;
@JacksonXmlElementWrapper(useWrapping = false)
public List<Link> link;
public Cluster() {}
// ===== nested DTOs =====
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class ClusterCpu {
public String architecture;
public String type;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class CustomSchedulingPolicyProperties {
@JacksonXmlElementWrapper(useWrapping = false)
@JsonProperty("property")
public List<Property> property;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Property {
public String name;
public String value;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class ErrorHandling {
@JsonProperty("on_error")
@JacksonXmlProperty(localName = "on_error")
public String onError; // "migrate"
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class FencingPolicy {
public String enabled;
@JsonProperty("skip_if_connectivity_broken")
@JacksonXmlProperty(localName = "skip_if_connectivity_broken")
public SkipIfConnectivityBroken skipIfConnectivityBroken;
@JsonProperty("skip_if_gluster_bricks_up")
@JacksonXmlProperty(localName = "skip_if_gluster_bricks_up")
public String skipIfGlusterBricksUp;
@JsonProperty("skip_if_gluster_quorum_not_met")
@JacksonXmlProperty(localName = "skip_if_gluster_quorum_not_met")
public String skipIfGlusterQuorumNotMet;
@JsonProperty("skip_if_sd_active")
@JacksonXmlProperty(localName = "skip_if_sd_active")
public SkipIfSdActive skipIfSdActive;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class SkipIfConnectivityBroken {
public String enabled;
public String threshold;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class SkipIfSdActive {
public String enabled;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Ksm {
public String enabled;
@JsonProperty("merge_across_nodes")
@JacksonXmlProperty(localName = "merge_across_nodes")
public String mergeAcrossNodes;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class MemoryPolicy {
@JsonProperty("over_commit")
@JacksonXmlProperty(localName = "over_commit")
public OverCommit overCommit;
@JsonProperty("transparent_hugepages")
@JacksonXmlProperty(localName = "transparent_hugepages")
public TransparentHugepages transparentHugepages;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class OverCommit {
public String percent;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class TransparentHugepages {
public String enabled;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Migration {
@JsonProperty("auto_converge")
@JacksonXmlProperty(localName = "auto_converge")
public String autoConverge;
public Bandwidth bandwidth;
public String compressed;
public String encrypted;
@JsonProperty("parallel_migrations_policy")
@JacksonXmlProperty(localName = "parallel_migrations_policy")
public String parallelMigrationsPolicy;
public Ref policy;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Bandwidth {
@JsonProperty("assignment_method")
@JacksonXmlProperty(localName = "assignment_method")
public String assignmentMethod;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class RequiredRngSources {
@JsonProperty("required_rng_source")
@JacksonXmlElementWrapper(useWrapping = false)
public List<String> requiredRngSource;
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Version {
public String major;
public String minor;
}
}

View File

@ -0,0 +1,40 @@
// 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.veeam.api.dto;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JacksonXmlRootElement(localName = "clusters")
public final class Clusters {
@JsonProperty("cluster")
@JacksonXmlElementWrapper(useWrapping = false)
public List<Cluster> cluster;
public Clusters() {}
public Clusters(final List<Cluster> cluster) {
this.cluster = cluster;
}
}

View File

@ -34,6 +34,7 @@
<bean id="veeamControlApiService" class="org.apache.cloudstack.veeam.api.ApiService" />
<bean id="vmsRouteHandler" class="org.apache.cloudstack.veeam.api.VmsRouteHandler"/>
<bean id="dataCentersRouteHandler" class="org.apache.cloudstack.veeam.api.DataCentersRouteHandler"/>
<bean id="clustersRouteHandler" class="org.apache.cloudstack.veeam.api.ClustersRouteHandler"/>
<bean id="disksRouteHandler" class="org.apache.cloudstack.veeam.api.DisksRouteHandler"/>
<bean id="veeamControlSsoService" class="org.apache.cloudstack.veeam.sso.SsoService"/>