[lxc-devel] [lxd/master] Storage: Restore VM generic backups using their original volume size

tomponline on Github lxc-bot at linuxcontainers.org
Thu Apr 30 15:16:05 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 724 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200430/a31383d1/attachment.bin>
-------------- next part --------------
From 259be8515763a0a7a9fdc3dbc4d622a5bb0d9eae Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:16:13 +0100
Subject: [PATCH 01/10] lxd/instances/post: Fix revert in createFromBackup

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

diff --git a/lxd/instances_post.go b/lxd/instances_post.go
index 77fba7b94e..d056df29c2 100644
--- a/lxd/instances_post.go
+++ b/lxd/instances_post.go
@@ -663,7 +663,7 @@ func createFromBackup(d *Daemon, project string, data io.Reader, pool string) re
 		if err != nil {
 			return errors.Wrap(err, "Create instance from backup")
 		}
-		revert.Add(revertHook)
+		runRevert.Add(revertHook)
 
 		body, err := json.Marshal(&internalImportPost{
 			Name:  bInfo.Name,

From 40003d5c942a7dac91bfe8b8493d3f2fab2c2cad Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:16:31 +0100
Subject: [PATCH 02/10] lxd/storage/drivers/volume: Adds allowUnsafeResize bool
 to Volume struct

Used to indicate unsafe resizing (shrinking) is wanted by the caller and all helpers (such as GPT moving) should be skipped.

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

diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go
index a97a311389..ac98e6e324 100644
--- a/lxd/storage/drivers/volume.go
+++ b/lxd/storage/drivers/volume.go
@@ -62,15 +62,16 @@ var BaseDirectories = map[VolumeType][]string{
 
 // Volume represents a storage volume, and provides functions to mount and unmount it.
 type Volume struct {
-	name            string
-	pool            string
-	poolConfig      map[string]string
-	volType         VolumeType
-	contentType     ContentType
-	config          map[string]string
-	driver          Driver
-	keepDevice      bool
-	customMountPath string
+	name              string
+	pool              string
+	poolConfig        map[string]string
+	volType           VolumeType
+	contentType       ContentType
+	config            map[string]string
+	driver            Driver
+	keepDevice        bool
+	customMountPath   string
+	allowUnsafeResize bool // Whether to allow potentially destructive unchecked resizing of volume.
 }
 
 // NewVolume instantiates a new Volume struct.

From 9e63148f3c7c8466511858894997a56762c96579 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:17:23 +0100
Subject: [PATCH 03/10] lxd/storage/backend/lxd: Adds cannot shrink error
 handling in CreateInstanceFromBackup

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

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 574ff172d7..1ff2fbb41b 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -587,7 +587,17 @@ func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io.
 			logger.Debug("Applying volume quota from root disk config", log.Ctx{"size": rootDiskConf["size"]})
 			err = b.driver.SetVolumeQuota(vol, rootDiskConf["size"], op)
 			if err != nil {
-				return err
+				// The restored volume can end up being larger than the root disk config's size
+				// property due to the block boundary rounding some storage drivers use. As such
+				// if the restored volume is larger than the config's size and it cannot be shrunk
+				// to the equivalent size on the target storage driver, don't fail as the backup
+				// has still been restored successfully.
+				if errors.Cause(err) == drivers.ErrCannotBeShrunk {
+					logger.Warn("Could not apply volume quota from root disk config as restored volume cannot be shrunk", log.Ctx{"size": rootDiskConf["size"]})
+
+				} else {
+					return err
+				}
 			}
 		}
 

From 96456f4d7c8b4226756148a33fd31deba7518d91 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:17:59 +0100
Subject: [PATCH 04/10] lxd/storage/drivers/generic/vfs: Sets block volume size
 to file size of volume in tarball in genericVFSBackupUnpack

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

diff --git a/lxd/storage/drivers/generic_vfs.go b/lxd/storage/drivers/generic_vfs.go
index 966cbb374d..57ad06a62a 100644
--- a/lxd/storage/drivers/generic_vfs.go
+++ b/lxd/storage/drivers/generic_vfs.go
@@ -653,6 +653,17 @@ func genericVFSBackupUnpack(d Driver, vol Volume, snapshots []string, srcData io
 					}
 					defer to.Close()
 
+					// Restore original size of volume from raw block backup file size.
+					d.Logger().Debug("Setting volume size from source", log.Ctx{"source": srcFile, "target": targetPath, "size": hdr.Size})
+
+					// Allow potentially destructive resize of volume as we are going to be
+					// overwriting it entirely anyway. This allows shrinking of block volumes.
+					vol.allowUnsafeResize = true
+					err = d.SetVolumeQuota(vol, fmt.Sprintf("%d", hdr.Size), op)
+					if err != nil {
+						return err
+					}
+
 					_, err = io.Copy(to, tr)
 					if err != nil {
 						return err

From 43b1a64cf91afaed7ea21dcf6141347557c4be26 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:18:57 +0100
Subject: [PATCH 05/10] lxd/storage/drivers/driver/btrfs/volumes: No need to
 move GPT header if no filler used in CreateVolume

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

diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go
index 0092085c8f..f9748538c0 100644
--- a/lxd/storage/drivers/driver_btrfs_volumes.go
+++ b/lxd/storage/drivers/driver_btrfs_volumes.go
@@ -65,8 +65,8 @@ func (d *btrfs) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Op
 			return err
 		}
 
-		// Move the GPT alt header to end of disk if needed.
-		if vol.IsVMBlock() {
+		// Move the GPT alt header to end of disk if needed and if filler specified.
+		if vol.IsVMBlock() && filler != nil && filler.Fill != nil {
 			err = d.moveGPTAltHeader(rootBlockPath)
 			if err != nil {
 				return err

From f8e4bf5d93832678bedcfc71bbee3a81eacaa909 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:19:30 +0100
Subject: [PATCH 06/10] lxd/storage/drivers/driver/btrfs/volumes: Skip GPT
 header move in SetVolumeQuota when allowUnsafeResize is enabled

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

diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go b/lxd/storage/drivers/driver_btrfs_volumes.go
index f9748538c0..a8ca986e9e 100644
--- a/lxd/storage/drivers/driver_btrfs_volumes.go
+++ b/lxd/storage/drivers/driver_btrfs_volumes.go
@@ -461,8 +461,10 @@ func (d *btrfs) SetVolumeQuota(vol Volume, size string, op *operations.Operation
 			return err
 		}
 
-		// Move the GPT alt header to end of disk if needed and resize has taken place.
-		if vol.IsVMBlock() && resized {
+		// Move the GPT alt header to end of disk if needed and resize has taken place (not needed in
+		// unsafe resize mode as it is  expected the caller will do all necessary post resize actions
+		// themselves).
+		if vol.IsVMBlock() && resized && !vol.allowUnsafeResize {
 			err = d.moveGPTAltHeader(rootBlockPath)
 			if err != nil {
 				return err

From 58cdf30ccd7c907ba26167b055bd2c7c165d4b33 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:20:25 +0100
Subject: [PATCH 07/10] lxd/storage/drivers/driver/dir/volumes: Skip GPT header
 move in SetVolumeQuota when allowUnsafeResize is enabled

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

diff --git a/lxd/storage/drivers/driver_dir_volumes.go b/lxd/storage/drivers/driver_dir_volumes.go
index 7f3fcadb38..58b1d0b157 100644
--- a/lxd/storage/drivers/driver_dir_volumes.go
+++ b/lxd/storage/drivers/driver_dir_volumes.go
@@ -260,8 +260,10 @@ func (d *dir) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 			return err
 		}
 
-		// Move the GPT alt header to end of disk if needed and resize has taken place.
-		if vol.IsVMBlock() && resized {
+		// Move the GPT alt header to end of disk if needed and resize has taken place (not needed in
+		// unsafe resize mode as it is  expected the caller will do all necessary post resize actions
+		// themselves).
+		if vol.IsVMBlock() && resized && !vol.allowUnsafeResize {
 			err = d.moveGPTAltHeader(rootBlockPath)
 			if err != nil {
 				return err

From 0d7925abb6fb5cb9dea3281fd7d400c1074be008 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:20:48 +0100
Subject: [PATCH 08/10] lxd/storage/drivers/driver/lvm/volumes: Allow unsafe
 shrinking when allowUnsafeResize is enabled

Also skip GPT header move.

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

diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go
index 363a6672ae..62ef87fc49 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -381,7 +381,7 @@ func (d *lvm) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 			d.logger.Debug("Logical volume filesystem grown", logCtx)
 		}
 	} else {
-		if newSizeBytes < oldSizeBytes {
+		if newSizeBytes < oldSizeBytes && !vol.allowUnsafeResize {
 			return errors.Wrap(ErrCannotBeShrunk, "You cannot shrink block volumes")
 		}
 
@@ -391,8 +391,9 @@ func (d *lvm) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 
 		}
 
-		// Move the GPT alt header to end of disk if needed.
-		if vol.IsVMBlock() {
+		// Move the VM GPT alt header to end of disk if needed (not needed in unsafe resize mode as it is
+		// expected the caller will do all necessary post resize actions themselves).
+		if vol.IsVMBlock() && !vol.allowUnsafeResize {
 			err = d.moveGPTAltHeader(volDevPath)
 			if err != nil {
 				return err

From 403f65543e8983160da811cf56eb55ea2c6a74df Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:21:43 +0100
Subject: [PATCH 09/10] lxd/storage/drivers/driver/zfs/volumes: Allow unsafe
 shrinking when allowUnsafeResize is enabled

Also skip GPT header move.

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

diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index 972c63211e..8ab7c255bf 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -914,7 +914,7 @@ func (d *zfs) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 			return nil
 		}
 
-		if sizeBytes < oldVolSizeBytes {
+		if sizeBytes < oldVolSizeBytes && !vol.allowUnsafeResize {
 			return errors.Wrap(ErrCannotBeShrunk, "You cannot shrink block volumes")
 		}
 
@@ -929,8 +929,9 @@ func (d *zfs) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 				return err
 			}
 
-			// Move the GPT alt header to end of disk if needed.
-			if vol.IsVMBlock() {
+			// Move the VM GPT alt header to end of disk if needed (not needed in unsafe resize mode as
+			// it is expected the caller will do all necessary post resize actions themselves).
+			if vol.IsVMBlock() && !vol.allowUnsafeResize {
 				err = d.moveGPTAltHeader(devPath)
 				if err != nil {
 					return err

From 4601d72857b58e9cc3bd636c750008cb6a2fd58f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 30 Apr 2020 15:48:42 +0100
Subject: [PATCH 10/10] lxd/storage/drivers/driver/ceph/volumes: Allow unsafe
 shrinking when allowUnsafeResize is enabled

Also skip GPT header move.

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

diff --git a/lxd/storage/drivers/driver_ceph_volumes.go b/lxd/storage/drivers/driver_ceph_volumes.go
index 623a12b182..643b4542e7 100644
--- a/lxd/storage/drivers/driver_ceph_volumes.go
+++ b/lxd/storage/drivers/driver_ceph_volumes.go
@@ -774,50 +774,33 @@ func (d *ceph) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 	}
 
 	// Resize filesystem if needed.
-	if vol.contentType == ContentTypeFS {
-		if newSizeBytes < oldSizeBytes {
-			err = shrinkFileSystem(fsType, RBDDevPath, vol, newSizeBytes)
-			if err != nil {
-				return err
-			}
+	if newSizeBytes < oldSizeBytes {
+		if vol.contentType == ContentTypeBlock && !vol.allowUnsafeResize {
+			return errors.Wrap(ErrCannotBeShrunk, "You cannot shrink block volumes")
+		}
 
-			_, err = shared.TryRunCommand(
-				"rbd",
-				"resize",
-				"--allow-shrink",
-				"--id", d.config["ceph.user.name"],
-				"--cluster", d.config["ceph.cluster_name"],
-				"--pool", d.config["ceph.osd.pool_name"],
-				"--size", fmt.Sprintf("%dB", newSizeBytes),
-				d.getRBDVolumeName(vol, "", false, false))
-			if err != nil {
-				return err
-			}
-		} else {
-			// Grow the block device.
-			_, err = shared.TryRunCommand(
-				"rbd",
-				"resize",
-				"--id", d.config["ceph.user.name"],
-				"--cluster", d.config["ceph.cluster_name"],
-				"--pool", d.config["ceph.osd.pool_name"],
-				"--size", fmt.Sprintf("%dB", newSizeBytes),
-				d.getRBDVolumeName(vol, "", false, false))
+		// Shrink the filesystem.
+		if vol.contentType == ContentTypeFS {
+			err = shrinkFileSystem(fsType, RBDDevPath, vol, newSizeBytes)
 			if err != nil {
 				return err
 			}
+		}
 
-			// Grow the filesystem.
-			err = growFileSystem(fsType, RBDDevPath, vol)
-			if err != nil {
-				return err
-			}
+		// Shrink the block device.
+		_, err = shared.TryRunCommand(
+			"rbd",
+			"resize",
+			"--allow-shrink",
+			"--id", d.config["ceph.user.name"],
+			"--cluster", d.config["ceph.cluster_name"],
+			"--pool", d.config["ceph.osd.pool_name"],
+			"--size", fmt.Sprintf("%dB", newSizeBytes),
+			d.getRBDVolumeName(vol, "", false, false))
+		if err != nil {
+			return err
 		}
 	} else {
-		if newSizeBytes < oldSizeBytes {
-			return errors.Wrap(ErrCannotBeShrunk, "You cannot shrink block volumes")
-		}
-
 		// Grow the block device.
 		_, err = shared.TryRunCommand(
 			"rbd",
@@ -831,15 +814,24 @@ func (d *ceph) SetVolumeQuota(vol Volume, size string, op *operations.Operation)
 			return err
 		}
 
-		// Move the GPT alt header to end of disk if needed.
-		if vol.IsVMBlock() {
-			err = d.moveGPTAltHeader(RBDDevPath)
+		// Grow the filesystem.
+		if vol.contentType == ContentTypeFS {
+			err = growFileSystem(fsType, RBDDevPath, vol)
 			if err != nil {
 				return err
 			}
 		}
 	}
 
+	// Move the VM GPT alt header to end of disk if needed (not needed in unsafe resize mode as it is
+	// expected the caller will do all necessary post resize actions themselves).
+	if vol.IsVMBlock() && !vol.allowUnsafeResize {
+		err = d.moveGPTAltHeader(RBDDevPath)
+		if err != nil {
+			return err
+		}
+	}
+
 	return nil
 }
 


More information about the lxc-devel mailing list