[lxc-devel] [lxd/master] Storage Driver Utils

tomponline on Github lxc-bot at linuxcontainers.org
Tue Oct 22 14:46:39 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 491 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20191022/0e75da0f/attachment.bin>
-------------- next part --------------
From 38ba4ac1342b7e5f0f7b54b74de059f4df856a59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 6 Oct 2019 22:44:13 -0400
Subject: [PATCH 1/6] lxd/storage/drivers/utils: Add common functions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/utils.go | 141 +++++++++++++++++++++++++++++++++++
 1 file changed, 141 insertions(+)
 create mode 100644 lxd/storage/drivers/utils.go

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
new file mode 100644
index 0000000000..e0cf6ce803
--- /dev/null
+++ b/lxd/storage/drivers/utils.go
@@ -0,0 +1,141 @@
+package drivers
+
+import (
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"time"
+
+	"golang.org/x/sys/unix"
+
+	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/api"
+)
+
+func wipeDirectory(path string) error {
+	// List all entries
+	entries, err := ioutil.ReadDir(path)
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil
+		}
+	}
+
+	// Individually wipe all entries
+	for _, entry := range entries {
+		entryPath := filepath.Join(path, entry.Name())
+		err := os.RemoveAll(entryPath)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func forceUnmount(path string) (bool, error) {
+	unmounted := false
+
+	for {
+		// Check if already unmounted
+		if !shared.IsMountPoint(path) {
+			return unmounted, nil
+		}
+
+		// Try a clean unmount first
+		err := unix.Unmount(path, 0)
+		if err != nil {
+			// Fallback to lazy unmounting
+			err = unix.Unmount(path, unix.MNT_DETACH)
+			if err != nil {
+				return false, err
+			}
+		}
+
+		unmounted = true
+	}
+}
+
+func sameMount(srcPath string, dstPath string) bool {
+	// Get the source vfs path information
+	var srcFsStat unix.Statfs_t
+	err := unix.Statfs(srcPath, &srcFsStat)
+	if err != nil {
+		return false
+	}
+
+	// Get the destination vfs path information
+	var dstFsStat unix.Statfs_t
+	err = unix.Statfs(dstPath, &dstFsStat)
+	if err != nil {
+		return false
+	}
+
+	// Compare statfs
+	if srcFsStat.Type != dstFsStat.Type || srcFsStat.Fsid != dstFsStat.Fsid {
+		return false
+	}
+
+	// Get the source path information
+	var srcStat unix.Stat_t
+	err = unix.Stat(srcPath, &srcStat)
+	if err != nil {
+		return false
+	}
+
+	// Get the destination path information
+	var dstStat unix.Stat_t
+	err = unix.Stat(dstPath, &dstStat)
+	if err != nil {
+		return false
+	}
+
+	// Compare inode
+	if srcStat.Ino != dstStat.Ino {
+		return false
+	}
+
+	return true
+}
+
+func tryMount(src string, dst string, fs string, flags uintptr, options string) error {
+	var err error
+
+	// Attempt 20 mounts over 10s
+	for i := 0; i < 20; i++ {
+		err = unix.Mount(src, dst, fs, flags, options)
+		if err == nil {
+			break
+		}
+
+		time.Sleep(500 * time.Millisecond)
+	}
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func vfsResources(path string) (*api.ResourcesStoragePool, error) {
+	// Get the VFS information
+	st, err := shared.Statvfs(path)
+	if err != nil {
+		return nil, err
+	}
+
+	// Fill in the struct
+	res := api.ResourcesStoragePool{}
+	res.Space.Total = st.Blocks * uint64(st.Bsize)
+	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)
+
+	// Some filesystems don't report inodes since they allocate them
+	// dynamically e.g. btrfs.
+	if st.Files > 0 {
+		res.Inodes.Total = st.Files
+		res.Inodes.Used = st.Files - st.Ffree
+	}
+
+	return &res, nil
+}

From 91b1e41b787a796db5c309f06465c61657a8b6a4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 10 Oct 2019 12:08:08 +0100
Subject: [PATCH 2/6] lxd/storage/drivers/utils: Adds GetVolumeMountPoint and
 GetPoolMountPoint functions

- Adds tests for GetVolumeMountPoint

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/storage/drivers/utils.go      | 19 ++++++++++++++++
 lxd/storage/drivers/utils_test.go | 37 +++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)
 create mode 100644 lxd/storage/drivers/utils_test.go

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index e0cf6ce803..8887643431 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -1,6 +1,7 @@
 package drivers
 
 import (
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -8,6 +9,7 @@ import (
 
 	"golang.org/x/sys/unix"
 
+	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 )
@@ -139,3 +141,20 @@ func vfsResources(path string) (*api.ResourcesStoragePool, error) {
 
 	return &res, nil
 }
+
+// GetPoolMountPoint returns the mountpoint of the given pool.
+// {LXD_DIR}/storage-pools/<pool>
+func GetPoolMountPoint(poolName string) string {
+	return shared.VarPath("storage-pools", poolName)
+}
+
+// GetVolumeMountPoint returns the mount path for a specific volume based on its pool and type and
+// whether it is a snapshot or not.
+// For VolumeTypeImage the volName is the image fingerprint.
+func GetVolumeMountPoint(poolName string, volType VolumeType, volName string) string {
+	if shared.IsSnapshot(volName) {
+		return shared.VarPath("storage-pools", poolName, fmt.Sprintf("%s-snapshots", string(volType)), project.Prefix("default", volName))
+	}
+
+	return shared.VarPath("storage-pools", poolName, string(volType), project.Prefix("default", volName))
+}
diff --git a/lxd/storage/drivers/utils_test.go b/lxd/storage/drivers/utils_test.go
new file mode 100644
index 0000000000..b2c358a501
--- /dev/null
+++ b/lxd/storage/drivers/utils_test.go
@@ -0,0 +1,37 @@
+package drivers
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// Test GetVolumeMountPoint
+func TestGetVolumeMountPoint(t *testing.T) {
+	poolName := "testpool"
+
+	// Test custom volume.
+	path := GetVolumeMountPoint(poolName, VolumeTypeCustom, "testvol")
+	expected := GetPoolMountPoint(poolName) + "/custom/testvol"
+	assert.Equal(t, expected, path)
+
+	// Test custom volume snapshot.
+	path = GetVolumeMountPoint(poolName, VolumeTypeCustom, "testvol/snap1")
+	expected = GetPoolMountPoint(poolName) + "/custom-snapshots/testvol/snap1"
+	assert.Equal(t, expected, path)
+
+	// Test image volume.
+	path = GetVolumeMountPoint(poolName, VolumeTypeImage, "fingerprint")
+	expected = GetPoolMountPoint(poolName) + "/images/fingerprint"
+	assert.Equal(t, expected, path)
+
+	// Test container volume.
+	path = GetVolumeMountPoint(poolName, VolumeTypeContainer, "testvol")
+	expected = GetPoolMountPoint(poolName) + "/containers/testvol"
+	assert.Equal(t, expected, path)
+
+	// Test virtual-machine volume.
+	path = GetVolumeMountPoint(poolName, VolumeTypeVM, "testvol")
+	expected = GetPoolMountPoint(poolName) + "/virtual-machines/testvol"
+	assert.Equal(t, expected, path)
+}

From a2a63d0b63cec218e39d2c735ed37470a5ac07c5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 15 Oct 2019 09:30:28 +0100
Subject: [PATCH 3/6] lxd/storage/drivers/utils: Adds
 DeleteParentSnapshotDirIfEmpty

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

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 8887643431..7875e20ec4 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -5,6 +5,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"strings"
 	"time"
 
 	"golang.org/x/sys/unix"
@@ -158,3 +159,30 @@ func GetVolumeMountPoint(poolName string, volType VolumeType, volName string) st
 
 	return shared.VarPath("storage-pools", poolName, string(volType), project.Prefix("default", volName))
 }
