[lxc-devel] [lxd/master] Storage: LVM snapshot parsing improvements

tomponline on Github lxc-bot at linuxcontainers.org
Mon May 11 13:43:33 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 494 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200511/34fb3b85/attachment.bin>
-------------- next part --------------
From 2517fefd67f5cbd01bad43662246a515727f6517 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 11 May 2020 14:39:59 +0100
Subject: [PATCH 1/4] lxd/storage/drivers/driver/lvm/utils: Adds
 lvmSnapshotSeparator constant and updates lvmFullVolumeName to use it

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

diff --git a/lxd/storage/drivers/driver_lvm_utils.go b/lxd/storage/drivers/driver_lvm_utils.go
index 4c6f543528..f4a46ae051 100644
--- a/lxd/storage/drivers/driver_lvm_utils.go
+++ b/lxd/storage/drivers/driver_lvm_utils.go
@@ -23,6 +23,9 @@ import (
 // lvmBlockVolSuffix suffix used for block content type svolumes.
 const lvmBlockVolSuffix = ".block"
 
+// lvmSnapshotSeparator separator character used between volume name and snaphot name in logical volume names.
+const lvmSnapshotSeparator = "-"
+
 var errLVMNotFound = fmt.Errorf("Not found")
 
 // usesThinpool indicates whether the config specifies to use a thin pool or not.
@@ -512,7 +515,7 @@ func (d *lvm) lvmFullVolumeName(volType VolumeType, contentType ContentType, vol
 	}
 
 	// Escape the volume name to a name suitable for using as a logical volume.
-	lvName := strings.Replace(strings.Replace(volName, "-", "--", -1), shared.SnapshotDelimiter, "-", -1)
+	lvName := strings.Replace(strings.Replace(volName, "-", "--", -1), shared.SnapshotDelimiter, lvmSnapshotSeparator, -1)
 
 	return fmt.Sprintf("%s_%s%s", volTypePrefix, lvName, contentTypeSuffix)
 }

From dacc3c4cb6bdd1b039a07a94c5d13d8807fda5cc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 11 May 2020 14:40:18 +0100
Subject: [PATCH 2/4] lxd/storage/drivers/driver/lvm/utils: Adds
 parseLogicalVolumeName function

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

diff --git a/lxd/storage/drivers/driver_lvm_utils.go b/lxd/storage/drivers/driver_lvm_utils.go
index f4a46ae051..0b78e94b3a 100644
--- a/lxd/storage/drivers/driver_lvm_utils.go
+++ b/lxd/storage/drivers/driver_lvm_utils.go
@@ -5,6 +5,7 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"strconv"
 	"strings"
 	"syscall"
@@ -740,3 +741,50 @@ func (d *lvm) thinPoolVolumeUsage(volDevPath string) (uint64, uint64, error) {
 
 	return totalSize, usedSize, nil
 }
+
+// parseLogicalVolumeName parses a raw logical volume name (from lvs command) and if it is recognised as one of the
+// LXD LVM formats then a partially populated Volume is returned reflecting the volume. Volume fields that are
+// populated are: pool name, volume name (including snapshot if relevant), volume type and content type.
+func (d *lvm) parseLogicalVolumeName(lvmVolName string) (Volume, error) {
+	vol := Volume{}
+
+	// Match image volumes and extract their various parts into a Volume struct.
+	// See Example_lvm_parseLogicalVolumeName() for examples.
+	reImage := regexp.MustCompile(`^(images)_([A-Za-z0-9]+)\.?(block)?$`)
+	if imageRes := reImage.FindStringSubmatch(lvmVolName); imageRes != nil {
+		vol.volType = VolumeType(imageRes[1])
+		vol.pool = d.name
+		vol.name = imageRes[2]
+
+		if imageRes[3] == "block" {
+			vol.contentType = ContentTypeBlock
+		} else {
+			vol.contentType = ContentTypeFS
+		}
+
+		return vol, nil
+	}
+
+	// Match normal instance volumes.
+	// See Example_lvm_parseLogicalVolumeName() for examples.
+	reInst := regexp.MustCompile(fmt.Sprintf(`^([a-z-]+)_((?:\w|--)+)%s?([-\w]+)?\.?(block)?$`, lvmSnapshotSeparator))
+	if instRes := reInst.FindStringSubmatch(lvmVolName); instRes != nil {
+		vol.volType = VolumeType(instRes[1])
+		vol.pool = d.name
+		vol.name = strings.Replace(instRes[2], "--", "-", -1)
+
+		if instRes[3] != "" {
+			vol.name = fmt.Sprintf("%s%s%s", vol.name, shared.SnapshotDelimiter, strings.Replace(instRes[3], "--", "-", -1))
+		}
+
+		if instRes[4] == "block" {
+			vol.contentType = ContentTypeBlock
+		} else {
+			vol.contentType = ContentTypeFS
+		}
+
+		return vol, nil
+	}
+
+	return vol, fmt.Errorf("Unrecognised logical volume name: %q", lvmVolName)
+}

From d0a2ffe8022e2082f80ed818337cccfa68fafae3 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 11 May 2020 14:40:58 +0100
Subject: [PATCH 3/4] lxd/storage/drivers/driver/lvm/volumes: Updates
 VolumeSnapshots to use parseLogicalVolumeName

Fixes #7335

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

diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go
index a001ba1561..491cbcf869 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -792,32 +792,16 @@ func (d *lvm) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, e
 	}
 
 	snapshots := []string{}
