This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch 4.18
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.18 by this push:
new cec6ade257c change live migration API used on kvm (#8952)
cec6ade257c is described below
commit cec6ade257ca231f7bb5a7062ee1ef0739b24e32
Author: João Jandre <[email protected]>
AuthorDate: Thu Apr 25 04:35:25 2024 -0300
change live migration API used on kvm (#8952)
---
.../hypervisor/kvm/resource/LibvirtVMDef.java | 4 ++
.../hypervisor/kvm/resource/MigrateKVMAsync.java | 45 +++++++++++-
.../wrapper/LibvirtMigrateCommandWrapper.java | 29 +++++++-
.../kvm/resource/MigrateKVMAsyncTest.java | 83 ++++++++++++++++++++++
.../wrapper/LibvirtMigrateCommandWrapperTest.java | 78 ++++++++++++++++++--
5 files changed, 230 insertions(+), 9 deletions(-)
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index 6d69b2f9664..c3b5f9857eb 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -972,6 +972,10 @@ public class LibvirtVMDef {
return _diskLabel;
}
+ public void setDiskLabel(String label) {
+ _diskLabel = label;
+ }
+
public DiskType getDiskType() {
return _diskType;
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
index c3917314250..bc94bb47ed8 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java
@@ -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;
}
+
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
index fb526626ef8..aebbaa4119d 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapper.java
@@ -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>
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsyncTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsyncTest.java
new file mode 100644
index 00000000000..28633b925b2
--- /dev/null
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsyncTest.java
@@ -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()));
+ }
+
+}
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
index c206f898e97..7071758fcbe 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtMigrateCommandWrapperTest.java
@@ -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")));
+ }
+
}