kvm: Wrap qemu-img into it's own util The KVM Agent used to run Script.runSimpleBashScript for various qemu-img methods, but the output was not always checked properly.
This patch wraps qemu-img into it's own util to make integration more clean. The goal is also to have more reliable executing since we can better check if qemu-img did it's job. Tests are also included to test the code. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/44197a0a Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/44197a0a Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/44197a0a Branch: refs/heads/qemu-img Commit: 44197a0ab140e4d12528a26b436f838b2f2e02d6 Parents: 5a16e70 Author: Wido den Hollander <w...@widodh.nl> Authored: Tue Feb 5 23:05:55 2013 +0100 Committer: Wido den Hollander <w...@42on.com> Committed: Sat Mar 30 10:57:56 2013 +0100 ---------------------------------------------------------------------- .../kvm/resource/LibvirtComputingResource.java | 30 +- .../kvm/resource/LibvirtDomainXMLParser.java | 1 + .../hypervisor/kvm/storage/KVMPhysicalDisk.java | 15 +- .../hypervisor/kvm/storage/KVMStoragePool.java | 2 +- .../kvm/storage/KVMStoragePoolManager.java | 9 +- .../kvm/storage/LibvirtStorageAdaptor.java | 150 ++++--- .../hypervisor/kvm/storage/LibvirtStoragePool.java | 2 +- .../hypervisor/kvm/storage/StorageAdaptor.java | 2 +- utils/src/com/cloud/utils/script/Script.java | 21 +- .../org/apache/cloudstack/utils/qemu/QemuImg.java | 352 +++++++++++++++ .../cloudstack/utils/qemu/QemuImgException.java | 25 + .../apache/cloudstack/utils/qemu/QemuImgFile.java | 72 +++ .../cloudstack/utils/qemu/QemuImgFileTest.java | 60 +++ .../apache/cloudstack/utils/qemu/QemuImgTest.java | 287 ++++++++++++ 14 files changed, 916 insertions(+), 112 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 6852e2c..556cbcc 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -57,6 +57,10 @@ import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImgException; import org.libvirt.Connect; import org.libvirt.Domain; import org.libvirt.DomainInfo; @@ -192,7 +196,6 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VirtioSerialDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; -import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; import com.cloud.network.Networks.BroadcastDomainType; @@ -1400,12 +1403,12 @@ ServerResource { StoragePoolType poolType = pool.getType(); PhysicalDiskFormat volFormat = vol.getFormat(); - if(pool.getType() == StoragePoolType.CLVM && volFormat == KVMPhysicalDisk.PhysicalDiskFormat.RAW) { + if(pool.getType() == StoragePoolType.CLVM && volFormat == PhysicalDiskFormat.RAW) { return "CLVM"; } else if ((poolType == StoragePoolType.NetworkFilesystem || poolType == StoragePoolType.SharedMountPoint || poolType == StoragePoolType.Filesystem) - && volFormat == KVMPhysicalDisk.PhysicalDiskFormat.QCOW2 ) { + && volFormat == PhysicalDiskFormat.QCOW2 ) { return "QCOW2"; } return null; @@ -2212,14 +2215,25 @@ ServerResource { } } else { s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + cmd.getUniqueName()); - Script.runSimpleBashScript("qemu-img convert" - + " -f raw -O qcow2 " - + KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), + + QemuImgFile srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(), primary.getAuthSecret(), - disk.getPath()) - + " " + tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); + disk.getPath())); + srcFile.setFormat(PhysicalDiskFormat.RAW); + + QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); + destFile.setFormat(PhysicalDiskFormat.QCOW2); + + QemuImg q = new QemuImg(); + try { + q.convert(srcFile, destFile); + } catch (QemuImgException e) { + s_logger.error("Failed to create new template while converting " + + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " + e.getMessage()); + } + File templateProp = new File(tmpltPath + "/template.properties"); if (!templateProp.exists()) { templateProp.createNewFile(); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index b622b6d..893dfa9 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -25,6 +25,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java index 08f51a4..907c251 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java @@ -16,24 +16,13 @@ // under the License. package com.cloud.hypervisor.kvm.storage; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + public class KVMPhysicalDisk { private String path; private String name; private KVMStoragePool pool; - public static enum PhysicalDiskFormat { - RAW("raw"), QCOW2("qcow2"); - String format; - - private PhysicalDiskFormat(String format) { - this.format = format; - } - - public String toString() { - return this.format; - } - } - public static String RBDStringBuilder(String monHost, int monPort, String authUserName, String authSecret, String image) { String rbdOpts; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java index d32a6fd..a1721e1 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java @@ -18,7 +18,7 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; -import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import com.cloud.storage.Storage.StoragePoolType; public interface KVMStoragePool { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index c2bfad9..2a3b14e 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -23,10 +23,11 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.HashMap; import java.util.UUID; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + import com.cloud.hypervisor.kvm.resource.KVMHABase; import com.cloud.hypervisor.kvm.resource.KVMHABase.PoolType; import com.cloud.hypervisor.kvm.resource.KVMHAMonitor; -import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.utils.exception.CloudRuntimeException; @@ -134,14 +135,14 @@ public class KVMStoragePoolManager { // LibvirtStorageAdaptor-specific statement if (destPool.getType() == StoragePoolType.RBD) { return adaptor.createDiskFromTemplate(template, name, - KVMPhysicalDisk.PhysicalDiskFormat.RAW, template.getSize(), destPool); + PhysicalDiskFormat.RAW, template.getSize(), destPool); } else if (destPool.getType() == StoragePoolType.CLVM) { return adaptor.createDiskFromTemplate(template, name, - KVMPhysicalDisk.PhysicalDiskFormat.RAW, template.getSize(), + PhysicalDiskFormat.RAW, template.getSize(), destPool); } else { return adaptor.createDiskFromTemplate(template, name, - KVMPhysicalDisk.PhysicalDiskFormat.QCOW2, + PhysicalDiskFormat.QCOW2, template.getSize(), destPool); } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 5e83ef6..b2f5faf 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -22,8 +22,14 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.Map; +import java.util.HashMap; import org.apache.log4j.Logger; import org.apache.commons.codec.binary.Base64; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImgException; import org.libvirt.Connect; import org.libvirt.LibvirtException; import org.libvirt.Secret; @@ -43,7 +49,6 @@ import com.cloud.hypervisor.kvm.resource.LibvirtStoragePoolDef.poolType; import com.cloud.hypervisor.kvm.resource.LibvirtStoragePoolDef.authType; import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeDef.volFormat; import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeXMLParser; -import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; import com.cloud.exception.InternalErrorException; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; @@ -392,11 +397,11 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { if (voldef.getFormat() == null) { disk.setFormat(pool.getDefaultFormat()); } else if (pool.getType() == StoragePoolType.RBD) { - disk.setFormat(KVMPhysicalDisk.PhysicalDiskFormat.RAW); + disk.setFormat(PhysicalDiskFormat.RAW); } else if (voldef.getFormat() == LibvirtStorageVolumeDef.volFormat.QCOW2) { - disk.setFormat(KVMPhysicalDisk.PhysicalDiskFormat.QCOW2); + disk.setFormat(PhysicalDiskFormat.QCOW2); } else if (voldef.getFormat() == LibvirtStorageVolumeDef.volFormat.RAW) { - disk.setFormat(KVMPhysicalDisk.PhysicalDiskFormat.RAW); + disk.setFormat(PhysicalDiskFormat.RAW); } return disk; } catch (LibvirtException e) { @@ -637,50 +642,52 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { We then create a KVMPhysicalDisk object that we can return */ - if (destPool.getType() != StoragePoolType.RBD) { - disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize()); - - if (format == PhysicalDiskFormat.QCOW2) { - Script.runSimpleBashScript("qemu-img create -f " - + template.getFormat() + " -b " + template.getPath() + " " - + disk.getPath()); - } else if (format == PhysicalDiskFormat.RAW) { - Script.runSimpleBashScript("qemu-img convert -f " - + template.getFormat() + " -O raw " + template.getPath() - + " " + disk.getPath()); - } - } else { - disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool); - disk.setFormat(format); - disk.setSize(template.getVirtualSize()); - disk.setVirtualSize(disk.getSize()); - - if (srcPool.getType() != StoragePoolType.RBD) { - Script.runSimpleBashScript("qemu-img convert" - + " -f " + template.getFormat() - + " -O " + format - + " " + template.getPath() - + " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), - destPool.getSourcePort(), - destPool.getAuthUserName(), - destPool.getAuthSecret(), - disk.getPath())); + try { + if (destPool.getType() != StoragePoolType.RBD) { + disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize()); + + if (format == PhysicalDiskFormat.QCOW2) { + QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); + QemuImgFile destFile = new QemuImgFile(disk.getPath()); + QemuImg qemu = new QemuImg(); + qemu.create(destFile, backingFile); + } else if (format == PhysicalDiskFormat.RAW) { + QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat()); + QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW); + QemuImg qemu = new QemuImg(); + qemu.convert(sourceFile, destFile); + } } else { - template.setFormat(PhysicalDiskFormat.RAW); - Script.runSimpleBashScript("qemu-img convert" - + " -f " + template.getFormat() - + " -O " + format - + " " + KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), - srcPool.getSourcePort(), - srcPool.getAuthUserName(), - srcPool.getAuthSecret(), - template.getPath()) - + " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), - destPool.getSourcePort(), - destPool.getAuthUserName(), - destPool.getAuthSecret(), - disk.getPath())); + disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool); + disk.setFormat(format); + disk.setSize(template.getVirtualSize()); + disk.setVirtualSize(disk.getSize()); + + QemuImg qemu = new QemuImg(); + QemuImgFile srcFile; + QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), + destPool.getSourcePort(), + destPool.getAuthUserName(), + destPool.getAuthSecret(), + disk.getPath())); + destFile.setFormat(format); + + if (srcPool.getType() != StoragePoolType.RBD) { + srcFile = new QemuImgFile(template.getPath(), template.getFormat()); + } else { + template.setFormat(PhysicalDiskFormat.RAW); + srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), + srcPool.getSourcePort(), + srcPool.getAuthUserName(), + srcPool.getAuthSecret(), + template.getPath())); + srcFile.setFormat(template.getFormat()); + } + qemu.convert(srcFile, destFile); } + } catch (QemuImgException e) { + s_logger.error("Failed to create " + disk.getPath() + + " due to a failed executing of qemu-img: " + e.getMessage()); } return disk; } @@ -738,39 +745,54 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { PhysicalDiskFormat sourceFormat = disk.getFormat(); PhysicalDiskFormat destFormat = newDisk.getFormat(); - if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() != StoragePoolType.RBD)) { - if (sourceFormat.equals(destFormat) && - Script.runSimpleBashScript("qemu-img info " + sourcePath + "|grep backing") == null) { - Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath); + QemuImg qemu = new QemuImg(); + QemuImgFile srcFile = null; + QemuImgFile destFile = null; - } else { - Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat - + " -O " + destFormat - + " " + sourcePath - + " " + destPath); + if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() != StoragePoolType.RBD)) { + srcFile = new QemuImgFile(sourcePath, sourceFormat); + try { + Map<String, String> info = qemu.info(srcFile); + String backingFile = info.get(new String("backing_file")); + if (sourceFormat.equals(destFormat) && backingFile == null) { + Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath); + } else { + destFile = new QemuImgFile(destPath, destFormat); + } + } catch (QemuImgException e) { + s_logger.error("Failed to fetch the information of file " + + srcFile.getFileName() + " the error was: " + e.getMessage()); } } else if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() == StoragePoolType.RBD)) { - Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat - + " -O " + destFormat - + " " + sourcePath - + " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), + srcFile = new QemuImgFile(sourcePath, sourceFormat); + destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), destPool.getAuthUserName(), destPool.getAuthSecret(), destPath)); + destFile.setFormat(destFormat); } else { - Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat - + " -O " + destFormat - + " " + KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), + srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(), srcPool.getSourcePort(), srcPool.getAuthUserName(), srcPool.getAuthSecret(), - sourcePath) - + " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), + sourcePath)); + srcFile.setFormat(sourceFormat); + destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), destPool.getAuthUserName(), destPool.getAuthSecret(), destPath)); + destFile.setFormat(destFormat); + } + + if (srcFile != null && destFile != null) { + try { + qemu.convert(srcFile, destFile); + } catch (QemuImgException e) { + s_logger.error("Failed to convert " + srcFile.getFileName() + " to " + + destFile.getFileName() + " the error was: " + e.getMessage()); + } } return newDisk; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 1396097..2ce5175 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -18,9 +18,9 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.libvirt.StoragePool; -import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; import com.cloud.storage.Storage.StoragePoolType; public class LibvirtStoragePool implements KVMStoragePool { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index 79c3b92..dd75677 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -18,7 +18,7 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; -import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import com.cloud.storage.Storage.StoragePoolType; public interface StorageAdaptor { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/utils/src/com/cloud/utils/script/Script.java ---------------------------------------------------------------------- diff --git a/utils/src/com/cloud/utils/script/Script.java b/utils/src/com/cloud/utils/script/Script.java index cb25844..3632bf5 100755 --- a/utils/src/com/cloud/utils/script/Script.java +++ b/utils/src/com/cloud/utils/script/Script.java @@ -460,20 +460,7 @@ public class Script implements Callable<String> { } public static String runSimpleBashScript(String command) { - - Script s = new Script("/bin/bash"); - s.add("-c"); - s.add(command); - - OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); - if (s.execute(parser) != null) - return null; - - String result = parser.getLine(); - if (result == null || result.trim().isEmpty()) - return null; - else - return result.trim(); + return Script.runSimpleBashScript(command, 0); } public static String runSimpleBashScript(String command, int timeout) { @@ -493,10 +480,4 @@ public class Script implements Callable<String> { return result.trim(); } - public static void main(String[] args) { - String path = findScript(".", "try.sh"); - Script script = new Script(path, 5000, s_logger); - script.execute(); - System.exit(1); - } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/utils/src/org/apache/cloudstack/utils/qemu/QemuImg.java ---------------------------------------------------------------------- diff --git a/utils/src/org/apache/cloudstack/utils/qemu/QemuImg.java b/utils/src/org/apache/cloudstack/utils/qemu/QemuImg.java new file mode 100644 index 0000000..26c1a61 --- /dev/null +++ b/utils/src/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -0,0 +1,352 @@ +// 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 +// 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 org.apache.cloudstack.utils.qemu; + +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImgException; + +import com.cloud.utils.script.Script; +import com.cloud.utils.script.OutputInterpreter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +public class QemuImg { + + /* The qemu-img binary. We expect this to be in $PATH */ + public String _qemuImgPath = "qemu-img"; + + /* Shouldn't we have KVMPhysicalDisk and LibvirtVMDef read this? */ + public static enum PhysicalDiskFormat { + RAW("raw"), QCOW2("qcow2"), VMDK("vmdk"), FILE("file"), RBD("rbd"), SHEEPDOG("sheepdog"), HTTP("http"), HTTPS("https"); + String format; + + private PhysicalDiskFormat(String format) { + this.format = format; + } + + public String toString() { + return this.format; + } + } + + public QemuImg() { + + } + + /** + * Create a QemuImg object + * + * + * @param qemuImgPath + * A alternative path to the qemu-img binary + * @return void + */ + public QemuImg(String qemuImgPath) { + this._qemuImgPath = qemuImgPath; + } + + /* These are all methods supported by the qemu-img tool */ + + /* Perform a consistency check on the disk image */ + public void check(QemuImgFile file) { + + } + + /** + * Create a new image + * + * This method calls 'qemu-img create' + * + * @param file + * The file to create + * @param backingFile + * A backing file if used (for example with qcow2) + * @param options + * Options for the create. Takes a Map<String, String> with key value + * pairs which are passed on to qemu-img without validation. + * @return void + */ + public void create(QemuImgFile file, QemuImgFile backingFile, Map<String, String> options) throws QemuImgException { + Script s = new Script(_qemuImgPath); + s.add("create"); + + if (options != null && !options.isEmpty()) { + s.add("-o"); + String optionsStr = ""; + for (Map.Entry<String, String> option : options.entrySet()) { + optionsStr += option.getKey() + "=" + option.getValue() + ","; + } + s.add(optionsStr); + } + + /* + -b for a backing file does not show up in the docs, but it works. + Shouldn't this be -o backing_file=filename instead? + */ + s.add("-f"); + if (backingFile != null) { + s.add(backingFile.getFormat().toString()); + s.add("-b"); + s.add(backingFile.getFileName()); + } else { + s.add(file.getFormat().toString()); + } + + s.add(file.getFileName()); + + if (backingFile == null) { + s.add(Long.toString(file.getSize())); + } + String result = s.execute(); + if (result != null) { + throw new QemuImgException(result); + } + } + + /** + * Create a new image + * + * This method calls 'qemu-img create' + * + * @param file + * The file to create + * @return void + */ + public void create(QemuImgFile file) throws QemuImgException { + this.create(file, null, null); + } + + /** + * Create a new image + * + * This method calls 'qemu-img create' + * + * @param file + * The file to create + * @param backingFile + * A backing file if used (for example with qcow2) + * @return void + */ + public void create(QemuImgFile file, QemuImgFile backingFile) throws QemuImgException { + this.create(file, backingFile, null); + } + + /** + * Create a new image + * + * This method calls 'qemu-img create' + * + * @param file + * The file to create + * @param options + * Options for the create. Takes a Map<String, String> with key value + * pairs which are passed on to qemu-img without validation. + * @return void + */ + public void create(QemuImgFile file, Map<String, String> options) throws QemuImgException { + this.create(file, null, options); + } + + /** + * Convert a image from source to destination + * + * This method calls 'qemu-img convert' and takes two objects + * as an argument. + * + * + * @param srcFile + * The source file + * @param destFile + * The destination file + * @param options + * Options for the convert. Takes a Map<String, String> with key value + * pairs which are passed on to qemu-img without validation. + * @return void + */ + public void convert(QemuImgFile srcFile, QemuImgFile destFile, Map<String, String> options) throws QemuImgException { + Script s = new Script(_qemuImgPath); + s.add("convert"); + s.add("-f"); + s.add(srcFile.getFormat().toString()); + s.add("-O"); + s.add(destFile.getFormat().toString()); + + if (options != null && !options.isEmpty()) { + s.add("-o"); + String optionsStr = ""; + for (Map.Entry<String, String> option : options.entrySet()) { + optionsStr += option.getKey() + "=" + option.getValue() + ","; + } + s.add(optionsStr); + } + + s.add(srcFile.getFileName()); + s.add(destFile.getFileName()); + + String result = s.execute(); + if (result != null) { + throw new QemuImgException(result); + } + } + + /** + * Convert a image from source to destination + * + * This method calls 'qemu-img convert' and takes two objects + * as an argument. + * + * + * @param srcFile + * The source file + * @param destFile + * The destination file + * @return void + */ + public void convert(QemuImgFile srcFile, QemuImgFile destFile) throws QemuImgException { + this.convert(srcFile, destFile, null); + } + + /** + * Commit the changes recorded in the file in its base image. + * + * This method calls 'qemu-img commit' and takes one object as + * an argument + * + * @param file + * The file of which changes have to be committed + * @return void + */ + public void commit(QemuImgFile file) throws QemuImgException { + + } + + /** + * Execute qemu-img info for the given file + * + * Qemu-img returns human readable output, but this method does it's best + * to turn that into machine readeable data. + * + * Spaces in keys are replaced by underscores (_). + * Sizes (virtual_size and disk_size) are returned in bytes + * Paths (image and backing_file) are the absolute path to the file + * + * @param file + * A QemuImgFile object containing the file to get the information from + * @return A HashMap with String key-value information as returned by 'qemu-img info' + */ + public Map<String, String> info(QemuImgFile file) throws QemuImgException { + Script s = new Script(_qemuImgPath); + s.add("info"); + s.add(file.getFileName()); + OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); + String result = s.execute(parser); + if (result != null) { + throw new QemuImgException(result); + } + + HashMap<String,String> info = new HashMap<String,String>(); + String[] outputBuffer = parser.getLines().trim().split("\n"); + for (int i = 0; i < outputBuffer.length; i++) { + String[] lineBuffer = outputBuffer[i].split(":", 2); + if (lineBuffer.length == 2) { + String key = lineBuffer[0].trim().replace(" ", "_"); + String value = null; + + if (key.equals("virtual_size")) { + value = lineBuffer[1].trim().replaceAll("^.*\\(([0-9]+).*$", "$1"); + } else { + value = lineBuffer[1].trim(); + } + + info.put(key, value); + } + } + return info; + } + + /* List, apply, create or delete snapshots in image */ + public void snapshot() throws QemuImgException { + + } + + /* Changes the backing file of an image */ + public void rebase() throws QemuImgException { + + } + + /** + * Resize an image + * + * This method simple calls 'qemu-img resize'. + * A negative size value will get prefixed with - and a positive with + + * + * Sizes are in bytes and will be passed on that way + * + * @param file + * The file to resize + * @param size + * The new size + * @param delta + * Flag if the new size is a delta + */ + public void resize(QemuImgFile file, long size, boolean delta) throws QemuImgException { + String newSize = null; + + if (size == 0) { + throw new QemuImgException("size should never be exactly zero"); + } + + if (delta) { + if (size > 0) { + newSize = "+" + Long.toString(size); + } else { + newSize = Long.toString(size); + } + } else { + if (size <= 0) { + throw new QemuImgException("size should not be negative if 'delta' is false!"); + } + newSize = Long.toString(size); + } + + Script s = new Script(_qemuImgPath); + s.add("resize"); + s.add(file.getFileName()); + s.add(newSize); + s.execute(); + } + + /** + * Resize an image + * + * This method simple calls 'qemu-img resize'. + * A negative size value will get prefixed with - and a positive with + + * + * Sizes are in bytes and will be passed on that way + * + * @param file + * The file to resize + * @param size + * The new size + */ + public void resize(QemuImgFile file, long size) throws QemuImgException { + this.resize(file, size, false); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/utils/src/org/apache/cloudstack/utils/qemu/QemuImgException.java ---------------------------------------------------------------------- diff --git a/utils/src/org/apache/cloudstack/utils/qemu/QemuImgException.java b/utils/src/org/apache/cloudstack/utils/qemu/QemuImgException.java new file mode 100644 index 0000000..082ebe4 --- /dev/null +++ b/utils/src/org/apache/cloudstack/utils/qemu/QemuImgException.java @@ -0,0 +1,25 @@ +// 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 +// 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 org.apache.cloudstack.utils.qemu; + +public class QemuImgException extends Exception { + + public QemuImgException(String message) { + super(message); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/utils/src/org/apache/cloudstack/utils/qemu/QemuImgFile.java ---------------------------------------------------------------------- diff --git a/utils/src/org/apache/cloudstack/utils/qemu/QemuImgFile.java b/utils/src/org/apache/cloudstack/utils/qemu/QemuImgFile.java new file mode 100644 index 0000000..90d925d --- /dev/null +++ b/utils/src/org/apache/cloudstack/utils/qemu/QemuImgFile.java @@ -0,0 +1,72 @@ +// 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 +// 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 org.apache.cloudstack.utils.qemu; + +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + +public class QemuImgFile { + + private long size = 0; + private String fileName; + private PhysicalDiskFormat format = PhysicalDiskFormat.RAW; + + public QemuImgFile(String fileName) { + this.fileName = fileName; + } + + public QemuImgFile(String fileName, long size) { + this.fileName = fileName; + this.size = size; + } + + public QemuImgFile(String fileName, long size, PhysicalDiskFormat format) { + this.fileName = fileName; + this.size = size; + this.format = format; + } + + public QemuImgFile(String fileName, PhysicalDiskFormat format) { + this.fileName = fileName; + this.size = size; + this.format = format; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public void setSize(long size) { + this.size = size; + } + + public void setFormat(PhysicalDiskFormat format) { + this.format = format; + } + + public String getFileName() { + return this.fileName; + } + + public long getSize() { + return this.size; + } + + public PhysicalDiskFormat getFormat() { + return this.format; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/utils/test/org/apache/cloudstack/utils/qemu/QemuImgFileTest.java ---------------------------------------------------------------------- diff --git a/utils/test/org/apache/cloudstack/utils/qemu/QemuImgFileTest.java b/utils/test/org/apache/cloudstack/utils/qemu/QemuImgFileTest.java new file mode 100644 index 0000000..761113f --- /dev/null +++ b/utils/test/org/apache/cloudstack/utils/qemu/QemuImgFileTest.java @@ -0,0 +1,60 @@ +// 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 +// 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 org.apache.cloudstack.utils.qemu; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + +public class QemuImgFileTest { + @Test + public void testFileNameAtContructor() { + String filename = "/tmp/test-image.qcow2"; + QemuImgFile file = new QemuImgFile(filename); + assertEquals(file.getFileName(), filename); + } + + @Test + public void testFileNameAndSizeAtContructor() { + long size = 1024; + String filename = "/tmp/test-image.qcow2"; + QemuImgFile file = new QemuImgFile(filename, size); + assertEquals(file.getFileName(), filename); + assertEquals(file.getSize(), size); + } + + @Test + public void testFileNameAndSizeAndFormatAtContructor() { + PhysicalDiskFormat format = PhysicalDiskFormat.RAW; + long size = 1024; + String filename = "/tmp/test-image.qcow2"; + QemuImgFile file = new QemuImgFile(filename, size, format); + assertEquals(file.getFileName(), filename); + assertEquals(file.getSize(), size); + assertEquals(file.getFormat(), format); + } + + @Test + public void testFileNameAndFormatAtContructor() { + PhysicalDiskFormat format = PhysicalDiskFormat.RAW; + String filename = "/tmp/test-image.qcow2"; + QemuImgFile file = new QemuImgFile(filename, format); + assertEquals(file.getFileName(), filename); + assertEquals(file.getFormat(), format); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/44197a0a/utils/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java ---------------------------------------------------------------------- diff --git a/utils/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java b/utils/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java new file mode 100644 index 0000000..22755eb --- /dev/null +++ b/utils/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java @@ -0,0 +1,287 @@ +// 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 +// 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 org.apache.cloudstack.utils.qemu; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import java.util.Map; +import java.util.HashMap; +import java.util.UUID; +import java.io.File; + +public class QemuImgTest { + + @Test + public void testCreateAndInfo() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + /* 10TB virtual_size */ + long size = 10995116277760l; + QemuImgFile file = new QemuImgFile(filename, size, PhysicalDiskFormat.QCOW2); + + QemuImg qemu = new QemuImg(); + qemu.create(file); + Map<String, String> info = qemu.info(file); + + if (info == null) { + fail("We didn't get any information back from qemu-img"); + } + + Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + assertEquals(Long.valueOf(size), Long.valueOf(infoSize)); + + String infoPath = info.get(new String("image")); + assertEquals(filename, infoPath); + + File f = new File(filename); + f.delete(); + + } + + @Test + public void testCreateAndInfoWithOptions() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + /* 10TB virtual_size */ + long size = 10995116277760l; + QemuImgFile file = new QemuImgFile(filename, size, PhysicalDiskFormat.QCOW2); + String clusterSize = "131072"; + Map<String, String> options = new HashMap<String, String>(); + + options.put("cluster_size", clusterSize); + + QemuImg qemu = new QemuImg(); + qemu.create(file, options); + Map<String, String> info = qemu.info(file); + + Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + assertEquals(Long.valueOf(size), Long.valueOf(infoSize)); + + String infoPath = info.get(new String("image")); + assertEquals(filename, infoPath); + + String infoClusterSize = info.get(new String("cluster_size")); + assertEquals(clusterSize, infoClusterSize); + + File f = new File(filename); + f.delete(); + + } + + @Test + public void testCreateAndResize() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + long startSize = 20480; + long endSize = 40960; + QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.QCOW2); + + try { + QemuImg qemu = new QemuImg(); + qemu.create(file); + qemu.resize(file, endSize); + Map<String, String> info = qemu.info(file); + + if (info == null) { + fail("We didn't get any information back from qemu-img"); + } + + Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + assertEquals(Long.valueOf(endSize), Long.valueOf(infoSize)); + } catch (QemuImgException e) { + fail(e.getMessage()); + } + + File f = new File(filename); + f.delete(); + + } + + @Test + public void testCreateAndResizeDeltaPositive() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + long startSize = 20480; + long increment = 20480; + QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.RAW); + + try { + QemuImg qemu = new QemuImg(); + qemu.create(file); + qemu.resize(file, increment, true); + Map<String, String> info = qemu.info(file); + + if (info == null) { + fail("We didn't get any information back from qemu-img"); + } + + Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + assertEquals(Long.valueOf(startSize + increment), Long.valueOf(infoSize)); + } catch (QemuImgException e) { + fail(e.getMessage()); + } + + File f = new File(filename); + f.delete(); + } + + @Test + public void testCreateAndResizeDeltaNegative() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + long startSize = 81920; + long increment = -40960; + QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.RAW); + + try { + QemuImg qemu = new QemuImg(); + qemu.create(file); + qemu.resize(file, increment, true); + Map<String, String> info = qemu.info(file); + + if (info == null) { + fail("We didn't get any information back from qemu-img"); + } + + Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + assertEquals(Long.valueOf(startSize + increment), Long.valueOf(infoSize)); + } catch (QemuImgException e) { + fail(e.getMessage()); + } + + File f = new File(filename); + f.delete(); + } + + @Test(expected = QemuImgException.class) + public void testCreateAndResizeFail() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + long startSize = 20480; + + /* Negative new size, expect failure */ + long endSize = -1; + QemuImgFile file = new QemuImgFile(filename, startSize, PhysicalDiskFormat.QCOW2); + + QemuImg qemu = new QemuImg(); + try { + qemu.create(file); + qemu.resize(file, endSize); + } finally { + File f = new File(filename); + f.delete(); + } + } + + @Test(expected = QemuImgException.class) + public void testCreateAndResizeZero() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + long startSize = 20480; + QemuImgFile file = new QemuImgFile(filename, 20480, PhysicalDiskFormat.QCOW2); + + QemuImg qemu = new QemuImg(); + qemu.create(file); + qemu.resize(file, 0); + + File f = new File(filename); + f.delete(); + + } + + @Test + public void testCreateWithBackingFile() throws QemuImgException { + String firstFileName = "/tmp/" + UUID.randomUUID() + ".qcow2"; + String secondFileName = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + QemuImgFile firstFile = new QemuImgFile(firstFileName, 20480, PhysicalDiskFormat.QCOW2); + QemuImgFile secondFile = new QemuImgFile(secondFileName, PhysicalDiskFormat.QCOW2); + + QemuImg qemu = new QemuImg(); + qemu.create(firstFile); + qemu.create(secondFile, firstFile); + + Map<String, String> info = qemu.info(secondFile); + if (info == null) { + fail("We didn't get any information back from qemu-img"); + } + + String backingFile = info.get(new String("backing_file")); + if (backingFile == null) { + fail("The second file does not have a property backing_file! Create failed?"); + } + } + + @Test + public void testConvertBasic() throws QemuImgException { + long srcSize = 20480; + String srcFileName = "/tmp/" + UUID.randomUUID() + ".qcow2"; + String destFileName = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + QemuImgFile srcFile = new QemuImgFile(srcFileName, srcSize); + QemuImgFile destFile = new QemuImgFile(destFileName); + + QemuImg qemu = new QemuImg(); + qemu.create(srcFile); + qemu.convert(srcFile, destFile); + Map<String, String> info = qemu.info(destFile); + if (info == null) { + fail("We didn't get any information back from qemu-img"); + } + + File sf = new File(srcFileName); + sf.delete(); + + File df = new File(destFileName); + df.delete(); + + } + + @Test + public void testConvertAdvanced() throws QemuImgException { + long srcSize = 4019200; + String srcFileName = "/tmp/" + UUID.randomUUID() + ".qcow2"; + String destFileName = "/tmp/" + UUID.randomUUID() + ".qcow2"; + PhysicalDiskFormat srcFormat = PhysicalDiskFormat.RAW; + PhysicalDiskFormat destFormat = PhysicalDiskFormat.QCOW2; + + QemuImgFile srcFile = new QemuImgFile(srcFileName, srcSize, srcFormat); + QemuImgFile destFile = new QemuImgFile(destFileName, destFormat); + + QemuImg qemu = new QemuImg(); + qemu.create(srcFile); + qemu.convert(srcFile, destFile); + + Map<String, String> info = qemu.info(destFile); + + PhysicalDiskFormat infoFormat = PhysicalDiskFormat.valueOf(info.get(new String("file_format")).toUpperCase()); + assertEquals(destFormat, infoFormat); + + Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + assertEquals(Long.valueOf(srcSize), Long.valueOf(infoSize)); + + File sf = new File(srcFileName); + sf.delete(); + + File df = new File(destFileName); + df.delete(); + + } +} \ No newline at end of file