-	fullVolName := d.lvmFullVolumeName(vol.volType, vol.contentType, vol.name)
-
-	// If block volume, remove the block suffix ready for comparison with LV list.
-	if vol.IsVMBlock() {
-		fullVolName = strings.TrimSuffix(fullVolName, lvmBlockVolSuffix)
-	}
-
-	prefix := fmt.Sprintf("%s-", fullVolName)
-
 	scanner := bufio.NewScanner(stdout)
 	for scanner.Scan() {
-		snapLine := strings.TrimSpace(scanner.Text())
-
-		// If block volume, skip any LVs that don't end with the block suffix, and then for those that do
-		// remove the block suffix so that they can be compared with the fullVolName prefix.
-		if vol.IsVMBlock() {
-			if !strings.HasSuffix(snapLine, lvmBlockVolSuffix) {
-				continue // Ignore non-block volumes.
-			}
-
-			snapLine = strings.TrimSuffix(snapLine, lvmBlockVolSuffix)
+		v, err := d.parseLogicalVolumeName(strings.TrimSpace(scanner.Text()))
+		if err != nil {
+			continue // Skip unrecognised LVMs (maybe other non-LXD volumes on the host).
 		}
 
-		if strings.HasPrefix(snapLine, prefix) {
-			// Remove volume name prefix (including snapshot delimiter) and unescape snapshot name.
-			snapshots = append(snapshots, strings.Replace(strings.TrimPrefix(snapLine, prefix), "--", "-", -1))
+		parent, snapName, isSnap := shared.InstanceGetParentAndSnapshotName(v.name)
+		if parent == vol.name && isSnap {
+			snapshots = append(snapshots, snapName)
 		}
 	}
 
@@ -828,7 +812,7 @@ func (d *lvm) VolumeSnapshots(vol Volume, op *operations.Operation) ([]string, e
 
 	err = cmd.Wait()
 	if err != nil {
-		return nil, errors.Wrapf(err, "Failed to get snapshot list for volume %q: %v", fullVolName, strings.TrimSpace(string(errMsg)))
+		return nil, errors.Wrapf(err, "Failed to get snapshot list for volume %q: %v", vol.name, strings.TrimSpace(string(errMsg)))
 	}
 
 	return snapshots, nil

From 2a96754a78bffb4ea6532c5f2f8e84689037dada Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 11 May 2020 14:41:52 +0100
Subject: [PATCH 4/4] lxd/storage/drivers/driver/lvm/utils: Adds tests for
 parseLogicalVolumeName

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

diff --git a/lxd/storage/drivers/driver_lvm_utils_test.go b/lxd/storage/drivers/driver_lvm_utils_test.go
new file mode 100644
index 0000000000..56f7d4cf81
--- /dev/null
+++ b/lxd/storage/drivers/driver_lvm_utils_test.go
@@ -0,0 +1,47 @@
+package drivers
+
+import (
+	"fmt"
+)
+
+func Example_lvm_parseLogicalVolumeName() {
+	d := &lvm{}
+	d.name = "pool"
+
+	lvNames := []string{
+		"containers_testcontainer",
+		"containers_testcontainer-snap1",
+		"containers_testproject--with--hyphens_testcontainer",
+		"containers_testproject_testcontainer",
+		"containers_testproject_testcontainer-snap1",
+		"containers_testcontainer--with--hyphens",
+		"containers_testcontainer--with--hyphens-snap--with--hypens",
+		"virtual-machines_testproject_testvm--with--hyphens.block",
+		"virtual-machines_testproject_testvm--with--hyphens-snap--with--hyphens.block",
+		"custom_default_testcustvol",
+		"custom_default_testcustvol--with--hyphens",
+		"custom_default_testcustvol--with--hyphens-snap--with--hyphens",
+		"images_016b05541bc82306d971e04c53fe4a7979f99c3d87220516c798ffa3544de347.block",
+		"images_93b9eeb85479af2029203b4a56a2f1fdca6a0e1bf23cdc26b567790bf0f3f3bd",
+	}
+
+	for _, lvName := range lvNames {
+		vol, err := d.parseLogicalVolumeName(lvName)
+		fmt.Println(vol.pool, vol.volType, vol.name, vol.contentType, err)
+	}
+
+	// Output: pool containers testcontainer fs <nil>
+	// pool containers testcontainer/snap1 fs <nil>
+	// pool containers testproject-with-hyphens_testcontainer fs <nil>
+	// pool containers testproject_testcontainer fs <nil>
+	// pool containers testproject_testcontainer/snap1 fs <nil>
+	// pool containers testcontainer-with-hyphens fs <nil>
+	// pool containers testcontainer-with-hyphens/snap-with-hypens fs <nil>
+	// pool virtual-machines testproject_testvm-with-hyphens block <nil>
+	// pool virtual-machines testproject_testvm-with-hyphens/snap-with-hyphens block <nil>
+	// pool custom default_testcustvol fs <nil>
+	// pool custom default_testcustvol-with-hyphens fs <nil>
+	// pool custom default_testcustvol-with-hyphens/snap-with-hyphens fs <nil>
+	// pool images 016b05541bc82306d971e04c53fe4a7979f99c3d87220516c798ffa3544de347 block <nil>
+	// pool images 93b9eeb85479af2029203b4a56a2f1fdca6a0e1bf23cdc26b567790bf0f3f3bd fs <nil>
+}


More information about the lxc-devel mailing list