[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