[lxc-devel] [lxd/master] Storage: Allow BTRFS to detect volume.size pool changes and regeneration image volumes

tomponline on Github lxc-bot at linuxcontainers.org
Mon Dec 7 19:31:58 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 334 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20201207/48738832/attachment.bin>
-------------- next part --------------
From c622519f3cc9260dda192811626696b8e9283c5b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 7 Dec 2020 19:27:15 +0000
Subject: [PATCH 1/4] lxd/storage/backend/lxd: Comment typo fix

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 79a72c541e..0259328829 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -2201,8 +2201,8 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op *operations.Operation) e
 
 			imgVol.SetConfigSize(newVolSize)
 
-			// Try applying the current size policy to the existin volume. If it is the same the driver
-			// should make no changes, and if not then attempt to resize it to the new policy.
+			// Try applying the current size policy to the existing volume. If it is the same the
+			// driver should make no changes, and if not then attempt to resize it to the new policy.
 			logger.Debug("Setting image volume size", "size", imgVol.ConfigSize())
 			err = b.driver.SetVolumeQuota(imgVol, imgVol.ConfigSize(), op)
 			if errors.Cause(err) == drivers.ErrCannotBeShrunk || errors.Cause(err) == drivers.ErrNotSupported {

From f245258acf7e23b74d6d16e050ab9656bde99c18 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 7 Dec 2020 19:27:38 +0000
Subject: [PATCH 2/4] lxd/storage/drivers/driver/btrfs/volumes: Enable
 allowUnsafeResize in CreateVolume when creating initial image volume

This is so the image volume can be resized to desired size after filler has run but before readonly snapshot is created.

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

diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go
index 3de65c5c78..687c935138 100644
--- a/lxd/storage/drivers/driver_btrfs_volumes.go
+++ b/lxd/storage/drivers/driver_btrfs_volumes.go
@@ -68,6 +68,13 @@ func (d *btrfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Op
 			return err
 		}
 
+		// Allow unsafe resize of image volumes as filler won't have been able to resize the volume to the
+		// target size as volume file didn't exist then (and we can't create in advance because qemu-img
+		// truncates the file to image size).
+		if vol.volType == VolumeTypeImage {
+			vol.allowUnsafeResize = true
+		}
+
 		_, err = ensureVolumeBlockFile(vol, rootBlockPath, sizeBytes)
 
 		// Ignore ErrCannotBeShrunk as this just means the filler has needed to increase the volume size.

From 059863e24bcca4e9f53cff413541553e8a3d6f39 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 7 Dec 2020 19:29:02 +0000
Subject: [PATCH 3/4] lxd/storage/drivers/utils: Updates ensureVolumeBlockFile
 to return unsupported when trying to resize image volume without
 allowUnsafeResize enabled

This is so the correct error response is returned to trigger backendLXD to regenerate cached BTRFS image volume when pool's volume.size changes.

Doesn't affect dir pools (which also use this function) as they do not have cached image volumes.

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

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 646d10c4a4..a64635bbc2 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -347,6 +347,13 @@ func ensureVolumeBlockFile(vol Volume, path string, sizeBytes int64) (bool, erro
 			return false, nil
 		}
 
+		// Block image volumes cannot be resized because they can have a readonly snapshot that doesn't get
+		// updated when the volume's size is changed, and this is what instances are created from.
+		// During initial volume fill allowUnsafeResize is enabled because snapshot hasn't been taken yet.
+		if !vol.allowUnsafeResize && vol.volType == VolumeTypeImage {
+			return false, ErrNotSupported
+		}
+
 		// Only perform pre-resize sanity checks if we are not in "unsafe" mode.
 		// In unsafe mode we expect the caller to know what they are doing and understand the risks.
 		if !vol.allowUnsafeResize {

From a751c6045ddb3052e8d98a88639c3adab077b3d3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 7 Dec 2020 19:30:32 +0000
Subject: [PATCH 4/4] lxd/storage/utils: Ensure pool's volume.size is checked
 when unpacking images to pools that use file based images

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

diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index cf79c49af1..adc6de91d3 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -512,28 +512,28 @@ func ImageUnpack(imageFile string, vol drivers.Volume, destBlockFile string, blo
 			return -1, fmt.Errorf("Unexpected image format %q", imgInfo.Format)
 		}
 
+		// Check whether image is allowed to be unpacked into pool volume. Create a partial image volume
+		// struct and then use it to check that target volume size can be set as needed.
+		imgVolConfig := map[string]string{
+			"volatile.rootfs.size": fmt.Sprintf("%d", imgInfo.VirtualSize),
+		}
+		imgVol := drivers.NewVolume(nil, "", drivers.VolumeTypeImage, drivers.ContentTypeBlock, "", imgVolConfig, nil)
+
+		logger.Debug("Checking image unpack size")
+		newVolSize, err := vol.ConfigSizeFromSource(imgVol)
+		if err != nil {
+			return -1, err
+		}
+
 		if shared.PathExists(dstPath) {
 			volSizeBytes, err := drivers.BlockDiskSizeBytes(dstPath)
 			if err != nil {
 				return -1, errors.Wrapf(err, "Error getting current size of %q", dstPath)
 			}
 
+			// If the target volume's size is smaller than the image unpack size, then we need to
+			// increase the target volume's size.
 			if volSizeBytes < imgInfo.VirtualSize {
-				// If the target volume's size is smaller than the image unpack size, then we need
-				// to check whether it is inline with the pool's settings to allow us to increase
-				// the target volume's size. Create a partial image volume struct and then use it
-				// to check that target volume size can be set as needed.
-				imgVolConfig := map[string]string{
-					"volatile.rootfs.size": fmt.Sprintf("%d", imgInfo.VirtualSize),
-				}
-				imgVol := drivers.NewVolume(nil, "", drivers.VolumeTypeImage, drivers.ContentTypeBlock, "", imgVolConfig, nil)
-
-				logger.Debug("Checking image unpack size")
-				newVolSize, err := vol.ConfigSizeFromSource(imgVol)
-				if err != nil {
-					return -1, err
-				}
-
 				logger.Debug("Increasing volume size", log.Ctx{"imgPath": imgPath, "dstPath": dstPath, "oldSize": volSizeBytes, "newSize": newVolSize})
 				err = vol.SetQuota(newVolSize, nil)
 				if err != nil {


More information about the lxc-devel mailing list