|
21 | 21 | import static com.cloud.utils.S3Utils.mputFile; |
22 | 22 | import static com.cloud.utils.S3Utils.putFile; |
23 | 23 |
|
24 | | -import java.io.BufferedOutputStream; |
25 | 24 | import java.io.File; |
26 | 25 | import java.io.FileNotFoundException; |
27 | 26 | import java.io.FileOutputStream; |
|
49 | 48 |
|
50 | 49 | import com.ceph.rados.IoCTX; |
51 | 50 | import com.ceph.rados.Rados; |
52 | | -import com.ceph.rados.RadosException; |
53 | 51 | import com.ceph.rbd.Rbd; |
54 | | -import com.ceph.rbd.RbdException; |
55 | 52 | import com.ceph.rbd.RbdImage; |
56 | 53 |
|
57 | 54 | import org.apache.cloudstack.storage.command.AttachAnswer; |
@@ -691,66 +688,48 @@ public Answer backupSnapshot(CopyCommand cmd) { |
691 | 688 |
|
692 | 689 | long size = 0; |
693 | 690 | /** |
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. |
696 | 693 | * |
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 |
702 | 695 | */ |
703 | 696 | if (primaryPool.getType() == StoragePoolType.RBD) { |
| 697 | + String rbdSnapshot = snapshotDisk.getPath() + "@" + snapshotName; |
| 698 | + String snapshotFile = snapshotDestPath + "/" + snapshotName; |
704 | 699 | 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); |
715 | 701 |
|
716 | 702 | 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"); |
718 | 704 | FileUtils.forceMkdir(snapDir); |
719 | 705 |
|
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); |
738 | 710 |
|
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()); |
741 | 713 |
|
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); |
749 | 724 | } catch (FileNotFoundException e) { |
750 | 725 | s_logger.error("Failed to open " + snapshotDestPath + ". The error was: " + e.getMessage()); |
751 | 726 | return new CopyCmdAnswer(e.toString()); |
752 | 727 | } 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()); |
754 | 733 | return new CopyCmdAnswer(e.toString()); |
755 | 734 | } |
756 | 735 | } else { |
|
0 commit comments