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

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) ===
Implements CreateInstanceFromCopy and its dependencies: CreateInstanceFromMigration and MigrateInstance.

Hooks CreateInstanceFromCopy into LXD API functionality for supported pool drivers.
From f05f3b32c95cdb8779542c38e9bfff74e96570ec Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:43:51 +0000
Subject: [PATCH 01/21] lxd/container: Adds operation arg to
 instanceCreateAsCopy

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 55aaed404c..62c58392d9 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -570,7 +570,7 @@ func instanceCreateFromImage(d *Daemon, args 
db.InstanceArgs, hash string, op *o
        return inst, nil
 }
 
-func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst 
Instance, instanceOnly bool, refresh bool) (Instance, error) {
+func instanceCreateAsCopy(s *state.State, args db.InstanceArgs, sourceInst 
Instance, instanceOnly bool, refresh bool, op *operations.Operation) (Instance, 
error) {
        var inst, revertInst Instance
        var err error
 

From b67393185468a32228b19c09666dc8154975cf68 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:46:25 +0000
Subject: [PATCH 02/21] lxd/containers/post: Passes operation to
 instanceCreateAsCopy

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

diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index dacc135db2..fc9b50c15b 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -604,7 +604,7 @@ func createFromCopy(d *Daemon, project string, req 
*api.InstancesPost) response.
 
        run := func(op *operations.Operation) error {
                instanceOnly := req.Source.InstanceOnly || 
req.Source.ContainerOnly
-               _, err := instanceCreateAsCopy(d.State(), args, source, 
instanceOnly, req.Source.Refresh)
+               _, err := instanceCreateAsCopy(d.State(), args, source, 
instanceOnly, req.Source.Refresh, op)
                if err != nil {
                        return err
                }

From 9f439ab952aa1199284e2044cfd4adee48bdcef8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 19 Nov 2019 12:09:23 +0000
Subject: [PATCH 03/21] lxd/container: Links instanceCreateAsCopy to new
 storage pkg

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

diff --git a/lxd/container.go b/lxd/container.go
index 62c58392d9..08eafd8abc 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -701,9 +701,25 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst Insta
                        return nil, err
                }
        } else {
-               err = inst.Storage().ContainerCopy(inst, sourceInst, 
instanceOnly)
-               if err != nil {
-                       return nil, err
+               // 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, errors.Wrap(err, "Load instance 
storage pool")
+                       }
+
+                       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 {
+                       err = inst.Storage().ContainerCopy(inst, sourceInst, 
instanceOnly)
+                       if err != nil {
+                               return nil, err
+                       }
+               } else {
+                       return nil, fmt.Errorf("Instance type not supported")
                }
        }
 

From 5b11204a364a250b981b18e48bf2aca3cf0edacd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:44:20 +0000
Subject: [PATCH 04/21] lxd/container: source snapshot var naming for clarity

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

diff --git a/lxd/container.go b/lxd/container.go
index 08eafd8abc..cf5e79dc03 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -642,15 +642,15 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst Insta
                        }
                }
 
