The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6625

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===

From d296b744b605fb8e497d01e2c8b361a4948021bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Sun, 15 Dec 2019 01:42:10 -0500
Subject: [PATCH 1/3] lxd/storage: Add createParentSnapshotDirIfMissing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/storage/drivers/driver_dir.go |  8 +++++++-
 lxd/storage/drivers/utils.go      | 12 ++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index 5e3788dd9e..d54c19f071 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -818,8 +818,14 @@ func (d *dir) CreateVolumeSnapshot(snapVol Volume, op 
*operations.Operation) err
        srcPath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
        snapPath := snapVol.MountPath()
 
+       // Create the parent directory.
+       err := createParentSnapshotDirIfMissing(d.name, snapVol.volType, 
parentName)
+       if err != nil {
+               return err
+       }
+
        // Create snapshot directory.
-       err := snapVol.CreateMountPath()
+       err = snapVol.CreateMountPath()
        if err != nil {
                return err
        }
diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index d3606a28a3..dc6b2fd87c 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -175,6 +175,18 @@ func GetSnapshotVolumeName(parentName, snapshotName 
string) string {
        return fmt.Sprintf("%s%s%s", parentName, shared.SnapshotDelimiter, 
snapshotName)
 }
 
+// createParentSnapshotDirIfMissing creates the parent directory for volume 
snapshots
+func createParentSnapshotDirIfMissing(poolName string, volType VolumeType, 
volName string) error {
+       snapshotsPath := GetVolumeSnapshotDir(poolName, volType, volName)
+
+       // If it's missing, create it.
+       if !shared.PathExists(snapshotsPath) {
+               return os.Mkdir(snapshotsPath, 0700)
+       }
+
+       return nil
+}
+
 // deleteParentSnapshotDirIfEmpty removes the parent snapshot directory if it 
