[lxc-devel] [lxd/master] Storage Instance Mount and Unmount

tomponline on Github lxc-bot at linuxcontainers.org
Tue Nov 5 11:20:04 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/1a743d5f/attachment.bin>
-------------- next part --------------
From 70b0bc0ff0c1a6a978c5dc04b5de5d353fe00032 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.parrott at 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.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 06/15] 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 07/15] 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 08/15] 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 09/15] 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 10/15] 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 11/15] 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 12/15] 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 13/15] 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 14/15] 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 15/15] 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


More information about the lxc-devel mailing list