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 88b28b97bb7..7e98070c6b0 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 @@ -357,10 +357,8 @@ public class ServerAdapter extends ManagerBase { protected static Map getDummyTags() { Map tags = new HashMap<>(); - Tag tag1 = getDummyTagByName("Automatic"); - tags.put(tag1.getId(), tag1); - Tag tag2 = getDummyTagByName("Manual"); - tags.put(tag2.getId(), tag2); + Tag rootTag = ResourceTagVOToTagConverter.getRootTag(); + tags.put(rootTag.getId(), rootTag); return tags; } @@ -696,7 +694,7 @@ public class ServerAdapter extends ManagerBase { vm = userVmManager.finalizeCreateVirtualMachine(vm.getId()); UserVmJoinVO vo = userVmJoinDao.findById(vm.getId()); return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::getDetailsByInstanceId, - this::listDiskAttachmentsByInstanceId, this::listNicsByInstance, false); + this::listTagsByInstanceId, this::listDiskAttachmentsByInstanceId, this::listNicsByInstance, false); } catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException | CloudRuntimeException e) { throw new CloudRuntimeException("Failed to create VM: " + e.getMessage(), e); } @@ -983,13 +981,15 @@ public class ServerAdapter extends ManagerBase { } @ApiAccess(command = ListVMsCmd.class) - public Vm getInstance(String uuid, boolean includeDisks, boolean includeNics, boolean allContent) { + public Vm getInstance(String uuid, boolean includeTags, boolean includeDisks, boolean includeNics, + boolean allContent) { UserVmJoinVO vo = userVmJoinDao.findByUuid(uuid); if (vo == null) { throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); } return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::getDetailsByInstanceId, + includeTags ? this::listTagsByInstanceId : null, includeDisks ? this::listDiskAttachmentsByInstanceId : null, includeNics ? this::listNicsByInstance : null, allContent); @@ -1064,7 +1064,7 @@ public class ServerAdapter extends ManagerBase { @ApiAccess(command = UpdateVMCmd.class) public Vm updateInstance(String uuid, Vm request) { logger.warn("Received request to update VM with ID {}. No action, returning existing VM data.", uuid); - return getInstance(uuid, false, false, false); + return getInstance(uuid, false, false, false, false); } @ApiAccess(command = DestroyVMCmd.class) @@ -1201,6 +1201,21 @@ public class ServerAdapter extends ManagerBase { throw new InvalidParameterValueException("Reduce Disk with ID " + uuid + " not implemented"); } + @ApiAccess(command = ListTagsCmd.class) + protected List listTagsByInstanceId(final long instanceId) { + List vmResourceTags = resourceTagDao.listBy(instanceId, + ResourceTag.ResourceObjectType.UserVm); + List tags = new ArrayList<>(); + for (ResourceTag t : vmResourceTags) { + if (t instanceof ResourceTagVO) { + tags.add((ResourceTagVO)t); + continue; + } + tags.add(resourceTagDao.findById(t.getId())); + } + return ResourceTagVOToTagConverter.toTags(tags); + } + @ApiAccess(command = ListVolumesCmd.class) protected List listDiskAttachmentsByInstanceId(final long instanceId) { List kvmVolumes = volumeJoinDao.listByInstanceId(instanceId); 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 a3d1ca236cb..0b5aff47823 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 @@ -256,6 +256,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGetById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { String followStr = req.getParameter("follow"); + boolean includeTags = false; boolean includeDisks = false; boolean includeNics = false; if (StringUtils.isNotBlank(followStr)) { @@ -263,12 +264,13 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { .map(String::trim) .filter(s -> !s.isEmpty()) .collect(java.util.stream.Collectors.toSet()); + includeTags = followParts.contains("tags"); includeDisks = followParts.contains("disk_attachments.disk"); includeNics = followParts.contains("nics.reporteddevices"); } boolean allContent = Boolean.parseBoolean(req.getParameter("all_content")); try { - Vm response = serverAdapter.getInstance(id, includeDisks, includeNics, allContent); + Vm response = serverAdapter.getInstance(id, includeTags, includeDisks, includeNics, allContent); io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { io.notFound(resp, e.getMessage(), outFormat); 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 dc2853dfd76..f8845804e8e 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 @@ -98,7 +98,7 @@ public class AsyncJobJoinVOToJobConverter { public static VmAction toVmAction(final AsyncJobJoinVO vo, final UserVmJoinVO vm) { VmAction action = new VmAction(); fillAction(action, vo); - action.setVm(UserVmJoinVOToVmConverter.toVm(vm, null, null, null, null, false)); + action.setVm(UserVmJoinVOToVmConverter.toVm(vm, null, null, null, null, null, false)); return action; } 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 d22a234d9e4..445b3c0ae33 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 @@ -38,7 +38,6 @@ public class ResourceTagVOToTagConverter { } public static Tag getRootTag() { - String basePath = VeeamControlService.ContextPath.value(); Tag tag = new Tag(); tag.setId(BaseDto.ZERO_UUID); tag.setName("root"); @@ -50,8 +49,8 @@ public class ResourceTagVOToTagConverter { String basePath = VeeamControlService.ContextPath.value(); Tag tag = new Tag(); tag.setId(vo.getUuid()); - tag.setName(vo.getKey()); - tag.setDescription(String.format("Tag %s-%s", vo.getKey(), vo.getValue())); + tag.setName(String.format("%s-%s", vo.getKey(), vo.getValue()).replaceAll("\\s+", "")); + tag.setDescription(String.format("Tag %s with value: %s", vo.getKey(), vo.getValue())); tag.setHref(basePath + TagsRouteHandler.BASE_ROUTE + "/" + vo.getUuid()); if (ResourceTag.ResourceObjectType.UserVm.equals(vo.getResourceType())) { tag.setVm(Ref.of(basePath + VmsRouteHandler.BASE_ROUTE + "/" + vo.getResourceUuid(), 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 7f148b8d65b..61269ab0410 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 @@ -31,12 +31,12 @@ import org.apache.cloudstack.veeam.api.VmsRouteHandler; import org.apache.cloudstack.veeam.api.dto.BaseDto; import org.apache.cloudstack.veeam.api.dto.Cpu; import org.apache.cloudstack.veeam.api.dto.DiskAttachment; -import org.apache.cloudstack.veeam.api.dto.EmptyElement; import org.apache.cloudstack.veeam.api.dto.NamedList; import org.apache.cloudstack.veeam.api.dto.Nic; import org.apache.cloudstack.veeam.api.dto.Os; import org.apache.cloudstack.veeam.api.dto.OvfXmlUtil; import org.apache.cloudstack.veeam.api.dto.Ref; +import org.apache.cloudstack.veeam.api.dto.Tag; import org.apache.cloudstack.veeam.api.dto.Topology; import org.apache.cloudstack.veeam.api.dto.Vm; import org.apache.commons.collections.MapUtils; @@ -58,6 +58,7 @@ public final class UserVmJoinVOToVmConverter { */ public static Vm toVm(final UserVmJoinVO src, final Function hostResolver, final Function> detailsResolver, + final Function> tagsResolver, final Function> disksResolver, final Function> nicsResolver, final boolean allContent) { @@ -160,7 +161,10 @@ public final class UserVmJoinVOToVmConverter { BaseDto.getActionLink("reporteddevices", dst.getHref()), BaseDto.getActionLink("snapshots", dst.getHref()) )); - dst.setTags(new EmptyElement()); + if (tagsResolver != null) { + List tags = tagsResolver.apply(src.getId()); + dst.setTags(NamedList.of("tag", tags)); + } dst.setCpuProfile(Ref.of( basePath + ApiService.BASE_ROUTE + "/cpuprofiles/" + src.getServiceOfferingUuid(), src.getServiceOfferingUuid())); @@ -189,7 +193,7 @@ public final class UserVmJoinVOToVmConverter { public static List toVmList(final List srcList, final Function hostResolver, final Function> detailsResolver) { return srcList.stream() - .map(v -> toVm(v, hostResolver, detailsResolver, null, null, false)) + .map(v -> toVm(v, hostResolver, detailsResolver, null, null, null, false)) .collect(Collectors.toList()); } 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 b939224d874..90a50207aac 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 @@ -58,8 +58,8 @@ public final class Vm extends BaseDto { private String origin; // "ovirt" private NamedList actions; // actions.link[] @JacksonXmlElementWrapper(useWrapping = false) - private List link; // related resources - private EmptyElement tags; // empty + private List link; + private NamedList tags; private NamedList diskAttachments; private NamedList nics; private Initialization initialization; @@ -252,11 +252,11 @@ public final class Vm extends BaseDto { this.link = link; } - public EmptyElement getTags() { + public NamedList getTags() { return tags; } - public void setTags(EmptyElement tags) { + public void setTags(NamedList tags) { this.tags = tags; }