You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by wi...@apache.org on 2013/02/26 17:29:33 UTC

git commit: refs/heads/qemu-img - kvm: Wrap qemu-img into it's own util

Updated Branches:
  refs/heads/qemu-img e1cb7927c -> 4d022483f (forced update)


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/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/4d022483
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/4d022483
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/4d022483

Branch: refs/heads/qemu-img
Commit: 4d022483f2a57e8bfb49c6ac36663023be0ee3fe
Parents: 0b35f71
Author: Wido den Hollander <wi...@widodh.nl>
Authored: Tue Feb 5 23:05:55 2013 +0100
Committer: Wido den Hollander <wi...@widodh.nl>
Committed: Tue Feb 26 17:14:49 2013 +0100

----------------------------------------------------------------------
 .../kvm/resource/LibvirtComputingResource.java     |   32 +-
 .../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, 917 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4d022483/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 99b8723..1094dc6 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
@@ -55,6 +55,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;
@@ -188,7 +192,6 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.hostNicType;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef;
 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;
@@ -1318,12 +1321,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;
@@ -2129,14 +2132,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();
@@ -3181,7 +3195,7 @@ ServerResource {
 
         if (!foundDisk) {
             s_logger.debug("generating new patch disk for " + vmName + " since none was found");
-            KVMPhysicalDisk disk = pool.createPhysicalDisk(patchName, KVMPhysicalDisk.PhysicalDiskFormat.RAW,
+            KVMPhysicalDisk disk = pool.createPhysicalDisk(patchName, PhysicalDiskFormat.RAW,
                     10L * 1024 * 1024);
         } else {
             s_logger.debug("found existing patch disk at " + patchDiskPath + " using it for " + vmName);

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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 5437e7c..9ab1216 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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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 d350ef9..eae2314 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;
@@ -411,11 +416,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) {
@@ -586,50 +591,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;
     }
@@ -687,39 +694,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/incubator-cloudstack/blob/4d022483/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 32f8ce9..cba8aad 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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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/incubator-cloudstack/blob/4d022483/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