From e63cde4c526040ceae6e5cb98a36bdcb15a47ab2 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 11 Mar 2024 13:07:29 +0530 Subject: [PATCH 01/34] Create/Export OVA file of the VM on external vCenter host, to temporary conversion location (NFS) --- .../com/cloud/hypervisor/HypervisorGuru.java | 3 +- .../com/cloud/hypervisor/guru/VMwareGuru.java | 48 +++++++++++++++++-- .../cloud/hypervisor/HypervisorGuruBase.java | 3 +- .../vm/UnmanagedVMsManagerImpl.java | 30 ++++++------ .../vm/UnmanagedVMsManagerImplTest.java | 4 +- .../hypervisor/vmware/mo/DatastoreMO.java | 14 ++++++ 6 files changed, 81 insertions(+), 21 deletions(-) diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 3c7dbac6442c..39667be48022 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -23,6 +23,7 @@ import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -115,7 +116,7 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu * @return a reference to the cloned VM */ UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, - Map params); + Map params, DataStoreTO convertLocation); /** * Removes a VM created as a clone of a VM on an external host diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index c6a4d68bb9bc..7ede93bd60d5 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -18,6 +18,7 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize; +import java.io.File; import java.net.URI; import java.util.ArrayList; import java.util.Date; @@ -28,10 +29,12 @@ import javax.inject.Inject; +import com.cloud.agent.api.to.NfsTO; import com.cloud.hypervisor.vmware.mo.DatastoreMO; import com.cloud.hypervisor.vmware.mo.HostMO; import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareHelper; +import com.cloud.utils.script.Script; import com.cloud.vm.VmDetailConstants; import com.vmware.vim25.VirtualMachinePowerState; import org.apache.cloudstack.acl.ControlledEntity; @@ -42,12 +45,14 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.storage.NfsMountManager; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.DownloadCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo; @@ -195,6 +200,7 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co @Inject DiskOfferingDao diskOfferingDao; @Inject PhysicalNetworkDao physicalNetworkDao; @Inject StoragePoolHostDao storagePoolHostDao; + @Inject NfsMountManager mountManager; protected VMwareGuru() { super(); @@ -1339,7 +1345,7 @@ private void relocateClonedVMToSourceHost(VirtualMachineMO clonedVM, HostMO sour } private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO vmMo, - DatacenterMO dataCenterMO) throws Exception { + DatacenterMO dataCenterMO, DataStoreTO convertLocation) throws Exception { HostMO sourceHost = vmMo.getRunningHost(); String cloneName = UUID.randomUUID().toString(); DatastoreMO datastoreMO = vmMo.getAllDatastores().get(0); //pick the first datastore @@ -1351,13 +1357,49 @@ private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO s_logger.error(err); throw new CloudRuntimeException(err); } + + String datastorePath = datastoreMO.getDatastorePathOnHost(sourceHost.getMor().getValue()); + s_logger.info("Host datastore path: " + datastorePath); + + String dataStoreUrl = null; + if (convertLocation instanceof NfsTO) { + NfsTO nfsStore = (NfsTO) convertLocation; + dataStoreUrl = nfsStore.getUrl(); + } else if (convertLocation instanceof PrimaryDataStoreTO) { + PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) convertLocation; + if (primaryDataStoreTO.getPoolType().equals(Storage.StoragePoolType.NetworkFilesystem)) { + String psHost = primaryDataStoreTO.getHost(); + String psPath = primaryDataStoreTO.getPath(); + dataStoreUrl = "nfs://" + psHost + File.separator + psPath; + } + } + + String mountPoint = mountManager.getMountPoint(dataStoreUrl, null); + s_logger.info("Convert storage location - dataStoreUrl: " + dataStoreUrl + ", mountPoint: " + mountPoint); + + String vmOvaName = UUID.randomUUID().toString(); + String vmOvaCreationPath = mountPoint + "/" + vmOvaName; + synchronized (vmOvaCreationPath.intern()) { + Script command = new Script("mkdir", s_logger); + command.add("-p"); + command.add(vmOvaCreationPath); + String cmdResult = command.execute(); + if (cmdResult != null) { + String msg = "Unable to prepare VM's OVA directory: " + vmOvaCreationPath + ", storage: " + dataStoreUrl + ", error msg: " + cmdResult; + s_logger.error(msg); + throw new Exception(msg); + } + } + s_logger.info("Creating OVA - " + vmOvaName); + clonedVM.exportVm(vmOvaCreationPath, vmOvaName, true, true); + s_logger.info("Created OVA - " + vmOvaName); relocateClonedVMToSourceHost(clonedVM, sourceHost); return clonedVM; } @Override public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, - Map params) { + Map params, DataStoreTO convertLocation) { s_logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp)); String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); @@ -1380,7 +1422,7 @@ public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmNa vmName)); } - VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO); + VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO, convertLocation); s_logger.debug(String.format("VM %s cloned successfully", vmName)); UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM); setNicsFromSourceVM(clonedInstance, vmMo); diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index 74d9130cc04b..aa07c52d1dfa 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -35,6 +35,7 @@ import org.apache.log4j.Logger; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; @@ -377,7 +378,7 @@ public ConfigKey[] getConfigKeys() { } @Override - public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { + public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation) { s_logger.error("Unsupported operation: cannot clone external VM"); return null; } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 2ff3bf2ca217..87ed9e710c76 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1531,13 +1531,13 @@ private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cl return userVm; } - private UnmanagedInstanceTO cloneSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM) { + private UnmanagedInstanceTO cloneSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM, DataStoreTO convertLocation) { HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); Map params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM); - return vmwareGuru.cloneHypervisorVMOutOfBand(sourceHostName, sourceVM, params); + return vmwareGuru.cloneHypervisorVMOutOfBand(sourceHostName, sourceVM, params, convertLocation); } protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster destinationCluster, VMTemplateVO template, @@ -1581,19 +1581,21 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster UnmanagedInstanceTO clonedInstance = null; try { String instanceName = getGeneratedInstanceName(owner); + DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, null); clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, - clusterName, sourceHostName, sourceVM); - checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); - UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, - sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId); - sanitizeConvertedInstance(convertedInstance, clonedInstance); - UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, - template, displayName, hostName, caller, owner, userId, - serviceOffering, dataDiskOfferingMap, - nicNetworkMap, nicIpAddressMap, - details, false, forced, false); - LOGGER.debug(String.format("VM %s imported successfully", sourceVM)); - return userVm; + clusterName, sourceHostName, sourceVM, temporaryConvertLocation); + return null; +// checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); +// UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, +// sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId); +// sanitizeConvertedInstance(convertedInstance, clonedInstance); +// UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, +// template, displayName, hostName, caller, owner, userId, +// serviceOffering, dataDiskOfferingMap, +// nicNetworkMap, nicIpAddressMap, +// details, false, forced, false); +// LOGGER.debug(String.format("VM %s imported successfully", sourceVM)); +// return userVm; } catch (CloudRuntimeException e) { LOGGER.error(String.format("Error importing VM: %s", e.getMessage()), e); ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT, diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 92131e4f75c9..a68e6d29889d 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -614,7 +614,7 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, HypervisorGuru vmwareGuru = mock(HypervisorGuru.class); when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware)).thenReturn(vmwareGuru); - when(vmwareGuru.cloneHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(instance); + when(vmwareGuru.cloneHypervisorVMOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class))).thenReturn(instance); when(vmwareGuru.removeClonedHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(true); HostVO convertHost = mock(HostVO.class); @@ -688,7 +688,7 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { unmanagedVMsManager.importVm(importVmCmd); - verify(vmwareGuru).cloneHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap()); + verify(vmwareGuru).cloneHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class)); verify(vmwareGuru).removeClonedHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap()); } } diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java index 7e9021ac9a41..c9e22922b237 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java @@ -467,4 +467,18 @@ public String getDatastoreType() throws Exception { DatastoreSummary summary = _context.getVimClient().getDynamicProperty(getMor(), "summary"); return summary.getType() == null ? "" : summary.getType(); } + + public String getDatastorePathOnHost(String hostValue) throws Exception { + String datastorePath = null; + List hostMounts = getHostMounts(); + for (DatastoreHostMount hostMount : hostMounts) { + String hostMountValue = hostMount.getKey().getValue(); + if (hostMountValue.equalsIgnoreCase(hostValue)) { + HostMountInfo mountInfo = hostMount.getMountInfo(); + datastorePath = mountInfo.getPath(); + break; + } + } + return datastorePath; + } } From 01d8cad0cb8d794e56456fd2b083f7247ed05c0a Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 11 Mar 2024 13:56:43 +0530 Subject: [PATCH 02/34] Fixed ova issue on untar/extract ovf from ova file "tar -xf" cmd on ova fails with "ovf: Not found in archive" while extracting ovf file --- .../java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 9b520cebc087..71b8737af252 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1892,7 +1892,9 @@ public void action(Long param) { command.add("-cf", exportName + ".ova"); command.add(exportName + ".ovf"); // OVF file should be the first file in OVA archive for (String name : fileNames) { - command.add((new File(name).getName())); + if (!name.endsWith(".ovf")) { + command.add((new File(name).getName())); + } } s_logger.info("Package OVA with command: " + command.toString()); From 42287cdd8aa0dd404ca573fe3a5d837c77ba3f98 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Fri, 15 Mar 2024 15:32:44 +0530 Subject: [PATCH 03/34] Updated VMware to KVM instance migration using OVA --- .../com/cloud/hypervisor/HypervisorGuru.java | 8 +- .../agent/api/ConvertInstanceCommand.java | 8 +- .../LibvirtConvertInstanceCommandWrapper.java | 43 +++++- .../com/cloud/hypervisor/guru/VMwareGuru.java | 126 ++++++++++++------ .../cloud/hypervisor/HypervisorGuruBase.java | 4 +- .../vm/UnmanagedVMsManagerImpl.java | 55 ++++---- .../vm/UnmanagedVMsManagerImplTest.java | 8 +- 7 files changed, 172 insertions(+), 80 deletions(-) diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 39667be48022..481ed68378a9 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -115,8 +115,8 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu * @param params hypervisor specific additional parameters * @return a reference to the cloned VM */ - UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO convertLocation); + Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, + Map params, DataStoreTO convertLocation); /** * Removes a VM created as a clone of a VM on an external host @@ -125,6 +125,6 @@ UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, * @param params hypervisor specific additional parameters * @return true if the operation succeeds, false if not */ - boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, - Map params); + boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, + Map params, DataStoreTO convertLocation, String templateOnConvertLocation); } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java index 63234b044807..42a0c901acc1 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -28,16 +28,18 @@ public class ConvertInstanceCommand extends Command { private Hypervisor.HypervisorType destinationHypervisorType; private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; + private String templateOnConversionLocation; public ConvertInstanceCommand() { } public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, - List destinationStoragePools, DataStoreTO conversionTemporaryLocation) { + List destinationStoragePools, DataStoreTO conversionTemporaryLocation, String templateOnConversionLocation) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; + this.templateOnConversionLocation = templateOnConversionLocation; } public RemoteInstanceTO getSourceInstance() { @@ -56,6 +58,10 @@ public DataStoreTO getConversionTemporaryLocation() { return conversionTemporaryLocation; } + public String getTemplateOnConversionLocation() { + return templateOnConversionLocation; + } + @Override public boolean executeInSequence() { return false; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java index a26311891c87..c0e364a6a157 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java @@ -75,6 +75,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve Hypervisor.HypervisorType destinationHypervisorType = cmd.getDestinationHypervisorType(); List destinationStoragePools = cmd.getDestinationStoragePools(); DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation(); + String ovaTemplateOnConversionLocation = cmd.getTemplateOnConversionLocation(); long timeout = (long) cmd.getWait() * 1000; if (!isInstanceConversionSupportedOnHost()) { @@ -101,14 +102,17 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve final String temporaryConvertUuid = UUID.randomUUID().toString(); final String temporaryPasswordFilePath = createTemporaryPasswordFileAndRetrievePath(sourceInstance); final String temporaryConvertPath = temporaryStoragePool.getLocalPath(); + final String sourceOVAFile = String.format("%s/%s/%s.ova", temporaryConvertPath, ovaTemplateOnConversionLocation, ovaTemplateOnConversionLocation); boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled(); try { - boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath, - temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); + boolean result = performInstanceConversionUsingOVA(sourceOVAFile, temporaryConvertPath, temporaryConvertUuid, + timeout, verboseModeEnabled); +// boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath, +// temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); if (!result) { - String err = String.format("The virt-v2v conversion of the instance %s failed. " + - "Please check the agent logs for the virt-v2v output", sourceInstanceName); + String err = String.format("The virt-v2v conversion for the ova %s failed. " + + "Please check the agent logs for the virt-v2v output", sourceOVAFile); s_logger.error(err); return new ConvertInstanceAnswer(cmd, false, err); } @@ -342,6 +346,37 @@ protected boolean performInstanceConversion(String convertInstanceUrl, String so return exitValue == 0; } + protected boolean performInstanceConversionUsingOVA(String sourceOVAFile, + String temporaryConvertFolder, + String temporaryConvertUuid, + long timeout, boolean verboseModeEnabled) { + //virt-v2v + // -i ova + // /mnt/2ba00d9f-8cb0-3f8a-89ec-7910b043eac0/18a88711-0db7-4b25-b5c8-816cd28d61c5/testova2-18a88711-0db7-4b25-b5c8-816cd28d61c5.ova + // -o local + // -os /tmp/testvms/test3 + // -of qcow2 + // -on test3vm + // -v + Script script = new Script("virt-v2v", timeout, s_logger); + script.add("--root", "first"); + script.add("-i", "ova"); + script.add(sourceOVAFile); + script.add("-o", "local"); + script.add("-os", temporaryConvertFolder); + script.add("-of", "qcow2"); + script.add("-on", temporaryConvertUuid); + if (verboseModeEnabled) { + script.add("-v"); + } + + String logPrefix = String.format("virt-v2v ova source: %s progress", sourceOVAFile); + OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix); + script.execute(outputLogger); + int exitValue = script.getExitValue(); + return exitValue == 0; + } + private String createTemporaryPasswordFileAndRetrievePath(RemoteInstanceTO sourceInstance) { String password = null; if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) { diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index 7ede93bd60d5..a0cea8bec29a 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -1345,7 +1345,7 @@ private void relocateClonedVMToSourceHost(VirtualMachineMO clonedVM, HostMO sour } private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO vmMo, - DatacenterMO dataCenterMO, DataStoreTO convertLocation) throws Exception { + DatacenterMO dataCenterMO) throws Exception { HostMO sourceHost = vmMo.getRunningHost(); String cloneName = UUID.randomUUID().toString(); DatastoreMO datastoreMO = vmMo.getAllDatastores().get(0); //pick the first datastore @@ -1361,45 +1361,23 @@ private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO String datastorePath = datastoreMO.getDatastorePathOnHost(sourceHost.getMor().getValue()); s_logger.info("Host datastore path: " + datastorePath); - String dataStoreUrl = null; - if (convertLocation instanceof NfsTO) { - NfsTO nfsStore = (NfsTO) convertLocation; - dataStoreUrl = nfsStore.getUrl(); - } else if (convertLocation instanceof PrimaryDataStoreTO) { - PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) convertLocation; - if (primaryDataStoreTO.getPoolType().equals(Storage.StoragePoolType.NetworkFilesystem)) { - String psHost = primaryDataStoreTO.getHost(); - String psPath = primaryDataStoreTO.getPath(); - dataStoreUrl = "nfs://" + psHost + File.separator + psPath; - } - } - - String mountPoint = mountManager.getMountPoint(dataStoreUrl, null); - s_logger.info("Convert storage location - dataStoreUrl: " + dataStoreUrl + ", mountPoint: " + mountPoint); - - String vmOvaName = UUID.randomUUID().toString(); - String vmOvaCreationPath = mountPoint + "/" + vmOvaName; - synchronized (vmOvaCreationPath.intern()) { - Script command = new Script("mkdir", s_logger); - command.add("-p"); - command.add(vmOvaCreationPath); - String cmdResult = command.execute(); - if (cmdResult != null) { - String msg = "Unable to prepare VM's OVA directory: " + vmOvaCreationPath + ", storage: " + dataStoreUrl + ", error msg: " + cmdResult; - s_logger.error(msg); - throw new Exception(msg); - } - } - s_logger.info("Creating OVA - " + vmOvaName); - clonedVM.exportVm(vmOvaCreationPath, vmOvaName, true, true); - s_logger.info("Created OVA - " + vmOvaName); relocateClonedVMToSourceHost(clonedVM, sourceHost); return clonedVM; } + private String createOVATemplateFileOfVM(VirtualMachineMO vmMO, DataStoreTO convertLocation) throws Exception { + String dataStoreUrl = getDataStoreUrlForConversion(convertLocation); + String vmOvaName = UUID.randomUUID().toString(); + String vmOvaCreationPath = createDirOnStorage(vmOvaName, dataStoreUrl, null); + s_logger.info("Creating OVA - " + vmOvaName + " at " + vmOvaCreationPath); + vmMO.exportVm(vmOvaCreationPath, vmOvaName, true, true); + s_logger.info("Created OVA - " + vmOvaName + " at " + vmOvaCreationPath); + return vmOvaName; + } + @Override - public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO convertLocation) { + public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, + Map params, DataStoreTO convertLocation) { s_logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp)); String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); @@ -1422,12 +1400,13 @@ public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmNa vmName)); } - VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO, convertLocation); + VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO); s_logger.debug(String.format("VM %s cloned successfully", vmName)); + String ovaTemplateName = createOVATemplateFileOfVM(clonedVM, convertLocation); UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM); setNicsFromSourceVM(clonedInstance, vmMo); clonedInstance.setCloneSourcePowerState(sourceVmPowerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn : UnmanagedInstanceTO.PowerState.PowerOff); - return clonedInstance; + return new Pair<> (clonedInstance, ovaTemplateName); } catch (Exception e) { String err = String.format("Error cloning VM: %s from external vCenter %s: %s", vmName, vcenter, e.getMessage()); s_logger.error(err, e); @@ -1452,7 +1431,7 @@ private void setNicsFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMach } @Override - public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { + public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation, String templateOnConvertLocation) { s_logger.debug(String.format("Removing VM %s on external vCenter %s", vmName, hostIp)); String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); @@ -1468,11 +1447,80 @@ public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, M s_logger.error(err); return false; } - return vmMo.destroy(); + if (!vmMo.destroy()) { + return false; + } + String dataStoreUrl = getDataStoreUrlForConversion(convertLocation); + deleteDirOnStorage(templateOnConvertLocation, dataStoreUrl, null); + return true; } catch (Exception e) { String err = String.format("Error destroying external VM %s: %s", vmName, e.getMessage()); s_logger.error(err, e); return false; } } + + private String getDataStoreUrlForConversion(DataStoreTO convertLocation) { + String dataStoreUrl = null; + if (convertLocation instanceof NfsTO) { + NfsTO nfsStore = (NfsTO) convertLocation; + dataStoreUrl = nfsStore.getUrl(); + } else if (convertLocation instanceof PrimaryDataStoreTO) { + PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) convertLocation; + if (primaryDataStoreTO.getPoolType().equals(Storage.StoragePoolType.NetworkFilesystem)) { + String psHost = primaryDataStoreTO.getHost(); + String psPath = primaryDataStoreTO.getPath(); + dataStoreUrl = "nfs://" + psHost + File.separator + psPath; + } + } + + if (dataStoreUrl == null) { + throw new CloudRuntimeException("Only NFS storages are supported for conversion"); + } + + return dataStoreUrl; + } + + private String createDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception { + String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion); + s_logger.info("Create dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName); + String dirMountPath = mountPoint + File.separator + dirName; + createDir(dirMountPath); + return dirMountPath; + } + + private void createDir(String dirName) throws Exception { + synchronized (dirName.intern()) { + Script command = new Script("mkdir", s_logger); + command.add("-p"); + command.add(dirName); + String cmdResult = command.execute(); + if (cmdResult != null) { + String msg = "Unable to create directory: " + dirName + ", error msg: " + cmdResult; + s_logger.error(msg); + throw new Exception(msg); + } + } + } + + private void deleteDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception { + String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion); + s_logger.info("Delete dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName); + String dirMountPath = mountPoint + File.separator + dirName; + deleteDir(dirMountPath); + } + + private void deleteDir(String dirName) throws Exception { + synchronized (dirName.intern()) { + Script command = new Script("rm", s_logger); + command.add("-rf"); + command.add(dirName); + String cmdResult = command.execute(); + if (cmdResult != null) { + String msg = "Unable to delete directory: " + dirName + ", error msg: " + cmdResult; + s_logger.error(msg); + throw new Exception(msg); + } + } + } } diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index aa07c52d1dfa..9a2bf9d7dcf8 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -378,13 +378,13 @@ public ConfigKey[] getConfigKeys() { } @Override - public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation) { + public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation) { s_logger.error("Unsupported operation: cannot clone external VM"); return null; } @Override - public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { + public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation, String templateOnConvertLocation) { s_logger.error("Unsupported operation: cannot remove external VM"); return false; } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 87ed9e710c76..3751e9b33490 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1531,13 +1531,13 @@ private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cl return userVm; } - private UnmanagedInstanceTO cloneSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM, DataStoreTO convertLocation) { + private Pair cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM, DataStoreTO convertLocation) { HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); Map params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM); - return vmwareGuru.cloneHypervisorVMOutOfBand(sourceHostName, sourceVM, params, convertLocation); + return vmwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(sourceHostName, sourceVM, params, convertLocation); } protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster destinationCluster, VMTemplateVO template, @@ -1579,30 +1579,33 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster } UnmanagedInstanceTO clonedInstance = null; + DataStoreTO temporaryConvertLocation = null; + String ovaTemplateOnConvertLocation = null; try { - String instanceName = getGeneratedInstanceName(owner); - DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, null); - clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, + temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, null); + Pair clonedInstanceAndOvaTemplate = cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM, temporaryConvertLocation); - return null; -// checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); -// UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, -// sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId); -// sanitizeConvertedInstance(convertedInstance, clonedInstance); -// UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, -// template, displayName, hostName, caller, owner, userId, -// serviceOffering, dataDiskOfferingMap, -// nicNetworkMap, nicIpAddressMap, -// details, false, forced, false); -// LOGGER.debug(String.format("VM %s imported successfully", sourceVM)); -// return userVm; + clonedInstance = clonedInstanceAndOvaTemplate.first(); + ovaTemplateOnConvertLocation = clonedInstanceAndOvaTemplate.second(); + String instanceName = getGeneratedInstanceName(owner); + checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); + UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, + sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, temporaryConvertLocation, ovaTemplateOnConvertLocation); + sanitizeConvertedInstance(convertedInstance, clonedInstance); + UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, + template, displayName, hostName, caller, owner, userId, + serviceOffering, dataDiskOfferingMap, + nicNetworkMap, nicIpAddressMap, + details, false, forced, false); + LOGGER.debug(String.format("VM %s imported successfully", sourceVM)); + return userVm; } catch (CloudRuntimeException e) { LOGGER.error(String.format("Error importing VM: %s", e.getMessage()), e); ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT, cmd.getEventDescription(), null, null, 0); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } finally { - removeClonedInstance(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM); + removeClonedInstanceAndTemplateFile(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM, temporaryConvertLocation, ovaTemplateOnConvertLocation); } } @@ -1687,13 +1690,13 @@ private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, Un } } - private void removeClonedInstance(String vcenter, String datacenterName, - String username, String password, - String sourceHostName, String clonedInstanceName, - String sourceVM) { + private void removeClonedInstanceAndTemplateFile(String vcenter, String datacenterName, + String username, String password, + String sourceHostName, String clonedInstanceName, + String sourceVM, DataStoreTO convertLocation, String ovaTemplateOnConvertLocation) { HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); Map params = createParamsForRemoveClonedInstance(vcenter, datacenterName, username, password, sourceVM); - boolean result = vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, clonedInstanceName, params); + boolean result = vmwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(sourceHostName, clonedInstanceName, params, convertLocation, ovaTemplateOnConvertLocation); if (!result) { String msg = String.format("Could not properly remove the cloned instance %s from VMware datacenter %s:%s", clonedInstanceName, vcenter, datacenterName); @@ -1753,7 +1756,7 @@ private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationClust private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName, String username, String password, String hostName, UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, - Long convertInstanceHostId, Long convertStoragePoolId) { + Long convertInstanceHostId, DataStoreTO temporaryConvertLocation, String ovaTemplateOnConvertLocation) { HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId); String vmName = clonedInstance.getName(); LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" + @@ -1761,10 +1764,10 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String da RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(hostName, vmName, vcenter, datacenterName, clusterName, username, password); - DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost); +// DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost); List destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks()); ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO, - Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation); + Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovaTemplateOnConvertLocation); int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60; cmd.setWait(timeoutSeconds); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index a68e6d29889d..0216277698a3 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -614,8 +614,8 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, HypervisorGuru vmwareGuru = mock(HypervisorGuru.class); when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware)).thenReturn(vmwareGuru); - when(vmwareGuru.cloneHypervisorVMOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class))).thenReturn(instance); - when(vmwareGuru.removeClonedHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(true); + when(vmwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class))).thenReturn(new Pair<>(instance, "")); + when(vmwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class), anyString())).thenReturn(true); HostVO convertHost = mock(HostVO.class); long convertHostId = 1L; @@ -688,8 +688,8 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { unmanagedVMsManager.importVm(importVmCmd); - verify(vmwareGuru).cloneHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class)); - verify(vmwareGuru).removeClonedHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap()); + verify(vmwareGuru).cloneHypervisorVMAndCreateTemplateFileOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class)); + verify(vmwareGuru).removeClonedHypervisorVMAandTemplateFileOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class), anyString()); } } From ca580f5d1c5e38675a9573f53d062b432d9be311 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 18 Mar 2024 13:17:21 +0530 Subject: [PATCH 04/34] Refactoring and cleanup --- .../cloud/agent/api/to/RemoteInstanceTO.java | 49 +----------- .../com/cloud/hypervisor/HypervisorGuru.java | 9 ++- .../agent/api/ConvertInstanceCommand.java | 10 +-- .../LibvirtConvertInstanceCommandWrapper.java | 74 +------------------ ...virtConvertInstanceCommandWrapperTest.java | 28 +++---- .../com/cloud/hypervisor/guru/VMwareGuru.java | 73 ++++++++++-------- .../cloud/hypervisor/HypervisorGuruBase.java | 8 +- .../vm/UnmanagedVMsManagerImpl.java | 16 ++-- 8 files changed, 82 insertions(+), 185 deletions(-) diff --git a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java index 6e7aa8b21e28..c24e5f73df60 100644 --- a/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/RemoteInstanceTO.java @@ -18,40 +18,21 @@ */ package com.cloud.agent.api.to; -import com.cloud.agent.api.LogLevel; -import com.cloud.hypervisor.Hypervisor; - import java.io.Serializable; +import com.cloud.hypervisor.Hypervisor; + public class RemoteInstanceTO implements Serializable { private Hypervisor.HypervisorType hypervisorType; - private String hostName; private String instanceName; - // Vmware Remote Instances parameters - // TODO: cloud.agent.transport.Request#getCommands() cannot handle gsoc decode for polymorphic classes - private String vcenterUsername; - @LogLevel(LogLevel.Log4jLevel.Off) - private String vcenterPassword; - private String vcenterHost; - private String datacenterName; - private String clusterName; - public RemoteInstanceTO() { } - public RemoteInstanceTO(String hostName, String instanceName, String vcenterHost, - String datacenterName, String clusterName, - String vcenterUsername, String vcenterPassword) { + public RemoteInstanceTO(String instanceName) { this.hypervisorType = Hypervisor.HypervisorType.VMware; - this.hostName = hostName; this.instanceName = instanceName; - this.vcenterHost = vcenterHost; - this.datacenterName = datacenterName; - this.clusterName = clusterName; - this.vcenterUsername = vcenterUsername; - this.vcenterPassword = vcenterPassword; } public Hypervisor.HypervisorType getHypervisorType() { @@ -61,28 +42,4 @@ public Hypervisor.HypervisorType getHypervisorType() { public String getInstanceName() { return this.instanceName; } - - public String getHostName() { - return this.hostName; - } - - public String getVcenterUsername() { - return vcenterUsername; - } - - public String getVcenterPassword() { - return vcenterPassword; - } - - public String getVcenterHost() { - return vcenterHost; - } - - public String getDatacenterName() { - return datacenterName; - } - - public String getClusterName() { - return clusterName; - } } diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 481ed68378a9..2a8080d894fe 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -113,18 +113,21 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu * @param hostIp VM's source host IP * @param vmName name of the source VM to clone from * @param params hypervisor specific additional parameters - * @return a reference to the cloned VM + * @param templateLocation datastore to create the template file + * @return a reference to the cloned VM and created template dir/name */ Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO convertLocation); + Map params, DataStoreTO templateLocation); /** * Removes a VM created as a clone of a VM on an external host * @param hostIp VM's source host IP * @param vmName name of the VM to remove * @param params hypervisor specific additional parameters + * @param templateLocation datastore to remove the template file + * @param templateDirAndName the template dir to remove from datastore * @return true if the operation succeeds, false if not */ boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO convertLocation, String templateOnConvertLocation); + Map params, DataStoreTO templateLocation, String templateDirAndName); } diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java index 42a0c901acc1..48db9af0b11f 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -28,18 +28,18 @@ public class ConvertInstanceCommand extends Command { private Hypervisor.HypervisorType destinationHypervisorType; private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; - private String templateOnConversionLocation; + private String templateDirAndNameOnConversionLocation; public ConvertInstanceCommand() { } public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, - List destinationStoragePools, DataStoreTO conversionTemporaryLocation, String templateOnConversionLocation) { + List destinationStoragePools, DataStoreTO conversionTemporaryLocation, String templateDirAndNameOnConversionLocation) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; - this.templateOnConversionLocation = templateOnConversionLocation; + this.templateDirAndNameOnConversionLocation = templateDirAndNameOnConversionLocation; } public RemoteInstanceTO getSourceInstance() { @@ -58,8 +58,8 @@ public DataStoreTO getConversionTemporaryLocation() { return conversionTemporaryLocation; } - public String getTemplateOnConversionLocation() { - return templateOnConversionLocation; + public String getTemplateDirAndNameOnConversionLocation() { + return templateDirAndNameOnConversionLocation; } @Override diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java index c0e364a6a157..706e4ae8e84f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java @@ -75,7 +75,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve Hypervisor.HypervisorType destinationHypervisorType = cmd.getDestinationHypervisorType(); List destinationStoragePools = cmd.getDestinationStoragePools(); DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation(); - String ovaTemplateOnConversionLocation = cmd.getTemplateOnConversionLocation(); + String ovaTemplateDirAndNameOnConversionLocation = cmd.getTemplateDirAndNameOnConversionLocation(); long timeout = (long) cmd.getWait() * 1000; if (!isInstanceConversionSupportedOnHost()) { @@ -98,18 +98,14 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve s_logger.info(String.format("Attempting to convert the instance %s from %s to KVM", sourceInstanceName, sourceHypervisorType)); - final String convertInstanceUrl = getConvertInstanceUrl(sourceInstance); final String temporaryConvertUuid = UUID.randomUUID().toString(); - final String temporaryPasswordFilePath = createTemporaryPasswordFileAndRetrievePath(sourceInstance); final String temporaryConvertPath = temporaryStoragePool.getLocalPath(); - final String sourceOVAFile = String.format("%s/%s/%s.ova", temporaryConvertPath, ovaTemplateOnConversionLocation, ovaTemplateOnConversionLocation); + final String sourceOVAFile = String.format("%s/%s/%s.ova", temporaryConvertPath, ovaTemplateDirAndNameOnConversionLocation, ovaTemplateDirAndNameOnConversionLocation); boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled(); try { boolean result = performInstanceConversionUsingOVA(sourceOVAFile, temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); -// boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath, -// temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); if (!result) { String err = String.format("The virt-v2v conversion for the ova %s failed. " + "Please check the agent logs for the virt-v2v output", sourceOVAFile); @@ -137,8 +133,6 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve s_logger.error(error, e); return new ConvertInstanceAnswer(cmd, false, error); } finally { - s_logger.debug("Cleaning up instance conversion temporary password file"); - Script.runSimpleBashScript(String.format("rm -rf %s", temporaryPasswordFilePath)); if (conversionTemporaryLocation instanceof NfsTO) { s_logger.debug("Cleaning up secondary storage temporary location"); storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid()); @@ -321,43 +315,10 @@ protected Pair getNfsStoragePoolHostAndPath(KVMStoragePool stora return new Pair<>(sourceHostIp, sourcePath); } - protected boolean performInstanceConversion(String convertInstanceUrl, String sourceInstanceName, - String temporaryPasswordFilePath, - String temporaryConvertFolder, - String temporaryConvertUuid, - long timeout, boolean verboseModeEnabled) { - Script script = new Script("virt-v2v", timeout, s_logger); - script.add("--root", "first"); - script.add("-ic", convertInstanceUrl); - script.add(sourceInstanceName); - script.add("--password-file", temporaryPasswordFilePath); - script.add("-o", "local"); - script.add("-os", temporaryConvertFolder); - script.add("-of", "qcow2"); - script.add("-on", temporaryConvertUuid); - if (verboseModeEnabled) { - script.add("-v"); - } - - String logPrefix = String.format("virt-v2v source: %s %s progress", convertInstanceUrl, sourceInstanceName); - OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix); - script.execute(outputLogger); - int exitValue = script.getExitValue(); - return exitValue == 0; - } - protected boolean performInstanceConversionUsingOVA(String sourceOVAFile, String temporaryConvertFolder, String temporaryConvertUuid, long timeout, boolean verboseModeEnabled) { - //virt-v2v - // -i ova - // /mnt/2ba00d9f-8cb0-3f8a-89ec-7910b043eac0/18a88711-0db7-4b25-b5c8-816cd28d61c5/testova2-18a88711-0db7-4b25-b5c8-816cd28d61c5.ova - // -o local - // -os /tmp/testvms/test3 - // -of qcow2 - // -on test3vm - // -v Script script = new Script("virt-v2v", timeout, s_logger); script.add("--root", "first"); script.add("-i", "ova"); @@ -377,37 +338,6 @@ protected boolean performInstanceConversionUsingOVA(String sourceOVAFile, return exitValue == 0; } - private String createTemporaryPasswordFileAndRetrievePath(RemoteInstanceTO sourceInstance) { - String password = null; - if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) { - password = sourceInstance.getVcenterPassword(); - } - String passwordFile = String.format("/tmp/vmw-%s", UUID.randomUUID()); - String msg = String.format("Creating a temporary password file for VMware instance %s conversion on: %s", sourceInstance.getInstanceName(), passwordFile); - s_logger.debug(msg); - Script.runSimpleBashScriptForExitValueAvoidLogging(String.format("echo \"%s\" > %s", password, passwordFile)); - return passwordFile; - } - - private String getConvertInstanceUrl(RemoteInstanceTO sourceInstance) { - String url = null; - if (sourceInstance.getHypervisorType() == Hypervisor.HypervisorType.VMware) { - url = getConvertInstanceUrlFromVmware(sourceInstance); - } - return url; - } - - private String getConvertInstanceUrlFromVmware(RemoteInstanceTO vmwareInstance) { - String vcenter = vmwareInstance.getVcenterHost(); - String datacenter = vmwareInstance.getDatacenterName(); - String username = vmwareInstance.getVcenterUsername(); - String host = vmwareInstance.getHostName(); - String cluster = vmwareInstance.getClusterName(); - - String encodedUsername = encodeUsername(username); - return String.format("vpx://%s@%s/%s/%s/%s?no_verify=1", - encodedUsername, vcenter, datacenter, cluster, host); - } protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String installPath) throws IOException { String xmlPath = String.format("%s.xml", installPath); if (!new File(xmlPath).exists()) { diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java index d0dccf4bd90f..aec6d023aa5e 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java @@ -71,12 +71,12 @@ public class LibvirtConvertInstanceCommandWrapperTest { private static final String secondaryPoolUrl = "nfs://192.168.1.1/secondary"; private static final String vmName = "VmToImport"; - private static final String hostName = "VmwareHost1"; - private static final String vmwareVcenter = "192.168.1.2"; - private static final String vmwareDatacenter = "Datacenter"; - private static final String vmwareCluster = "Cluster"; - private static final String vmwareUsername = "administrator@vsphere.local"; - private static final String vmwarePassword = "password"; +// private static final String hostName = "VmwareHost1"; +// private static final String vmwareVcenter = "192.168.1.2"; +// private static final String vmwareDatacenter = "Datacenter"; +// private static final String vmwareCluster = "Cluster"; +// private static final String vmwareUsername = "administrator@vsphere.local"; +// private static final String vmwarePassword = "password"; @Before public void setUp() { @@ -244,12 +244,12 @@ private RemoteInstanceTO getRemoteInstanceTO(Hypervisor.HypervisorType hyperviso RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class); Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType); Mockito.when(remoteInstanceTO.getInstanceName()).thenReturn(vmName); - Mockito.when(remoteInstanceTO.getHostName()).thenReturn(hostName); - Mockito.when(remoteInstanceTO.getVcenterHost()).thenReturn(vmwareVcenter); - Mockito.when(remoteInstanceTO.getDatacenterName()).thenReturn(vmwareDatacenter); - Mockito.when(remoteInstanceTO.getClusterName()).thenReturn(vmwareCluster); - Mockito.when(remoteInstanceTO.getVcenterUsername()).thenReturn(vmwareUsername); - Mockito.when(remoteInstanceTO.getVcenterPassword()).thenReturn(vmwarePassword); +// Mockito.when(remoteInstanceTO.getHostName()).thenReturn(hostName); +// Mockito.when(remoteInstanceTO.getVcenterHost()).thenReturn(vmwareVcenter); +// Mockito.when(remoteInstanceTO.getDatacenterName()).thenReturn(vmwareDatacenter); +// Mockito.when(remoteInstanceTO.getClusterName()).thenReturn(vmwareCluster); +// Mockito.when(remoteInstanceTO.getVcenterUsername()).thenReturn(vmwareUsername); +// Mockito.when(remoteInstanceTO.getVcenterPassword()).thenReturn(vmwarePassword); return remoteInstanceTO; } @@ -303,8 +303,8 @@ public void testExecuteConvertFailure() { Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock); Assert.assertFalse(answer.getResult()); - Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(), - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean()); +// Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(), +// Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean()); } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index a0cea8bec29a..454900af9df7 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -1366,23 +1366,23 @@ private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO } private String createOVATemplateFileOfVM(VirtualMachineMO vmMO, DataStoreTO convertLocation) throws Exception { - String dataStoreUrl = getDataStoreUrlForConversion(convertLocation); - String vmOvaName = UUID.randomUUID().toString(); - String vmOvaCreationPath = createDirOnStorage(vmOvaName, dataStoreUrl, null); - s_logger.info("Creating OVA - " + vmOvaName + " at " + vmOvaCreationPath); - vmMO.exportVm(vmOvaCreationPath, vmOvaName, true, true); - s_logger.info("Created OVA - " + vmOvaName + " at " + vmOvaCreationPath); - return vmOvaName; + String dataStoreUrl = getDataStoreUrlForTemplate(convertLocation); + String vmOvaDirAndName = UUID.randomUUID().toString(); + String vmOvaCreationPath = createDirOnStorage(vmOvaDirAndName, dataStoreUrl, null); + s_logger.debug(String.format("Creating OVA %s.ova for the VM %s at %s", vmOvaDirAndName, vmMO.getName(), vmOvaCreationPath)); + vmMO.exportVm(vmOvaCreationPath, vmOvaDirAndName, true, true); + s_logger.debug(String.format("Created OVA %s.ova for the VM %s at %s", vmOvaDirAndName, vmMO.getName(), vmOvaCreationPath)); + return vmOvaDirAndName; } @Override public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO convertLocation) { - s_logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp)); + Map params, DataStoreTO templateLocation) { String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD); + s_logger.debug(String.format("Cloning VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter)); try { VmwareContext context = connectToVcenter(vcenter, username, password); @@ -1402,11 +1402,12 @@ public Pair cloneHypervisorVMAndCreateTemplateFileO VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO); s_logger.debug(String.format("VM %s cloned successfully", vmName)); - String ovaTemplateName = createOVATemplateFileOfVM(clonedVM, convertLocation); + String ovaTemplateDirAndName = createOVATemplateFileOfVM(clonedVM, templateLocation); + s_logger.debug(String.format("OVA %s/%s.ova created successfully on the datastore", ovaTemplateDirAndName, ovaTemplateDirAndName)); UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM); setNicsFromSourceVM(clonedInstance, vmMo); clonedInstance.setCloneSourcePowerState(sourceVmPowerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn : UnmanagedInstanceTO.PowerState.PowerOff); - return new Pair<> (clonedInstance, ovaTemplateName); + return new Pair<> (clonedInstance, ovaTemplateDirAndName); } catch (Exception e) { String err = String.format("Error cloning VM: %s from external vCenter %s: %s", vmName, vcenter, e.getMessage()); s_logger.error(err, e); @@ -1431,12 +1432,14 @@ private void setNicsFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMach } @Override - public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation, String templateOnConvertLocation) { - s_logger.debug(String.format("Removing VM %s on external vCenter %s", vmName, hostIp)); + public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, + DataStoreTO templateLocation, String templateDirAndName) { String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD); + s_logger.debug(String.format("Removing cloned VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter)); + try { VmwareContext context = connectToVcenter(vcenter, username, password); DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter); @@ -1447,26 +1450,25 @@ public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, s_logger.error(err); return false; } - if (!vmMo.destroy()) { - return false; - } - String dataStoreUrl = getDataStoreUrlForConversion(convertLocation); - deleteDirOnStorage(templateOnConvertLocation, dataStoreUrl, null); - return true; + + boolean clonedVMDestroyed = vmMo.destroy(); + String dataStoreUrl = getDataStoreUrlForTemplate(templateLocation); + boolean templateDirDeleted = deleteDirOnStorage(templateDirAndName, dataStoreUrl, null); + return clonedVMDestroyed && templateDirDeleted; } catch (Exception e) { - String err = String.format("Error destroying external VM %s: %s", vmName, e.getMessage()); + String err = String.format("Error destroying cloned VM %s or template %s/%s.ova: %s", vmName, templateDirAndName, templateDirAndName, e.getMessage()); s_logger.error(err, e); return false; } } - private String getDataStoreUrlForConversion(DataStoreTO convertLocation) { + private String getDataStoreUrlForTemplate(DataStoreTO templateLocation) { String dataStoreUrl = null; - if (convertLocation instanceof NfsTO) { - NfsTO nfsStore = (NfsTO) convertLocation; + if (templateLocation instanceof NfsTO) { + NfsTO nfsStore = (NfsTO) templateLocation; dataStoreUrl = nfsStore.getUrl(); - } else if (convertLocation instanceof PrimaryDataStoreTO) { - PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) convertLocation; + } else if (templateLocation instanceof PrimaryDataStoreTO) { + PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) templateLocation; if (primaryDataStoreTO.getPoolType().equals(Storage.StoragePoolType.NetworkFilesystem)) { String psHost = primaryDataStoreTO.getHost(); String psPath = primaryDataStoreTO.getPath(); @@ -1475,7 +1477,7 @@ private String getDataStoreUrlForConversion(DataStoreTO convertLocation) { } if (dataStoreUrl == null) { - throw new CloudRuntimeException("Only NFS storages are supported for conversion"); + throw new CloudRuntimeException("Only NFS storage is supported for template creation"); } return dataStoreUrl; @@ -1483,7 +1485,7 @@ private String getDataStoreUrlForConversion(DataStoreTO convertLocation) { private String createDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception { String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion); - s_logger.info("Create dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName); + s_logger.debug("Create dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName); String dirMountPath = mountPoint + File.separator + dirName; createDir(dirMountPath); return dirMountPath; @@ -1503,11 +1505,18 @@ private void createDir(String dirName) throws Exception { } } - private void deleteDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception { - String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion); - s_logger.info("Delete dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName); - String dirMountPath = mountPoint + File.separator + dirName; - deleteDir(dirMountPath); + private boolean deleteDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception { + try { + String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion); + s_logger.debug("Delete dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName); + String dirMountPath = mountPoint + File.separator + dirName; + deleteDir(dirMountPath); + return true; + } catch (Exception e) { + String err = String.format("Unable to delete dir %s: %s", dirName, e.getMessage()); + s_logger.error(err, e); + return false; + } } private void deleteDir(String dirName) throws Exception { diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index 9a2bf9d7dcf8..f53a2b2e156a 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -378,14 +378,14 @@ public ConfigKey[] getConfigKeys() { } @Override - public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation) { - s_logger.error("Unsupported operation: cannot clone external VM"); + public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation) { + s_logger.error("Unsupported operation: cannot clone external VM / create template file"); return null; } @Override - public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO convertLocation, String templateOnConvertLocation) { - s_logger.error("Unsupported operation: cannot remove external VM"); + public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation, String templateDirAndName) { + s_logger.error("Unsupported operation: cannot remove external VM and template file"); return false; } } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 3751e9b33490..19b437647bfc 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1580,17 +1580,17 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster UnmanagedInstanceTO clonedInstance = null; DataStoreTO temporaryConvertLocation = null; - String ovaTemplateOnConvertLocation = null; + String ovaTemplateDirAndNameOnConvertLocation = null; try { temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, null); Pair clonedInstanceAndOvaTemplate = cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM, temporaryConvertLocation); clonedInstance = clonedInstanceAndOvaTemplate.first(); - ovaTemplateOnConvertLocation = clonedInstanceAndOvaTemplate.second(); + ovaTemplateDirAndNameOnConvertLocation = clonedInstanceAndOvaTemplate.second(); String instanceName = getGeneratedInstanceName(owner); checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, - sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, temporaryConvertLocation, ovaTemplateOnConvertLocation); + sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); sanitizeConvertedInstance(convertedInstance, clonedInstance); UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, template, displayName, hostName, caller, owner, userId, @@ -1605,7 +1605,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster cmd.getEventDescription(), null, null, 0); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } finally { - removeClonedInstanceAndTemplateFile(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM, temporaryConvertLocation, ovaTemplateOnConvertLocation); + removeClonedInstanceAndTemplateFile(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); } } @@ -1756,18 +1756,16 @@ private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationClust private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName, String username, String password, String hostName, UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, - Long convertInstanceHostId, DataStoreTO temporaryConvertLocation, String ovaTemplateOnConvertLocation) { + Long convertInstanceHostId, DataStoreTO temporaryConvertLocation, String ovaTemplateDirAndNameOnConvertLocation) { HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId); String vmName = clonedInstance.getName(); LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" + " from VMware to KVM ", convertHost.getId(), convertHost.getName(), vmName)); - RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(hostName, vmName, - vcenter, datacenterName, clusterName, username, password); -// DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost); + RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(vmName); List destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks()); ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO, - Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovaTemplateOnConvertLocation); + Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60; cmd.setWait(timeoutSeconds); From 36cfeb7c97aa672810f356901bf610112625cef2 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 18 Mar 2024 14:10:58 +0530 Subject: [PATCH 05/34] test fixes --- ...ibvirtConvertInstanceCommandWrapperTest.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java index aec6d023aa5e..d44d333750fe 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java @@ -71,12 +71,6 @@ public class LibvirtConvertInstanceCommandWrapperTest { private static final String secondaryPoolUrl = "nfs://192.168.1.1/secondary"; private static final String vmName = "VmToImport"; -// private static final String hostName = "VmwareHost1"; -// private static final String vmwareVcenter = "192.168.1.2"; -// private static final String vmwareDatacenter = "Datacenter"; -// private static final String vmwareCluster = "Cluster"; -// private static final String vmwareUsername = "administrator@vsphere.local"; -// private static final String vmwarePassword = "password"; @Before public void setUp() { @@ -244,12 +238,6 @@ private RemoteInstanceTO getRemoteInstanceTO(Hypervisor.HypervisorType hyperviso RemoteInstanceTO remoteInstanceTO = Mockito.mock(RemoteInstanceTO.class); Mockito.when(remoteInstanceTO.getHypervisorType()).thenReturn(hypervisorType); Mockito.when(remoteInstanceTO.getInstanceName()).thenReturn(vmName); -// Mockito.when(remoteInstanceTO.getHostName()).thenReturn(hostName); -// Mockito.when(remoteInstanceTO.getVcenterHost()).thenReturn(vmwareVcenter); -// Mockito.when(remoteInstanceTO.getDatacenterName()).thenReturn(vmwareDatacenter); -// Mockito.when(remoteInstanceTO.getClusterName()).thenReturn(vmwareCluster); -// Mockito.when(remoteInstanceTO.getVcenterUsername()).thenReturn(vmwareUsername); -// Mockito.when(remoteInstanceTO.getVcenterPassword()).thenReturn(vmwarePassword); return remoteInstanceTO; } @@ -303,9 +291,8 @@ public void testExecuteConvertFailure() { Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock); Assert.assertFalse(answer.getResult()); -// Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(), -// Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean()); + Mockito.verify(convertInstanceCommandWrapper).performInstanceConversionUsingOVA(Mockito.anyString(), + Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean()); } } - } From f1bfd6387a5fc86b597735fd89cb07d6850dc6bf Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 18 Mar 2024 17:45:25 +0530 Subject: [PATCH 06/34] Consider zone wide pools in the destination cluster for instance conversion --- .../cloudstack/vm/UnmanagedVMsManagerImpl.java | 13 +++++++------ .../cloudstack/vm/UnmanagedVMsManagerImplTest.java | 2 ++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 19b437647bfc..b01a76baa03e 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1589,8 +1589,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster ovaTemplateDirAndNameOnConvertLocation = clonedInstanceAndOvaTemplate.second(); String instanceName = getGeneratedInstanceName(owner); checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); - UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password, - sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); + UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(clonedInstance, destinationCluster, convertInstanceHostId, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); sanitizeConvertedInstance(convertedInstance, clonedInstance); UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, template, displayName, hostName, caller, owner, userId, @@ -1753,9 +1752,7 @@ private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationClust return filteredHosts.get(new Random().nextInt(filteredHosts.size())); } - private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName, - String username, String password, String hostName, - UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, + private UnmanagedInstanceTO convertVmwareInstanceToKVM(UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, Long convertInstanceHostId, DataStoreTO temporaryConvertLocation, String ovaTemplateDirAndNameOnConvertLocation) { HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId); String vmName = clonedInstance.getName(); @@ -1790,7 +1787,11 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String da private List selectInstanceConvertionStoragePools(Cluster destinationCluster, List disks) { List storagePools = new ArrayList<>(disks.size()); - List pools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId()); + List pools = new ArrayList<>(); + List clusterPools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId()); + pools.addAll(clusterPools); + List zonePools = primaryDataStoreDao.findZoneWideStoragePoolsByHypervisor(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM); + pools.addAll(zonePools); //TODO: Choose pools by capacity for (UnmanagedInstanceTO.Disk disk : disks) { Long capacity = disk.getCapacity(); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 0216277698a3..09ae3fa0bc34 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -639,6 +639,7 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, when(destPool.getDataCenterId()).thenReturn(zoneId); when(destPool.getClusterId()).thenReturn(null); when(destPool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); + StoragePoolVO zoneDestPool = mock(StoragePoolVO.class); if (selectTemporaryStorage) { long temporaryStoragePoolId = 1L; when(importVmCmd.getConvertStoragePoolId()).thenReturn(temporaryStoragePoolId); @@ -652,6 +653,7 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, } when(primaryDataStoreDao.listPoolsByCluster(clusterId)).thenReturn(List.of(destPool)); when(primaryDataStoreDao.listPoolByHostPath(Mockito.anyString(), Mockito.anyString())).thenReturn(List.of(destPool)); + when(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisor(zoneId, Hypervisor.HypervisorType.KVM)).thenReturn(List.of(zoneDestPool)); if (VcenterParameter.EXISTING == vcenterParameter) { VmwareDatacenterVO datacenterVO = mock(VmwareDatacenterVO.class); From dbe9f0124d51cf2fc83a3dd229483924c7dfee65 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Tue, 19 Mar 2024 14:37:29 +0530 Subject: [PATCH 07/34] Remove local storage pool support as temporary conversion location - OVA export not possible as the pool is not accessible outside host, NFS pools are supported. --- .../LibvirtConvertInstanceCommandWrapper.java | 4 ++-- .../LibvirtConvertInstanceCommandWrapperTest.java | 2 +- .../cloudstack/vm/UnmanagedVMsManagerImpl.java | 11 +++++------ .../cloudstack/vm/UnmanagedVMsManagerImplTest.java | 13 +++++-------- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java index 706e4ae8e84f..4c5b46dd699b 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java @@ -104,7 +104,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled(); try { - boolean result = performInstanceConversionUsingOVA(sourceOVAFile, temporaryConvertPath, temporaryConvertUuid, + boolean result = performInstanceConversion(sourceOVAFile, temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); if (!result) { String err = String.format("The virt-v2v conversion for the ova %s failed. " + @@ -315,7 +315,7 @@ protected Pair getNfsStoragePoolHostAndPath(KVMStoragePool stora return new Pair<>(sourceHostIp, sourcePath); } - protected boolean performInstanceConversionUsingOVA(String sourceOVAFile, + protected boolean performInstanceConversion(String sourceOVAFile, String temporaryConvertFolder, String temporaryConvertUuid, long timeout, boolean verboseModeEnabled) { diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java index d44d333750fe..a3fee511caba 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java @@ -291,7 +291,7 @@ public void testExecuteConvertFailure() { Answer answer = convertInstanceCommandWrapper.execute(cmd, libvirtComputingResourceMock); Assert.assertFalse(answer.getResult()); - Mockito.verify(convertInstanceCommandWrapper).performInstanceConversionUsingOVA(Mockito.anyString(), + Mockito.verify(convertInstanceCommandWrapper).performInstanceConversion(Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyLong(), Mockito.anyBoolean()); } } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index b01a76baa03e..eabaa1a67148 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1582,7 +1582,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster DataStoreTO temporaryConvertLocation = null; String ovaTemplateDirAndNameOnConvertLocation = null; try { - temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, null); + temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId); Pair clonedInstanceAndOvaTemplate = cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM, temporaryConvertLocation); clonedInstance = clonedInstanceAndOvaTemplate.first(); @@ -1805,7 +1805,7 @@ private void logFailureAndThrowException(String msg) { throw new CloudRuntimeException(msg); } - protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId, HostVO convertHost) { + protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinationCluster, Long convertStoragePoolId) { if (convertStoragePoolId != null) { StoragePoolVO selectedStoragePool = primaryDataStoreDao.findById(convertStoragePoolId); if (selectedStoragePool == null) { @@ -1816,11 +1816,10 @@ protected DataStoreTO selectInstanceConversionTemporaryLocation(Cluster destinat logFailureAndThrowException(String.format("Cannot use the storage pool %s for the instance conversion as " + "it is not in the scope of the cluster %s", selectedStoragePool.getName(), destinationCluster.getName())); } - if (selectedStoragePool.getScope() == ScopeType.HOST && - storagePoolHostDao.findByPoolHost(selectedStoragePool.getId(), convertHost.getId()) == null) { - logFailureAndThrowException(String.format("The storage pool %s is not a local storage pool for the host %s", selectedStoragePool.getName(), convertHost.getName())); + if (selectedStoragePool.getScope() == ScopeType.HOST) { + logFailureAndThrowException(String.format("The storage pool %s is a local storage pool and not supported for temporary conversion location, cluster and zone wide NFS storage pools are supported", selectedStoragePool.getName())); } else if (selectedStoragePool.getPoolType() != Storage.StoragePoolType.NetworkFilesystem) { - logFailureAndThrowException(String.format("The storage pool %s is not supported for temporary conversion location, supported pools are NFS storage pools", selectedStoragePool.getName())); + logFailureAndThrowException(String.format("The storage pool %s is not supported for temporary conversion location, only NFS storage pools are supported", selectedStoragePool.getName())); } return dataStoreManager.getPrimaryDataStore(convertStoragePoolId).getTO(); } else { diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 09ae3fa0bc34..4257a3412306 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -799,7 +799,7 @@ public void testSelectInstanceConversionTemporaryLocationInvalidStorage() { long poolId = 1L; when(primaryDataStoreDao.findById(poolId)).thenReturn(null); - unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null); + unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); } @Test(expected = CloudRuntimeException.class) @@ -810,7 +810,7 @@ public void testSelectInstanceConversionTemporaryLocationPoolInvalidScope() { Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); Mockito.when(pool.getClusterId()).thenReturn(100L); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); - unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, null); + unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); } @Test(expected = CloudRuntimeException.class) @@ -820,9 +820,7 @@ public void testSelectInstanceConversionTemporaryLocationLocalStoragePoolInvalid StoragePoolVO pool = mock(StoragePoolVO.class); Mockito.when(pool.getScope()).thenReturn(ScopeType.HOST); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); - HostVO convertHost = Mockito.mock(HostVO.class); - Mockito.when(convertHost.getId()).thenReturn(1L); - unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost); + unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); } @Test(expected = CloudRuntimeException.class) @@ -833,15 +831,14 @@ public void testSelectInstanceConversionTemporaryLocationStoragePoolInvalidType( Mockito.when(pool.getScope()).thenReturn(ScopeType.CLUSTER); Mockito.when(pool.getClusterId()).thenReturn(1L); when(primaryDataStoreDao.findById(poolId)).thenReturn(pool); - HostVO convertHost = Mockito.mock(HostVO.class); Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.RBD); - unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId, convertHost); + unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, poolId); } @Test(expected = CloudRuntimeException.class) public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() { ClusterVO cluster = getClusterForTests(); Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null); - unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, null); + unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null); } } From b76092bcc3f9ec4fdd8c244408179d265e3acf19 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Thu, 21 Mar 2024 17:39:50 +0530 Subject: [PATCH 08/34] cleanup unused code --- .../java/com/cloud/hypervisor/guru/VMwareGuru.java | 3 --- .../cloud/hypervisor/vmware/mo/DatastoreMO.java | 14 -------------- 2 files changed, 17 deletions(-) diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index 454900af9df7..7005dab2d763 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -1358,9 +1358,6 @@ private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO throw new CloudRuntimeException(err); } - String datastorePath = datastoreMO.getDatastorePathOnHost(sourceHost.getMor().getValue()); - s_logger.info("Host datastore path: " + datastorePath); - relocateClonedVMToSourceHost(clonedVM, sourceHost); return clonedVM; } diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java index c9e22922b237..7e9021ac9a41 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/DatastoreMO.java @@ -467,18 +467,4 @@ public String getDatastoreType() throws Exception { DatastoreSummary summary = _context.getVimClient().getDynamicProperty(getMor(), "summary"); return summary.getType() == null ? "" : summary.getType(); } - - public String getDatastorePathOnHost(String hostValue) throws Exception { - String datastorePath = null; - List hostMounts = getHostMounts(); - for (DatastoreHostMount hostMount : hostMounts) { - String hostMountValue = hostMount.getKey().getValue(); - if (hostMountValue.equalsIgnoreCase(hostValue)) { - HostMountInfo mountInfo = hostMount.getMountInfo(); - datastorePath = mountInfo.getPath(); - break; - } - } - return datastorePath; - } } From 52e74e33edb0dd688302716ee237d16c5e12d978 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Fri, 22 Mar 2024 12:00:44 +0530 Subject: [PATCH 09/34] some improvements, and refactoring --- .../orchestration/NetworkOrchestrator.java | 12 +++-- .../datastore/db/PrimaryDataStoreDao.java | 5 +++ .../datastore/db/PrimaryDataStoreDaoImpl.java | 23 ++++++++++ .../LibvirtConvertInstanceCommandWrapper.java | 28 +++++++----- ...virtConvertInstanceCommandWrapperTest.java | 1 + .../vm/UnmanagedVMsManagerImpl.java | 45 +++++++++++++------ .../vm/UnmanagedVMsManagerImplTest.java | 4 +- .../views/tools/ImportUnmanagedInstance.vue | 4 +- 8 files changed, 92 insertions(+), 30 deletions(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 4da4d7031a23..b59e720c286e 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -4607,10 +4607,16 @@ public Pair importNic(final String macAddress, int deviceId final NicVO vo = Transaction.execute(new TransactionCallback() { @Override public NicVO doInTransaction(TransactionStatus status) { - NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddress); - String macAddressToPersist = macAddress; + if (StringUtils.isBlank(macAddress)) { + throw new CloudRuntimeException("Mac address not specified"); + } + String macAddressToPersist = macAddress.trim(); + if (!NetUtils.isValidMac(macAddressToPersist)) { + throw new CloudRuntimeException("Invalid mac address: " + macAddressToPersist); + } + NicVO existingNic = _nicDao.findByNetworkIdAndMacAddress(network.getId(), macAddressToPersist); if (existingNic != null) { - macAddressToPersist = generateNewMacAddressIfForced(network, macAddress, forced); + macAddressToPersist = generateNewMacAddressIfForced(network, macAddressToPersist, forced); } NicVO vo = new NicVO(network.getGuruName(), vm.getId(), network.getId(), vm.getType()); vo.setMacAddress(macAddressToPersist); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java index cf2cad654929..2fce0628554c 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java @@ -21,6 +21,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.ScopeType; +import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolStatus; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; @@ -126,6 +127,10 @@ public interface PrimaryDataStoreDao extends GenericDao { List findZoneWideStoragePoolsByHypervisor(long dataCenterId, HypervisorType hypervisorType, String keyword); + List findZoneWideStoragePoolsByHypervisorAndPoolType(long dataCenterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType); + + List findClusterWideStoragePoolsByHypervisorAndPoolType(long clusterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType); + List findLocalStoragePoolsByHostAndTags(long hostId, String[] tags); List listLocalStoragePoolByPath(long datacenterId, String path); diff --git a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java index ce42f9f4d161..9b03bfe29a7a 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java @@ -28,6 +28,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.storage.Storage; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import org.apache.commons.collections.CollectionUtils; @@ -621,6 +622,28 @@ public List findZoneWideStoragePoolsByHypervisor(long dataCenterI return sc.list(); } + @Override + public List findZoneWideStoragePoolsByHypervisorAndPoolType(long dataCenterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getDataCenterId(), Op.EQ, dataCenterId); + sc.and(sc.entity().getStatus(), Op.EQ, StoragePoolStatus.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.ZONE); + sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType); + sc.and(sc.entity().getPoolType(), Op.EQ, poolType); + return sc.list(); + } + + @Override + public List findClusterWideStoragePoolsByHypervisorAndPoolType(long clusterId, HypervisorType hypervisorType, Storage.StoragePoolType poolType) { + QueryBuilder sc = QueryBuilder.create(StoragePoolVO.class); + sc.and(sc.entity().getClusterId(), Op.EQ, clusterId); + sc.and(sc.entity().getStatus(), Op.EQ, StoragePoolStatus.Up); + sc.and(sc.entity().getScope(), Op.EQ, ScopeType.CLUSTER); + sc.and(sc.entity().getHypervisor(), Op.EQ, hypervisorType); + sc.and(sc.entity().getPoolType(), Op.EQ, poolType); + return sc.list(); + } + @Override public void deletePoolTags(long poolId) { _tagsDao.deleteTags(poolId); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java index 4c5b46dd699b..2783e2985e9f 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java @@ -76,6 +76,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve List destinationStoragePools = cmd.getDestinationStoragePools(); DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation(); String ovaTemplateDirAndNameOnConversionLocation = cmd.getTemplateDirAndNameOnConversionLocation(); + String sourceOVAFile = ovaTemplateDirAndNameOnConversionLocation + ".ova"; long timeout = (long) cmd.getWait() * 1000; if (!isInstanceConversionSupportedOnHost()) { @@ -96,18 +97,17 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr(); KVMStoragePool temporaryStoragePool = getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr); - s_logger.info(String.format("Attempting to convert the instance %s from %s to KVM", - sourceInstanceName, sourceHypervisorType)); + s_logger.info(String.format("Attempting to convert the OVA %s of the instance %s from %s to KVM", sourceOVAFile, sourceInstanceName, sourceHypervisorType)); final String temporaryConvertUuid = UUID.randomUUID().toString(); final String temporaryConvertPath = temporaryStoragePool.getLocalPath(); - final String sourceOVAFile = String.format("%s/%s/%s.ova", temporaryConvertPath, ovaTemplateDirAndNameOnConversionLocation, ovaTemplateDirAndNameOnConversionLocation); + final String sourceOVAFilePath = String.format("%s/%s/%s", temporaryConvertPath, ovaTemplateDirAndNameOnConversionLocation, sourceOVAFile); boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled(); try { - boolean result = performInstanceConversion(sourceOVAFile, temporaryConvertPath, temporaryConvertUuid, + boolean result = performInstanceConversion(sourceOVAFilePath, temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled); if (!result) { - String err = String.format("The virt-v2v conversion for the ova %s failed. " + + String err = String.format("The virt-v2v conversion for the OVA %s failed. " + "Please check the agent logs for the virt-v2v output", sourceOVAFile); s_logger.error(err); return new ConvertInstanceAnswer(cmd, false, err); @@ -235,6 +235,11 @@ protected List moveTemporaryDisksToDestination(List getNfsStoragePoolHostAndPath(KVMStoragePool stora String sourceHostIp = null; String sourcePath = null; String storagePoolMountPoint = Script.runSimpleBashScript(String.format("mount | grep %s", storagePool.getLocalPath())); + s_logger.debug(String.format("NFS Storage pool: %s - local path: %s, mount point: %s", storagePool.getUuid(), storagePool.getLocalPath(), storagePoolMountPoint)); if (StringUtils.isNotEmpty(storagePoolMountPoint)) { String[] res = storagePoolMountPoint.strip().split(" "); res = res[0].split(":"); - sourceHostIp = res[0].strip(); - sourcePath = res[1].strip(); + if (res.length > 1) { + sourceHostIp = res[0].strip(); + sourcePath = res[1].strip(); + } } return new Pair<>(sourceHostIp, sourcePath); } - protected boolean performInstanceConversion(String sourceOVAFile, + protected boolean performInstanceConversion(String sourceOVAFilePath, String temporaryConvertFolder, String temporaryConvertUuid, long timeout, boolean verboseModeEnabled) { Script script = new Script("virt-v2v", timeout, s_logger); script.add("--root", "first"); script.add("-i", "ova"); - script.add(sourceOVAFile); + script.add(sourceOVAFilePath); script.add("-o", "local"); script.add("-os", temporaryConvertFolder); script.add("-of", "qcow2"); @@ -331,7 +339,7 @@ protected boolean performInstanceConversion(String sourceOVAFile, script.add("-v"); } - String logPrefix = String.format("virt-v2v ova source: %s progress", sourceOVAFile); + String logPrefix = String.format("virt-v2v ova source: %s progress", sourceOVAFilePath); OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix); script.execute(outputLogger); int exitValue = script.getExitValue(); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java index a3fee511caba..812e7805c55b 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapperTest.java @@ -185,6 +185,7 @@ public void testMoveTemporaryDisksToDestination() { Mockito.when(destDisk.getPath()).thenReturn("xyz"); Mockito.when(storagePoolManager.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, destinationPoolUuid)) .thenReturn(destinationPool); + Mockito.when(destinationPool.getType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); Mockito.when(storagePoolManager.copyPhysicalDisk(Mockito.eq(sourceDisk), Mockito.anyString(), Mockito.eq(destinationPool), Mockito.anyInt())) .thenReturn(destDisk); diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index eabaa1a67148..58e09f2d6aa7 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1583,13 +1583,14 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster String ovaTemplateDirAndNameOnConvertLocation = null; try { temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId); + List convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster); Pair clonedInstanceAndOvaTemplate = cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM, temporaryConvertLocation); clonedInstance = clonedInstanceAndOvaTemplate.first(); ovaTemplateDirAndNameOnConvertLocation = clonedInstanceAndOvaTemplate.second(); String instanceName = getGeneratedInstanceName(owner); checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); - UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(clonedInstance, destinationCluster, convertInstanceHostId, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); + UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(sourceVM, clonedInstance, destinationCluster, convertStoragePools, convertInstanceHostId, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); sanitizeConvertedInstance(convertedInstance, clonedInstance); UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, template, displayName, hostName, caller, owner, userId, @@ -1604,7 +1605,9 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster cmd.getEventDescription(), null, null, 0); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } finally { - removeClonedInstanceAndTemplateFile(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); + if (clonedInstance != null) { + removeClonedInstanceAndTemplateFile(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); + } } } @@ -1681,11 +1684,19 @@ private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, Un nic.setAdapterType("virtio"); } convertedInstance.setNics(clonedInstanceNics); - } else { for (int i = 0; i < convertedInstanceNics.size(); i++) { UnmanagedInstanceTO.Nic nic = convertedInstanceNics.get(i); nic.setNicId(clonedInstanceNics.get(i).getNicId()); } + } else if (CollectionUtils.isNotEmpty(convertedInstanceNics) && CollectionUtils.isNotEmpty(clonedInstanceNics) + && convertedInstanceNics.size() == clonedInstanceNics.size()) { + for (int i = 0; i < convertedInstanceNics.size(); i++) { + UnmanagedInstanceTO.Nic nic = convertedInstanceNics.get(i); + nic.setNicId(clonedInstanceNics.get(i).getNicId()); + if (nic.getMacAddress() == null) { + nic.setMacAddress(clonedInstanceNics.get(i).getMacAddress()); + } + } } } @@ -1752,15 +1763,14 @@ private HostVO selectInstanceConvertionKVMHostInCluster(Cluster destinationClust return filteredHosts.get(new Random().nextInt(filteredHosts.size())); } - private UnmanagedInstanceTO convertVmwareInstanceToKVM(UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, + private UnmanagedInstanceTO convertVmwareInstanceToKVM(String sourceVM, UnmanagedInstanceTO clonedInstance, Cluster destinationCluster, List convertStoragePools, Long convertInstanceHostId, DataStoreTO temporaryConvertLocation, String ovaTemplateDirAndNameOnConvertLocation) { HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId); - String vmName = clonedInstance.getName(); LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" + - " from VMware to KVM ", convertHost.getId(), convertHost.getName(), vmName)); + " from VMware to KVM ", convertHost.getId(), convertHost.getName(), sourceVM)); - RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(vmName); - List destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks()); + RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM); + List destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, clonedInstance.getDisks()); ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60; @@ -1778,20 +1788,29 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVM(UnmanagedInstanceTO clone if (!convertAnswer.getResult()) { String err = String.format("The convert process failed for instance %s from Vmware to KVM on host %s: %s", - vmName, convertHost.getName(), convertAnswer.getDetails()); + sourceVM, convertHost.getName(), convertAnswer.getDetails()); LOGGER.error(err); throw new CloudRuntimeException(err); } return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance(); } - private List selectInstanceConvertionStoragePools(Cluster destinationCluster, List disks) { - List storagePools = new ArrayList<>(disks.size()); + private List findInstanceConversionStoragePoolsInCluster(Cluster destinationCluster) { List pools = new ArrayList<>(); - List clusterPools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId()); + List clusterPools = primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem); pools.addAll(clusterPools); - List zonePools = primaryDataStoreDao.findZoneWideStoragePoolsByHypervisor(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM); + List zonePools = primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem); pools.addAll(zonePools); + if (pools.isEmpty()) { + String msg = String.format("Cannot find suitable storage pools in cluster %s for the conversion", destinationCluster.getName()); + LOGGER.error(msg); + throw new CloudRuntimeException(msg); + } + return pools; + } + + private List selectInstanceConversionStoragePools(List pools, List disks) { + List storagePools = new ArrayList<>(disks.size()); //TODO: Choose pools by capacity for (UnmanagedInstanceTO.Disk disk : disks) { Long capacity = disk.getCapacity(); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 4257a3412306..94b80b6b7858 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -651,9 +651,9 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, when(imageStoreDao.findOneByZoneAndProtocol(zoneId, "nfs")).thenReturn(imageStoreVO); when(dataStoreManager.getDataStore(1L, DataStoreRole.Image)).thenReturn(dataStore); } - when(primaryDataStoreDao.listPoolsByCluster(clusterId)).thenReturn(List.of(destPool)); when(primaryDataStoreDao.listPoolByHostPath(Mockito.anyString(), Mockito.anyString())).thenReturn(List.of(destPool)); - when(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisor(zoneId, Hypervisor.HypervisorType.KVM)).thenReturn(List.of(zoneDestPool)); + when(primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(clusterId, Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)).thenReturn(List.of(destPool)); + when(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(zoneId, Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)).thenReturn(List.of(zoneDestPool)); if (VcenterParameter.EXISTING == vcenterParameter) { VmwareDatacenterVO datacenterVO = mock(VmwareDatacenterVO.class); diff --git a/ui/src/views/tools/ImportUnmanagedInstance.vue b/ui/src/views/tools/ImportUnmanagedInstance.vue index 4fb559b26283..bbe1a64a9c4d 100644 --- a/ui/src/views/tools/ImportUnmanagedInstance.vue +++ b/ui/src/views/tools/ImportUnmanagedInstance.vue @@ -915,7 +915,7 @@ export default { if (this.selectedStorageOptionForConversion === 'primary') { api('listStoragePools', { zoneid: this.cluster.zoneid, - state: 'Up' + status: 'Up' }).then(json => { this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || [] }) @@ -924,7 +924,7 @@ export default { api('listStoragePools', { scope: 'HOST', ipaddress: kvmHost.ipaddress, - state: 'Up' + status: 'Up' }).then(json => { this.storagePoolsForConversion = json.liststoragepoolsresponse.storagepool || [] }) From e4493110cecfd5c24ec70aa60380bf3a54e135da Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 27 Mar 2024 18:36:33 +0530 Subject: [PATCH 10/34] import nic unit tests --- .../orchestration/NetworkOrchestrator.java | 2 +- .../NetworkOrchestratorTest.java | 120 ++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index b59e720c286e..a3662b776321 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -4661,7 +4661,7 @@ public NicVO doInTransaction(TransactionStatus status) { final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); - return new Pair(vmNic, Integer.valueOf(deviceId)); + return new Pair<>(vmNic, Integer.valueOf(deviceId)); } protected String getSelectedIpForNicImport(Network network, DataCenter dataCenter, Network.IpAddresses ipAddresses) { diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index 45ed646240f4..2d016d03f2e5 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.engine.orchestration; import static org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService.NetworkLockTimeout; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -31,8 +32,10 @@ import java.util.Map; import com.cloud.dc.DataCenter; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; import com.cloud.network.IpAddressManager; import com.cloud.utils.Pair; + import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; @@ -40,6 +43,7 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Matchers; +import org.mockito.MockedStatic; import org.mockito.Mockito; import com.cloud.api.query.dao.DomainRouterJoinDao; @@ -72,6 +76,8 @@ import com.cloud.network.vpc.VpcVO; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.utils.db.EntityManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; import com.cloud.vm.DomainRouterVO; @@ -892,4 +898,118 @@ public void testShutdownNetworkInImplementingState() { verify(testOrchestrator._networksDao, times(1)).acquireInLockTable(networkId, NetworkLockTimeout.value()); verify(testOrchestrator._networksDao, times(1)).releaseFromLockTable(networkId); } + + @Test(expected = InsufficientVirtualNetworkCapacityException.class) + public void testImportNicAcquireGuestIPFailed() throws Exception { + DataCenter dataCenter = Mockito.mock(DataCenter.class); + VirtualMachine vm = mock(VirtualMachine.class); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated); + Mockito.when(network.getNetworkOfferingId()).thenReturn(networkOfferingId); + long dataCenterId = 1L; + Mockito.when(network.getDataCenterId()).thenReturn(dataCenterId); + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + String ipAddress = "10.1.10.10"; + Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress); + Mockito.when(testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses)).thenReturn(null); + Mockito.when(testOrchestrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.Dns, Service.Dhcp)); + String macAddress = "02:01:01:82:00:01"; + int deviceId = 0; + testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false); + } + + @Test(expected = InsufficientVirtualNetworkCapacityException.class) + public void testImportNicAutoAcquireGuestIPFailed() throws Exception { + DataCenter dataCenter = Mockito.mock(DataCenter.class); + VirtualMachine vm = mock(VirtualMachine.class); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getGuestType()).thenReturn(GuestType.Isolated); + Mockito.when(network.getNetworkOfferingId()).thenReturn(networkOfferingId); + long dataCenterId = 1L; + Mockito.when(network.getDataCenterId()).thenReturn(dataCenterId); + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + String ipAddress = "auto"; + Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress); + Mockito.when(testOrchestrator.getSelectedIpForNicImport(network, dataCenter, ipAddresses)).thenReturn(null); + Mockito.when(testOrchestrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.Dns, Service.Dhcp)); + String macAddress = "02:01:01:82:00:01"; + int deviceId = 0; + testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false); + } + + @Test + public void testImportNicNoIP4Address() throws Exception { + DataCenter dataCenter = Mockito.mock(DataCenter.class); + Long vmId = 1L; + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + VirtualMachine vm = mock(VirtualMachine.class); + Mockito.when(vm.getId()).thenReturn(vmId); + Mockito.when(vm.getHypervisorType()).thenReturn(hypervisorType); + Long networkId = 1L; + Network network = Mockito.mock(Network.class); + Mockito.when(network.getId()).thenReturn(networkId); + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + Mockito.when(ipAddresses.getIp4Address()).thenReturn(null); + URI broadcastUri = URI.create("vlan://123"); + NicVO nic = mock(NicVO.class); + Mockito.when(nic.getBroadcastUri()).thenReturn(broadcastUri); + String macAddress = "02:01:01:82:00:01"; + int deviceId = 1; + Integer networkRate = 200; + Mockito.when(testOrchestrator._networkModel.getNetworkRate(networkId, vmId)).thenReturn(networkRate); + Mockito.when(testOrchestrator._networkModel.isSecurityGroupSupportedInNetwork(network)).thenReturn(false); + Mockito.when(testOrchestrator._networkModel.getNetworkTag(hypervisorType, network)).thenReturn("testtag"); + try (MockedStatic transactionMocked = Mockito.mockStatic(Transaction.class)) { + transactionMocked.when(() -> Transaction.execute(any(TransactionCallback.class))).thenReturn(nic); + Pair nicProfileIntegerPair = testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false); + verify(testOrchestrator._networkModel, times(1)).getNetworkRate(networkId, vmId); + verify(testOrchestrator._networkModel, times(1)).isSecurityGroupSupportedInNetwork(network); + verify(testOrchestrator._networkModel, times(1)).getNetworkTag(Hypervisor.HypervisorType.KVM, network); + assertEquals(deviceId, nicProfileIntegerPair.second().intValue()); + NicProfile nicProfile = nicProfileIntegerPair.first(); + assertEquals(broadcastUri, nicProfile.getBroadCastUri()); + assertEquals(networkRate, nicProfile.getNetworkRate()); + assertFalse(nicProfile.isSecurityGroupEnabled()); + assertEquals("testtag", nicProfile.getName()); + } + } + + @Test + public void testImportNicWithIP4Address() throws Exception { + DataCenter dataCenter = Mockito.mock(DataCenter.class); + Long vmId = 1L; + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + VirtualMachine vm = mock(VirtualMachine.class); + Mockito.when(vm.getId()).thenReturn(vmId); + Mockito.when(vm.getHypervisorType()).thenReturn(hypervisorType); + Long networkId = 1L; + Network network = Mockito.mock(Network.class); + Mockito.when(network.getId()).thenReturn(networkId); + String ipAddress = "10.1.10.10"; + Network.IpAddresses ipAddresses = Mockito.mock(Network.IpAddresses.class); + Mockito.when(ipAddresses.getIp4Address()).thenReturn(ipAddress); + URI broadcastUri = URI.create("vlan://123"); + NicVO nic = mock(NicVO.class); + Mockito.when(nic.getBroadcastUri()).thenReturn(broadcastUri); + String macAddress = "02:01:01:82:00:01"; + int deviceId = 1; + Integer networkRate = 200; + Mockito.when(testOrchestrator._networkModel.getNetworkRate(networkId, vmId)).thenReturn(networkRate); + Mockito.when(testOrchestrator._networkModel.isSecurityGroupSupportedInNetwork(network)).thenReturn(false); + Mockito.when(testOrchestrator._networkModel.getNetworkTag(hypervisorType, network)).thenReturn("testtag"); + try (MockedStatic transactionMocked = Mockito.mockStatic(Transaction.class)) { + transactionMocked.when(() -> Transaction.execute(any(TransactionCallback.class))).thenReturn(nic); + Pair nicProfileIntegerPair = testOrchestrator.importNic(macAddress, deviceId, network, true, vm, ipAddresses, dataCenter, false); + verify(testOrchestrator, times(1)).getSelectedIpForNicImport(network, dataCenter, ipAddresses); + verify(testOrchestrator._networkModel, times(1)).getNetworkRate(networkId, vmId); + verify(testOrchestrator._networkModel, times(1)).isSecurityGroupSupportedInNetwork(network); + verify(testOrchestrator._networkModel, times(1)).getNetworkTag(Hypervisor.HypervisorType.KVM, network); + assertEquals(deviceId, nicProfileIntegerPair.second().intValue()); + NicProfile nicProfile = nicProfileIntegerPair.first(); + assertEquals(broadcastUri, nicProfile.getBroadCastUri()); + assertEquals(networkRate, nicProfile.getNetworkRate()); + assertFalse(nicProfile.isSecurityGroupEnabled()); + assertEquals("testtag", nicProfile.getName()); + } + } } From 9808e5cfa33c1ce06b5b7a6a50cca75e45523942 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Fri, 5 Apr 2024 17:27:43 +0530 Subject: [PATCH 11/34] vmware guru unit tests --- .../com/cloud/hypervisor/guru/VMwareGuru.java | 15 +- .../cloud/hypervisor/guru/VMwareGuruTest.java | 304 ++++++++++++++++++ 2 files changed, 315 insertions(+), 4 deletions(-) diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index 7005dab2d763..b0989a122b8b 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -1348,7 +1348,13 @@ private VirtualMachineMO createCloneFromSourceVM(String vmName, VirtualMachineMO DatacenterMO dataCenterMO) throws Exception { HostMO sourceHost = vmMo.getRunningHost(); String cloneName = UUID.randomUUID().toString(); - DatastoreMO datastoreMO = vmMo.getAllDatastores().get(0); //pick the first datastore + List vmDatastores = vmMo.getAllDatastores(); + if (CollectionUtils.isEmpty(vmDatastores)) { + String err = String.format("Unable to fetch datastores, could not clone VM %s before migration from VMware", vmName); + s_logger.error(err); + throw new CloudRuntimeException(err); + } + DatastoreMO datastoreMO = vmDatastores.get(0); //pick the first datastore ManagedObjectReference morPool = vmMo.getRunningHost().getHyperHostOwnerResourcePool(); boolean result = vmMo.createFullClone(cloneName, dataCenterMO.getVmFolder(), morPool, datastoreMO.getMor(), Storage.ProvisioningType.THIN); VirtualMachineMO clonedVM = dataCenterMO.findVm(cloneName); @@ -1392,9 +1398,10 @@ public Pair cloneHypervisorVMAndCreateTemplateFileO } VirtualMachinePowerState sourceVmPowerState = vmMo.getPowerState(); if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON && isWindowsVm(vmMo)) { - s_logger.debug(String.format("VM %s is a Windows VM and its Running, cannot be imported." + - "Please gracefully shut it down before attempting the import", - vmName)); + String err = String.format("VM %s is a Windows VM and its Running, cannot be imported." + + "Please gracefully shut it down before attempting the import", vmName); + s_logger.error(err); + throw new CloudRuntimeException(err); } VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO); diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java index 06bf539c962a..f3139fce23cf 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java @@ -17,21 +17,34 @@ package com.cloud.hypervisor.guru; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.cloudstack.storage.NfsMountManager; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.vm.UnmanagedInstanceTO; import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; @@ -41,9 +54,19 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.MigrateVmToPoolCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.NfsTO; import com.cloud.dc.ClusterDetailsDao; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.vmware.mo.DatacenterMO; +import com.cloud.hypervisor.vmware.mo.DatastoreMO; +import com.cloud.hypervisor.vmware.mo.HostMO; +import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; +import com.cloud.hypervisor.vmware.util.VmwareClient; +import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.hypervisor.vmware.util.VmwareHelper; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; @@ -51,8 +74,14 @@ import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.Pair; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VmDetailConstants; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.ServiceContent; +import com.vmware.vim25.VimPortType; +import com.vmware.vim25.VirtualMachinePowerState; @RunWith(MockitoJUnitRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class) @@ -79,6 +108,21 @@ public class VMwareGuruTest { AutoCloseable closeable; + @Mock + NfsMountManager mountManager; + + private static MockedStatic mockedVmwareHelper; + + @BeforeClass + public static void init() { + mockedVmwareHelper = Mockito.mockStatic(VmwareHelper.class);; + } + + @AfterClass + public static void close() { + mockedVmwareHelper.close(); + } + @Before public void testSetUp() throws Exception { closeable = MockitoAnnotations.openMocks(this); @@ -155,4 +199,264 @@ public void createVolumeInfoFromVolumesTestCorrectlyConvertOfVolumes() { assertEquals(expected, result); } + + @Test(expected=CloudRuntimeException.class) + public void testCloneHypervisorVMAndCreateTemplateFile_NoExternalVM() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName); + })) { + vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + } + } + + @Test(expected=CloudRuntimeException.class) + public void testCloneHypervisorVMAndCreateTemplateFile_WindowsVMRunning() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); + HostMO hostMo = Mockito.mock(HostMO.class);; + Mockito.doReturn(VirtualMachinePowerState.POWERED_ON).when(vmMo).getPowerState(); + Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); + UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class); + Mockito.doReturn("Windows 2019").when(instance).getOperatingSystem(); + when(VmwareHelper.getUnmanagedInstance(hostMo, vmMo)).thenReturn(instance); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); + })) { + vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + } + } + + @Test(expected=CloudRuntimeException.class) + public void testCloneHypervisorVMAndCreateTemplateFile_GetDatastoresFailed() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); + HostMO hostMo = Mockito.mock(HostMO.class);; + Mockito.doReturn(VirtualMachinePowerState.POWERED_OFF).when(vmMo).getPowerState(); + Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); + List datastores = new ArrayList<>(); + Mockito.doReturn(datastores).when(vmMo).getAllDatastores(); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); + })) { + vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + } + } + + @Test(expected=CloudRuntimeException.class) + public void testCloneHypervisorVMAndCreateTemplateFile_CloneVMFailed() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); + HostMO hostMo = Mockito.mock(HostMO.class);; + Mockito.doReturn(VirtualMachinePowerState.POWERED_OFF).when(vmMo).getPowerState(); + Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); + Mockito.doReturn(mor).when(hostMo).getHyperHostOwnerResourcePool(); + DatastoreMO datastoreMO = Mockito.mock(DatastoreMO.class);; + List datastores = new ArrayList<>(); + datastores.add(datastoreMO); + Mockito.doReturn(datastores).when(vmMo).getAllDatastores(); + Mockito.lenient().doReturn(false).when(vmMo).createFullClone(anyString(), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(Storage.ProvisioningType.class)); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(anyString()); + Mockito.doReturn(mor).when(mockDatacenterMO).getVmFolder(); + Mockito.doReturn(mor).when(mockDatacenterMO).getMor(); + })) { + vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + } + } + + @Test + public void testRemoveClonedHypervisorVMAandTemplateFileOutOfBand_NoClonedVM() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "cloned-test-vm"; + String templateDirAndName = "f887b7b3-3d1f-4a7d-93e5-3147f58866c6"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName); + })) { + boolean result = vMwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(hostIp, vmName, params, dataStore, templateDirAndName); + assertFalse(result); + } + } + + @Test + public void testRemoveClonedHypervisorVMAandTemplateFileOutOfBand() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "cloned-test-vm"; + String templateDirAndName = "f887b7b3-3d1f-4a7d-93e5-3147f58866c6"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + NfsTO dataStore = Mockito.mock(NfsTO.class); + Mockito.doReturn("nfs://10.1.1.4/testdir").when(dataStore).getUrl(); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); + Mockito.doReturn(true).when(vmMo).destroy(); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); + })) { + boolean result = vMwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(hostIp, vmName, params, dataStore, templateDirAndName); + assertTrue(result); + } + } } From d187a12c271780a29e8efc96d9496dd259488c2c Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Mon, 8 Apr 2024 19:37:16 +0530 Subject: [PATCH 12/34] Separate clone VM and create template file for VMware migration - Export OVA (of the cloned VM) to the conversion location takes time. - Do any validations with cloned VM before creating the template (and fail early). - Updated unit tests. --- .../com/cloud/hypervisor/HypervisorGuru.java | 27 ++- .../com/cloud/hypervisor/guru/VMwareGuru.java | 62 ++++-- .../cloud/hypervisor/guru/VMwareGuruTest.java | 192 +++++++++++++++--- .../cloud/hypervisor/HypervisorGuruBase.java | 20 +- .../vm/UnmanagedVMsManagerImpl.java | 52 +++-- .../vm/UnmanagedVMsManagerImplTest.java | 13 +- 6 files changed, 300 insertions(+), 66 deletions(-) diff --git a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java index 2a8080d894fe..2d815f283bcc 100644 --- a/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/main/java/com/cloud/hypervisor/HypervisorGuru.java @@ -102,7 +102,7 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu * Will generate commands to migrate a vm to a pool. For now this will only work for stopped VMs on Vmware. * * @param vm the stopped vm to migrate - * @param destination the primary storage pool to migrate to + * @param volumeToPool the primary storage pools to migrate to * @return a list of commands to perform for a successful migration */ List finalizeMigrate(VirtualMachine vm, Map volumeToPool); @@ -113,21 +113,34 @@ boolean attachRestoredVolumeToVirtualMachine(long zoneId, String location, Backu * @param hostIp VM's source host IP * @param vmName name of the source VM to clone from * @param params hypervisor specific additional parameters - * @param templateLocation datastore to create the template file - * @return a reference to the cloned VM and created template dir/name + * @return a reference to the cloned VM */ - Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO templateLocation); + UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map params); /** * Removes a VM created as a clone of a VM on an external host * @param hostIp VM's source host IP * @param vmName name of the VM to remove * @param params hypervisor specific additional parameters + * @return true if the operation succeeds, false if not + */ + boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map params); + + /** + * Will create an OVA template of a VM on an external host (if the guru can handle) + * @param hostIp VM's source host IP + * @param vmName name of the source VM to create template from + * @param params hypervisor specific additional parameters + * @param templateLocation datastore to create the template file + * @return the created template dir/name + */ + String createVMTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation); + + /** + * Removes the template file on the location * @param templateLocation datastore to remove the template file * @param templateDirAndName the template dir to remove from datastore * @return true if the operation succeeds, false if not */ - boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO templateLocation, String templateDirAndName); + boolean removeVMTemplateFileOutOfBand(DataStoreTO templateLocation, String templateDirAndName); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index b0989a122b8b..6d56c5098ef6 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -1379,8 +1379,7 @@ private String createOVATemplateFileOfVM(VirtualMachineMO vmMO, DataStoreTO conv } @Override - public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, - Map params, DataStoreTO templateLocation) { + public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); @@ -1392,7 +1391,7 @@ public Pair cloneHypervisorVMAndCreateTemplateFileO DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter); VirtualMachineMO vmMo = dataCenterMO.findVm(vmName); if (vmMo == null) { - String err = String.format("Cannot find VM with name %s on %s/%s", vmName, vcenter, datacenter); + String err = String.format("Cannot find VM with name %s on vCenter %s/%s", vmName, vcenter, datacenter); s_logger.error(err); throw new CloudRuntimeException(err); } @@ -1406,12 +1405,10 @@ public Pair cloneHypervisorVMAndCreateTemplateFileO VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO); s_logger.debug(String.format("VM %s cloned successfully", vmName)); - String ovaTemplateDirAndName = createOVATemplateFileOfVM(clonedVM, templateLocation); - s_logger.debug(String.format("OVA %s/%s.ova created successfully on the datastore", ovaTemplateDirAndName, ovaTemplateDirAndName)); UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM); - setNicsFromSourceVM(clonedInstance, vmMo); + setDisksFromSourceVM(clonedInstance, vmMo); clonedInstance.setCloneSourcePowerState(sourceVmPowerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn : UnmanagedInstanceTO.PowerState.PowerOff); - return new Pair<> (clonedInstance, ovaTemplateDirAndName); + return clonedInstance; } catch (Exception e) { String err = String.format("Error cloning VM: %s from external vCenter %s: %s", vmName, vcenter, e.getMessage()); s_logger.error(err, e); @@ -1424,7 +1421,7 @@ private boolean isWindowsVm(VirtualMachineMO vmMo) throws Exception { return sourceInstance.getOperatingSystem().toLowerCase().contains("windows"); } - private void setNicsFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMachineMO vmMo) throws Exception { + private void setDisksFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMachineMO vmMo) throws Exception { UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo); List sourceDisks = sourceInstance.getDisks(); List clonedDisks = clonedInstance.getDisks(); @@ -1436,8 +1433,34 @@ private void setNicsFromSourceVM(UnmanagedInstanceTO clonedInstance, VirtualMach } @Override - public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, - DataStoreTO templateLocation, String templateDirAndName) { + public String createVMTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation) { + String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); + String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); + String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); + String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD); + s_logger.debug(String.format("Creating template of the VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter)); + + try { + VmwareContext context = connectToVcenter(vcenter, username, password); + DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter); + VirtualMachineMO vmMo = dataCenterMO.findVm(vmName); + if (vmMo == null) { + String err = String.format("Cannot find VM with name %s on vCenter %s/%s, to create template file", vmName, vcenter, datacenter); + s_logger.error(err); + throw new CloudRuntimeException(err); + } + String ovaTemplateDirAndName = createOVATemplateFileOfVM(vmMo, templateLocation); + s_logger.debug(String.format("OVA %s/%s.ova created successfully on the datastore", ovaTemplateDirAndName, ovaTemplateDirAndName)); + return ovaTemplateDirAndName; + } catch (Exception e) { + String err = String.format("Error create template file of the VM: %s from vCenter %s: %s", vmName, vcenter, e.getMessage()); + s_logger.error(err, e); + throw new CloudRuntimeException(err, e); + } + } + + @Override + public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST); String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME); String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME); @@ -1455,12 +1478,23 @@ public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, return false; } - boolean clonedVMDestroyed = vmMo.destroy(); + return vmMo.destroy(); + } catch (Exception e) { + String err = String.format("Error destroying cloned VM %s: %s", vmName, e.getMessage()); + s_logger.error(err, e); + return false; + } + } + + @Override + public boolean removeVMTemplateFileOutOfBand(DataStoreTO templateLocation, String templateDirAndName) { + s_logger.debug(String.format("Removing template file %s/%s.ova", templateDirAndName, templateDirAndName)); + + try { String dataStoreUrl = getDataStoreUrlForTemplate(templateLocation); - boolean templateDirDeleted = deleteDirOnStorage(templateDirAndName, dataStoreUrl, null); - return clonedVMDestroyed && templateDirDeleted; + return deleteDirOnStorage(templateDirAndName, dataStoreUrl, null); } catch (Exception e) { - String err = String.format("Error destroying cloned VM %s or template %s/%s.ova: %s", vmName, templateDirAndName, templateDirAndName, e.getMessage()); + String err = String.format("Error removing template file %s/%s.ova: %s", templateDirAndName, templateDirAndName, e.getMessage()); s_logger.error(err, e); return false; } diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java index f3139fce23cf..c7885e7b70d5 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java @@ -18,8 +18,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -74,6 +76,7 @@ import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.Pair; +import com.cloud.utils.UuidUtils; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; @@ -115,7 +118,7 @@ public class VMwareGuruTest { @BeforeClass public static void init() { - mockedVmwareHelper = Mockito.mockStatic(VmwareHelper.class);; + mockedVmwareHelper = Mockito.mockStatic(VmwareHelper.class); } @AfterClass @@ -201,7 +204,7 @@ public void createVolumeInfoFromVolumesTestCorrectlyConvertOfVolumes() { } @Test(expected=CloudRuntimeException.class) - public void testCloneHypervisorVMAndCreateTemplateFile_NoExternalVM() throws Exception { + public void testCloneHypervisorVM_NoExternalVM() throws Exception { String vCenterHost = "10.1.1.2"; String datacenterName = "datacenter"; String hostIp = "10.1.1.3"; @@ -212,7 +215,6 @@ public void testCloneHypervisorVMAndCreateTemplateFile_NoExternalVM() throws Exc params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); - DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); ServiceContent serviceContent = Mockito.mock(ServiceContent.class); VimPortType vimPort = Mockito.mock(VimPortType.class); @@ -234,12 +236,12 @@ public void testCloneHypervisorVMAndCreateTemplateFile_NoExternalVM() throws Exc }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName); })) { - vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + vMwareGuru.cloneHypervisorVMOutOfBand(hostIp, vmName, params); } } @Test(expected=CloudRuntimeException.class) - public void testCloneHypervisorVMAndCreateTemplateFile_WindowsVMRunning() throws Exception { + public void testCloneHypervisorVM_WindowsVMRunning() throws Exception { String vCenterHost = "10.1.1.2"; String datacenterName = "datacenter"; String hostIp = "10.1.1.3"; @@ -250,7 +252,6 @@ public void testCloneHypervisorVMAndCreateTemplateFile_WindowsVMRunning() throws params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); - DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); ServiceContent serviceContent = Mockito.mock(ServiceContent.class); VimPortType vimPort = Mockito.mock(VimPortType.class); @@ -261,7 +262,7 @@ public void testCloneHypervisorVMAndCreateTemplateFile_WindowsVMRunning() throws Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); - HostMO hostMo = Mockito.mock(HostMO.class);; + HostMO hostMo = Mockito.mock(HostMO.class); Mockito.doReturn(VirtualMachinePowerState.POWERED_ON).when(vmMo).getPowerState(); Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class); @@ -279,12 +280,12 @@ public void testCloneHypervisorVMAndCreateTemplateFile_WindowsVMRunning() throws }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); })) { - vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + vMwareGuru.cloneHypervisorVMOutOfBand(hostIp, vmName, params); } } @Test(expected=CloudRuntimeException.class) - public void testCloneHypervisorVMAndCreateTemplateFile_GetDatastoresFailed() throws Exception { + public void testCloneHypervisorVM_GetDatastoresFailed() throws Exception { String vCenterHost = "10.1.1.2"; String datacenterName = "datacenter"; String hostIp = "10.1.1.3"; @@ -295,7 +296,6 @@ public void testCloneHypervisorVMAndCreateTemplateFile_GetDatastoresFailed() thr params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); - DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); ServiceContent serviceContent = Mockito.mock(ServiceContent.class); VimPortType vimPort = Mockito.mock(VimPortType.class); @@ -306,7 +306,7 @@ public void testCloneHypervisorVMAndCreateTemplateFile_GetDatastoresFailed() thr Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); - HostMO hostMo = Mockito.mock(HostMO.class);; + HostMO hostMo = Mockito.mock(HostMO.class); Mockito.doReturn(VirtualMachinePowerState.POWERED_OFF).when(vmMo).getPowerState(); Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); List datastores = new ArrayList<>(); @@ -323,12 +323,12 @@ public void testCloneHypervisorVMAndCreateTemplateFile_GetDatastoresFailed() thr }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); })) { - vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + vMwareGuru.cloneHypervisorVMOutOfBand(hostIp, vmName, params); } } @Test(expected=CloudRuntimeException.class) - public void testCloneHypervisorVMAndCreateTemplateFile_CloneVMFailed() throws Exception { + public void testCloneHypervisorVM_CloneVMFailed() throws Exception { String vCenterHost = "10.1.1.2"; String datacenterName = "datacenter"; String hostIp = "10.1.1.3"; @@ -339,7 +339,6 @@ public void testCloneHypervisorVMAndCreateTemplateFile_CloneVMFailed() throws Ex params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); - DataStoreTO dataStore = Mockito.mock(DataStoreTO.class); ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); ServiceContent serviceContent = Mockito.mock(ServiceContent.class); VimPortType vimPort = Mockito.mock(VimPortType.class); @@ -350,11 +349,12 @@ public void testCloneHypervisorVMAndCreateTemplateFile_CloneVMFailed() throws Ex Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); - HostMO hostMo = Mockito.mock(HostMO.class);; + HostMO hostMo = Mockito.mock(HostMO.class); Mockito.doReturn(VirtualMachinePowerState.POWERED_OFF).when(vmMo).getPowerState(); Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); Mockito.doReturn(mor).when(hostMo).getHyperHostOwnerResourcePool(); - DatastoreMO datastoreMO = Mockito.mock(DatastoreMO.class);; + DatastoreMO datastoreMO = Mockito.mock(DatastoreMO.class); + Mockito.doReturn(mor).when(datastoreMO).getMor(); List datastores = new ArrayList<>(); datastores.add(datastoreMO); Mockito.doReturn(datastores).when(vmMo).getAllDatastores(); @@ -373,17 +373,75 @@ public void testCloneHypervisorVMAndCreateTemplateFile_CloneVMFailed() throws Ex Mockito.doReturn(mor).when(mockDatacenterMO).getVmFolder(); Mockito.doReturn(mor).when(mockDatacenterMO).getMor(); })) { - vMwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + vMwareGuru.cloneHypervisorVMOutOfBand(hostIp, vmName, params); } } @Test - public void testRemoveClonedHypervisorVMAandTemplateFileOutOfBand_NoClonedVM() throws Exception { + public void testCloneHypervisorVM() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); + HostMO hostMo = Mockito.mock(HostMO.class); + Mockito.doReturn(VirtualMachinePowerState.POWERED_OFF).when(vmMo).getPowerState(); + Mockito.doReturn(hostMo).when(vmMo).getRunningHost(); + Mockito.doReturn(mor).when(hostMo).getHyperHostOwnerResourcePool(); + Mockito.doReturn(mor).when(hostMo).getMor(); + DatastoreMO datastoreMO = Mockito.mock(DatastoreMO.class); + Mockito.doReturn(mor).when(datastoreMO).getMor(); + List datastores = new ArrayList<>(); + datastores.add(datastoreMO); + Mockito.doReturn(datastores).when(vmMo).getAllDatastores(); + Mockito.lenient().doReturn(true).when(vmMo).createFullClone(anyString(), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(ManagedObjectReference.class), any(Storage.ProvisioningType.class)); + UnmanagedInstanceTO instance = Mockito.mock(UnmanagedInstanceTO.class); + when(VmwareHelper.getUnmanagedInstance(hostMo, vmMo)).thenReturn(instance); + UnmanagedInstanceTO.Disk disk = Mockito.mock(UnmanagedInstanceTO.Disk.class); + Mockito.doReturn("1").when(disk).getDiskId(); + List disks = new ArrayList<>(); + disks.add(disk); + Mockito.doReturn(disks).when(instance).getDisks(); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(anyString()); + Mockito.doReturn(mor).when(mockDatacenterMO).getVmFolder(); + Mockito.doReturn(mor).when(mockDatacenterMO).getMor(); + })) { + UnmanagedInstanceTO clonedVm = vMwareGuru.cloneHypervisorVMOutOfBand(hostIp, vmName, params); + assertNotNull(clonedVm); + } + } + + @Test(expected=CloudRuntimeException.class) + public void testCreateVMTemplateFileOutOfBand_NoClonedVM() throws Exception { String vCenterHost = "10.1.1.2"; String datacenterName = "datacenter"; String hostIp = "10.1.1.3"; String vmName = "cloned-test-vm"; - String templateDirAndName = "f887b7b3-3d1f-4a7d-93e5-3147f58866c6"; Map params = new HashMap<>(); params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); @@ -412,26 +470,103 @@ public void testRemoveClonedHypervisorVMAandTemplateFileOutOfBand_NoClonedVM() t }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName); })) { - boolean result = vMwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(hostIp, vmName, params, dataStore, templateDirAndName); - assertFalse(result); + vMwareGuru.createVMTemplateFileOutOfBand(hostIp, vmName, params, dataStore); } } @Test - public void testRemoveClonedHypervisorVMAandTemplateFileOutOfBand() throws Exception { + public void testCreateVMTemplateFileOutOfBand() throws Exception { String vCenterHost = "10.1.1.2"; String datacenterName = "datacenter"; String hostIp = "10.1.1.3"; String vmName = "cloned-test-vm"; - String templateDirAndName = "f887b7b3-3d1f-4a7d-93e5-3147f58866c6"; Map params = new HashMap<>(); params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + VirtualMachineMO vmMo = Mockito.mock(VirtualMachineMO.class); + Mockito.doNothing().when(vmMo).exportVm(anyString(), anyString(), anyBoolean(), anyBoolean()); NfsTO dataStore = Mockito.mock(NfsTO.class); Mockito.doReturn("nfs://10.1.1.4/testdir").when(dataStore).getUrl(); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); + })) { + String templateDirAndName = vMwareGuru.createVMTemplateFileOutOfBand(hostIp, vmName, params, dataStore); + assertNotNull(templateDirAndName); + assertTrue(UuidUtils.isUuid(templateDirAndName)); + } + } + + @Test + public void testRemoveClonedHypervisorVM_NoClonedVM() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "cloned-test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); + ServiceContent serviceContent = Mockito.mock(ServiceContent.class); + VimPortType vimPort = Mockito.mock(VimPortType.class); + VmwareClient vimClient = spy(new VmwareClient(vCenterHost)); + VmwareContext vmwareContext = spy(new VmwareContext(vimClient, vCenterHost)); + Mockito.doReturn(vimClient).when(vmwareContext).getVimClient(); + Mockito.doReturn(mor).when(vmwareContext).getRootFolder(); + Mockito.doReturn(mor).when(vimClient).getDecendentMoRef(any(ManagedObjectReference.class), anyString(), anyString()); + DatacenterMO dataCenterMO = spy(new DatacenterMO(vmwareContext, datacenterName)); + + try (MockedConstruction ignored1 = Mockito.mockConstruction(VmwareClient.class, withSettings().spiedInstance(vimClient), (mockVmwareClient, contextVmwareClient) -> { + Mockito.doReturn(vimPort).when(mockVmwareClient).getService(); + Mockito.doReturn(serviceContent).when(mockVmwareClient).getServiceContent(); + Mockito.doNothing().when(mockVmwareClient).connect(anyString(), anyString(), anyString()); + Mockito.doReturn(mor).when(mockVmwareClient).getRootFolder(); + }); MockedConstruction ignored2 = Mockito.mockConstruction(VmwareContext.class, withSettings().spiedInstance(vmwareContext), (mockVmwareContext, contextVmwareContext) -> { + Mockito.doReturn(vimClient).when(mockVmwareContext).getVimClient(); + Mockito.doReturn(mor).when(mockVmwareContext).getRootFolder(); + }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { + Mockito.doReturn(null).when(mockDatacenterMO).findVm(vmName); + })) { + boolean result = vMwareGuru.removeClonedHypervisorVMOutOfBand(hostIp, vmName, params); + assertFalse(result); + } + } + + @Test + public void testRemoveClonedHypervisorVM() throws Exception { + String vCenterHost = "10.1.1.2"; + String datacenterName = "datacenter"; + String hostIp = "10.1.1.3"; + String vmName = "cloned-test-vm"; + Map params = new HashMap<>(); + params.put(VmDetailConstants.VMWARE_VCENTER_HOST, vCenterHost); + params.put(VmDetailConstants.VMWARE_DATACENTER_NAME, datacenterName); + params.put(VmDetailConstants.VMWARE_VCENTER_USERNAME, "username"); + params.put(VmDetailConstants.VMWARE_VCENTER_PASSWORD, "password"); + ManagedObjectReference mor = Mockito.mock(ManagedObjectReference.class); ServiceContent serviceContent = Mockito.mock(ServiceContent.class); VimPortType vimPort = Mockito.mock(VimPortType.class); @@ -455,8 +590,17 @@ public void testRemoveClonedHypervisorVMAandTemplateFileOutOfBand() throws Excep }); MockedConstruction ignored3 = Mockito.mockConstruction(DatacenterMO.class, withSettings().spiedInstance(dataCenterMO), (mockDatacenterMO, contextDatacenterMO) -> { Mockito.doReturn(vmMo).when(mockDatacenterMO).findVm(vmName); })) { - boolean result = vMwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(hostIp, vmName, params, dataStore, templateDirAndName); + boolean result = vMwareGuru.removeClonedHypervisorVMOutOfBand(hostIp, vmName, params); assertTrue(result); } } + + @Test + public void testRemoveVMTemplateFileOutOfBand() throws Exception { + NfsTO dataStore = Mockito.mock(NfsTO.class); + Mockito.doReturn("nfs://10.1.1.4/testdir").when(dataStore).getUrl(); + String templateDirAndName = "f887b7b3-3d1f-4a7d-93e5-3147f58866c6"; + boolean result = vMwareGuru.removeVMTemplateFileOutOfBand(dataStore, templateDirAndName); + assertTrue(result); + } } diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index f53a2b2e156a..4d9101dcfdb5 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -378,14 +378,26 @@ public ConfigKey[] getConfigKeys() { } @Override - public Pair cloneHypervisorVMAndCreateTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation) { - s_logger.error("Unsupported operation: cannot clone external VM / create template file"); + public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { + s_logger.error("Unsupported operation: cannot clone external VM"); return null; } @Override - public boolean removeClonedHypervisorVMAandTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation, String templateDirAndName) { - s_logger.error("Unsupported operation: cannot remove external VM and template file"); + public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map params) { + s_logger.error("Unsupported operation: cannot remove external VM"); + return false; + } + + @Override + public String createVMTemplateFileOutOfBand(String hostIp, String vmName, Map params, DataStoreTO templateLocation) { + s_logger.error("Unsupported operation: cannot create template file"); + return null; + } + + @Override + public boolean removeVMTemplateFileOutOfBand(DataStoreTO templateLocation, String templateDirAndName) { + s_logger.error("Unsupported operation: cannot remove template file"); return false; } } diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 58e09f2d6aa7..dba90a48a1b2 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -1531,13 +1531,26 @@ private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cl return userVm; } - private Pair cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(String vcenter, String datacenterName, String username, String password, String clusterName, String sourceHostName, String sourceVM, DataStoreTO convertLocation) { + private UnmanagedInstanceTO cloneSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, + String password, String clusterName, String sourceHostName, + String sourceVM) { HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); Map params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM); - return vmwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(sourceHostName, sourceVM, params, convertLocation); + return vmwareGuru.cloneHypervisorVMOutOfBand(sourceHostName, sourceVM, params); + } + + private String createOvaTemplateFileOfSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username, + String password, String clusterName, String sourceHostName, + String sourceVM, DataStoreTO convertLocation) { + HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); + + Map params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName, + username, password, clusterName, sourceHostName, sourceVM); + + return vmwareGuru.createVMTemplateFileOutOfBand(sourceHostName, sourceVM, params, convertLocation); } protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster destinationCluster, VMTemplateVO template, @@ -1584,12 +1597,11 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster try { temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId); List convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster); - Pair clonedInstanceAndOvaTemplate = cloneSourceVmwareUnmanagedInstanceAndCreateOvaTemplateFile(vcenter, datacenterName, username, password, - clusterName, sourceHostName, sourceVM, temporaryConvertLocation); - clonedInstance = clonedInstanceAndOvaTemplate.first(); - ovaTemplateDirAndNameOnConvertLocation = clonedInstanceAndOvaTemplate.second(); + clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVM); String instanceName = getGeneratedInstanceName(owner); checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced); + ovaTemplateDirAndNameOnConvertLocation = createOvaTemplateFileOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, + clusterName, sourceHostName, clonedInstance.getName(), temporaryConvertLocation); UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(sourceVM, clonedInstance, destinationCluster, convertStoragePools, convertInstanceHostId, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); sanitizeConvertedInstance(convertedInstance, clonedInstance); UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null, @@ -1606,7 +1618,10 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); } finally { if (clonedInstance != null) { - removeClonedInstanceAndTemplateFile(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM, temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); + removeClonedInstance(vcenter, datacenterName, username, password, sourceHostName, clonedInstance.getName(), sourceVM); + } + if (temporaryConvertLocation != null && StringUtils.isNotBlank(ovaTemplateDirAndNameOnConvertLocation)) { + removeTemplateFile(temporaryConvertLocation, ovaTemplateDirAndNameOnConvertLocation); } } } @@ -1700,23 +1715,34 @@ private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, Un } } - private void removeClonedInstanceAndTemplateFile(String vcenter, String datacenterName, - String username, String password, - String sourceHostName, String clonedInstanceName, - String sourceVM, DataStoreTO convertLocation, String ovaTemplateOnConvertLocation) { + private void removeClonedInstance(String vcenter, String datacenterName, String username, String password, + String sourceHostName, String clonedInstanceName, String sourceVM) { HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); Map params = createParamsForRemoveClonedInstance(vcenter, datacenterName, username, password, sourceVM); - boolean result = vmwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(sourceHostName, clonedInstanceName, params, convertLocation, ovaTemplateOnConvertLocation); + boolean result = vmwareGuru.removeClonedHypervisorVMOutOfBand(sourceHostName, clonedInstanceName, params); if (!result) { String msg = String.format("Could not properly remove the cloned instance %s from VMware datacenter %s:%s", clonedInstanceName, vcenter, datacenterName); LOGGER.warn(msg); return; } - LOGGER.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s:%s", + LOGGER.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s/%s", clonedInstanceName, vcenter, datacenterName)); } + private void removeTemplateFile(DataStoreTO convertLocation, String ovaTemplateOnConvertLocation) { + HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware); + boolean result = vmwareGuru.removeVMTemplateFileOutOfBand(convertLocation, ovaTemplateOnConvertLocation); + if (!result) { + String msg = String.format("Could not remove the template file %s on datastore %s", + ovaTemplateOnConvertLocation, convertLocation.getUrl()); + LOGGER.warn(msg); + return; + } + LOGGER.debug(String.format("Removed the template file %s on datastore %s", + ovaTemplateOnConvertLocation, convertLocation.getUrl())); + } + private Map createParamsForRemoveClonedInstance(String vcenter, String datacenterName, String username, String password, String sourceVM) { Map params = new HashMap<>(); diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index 94b80b6b7858..457a01530b5b 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -586,6 +586,7 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, String host = "192.168.1.10"; String vmName = "TestInstanceFromVmware"; instance.setName(vmName); + String tmplFileName = "5b8d689a-e61a-4ac3-9b76-e121ff90fbd3"; long newVmId = 2L; long networkId = 1L; when(vmDao.getNextInSequence(Long.class, "id")).thenReturn(newVmId); @@ -614,8 +615,10 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, HypervisorGuru vmwareGuru = mock(HypervisorGuru.class); when(hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware)).thenReturn(vmwareGuru); - when(vmwareGuru.cloneHypervisorVMAndCreateTemplateFileOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class))).thenReturn(new Pair<>(instance, "")); - when(vmwareGuru.removeClonedHypervisorVMAandTemplateFileOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class), anyString())).thenReturn(true); + when(vmwareGuru.cloneHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(instance); + when(vmwareGuru.removeClonedHypervisorVMOutOfBand(anyString(), anyString(), anyMap())).thenReturn(true); + when(vmwareGuru.createVMTemplateFileOutOfBand(anyString(), anyString(), anyMap(), any(DataStoreTO.class))).thenReturn(tmplFileName); + when(vmwareGuru.removeVMTemplateFileOutOfBand(any(DataStoreTO.class), anyString())).thenReturn(true); HostVO convertHost = mock(HostVO.class); long convertHostId = 1L; @@ -690,8 +693,10 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { unmanagedVMsManager.importVm(importVmCmd); - verify(vmwareGuru).cloneHypervisorVMAndCreateTemplateFileOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class)); - verify(vmwareGuru).removeClonedHypervisorVMAandTemplateFileOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class), anyString()); + verify(vmwareGuru).cloneHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap()); + verify(vmwareGuru).createVMTemplateFileOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap(), any(DataStoreTO.class)); + verify(vmwareGuru).removeClonedHypervisorVMOutOfBand(Mockito.eq(host), Mockito.eq(vmName), anyMap()); + verify(vmwareGuru).removeVMTemplateFileOutOfBand(any(DataStoreTO.class), anyString()); } } From 8f3141fc89a4866d13486043c4d9d808fa47ca1d Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 10 Apr 2024 22:42:11 +0530 Subject: [PATCH 13/34] Check conversion support on host before clone vm / create template on vmware (and fail early) --- .../agent/api/CheckConvertInstanceAnswer.java | 32 ++++++++ .../api/CheckConvertInstanceCommand.java | 27 ++++++ .../agent/api/ConvertInstanceCommand.java | 9 +- ...irtCheckConvertInstanceCommandWrapper.java | 54 ++++++++++++ .../LibvirtConvertInstanceCommandWrapper.java | 2 +- ...heckConvertInstanceCommandWrapperTest.java | 82 +++++++++++++++++++ .../vm/UnmanagedVMsManagerImpl.java | 45 ++++++++-- .../vm/UnmanagedVMsManagerImplTest.java | 16 +++- 8 files changed, 254 insertions(+), 13 deletions(-) create mode 100644 core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java create mode 100644 core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java create mode 100644 plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java create mode 100644 plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapperTest.java diff --git a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java new file mode 100644 index 000000000000..69e5c43647d2 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java @@ -0,0 +1,32 @@ +// 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 com.cloud.agent.api; + +public class CheckConvertInstanceAnswer extends Answer { + + public CheckConvertInstanceAnswer() { + super(); + } + + public CheckConvertInstanceAnswer(Command command, boolean success) { + super(command, success, ""); + } + + public CheckConvertInstanceAnswer(Command command, boolean success, String details) { + super(command, success, details); + } +} diff --git a/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java new file mode 100644 index 000000000000..8ccb9ed45ac9 --- /dev/null +++ b/core/src/main/java/com/cloud/agent/api/CheckConvertInstanceCommand.java @@ -0,0 +1,27 @@ +// 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 com.cloud.agent.api; + +public class CheckConvertInstanceCommand extends Command { + public CheckConvertInstanceCommand() { + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java index 48db9af0b11f..3f9b6bb734c3 100644 --- a/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java +++ b/core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java @@ -29,17 +29,20 @@ public class ConvertInstanceCommand extends Command { private List destinationStoragePools; private DataStoreTO conversionTemporaryLocation; private String templateDirAndNameOnConversionLocation; + private boolean checkConversionSupport; public ConvertInstanceCommand() { } public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, - List destinationStoragePools, DataStoreTO conversionTemporaryLocation, String templateDirAndNameOnConversionLocation) { + List destinationStoragePools, DataStoreTO conversionTemporaryLocation, + String templateDirAndNameOnConversionLocation, boolean checkConversionSupport) { this.sourceInstance = sourceInstance; this.destinationHypervisorType = destinationHypervisorType; this.destinationStoragePools = destinationStoragePools; this.conversionTemporaryLocation = conversionTemporaryLocation; this.templateDirAndNameOnConversionLocation = templateDirAndNameOnConversionLocation; + this.checkConversionSupport = checkConversionSupport; } public RemoteInstanceTO getSourceInstance() { @@ -62,6 +65,10 @@ public String getTemplateDirAndNameOnConversionLocation() { return templateDirAndNameOnConversionLocation; } + public boolean getCheckConversionSupport() { + return checkConversionSupport; + } + @Override public boolean executeInSequence() { return false; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java new file mode 100644 index 000000000000..ba66981fe330 --- /dev/null +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java @@ -0,0 +1,54 @@ +// +// 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckConvertInstanceAnswer; +import com.cloud.agent.api.CheckConvertInstanceCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.resource.CommandWrapper; +import com.cloud.resource.ResourceWrapper; +import com.cloud.utils.script.Script; + +@ResourceWrapper(handles = CheckConvertInstanceCommand.class) +public class LibvirtCheckConvertInstanceCommandWrapper extends CommandWrapper { + + private static final Logger s_logger = Logger.getLogger(LibvirtCheckConvertInstanceCommandWrapper.class); + + protected static final String checkIfConversionIsSupportedCommand = "which virt-v2v"; + + @Override + public Answer execute(CheckConvertInstanceCommand cmd, LibvirtComputingResource serverResource) { + if (!isInstanceConversionSupportedOnHost()) { + String msg = String.format("Cannot convert the instance from VMware as the virt-v2v binary is not found on host %s (%s). " + + "Please install virt-v2v on the host before attempting the instance conversion", serverResource.getName(), serverResource.getPrivateIp()); + s_logger.info(msg); + return new CheckConvertInstanceAnswer(cmd, false, msg); + } + + return new CheckConvertInstanceAnswer(cmd, true, ""); + } + + protected boolean isInstanceConversionSupportedOnHost() { + int exitValue = Script.runSimpleBashScriptForExitValue(checkIfConversionIsSupportedCommand); + return exitValue == 0; + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java index 2783e2985e9f..8028a99ebef6 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java @@ -79,7 +79,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve String sourceOVAFile = ovaTemplateDirAndNameOnConversionLocation + ".ova"; long timeout = (long) cmd.getWait() * 1000; - if (!isInstanceConversionSupportedOnHost()) { + if (cmd.getCheckConversionSupport() && !isInstanceConversionSupportedOnHost()) { String msg = String.format("Cannot convert the instance %s from VMware as the virt-v2v binary is not found. " + "Please install virt-v2v on the host before attempting the instance conversion", sourceInstanceName); s_logger.info(msg); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapperTest.java new file mode 100644 index 000000000000..603dc551f04b --- /dev/null +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapperTest.java @@ -0,0 +1,82 @@ +// +// 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 com.cloud.hypervisor.kvm.resource.wrapper; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckConvertInstanceCommand; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.utils.script.Script; + +@RunWith(MockitoJUnitRunner.class) +public class LibvirtCheckConvertInstanceCommandWrapperTest { + + @Spy + private LibvirtCheckConvertInstanceCommandWrapper checkConvertInstanceCommandWrapper = Mockito.spy(LibvirtCheckConvertInstanceCommandWrapper.class); + + @Mock + private LibvirtComputingResource libvirtComputingResourceMock; + + @Mock + CheckConvertInstanceCommand checkConvertInstanceCommandMock; + + @Before + public void setUp() { + } + + @Test + public void testIsInstanceConversionSupportedOnHost() { + try (MockedStatic