This is an automated email from the ASF dual-hosted git repository.
weizhouapache pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 72197fea6c6 [VMware to KVM] Cleanup leftover migrated volumes in case
of migration failures (#13151)
72197fea6c6 is described below
commit 72197fea6c67aecc70de4124d6e308fd2b899869
Author: Nicolas Vazquez <[email protected]>
AuthorDate: Wed Jul 8 08:07:36 2026 -0300
[VMware to KVM] Cleanup leftover migrated volumes in case of migration
failures (#13151)
---
.../api/CleanupConvertedInstanceDisksCommand.java | 57 +++++
....java => LibvirtBaseConvertCommandWrapper.java} | 148 ++++++-------
...leanupConvertedInstanceDisksCommandWrapper.java | 67 ++++++
...bvirtImportConvertedInstanceCommandWrapper.java | 231 +--------------------
...tImportConvertedInstanceCommandWrapperTest.java | 2 +-
.../cloudstack/vm/UnmanagedVMsManagerImpl.java | 51 +++--
6 files changed, 238 insertions(+), 318 deletions(-)
diff --git
a/core/src/main/java/com/cloud/agent/api/CleanupConvertedInstanceDisksCommand.java
b/core/src/main/java/com/cloud/agent/api/CleanupConvertedInstanceDisksCommand.java
new file mode 100644
index 00000000000..00373ec7536
--- /dev/null
+++
b/core/src/main/java/com/cloud/agent/api/CleanupConvertedInstanceDisksCommand.java
@@ -0,0 +1,57 @@
+//
+// 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;
+
+import com.cloud.agent.api.to.DataStoreTO;
+
+/**
+ * This command is used to cleanup the converted instance disks from the
storage pool: vmVolumesStore and the prefix: vmVolumesPrefix.
+ */
+public class CleanupConvertedInstanceDisksCommand extends Command {
+
+ private DataStoreTO vmVolumesStore;
+ private String vmVolumesPrefix;
+
+ public CleanupConvertedInstanceDisksCommand(DataStoreTO vmVolumesStore,
String vmVolumesPrefix) {
+ this.vmVolumesStore = vmVolumesStore;
+ this.vmVolumesPrefix = vmVolumesPrefix;
+ }
+
+ public DataStoreTO getVmVolumesStore() {
+ return vmVolumesStore;
+ }
+
+ public void setVmVolumesStore(DataStoreTO vmVolumesStore) {
+ this.vmVolumesStore = vmVolumesStore;
+ }
+
+ public String getVmVolumesPrefix() {
+ return vmVolumesPrefix;
+ }
+
+ public void setVmVolumesPrefix(String vmVolumesPrefix) {
+ this.vmVolumesPrefix = vmVolumesPrefix;
+ }
+
+ @Override
+ public boolean executeInSequence() {
+ return false;
+ }
+}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBaseConvertCommandWrapper.java
similarity index 73%
copy from
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
copy to
plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBaseConvertCommandWrapper.java
index 5602da15679..dc34a4cb62d 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBaseConvertCommandWrapper.java
@@ -18,94 +18,40 @@
//
package com.cloud.hypervisor.kvm.resource.wrapper;
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-import java.util.stream.Collectors;
-
-import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
-import org.apache.cloudstack.vm.UnmanagedInstanceTO;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-
import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.ImportConvertedInstanceAnswer;
-import com.cloud.agent.api.ImportConvertedInstanceCommand;
+import com.cloud.agent.api.Command;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.NfsTO;
-import com.cloud.agent.api.to.RemoteInstanceTO;
-import com.cloud.hypervisor.Hypervisor;
-import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
import com.cloud.resource.CommandWrapper;
-import com.cloud.resource.ResourceWrapper;
+import com.cloud.resource.ServerResource;
import com.cloud.storage.Storage;
import com.cloud.utils.FileUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.script.Script;
+import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
+import org.apache.cloudstack.vm.UnmanagedInstanceTO;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
-@ResourceWrapper(handles = ImportConvertedInstanceCommand.class)
-public class LibvirtImportConvertedInstanceCommandWrapper extends
CommandWrapper<ImportConvertedInstanceCommand, Answer,
LibvirtComputingResource> {
-
- @Override
- public Answer execute(ImportConvertedInstanceCommand cmd,
LibvirtComputingResource serverResource) {
- RemoteInstanceTO sourceInstance = cmd.getSourceInstance();
- Hypervisor.HypervisorType sourceHypervisorType =
sourceInstance.getHypervisorType();
- String sourceInstanceName = sourceInstance.getInstanceName();
- List<String> destinationStoragePools =
cmd.getDestinationStoragePools();
- DataStoreTO conversionTemporaryLocation =
cmd.getConversionTemporaryLocation();
- final String temporaryConvertUuid = cmd.getTemporaryConvertUuid();
- final boolean forceConvertToPool = cmd.isForceConvertToPool();
-
- final KVMStoragePoolManager storagePoolMgr =
serverResource.getStoragePoolMgr();
- KVMStoragePool temporaryStoragePool =
getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr);
- final String temporaryConvertPath =
temporaryStoragePool.getLocalPath();
-
- try {
- String convertedBasePath = String.format("%s/%s",
temporaryConvertPath, temporaryConvertUuid);
- LibvirtDomainXMLParser xmlParser =
parseMigratedVMXmlDomain(convertedBasePath);
-
- List<KVMPhysicalDisk> temporaryDisks = xmlParser == null ?
-
getTemporaryDisksWithPrefixFromTemporaryPool(temporaryStoragePool,
temporaryConvertPath, temporaryConvertUuid) :
- getTemporaryDisksFromParsedXml(temporaryStoragePool,
xmlParser, convertedBasePath);
-
- List<KVMPhysicalDisk> disks = null;
- if (forceConvertToPool) {
- // Force flag to use the conversion path, no need to move disks
- disks = temporaryDisks;
- } else {
- disks = moveTemporaryDisksToDestination(temporaryDisks,
- destinationStoragePools, storagePoolMgr);
- cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks,
temporaryStoragePool, temporaryConvertUuid);
- }
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
- UnmanagedInstanceTO convertedInstanceTO =
getConvertedUnmanagedInstance(temporaryConvertUuid,
- disks, xmlParser);
- return new ImportConvertedInstanceAnswer(cmd, convertedInstanceTO);
- } catch (Exception e) {
- String error = String.format("Error converting instance %s from
%s, due to: %s",
- sourceInstanceName, sourceHypervisorType, e.getMessage());
- logger.error(error, e);
- return new ImportConvertedInstanceAnswer(cmd, false, error);
- } finally {
- if (conversionTemporaryLocation instanceof NfsTO) {
- logger.debug("Cleaning up secondary storage temporary
location");
-
storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(),
temporaryStoragePool.getUuid());
- }
- }
- }
+public abstract class LibvirtBaseConvertCommandWrapper <T extends Command, A
extends Answer, R extends ServerResource> extends CommandWrapper<T, A, R> {
protected KVMStoragePool getTemporaryStoragePool(DataStoreTO
conversionTemporaryLocation, KVMStoragePoolManager storagePoolMgr) {
if (conversionTemporaryLocation instanceof NfsTO) {
@@ -117,14 +63,17 @@ public class LibvirtImportConvertedInstanceCommandWrapper
extends CommandWrapper
}
}
- protected List<KVMPhysicalDisk>
getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser
xmlParser, String convertedBasePath) {
+ protected List<KVMPhysicalDisk>
getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser
xmlParser,
+ String
convertedBasePath,
+ String
conversionPoolPath, String vmVolumesPrefix) {
List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
disksDefs = disksDefs.stream().filter(x -> x.getDiskType() ==
LibvirtVMDef.DiskDef.DiskType.FILE &&
x.getDeviceType() ==
LibvirtVMDef.DiskDef.DeviceType.DISK).collect(Collectors.toList());
if (CollectionUtils.isEmpty(disksDefs)) {
- String err = String.format("Cannot find any disk defined on the
converted XML domain %s.xml", convertedBasePath);
- logger.error(err);
- throw new CloudRuntimeException(err);
+ String err = String.format("Cannot find any disk defined on the
converted XML domain %s.xml, " +
+ "checking disks at: %s with prefix: %s",
convertedBasePath, conversionPoolPath, vmVolumesPrefix);
+ logger.warn(err);
+ return getTemporaryDisksWithPrefixFromTemporaryPool(pool,
conversionPoolPath, vmVolumesPrefix);
}
sanitizeDisksPath(disksDefs);
return getPhysicalDisksFromDefPaths(disksDefs, pool);
@@ -155,15 +104,17 @@ public class LibvirtImportConvertedInstanceCommandWrapper
extends CommandWrapper
return disksWithPrefix;
}
- private void
cleanupDisksAndDomainFromTemporaryLocation(List<KVMPhysicalDisk> disks,
+ protected void
cleanupDisksAndDomainFromTemporaryLocation(List<KVMPhysicalDisk> disks,
KVMStoragePool
temporaryStoragePool,
- String
temporaryConvertUuid) {
+ String
temporaryConvertUuid, boolean xmlExists) {
for (KVMPhysicalDisk disk : disks) {
logger.info(String.format("Cleaning up temporary disk %s after
conversion from temporary location", disk.getName()));
temporaryStoragePool.deletePhysicalDisk(disk.getName(),
Storage.ImageFormat.QCOW2);
}
- logger.info(String.format("Cleaning up temporary domain %s after
conversion from temporary location", temporaryConvertUuid));
- FileUtil.deleteFiles(temporaryStoragePool.getLocalPath(),
temporaryConvertUuid, ".xml");
+ if (xmlExists) {
+ logger.info(String.format("Cleaning up temporary domain %s after
conversion from temporary location", temporaryConvertUuid));
+ FileUtil.deleteFiles(temporaryStoragePool.getLocalPath(),
temporaryConvertUuid, ".xml");
+ }
}
protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
@@ -205,13 +156,38 @@ public class LibvirtImportConvertedInstanceCommandWrapper
extends CommandWrapper
String destinationName = UUID.randomUUID().toString();
- KVMPhysicalDisk destinationDisk =
storagePoolMgr.copyPhysicalDisk(sourceDisk, destinationName, destinationPool,
7200 * 1000);
- targetDisks.add(destinationDisk);
+ try {
+ if (destinationPool.getAvailable() < sourceDisk.getSize()) {
+ String msg = String.format("Not enough space on
destination pool %s (%s bytes) to copy disk %s (size %s)",
+ destinationPool.getUuid(),
destinationPool.getAvailable(), sourceDisk.getName(), sourceDisk.getSize());
+ logger.error(msg);
+ throw new CloudRuntimeException(msg);
+ }
+
+ KVMPhysicalDisk destinationDisk =
storagePoolMgr.copyPhysicalDisk(sourceDisk, destinationName, destinationPool,
7200 * 1000);
+ targetDisks.add(destinationDisk);
+ } catch (Exception e) {
+ String err = String.format("Error copying converted instance
disk number %s from the temporary location %s" +
+ " to destination storage pool %s: %s", i,
sourceDisk.getPool().getLocalPath(), destinationPool.getUuid(), e.getMessage());
+ logger.error(err, e);
+ cleanupMovedDisksOnDestinationPool(targetDisks);
+ return null;
+ }
}
return targetDisks;
}
- private UnmanagedInstanceTO getConvertedUnmanagedInstance(String baseName,
+ private void cleanupMovedDisksOnDestinationPool(List<KVMPhysicalDisk>
targetDisks) {
+ if (CollectionUtils.isEmpty(targetDisks)) {
+ return;
+ }
+ for (KVMPhysicalDisk disk : targetDisks) {
+ logger.info(String.format("Cleaning up disk %s from pool %s after
conversion", disk.getName(), disk.getPool().getUuid()));
+ disk.getPool().deletePhysicalDisk(disk.getName(),
Storage.ImageFormat.QCOW2);
+ }
+ }
+
+ protected UnmanagedInstanceTO getConvertedUnmanagedInstance(String
baseName,
List<KVMPhysicalDisk> vmDisks,
LibvirtDomainXMLParser xmlParser) {
UnmanagedInstanceTO instanceTO = new UnmanagedInstanceTO();
@@ -289,8 +265,10 @@ public class LibvirtImportConvertedInstanceCommandWrapper
extends CommandWrapper
logger.error(err);
throw new CloudRuntimeException(err);
}
- InputStream is = new BufferedInputStream(new FileInputStream(xmlPath));
- String xml = IOUtils.toString(is, Charset.defaultCharset());
+ String xml;
+ try (InputStream is = new BufferedInputStream(new
FileInputStream(xmlPath))) {
+ xml = IOUtils.toString(is, Charset.defaultCharset());
+ }
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
try {
parser.parseDomainXML(xml);
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCleanupConvertedInstanceDisksCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCleanupConvertedInstanceDisksCommandWrapper.java
new file mode 100644
index 00000000000..05afd8b2234
--- /dev/null
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCleanupConvertedInstanceDisksCommandWrapper.java
@@ -0,0 +1,67 @@
+//
+// 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 com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CleanupConvertedInstanceDisksCommand;
+import com.cloud.agent.api.to.DataStoreTO;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
+import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
+import com.cloud.resource.ResourceWrapper;
+
+import java.io.File;
+import java.util.List;
+
+@ResourceWrapper(handles = CleanupConvertedInstanceDisksCommand.class)
+public class LibvirtCleanupConvertedInstanceDisksCommandWrapper extends
LibvirtBaseConvertCommandWrapper<CleanupConvertedInstanceDisksCommand, Answer,
LibvirtComputingResource> {
+
+ @Override
+ public Answer execute(CleanupConvertedInstanceDisksCommand command,
LibvirtComputingResource serverResource) {
+ DataStoreTO vmVolumesStore = command.getVmVolumesStore();
+ String vmVolumesPrefix = command.getVmVolumesPrefix();
+
+ final KVMStoragePoolManager storagePoolMgr =
serverResource.getStoragePoolMgr();
+ KVMStoragePool conversionPool =
getTemporaryStoragePool(vmVolumesStore, storagePoolMgr);
+ final String conversionPoolPath = conversionPool.getLocalPath();
+
+ try {
+ String volumesBasePath = String.format("%s/%s",
conversionPoolPath, vmVolumesPrefix);
+ String xmlPath = String.format("%s.xml", volumesBasePath);
+ boolean xmlExists = new File(xmlPath).exists();
+
+ LibvirtDomainXMLParser xmlParser = xmlExists ?
parseMigratedVMXmlDomain(volumesBasePath) : null;
+ List<KVMPhysicalDisk> temporaryDisks = xmlExists && xmlParser !=
null ?
+ getTemporaryDisksFromParsedXml(conversionPool, xmlParser,
volumesBasePath, conversionPoolPath, vmVolumesPrefix) :
+
getTemporaryDisksWithPrefixFromTemporaryPool(conversionPool,
conversionPoolPath, vmVolumesPrefix);
+
+ cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks,
conversionPool, vmVolumesPrefix, xmlExists);
+
+ } catch (Exception e) {
+ String error = String.format("Error cleaning up converted disks
with prefix %s from %s, due to: %s",
+ vmVolumesPrefix, conversionPoolPath, e.getMessage());
+ logger.error(error, e);
+ return new Answer(command, false, error);
+ }
+
+ return new Answer(command);
+ }
+}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
index 5602da15679..28e24a9e0f2 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapper.java
@@ -18,22 +18,9 @@
//
package com.cloud.hypervisor.kvm.resource.wrapper;
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
import java.util.List;
-import java.util.UUID;
-import java.util.stream.Collectors;
-import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.ImportConvertedInstanceAnswer;
@@ -44,20 +31,14 @@ import com.cloud.agent.api.to.RemoteInstanceTO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
-import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
-import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
-import com.cloud.storage.Storage;
-import com.cloud.utils.FileUtil;
-import com.cloud.utils.Pair;
-import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.utils.script.Script;
+import org.apache.commons.collections4.CollectionUtils;
@ResourceWrapper(handles = ImportConvertedInstanceCommand.class)
-public class LibvirtImportConvertedInstanceCommandWrapper extends
CommandWrapper<ImportConvertedInstanceCommand, Answer,
LibvirtComputingResource> {
+public class LibvirtImportConvertedInstanceCommandWrapper extends
LibvirtBaseConvertCommandWrapper<ImportConvertedInstanceCommand, Answer,
LibvirtComputingResource> {
@Override
public Answer execute(ImportConvertedInstanceCommand cmd,
LibvirtComputingResource serverResource) {
@@ -79,16 +60,22 @@ public class LibvirtImportConvertedInstanceCommandWrapper
extends CommandWrapper
List<KVMPhysicalDisk> temporaryDisks = xmlParser == null ?
getTemporaryDisksWithPrefixFromTemporaryPool(temporaryStoragePool,
temporaryConvertPath, temporaryConvertUuid) :
- getTemporaryDisksFromParsedXml(temporaryStoragePool,
xmlParser, convertedBasePath);
+ getTemporaryDisksFromParsedXml(temporaryStoragePool,
xmlParser, convertedBasePath, temporaryConvertPath, temporaryConvertUuid);
- List<KVMPhysicalDisk> disks = null;
+ List<KVMPhysicalDisk> disks;
if (forceConvertToPool) {
// Force flag to use the conversion path, no need to move disks
disks = temporaryDisks;
} else {
disks = moveTemporaryDisksToDestination(temporaryDisks,
destinationStoragePools, storagePoolMgr);
- cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks,
temporaryStoragePool, temporaryConvertUuid);
+ cleanupDisksAndDomainFromTemporaryLocation(temporaryDisks,
temporaryStoragePool, temporaryConvertUuid, true);
+ }
+
+ if (CollectionUtils.isEmpty(disks)) {
+ String msg = String.format("Unable to import the converted
disks for VM %s from destination pools", sourceInstanceName);
+ logger.error(msg);
+ return new ImportConvertedInstanceAnswer(cmd, false, msg);
}
UnmanagedInstanceTO convertedInstanceTO =
getConvertedUnmanagedInstance(temporaryConvertUuid,
@@ -106,200 +93,4 @@ public class LibvirtImportConvertedInstanceCommandWrapper
extends CommandWrapper
}
}
}
-
- protected KVMStoragePool getTemporaryStoragePool(DataStoreTO
conversionTemporaryLocation, KVMStoragePoolManager storagePoolMgr) {
- if (conversionTemporaryLocation instanceof NfsTO) {
- NfsTO nfsTO = (NfsTO) conversionTemporaryLocation;
- return storagePoolMgr.getStoragePoolByURI(nfsTO.getUrl());
- } else {
- PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO)
conversionTemporaryLocation;
- return
storagePoolMgr.getStoragePool(primaryDataStoreTO.getPoolType(),
primaryDataStoreTO.getUuid());
- }
- }
-
- protected List<KVMPhysicalDisk>
getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser
xmlParser, String convertedBasePath) {
- List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
- disksDefs = disksDefs.stream().filter(x -> x.getDiskType() ==
LibvirtVMDef.DiskDef.DiskType.FILE &&
- x.getDeviceType() ==
LibvirtVMDef.DiskDef.DeviceType.DISK).collect(Collectors.toList());
- if (CollectionUtils.isEmpty(disksDefs)) {
- String err = String.format("Cannot find any disk defined on the
converted XML domain %s.xml", convertedBasePath);
- logger.error(err);
- throw new CloudRuntimeException(err);
- }
- sanitizeDisksPath(disksDefs);
- return getPhysicalDisksFromDefPaths(disksDefs, pool);
- }
-
- private List<KVMPhysicalDisk>
getPhysicalDisksFromDefPaths(List<LibvirtVMDef.DiskDef> disksDefs,
KVMStoragePool pool) {
- List<KVMPhysicalDisk> disks = new ArrayList<>();
- for (LibvirtVMDef.DiskDef diskDef : disksDefs) {
- KVMPhysicalDisk physicalDisk =
pool.getPhysicalDisk(diskDef.getDiskPath());
- disks.add(physicalDisk);
- }
- return disks;
- }
-
- protected List<KVMPhysicalDisk>
getTemporaryDisksWithPrefixFromTemporaryPool(KVMStoragePool pool, String path,
String prefix) {
- String msg = String.format("Could not parse correctly the converted
XML domain, checking for disks on %s with prefix %s", path, prefix);
- logger.info(msg);
- pool.refresh();
- List<KVMPhysicalDisk> disksWithPrefix = pool.listPhysicalDisks()
- .stream()
- .filter(x -> x.getName().startsWith(prefix) &&
!x.getName().endsWith(".xml"))
- .collect(Collectors.toList());
- if (CollectionUtils.isEmpty(disksWithPrefix)) {
- msg = String.format("Could not find any converted disk with prefix
%s on temporary location %s", prefix, path);
- logger.error(msg);
- throw new CloudRuntimeException(msg);
- }
- return disksWithPrefix;
- }
-
- private void
cleanupDisksAndDomainFromTemporaryLocation(List<KVMPhysicalDisk> disks,
- KVMStoragePool
temporaryStoragePool,
- String
temporaryConvertUuid) {
- for (KVMPhysicalDisk disk : disks) {
- logger.info(String.format("Cleaning up temporary disk %s after
conversion from temporary location", disk.getName()));
- temporaryStoragePool.deletePhysicalDisk(disk.getName(),
Storage.ImageFormat.QCOW2);
- }
- logger.info(String.format("Cleaning up temporary domain %s after
conversion from temporary location", temporaryConvertUuid));
- FileUtil.deleteFiles(temporaryStoragePool.getLocalPath(),
temporaryConvertUuid, ".xml");
- }
-
- protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
- for (LibvirtVMDef.DiskDef disk : disks) {
- String[] diskPathParts = disk.getDiskPath().split("/");
- String relativePath = diskPathParts[diskPathParts.length - 1];
- disk.setDiskPath(relativePath);
- }
- }
-
- protected List<KVMPhysicalDisk>
moveTemporaryDisksToDestination(List<KVMPhysicalDisk> temporaryDisks,
-
List<String> destinationStoragePools,
-
KVMStoragePoolManager storagePoolMgr) {
- List<KVMPhysicalDisk> targetDisks = new ArrayList<>();
- if (temporaryDisks.size() != destinationStoragePools.size()) {
- String warn = String.format("Discrepancy between the converted
instance disks (%s) " +
- "and the expected number of disks (%s)",
temporaryDisks.size(), destinationStoragePools.size());
- logger.warn(warn);
- }
- for (int i = 0; i < temporaryDisks.size(); i++) {
- String poolPath = destinationStoragePools.get(i);
- KVMStoragePool destinationPool =
storagePoolMgr.getStoragePool(Storage.StoragePoolType.NetworkFilesystem,
poolPath);
- if (destinationPool == null) {
- String err = String.format("Could not find a storage pool by
URI: %s", poolPath);
- logger.error(err);
- continue;
- }
- if (destinationPool.getType() !=
Storage.StoragePoolType.NetworkFilesystem) {
- String err = String.format("Storage pool by URI: %s is not an
NFS storage", poolPath);
- logger.error(err);
- continue;
- }
- KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
- if (logger.isDebugEnabled()) {
- String msg = String.format("Trying to copy converted instance
disk number %s from the temporary location %s" +
- " to destination storage pool %s", i,
sourceDisk.getPool().getLocalPath(), destinationPool.getUuid());
- logger.debug(msg);
- }
-
- String destinationName = UUID.randomUUID().toString();
-
- KVMPhysicalDisk destinationDisk =
storagePoolMgr.copyPhysicalDisk(sourceDisk, destinationName, destinationPool,
7200 * 1000);
- targetDisks.add(destinationDisk);
- }
- return targetDisks;
- }
-
- private UnmanagedInstanceTO getConvertedUnmanagedInstance(String baseName,
-
List<KVMPhysicalDisk> vmDisks,
-
LibvirtDomainXMLParser xmlParser) {
- UnmanagedInstanceTO instanceTO = new UnmanagedInstanceTO();
- instanceTO.setName(baseName);
- instanceTO.setDisks(getUnmanagedInstanceDisks(vmDisks, xmlParser));
- instanceTO.setNics(getUnmanagedInstanceNics(xmlParser));
- return instanceTO;
- }
-
- private List<UnmanagedInstanceTO.Nic>
getUnmanagedInstanceNics(LibvirtDomainXMLParser xmlParser) {
- List<UnmanagedInstanceTO.Nic> nics = new ArrayList<>();
- if (xmlParser != null) {
- List<LibvirtVMDef.InterfaceDef> interfaces =
xmlParser.getInterfaces();
- for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
- UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic();
- nic.setMacAddress(interfaceDef.getMacAddress());
- nic.setNicId(interfaceDef.getBrName());
- nic.setAdapterType(interfaceDef.getModel().toString());
- nics.add(nic);
- }
- }
- return nics;
- }
-
- protected List<UnmanagedInstanceTO.Disk>
getUnmanagedInstanceDisks(List<KVMPhysicalDisk> vmDisks, LibvirtDomainXMLParser
xmlParser) {
- List<UnmanagedInstanceTO.Disk> instanceDisks = new ArrayList<>();
- List<LibvirtVMDef.DiskDef> diskDefs = xmlParser != null ?
xmlParser.getDisks() : null;
- for (int i = 0; i< vmDisks.size(); i++) {
- KVMPhysicalDisk physicalDisk = vmDisks.get(i);
- KVMStoragePool storagePool = physicalDisk.getPool();
- UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk();
- disk.setPosition(i);
- Pair<String, String> storagePoolHostAndPath =
getNfsStoragePoolHostAndPath(storagePool);
- disk.setDatastoreHost(storagePoolHostAndPath.first());
- disk.setDatastorePath(storagePoolHostAndPath.second());
- disk.setDatastoreName(storagePool.getUuid());
- disk.setDatastoreType(storagePool.getType().name());
- disk.setCapacity(physicalDisk.getVirtualSize());
- disk.setFileBaseName(physicalDisk.getName());
- if (CollectionUtils.isNotEmpty(diskDefs)) {
- LibvirtVMDef.DiskDef diskDef = diskDefs.get(i);
- disk.setController(diskDef.getBusType() != null ?
diskDef.getBusType().toString() :
LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
- } else {
- // If the job is finished but we cannot parse the XML, the
guest VM can use the virtio driver
-
disk.setController(LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
- }
- instanceDisks.add(disk);
- }
- return instanceDisks;
- }
-
- protected Pair<String, String> getNfsStoragePoolHostAndPath(KVMStoragePool
storagePool) {
- String sourceHostIp = null;
- String sourcePath = null;
- List<String[]> commands = new ArrayList<>();
- commands.add(new String[]{Script.getExecutableAbsolutePath("mount")});
- commands.add(new String[]{Script.getExecutableAbsolutePath("grep"),
storagePool.getLocalPath()});
- String storagePoolMountPoint = Script.executePipedCommands(commands,
0).second();
- 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(":");
- if (res.length > 1) {
- sourceHostIp = res[0].strip();
- sourcePath = res[1].strip();
- }
- }
- return new Pair<>(sourceHostIp, sourcePath);
- }
-
- protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String
installPath) throws IOException {
- String xmlPath = String.format("%s.xml", installPath);
- if (!new File(xmlPath).exists()) {
- String err = String.format("Conversion failed. Unable to find the
converted XML domain, expected %s", xmlPath);
- logger.error(err);
- throw new CloudRuntimeException(err);
- }
- InputStream is = new BufferedInputStream(new FileInputStream(xmlPath));
- String xml = IOUtils.toString(is, Charset.defaultCharset());
- final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
- try {
- parser.parseDomainXML(xml);
- return parser;
- } catch (RuntimeException e) {
- String err = String.format("Error parsing the converted instance
XML domain at %s: %s", xmlPath, e.getMessage());
- logger.error(err, e);
- logger.debug(xml);
- return null;
- }
- }
}
diff --git
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapperTest.java
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapperTest.java
index 343a15b367d..a30168266c0 100644
---
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapperTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtImportConvertedInstanceCommandWrapperTest.java
@@ -127,7 +127,7 @@ public class
LibvirtImportConvertedInstanceCommandWrapperTest {
Mockito.when(convertedDisk1.getName()).thenReturn("disk1");
Mockito.when(temporaryPool.getPhysicalDisk(relativePath)).thenReturn(convertedDisk1);
- List<KVMPhysicalDisk> disks =
importInstanceCommandWrapper.getTemporaryDisksFromParsedXml(temporaryPool,
parser, "");
+ List<KVMPhysicalDisk> disks =
importInstanceCommandWrapper.getTemporaryDisksFromParsedXml(temporaryPool,
parser, "", "", "prefix");
Mockito.verify(importInstanceCommandWrapper).sanitizeDisksPath(List.of(diskDef));
Assert.assertEquals(1, disks.size());
Assert.assertEquals("disk1", disks.get(0).getName());
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 36905fb40ec..4d9db4a55b1 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -23,6 +23,7 @@ import com.cloud.agent.api.CheckConvertInstanceAnswer;
import com.cloud.agent.api.CheckConvertInstanceCommand;
import com.cloud.agent.api.CheckVolumeAnswer;
import com.cloud.agent.api.CheckVolumeCommand;
+import com.cloud.agent.api.CleanupConvertedInstanceDisksCommand;
import com.cloud.agent.api.ConvertInstanceAnswer;
import com.cloud.agent.api.ConvertInstanceCommand;
import com.cloud.agent.api.CopyRemoteVolumeAnswer;
@@ -1717,13 +1718,14 @@ public class UnmanagedVMsManagerImpl implements
UnmanagedVMsManager {
logger.debug("The host {} is selected to execute the conversion
of the " +
"instance {} from VMware to KVM ", convertHost,
sourceVMName);
+ long importStartTime = System.currentTimeMillis();
+ importVMTask = importVmTasksManager.createImportVMTaskRecord(zone,
owner, userId, displayName, vcenter, datacenterName, sourceVMName,
+ convertHost, importHost);
+
temporaryConvertLocation =
selectInstanceConversionTemporaryLocation(
destinationCluster, convertHost, importHost,
convertStoragePoolId, forceConvertToPool);
List<StoragePoolVO> convertStoragePools =
findInstanceConversionDestinationStoragePoolsInCluster(destinationCluster,
serviceOffering, dataDiskOfferingMap, temporaryConvertLocation,
forceConvertToPool);
- long importStartTime = System.currentTimeMillis();
- importVMTask = importVmTasksManager.createImportVMTaskRecord(zone,
owner, userId, displayName, vcenter, datacenterName, sourceVMName,
- convertHost, importHost);
importVmTasksManager.updateImportVMTaskStep(importVMTask, zone,
owner, convertHost, importHost, null, CloningInstance);
// sourceVMwareInstance could be a cloned instance from
sourceVMName, of the sourceVMName itself if its powered off.
@@ -2214,31 +2216,56 @@ public class UnmanagedVMsManagerImpl implements
UnmanagedVMsManager {
throw new CloudRuntimeException(err);
}
+ boolean cleanupConvertedDisks = false;
+ String convertedDisksPrefix = null;
Answer importAnswer;
try {
+ convertedDisksPrefix =
((ConvertInstanceAnswer)convertAnswer).getTemporaryConvertUuid();
ImportConvertedInstanceCommand importCmd = new
ImportConvertedInstanceCommand(
remoteInstanceTO, destinationStoragePools,
temporaryConvertLocation,
-
((ConvertInstanceAnswer)convertAnswer).getTemporaryConvertUuid(),
forceConvertToPool);
+ convertedDisksPrefix, forceConvertToPool);
importAnswer = agentManager.send(importHost.getId(), importCmd);
+
+ if (!importAnswer.getResult()) {
+ cleanupConvertedDisks = true;
+ String err = String.format(
+ "The import process failed for instance %s from VMware
to KVM on host %s: %s",
+ sourceVM, importHost, importAnswer.getDetails());
+ logger.error(err);
+ throw new CloudRuntimeException(err);
+ }
} catch (AgentUnavailableException | OperationTimedoutException e) {
+ cleanupConvertedDisks = true;
String err = String.format(
"Could not send the import converted instance command to
host %s due to: %s",
importHost, e.getMessage());
logger.error(err, e);
throw new CloudRuntimeException(err);
- }
-
- if (!importAnswer.getResult()) {
- String err = String.format(
- "The import process failed for instance %s from VMware to
KVM on host %s: %s",
- sourceVM, importHost, importAnswer.getDetails());
- logger.error(err);
- throw new CloudRuntimeException(err);
+ } finally {
+ if (cleanupConvertedDisks) {
+ cleanupConvertedDisks(sourceVM, convertHost,
temporaryConvertLocation, convertedDisksPrefix);
+ }
}
return ((ImportConvertedInstanceAnswer)
importAnswer).getConvertedInstance();
}
+ private void cleanupConvertedDisks(String sourceVM, HostVO convertHost,
DataStoreTO temporaryConvertLocation, String convertedDisksPrefix) {
+ logger.debug("Cleaning up the converted disks for the VM {} through
the conversion host {}", sourceVM, convertHost.getName());
+ CleanupConvertedInstanceDisksCommand cleanupCommand =
+ new
CleanupConvertedInstanceDisksCommand(temporaryConvertLocation,
convertedDisksPrefix);
+ try {
+ Answer cleanupAnswer = agentManager.send(convertHost.getId(),
cleanupCommand);
+ if (!cleanupAnswer.getResult()) {
+ logger.warn("Failed to cleanup the converted disks for the VM
{} through " +
+ "the conversion host {}: {}", sourceVM,
convertHost.getName(), cleanupAnswer.getDetails());
+ }
+ } catch (AgentUnavailableException | OperationTimedoutException e) {
+ logger.error("Error cleaning up converted disks for VM {} through
the conversion host {}",
+ sourceVM, convertHost.getName(), e);
+ }
+ }
+
private List<StoragePoolVO>
findInstanceConversionDestinationStoragePoolsInCluster(
Cluster destinationCluster, ServiceOfferingVO serviceOffering,
Map<String, Long> dataDiskOfferingMap,