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

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Includes https://github.com/lxc/lxd/pull/6400
From 70b0bc0ff0c1a6a978c5dc04b5de5d353fe00032 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 5 Nov 2019 08:54:10 +0000
Subject: [PATCH 01/15] lxd/storage/drivers/driver/dir: Comment grammar
 consistency

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

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index 0062e75798..d95bc00f1a 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -304,13 +304,13 @@ func (d *dir) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, vol
                                return err
                        }
 
-                       // Create the snapshot itself
+                       // Create the snapshot itself.
                        err = d.CreateVolumeSnapshot(vol.volType, vol.name, 
snapName, op)
                        if err != nil {
                                return err
                        }
 
-                       // Setup the revert
+                       // Setup the revert.
                        snapPath := GetVolumeMountPath(d.name, vol.volType, 
GetSnapshotVolumeName(vol.name, snapName))
                        revertPaths = append(revertPaths, snapPath)
                }
@@ -389,20 +389,20 @@ func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots bool
                        for _, srcSnapshot := range srcSnapshots {
                                _, snapName, _ := 
shared.ContainerGetParentAndSnapshotName(srcSnapshot.name)
 
-                               // Mount the source snapshot
+                               // Mount the source snapshot.
                                err = srcSnapshot.MountTask(func(srcMountPath 
string, op *operations.Operation) error {
-                                       // Copy the snapshot
+                                       // Copy the snapshot.
                                        _, err = rsync.LocalCopy(srcMountPath, 
mountPath, bwlimit, true)
                                        return err
                                }, op)
 
-                               // Create the snapshot itself
+                               // Create the snapshot itself.
                                err = d.CreateVolumeSnapshot(vol.volType, 
vol.name, snapName, op)
                                if err != nil {
                                        return err
                                }
 
-                               // Setup the revert
+                               // Setup the revert.
                                snapPath := GetVolumeMountPath(d.name, 
vol.volType, GetSnapshotVolumeName(vol.name, snapName))
                                revertPaths = append(revertPaths, snapPath)
                        }

From 0bbcae61d5e0722e15078a199e3cf7360db29f3a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 5 Nov 2019 09:08:58 +0000
Subject: [PATCH 02/15] lxd/storage/load: Renames GetPoolByInstanceName to
 GetPoolByInstance

- Adds additional check to GetPoolByInstance that checks that the Instance is 
of a type that is compatible with the underlying storage pool driver.
- If not it returns storageDrivers.ErrNotImplemented (for consistency with 
storageDrivers.ErrUnknownDriver where the pool's driver itself is not 
implemented).
- This allows the caller to decide to fallback to the old storage layer if 
either the pool's driver is not known or the driver doesn't implement support 
for the instance's type.
- This allows for operations to cleanly fail when we introduce VM support 
rather than have unexpected failures.
- It also allows for new storage drivers to be partially implemented, such that 
they only implement custom volume functionality, allowing quicker iteration and 
testing of new drivers.

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

diff --git a/lxd/storage/load.go b/lxd/storage/load.go
index d2f1e24072..5249f9a32e 100644
--- a/lxd/storage/load.go
+++ b/lxd/storage/load.go
@@ -60,6 +60,7 @@ func volIDFuncMake(state *state.State, poolID int64) 
func(volType drivers.Volume
 }
 
 // CreatePool creates a new storage pool on disk and returns a Pool interface.
