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 67139120fa08..187afece84fc 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -536,7 +536,7 @@ private Map getNicIpAddresses(final List pools = primaryDataStoreDao.listPoolByHostPath(dsHost, dsPath); for (StoragePool pool : pools) { if (pool.getDataCenterId() == zone.getId() && - (pool.getClusterId() == null || pool.getClusterId().equals(cluster.getId()))) { + (pool.getClusterId() == null || pool.getClusterId().equals(cluster.getId())) && + volumeApiService.doesTargetStorageSupportDiskOffering(pool, diskOfferingTags)) { storagePool = pool; break; } @@ -558,7 +559,8 @@ private StoragePool getStoragePool(final UnmanagedInstanceTO.Disk disk, final Da pools.addAll(primaryDataStoreDao.listByDataCenterId(zone.getId())); for (StoragePool pool : pools) { String searchPoolParam = StringUtils.isNotBlank(dsPath) ? dsPath : dsName; - if (StringUtils.contains(pool.getPath(), searchPoolParam)) { + if (StringUtils.contains(pool.getPath(), searchPoolParam) && + volumeApiService.doesTargetStorageSupportDiskOffering(pool, diskOfferingTags)) { storagePool = pool; break; } @@ -621,7 +623,8 @@ private void checkUnmanagedDiskAndOfferingForImport(String instanceName, Unmanag if (diskOffering != null && !diskOffering.isCustomized() && diskOffering.getDiskSize() < disk.getCapacity()) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Size of disk offering(ID: %s) %dGB is found less than the size of disk(ID: %s) %dGB during VM import", diskOffering.getUuid(), (diskOffering.getDiskSize() / Resource.ResourceType.bytesToGiB), disk.getDiskId(), (disk.getCapacity() / (Resource.ResourceType.bytesToGiB)))); } - StoragePool storagePool = getStoragePool(disk, zone, cluster); + diskOffering = diskOffering != null ? diskOffering : diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + StoragePool storagePool = getStoragePool(disk, zone, cluster, diskOffering != null ? diskOffering.getTags() : null); if (diskOffering != null && !migrateAllowed && !storagePoolSupportsDiskOffering(storagePool, diskOffering)) { throw new InvalidParameterValueException(String.format("Disk offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", diskOffering.getUuid(), storagePool.getUuid(), disk.getDiskId())); } @@ -858,7 +861,7 @@ private Pair importDisk(UnmanagedInstanceTO.Disk disk, diskInfo.setDiskChain(new String[]{disk.getImagePath()}); chainInfo = gson.toJson(diskInfo); } - StoragePool storagePool = getStoragePool(disk, zone, cluster); + StoragePool storagePool = getStoragePool(disk, zone, cluster, diskOffering != null ? diskOffering.getTags() : null); DiskProfile profile = volumeManager.importVolume(type, name, diskOffering, diskSize, minIops, maxIops, vm.getDataCenterId(), vm.getHypervisorType(), vm, template, owner, deviceId, storagePool.getId(), path, chainInfo); @@ -1612,7 +1615,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster temporaryConvertLocation = selectInstanceConversionTemporaryLocation( destinationCluster, convertHost, convertStoragePoolId); - List convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster); + List convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster, serviceOffering, dataDiskOfferingMap); long importStartTime = System.currentTimeMillis(); Pair sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName); sourceVMwareInstance = sourceInstanceDetails.first(); @@ -1628,15 +1631,18 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster if (cmd.getForceMsToImportVmFiles() || !conversionSupportAnswer.isOvfExportSupported()) { // Uses MS for OVF export to temporary conversion location int noOfThreads = UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.value(); - ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, - clusterName, sourceHostName, sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads); + ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance( + vcenter, datacenterName, username, password, clusterName, sourceHostName, + sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads); convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName, - sourceVMwareInstance, convertHost, importHost, convertStoragePools, - temporaryConvertLocation, ovfTemplateOnConvertLocation); + sourceVMwareInstance, convertHost, importHost, convertStoragePools, + serviceOffering, dataDiskOfferingMap, temporaryConvertLocation, + ovfTemplateOnConvertLocation); } else { // Uses KVM Host for OVF export to temporary conversion location, through ovftool convertedInstance = convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation( - sourceVMName, sourceVMwareInstance, convertHost, importHost, convertStoragePools, + sourceVMName, sourceVMwareInstance, convertHost, importHost, + convertStoragePools, serviceOffering, dataDiskOfferingMap, temporaryConvertLocation, vcenter, username, password, datacenterName); } @@ -1726,9 +1732,9 @@ private void sanitizeConvertedInstance(UnmanagedInstanceTO convertedInstance, Un convertedInstance.setPowerState(UnmanagedInstanceTO.PowerState.PowerOff); List convertedInstanceDisks = convertedInstance.getDisks(); List sourceVMwareInstanceDisks = sourceVMwareInstance.getDisks(); - for (int i = 0; i < convertedInstanceDisks.size(); i++) { - UnmanagedInstanceTO.Disk disk = convertedInstanceDisks.get(i); - disk.setDiskId(sourceVMwareInstanceDisks.get(i).getDiskId()); + for (UnmanagedInstanceTO.Disk sourceVMwareInstanceDisk : sourceVMwareInstanceDisks) { + UnmanagedInstanceTO.Disk convertedDisk = convertedInstanceDisks.get(sourceVMwareInstanceDisk.getPosition()); + convertedDisk.setDiskId(sourceVMwareInstanceDisk.getDiskId()); } List convertedInstanceNics = convertedInstance.getNics(); List sourceVMwareInstanceNics = sourceVMwareInstance.getNics(); @@ -1912,16 +1918,16 @@ private CheckConvertInstanceAnswer checkConversionSupportOnHost(HostVO convertHo } private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation( - String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, - HostVO convertHost, HostVO importHost, - List convertStoragePools, DataStoreTO temporaryConvertLocation, - String ovfTemplateDirConvertLocation + String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost, + HostVO importHost, List convertStoragePools, + ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, + DataStoreTO temporaryConvertLocation, String ovfTemplateDirConvertLocation ) { LOGGER.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) using OVF %s on conversion datastore", sourceVM, convertHost.getId(), convertHost.getName(), ovfTemplateDirConvertLocation)); RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM); - List destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks()); + List destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks(), serviceOffering, dataDiskOfferingMap); ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, temporaryConvertLocation, ovfTemplateDirConvertLocation, false, false); int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60; @@ -1932,16 +1938,17 @@ private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation( } private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation( - String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, - HostVO convertHost, HostVO importHost, List convertStoragePools, - DataStoreTO temporaryConvertLocation, String vcenterHost, - String vcenterUsername, String vcenterPassword, String datacenterName + String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost, + HostVO importHost, List convertStoragePools, + ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap, + DataStoreTO temporaryConvertLocation, String vcenterHost, String vcenterUsername, + String vcenterPassword, String datacenterName ) { LOGGER.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) after OVF export through ovftool", sourceVM, convertHost.getId(), convertHost.getName())); RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVMwareInstance.getName(), vcenterHost, vcenterUsername, vcenterPassword, datacenterName); - List destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks()); + List destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks(), serviceOffering, dataDiskOfferingMap); ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO, Hypervisor.HypervisorType.KVM, temporaryConvertLocation, null, false, true); int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60; @@ -2004,12 +2011,31 @@ private UnmanagedInstanceTO convertAndImportToKVM(ConvertInstanceCommand convert return ((ImportConvertedInstanceAnswer) importAnswer).getConvertedInstance(); } - private List findInstanceConversionStoragePoolsInCluster(Cluster destinationCluster) { + private List findInstanceConversionStoragePoolsInCluster( + Cluster destinationCluster, ServiceOfferingVO serviceOffering, + Map dataDiskOfferingMap + ) { List pools = new ArrayList<>(); - List clusterPools = primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem); - pools.addAll(clusterPools); - List zonePools = primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem); - pools.addAll(zonePools); + pools.addAll(primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)); + pools.addAll(primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem)); + List diskOfferingTags = new ArrayList<>(); + for (Long diskOfferingId : dataDiskOfferingMap.values()) { + DiskOfferingVO diskOffering = diskOfferingDao.findById(diskOfferingId); + if (diskOffering == null) { + String msg = String.format("Cannot find disk offering with ID %s", diskOfferingId); + LOGGER.error(msg); + throw new CloudRuntimeException(msg); + } + diskOfferingTags.add(diskOffering.getTags()); + } + if (serviceOffering.getDiskOfferingId() != null) { + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + if (diskOffering != null) { + diskOfferingTags.add(diskOffering.getTags()); + } + } + + pools = getPoolsWithMatchingTags(pools, diskOfferingTags); if (pools.isEmpty()) { String msg = String.format("Cannot find suitable storage pools in cluster %s for the conversion", destinationCluster.getName()); LOGGER.error(msg); @@ -2018,12 +2044,54 @@ private List findInstanceConversionStoragePoolsInCluster(Cluster return pools; } - private List selectInstanceConversionStoragePools(List pools, List disks) { + private List getPoolsWithMatchingTags(List pools, List diskOfferingTags) { + if (diskOfferingTags.isEmpty()) { + return pools; + } + List poolsSupportingTags = new ArrayList<>(pools); + for (String tags : diskOfferingTags) { + boolean tagsMatched = false; + for (StoragePoolVO pool : pools) { + if (volumeApiService.doesTargetStorageSupportDiskOffering(pool, tags)) { + poolsSupportingTags.add(pool); + tagsMatched = true; + } + } + if (!tagsMatched) { + String msg = String.format("Cannot find suitable storage pools for the conversion with disk offering tags %s", tags); + LOGGER.error(msg); + throw new CloudRuntimeException(msg); + } + } + return poolsSupportingTags; + } + + private List selectInstanceConversionStoragePools( + List pools, List disks, + ServiceOfferingVO serviceOffering, Map dataDiskOfferingMap + ) { List storagePools = new ArrayList<>(disks.size()); - //TODO: Choose pools by capacity + for (int i = 0; i < disks.size(); i++) { + storagePools.add(null); + } + Set dataDiskIds = dataDiskOfferingMap.keySet(); for (UnmanagedInstanceTO.Disk disk : disks) { - Long capacity = disk.getCapacity(); - storagePools.add(pools.get(0).getUuid()); + Long diskOfferingId = dataDiskOfferingMap.get(disk.getDiskId()); + if (diskOfferingId == null && !dataDiskIds.contains(disk.getDiskId())) { + diskOfferingId = serviceOffering.getDiskOfferingId(); + } + //TODO: Choose pools by capacity + if (diskOfferingId == null) { + storagePools.set(disk.getPosition(), pools.get(0).getUuid()); + } else { + DiskOfferingVO diskOffering = diskOfferingDao.findById(diskOfferingId); + for (StoragePoolVO pool : pools) { + if (volumeApiService.doesTargetStorageSupportDiskOffering(pool, diskOffering.getTags())) { + storagePools.set(disk.getPosition(), pool.getUuid()); + break; + } + } + } } return storagePools; } 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 e9a587608286..85402c0e2548 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -277,6 +277,7 @@ public void setUp() throws Exception { List instanceDisks = new ArrayList<>(); UnmanagedInstanceTO.Disk instanceDisk = new UnmanagedInstanceTO.Disk(); instanceDisk.setDiskId("1000-1"); + instanceDisk.setPosition(0); instanceDisk.setLabel("DiskLabel"); instanceDisk.setController("scsi"); instanceDisk.setImagePath("[b6ccf44a1fa13e29b3667b4954fa10ee] TestInstance/ROOT-1.vmdk"); @@ -423,6 +424,7 @@ public void importUnmanagedInstanceTest() { ImportUnmanagedInstanceCmd importUnmanageInstanceCmd = Mockito.mock(ImportUnmanagedInstanceCmd.class); when(importUnmanageInstanceCmd.getName()).thenReturn("TestInstance"); when(importUnmanageInstanceCmd.getDomainId()).thenReturn(null); + when(volumeApiService.doesTargetStorageSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true); try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) { unmanagedVMsManager.importUnmanagedInstance(importUnmanageInstanceCmd); } @@ -702,6 +704,8 @@ private void baseTestImportVmFromVmwareToKvm(VcenterParameter vcenterParameter, when(agentManager.send(Mockito.eq(convertHostId), Mockito.any(CheckConvertInstanceCommand.class))).thenReturn(checkConvertInstanceAnswer); } + when(volumeApiService.doesTargetStorageSupportDiskOffering(any(StoragePool.class), any())).thenReturn(true); + ConvertInstanceAnswer convertInstanceAnswer = mock(ConvertInstanceAnswer.class); ImportConvertedInstanceAnswer convertImportedInstanceAnswer = mock(ImportConvertedInstanceAnswer.class); when(convertInstanceAnswer.getResult()).thenReturn(vcenterParameter != VcenterParameter.CONVERT_FAILURE);