-               for _, snap := range snapshots {
-                       fields := strings.SplitN(snap.Name(), 
shared.SnapshotDelimiter, 2)
+               for _, srcSnap := range snapshots {
+                       fields := strings.SplitN(srcSnap.Name(), 
shared.SnapshotDelimiter, 2)
 
                        // Ensure that snapshot and parent instance have the
                        // same storage pool in their local root disk device.
                        // If the root disk device for the snapshot comes from a
                        // profile on the new instance as well we don't need to
                        // do anything.
-                       snapDevices := snap.LocalDevices()
+                       snapDevices := srcSnap.LocalDevices()
                        if snapDevices != nil {
                                snapLocalRootDiskDeviceKey, _, _ := 
shared.GetRootDiskDevice(snapDevices.CloneNative())
                                if snapLocalRootDiskDeviceKey != "" {
@@ -666,15 +666,15 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst Insta
 
                        newSnapName := fmt.Sprintf("%s/%s", inst.Name(), 
fields[1])
                        snapInstArgs := db.InstanceArgs{
-                               Architecture: snap.Architecture(),
-                               Config:       snap.LocalConfig(),
+                               Architecture: srcSnap.Architecture(),
+                               Config:       srcSnap.LocalConfig(),
                                Type:         sourceInst.Type(),
                                Snapshot:     true,
                                Devices:      snapDevices,
-                               Description:  snap.Description(),
-                               Ephemeral:    snap.IsEphemeral(),
+                               Description:  srcSnap.Description(),
+                               Ephemeral:    srcSnap.IsEphemeral(),
                                Name:         newSnapName,
-                               Profiles:     snap.Profiles(),
+                               Profiles:     srcSnap.Profiles(),
                                Project:      args.Project,
                        }
 
@@ -684,8 +684,8 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst Insta
                                return nil, err
                        }
 
-                       // Restore snapshot creation date.
-                       err = s.Cluster.ContainerCreationUpdate(snapInst.ID(), 
snap.CreationDate())
+                       // Set snapshot creation date to that of the source 
snapshot.
+                       err = 
s.Cluster.InstanceSnapshotCreationUpdate(snapInst.ID(), srcSnap.CreationDate())
                        if err != nil {
                                return nil, err
                        }

From d55c969b7344fa81c1c721ae473d5def2de9a1f7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:36:25 +0000
Subject: [PATCH 05/21] lxd/storage/interfaces: Exposes ExpandedDevices() on
 Instance interface

Used for accessing the root device config when managing instance storage 
volumes.

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

diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index b9e26a6305..1412555764 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -3,6 +3,7 @@ package storage
 import (
        "io"
 
+       deviceConfig "github.com/lxc/lxd/lxd/device/config"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
@@ -20,6 +21,7 @@ type Instance interface {
        IsRunning() bool
        IsSnapshot() bool
        DeferTemplateApply(trigger string) error
+       ExpandedDevices() deviceConfig.Devices
 }
 
 // Pool represents a LXD storage pool.

From a2f4cda3b5ea701bbea8b0cf57b5af2db81efaa0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:39:09 +0000
Subject: [PATCH 06/21] lxd/storage/interfaces: Updates Instance migration
 signatures

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

diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index 1412555764..1c4adba620 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -45,11 +45,11 @@ type Pool interface {
        CreateInstanceFromBackup(i Instance, sourcePath string, op 
*operations.Operation) error
        CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op 
*operations.Operation) error
        CreateInstanceFromImage(i Instance, fingerprint string, op 
*operations.Operation) error
-       CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args 
migration.SinkArgs, op *operations.Operation) error
+       CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args 
migration.VolumeTargetArgs, op *operations.Operation) error
        RenameInstance(i Instance, newName string, op *operations.Operation) 
error
        DeleteInstance(i Instance, op *operations.Operation) error
 
-       MigrateInstance(i Instance, snapshots bool, args migration.SourceArgs) 
(migration.StorageSourceDriver, error)
+       MigrateInstance(i Instance, conn io.ReadWriteCloser, args 
migration.VolumeSourceArgs, op *operations.Operation) error
        RefreshInstance(i Instance, src Instance, snapshots bool, op 
*operations.Operation) error
        BackupInstance(i Instance, targetPath string, optimized bool, snapshots 
bool, op *operations.Operation) error
 

From 28d902588beb86b257543a821a5c2048db6553f3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:40:18 +0000
Subject: [PATCH 07/21] lxd/storage/interfaces: Changes i arg var to inst to
 represent Instance

We don't use "i" for instance variables as its confused with the commonly used 
"i" for iterator.

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

diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index 1c4adba620..7632620464 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -41,32 +41,32 @@ type Pool interface {
        Unmount() (bool, error)
 
        // Instances.
-       CreateInstance(i Instance, op *operations.Operation) error
-       CreateInstanceFromBackup(i Instance, sourcePath string, op 
*operations.Operation) error
-       CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op 
*operations.Operation) error
-       CreateInstanceFromImage(i Instance, fingerprint string, op 
*operations.Operation) error
-       CreateInstanceFromMigration(i Instance, conn io.ReadWriteCloser, args 
migration.VolumeTargetArgs, op *operations.Operation) error
-       RenameInstance(i Instance, newName string, op *operations.Operation) 
error
-       DeleteInstance(i Instance, op *operations.Operation) error
+       CreateInstance(inst Instance, op *operations.Operation) error
+       CreateInstanceFromBackup(inst Instance, sourcePath string, op 
*operations.Operation) error
+       CreateInstanceFromCopy(inst Instance, src Instance, snapshots bool, op 
*operations.Operation) error
+       CreateInstanceFromImage(inst Instance, fingerprint string, op 
*operations.Operation) error
+       CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, 
args migration.VolumeTargetArgs, op *operations.Operation) error
+       RenameInstance(inst Instance, newName string, op *operations.Operation) 
error
+       DeleteInstance(inst Instance, op *operations.Operation) error
 
-       MigrateInstance(i Instance, conn io.ReadWriteCloser, args 
migration.VolumeSourceArgs, op *operations.Operation) error
-       RefreshInstance(i Instance, src Instance, snapshots bool, op 
*operations.Operation) error
-       BackupInstance(i Instance, targetPath string, optimized bool, snapshots 
bool, 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
+       BackupInstance(inst Instance, targetPath string, optimized bool, 
snapshots bool, op *operations.Operation) error
 
-       GetInstanceUsage(i Instance) (int64, error)
-       SetInstanceQuota(i Instance, size string, op *operations.Operation) 
error
+       GetInstanceUsage(inst Instance) (int64, error)
+       SetInstanceQuota(inst Instance, size string, op *operations.Operation) 
error
 
-       MountInstance(i Instance, op *operations.Operation) (bool, error)
-       UnmountInstance(i Instance, op *operations.Operation) (bool, error)
-       GetInstanceDisk(i Instance) (string, error)
+       MountInstance(inst Instance, op *operations.Operation) (bool, error)
+       UnmountInstance(inst Instance, op *operations.Operation) (bool, error)
+       GetInstanceDisk(inst Instance) (string, error)
 
        // Instance snapshots.
-       CreateInstanceSnapshot(i Instance, name string, op 
*operations.Operation) error
-       RenameInstanceSnapshot(i Instance, newName string, op 
*operations.Operation) error
-       DeleteInstanceSnapshot(i Instance, op *operations.Operation) error
-       RestoreInstanceSnapshot(i Instance, op *operations.Operation) error
-       MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, 
error)
-       UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, 
error)
+       CreateInstanceSnapshot(inst Instance, name string, op 
*operations.Operation) error
+       RenameInstanceSnapshot(inst Instance, newName string, op 
*operations.Operation) error
+       DeleteInstanceSnapshot(inst Instance, op *operations.Operation) error
+       RestoreInstanceSnapshot(inst Instance, op *operations.Operation) error
+       MountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, 
error)
+       UnmountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, 
error)
 
        // Images.
        EnsureImage(fingerprint string, op *operations.Operation) error

