[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