+// If the pool's driver is not recognised then drivers.ErrUnknownDriver is 
returned.
 func CreatePool(state *state.State, poolID int64, dbPool *api.StoragePool, op 
*operations.Operation) (Pool, error) {
        // Sanity checks.
        if dbPool == nil {
@@ -106,6 +107,7 @@ func CreatePool(state *state.State, poolID int64, dbPool 
*api.StoragePool, op *o
 }
 
 // GetPoolByName retrieves the pool from the database by its name and returns 
a Pool interface.
+// If the pool's driver is not recognised then drivers.ErrUnknownDriver is 
returned.
 func GetPoolByName(state *state.State, name string) (Pool, error) {
        // Handle mock requests.
        if MockBackend {
@@ -146,12 +148,32 @@ func GetPoolByName(state *state.State, name string) 
(Pool, error) {
        return &pool, nil
 }
 
-// GetPoolByInstanceName retrieves the pool from the database using the 
instance's project and name.
-func GetPoolByInstanceName(s *state.State, projectName, instanceName string) 
(Pool, error) {
-       poolName, err := s.Cluster.ContainerPool(projectName, instanceName)
+// 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) {
+       poolName, err := s.Cluster.ContainerPool(inst.Project(), inst.Name())
        if err != nil {
                return nil, err
        }
 
-       return GetPoolByName(s, poolName)
+       pool, err := GetPoolByName(s, poolName)
+       if err != nil {
+               return nil, err
+       }
+
+       volType, err := InstanceTypeToVolumeType(inst.Type())
+       if err != nil {
+               return nil, err
+       }
+
+       for _, supportedType := range pool.Driver().Info().VolumeTypes {
+               if supportedType == volType {
+                       return pool, nil
+               }
+       }
+
+       // Return drivers not implemented error for consistency with predefined 
errors returned by
+       // GetPoolByName (which can return drivers.ErrUnknownDriver).
+       return nil, drivers.ErrNotImplemented
 }

From 90639b9c3c875e0b26246a2dd71ab10e09041f3f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 5 Nov 2019 09:12:34 +0000
Subject: [PATCH 03/15] lxd/container: Updates use of
 storagePools.GetPoolByInstance and fallback for container types

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

diff --git a/lxd/container.go b/lxd/container.go
index 8f52cc1a8f..b841fc7e17 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -406,7 +406,7 @@ func containerCreateEmptySnapshot(s *state.State, args 
db.InstanceArgs) (contain
 func containerCreateFromImage(d *Daemon, args db.InstanceArgs, hash string, op 
*operations.Operation) (container, error) {
        s := d.State()
 
-       // Get the image properties
+       // Get the image properties.
        _, img, err := s.Cluster.ImageGet(args.Project, hash, false, false)
        if err != nil {
                return nil, errors.Wrapf(err, "Fetch image %s from database", 
hash)
@@ -428,8 +428,7 @@ func containerCreateFromImage(d *Daemon, args 
db.InstanceArgs, hash string, op *
                return nil, errors.Wrapf(err, "Locate image %s in the cluster", 
hash)
        }
        if nodeAddress != "" {
-               // The image is available from another node, let's try to
-               // import it.
+               // The image is available from another node, let's try to 
import it.
                logger.Debugf("Transferring image %s from node %s", hash, 
nodeAddress)
                client, err := cluster.Connect(nodeAddress, 
d.endpoints.NetworkCert(), false)
                if err != nil {
@@ -449,14 +448,14 @@ func containerCreateFromImage(d *Daemon, args 
db.InstanceArgs, hash string, op *
                }
        }
 
-       // Set the "image.*" keys
+       // Set the "image.*" keys.
        if img.Properties != nil {
                for k, v := range img.Properties {
                        args.Config[fmt.Sprintf("image.%s", k)] = v
                }
        }
 
-       // Set the BaseImage field (regardless of previous value)
+       // Set the BaseImage field (regardless of previous value).
        args.BaseImage = hash
 
        // Create the container
@@ -472,8 +471,8 @@ func containerCreateFromImage(d *Daemon, args 
db.InstanceArgs, hash string, op *
        }
 
        // Check if we can load new storage layer for pool driver type.
-       pool, err := storagePools.GetPoolByInstanceName(d.State(), c.Project(), 
c.Name())
-       if err != storageDrivers.ErrUnknownDriver {
+       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")
                }
@@ -482,7 +481,7 @@ func containerCreateFromImage(d *Daemon, args 
db.InstanceArgs, hash string, op *
                if err != nil {
                        return nil, errors.Wrap(err, "Create instance from 
image")
                }
-       } else {
+       } else if c.Type() == instancetype.Container {
                metadata := make(map[string]interface{})
                var tracker *ioprogress.ProgressTracker
                if op != nil {
@@ -493,15 +492,17 @@ func containerCreateFromImage(d *Daemon, args 
db.InstanceArgs, hash string, op *
                                }}
                }
 
-               // Now create the storage from an image
+               // Now create the storage from an image.
                err = c.Storage().ContainerCreateFromImage(c, hash, tracker)
                if err != nil {
                        c.Delete()
                        return nil, errors.Wrap(err, "Create container from 
image")
                }
+       } 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 435d598bc9206be776fef4601b047d25a9c25258 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 5 Nov 2019 09:12:51 +0000
Subject: [PATCH 04/15] lxd/storage/drivers/errors: Removes unused error

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

diff --git a/lxd/storage/drivers/errors.go b/lxd/storage/drivers/errors.go
index 7cca047d15..56d1bdab3c 100644
--- a/lxd/storage/drivers/errors.go
+++ b/lxd/storage/drivers/errors.go
@@ -4,9 +4,6 @@ import (
        "fmt"
 )
 
-// ErrNilValue is the "Nil value provided" error
-var ErrNilValue = fmt.Errorf("Nil value provided")
-
 // ErrNotImplemented is the "Not implemented" error
 var ErrNotImplemented = fmt.Errorf("Not implemented")
 

From e662852c556886462d4712d4aafff4819daa258c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 5 Nov 2019 09:28:06 +0000
Subject: [PATCH 05/15] lxd/container: Links containerCreateAsEmpty to new
 storage package

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 09:37:43 +0000
Subject: [PATCH 06/15] lxd/container: Adds revert to containerCreateFromImage

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 09:40:40 +0000
Subject: [PATCH 07/15] lxd/container: containerCreateFromImage comment

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 10:31:15 +0000
Subject: [PATCH 08/15] lxd/storage/drivers/utils: Makes GetVolumeSnapshotDir
 work with either snapshot or parent vol name

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 10:31:35 +0000
Subject: [PATCH 09/15] lxd/storage/drivers/utils: Removes symlink removal from
 DeleteParentSnapshotDirIfEmpty

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 10:37:30 +0000
Subject: [PATCH 10/15] lxd/storage/backend/lxd: CreateInstance

- Adds todo question regarding templating

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 10:37:53 +0000
Subject: [PATCH 11/15] lxd/storage/backend/lxd: Updates instance snapshot
 symlink removal

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 10:34:26 +0000
Subject: [PATCH 12/15] lxd/storage/backend/lxd: Updates instance snapshot
 symlink management functions

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 11:18:01 +0000
Subject: [PATCH 13/15] lxd/storage/interfaces: Updates instance mount function
 definitions

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 11:18:19 +0000
Subject: [PATCH 14/15] lxd/storage/backend/mock: Updates instance mount
 function definitions

Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@canonical.com>
Date: Tue, 5 Nov 2019 11:18:44 +0000
Subject: [PATCH 15/15] lxd/storage/backend/lxd: Implements instance mount and
 unmount functions

Signed-off-by: Thomas Parrott <thomas.parr...@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
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to