From 61b0f68e0755f6949746bd086aab38135e625826 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:41:00 +0000
Subject: [PATCH 08/21] lxd/storage/backend/mock: Updates instance migration
 signatures

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

diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 4fd603bd48..f87beec2ee 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -74,7 +74,7 @@ func (b *mockBackend) CreateInstanceFromImage(i Instance, 
fingerprint string, op
        return nil
 }
 
-func (b *mockBackend) CreateInstanceFromMigration(i Instance, conn 
io.ReadWriteCloser, args migration.SinkArgs, op *operations.Operation) error {
+func (b *mockBackend) CreateInstanceFromMigration(inst Instance, conn 
io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) 
error {
        return nil
 }
 
@@ -86,8 +86,8 @@ func (b *mockBackend) DeleteInstance(i Instance, op 
*operations.Operation) error
        return nil
 }
 
-func (b *mockBackend) MigrateInstance(i Instance, snapshots bool, args 
migration.SourceArgs) (migration.StorageSourceDriver, error) {
-       return nil, nil
+func (b *mockBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, 
args migration.VolumeSourceArgs, op *operations.Operation) error {
+       return nil
 }
 
 func (b *mockBackend) RefreshInstance(i Instance, src Instance, snapshots 
bool, op *operations.Operation) error {

From 91cc70939a712aa1af049bd6fb765891f98a2ff1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:45:49 +0000
Subject: [PATCH 09/21] lxd/container/lxc: Dont fail Delete() when removing
 storage fails

Allows for partially created containers to be deleted.

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

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 1c800c0b08..0c02c20ff9 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3561,7 +3561,7 @@ func (c *containerLXC) Delete() error {
                                // Remove snapshot volume and database record.
                                err = pool.DeleteInstanceSnapshot(c, nil)
                                if err != nil {
-                                       return err
+                                       logger.Error("Failed to delete instance 
snapshot volume", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": 
err})
                                }
                        }
                } else {
@@ -3569,8 +3569,7 @@ func (c *containerLXC) Delete() error {
                        // calling its Delete function.
                        err := instanceDeleteSnapshots(c.state, c.Project(), 
c.Name())
                        if err != nil {
-                               logger.Error("Failed to delete instance 
snapshots", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err})
-                               return err
+                               logger.Error("Failed to delete instance 
snapshot volumes", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": 
err})
                        }
 
                        // Remove all backups.
@@ -3590,7 +3589,7 @@ func (c *containerLXC) Delete() error {
                                // Remove the storage volume, snapshot volumes 
and database records.
                                err = pool.DeleteInstance(c, nil)
                                if err != nil {
-                                       return err
+                                       logger.Error("Failed to delete instance 
volume", log.Ctx{"project": c.Project(), "instance": c.Name(), "err": err})
                                }
                        }
 

From 33eda3dbf4f10c3d711121780530dd43d931edc5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:46:55 +0000
Subject: [PATCH 10/21] lxd/db/containers: Removes unused
 ContainerCreationUpdate replaces with InstanceSnapshotCreationUpdate

This aims to make it clear that using the former ContainerCreationUpdate 
function to update creation date of instance snapshot IDs is a bug and should 
not be done (it will be fixed in a separate commit).

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/db/containers.go | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index 6f45b5b72c..271bb58599 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -973,11 +973,10 @@ func ContainerUpdate(tx *sql.Tx, id int, description 
string, architecture int, e
        return nil
 }
 
-// ContainerCreationUpdate updates the cration_date field of the container
-// with the given ID.
-func (c *Cluster) ContainerCreationUpdate(id int, date time.Time) error {
-       stmt := `UPDATE instances SET creation_date=? WHERE id=?`
-       err := exec(c.db, stmt, date, id)
+// InstanceSnapshotCreationUpdate updates the creation_date field of the 
instance snapshot with ID.
+func (c *Cluster) InstanceSnapshotCreationUpdate(instanceID int, date 
time.Time) error {
+       stmt := `UPDATE instances_snapshots SET creation_date=? WHERE id=?`
+       err := exec(c.db, stmt, date, instanceID)
        return err
 }
 

From 2f1ac2c611ebc30a265097547817e33e8620937b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:48:28 +0000
Subject: [PATCH 11/21] lxd/migrate/storage/volumes: Fixes typo

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

diff --git a/lxd/migrate_storage_volumes.go b/lxd/migrate_storage_volumes.go
index 978d95ed88..90ebb3218c 100644
--- a/lxd/migrate_storage_volumes.go
+++ b/lxd/migrate_storage_volumes.go
@@ -145,7 +145,7 @@ func (s *migrationSourceWs) DoStorage(state *state.State, 
poolName string, volNa
        if pool != nil {
                migrationType, err := migration.MatchTypes(respHeader, 
migration.MigrationFSType_RSYNC, poolMigrationTypes)
                if err != nil {
-                       logger.Errorf("Failed to neogotiate migration type: 
%v", err)
+                       logger.Errorf("Failed to negotiate migration type: %v", 
err)
                        s.sendControl(err)
                        return err
                }

From 7c2ba07760ae04046eee969f2f5f7443a7cadcac Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:48:56 +0000
Subject: [PATCH 12/21] lxd/migration/interfaces: Removes unused definitions

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/migration/interfaces.go | 12 ------------
 1 file changed, 12 deletions(-)
 delete mode 100644 lxd/migration/interfaces.go

diff --git a/lxd/migration/interfaces.go b/lxd/migration/interfaces.go
deleted file mode 100644
index 7264e9ef91..0000000000
--- a/lxd/migration/interfaces.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package migration
-
-// FIXME: empty stubs until we're ready to move the migration code over
-
-type SourceArgs struct {
-}
-
-type SinkArgs struct {
-}
-
-type StorageSourceDriver interface {
-}

From 06072d9dab2672e4fe39dddd80d73870790287a2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:49:45 +0000
Subject: [PATCH 13/21] lxd/storage/backend/lxd: Updates CreateInstance to use
 root disk device config

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 14e72c511f..f905181ef8 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -296,7 +296,13 @@ func (b *lxdBackend) CreateInstance(inst Instance, op 
*operations.Operation) err
                contentType = drivers.ContentTypeBlock
        }
 
-       vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), 
inst.Name()), nil)
+       // Find the root device config for instance.
+       _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
+       if err != nil {
+               return err
+       }
+
+       vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), 
inst.Name()), rootDiskConf)
        err = b.driver.CreateVolume(vol, nil, op)
        if err != nil {
                return err

From 00aac41b09226fe0490219b454bf66008450b2d7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:50:25 +0000
Subject: [PATCH 14/21] lxd/storage/backend/lxd: Implements
 CreateInstanceFromCopy

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index f905181ef8..0a9a5921e4 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -326,8 +326,148 @@ func (b *lxdBackend) CreateInstanceFromBackup(inst 
Instance, sourcePath string,
        return ErrNotImplemented
 }
 
+// CreateInstanceFromCopy copies an instance volume and optionally its 
snapshots to new volume(s).
 func (b *lxdBackend) CreateInstanceFromCopy(inst Instance, src Instance, 
snapshots bool, op *operations.Operation) error {
-       return ErrNotImplemented
+       logger := logging.AddContext(b.logger, log.Ctx{"project": 
inst.Project(), "instance": inst.Name(), "src": src.Name(), "snapshots": 
snapshots})
+       logger.Debug("CreateInstanceFromCopy started")
+       defer logger.Debug("CreateInstanceFromCopy finished")
+
+       if inst.Type() != src.Type() {
+               return fmt.Errorf("Instance types must match")
+       }
+
+       volType, err := InstanceTypeToVolumeType(inst.Type())
+       if err != nil {
+               return err
+       }
+
+       volDBType, err := VolumeTypeToDBType(volType)
+       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)
+
+       revert := true
+
+       srcPool, err := GetPoolByInstance(b.state, src)
+       if err != nil {
+               return err
+       }
+
+       if b.Name() == srcPool.Name() {
+               logger.Debug("CreateInstanceFromCopy same-pool mode detected")
+               defer func() {
+                       if !revert {
+                               return
+                       }
+                       b.DeleteInstance(inst, op)
+               }()
+
+               err = b.driver.CreateVolumeFromCopy(vol, srcVol, snapshots, 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("CreateInstanceFromCopy cross-pool mode detected")
+
+               // If we are copying snapshots, retrieve a list of snapshots 
from source volume.
+               snapshotNames := []string{}
+               if snapshots {
+                       snapshots, err := VolumeSnapshotsGet(b.state, 
srcPool.Name(), src.Name(), volDBType)
+                       if err != nil {
+                               return err
+                       }
+
+                       for _, snapshot := range snapshots {
+                               _, snapShotName, _ := 
shared.ContainerGetParentAndSnapshotName(snapshot.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,
+                               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
+       }
+
+       revert = false
+       return nil
 }
 
 // imageFiller returns a function that can be used as a filler function with 
CreateVolume().

From 4ebad588c12a90d3318ec83c4627e1c47f339861 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:51:18 +0000
Subject: [PATCH 15/21] lxd/storage/backend/lxd: Updates
 CreateInstanceFromImage to use instance root disk config

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 0a9a5921e4..212de9a882 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -500,6 +500,11 @@ func (b *lxdBackend) CreateInstanceFromImage(inst 
Instance, fingerprint string,
                return err
        }
 
+       contentType := drivers.ContentTypeFS
+       if inst.Type() == instancetype.VM {
+               contentType = drivers.ContentTypeBlock
+       }
+
        revert := true
        defer func() {
                if !revert {
@@ -508,12 +513,13 @@ func (b *lxdBackend) CreateInstanceFromImage(inst 
Instance, fingerprint string,
                b.DeleteInstance(inst, op)
        }()
 
-       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
        }
 
-       vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), 
inst.Name()), nil)
+       vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), 
inst.Name()), rootDiskConf)
 
        // If the driver doesn't support optimized image volumes then create a 
