The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6339
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === This PR comes from #6285, but to make that PR smaller I am breaking it into parts. This PR adds some generic storage driver utils which will be used by the driver implementations to come.
From 38ba4ac1342b7e5f0f7b54b74de059f4df856a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@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 <stgra...@ubuntu.com> Signed-off-by: Thomas Parrott <thomas.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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.parr...@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) }
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel