Skip to content

Commit b1447c6

Browse files
[VMware to KVM Migration] Fix for converted instance npe issue when source vmware instance ovf is exported from management server
1 parent 0b841fe commit b1447c6

File tree

7 files changed

+12
-353
lines changed

7 files changed

+12
-353
lines changed

api/src/main/java/com/cloud/storage/VolumeApiService.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
171171
* </table>
172172
*/
173173
boolean doesStoragePoolSupportDiskOffering(StoragePool destPool, DiskOffering diskOffering);
174+
174175
boolean doesStoragePoolSupportDiskOfferingTags(StoragePool destPool, String diskOfferingTags);
175176

176177
Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge);

core/src/main/java/com/cloud/agent/api/ConvertInstanceAnswer.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,13 @@
1616
// under the License.
1717
package com.cloud.agent.api;
1818

19-
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
20-
2119
public class ConvertInstanceAnswer extends Answer {
2220

2321
private String temporaryConvertUuid;
2422

2523
public ConvertInstanceAnswer() {
2624
super();
2725
}
28-
private UnmanagedInstanceTO convertedInstance;
29-
30-
public ConvertInstanceAnswer(Command command, boolean success, String details) {
31-
super(command, success, details);
32-
}
33-
34-
public ConvertInstanceAnswer(Command command, UnmanagedInstanceTO convertedInstance) {
35-
super(command, true, "");
36-
this.convertedInstance = convertedInstance;
37-
}
3826

3927
public ConvertInstanceAnswer(Command command, String temporaryConvertUuid) {
4028
super(command, true, "");
@@ -44,8 +32,4 @@ public ConvertInstanceAnswer(Command command, String temporaryConvertUuid) {
4432
public String getTemporaryConvertUuid() {
4533
return temporaryConvertUuid;
4634
}
47-
48-
public UnmanagedInstanceTO getConvertedInstance() {
49-
return convertedInstance;
50-
}
5135
}

core/src/main/java/com/cloud/agent/api/ConvertInstanceCommand.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,10 @@
2020
import com.cloud.agent.api.to.RemoteInstanceTO;
2121
import com.cloud.hypervisor.Hypervisor;
2222

23-
import java.util.List;
24-
2523
public class ConvertInstanceCommand extends Command {
2624

2725
private RemoteInstanceTO sourceInstance;
2826
private Hypervisor.HypervisorType destinationHypervisorType;
29-
private List<String> destinationStoragePools;
3027
private DataStoreTO conversionTemporaryLocation;
3128
private String templateDirOnConversionLocation;
3229
private boolean checkConversionSupport;
@@ -36,12 +33,10 @@ public class ConvertInstanceCommand extends Command {
3633
public ConvertInstanceCommand() {
3734
}
3835

39-
public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType,
40-
List<String> destinationStoragePools, DataStoreTO conversionTemporaryLocation,
36+
public ConvertInstanceCommand(RemoteInstanceTO sourceInstance, Hypervisor.HypervisorType destinationHypervisorType, DataStoreTO conversionTemporaryLocation,
4137
String templateDirOnConversionLocation, boolean checkConversionSupport, boolean exportOvfToConversionLocation) {
4238
this.sourceInstance = sourceInstance;
4339
this.destinationHypervisorType = destinationHypervisorType;
44-
this.destinationStoragePools = destinationStoragePools;
4540
this.conversionTemporaryLocation = conversionTemporaryLocation;
4641
this.templateDirOnConversionLocation = templateDirOnConversionLocation;
4742
this.checkConversionSupport = checkConversionSupport;
@@ -56,10 +51,6 @@ public Hypervisor.HypervisorType getDestinationHypervisorType() {
5651
return destinationHypervisorType;
5752
}
5853

59-
public List<String> getDestinationStoragePools() {
60-
return destinationStoragePools;
61-
}
62-
6354
public DataStoreTO getConversionTemporaryLocation() {
6455
return conversionTemporaryLocation;
6556
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java

Lines changed: 6 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,12 @@
1818
//
1919
package com.cloud.hypervisor.kvm.resource.wrapper;
2020

21-
import java.io.BufferedInputStream;
22-
import java.io.File;
23-
import java.io.FileInputStream;
24-
import java.io.IOException;
25-
import java.io.InputStream;
2621
import java.net.URLEncoder;
2722
import java.nio.charset.Charset;
28-
import java.util.ArrayList;
2923
import java.util.List;
3024
import java.util.UUID;
31-
import java.util.stream.Collectors;
3225

3326
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
34-
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
35-
import org.apache.commons.collections.CollectionUtils;
36-
import org.apache.commons.io.IOUtils;
3727
import org.apache.commons.lang3.StringUtils;
3828

3929
import com.cloud.agent.api.Answer;
@@ -44,17 +34,12 @@
4434
import com.cloud.agent.api.to.RemoteInstanceTO;
4535
import com.cloud.hypervisor.Hypervisor;
4636
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
47-
import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser;
4837
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef;
49-
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
5038
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
5139
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
5240
import com.cloud.resource.CommandWrapper;
5341
import com.cloud.resource.ResourceWrapper;
54-
import com.cloud.storage.Storage;
5542
import com.cloud.utils.FileUtil;
56-
import com.cloud.utils.Pair;
57-
import com.cloud.utils.exception.CloudRuntimeException;
5843
import com.cloud.utils.script.OutputInterpreter;
5944
import com.cloud.utils.script.Script;
6045

@@ -77,15 +62,15 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve
7762
String msg = String.format("Cannot convert the instance %s from VMware as the virt-v2v binary is not found. " +
7863
"Please install virt-v2v%s on the host before attempting the instance conversion.", sourceInstanceName, serverResource.isUbuntuHost()? ", nbdkit" : "");
7964
logger.info(msg);
80-
return new ConvertInstanceAnswer(cmd, false, msg);
65+
return new Answer(cmd, false, msg);
8166
}
8267

8368
if (!areSourceAndDestinationHypervisorsSupported(sourceHypervisorType, destinationHypervisorType)) {
8469
String err = destinationHypervisorType != Hypervisor.HypervisorType.KVM ?
8570
String.format("The destination hypervisor type is %s, KVM was expected, cannot handle it", destinationHypervisorType) :
8671
String.format("The source hypervisor type %s is not supported for KVM conversion", sourceHypervisorType);
8772
logger.error(err);
88-
return new ConvertInstanceAnswer(cmd, false, err);
73+
return new Answer(cmd, false, err);
8974
}
9075

9176
final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
@@ -103,7 +88,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve
10388
if (StringUtils.isBlank(exportInstanceOVAUrl)) {
10489
String err = String.format("Couldn't export OVA for the VM %s, due to empty url", sourceInstanceName);
10590
logger.error(err);
106-
return new ConvertInstanceAnswer(cmd, false, err);
91+
return new Answer(cmd, false, err);
10792
}
10893

10994
int noOfThreads = cmd.getThreadsCountToExportOvf();
@@ -117,7 +102,7 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve
117102
if (!ovfExported) {
118103
String err = String.format("Export OVA for the VM %s failed", sourceInstanceName);
119104
logger.error(err);
120-
return new ConvertInstanceAnswer(cmd, false, err);
105+
return new Answer(cmd, false, err);
121106
}
122107
sourceOVFDirPath = String.format("%s%s/", sourceOVFDirPath, sourceInstanceName);
123108
} else {
@@ -140,15 +125,15 @@ public Answer execute(ConvertInstanceCommand cmd, LibvirtComputingResource serve
140125
"has a different virt-v2v version.",
141126
ovfTemplateDirOnConversionLocation);
142127
logger.error(err);
143-
return new ConvertInstanceAnswer(cmd, false, err);
128+
return new Answer(cmd, false, err);
144129
}
145130
return new ConvertInstanceAnswer(cmd, temporaryConvertUuid);
146131
} catch (Exception e) {
147132
String error = String.format("Error converting instance %s from %s, due to: %s",
148133
sourceInstanceName, sourceHypervisorType, e.getMessage());
149134
logger.error(error, e);
150135
cleanupSecondaryStorage = true;
151-
return new ConvertInstanceAnswer(cmd, false, error);
136+
return new Answer(cmd, false, error);
152137
} finally {
153138
if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) {
154139
String sourceOVFDir = String.format("%s/%s", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
@@ -205,55 +190,6 @@ private String getExportOVAUrlFromRemoteInstance(RemoteInstanceTO vmwareInstance
205190
encodedUsername, encodedPassword, vcenter, datacenter, vm);
206191
}
207192

208-
protected List<KVMPhysicalDisk> getTemporaryDisksFromParsedXml(KVMStoragePool pool, LibvirtDomainXMLParser xmlParser, String convertedBasePath) {
209-
List<LibvirtVMDef.DiskDef> disksDefs = xmlParser.getDisks();
210-
disksDefs = disksDefs.stream().filter(x -> x.getDiskType() == LibvirtVMDef.DiskDef.DiskType.FILE &&
211-
x.getDeviceType() == LibvirtVMDef.DiskDef.DeviceType.DISK).collect(Collectors.toList());
212-
if (CollectionUtils.isEmpty(disksDefs)) {
213-
String err = String.format("Cannot find any disk defined on the converted XML domain %s.xml", convertedBasePath);
214-
logger.error(err);
215-
throw new CloudRuntimeException(err);
216-
}
217-
sanitizeDisksPath(disksDefs);
218-
return getPhysicalDisksFromDefPaths(disksDefs, pool);
219-
}
220-
221-
private List<KVMPhysicalDisk> getPhysicalDisksFromDefPaths(List<LibvirtVMDef.DiskDef> disksDefs, KVMStoragePool pool) {
222-
List<KVMPhysicalDisk> disks = new ArrayList<>();
223-
for (LibvirtVMDef.DiskDef diskDef : disksDefs) {
224-
KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(diskDef.getDiskPath());
225-
disks.add(physicalDisk);
226-
}
227-
return disks;
228-
}
229-
230-
protected List<KVMPhysicalDisk> getTemporaryDisksWithPrefixFromTemporaryPool(KVMStoragePool pool, String path, String prefix) {
231-
String msg = String.format("Could not parse correctly the converted XML domain, checking for disks on %s with prefix %s", path, prefix);
232-
logger.info(msg);
233-
pool.refresh();
234-
List<KVMPhysicalDisk> disksWithPrefix = pool.listPhysicalDisks()
235-
.stream()
236-
.filter(x -> x.getName().startsWith(prefix) && !x.getName().endsWith(".xml"))
237-
.collect(Collectors.toList());
238-
if (CollectionUtils.isEmpty(disksWithPrefix)) {
239-
msg = String.format("Could not find any converted disk with prefix %s on temporary location %s", prefix, path);
240-
logger.error(msg);
241-
throw new CloudRuntimeException(msg);
242-
}
243-
return disksWithPrefix;
244-
}
245-
246-
private void cleanupDisksAndDomainFromTemporaryLocation(List<KVMPhysicalDisk> disks,
247-
KVMStoragePool temporaryStoragePool,
248-
String temporaryConvertUuid) {
249-
for (KVMPhysicalDisk disk : disks) {
250-
logger.info(String.format("Cleaning up temporary disk %s after conversion from temporary location", disk.getName()));
251-
temporaryStoragePool.deletePhysicalDisk(disk.getName(), Storage.ImageFormat.QCOW2);
252-
}
253-
logger.info(String.format("Cleaning up temporary domain %s after conversion from temporary location", temporaryConvertUuid));
254-
FileUtil.deleteFiles(temporaryStoragePool.getLocalPath(), temporaryConvertUuid, ".xml");
255-
}
256-
257193
protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
258194
for (LibvirtVMDef.DiskDef disk : disks) {
259195
String[] diskPathParts = disk.getDiskPath().split("/");
@@ -262,114 +198,6 @@ protected void sanitizeDisksPath(List<LibvirtVMDef.DiskDef> disks) {
262198
}
263199
}
264200

265-
protected List<KVMPhysicalDisk> moveTemporaryDisksToDestination(List<KVMPhysicalDisk> temporaryDisks,
266-
List<String> destinationStoragePools,
267-
KVMStoragePoolManager storagePoolMgr) {
268-
List<KVMPhysicalDisk> targetDisks = new ArrayList<>();
269-
if (temporaryDisks.size() != destinationStoragePools.size()) {
270-
String warn = String.format("Discrepancy between the converted instance disks (%s) " +
271-
"and the expected number of disks (%s)", temporaryDisks.size(), destinationStoragePools.size());
272-
logger.warn(warn);
273-
}
274-
for (int i = 0; i < temporaryDisks.size(); i++) {
275-
String poolPath = destinationStoragePools.get(i);
276-
KVMStoragePool destinationPool = storagePoolMgr.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, poolPath);
277-
if (destinationPool == null) {
278-
String err = String.format("Could not find a storage pool by URI: %s", poolPath);
279-
logger.error(err);
280-
continue;
281-
}
282-
if (destinationPool.getType() != Storage.StoragePoolType.NetworkFilesystem) {
283-
String err = String.format("Storage pool by URI: %s is not an NFS storage", poolPath);
284-
logger.error(err);
285-
continue;
286-
}
287-
KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
288-
if (logger.isDebugEnabled()) {
289-
String msg = String.format("Trying to copy converted instance disk number %s from the temporary location %s" +
290-
" to destination storage pool %s", i, sourceDisk.getPool().getLocalPath(), destinationPool.getUuid());
291-
logger.debug(msg);
292-
}
293-
294-
String destinationName = UUID.randomUUID().toString();
295-
296-
KVMPhysicalDisk destinationDisk = storagePoolMgr.copyPhysicalDisk(sourceDisk, destinationName, destinationPool, 7200 * 1000);
297-
targetDisks.add(destinationDisk);
298-
}
299-
return targetDisks;
300-
}
301-
302-
private UnmanagedInstanceTO getConvertedUnmanagedInstance(String baseName,
303-
List<KVMPhysicalDisk> vmDisks,
304-
LibvirtDomainXMLParser xmlParser) {
305-
UnmanagedInstanceTO instanceTO = new UnmanagedInstanceTO();
306-
instanceTO.setName(baseName);
307-
instanceTO.setDisks(getUnmanagedInstanceDisks(vmDisks, xmlParser));
308-
instanceTO.setNics(getUnmanagedInstanceNics(xmlParser));
309-
return instanceTO;
310-
}
311-
312-
private List<UnmanagedInstanceTO.Nic> getUnmanagedInstanceNics(LibvirtDomainXMLParser xmlParser) {
313-
List<UnmanagedInstanceTO.Nic> nics = new ArrayList<>();
314-
if (xmlParser != null) {
315-
List<LibvirtVMDef.InterfaceDef> interfaces = xmlParser.getInterfaces();
316-
for (LibvirtVMDef.InterfaceDef interfaceDef : interfaces) {
317-
UnmanagedInstanceTO.Nic nic = new UnmanagedInstanceTO.Nic();
318-
nic.setMacAddress(interfaceDef.getMacAddress());
319-
nic.setNicId(interfaceDef.getBrName());
320-
nic.setAdapterType(interfaceDef.getModel().toString());
321-
nics.add(nic);
322-
}
323-
}
324-
return nics;
325-
}
326-
327-
protected List<UnmanagedInstanceTO.Disk> getUnmanagedInstanceDisks(List<KVMPhysicalDisk> vmDisks, LibvirtDomainXMLParser xmlParser) {
328-
List<UnmanagedInstanceTO.Disk> instanceDisks = new ArrayList<>();
329-
List<LibvirtVMDef.DiskDef> diskDefs = xmlParser != null ? xmlParser.getDisks() : null;
330-
for (int i = 0; i< vmDisks.size(); i++) {
331-
KVMPhysicalDisk physicalDisk = vmDisks.get(i);
332-
KVMStoragePool storagePool = physicalDisk.getPool();
333-
UnmanagedInstanceTO.Disk disk = new UnmanagedInstanceTO.Disk();
334-
disk.setPosition(i);
335-
Pair<String, String> storagePoolHostAndPath = getNfsStoragePoolHostAndPath(storagePool);
336-
disk.setDatastoreHost(storagePoolHostAndPath.first());
337-
disk.setDatastorePath(storagePoolHostAndPath.second());
338-
disk.setDatastoreName(storagePool.getUuid());
339-
disk.setDatastoreType(storagePool.getType().name());
340-
disk.setCapacity(physicalDisk.getVirtualSize());
341-
disk.setFileBaseName(physicalDisk.getName());
342-
if (CollectionUtils.isNotEmpty(diskDefs)) {
343-
LibvirtVMDef.DiskDef diskDef = diskDefs.get(i);
344-
disk.setController(diskDef.getBusType() != null ? diskDef.getBusType().toString() : LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
345-
} else {
346-
// If the job is finished but we cannot parse the XML, the guest VM can use the virtio driver
347-
disk.setController(LibvirtVMDef.DiskDef.DiskBus.VIRTIO.toString());
348-
}
349-
instanceDisks.add(disk);
350-
}
351-
return instanceDisks;
352-
}
353-
354-
protected Pair<String, String> getNfsStoragePoolHostAndPath(KVMStoragePool storagePool) {
355-
String sourceHostIp = null;
356-
String sourcePath = null;
357-
List<String[]> commands = new ArrayList<>();
358-
commands.add(new String[]{Script.getExecutableAbsolutePath("mount")});
359-
commands.add(new String[]{Script.getExecutableAbsolutePath("grep"), storagePool.getLocalPath()});
360-
String storagePoolMountPoint = Script.executePipedCommands(commands, 0).second();
361-
logger.debug(String.format("NFS Storage pool: %s - local path: %s, mount point: %s", storagePool.getUuid(), storagePool.getLocalPath(), storagePoolMountPoint));
362-
if (StringUtils.isNotEmpty(storagePoolMountPoint)) {
363-
String[] res = storagePoolMountPoint.strip().split(" ");
364-
res = res[0].split(":");
365-
if (res.length > 1) {
366-
sourceHostIp = res[0].strip();
367-
sourcePath = res[1].strip();
368-
}
369-
}
370-
return new Pair<>(sourceHostIp, sourcePath);
371-
}
372-
373201
private boolean exportOVAFromVMOnVcenter(String vmExportUrl,
374202
String targetOvfDir,
375203
int noOfThreads,
@@ -412,27 +240,6 @@ protected boolean performInstanceConversion(String sourceOVFDirPath,
412240
return exitValue == 0;
413241
}
414242

415-
protected LibvirtDomainXMLParser parseMigratedVMXmlDomain(String installPath) throws IOException {
416-
String xmlPath = String.format("%s.xml", installPath);
417-
if (!new File(xmlPath).exists()) {
418-
String err = String.format("Conversion failed. Unable to find the converted XML domain, expected %s", xmlPath);
419-
logger.error(err);
420-
throw new CloudRuntimeException(err);
421-
}
422-
InputStream is = new BufferedInputStream(new FileInputStream(xmlPath));
423-
String xml = IOUtils.toString(is, Charset.defaultCharset());
424-
final LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser();
425-
try {
426-
parser.parseDomainXML(xml);
427-
return parser;
428-
} catch (RuntimeException e) {
429-
String err = String.format("Error parsing the converted instance XML domain at %s: %s", xmlPath, e.getMessage());
430-
logger.error(err, e);
431-
logger.debug(xml);
432-
return null;
433-
}
434-
}
435-
436243
protected String encodeUsername(String username) {
437244
return URLEncoder.encode(username, Charset.defaultCharset());
438245
}

0 commit comments

Comments
 (0)