diff --git a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java index 3b946eba962..034ea61ee0e 100644 --- a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java +++ b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagDao.java @@ -63,6 +63,8 @@ public interface ResourceTagDao extends GenericDao { List listByResourceUuid(String resourceUuid); - List listByResourceTypeAndOwners(ResourceObjectType resourceType, List accountIds, - List domainIds, Filter filter); + List listByResourceTypeKeyAndOwners(ResourceObjectType resourceType, String key, + List accountIds, List domainIds, Filter filter); + + ResourceTagVO findByResourceTypeKeyAndValue(ResourceObjectType resourceType, String key, String value); } diff --git a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java index 5dd79983766..b82dd5ec3de 100644 --- a/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/tags/dao/ResourceTagsDaoImpl.java @@ -124,10 +124,12 @@ public class ResourceTagsDaoImpl extends GenericDaoBase imp } @Override - public List listByResourceTypeAndOwners(ResourceObjectType resourceType, List accountIds, - List domainIds, Filter filter) { + public List listByResourceTypeKeyAndOwners(ResourceObjectType resourceType, String key, + List accountIds, List domainIds, + Filter filter) { SearchBuilder sb = createSearchBuilder(); sb.and("resourceType", sb.entity().getResourceType(), Op.EQ); + sb.and("key", sb.entity().getKey(), Op.EQ); boolean accountIdsNotEmpty = CollectionUtils.isNotEmpty(accountIds); boolean domainIdsNotEmpty = CollectionUtils.isNotEmpty(domainIds); if (accountIdsNotEmpty || domainIdsNotEmpty) { @@ -136,8 +138,9 @@ public class ResourceTagsDaoImpl extends GenericDaoBase imp sb.cp(); } sb.done(); - final SearchCriteria sc = sb.create();; + final SearchCriteria sc = sb.create(); sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key); if (accountIdsNotEmpty) { sc.setParameters("account", accountIds.toArray()); } @@ -146,4 +149,19 @@ public class ResourceTagsDaoImpl extends GenericDaoBase imp } return listBy(sc, filter); } + + @Override + public ResourceTagVO findByResourceTypeKeyAndValue(ResourceObjectType resourceType, String key, + String value) { + SearchBuilder sb = createSearchBuilder(); + sb.and("resourceType", sb.entity().getResourceType(), Op.EQ); + sb.and("key", sb.entity().getKey(), Op.EQ); + sb.and("value", sb.entity().getValue(), Op.EQ); + sb.done(); + final SearchCriteria sc = sb.create(); + sc.setParameters("resourceType", resourceType); + sc.setParameters("key", key); + sc.setParameters("value", value); + return findOneBy(sc); + } } diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql index 6f31fc17bce..db3fd8be484 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.user_vm_view.sql @@ -56,6 +56,7 @@ SELECT `vm_instance`.`display_vm` AS `display_vm`, `vm_instance`.`delete_protection` AS `delete_protection`, `guest_os`.`uuid` AS `guest_os_uuid`, + `guest_os`.`display_name` AS `guest_os_display_name`, `vm_instance`.`pod_id` AS `pod_id`, `host_pod_ref`.`uuid` AS `pod_uuid`, `vm_instance`.`private_ip_address` AS `private_ip_address`, diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java index d59ef9e2f79..693bfb287c6 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/RouteHandler.java @@ -19,7 +19,6 @@ package org.apache.cloudstack.veeam; import java.io.BufferedReader; import java.io.IOException; -import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -30,7 +29,6 @@ import org.apache.logging.log4j.Logger; import com.cloud.utils.component.Adapter; public interface RouteHandler extends Adapter { - static final Pattern PAGE_PATTERN = Pattern.compile("\\bpage\\s+(\\d+)"); default int priority() { return 0; } boolean canHandle(String method, String path) throws IOException; void handle(HttpServletRequest req, HttpServletResponse resp, String path, Negotiation.OutFormat outFormat, VeeamControlServlet io) @@ -60,7 +58,6 @@ public interface RouteHandler extends Adapter { if (!"application/json".equals(mime) && !"application/x-www-form-urlencoded".equals(mime)) { return null; } - String result = null; try { StringBuilder data = new StringBuilder(); String line; @@ -74,4 +71,9 @@ public interface RouteHandler extends Adapter { return null; } } + + static boolean isRequestAsync(HttpServletRequest req) { + String asyncStr = req.getParameter("async"); + return Boolean.TRUE.toString().equals(asyncStr); + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServer.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServer.java index 3121fd6ecf4..a70babe9b27 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServer.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServer.java @@ -28,7 +28,7 @@ import javax.servlet.DispatcherType; import javax.servlet.http.HttpServletRequest; import org.apache.cloudstack.utils.server.ServerPropertiesUtil; -import org.apache.cloudstack.veeam.api.ApiService; +import org.apache.cloudstack.veeam.api.ApiRouteHandler; import org.apache.cloudstack.veeam.filter.AllowedClientCidrsFilter; import org.apache.cloudstack.veeam.filter.BearerOrBasicAuthFilter; import org.apache.commons.lang3.StringUtils; @@ -125,12 +125,12 @@ public class VeeamControlServer { // CIDR filter for all routes AllowedClientCidrsFilter cidrFilter = new AllowedClientCidrsFilter(veeamControlService); FilterHolder cidrHolder = new FilterHolder(cidrFilter); - ctx.addFilter(cidrHolder, ApiService.BASE_ROUTE + "/*", EnumSet.of(DispatcherType.REQUEST)); + ctx.addFilter(cidrHolder, ApiRouteHandler.BASE_ROUTE + "/*", EnumSet.of(DispatcherType.REQUEST)); // Bearer or Basic Auth for all routes BearerOrBasicAuthFilter authFilter = new BearerOrBasicAuthFilter(veeamControlService); FilterHolder authHolder = new FilterHolder(authFilter); - ctx.addFilter(authHolder, ApiService.BASE_ROUTE + "/*", EnumSet.of(DispatcherType.REQUEST)); + ctx.addFilter(authHolder, ApiRouteHandler.BASE_ROUTE + "/*", EnumSet.of(DispatcherType.REQUEST)); // Front controller servlet ctx.addServlet(new ServletHolder(new VeeamControlServlet(routeHandlers)), "/*"); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlService.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlService.java index 8e4abef9743..159d7eead06 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlService.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlService.java @@ -21,10 +21,13 @@ import java.util.List; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.utils.CloudStackVersion; import com.cloud.utils.component.PluggableService; public interface VeeamControlService extends PluggableService, Configurable { + String PLUGIN_NAME = "CloudStack Veeam Control Service"; + ConfigKey Enabled = new ConfigKey<>("Advanced", Boolean.class, "integration.veeam.control.enabled", "false", "Enable the Veeam Integration REST API server", false); ConfigKey BindAddress = new ConfigKey<>("Advanced", String.class, "integration.veeam.control.bind.address", @@ -57,4 +60,16 @@ public interface VeeamControlService extends PluggableService, Configurable { List getAllowedClientCidrs(); boolean validateCredentials(String username, String password); + + static String getPackageVersion() { + return VeeamControlService.class.getPackage().getImplementationVersion(); + } + + static CloudStackVersion getCSVersion() { + try { + return CloudStackVersion.parse(getPackageVersion()); + } catch (Exception e) { + return null; + } + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServlet.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServlet.java index 8016bf9c17a..172aa16e5d7 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServlet.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/VeeamControlServlet.java @@ -101,19 +101,6 @@ public class VeeamControlServlet extends HttpServlet { String name = headerNames.nextElement(); details.append(name).append("=").append(req.getHeader(name)).append("; "); } -// String body = ""; -// if (!"GET".equalsIgnoreCase(method)) { -// StringBuilder bodySb = new StringBuilder(); -// java.io.BufferedReader reader = req.getReader(); -// if (reader != null) { -// String line; -// while ((line = reader.readLine()) != null) { -// bodySb.append(line).append('\n'); -// } -// } -// body = bodySb.toString().trim(); -// } -// details.append(", Body: ").append(body); LOGGER.debug(details.toString()); } catch (Exception e) { LOGGER.debug("Failed to capture request details", e); @@ -135,8 +122,8 @@ public class VeeamControlServlet extends HttpServlet { } writer.write(resp, 200, Map.of( - "name", "CloudStack Veeam Control Service", - "pluginVersion", "0.1"), outFormat); + "name", VeeamControlService.PLUGIN_NAME, + "pluginVersion", this.getClass().getPackage().getImplementationVersion()), outFormat); } public void methodNotAllowed(final HttpServletResponse resp, final String allow, final Negotiation.OutFormat outFormat) throws IOException { diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java index 706752c6281..48332b702d1 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java @@ -238,7 +238,8 @@ public class ServerAdapter extends ManagerBase { Storage.StoragePoolType.NetworkFilesystem, Storage.StoragePoolType.SharedMountPoint ); - public static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough"; + private static final String VM_TA_KEY = "veeam_tag"; + private static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough"; @Inject RoleService roleService; @@ -413,22 +414,6 @@ public class ServerAdapter extends ManagerBase { accountService.getActiveAccountById(userAccount.getAccountId())); } - protected Pair getServiceAccount() { - String serviceAccountUuid = VeeamControlService.ServiceAccountId.value(); - if (StringUtils.isEmpty(serviceAccountUuid)) { - throw new CloudRuntimeException("Service account is not configured, unable to proceed"); - } - Account account = accountService.getActiveAccountByUuid(serviceAccountUuid); - if (account == null) { - throw new CloudRuntimeException("Service account with ID " + serviceAccountUuid + " not found, unable to proceed"); - } - User user = accountService.getOneActiveUserForAccount(account); - if (user == null) { - throw new CloudRuntimeException("No active user found for service account with ID " + serviceAccountUuid); - } - return new Pair<>(user, account); - } - protected void waitForJobCompletion(long jobId) { long timeoutNanos = TimeUnit.MINUTES.toNanos(5); final long deadline = System.nanoTime() + timeoutNanos; @@ -858,6 +843,22 @@ public class ServerAdapter extends ManagerBase { return vmInstanceDetailsDao.listDetailsKeyPairs(instanceId, true); } + public Pair getServiceAccount() { + String serviceAccountUuid = VeeamControlService.ServiceAccountId.value(); + if (StringUtils.isEmpty(serviceAccountUuid)) { + throw new CloudRuntimeException("Service account is not configured, unable to proceed"); + } + Account account = accountService.getActiveAccountByUuid(serviceAccountUuid); + if (account == null) { + throw new CloudRuntimeException("Service account with ID " + serviceAccountUuid + " not found, unable to proceed"); + } + User user = accountService.getOneActiveUserForAccount(account); + if (user == null) { + throw new CloudRuntimeException("No active user found for service account with ID " + serviceAccountUuid); + } + return new Pair<>(user, account); + } + @Override public boolean start() { getServiceAccount(); @@ -1185,15 +1186,13 @@ public class ServerAdapter extends ManagerBase { @ApiAccess(command = ListTagsCmd.class) protected List listTagsByInstanceId(final long instanceId) { - List vmResourceTags = resourceTagDao.listBy(instanceId, - ResourceTag.ResourceObjectType.UserVm); + ResourceTag vmResourceTag = resourceTagDao.findByKey(instanceId, + ResourceTag.ResourceObjectType.UserVm, VM_TA_KEY); List tags = new ArrayList<>(); - for (ResourceTag t : vmResourceTags) { - if (t instanceof ResourceTagVO) { - tags.add((ResourceTagVO)t); - continue; - } - tags.add(resourceTagDao.findById(t.getId())); + if (vmResourceTag instanceof ResourceTagVO) { + tags.add((ResourceTagVO)vmResourceTag); + } else { + tags.add(resourceTagDao.findById(vmResourceTag.getId())); } return ResourceTagVOToTagConverter.toTags(tags); } @@ -1760,8 +1759,8 @@ public class ServerAdapter extends ManagerBase { List tags = new ArrayList<>(getDummyTags().values()); Filter filter = new Filter(ResourceTagVO.class, "id", true, offset, limit); Pair, List> ownerDetails = getResourceOwnerFiltersWithDomainIds(); - List vmResourceTags = resourceTagDao.listByResourceTypeAndOwners( - ResourceTag.ResourceObjectType.UserVm, ownerDetails.first(), ownerDetails.second(), filter); + List vmResourceTags = resourceTagDao.listByResourceTypeKeyAndOwners( + ResourceTag.ResourceObjectType.UserVm, VM_TA_KEY, ownerDetails.first(), ownerDetails.second(), filter); if (CollectionUtils.isNotEmpty(vmResourceTags)) { tags.addAll(ResourceTagVOToTagConverter.toTags(vmResourceTags)); } @@ -1775,7 +1774,8 @@ public class ServerAdapter extends ManagerBase { } Tag tag = getDummyTags().get(uuid); if (tag == null) { - ResourceTagVO resourceTagVO = resourceTagDao.findByUuid(uuid); + ResourceTagVO resourceTagVO = resourceTagDao.findByResourceTypeKeyAndValue( + ResourceTag.ResourceObjectType.UserVm, VM_TA_KEY, uuid); accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, resourceTagVO); if (resourceTagVO != null) { diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiRouteHandler.java similarity index 80% rename from plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java rename to plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiRouteHandler.java index d076604515a..be71164d672 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiService.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ApiRouteHandler.java @@ -21,21 +21,21 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.UUID; +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.VeeamControlService; import org.apache.cloudstack.veeam.VeeamControlServlet; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Api; import org.apache.cloudstack.veeam.api.dto.ApiSummary; import org.apache.cloudstack.veeam.api.dto.EmptyElement; import org.apache.cloudstack.veeam.api.dto.Link; import org.apache.cloudstack.veeam.api.dto.ProductInfo; import org.apache.cloudstack.veeam.api.dto.Ref; -import org.apache.cloudstack.veeam.api.dto.SpecialObjects; import org.apache.cloudstack.veeam.api.dto.SummaryCount; import org.apache.cloudstack.veeam.api.dto.Version; import org.apache.cloudstack.veeam.utils.Negotiation; @@ -43,9 +43,12 @@ import org.apache.cloudstack.veeam.utils.Negotiation; import com.cloud.utils.UuidUtils; import com.cloud.utils.component.ManagerBase; -public class ApiService extends ManagerBase implements RouteHandler { +public class ApiRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api"; + @Inject + ServerAdapter serverAdapter; + @Override public boolean canHandle(String method, String path) { return getSanitizedPath(path).startsWith("/api"); @@ -63,11 +66,11 @@ public class ApiService extends ManagerBase implements RouteHandler { private void handleRootApiRequest(HttpServletRequest req, HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { io.getWriter().write(resp, HttpServletResponse.SC_OK, - createDummyApi(VeeamControlService.ContextPath.value() + BASE_ROUTE), + createApiObject(VeeamControlService.ContextPath.value() + BASE_ROUTE), outFormat); } - private static Api createDummyApi(String basePath) { + protected Api createApiObject(String basePath) { Api api = new Api(); /* ---------------- Links ---------------- */ @@ -96,30 +99,11 @@ public class ApiService extends ManagerBase implements RouteHandler { ProductInfo productInfo = new ProductInfo(); productInfo.setInstanceId(UuidUtils.nameUUIDFromBytes( VeeamControlService.BindAddress.value().getBytes(StandardCharsets.UTF_8)).toString()); - productInfo.name = "oVirt Engine"; + productInfo.name = VeeamControlService.PLUGIN_NAME; - Version version = new Version(); - version.setBuild("8"); - version.setFullVersion("4.5.8-0.master.fake.el9"); - version.setMajor("4"); - version.setMinor("5"); - version.setRevision("0"); - - productInfo.version = version; + productInfo.version = Version.fromPackageAndCSVersion(true); api.setProductInfo(productInfo); - /* ---------------- Special objects ---------------- */ - SpecialObjects specialObjects = new SpecialObjects(); - specialObjects.setBlankTemplate(Ref.of( - basePath + "/templates/00000000-0000-0000-0000-000000000000", - "00000000-0000-0000-0000-000000000000" - )); - specialObjects.setRootTag(Ref.of( - basePath + "/tags/00000000-0000-0000-0000-000000000000", - "00000000-0000-0000-0000-000000000000" - )); - api.setSpecialObjects(specialObjects); - /* ---------------- Summary ---------------- */ ApiSummary summary = new ApiSummary(); summary.setHosts(new SummaryCount(1, 1)); @@ -132,7 +116,7 @@ public class ApiService extends ManagerBase implements RouteHandler { api.setTime(System.currentTimeMillis()); /* ---------------- Users ---------------- */ - String userId = UUID.randomUUID().toString(); + String userId = serverAdapter.getServiceAccount().first().getUuid(); api.setAuthenticatedUser(Ref.of(basePath + "/users/" + userId, userId)); api.setEffectiveUser(Ref.of(basePath + "/users/" + userId, userId)); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java index 92156be5e69..4855147a333 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VmsRouteHandler.java @@ -222,11 +222,6 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { io.notFound(resp, null, outFormat); } - protected static boolean isRequestAsync(HttpServletRequest req) { - String asyncStr = req.getParameter("async"); - return Boolean.TRUE.toString().equals(asyncStr); - } - protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { try { @@ -287,7 +282,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleDeleteById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - boolean async = isRequestAsync(req); + boolean async = RouteHandler.isRequestAsync(req); try { VmAction vm = serverAdapter.deleteInstance(id, async); io.getWriter().write(resp, HttpServletResponse.SC_OK, vm, outFormat); @@ -298,7 +293,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleStartVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - boolean async = isRequestAsync(req); + boolean async = RouteHandler.isRequestAsync(req); try { VmAction vm = serverAdapter.startInstance(id, async); io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); @@ -309,7 +304,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleStopVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - boolean async = isRequestAsync(req); + boolean async = RouteHandler.isRequestAsync(req); try { VmAction vm = serverAdapter.stopInstance(id, async); io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); @@ -320,7 +315,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleShutdownVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - boolean async = isRequestAsync(req); + boolean async = RouteHandler.isRequestAsync(req); try { VmAction vm = serverAdapter.shutdownInstance(id, async); io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); @@ -422,7 +417,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleDeleteSnapshotById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - boolean async = isRequestAsync(req); + boolean async = RouteHandler.isRequestAsync(req); try { ResourceAction action = serverAdapter.deleteSnapshot(id, async); if (action != null) { @@ -438,7 +433,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleRestoreSnapshotById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - boolean async = isRequestAsync(req); + boolean async = RouteHandler.isRequestAsync(req); String data = RouteHandler.getRequestData(req, logger); try { ResourceAction response = serverAdapter.revertInstanceToSnapshot(id, async); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/AsyncJobJoinVOToJobConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/AsyncJobJoinVOToJobConverter.java index f8845804e8e..c50f4a0ecfe 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/AsyncJobJoinVOToJobConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/AsyncJobJoinVOToJobConverter.java @@ -34,26 +34,6 @@ import com.cloud.api.query.vo.UserVmJoinVO; public class AsyncJobJoinVOToJobConverter { - public static Job toJob(String uuid, String state, long startTime) { - Job job = new Job(); - final String basePath = VeeamControlService.ContextPath.value(); - // Fill in dummy data for now, as the AsyncJobJoinVO does not contain all the necessary information to populate a Job object. - job.setId(uuid); - job.setHref(basePath + JobsRouteHandler.BASE_ROUTE + "/" + uuid); - job.setAutoCleared(Boolean.TRUE.toString()); - job.setExternal(Boolean.TRUE.toString()); - job.setLastUpdated(System.currentTimeMillis()); - job.setStartTime(startTime); - job.setStatus(state); - if ("complete".equalsIgnoreCase(state) || "finished".equalsIgnoreCase(state)) { - job.setEndTime(System.currentTimeMillis()); - } - job.setOwner(Ref.of(basePath + "/api/users/" + uuid, uuid)); - job.setDescription("Something"); - job.setLink(Collections.emptyList()); - return job; - } - public static Job toJob(AsyncJobJoinVO vo) { Job job = new Job(); final String basePath = VeeamControlService.ContextPath.value(); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/BackupVOToBackupConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/BackupVOToBackupConverter.java index 728d38e6c31..2f2b40908e8 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/BackupVOToBackupConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/BackupVOToBackupConverter.java @@ -23,9 +23,12 @@ import java.util.stream.Collectors; import org.apache.cloudstack.backup.BackupVO; import org.apache.cloudstack.veeam.VeeamControlService; +import org.apache.cloudstack.veeam.api.ApiRouteHandler; import org.apache.cloudstack.veeam.api.VmsRouteHandler; import org.apache.cloudstack.veeam.api.dto.Backup; import org.apache.cloudstack.veeam.api.dto.Disk; +import org.apache.cloudstack.veeam.api.dto.Host; +import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.Vm; import com.cloud.api.query.vo.HostJoinVO; @@ -55,6 +58,16 @@ public class BackupVOToBackupConverter { backup.setVm(Vm.of(basePath + VmsRouteHandler.BASE_ROUTE + "/" + vmVO.getUuid(), vmVO.getUuid())); } } + if (backupVO.getHostId() != null && hostResolver != null) { + final HostJoinVO hostVO = hostResolver.apply(backupVO.getHostId()); + if (hostVO != null) { + backup.setHost(Host.of(basePath + ApiRouteHandler.BASE_ROUTE + "/" + hostVO.getUuid(), hostVO.getUuid())); + } + } + if (disksResolver != null) { + List disks = disksResolver.apply(backupVO); + backup.setDisks(NamedList.of("disks", disks)); + } return backup; } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ClusterVOToClusterConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ClusterVOToClusterConverter.java index 7b532f26c02..42b2233393d 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ClusterVOToClusterConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ClusterVOToClusterConverter.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.veeam.api.dto.Cpu; import org.apache.cloudstack.veeam.api.dto.Link; import org.apache.cloudstack.veeam.api.dto.Ref; import org.apache.cloudstack.veeam.api.dto.Version; +import org.apache.cloudstack.veeam.api.dto.Vm; import com.cloud.api.query.vo.DataCenterJoinVO; import com.cloud.dc.ClusterVO; @@ -39,19 +40,14 @@ public class ClusterVOToClusterConverter { public static Cluster toCluster(final ClusterVO vo, final Function 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:" final String clusterId = vo.getUuid(); c.setId(clusterId); c.setHref(basePath + ClustersRouteHandler.BASE_ROUTE + "/" + clusterId); c.setName(vo.getName()); - // --- sensible defaults (match your sample) c.setBallooningEnabled("true"); - c.setBiosType("q35_ovmf"); // or "q35_secure_boot" if you want to align with VM BIOS you saw + c.setBiosType(Vm.Bios.getDefault().getType()); c.setFipsMode("disabled"); c.setFirewallType("firewalld"); c.setGlusterService("false"); @@ -64,19 +60,14 @@ public class ClusterVOToClusterConverter { c.setUpgradePercentComplete("0"); c.setVirtService("true"); c.setVncEncryption("false"); - c.setLogMaxMemoryUsedThreshold("95"); - c.setLogMaxMemoryUsedThresholdType("percentage"); // --- cpu (best-effort defaults) final Cpu cpu = new Cpu(); - cpu.setArchitecture("x86_64"); - cpu.setType("x86_64"); // replace if you can detect host cpu model + cpu.setArchitecture(vo.getArch().getType()); + cpu.setType(vo.getArch().getType()); // replace if you can detect host cpu model c.setCpu(cpu); - // --- version (ovirt engine version; keep fixed unless you want to expose something else) - final Version ver = new Version(); - ver.setMajor("4"); - ver.setMinor("8"); + final Version ver = Version.fromPackageAndCSVersion(false); c.setVersion(ver); // --- ksm / memory policy (defaults) diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/DataCenterJoinVOToDataCenterConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/DataCenterJoinVOToDataCenterConverter.java index 74f49aa1242..659e0e1f5a8 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/DataCenterJoinVOToDataCenterConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/DataCenterJoinVOToDataCenterConverter.java @@ -52,12 +52,10 @@ public class DataCenterJoinVOToDataCenterConverter { dc.setQuotaMode("disabled"); dc.setStorageFormat("v5"); - // ---- Versions (static but valid) ---- - final Version v48 = new Version(); - v48.setMajor("4"); - v48.setMinor("8"); - dc.setVersion(v48); - dc.setSupportedVersions(new SupportedVersions(List.of(v48))); + // ---- Versions ---- + final Version ver = Version.fromPackageAndCSVersion(false); + dc.setVersion(ver); + dc.setSupportedVersions(new SupportedVersions(List.of(ver))); // ---- mac_pool (static placeholder) ---- dc.setMacPool(Ref.of(basePath + "/macpools/default", "default")); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java index 4df1dd91e1c..d8230fddc62 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/HostJoinVOToHostConverter.java @@ -38,7 +38,6 @@ public class HostJoinVOToHostConverter { /** * Convert CloudStack HostJoinVO -> oVirt-like Host. * - * @param vo HostJoinVO from listHosts (join query) */ public static Host toHost(final HostJoinVO vo) { final Host h = new Host(); @@ -49,8 +48,6 @@ public class HostJoinVOToHostConverter { final String basePath = VeeamControlService.ContextPath.value(); h.setHref(basePath + HostsRouteHandler.BASE_ROUTE + "/" + hostUuid); - // --- name / address --- - // Prefer DNS name if set; otherwise fall back to IP final String name = vo.getName() != null ? vo.getName() : ("host-" + hostUuid); h.setName(name); @@ -75,9 +72,7 @@ public class HostJoinVOToHostConverter { h.setMemory(String.valueOf(vo.getTotalMemory())); h.setMaxSchedulingMemory(String.valueOf(vo.getTotalMemory() - vo.getMemUsedCapacity())); - // --- OS / versions (optional placeholders) --- - // If you want, you can set conservative defaults to match oVirt shape. - h.setType("rhel"); + h.setType("ovirt_node"); h.setAutoNumaStatus("unknown"); h.setKdumpStatus("disabled"); h.setNumaSupported("false"); @@ -85,8 +80,6 @@ public class HostJoinVOToHostConverter { h.setUpdateAvailable("false"); - // --- links/actions --- - // Start minimal (empty). Add actions only if Veeam tries to follow them. h.setActions(null); h.setLink(Collections.emptyList()); @@ -98,13 +91,13 @@ public class HostJoinVOToHostConverter { } private static String mapStatus(final HostJoinVO vo) { - // CloudStack examples: - // state: Up/Down/Maintenance/Error/Disconnected - // status: Up/Down/Connecting/etc - if (vo.isInMaintenanceStates()) return "maintenance"; - if (Status.Up.equals(vo.getStatus()) && ResourceState.Enabled.equals(vo.getResourceState())) return "up"; - - // Default + if (vo.isInMaintenanceStates()) { + return "maintenance"; + } + if (Status.Up.equals(vo.getStatus()) && + ResourceState.Enabled.equals(vo.getResourceState())) { + return "up"; + } return "down"; } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToNetworkConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToNetworkConverter.java index 114311225d3..82198997e7d 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToNetworkConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToNetworkConverter.java @@ -55,7 +55,6 @@ public class NetworkVOToNetworkConverter { // Best-effort mapping for vdsm_name dto.setVdsmName(dto.getName()); - // zone -> oVirt datacenter ref if (dcResolver != null) { final DataCenterJoinVO dc = dcResolver.apply(vo.getDataCenterId()); if (dc != null) { diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToVnicProfileConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToVnicProfileConverter.java index b9d660f1fa6..af10d586c89 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToVnicProfileConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NetworkVOToVnicProfileConverter.java @@ -45,7 +45,6 @@ public class NetworkVOToVnicProfileConverter { vnicProfile.setNetwork(Ref.of(basePath + NetworksRouteHandler.BASE_ROUTE + "/" + networkUuid, networkUuid)); vnicProfile.setDescription(vo.getDisplayText()); - // zone -> oVirt datacenter ref if (dcResolver != null) { final DataCenterJoinVO dc = dcResolver.apply(vo.getDataCenterId()); if (dc != null) { diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NicVOToNicConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NicVOToNicConverter.java index 165dbd1db58..b55201327ea 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NicVOToNicConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NicVOToNicConverter.java @@ -39,6 +39,8 @@ import com.cloud.network.dao.NetworkVO; import com.cloud.vm.NicVO; public class NicVOToNicConverter { + private static final String DEFAULT_INTERFACE_TYPE = "virtio"; + private static final String DEFAULT_REPORTED_DEVICE_NAME = "eth0"; public static Nic toNic(final NicVO vo, final String vmUuid, final Function networkResolver) { final String basePath = VeeamControlService.ContextPath.value(); @@ -56,7 +58,7 @@ public class NicVOToNicConverter { nic.setVm(vm); nic.setHref(vm.getHref() + "/nics/" + vo.getUuid()); } - nic.setInterfaceType("virtio"); + nic.setInterfaceType(DEFAULT_INTERFACE_TYPE); ReportedDevice device = getReportedDevice(vo, mac, nic.getVm()); nic.setReportedDevices(NamedList.of("reported_device", List.of(device))); if (networkResolver != null) { @@ -73,7 +75,7 @@ public class NicVOToNicConverter { ReportedDevice device = new ReportedDevice(); device.setType("network"); device.setId(vo.getUuid()); - device.setName("eth0"); + device.setName(DEFAULT_REPORTED_DEVICE_NAME); device.setDescription(String.format("%s device", vo.getReserver())); device.setMac(mac); if (ObjectUtils.anyNotNull(vo.getIPv4Address(), vo.getIPv6Address())) { diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ResourceTagVOToTagConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ResourceTagVOToTagConverter.java index 445b3c0ae33..9715b032110 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ResourceTagVOToTagConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/ResourceTagVOToTagConverter.java @@ -48,10 +48,11 @@ public class ResourceTagVOToTagConverter { public static Tag toTag(ResourceTagVO vo) { String basePath = VeeamControlService.ContextPath.value(); Tag tag = new Tag(); - tag.setId(vo.getUuid()); - tag.setName(String.format("%s-%s", vo.getKey(), vo.getValue()).replaceAll("\\s+", "")); + String id = vo.getValue(); + tag.setId(id); + tag.setName(vo.getValue()); tag.setDescription(String.format("Tag %s with value: %s", vo.getKey(), vo.getValue())); - tag.setHref(basePath + TagsRouteHandler.BASE_ROUTE + "/" + vo.getUuid()); + tag.setHref(basePath + TagsRouteHandler.BASE_ROUTE + "/" + id); if (ResourceTag.ResourceObjectType.UserVm.equals(vo.getResourceType())) { tag.setVm(Ref.of(basePath + VmsRouteHandler.BASE_ROUTE + "/" + vo.getResourceUuid(), vo.getResourceUuid())); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/StoreVOToStorageDomainConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/StoreVOToStorageDomainConverter.java index a70eceb1b46..b32c9ceaec5 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/StoreVOToStorageDomainConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/StoreVOToStorageDomainConverter.java @@ -21,7 +21,7 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.cloudstack.veeam.VeeamControlService; -import org.apache.cloudstack.veeam.api.ApiService; +import org.apache.cloudstack.veeam.api.ApiRouteHandler; import org.apache.cloudstack.veeam.api.DataCentersRouteHandler; import org.apache.cloudstack.veeam.api.dto.DataCenter; import org.apache.cloudstack.veeam.api.dto.Link; @@ -42,7 +42,7 @@ public class StoreVOToStorageDomainConverter { StorageDomain sd = new StorageDomain(); sd.setId(id); - final String href = href(basePath, ApiService.BASE_ROUTE + "/storagedomains/" + id); + final String href = href(basePath, ApiRouteHandler.BASE_ROUTE + "/storagedomains/" + id); sd.setHref(href); sd.setName(pool.getName()); @@ -96,7 +96,7 @@ public class StoreVOToStorageDomainConverter { StorageDomain sd = new StorageDomain(); sd.setId(id); - final String href = href(basePath, ApiService.BASE_ROUTE + "/storagedomains/" + id); + final String href = href(basePath, ApiRouteHandler.BASE_ROUTE + "/storagedomains/" + id); sd.setHref(href); sd.setName(store.getName()); diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java index dafec627e96..40743a2e3c1 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/UserVmJoinVOToVmConverter.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.veeam.VeeamControlService; -import org.apache.cloudstack.veeam.api.ApiService; +import org.apache.cloudstack.veeam.api.ApiRouteHandler; import org.apache.cloudstack.veeam.api.VmsRouteHandler; import org.apache.cloudstack.veeam.api.dto.BaseDto; import org.apache.cloudstack.veeam.api.dto.Cpu; @@ -54,7 +54,6 @@ public final class UserVmJoinVOToVmConverter { /** * Convert CloudStack UserVmJoinVO -> oVirt-like Vm DTO. * - * @param src UserVmJoinVO */ public static Vm toVm(final UserVmJoinVO src, final Function hostResolver, @@ -84,7 +83,7 @@ public final class UserVmJoinVOToVmConverter { dst.setStartTime(lastUpdated.getTime()); } final Ref template = buildRef( - basePath + ApiService.BASE_ROUTE, + basePath + ApiRouteHandler.BASE_ROUTE, "templates", src.getTemplateUuid() ); @@ -92,20 +91,19 @@ public final class UserVmJoinVOToVmConverter { dst.setOriginalTemplate(template); if (StringUtils.isNotBlank(src.getHostUuid())) { dst.setHost(buildRef( - basePath + ApiService.BASE_ROUTE, + basePath + ApiRouteHandler.BASE_ROUTE, "hosts", src.getHostUuid())); - } if (hostResolver != null) { HostJoinVO hostVo = hostResolver.apply(src.getHostId() == null ? src.getLastHostId() : src.getHostId()); if (hostVo != null) { dst.setHost(buildRef( - basePath + ApiService.BASE_ROUTE, + basePath + ApiRouteHandler.BASE_ROUTE, "hosts", hostVo.getUuid())); dst.setCluster(buildRef( - basePath + ApiService.BASE_ROUTE, + basePath + ApiRouteHandler.BASE_ROUTE, "clusters", hostVo.getClusterUuid())); } @@ -123,9 +121,7 @@ public final class UserVmJoinVOToVmConverter { cpu.setTopology(new Topology(src.getCpu(), 1, 1)); dst.setCpu(cpu); Os os = new Os(); - os.setType(src.getGuestOsId() % 2 == 0 - ? "windows" - : "linux"); + os.setType(src.getGuestOsDisplayName()); Os.Boot boot = new Os.Boot(); boot.setDevices(NamedList.of("device", List.of("hd"))); os.setBoot(boot); @@ -167,7 +163,7 @@ public final class UserVmJoinVOToVmConverter { dst.setTags(NamedList.of("tag", tags)); } dst.setCpuProfile(Ref.of( - basePath + ApiService.BASE_ROUTE + "/cpuprofiles/" + src.getServiceOfferingUuid(), + basePath + ApiRouteHandler.BASE_ROUTE + "/cpuprofiles/" + src.getServiceOfferingUuid(), src.getServiceOfferingUuid())); if (allContent) { dst.setInitialization(getOvfInitialization(dst, src)); @@ -204,9 +200,10 @@ public final class UserVmJoinVOToVmConverter { } private static String mapStatus(final VirtualMachine.State state) { - // CloudStack-ish states -> oVirt-ish up/down - if (Arrays.asList(VirtualMachine.State.Running, - VirtualMachine.State.Migrating, VirtualMachine.State.Restoring).contains(state)) { + if (Arrays.asList( + VirtualMachine.State.Running, + VirtualMachine.State.Migrating, + VirtualMachine.State.Restoring).contains(state)) { return "up"; } return "down"; diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/VolumeJoinVOToDiskConverter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/VolumeJoinVOToDiskConverter.java index b1be9b98804..af92e7a10f2 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/VolumeJoinVOToDiskConverter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/VolumeJoinVOToDiskConverter.java @@ -24,7 +24,7 @@ import java.util.stream.Collectors; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.veeam.VeeamControlService; -import org.apache.cloudstack.veeam.api.ApiService; +import org.apache.cloudstack.veeam.api.ApiRouteHandler; import org.apache.cloudstack.veeam.api.DisksRouteHandler; import org.apache.cloudstack.veeam.api.VmsRouteHandler; import org.apache.cloudstack.veeam.api.dto.Disk; @@ -43,7 +43,7 @@ public class VolumeJoinVOToDiskConverter { public static Disk toDisk(final VolumeJoinVO vol, final Function physicalSizeResolver) { final Disk disk = new Disk(); final String basePath = VeeamControlService.ContextPath.value(); - final String apiBasePath = basePath + ApiService.BASE_ROUTE; + final String apiBasePath = basePath + ApiRouteHandler.BASE_ROUTE; final String diskId = vol.getUuid(); final String diskHref = basePath + DisksRouteHandler.BASE_ROUTE + "/" + diskId; diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Backup.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Backup.java index 6d612fa38eb..b337541bf5c 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Backup.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Backup.java @@ -23,9 +23,11 @@ public class Backup extends BaseDto { private String description; private Long creationDate; private Vm vm; + private Host host; private String phase; private String fromCheckpointId; private String toCheckpointId; + private NamedList disks; public String getName() { return name; @@ -59,6 +61,14 @@ public class Backup extends BaseDto { this.vm = vm; } + public Host getHost() { + return host; + } + + public void setHost(Host host) { + this.host = host; + } + public String getPhase() { return phase; } @@ -82,4 +92,12 @@ public class Backup extends BaseDto { public void setToCheckpointId(String toCheckpointId) { this.toCheckpointId = toCheckpointId; } + + public NamedList getDisks() { + return disks; + } + + public void setDisks(NamedList disks) { + this.disks = disks; + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/BaseDto.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/BaseDto.java index 5f98ca775dc..0b260a5cdcd 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/BaseDto.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/BaseDto.java @@ -46,4 +46,10 @@ public class BaseDto { public static Link getActionLink(final String action, final String baseHref) { return Link.of(action, baseHref + "/" + action); } + + protected static T withHrefAndId(T dto, String href, String id) { + dto.setHref(href); + dto.setId(id); + return dto; + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Host.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Host.java index 8c4dba1d57c..73efba5eeb8 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Host.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Host.java @@ -308,4 +308,8 @@ public class Host extends BaseDto { this.version = version; } } + + public static Host of(String href, String id) { + return withHrefAndId(new Host(), href, id); + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java index 667eb7d00b1..7b7d80a0f16 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java @@ -17,6 +17,10 @@ package org.apache.cloudstack.veeam.api.dto; +import org.apache.cloudstack.utils.CloudStackVersion; +import org.apache.cloudstack.veeam.VeeamControlService; +import org.apache.commons.lang3.StringUtils; + import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_NULL) @@ -70,4 +74,21 @@ public final class Version { public void setRevision(String revision) { this.revision = revision; } + + public static Version fromPackageAndCSVersion(boolean complete) { + Version version = new Version(); + String packageVersion = VeeamControlService.getPackageVersion(); + if (StringUtils.isNotBlank(packageVersion) && complete) { + version.setFullVersion(packageVersion); + } + CloudStackVersion csVersion = VeeamControlService.getCSVersion(); + if (csVersion == null) { + return version; + } + version.setMajor(String.valueOf(csVersion.getMajorRelease())); + version.setMinor(String.valueOf(csVersion.getMinorRelease())); + version.setBuild(String.valueOf(csVersion.getPatchRelease())); + version.setRevision(String.valueOf(csVersion.getSecurityRelease())); + return version; + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java index 90a50207aac..9607e794998 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vm.java @@ -513,9 +513,6 @@ public final class Vm extends BaseDto { } public static Vm of(String href, String id) { - Vm vm = new Vm(); - vm.setHref(href); - vm.setId(id); - return vm; + return withHrefAndId(new Vm(), href, id); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/FaultResponse.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/FaultResponse.java deleted file mode 100644 index fa67367773e..00000000000 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/FaultResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -// 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.response; - -import org.apache.cloudstack.veeam.api.dto.Fault; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; - -@JsonInclude(JsonInclude.Include.NON_NULL) -@JacksonXmlRootElement(localName = "fault") -public final class FaultResponse { - public Fault fault; - - public FaultResponse() {} - - public FaultResponse(final Fault fault) { - this.fault = fault; - } - - public static FaultResponse of(final String reason, final String detail) { - return new FaultResponse(new Fault(reason, detail)); - } -} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/PathUtil.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/PathUtil.java index b69748bf8bd..8fe2a48c702 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/PathUtil.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/PathUtil.java @@ -25,6 +25,7 @@ import org.apache.commons.lang3.StringUtils; import com.cloud.utils.UuidUtils; public class PathUtil { + private static final boolean CONSIDER_ONLY_UUID_AS_ID = false; public static List extractIdAndSubPath(final String path, final String baseRoute) { @@ -65,7 +66,7 @@ public class PathUtil { } // Validate first segment is a UUID - if (validParts.isEmpty() || !UuidUtils.isUuid(validParts.get(0))) { + if (validParts.isEmpty() || (CONSIDER_ONLY_UUID_AS_ID && !UuidUtils.isUuid(validParts.get(0)))) { return null; } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/ResponseWriter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/ResponseWriter.java index 4b191c6c3ad..51d2f829f3d 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/ResponseWriter.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/ResponseWriter.java @@ -23,7 +23,6 @@ import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.api.dto.Fault; -import org.apache.cloudstack.veeam.api.response.FaultResponse; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -78,7 +77,7 @@ public final class ResponseWriter { if (fmt == Negotiation.OutFormat.XML) { write(resp, status, fault, fmt); } else { - write(resp, status, new FaultResponse(fault), fmt); + write(resp, status, fault, fmt); } } } diff --git a/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml b/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml index 4d66d5248e0..83d100ec76e 100644 --- a/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml +++ b/plugins/integrations/veeam-control-service/src/main/resources/META-INF/cloudstack/veeam-control-service/spring-veeam-control-service-context.xml @@ -36,7 +36,7 @@ - + diff --git a/plugins/integrations/veeam-control-service/src/main/resources/test.xml b/plugins/integrations/veeam-control-service/src/main/resources/test-ovf.xml similarity index 92% rename from plugins/integrations/veeam-control-service/src/main/resources/test.xml rename to plugins/integrations/veeam-control-service/src/main/resources/test-ovf.xml index 5af3b9be435..53688f0b82e 100644 --- a/plugins/integrations/veeam-control-service/src/main/resources/test.xml +++ b/plugins/integrations/veeam-control-service/src/main/resources/test-ovf.xml @@ -1,3 +1,21 @@ +