[lxc-devel] [lxd/master] Storage: Moves functions in generic.go to generic_vfs.go
tomponline on Github
lxc-bot at linuxcontainers.org
Tue Mar 24 14:42:08 UTC 2020
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/20200324/af87a742/attachment-0001.bin>
-------------- next part --------------
From 949aaf40b2faa5eff6c6c8caeb22787fb2c5af9a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 24 Mar 2020 14:39:33 +0000
Subject: [PATCH 1/2] lxd/storage/drivers: Moves functions from generic.go to
generic_vfs.go
And adds VFS to function names.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/generic.go | 423 -----------------------------
lxd/storage/drivers/generic_vfs.go | 407 +++++++++++++++++++++++++++
2 files changed, 407 insertions(+), 423 deletions(-)
delete mode 100644 lxd/storage/drivers/generic.go
diff --git a/lxd/storage/drivers/generic.go b/lxd/storage/drivers/generic.go
deleted file mode 100644
index ca18a04fc1..0000000000
--- a/lxd/storage/drivers/generic.go
+++ /dev/null
@@ -1,423 +0,0 @@
-package drivers
-
-import (
- "fmt"
- "io"
- "os"
-
- "github.com/pkg/errors"
-
- "github.com/lxc/lxd/lxd/migration"
- "github.com/lxc/lxd/lxd/operations"
- "github.com/lxc/lxd/lxd/revert"
- "github.com/lxc/lxd/lxd/rsync"
- "github.com/lxc/lxd/shared"
- "github.com/lxc/lxd/shared/ioprogress"
- log "github.com/lxc/lxd/shared/log15"
-)
-
-// genericCopyVolume copies a volume and its snapshots using a non-optimized method.
-// initVolume is run against the main volume (not the snapshots) and is often used for quota initialization.
-func genericCopyVolume(d Driver, initVolume func(vol Volume) (func(), error), vol Volume, srcVol Volume, srcSnapshots []Volume, refresh bool, op *operations.Operation) error {
- if vol.contentType != srcVol.contentType {
- return fmt.Errorf("Content type of source and target must be the same")
- }
-
- bwlimit := d.Config()["rsync.bwlimit"]
-
- revert := revert.New()
- defer revert.Fail()
-
- // Create the main volume if not refreshing.
- if !refresh {
- err := d.CreateVolume(vol, nil, op)
- if err != nil {
- return err
- }
-
- revert.Add(func() { d.DeleteVolume(vol, op) })
- }
-
- // Ensure the volume is mounted.
- err := vol.MountTask(func(mountPath string, op *operations.Operation) error {
- // If copying snapshots is indicated, check the source isn't itself a snapshot.
- if len(srcSnapshots) > 0 && !srcVol.IsSnapshot() {
- 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, true)
- if err != nil {
- return err
- }
-
- if srcSnapshot.IsVMBlock() {
- srcDevPath, err := d.GetVolumeDiskPath(srcSnapshot)
- if err != nil {
- return err
- }
-
- targetDevPath, err := d.GetVolumeDiskPath(vol)
- if err != nil {
- return err
- }
-
- err = copyDevice(srcDevPath, targetDevPath)
- if err != nil {
- return err
- }
- }
-
- return nil
- }, op)
- if err != nil {
- return err
- }
-
- fullSnapName := GetSnapshotVolumeName(vol.name, snapName)
- snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapName, vol.config, vol.poolConfig)
-
- // Create the snapshot itself.
- err = d.CreateVolumeSnapshot(snapVol, op)
- if err != nil {
- return err
- }
-
- // Setup the revert.
- revert.Add(func() {
- d.DeleteVolumeSnapshot(snapVol, op)
- })
- }
- }
-
- // Run volume-specific init logic.
- if initVolume != nil {
- _, err := initVolume(vol)
- if err != nil {
- return err
- }
- }
-
- // Copy source to destination (mounting each volume if needed).
- err := srcVol.MountTask(func(srcMountPath string, op *operations.Operation) error {
- _, err := rsync.LocalCopy(srcMountPath, mountPath, bwlimit, true)
- if err != nil {
- return err
- }
-
- if srcVol.IsVMBlock() {
- srcDevPath, err := d.GetVolumeDiskPath(srcVol)
- if err != nil {
- return err
- }
-
- targetDevPath, err := d.GetVolumeDiskPath(vol)
- if err != nil {
- return err
- }
-
- err = copyDevice(srcDevPath, targetDevPath)
- if err != nil {
- return err
- }
- }
-
- return nil
- }, op)
- if err != nil {
- return err
- }
-
- // Run EnsureMountPath after mounting and copying to ensure the mounted directory has the
- // correct permissions set.
- err = vol.EnsureMountPath()
- if err != nil {
- return err
- }
-
- return nil
- }, op)
- if err != nil {
- return err
- }
-
- revert.Success()
- return nil
-}
-
-// genericCreateVolumeFromMigration receives a volume and its snapshots over a non-optimized method.
-// initVolume is run against the main volume (not the snapshots) and is often used for quota initialization.
-func genericCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) (func(), error), vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
- // Check migration transport type matches volume type.
- if vol.IsVMBlock() {
- if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_BLOCK_AND_RSYNC {
- return ErrNotSupported
- }
- } else if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC {
- return ErrNotSupported
- }
-
- revert := revert.New()
- defer revert.Fail()
-
- // Create the main volume if not refreshing.
- if !volTargetArgs.Refresh {
- err := d.CreateVolume(vol, preFiller, op)
- if err != nil {
- return err
- }
-
- revert.Add(func() { d.DeleteVolume(vol, op) })
- }
-
- recvFSVol := func(volName string, conn io.ReadWriteCloser, path string) error {
- var wrapper *ioprogress.ProgressTracker
- if volTargetArgs.TrackProgress {
- wrapper = migration.ProgressTracker(op, "fs_progress", volName)
- }
-
- d.Logger().Debug("Receiving filesystem volume", log.Ctx{"volName": volName, "path": path})
- return rsync.Recv(path, conn, wrapper, volTargetArgs.MigrationType.Features)
- }
-
- recvBlockVol := func(volName string, conn io.ReadWriteCloser, path string) error {
- var wrapper *ioprogress.ProgressTracker
- if volTargetArgs.TrackProgress {
- wrapper = migration.ProgressTracker(op, "block_progress", volName)
- }
-
- to, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
- if err != nil {
- return errors.Wrapf(err, "Error opening file for writing %q", path)
- }
- defer to.Close()
-
- // Setup progress tracker.
- fromPipe := io.ReadCloser(conn)
- if wrapper != nil {
- fromPipe = &ioprogress.ProgressReader{
- ReadCloser: fromPipe,
- Tracker: wrapper,
- }
- }
-
- d.Logger().Debug("Receiving block volume", log.Ctx{"volName": volName, "path": path})
- _, err = io.Copy(to, fromPipe)
- if err != nil {
- return errors.Wrapf(err, "Error copying from migration connection to %q", path)
- }
-
- return nil
- }
-
- // Ensure the volume is mounted.
- err := vol.MountTask(func(mountPath string, op *operations.Operation) error {
- var err error
-
- // Setup paths to the main volume. We will receive each snapshot to these paths and then create
- // a snapshot of the main volume for each one.
- path := shared.AddSlash(mountPath)
- pathBlock := ""
-
- if vol.IsVMBlock() {
- pathBlock, err = d.GetVolumeDiskPath(vol)
- if err != nil {
- return errors.Wrapf(err, "Error getting VM block volume disk path")
- }
- }
-
- // Snapshots are sent first by the sender, so create these first.
- for _, snapName := range volTargetArgs.Snapshots {
- fullSnapshotName := GetSnapshotVolumeName(vol.name, snapName)
- snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapshotName, vol.config, vol.poolConfig)
-
- // Receive the filesystem snapshot first (as it is sent first).
- err = recvFSVol(snapVol.name, conn, path)
- if err != nil {
- return err
- }
-
- // Receive the block snapshot next (if needed).
- if vol.IsVMBlock() {
- err = recvBlockVol(snapVol.name, conn, pathBlock)
- if err != nil {
- return err
- }
- }
-
- // Create the snapshot itself.
- err = d.CreateVolumeSnapshot(snapVol, op)
- if err != nil {
- return err
- }
-
- // Setup the revert.
- revert.Add(func() {
- d.DeleteVolumeSnapshot(snapVol, op)
- })
- }
-
- // Run volume-specific init logic.
- if initVolume != nil {
- _, err := initVolume(vol)
- if err != nil {
- return err
- }
- }
-
- // Receive main volume.
- err = recvFSVol(vol.name, conn, path)
- if err != nil {
- return err
- }
-
- // Receive the final main volume sync if needed.
- if volTargetArgs.Live {
- d.Logger().Debug("Starting main volume final sync", log.Ctx{"volName": vol.name, "path": path})
- err = recvFSVol(vol.name, conn, path)
- if err != nil {
- return err
- }
- }
-
- // Run EnsureMountPath after mounting and syncing to ensure the mounted directory has the
- // correct permissions set.
- err = vol.EnsureMountPath()
- if err != nil {
- return err
- }
-
- // Receive the block volume next (if needed).
- if vol.IsVMBlock() {
- err = recvBlockVol(vol.name, conn, pathBlock)
- if err != nil {
- return err
- }
- }
-
- return nil
- }, op)
- if err != nil {
- return err
- }
-
- revert.Success()
- return nil
-}
-
-// genericBackupUnpack unpacks a non-optimized backup tarball through a storage driver.
-// Returns a post hook function that should be called once the database entries for the restored backup have been
-// created and a revert function that can be used to undo the actions this function performs should something
-// subsequently fail.
-func genericBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error) {
- revert := revert.New()
- defer revert.Fail()
-
- // Find the compression algorithm used for backup source data.
- srcData.Seek(0, 0)
- tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
- if err != nil {
- return nil, nil, err
- }
-
- if d.HasVolume(vol) {
- return nil, nil, fmt.Errorf("Cannot restore volume, already exists on target")
- }
-
- // Create new empty volume.
- err = d.CreateVolume(vol, nil, nil)
- if err != nil {
- return nil, nil, err
- }
- revert.Add(func() { d.DeleteVolume(vol, op) })
-
- if len(snapshots) > 0 {
- // Create new snapshots directory.
- err := createParentSnapshotDirIfMissing(d.Name(), vol.volType, vol.name)
- if err != nil {
- return nil, nil, err
- }
- }
-
- for _, snapName := range snapshots {
- err = vol.MountTask(func(mountPath string, op *operations.Operation) error {
- // Prepare tar arguments.
- args := append(tarArgs, []string{
- "-",
- "--recursive-unlink",
- "--xattrs-include=*",
- "--strip-components=3",
- "-C", mountPath, fmt.Sprintf("backup/snapshots/%s", snapName),
- }...)
-
- // Extract snapshot.
- srcData.Seek(0, 0)
- err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
- if err != nil {
- return err
- }
-
- return nil
- }, op)
- if err != nil {
- return nil, nil, err
- }
-
- snapVol, err := vol.NewSnapshot(snapName)
- if err != nil {
- return nil, nil, err
- }
-
- err = d.CreateVolumeSnapshot(snapVol, op)
- if err != nil {
- return nil, nil, err
- }
- revert.Add(func() { d.DeleteVolumeSnapshot(snapVol, op) })
- }
-
- // Mount main volume and leave mounted (as is needed during backup.yaml generation during latter parts of
- // the backup restoration process).
- ourMount, err := d.MountVolume(vol, op)
- if err != nil {
- return nil, nil, err
- }
-
- // Create a post hook function that will be called at the end of the backup restore process to unmount
- // the volume if needed.
- postHook := func(vol Volume) error {
- if ourMount {
- d.UnmountVolume(vol, op)
- }
-
- return nil
- }
-
- // Prepare tar extraction arguments.
- args := append(tarArgs, []string{
- "-",
- "--recursive-unlink",
- "--strip-components=2",
- "--xattrs-include=*",
- "-C", vol.MountPath(), "backup/container",
- }...)
-
- // Extract instance.
- srcData.Seek(0, 0)
- err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
- if err != nil {
- return nil, nil, err
- }
-
- // Run EnsureMountPath after mounting and unpacking to ensure the mounted directory has the
- // correct permissions set.
- err = vol.EnsureMountPath()
- if err != nil {
- return nil, nil, err
- }
-
- revertExternal := revert.Clone() // Clone before calling revert.Success() so we can return the Fail func.
- revert.Success()
- return postHook, revertExternal.Fail, nil
-}
diff --git a/lxd/storage/drivers/generic_vfs.go b/lxd/storage/drivers/generic_vfs.go
index f62686ba19..d52421066b 100644
--- a/lxd/storage/drivers/generic_vfs.go
+++ b/lxd/storage/drivers/generic_vfs.go
@@ -12,6 +12,7 @@ import (
"github.com/lxc/lxd/lxd/migration"
"github.com/lxc/lxd/lxd/operations"
+ "github.com/lxc/lxd/lxd/revert"
"github.com/lxc/lxd/lxd/rsync"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/shared"
@@ -257,6 +258,166 @@ func genericVFSMigrateVolume(d Driver, s *state.State, vol Volume, conn io.ReadW
}, op)
}
+// genericVFSCreateVolumeFromMigration receives a volume and its snapshots over a non-optimized method.
+// initVolume is run against the main volume (not the snapshots) and is often used for quota initialization.
+func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) (func(), error), vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
+ // Check migration transport type matches volume type.
+ if vol.IsVMBlock() {
+ if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_BLOCK_AND_RSYNC {
+ return ErrNotSupported
+ }
+ } else if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_RSYNC {
+ return ErrNotSupported
+ }
+
+ revert := revert.New()
+ defer revert.Fail()
+
+ // Create the main volume if not refreshing.
+ if !volTargetArgs.Refresh {
+ err := d.CreateVolume(vol, preFiller, op)
+ if err != nil {
+ return err
+ }
+
+ revert.Add(func() { d.DeleteVolume(vol, op) })
+ }
+
+ recvFSVol := func(volName string, conn io.ReadWriteCloser, path string) error {
+ var wrapper *ioprogress.ProgressTracker
+ if volTargetArgs.TrackProgress {
+ wrapper = migration.ProgressTracker(op, "fs_progress", volName)
+ }
+
+ d.Logger().Debug("Receiving filesystem volume", log.Ctx{"volName": volName, "path": path})
+ return rsync.Recv(path, conn, wrapper, volTargetArgs.MigrationType.Features)
+ }
+
+ recvBlockVol := func(volName string, conn io.ReadWriteCloser, path string) error {
+ var wrapper *ioprogress.ProgressTracker
+ if volTargetArgs.TrackProgress {
+ wrapper = migration.ProgressTracker(op, "block_progress", volName)
+ }
+
+ to, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
+ if err != nil {
+ return errors.Wrapf(err, "Error opening file for writing %q", path)
+ }
+ defer to.Close()
+
+ // Setup progress tracker.
+ fromPipe := io.ReadCloser(conn)
+ if wrapper != nil {
+ fromPipe = &ioprogress.ProgressReader{
+ ReadCloser: fromPipe,
+ Tracker: wrapper,
+ }
+ }
+
+ d.Logger().Debug("Receiving block volume", log.Ctx{"volName": volName, "path": path})
+ _, err = io.Copy(to, fromPipe)
+ if err != nil {
+ return errors.Wrapf(err, "Error copying from migration connection to %q", path)
+ }
+
+ return nil
+ }
+
+ // Ensure the volume is mounted.
+ err := vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ var err error
+
+ // Setup paths to the main volume. We will receive each snapshot to these paths and then create
+ // a snapshot of the main volume for each one.
+ path := shared.AddSlash(mountPath)
+ pathBlock := ""
+
+ if vol.IsVMBlock() {
+ pathBlock, err = d.GetVolumeDiskPath(vol)
+ if err != nil {
+ return errors.Wrapf(err, "Error getting VM block volume disk path")
+ }
+ }
+
+ // Snapshots are sent first by the sender, so create these first.
+ for _, snapName := range volTargetArgs.Snapshots {
+ fullSnapshotName := GetSnapshotVolumeName(vol.name, snapName)
+ snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapshotName, vol.config, vol.poolConfig)
+
+ // Receive the filesystem snapshot first (as it is sent first).
+ err = recvFSVol(snapVol.name, conn, path)
+ if err != nil {
+ return err
+ }
+
+ // Receive the block snapshot next (if needed).
+ if vol.IsVMBlock() {
+ err = recvBlockVol(snapVol.name, conn, pathBlock)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Create the snapshot itself.
+ err = d.CreateVolumeSnapshot(snapVol, op)
+ if err != nil {
+ return err
+ }
+
+ // Setup the revert.
+ revert.Add(func() {
+ d.DeleteVolumeSnapshot(snapVol, op)
+ })
+ }
+
+ // Run volume-specific init logic.
+ if initVolume != nil {
+ _, err := initVolume(vol)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Receive main volume.
+ err = recvFSVol(vol.name, conn, path)
+ if err != nil {
+ return err
+ }
+
+ // Receive the final main volume sync if needed.
+ if volTargetArgs.Live {
+ d.Logger().Debug("Starting main volume final sync", log.Ctx{"volName": vol.name, "path": path})
+ err = recvFSVol(vol.name, conn, path)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Run EnsureMountPath after mounting and syncing to ensure the mounted directory has the
+ // correct permissions set.
+ err = vol.EnsureMountPath()
+ if err != nil {
+ return err
+ }
+
+ // Receive the block volume next (if needed).
+ if vol.IsVMBlock() {
+ err = recvBlockVol(vol.name, conn, pathBlock)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }, op)
+ if err != nil {
+ return err
+ }
+
+ revert.Success()
+ return nil
+}
+
// genericVFSHasVolume is a generic HasVolume implementation for VFS-only drivers.
func genericVFSHasVolume(vol Volume) bool {
if shared.PathExists(vol.MountPath()) {
@@ -349,6 +510,121 @@ func genericVFSBackupVolume(d Driver, vol Volume, tarWriter *instancewriter.Inst
return nil
}
+// genericVFSBackupUnpack unpacks a non-optimized backup tarball through a storage driver.
+// Returns a post hook function that should be called once the database entries for the restored backup have been
+// created and a revert function that can be used to undo the actions this function performs should something
+// subsequently fail.
+func genericVFSBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error) {
+ revert := revert.New()
+ defer revert.Fail()
+
+ // Find the compression algorithm used for backup source data.
+ srcData.Seek(0, 0)
+ tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ if d.HasVolume(vol) {
+ return nil, nil, fmt.Errorf("Cannot restore volume, already exists on target")
+ }
+
+ // Create new empty volume.
+ err = d.CreateVolume(vol, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+ revert.Add(func() { d.DeleteVolume(vol, op) })
+
+ if len(snapshots) > 0 {
+ // Create new snapshots directory.
+ err := createParentSnapshotDirIfMissing(d.Name(), vol.volType, vol.name)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+
+ for _, snapName := range snapshots {
+ err = vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ // Prepare tar arguments.
+ args := append(tarArgs, []string{
+ "-",
+ "--recursive-unlink",
+ "--xattrs-include=*",
+ "--strip-components=3",
+ "-C", mountPath, fmt.Sprintf("backup/snapshots/%s", snapName),
+ }...)
+
+ // Extract snapshot.
+ srcData.Seek(0, 0)
+ err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }, op)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ snapVol, err := vol.NewSnapshot(snapName)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ err = d.CreateVolumeSnapshot(snapVol, op)
+ if err != nil {
+ return nil, nil, err
+ }
+ revert.Add(func() { d.DeleteVolumeSnapshot(snapVol, op) })
+ }
+
+ // Mount main volume and leave mounted (as is needed during backup.yaml generation during latter parts of
+ // the backup restoration process).
+ ourMount, err := d.MountVolume(vol, op)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Create a post hook function that will be called at the end of the backup restore process to unmount
+ // the volume if needed.
+ postHook := func(vol Volume) error {
+ if ourMount {
+ d.UnmountVolume(vol, op)
+ }
+
+ return nil
+ }
+
+ // Prepare tar extraction arguments.
+ args := append(tarArgs, []string{
+ "-",
+ "--recursive-unlink",
+ "--strip-components=2",
+ "--xattrs-include=*",
+ "-C", vol.MountPath(), "backup/container",
+ }...)
+
+ // Extract instance.
+ srcData.Seek(0, 0)
+ err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Run EnsureMountPath after mounting and unpacking to ensure the mounted directory has the
+ // correct permissions set.
+ err = vol.EnsureMountPath()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ revertExternal := revert.Clone() // Clone before calling revert.Success() so we can return the Fail func.
+ revert.Success()
+ return postHook, revertExternal.Fail, nil
+}
+
// genericVFSResizeBlockFile resizes an existing block file to the specified size. Returns true if resize took
// place, false if not. Both requested size and existing file size are rounded to nearest block size using
// roundVolumeBlockFileSizeBytes() before decision whether to resize is taken.
@@ -386,3 +662,134 @@ func genericVFSResizeBlockFile(filePath, size string) (bool, error) {
return true, nil
}
+
+// genericVFSCopyVolume copies a volume and its snapshots using a non-optimized method.
+// initVolume is run against the main volume (not the snapshots) and is often used for quota initialization.
+func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (func(), error), vol Volume, srcVol Volume, srcSnapshots []Volume, refresh bool, op *operations.Operation) error {
+ if vol.contentType != srcVol.contentType {
+ return fmt.Errorf("Content type of source and target must be the same")
+ }
+
+ bwlimit := d.Config()["rsync.bwlimit"]
+
+ revert := revert.New()
+ defer revert.Fail()
+
+ // Create the main volume if not refreshing.
+ if !refresh {
+ err := d.CreateVolume(vol, nil, op)
+ if err != nil {
+ return err
+ }
+
+ revert.Add(func() { d.DeleteVolume(vol, op) })
+ }
+
+ // Ensure the volume is mounted.
+ err := vol.MountTask(func(mountPath string, op *operations.Operation) error {
+ // If copying snapshots is indicated, check the source isn't itself a snapshot.
+ if len(srcSnapshots) > 0 && !srcVol.IsSnapshot() {
+ 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, true)
+ if err != nil {
+ return err
+ }
+
+ if srcSnapshot.IsVMBlock() {
+ srcDevPath, err := d.GetVolumeDiskPath(srcSnapshot)
+ if err != nil {
+ return err
+ }
+
+ targetDevPath, err := d.GetVolumeDiskPath(vol)
+ if err != nil {
+ return err
+ }
+
+ err = copyDevice(srcDevPath, targetDevPath)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }, op)
+ if err != nil {
+ return err
+ }
+
+ fullSnapName := GetSnapshotVolumeName(vol.name, snapName)
+ snapVol := NewVolume(d, d.Name(), vol.volType, vol.contentType, fullSnapName, vol.config, vol.poolConfig)
+
+ // Create the snapshot itself.
+ err = d.CreateVolumeSnapshot(snapVol, op)
+ if err != nil {
+ return err
+ }
+
+ // Setup the revert.
+ revert.Add(func() {
+ d.DeleteVolumeSnapshot(snapVol, op)
+ })
+ }
+ }
+
+ // Run volume-specific init logic.
+ if initVolume != nil {
+ _, err := initVolume(vol)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Copy source to destination (mounting each volume if needed).
+ err := srcVol.MountTask(func(srcMountPath string, op *operations.Operation) error {
+ _, err := rsync.LocalCopy(srcMountPath, mountPath, bwlimit, true)
+ if err != nil {
+ return err
+ }
+
+ if srcVol.IsVMBlock() {
+ srcDevPath, err := d.GetVolumeDiskPath(srcVol)
+ if err != nil {
+ return err
+ }
+
+ targetDevPath, err := d.GetVolumeDiskPath(vol)
+ if err != nil {
+ return err
+ }
+
+ err = copyDevice(srcDevPath, targetDevPath)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }, op)
+ if err != nil {
+ return err
+ }
+
+ // Run EnsureMountPath after mounting and copying to ensure the mounted directory has the
+ // correct permissions set.
+ err = vol.EnsureMountPath()
+ if err != nil {
+ return err
+ }
+
+ return nil
+ }, op)
+ if err != nil {
+ return err
+ }
+
+ revert.Success()
+ return nil
+}
From 563e6bdaa4310146aa92f3222c5442f41de95e07 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 24 Mar 2020 14:40:05 +0000
Subject: [PATCH 2/2] lxd/storage/drivers: Generic VFS function usage after
move &rename
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/storage/drivers/driver_btrfs_volumes.go | 6 +++---
lxd/storage/drivers/driver_ceph_volumes.go | 6 +++---
lxd/storage/drivers/driver_dir_volumes.go | 8 ++++----
lxd/storage/drivers/driver_lvm_volumes.go | 8 ++++----
lxd/storage/drivers/driver_zfs_volumes.go | 6 +++---
5 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go
index 34d89d3247..97d6773305 100644
--- a/lxd/storage/drivers/driver_btrfs_volumes.go
+++ b/lxd/storage/drivers/driver_btrfs_volumes.go
@@ -101,7 +101,7 @@ func (d *btrfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Op
func (d *btrfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData io.ReadSeeker, optimized bool, op *operations.Operation) (func(vol Volume) error, func(), error) {
// Handle the non-optimized tarballs through the generic unpacker.
if !optimized {
- return genericBackupUnpack(d, vol, snapshots, srcData, op)
+ return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
}
revert := revert.New()
@@ -265,7 +265,7 @@ func (d *btrfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bo
func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
// Handle simple rsync and block_and_rsync through generic.
if volTargetArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC {
- return genericCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
+ return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
} else if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_BTRFS {
return ErrNotSupported
}
@@ -318,7 +318,7 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, v
// RefreshVolume provides same-pool volume and specific snapshots syncing functionality.
func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error {
- return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+ return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
}
// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then
diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go
index f416d8d2e9..9cb8cd49a8 100644
--- a/lxd/storage/drivers/driver_ceph_volumes.go
+++ b/lxd/storage/drivers/driver_ceph_volumes.go
@@ -186,7 +186,7 @@ func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope
// CreateVolumeFromBackup re-creates a volume from its exported state.
func (d *ceph) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol Volume) error, func(), error) {
- return genericBackupUnpack(d, vol, snapshots, srcData, op)
+ return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
}
// CreateVolumeFromCopy provides same-pool volume copying functionality.
@@ -376,7 +376,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo
func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
// Handle simple rsync and block_and_rsync through generic.
if volTargetArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC {
- return genericCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
+ return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
} else if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_RBD {
return ErrNotSupported
}
@@ -473,7 +473,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo
// RefreshVolume updates an existing volume to match the state of another.
func (d *ceph) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error {
- return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+ return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
}
// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then
diff --git a/lxd/storage/drivers/driver_dir_volumes.go b/lxd/storage/drivers/driver_dir_volumes.go
index bb2ca165ba..d79ce2b155 100644
--- a/lxd/storage/drivers/driver_dir_volumes.go
+++ b/lxd/storage/drivers/driver_dir_volumes.go
@@ -85,7 +85,7 @@ func (d *dir) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper
// 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) {
// Run the generic backup unpacker
- postHook, revertHook, err := genericBackupUnpack(d.withoutGetVolID(), vol, snapshots, srcData, op)
+ postHook, revertHook, err := genericVFSBackupUnpack(d.withoutGetVolID(), vol, snapshots, srcData, op)
if err != nil {
return nil, nil, err
}
@@ -125,17 +125,17 @@ func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool
}
// Run the generic copy.
- return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, false, op)
+ return genericVFSCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, false, op)
}
// CreateVolumeFromMigration creates a volume being sent via a migration.
func (d *dir) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
- return genericCreateVolumeFromMigration(d, d.setupInitialQuota, vol, conn, volTargetArgs, preFiller, op)
+ return genericVFSCreateVolumeFromMigration(d, d.setupInitialQuota, vol, conn, volTargetArgs, preFiller, op)
}
// RefreshVolume provides same-pool volume and specific snapshots syncing functionality.
func (d *dir) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error {
- return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, true, op)
+ return genericVFSCopyVolume(d, d.setupInitialQuota, vol, srcVol, srcSnapshots, true, op)
}
// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then
diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go
index 6527de2364..aa15f5b73c 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -104,7 +104,7 @@ func (d *lvm) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper
// CreateVolumeFromBackup restores a backup tarball onto the storage device.
func (d *lvm) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol Volume) error, func(), error) {
- return genericBackupUnpack(d, vol, snapshots, srcData, op)
+ return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
}
// CreateVolumeFromCopy provides same-pool volume copying functionality.
@@ -138,12 +138,12 @@ func (d *lvm) CreateVolumeFromCopy(vol, srcVol Volume, copySnapshots bool, op *o
}
// Otherwise run the generic copy.
- return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, false, op)
+ return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, false, op)
}
// CreateVolumeFromMigration creates a volume being sent via a migration.
func (d *lvm) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
- return genericCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
+ return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
}
// RefreshVolume provides same-pool volume and specific snapshots syncing functionality.
@@ -154,7 +154,7 @@ func (d *lvm) RefreshVolume(vol, srcVol Volume, srcSnapshots []Volume, op *opera
}
// Otherwise run the generic copy.
- return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+ return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
}
// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then this function
diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index 7fc9ac2cb3..4e899166c1 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -191,7 +191,7 @@ func (d *zfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper
func (d *zfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData io.ReadSeeker, optimized bool, op *operations.Operation) (func(vol Volume) error, func(), error) {
// Handle the non-optimized tarballs through the generic unpacker.
if !optimized {
- return genericBackupUnpack(d, vol, snapshots, srcData, op)
+ return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
}
revert := revert.New()
@@ -502,7 +502,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool
func (d *zfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) error {
// Handle simple rsync and block_and_rsync through generic.
if volTargetArgs.MigrationType.FSType == migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == migration.MigrationFSType_BLOCK_AND_RSYNC {
- return genericCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
+ return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, volTargetArgs, preFiller, op)
} else if volTargetArgs.MigrationType.FSType != migration.MigrationFSType_ZFS {
return ErrNotSupported
}
@@ -581,7 +581,7 @@ func (d *zfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vol
// RefreshVolume updates an existing volume to match the state of another.
func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op *operations.Operation) error {
- return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+ return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
}
// DeleteVolume deletes a volume of the storage device. If any snapshots of the volume remain then
More information about the lxc-devel
mailing list