mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	change live migration API used on kvm (#8952)
This commit is contained in:
		
							parent
							
								
									0e08a126df
								
							
						
					
					
						commit
						cec6ade257
					
				| @ -972,6 +972,10 @@ public class LibvirtVMDef { | ||||
|             return _diskLabel; | ||||
|         } | ||||
| 
 | ||||
|         public void setDiskLabel(String label) { | ||||
|             _diskLabel = label; | ||||
|         } | ||||
| 
 | ||||
|         public DiskType getDiskType() { | ||||
|             return _diskType; | ||||
|         } | ||||
|  | ||||
| @ -18,13 +18,20 @@ | ||||
|  */ | ||||
| package com.cloud.hypervisor.kvm.resource; | ||||
| 
 | ||||
| import java.util.Iterator; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.Callable; | ||||
| 
 | ||||
| import org.apache.log4j.Logger; | ||||
| import org.libvirt.Connect; | ||||
| import org.libvirt.Domain; | ||||
| import org.libvirt.LibvirtException; | ||||
| import org.libvirt.TypedParameter; | ||||
| import org.libvirt.TypedStringParameter; | ||||
| import org.libvirt.TypedUlongParameter; | ||||
| 
 | ||||
| public class MigrateKVMAsync implements Callable<Domain> { | ||||
|     protected Logger logger = Logger.getLogger(getClass()); | ||||
| 
 | ||||
|     private final LibvirtComputingResource libvirtComputingResource; | ||||
| 
 | ||||
| @ -37,6 +44,8 @@ public class MigrateKVMAsync implements Callable<Domain> { | ||||
|     private boolean migrateNonSharedInc; | ||||
|     private boolean autoConvergence; | ||||
| 
 | ||||
|     protected Set<String> migrateDiskLabels; | ||||
| 
 | ||||
|     // Libvirt Migrate Flags reference: | ||||
|     // https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainMigrateFlags | ||||
| 
 | ||||
| @ -87,7 +96,7 @@ public class MigrateKVMAsync implements Callable<Domain> { | ||||
|     private static final int LIBVIRT_VERSION_SUPPORTS_AUTO_CONVERGE = 1002003; | ||||
| 
 | ||||
|     public MigrateKVMAsync(final LibvirtComputingResource libvirtComputingResource, final Domain dm, final Connect dconn, final String dxml, | ||||
|             final boolean migrateStorage, final boolean migrateNonSharedInc, final boolean autoConvergence, final String vmName, final String destIp) { | ||||
|             final boolean migrateStorage, final boolean migrateNonSharedInc, final boolean autoConvergence, final String vmName, final String destIp, Set<String> migrateDiskLabels) { | ||||
|         this.libvirtComputingResource = libvirtComputingResource; | ||||
| 
 | ||||
|         this.dm = dm; | ||||
| @ -98,6 +107,7 @@ public class MigrateKVMAsync implements Callable<Domain> { | ||||
|         this.autoConvergence = autoConvergence; | ||||
|         this.vmName = vmName; | ||||
|         this.destIp = destIp; | ||||
|         this.migrateDiskLabels = migrateDiskLabels; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -121,6 +131,37 @@ public class MigrateKVMAsync implements Callable<Domain> { | ||||
|             flags |= VIR_MIGRATE_AUTO_CONVERGE; | ||||
|         } | ||||
| 
 | ||||
|         return dm.migrate(dconn, flags, dxml, vmName, "tcp:" + destIp, libvirtComputingResource.getMigrateSpeed()); | ||||
|         TypedParameter [] parameters = createTypedParameterList(); | ||||
| 
 | ||||
|         logger.debug(String.format("Migrating [%s] with flags [%s], destination [%s] and speed [%s]. The disks with the following labels will be migrated [%s].", vmName, flags, | ||||
|                 destIp, libvirtComputingResource.getMigrateSpeed(), migrateDiskLabels)); | ||||
| 
 | ||||
|         return dm.migrate(dconn, parameters, flags); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     protected TypedParameter[] createTypedParameterList() { | ||||
|         int sizeOfMigrateDiskLabels = 0; | ||||
|         if (migrateDiskLabels != null) { | ||||
|             sizeOfMigrateDiskLabels = migrateDiskLabels.size(); | ||||
|         } | ||||
| 
 | ||||
|         TypedParameter[] parameters = new TypedParameter[4 + sizeOfMigrateDiskLabels]; | ||||
|         parameters[0] = new TypedStringParameter(Domain.DomainMigrateParameters.VIR_MIGRATE_PARAM_DEST_NAME, vmName); | ||||
|         parameters[1] = new TypedStringParameter(Domain.DomainMigrateParameters.VIR_MIGRATE_PARAM_DEST_XML, dxml); | ||||
|         parameters[2] = new TypedStringParameter(Domain.DomainMigrateParameters.VIR_MIGRATE_PARAM_URI, "tcp:" + destIp); | ||||
|         parameters[3] = new TypedUlongParameter(Domain.DomainMigrateParameters.VIR_MIGRATE_PARAM_BANDWIDTH, libvirtComputingResource.getMigrateSpeed()); | ||||
| 
 | ||||
|         if (sizeOfMigrateDiskLabels == 0) { | ||||
|             return parameters; | ||||
|         } | ||||
| 
 | ||||
|         Iterator<String> iterator = migrateDiskLabels.iterator(); | ||||
|         for (int i = 0; i < sizeOfMigrateDiskLabels; i++) { | ||||
|             parameters[4 + i] = new TypedStringParameter(Domain.DomainMigrateParameters.VIR_MIGRATE_PARAM_MIGRATE_DISKS, iterator.next()); | ||||
|         } | ||||
| 
 | ||||
|         return parameters; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -24,6 +24,7 @@ import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.net.URISyntaxException; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
| @ -190,6 +191,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo | ||||
|             // migrateStorage's value should always only be associated with the initial state of mapMigrateStorage. | ||||
|             final boolean migrateStorage = MapUtils.isNotEmpty(mapMigrateStorage); | ||||
|             final boolean migrateStorageManaged = command.isMigrateStorageManaged(); | ||||
|             Set<String> migrateDiskLabels = null; | ||||
| 
 | ||||
|             if (migrateStorage) { | ||||
|                 if (s_logger.isDebugEnabled()) { | ||||
| @ -199,6 +201,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo | ||||
|                 if (s_logger.isDebugEnabled()) { | ||||
|                     s_logger.debug(String.format("Changed VM [%s] XML configuration of used storage. New XML configuration is [%s].", vmName, xmlDesc)); | ||||
|                 } | ||||
|                 migrateDiskLabels = getMigrateStorageDeviceLabels(disks, mapMigrateStorage); | ||||
|             } | ||||
| 
 | ||||
|             Map<String, DpdkTO> dpdkPortsMapping = command.getDpdkInterfaceMapping(); | ||||
| @ -227,7 +230,7 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo | ||||
| 
 | ||||
|             final Callable<Domain> worker = new MigrateKVMAsync(libvirtComputingResource, dm, dconn, xmlDesc, | ||||
|                     migrateStorage, migrateNonSharedInc, | ||||
|                     command.isAutoConvergence(), vmName, command.getDestinationIp()); | ||||
|                     command.isAutoConvergence(), vmName, command.getDestinationIp(), migrateDiskLabels); | ||||
|             final Future<Domain> migrateThread = executor.submit(worker); | ||||
|             executor.shutdown(); | ||||
|             long sleeptime = 0; | ||||
| @ -365,6 +368,30 @@ public final class LibvirtMigrateCommandWrapper extends CommandWrapper<MigrateCo | ||||
|         return new MigrateAnswer(command, result == null, result, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the disk labels (vda, vdb...) of the disks mapped for migration on mapMigrateStorage. | ||||
|      * @param diskDefinitions list of all the disksDefinitions of the VM. | ||||
|      * @param mapMigrateStorage map of the disks that should be migrated. | ||||
|      * @return set with the labels of the disks that should be migrated. | ||||
|      * */ | ||||
|     protected Set<String> getMigrateStorageDeviceLabels(List<DiskDef> diskDefinitions, Map<String, MigrateCommand.MigrateDiskInfo> mapMigrateStorage) { | ||||
|         HashSet<String> setOfLabels = new HashSet<>(); | ||||
|         s_logger.debug(String.format("Searching for disk labels of disks [%s].", mapMigrateStorage.keySet())); | ||||
|         for (String fileName : mapMigrateStorage.keySet()) { | ||||
|             for (DiskDef diskDef : diskDefinitions) { | ||||
|                 String diskPath = diskDef.getDiskPath(); | ||||
|                 if (diskPath != null && diskPath.contains(fileName)) { | ||||
|                     setOfLabels.add(diskDef.getDiskLabel()); | ||||
|                     s_logger.debug(String.format("Found label [%s] for disk [%s].", diskDef.getDiskLabel(), fileName)); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return setOfLabels; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the CPU shares are equal in the source host and destination host. | ||||
|      *  <ul> | ||||
|  | ||||
| @ -0,0 +1,83 @@ | ||||
| // | ||||
| // 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; | ||||
| 
 | ||||
| import org.junit.Assert; | ||||
| import org.junit.Test; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.libvirt.Connect; | ||||
| import org.libvirt.Domain; | ||||
| import org.libvirt.TypedParameter; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.Mockito; | ||||
| import org.mockito.junit.MockitoJUnitRunner; | ||||
| 
 | ||||
| import java.util.Set; | ||||
| 
 | ||||
| @RunWith(MockitoJUnitRunner.class) | ||||
| public class MigrateKVMAsyncTest { | ||||
| 
 | ||||
|     @Mock | ||||
|     private LibvirtComputingResource libvirtComputingResource; | ||||
|     @Mock | ||||
|     private Connect connect; | ||||
|     @Mock | ||||
|     private Domain domain; | ||||
| 
 | ||||
| 
 | ||||
|     @Test | ||||
|     public void createTypedParameterListTestNoMigrateDiskLabels() { | ||||
|         MigrateKVMAsync migrateKVMAsync = new MigrateKVMAsync(libvirtComputingResource, domain, connect, "testxml", | ||||
|                 false, false, false, "tst", "1.1.1.1", null); | ||||
| 
 | ||||
|         Mockito.doReturn(10).when(libvirtComputingResource).getMigrateSpeed(); | ||||
| 
 | ||||
|         TypedParameter[] result = migrateKVMAsync.createTypedParameterList(); | ||||
| 
 | ||||
|         Assert.assertEquals(4, result.length); | ||||
| 
 | ||||
|         Assert.assertEquals("tst", result[0].getValueAsString()); | ||||
|         Assert.assertEquals("testxml", result[1].getValueAsString()); | ||||
|         Assert.assertEquals("tcp:1.1.1.1", result[2].getValueAsString()); | ||||
|         Assert.assertEquals("10", result[3].getValueAsString()); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void createTypedParameterListTestWithMigrateDiskLabels() { | ||||
|         Set<String> labels = Set.of("vda", "vdb"); | ||||
|         MigrateKVMAsync migrateKVMAsync = new MigrateKVMAsync(libvirtComputingResource, domain, connect, "testxml", | ||||
|                 false, false, false, "tst", "1.1.1.1", labels); | ||||
| 
 | ||||
|         Mockito.doReturn(10).when(libvirtComputingResource).getMigrateSpeed(); | ||||
| 
 | ||||
|         TypedParameter[] result = migrateKVMAsync.createTypedParameterList(); | ||||
| 
 | ||||
|         Assert.assertEquals(6, result.length); | ||||
| 
 | ||||
|         Assert.assertEquals("tst", result[0].getValueAsString()); | ||||
|         Assert.assertEquals("testxml", result[1].getValueAsString()); | ||||
|         Assert.assertEquals("tcp:1.1.1.1", result[2].getValueAsString()); | ||||
|         Assert.assertEquals("10", result[3].getValueAsString()); | ||||
| 
 | ||||
|         Assert.assertEquals(labels, Set.of(result[4].getValueAsString(), result[5].getValueAsString())); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -26,10 +26,12 @@ import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.nio.charset.StandardCharsets; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Scanner; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| import javax.xml.parsers.DocumentBuilder; | ||||
| import javax.xml.parsers.DocumentBuilderFactory; | ||||
| @ -585,6 +587,14 @@ public class LibvirtMigrateCommandWrapperTest { | ||||
|             "  </devices>\n" + | ||||
|             "</domain>\n"; | ||||
| 
 | ||||
|     private Map<String, MigrateDiskInfo> createMapMigrateStorage(String sourceText, String path) { | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = new HashMap<String, MigrateDiskInfo>(); | ||||
| 
 | ||||
|         MigrateDiskInfo diskInfo = new MigrateDiskInfo("123456", DiskType.BLOCK, DriverType.RAW, Source.FILE, sourceText); | ||||
|         mapMigrateStorage.put(path, diskInfo); | ||||
|         return mapMigrateStorage; | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testReplaceIpForVNCInDescFile() { | ||||
|         final String targetIp = "192.168.22.21"; | ||||
| @ -761,10 +771,8 @@ public class LibvirtMigrateCommandWrapperTest { | ||||
| 
 | ||||
|     @Test | ||||
|     public void testReplaceStorage() throws Exception { | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = new HashMap<String, MigrateDiskInfo>(); | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = createMapMigrateStorage("sourceTest", "/mnt/812ea6a3-7ad0-30f4-9cab-01e3f2985b98/4650a2f7-fce5-48e2-beaa-bcdf063194e6"); | ||||
| 
 | ||||
|         MigrateDiskInfo diskInfo = new MigrateDiskInfo("123456", DiskType.BLOCK, DriverType.RAW, Source.FILE, "sourctest"); | ||||
|         mapMigrateStorage.put("/mnt/812ea6a3-7ad0-30f4-9cab-01e3f2985b98/4650a2f7-fce5-48e2-beaa-bcdf063194e6", diskInfo); | ||||
|         final String result = libvirtMigrateCmdWrapper.replaceStorage(fullfile, mapMigrateStorage, true); | ||||
| 
 | ||||
|         InputStream in = IOUtils.toInputStream(result); | ||||
| @ -778,7 +786,6 @@ public class LibvirtMigrateCommandWrapperTest { | ||||
| 
 | ||||
|     @Test | ||||
|     public void testReplaceStorageWithSecrets() throws Exception { | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = new HashMap<String, MigrateDiskInfo>(); | ||||
| 
 | ||||
|         final String xmlDesc = | ||||
|             "<domain type='kvm' id='3'>" + | ||||
| @ -799,8 +806,7 @@ public class LibvirtMigrateCommandWrapperTest { | ||||
| 
 | ||||
|         final String volumeFile = "3530f749-82fd-458e-9485-a357e6e541db"; | ||||
|         String newDiskPath = "/mnt/2d0435e1-99e0-4f1d-94c0-bee1f6f8b99e/" + volumeFile; | ||||
|         MigrateDiskInfo diskInfo = new MigrateDiskInfo("123456", DiskType.BLOCK, DriverType.RAW, Source.FILE, newDiskPath); | ||||
|         mapMigrateStorage.put("/mnt/07eb495b-5590-3877-9fb7-23c6e9a40d40/bf8621b3-027c-497d-963b-06319650f048", diskInfo); | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = createMapMigrateStorage(newDiskPath, "/mnt/07eb495b-5590-3877-9fb7-23c6e9a40d40/bf8621b3-027c-497d-963b-06319650f048"); | ||||
|         final String result = libvirtMigrateCmdWrapper.replaceStorage(xmlDesc, mapMigrateStorage, false); | ||||
|         final String expectedSecretUuid = LibvirtComputingResource.generateSecretUUIDFromString(volumeFile); | ||||
| 
 | ||||
| @ -951,4 +957,64 @@ public class LibvirtMigrateCommandWrapperTest { | ||||
| 
 | ||||
|         Assert.assertEquals(updateShares, newVmCpuShares); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void getMigrateStorageDeviceLabelsTestNoDiskDefinitions() { | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = createMapMigrateStorage("sourceTest", "/mnt/812ea6a3-7ad0-30f4-9cab-01e3f2985b98/4650a2f7-fce5-48e2-beaa-bcdf063194e6"); | ||||
| 
 | ||||
|         Set<String> result = libvirtMigrateCmdWrapper.getMigrateStorageDeviceLabels(new ArrayList<>(), mapMigrateStorage); | ||||
| 
 | ||||
|         assertTrue(result.isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void getMigrateStorageDeviceLabelsTestNoMapMigrateStorage() { | ||||
|         List<DiskDef> disks = new ArrayList<>(); | ||||
|         DiskDef diskDef0 = new DiskDef(); | ||||
| 
 | ||||
|         diskDef0.setDiskPath("volPath"); | ||||
|         disks.add(diskDef0); | ||||
| 
 | ||||
|         Set<String> result = libvirtMigrateCmdWrapper.getMigrateStorageDeviceLabels(disks, new HashMap<>()); | ||||
| 
 | ||||
|         assertTrue(result.isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void getMigrateStorageDeviceLabelsTestPathIsNotFound() { | ||||
|         List<DiskDef> disks = new ArrayList<>(); | ||||
|         DiskDef diskDef0 = new DiskDef(); | ||||
| 
 | ||||
|         diskDef0.setDiskPath("volPath"); | ||||
|         disks.add(diskDef0); | ||||
| 
 | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = createMapMigrateStorage("sourceTest", "/mnt/812ea6a3-7ad0-30f4-9cab-01e3f2985b98/4650a2f7-fce5-48e2-beaa-bcdf063194e6"); | ||||
| 
 | ||||
|         Set<String> result = libvirtMigrateCmdWrapper.getMigrateStorageDeviceLabels(disks, mapMigrateStorage); | ||||
| 
 | ||||
|         assertTrue(result.isEmpty()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void getMigrateStorageDeviceLabelsTestFindPathAndLabels() { | ||||
|         List<DiskDef> disks = new ArrayList<>(); | ||||
|         DiskDef diskDef0 = new DiskDef(); | ||||
|         DiskDef diskDef1 = new DiskDef(); | ||||
| 
 | ||||
|         diskDef0.setDiskPath("volPath1"); | ||||
|         diskDef0.setDiskLabel("vda"); | ||||
|         disks.add(diskDef0); | ||||
| 
 | ||||
|         diskDef1.setDiskPath("volPath2"); | ||||
|         diskDef1.setDiskLabel("vdb"); | ||||
|         disks.add(diskDef1); | ||||
| 
 | ||||
|         Map<String, MigrateDiskInfo> mapMigrateStorage = createMapMigrateStorage("sourceTest", "volPath1"); | ||||
|         mapMigrateStorage.put("volPath2", new MigrateDiskInfo("123457", DiskType.BLOCK, DriverType.RAW, Source.FILE, "sourceText")); | ||||
| 
 | ||||
|         Set<String> result = libvirtMigrateCmdWrapper.getMigrateStorageDeviceLabels(disks, mapMigrateStorage); | ||||
| 
 | ||||
|         assertTrue(result.containsAll(Arrays.asList("vda", "vdb"))); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user