is empty.
 // It accepts the pool name, volume type and parent volume name.
 func deleteParentSnapshotDirIfEmpty(poolName string, volType VolumeType, 
volName string) error {

From 8d83d2f4202dd5b2875402c52070f36ea1b37f72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Sun, 15 Dec 2019 20:30:59 -0500
Subject: [PATCH 2/3] lxd/storage/cephfs: Cleanup driver
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This doesn't affect any of the functional logic.
It only:
 - Splits functions between different files
 - Adds comments to all functions
 - Re-order functions in a more readable order.
 - Removes un-needed checks.

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/storage/drivers/driver_cephfs.go         | 749 +------------------
 lxd/storage/drivers/driver_cephfs_utils.go   |  92 +++
 lxd/storage/drivers/driver_cephfs_volumes.go | 609 +++++++++++++++
 3 files changed, 723 insertions(+), 727 deletions(-)
 create mode 100644 lxd/storage/drivers/driver_cephfs_utils.go
 create mode 100644 lxd/storage/drivers/driver_cephfs_volumes.go

diff --git a/lxd/storage/drivers/driver_cephfs.go 
b/lxd/storage/drivers/driver_cephfs.go
index 7049a0568c..b4923586e3 100644
--- a/lxd/storage/drivers/driver_cephfs.go
+++ b/lxd/storage/drivers/driver_cephfs.go
@@ -1,23 +1,17 @@
 package drivers
 
 import (
-       "bufio"
        "fmt"
-       "io"
        "io/ioutil"
        "os"
        "os/exec"
        "path/filepath"
-       "strconv"
        "strings"
 
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
-       "github.com/lxc/lxd/lxd/rsync"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
-       "github.com/lxc/lxd/shared/ioprogress"
-       "github.com/lxc/lxd/shared/units"
 )
 
 var cephfsVersion string
@@ -27,6 +21,7 @@ type cephfs struct {
        common
 }
 
+// load is used to run one-time action per-driver rather than per-pool.
 func (d *cephfs) load() error {
        if cephfsLoaded {
                return nil
@@ -54,28 +49,25 @@ func (d *cephfs) load() error {
        return nil
 }
 
+// Info returns the pool driver information.
 func (d *cephfs) Info() Info {
        return Info{
-               Name:               "cephfs",
-               Version:            cephfsVersion,
-               Remote:             true,
-               OptimizedImages:    false,
-               PreservesInodes:    false,
-               VolumeTypes:        []VolumeType{VolumeTypeCustom},
-               BlockBacking:       false,
-               RunningQuotaResize: true,
+               Name:                  "cephfs",
+               Version:               cephfsVersion,
+               OptimizedImages:       false,
+               PreservesInodes:       false,
+               Remote:                true,
+               VolumeTypes:           []VolumeType{VolumeTypeCustom},
+               BlockBacking:          false,
+               RunningQuotaResize:    true,
+               RunningSnapshotFreeze: false,
        }
 }
 
-func (d *cephfs) HasVolume(vol Volume) bool {
-       if shared.PathExists(vol.MountPath()) {
-               return true
-       }
-
-       return false
-}
-
+// Create is called during pool creation and is effectively using an empty 
driver struct.
+// WARNING: The Create() function cannot rely on any of the struct attributes 
being set.
 func (d *cephfs) Create() error {
+       // Config validation.
        if d.config["source"] == "" {
                return fmt.Errorf("Missing required source name/path")
        }
@@ -84,6 +76,7 @@ func (d *cephfs) Create() error {
                return fmt.Errorf("cephfs.path must match the source")
        }
 
+       // Set default properties if missing.
        if d.config["cephfs.cluster_name"] == "" {
                d.config["cephfs.cluster_name"] = "ceph"
        }
@@ -155,6 +148,7 @@ func (d *cephfs) Create() error {
        return nil
 }
 
+// Delete clears any local and remote data related to this driver instance.
 func (d *cephfs) Delete(op *operations.Operation) error {
        // Parse the namespace / path.
        fields := strings.SplitN(d.config["cephfs.path"], "/", 2)
@@ -237,14 +231,17 @@ func (d *cephfs) Delete(op *operations.Operation) error {
        return nil
 }
 
+// Validate checks that all provide keys are supported and that no conflicting 
or missing configuration is present.
 func (d *cephfs) Validate(config map[string]string) error {
        return nil
 }
 
+// Update applies any driver changes required from a configuration change.
 func (d *cephfs) Update(changedConfig map[string]string) error {
        return nil
 }
 
+// Mount brings up the driver and sets it up to be used.
 func (d *cephfs) Mount() (bool, error) {
        // Check if already mounted.
        if shared.IsMountPoint(GetPoolMountPath(d.name)) {
@@ -275,719 +272,17 @@ func (d *cephfs) Mount() (bool, error) {
        return true, nil
 }
 
+// Unmount clears any of the runtime state of the driver.
 func (d *cephfs) Unmount() (bool, error) {
        return forceUnmount(GetPoolMountPath(d.name))
 }
 
+// GetResources returns the pool resource usage information.
 func (d *cephfs) GetResources() (*api.ResourcesStoragePool, error) {
        return d.vfsGetResources()
 }
 
-func (d *cephfs) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
-       return d.validateVolume(vol, nil, removeUnknownKeys)
-}
-
-// GetVolumeDiskPath returns the location of a root disk block device.
-func (d *cephfs) GetVolumeDiskPath(vol Volume) (string, error) {
-       return "", ErrNotImplemented
-}
-
-func (d *cephfs) CreateVolume(vol Volume, filler *VolumeFiller, op 
*operations.Operation) error {
-       if vol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       if vol.contentType != ContentTypeFS {
-               return fmt.Errorf("Content type not supported")
-       }
-
-       volPath := vol.MountPath()
-
-       err := os.MkdirAll(volPath, 0711)
-       if err != nil {
-               return err
-       }
-
-       revertPath := true
-       defer func() {
-               if revertPath {
-                       os.RemoveAll(volPath)
-               }
-       }()
-
-       if filler != nil && filler.Fill != nil {
-               d.logger.Debug("Running filler function")
-               err = filler.Fill(volPath, "")
-               if err != nil {
-                       return err
-               }
-       }
-
-       revertPath = false
-       return nil
-}
-
-func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots 
bool, op *operations.Operation) error {
-       if vol.volType != VolumeTypeCustom || srcVol.volType != 
VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       if vol.contentType != ContentTypeFS || srcVol.contentType != 
ContentTypeFS {
-               return fmt.Errorf("Content type not supported")
-       }
-
-       bwlimit := d.config["rsync.bwlimit"]
-
-       // Create the main volume path.
-       volPath := vol.MountPath()
-       err := vol.CreateMountPath()
-       if err != nil {
-               return err
-       }
-
-       // Create slice of snapshots created if revert needed later.
-       revertSnaps := []string{}
-       defer func() {
-               if revertSnaps == nil {
-                       return
-               }
-
-               // Remove any paths created if we are reverting.
-               for _, snapName := range revertSnaps {
-                       fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
-
-                       snapVol := NewVolume(d, d.name, vol.volType, 
vol.contentType, fullSnapName, vol.config)
-                       d.DeleteVolumeSnapshot(snapVol, op)
-               }
-
-               os.RemoveAll(volPath)
-       }()
-
-       // Ensure the volume is mounted.
-       err = vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
-               // If copyring snapshots is indicated, check the source isn't 
itself a snapshot.
-               if copySnapshots && !srcVol.IsSnapshot() {
-                       // Get the list of snapshots from the source.
-                       srcSnapshots, err := srcVol.Snapshots(op)
-                       if err != nil {
-                               return err
-                       }
-
-                       for _, srcSnapshot := range srcSnapshots {
-                               _, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(srcSnapshot.name)
-
-                               // Mount the source snapshot.
-                               err = srcSnapshot.MountTask(func(srcMountPath 
string, op *operations.Operation) error {
-                                       // Copy the snapshot.
-                                       _, err = rsync.LocalCopy(srcMountPath, 
mountPath, bwlimit, false)
-                                       return err
-                               }, op)
-
-                               // Create the snapshot itself.
-                               err = d.CreateVolumeSnapshot(srcSnapshot, op)
-                               if err != nil {
-                                       return err
-                               }
-
-                               // Setup the revert.
-                               revertSnaps = append(revertSnaps, snapName)
-                       }
-               }
-
-               // Apply the volume quota if specified.
-               err = d.SetVolumeQuota(vol, vol.config["size"], op)
-               if err != nil {
-                       return err
-               }
-
-               // Copy source to destination (mounting each volume if needed).
-               return srcVol.MountTask(func(srcMountPath string, op 
*operations.Operation) error {
-                       _, err := rsync.LocalCopy(srcMountPath, mountPath, 
bwlimit, false)
-                       return err
-               }, op)
-       }, op)
-       if err != nil {
-               return err
-       }
-
-       revertSnaps = nil // Don't revert.
-       return nil
-}
-
-func (d *cephfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots 
[]Volume, op *operations.Operation) error {
-       return ErrNotImplemented
-}
-
-func (d *cephfs) DeleteVolume(vol Volume, op *operations.Operation) error {
-       if vol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       snapshots, err := d.VolumeSnapshots(vol, op)
-       if err != nil {
-               return err
-       }
-
-       if len(snapshots) > 0 {
-               return fmt.Errorf("Cannot remove a volume that has snapshots")
-       }
-
-       volPath := GetVolumeMountPath(d.name, vol.volType, vol.name)
-
-       // If the volume doesn't exist, then nothing more to do.
-       if !shared.PathExists(volPath) {
-               return nil
-       }
-
-       // Remove the volume from the storage device.
-       err = os.RemoveAll(volPath)
-       if err != nil {
-               return err
-       }
-
-       // Although the volume snapshot directory should already be removed, 
lets remove it here
-       // to just in case the top-level directory is left.
-       snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
-
-       err = os.RemoveAll(snapshotDir)
-       if err != nil {
-               return err
-       }
-
-       return nil
-}
-
-func (d *cephfs) RenameVolume(vol Volume, newName string, op 
*operations.Operation) error {
-       if vol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       // Create new snapshots directory.
-       snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, newName)
-
-       err := os.MkdirAll(snapshotDir, 0711)
-       if err != nil {
-               return err
-       }
-
-       type volRevert struct {
-               oldPath   string
-               newPath   string
-               isSymlink bool
-       }
-
-       // Create slice to record paths renamed if revert needed later.
-       revertPaths := []volRevert{}
-       defer func() {
-               // Remove any paths rename if we are reverting.
-               for _, vol := range revertPaths {
-                       if vol.isSymlink {
-                               os.Symlink(vol.oldPath, vol.newPath)
-                       } else {
-                               os.Rename(vol.newPath, vol.oldPath)
-                       }
-               }
-
-               // Remove the new snapshot directory if we are reverting.
-               if len(revertPaths) > 0 {
-                       err = os.RemoveAll(snapshotDir)
-               }
-       }()
-
-       // Rename the snapshot directory first.
-       srcSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
-
-       if shared.PathExists(srcSnapshotDir) {
-               targetSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, 
newName)
-
-               err = os.Rename(srcSnapshotDir, targetSnapshotDir)
-               if err != nil {
-                       return err
-               }
-
-               revertPaths = append(revertPaths, volRevert{
-                       oldPath: srcSnapshotDir,
-                       newPath: targetSnapshotDir,
-               })
-       }
-
-       // Rename any snapshots of the volume too.
-       snapshots, err := vol.Snapshots(op)
-       if err != nil {
-               return err
-       }
-
-       sourcePath := GetVolumeMountPath(d.name, vol.volType, newName)
-       targetPath := GetVolumeMountPath(d.name, vol.volType, newName)
-
-       for _, snapshot := range snapshots {
-               // Figure out the snapshot paths.
-               _, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapshot.name)
-               oldCephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
-               newCephSnapPath := filepath.Join(targetPath, ".snap", snapName)
-               oldPath := GetVolumeMountPath(d.name, vol.volType, 
GetSnapshotVolumeName(vol.name, snapName))
-               newPath := GetVolumeMountPath(d.name, vol.volType, 
GetSnapshotVolumeName(newName, snapName))
-
-               // Update the symlink.
-               err = os.Symlink(newCephSnapPath, newPath)
-               if err != nil {
-                       return err
-               }
-
-               revertPaths = append(revertPaths, volRevert{
-                       oldPath:   oldPath,
-                       newPath:   oldCephSnapPath,
-                       isSymlink: true,
-               })
-       }
-
-       oldPath := GetVolumeMountPath(d.name, vol.volType, vol.name)
-       newPath := GetVolumeMountPath(d.name, vol.volType, newName)
-       err = os.Rename(oldPath, newPath)
-       if err != nil {
-               return err
-       }
-
-       revertPaths = append(revertPaths, volRevert{
-               oldPath: oldPath,
-               newPath: newPath,
-       })
-
-       revertPaths = nil
-       return nil
-}
-
-func (d *cephfs) UpdateVolume(vol Volume, changedConfig map[string]string) 
error {
-       value, ok := changedConfig["size"]
-       if !ok {
-               return nil
-       }
-
-       return d.SetVolumeQuota(vol, value, nil)
-}
-
-func (d *cephfs) GetVolumeUsage(vol Volume) (int64, error) {
-       out, err := shared.RunCommand("getfattr", "-n", "ceph.quota.max_bytes", 
"--only-values", GetVolumeMountPath(d.name, vol.volType, vol.name))
-       if err != nil {
-               return -1, err
-       }
-
-       size, err := strconv.ParseInt(out, 10, 64)
-       if err != nil {
-               return -1, err
-       }
-
-       return size, nil
-}
-
-func (d *cephfs) SetVolumeQuota(vol Volume, size string, op 
*operations.Operation) error {
-       if size == "" || size == "0" {
-               size = d.config["volume.size"]
-       }
-
-       sizeBytes, err := units.ParseByteSizeString(size)
-       if err != nil {
-               return err
-       }
-
-       _, err = shared.RunCommand("setfattr", "-n", "ceph.quota.max_bytes", 
"-v", fmt.Sprintf("%d", sizeBytes), GetVolumeMountPath(d.name, vol.volType, 
vol.name))
-       return err
-}
-
-func (d *cephfs) MountVolume(vol Volume, op *operations.Operation) (bool, 
error) {
-       if vol.volType != VolumeTypeCustom {
-               return false, fmt.Errorf("Volume type not supported")
-       }
-
-       return false, nil
-}
-
-func (d *cephfs) MountVolumeSnapshot(snapVol Volume, op *operations.Operation) 
(bool, error) {
-       if snapVol.volType != VolumeTypeCustom {
-               return false, fmt.Errorf("Volume type not supported")
-       }
-
-       return false, nil
-}
-
-func (d *cephfs) UnmountVolume(vol Volume, op *operations.Operation) (bool, 
error) {
-       if vol.volType != VolumeTypeCustom {
-               return false, fmt.Errorf("Volume type not supported")
-       }
-
-       return false, nil
-}
-
-func (d *cephfs) UnmountVolumeSnapshot(snapVol Volume, op 
*operations.Operation) (bool, error) {
-       if snapVol.volType != VolumeTypeCustom {
-               return false, fmt.Errorf("Volume type not supported")
-       }
-
-       return false, nil
-}
-
-func (d *cephfs) CreateVolumeSnapshot(snapVol Volume, op 
*operations.Operation) error {
-       if snapVol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       parentName, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapVol.name)
-
-       // Create the snapshot.
-       sourcePath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
-       cephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
-
-       err := os.Mkdir(cephSnapPath, 0711)
-       if err != nil {
-               return err
-       }
-
-       targetPath := snapVol.MountPath()
-
-       err = os.MkdirAll(filepath.Dir(targetPath), 0711)
-       if err != nil {
-               return err
-       }
-
-       err = os.Symlink(cephSnapPath, targetPath)
-       if err != nil {
-               return err
-       }
-
-       return nil
-}
-
-func (d *cephfs) DeleteVolumeSnapshot(snapVol Volume, op 
*operations.Operation) error {
-       if snapVol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       parentName, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapVol.name)
-
-       // Delete the snapshot itself.
-       sourcePath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
-       cephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
-
-       err := os.Remove(cephSnapPath)
-       if err != nil {
-               return err
-       }
-
-       // Remove the symlink.
-       snapPath := snapVol.MountPath()
-       err = os.Remove(snapPath)
-       if err != nil {
-               return err
-       }
-
-       return nil
-}
-
-func (d *cephfs) RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, 
op *operations.Operation) error {
-       if snapVol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       parentName, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapVol.name)
-       sourcePath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
-       oldCephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
-       newCephSnapPath := filepath.Join(sourcePath, ".snap", newSnapshotName)
-
-       err := os.Rename(oldCephSnapPath, newCephSnapPath)
-       if err != nil {
-               return err
-       }
-
-       // Re-generate the snapshot symlink.
-       oldPath := snapVol.MountPath()
-       err = os.Remove(oldPath)
-       if err != nil {
-               return err
-       }
-
-       newPath := GetVolumeMountPath(d.name, snapVol.volType, 
GetSnapshotVolumeName(parentName, newSnapshotName))
-       err = os.Symlink(newCephSnapPath, newPath)
-       if err != nil {
-               return err
-       }
-
-       return nil
-}
-
-func (d *cephfs) VolumeSnapshots(vol Volume, op *operations.Operation) 
([]string, error) {
-       if vol.volType != VolumeTypeCustom {
-               return nil, fmt.Errorf("Volume type not supported")
-       }
-
-       snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
-       snapshots := []string{}
-
-       ents, err := ioutil.ReadDir(snapshotDir)
-       if err != nil {
-               // If the snapshots directory doesn't exist, there are no 
snapshots.
-               if os.IsNotExist(err) {
-                       return snapshots, nil
-               }
-
-               return nil, err
-       }
-
-       for _, ent := range ents {
-               fileInfo, err := os.Stat(filepath.Join(snapshotDir, ent.Name()))
-               if err != nil {
-                       return nil, err
-               }
-
-               if !fileInfo.IsDir() {
-                       continue
-               }
-
-               snapshots = append(snapshots, ent.Name())
-       }
-
-       return snapshots, nil
-}
-
-func (d *cephfs) RestoreVolume(vol Volume, snapshotName string, op 
*operations.Operation) error {
-       sourcePath := GetVolumeMountPath(d.name, vol.volType, vol.name)
-       cephSnapPath := filepath.Join(sourcePath, ".snap", snapshotName)
-
-       // Restore using rsync.
-       bwlimit := d.config["rsync.bwlimit"]
-       output, err := rsync.LocalCopy(cephSnapPath, vol.MountPath(), bwlimit, 
false)
-       if err != nil {
-               return fmt.Errorf("Failed to rsync volume: %s: %s", 
string(output), err)
-       }
-
-       return nil
-}
-
-func (d *cephfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs 
migration.VolumeSourceArgs, op *operations.Operation) error {
-       if vol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       if vol.contentType != ContentTypeFS {
-               return fmt.Errorf("Content type not supported")
-       }
-
-       if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC {
-               return fmt.Errorf("Migration type not supported")
-       }
-
-       bwlimit := d.config["rsync.bwlimit"]
-
-       for _, snapName := range volSrcArgs.Snapshots {
-               snapshot, err := vol.NewSnapshot(snapName)
-               if err != nil {
-                       return err
-               }
-
-               // Send snapshot to recipient (ensure local snapshot volume is 
mounted if needed).
-               err = snapshot.MountTask(func(mountPath string, op 
*operations.Operation) error {
-                       var wrapper *ioprogress.ProgressTracker
-                       if volSrcArgs.TrackProgress {
-                               wrapper = migration.ProgressTracker(op, 
"fs_progress", snapshot.name)
-                       }
-
-                       path := shared.AddSlash(mountPath)
-                       return rsync.Send(snapshot.name, path, conn, wrapper, 
volSrcArgs.MigrationType.Features, bwlimit, d.state.OS.ExecPath)
-               }, op)
-               if err != nil {
-                       return err
-               }
-       }
-
-       // Send volume to recipient (ensure local volume is mounted if needed).
-       return vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
-               var wrapper *ioprogress.ProgressTracker
-               if volSrcArgs.TrackProgress {
-                       wrapper = migration.ProgressTracker(op, "fs_progress", 
vol.name)
-               }
-
-               path := shared.AddSlash(mountPath)
-               return rsync.Send(vol.name, path, conn, wrapper, 
volSrcArgs.MigrationType.Features, bwlimit, d.state.OS.ExecPath)
-       }, op)
-}
-
-func (d *cephfs) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller 
*VolumeFiller, op *operations.Operation) error {
-       if vol.volType != VolumeTypeCustom {
-               return fmt.Errorf("Volume type not supported")
-       }
-
-       if vol.contentType != ContentTypeFS {
-               return fmt.Errorf("Content type not supported")
-       }
-
-       if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_RSYNC {
-               return fmt.Errorf("Migration type not supported")
-       }
-
-       // Create the main volume path.
-       volPath := vol.MountPath()
-       err := vol.CreateMountPath()
-       if err != nil {
-               return err
-       }
-
-       // Create slice of snapshots created if revert needed later.
-       revertSnaps := []string{}
-       defer func() {
-               if revertSnaps == nil {
-                       return
-               }
-
-               // Remove any paths created if we are reverting.
-               for _, snapName := range revertSnaps {
-                       fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
-                       snapVol := NewVolume(d, d.name, vol.volType, 
vol.contentType, fullSnapName, vol.config)
-
-                       d.DeleteVolumeSnapshot(snapVol, op)
-               }
-
-               os.RemoveAll(volPath)
-       }()
-
-       // Ensure the volume is mounted.
-       err = vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
-               path := shared.AddSlash(mountPath)
-
-               // Snapshots are sent first by the sender, so create these 
first.
-               for _, snapName := range volTargetArgs.Snapshots {
-                       // Receive the snapshot.
-                       var wrapper *ioprogress.ProgressTracker
-                       if volTargetArgs.TrackProgress {
-                               wrapper = migration.ProgressTracker(op, 
"fs_progress", snapName)
-                       }
-
-                       err = rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
-                       if err != nil {
-                               return err
-                       }
-
-                       fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
-                       snapVol := NewVolume(d, d.name, vol.volType, 
vol.contentType, fullSnapName, vol.config)
-
-                       // Create the snapshot itself.
-                       err = d.CreateVolumeSnapshot(snapVol, op)
-                       if err != nil {
-                               return err
-                       }
-
-                       // Setup the revert.
-                       revertSnaps = append(revertSnaps, snapName)
-               }
-
-               // Apply the volume quota if specified.
-               err = d.SetVolumeQuota(vol, vol.config["size"], op)
-               if err != nil {
-                       return err
-               }
-
-               // Receive the main volume from sender.
-               var wrapper *ioprogress.ProgressTracker
-               if volTargetArgs.TrackProgress {
-                       wrapper = migration.ProgressTracker(op, "fs_progress", 
vol.name)
-               }
-
-               return rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
-       }, op)
-       if err != nil {
-               return err
-       }
-
-       revertSnaps = nil
-       return nil
-}
-
-func (d *cephfs) fsExists(clusterName string, userName string, fsName string) 
bool {
-       _, err := shared.RunCommand("ceph", "--name", fmt.Sprintf("client.%s", 
userName), "--cluster", clusterName, "fs", "get", fsName)
-       if err != nil {
-               return false
-       }
-
-       return true
-}
-
-func (d *cephfs) getConfig(clusterName string, userName string) ([]string, 
string, error) {
-       // Parse the CEPH configuration.
-       cephConf, err := os.Open(fmt.Sprintf("/etc/ceph/%s.conf", clusterName))
-       if err != nil {
-               return nil, "", err
-       }
-
-       cephMon := []string{}
-
-       scan := bufio.NewScanner(cephConf)
-       for scan.Scan() {
-               line := scan.Text()
-               line = strings.TrimSpace(line)
-
-               if line == "" {
-                       continue
-               }
-
-               if strings.HasPrefix(line, "mon_host") {
-                       fields := strings.SplitN(line, "=", 2)
-                       if len(fields) < 2 {
-                               continue
-                       }
-
-                       servers := strings.Split(fields[1], ",")
-                       for _, server := range servers {
-                               cephMon = append(cephMon, 
strings.TrimSpace(server))
-                       }
-                       break
-               }
-       }
-
-       if len(cephMon) == 0 {
-               return nil, "", fmt.Errorf("Couldn't find a CPEH mon")
-       }
-
-       // Parse the CEPH keyring.
-       cephKeyring, err := 
os.Open(fmt.Sprintf("/etc/ceph/%v.client.%v.keyring", clusterName, userName))
-       if err != nil {
-               return nil, "", err
-       }
-
-       var cephSecret string
-
-       scan = bufio.NewScanner(cephKeyring)
-       for scan.Scan() {
-               line := scan.Text()
-               line = strings.TrimSpace(line)
-
-               if line == "" {
-                       continue
-               }
-
-               if strings.HasPrefix(line, "key") {
-                       fields := strings.SplitN(line, "=", 2)
-                       if len(fields) < 2 {
-                               continue
-                       }
-
-                       cephSecret = strings.TrimSpace(fields[1])
-                       break
-               }
-       }
-
-       if cephSecret == "" {
-               return nil, "", fmt.Errorf("Couldn't find a keyring entry")
-       }
-
-       return cephMon, cephSecret, nil
-}
-
-func (d *cephfs) BackupVolume(vol Volume, targetPath string, optimized bool, 
snapshots bool, op *operations.Operation) error {
-       return ErrNotImplemented
-}
-
-func (d *cephfs) RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
-       return nil, nil, ErrNotImplemented
-}
-
+// MigrationTypes returns the supported migration types and options supported 
by the driver.
 func (d *cephfs) MigrationTypes(contentType ContentType, refresh bool) 
