CLOUDSTACK-1158: Wrap qemu-img into it's own util

Signed-off-by: Edison Su <sudison@gmail.com>
This commit is contained in:
Wido den Hollander 2013-04-01 16:37:18 -07:00 committed by Edison Su
parent aa79ccf985
commit ea0daeaee3
15 changed files with 949 additions and 133 deletions

View File

@ -67,6 +67,16 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.14</version>
<configuration>
<excludes>
<exclude>**/Qemu*.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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;
@ -193,7 +197,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;
@ -1418,12 +1421,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;
@ -2230,14 +2233,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();

View File

@ -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;

View File

@ -15,25 +15,13 @@
// specific language governing permissions and limitations
// 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"), TAR("tar"), DIR("dir");
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;

View File

@ -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 {

View File

@ -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,18 +135,18 @@ 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 if (template.getFormat() == KVMPhysicalDisk.PhysicalDiskFormat.DIR) {
} else if (template.getFormat() == PhysicalDiskFormat.DIR) {
return adaptor.createDiskFromTemplate(template, name,
KVMPhysicalDisk.PhysicalDiskFormat.DIR,
PhysicalDiskFormat.DIR,
template.getSize(), destPool);
} else {
return adaptor.createDiskFromTemplate(template, name,
KVMPhysicalDisk.PhysicalDiskFormat.QCOW2,
PhysicalDiskFormat.QCOW2,
template.getSize(), destPool);
}
}

View File

@ -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;
@ -399,11 +404,11 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
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) {
@ -647,57 +652,57 @@ 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 (template.getFormat() == PhysicalDiskFormat.TAR) {
Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath());
} else if (template.getFormat() == PhysicalDiskFormat.DIR) {
Script.runSimpleBashScript("mkdir -p " + disk.getPath());
Script.runSimpleBashScript("chmod 755 " + disk.getPath());
Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath());
} else 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 (template.getFormat() == PhysicalDiskFormat.TAR) {
Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath());
} else if (template.getFormat() == PhysicalDiskFormat.DIR) {
Script.runSimpleBashScript("mkdir -p " + disk.getPath());
Script.runSimpleBashScript("chmod 755 " + disk.getPath());
Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath());
} else 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;
}
@ -759,49 +764,65 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
PhysicalDiskFormat sourceFormat = disk.getFormat();
PhysicalDiskFormat destFormat = newDisk.getFormat();
QemuImg qemu = new QemuImg();
QemuImgFile srcFile = null;
QemuImgFile destFile = null;
if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() != StoragePoolType.RBD)) {
if (sourceFormat == PhysicalDiskFormat.TAR) {
Script.runSimpleBashScript("tar -x -f " + sourcePath + " -C " + destPath);
} else if (sourceFormat == PhysicalDiskFormat.DIR) {
Script.runSimpleBashScript("mkdir -p " + destPath);
Script.runSimpleBashScript("chmod 755 " + destPath);
Script.runSimpleBashScript("cp -p -r " + sourcePath + "/* " + destPath);
} else if (sourceFormat.equals(destFormat) &&
Script.runSimpleBashScript("qemu-img info " + sourcePath + "|grep backing") == null) {
Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath);
} else {
Script.runSimpleBashScript("qemu-img convert -f " + sourceFormat
+ " -O " + destFormat
+ " " + sourcePath
+ " " + destPath);
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(),
destPool.getSourcePort(),
destPool.getAuthUserName(),
destPool.getAuthSecret(),
destPath));
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(),
srcPool.getSourcePort(),
srcPool.getAuthUserName(),
srcPool.getAuthSecret(),
sourcePath)
+ " " + KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(),
destPool.getSourcePort(),
destPool.getAuthUserName(),
destPool.getAuthSecret(),
destPath));
srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(),
srcPool.getSourcePort(),
srcPool.getAuthUserName(),
srcPool.getAuthSecret(),
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;
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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"), TAR("tar"), DIR("dir");
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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,62 @@
// 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.Ignore;
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;
@Ignore
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);
}
}

View File

@ -0,0 +1,289 @@
// 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.Ignore;
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;
@Ignore
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();
}
}

View File

@ -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);
}
}