new empty volume and
        // populate it with the contents of the image archive.
@@ -531,6 +537,7 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, 
fingerprint string,
                        return err
                }
 
+               // No config for an image volume so set to nil.
                imgVol := b.newVolume(drivers.VolumeTypeImage, contentType, 
fingerprint, nil)
                err = b.driver.CreateVolumeFromCopy(vol, imgVol, false, op)
                if err != nil {

From fe161fd0537cac33eed87f7fd829b290d34e46a1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:51:41 +0000
Subject: [PATCH 16/21] lxd/storage/backend/lxd: Implements
 CreateInstanceFromMigration

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 212de9a882..89c59ab8ae 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -559,8 +559,41 @@ func (b *lxdBackend) CreateInstanceFromImage(inst 
Instance, fingerprint string,
        return nil
 }
 
-func (b *lxdBackend) CreateInstanceFromMigration(inst Instance, conn 
io.ReadWriteCloser, args migration.SinkArgs, op *operations.Operation) error {
-       return ErrNotImplemented
+// CreateInstanceFromMigration receives an instance being migrated.
+// The args.Name and args.Config fields are ignored and, instance properties 
are used instead.
+func (b *lxdBackend) CreateInstanceFromMigration(inst Instance, conn 
io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) 
error {
+       logger := logging.AddContext(b.logger, log.Ctx{"project": 
inst.Project(), "instance": inst.Name(), "args": args})
+       logger.Debug("CreateInstanceFromMigration started")
+       defer logger.Debug("CreateInstanceFromMigration finished")
+
+       volType, err := InstanceTypeToVolumeType(inst.Type())
+       if err != nil {
+               return err
+       }
+
+       contentType := drivers.ContentTypeFS
+       if inst.Type() == instancetype.VM {
+               contentType = drivers.ContentTypeBlock
+       }
+
+       // Find the root device config for instance.
+       _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
+       if err != nil {
+               return err
+       }
+
+       // Override args.Name and args.Config to ensure volume is created based 
on instance.
+       args.Config = rootDiskConf
+       args.Name = inst.Name()
+
+       vol := b.newVolume(volType, contentType, args.Name, args.Config)
+       err = b.driver.CreateVolumeFromMigration(vol, conn, args, op)
+       if err != nil {
+               conn.Close()
+               return err
+       }
+
+       return nil
 }
 
 // RenameInstance renames the instance's root volume and any snapshot volumes.

From 66fd38fa25580f7adf70bb2dd17cc3108fe6b751 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:51:54 +0000
Subject: [PATCH 17/21] lxd/storage/backend/lxd: Implements MigrateInstance

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 89c59ab8ae..e2f0a91bae 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -766,8 +766,37 @@ func (b *lxdBackend) DeleteInstance(inst Instance, op 
*operations.Operation) err
        return nil
 }
 
-func (b *lxdBackend) MigrateInstance(inst Instance, snapshots bool, args 
migration.SourceArgs) (migration.StorageSourceDriver, error) {
-       return nil, ErrNotImplemented
+// MigrateInstance sends an instance volume for migration.
+// The args.Name field is ignored and the name of the instance is used instead.
+func (b *lxdBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, 
args migration.VolumeSourceArgs, op *operations.Operation) error {
+       logger := logging.AddContext(b.logger, log.Ctx{"project": 
inst.Project(), "instance": inst.Name(), "args": args})
+       logger.Debug("MigrateInstance started")
+       defer logger.Debug("MigrateInstance finished")
+
+       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
+       }
+
+       args.Name = inst.Name() // Override args.Name to ensure instance volume 
is sent.
+       vol := b.newVolume(volType, contentType, args.Name, rootDiskConf)
+       err = b.driver.MigrateVolume(vol, conn, args, op)
+       if err != nil {
+               return err
+       }
+
+       return nil
 }
 
 func (b *lxdBackend) RefreshInstance(inst Instance, src Instance, snapshots 
bool, op *operations.Operation) error {

From 049e5b671e229dcefe750f316c00bd491b2dee39 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:52:18 +0000
Subject: [PATCH 18/21] lxd/storage/backend/lxd: Adds comment to EnsureImage
 explaining for volume config not needed

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index e2f0a91bae..fc587b95c0 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1086,9 +1086,9 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op 
*operations.Operation) e
                contentType = drivers.ContentTypeBlock
        }
 