[]migration.Type {
        if contentType != ContentTypeFS {
                return nil
diff --git a/lxd/storage/drivers/driver_cephfs_utils.go 
b/lxd/storage/drivers/driver_cephfs_utils.go
new file mode 100644
index 0000000000..638bdfe68f
--- /dev/null
+++ b/lxd/storage/drivers/driver_cephfs_utils.go
@@ -0,0 +1,92 @@
+package drivers
+
+import (
+       "bufio"
+       "fmt"
+       "os"
+       "strings"
+
+       "github.com/lxc/lxd/shared"
+)
+
+// fsExists checks that the Ceph FS instance indeed exists.
+func (d *cephfs) fsExists(clusterName string, userName string, fsName string) 
bool {
+       _, err := shared.RunCommand("ceph", "--name", fmt.Sprintf("client.%s", 
userName), "--cluster", clusterName, "fs", "get", fsName)
+       if err != nil {
+               return false
+       }
+
+       return true
+}
+
+// getConfig parses the Ceph configuration file and returns the list of 
monitors and secret key.
+func (d *cephfs) getConfig(clusterName string, userName string) ([]string, 
string, error) {
+       // Parse the CEPH configuration.
+       cephConf, err := os.Open(fmt.Sprintf("/etc/ceph/%s.conf", clusterName))
+       if err != nil {
+               return nil, "", err
+       }
+
+       cephMon := []string{}
+
+       scan := bufio.NewScanner(cephConf)
+       for scan.Scan() {
+               line := scan.Text()
+               line = strings.TrimSpace(line)
+
+               if line == "" {
+                       continue
+               }
+
+               if strings.HasPrefix(line, "mon_host") {
+                       fields := strings.SplitN(line, "=", 2)
+                       if len(fields) < 2 {
+                               continue
+                       }
+
+                       servers := strings.Split(fields[1], ",")
+                       for _, server := range servers {
+                               cephMon = append(cephMon, 
strings.TrimSpace(server))
+                       }
+                       break
+               }
+       }
+
+       if len(cephMon) == 0 {
+               return nil, "", fmt.Errorf("Couldn't find a CPEH mon")
+       }
+
+       // Parse the CEPH keyring.
+       cephKeyring, err := 
os.Open(fmt.Sprintf("/etc/ceph/%v.client.%v.keyring", clusterName, userName))
+       if err != nil {
+               return nil, "", err
+       }
+
+       var cephSecret string
+
+       scan = bufio.NewScanner(cephKeyring)
+       for scan.Scan() {
+               line := scan.Text()
+               line = strings.TrimSpace(line)
+
+               if line == "" {
+                       continue
+               }
+
+               if strings.HasPrefix(line, "key") {
+                       fields := strings.SplitN(line, "=", 2)
+                       if len(fields) < 2 {
+                               continue
+                       }
+
+                       cephSecret = strings.TrimSpace(fields[1])
+                       break
+               }
+       }
+
+       if cephSecret == "" {
+               return nil, "", fmt.Errorf("Couldn't find a keyring entry")
+       }
+
+       return cephMon, cephSecret, nil
+}
diff --git a/lxd/storage/drivers/driver_cephfs_volumes.go 
b/lxd/storage/drivers/driver_cephfs_volumes.go
new file mode 100644
index 0000000000..53f758e690
--- /dev/null
+++ b/lxd/storage/drivers/driver_cephfs_volumes.go
@@ -0,0 +1,609 @@
+package drivers
+
+import (
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+       "strconv"
+
+       "github.com/lxc/lxd/lxd/migration"
+       "github.com/lxc/lxd/lxd/operations"
+       "github.com/lxc/lxd/lxd/rsync"
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/ioprogress"
+       "github.com/lxc/lxd/shared/units"
+)
+
+// CreateVolume creates a new storage volume on disk.
+func (d *cephfs) CreateVolume(vol Volume, filler *VolumeFiller, op 
*operations.Operation) error {
+       if vol.volType != VolumeTypeCustom {
+               return fmt.Errorf("Volume type not supported")
+       }
+
+       if vol.contentType != ContentTypeFS {
+               return fmt.Errorf("Content type not supported")
+       }
+
+       volPath := vol.MountPath()
+
+       err := os.MkdirAll(volPath, 0711)
+       if err != nil {
+               return err
+       }
+
+       revertPath := true
+       defer func() {
+               if revertPath {
+                       os.RemoveAll(volPath)
+               }
+       }()
+
+       if filler != nil && filler.Fill != nil {
+               d.logger.Debug("Running filler function")
+               err = filler.Fill(volPath, "")
+               if err != nil {
+                       return err
+               }
+       }
+
+       revertPath = false
+       return nil
+}
+
+// RestoreBackupVolume re-creates a volume from its exported state.
+func (d *cephfs) RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
+       return nil, nil, ErrNotImplemented
+}
+
+// CreateVolumeFromCopy copies an existing storage volume (with or without 
snapshots) into a new volume.
+func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots 
bool, op *operations.Operation) error {
+       bwlimit := d.config["rsync.bwlimit"]
+
+       // Create the main volume path.
+       volPath := vol.MountPath()
+       err := vol.CreateMountPath()
+       if err != nil {
+               return err
+       }
+
+       // Create slice of snapshots created if revert needed later.
+       revertSnaps := []string{}
+       defer func() {
+               if revertSnaps == nil {
+                       return
+               }
+
+               // Remove any paths created if we are reverting.
+               for _, snapName := range revertSnaps {
+                       fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
+
+                       snapVol := NewVolume(d, d.name, vol.volType, 
vol.contentType, fullSnapName, vol.config)
+                       d.DeleteVolumeSnapshot(snapVol, op)
+               }
+
+               os.RemoveAll(volPath)
+       }()
+
+       // Ensure the volume is mounted.
+       err = vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
+               // If copyring snapshots is indicated, check the source isn't 
itself a snapshot.
+               if copySnapshots && !srcVol.IsSnapshot() {
+                       // Get the list of snapshots from the source.
+                       srcSnapshots, err := srcVol.Snapshots(op)
+                       if err != nil {
+                               return err
+                       }
+
+                       for _, srcSnapshot := range srcSnapshots {
+                               _, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(srcSnapshot.name)
+
+                               // Mount the source snapshot.
+                               err = srcSnapshot.MountTask(func(srcMountPath 
string, op *operations.Operation) error {
+                                       // Copy the snapshot.
+                                       _, err = rsync.LocalCopy(srcMountPath, 
mountPath, bwlimit, false)
+                                       return err
+                               }, op)
+
+                               // Create the snapshot itself.
+                               err = d.CreateVolumeSnapshot(srcSnapshot, op)
+                               if err != nil {
+                                       return err
+                               }
+
+                               // Setup the revert.
+                               revertSnaps = append(revertSnaps, snapName)
+                       }
+               }
+
+               // Apply the volume quota if specified.
+               err = d.SetVolumeQuota(vol, vol.config["size"], op)
+               if err != nil {
+                       return err
+               }
+
+               // Copy source to destination (mounting each volume if needed).
+               return srcVol.MountTask(func(srcMountPath string, op 
*operations.Operation) error {
+                       _, err := rsync.LocalCopy(srcMountPath, mountPath, 
bwlimit, false)
+                       return err
+               }, op)
+       }, op)
+       if err != nil {
+               return err
+       }
+
+       revertSnaps = nil // Don't revert.
+       return nil
+}
+
+// CreateVolumeFromMigration creates a new volume (with or without snapshots) 
from a migration data stream.
+func (d *cephfs) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller 
*VolumeFiller, op *operations.Operation) error {
+       if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_RSYNC {
+               return fmt.Errorf("Migration type not supported")
+       }
+
+       // Create the main volume path.
+       volPath := vol.MountPath()
+       err := vol.CreateMountPath()
+       if err != nil {
+               return err
+       }
+
+       // Create slice of snapshots created if revert needed later.
+       revertSnaps := []string{}
+       defer func() {
+               if revertSnaps == nil {
+                       return
+               }
+
+               // Remove any paths created if we are reverting.
+               for _, snapName := range revertSnaps {
+                       fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
+                       snapVol := NewVolume(d, d.name, vol.volType, 
vol.contentType, fullSnapName, vol.config)
+
+                       d.DeleteVolumeSnapshot(snapVol, op)
+               }
+
+               os.RemoveAll(volPath)
+       }()
+
+       // Ensure the volume is mounted.
+       err = vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
+               path := shared.AddSlash(mountPath)
+
+               // Snapshots are sent first by the sender, so create these 
first.
+               for _, snapName := range volTargetArgs.Snapshots {
+                       // Receive the snapshot.
+                       var wrapper *ioprogress.ProgressTracker
+                       if volTargetArgs.TrackProgress {
+                               wrapper = migration.ProgressTracker(op, 
"fs_progress", snapName)
+                       }
+
+                       err = rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
+                       if err != nil {
+                               return err
+                       }
+
+                       fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
+                       snapVol := NewVolume(d, d.name, vol.volType, 
vol.contentType, fullSnapName, vol.config)
+
+                       // Create the snapshot itself.
+                       err = d.CreateVolumeSnapshot(snapVol, op)
+                       if err != nil {
+                               return err
+                       }
+
+                       // Setup the revert.
+                       revertSnaps = append(revertSnaps, snapName)
+               }
+
+               // Apply the volume quota if specified.
+               err = d.SetVolumeQuota(vol, vol.config["size"], op)
+               if err != nil {
+                       return err
+               }
+
+               // Receive the main volume from sender.
+               var wrapper *ioprogress.ProgressTracker
+               if volTargetArgs.TrackProgress {
+                       wrapper = migration.ProgressTracker(op, "fs_progress", 
vol.name)
+               }
+
+               return rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
+       }, op)
+       if err != nil {
+               return err
+       }
+
+       revertSnaps = nil
+       return nil
+}
+
+// RefreshVolume updates an existing volume to match the state of another.
+func (d *cephfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots 
[]Volume, op *operations.Operation) error {
+       return ErrNotImplemented
+}
+
+// DeleteVolume destroys the on-disk state of a volume.
+func (d *cephfs) DeleteVolume(vol Volume, op *operations.Operation) error {
+       snapshots, err := d.VolumeSnapshots(vol, op)
+       if err != nil {
+               return err
+       }
+
+       if len(snapshots) > 0 {
+               return fmt.Errorf("Cannot remove a volume that has snapshots")
+       }
+
+       volPath := GetVolumeMountPath(d.name, vol.volType, vol.name)
+
+       // If the volume doesn't exist, then nothing more to do.
+       if !shared.PathExists(volPath) {
+               return nil
+       }
+
+       // Remove the volume from the storage device.
+       err = os.RemoveAll(volPath)
+       if err != nil {
+               return err
+       }
+
+       // Although the volume snapshot directory should already be removed, 
lets remove it here
+       // to just in case the top-level directory is left.
+       snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
+
+       err = os.RemoveAll(snapshotDir)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// HasVolume indicates whether a specific volume exists on the storage pool.
+func (d *cephfs) HasVolume(vol Volume) bool {
+       if shared.PathExists(vol.MountPath()) {
+               return true
+       }
+
+       return false
+}
+
+// ValidateVolume validates the supplied volume config.
+func (d *cephfs) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
+       return d.validateVolume(vol, nil, removeUnknownKeys)
+}
+
+// UpdateVolume applies the driver specific changes of a volume configuration 
change.
+func (d *cephfs) UpdateVolume(vol Volume, changedConfig map[string]string) 
error {
+       value, ok := changedConfig["size"]
+       if !ok {
+               return nil
+       }
+
+       return d.SetVolumeQuota(vol, value, nil)
+}
+
+// GetVolumeUsage returns the disk space usage of a volume.
+func (d *cephfs) GetVolumeUsage(vol Volume) (int64, error) {
+       out, err := shared.RunCommand("getfattr", "-n", "ceph.quota.max_bytes", 
"--only-values", GetVolumeMountPath(d.name, vol.volType, vol.name))
+       if err != nil {
+               return -1, err
+       }
+
+       size, err := strconv.ParseInt(out, 10, 64)
+       if err != nil {
+               return -1, err
+       }
+
+       return size, nil
+}
+
+// SetVolumeQuota applies a size limit on volume.
+func (d *cephfs) SetVolumeQuota(vol Volume, size string, op 
*operations.Operation) error {
+       if size == "" || size == "0" {
+               size = d.config["volume.size"]
+       }
+
+       sizeBytes, err := units.ParseByteSizeString(size)
+       if err != nil {
+               return err
+       }
+
+       _, err = shared.RunCommand("setfattr", "-n", "ceph.quota.max_bytes", 
"-v", fmt.Sprintf("%d", sizeBytes), GetVolumeMountPath(d.name, vol.volType, 
vol.name))
+       return err
+}
+
+// GetVolumeDiskPath returns the location of a root disk block device.
+func (d *cephfs) GetVolumeDiskPath(vol Volume) (string, error) {
+       return "", ErrNotImplemented
+}
+
+// MountVolume sets up the volume for use.
+func (d *cephfs) MountVolume(vol Volume, op *operations.Operation) (bool, 
error) {
+       return false, nil
+}
+
+// UnmountVolume clears any runtime state for the volume.
+func (d *cephfs) UnmountVolume(vol Volume, op *operations.Operation) (bool, 
error) {
+       return false, nil
+}
+
+// RenameVolume renames the volume and all related filesystem entries.
+func (d *cephfs) RenameVolume(vol Volume, newName string, op 
*operations.Operation) error {
+       // Create new snapshots directory.
+       snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, newName)
+
+       err := os.MkdirAll(snapshotDir, 0711)
+       if err != nil {
+               return err
+       }
+
+       type volRevert struct {
+               oldPath   string
+               newPath   string
+               isSymlink bool
+       }
+
+       // Create slice to record paths renamed if revert needed later.
+       revertPaths := []volRevert{}
+       defer func() {
+               // Remove any paths rename if we are reverting.
+               for _, vol := range revertPaths {
+                       if vol.isSymlink {
+                               os.Symlink(vol.oldPath, vol.newPath)
+                       } else {
+                               os.Rename(vol.newPath, vol.oldPath)
+                       }
+               }
+
+               // Remove the new snapshot directory if we are reverting.
+               if len(revertPaths) > 0 {
+                       err = os.RemoveAll(snapshotDir)
+               }
+       }()
+
+       // Rename the snapshot directory first.
+       srcSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
+
+       if shared.PathExists(srcSnapshotDir) {
+               targetSnapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, 
newName)
+
+               err = os.Rename(srcSnapshotDir, targetSnapshotDir)
+               if err != nil {
+                       return err
+               }
+
+               revertPaths = append(revertPaths, volRevert{
+                       oldPath: srcSnapshotDir,
+                       newPath: targetSnapshotDir,
+               })
+       }
+
+       // Rename any snapshots of the volume too.
+       snapshots, err := vol.Snapshots(op)
+       if err != nil {
+               return err
+       }
+
+       sourcePath := GetVolumeMountPath(d.name, vol.volType, newName)
+       targetPath := GetVolumeMountPath(d.name, vol.volType, newName)
+
+       for _, snapshot := range snapshots {
+               // Figure out the snapshot paths.
+               _, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapshot.name)
+               oldCephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
+               newCephSnapPath := filepath.Join(targetPath, ".snap", snapName)
+               oldPath := GetVolumeMountPath(d.name, vol.volType, 
GetSnapshotVolumeName(vol.name, snapName))
+               newPath := GetVolumeMountPath(d.name, vol.volType, 
GetSnapshotVolumeName(newName, snapName))
+
+               // Update the symlink.
+               err = os.Symlink(newCephSnapPath, newPath)
+               if err != nil {
+                       return err
+               }
+
+               revertPaths = append(revertPaths, volRevert{
+                       oldPath:   oldPath,
+                       newPath:   oldCephSnapPath,
+                       isSymlink: true,
+               })
+       }
+
+       oldPath := GetVolumeMountPath(d.name, vol.volType, vol.name)
+       newPath := GetVolumeMountPath(d.name, vol.volType, newName)
+       err = os.Rename(oldPath, newPath)
+       if err != nil {
+               return err
+       }
+
+       revertPaths = append(revertPaths, volRevert{
+               oldPath: oldPath,
+               newPath: newPath,
+       })
+
+       revertPaths = nil
+       return nil
+}
+
+// MigrateVolume streams the volume (with or without snapshots)
+func (d *cephfs) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs 
migration.VolumeSourceArgs, op *operations.Operation) error {
+       if volSrcArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC {
+               return fmt.Errorf("Migration type not supported")
+       }
+
+       bwlimit := d.config["rsync.bwlimit"]
+
+       for _, snapName := range volSrcArgs.Snapshots {
+               snapshot, err := vol.NewSnapshot(snapName)
+               if err != nil {
+                       return err
+               }
+
+               // Send snapshot to recipient (ensure local snapshot volume is 
mounted if needed).
+               err = snapshot.MountTask(func(mountPath string, op 
*operations.Operation) error {
+                       var wrapper *ioprogress.ProgressTracker
+                       if volSrcArgs.TrackProgress {
+                               wrapper = migration.ProgressTracker(op, 
"fs_progress", snapshot.name)
+                       }
+
+                       path := shared.AddSlash(mountPath)
+                       return rsync.Send(snapshot.name, path, conn, wrapper, 
volSrcArgs.MigrationType.Features, bwlimit, d.state.OS.ExecPath)
+               }, op)
+               if err != nil {
+                       return err
+               }
+       }
+
+       // Send volume to recipient (ensure local volume is mounted if needed).
+       return vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
+               var wrapper *ioprogress.ProgressTracker
+               if volSrcArgs.TrackProgress {
+                       wrapper = migration.ProgressTracker(op, "fs_progress", 
vol.name)
+               }
+
+               path := shared.AddSlash(mountPath)
+               return rsync.Send(vol.name, path, conn, wrapper, 
volSrcArgs.MigrationType.Features, bwlimit, d.state.OS.ExecPath)
+       }, op)
+}
+
+// BackupVolume creates an exported version of a volume.
+func (d *cephfs) BackupVolume(vol Volume, targetPath string, optimized bool, 
snapshots bool, op *operations.Operation) error {
+       return ErrNotImplemented
+}
+
+// CreateVolumeSnapshot creates a new snapshot.
+func (d *cephfs) CreateVolumeSnapshot(snapVol Volume, op 
*operations.Operation) error {
+       parentName, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapVol.name)
+
+       // Create the snapshot.
+       sourcePath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
+       cephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
+
+       err := os.Mkdir(cephSnapPath, 0711)
+       if err != nil {
+               return err
+       }
+
+       targetPath := snapVol.MountPath()
+
+       err = os.MkdirAll(filepath.Dir(targetPath), 0711)
+       if err != nil {
+               return err
+       }
+
+       err = os.Symlink(cephSnapPath, targetPath)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// DeleteVolumeSnapshot deletes a snapshot.
+func (d *cephfs) DeleteVolumeSnapshot(snapVol Volume, op 
*operations.Operation) error {
+       parentName, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapVol.name)
+
+       // Delete the snapshot itself.
+       sourcePath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
+       cephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
+
+       err := os.Remove(cephSnapPath)
+       if err != nil {
+               return err
+       }
+
+       // Remove the symlink.
+       snapPath := snapVol.MountPath()
+       err = os.Remove(snapPath)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+// MountVolumeSnapshot makes the snapshot available for use.
+func (d *cephfs) MountVolumeSnapshot(snapVol Volume, op *operations.Operation) 
(bool, error) {
+       return false, nil
+}
+
+// UnmountVolumeSnapshot clears any runtime state for the snapshot.
+func (d *cephfs) UnmountVolumeSnapshot(snapVol Volume, op 
*operations.Operation) (bool, error) {
+       return false, nil
+}
+
+// VolumeSnapshots returns a list of snapshot names for the volume.
+func (d *cephfs) VolumeSnapshots(vol Volume, op *operations.Operation) 
([]string, error) {
+       snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, vol.name)
+       snapshots := []string{}
+
+       ents, err := ioutil.ReadDir(snapshotDir)
+       if err != nil {
+               // If the snapshots directory doesn't exist, there are no 
snapshots.
+               if os.IsNotExist(err) {
+                       return snapshots, nil
+               }
+
+               return nil, err
+       }
+
+       for _, ent := range ents {
+               fileInfo, err := os.Stat(filepath.Join(snapshotDir, ent.Name()))
+               if err != nil {
+                       return nil, err
+               }
+
+               if !fileInfo.IsDir() {
+                       continue
+               }
+
+               snapshots = append(snapshots, ent.Name())
+       }
+
+       return snapshots, nil
+}
+
+// RestoreVolume resets a volume to its snapshotted state.
+func (d *cephfs) RestoreVolume(vol Volume, snapshotName string, op 
*operations.Operation) error {
+       sourcePath := GetVolumeMountPath(d.name, vol.volType, vol.name)
+       cephSnapPath := filepath.Join(sourcePath, ".snap", snapshotName)
+
+       // Restore using rsync.
+       bwlimit := d.config["rsync.bwlimit"]
+       output, err := rsync.LocalCopy(cephSnapPath, vol.MountPath(), bwlimit, 
false)
+       if err != nil {
+               return fmt.Errorf("Failed to rsync volume: %s: %s", 
string(output), err)
+       }
+
+       return nil
+}
+
+// RenameVolumeSnapshot renames a snapshot.
+func (d *cephfs) RenameVolumeSnapshot(snapVol Volume, newSnapshotName string, 
op *operations.Operation) error {
+       parentName, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(snapVol.name)
+       sourcePath := GetVolumeMountPath(d.name, snapVol.volType, parentName)
+       oldCephSnapPath := filepath.Join(sourcePath, ".snap", snapName)
+       newCephSnapPath := filepath.Join(sourcePath, ".snap", newSnapshotName)
+
+       err := os.Rename(oldCephSnapPath, newCephSnapPath)
+       if err != nil {
+               return err
+       }
+
+       // Re-generate the snapshot symlink.
+       oldPath := snapVol.MountPath()
+       err = os.Remove(oldPath)
+       if err != nil {
+               return err
+       }
+
+       newPath := GetVolumeMountPath(d.name, snapVol.volType, 
GetSnapshotVolumeName(parentName, newSnapshotName))
+       err = os.Symlink(newCephSnapPath, newPath)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}

