[lxc-devel] [lxd/master] Storage CreateInstanceFromCopy

tomponline on Github lxc-bot at linuxcontainers.org
Wed Nov 20 14:57:50 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 492 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191120/8825b879/attachment-0001.bin>
-------------- next part --------------
From f05f3b32c95cdb8779542c38e9bfff74e96570ec Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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


More information about the lxc-devel mailing list