[lxc-devel] [lxd/master] Storage Instance Quota

tomponline on Github lxc-bot at linuxcontainers.org
Tue Nov 5 13:50:28 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/20191105/784dd799/attachment-0001.bin>
-------------- next part --------------
From e662852c556886462d4712d4aafff4819daa258c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 09:28:06 +0000
Subject: [PATCH 01/23] lxd/container: Links containerCreateAsEmpty to new
 storage package

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

diff --git a/lxd/container.go b/lxd/container.go
index b841fc7e17..774b467bbc 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -254,22 +254,38 @@ type container interface {
 	NextIdmap() (*idmap.IdmapSet, error)
 }
 
-// Loader functions
+// containerCreateAsEmpty creates an empty instance.
 func containerCreateAsEmpty(d *Daemon, args db.InstanceArgs) (container, error) {
-	// Create the container
+	// Create the container.
 	c, err := containerCreateInternal(d.State(), args)
 	if err != nil {
 		return nil, err
 	}
 
-	// Now create the empty storage
-	err = c.Storage().ContainerCreate(c)
-	if err != nil {
-		c.Delete()
-		return nil, err
+	// Check if we can load new storage layer for pool driver type.
+	pool, err := storagePools.GetPoolByInstance(d.State(), c)
+	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
+		if err != nil {
+			return nil, errors.Wrap(err, "Load instance storage pool")
+		}
+
+		err = pool.CreateInstance(c, nil)
+		if err != nil {
+			c.Delete()
+			return nil, errors.Wrap(err, "Create instance")
+		}
+	} else if c.Type() == instancetype.Container {
+		// Now create the empty storage.
+		err = c.Storage().ContainerCreate(c)
+		if err != nil {
+			c.Delete()
+			return nil, err
+		}
+	} else {
+		return nil, fmt.Errorf("Instance type not supported")
 	}
 
-	// Apply any post-storage configuration
+	// Apply any post-storage configuration.
 	err = containerConfigureInternal(c)
 	if err != nil {
 		c.Delete()

From 561cfc75185c7235e0ef523c3292825b6dff24e8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 09:37:43 +0000
Subject: [PATCH 02/23] lxd/container: Adds revert to containerCreateFromImage

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

diff --git a/lxd/container.go b/lxd/container.go
index 774b467bbc..e81cdbd833 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -495,6 +495,7 @@ func containerCreateFromImage(d *Daemon, args db.InstanceArgs, hash string, op *
 
 		err = pool.CreateInstanceFromImage(c, hash, op)
 		if err != nil {
+			c.Delete()
 			return nil, errors.Wrap(err, "Create instance from image")
 		}
 	} else if c.Type() == instancetype.Container {

From 50e540db953de9e133204ce54cd2e5455a0b8989 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 09:40:40 +0000
Subject: [PATCH 03/23] lxd/container: containerCreateFromImage comment

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

diff --git a/lxd/container.go b/lxd/container.go
index e81cdbd833..422ec47da8 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -419,6 +419,7 @@ func containerCreateEmptySnapshot(s *state.State, args db.InstanceArgs) (contain
 	return c, nil
 }
 
+// containerCreateFromImage creates an instance from a rootfs image.
 func containerCreateFromImage(d *Daemon, args db.InstanceArgs, hash string, op *operations.Operation) (container, error) {
 	s := d.State()
 

From f0b2bd348f386f0448dcbf18b002c79ff21ed475 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 10:31:15 +0000
Subject: [PATCH 04/23] lxd/storage/drivers/utils: Makes GetVolumeSnapshotDir
 work with either snapshot or parent vol name

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

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 2277d3c57a..94715add21 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -183,11 +183,8 @@ func GetVolumeMountPath(poolName string, volType VolumeType, volName string) str
 
 // GetVolumeSnapshotDir gets the snapshot mount directory for the parent volume.
 func GetVolumeSnapshotDir(poolName string, volType VolumeType, volName string) (string, error) {
-	if shared.IsSnapshot(volName) {
-		return "", fmt.Errorf("Volume cannot be a snapshot")
-	}
-
-	return shared.VarPath("storage-pools", poolName, fmt.Sprintf("%s-snapshots", string(volType)), project.Prefix("default", volName)), nil
+	parent, _, _ := shared.ContainerGetParentAndSnapshotName(volName)
+	return shared.VarPath("storage-pools", poolName, fmt.Sprintf("%s-snapshots", string(volType)), project.Prefix("default", parent)), nil
 }
 
 // GetSnapshotVolumeName returns the full volume name for a parent volume and snapshot name.

From 7ed69fa051fda3392785891fd9bd9547fb3207e0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 10:31:35 +0000
Subject: [PATCH 05/23] lxd/storage/drivers/utils: Removes symlink removal from
 DeleteParentSnapshotDirIfEmpty

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

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 94715add21..ffc09539ed 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -215,16 +215,5 @@ func DeleteParentSnapshotDirIfEmpty(poolName string, volType VolumeType, volName
 		}
 	}
 
-	// If it no longer exists (may have just removed it), remove symlink.
-	if !shared.PathExists(snapshotsPath) {
-		snapshotSymlink := shared.VarPath("snapshots", volName)
-		if shared.PathExists(snapshotSymlink) {
-			err := os.Remove(snapshotSymlink)
-			if err != nil {
-				return err
-			}
-		}
-	}
-
 	return nil
 }

From d9fed98fb7c2868025a9f3417dd35cddf72b6d48 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 10:37:30 +0000
Subject: [PATCH 06/23] lxd/storage/backend/lxd: CreateInstance

- Adds todo question regarding templating

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 9c1f4651e5..4547f7a886 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -161,18 +161,6 @@ func (b *lxdBackend) Unmount() (bool, error) {
 	return b.driver.Unmount()
 }
 
-func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) error {
-	return ErrNotImplemented
-}
-
-func (b *lxdBackend) CreateInstanceFromBackup(inst Instance, sourcePath string, op *operations.Operation) error {
-	return ErrNotImplemented
-}
-
-func (b *lxdBackend) CreateInstanceFromCopy(inst Instance, src Instance, snapshots bool, op *operations.Operation) error {
-	return ErrNotImplemented
-}
-
 // createInstanceSymlink creates a symlink in the instance directory to the instance's mount path.
 func (b *lxdBackend) createInstanceSymlink(inst Instance, mountPath string) error {
 	symlinkPath := inst.Path()
@@ -226,6 +214,56 @@ func (b *lxdBackend) createInstanceSnapshotSymlink(inst Instance, mountPath stri
 	return nil
 }
 
+// CreateInstance creates an empty instance.
+func (b *lxdBackend) CreateInstance(inst 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")
+
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return err
+	}
+
+	revert := true
+	defer func() {
+		if !revert {
+			return
+		}
+		b.DeleteInstance(inst, op)
+	}()
+
+	vol := b.newVolume(volType, drivers.ContentTypeFS, project.Prefix(inst.Project(), inst.Name()), nil)
+	err = b.driver.CreateVolume(vol, nil, op)
+	if err != nil {
+		return err
+	}
+
+	err = b.createInstanceSymlink(inst, vol.MountPath())
+	if err != nil {
+		return err
+	}
+
+	// tomp TODO as this modifies the rootfs after being unpacked, it probably needs to run
+	// as part of the filler function above or perform a separate mount so that the rootfs is
+	// actually mounted (in the case of optimised images there is no filler function).
+	err = inst.TemplateApply("create")
+	if err != nil {
+		return err
+	}
+
+	revert = false
+	return nil
+}
+
+func (b *lxdBackend) CreateInstanceFromBackup(inst Instance, sourcePath string, op *operations.Operation) error {
+	return ErrNotImplemented
+}
+
+func (b *lxdBackend) CreateInstanceFromCopy(inst Instance, src Instance, snapshots bool, op *operations.Operation) error {
+	return ErrNotImplemented
+}
+
 // imageFiller returns a function that can be used as a filler function with CreateVolume(). This
 // function will unpack the specified image archive into the specified mount path of the volume.
 func (b *lxdBackend) imageFiller(fingerprint string, op *operations.Operation) func(mountPath string) error {
@@ -295,6 +333,9 @@ func (b *lxdBackend) CreateInstanceFromImage(inst Instance, fingerprint string,
 		return err
 	}
 
+	// tomp TODO as this modifies the rootfs after being unpacked, it probably needs to run
+	// as part of the filler function above or perform a separate mount so that the rootfs is
+	// actually mounted (in the case of optimised images there is no filler function).
 	err = inst.TemplateApply("create")
 	if err != nil {
 		return err

From 382a4dbc0e6f86a3908e8c7269adb3778923b967 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 10:37:53 +0000
Subject: [PATCH 07/23] lxd/storage/backend/lxd: Updates instance snapshot
 symlink removal

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 4547f7a886..d43e8aaa46 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -396,12 +396,17 @@ func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) err
 		return err
 	}
 
-	// Remove symlink.
+	// Remove symlinks.
 	err = b.removeInstanceSymlink(inst)
 	if err != nil {
 		return err
 	}
 
+	err = b.removeInstanceSnapshotSymlinkIfUnused(inst)
+	if err != nil {
+		return err
+	}
+
 	// Remove the volume record from the database.
 	err = b.state.Cluster.StoragePoolVolumeDelete(inst.Project(), inst.Name(), volDBType, b.ID())
 	if err != nil {
@@ -493,6 +498,12 @@ func (b *lxdBackend) DeleteInstanceSnapshot(inst Instance, op *operations.Operat
 		return err
 	}
 
+	// Delete symlink if needed.
+	err = b.removeInstanceSnapshotSymlinkIfUnused(inst)
+	if err != nil {
+		return err
+	}
+
 	// Remove the snapshot volume record from the database.
 	err = b.state.Cluster.StoragePoolVolumeDelete(inst.Project(), drivers.GetSnapshotVolumeName(parentName, snapName), volDBType, b.ID())
 	if err != nil {

From 1744771871756f3e4fdecefc161dee402106b0a0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 10:34:26 +0000
Subject: [PATCH 08/23] lxd/storage/backend/lxd: Updates instance snapshot
 symlink management functions

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index d43e8aaa46..167fb98084 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -187,16 +187,16 @@ func (b *lxdBackend) removeInstanceSymlink(inst Instance) error {
 	return nil
 }
 
-// createInstanceSnapshotSymlink creates a symlink in the snapshot directory to the instance's
-// snapshot path.
-func (b *lxdBackend) createInstanceSnapshotSymlink(inst Instance, mountPath string) error {
+// ensureInstanceSnapshotSymlink creates a symlink in the snapshot directory to the instance's
+// snapshot path if doesn't exist already.
+func (b *lxdBackend) ensureInstanceSnapshotSymlink(inst Instance) error {
 	// Check we can convert the instance to the volume types needed.
 	volType, err := InstanceTypeToVolumeType(inst.Type())
 	if err != nil {
 		return err
 	}
 
-	snapshotMntPointSymlink := shared.VarPath("snapshots", project.Prefix(inst.Project(), inst.Name()))
+	snapshotSymlink := shared.VarPath("snapshots", project.Prefix(inst.Project(), inst.Name()))
 	volStorageName := project.Prefix(inst.Project(), inst.Name())
 
 	snapshotTargetPath, err := drivers.GetVolumeSnapshotDir(b.name, volType, volStorageName)
@@ -204,8 +204,8 @@ func (b *lxdBackend) createInstanceSnapshotSymlink(inst Instance, mountPath stri
 		return err
 	}
 
-	if !shared.PathExists(snapshotMntPointSymlink) {
-		err := os.Symlink(snapshotTargetPath, snapshotMntPointSymlink)
+	if !shared.PathExists(snapshotSymlink) {
+		err := os.Symlink(snapshotTargetPath, snapshotSymlink)
 		if err != nil {
 			return err
 		}
@@ -214,6 +214,37 @@ func (b *lxdBackend) createInstanceSnapshotSymlink(inst Instance, mountPath stri
 	return nil
 }
 
+// removeInstanceSnapshotSymlinkIfUnused removes the symlink in the snapshot directory to the
+// instance's snapshot path if the snapshot path is missing. It is expected that the driver will
+// remove the instance's snapshot path after the last snapshot is removed or the volume is deleted.
+func (b *lxdBackend) removeInstanceSnapshotSymlinkIfUnused(inst Instance) error {
+	// Check we can convert the instance to the volume types needed.
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return err
+	}
+
+	snapshotSymlink := shared.VarPath("snapshots", project.Prefix(inst.Project(), inst.Name()))
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	snapshotTargetPath, err := drivers.GetVolumeSnapshotDir(b.name, volType, volStorageName)
+	if err != nil {
+		return err
+	}
+
+	// If snapshot parent directory doesn't exist, remove symlink.
+	if !shared.PathExists(snapshotTargetPath) {
+		if shared.PathExists(snapshotSymlink) {
+			err := os.Remove(snapshotSymlink)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
 // CreateInstance creates an empty instance.
 func (b *lxdBackend) CreateInstance(inst Instance, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})

From d1df84654a53ad2ba86823193198dd0be86468db Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 11:18:01 +0000
Subject: [PATCH 09/23] lxd/storage/interfaces: Updates instance mount function
 definitions

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

diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index 4e787f07bb..78527691df 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -55,8 +55,8 @@ type Pool interface {
 	GetInstanceUsage(i Instance) (int64, error)
 	SetInstanceQuota(i Instance, quota uint64) error
 
-	MountInstance(i Instance) (bool, error)
-	UnmountInstance(i Instance) (bool, error)
+	MountInstance(i Instance, op *operations.Operation) (bool, error)
+	UnmountInstance(i Instance, op *operations.Operation) (bool, error)
 	GetInstanceDisk(i Instance) (string, string, error)
 
 	// Instance snapshots.
@@ -64,8 +64,8 @@ type Pool interface {
 	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) (bool, error)
-	UnmountInstanceSnapshot(i Instance) (bool, error)
+	MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error)
+	UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error)
 
 	// Images.
 	EnsureImage(fingerprint string, op *operations.Operation) error

From 820ae1dcc7d5fcc5555664b10ee1ca81abfd3735 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 11:18:19 +0000
Subject: [PATCH 10/23] lxd/storage/backend/mock: Updates instance mount
 function definitions

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

diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 9f20fc1b55..20ccd09129 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -106,11 +106,11 @@ func (b *mockBackend) SetInstanceQuota(i Instance, quota uint64) error {
 	return nil
 }
 
-func (b *mockBackend) MountInstance(i Instance) (bool, error) {
+func (b *mockBackend) MountInstance(i Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
-func (b *mockBackend) UnmountInstance(i Instance) (bool, error) {
+func (b *mockBackend) UnmountInstance(i Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
@@ -134,11 +134,11 @@ func (b *mockBackend) RestoreInstanceSnapshot(i Instance, op *operations.Operati
 	return nil
 }
 
-func (b *mockBackend) MountInstanceSnapshot(i Instance) (bool, error) {
+func (b *mockBackend) MountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 
-func (b *mockBackend) UnmountInstanceSnapshot(i Instance) (bool, error) {
+func (b *mockBackend) UnmountInstanceSnapshot(i Instance, op *operations.Operation) (bool, error) {
 	return true, nil
 }
 

From 73f0325c8c3f1125d3efd63e27ba2ee503ed8af8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 11:18:44 +0000
Subject: [PATCH 11/23] lxd/storage/backend/lxd: Implements instance mount and
 unmount functions

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 167fb98084..4f2e3c00ec 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -476,12 +476,40 @@ func (b *lxdBackend) SetInstanceQuota(inst Instance, quota uint64) error {
 	return ErrNotImplemented
 }
 
-func (b *lxdBackend) MountInstance(inst Instance) (bool, error) {
-	return true, ErrNotImplemented
+// MountInstance mounts the instance's rootfs.
+func (b *lxdBackend) MountInstance(inst 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")
+
+	// Check we can convert the instance to the volume type needed.
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return false, err
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	return b.driver.MountVolume(volType, volStorageName, op)
 }
 
-func (b *lxdBackend) UnmountInstance(inst Instance) (bool, error) {
-	return true, ErrNotImplemented
+// UnmountInstance unmounts the instance's rootfs.
+func (b *lxdBackend) UnmountInstance(inst 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")
+
+	// Check we can convert the instance to the volume type needed.
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return false, err
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	return b.driver.UnmountVolume(volType, volStorageName, op)
 }
 
 func (b *lxdBackend) GetInstanceDisk(inst Instance) (string, string, error) {
@@ -548,12 +576,55 @@ func (b *lxdBackend) RestoreInstanceSnapshot(inst Instance, op *operations.Opera
 	return ErrNotImplemented
 }
 
-func (b *lxdBackend) MountInstanceSnapshot(inst Instance) (bool, error) {
-	return true, 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) {
+	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
+	logger.Debug("MountInstanceSnapshot started")
+	defer logger.Debug("MountInstanceSnapshot finished")
+
+	if !inst.IsSnapshot() {
+		return false, fmt.Errorf("Instance must be a snapshot")
+	}
+
+	// Check we can convert the instance to the volume type needed.
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return false, err
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	// Get the snapshot name.
+	_, snapName, _ := shared.ContainerGetParentAndSnapshotName(inst.Name())
+
+	return b.driver.MountVolumeSnapshot(volType, volStorageName, snapName, op)
 }
 
-func (b *lxdBackend) UnmountInstanceSnapshot(inst Instance) (bool, error) {
-	return true, ErrNotImplemented
+// UnmountInstanceSnapshot unmounts an instance snapshot.
+func (b *lxdBackend) UnmountInstanceSnapshot(inst 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")
+
+	if !inst.IsSnapshot() {
+		return false, fmt.Errorf("Instance must be a snapshot")
+	}
+
+	// Check we can convert the instance to the volume type needed.
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return false, err
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	// Get the snapshot name.
+	_, snapName, _ := shared.ContainerGetParentAndSnapshotName(inst.Name())
+
+	return b.driver.UnmountVolumeSnapshot(volType, volStorageName, snapName, op)
 }
 
 // EnsureImage creates an optimized volume of the image if supported by the storage pool driver and

From cb69b4bde8b2f6844657a6122678e29bd75f3a16 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 11:42:09 +0000
Subject: [PATCH 12/23] lxd/storage/backend/lxd: Instance function comment
 consistency

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 4f2e3c00ec..bc2b2103bb 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -384,7 +384,7 @@ func (b *lxdBackend) RenameInstance(inst Instance, newName string, op *operation
 	return ErrNotImplemented
 }
 
-// DeleteInstance removes the Instance's root volume (all snapshots need to be removed first).
+// DeleteInstance removes the instance's root volume (all snapshots need to be removed first).
 func (b *lxdBackend) DeleteInstance(inst Instance, op *operations.Operation) error {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("DeleteInstance started")
@@ -459,7 +459,7 @@ func (b *lxdBackend) BackupInstance(inst Instance, targetPath string, optimized
 	return ErrNotImplemented
 }
 
-// GetInstanceUsage returns the disk usage of the Instance's root device.
+// GetInstanceUsage returns the disk usage of the instance's root device.
 func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("GetInstanceUsage started")
@@ -473,10 +473,11 @@ func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) {
 }
 
 func (b *lxdBackend) SetInstanceQuota(inst Instance, quota uint64) error {
+
 	return ErrNotImplemented
 }
 
-// MountInstance mounts the instance's rootfs.
+// MountInstance mounts the instance's device.
 func (b *lxdBackend) MountInstance(inst Instance, op *operations.Operation) (bool, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("MountInstance started")
@@ -494,7 +495,7 @@ func (b *lxdBackend) MountInstance(inst Instance, op *operations.Operation) (boo
 	return b.driver.MountVolume(volType, volStorageName, op)
 }
 
-// UnmountInstance unmounts the instance's rootfs.
+// UnmountInstance unmounts the instance's device.
 func (b *lxdBackend) UnmountInstance(inst Instance, op *operations.Operation) (bool, error) {
 	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
 	logger.Debug("UnmountInstance started")

From ec70a0961943b0330f0386331b908d83eded9544 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:13:34 +0000
Subject: [PATCH 13/23] lxd/device/device/utils/disk: Changes signature of
 StorageRootFSApplyQuota

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

diff --git a/lxd/device/device_utils_disk.go b/lxd/device/device_utils_disk.go
index c6545869fd..469f32ad54 100644
--- a/lxd/device/device_utils_disk.go
+++ b/lxd/device/device_utils_disk.go
@@ -17,7 +17,7 @@ var StorageVolumeMount func(s *state.State, poolName string, volumeName string,
 var StorageVolumeUmount func(s *state.State, poolName string, volumeName string, volumeType int) error
 
 // StorageRootFSApplyQuota applies a new quota.
-var StorageRootFSApplyQuota func(instance Instance, newSize int64) (bool, error)
+var StorageRootFSApplyQuota func(s *state.State, instance Instance, size string) error
 
 // BlockFsDetect detects the type of block device.
 func BlockFsDetect(dev string) (string, error) {

From 12bcb26107633af1be943338ffc3c950cfae9aea Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:14:27 +0000
Subject: [PATCH 14/23] lxd/device/disk: Updates applyQuota to use error from
 storage package

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/disk.go | 29 +++++++++--------------------
 1 file changed, 9 insertions(+), 20 deletions(-)

diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index 853598006b..ee430b6718 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -15,6 +15,7 @@ import (
 	"github.com/lxc/lxd/lxd/db"
 	deviceConfig "github.com/lxc/lxd/lxd/device/config"
 	"github.com/lxc/lxd/lxd/instance/instancetype"
+	storagePools "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/logger"
@@ -226,8 +227,8 @@ func (d *disk) Start() (*RunConfig, error) {
 
 		// Handle previous requests for setting new quotas.
 		if v["volatile.apply_quota"] != "" {
-			applied, err := d.applyQuota(v["volatile.apply_quota"])
-			if err != nil || !applied {
+			err := d.applyQuota(v["volatile.apply_quota"])
+			if err != nil {
 				return nil, err
 			}
 
@@ -369,17 +370,15 @@ func (d *disk) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
 
 		// Apply disk quota changes.
 		if newRootDiskDeviceSize != oldRootDiskDeviceSize {
-			applied, err := d.applyQuota(newRootDiskDeviceSize)
-			if err != nil {
-				return err
-			}
-
-			if !applied {
+			err := d.applyQuota(newRootDiskDeviceSize)
+			if err == storagePools.ErrRunningQuotaResizeNotSupported {
 				// Save volatile apply_quota key for next boot if cannot apply now.
 				err = d.volatileSet(map[string]string{"volatile.apply_quota": newRootDiskDeviceSize})
 				if err != nil {
 					return err
 				}
+			} else if err != nil {
+				return err
 			}
 		}
 	}
@@ -401,18 +400,8 @@ func (d *disk) Update(oldDevices deviceConfig.Devices, isRunning bool) error {
 	return nil
 }
 
-func (d *disk) applyQuota(newSize string) (bool, error) {
-	newSizeBytes, err := units.ParseByteSizeString(newSize)
-	if err != nil {
-		return false, err
-	}
-
-	applied, err := StorageRootFSApplyQuota(d.instance, newSizeBytes)
-	if err != nil {
-		return applied, err
-	}
-
-	return applied, nil
+func (d *disk) applyQuota(newSize string) error {
+	return StorageRootFSApplyQuota(d.state, d.instance, newSize)
 }
 
 // generateLimits adds a set of cgroup rules to apply specified limits to the supplied RunConfig.

From 8c963d054585e56a207aa3b8943291999a3e9694 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:14:57 +0000
Subject: [PATCH 15/23] lxd/storage: Links storageRootFSApplyQuota to new
 storage package

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

diff --git a/lxd/storage.go b/lxd/storage.go
index eae87e50e4..6a3aa7cf7d 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -18,12 +18,14 @@ import (
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/operations"
 	"github.com/lxc/lxd/lxd/state"
-	driver "github.com/lxc/lxd/lxd/storage"
+	storagePools "github.com/lxc/lxd/lxd/storage"
+	storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/idmap"
 	"github.com/lxc/lxd/shared/ioprogress"
 	"github.com/lxc/lxd/shared/logger"
+	"github.com/lxc/lxd/shared/units"
 	"github.com/lxc/lxd/shared/version"
 )
 
@@ -480,7 +482,7 @@ func storagePoolVolumeAttachInit(s *state.State, poolName string, volumeName str
 	poolVolumePut.Config["volatile.idmap.next"] = nextJsonMap
 
 	// get mountpoint of storage volume
-	remapPath := driver.GetStoragePoolVolumeMountPoint(poolName, volumeName)
+	remapPath := storagePools.GetStoragePoolVolumeMountPoint(poolName, volumeName)
 
 	if !nextIdmap.Equals(lastIdmap) {
 		logger.Debugf("Shifting storage volume")
@@ -840,7 +842,7 @@ func storageVolumeMount(state *state.State, poolName string, volumeName string,
 		return fmt.Errorf("Received non-LXC container instance")
 	}
 
-	volumeType, _ := driver.VolumeTypeNameToType(volumeTypeName)
+	volumeType, _ := storagePools.VolumeTypeNameToType(volumeTypeName)
 	s, err := storagePoolVolumeAttachInit(state, poolName, volumeName, volumeType, c)
 	if err != nil {
 		return err
@@ -872,29 +874,42 @@ func storageVolumeUmount(state *state.State, poolName string, volumeName string,
 
 // storageRootFSApplyQuota applies a quota to an instance if it can, if it cannot then it will
 // return false indicating that the quota needs to be stored in volatile to be applied on next boot.
-func storageRootFSApplyQuota(instance device.Instance, newSizeBytes int64) (bool, error) {
-	c, ok := instance.(*containerLXC)
+func storageRootFSApplyQuota(state *state.State, inst device.Instance, size string) error {
+	c, ok := inst.(*containerLXC)
 	if !ok {
-		return false, fmt.Errorf("Received non-LXC container instance")
+		return fmt.Errorf("Received non-LXC container instance")
 	}
 
-	err := c.initStorage()
-	if err != nil {
-		return false, errors.Wrap(err, "Initialize storage")
-	}
+	pool, err := storagePools.GetPoolByInstance(state, c)
+	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
+		err = pool.SetInstanceQuota(c, size, nil)
+		if err != nil {
+			return err
+		}
+	} else {
+		err := c.initStorage()
+		if err != nil {
+			return errors.Wrap(err, "Initialize storage")
+		}
 
-	storageTypeName := c.storage.GetStorageTypeName()
-	storageIsReady := c.storage.ContainerStorageReady(c)
+		storageTypeName := c.storage.GetStorageTypeName()
+		storageIsReady := c.storage.ContainerStorageReady(c)
 
-	// If we cannot apply the quota now, then return false as needs to be applied on next boot.
-	if (storageTypeName == "lvm" || storageTypeName == "ceph") && c.IsRunning() || !storageIsReady {
-		return false, nil
-	}
+		// If we cannot apply the quota now, then return false as needs to be applied on next boot.
+		if (storageTypeName == "lvm" || storageTypeName == "ceph") && c.IsRunning() || !storageIsReady {
+			return storagePools.ErrRunningQuotaResizeNotSupported
+		}
 
-	err = c.storage.StorageEntitySetQuota(storagePoolVolumeTypeContainer, newSizeBytes, c)
-	if err != nil {
-		return false, errors.Wrap(err, "Set storage quota")
+		newSizeBytes, err := units.ParseByteSizeString(size)
+		if err != nil {
+			return err
+		}
+
+		err = c.storage.StorageEntitySetQuota(storagePoolVolumeTypeContainer, newSizeBytes, c)
+		if err != nil {
+			return errors.Wrap(err, "Set storage quota")
+		}
 	}
 
-	return true, nil
+	return nil
 }

From 2a0182cc3044cdabde58c75902686f42d4c7f8d8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:15:20 +0000
Subject: [PATCH 16/23] lxd/storage/backend/lxd: SetInstanceQuota

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index bc2b2103bb..18d7a82718 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -190,7 +190,7 @@ func (b *lxdBackend) removeInstanceSymlink(inst Instance) error {
 // ensureInstanceSnapshotSymlink creates a symlink in the snapshot directory to the instance's
 // snapshot path if doesn't exist already.
 func (b *lxdBackend) ensureInstanceSnapshotSymlink(inst Instance) error {
-	// Check we can convert the instance to the volume types needed.
+	// Check we can convert the instance to the volume type needed.
 	volType, err := InstanceTypeToVolumeType(inst.Type())
 	if err != nil {
 		return err
@@ -218,7 +218,7 @@ func (b *lxdBackend) ensureInstanceSnapshotSymlink(inst Instance) error {
 // instance's snapshot path if the snapshot path is missing. It is expected that the driver will
 // remove the instance's snapshot path after the last snapshot is removed or the volume is deleted.
 func (b *lxdBackend) removeInstanceSnapshotSymlinkIfUnused(inst Instance) error {
-	// Check we can convert the instance to the volume types needed.
+	// Check we can convert the instance to the volume type needed.
 	volType, err := InstanceTypeToVolumeType(inst.Type())
 	if err != nil {
 		return err
@@ -472,9 +472,28 @@ func (b *lxdBackend) GetInstanceUsage(inst Instance) (int64, error) {
 	return -1, ErrNotImplemented
 }
 
-func (b *lxdBackend) SetInstanceQuota(inst Instance, quota uint64) error {
+// SetInstanceQuota sets the quota on the instance's root device.
+// 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 {
+	logger := logging.AddContext(b.logger, log.Ctx{"project": inst.Project(), "instance": inst.Name()})
+	logger.Debug("SetInstanceQuota started")
+	defer logger.Debug("SetInstanceQuota finished")
 
-	return ErrNotImplemented
+	if inst.IsRunning() && !b.driver.Info().RunningQuotaResize {
+		return ErrRunningQuotaResizeNotSupported
+	}
+
+	// Check we can convert the instance to the volume type needed.
+	volType, err := InstanceTypeToVolumeType(inst.Type())
+	if err != nil {
+		return err
+	}
+
+	// Get the volume name on storage.
+	volStorageName := project.Prefix(inst.Project(), inst.Name())
+
+	return b.driver.SetVolumeQuota(volType, volStorageName, size, op)
 }
 
 // MountInstance mounts the instance's device.

From 8ab79c017d1d00759a0cfcb5cc840ba4caacbd1a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:15:37 +0000
Subject: [PATCH 17/23] lxd/storage/backend/mock: SetInstanceQuota

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 20ccd09129..a0ddb34707 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -102,7 +102,7 @@ func (b *mockBackend) GetInstanceUsage(i Instance) (int64, error) {
 	return 0, nil
 }
 
-func (b *mockBackend) SetInstanceQuota(i Instance, quota uint64) error {
+func (b *mockBackend) SetInstanceQuota(i Instance, size string, op *operations.Operation) error {
 	return nil
 }
 

From 888b1112ae50193275cf857b0c0740b8b498307e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:16:04 +0000
Subject: [PATCH 18/23] lxd/storage/drivers/dir: Adds SetVolumeQuota and
 RunningQuotaResize info flag

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

diff --git a/lxd/storage/drivers/driver_dir.go b/lxd/storage/drivers/driver_dir.go
index d95bc00f1a..ac24101ce7 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -27,14 +27,15 @@ type dir struct {
 // Info returns info about the driver and its environment.
 func (d *dir) Info() Info {
 	return Info{
-		Name:            "dir",
-		Version:         "1",
-		OptimizedImages: false,
-		PreservesInodes: false,
-		Usable:          true,
-		Remote:          false,
-		VolumeTypes:     []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer},
-		BlockBacking:    false,
+		Name:               "dir",
+		Version:            "1",
+		OptimizedImages:    false,
+		PreservesInodes:    false,
+		Usable:             true,
+		Remote:             false,
+		VolumeTypes:        []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer},
+		BlockBacking:       false,
+		RunningQuotaResize: true,
 	}
 }
 
@@ -659,6 +660,17 @@ func (d *dir) UnmountVolumeSnapshot(volType VolumeType, volName, snapshotName st
 	return forceUnmount(snapPath)
 }
 
+// SetVolumeQuota sets the quota on the volume.
+func (d *dir) SetVolumeQuota(volType VolumeType, volName, size string, op *operations.Operation) error {
+	volPath := GetVolumeMountPath(d.name, volType, volName)
+	volID, err := d.getVolID(volType, volName)
+	if err != nil {
+		return err
+	}
+
+	return d.setQuota(volPath, volID, size)
+}
+
 // quotaProjectID generates a project quota ID from a volume ID.
 func (d *dir) quotaProjectID(volID int64) uint32 {
 	return uint32(volID + 10000)

From 58d9bb11619ac3d87e9bd15bab9755ef689d5748 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:16:31 +0000
Subject: [PATCH 19/23] lxd/storage/drivers/interface: SetVolumeQuota signature

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

diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
index 50bab56cb3..ed5a066a74 100644
--- a/lxd/storage/drivers/interface.go
+++ b/lxd/storage/drivers/interface.go
@@ -38,6 +38,7 @@ type Driver interface {
 	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error
 	UpdateVolume(vol Volume, changedConfig map[string]string) error
 	GetVolumeUsage(volType VolumeType, volName string) (int64, error)
+	SetVolumeQuota(volType VolumeType, volName, size string, op *operations.Operation) error
 
 	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
 	// already mounted.

From e85ac5c893e1cc0a4344bc38ece8a410ba8c74f2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:16:45 +0000
Subject: [PATCH 20/23] lxd/storage/drivers/load: Adds RunningQuotaResize to
 driver Info struct

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

diff --git a/lxd/storage/drivers/load.go b/lxd/storage/drivers/load.go
index c3ebfed335..f5732767cd 100644
--- a/lxd/storage/drivers/load.go
+++ b/lxd/storage/drivers/load.go
@@ -25,14 +25,15 @@ func Load(state *state.State, driverName string, name string, config map[string]
 
 // Info represents information about a storage driver.
 type Info struct {
-	Name            string
-	Version         string
-	Usable          bool
-	Remote          bool
-	OptimizedImages bool
-	PreservesInodes bool
-	VolumeTypes     []VolumeType
-	BlockBacking    bool
+	Name               string
+	Version            string
+	Usable             bool
+	Remote             bool
+	OptimizedImages    bool
+	PreservesInodes    bool
+	VolumeTypes        []VolumeType
+	BlockBacking       bool
+	RunningQuotaResize bool
 }
 
 // SupportedDrivers returns a list of supported storage drivers.

From fc2678e971621b85f0708161618ae4f96cad94d0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:17:02 +0000
Subject: [PATCH 21/23] lxd/storage/errors: Adds
 ErrRunningQuotaResizeNotSupported error

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

diff --git a/lxd/storage/errors.go b/lxd/storage/errors.go
index efbe5c6c75..64df5f1579 100644
--- a/lxd/storage/errors.go
+++ b/lxd/storage/errors.go
@@ -9,3 +9,6 @@ var ErrNilValue = fmt.Errorf("Nil value provided")
 
 // ErrNotImplemented is the "Not implemented" error
 var ErrNotImplemented = fmt.Errorf("Not implemented")
+
+// ErrRunningQuotaResizeNotSupported is the "Running quota resize not supported" error.
+var ErrRunningQuotaResizeNotSupported = fmt.Errorf("Running quota resize not supported")

From 3749799782ed92260a6f1b39f4488aa1c9cec39f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:17:20 +0000
Subject: [PATCH 22/23] lxd/storage/interfaces: SetInstanceQuota signature

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

diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index 78527691df..87c9bb02e6 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -53,7 +53,7 @@ type Pool interface {
 	BackupInstance(i Instance, targetPath string, optimized bool, snapshots bool, op *operations.Operation) error
 
 	GetInstanceUsage(i Instance) (int64, error)
-	SetInstanceQuota(i Instance, quota uint64) error
+	SetInstanceQuota(i Instance, size string, op *operations.Operation) error
 
 	MountInstance(i Instance, op *operations.Operation) (bool, error)
 	UnmountInstance(i Instance, op *operations.Operation) (bool, error)

From 94def6ac2726f110e18661f32cd4d3e9d606c69c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 5 Nov 2019 13:49:07 +0000
Subject: [PATCH 23/23] lxd/container: Links containerConfigureInternal to new
 storage package

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

diff --git a/lxd/container.go b/lxd/container.go
index 422ec47da8..41b94c9f85 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -286,7 +286,7 @@ func containerCreateAsEmpty(d *Daemon, args db.InstanceArgs) (container, error)
 	}
 
 	// Apply any post-storage configuration.
-	err = containerConfigureInternal(c)
+	err = containerConfigureInternal(d.State(), c)
 	if err != nil {
 		c.Delete()
 		return nil, err
@@ -521,7 +521,7 @@ func containerCreateFromImage(d *Daemon, args db.InstanceArgs, hash string, op *
 	}
 
 	// Apply any post-storage configuration.
-	err = containerConfigureInternal(c)
+	err = containerConfigureInternal(d.State(), c)
 	if err != nil {
 		c.Delete()
 		return nil, errors.Wrap(err, "Configure container")
@@ -673,7 +673,7 @@ func containerCreateAsCopy(s *state.State, args db.InstanceArgs, sourceContainer
 	}
 
 	// Apply any post-storage configuration.
-	err = containerConfigureInternal(ct)
+	err = containerConfigureInternal(s, ct)
 	if err != nil {
 		if !refresh {
 			ct.Delete()
@@ -685,7 +685,7 @@ func containerCreateAsCopy(s *state.State, args db.InstanceArgs, sourceContainer
 	if !containerOnly {
 		for _, cs := range csList {
 			// Apply any post-storage configuration.
-			err = containerConfigureInternal(*cs)
+			err = containerConfigureInternal(s, *cs)
 			if err != nil {
 				if !refresh {
 					ct.Delete()
@@ -994,47 +994,77 @@ func containerCreateInternal(s *state.State, args db.InstanceArgs) (container, e
 	return c, nil
 }
 
-func containerConfigureInternal(c Instance) error {
+func containerConfigureInternal(state *state.State, c Instance) error {
 	// Find the root device
 	_, rootDiskDevice, err := shared.GetRootDiskDevice(c.ExpandedDevices().CloneNative())
 	if err != nil {
 		return err
 	}
 
-	ourStart, err := c.StorageStart()
-	if err != nil {
-		return err
-	}
+	// Check if we can load new storage layer for pool driver type.
+	pool, err := storagePools.GetPoolByInstance(state, c)
+	if err != storageDrivers.ErrUnknownDriver && err != storageDrivers.ErrNotImplemented {
+		if err != nil {
+			return errors.Wrap(err, "Load instance storage pool")
+		}
 
-	// handle quota: at this point, storage is guaranteed to be ready
-	storage := c.Storage()
-	if rootDiskDevice["size"] != "" {
-		storageTypeName := storage.GetStorageTypeName()
-		if (storageTypeName == "lvm" || storageTypeName == "ceph") && c.IsRunning() {
-			err = c.VolatileSet(map[string]string{"volatile.apply_quota": rootDiskDevice["size"]})
-			if err != nil {
-				return err
-			}
-		} else {
-			size, err := units.ParseByteSizeString(rootDiskDevice["size"])
-			if err != nil {
+		if rootDiskDevice["size"] != "" {
+			err = pool.SetInstanceQuota(c, rootDiskDevice["size"], nil)
+
+			// If the storage driver can't set the quota now, store in volatile.
+			if err == storagePools.ErrRunningQuotaResizeNotSupported {
+				err = c.VolatileSet(map[string]string{"volatile.apply_quota": rootDiskDevice["size"]})
+				if err != nil {
+					return err
+				}
+			} else if err != nil {
 				return err
 			}
+		}
 
-			err = storage.StorageEntitySetQuota(storagePoolVolumeTypeContainer, size, c)
-			if err != nil {
-				return err
+		// tomp TODO this likely needs to mount the volume first.
+		err = writeBackupFile(c)
+		if err != nil {
+			return err
+		}
+	} else if c.Type() == instancetype.Container {
+		ourStart, err := c.StorageStart()
+		if err != nil {
+			return err
+		}
+
+		// handle quota: at this point, storage is guaranteed to be ready
+		storage := c.Storage()
+		if rootDiskDevice["size"] != "" {
+			storageTypeName := storage.GetStorageTypeName()
+			if (storageTypeName == "lvm" || storageTypeName == "ceph") && c.IsRunning() {
+				err = c.VolatileSet(map[string]string{"volatile.apply_quota": rootDiskDevice["size"]})
+				if err != nil {
+					return err
+				}
+			} else {
+				size, err := units.ParseByteSizeString(rootDiskDevice["size"])
+				if err != nil {
+					return err
+				}
+
+				err = storage.StorageEntitySetQuota(storagePoolVolumeTypeContainer, size, c)
+				if err != nil {
+					return err
+				}
 			}
 		}
-	}
 
-	if ourStart {
-		defer c.StorageStop()
-	}
+		if ourStart {
+			defer c.StorageStop()
+		}
 
-	err = writeBackupFile(c)
-	if err != nil {
-		return err
+		err = writeBackupFile(c)
+		if err != nil {
+			return err
+		}
+	} else {
+		return fmt.Errorf("Instance type not supported")
 	}
 
 	return nil


More information about the lxc-devel mailing list