-       // Create the new image volume.
-       vol := b.newVolume(drivers.VolumeTypeImage, contentType, fingerprint, 
nil)
-       err = b.driver.CreateVolume(vol, b.imageFiller(fingerprint, op), op)
+       // Create the new image volume. No config for an image volume so set to 
nil.
+       imgVol := b.newVolume(drivers.VolumeTypeImage, contentType, 
fingerprint, nil)
+       err = b.driver.CreateVolume(imgVol, b.imageFiller(fingerprint, op), op)
        if err != nil {
                return err
        }

From 0f3864d2887229913b3e00f54d0abb58e1d6395e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:52:45 +0000
Subject: [PATCH 19/21] lxd/storage/backend/lxd: Comment consistency in
 CreateCustomVolumeFromCopy

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index fc587b95c0..7b4b17375e 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1279,7 +1279,7 @@ func (b *lxdBackend) CreateCustomVolumeFromCopy(volName, 
desc string, config map
        // to negotiate a common transfer method between pool types.
        logger.Debug("CreateCustomVolumeFromCopy cross-pool mode detected")
 
-       // Create in-memory pipe pair to simulate a connection between the 
sender and receiver.
+       // Use in-memory pipe pair to simulate a connection between the sender 
and receiver.
        aEnd, bEnd := memorypipe.NewPipePair()
 
        // Negotiate the migration type to use.

From a6956cc6cffbece99d7ecc876df132a28250537c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:53:07 +0000
Subject: [PATCH 20/21] lxd/storage/backend/lxd: Add comment to
 MigrateCustomVolume explaining volume config not needed

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 7b4b17375e..db740e13d1 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1343,6 +1343,7 @@ func (b *lxdBackend) MigrateCustomVolume(conn 
io.ReadWriteCloser, args migration
        logger.Debug("MigrateCustomVolume started")
        defer logger.Debug("MigrateCustomVolume finished")
 
+       // Volume config not needed to send a volume so set to nil.
        vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, 
args.Name, nil)
        err := b.driver.MigrateVolume(vol, conn, args, op)
        if err != nil {

From 5d53653e88469a70d76c060b04e493cbcac8bac3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 20 Nov 2019 14:53:33 +0000
Subject: [PATCH 21/21] lxd/storage/backend/lxd: Close migration connection on
 error in CreateCustomVolumeFromMigration

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index db740e13d1..83e0c9bcf4 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1399,7 +1399,8 @@ func (b *lxdBackend) CreateCustomVolumeFromMigration(conn 
io.ReadWriteCloser, ar
        vol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, 
args.Name, args.Config)
        err = b.driver.CreateVolumeFromMigration(vol, conn, args, op)
        if err != nil {
-               return nil
+               conn.Close()
+               return err
        }
 
        revertDBVolumes = nil
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to