From a3669298afcc9c3654b032721ea2abca8502209c Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 6 Feb 2026 14:05:13 +0530 Subject: [PATCH] worker vm deployment wip Signed-off-by: Abhishek Kumar --- .../command/admin/vm/DeployVMCmdByAdmin.java | 8 + .../offering/ListServiceOfferingsCmd.java | 12 + .../api/command/user/vm/AddNicToVMCmd.java | 20 + .../api/command/user/vm/BaseDeployVMCmd.java | 212 +++++ .../api/command/user/vm/DeployVMCmd.java | 38 +- .../backup/IncrementalBackupService.java | 4 + .../cloud/vm/VirtualMachineManagerImpl.java | 2 +- .../veeam/adapter/ServerAdapter.java | 833 ++++++++++++++++++ .../veeam/adapter/UserResourceAdapter.java | 345 -------- .../cloudstack/veeam/api/ApiService.java | 4 +- .../veeam/api/ClustersRouteHandler.java | 39 +- .../veeam/api/DataCentersRouteHandler.java | 90 +- .../veeam/api/DisksRouteHandler.java | 50 +- .../veeam/api/HostsRouteHandler.java | 27 +- .../veeam/api/ImageTransfersRouteHandler.java | 37 +- .../veeam/api/JobsRouteHandler.java | 102 +++ .../veeam/api/NetworksRouteHandler.java | 39 +- .../cloudstack/veeam/api/VmsRouteHandler.java | 229 +++-- .../veeam/api/VnicProfilesRouteHandler.java | 39 +- .../AsyncJobJoinVOToJobConverter.java | 50 ++ .../api/converter/NicVOToNicConverter.java | 94 ++ .../converter/UserVmJoinVOToVmConverter.java | 55 +- .../VolumeJoinVOToDiskConverter.java | 22 +- .../veeam/api/dto/DiskAttachment.java | 2 +- .../apache/cloudstack/veeam/api/dto/Ip.java | 61 ++ .../apache/cloudstack/veeam/api/dto/Ips.java | 42 + .../apache/cloudstack/veeam/api/dto/Job.java | 75 ++ .../apache/cloudstack/veeam/api/dto/Jobs.java | 42 + .../VmEntityResponse.java => dto/Mac.java} | 22 +- .../apache/cloudstack/veeam/api/dto/Nic.java | 131 +++ .../apache/cloudstack/veeam/api/dto/Nics.java | 40 + .../veeam/api/dto/ReportedDevice.java | 93 ++ .../veeam/api/dto/ReportedDevices.java | 42 + .../apache/cloudstack/veeam/api/dto/Vm.java | 38 + .../cloudstack/veeam/api/dto/VmAction.java | 51 ++ .../veeam/api/dto/VmInitialization.java | 34 + .../Vms.java} | 10 +- .../apache/cloudstack/veeam/utils/Mapper.java | 2 + .../spring-veeam-control-service-context.xml | 3 +- .../main/java/com/cloud/vm/UserVmManager.java | 2 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 59 +- .../backup/IncrementalBackupServiceImpl.java | 20 +- 42 files changed, 2435 insertions(+), 685 deletions(-) create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java delete mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/UserResourceAdapter.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/AsyncJobJoinVOToJobConverter.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NicVOToNicConverter.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ip.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ips.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Job.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Jobs.java rename plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/{response/VmEntityResponse.java => dto/Mac.java} (71%) create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nic.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nics.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevice.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevices.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmAction.java create mode 100644 plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmInitialization.java rename plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/{response/VmCollectionResponse.java => dto/Vms.java} (86%) diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java index e64c8b3f46c..5760bd25a36 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/DeployVMCmdByAdmin.java @@ -48,4 +48,12 @@ public class DeployVMCmdByAdmin extends DeployVMCmd implements AdminCmd { public Long getClusterId() { return clusterId; } + + ///////////////////////////////////////////////////// + ////////////////// Setters ////////////////////////// + ///////////////////////////////////////////////////// + + public void setClusterId(Long clusterId) { + this.clusterId = clusterId; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java index 5c5c8776bce..164a97891bc 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListServiceOfferingsCmd.java @@ -193,6 +193,18 @@ public class ListServiceOfferingsCmd extends BaseListProjectAndAccountResourcesC return gpuEnabled; } + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setCpuNumber(Integer cpuNumber) { + this.cpuNumber = cpuNumber; + } + + public void setMemory(Integer memory) { + this.memory = memory; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java index 6347c38811e..f6ef955956f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/AddNicToVMCmd.java @@ -100,6 +100,26 @@ public class AddNicToVMCmd extends BaseAsyncCmd implements UserCmd { return NetUtils.standardizeMacAddress(macaddr); } + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public void setNetworkId(Long netId) { + this.netId = netId; + } + + public void setIpaddr(String ipaddr) { + this.ipaddr = ipaddr; + } + + public void setMacAddress(String macaddr) { + this.macaddr = macaddr; + } + + public void setDhcpOptions(Map dhcpOptions) { + this.dhcpOptions = dhcpOptions; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java index 8c29d7338b8..8d02dfa0a79 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java @@ -798,6 +798,218 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme } return null; } + + ///////////////////////////////////////////////////// + ////////////////// Setters ////////////////////////// + ///////////////////////////////////////////////////// + public void setZoneId(Long zoneId) { + this.zoneId = zoneId; + } + + public void setName(String name) { + this.name = name; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public void setNetworkIds(List networkIds) { + this.networkIds = networkIds; + } + + public void setBootType(String bootType) { + this.bootType = bootType; + } + + public void setBootMode(String bootMode) { + this.bootMode = bootMode; + } + + public void setBootIntoSetup(Boolean bootIntoSetup) { + this.bootIntoSetup = bootIntoSetup; + } + + public void setDiskOfferingId(Long diskOfferingId) { + this.diskOfferingId = diskOfferingId; + } + + public void setSize(Long size) { + this.size = size; + } + + public void setRootdisksize(Long rootdisksize) { + this.rootdisksize = rootdisksize; + } + + public void setDataDisksDetails(Map dataDisksDetails) { + this.dataDisksDetails = dataDisksDetails; + } + + public void setGroup(String group) { + this.group = group; + } + + public void setHypervisor(String hypervisor) { + this.hypervisor = hypervisor; + } + + public void setUserData(String userData) { + this.userData = userData; + } + + public void setUserdataId(Long userdataId) { + this.userdataId = userdataId; + } + + public void setUserdataDetails(Map userdataDetails) { + this.userdataDetails = userdataDetails; + } + + public void setSshKeyPairName(String sshKeyPairName) { + this.sshKeyPairName = sshKeyPairName; + } + + public void setSshKeyPairNames(List sshKeyPairNames) { + this.sshKeyPairNames = sshKeyPairNames; + } + + public void setHostId(Long hostId) { + this.hostId = hostId; + } + + public void setSecurityGroupIdList(List securityGroupIdList) { + this.securityGroupIdList = securityGroupIdList; + } + + public void setSecurityGroupNameList(List securityGroupNameList) { + this.securityGroupNameList = securityGroupNameList; + } + + public void setIpToNetworkList(Map ipToNetworkList) { + this.ipToNetworkList = ipToNetworkList; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setIp6Address(String ip6Address) { + this.ip6Address = ip6Address; + } + + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + public void setKeyboard(String keyboard) { + this.keyboard = keyboard; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public void setStartVm(Boolean startVm) { + this.startVm = startVm; + } + + public void setAffinityGroupIdList(List affinityGroupIdList) { + this.affinityGroupIdList = affinityGroupIdList; + } + + public void setAffinityGroupNameList(List affinityGroupNameList) { + this.affinityGroupNameList = affinityGroupNameList; + } + + public void setDisplayVm(Boolean displayVm) { + this.displayVm = displayVm; + } + + public void setDetails(Map details) { + this.details = details; + } + + public void setDeploymentPlanner(String deploymentPlanner) { + this.deploymentPlanner = deploymentPlanner; + } + + public void setDhcpOptionsNetworkList(Map dhcpOptionsNetworkList) { + this.dhcpOptionsNetworkList = dhcpOptionsNetworkList; + } + + public void setDataDiskTemplateToDiskOfferingList(Map dataDiskTemplateToDiskOfferingList) { + this.dataDiskTemplateToDiskOfferingList = dataDiskTemplateToDiskOfferingList; + } + + public void setExtraConfig(String extraConfig) { + this.extraConfig = extraConfig; + } + + public void setCopyImageTags(Boolean copyImageTags) { + this.copyImageTags = copyImageTags; + } + + public void setvAppProperties(Map vAppProperties) { + this.vAppProperties = vAppProperties; + } + + public void setvAppNetworks(Map vAppNetworks) { + this.vAppNetworks = vAppNetworks; + } + + public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { + this.dynamicScalingEnabled = dynamicScalingEnabled; + } + + public void setOverrideDiskOfferingId(Long overrideDiskOfferingId) { + this.overrideDiskOfferingId = overrideDiskOfferingId; + } + + public void setIothreadsEnabled(Boolean iothreadsEnabled) { + this.iothreadsEnabled = iothreadsEnabled; + } + + public void setIoDriverPolicy(String ioDriverPolicy) { + this.ioDriverPolicy = ioDriverPolicy; + } + + public void setNicMultiqueueNumber(Integer nicMultiqueueNumber) { + this.nicMultiqueueNumber = nicMultiqueueNumber; + } + + public void setNicPackedVirtQueues(Boolean nicPackedVirtQueues) { + this.nicPackedVirtQueues = nicPackedVirtQueues; + } + + public void setLeaseDuration(Integer leaseDuration) { + this.leaseDuration = leaseDuration; + } + + public void setLeaseExpiryAction(String leaseExpiryAction) { + this.leaseExpiryAction = leaseExpiryAction; + } + + public void setExternalDetails(Map externalDetails) { + this.externalDetails = externalDetails; + } + + public void setDataDiskInfoList(List dataDiskInfoList) { + this.dataDiskInfoList = dataDiskInfoList; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index dd6281ba65e..06b4f64b859 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -64,8 +64,8 @@ public class DeployVMCmd extends BaseDeployVMCmd { @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, since = "4.21") private Long snapshotId; - @Parameter(name = ApiConstants.DUMMY, type = CommandType.BOOLEAN, since = "4.23", description = "Deploy a dummy VM without any disk. False by default. This supports KVM only.") - private Boolean dummy; + @Parameter(name = "blank", type = CommandType.BOOLEAN, since = "4.22.1") + private Boolean blankInstance; ///////////////////////////////////////////////////// @@ -88,14 +88,38 @@ public class DeployVMCmd extends BaseDeployVMCmd { return snapshotId; } - public boolean getDummy() { - return dummy != null && dummy; - } - public boolean isVolumeOrSnapshotProvided() { return volumeId != null || snapshotId != null; } + public boolean isBlankInstance() { + return Boolean.TRUE.equals(blankInstance); + } + + ///////////////////////////////////////////////////// + ////////////////// Setters ////////////////////////// + ///////////////////////////////////////////////////// + + public void setServiceOfferingId(Long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + public void setTemplateId(Long templateId) { + this.templateId = templateId; + } + + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + + public void setSnapshotId(Long snapshotId) { + this.snapshotId = snapshotId; + } + + public void setBlankInstance(boolean blankInstance) { + this.blankInstance = blankInstance; + } + @Override public void execute() { UserVm result; @@ -140,7 +164,7 @@ public class DeployVMCmd extends BaseDeployVMCmd { @Override public void create() throws ResourceAllocationException { - if (!getDummy() && Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) { + if (!isBlankInstance() && Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) { throw new CloudRuntimeException("Please provide only one of the following parameters - template ID, volume ID or snapshot ID"); } diff --git a/api/src/main/java/org/apache/cloudstack/backup/IncrementalBackupService.java b/api/src/main/java/org/apache/cloudstack/backup/IncrementalBackupService.java index 45f73a08dcf..c37aa5b89ee 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/IncrementalBackupService.java +++ b/api/src/main/java/org/apache/cloudstack/backup/IncrementalBackupService.java @@ -64,12 +64,16 @@ public interface IncrementalBackupService extends Configurable, PluggableService ImageTransfer createImageTransfer(long volumeId, Long backupId, ImageTransfer.Direction direction); + boolean cancelImageTransfer(long imageTransferId); + /** * Finalize an image transfer * Marks transfer as complete (NBD is closed globally in finalize backup) */ boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd); + boolean finalizeImageTransfer(long imageTransferId); + /** * List image transfers for a backup */ diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 423aaececd6..47b8eba172a 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -577,7 +577,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac logger.debug("Allocating disks for {}", persistedVm); - if (_userVmMgr.isDummyTemplate(hyperType, template.getId())) { + if (_userVmMgr.isBlankInstanceTemplate(template)) { logger.debug("Template is a dummy template for hypervisor {}, skipping volume allocation", hyperType); return; } else { 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 new file mode 100644 index 00000000000..0cb2b56d071 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/ServerAdapter.java @@ -0,0 +1,833 @@ +// 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.adapter; + +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.Role; +import org.apache.cloudstack.acl.RolePermissionEntity; +import org.apache.cloudstack.acl.RoleService; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.acl.Rule; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.command.admin.vm.DeployVMCmdByAdmin; +import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +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.vm.AddNicToVMCmd; +import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; +import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; +import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; +import org.apache.cloudstack.api.command.user.vm.StartVMCmd; +import org.apache.cloudstack.api.command.user.vm.StopVMCmd; +import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd; +import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.backup.ImageTransfer.Direction; +import org.apache.cloudstack.backup.ImageTransferVO; +import org.apache.cloudstack.backup.IncrementalBackupService; +import org.apache.cloudstack.backup.dao.ImageTransferDao; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.query.QueryService; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.veeam.api.converter.AsyncJobJoinVOToJobConverter; +import org.apache.cloudstack.veeam.api.converter.ClusterVOToClusterConverter; +import org.apache.cloudstack.veeam.api.converter.DataCenterJoinVOToDataCenterConverter; +import org.apache.cloudstack.veeam.api.converter.HostJoinVOToHostConverter; +import org.apache.cloudstack.veeam.api.converter.ImageTransferVOToImageTransferConverter; +import org.apache.cloudstack.veeam.api.converter.NetworkVOToNetworkConverter; +import org.apache.cloudstack.veeam.api.converter.NetworkVOToVnicProfileConverter; +import org.apache.cloudstack.veeam.api.converter.NicVOToNicConverter; +import org.apache.cloudstack.veeam.api.converter.StoreVOToStorageDomainConverter; +import org.apache.cloudstack.veeam.api.converter.UserVmJoinVOToVmConverter; +import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter; +import org.apache.cloudstack.veeam.api.dto.Cluster; +import org.apache.cloudstack.veeam.api.dto.DataCenter; +import org.apache.cloudstack.veeam.api.dto.Disk; +import org.apache.cloudstack.veeam.api.dto.DiskAttachment; +import org.apache.cloudstack.veeam.api.dto.Host; +import org.apache.cloudstack.veeam.api.dto.ImageTransfer; +import org.apache.cloudstack.veeam.api.dto.Job; +import org.apache.cloudstack.veeam.api.dto.Network; +import org.apache.cloudstack.veeam.api.dto.Nic; +import org.apache.cloudstack.veeam.api.dto.Ref; +import org.apache.cloudstack.veeam.api.dto.StorageDomain; +import org.apache.cloudstack.veeam.api.dto.Vm; +import org.apache.cloudstack.veeam.api.dto.VmAction; +import org.apache.cloudstack.veeam.api.dto.VnicProfile; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import com.cloud.api.query.dao.DataCenterJoinDao; +import com.cloud.api.query.dao.HostJoinDao; +import com.cloud.api.query.dao.ImageStoreJoinDao; +import com.cloud.api.query.dao.StoragePoolJoinDao; +import com.cloud.api.query.dao.UserVmJoinDao; +import com.cloud.api.query.dao.VolumeJoinDao; +import com.cloud.api.query.vo.DataCenterJoinVO; +import com.cloud.api.query.vo.HostJoinVO; +import com.cloud.api.query.vo.ImageStoreJoinVO; +import com.cloud.api.query.vo.StoragePoolJoinVO; +import com.cloud.api.query.vo.UserVmJoinVO; +import com.cloud.api.query.vo.VolumeJoinVO; +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.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.offering.ServiceOffering; +import com.cloud.org.Grouping; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeApiService; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; +import com.cloud.user.Account; +import com.cloud.user.AccountService; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserAccount; +import com.cloud.user.dao.AccountDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.EnumUtils; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicVO; +import com.cloud.vm.UserVmService; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.UserVmDao; + +public class ServerAdapter extends ManagerBase { + private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser"; + private static final String SERVICE_ACCOUNT_ROLE_NAME = "Veeam Service Role"; + private static final String SERVICE_ACCOUNT_FIRST_NAME = "Veeam"; + private static final String SERVICE_ACCOUNT_LAST_NAME = "Service User"; + private static final List> SERVICE_ACCOUNT_ROLE_ALLOWED_APIS = Arrays.asList( + QueryAsyncJobResultCmd.class, + ListVMsCmd.class, + DeployVMCmd.class, + StartVMCmd.class, + StopVMCmd.class, + DestroyVMCmd.class, + ListVolumesCmd.class, + CreateVolumeCmd.class, + DeleteVolumeCmd.class, + AttachVolumeCmd.class, + DetachVolumeCmd.class, + ResizeVolumeCmd.class, + ListNetworksCmd.class + ); + + @Inject + RoleService roleService; + + @Inject + AccountService accountService; + + @Inject + AccountDao accountDao; + + @Inject + DataCenterDao dataCenterDao; + + @Inject + DataCenterJoinDao dataCenterJoinDao; + + @Inject + StoragePoolJoinDao storagePoolJoinDao; + + @Inject + ImageStoreJoinDao imageStoreJoinDao; + + @Inject + ClusterDao clusterDao; + + @Inject + HostJoinDao hostJoinDao; + + @Inject + NetworkDao networkDao; + + @Inject + UserVmDao userVmDao; + + @Inject + UserVmJoinDao userVmJoinDao; + + @Inject + VolumeDao volumeDao; + + @Inject + VolumeJoinDao volumeJoinDao; + + @Inject + VolumeDetailsDao volumeDetailsDao; + + @Inject + VolumeApiService volumeApiService; + + @Inject + PrimaryDataStoreDao primaryDataStoreDao; + + @Inject + ImageTransferDao imageTransferDao; + + @Inject + IncrementalBackupService incrementalBackupService; + + @Inject + QueryService queryService; + + @Inject + ServiceOfferingDao serviceOfferingDao; + + @Inject + UserVmService userVmService; + + @Inject + NicDao nicDao; + + private Map jobsMap = new ConcurrentHashMap<>(); + + protected Role createServiceAccountRole() { + Role role = roleService.createRole(SERVICE_ACCOUNT_ROLE_NAME, RoleType.User, + SERVICE_ACCOUNT_ROLE_NAME, false); + for (Class allowedApi : SERVICE_ACCOUNT_ROLE_ALLOWED_APIS) { + final String apiName = BaseCmd.getCommandNameByClass(allowedApi); + roleService.createRolePermission(role, new Rule(apiName), RolePermissionEntity.Permission.ALLOW, + String.format("Allow %s", apiName)); + } + roleService.createRolePermission(role, new Rule("*"), RolePermissionEntity.Permission.DENY, + "Deny all"); + logger.debug("Created default role for Veeam service account in projects: {}", role); + return role; + } + + public Role getServiceAccountRole() { + List roles = roleService.findRolesByName(SERVICE_ACCOUNT_ROLE_NAME); + if (CollectionUtils.isNotEmpty(roles)) { + Role role = roles.get(0); + logger.debug("Found default role for Veeam service account in projects: {}", role); + return role; + } + return createServiceAccountRole(); + } + + protected Account createServiceAccount() { + CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM); + try { + Role role = getServiceAccountRole(); + UserAccount userAccount = accountService.createUserAccount(SERVICE_ACCOUNT_NAME, + UUID.randomUUID().toString(), SERVICE_ACCOUNT_FIRST_NAME, + SERVICE_ACCOUNT_LAST_NAME, null, null, SERVICE_ACCOUNT_NAME, Account.Type.NORMAL, role.getId(), + 1L, null, null, null, null, User.Source.NATIVE); + Account account = accountService.getAccount(userAccount.getAccountId()); + logger.debug("Created Veeam service account: {}", account); + return account; + } finally { + CallContext.unregister(); + } + } + + protected Account createServiceAccountIfNeeded() { + List accounts = accountDao.findAccountsByName(SERVICE_ACCOUNT_NAME); + for (AccountVO account : accounts) { + if (Account.State.ENABLED.equals(account.getState())) { + logger.debug("Veeam service account found: {}", account); + return account; + } + } + return createServiceAccount(); + } + + @Override + public boolean start() { + createServiceAccountIfNeeded(); + //find public custom disk offering + return true; + } + + public List listAllDataCenters() { + final List clusters = dataCenterJoinDao.listAll(); + return DataCenterJoinVOToDataCenterConverter.toDCList(clusters); + } + + public DataCenter getDataCenter(String uuid) { + final DataCenterJoinVO vo = dataCenterJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); + } + return DataCenterJoinVOToDataCenterConverter.toDataCenter(vo); + } + + public List listStorageDomainsByDcId(final String uuid) { + final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid); + if (dataCenterVO == null) { + throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); + } + List storagePoolVOS = storagePoolJoinDao.listAll(); + List storageDomains = StoreVOToStorageDomainConverter.toStorageDomainListFromPools(storagePoolVOS); + List imageStoreJoinVOS = imageStoreJoinDao.listAll(); + storageDomains.addAll(StoreVOToStorageDomainConverter.toStorageDomainListFromStores(imageStoreJoinVOS)); + return storageDomains; + } + + public List listNetworksByDcId(final String uuid) { + final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(uuid); + if (dataCenterVO == null) { + throw new InvalidParameterValueException("DataCenter with ID " + uuid + " not found"); + } + List networks = networkDao.listAll(); + return NetworkVOToNetworkConverter.toNetworkList(networks, (dcId) -> dataCenterVO); + } + + public List listAllClusters() { + final List clusters = clusterDao.listAll(); + return ClusterVOToClusterConverter.toClusterList(clusters, this::getZoneById); + } + + public Cluster getCluster(String uuid) { + final ClusterVO vo = clusterDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Cluster with ID " + uuid + " not found"); + } + return ClusterVOToClusterConverter.toCluster(vo, this::getZoneById); + } + + public List listAllHosts() { + final List hosts = hostJoinDao.listAll(); + return HostJoinVOToHostConverter.toHostList(hosts); + } + + public Host getHost(String uuid) { + final HostJoinVO vo = hostJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); + } + return HostJoinVOToHostConverter.toHost(vo); + } + + public List listAllNetworks() { + final List networks = networkDao.listAll(); + return NetworkVOToNetworkConverter.toNetworkList(networks, this::getZoneById); + } + + public Network getNetwork(String uuid) { + final NetworkVO vo = networkDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); + } + return NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById); + } + + public List listAllVnicProfiles() { + final List networks = networkDao.listAll(); + return NetworkVOToVnicProfileConverter.toVnicProfileList(networks, this::getZoneById); + } + + public VnicProfile getVnicProfile(String uuid) { + final NetworkVO vo = networkDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Host with ID " + uuid + " not found"); + } + return NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById); + } + + public List listAllUserVms() { + // Todo: add filtering, pagination + List vms = userVmJoinDao.listAll(); + return UserVmJoinVOToVmConverter.toVmList(vms, this::getHostById); + } + + public Vm getVm(String uuid) { + UserVmJoinVO vo = userVmJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::listDiskAttachmentsByInstanceId, + this::listNicsByInstance); + } + + public Vm handleCreateVm(Vm request) { + if (request == null) { + throw new InvalidParameterValueException("Request disk data is empty"); + } + String name = request.name; + Long zoneId = null; + Long clusterId = null; + if (request.cluster != null && StringUtils.isNotEmpty(request.cluster.id)) { + ClusterVO clusterVO = clusterDao.findByUuid(request.cluster.id); + if (clusterVO != null) { + zoneId = clusterVO.getDataCenterId(); + clusterId = clusterVO.getId(); + } + } + if (zoneId == null) { + throw new InvalidParameterValueException("Failed to determine datacenter for VM creation request"); + } + Integer cpu = null; + try { + cpu = request.cpu.topology.sockets; + } catch (Exception ignored) {} + if (cpu == null) { + throw new InvalidParameterValueException("CPU topology sockets must be specified"); + } + Long memory = null; + try { + memory = request.memory; + } catch (Exception ignored) {} + if (memory == null) { + throw new InvalidParameterValueException("Memory must be specified"); + } + String userdata = null; + if (request.getInitialization() != null) { + userdata = request.getInitialization().getContentData(); + } + ApiConstants.BootType bootType = ApiConstants.BootType.BIOS; + ApiConstants.BootMode bootMode = ApiConstants.BootMode.LEGACY; + if (request.bios != null && StringUtils.isNotEmpty(request.bios.type) && request.bios.type.contains("secure")) { + bootType = ApiConstants.BootType.UEFI; + bootMode = ApiConstants.BootMode.SECURE; + } + Account serviceAccount = createServiceAccountIfNeeded(); + CallContext.register(serviceAccount.getId(), serviceAccount.getId()); + try { + return createVm(zoneId, clusterId, name, cpu, memory, userdata, bootType, bootMode); + } finally { + CallContext.unregister(); + } + } + + protected ServiceOffering getServiceOfferingIdForVmCreation(long zoneId, int cpu, long memory) { + ListServiceOfferingsCmd cmd = new ListServiceOfferingsCmd(); + ComponentContext.inject(cmd); + cmd.setZoneId(zoneId); + cmd.setCpuNumber(cpu); + Integer memoryMB = (int)(memory / (1024L * 1024L)); + cmd.setMemory(memoryMB); + ListResponse offerings = queryService.searchForServiceOfferings(cmd); + if (offerings.getResponses().isEmpty()) { + return null; + } + String uuid = offerings.getResponses().get(0).getId(); + return serviceOfferingDao.findByUuid(uuid); + } + + protected Vm createVm(Long zoneId, Long clusterId, String name, int cpu, long memory, String userdata, + ApiConstants.BootType bootType, ApiConstants.BootMode bootMode) { + ServiceOffering serviceOffering = getServiceOfferingIdForVmCreation(zoneId, cpu, memory); + if (serviceOffering == null) { + throw new CloudRuntimeException("No service offering found for VM creation with specified CPU and memory"); + } + DeployVMCmdByAdmin cmd = new DeployVMCmdByAdmin(); + ComponentContext.inject(cmd); + cmd.setZoneId(zoneId); + cmd.setClusterId(clusterId); + cmd.setName(name); + cmd.setServiceOfferingId(serviceOffering.getId()); + if (StringUtils.isNotEmpty(userdata)) { + cmd.setUserData(Base64.getEncoder().encodeToString(userdata.getBytes())); + } + if (bootType != null) { + cmd.setBootType(bootType.toString()); + } + if (bootMode != null) { + cmd.setBootMode(bootMode.toString()); + } + // ToDo: handle other. + cmd.setHypervisor(Hypervisor.HypervisorType.KVM.name()); + cmd.setBlankInstance(true); + try { + UserVm vm = userVmService.createVirtualMachine(cmd); + vm = userVmService.finalizeCreateVirtualMachine(vm.getId()); + UserVmJoinVO vo = userVmJoinDao.findById(vm.getId()); + return UserVmJoinVOToVmConverter.toVm(vo, this::getHostById, this::listDiskAttachmentsByInstanceId, + this::listNicsByInstance); + } catch (InsufficientCapacityException | ResourceUnavailableException | ResourceAllocationException | CloudRuntimeException e) { + throw new CloudRuntimeException("Failed to create VM: " + e.getMessage(), e); + } + } + + public void deleteVm(String uuid) { + UserVmVO vo = userVmDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + try { + userVmService.destroyVm(vo.getId(), true); + } catch (ResourceUnavailableException e) { + throw new CloudRuntimeException("Failed to delete VM: " + e.getMessage(), e); + } + } + + public VmAction startVm(String uuid) { + UserVmVO vo = userVmDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + try { + userVmService.startVirtualMachine(vo, null); + return UserVmJoinVOToVmConverter.toVmAction(userVmJoinDao.findById(vo.getId())); + } catch (ResourceUnavailableException | OperationTimedoutException | InsufficientCapacityException | CloudRuntimeException e) { + throw new CloudRuntimeException("Failed to start VM: " + e.getMessage(), e); + } + } + + public VmAction stopVm(String uuid) { + UserVmVO vo = userVmDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + try { + userVmService.stopVirtualMachine(vo.getId(), true); + return UserVmJoinVOToVmConverter.toVmAction(userVmJoinDao.findById(vo.getId())); + } catch (CloudRuntimeException e) { + throw new CloudRuntimeException("Failed to stop VM: " + e.getMessage(), e); + } + } + + public VmAction shutdownVm(String uuid) { + UserVmVO vo = userVmDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + try { + userVmService.stopVirtualMachine(vo.getId(), false); + return UserVmJoinVOToVmConverter.toVmAction(userVmJoinDao.findById(vo.getId())); + } catch (CloudRuntimeException e) { + throw new CloudRuntimeException("Failed to shutdown VM: " + e.getMessage(), e); + } + } + + public List listAllDisks() { + List kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM); + return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes); + } + + public Disk getDisk(String uuid) { + VolumeJoinVO vo = volumeJoinDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); + } + return VolumeJoinVOToDiskConverter.toDisk(vo); + } + + protected List listDiskAttachmentsByInstanceId(final long instanceId) { + List kvmVolumes = volumeJoinDao.listByInstanceId(instanceId); + return VolumeJoinVOToDiskConverter.toDiskAttachmentList(kvmVolumes); + } + + public List listDiskAttachmentsByInstanceUuid(final String uuid) { + UserVmVO vo = userVmDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + return listDiskAttachmentsByInstanceId(vo.getId()); + } + + public DiskAttachment handleVmAttachDisk(final String vmUuid, final DiskAttachment request) { + UserVmVO vmVo = userVmDao.findByUuid(vmUuid); + if (vmVo == null) { + throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); + } + if (request == null || request.disk == null || StringUtils.isEmpty(request.disk.id)) { + throw new InvalidParameterValueException("Request disk data is empty"); + } + VolumeVO volumeVO = volumeDao.findByUuid(request.disk.id); + if (volumeVO == null) { + throw new InvalidParameterValueException("Disk with ID " + request.disk.id + " not found"); + } + Account serviceAccount = createServiceAccountIfNeeded(); + CallContext.register(serviceAccount.getId(), serviceAccount.getId()); + try { + Volume volume = volumeApiService.attachVolumeToVM(vmVo.getId(), volumeVO.getId(), 0L, false); + VolumeJoinVO attachedVolumeVO = volumeJoinDao.findById(volume.getId()); + return VolumeJoinVOToDiskConverter.toDiskAttachment(attachedVolumeVO); + } finally { + CallContext.unregister(); + } + } + + public void deleteDisk(String uuid) { + VolumeVO vo = volumeDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); + } + volumeApiService.deleteVolume(vo.getId(), accountService.getSystemAccount()); + } + + public Disk handleCreateDisk(Disk request) { + if (request == null) { + throw new InvalidParameterValueException("Request disk data is empty"); + } + String name = request.name; + if (StringUtils.isBlank(name) && !name.startsWith("Veeam_KvmBackupDisk_")) { + throw new InvalidParameterValueException("Only worker VM disk creation is supported"); + } + if (request.storageDomains == null || CollectionUtils.isEmpty(request.storageDomains.storageDomain) || + request.storageDomains.storageDomain.size() > 1) { + throw new InvalidParameterValueException("Exactly one storage domain must be specified"); + } + Ref domain = request.storageDomains.storageDomain.get(0); + if (domain == null || domain.id == null) { + throw new InvalidParameterValueException("Storage domain ID must be specified"); + } + StoragePoolVO pool = primaryDataStoreDao.findByUuid(domain.id); + if (pool == null) { + throw new InvalidParameterValueException("Storage domain with ID " + domain.id + " not found"); + } + String sizeStr = request.provisionedSize; + if (StringUtils.isBlank(sizeStr)) { + throw new InvalidParameterValueException("Provisioned size must be specified"); + } + long provisionedSizeInGb; + try { + provisionedSizeInGb = Long.parseLong(sizeStr); + } catch (NumberFormatException ex) { + throw new InvalidParameterValueException("Invalid provisioned size: " + sizeStr); + } + if (provisionedSizeInGb <= 0) { + throw new InvalidParameterValueException("Provisioned size must be greater than zero"); + } + provisionedSizeInGb = Math.max(1L, provisionedSizeInGb / (1024L * 1024L * 1024L)); + Long initialSize = null; + if (StringUtils.isNotBlank(request.initialSize)) { + try { + initialSize = Long.parseLong(request.initialSize); + } catch (NumberFormatException ignored) {} + } + Account serviceAccount = createServiceAccountIfNeeded(); + DataCenterVO zone = dataCenterDao.findById(pool.getDataCenterId()); + if (zone == null || !Grouping.AllocationState.Enabled.equals(zone.getAllocationState())) { + throw new InvalidParameterValueException("Datacenter for the specified storage domain is not found or not active"); + } + Long diskOfferingId = volumeApiService.getCustomDiskOfferingIdForVolumeUpload(serviceAccount, zone); + if (diskOfferingId == null) { + throw new CloudRuntimeException("Failed to find custom offering for disk" + zone.getName()); + } + CallContext.register(serviceAccount.getId(), serviceAccount.getId()); + try { + return createDisk(serviceAccount, pool, name, diskOfferingId, provisionedSizeInGb, initialSize); + } finally { + CallContext.unregister(); + } + } + + @NotNull + private Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb, Long initialSize) { + Volume volume; + try { + volume = volumeApiService.allocVolume(serviceAccount.getId(), pool.getDataCenterId(), diskOfferingId, null, + null, name, sizeInGb, null, null, null, null); + } catch (ResourceAllocationException e) { + throw new CloudRuntimeException(e.getMessage(), e); + } + if (volume == null) { + throw new CloudRuntimeException("Failed to create volume"); + } + volume = volumeApiService.createVolume(volume.getId(), null, null, pool.getId(), true); + if (initialSize != null) { + volumeDetailsDao.addDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE, String.valueOf(initialSize), true); + } + + // Implementation for creating a Disk resource + return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId())); + } + + protected List listNicsByInstance(final long instanceId, final String instanceUuid) { + List nics = nicDao.listByVmId(instanceId); + return NicVOToNicConverter.toNicList(nics, instanceUuid, this::getNetworkById); + } + + protected List listNicsByInstance(final UserVmJoinVO vo) { + return listNicsByInstance(vo.getId(), vo.getUuid()); + } + + public List listNicsByInstanceId(final String uuid) { + UserVmVO vo = userVmDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("VM with ID " + uuid + " not found"); + } + return listNicsByInstance(vo.getId(), vo.getUuid()); + } + + public Nic handleVmAttachNic(final String vmUuid, final Nic request) { + UserVmVO vmVo = userVmDao.findByUuid(vmUuid); + if (vmVo == null) { + throw new InvalidParameterValueException("VM with ID " + vmUuid + " not found"); + } + if (request == null || request.getVnicProfile() == null || StringUtils.isEmpty(request.getVnicProfile().id)) { + throw new InvalidParameterValueException("Request nic data is empty"); + } + NetworkVO networkVO = networkDao.findByUuid(request.getVnicProfile().id); + if (networkVO == null) { + throw new InvalidParameterValueException("VNic profile " + request.getVnicProfile().id+ " not found"); + } + Account serviceAccount = createServiceAccountIfNeeded(); + CallContext.register(serviceAccount.getId(), serviceAccount.getId()); + try { + AddNicToVMCmd cmd = new AddNicToVMCmd(); + ComponentContext.inject(cmd); + cmd.setVmId(vmVo.getId()); + cmd.setNetworkId(networkVO.getId()); + if (request.getMac() != null && StringUtils.isNotBlank(request.getMac().getAddress())) { + cmd.setMacAddress(request.getMac().getAddress()); + } + userVmService.addNicToVirtualMachine(cmd); + NicVO nic = nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkVO.getId(), vmVo.getId()); + if (nic == null) { + throw new CloudRuntimeException("Failed to attach NIC to VM"); + } + return NicVOToNicConverter.toNic(nic, vmUuid, this::getNetworkById); + } finally { + CallContext.unregister(); + } + } + + public List listAllImageTransfers() { + List imageTransfers = imageTransferDao.listAll(); + return ImageTransferVOToImageTransferConverter.toImageTransferList(imageTransfers, this::getHostById, this::getVolumeById); + } + + public ImageTransfer getImageTransfer(String uuid) { + ImageTransferVO vo = imageTransferDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); + } + return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById); + } + + public ImageTransfer handleCreateImageTransfer(ImageTransfer request) { + if (request == null) { + throw new InvalidParameterValueException("Request image transfer data is empty"); + } + if (request.getDisk() == null || StringUtils.isBlank(request.getDisk().id)) { + throw new InvalidParameterValueException("Disk ID must be specified"); + } + VolumeJoinVO volumeVO = volumeJoinDao.findByUuid(request.getDisk().id); + if (volumeVO == null) { + throw new InvalidParameterValueException("Disk with ID " + request.getDisk().id + " not found"); + } + Direction direction = EnumUtils.fromString(Direction.class, request.getDirection()); + if (direction == null) { + throw new InvalidParameterValueException("Invalid or missing direction"); + } + return createImageTransfer(null, volumeVO.getId(), direction); + } + + public boolean handleCancelImageTransfer(String uuid) { + ImageTransferVO vo = imageTransferDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); + } + return incrementalBackupService.cancelImageTransfer(vo.getId()); + } + + public boolean handleFinalizeImageTransfer(String uuid) { + ImageTransferVO vo = imageTransferDao.findByUuid(uuid); + if (vo == null) { + throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); + } + return incrementalBackupService.finalizeImageTransfer(vo.getId()); + } + + private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction) { + Account serviceAccount = createServiceAccountIfNeeded(); + CallContext.register(serviceAccount.getId(), serviceAccount.getId()); + try { + org.apache.cloudstack.backup.ImageTransfer imageTransfer = + incrementalBackupService.createImageTransfer(volumeId, null, direction); + ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId()); + return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, this::getVolumeById); + } finally { + CallContext.unregister(); + } + } + + protected DataCenterJoinVO getZoneById(Long zoneId) { + if (zoneId == null) { + return null; + } + return dataCenterJoinDao.findById(zoneId); + } + + private HostJoinVO getHostById(Long hostId) { + if (hostId == null) { + return null; + } + return hostJoinDao.findById(hostId); + } + + private VolumeJoinVO getVolumeById(Long volumeId) { + if (volumeId == null) { + return null; + } + return volumeJoinDao.findById(volumeId); + } + + protected NetworkVO getNetworkById(Long networkId) { + if (networkId == null) { + return null; + } + return networkDao.findById(networkId); + } + + public List listAllJobs() { + return Collections.emptyList(); + } + + public Job getJob(String uuid) { +// final ClusterVO vo = clusterDao.findByUuid(uuid); +// if (vo == null) { +// throw new InvalidParameterValueException("Cluster with ID " + uuid + " not found"); +// } + long startTime = jobsMap.computeIfAbsent(uuid, k -> System.currentTimeMillis()); + long elapsed = System.currentTimeMillis() - startTime; + if (elapsed > 10000L) { + return AsyncJobJoinVOToJobConverter.toJob(uuid, "finished", startTime); + } else { + return AsyncJobJoinVOToJobConverter.toJob(uuid, "started", startTime); + } + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/UserResourceAdapter.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/UserResourceAdapter.java deleted file mode 100644 index ad1be6af85e..00000000000 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/adapter/UserResourceAdapter.java +++ /dev/null @@ -1,345 +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.adapter; - -import java.util.Arrays; -import java.util.List; -import java.util.UUID; - -import javax.inject.Inject; - -import org.apache.cloudstack.acl.Role; -import org.apache.cloudstack.acl.RolePermissionEntity; -import org.apache.cloudstack.acl.RoleService; -import org.apache.cloudstack.acl.RoleType; -import org.apache.cloudstack.acl.Rule; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; -import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; -import org.apache.cloudstack.api.command.user.vm.DeployVMCmd; -import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd; -import org.apache.cloudstack.api.command.user.vm.ListVMsCmd; -import org.apache.cloudstack.api.command.user.vm.StartVMCmd; -import org.apache.cloudstack.api.command.user.vm.StopVMCmd; -import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; -import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; -import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd; -import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; -import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd; -import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; -import org.apache.cloudstack.backup.ImageTransfer.Direction; -import org.apache.cloudstack.backup.ImageTransferVO; -import org.apache.cloudstack.backup.IncrementalBackupService; -import org.apache.cloudstack.backup.dao.ImageTransferDao; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.veeam.api.converter.ImageTransferVOToImageTransferConverter; -import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter; -import org.apache.cloudstack.veeam.api.dto.Disk; -import org.apache.cloudstack.veeam.api.dto.ImageTransfer; -import org.apache.cloudstack.veeam.api.dto.Ref; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; - -import com.cloud.api.query.dao.HostJoinDao; -import com.cloud.api.query.dao.VolumeJoinDao; -import com.cloud.api.query.vo.HostJoinVO; -import com.cloud.api.query.vo.VolumeJoinVO; -import com.cloud.dc.DataCenterVO; -import com.cloud.dc.dao.DataCenterDao; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.hypervisor.Hypervisor; -import com.cloud.org.Grouping; -import com.cloud.storage.Volume; -import com.cloud.storage.VolumeApiService; -import com.cloud.storage.dao.VolumeDetailsDao; -import com.cloud.user.Account; -import com.cloud.user.AccountService; -import com.cloud.user.AccountVO; -import com.cloud.user.User; -import com.cloud.user.UserAccount; -import com.cloud.user.dao.AccountDao; -import com.cloud.utils.EnumUtils; -import com.cloud.utils.component.ManagerBase; -import com.cloud.utils.exception.CloudRuntimeException; - -public class UserResourceAdapter extends ManagerBase { - private static final String SERVICE_ACCOUNT_NAME = "veemserviceuser"; - private static final String SERVICE_ACCOUNT_ROLE_NAME = "Veeam Service Role"; - private static final String SERVICE_ACCOUNT_FIRST_NAME = "Veeam"; - private static final String SERVICE_ACCOUNT_LAST_NAME = "Service User"; - private static final List> SERVICE_ACCOUNT_ROLE_ALLOWED_APIS = Arrays.asList( - QueryAsyncJobResultCmd.class, - ListVMsCmd.class, - DeployVMCmd.class, - StartVMCmd.class, - StopVMCmd.class, - DestroyVMCmd.class, - ListVolumesCmd.class, - CreateVolumeCmd.class, - DeleteVolumeCmd.class, - AttachVolumeCmd.class, - DetachVolumeCmd.class, - ResizeVolumeCmd.class, - ListNetworksCmd.class - ); - - @Inject - DataCenterDao dataCenterDao; - - @Inject - RoleService roleService; - - @Inject - AccountService accountService; - - @Inject - AccountDao accountDao; - - @Inject - VolumeJoinDao volumeJoinDao; - - @Inject - VolumeDetailsDao volumeDetailsDao; - - @Inject - VolumeApiService volumeApiService; - - @Inject - PrimaryDataStoreDao primaryDataStoreDao; - - @Inject - ImageTransferDao imageTransferDao; - - @Inject - HostJoinDao hostJoinDao; - - @Inject - IncrementalBackupService incrementalBackupService; - - protected Role createServiceAccountRole() { - Role role = roleService.createRole(SERVICE_ACCOUNT_ROLE_NAME, RoleType.User, - SERVICE_ACCOUNT_ROLE_NAME, false); - for (Class allowedApi : SERVICE_ACCOUNT_ROLE_ALLOWED_APIS) { - final String apiName = BaseCmd.getCommandNameByClass(allowedApi); - roleService.createRolePermission(role, new Rule(apiName), RolePermissionEntity.Permission.ALLOW, - String.format("Allow %s", apiName)); - } - roleService.createRolePermission(role, new Rule("*"), RolePermissionEntity.Permission.DENY, - "Deny all"); - logger.debug("Created default role for Veeam service account in projects: {}", role); - return role; - } - - public Role getServiceAccountRole() { - List roles = roleService.findRolesByName(SERVICE_ACCOUNT_ROLE_NAME); - if (CollectionUtils.isNotEmpty(roles)) { - Role role = roles.get(0); - logger.debug("Found default role for Veeam service account in projects: {}", role); - return role; - } - return createServiceAccountRole(); - } - - protected Account createServiceAccount() { - CallContext.register(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM); - try { - Role role = getServiceAccountRole(); - UserAccount userAccount = accountService.createUserAccount(SERVICE_ACCOUNT_NAME, - UUID.randomUUID().toString(), SERVICE_ACCOUNT_FIRST_NAME, - SERVICE_ACCOUNT_LAST_NAME, null, null, SERVICE_ACCOUNT_NAME, Account.Type.NORMAL, role.getId(), - 1L, null, null, null, null, User.Source.NATIVE); - Account account = accountService.getAccount(userAccount.getAccountId()); - logger.debug("Created Veeam service account: {}", account); - return account; - } finally { - CallContext.unregister(); - } - } - - protected Account createServiceAccountIfNeeded() { - List accounts = accountDao.findAccountsByName(SERVICE_ACCOUNT_NAME); - for (AccountVO account : accounts) { - if (Account.State.ENABLED.equals(account.getState())) { - logger.debug("Veeam service account found: {}", account); - return account; - } - } - return createServiceAccount(); - } - - @Override - public boolean start() { - createServiceAccountIfNeeded(); - //find public custom disk offering - return true; - } - - public List listAllDisks() { - List kvmVolumes = volumeJoinDao.listByHypervisor(Hypervisor.HypervisorType.KVM); - return VolumeJoinVOToDiskConverter.toDiskList(kvmVolumes); - } - - public Disk getDisk(String uuid) { - VolumeJoinVO vo = volumeJoinDao.findByUuid(uuid); - if (vo == null) { - throw new InvalidParameterValueException("Disk with ID " + uuid + " not found"); - } - return VolumeJoinVOToDiskConverter.toDisk(vo); - } - - public Disk handleCreateDisk(Disk request) { - if (request == null) { - throw new InvalidParameterValueException("Request disk data is empty"); - } - String name = request.name; - if (StringUtils.isBlank(name) && !name.startsWith("Veeam_KvmBackupDisk_")) { - throw new InvalidParameterValueException("Only worker VM disk creation is supported"); - } - if (request.storageDomains == null || CollectionUtils.isEmpty(request.storageDomains.storageDomain) || - request.storageDomains.storageDomain.size() > 1) { - throw new InvalidParameterValueException("Exactly one storage domain must be specified"); - } - Ref domain = request.storageDomains.storageDomain.get(0); - if (domain == null || domain.id == null) { - throw new InvalidParameterValueException("Storage domain ID must be specified"); - } - StoragePoolVO pool = primaryDataStoreDao.findByUuid(domain.id); - if (pool == null) { - throw new InvalidParameterValueException("Storage domain with ID " + domain.id + " not found"); - } - String sizeStr = request.provisionedSize; - if (StringUtils.isBlank(sizeStr)) { - throw new InvalidParameterValueException("Provisioned size must be specified"); - } - long provisionedSizeInGb; - try { - provisionedSizeInGb = Long.parseLong(sizeStr); - } catch (NumberFormatException ex) { - throw new InvalidParameterValueException("Invalid provisioned size: " + sizeStr); - } - if (provisionedSizeInGb <= 0) { - throw new InvalidParameterValueException("Provisioned size must be greater than zero"); - } - provisionedSizeInGb = Math.max(1L, provisionedSizeInGb / (1024L * 1024L * 1024L)); - Long initialSize = null; - if (StringUtils.isNotBlank(request.initialSize)) { - try { - initialSize = Long.parseLong(request.initialSize); - } catch (NumberFormatException ignored) {} - } - Account serviceAccount = createServiceAccountIfNeeded(); - DataCenterVO zone = dataCenterDao.findById(pool.getDataCenterId()); - if (zone == null || !Grouping.AllocationState.Enabled.equals(zone.getAllocationState())) { - throw new InvalidParameterValueException("Datacenter for the specified storage domain is not found or not active"); - } - Long diskOfferingId = volumeApiService.getCustomDiskOfferingIdForVolumeUpload(serviceAccount, zone); - if (diskOfferingId == null) { - throw new CloudRuntimeException("Failed to find custom offering for disk" + zone.getName()); - } - CallContext.register(serviceAccount.getId(), serviceAccount.getId()); - try { - return createDisk(serviceAccount, pool, name, diskOfferingId, provisionedSizeInGb, initialSize); - } finally { - CallContext.unregister(); - } - } - - @NotNull - private Disk createDisk(Account serviceAccount, StoragePoolVO pool, String name, Long diskOfferingId, long sizeInGb, Long initialSize) { - Volume volume; - try { - volume = volumeApiService.allocVolume(serviceAccount.getId(), pool.getDataCenterId(), diskOfferingId, null, - null, name, sizeInGb, null, null, null, null); - } catch (ResourceAllocationException e) { - throw new CloudRuntimeException(e.getMessage(), e); - } - if (volume == null) { - throw new CloudRuntimeException("Failed to create volume"); - } - volume = volumeApiService.createVolume(volume.getId(), null, null, pool.getId(), true); - if (initialSize != null) { - volumeDetailsDao.addDetail(volume.getId(), ApiConstants.VIRTUAL_SIZE, String.valueOf(initialSize), true); - } - - // Implementation for creating a Disk resource - return VolumeJoinVOToDiskConverter.toDisk(volumeJoinDao.findById(volume.getId())); - } - - public List listAllImageTransfers() { - List imageTransfers = imageTransferDao.listAll(); - return ImageTransferVOToImageTransferConverter.toImageTransferList(imageTransfers, this::getHostById, this::getVolumeById); - } - - private HostJoinVO getHostById(Long hostId) { - if (hostId == null) { - return null; - } - return hostJoinDao.findById(hostId); - } - - private VolumeJoinVO getVolumeById(Long volumeId) { - if (volumeId == null) { - return null; - } - return volumeJoinDao.findById(volumeId); - } - - public ImageTransfer getImageTransfer(String uuid) { - ImageTransferVO vo = imageTransferDao.findByUuid(uuid); - if (vo == null) { - throw new InvalidParameterValueException("Image transfer with ID " + uuid + " not found"); - } - return ImageTransferVOToImageTransferConverter.toImageTransfer(vo, this::getHostById, this::getVolumeById); - } - - public ImageTransfer handleCreateImageTransfer(ImageTransfer request) { - if (request == null) { - throw new InvalidParameterValueException("Request image transfer data is empty"); - } - if (request.getDisk() == null || StringUtils.isBlank(request.getDisk().id)) { - throw new InvalidParameterValueException("Disk ID must be specified"); - } - VolumeJoinVO volumeVO = volumeJoinDao.findByUuid(request.getDisk().id); - if (volumeVO == null) { - throw new InvalidParameterValueException("Disk with ID " + request.getDisk().id + " not found"); - } - Direction direction = EnumUtils.fromString(Direction.class, request.getDirection()); - if (direction == null) { - throw new InvalidParameterValueException("Invalid or missing direction"); - } - return createImageTransfer(null, volumeVO.getId(), direction); - } - - private ImageTransfer createImageTransfer(Long backupId, Long volumeId, Direction direction) { - Account serviceAccount = createServiceAccountIfNeeded(); - CallContext.register(serviceAccount.getId(), serviceAccount.getId()); - try { - org.apache.cloudstack.backup.ImageTransfer imageTransfer = - incrementalBackupService.createImageTransfer(volumeId, null, direction); - ImageTransferVO imageTransferVO = imageTransferDao.findById(imageTransfer.getId()); - return ImageTransferVOToImageTransferConverter.toImageTransfer(imageTransferVO, this::getHostById, this::getVolumeById); - } finally { - CallContext.unregister(); - } - } -} 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/ApiService.java index 24a9dbb730e..380a64715fe 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/ApiService.java @@ -32,13 +32,13 @@ import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlService; import org.apache.cloudstack.veeam.VeeamControlServlet; 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.SpecialObjectRef; import org.apache.cloudstack.veeam.api.dto.SpecialObjects; -import org.apache.cloudstack.veeam.api.dto.ApiSummary; import org.apache.cloudstack.veeam.api.dto.SummaryCount; import org.apache.cloudstack.veeam.api.dto.Version; import org.apache.cloudstack.veeam.utils.Negotiation; @@ -65,7 +65,7 @@ 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, 200, + io.getWriter().write(resp, HttpServletResponse.SC_OK, createDummyApi(VeeamControlService.ContextPath.value() + BASE_ROUTE), outFormat); } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java index 4c4dda45f8c..a80d0ec8d61 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ClustersRouteHandler.java @@ -26,27 +26,21 @@ 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.adapter.ServerAdapter; 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 org.apache.commons.collections.CollectionUtils; -import com.cloud.api.query.dao.DataCenterJoinDao; -import com.cloud.api.query.vo.DataCenterJoinVO; -import com.cloud.dc.ClusterVO; -import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.InvalidParameterValueException; 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 - DataCenterJoinDao dataCenterJoinDao; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -90,32 +84,19 @@ public class ClustersRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = ClusterVOToClusterConverter.toClusterList(listClusters(), this::getZoneById); + final List result = serverAdapter.listAllClusters(); final Clusters response = new Clusters(result); - io.getWriter().write(resp, 200, response, outFormat); - } - - protected List listClusters() { - return clusterDao.listAll(); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected 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; + try { + Cluster response = serverAdapter.getCluster(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - Cluster response = ClusterVOToClusterConverter.toCluster(vo, this::getZoneById); - - io.getWriter().write(resp, 200, response, outFormat); - } - - protected DataCenterJoinVO getZoneById(Long zoneId) { - if (zoneId == null) { - return null; - } - return dataCenterJoinDao.findById(zoneId); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java index 5c84a20bc10..e2e60fe8479 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DataCentersRouteHandler.java @@ -26,9 +26,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.api.converter.DataCenterJoinVOToDataCenterConverter; -import org.apache.cloudstack.veeam.api.converter.NetworkVOToNetworkConverter; -import org.apache.cloudstack.veeam.api.converter.StoreVOToStorageDomainConverter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.DataCenter; import org.apache.cloudstack.veeam.api.dto.DataCenters; import org.apache.cloudstack.veeam.api.dto.Network; @@ -39,33 +37,14 @@ import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; -import com.cloud.api.query.dao.DataCenterJoinDao; -import com.cloud.api.query.dao.ImageStoreJoinDao; -import com.cloud.api.query.dao.StoragePoolJoinDao; -import com.cloud.api.query.vo.DataCenterJoinVO; -import com.cloud.api.query.vo.ImageStoreJoinVO; -import com.cloud.api.query.vo.StoragePoolJoinVO; -import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.NetworkVO; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; public class DataCentersRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/datacenters"; - private static final int DEFAULT_MAX = 50; - private static final int HARD_CAP_MAX = 1000; - private static final int DEFAULT_PAGE = 1; @Inject - DataCenterJoinDao dataCenterJoinDao; - - @Inject - StoragePoolJoinDao storagePoolJoinDao; - - @Inject - ImageStoreJoinDao imageStoreJoinDao; - - @Inject - NetworkDao networkDao; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -119,66 +98,41 @@ public class DataCentersRouteHandler extends ManagerBase implements RouteHandler protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = DataCenterJoinVOToDataCenterConverter.toDCList(listDCs()); + final List result = serverAdapter.listAllDataCenters(); final DataCenters response = new DataCenters(result); - io.getWriter().write(resp, 200, response, outFormat); - } - - protected List listDCs() { - return dataCenterJoinDao.listAll(); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(id); - if (dataCenterVO == null) { - io.notFound(resp, "DataCenter not found: " + id, outFormat); - return; + try { + DataCenter response = serverAdapter.getDataCenter(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - DataCenter response = DataCenterJoinVOToDataCenterConverter.toDataCenter(dataCenterVO); - - io.getWriter().write(resp, 200, response, outFormat); - } - - protected List listStoragePoolsByDcId(final long dcId) { - return storagePoolJoinDao.listAll(); - } - - protected List listImageStoresByDcId(final long dcId) { - return imageStoreJoinDao.listAll(); - } - - protected List listNetworksByDcId(final long dcId) { - return networkDao.listAll(); } protected void handleGetStorageDomainsByDcId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(id); - if (dataCenterVO == null) { - io.notFound(resp, "DataCenter not found: " + id, outFormat); - return; + try { + List storageDomains = serverAdapter.listStorageDomainsByDcId(id); + StorageDomains response = new StorageDomains(storageDomains); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - List storageDomains = StoreVOToStorageDomainConverter.toStorageDomainListFromPools(listStoragePoolsByDcId(dataCenterVO.getId())); - storageDomains.addAll(StoreVOToStorageDomainConverter.toStorageDomainListFromStores(listImageStoresByDcId(dataCenterVO.getId()))); - - StorageDomains response = new StorageDomains(storageDomains); - - io.getWriter().write(resp, 200, response, outFormat); } protected void handleGetNetworksByDcId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final DataCenterJoinVO dataCenterVO = dataCenterJoinDao.findByUuid(id); - if (dataCenterVO == null) { - io.notFound(resp, "DataCenter not found: " + id, outFormat); - return; + try { + List networks = serverAdapter.listNetworksByDcId(id); + Networks response = new Networks(networks); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - List networks = NetworkVOToNetworkConverter.toNetworkList(listNetworksByDcId(dataCenterVO.getId()), (dcId) -> dataCenterVO); - - Networks response = new Networks(networks); - - io.getWriter().write(resp, 200, response, outFormat); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java index 6cac244e133..0bd618a8111 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/DisksRouteHandler.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.adapter.UserResourceAdapter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Disk; import org.apache.cloudstack.veeam.api.dto.Disks; import org.apache.cloudstack.veeam.utils.Negotiation; @@ -42,7 +42,7 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/disks"; @Inject - UserResourceAdapter userResourceAdapter; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -74,16 +74,22 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler { } } - if (!"GET".equalsIgnoreCase(method)) { - io.methodNotAllowed(resp, "GET", outFormat); - return; - } List idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE); if (CollectionUtils.isNotEmpty(idAndSubPath)) { String id = idAndSubPath.get(0); if (idAndSubPath.size() == 1) { - handleGetById(id, resp, outFormat, io); - return; + if (!"GET".equalsIgnoreCase(method) && !"DELETE".equalsIgnoreCase(method)) { + io.methodNotAllowed(resp, "GET, DELETE", outFormat); + return; + } + if ("GET".equalsIgnoreCase(method)) { + handleGetById(id, resp, outFormat, io); + return; + } + if ("DELETE".equalsIgnoreCase(method)) { + handleDeleteById(id, resp, outFormat, io); + return; + } } } @@ -92,32 +98,42 @@ public class DisksRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = userResourceAdapter.listAllDisks(); + final List result = serverAdapter.listAllDisks(); final Disks response = new Disks(result); - io.getWriter().write(resp, 200, response, outFormat); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { String data = RouteHandler.getRequestData(req); - logger.info("Received POST request on /api/disks endpoint, but method: POST is not supported atm. Request-data: {}", data); + logger.info("Received POST request on /api/disks endpoint. Request-data: {}", data); // ToDo: remove try { Disk request = io.getMapper().jsonMapper().readValue(data, Disk.class); - Disk response = userResourceAdapter.handleCreateDisk(request); - io.getWriter().write(resp, 201, response, outFormat); + Disk response = serverAdapter.handleCreateDisk(request); + io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat); } catch (JsonProcessingException | CloudRuntimeException e) { - io.getWriter().write(resp, 400, e.getMessage(), outFormat); + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat); } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { try { - Disk response = userResourceAdapter.getDisk(id); - io.getWriter().write(resp, 200, response, outFormat); + Disk response = serverAdapter.getDisk(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { - io.getWriter().write(resp, 404, e.getMessage(), outFormat); + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handleDeleteById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + serverAdapter.deleteDisk(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, "Deleted disk ID: " + id, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat); } } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java index 6ed3a3af0b7..37ac17b2364 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/HostsRouteHandler.java @@ -26,22 +26,21 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.api.converter.HostJoinVOToHostConverter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Host; import org.apache.cloudstack.veeam.api.dto.Hosts; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; -import com.cloud.api.query.dao.HostJoinDao; -import com.cloud.api.query.vo.HostJoinVO; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; public class HostsRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/hosts"; @Inject - HostJoinDao hostJoinDao; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -85,25 +84,19 @@ public class HostsRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = HostJoinVOToHostConverter.toHostList(listHosts()); + final List result = serverAdapter.listAllHosts(); final Hosts response = new Hosts(result); - io.getWriter().write(resp, 200, response, outFormat); - } - - protected List listHosts() { - return hostJoinDao.listAll(); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final HostJoinVO vo = hostJoinDao.findByUuid(id); - if (vo == null) { - io.notFound(resp, "DataCenter not found: " + id, outFormat); - return; + try { + Host response = serverAdapter.getHost(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - Host response = HostJoinVOToHostConverter.toHost(vo); - - io.getWriter().write(resp, 200, response, outFormat); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java index a469afc08b5..3cdd5d0469d 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/ImageTransfersRouteHandler.java @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.adapter.UserResourceAdapter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.ImageTransfer; import org.apache.cloudstack.veeam.api.dto.ImageTransfers; import org.apache.cloudstack.veeam.utils.Negotiation; @@ -42,7 +42,7 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand public static final String BASE_ROUTE = "/api/imagetransfers"; @Inject - UserResourceAdapter userResourceAdapter; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -105,11 +105,10 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = userResourceAdapter.listAllImageTransfers(); + final List result = serverAdapter.listAllImageTransfers(); final ImageTransfers response = new ImageTransfers(); response.setImageTransfer(result); - - io.getWriter().write(resp, 400, response, outFormat); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, @@ -118,32 +117,40 @@ public class ImageTransfersRouteHandler extends ManagerBase implements RouteHand logger.info("Received POST request on /api/imagetransfers endpoint. Request-data: {}", data); try { ImageTransfer request = io.getMapper().jsonMapper().readValue(data, ImageTransfer.class); - ImageTransfer response = userResourceAdapter.handleCreateImageTransfer(request); - io.getWriter().write(resp, 201, response, outFormat); + ImageTransfer response = serverAdapter.handleCreateImageTransfer(request); + io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat); } catch (JsonProcessingException | CloudRuntimeException e) { - io.getWriter().write(resp, 400, e.getMessage(), outFormat); + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad Request", e.getMessage(), outFormat); } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { try { - ImageTransfer response = userResourceAdapter.getImageTransfer(id); - io.getWriter().write(resp, 200, response, outFormat); + ImageTransfer response = serverAdapter.getImageTransfer(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } catch (InvalidParameterValueException e) { - io.getWriter().write(resp, 404, e.getMessage(), outFormat); + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } } protected void handleCancelById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - //ToDo: implement cancel logic - io.getWriter().write(resp, 200, "Image transfer cancelled successfully", outFormat); + try { + serverAdapter.handleCancelImageTransfer(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, "Image transfer cancelled successfully", outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } } protected void handleFinalizeById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - //ToDo: implement finalize logic - io.getWriter().write(resp, 200, "Image transfer finalized successfully", outFormat); + try { + serverAdapter.handleFinalizeImageTransfer(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, "Image transfer finalized successfully", outFormat); + } catch (CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java new file mode 100644 index 00000000000..516ea8de4d8 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/JobsRouteHandler.java @@ -0,0 +1,102 @@ +// 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.adapter.ServerAdapter; +import org.apache.cloudstack.veeam.api.dto.Job; +import org.apache.cloudstack.veeam.api.dto.Jobs; +import org.apache.cloudstack.veeam.utils.Negotiation; +import org.apache.cloudstack.veeam.utils.PathUtil; +import org.apache.commons.collections.CollectionUtils; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.component.ManagerBase; + +public class JobsRouteHandler extends ManagerBase implements RouteHandler { + public static final String BASE_ROUTE = "/api/jobs"; + + @Inject + ServerAdapter serverAdapter; + + @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; + } + + List idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE); + if (CollectionUtils.isNotEmpty(idAndSubPath)) { + String id = idAndSubPath.get(0); + if (idAndSubPath.size() == 1) { + handleGetById(id, resp, outFormat, io); + return; + } + } + + resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not found"); + } + + protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, + Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { + final List result = serverAdapter.listAllJobs(); + final Jobs response = new Jobs(result); + + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } + + protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + Job response = serverAdapter.getJob(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java index 2b895a2a647..d11397e1eee 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/NetworksRouteHandler.java @@ -26,27 +26,21 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.api.converter.NetworkVOToNetworkConverter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.Network; import org.apache.cloudstack.veeam.api.dto.Networks; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; -import com.cloud.api.query.dao.DataCenterJoinDao; -import com.cloud.api.query.vo.DataCenterJoinVO; -import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.NetworkVO; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; public class NetworksRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/networks"; @Inject - NetworkDao networkDao; - - @Inject - DataCenterJoinDao dataCenterJoinDao; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -90,32 +84,19 @@ public class NetworksRouteHandler extends ManagerBase implements RouteHandler { protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = NetworkVOToNetworkConverter.toNetworkList(listNetworks(), this::getZoneById); + final List result = serverAdapter.listAllNetworks(); final Networks response = new Networks(result); - io.getWriter().write(resp, 200, response, outFormat); - } - - protected List listNetworks() { - return networkDao.listAll(); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final NetworkVO vo = networkDao.findByUuid(id); - if (vo == null) { - io.notFound(resp, "DataCenter not found: " + id, outFormat); - return; + try { + Network response = serverAdapter.getNetwork(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - Network response = NetworkVOToNetworkConverter.toNetwork(vo, this::getZoneById); - - io.getWriter().write(resp, 200, response, outFormat); - } - - protected DataCenterJoinVO getZoneById(Long zoneId) { - if (zoneId == null) { - return null; - } - return dataCenterJoinDao.findById(zoneId); } } 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 6971c81b69f..30d781e868b 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 @@ -27,27 +27,26 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.api.converter.UserVmJoinVOToVmConverter; -import org.apache.cloudstack.veeam.api.converter.VolumeJoinVOToDiskConverter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.DiskAttachment; import org.apache.cloudstack.veeam.api.dto.DiskAttachments; +import org.apache.cloudstack.veeam.api.dto.Nic; +import org.apache.cloudstack.veeam.api.dto.Nics; import org.apache.cloudstack.veeam.api.dto.Vm; +import org.apache.cloudstack.veeam.api.dto.VmAction; +import org.apache.cloudstack.veeam.api.dto.Vms; import org.apache.cloudstack.veeam.api.request.VmListQuery; import org.apache.cloudstack.veeam.api.request.VmSearchExpr; import org.apache.cloudstack.veeam.api.request.VmSearchFilters; import org.apache.cloudstack.veeam.api.request.VmSearchParser; -import org.apache.cloudstack.veeam.api.response.VmCollectionResponse; -import org.apache.cloudstack.veeam.api.response.VmEntityResponse; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; -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.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.exception.CloudRuntimeException; +import com.fasterxml.jackson.core.JsonProcessingException; public class VmsRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/vms"; @@ -56,13 +55,7 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { private static final int DEFAULT_PAGE = 1; @Inject - UserVmJoinDao userVmJoinDao; - - @Inject - HostJoinDao hostJoinDao; - - @Inject - VolumeJoinDao volumeJoinDao; + ServerAdapter serverAdapter; private VmSearchParser searchParser; @@ -90,24 +83,74 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { final String method = req.getMethod(); final String sanitizedPath = getSanitizedPath(path); if (sanitizedPath.equals(BASE_ROUTE)) { - if (!"GET".equalsIgnoreCase(method)) { - io.methodNotAllowed(resp, "GET", outFormat); + if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method) && !"DELETE".equalsIgnoreCase(method)) { + io.methodNotAllowed(resp, "GET, POST, DELETE", outFormat); + return; + } + if ("GET".equalsIgnoreCase(method)) { + handleGet(req, resp, outFormat, io); + return; + } + if ("POST".equalsIgnoreCase(method)) { + handlePost(req, resp, outFormat, io); return; } - handleGet(req, resp, outFormat, io); - return; } List idAndSubPath = PathUtil.extractIdAndSubPath(sanitizedPath, BASE_ROUTE); if (CollectionUtils.isNotEmpty(idAndSubPath)) { String id = idAndSubPath.get(0); if (idAndSubPath.size() == 1) { - handleGetById(id, resp, outFormat, io); + if (!"GET".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method) && !"DELETE".equalsIgnoreCase(method)) { + io.methodNotAllowed(resp, "GET, PUT, DELETE", outFormat); + } else if ("GET".equalsIgnoreCase(method)) { + handleGetById(id, resp, outFormat, io); + } else if ("DELETE".equalsIgnoreCase(method)) { + handleUpdateById(id, req, resp, outFormat, io); + } else if ("DELETE".equalsIgnoreCase(method)) { + handleDeleteById(id, resp, outFormat, io); + } return; } else if (idAndSubPath.size() == 2) { String subPath = idAndSubPath.get(1); - if ("diskattachments".equals(subPath)) { - handleGetDisAttachmentsByVmId(id, resp, outFormat, io); + if ("start".equals(subPath)) { + if ("POST".equalsIgnoreCase(method)) { + handleStartVmById(id, req, resp, outFormat, io); + } else { + io.methodNotAllowed(resp, "POST", outFormat); + } + return; + } else if ("stop".equals(subPath)) { + if ("POST".equalsIgnoreCase(method)) { + handleStopVmById(id, req, resp, outFormat, io); + } else { + io.methodNotAllowed(resp, "POST", outFormat); + } + return; + } else if ("shutdown".equals(subPath)) { + if ("POST".equalsIgnoreCase(method)) { + handleShutdownVmById(id, req, resp, outFormat, io); + } else { + io.methodNotAllowed(resp, "POST", outFormat); + } + return; + } else if ("diskattachments".equals(subPath)) { + if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)) { + io.methodNotAllowed(resp, "GET, POST", outFormat); + } else if ("GET".equalsIgnoreCase(method)) { + handleGetDiskAttachmentsByVmId(id, resp, outFormat, io); + } else if ("POST".equalsIgnoreCase(method)) { + handlePostDiskAttachmentForVmId(id, req, resp, outFormat, io); + } + return; + } else if ("nics".equals(subPath)) { + if (!"GET".equalsIgnoreCase(method) && !"POST".equalsIgnoreCase(method)) { + io.methodNotAllowed(resp, "GET, POST", outFormat); + } else if ("GET".equalsIgnoreCase(method)) { + handleGetNicsByVmId(id, resp, outFormat, io); + } else if ("POST".equalsIgnoreCase(method)) { + handlePostNicForVmId(id, req, resp, outFormat, io); + } return; } } @@ -149,10 +192,10 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { return; } - final List result = UserVmJoinVOToVmConverter.toVmList(listUserVms(), this::getHostById); - final VmCollectionResponse response = new VmCollectionResponse(result); + final List result = serverAdapter.listAllUserVms(); + final Vms response = new Vms(result); - io.getWriter().write(resp, 200, response, outFormat); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected static VmListQuery fromRequest(final HttpServletRequest req) { @@ -172,41 +215,123 @@ public class VmsRouteHandler extends ManagerBase implements RouteHandler { } } - protected List listUserVms() { - // Todo: add filtering, pagination - return userVmJoinDao.listAll(); + protected void handlePost(final HttpServletRequest req, final HttpServletResponse resp, + Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { + String data = RouteHandler.getRequestData(req); + logger.info("Received method: POST request. Request-data: {}", data); + try { + Vm request = io.getMapper().jsonMapper().readValue(data, Vm.class); + Vm response = serverAdapter.handleCreateVm(request); + io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat); + } catch (JsonProcessingException | CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat); + } } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final UserVmJoinVO userVmJoinVO = userVmJoinDao.findByUuid(id); - if (userVmJoinVO == null) { - io.notFound(resp, "VM not found: " + id, outFormat); - return; + try { + Vm response = serverAdapter.getVm(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - VmEntityResponse response = new VmEntityResponse(UserVmJoinVOToVmConverter.toVm(userVmJoinVO, this::getHostById)); - - io.getWriter().write(resp, 200, response, outFormat); } - protected void handleGetDisAttachmentsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, - final VeeamControlServlet io) throws IOException { - final UserVmJoinVO userVmJoinVO = userVmJoinDao.findByUuid(id); - if (userVmJoinVO == null) { - io.notFound(resp, "VM not found: " + id, outFormat); - return; - } - List disks = VolumeJoinVOToDiskConverter.toDiskAttachmentList( - volumeJoinDao.listByInstanceId(userVmJoinVO.getId())); - DiskAttachments response = new DiskAttachments(disks); - - io.getWriter().write(resp, 200, response, outFormat); + protected void handleUpdateById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + String data = RouteHandler.getRequestData(req); + logger.info("Received POST request, but method: POST is not supported atm. Request-data: {}", data); + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Not implemented", "", outFormat); } - protected HostJoinVO getHostById(Long hostId) { - if (hostId == null) { - return null; + protected void handleDeleteById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + serverAdapter.deleteVm(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, "", outFormat); + } catch (CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handleStartVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + VmAction vm = serverAdapter.startVm(id); + io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); + } catch (CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handleStopVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + VmAction vm = serverAdapter.stopVm(id); + io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); + } catch (CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handleShutdownVmById(final String id, final HttpServletRequest req, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + VmAction vm = serverAdapter.shutdownVm(id); + io.getWriter().write(resp, HttpServletResponse.SC_ACCEPTED, vm, outFormat); + } catch (CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handleGetDiskAttachmentsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + List disks = serverAdapter.listDiskAttachmentsByInstanceUuid(id); + DiskAttachments response = new DiskAttachments(disks); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handlePostDiskAttachmentForVmId(final String id, final HttpServletRequest req, + final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) + throws IOException { + String data = RouteHandler.getRequestData(req); + logger.info("Received method: POST request. Request-data: {}", data); + try { + DiskAttachment request = io.getMapper().jsonMapper().readValue(data, DiskAttachment.class); + DiskAttachment response = serverAdapter.handleVmAttachDisk(id, request); + io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat); + } catch (JsonProcessingException | CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat); + } + } + + protected void handleGetNicsByVmId(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, + final VeeamControlServlet io) throws IOException { + try { + List nics = serverAdapter.listNicsByInstanceId(id); + Nics response = new Nics(nics); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); + } + } + + protected void handlePostNicForVmId(final String id, final HttpServletRequest req, + final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) + throws IOException { + String data = RouteHandler.getRequestData(req); + logger.info("Received method: POST request. Request-data: {}", data); + try { + Nic request = io.getMapper().jsonMapper().readValue(data, Nic.class); + Nic response = serverAdapter.handleVmAttachNic(id, request); + io.getWriter().write(resp, HttpServletResponse.SC_CREATED, response, outFormat); + } catch (JsonProcessingException | CloudRuntimeException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_BAD_REQUEST, "Bad request", e.getMessage(), outFormat); } - return hostJoinDao.findById(hostId); } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java index ba7e040e455..c62fbf69482 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/VnicProfilesRouteHandler.java @@ -26,27 +26,21 @@ import javax.servlet.http.HttpServletResponse; import org.apache.cloudstack.veeam.RouteHandler; import org.apache.cloudstack.veeam.VeeamControlServlet; -import org.apache.cloudstack.veeam.api.converter.NetworkVOToVnicProfileConverter; +import org.apache.cloudstack.veeam.adapter.ServerAdapter; import org.apache.cloudstack.veeam.api.dto.VnicProfile; import org.apache.cloudstack.veeam.api.dto.VnicProfiles; import org.apache.cloudstack.veeam.utils.Negotiation; import org.apache.cloudstack.veeam.utils.PathUtil; import org.apache.commons.collections.CollectionUtils; -import com.cloud.api.query.dao.DataCenterJoinDao; -import com.cloud.api.query.vo.DataCenterJoinVO; -import com.cloud.network.dao.NetworkDao; -import com.cloud.network.dao.NetworkVO; +import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.component.ManagerBase; public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandler { public static final String BASE_ROUTE = "/api/vnicprofiles"; @Inject - NetworkDao networkDao; - - @Inject - DataCenterJoinDao dataCenterJoinDao; + ServerAdapter serverAdapter; @Override public boolean start() { @@ -90,32 +84,19 @@ public class VnicProfilesRouteHandler extends ManagerBase implements RouteHandle protected void handleGet(final HttpServletRequest req, final HttpServletResponse resp, Negotiation.OutFormat outFormat, VeeamControlServlet io) throws IOException { - final List result = NetworkVOToVnicProfileConverter.toVnicProfileList(listNetworks(), this::getZoneById); + final List result = serverAdapter.listAllVnicProfiles(); final VnicProfiles response = new VnicProfiles(result); - io.getWriter().write(resp, 200, response, outFormat); - } - - protected List listNetworks() { - return networkDao.listAll(); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); } protected void handleGetById(final String id, final HttpServletResponse resp, final Negotiation.OutFormat outFormat, final VeeamControlServlet io) throws IOException { - final NetworkVO vo = networkDao.findByUuid(id); - if (vo == null) { - io.notFound(resp, "DataCenter not found: " + id, outFormat); - return; + try { + VnicProfile response = serverAdapter.getVnicProfile(id); + io.getWriter().write(resp, HttpServletResponse.SC_OK, response, outFormat); + } catch (InvalidParameterValueException e) { + io.getWriter().writeFault(resp, HttpServletResponse.SC_NOT_FOUND, "Not found", e.getMessage(), outFormat); } - VnicProfile response = NetworkVOToVnicProfileConverter.toVnicProfile(vo, this::getZoneById); - - io.getWriter().write(resp, 200, response, outFormat); - } - - protected DataCenterJoinVO getZoneById(Long zoneId) { - if (zoneId == null) { - return null; - } - return dataCenterJoinDao.findById(zoneId); } } 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 new file mode 100644 index 00000000000..f3aa1dd4002 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/AsyncJobJoinVOToJobConverter.java @@ -0,0 +1,50 @@ +// 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 org.apache.cloudstack.veeam.VeeamControlService; +import org.apache.cloudstack.veeam.api.JobsRouteHandler; +import org.apache.cloudstack.veeam.api.dto.Actions; +import org.apache.cloudstack.veeam.api.dto.Job; +import org.apache.cloudstack.veeam.api.dto.Ref; + +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.setActions(new Actions()); + job.setDescription("Something"); + job.setLink(Collections.emptyList()); + return job; + } +} 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 new file mode 100644 index 00000000000..72fe2d55965 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/converter/NicVOToNicConverter.java @@ -0,0 +1,94 @@ +// 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.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.apache.cloudstack.veeam.VeeamControlService; +import org.apache.cloudstack.veeam.api.VnicProfilesRouteHandler; +import org.apache.cloudstack.veeam.api.dto.Ip; +import org.apache.cloudstack.veeam.api.dto.Ips; +import org.apache.cloudstack.veeam.api.dto.Mac; +import org.apache.cloudstack.veeam.api.dto.Nic; +import org.apache.cloudstack.veeam.api.dto.Ref; +import org.apache.cloudstack.veeam.api.dto.ReportedDevice; +import org.apache.cloudstack.veeam.api.dto.ReportedDevices; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import com.cloud.network.dao.NetworkVO; +import com.cloud.vm.NicVO; + +public class NicVOToNicConverter { + + public static Nic toNic(final NicVO vo, final String vmUuid, final Function networkResolver) { + final String basePath = VeeamControlService.ContextPath.value(); + final Nic nic = new Nic(); + nic.setId(vo.getUuid()); + nic.setName(vo.getReserver()); + Mac mac = new Mac(); + mac.setAddress(vo.getMacAddress()); + nic.setMac(mac); + nic.setLinked(true); + nic.setPlugged(true); + if (StringUtils.isBlank(vmUuid)) { + nic.setVm(Ref.of(basePath + "/vms/" + vmUuid, vmUuid)); + nic.setHref(nic.getVm().href + "/nics/" + vo.getUuid()); + } + nic.setInterfaceType("virtio"); + ReportedDevice device = getReportedDevice(vo, mac, nic.getVm()); + nic.setReportedDevices(new ReportedDevices(List.of(device))); + if (networkResolver != null) { + final NetworkVO network = networkResolver.apply(vo.getNetworkId()); + if (network != null) { + nic.setVnicProfile(Ref.of(basePath + VnicProfilesRouteHandler.BASE_ROUTE + "/" + network.getUuid(), network.getUuid())); + } + } + return nic; + } + + @NotNull + private static ReportedDevice getReportedDevice(NicVO vo, Mac mac, Ref vm) { + ReportedDevice device = new ReportedDevice(); + device.setType("network"); + device.setId(vo.getUuid()); + device.setName("eth0"); + device.setMac(mac); + Ip ip = new Ip(); + if (vo.getIPv4Address() != null) { + ip.setAddress(vo.getIPv4Address()); + ip.setGateway(vo.getIPv4Gateway()); + ip.setVersion("v4"); + } else if (vo.getIPv6Address() != null) { + ip.setAddress(vo.getIPv6Address()); + ip.setGateway(vo.getIPv6Gateway()); + ip.setVersion("v6"); + } + device.setIps(new Ips(List.of(ip))); + device.setVm(vm); + return device; + } + + public static List toNicList(final List vos, final String vmUuid, final Function networkResolver) { + return vos.stream() + .map(vo -> toNic(vo, vmUuid, networkResolver)) + .collect(Collectors.toList()); + } +} 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 7216eb89af1..a4f59dfee52 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 @@ -25,14 +25,22 @@ import java.util.stream.Collectors; import org.apache.cloudstack.veeam.VeeamControlService; import org.apache.cloudstack.veeam.api.ApiService; +import org.apache.cloudstack.veeam.api.JobsRouteHandler; import org.apache.cloudstack.veeam.api.VmsRouteHandler; +import org.apache.cloudstack.veeam.api.dto.Actions; import org.apache.cloudstack.veeam.api.dto.Bios; import org.apache.cloudstack.veeam.api.dto.Cpu; +import org.apache.cloudstack.veeam.api.dto.DiskAttachment; +import org.apache.cloudstack.veeam.api.dto.DiskAttachments; +import org.apache.cloudstack.veeam.api.dto.EmptyElement; import org.apache.cloudstack.veeam.api.dto.Link; +import org.apache.cloudstack.veeam.api.dto.Nic; +import org.apache.cloudstack.veeam.api.dto.Nics; import org.apache.cloudstack.veeam.api.dto.Os; import org.apache.cloudstack.veeam.api.dto.Ref; import org.apache.cloudstack.veeam.api.dto.Topology; import org.apache.cloudstack.veeam.api.dto.Vm; +import org.apache.cloudstack.veeam.api.dto.VmAction; import org.apache.commons.lang3.StringUtils; import com.cloud.api.query.vo.HostJoinVO; @@ -49,7 +57,8 @@ public final class UserVmJoinVOToVmConverter { * * @param src UserVmJoinVO */ - public static Vm toVm(final UserVmJoinVO src, final Function hostResolver) { + public static Vm toVm(final UserVmJoinVO src, final Function hostResolver, + final Function> disksResolver, final Function> nicsResolver) { if (src == null) { return null; } @@ -62,10 +71,13 @@ public final class UserVmJoinVOToVmConverter { dst.description = src.getDisplayName(); dst.href = basePath + VmsRouteHandler.BASE_ROUTE + "/" + src.getUuid(); dst.status = mapStatus(src.getState()); - final Date lastUpdated = src.getLastUpdated(); + final Date lastUpdated = src.getLastUpdated() != null ? src.getLastUpdated() : src.getCreated(); if ("down".equals(dst.status)) { dst.stopTime = lastUpdated.getTime(); } + if ("up".equals(dst.status)) { + dst.setStartTime(lastUpdated.getTime()); + } final Ref template = buildRef( basePath + ApiService.BASE_ROUTE, "templates", @@ -93,24 +105,35 @@ public final class UserVmJoinVOToVmConverter { 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)); + dst.cpu = new Cpu(src.getArch(), new Topology(src.getCpu(), 1, 1)); dst.os = new Os(); dst.os.type = src.getGuestOsId() % 2 == 0 ? "windows" : "linux"; dst.bios = new Bios(); dst.bios.type = "q35_secure_boot"; - dst.type = "server"; + dst.type = "desktop"; dst.origin = "ovirt"; - dst.actions = null;dst.link = List.of( + + if (disksResolver != null) { + List diskAttachments = disksResolver.apply(src.getId()); + dst.setDiskAttachments(new DiskAttachments(diskAttachments)); + } + + if (disksResolver != null) { + List nics = nicsResolver.apply(src); + dst.setNics(new Nics(nics)); + } + + dst.actions = new Actions(List.of( + new Link("start", dst.href + "/start"), + new Link("stop", dst.href + "/stop"), + new Link("shutdown", dst.href + "/shutdown") + )); + dst.link = List.of( new Link("diskattachments", dst.href + "/diskattachments"), new Link("nics", @@ -118,16 +141,26 @@ public final class UserVmJoinVOToVmConverter { new Link("snapshots", dst.href + "/snapshots") ); + dst.tags = new EmptyElement(); return dst; } public static List toVmList(final List srcList, final Function hostResolver) { return srcList.stream() - .map(v -> toVm(v, hostResolver)) + .map(v -> toVm(v, hostResolver, null, null)) .collect(Collectors.toList()); } + public static VmAction toVmAction(final UserVmJoinVO vm) { + VmAction action = new VmAction(); + final String basePath = VeeamControlService.ContextPath.value(); + action.setVm(toVm(vm, null, null, null)); + action.setJob(Ref.of(basePath + JobsRouteHandler.BASE_ROUTE + vm.getUuid(), vm.getUuid())); + action.setStatus("complete"); + return action; + } + private static String mapStatus(final VirtualMachine.State state) { if (state == null) { return null; 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 44f56bfbd00..0bb8e40d92a 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,6 +24,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.VmsRouteHandler; import org.apache.cloudstack.veeam.api.dto.Actions; import org.apache.cloudstack.veeam.api.dto.Disk; import org.apache.cloudstack.veeam.api.dto.DiskAttachment; @@ -132,24 +133,21 @@ public class VolumeJoinVOToDiskConverter { public static DiskAttachment toDiskAttachment(final VolumeJoinVO vol) { final DiskAttachment da = new DiskAttachment(); - final String apiBase = VeeamControlService.ContextPath.value() + ApiService.BASE_ROUTE; + final String basePath = VeeamControlService.ContextPath.value(); + final String apiBase = basePath + ApiService.BASE_ROUTE; final String diskAttachmentId = vol.getUuid(); - final String diskAttachmentHref = apiBase + "/diskattachments/" + diskAttachmentId; - - da.id = diskAttachmentId; - da.href = diskAttachmentHref; - - // Links - da.disk = Ref.of( - apiBase + "/disks/" + vol.getUuid(), - vol.getUuid() - ); da.vm = Ref.of( - apiBase + "/vms/" + vol.getVmUuid(), + basePath + VmsRouteHandler.BASE_ROUTE + "/" + vol.getVmUuid(), vol.getVmUuid() ); + da.id = diskAttachmentId; + da.href = da.vm.href + "/diskattachements/" + diskAttachmentId;; + + // Links + da.disk = toDisk(vol); + // Properties da.active = "true"; da.bootable = "false"; diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/DiskAttachment.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/DiskAttachment.java index ca041e993f5..578b9462c41 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/DiskAttachment.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/DiskAttachment.java @@ -43,7 +43,7 @@ public final class DiskAttachment { @JsonProperty("uses_scsi_reservation") public String usesScsiReservation; - public Ref disk; + public Disk disk; public Ref vm; public String href; diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ip.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ip.java new file mode 100644 index 00000000000..7afbc0710ff --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ip.java @@ -0,0 +1,61 @@ +// 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 com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Ip { + + private String address; + private String gateway; + private String netmask; + private String version; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getGateway() { + return gateway; + } + + public void setGateway(String gateway) { + this.gateway = gateway; + } + + public String getNetmask() { + return netmask; + } + + public void setNetmask(String netmask) { + this.netmask = netmask; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ips.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ips.java new file mode 100644 index 00000000000..11d94cc4179 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Ips.java @@ -0,0 +1,42 @@ +// 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.dataformat.xml.annotation.JacksonXmlElementWrapper; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Ips { + + @JacksonXmlElementWrapper(useWrapping = false) + private List ip; + + public Ips(final List ip) { + this.ip = ip; + } + + public List getIp() { + return ip; + } + + public void setIp(List ip) { + this.ip = ip; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Job.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Job.java new file mode 100644 index 00000000000..042d45c133d --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Job.java @@ -0,0 +1,75 @@ +// 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; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Job { + private String autoCleared; + private String external; + private Long lastUpdated; + private Long startTime; + private Long endTime; + private String status; + private Ref owner; + private Actions actions; + private String description; + private List link; + private String href; + private String id; + + // getters and setters + public String getAutoCleared() { return autoCleared; } + public void setAutoCleared(String autoCleared) { this.autoCleared = autoCleared; } + + public String getExternal() { return external; } + public void setExternal(String external) { this.external = external; } + + public Long getLastUpdated() { return lastUpdated; } + public void setLastUpdated(Long lastUpdated) { this.lastUpdated = lastUpdated; } + + public Long getStartTime() { return startTime; } + public void setStartTime(Long startTime) { this.startTime = startTime; } + + public Long getEndTime() { return endTime; } + public void setEndTime(Long endTime) { this.endTime = endTime; } + + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + + public Ref getOwner() { return owner; } + public void setOwner(Ref owner) { this.owner = owner; } + + public Actions getActions() { return actions; } + public void setActions(Actions actions) { this.actions = actions; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + + public List getLink() { return link; } + public void setLink(List link) { this.link = link; } + + public String getHref() { return href; } + public void setHref(String href) { this.href = href; } + + public String getId() { return id; } + public void setId(String id) { this.id = id; } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Jobs.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Jobs.java new file mode 100644 index 00000000000..904950ae0a7 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Jobs.java @@ -0,0 +1,42 @@ +// 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 ownershjob. 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.dataformat.xml.annotation.JacksonXmlElementWrapper; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Jobs { + + @JacksonXmlElementWrapper(useWrapping = false) + private List job; + + public Jobs(final List job) { + this.job = job; + } + + public List getJob() { + return job; + } + + public void setJob(List job) { + this.job = job; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/VmEntityResponse.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Mac.java similarity index 71% rename from plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/VmEntityResponse.java rename to plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Mac.java index 92547b337d5..02d90805460 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/VmEntityResponse.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Mac.java @@ -15,20 +15,20 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.veeam.api.response; +package org.apache.cloudstack.veeam.api.dto; -import org.apache.cloudstack.veeam.api.dto.Vm; +import com.fasterxml.jackson.annotation.JsonInclude; -/** - * Required entity response: - * { "vm": { .. } } - */ -public final class VmEntityResponse { - public Vm vm; +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Mac { - public VmEntityResponse() {} + private String address; - public VmEntityResponse(final Vm vm) { - this.vm = vm; + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nic.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nic.java new file mode 100644 index 00000000000..7eca9aff4f7 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nic.java @@ -0,0 +1,131 @@ +// 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 com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Nic { + + private String href; + private String id; + private String name; + private String description; + @JacksonXmlProperty(localName = "interface") + @JsonProperty("interface") + private String interfaceType; + private String linked; + private Mac mac; + private String plugged; + private Ref vnicProfile; + private Ref vm; + private ReportedDevices reportedDevices; + + public Nic() { + } + + public String getHref() { + return href; + } + + public void setHref(final String href) { + this.href = href; + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public String getInterfaceType() { + return interfaceType; + } + + public void setInterfaceType(String interfaceType) { + this.interfaceType = interfaceType; + } + + public boolean isLinked() { + return Boolean.parseBoolean(linked); + } + + public void setLinked(boolean linked) { + this.linked = Boolean.toString(linked); + } + + public Mac getMac() { + return mac; + } + + public void setMac(Mac mac) { + this.mac = mac; + } + + public boolean isPlugged() { + return Boolean.parseBoolean(plugged); + } + + public void setPlugged(boolean plugged) { + this.plugged = Boolean.toString(plugged); + } + + public Ref getVnicProfile() { + return vnicProfile; + } + + public void setVnicProfile(Ref vnicProfile) { + this.vnicProfile = vnicProfile; + } + + public Ref getVm() { + return vm; + } + + public void setVm(Ref vm) { + this.vm = vm; + } + + public ReportedDevices getReportedDevices() { + return reportedDevices; + } + + public void setReportedDevices(ReportedDevices reportedDevices) { + this.reportedDevices = reportedDevices; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nics.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nics.java new file mode 100644 index 00000000000..37c0259fa53 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Nics.java @@ -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 = "nics") +public final class Nics { + + @JsonProperty("nic") + @JacksonXmlElementWrapper(useWrapping = false) + public List nic; + + public Nics() {} + + public Nics(final List nic) { + this.nic = nic; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevice.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevice.java new file mode 100644 index 00000000000..7c36f2d02f5 --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevice.java @@ -0,0 +1,93 @@ +// 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; + +public class ReportedDevice { + private String comment; + private String description; + private Ips ips; + private String id; + private Mac Mac; + private String name; + private String type; + private Ref vm; + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Ips getIps() { + return ips; + } + + public void setIps(Ips ips) { + this.ips = ips; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Mac getMac() { + return Mac; + } + + public void setMac(Mac mac) { + Mac = mac; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Ref getVm() { + return vm; + } + + public void setVm(Ref vm) { + this.vm = vm; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevices.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevices.java new file mode 100644 index 00000000000..7348b0ca6fa --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/ReportedDevices.java @@ -0,0 +1,42 @@ +// 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.dataformat.xml.annotation.JacksonXmlElementWrapper; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ReportedDevices { + + @JacksonXmlElementWrapper(useWrapping = false) + private List reportedDevice; + + public ReportedDevices(final List reportedDevice) { + this.reportedDevice = reportedDevice; + } + + public List getReportedDevice() { + return reportedDevice; + } + + public void setReportedDevice(List reportedDevice) { + this.reportedDevice = reportedDevice; + } +} 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 5a21f84c4ae..c83a7536e6a 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 @@ -46,6 +46,7 @@ public final class Vm { @JsonProperty("stop_time") @JacksonXmlProperty(localName = "stop_time") public Long stopTime; // epoch millis + private Long startTime; // epoch millis public Ref template; @@ -68,6 +69,43 @@ public final class Vm { public Actions actions; // actions.link[] @JacksonXmlElementWrapper(useWrapping = false) public List link; // related resources + public EmptyElement tags; // empty + private DiskAttachments diskAttachments; + private Nics nics; + + private VmInitialization initialization; public Vm() {} + + public Long getStartTime() { + return startTime; + } + + public void setStartTime(Long startTime) { + this.startTime = startTime; + } + + public DiskAttachments getDiskAttachments() { + return diskAttachments; + } + + public void setDiskAttachments(DiskAttachments diskAttachments) { + this.diskAttachments = diskAttachments; + } + + public Nics getNics() { + return nics; + } + + public void setNics(Nics nics) { + this.nics = nics; + } + + public VmInitialization getInitialization() { + return initialization; + } + + public void setInitialization(VmInitialization initialization) { + this.initialization = initialization; + } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmAction.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmAction.java new file mode 100644 index 00000000000..9be7ab6891e --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmAction.java @@ -0,0 +1,51 @@ +// 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 com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VmAction { + private Ref job; + private Vm vm; + private String status; + + public Ref getJob() { + return job; + } + + public void setJob(Ref job) { + this.job = job; + } + + public Vm getVm() { + return vm; + } + + public void setVm(Vm vm) { + this.vm = vm; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmInitialization.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmInitialization.java new file mode 100644 index 00000000000..61982872afc --- /dev/null +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/VmInitialization.java @@ -0,0 +1,34 @@ +// 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 com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class VmInitialization { + + private String contentData; + + public String getContentData() { + return contentData; + } + + public void setContentData(String contentData) { + this.contentData = contentData; + } +} diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/VmCollectionResponse.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vms.java similarity index 86% rename from plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/VmCollectionResponse.java rename to plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vms.java index fc858f51ca0..df981129f1c 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/response/VmCollectionResponse.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Vms.java @@ -15,12 +15,10 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.veeam.api.response; +package org.apache.cloudstack.veeam.api.dto; import java.util.List; -import org.apache.cloudstack.veeam.api.dto.Vm; - import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @@ -34,14 +32,14 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "vm" }) @JacksonXmlRootElement(localName = "vms") -public final class VmCollectionResponse { +public final class Vms { @JsonProperty("vm") @JacksonXmlElementWrapper(useWrapping = false) public List vm; - public VmCollectionResponse() {} + public Vms() {} - public VmCollectionResponse(final List vm) { + public Vms(final List vm) { this.vm = vm; } } diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/Mapper.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/Mapper.java index 0d6af22599e..933e57b202a 100644 --- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/Mapper.java +++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/utils/Mapper.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.veeam.utils; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.dataformat.xml.XmlMapper; @@ -38,6 +39,7 @@ public class Mapper { private static void configure(final ObjectMapper mapper) { mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // If you ever add enums etc: // mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); // mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); 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 b247550cf14..f56a19d8471 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 @@ -42,11 +42,12 @@ + - + diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index a72498c1371..fed8de36c3d 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -204,5 +204,5 @@ public interface UserVmManager extends UserVmService { */ boolean isVMPartOfAnyCKSCluster(VMInstanceVO vm); - boolean isDummyTemplate(HypervisorType hypervisorType, Long templateId); + boolean isBlankInstanceTemplate(VirtualMachineTemplate template); } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 02a16e7a9e4..3fa6cd105c9 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -423,7 +423,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final long GiB_TO_BYTES = 1024 * 1024 * 1024; - public static final String KVM_VM_DUMMY_TEMPLATE_NAME = "kvm-vm-dummy-template"; + private static final String KVM_VM_DUMMY_TEMPLATE_NAME = "kvm-vm-dummy-template"; @Inject @@ -658,7 +658,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private boolean _instanceNameFlag; private int _scaleRetry; private Map vmIdCountMap = new ConcurrentHashMap<>(); - private static VMTemplateVO KVM_VM_DUMMY_TEMPLATE; protected static long ROOT_DEVICE_ID = 0; @@ -2505,17 +2504,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2); _vmIpFetchThreadExecutor = Executors.newFixedThreadPool(VmIpFetchThreadPoolMax.value(), new NamedThreadFactory("vmIpFetchThread")); - - KVM_VM_DUMMY_TEMPLATE = _templateDao.findByAccountAndName(Account.ACCOUNT_ID_SYSTEM, KVM_VM_DUMMY_TEMPLATE_NAME); - if (KVM_VM_DUMMY_TEMPLATE == null) { - KVM_VM_DUMMY_TEMPLATE = VMTemplateVO.createSystemIso(_templateDao.getNextInSequence(Long.class, "id"), KVM_VM_DUMMY_TEMPLATE_NAME, KVM_VM_DUMMY_TEMPLATE_NAME, true, - "", true, 64, Account.ACCOUNT_ID_SYSTEM, "", - "Dummy Template for KVM VM", false, 1); - KVM_VM_DUMMY_TEMPLATE.setState(VirtualMachineTemplate.State.Active); - KVM_VM_DUMMY_TEMPLATE.setFormat(ImageFormat.QCOW2); - KVM_VM_DUMMY_TEMPLATE = _templateDao.persist(KVM_VM_DUMMY_TEMPLATE); - } - logger.info("User VM Manager is configured."); return true; @@ -3945,7 +3933,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _accountMgr.checkAccess(owner, _diskOfferingDao.findById(diskOfferingId), zone); // If no network is specified, find system security group enabled network - if (isDummyTemplate(hypervisor, template.getId())) { + if (isBlankInstanceTemplate(template)) { logger.debug("Template is a dummy template for hypervisor {}, skipping network allocation in an advanced security group enabled zone", hypervisor); } else if (networkIdList == null || networkIdList.isEmpty()) { Network networkWithSecurityGroup = _networkModel.getNetworkWithSGWithFreeIPs(owner, zone.getId()); @@ -4060,7 +4048,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _accountMgr.checkAccess(owner, diskOffering, zone); List vpcSupportedHTypes = _vpcMgr.getSupportedVpcHypervisors(); - if (isDummyTemplate(hypervisor, template.getId())) { + if (isBlankInstanceTemplate(template)) { logger.debug("Template is a dummy template for hypervisor {}, skipping network allocation in an advanced zone", hypervisor); } else if (networkIdList == null || networkIdList.isEmpty()) { NetworkVO defaultNetwork = getDefaultNetwork(zone, owner, false); @@ -4497,7 +4485,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } - if (TemplateType.SYSTEM.equals(template.getTemplateType()) && !CKS_NODE.equals(vmType) && !SHAREDFSVM.equals(vmType) && !isDummyTemplate(hypervisorType, template.getId())) { + if (TemplateType.SYSTEM.equals(template.getTemplateType()) && !CKS_NODE.equals(vmType) && !SHAREDFSVM.equals(vmType) && !isBlankInstanceTemplate(template)) { throw new InvalidParameterValueException(String.format("Unable to use system template %s to deploy a user vm", template)); } @@ -4510,7 +4498,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (CollectionUtils.isEmpty(snapshotsOnZone)) { throw new InvalidParameterValueException("The snapshot does not exist on zone " + zone.getId()); } - } else if (!isDummyTemplate(hypervisorType, template.getId())) { + } else if (!isBlankInstanceTemplate(template)) { List listZoneTemplate = _templateZoneDao.listByZoneTemplate(zone.getId(), template.getId()); if (listZoneTemplate == null || listZoneTemplate.isEmpty()) { throw new InvalidParameterValueException("The template " + template.getId() + " is not available for use"); @@ -4625,7 +4613,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // by Agent Manager in order to configure default // gateway for the vm if (defaultNetworkNumber == 0) { - if (isDummyTemplate(hypervisorType, template.getId())) { + if (isBlankInstanceTemplate(template)) { logger.debug("Template is a dummy template for hypervisor {}, vm can be created without a default network", hypervisorType); } else { throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); @@ -4791,7 +4779,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return rootDiskSize; } else { // For baremetal, size can be 0 (zero) - Long templateSize = _templateDao.findById(template.getId()).getSize(); + Long templateSize = template.getSize(); if (templateSize != null) { return templateSize; } @@ -5347,7 +5335,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", async = true) public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException { long vmId = cmd.getEntityId(); - if (!cmd.getStartVm() || cmd.getDummy()) { + if (!cmd.getStartVm() || cmd.isBlankInstance()) { return getUserVm(vmId); } Long podId = null; @@ -6495,10 +6483,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir (!(HypervisorType.KVM.equals(template.getHypervisorType()) || HypervisorType.KVM.equals(cmd.getHypervisor())))) { throw new InvalidParameterValueException("Deploying a virtual machine with existing volume/snapshot is supported only from KVM hypervisors"); } - if (template == null && HypervisorType.KVM.equals(cmd.getHypervisor()) && cmd.getDummy()) { - template = KVM_VM_DUMMY_TEMPLATE; + if (template == null && HypervisorType.KVM.equals(cmd.getHypervisor()) && cmd.isBlankInstance()) { + template = getBlankInstanceTemplate(); logger.info("Creating launch permission for Dummy template"); - LaunchPermissionVO launchPermission = new LaunchPermissionVO(KVM_VM_DUMMY_TEMPLATE.getId(), owner.getId()); + LaunchPermissionVO launchPermission = new LaunchPermissionVO(template.getId(), owner.getId()); launchPermissionDao.persist(launchPermission); } // Make sure a valid template ID was specified @@ -6660,9 +6648,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir applyLeaseOnCreateInstance(vm, cmd.getLeaseDuration(), cmd.getLeaseExpiryAction(), svcOffering); } - if (KVM_VM_DUMMY_TEMPLATE != null && template.getId() == KVM_VM_DUMMY_TEMPLATE.getId() && cmd instanceof DeployVMCmd && ((DeployVMCmd) cmd).getDummy()) { + if (isBlankInstanceTemplate(template) && cmd instanceof DeployVMCmd && ((DeployVMCmd) cmd).isBlankInstance()) { logger.info("Revoking launch permission for Dummy template"); - launchPermissionDao.removePermissions(KVM_VM_DUMMY_TEMPLATE.getId(), Collections.singletonList(owner.getId())); + launchPermissionDao.removePermissions(template.getId(), Collections.singletonList(owner.getId())); } return vm; @@ -10101,10 +10089,23 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override - public boolean isDummyTemplate(HypervisorType hypervisorType, Long templateId) { - if (HypervisorType.KVM.equals(hypervisorType) && KVM_VM_DUMMY_TEMPLATE != null && KVM_VM_DUMMY_TEMPLATE.getId() == templateId) { - return true; + public boolean isBlankInstanceTemplate(VirtualMachineTemplate template) { + return KVM_VM_DUMMY_TEMPLATE_NAME.equals(template.getUniqueName()); + } + + VMTemplateVO getBlankInstanceTemplate() { + VMTemplateVO template = _templateDao.findByName(KVM_VM_DUMMY_TEMPLATE_NAME); + if (template != null) { + return template; } - return false; + template = VMTemplateVO.createSystemIso(_templateDao.getNextInSequence(Long.class, "id"), + KVM_VM_DUMMY_TEMPLATE_NAME, KVM_VM_DUMMY_TEMPLATE_NAME, true, + "", true, 64, Account.ACCOUNT_ID_SYSTEM, "", + "Dummy Template for KVM VM", false, 1); + template.setState(VirtualMachineTemplate.State.Active); + template.setFormat(ImageFormat.QCOW2); + template = _templateDao.persist(template); +// _templateDao.remove(template.getId()); + return template; } } diff --git a/server/src/main/java/org/apache/cloudstack/backup/IncrementalBackupServiceImpl.java b/server/src/main/java/org/apache/cloudstack/backup/IncrementalBackupServiceImpl.java index e5894430fbf..ca0de822769 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/IncrementalBackupServiceImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/IncrementalBackupServiceImpl.java @@ -478,6 +478,16 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme return imageTransferDao.findById(imageTransfer.getId()); } + @Override + public boolean cancelImageTransfer(long imageTransferId) { + ImageTransferVO imageTransfer = imageTransferDao.findById(imageTransferId); + if (imageTransfer == null) { + throw new CloudRuntimeException("Image transfer not found: " + imageTransferId); + } + // ToDo: Implement cancel logic + return true; + } + private void finalizeDownloadImageTransfer(ImageTransferVO imageTransfer) { String transferId = imageTransfer.getUuid(); @@ -552,8 +562,11 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme @Override public boolean finalizeImageTransfer(FinalizeImageTransferCmd cmd) { - Long imageTransferId = cmd.getImageTransferId(); + return finalizeImageTransfer(cmd.getImageTransferId()); + } + @Override + public boolean finalizeImageTransfer(final long imageTransferId) { ImageTransferVO imageTransfer = imageTransferDao.findById(imageTransferId); if (imageTransfer == null) { throw new CloudRuntimeException("Image transfer not found: " + imageTransferId); @@ -566,6 +579,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme } imageTransfer.setPhase(ImageTransferVO.Phase.finished); imageTransferDao.update(imageTransfer.getId(), imageTransfer); +// ToDo: check this +// imageTransferDao.remove(imageTransfer.getId()); return true; } @@ -656,7 +671,8 @@ public class IncrementalBackupServiceImpl extends ManagerBase implements Increme response.setBackupId(backup.getUuid()); } Long volumeId = imageTransferVO.getDiskId(); - Volume volume = volumeDao.findById(volumeId); + // ToDo: fix volume deletion leaving orphan image transfer record + Volume volume = volumeDao.findByIdIncludingRemoved(volumeId); response.setDiskId(volume.getUuid()); response.setTransferUrl(imageTransferVO.getTransferUrl()); response.setPhase(imageTransferVO.getPhase().toString());