[lxc-devel] [lxd/master] Storage cleanups
stgraber on Github
lxc-bot at linuxcontainers.org
Mon Dec 16 01:37:47 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191215/dfcc80d4/attachment-0001.bin>
-------------- next part --------------
From d296b744b605fb8e497d01e2c8b361a4948021bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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?= <stgraber at 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 <stgraber at 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)
}
More information about the lxc-devel
mailing list