+
+// DeleteParentSnapshotDirIfEmpty removes the parent snapshot directory if it is empty.
+// It accepts the volume name of a snapshot in the form "volume/snap" and the volume path of the
+// snapshot. It will then remove the snapshots directory above "/snap" if it is empty.
+func DeleteParentSnapshotDirIfEmpty(volName string, volPath string) error {
+	_, snapName, isSnap := shared.ContainerGetParentAndSnapshotName(volName)
+	if !isSnap {
+		return fmt.Errorf("Volume is not a snapshot")
+	}
+
+	// Extract just the snapshot name from the volume name and then remove that suffix
+	// from the volume path. This will get us the parent snapshots directory we need.
+	snapshotsPath := strings.TrimSuffix(volPath, snapName)
+	isEmpty, err := shared.PathIsEmpty(snapshotsPath)
+	if err != nil {
+		return err
+	}
+
+	if isEmpty {
+		err := os.Remove(snapshotsPath)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}

From 86fda731ed974dd36e29b88260dc7bd5c77af0de Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 15 Oct 2019 12:11:57 +0100
Subject: [PATCH 4/6] lxd/storage/drivers/utils: Add GetVolumeSnapshotDir

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

diff --git a/lxd/storage/drivers/utils.go b/lxd/storage/drivers/utils.go
index 7875e20ec4..9fa811ff86 100644
--- a/lxd/storage/drivers/utils.go
+++ b/lxd/storage/drivers/utils.go
@@ -160,6 +160,15 @@ func GetVolumeMountPoint(poolName string, volType VolumeType, volName string) st
 	return shared.VarPath("storage-pools", poolName, string(volType), project.Prefix("default", volName))
 }
 