From 48d27e5052037af443a31601e0dcdb4e8dc71e5e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Sun, 15 Dec 2019 20:34:08 -0500
Subject: [PATCH 3/3] lxd/storage: Rename RestoreBackupVolume to
 CreateVolumeFromBackup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/storage/backend_lxd.go                   | 2 +-
 lxd/storage/drivers/driver_cephfs_volumes.go | 4 ++--
 lxd/storage/drivers/driver_dir.go            | 4 ++--
 lxd/storage/drivers/interface.go             | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index c2c592a904..203f9590f7 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -395,7 +395,7 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup 
backup.Info, srcData io.
        }()
 
        // Unpack the backup into the new storage volume(s).
-       volPostHook, revertHook, err := b.driver.RestoreBackupVolume(vol, 
srcBackup.Snapshots, srcData, srcBackup.OptimizedStorage, op)
+       volPostHook, revertHook, err := b.driver.CreateVolumeFromBackup(vol, 
srcBackup.Snapshots, srcData, srcBackup.OptimizedStorage, op)
        if err != nil {
                return nil, nil, err
        }
diff --git a/lxd/storage/drivers/driver_cephfs_volumes.go 
b/lxd/storage/drivers/driver_cephfs_volumes.go
index 53f758e690..337cade4cd 100644
--- a/lxd/storage/drivers/driver_cephfs_volumes.go
+++ b/lxd/storage/drivers/driver_cephfs_volumes.go
@@ -52,8 +52,8 @@ func (d *cephfs) CreateVolume(vol Volume, filler 
*VolumeFiller, op *operations.O
        return nil
 }
 
