From d8c7ee7dc33b988fb6664543246266a89cf90526 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 7 May 2026 01:46:25 +0530 Subject: [PATCH] fix tags restore Signed-off-by: Abhishek Kumar --- .../veeam/adapter/ServerAdapter.java | 42 +++++++++++++++++-- .../cloudstack/veeam/api/VmsRouteHandler.java | 21 ++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) 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 94300ed0381..0a31b969e55 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 @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -54,6 +55,7 @@ import org.apache.cloudstack.api.command.user.backup.ListBackupsCmd; import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; +import org.apache.cloudstack.api.command.user.tag.CreateTagsCmd; import org.apache.cloudstack.api.command.user.tag.ListTagsCmd; import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd; import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; @@ -164,6 +166,7 @@ import com.cloud.org.Grouping; import com.cloud.projects.Project; import com.cloud.projects.ProjectManager; import com.cloud.server.ResourceTag; +import com.cloud.server.TaggedResourceService; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.Storage; @@ -186,6 +189,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.EnumUtils; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; +import com.cloud.utils.UuidUtils; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.Filter; @@ -207,7 +211,7 @@ public class ServerAdapter extends ManagerBase { Storage.StoragePoolType.NetworkFilesystem, Storage.StoragePoolType.SharedMountPoint ); - private static final String VM_TA_KEY = "veeam_tag"; + private static final String VM_TAG_KEY = "veeam_tag"; private static final String WORKER_VM_GUEST_CPU_MODE = "host-passthrough"; private static final String RESTORE_CONFIG = "restore.config"; @@ -313,6 +317,9 @@ public class ServerAdapter extends ManagerBase { @Inject DomainDao domainDao; + @Inject + TaggedResourceService taggedResourceService; + protected static Map getDummyTags() { Map tags = new HashMap<>(); Tag rootTag = ResourceTagVOToTagConverter.getRootTag(); @@ -1154,7 +1161,7 @@ public class ServerAdapter extends ManagerBase { @ApiAccess(command = ListTagsCmd.class) protected List listTagsByInstanceId(final long instanceId) { List tags = resourceTagDao.listByResourceTypeIdAndKeyPrefix( - ResourceTag.ResourceObjectType.UserVm, instanceId, VM_TA_KEY); + ResourceTag.ResourceObjectType.UserVm, instanceId, VM_TAG_KEY); return ResourceTagVOToTagConverter.toTags(tags); } @@ -1725,7 +1732,7 @@ public class ServerAdapter extends ManagerBase { Filter filter = new Filter(ResourceTagVO.class, "id", true, offset, limit); Pair, List> ownerDetails = getResourceOwnerFiltersWithDomainIds(); List vmResourceTags = resourceTagDao.listByResourceTypeKeyPrefixAndOwners( - ResourceTag.ResourceObjectType.UserVm, VM_TA_KEY, ownerDetails.first(), ownerDetails.second(), filter); + ResourceTag.ResourceObjectType.UserVm, VM_TAG_KEY, ownerDetails.first(), ownerDetails.second(), filter); if (CollectionUtils.isNotEmpty(vmResourceTags)) { tags.addAll(ResourceTagVOToTagConverter.toTagsFromValues(vmResourceTags)); } @@ -1740,7 +1747,7 @@ public class ServerAdapter extends ManagerBase { Tag tag = getDummyTags().get(uuid); if (tag == null) { ResourceTagVO resourceTagVO = resourceTagDao.findByResourceTypeKeyPrefixAndValue( - ResourceTag.ResourceObjectType.UserVm, VM_TA_KEY, uuid); + ResourceTag.ResourceObjectType.UserVm, VM_TAG_KEY, uuid); accountService.checkAccess(CallContext.current().getCallingAccount(), null, false, resourceTagVO); if (resourceTagVO != null) { @@ -1752,4 +1759,31 @@ public class ServerAdapter extends ManagerBase { } return tag; } + + @ApiAccess(command = CreateTagsCmd.class) + public Tag createInstanceTag(final String vmUuid, final Tag request) { + UserVmVO vmVo = userVmDao.findByUuid(vmUuid); + if (vmVo == null) { + throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); + } + accountService.checkAccess(CallContext.current().getCallingAccount(), SecurityChecker.AccessType.OperateEntry, + false, vmVo); + Map tags = new HashMap<>(); + String name = request.getId(); + tags.put(String.format( "%s%s", VM_TAG_KEY, UuidUtils.first(UUID.randomUUID().toString())), name); + try { + List resourceTags = taggedResourceService.createTags(Collections.singletonList(vmUuid), + ResourceTag.ResourceObjectType.UserVm, tags, null); + ResourceTagVO tag = null; + if (CollectionUtils.isNotEmpty(resourceTags)) { + tag = resourceTagDao.findById(resourceTags.get(0).getId()); + } + if (tag == null) { + throw new CloudRuntimeException("Unknown error"); + } + return ResourceTagVOToTagConverter.toTag(tag); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to create tag for %s: %s", name, e.getMessage()), e); + } + } } 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 4855147a333..2a3e3a17ae4 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 @@ -35,6 +35,7 @@ import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.Nic; import org.apache.cloudstack.veeam.api.dto.ResourceAction; import org.apache.cloudstack.veeam.api.dto.Snapshot; +import org.apache.cloudstack.veeam.api.dto.Tag; import org.apache.cloudstack.veeam.api.dto.Vm; import org.apache.cloudstack.veeam.api.dto.VmAction; import org.apache.cloudstack.veeam.api.request.ListQuery; @@ -162,6 +163,13 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { io.methodNotAllowed(resp, "GET, POST", outFormat); } return; + } else if ("tags".equals(subPath)) { + if ("POST".equalsIgnoreCase(method)) { + handlePostTagForVmId(id, req, resp, outFormat, io); + } else { + io.methodNotAllowed(resp, "POST", outFormat); + } + return; } } else if (idAndSubPath.size() == 3) { String subPath = idAndSubPath.get(1); @@ -529,4 +537,17 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { io.badRequest(resp, e.getMessage(), outFormat); } } + + protected void handlePostTagForVmId(final String id, final HttpServletRequest req, + final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) + throws IOException { + String data = RouteHandler.getRequestData(req, logger); + try { + Tag request = io.getMapper().jsonMapper().readValue(data, Tag.class); + Tag response = serverAdapter.createInstanceTag(id, request); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (JsonProcessingException | CloudRuntimeException e) { + io.badRequest(resp, e.getMessage(), outFormat); + } + } }