+// 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
+}
+
 // DeleteParentSnapshotDirIfEmpty removes the parent snapshot directory if it is empty.
 // It accepts the volume name of a snapshot in the form "volume/snap" and the volume path of the
 // snapshot. It will then remove the snapshots directory above "/snap" if it is empty.

From 11fd61a6ca700a94cf12a0c6a4d2af6693df01bf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Tue, 22 Oct 2019 15:43:37 +0100
Subject: [PATCH 5/6] lxd/storage/drivers/volume: Adds VolumeType and
 ContentType definitions

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

diff --git a/lxd/storage/drivers/volume.go b/lxd/storage/drivers/volume.go
new file mode 100644
index 0000000000..0bd76110cc
--- /dev/null
+++ b/lxd/storage/drivers/volume.go
@@ -0,0 +1,26 @@
+package drivers
+
+// VolumeType represents a storage volume type.
+type VolumeType string
+
+// VolumeTypeImage represents an image storage volume.
+const VolumeTypeImage = VolumeType("images")
+
+// VolumeTypeCustom represents a custom storage volume.
+const VolumeTypeCustom = VolumeType("custom")
+
+// VolumeTypeContainer represents a container storage volume.
+const VolumeTypeContainer = VolumeType("containers")
+
+// VolumeTypeVM represents a virtual-machine storage volume.
+const VolumeTypeVM = VolumeType("virtual-machines")
+
+// ContentType indicates the format of the volume.
+type ContentType string
+
+// ContentTypeFS indicates the volume will be populated with a mountabble filesystem.
+const ContentTypeFS = ContentType("fs")
+
+// ContentTypeBlock indicates the volume will be a block device and its contents and we do not
+// know which filesystem(s) (if any) are in use.
+const ContentTypeBlock = ContentType("block")

From a0668d084f7e57a60cb416fe9b02639a53bf0004 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 10 Oct 2019 12:13:50 +0100
Subject: [PATCH 6/6] lxd/storage/storage: Deprecates pool path function

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

diff --git a/lxd/storage/storage.go b/lxd/storage/storage.go
index b8d8d42b31..2f2e9aec13 100644
--- a/lxd/storage/storage.go
+++ b/lxd/storage/storage.go
@@ -18,6 +18,7 @@ func ContainerPath(name string, isSnapshot bool) string {
 
 // GetStoragePoolMountPoint returns the mountpoint of the given pool.
 // {LXD_DIR}/storage-pools/<pool>
+// Deprecated, use GetPoolMountPoint in storage/drivers package.
 func GetStoragePoolMountPoint(poolName string) string {
 	return shared.VarPath("storage-pools", poolName)
 }


More information about the lxc-devel mailing list