[lxc-devel] [lxd/master] Storage create backup

tomponline on Github lxc-bot at linuxcontainers.org
Tue Nov 26 13:53:21 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 346 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191126/fe597447/attachment-0001.bin>
-------------- next part --------------
From 5530061122c032c3f0f3b0ba95e3890229fd38fe Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 10:19:18 +0000
Subject: [PATCH 01/16] lxd/storage: Renames interfaces.go to pool_interface.go

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/{interfaces.go => pool_interface.go} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename lxd/storage/{interfaces.go => pool_interface.go} (100%)

diff --git a/lxd/storage/interfaces.go b/lxd/storage/pool_interface.go
similarity index 100%
rename from lxd/storage/interfaces.go
rename to lxd/storage/pool_interface.go

From b6c98a2fc835e554607c3f0060c387748352fa16 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 10:19:41 +0000
Subject: [PATCH 02/16] lxd/storage/pool/interface: Removes Instance interface

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/pool_interface.go | 54 +++++++++++++----------------------
 1 file changed, 20 insertions(+), 34 deletions(-)

diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index 11695abe4b..ca821cc4a9 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -3,9 +3,7 @@ package storage
 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"
 	"github.com/lxc/lxd/lxd/state"
@@ -13,18 +11,6 @@ import (
 	"github.com/lxc/lxd/shared/api"
 )
 
-// Instance represents the storage relevant subset of a LXD instance.
-type Instance interface {
-	Name() string
-	Project() string
-	Type() instancetype.Type
-
-	IsRunning() bool
-	IsSnapshot() bool
-	DeferTemplateApply(trigger string) error
-	ExpandedDevices() deviceConfig.Devices
-}
-
 // Pool represents a LXD storage pool.
 type Pool interface {
 	// Internal.
@@ -42,32 +28,32 @@ type Pool interface {
 	Unmount() (bool, error)
 
 	// Instances.
-	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(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error
+	CreateInstance(inst instance.Instance, op *operations.Operation) error
+	CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op *operations.Operation) error
+	CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, snapshots bool, op *operations.Operation) error
+	CreateInstanceFromImage(inst instance.Instance, fingerprint string, op *operations.Operation) error
+	CreateInstanceFromMigration(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error
+	RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error
+	DeleteInstance(inst instance.Instance, op *operations.Operation) error
+
+	MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, 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
+	BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error
 
-	GetInstanceUsage(inst Instance) (int64, error)
-	SetInstanceQuota(inst Instance, size string, op *operations.Operation) error
+	GetInstanceUsage(inst instance.Instance) (int64, error)
+	SetInstanceQuota(inst instance.Instance, size string, op *operations.Operation) error
 
-	MountInstance(inst Instance, op *operations.Operation) (bool, error)
-	UnmountInstance(inst Instance, op *operations.Operation) (bool, error)
-	GetInstanceDisk(inst Instance) (string, error)
+	MountInstance(inst instance.Instance, op *operations.Operation) (bool, error)
+	UnmountInstance(inst instance.Instance, op *operations.Operation) (bool, error)
+	GetInstanceDisk(inst instance.Instance) (string, error)
 
 	// Instance snapshots.
 	CreateInstanceSnapshot(inst instance.Instance, src instance.Instance, 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)
+	RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error
+	DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error
+	RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error
+	MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error)
+	UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error)
 
 	// Images.
 	EnsureImage(fingerprint string, op *operations.Operation) error

From 4762e9c4cdb64b2fdbb2ba5a4d9c24d9c6752185 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 10:20:03 +0000
Subject: [PATCH 03/16] lxd/storage: Switches to use instance.Instance
 interface

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/backend_lxd.go  | 38 ++++++++++++++++++-------------------
 lxd/storage/backend_mock.go | 38 ++++++++++++++++++-------------------
 lxd/storage/load.go         |  3 ++-
 3 files changed, 40 insertions(+), 39 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 76f85c0e90..ac8ea670a3 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -278,7 +278,7 @@ func (b *lxdBackend) removeInstanceSnapshotSymlinkIfUnused(instanceType instance
 }
 
 // CreateInstance creates an empty instance.
-func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) error {
+func (b *lxdBackend) CreateInstance(inst instance.Instance, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("CreateInstance started")
 	defer logger.Debug("CreateInstance finished")
@@ -327,12 +327,12 @@ func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) err
 	return nil
 }
 
-func (b *lxdBackend) CreateInstanceFromBackup(inst Instance, sourcePath string, op *operations.Operation) error {
+func (b *lxdBackend) CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op *operations.Operation) error {
 	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 {
+func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, snapshots bool, op *operations.Operation) error {
 	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")
@@ -632,7 +632,7 @@ func (b *lxdBackend) imageFiller(fingerprint string, op *operations.Operation) f
 }
 
 // CreateInstanceFromImage creates a new volume for an instance populated with the image requested.
-func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string, op *operations.Operation) error {
+func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint string, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("CreateInstanceFromImage started")
 	defer logger.Debug("CreateInstanceFromImage finished")
@@ -703,7 +703,7 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string,
 
 // 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 {
+func (b *lxdBackend) CreateInstanceFromMigration(inst instance.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")
@@ -746,7 +746,7 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWrit
 }
 
 // RenameInstance renames the instance's root volume and any snapshot volumes.
-func (b *lxdBackend) RenameInstance(inst Instance, newName string, op *operations.Operation) error {
+func (b *lxdBackend) RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "newName": newName})
 	logger.Debug("RenameInstance started")
 	defer logger.Debug("RenameInstance finished")