-// RestoreBackupVolume re-creates a volume from its exported state.
-func (d *cephfs) RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
+// CreateVolumeFromBackup re-creates a volume from its exported state.
+func (d *cephfs) CreateVolumeFromBackup(vol Volume, snapshots []string, 
srcData io.ReadSeeker, optimizedStorage bool, op *operations.Operation) 
(func(vol Volume) error, func(), error) {
        return nil, nil, ErrNotImplemented
 }
 
diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index d54c19f071..7c3afd9f01 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -928,8 +928,8 @@ func (d *dir) BackupVolume(vol Volume, targetPath string, 
_, snapshots bool, op
        return nil
 }
 
-// RestoreBackupVolume restores a backup tarball onto the storage device.
-func (d *dir) RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
+// CreateVolumeFromBackup restores a backup tarball onto the storage device.
+func (d *dir) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
        revert := true
        revertPaths := []string{}
 
diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
index 6b4260f8f4..e0bad2ee89 100644
--- a/lxd/storage/drivers/interface.go
+++ b/lxd/storage/drivers/interface.go
@@ -75,5 +75,5 @@ type Driver interface {
 
        // Backup.
        BackupVolume(vol Volume, targetPath string, optimized bool, snapshots 
bool, op *operations.Operation) error
-       RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error)
+       CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error)
 }
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to