lxd/storage_ceph_utils.go
func (s *storageCeph) cephRBDVolumeBackupCreate(tmpPath string, backup backup, source container) error {
	sourceIsSnapshot := source.IsSnapshot()
	sourceContainerName := source.Name()
	sourceContainerOnlyName := projectPrefix(source.Project(), sourceContainerName)
	sourceSnapshotOnlyName := ""

	// Prepare for rsync
	rsync := func(oldPath string, newPath string, bwlimit string) error {
		output, err := rsyncLocalCopy(oldPath, newPath, bwlimit)
		if err != nil {
			return fmt.Errorf("Failed to rsync: %s: %s", string(output), err)
		}

		return nil
	}

	bwlimit := s.pool.Config["rsync.bwlimit"]
	// Create a temporary snapshot
	snapshotName := fmt.Sprintf("zombie_snapshot_%s", uuid.NewRandom().String())
	if sourceIsSnapshot {
		sourceContainerOnlyName, sourceSnapshotOnlyName, _ = containerGetParentAndSnapshotName(sourceContainerName)
		sourceContainerOnlyName = projectPrefix(source.Project(), sourceContainerOnlyName)
		snapshotName = fmt.Sprintf("snapshot_%s", projectPrefix(source.Project(), sourceSnapshotOnlyName))
	} else {
		// This is costly but we need to ensure that all cached data has
		// been committed to disk. If we don't then the rbd snapshot of
		// the underlying filesystem can be inconsistent or - worst case
		// - empty.
		syscall.Sync()

		// create snapshot
		err := cephRBDSnapshotCreate(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)
		if err != nil {
			return err
		}
		defer cephRBDSnapshotDelete(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)
	}

	// Protect volume so we can create clones of it
	err := cephRBDSnapshotProtect(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)
	if err != nil {
		return err
	}
	defer cephRBDSnapshotUnprotect(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)

	// Create a new volume from the snapshot
	cloneName := uuid.NewRandom().String()
	err = cephRBDCloneCreate(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.OSDPoolName, cloneName, "backup", s.UserName)
	if err != nil {
		return err
	}
	defer cephRBDVolumeDelete(s.ClusterName, s.OSDPoolName, cloneName, "backup", s.UserName)

	// Map the new volume
	RBDDevPath, err := cephRBDVolumeMap(s.ClusterName, s.OSDPoolName, cloneName, "backup", s.UserName)
	if err != nil {
		return err
	}
	defer cephRBDVolumeUnmap(s.ClusterName, s.OSDPoolName, cloneName, "backup", s.UserName, true)

	// Generate a new UUID if needed
	RBDFilesystem := s.getRBDFilesystem()
	msg, err := fsGenerateNewUUID(RBDFilesystem, RBDDevPath)
	if err != nil {
		logger.Errorf("Failed to create new UUID for filesystem \"%s\": %s: %s", RBDFilesystem, msg, err)
		return err
	}

	// Create a temporary mountpoing
	tmpContainerMntPoint, err := ioutil.TempDir("", "lxd_backup_")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpContainerMntPoint)

	err = os.Chmod(tmpContainerMntPoint, 0700)
	if err != nil {
		return err
	}

	// Mount the volume
	mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions())
	err = tryMount(RBDDevPath, tmpContainerMntPoint, RBDFilesystem, mountFlags, mountOptions)
	if err != nil {
		logger.Errorf("Failed to mount RBD device %s onto %s: %s", RBDDevPath, tmpContainerMntPoint, err)
		return err
	}
	logger.Debugf("Mounted RBD device %s onto %s", RBDDevPath, tmpContainerMntPoint)
	defer tryUnmount(tmpContainerMntPoint, syscall.MNT_DETACH)

	// Figure out the target name
	targetName := sourceContainerName
	if sourceIsSnapshot {
		_, targetName, _ = containerGetParentAndSnapshotName(sourceContainerName)
	}

	// Create the path for the backup.
	targetBackupMntPoint := fmt.Sprintf("%s/container", tmpPath)
	if sourceIsSnapshot {
		targetBackupMntPoint = fmt.Sprintf("%s/snapshots/%s", tmpPath, targetName)
	}

	err = os.MkdirAll(targetBackupMntPoint, 0711)
	if err != nil {
		return err
	}

	err = rsync(tmpContainerMntPoint, targetBackupMntPoint, bwlimit)
	if err != nil {
		return err
	}

	return nil
}
func (s *storageCeph) cephRBDVolumeBackupCreate(tmpPath string, backup backup, source container) error {
	sourceIsSnapshot := source.IsSnapshot()
	sourceContainerName := source.Name()
	sourceContainerOnlyName := projectPrefix(source.Project(), sourceContainerName)
	sourceSnapshotOnlyName := ""

	// Prepare for rsync
	rsync := func(oldPath string, newPath string, bwlimit string) error {
		output, err := rsyncLocalCopy(oldPath, newPath, bwlimit)
		if err != nil {
			return fmt.Errorf("Failed to rsync: %s: %s", string(output), err)
		}

		return nil
	}

	bwlimit := s.pool.Config["rsync.bwlimit"]
	// Create a temporary snapshot
	snapshotName := fmt.Sprintf("zombie_snapshot_%s", uuid.NewRandom().String())
	if sourceIsSnapshot {
		sourceContainerOnlyName, sourceSnapshotOnlyName, _ = containerGetParentAndSnapshotName(sourceContainerName)
		sourceContainerOnlyName = projectPrefix(source.Project(), sourceContainerOnlyName)
		snapshotName = fmt.Sprintf("snapshot_%s", projectPrefix(source.Project(), sourceSnapshotOnlyName))
	} else {
		// This is costly but we need to ensure that all cached data has
		// been committed to disk. If we don't then the rbd snapshot of
		// the underlying filesystem can be inconsistent or - worst case
		// - empty.
		syscall.Sync()

		// create snapshot
		err := cephRBDSnapshotCreate(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)
		if err != nil {
			return err
		}
		defer cephRBDSnapshotDelete(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)
	}

	// Protect volume so we can create clones of it
	err := cephRBDSnapshotProtect(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)
	if err != nil {
		return err
	}
	defer cephRBDSnapshotUnprotect(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.UserName)

	// Create a new volume from the snapshot
	cloneName := uuid.NewRandom().String()
	err = cephRBDCloneCreate(s.ClusterName, s.OSDPoolName, sourceContainerOnlyName, storagePoolVolumeTypeNameContainer, snapshotName, s.OSDPoolName, cloneName, "backup", s.UserName)
	if err != nil {
		return err
	}
	defer cephRBDVolumeDelete(s.ClusterName, s.OSDPoolName, cloneName, "backup", s.UserName)

	// Map the new volume
	RBDDevPath, err := cephRBDVolumeMap(s.ClusterName, s.OSDPoolName, cloneName, "backup", s.UserName)
	if err != nil {
		return err
	}
	defer cephRBDVolumeUnmap(s.ClusterName, s.OSDPoolName, cloneName, "backup", s.UserName, true)

	// Generate a new UUID if needed
	RBDFilesystem := s.getRBDFilesystem()
	msg, err := fsGenerateNewUUID(RBDFilesystem, RBDDevPath)
	if err != nil {
		logger.Errorf("Failed to create new UUID for filesystem \"%s\": %s: %s", RBDFilesystem, msg, err)
		return err
	}

	// Create a temporary mountpoing
	tmpContainerMntPoint, err := ioutil.TempDir("", "lxd_backup_")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tmpContainerMntPoint)

	err = os.Chmod(tmpContainerMntPoint, 0700)
	if err != nil {
		return err
	}

	// Mount the volume
	mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions())
	err = tryMount(RBDDevPath, tmpContainerMntPoint, RBDFilesystem, mountFlags, mountOptions)
	if err != nil {
		logger.Errorf("Failed to mount RBD device %s onto %s: %s", RBDDevPath, tmpContainerMntPoint, err)
		return err
	}
	logger.Debugf("Mounted RBD device %s onto %s", RBDDevPath, tmpContainerMntPoint)
	defer tryUnmount(tmpContainerMntPoint, syscall.MNT_DETACH)

	// Figure out the target name
	targetName := sourceContainerName
	if sourceIsSnapshot {
		_, targetName, _ = containerGetParentAndSnapshotName(sourceContainerName)
	}

	// Create the path for the backup.
	targetBackupMntPoint := fmt.Sprintf("%s/container", tmpPath)
	if sourceIsSnapshot {
		targetBackupMntPoint = fmt.Sprintf("%s/snapshots/%s", tmpPath, targetName)
	}

	err = os.MkdirAll(targetBackupMntPoint, 0711)
	if err != nil {
		return err
	}

	err = rsync(tmpContainerMntPoint, targetBackupMntPoint, bwlimit)
	if err != nil {
		return err
	}

	return nil
}
// cephRBDVolumeBackupCreate creates a backup of a container or snapshot.