Skip to content

Commit 8d2baf8

Browse files
committed
rbd: Use qemu-img to backup up a RBD snapshot to Secondary Storage
This reduces the amount of time and storage it takes dramatically. We no longer do a full copy, but a sparse copy. The destination image is still in RAW format, but we only copy over used blocks. Qemu is also better in doing this then us doing it in Java code.
1 parent ea29adb commit 8d2baf8

File tree

1 file changed

+28
-49
lines changed

1 file changed

+28
-49
lines changed

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import static com.cloud.utils.S3Utils.mputFile;
2222
import static com.cloud.utils.S3Utils.putFile;
2323

24-
import java.io.BufferedOutputStream;
2524
import java.io.File;
2625
import java.io.FileNotFoundException;
2726
import java.io.FileOutputStream;
@@ -49,9 +48,7 @@
4948

5049
import com.ceph.rados.IoCTX;
5150
import com.ceph.rados.Rados;
52-
import com.ceph.rados.RadosException;
5351
import com.ceph.rbd.Rbd;
54-
import com.ceph.rbd.RbdException;
5552
import com.ceph.rbd.RbdImage;
5653

5754
import org.apache.cloudstack.storage.command.AttachAnswer;
@@ -691,66 +688,48 @@ public Answer backupSnapshot(CopyCommand cmd) {
691688

692689
long size = 0;
693690
/**
694-
* RBD snapshots can't be copied using qemu-img, so we have to use
695-
* the Java bindings for librbd here.
691+
* Since Ceph version Dumpling (0.67.X) librbd / Qemu supports converting RBD
692+
* snapshots to RAW/QCOW2 files directly.
696693
*
697-
* These bindings will read the snapshot and write the contents to
698-
* the secondary storage directly
699-
*
700-
* It will stop doing so if the amount of time spend is longer then
701-
* cmds.timeout
694+
* This reduces the amount of time and storage it takes to back up a snapshot dramatically
702695
*/
703696
if (primaryPool.getType() == StoragePoolType.RBD) {
697+
String rbdSnapshot = snapshotDisk.getPath() + "@" + snapshotName;
698+
String snapshotFile = snapshotDestPath + "/" + snapshotName;
704699
try {
705-
Rados r = new Rados(primaryPool.getAuthUserName());
706-
r.confSet("mon_host", primaryPool.getSourceHost() + ":" + primaryPool.getSourcePort());
707-
r.confSet("key", primaryPool.getAuthSecret());
708-
r.confSet("client_mount_timeout", "30");
709-
r.connect();
710-
s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));
711-
712-
IoCTX io = r.ioCtxCreate(primaryPool.getSourceDir());
713-
Rbd rbd = new Rbd(io);
714-
RbdImage image = rbd.open(snapshotDisk.getName(), snapshotName);
700+
s_logger.debug("Attempting to backup RBD snapshot " + rbdSnapshot);
715701

716702
File snapDir = new File(snapshotDestPath);
717-
s_logger.debug("Attempting to create " + snapDir.getAbsolutePath() + " recursively");
703+
s_logger.debug("Attempting to create " + snapDir.getAbsolutePath() + " recursively for snapshot storage");
718704
FileUtils.forceMkdir(snapDir);
719705

720-
File snapFile = new File(snapshotDestPath + "/" + snapshotName);
721-
s_logger.debug("Backing up RBD snapshot to " + snapFile.getAbsolutePath());
722-
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(snapFile));
723-
int chunkSize = 4194304;
724-
long offset = 0;
725-
while (true) {
726-
byte[] buf = new byte[chunkSize];
727-
728-
int bytes = image.read(offset, buf, chunkSize);
729-
if (bytes <= 0) {
730-
break;
731-
}
732-
bos.write(buf, 0, bytes);
733-
offset += bytes;
734-
}
735-
s_logger.debug("Completed backing up RBD snapshot " + snapshotName + " to " + snapFile.getAbsolutePath() + ". Bytes written: " + offset);
736-
size = offset;
737-
bos.close();
706+
QemuImgFile srcFile =
707+
new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primaryPool.getSourceHost(), primaryPool.getSourcePort(), primaryPool.getAuthUserName(),
708+
primaryPool.getAuthSecret(), rbdSnapshot));
709+
srcFile.setFormat(PhysicalDiskFormat.RAW);
738710

739-
s_logger.debug("Attempting to remove snapshot RBD " + snapshotName + " from image " + snapshotDisk.getName());
740-
image.snapRemove(snapshotName);
711+
QemuImgFile destFile = new QemuImgFile(snapshotFile);
712+
destFile.setFormat(srcFile.getFormat());
741713

742-
r.ioCtxDestroy(io);
743-
} catch (RadosException e) {
744-
s_logger.error("A RADOS operation failed. The error was: " + e.getMessage());
745-
return new CopyCmdAnswer(e.toString());
746-
} catch (RbdException e) {
747-
s_logger.error("A RBD operation on " + snapshotDisk.getName() + " failed. The error was: " + e.getMessage());
748-
return new CopyCmdAnswer(e.toString());
714+
s_logger.debug("Backing up RBD snapshot " + rbdSnapshot + " to " + snapshotFile);
715+
QemuImg q = new QemuImg(cmd.getWaitInMillSeconds());
716+
q.convert(srcFile, destFile);
717+
718+
File snapFile = new File(snapshotFile);
719+
if(snapFile.exists()) {
720+
size = snapFile.length();
721+
}
722+
723+
s_logger.debug("Finished backing up RBD snapshot " + rbdSnapshot + " to " + snapshotFile + " Snapshot size: " + size);
749724
} catch (FileNotFoundException e) {
750725
s_logger.error("Failed to open " + snapshotDestPath + ". The error was: " + e.getMessage());
751726
return new CopyCmdAnswer(e.toString());
752727
} catch (IOException e) {
753-
s_logger.debug("An I/O error occured during a snapshot operation on " + snapshotDestPath);
728+
s_logger.error("Failed to create " + snapshotDestPath + ". The error was: " + e.getMessage());
729+
return new CopyCmdAnswer(e.toString());
730+
} catch (QemuImgException e) {
731+
s_logger.error("Failed to backup the RBD snapshot from " + rbdSnapshot +
732+
" to " + snapshotFile + " the error was: " + e.getMessage());
754733
return new CopyCmdAnswer(e.toString());
755734
}
756735
} else {

0 commit comments

Comments
 (0)