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

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 2da6f44a5f9c5c1d5aaedf7554c83e4680044998 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 14:03:25 +0000
Subject: [PATCH 01/13] lxd/container: Removes instanceCompareSnapshots

Moved to instance pkg.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 63 ------------------------------------------------
 1 file changed, 63 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 5a4a5274d3..e1753215f8 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1369,69 +1369,6 @@ func instanceLoad(s *state.State, args db.InstanceArgs, 
profiles []api.Profile)
        return inst, nil
 }
 
-// instanceCompareSnapshots returns a list of snapshots to sync to the target 
and a list of
-// snapshots to remove from the target. A snapshot will be marked as "to sync" 
if it either doesn't
-// exist in the target or its creation date is different to the source. A 
snapshot will be marked
-// as "to delete" if it doesn't exist in the source or creation date is 
different to the source.
-func instanceCompareSnapshots(source instance.Instance, target 
instance.Instance) ([]instance.Instance, []instance.Instance, error) {
-       // Get the source snapshots.
-       sourceSnapshots, err := source.Snapshots()
-       if err != nil {
-               return nil, nil, err
-       }
-
-       // Get the target snapshots.
-       targetSnapshots, err := target.Snapshots()
-       if err != nil {
-               return nil, nil, err
-       }
-
-       // Compare source and target.
-       sourceSnapshotsTime := map[string]time.Time{}
-       targetSnapshotsTime := map[string]time.Time{}
-
-       toDelete := []instance.Instance{}
-       toSync := []instance.Instance{}
-
-       // Generate a list of source snapshot creation dates.
-       for _, snap := range sourceSnapshots {
-               _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(snap.Name())
-
-               sourceSnapshotsTime[snapName] = snap.CreationDate()
-       }
-
-       // Generate a list of target snapshot creation times, if the source 
doesn't contain the
-       // the snapshot or the creation time is different on the source then 
add the target snapshot
-       // to the "to delete" list.
-       for _, snap := range targetSnapshots {
-               _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(snap.Name())
-
-               targetSnapshotsTime[snapName] = snap.CreationDate()
-               existDate, exists := sourceSnapshotsTime[snapName]
-               if !exists {
-                       // Snapshot doesn't exist in source, mark it for 
deletion on target.
-                       toDelete = append(toDelete, snap)
-               } else if existDate != snap.CreationDate() {
-                       // Snapshot creation date is different in source, mark 
it for deletion on
-                       // target.
-                       toDelete = append(toDelete, snap)
-               }
-       }
-
-       // For each of the source snapshots, decide whether it needs to be 
synced or not based on
-       // whether it already exists in the target and whether the creation 
dates match.
-       for _, snap := range sourceSnapshots {
-               _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(snap.Name())
-
-               existDate, exists := targetSnapshotsTime[snapName]
-               if !exists || existDate != snap.CreationDate() {
-                       toSync = append(toSync, snap)
-               }
-       }
-
-       return toSync, toDelete, nil
-}
-
 func autoCreateContainerSnapshotsTask(d *Daemon) (task.Func, task.Schedule) {
        f := func(ctx context.Context) {
                // Load all local instances

From 81d090982385c061824c282fce5fc5f82e44c727 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 14:03:49 +0000
Subject: [PATCH 02/13] lxd/instance/instance/utils: Adds CompareSnapshots
 function

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/instance/instance_utils.go | 70 ++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100644 lxd/instance/instance_utils.go

diff --git a/lxd/instance/instance_utils.go b/lxd/instance/instance_utils.go
new file mode 100644
index 0000000000..0cacf16868
--- /dev/null
+++ b/lxd/instance/instance_utils.go
@@ -0,0 +1,70 @@
+package instance
+
+import (
+       "time"
+
+       "github.com/lxc/lxd/shared"
+)
+
+// CompareSnapshots returns a list of snapshots to sync to the target and a 
list of
+// snapshots to remove from the target. A snapshot will be marked as "to sync" 
if it either doesn't
+// exist in the target or its creation date is different to the source. A 
snapshot will be marked
+// as "to delete" if it doesn't exist in the source or creation date is 
different to the source.
+func CompareSnapshots(source Instance, target Instance) ([]Instance, 
[]Instance, error) {
+       // Get the source snapshots.
+       sourceSnapshots, err := source.Snapshots()
+       if err != nil {
+               return nil, nil, err
+       }
+
+       // Get the target snapshots.
+       targetSnapshots, err := target.Snapshots()
+       if err != nil {
+               return nil, nil, err
+       }
+
+       // Compare source and target.
+       sourceSnapshotsTime := map[string]time.Time{}
+       targetSnapshotsTime := map[string]time.Time{}
+
+       toDelete := []Instance{}
+       toSync := []Instance{}
+
+       // Generate a list of source snapshot creation dates.
+       for _, snap := range sourceSnapshots {
+               _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(snap.Name())
+
+               sourceSnapshotsTime[snapName] = snap.CreationDate()
+       }
+
+       // Generate a list of target snapshot creation times, if the source 
doesn't contain the
+       // the snapshot or the creation time is different on the source then 
add the target snapshot
+       // to the "to delete" list.
+       for _, snap := range targetSnapshots {
+               _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(snap.Name())
+
+               targetSnapshotsTime[snapName] = snap.CreationDate()
+               existDate, exists := sourceSnapshotsTime[snapName]
+               if !exists {
+                       // Snapshot doesn't exist in source, mark it for 
deletion on target.
+                       toDelete = append(toDelete, snap)
+               } else if existDate != snap.CreationDate() {
+                       // Snapshot creation date is different in source, mark 
it for deletion on
+                       // target.
+                       toDelete = append(toDelete, snap)
+               }
+       }
+
+       // For each of the source snapshots, decide whether it needs to be 
synced or not based on
+       // whether it already exists in the target and whether the creation 
dates match.
+       for _, snap := range sourceSnapshots {
+               _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(snap.Name())
+
+               existDate, exists := targetSnapshotsTime[snapName]
+               if !exists || existDate != snap.CreationDate() {
+                       toSync = append(toSync, snap)
+               }
+       }
+
+       return toSync, toDelete, nil
+}

From 055b3c90be7fc2ab518dd276ad04ac18cf19af9c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 14:04:37 +0000
Subject: [PATCH 03/13] lxd/container: Updates instance.CompareSnapshots usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/container.go b/lxd/container.go
index e1753215f8..712dedf7c9 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -629,7 +629,7 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst insta
        if !instanceOnly {
                if refresh {
                        // Compare snapshots.
-                       syncSnapshots, deleteSnapshots, err := 
instanceCompareSnapshots(sourceInst, inst)
+                       syncSnapshots, deleteSnapshots, err := 
instance.CompareSnapshots(sourceInst, inst)
                        if err != nil {
                                return nil, err
                        }

From f5313ccdd4f84b3d2a7a39a7a9d1700d1c77b36a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 14:24:07 +0000
Subject: [PATCH 04/13] lxd/container: Links instanceCreateAsCopy refresh
 instance to new storage pkg

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 712dedf7c9..f109e30d86 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -704,39 +704,41 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst insta
                }
        }
 
-       // Now clone or refresh the storage.
-       if refresh {
-               if inst.Type() != instancetype.Container {
-                       return nil, fmt.Errorf("Instance type must be 
container")
-               }
-
-               ct := inst.(*containerLXC)
-               err = ct.Storage().ContainerRefresh(inst, sourceInst, snapshots)
+       // Check if we can load new storage layer for both target and source 
pool driver types.
+       pool, err := storagePools.GetPoolByInstance(s, inst)
+       _, srcPoolErr := storagePools.GetPoolByInstance(s, sourceInst)
+       if err != storageDrivers.ErrUnknownDriver && err != 
storageDrivers.ErrNotImplemented && srcPoolErr != 
storageDrivers.ErrUnknownDriver && srcPoolErr != 
storageDrivers.ErrNotImplemented {
                if err != nil {
-                       return nil, err
+                       return nil, errors.Wrap(err, "Load instance storage 
pool")
                }
-       } else {
-               // Check if we can load new storage layer for both target and 
source pool driver types.
-               pool, err := storagePools.GetPoolByInstance(s, inst)
-               _, srcPoolErr := storagePools.GetPoolByInstance(s, sourceInst)
-               if err != storageDrivers.ErrUnknownDriver && err != 
storageDrivers.ErrNotImplemented && srcPoolErr != 
storageDrivers.ErrUnknownDriver && srcPoolErr != 
storageDrivers.ErrNotImplemented {
+
+               if refresh {
+                       err = pool.RefreshInstance(inst, sourceInst, snapshots, 
op)
                        if err != nil {
-                               return nil, errors.Wrap(err, "Load instance 
storage pool")
+                               return nil, errors.Wrap(err, "Refresh instance")
                        }
-
+               } else {
                        err = pool.CreateInstanceFromCopy(inst, sourceInst, 
!instanceOnly, op)
                        if err != nil {
                                return nil, errors.Wrap(err, "Create instance 
from copy")
                        }
-               } else if inst.Type() == instancetype.Container {
-                       ct := inst.(*containerLXC)
-                       err = ct.Storage().ContainerCopy(inst, sourceInst, 
instanceOnly)
+               }
+       } else if inst.Type() == instancetype.Container {
+               ct := inst.(*containerLXC)
+
+               if refresh {
+                       err = ct.Storage().ContainerRefresh(inst, sourceInst, 
snapshots)
                        if err != nil {
                                return nil, err
                        }
                } else {
-                       return nil, fmt.Errorf("Instance type not supported")
+                       err = ct.Storage().ContainerCopy(inst, sourceInst, 
instanceOnly)
+                       if err != nil {
+                               return nil, err
+                       }
                }
+       } else {
+               return nil, fmt.Errorf("Instance type not supported")
        }
 
        // Apply any post-storage configuration.

From 334ab9721992f36688b35f71ef26648d2267aeba Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 22 Nov 2019 15:53:09 +0000
Subject: [PATCH 05/13] lxd/storage/interfaces: RefreshInstance signature

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/interfaces.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index 7632620464..27ed6bf7de 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -4,6 +4,7 @@ import (
        "io"
 
        deviceConfig "github.com/lxc/lxd/lxd/device/config"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
@@ -50,7 +51,7 @@ type Pool interface {
        DeleteInstance(inst Instance, op *operations.Operation) error
 
        MigrateInstance(inst Instance, conn io.ReadWriteCloser, args 
migration.VolumeSourceArgs, op *operations.Operation) error
-       RefreshInstance(inst Instance, src Instance, snapshots bool, op 
*operations.Operation) error
+       RefreshInstance(inst instance.Instance, src instance.Instance, 
srcSnapshots []instance.Instance, op *operations.Operation) error
        BackupInstance(inst Instance, targetPath string, optimized bool, 
snapshots bool, op *operations.Operation) error
 
        GetInstanceUsage(inst Instance) (int64, error)

From a90163f55942aff1ace13328a33d09aeadc67107 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 15:04:21 +0000
Subject: [PATCH 06/13] lxd/storage/backend/lxd: Implements RefreshInstance

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 140 +++++++++++++++++++++++++++++++++++--
 1 file changed, 136 insertions(+), 4 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 27c3a02911..5edd4025f5 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -9,6 +9,7 @@ import (
        "strings"
 
        "github.com/lxc/lxd/lxd/db"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
@@ -468,6 +469,141 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst 
Instance, src Instance, snapsho
        return nil
 }
 
+// RefreshInstance synchronises one instance's volume (and optionally 
snapshots) over another.
+// Snapshots that are not present in the source but are in the destination are 
removed from the
+// destination if snapshots are included in the synchronisation.
+func (b *lxdBackend) RefreshInstance(inst instance.Instance, src 
instance.Instance, srcSnapshots []instance.Instance, op *operations.Operation) 
error {
+       logger := logging.AddContext(b.logger, log.Ctx{"project": 
inst.Project(), "instance": inst.Name(), "src": src.Name(), "srcSnapshots": 
len(srcSnapshots)})
+       logger.Debug("RefreshInstance started")
+       defer logger.Debug("RefreshInstance finished")
+
+       if inst.Type() != src.Type() {
+               return fmt.Errorf("Instance types must match")
+       }
+
+       volType, err := InstanceTypeToVolumeType(inst.Type())
+       if err != nil {
+               return err
+       }
+
+       contentType := drivers.ContentTypeFS
+       if inst.Type() == instancetype.VM {
+               contentType = drivers.ContentTypeBlock
+       }
+
+       // Get the root disk device config.
+       _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
+       if err != nil {
+               return err
+       }
+
+       // Initialise a new volume containing the root disk config supplied in 
the new instance.
+       vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), 
inst.Name()), rootDiskConf)
+
+       // We don't need to use the source instance's root disk config, so set 
to nil.
+       srcVol := b.newVolume(volType, contentType, 
project.Prefix(src.Project(), src.Name()), nil)
+
+       srcSnapVols := []drivers.Volume{}
+       for _, snapInst := range srcSnapshots {
+               // Initialise a new volume containing the root disk config 
supplied in the
+               // new instance. We don't need to use the source instance's 
snapshot root
+               // disk config, so set to nil. This is because snapshots are 
immutable yet
+               // the instance and its snapshots can be transferred between 
pools, so using
+               // the data from the snapshot is incorrect.
+               srcSnapVol := b.newVolume(volType, contentType, 
project.Prefix(snapInst.Project(), snapInst.Name()), nil)
+               srcSnapVols = append(srcSnapVols, srcSnapVol)
+       }
+
+       srcPool, err := GetPoolByInstance(b.state, src)
+       if err != nil {
+               return err
+       }
+
+       if b.Name() == srcPool.Name() {
+               logger.Debug("RefreshInstance same-pool mode detected")
+               err = b.driver.RefreshVolume(vol, srcVol, srcSnapVols, op)
+               if err != nil {
+                       return err
+               }
+       } else {
+               // We are copying volumes between storage pools so use 
migration system as it will
+               // be able to negotiate a common transfer method between pool 
types.
+               logger.Debug("RefreshInstance cross-pool mode detected")
+
+               // Retrieve a list of snapshots we are copying.
+               snapshotNames := []string{}
+               for _, srcSnapVol := range srcSnapVols {
+                       _, snapShotName, _ := 
shared.ContainerGetParentAndSnapshotName(srcSnapVol.Name())
+                       snapshotNames = append(snapshotNames, snapShotName)
+               }
+
+               // Use in-memory pipe pair to simulate a connection between the 
sender and receiver.
+               aEnd, bEnd := memorypipe.NewPipePair()
+
+               // Negotiate the migration type to use.
+               offeredTypes := srcPool.MigrationTypes(contentType)
+               offerHeader := migration.TypesToHeader(offeredTypes...)
+               migrationType, err := migration.MatchTypes(offerHeader, 
migration.MigrationFSType_RSYNC, b.MigrationTypes(contentType))
+               if err != nil {
+                       return fmt.Errorf("Failed to negotiate copy migration 
type: %v", err)
+               }
+
+               // Run sender and receiver in separate go routines to prevent 
deadlocks.
+               aEndErrCh := make(chan error, 1)
+               bEndErrCh := make(chan error, 1)
+               go func() {
+                       err := srcPool.MigrateInstance(src, aEnd, 
migration.VolumeSourceArgs{
+                               Name:          src.Name(),
+                               Snapshots:     snapshotNames,
+                               MigrationType: migrationType,
+                               TrackProgress: true, // Do use a progress 
tracker on sender.
+                       }, op)
+
+                       aEndErrCh <- err
+               }()
+
+               go func() {
+                       err := b.CreateInstanceFromMigration(inst, bEnd, 
migration.VolumeTargetArgs{
+                               Name:          inst.Name(),
+                               Snapshots:     snapshotNames,
+                               MigrationType: migrationType,
+                               Refresh:       true,  // Indicate to receiver 
volume should exist.
+                               TrackProgress: false, // Do not a progress 
tracker on receiver.
+                       }, op)
+
+                       bEndErrCh <- err
+               }()
+
+               // Capture errors from the sender and receiver from their 
result channels.
+               errs := []error{}
+               aEndErr := <-aEndErrCh
+               if aEndErr != nil {
+                       errs = append(errs, aEndErr)
+               }
+
+               bEndErr := <-bEndErrCh
+               if bEndErr != nil {
+                       errs = append(errs, bEndErr)
+               }
+
+               if len(errs) > 0 {
+                       return fmt.Errorf("Create instance volume from copy 
failed: %v", errs)
+               }
+       }
+
+       err = b.ensureInstanceSymlink(inst.Type(), inst.Project(), inst.Name(), 
vol.MountPath())
+       if err != nil {
+               return err
+       }
+
+       err = inst.DeferTemplateApply("copy")
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
 // imageFiller returns a function that can be used as a filler function with 
CreateVolume().
 // The function returned will unpack the specified image archive into the 
specified mount path
 // provided, and for VM images, a raw root block path is required to unpack 
the qcow2 image into.
@@ -797,10 +933,6 @@ func (b *lxdBackend) MigrateInstance(inst Instance, conn 
io.ReadWriteCloser, arg
        return nil
 }
 
-func (b *lxdBackend) RefreshInstance(inst Instance, src Instance, snapshots 
bool, op *operations.Operation) error {
-       return ErrNotImplemented
-}
-
 func (b *lxdBackend) BackupInstance(inst Instance, targetPath string, 
optimized bool, snapshots bool, op *operations.Operation) error {
        return ErrNotImplemented
 }

From 4001a9942eb1d9386c0d1e3d4b628787a19c8cbf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 22 Nov 2019 15:54:06 +0000
Subject: [PATCH 07/13] lxd/storage/backend/mock: RefreshInstance placeholder

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_mock.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index f87beec2ee..0bc13b66b7 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -3,6 +3,7 @@ package storage
 import (
        "io"
 
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
        "github.com/lxc/lxd/lxd/state"
@@ -90,7 +91,7 @@ func (b *mockBackend) MigrateInstance(inst Instance, conn 
io.ReadWriteCloser, ar
        return nil
 }
 
-func (b *mockBackend) RefreshInstance(i Instance, src Instance, snapshots 
bool, op *operations.Operation) error {
+func (b *mockBackend) RefreshInstance(i instance.Instance, src 
instance.Instance, srcSnapshots []instance.Instance, op *operations.Operation) 
error {
        return nil
 }
 

From 6989f4ab191b8ba5704393d22354542815c2098f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 15:26:58 +0000
Subject: [PATCH 08/13] lxd/storage/drivers/interface: Adds RefreshVolume

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/interface.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
index 75360761c2..064a707936 100644
--- a/lxd/storage/drivers/interface.go
+++ b/lxd/storage/drivers/interface.go
@@ -35,6 +35,7 @@ type Driver interface {
        ValidateVolume(vol Volume, removeUnknownKeys bool) error
        CreateVolume(vol Volume, filler func(mountPath, rootBlockPath string) 
error, op *operations.Operation) error
        CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op 
*operations.Operation) error
+       RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op 
*operations.Operation) error
        DeleteVolume(volType VolumeType, volName string, op 
*operations.Operation) error
        RenameVolume(volType VolumeType, volName string, newName string, op 
*operations.Operation) error
        UpdateVolume(vol Volume, changedConfig map[string]string) error

From 2525d4fc778b3c9ebf1e0e8d841ed65b6b459976 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 15:27:11 +0000
Subject: [PATCH 09/13] lxd/storage/drivers/driver/cephfs: Adds RefreshVolume
 placeholder

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_cephfs.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/storage/drivers/driver_cephfs.go 
b/lxd/storage/drivers/driver_cephfs.go
index 4ed2b258a5..1d0071ac09 100644
--- a/lxd/storage/drivers/driver_cephfs.go
+++ b/lxd/storage/drivers/driver_cephfs.go
@@ -431,6 +431,10 @@ func (d *cephfs) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots b
        return nil
 }
 
+func (d *cephfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots 
[]Volume, op *operations.Operation) error {
+       return ErrNotImplemented
+}
+
 func (d *cephfs) DeleteVolume(volType VolumeType, volName string, op 
*operations.Operation) error {
        if volType != VolumeTypeCustom {
                return fmt.Errorf("Volume type not supported")

From 5e08abc9639af772eab288d4eaeaed7456eb3d59 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 15:27:34 +0000
Subject: [PATCH 10/13] lxd/migration/migration/volumes: Adds Refresh property
 to VolumeTargetArgs

Indicates this migration target is operating in refresh mode, so allow the 
target volume to exist already and sync over it.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migration/migration_volumes.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/migration/migration_volumes.go 
b/lxd/migration/migration_volumes.go
index 415ecac98d..18b329d1a9 100644
--- a/lxd/migration/migration_volumes.go
+++ b/lxd/migration/migration_volumes.go
@@ -33,6 +33,7 @@ type VolumeTargetArgs struct {
        Snapshots     []string
        MigrationType Type
        TrackProgress bool
+       Refresh       bool
 }
 
 // TypesToHeader converts one or more Types to a MigrationHeader. It uses the 
first type argument

From 3e763e170a3c7e29ca7c6568dbb26a29efec533a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 21 Nov 2019 16:16:30 +0000
Subject: [PATCH 11/13] lxd/storage/drivers/driver/dir: RefreshVolume
 implementation

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_dir.go | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index 2b8192a72c..38641d5b00 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -409,6 +409,27 @@ func (d *dir) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, vol
 
 // CreateVolumeFromCopy provides same-pool volume copying functionality.
 func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots 
bool, op *operations.Operation) error {
+       var err error
+       var srcSnapshots []Volume
+
+       if copySnapshots && !srcVol.IsSnapshot() {
+               // Get the list of snapshots from the source.
+               srcSnapshots, err = srcVol.Snapshots(op)
+               if err != nil {
+                       return err
+               }
+       }
+
+       return d.copyVolume(vol, srcVol, srcSnapshots, 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 d.copyVolume(vol, srcVol, srcSnapshots, op)
+}
+
+// copyVolume copies a volume and its specific snapshots.
+func (d *dir) copyVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, op 
*operations.Operation) error {
        if vol.contentType != ContentTypeFS || srcVol.contentType != 
ContentTypeFS {
                return fmt.Errorf("Content type not supported")
        }
@@ -446,13 +467,7 @@ func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots bool
        // 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 copySnapshots && !srcVol.IsSnapshot() {
-                       // Get the list of snapshots from the source.
-                       srcSnapshots, err := srcVol.Snapshots(op)
-                       if err != nil {
-                               return err
-                       }
-
+               if len(srcSnapshots) > 0 && !srcVol.IsSnapshot() {
                        for _, srcSnapshot := range srcSnapshots {
                                _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(srcSnapshot.name)
 

From cb6c934756c7cb364cc32645ba76de4dbd03cd74 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 22 Nov 2019 15:53:23 +0000
Subject: [PATCH 12/13] lxd/storage/drivers/volume: Adds Name() function

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/volume.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go
index 2543a86304..1ac07aa453 100644
--- a/lxd/storage/drivers/volume.go
+++ b/lxd/storage/drivers/volume.go
@@ -55,6 +55,11 @@ func NewVolume(driver Driver, poolName string, volType 
VolumeType, contentType C
        }
 }
 
+// Name returns volume's name.
+func (v Volume) Name() string {
+       return v.name
+}
+
 // NewSnapshot instantiates a new Volume struct representing a snapshot of the 
parent volume.
 func (v Volume) NewSnapshot(snapshotName string) (Volume, error) {
        if v.IsSnapshot() {

From 456508826eb5129d19e29c571fc0dd09818a316a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Fri, 22 Nov 2019 15:54:55 +0000
Subject: [PATCH 13/13] lxd/storage/backend/lxd: Adds HasVolume checks to
 CreateInstanceFromMigration and CreateInstanceFromCopy

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 5edd4025f5..efa698b4f3 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -352,6 +352,10 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst Instance, 
src Instance, snapsho
                contentType = drivers.ContentTypeBlock
        }
 
+       if b.driver.HasVolume(volType, project.Prefix(inst.Project(), 
inst.Name())) {
+               return fmt.Errorf("Cannot create volume, already exists on 
target")
+       }
+
        // Get the root disk device config.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
        if err != nil {
@@ -710,6 +714,13 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst 
Instance, conn io.ReadWrit
                contentType = drivers.ContentTypeBlock
        }
 
+       volExists := b.driver.HasVolume(volType, project.Prefix(inst.Project(), 
inst.Name()))
+       if args.Refresh && !volExists {
+               return fmt.Errorf("Cannot refresh volume, doesn't exist on 
target")
+       } else if !args.Refresh && volExists {
+               return fmt.Errorf("Cannot create volume, already exists on 
target")
+       }
+
        // Find the root device config for instance.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
        if err != nil {
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to