@@ -853,7 +853,7 @@ func (b *lxdBackend) RenameInstance(inst Instance, newName string, op *operation
 }
 
 // DeleteInstance removes the instance's root volume (all snapshots need to be removed first).
-func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) error {
+func (b *lxdBackend) DeleteInstance(inst instance.Instance, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("DeleteInstance started")
 	defer logger.Debug("DeleteInstance finished")
@@ -917,7 +917,7 @@ func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) err
 
 // 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 {
+func (b *lxdBackend) MigrateInstance(inst instance.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")
@@ -948,12 +948,12 @@ func (b *lxdBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, arg
 	return nil
 }
 
-func (b *lxdBackend) BackupInstance(inst Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
+func (b *lxdBackend) BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
 	return ErrNotImplemented
 }
 
 // GetInstanceUsage returns the disk usage of the instance's root volume.
-func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) {
+func (b *lxdBackend) GetInstanceUsage(inst instance.Instance) (int64, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("GetInstanceUsage started")
 	defer logger.Debug("GetInstanceUsage finished")
@@ -968,7 +968,7 @@ func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) {
 // SetInstanceQuota sets the quota on the instance's root volume.
 // Returns ErrRunningQuotaResizeNotSupported if the instance is running and the storage driver
 // doesn't support resizing whilst the instance is running.
-func (b *lxdBackend) SetInstanceQuota(inst Instance, size string, op *operations.Operation) error {
+func (b *lxdBackend) SetInstanceQuota(inst instance.Instance, size string, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("SetInstanceQuota started")
 	defer logger.Debug("SetInstanceQuota finished")
@@ -990,7 +990,7 @@ func (b *lxdBackend) SetInstanceQuota(inst Instance, size string, op *operations
 }
 
 // MountInstance mounts the instance's root volume.
-func (b *lxdBackend) MountInstance(inst Instance, op *operations.Operation) (bool, error) {
+func (b *lxdBackend) MountInstance(inst instance.Instance, op *operations.Operation) (bool, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("MountInstance started")
 	defer logger.Debug("MountInstance finished")
@@ -1008,7 +1008,7 @@ func (b *lxdBackend) MountInstance(inst Instance, op *operations.Operation) (boo
 }
 
 // UnmountInstance unmounts the instance's root volume.
-func (b *lxdBackend) UnmountInstance(inst Instance, op *operations.Operation) (bool, error) {
+func (b *lxdBackend) UnmountInstance(inst instance.Instance, op *operations.Operation) (bool, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("UnmountInstance started")
 	defer logger.Debug("UnmountInstance finished")
@@ -1026,7 +1026,7 @@ func (b *lxdBackend) UnmountInstance(inst Instance, op *operations.Operation) (b
 }
 
 // GetInstanceDisk returns the location of the disk.
-func (b *lxdBackend) GetInstanceDisk(inst Instance) (string, error) {
+func (b *lxdBackend) GetInstanceDisk(inst instance.Instance) (string, error) {
 	if inst.Type() != instancetype.VM {
 		return "", ErrNotImplemented
 	}
@@ -1104,7 +1104,7 @@ func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src instance
 }
 
 // RenameInstanceSnapshot renames an instance snapshot.
-func (b *lxdBackend) RenameInstanceSnapshot(inst Instance, newName string, op *operations.Operation) error {
+func (b *lxdBackend) RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "newName": newName})
 	logger.Debug("RenameInstanceSnapshot started")
 	defer logger.Debug("RenameInstanceSnapshot finished")
@@ -1152,7 +1152,7 @@ func (b *lxdBackend) RenameInstanceSnapshot(inst Instance, newName string, op *o
 }
 
 // DeleteInstanceSnapshot removes the snapshot volume for the supplied snapshot instance.
-func (b *lxdBackend) DeleteInstanceSnapshot(inst Instance, op *operations.Operation) error {
+func (b *lxdBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("DeleteInstanceSnapshot started")
 	defer logger.Debug("DeleteInstanceSnapshot finished")
@@ -1199,13 +1199,13 @@ func (b *lxdBackend) DeleteInstanceSnapshot(inst Instance, op *operations.Operat
 	return nil
 }
 
-func (b *lxdBackend) RestoreInstanceSnapshot(inst Instance, op *operations.Operation) error {
+func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error {
 	return ErrNotImplemented
 }
 
 // MountInstanceSnapshot mounts an instance snapshot. It is mounted as read only so that the
 // snapshot cannot be modified.
-func (b *lxdBackend) MountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, error) {
+func (b *lxdBackend) MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("MountInstanceSnapshot started")
 	defer logger.Debug("MountInstanceSnapshot finished")
@@ -1230,7 +1230,7 @@ func (b *lxdBackend) MountInstanceSnapshot(inst Instance, op *operations.Operati
 }
 
 // UnmountInstanceSnapshot unmounts an instance snapshot.
-func (b *lxdBackend) UnmountInstanceSnapshot(inst Instance, op *operations.Operation) (bool, error) {
+func (b *lxdBackend) UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("UnmountInstanceSnapshot started")
 	defer logger.Debug("UnmountInstanceSnapshot finished")
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index c8e14c1aa1..3fb98dec58 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -59,35 +59,35 @@ func (b *mockBackend) Unmount() (bool, error) {
 	return true, nil
 }
 
-func (b *mockBackend) CreateInstance(i Instance, op *operations.Operation) error {
+func (b *mockBackend) CreateInstance(inst instance.Instance, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) CreateInstanceFromBackup(i Instance, sourcePath string, op *operations.Operation) error {
+func (b *mockBackend) CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) CreateInstanceFromCopy(i Instance, src Instance, snapshots bool, op *operations.Operation) error {
+func (b *mockBackend) CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, snapshots bool, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) CreateInstanceFromImage(i Instance, fingerprint string, op *operations.Operation) error {
+func (b *mockBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint string, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) CreateInstanceFromMigration(inst Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error {
+func (b *mockBackend) CreateInstanceFromMigration(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) RenameInstance(i Instance, newName string, op *operations.Operation) error {
+func (b *mockBackend) RenameInstance(inst instance.Instance, newName string, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) DeleteInstance(i Instance, op *operations.Operation) error {
+func (b *mockBackend) DeleteInstance(inst instance.Instance, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) MigrateInstance(inst Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
+func (b *mockBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCloser, args migration.VolumeSourceArgs, op *operations.Operation) error {
 	return nil
 }
 
@@ -95,27 +95,27 @@ func (b *mockBackend) RefreshInstance(i instance.Instance, src instance.Instance
 	return nil
 }
 
-func (b *mockBackend) BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
+func (b *mockBackend) BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) GetInstanceUsage(i Instance) (int64, error) {
+func (b *mockBackend) GetInstanceUsage(inst instance.Instance) (int64, error) {
 	return 0, nil
 }
 
-func (b *mockBackend) SetInstanceQuota(i Instance, size string, op *operations.Operation) error {
+func (b *mockBackend) SetInstanceQuota(inst instance.Instance, size string, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) MountInstance(i Instance, op *operations.Operation) (bool, error) {
+func (b *mockBackend) MountInstance(inst instance.Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
-func (b *mockBackend) UnmountInstance(i Instance, op *operations.Operation) (bool, error) {
+func (b *mockBackend) UnmountInstance(inst instance.Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
-func (b *mockBackend) GetInstanceDisk(i Instance) (string, error) {
+func (b *mockBackend) GetInstanceDisk(inst instance.Instance) (string, error) {
 	return "", nil
 }
 
@@ -123,23 +123,23 @@ func (b *mockBackend) CreateInstanceSnapshot(i instance.Instance, src instance.I
 	return nil
 }
 
-func (b *mockBackend) RenameInstanceSnapshot(i Instance, newName string, op *operations.Operation) error {
+func (b *mockBackend) RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) DeleteInstanceSnapshot(i Instance, op *operations.Operation) error {
+func (b *mockBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operation) error {
+func (b *mockBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error {
 	return nil
 }
 
-func (b *mockBackend) MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
+func (b *mockBackend) MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
-func (b *mockBackend) UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
+func (b *mockBackend) UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
diff --git a/lxd/storage/load.go b/lxd/storage/load.go
index bdad09351b..c530bcf9cd 100644
--- a/lxd/storage/load.go
+++ b/lxd/storage/load.go
@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	"github.com/lxc/lxd/lxd/db"
+	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/operations"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/lxd/storage/drivers"
@@ -151,7 +152,7 @@ func GetPoolByName(state *state.State, name string) (Pool, error) {
 // GetPoolByInstance retrieves the pool from the database using the instance's pool.
 // If the pool's driver is not recognised then drivers.ErrUnknownDriver is returned. If the pool's
 // driver does not support the instance's type then drivers.ErrNotImplemented is returned.
-func GetPoolByInstance(s *state.State, inst Instance) (Pool, error) {
+func GetPoolByInstance(s *state.State, inst instance.Instance) (Pool, error) {
 	poolName, err := s.Cluster.InstancePool(inst.Project(), inst.Name())
 	if err != nil {
 		return nil, err

From 9b80beed14fb4b67c61ee844150d6589ef857db9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 09:15:45 +0000
Subject: [PATCH 04/16] lxd/container/put: Renames containerSnapRestore to
 instanceSnapRestore

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container_put.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/container_put.go b/lxd/container_put.go
index de99565ace..0e221afb13 100644
--- a/lxd/container_put.go
+++ b/lxd/container_put.go
@@ -92,7 +92,7 @@ func containerPut(d *Daemon, r *http.Request) response.Response {
 	} else {
 		// Snapshot Restore
 		do = func(op *operations.Operation) error {
-			return containerSnapRestore(d.State(), project, name, configRaw.Restore, configRaw.Stateful)
+			return instanceSnapRestore(d.State(), project, name, configRaw.Restore, configRaw.Stateful)
 		}
 
 		opType = db.OperationSnapshotRestore
@@ -109,13 +109,13 @@ func containerPut(d *Daemon, r *http.Request) response.Response {
 	return operations.OperationResponse(op)
 }
 
-func containerSnapRestore(s *state.State, project, name, snap string, stateful bool) error {
+func instanceSnapRestore(s *state.State, project, name, snap string, stateful bool) error {
 	// normalize snapshot name
 	if !shared.IsSnapshot(snap) {
 		snap = name + shared.SnapshotDelimiter + snap
 	}
 
-	c, err := instanceLoadByProjectAndName(s, project, name)
+	inst, err := instanceLoadByProjectAndName(s, project, name)
 	if err != nil {
 		return err
 	}
@@ -130,7 +130,7 @@ func containerSnapRestore(s *state.State, project, name, snap string, stateful b
 		}
 	}
 
-	err = c.Restore(source, stateful)
+	err = inst.Restore(source, stateful)
 	if err != nil {
 		return err
 	}

From a249a3100af6eb96ddf4fc9349a8a244311c9478 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 09:36:56 +0000
Subject: [PATCH 05/16] lxd/container/lxc: Links snapshot Restore() to new
 storage pkg

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container_lxc.go | 106 +++++++++++++++++++++++++++----------------
 1 file changed, 67 insertions(+), 39 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index c93b7c22bf..4a8a41a203 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3316,26 +3316,41 @@ func (c *containerLXC) Backups() ([]backup.Backup, error) {
 	return backups, nil
 }
 
+// Restore restores a snapshot.
 func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool) error {
 	var ctxMap log.Ctx
 
-	// Initialize storage interface for the container.
-	err := c.initStorage()
-	if err != nil {
-		return err
-	}
+	// Initialize storage interface for the container and mount the rootfs for criu state check.
+	// Check if we can load new storage layer for pool driver type.
+	pool, err := storagePools.GetPoolByInstance(c.state, c)
+	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
+		if err != nil {
+			return err
+		}
 
-	ourStart, err := c.StorageStart()
-	if err != nil {
-		return err
-	}
-	if ourStart {
-		defer c.StorageStop()
+		ourStart, err := pool.MountInstance(c, nil)
+		if err != nil {
+			return err
+		}
+		if ourStart {
+			defer pool.UnmountInstance(c, nil)
+		}
+	} else {
+		err = c.initStorage()
+		if err != nil {
+			return err
+		}
+
+		ourStart, err := c.StorageStart()
+		if err != nil {
+			return err
+		}
+		if ourStart {
+			defer c.StorageStop()
+		}
 	}
 
-	/* let's also check for CRIU if necessary, before doing a bunch of
-	 * filesystem manipulations
-	 */
+	// Check for CRIU if necessary, before doing a bunch of filesystem manipulations.
 	if shared.PathExists(c.StatePath()) {
 		_, err := exec.LookPath("criu")
 		if err != nil {
@@ -3343,14 +3358,14 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 		}
 	}
 
-	// Stop the container
+	// Stop the container.
 	wasRunning := false
 	if c.IsRunning() {
 		wasRunning = true
 
 		ephemeral := c.IsEphemeral()
 		if ephemeral {
-			// Unset ephemeral flag
+			// Unset ephemeral flag.
 			args := db.InstanceArgs{
 				Architecture: c.Architecture(),
 				Config:       c.LocalConfig(),
@@ -3368,7 +3383,7 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 				return err
 			}
 
-			// On function return, set the flag back on
+			// On function return, set the flag back on.
 			defer func() {
 				args.Ephemeral = ephemeral
 				c.Update(args, true)
@@ -3382,12 +3397,22 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 		}
 
 		// Ensure that storage is mounted for state path checks.
-		ourStart, err := c.StorageStart()
-		if err != nil {
-			return err
-		}
-		if ourStart {
-			defer c.StorageStop()
+		if pool != nil {
+			ourStart, err := pool.MountInstance(c, nil)
+			if err != nil {
+				return err
+			}
+			if ourStart {
+				defer pool.UnmountInstance(c, nil)
+			}
+		} else {
+			ourStart, err := c.StorageStart()
+			if err != nil {
+				return err
+			}
+			if ourStart {
+				defer c.StorageStop()
+			}
 		}
 	}
 
@@ -3401,14 +3426,21 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 
 	logger.Info("Restoring container", ctxMap)
 
-	// Restore the rootfs
-	err = c.storage.ContainerRestore(c, sourceContainer)
-	if err != nil {
-		logger.Error("Failed restoring container filesystem", ctxMap)
-		return err
+	// Restore the rootfs.
+	if pool != nil {
+		err = pool.RestoreInstanceSnapshot(c, sourceContainer, nil)
+		if err != nil {
+			return err
+		}
+	} else {
+		err = c.storage.ContainerRestore(c, sourceContainer)
+		if err != nil {
+			logger.Error("Failed restoring container filesystem", ctxMap)
+			return err
+		}
 	}
 
-	// Restore the configuration
+	// Restore the configuration.
 	args := db.InstanceArgs{
 		Architecture: sourceContainer.Architecture(),
 		Config:       sourceContainer.LocalConfig(),
@@ -3427,16 +3459,14 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 		return err
 	}
 
-	// The old backup file may be out of date (e.g. it doesn't have all the
-	// current snapshots of the container listed); let's write a new one to
-	// be safe.
+	// The old backup file may be out of date (e.g. it doesn't have all the current snapshots of
+	// the container listed); let's write a new one to be safe.
 	err = writeBackupFile(c)
 	if err != nil {
 		return err
 	}
 
-	// If the container wasn't running but was stateful, should we restore
-	// it as running?
+	// If the container wasn't running but was stateful, should we restore it as running?
 	if stateful == true {
 		if !shared.PathExists(c.StatePath()) {
 			return fmt.Errorf("Stateful snapshot restore requested by snapshot is stateless")
@@ -3455,14 +3485,13 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 			preDumpDir:   "",
 		}
 
-		// Checkpoint
+		// Checkpoint.
 		err := c.Migrate(&criuMigrationArgs)
 		if err != nil {
 			return err
 		}
 
-		// Remove the state from the parent container; we only keep
-		// this in snapshots.
+		// Remove the state from the parent container; we only keep this in snapshots.
 		err2 := os.RemoveAll(c.StatePath())
 		if err2 != nil {
 			logger.Error("Failed to delete snapshot state", log.Ctx{"path": c.StatePath(), "err": err2})
@@ -3483,14 +3512,13 @@ func (c *containerLXC) Restore(sourceContainer instance.Instance, stateful bool)
 			"snapshot_name": c.name,
 		})
 
-	// Restart the container
+	// Restart the container.
 	if wasRunning {
 		logger.Info("Restored container", ctxMap)
 		return c.Start(false)
 	}
 
 	logger.Info("Restored container", ctxMap)
-
 	return nil
 }
 

From 8c9b1439f0c80930aff5ad3ba37f4a1b9bd80ad7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 09:37:27 +0000
Subject: [PATCH 06/16] lxd/storage/interfaces: RestoreInstanceSnapshot
 signature

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

diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index ca821cc4a9..e00cbcc24f 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -51,7 +51,7 @@ type Pool interface {
 	CreateInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error
 	RenameInstanceSnapshot(inst instance.Instance, newName string, op *operations.Operation) error
 	DeleteInstanceSnapshot(inst instance.Instance, op *operations.Operation) error
-	RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error
+	RestoreInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error
 	MountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error)
 	UnmountInstanceSnapshot(inst instance.Instance, op *operations.Operation) (bool, error)
 

From 4ee7fee451566eaafe68e171c17e78bf66a4f3dc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 09:37:48 +0000
Subject: [PATCH 07/16] lxd/storage/backend/mock: RestoreInstanceSnapshot
 signature

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

diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 3fb98dec58..cb3a464b71 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -131,7 +131,7 @@ func (b *mockBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operati
 	return nil
 }
 
-func (b *mockBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error {
+func (b *mockBackend) RestoreInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error {
 	return nil
 }
 

From 47a7e123e85bbd17afd3b5b4175cff10b9e56619 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 10:14:04 +0000
Subject: [PATCH 08/16] lxd/storage/backend/lxd: Implements
 RestoreInstanceSnapshot

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/backend_lxd.go | 62 +++++++++++++++++++++++++++++++++++---
 1 file changed, 58 insertions(+), 4 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index ac8ea670a3..7693bf90dc 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1060,11 +1060,11 @@ func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src instance
 	}
 
 	if !inst.IsSnapshot() {
-		return fmt.Errorf("Instance must be snapshot")
+		return fmt.Errorf("Instance must be a snapshot")
 	}
 
 	if src.IsSnapshot() {
-		return fmt.Errorf("Source instance cannot be snapshot")
+		return fmt.Errorf("Source instance cannot be a snapshot")
 	}
 
 	// Check we can convert the instance to the volume type needed.
@@ -1199,8 +1199,62 @@ func (b *lxdBackend) DeleteInstanceSnapshot(inst instance.Instance, op *operatio
 	return nil
 }
 
-func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, op *operations.Operation) error {
-	return ErrNotImplemented
+// RestoreInstanceSnapshot restores an instance snapshot.
+func (b *lxdBackend) RestoreInstanceSnapshot(inst instance.Instance, src instance.Instance, op *operations.Operation) error {
+	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "src": src.Name()})
+	logger.Debug("RestoreInstanceSnapshot started")
+	defer logger.Debug("RestoreInstanceSnapshot finished")
+
+	if inst.Type() != src.Type() {
+		return fmt.Errorf("Instance types must match")
+	}
+
+	if inst.IsSnapshot() {
+		return fmt.Errorf("Instance must not be snapshot")
+	}
+
+	if !src.IsSnapshot() {
+		return fmt.Errorf("Source instance must be a snapshot")
+	}
+
+	// Target instance must not be running.
+	if inst.IsRunning() {
+		return fmt.Errorf("Instance must not be running to restore")
+	}
+
+	// Check we can convert the instance to the volume type needed.
+	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 source snapshot instance.
+	_, rootDiskConf, err := shared.GetRootDiskDevice(src.ExpandedDevices().CloneNative())
+	if err != nil {
+		return err
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	_, snapshotName, isSnap := shared.InstanceGetParentAndSnapshotName(src.Name())
+	if !isSnap {
+		return fmt.Errorf("Volume name must be a snapshot")
+	}
+
+	// Use the source snapshot's rootfs config (as this will later be restored into inst too).
+	vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
+	err = b.driver.RestoreVolume(vol, snapshotName, op)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // MountInstanceSnapshot mounts an instance snapshot. It is mounted as read only so that the

From f988bc1b94ad1715c98ec191c23ae3809f423d9e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 11:18:14 +0000
Subject: [PATCH 09/16] lxd/container/backup: Comment tweaks and inst var
 rename

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container_backup.go | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/lxd/container_backup.go b/lxd/container_backup.go
index f8ab231f82..d8cc1f1a53 100644
--- a/lxd/container_backup.go
+++ b/lxd/container_backup.go
@@ -81,7 +81,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 	project := projectParam(r)
 	name := mux.Vars(r)["name"]
 
-	// Handle requests targeted to a container on a different node
+	// Handle requests targeted to a container on a different node.
 	resp, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
 	if err != nil {
 		return response.SmartError(err)
@@ -90,7 +90,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 		return resp
 	}
 
-	c, err := instanceLoadByProjectAndName(d.State(), project, name)
+	inst, err := instanceLoadByProjectAndName(d.State(), project, name)
 	if err != nil {
 		return response.SmartError(err)
 	}
@@ -103,11 +103,11 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 
 	expiry, _ := rj.GetString("expires_at")
 	if expiry == "" {
-		// Disable expiration by setting it to zero time
+		// Disable expiration by setting it to zero time.
 		rj["expires_at"] = time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)
 	}
 
-	// Create body with correct expiry
+	// Create body with correct expiry.
 	body, err := json.Marshal(rj)
 	if err != nil {
 		return response.InternalError(err)
@@ -121,8 +121,8 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	if req.Name == "" {
-		// come up with a name
-		backups, err := c.Backups()
+		// come up with a name.
+		backups, err := inst.Backups()
 		if err != nil {
 			return response.BadRequest(err)
 		}
@@ -132,7 +132,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 		max := 0
 
 		for _, backup := range backups {
-			// Ignore backups not containing base
+			// Ignore backups not containing base.
 			if !strings.HasPrefix(backup.Name(), base) {
 				continue
 			}
@@ -151,7 +151,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 		req.Name = fmt.Sprintf("backup%d", max)
 	}
 
-	// Validate the name
+	// Validate the name.
 	if strings.Contains(req.Name, "/") {
 		return response.BadRequest(fmt.Errorf("Backup names may not contain slashes"))
 	}
@@ -162,7 +162,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 	backup := func(op *operations.Operation) error {
 		args := db.InstanceBackupArgs{
 			Name:                 fullName,
-			InstanceID:           c.ID(),
+			InstanceID:           inst.ID(),
 			CreationDate:         time.Now(),
 			ExpiryDate:           req.ExpiresAt,
 			InstanceOnly:         instanceOnly,
@@ -170,7 +170,7 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 			CompressionAlgorithm: req.CompressionAlgorithm,
 		}
 
-		err := backupCreate(d.State(), args, c)
+		err := backupCreate(d.State(), args, inst)
 		if err != nil {
 			return errors.Wrap(err, "Create backup")
 		}
@@ -179,7 +179,8 @@ func containerBackupsPost(d *Daemon, r *http.Request) response.Response {
 	}
 
 	resources := map[string][]string{}
-	resources["containers"] = []string{name}
+	resources["instances"] = []string{name}
+	resources["containers"] = resources["instances"]
 	resources["backups"] = []string{req.Name}
 
 	op, err := operations.OperationCreate(d.State(), project, operations.OperationClassTask,

From c7c4f757824dbf4367faa892e8bbda9cb9a0d83d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 11:45:28 +0000
Subject: [PATCH 10/16] lxd/backup: Links backupCreate to new storage pkg

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/backup.go | 71 ++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 24 deletions(-)

diff --git a/lxd/backup.go b/lxd/backup.go
index b9557fd5b2..be3b24511b 100644
--- a/lxd/backup.go
+++ b/lxd/backup.go
@@ -19,6 +19,8 @@ import (
 	"github.com/lxc/lxd/lxd/operations"
 	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
+	storagePools "github.com/lxc/lxd/lxd/storage"
+	storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
 	"github.com/lxc/lxd/lxd/task"
 	"github.com/lxc/lxd/shared"
 	log "github.com/lxc/lxd/shared/log15"
@@ -26,9 +28,9 @@ import (
 	"github.com/pkg/errors"
 )
 
-// Create a new backup
-func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceContainer instance.Instance) error {
-	// Create the database entry
+// Create a new backup.
+func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceInst instance.Instance) error {
+	// Create the database entry.
 	err := s.Cluster.ContainerBackupCreate(args)
 	if err != nil {
 		if err == db.ErrAlreadyDefined {
@@ -38,44 +40,65 @@ func backupCreate(s *state.State, args db.InstanceBackupArgs, sourceContainer in
 		return errors.Wrap(err, "Insert backup info into database")
 	}
 
-	// Get the backup struct
-	b, err := backup.LoadByName(s, sourceContainer.Project(), args.Name)
+	revert := true
+	defer func() {
+		if !revert {
+			return
+		}
+		s.Cluster.ContainerBackupRemove(args.Name)
+	}()
+
+	// Get the backup struct.
+	b, err := backup.LoadByName(s, sourceInst.Project(), args.Name)
 	if err != nil {
 		return errors.Wrap(err, "Load backup object")
 	}
 
 	b.SetCompressionAlgorithm(args.CompressionAlgorithm)
 
-	ourStart, err := sourceContainer.StorageStart()
-	if err != nil {
-		return err
-	}
-	if ourStart {
-		defer sourceContainer.StorageStop()
-	}
-
-	// Create a temporary path for the backup
+	// Create a temporary path for the backup.
 	tmpPath, err := ioutil.TempDir(shared.VarPath("backups"), "lxd_backup_")
 	if err != nil {
 		return err
 	}
 	defer os.RemoveAll(tmpPath)
 
-	if sourceContainer.Type() != instancetype.Container {
-		return fmt.Errorf("Instance type must be container")
-	}
+	pool, err := storagePools.GetPoolByInstance(s, sourceInst)
+	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
+		if err != nil {
+			return errors.Wrap(err, "Load instance storage pool")
+		}
 
-	ct := sourceContainer.(*containerLXC)
+		err = pool.BackupInstance(sourceInst, tmpPath, b.OptimizedStorage(), !b.InstanceOnly(), nil)
+		if err != nil {
+			return errors.Wrap(err, "Backup create")
+		}
+	} else if sourceInst.Type() == instancetype.Container {
+		ourStart, err := sourceInst.StorageStart()
+		if err != nil {
+			return err
+		}
+		if ourStart {
+			defer sourceInst.StorageStop()
+		}
+
+		ct := sourceInst.(*containerLXC)
+		err = ct.Storage().ContainerBackupCreate(tmpPath, *b, sourceInst)
+		if err != nil {
+			return errors.Wrap(err, "Backup create")
+		}
+	} else {
+		return fmt.Errorf("Instance type not supported")
+	}
 
-	// Now create the empty snapshot
-	err = ct.Storage().ContainerBackupCreate(tmpPath, *b, sourceContainer)
+	// Pack the backup.
+	err = backupCreateTarball(s, tmpPath, *b, sourceInst)
 	if err != nil {
-		s.Cluster.ContainerBackupRemove(args.Name)
-		return errors.Wrap(err, "Backup storage")
+		return err
 	}
 
-	// Pack the backup
-	return backupCreateTarball(s, tmpPath, *b, sourceContainer)
+	revert = false
+	return nil
 }
 
 // fixBackupStoragePool changes the pool information in the backup.yaml. This

From 948a978d95a9da85c8e143ec0927caa00191990e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 13:24:16 +0000
Subject: [PATCH 11/16] lxd/storage/drivers/driver/dir: rsync.LocalCopy return
 value consistency

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

diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go
index cc6da99bc8..fcc02a0807 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -657,9 +657,9 @@ func (d *dir) RestoreVolume(vol Volume, snapshotName string, op *operations.Oper
 
 	// Restore using rsync.
 	bwlimit := d.config["rsync.bwlimit"]
-	output, err := rsync.LocalCopy(srcPath, volPath, bwlimit, true)
+	_, err := rsync.LocalCopy(srcPath, volPath, bwlimit, true)
 	if err != nil {
-		return fmt.Errorf("Failed to rsync volume: %s: %s", string(output), err)
+		return fmt.Errorf("Failed to rsync volume: %s", err)
 	}
 
 	return nil

From 5de417189b639b2523bc027ff2280f1a02b97fcd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 13:25:33 +0000
Subject: [PATCH 12/16] lxd/storage/backend/lxd: Ensures all instance functions
 use project aware storage names

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/backend_lxd.go | 46 ++++++++++++++++++++++++++++++--------
 1 file changed, 37 insertions(+), 9 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 7693bf90dc..bbb4fefa88 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -307,7 +307,10 @@ func (b *lxdBackend) CreateInstance(inst instance.Instance, op *operations.Opera
 		return err
 	}
 
-	vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf)
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
 	err = b.driver.CreateVolume(vol, nil, op)
 	if err != nil {
 		return err
@@ -366,11 +369,17 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst instance.Instance, src instance
 		return err
 	}
 
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
 	// 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)
+	vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
+
+	// Get the src volume name on storage.
+	srcVolStorageName := project.Prefix(src.Project(), src.Name())
 
 	// 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)
+	srcVol := b.newVolume(volType, contentType, srcVolStorageName, nil)
 
 	revert := true
 	defer func() {
@@ -505,11 +514,17 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan
 		return err
 	}
 
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
 	// 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)
+	vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
+
+	// Get the src volume name on storage.
+	srcVolStorageName := project.Prefix(src.Project(), src.Name())
 
 	// 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)
+	srcVol := b.newVolume(volType, contentType, srcVolStorageName, nil)
 
 	srcSnapVols := []drivers.Volume{}
 	for _, snapInst := range srcSnapshots {
@@ -518,7 +533,10 @@ func (b *lxdBackend) RefreshInstance(inst instance.Instance, src instance.Instan
 		// 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)
+
+		// Get the snap volume name on storage.
+		snapVolStorageName := project.Prefix(snapInst.Project(), snapInst.Name())
+		srcSnapVol := b.newVolume(volType, contentType, snapVolStorageName, nil)
 		srcSnapVols = append(srcSnapVols, srcSnapVol)
 	}
 
@@ -661,7 +679,10 @@ func (b *lxdBackend) CreateInstanceFromImage(inst instance.Instance, fingerprint
 		return err
 	}
 
-	vol := b.newVolume(volType, contentType, project.Prefix(inst.Project(), inst.Name()), rootDiskConf)
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	vol := b.newVolume(volType, contentType, volStorageName, 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.
@@ -735,7 +756,10 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst instance.Instance, conn io
 	args.Config = rootDiskConf
 	args.Name = inst.Name()
 
-	vol := b.newVolume(volType, contentType, args.Name, args.Config)
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), args.Name)
+
+	vol := b.newVolume(volType, contentType, volStorageName, args.Config)
 	err = b.driver.CreateVolumeFromMigration(vol, conn, args, op)
 	if err != nil {
 		conn.Close()
@@ -939,7 +963,11 @@ func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCl
 	}
 
 	args.Name = inst.Name() // Override args.Name to ensure instance volume is sent.
-	vol := b.newVolume(volType, contentType, args.Name, rootDiskConf)
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), args.Name)
+
+	vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
 	err = b.driver.MigrateVolume(vol, conn, args, op)
 	if err != nil {
 		return err

From 122850516a92e549a982c02047a805cc02bd9687 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 11:59:38 +0000
Subject: [PATCH 13/16] lxd/storage/backend/lxd: Implements BackupInstance

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/backend_lxd.go | 32 +++++++++++++++++++++++++++++++-
 1 file changed, 31 insertions(+), 1 deletion(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index bbb4fefa88..1a904bc203 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -976,8 +976,38 @@ func (b *lxdBackend) MigrateInstance(inst instance.Instance, conn io.ReadWriteCl
 	return nil
 }
 
+// BackupInstance creates an instance backup.
 func (b *lxdBackend) BackupInstance(inst instance.Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
-	return ErrNotImplemented
+	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name(), "targetPath": targetPath, "optimized": optimized, "snapshots": snapshots})
+	logger.Debug("BackupInstance started")
+	defer logger.Debug("BackupInstance 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
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	vol := b.newVolume(volType, contentType, volStorageName, rootDiskConf)
+	err = b.driver.BackupVolume(vol, targetPath, optimized, snapshots, op)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // GetInstanceUsage returns the disk usage of the instance's root volume.

From cab161f0e47adc02db09119c2ade06780e2d5959 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 13:23:01 +0000
Subject: [PATCH 14/16] lxd/storage/drivers/interface: Adds BackupVolume

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/interface.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
index 064a707936..11d8343f05 100644
--- a/lxd/storage/drivers/interface.go
+++ b/lxd/storage/drivers/interface.go
@@ -69,4 +69,7 @@ type Driver interface {
 	MigrationTypes(contentType ContentType) []migration.Type
 	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
 	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
+
+	// Backup.
+	BackupVolume(vol Volume, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error
 }

From e9444bf236557e35416769c2642ec3789a8c00b1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 13:23:16 +0000
Subject: [PATCH 15/16] lxd/storage/drivers/driver/cephfs: Adds BackupVolume
 placeholder

Signed-off-by: Thomas Parrott <thomas.parrott at 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 b3622e86ce..98b593cd80 100644
--- a/lxd/storage/drivers/driver_cephfs.go
+++ b/lxd/storage/drivers/driver_cephfs.go
@@ -987,3 +987,7 @@ func (d *cephfs) getConfig(clusterName string, userName string) ([]string, strin
 
 	return cephMon, cephSecret, nil
 }
+
+func (d *cephfs) BackupVolume(vol Volume, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error {
+	return ErrNotImplemented
+}

From df97224bb12d7635f3dda61506211a7c727c31d2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 26 Nov 2019 13:23:57 +0000
Subject: [PATCH 16/16] lxd/storage/drivers/driver/dir: Adds BackupVolume

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/driver_dir.go | 52 +++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go
index fcc02a0807..66c85d9ce7 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -893,3 +893,55 @@ func (d *dir) RenameVolumeSnapshot(volType VolumeType, volName string, snapshotN
 
 	return nil
 }
+
+// BackupVolume copies a volume (and optionally its snapshots) to a specified target path.
+// This driver does not support optimized backups.
+func (d *dir) BackupVolume(vol Volume, targetPath string, _, snapshots bool, op *operations.Operation) error {
+	bwlimit := d.config["rsync.bwlimit"]
+
+	var parentVolDir string
+
+	// Backups only implemented for containers currently.
+	if vol.volType == VolumeTypeContainer {
+		parentVolDir = "container"
+	} else {
+		return ErrNotImplemented
+	}
+
+	// Handle snapshots.
+	if snapshots {
+		snapshotsPath := filepath.Join(targetPath, "snapshots")
+		snapshots, err := vol.Snapshots(op)
+		if err != nil {
+			return err
+		}
+
+		// Create the snapshot path.
+		if len(snapshots) > 0 {
+			err = os.MkdirAll(snapshotsPath, 0711)
+			if err != nil {
+				return err
+			}
+		}
+
+		for _, snap := range snapshots {
+			_, snapName, _ := shared.InstanceGetParentAndSnapshotName(snap.Name())
+			target := filepath.Join(snapshotsPath, snapName)
+
+			// Copy the snapshot.
+			_, err := rsync.LocalCopy(snap.MountPath(), target, bwlimit, true)
+			if err != nil {
+				return fmt.Errorf("Failed to rsync: %s", err)
+			}
+		}
+	}
+
+	// Copy the parent volume itself.
+	target := filepath.Join(targetPath, parentVolDir)
+	_, err := rsync.LocalCopy(vol.MountPath(), target, bwlimit, true)
+	if err != nil {
+		return fmt.Errorf("Failed to rsync: %s", err)
+	}
+
+	return nil
+}


More information about the lxc-devel mailing list