}
String destPath = newDisk.getPath();
PhysicalDiskFormat destFormat = newDisk.getFormat();
QemuImg qemu = new QemuImg(timeout);
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, timeout);
} else if (sourceFormat == PhysicalDiskFormat.DIR) {
Script.runSimpleBashScript("mkdir -p " + destPath);
Script.runSimpleBashScript("chmod 755 " + destPath);
Script.runSimpleBashScript("cp -p -r " + sourcePath + "/* " + destPath, timeout);
} else {
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) {
String result = Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath, timeout);
if (result != null) {
throw new CloudRuntimeException("Failed to create disk: " + result);
}
} else {
destFile = new QemuImgFile(destPath, destFormat);
try {
qemu.convert(srcFile, destFile);
} catch (QemuImgException e) {
s_logger.error("Failed to convert " + srcFile.getFileName() + " to "
+ destFile.getFileName() + " the error was: " + e.getMessage());
newDisk = null;
}
}
} catch (QemuImgException e) {
s_logger.error("Failed to fetch the information of file "
+ srcFile.getFileName() + " the error was: " + e.getMessage());
newDisk = null;
}
}
} else if ((srcPool.getType() != StoragePoolType.RBD) && (destPool.getType() == StoragePoolType.RBD)) {
/**
* Qemu doesn't support writing to RBD format 2 directly, so we have to write to a temporary RAW file first
* which we then convert to RBD format 2.
*
* A HUGE performance gain can be achieved here if QCOW2 -> RBD format 2 can be done in one step
*/
s_logger.debug("The source image is not RBD, but the destination is. We will convert into RBD format 2");
String sourceFile;
boolean useTmpFile = false;
try {
if (sourceFormat != destFormat) {
srcFile = new QemuImgFile(sourcePath, sourceFormat);
destFile = new QemuImgFile("/tmp/" + name);
s_logger.debug("Converting " + srcFile.getFileName() + " to " + destFile.getFileName() + " as a temporary file for RBD conversion");
qemu.convert(srcFile, destFile);
sourceFile = destFile.getFileName();
useTmpFile = true;
} else {
// Source file is RAW, we can write directly to RBD
sourceFile = sourcePath;
}
// We now convert the temporary file to a RBD image with format 2
Rados r = new Rados(destPool.getAuthUserName());
r.confSet("mon_host", destPool.getSourceHost() + ":" + destPool.getSourcePort());
r.confSet("key", destPool.getAuthSecret());
r.connect();
s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));
IoCTX io = r.ioCtxCreate(destPool.getSourceDir());
Rbd rbd = new Rbd(io);
s_logger.debug("Creating RBD image " + name + " in Ceph pool " + destPool.getSourceDir() + " with RBD format 2");
rbd.create(name, disk.getVirtualSize(), this.rbdFeatures, this.rbdOrder);
RbdImage image = rbd.open(name);
File fh = new File(sourceFile);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fh));
int chunkSize = 4194304;
long offset = 0;
s_logger.debug("Reading file " + sourceFile + " (" + fh.length() + " bytes) into RBD image " + name + " in chunks of " + chunkSize + " bytes");
while(true) {
byte[] buf = new byte[chunkSize];
int bytes = bis.read(buf);
if (bytes <= 0) {
break;
}
image.write(buf, offset, bytes);
offset += bytes;
}
s_logger.debug("Completed writing " + sourceFile + " to RBD image " + name + ". Bytes written: " + offset);
bis.close();
if (useTmpFile) {
s_logger.debug("Removing temporary file " + sourceFile);
fh.delete();
}
/* Snapshot the image and protect that snapshot so we can clone (layer) from it */
s_logger.debug("Creating RBD snapshot " + this.rbdTemplateSnapName + " on image " + name);
image.snapCreate(this.rbdTemplateSnapName);
s_logger.debug("Protecting RBD snapshot " + this.rbdTemplateSnapName + " on image " + name);
image.snapProtect(this.rbdTemplateSnapName);
rbd.close(image);
r.ioCtxDestroy(io);
} catch (QemuImgException e) {
s_logger.error("Failed to do a temp convert from " + srcFile.getFileName() + " to "
+ destFile.getFileName() + " the error was: " + e.getMessage());
newDisk = null;
} catch (RadosException e) {
s_logger.error("A Ceph RADOS operation failed (" + e.getReturnValue() + "). The error was: " + e.getMessage());
newDisk = null;
} catch (RbdException e) {
s_logger.error("A Ceph RBD operation failed (" + e.getReturnValue() + "). The error was: " + e.getMessage());
newDisk = null;
} catch (IOException e) {
s_logger.error("Failed reading the temporary file during the conversion to RBD: " + e.getMessage());
newDisk = null;
}
} else {
/**
We let Qemu-Img do the work here. Although we could work with librbd and have that do the cloning
it doesn't benefit us. It's better to keep the current code in place which works
*/
srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(srcPool.getSourceHost(),
srcPool.getSourcePort(),
srcPool.getAuthUserName(),
srcPool.getAuthSecret(),
sourcePath));
srcFile.setFormat(sourceFormat);
destFile = new QemuImgFile(destPath);
destFile.setFormat(destFormat);
try {
qemu.convert(srcFile, destFile);
} catch (QemuImgException e) {
s_logger.error("Failed to convert " + srcFile.getFileName() + " to "
+ destFile.getFileName() + " the error was: " + e.getMessage());
newDisk = null;
}