The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6088
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) === See #5717
From 8455cc8273c0a62c3d381ae7c7c0ce1f72f40521 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Thu, 2 May 2019 15:04:28 +0200 Subject: [PATCH 1/4] lxd: Move storage cgo to storage package Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/{ => storage}/storage_cgo.go | 16 +++++++++------- lxd/storage_btrfs.go | 3 ++- lxd/storage_lvm.go | 7 ++++--- 3 files changed, 15 insertions(+), 11 deletions(-) rename lxd/{ => storage}/storage_cgo.go (93%) diff --git a/lxd/storage_cgo.go b/lxd/storage/storage_cgo.go similarity index 93% rename from lxd/storage_cgo.go rename to lxd/storage/storage_cgo.go index 7edba24f92..437e67ef1b 100644 --- a/lxd/storage_cgo.go +++ b/lxd/storage/storage_cgo.go @@ -1,7 +1,7 @@ // +build linux // +build cgo -package main +package storage /* #define _GNU_SOURCE @@ -19,8 +19,8 @@ package main #include <sys/stat.h> #include <sys/types.h> -#include "include/macro.h" -#include "include/memory_utils.h" +#include "../include/macro.h" +#include "../include/memory_utils.h" #define LXD_MAXPATH 4096 #define LXD_NUMSTRLEN64 21 @@ -256,10 +256,10 @@ import ( // close. const LoFlagsAutoclear int = C.LO_FLAGS_AUTOCLEAR -// prepareLoopDev() detects and sets up a loop device for source. It returns an +// PrepareLoopDev detects and sets up a loop device for source. It returns an // open file descriptor to the free loop device and the path of the free loop // device. It's the callers responsibility to close the open file descriptor. -func prepareLoopDev(source string, flags int) (*os.File, error) { +func PrepareLoopDev(source string, flags int) (*os.File, error) { cLoopDev := C.malloc(C.size_t(C.LO_NAME_SIZE)) if cLoopDev == nil { return nil, fmt.Errorf("Failed to allocate memory in C") @@ -285,7 +285,8 @@ func prepareLoopDev(source string, flags int) (*os.File, error) { return os.NewFile(uintptr(loopFd), C.GoString((*C.char)(cLoopDev))), nil } -func setAutoclearOnLoopDev(loopFd int) error { +// SetAutoclearOnLoopDev enables autodestruction of the provided loopback device. +func SetAutoclearOnLoopDev(loopFd int) error { ret, err := C.set_autoclear_loop_device(C.int(loopFd)) if ret < 0 { if err != nil { @@ -297,7 +298,8 @@ func setAutoclearOnLoopDev(loopFd int) error { return nil } -func unsetAutoclearOnLoopDev(loopFd int) error { +// UnsetAutoclearOnLoopDev disables autodestruction of the provided loopback device. +func UnsetAutoclearOnLoopDev(loopFd int) error { ret, err := C.unset_autoclear_loop_device(C.int(loopFd)) if ret < 0 { if err != nil { diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go index 043b2f7aad..66d541a9ab 100644 --- a/lxd/storage_btrfs.go +++ b/lxd/storage_btrfs.go @@ -19,6 +19,7 @@ import ( "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" @@ -422,7 +423,7 @@ func (s *storageBtrfs) StoragePoolMount() (bool, error) { // Since we mount the loop device LO_FLAGS_AUTOCLEAR is // fine since the loop device will be kept around for as // long as the mount exists. - loopF, loopErr := prepareLoopDev(source, LoFlagsAutoclear) + loopF, loopErr := driver.PrepareLoopDev(source, driver.LoFlagsAutoclear) if loopErr != nil { return false, loopErr } diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go index d805035435..97ece383c6 100644 --- a/lxd/storage_lvm.go +++ b/lxd/storage_lvm.go @@ -15,6 +15,7 @@ import ( "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/ioprogress" @@ -387,7 +388,7 @@ func (s *storageLvm) StoragePoolDelete() error { if s.loopInfo != nil { // Set LO_FLAGS_AUTOCLEAR before we remove the loop file // otherwise we will get EBADF. - err = setAutoclearOnLoopDev(int(s.loopInfo.Fd())) + err = driver.SetAutoclearOnLoopDev(int(s.loopInfo.Fd())) if err != nil { logger.Warnf("Failed to set LO_FLAGS_AUTOCLEAR on loop device: %s, manual cleanup needed", err) } @@ -459,12 +460,12 @@ func (s *storageLvm) StoragePoolMount() (bool, error) { if filepath.IsAbs(source) && !shared.IsBlockdevPath(source) { // Try to prepare new loop device. - loopF, loopErr := prepareLoopDev(source, 0) + loopF, loopErr := driver.PrepareLoopDev(source, 0) if loopErr != nil { return false, loopErr } // Make sure that LO_FLAGS_AUTOCLEAR is unset. - loopErr = unsetAutoclearOnLoopDev(int(loopF.Fd())) + loopErr = driver.UnsetAutoclearOnLoopDev(int(loopF.Fd())) if loopErr != nil { return false, loopErr } From 3faeabb253bcbf28b08198c7a9e4508414ea69b8 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Tue, 13 Aug 2019 15:04:10 +0200 Subject: [PATCH 2/4] lxd: Move ContainerPath() to storage package Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/api_internal.go | 10 +++++----- lxd/container.go | 8 -------- lxd/container_lxc.go | 3 ++- lxd/container_test.go | 4 ++-- lxd/storage/storage.go | 12 ++++++++++++ lxd/storage_dir.go | 3 ++- lxd/storage_zfs.go | 3 ++- 7 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 lxd/storage/storage.go diff --git a/lxd/api_internal.go b/lxd/api_internal.go index fee4d9ebfa..babac2c8d6 100644 --- a/lxd/api_internal.go +++ b/lxd/api_internal.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "runtime" + runtimeDebug "runtime/debug" "strconv" "strings" @@ -21,13 +22,12 @@ import ( "github.com/lxc/lxd/lxd/db/node" "github.com/lxc/lxd/lxd/db/query" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" + log "github.com/lxc/lxd/shared/log15" "github.com/lxc/lxd/shared/logger" "github.com/lxc/lxd/shared/osarch" - - log "github.com/lxc/lxd/shared/log15" - runtimeDebug "runtime/debug" ) var apiInternal = []APIEndpoint{ @@ -680,7 +680,7 @@ func internalImport(d *Daemon, r *http.Request) Response { onDiskPoolName = poolName } snapName := fmt.Sprintf("%s/%s", req.Name, od) - snapPath := containerPath(snapName, true) + snapPath := driver.ContainerPath(snapName, true) err = lvmContainerDeleteInternal(projectName, poolName, req.Name, true, onDiskPoolName, snapPath) case "ceph": @@ -1022,7 +1022,7 @@ func internalImport(d *Daemon, r *http.Request) Response { return SmartError(err) } - containerPath := containerPath(project.Prefix(projectName, req.Name), false) + containerPath := driver.ContainerPath(project.Prefix(projectName, req.Name), false) isPrivileged := false if backup.Container.Config["security.privileged"] == "" { isPrivileged = true diff --git a/lxd/container.go b/lxd/container.go index 5b795f3d55..f01f2bc58d 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -75,14 +75,6 @@ func containerGetParentAndSnapshotName(name string) (string, string, bool) { return fields[0], fields[1], true } -func containerPath(name string, isSnapshot bool) string { - if isSnapshot { - return shared.VarPath("snapshots", name) - } - - return shared.VarPath("containers", name) -} - func containerValidName(name string) error { if strings.Contains(name, shared.SnapshotDelimiter) { return fmt.Errorf( diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index 6a97f78242..95400bdb39 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -35,6 +35,7 @@ import ( "github.com/lxc/lxd/lxd/maas" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/lxd/template" "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" @@ -7858,7 +7859,7 @@ func (c *containerLXC) State() string { // Various container paths func (c *containerLXC) Path() string { name := project.Prefix(c.Project(), c.Name()) - return containerPath(name, c.IsSnapshot()) + return driver.ContainerPath(name, c.IsSnapshot()) } func (c *containerLXC) DevicesPath() string { diff --git a/lxd/container_test.go b/lxd/container_test.go index 4bbebde328..6b0d946f1c 100644 --- a/lxd/container_test.go +++ b/lxd/container_test.go @@ -163,7 +163,7 @@ func (suite *containerTestSuite) TestContainer_Path_Regular() { suite.Req.False(c.IsSnapshot(), "Shouldn't be a snapshot.") suite.Req.Equal(shared.VarPath("containers", "testFoo"), c.Path()) - suite.Req.Equal(shared.VarPath("containers", "testFoo2"), containerPath("testFoo2", false)) + suite.Req.Equal(shared.VarPath("containers", "testFoo2"), driver.ContainerPath("testFoo2", false)) } func (suite *containerTestSuite) TestContainer_Path_Snapshot() { @@ -184,7 +184,7 @@ func (suite *containerTestSuite) TestContainer_Path_Snapshot() { c.Path()) suite.Req.Equal( shared.VarPath("snapshots", "test", "snap1"), - containerPath("test/snap1", true)) + driver.ContainerPath("test/snap1", true)) } func (suite *containerTestSuite) TestContainer_LogPath() { diff --git a/lxd/storage/storage.go b/lxd/storage/storage.go new file mode 100644 index 0000000000..7d378ef50c --- /dev/null +++ b/lxd/storage/storage.go @@ -0,0 +1,12 @@ +package storage + +import "github.com/lxc/lxd/shared" + +// ContainerPath returns the directory of a container or snapshot. +func ContainerPath(name string, isSnapshot bool) string { + if isSnapshot { + return shared.VarPath("snapshots", name) + } + + return shared.VarPath("containers", name) +} diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go index 967a4656f1..46482a01aa 100644 --- a/lxd/storage_dir.go +++ b/lxd/storage_dir.go @@ -14,6 +14,7 @@ import ( "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/lxd/storage/quota" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" @@ -1221,7 +1222,7 @@ func (s *storageDir) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, ta // Create mountpoints containerMntPoint := getContainerMountPoint(info.Project, s.pool.Name, info.Name) - err = createContainerMountpoint(containerMntPoint, containerPath(project.Prefix(info.Project, info.Name), false), info.Privileged) + err = createContainerMountpoint(containerMntPoint, driver.ContainerPath(project.Prefix(info.Project, info.Name), false), info.Privileged) if err != nil { return errors.Wrap(err, "Create container mount point") } diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go index 5d9500b9b8..d98f530956 100644 --- a/lxd/storage_zfs.go +++ b/lxd/storage_zfs.go @@ -16,6 +16,7 @@ import ( "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" @@ -2131,7 +2132,7 @@ func (s *storageZfs) ContainerBackupCreate(backup backup, source container) erro func (s *storageZfs) doContainerBackupLoadOptimized(info backupInfo, data io.ReadSeeker, tarArgs []string) error { containerName, _, _ := containerGetParentAndSnapshotName(info.Name) containerMntPoint := getContainerMountPoint(info.Project, s.pool.Name, containerName) - err := createContainerMountpoint(containerMntPoint, containerPath(info.Name, false), info.Privileged) + err := createContainerMountpoint(containerMntPoint, driver.ContainerPath(info.Name, false), info.Privileged) if err != nil { return err } From b2405f5831ae5694f742d2f3177a6e3c55218355 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Thu, 15 Aug 2019 12:16:59 +0200 Subject: [PATCH 3/4] lxd: Move storage_utils to storage/utils Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- lxd/container_test.go | 4 +- lxd/patches.go | 18 +- lxd/storage/utils.go | 362 ++++++++++++++++++++++++++++++++ lxd/storage_btrfs.go | 36 ++-- lxd/storage_ceph.go | 31 +-- lxd/storage_ceph_utils.go | 21 +- lxd/storage_cephfs.go | 15 +- lxd/storage_dir.go | 2 +- lxd/storage_lvm.go | 34 +-- lxd/storage_lvm_utils.go | 9 +- lxd/storage_pools_utils.go | 13 +- lxd/storage_utils.go | 339 +----------------------------- lxd/storage_volumes_snapshot.go | 3 +- lxd/storage_zfs.go | 30 +-- lxd/storage_zfs_utils.go | 5 +- 15 files changed, 480 insertions(+), 442 deletions(-) create mode 100644 lxd/storage/utils.go diff --git a/lxd/container_test.go b/lxd/container_test.go index 6b0d946f1c..d6698cbd16 100644 --- a/lxd/container_test.go +++ b/lxd/container_test.go @@ -4,12 +4,14 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/suite" + "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/device/config" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/idmap" - "github.com/stretchr/testify/suite" ) type containerTestSuite struct { diff --git a/lxd/patches.go b/lxd/patches.go index f6023eab95..b876b590de 100644 --- a/lxd/patches.go +++ b/lxd/patches.go @@ -10,14 +10,16 @@ import ( "strings" "syscall" + "github.com/pkg/errors" + "golang.org/x/sys/unix" + "github.com/lxc/lxd/lxd/cluster" "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/db/query" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" log "github.com/lxc/lxd/shared/log15" "github.com/lxc/lxd/shared/logger" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) /* Patches are one-time actions that are sometimes needed to update @@ -1088,7 +1090,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d // Unmount the logical volume. oldContainerMntPoint := shared.VarPath("containers", ct) if shared.IsMountPoint(oldContainerMntPoint) { - err := tryUnmount(oldContainerMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(oldContainerMntPoint, unix.MNT_DETACH) if err != nil { logger.Errorf("Failed to unmount LVM logical volume \"%s\": %s", oldContainerMntPoint, err) return err @@ -1263,7 +1265,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d if shared.PathExists(oldLvDevPath) { // Unmount the logical volume. if shared.IsMountPoint(oldSnapshotMntPoint) { - err := tryUnmount(oldSnapshotMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(oldSnapshotMntPoint, unix.MNT_DETACH) if err != nil { logger.Errorf("Failed to unmount LVM logical volume \"%s\": %s", oldSnapshotMntPoint, err) return err @@ -1366,7 +1368,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d } if !shared.IsMountPoint(newContainerMntPoint) { - err := tryMount(containerLvDevPath, newContainerMntPoint, lvFsType, 0, mountOptions) + err := driver.TryMount(containerLvDevPath, newContainerMntPoint, lvFsType, 0, mountOptions) if err != nil { logger.Errorf("Failed to mount LVM logical \"%s\" onto \"%s\" : %s", containerLvDevPath, newContainerMntPoint, err) return err @@ -1414,7 +1416,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d // Unmount the logical volume. oldImageMntPoint := shared.VarPath("images", img+".lv") if shared.IsMountPoint(oldImageMntPoint) { - err := tryUnmount(oldImageMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(oldImageMntPoint, unix.MNT_DETACH) if err != nil { return err } @@ -1614,7 +1616,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d _, err := shared.TryRunCommand("zfs", "unmount", "-f", ctDataset) if err != nil { logger.Warnf("Failed to unmount ZFS filesystem via zfs unmount, trying lazy umount (MNT_DETACH)...") - err := tryUnmount(oldContainerMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(oldContainerMntPoint, unix.MNT_DETACH) if err != nil { failedUpgradeEntities = append(failedUpgradeEntities, fmt.Sprintf("containers/%s: Failed to umount zfs filesystem.", ct)) continue @@ -1762,7 +1764,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d _, err := shared.TryRunCommand("zfs", "unmount", "-f", imageDataset) if err != nil { logger.Warnf("Failed to unmount ZFS filesystem via zfs unmount, trying lazy umount (MNT_DETACH)...") - err := tryUnmount(oldImageMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(oldImageMntPoint, unix.MNT_DETACH) if err != nil { logger.Warnf("Failed to unmount ZFS filesystem: %s", err) } diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go new file mode 100644 index 0000000000..d23d4f4171 --- /dev/null +++ b/lxd/storage/utils.go @@ -0,0 +1,362 @@ +package storage + +import ( + "fmt" + "os" + "strings" + "time" + + "golang.org/x/sys/unix" + + "github.com/lxc/lxd/lxd/db" + "github.com/lxc/lxd/shared" + "github.com/lxc/lxd/shared/api" + "github.com/lxc/lxd/shared/logger" +) + +// MkfsOptions represents options for filesystem creation. +type MkfsOptions struct { + Label string +} + +// Export the mount options map since we might find it useful in other parts of +// LXD. +type mountOptions struct { + capture bool + flag uintptr +} + +// MountOptions represents a list of possible mount options. +var MountOptions = map[string]mountOptions{ + "async": {false, unix.MS_SYNCHRONOUS}, + "atime": {false, unix.MS_NOATIME}, + "bind": {true, unix.MS_BIND}, + "defaults": {true, 0}, + "dev": {false, unix.MS_NODEV}, + "diratime": {false, unix.MS_NODIRATIME}, + "dirsync": {true, unix.MS_DIRSYNC}, + "exec": {false, unix.MS_NOEXEC}, + "lazytime": {true, unix.MS_LAZYTIME}, + "mand": {true, unix.MS_MANDLOCK}, + "noatime": {true, unix.MS_NOATIME}, + "nodev": {true, unix.MS_NODEV}, + "nodiratime": {true, unix.MS_NODIRATIME}, + "noexec": {true, unix.MS_NOEXEC}, + "nomand": {false, unix.MS_MANDLOCK}, + "norelatime": {false, unix.MS_RELATIME}, + "nostrictatime": {false, unix.MS_STRICTATIME}, + "nosuid": {true, unix.MS_NOSUID}, + "rbind": {true, unix.MS_BIND | unix.MS_REC}, + "relatime": {true, unix.MS_RELATIME}, + "remount": {true, unix.MS_REMOUNT}, + "ro": {true, unix.MS_RDONLY}, + "rw": {false, unix.MS_RDONLY}, + "strictatime": {true, unix.MS_STRICTATIME}, + "suid": {false, unix.MS_NOSUID}, + "sync": {true, unix.MS_SYNCHRONOUS}, +} + +// LXDResolveMountoptions resolves the provided mount options. +func LXDResolveMountoptions(options string) (uintptr, string) { + mountFlags := uintptr(0) + tmp := strings.SplitN(options, ",", -1) + for i := 0; i < len(tmp); i++ { + opt := tmp[i] + do, ok := MountOptions[opt] + if !ok { + continue + } + + if do.capture { + mountFlags |= do.flag + } else { + mountFlags &= ^do.flag + } + + copy(tmp[i:], tmp[i+1:]) + tmp[len(tmp)-1] = "" + tmp = tmp[:len(tmp)-1] + i-- + } + + return mountFlags, strings.Join(tmp, ",") +} + +// TryMount tries mounting a filesystem multiple times. This is useful for unreliable backends. +func TryMount(src string, dst string, fs string, flags uintptr, options string) error { + var err error + + 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 +} + +// TryUnmount tries unmounting a filesystem multiple times. This is useful for unreliable backends. +func TryUnmount(path string, flags int) error { + var err error + + for i := 0; i < 20; i++ { + err = unix.Unmount(path, flags) + if err == nil { + break + } + + time.Sleep(500 * time.Millisecond) + } + + if err != nil { + return err + } + + return nil +} + +// ValidName validates the provided name, and returns an error if it's not a valid storage name. +func ValidName(value string) error { + if strings.Contains(value, "/") { + return fmt.Errorf("Invalid storage volume name \"%s\". Storage volumes cannot contain \"/\" in their name", value) + } + + return nil +} + +// ConfigDiff returns a diff of the provided configs. Additionally, it returns whether or not +// only user properties have been changed. +func ConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) { + changedConfig := []string{} + userOnly := true + for key := range oldConfig { + if oldConfig[key] != newConfig[key] { + if !strings.HasPrefix(key, "user.") { + userOnly = false + } + + if !shared.StringInSlice(key, changedConfig) { + changedConfig = append(changedConfig, key) + } + } + } + + for key := range newConfig { + if oldConfig[key] != newConfig[key] { + if !strings.HasPrefix(key, "user.") { + userOnly = false + } + + if !shared.StringInSlice(key, changedConfig) { + changedConfig = append(changedConfig, key) + } + } + } + + // Skip on no change + if len(changedConfig) == 0 { + return nil, false + } + + return changedConfig, userOnly +} + +// StoragePoolsDirMode represents the default permissions for the storage pool directory. +const StoragePoolsDirMode os.FileMode = 0711 + +// ContainersDirMode represents the default permissions for the containers directory. +const ContainersDirMode os.FileMode = 0711 + +// CustomDirMode represents the default permissions for the custom directory. +const CustomDirMode os.FileMode = 0711 + +// ImagesDirMode represents the default permissions for the images directory. +const ImagesDirMode os.FileMode = 0700 + +// SnapshotsDirMode represents the default permissions for the snapshots directory. +const SnapshotsDirMode os.FileMode = 0700 + +// LXDUsesPool detect whether LXD already uses the given storage pool. +func LXDUsesPool(dbObj *db.Cluster, onDiskPoolName string, driver string, onDiskProperty string) (bool, string, error) { + pools, err := dbObj.StoragePools() + if err != nil && err != db.ErrNoSuchObject { + return false, "", err + } + + for _, pool := range pools { + _, pl, err := dbObj.StoragePoolGet(pool) + if err != nil { + continue + } + + if pl.Driver != driver { + continue + } + + if pl.Config[onDiskProperty] == onDiskPoolName { + return true, pl.Name, nil + } + } + + return false, "", nil +} + +// MakeFSType creates the provided filesystem. +func MakeFSType(path string, fsType string, options *MkfsOptions) (string, error) { + var err error + var msg string + + fsOptions := options + if fsOptions == nil { + fsOptions = &MkfsOptions{} + } + + cmd := []string{fmt.Sprintf("mkfs.%s", fsType), path} + if fsOptions.Label != "" { + cmd = append(cmd, "-L", fsOptions.Label) + } + + if fsType == "ext4" { + cmd = append(cmd, "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0") + } + + msg, err = shared.TryRunCommand(cmd[0], cmd[1:]...) + if err != nil { + return msg, err + } + + return "", nil +} + +// FSGenerateNewUUID generates a UUID for the given path for btrfs and xfs filesystems. +func FSGenerateNewUUID(fstype string, lvpath string) (string, error) { + switch fstype { + case "btrfs": + return btrfsGenerateNewUUID(lvpath) + case "xfs": + return xfsGenerateNewUUID(lvpath) + } + + return "", nil +} + +func xfsGenerateNewUUID(devPath string) (string, error) { + // Attempt to generate a new UUID + msg, err := shared.RunCommand("xfs_admin", "-U", "generate", devPath) + if err != nil { + return msg, err + } + + if msg != "" { + // Exit 0 with a msg usually means some log entry getting in the way + msg, err = shared.RunCommand("xfs_repair", "-o", "force_geometry", "-L", devPath) + if err != nil { + return msg, err + } + + // Attempt to generate a new UUID again + msg, err = shared.RunCommand("xfs_admin", "-U", "generate", devPath) + if err != nil { + return msg, err + } + } + + return msg, nil +} + +func btrfsGenerateNewUUID(lvpath string) (string, error) { + msg, err := shared.RunCommand( + "btrfstune", + "-f", + "-u", + lvpath) + if err != nil { + return msg, err + } + + return msg, nil +} + +// GrowFileSystem grows a filesystem if it is supported. +func GrowFileSystem(fsType string, devPath string, mntpoint string) error { + var msg string + var err error + switch fsType { + case "": // if not specified, default to ext4 + fallthrough + case "ext4": + msg, err = shared.TryRunCommand("resize2fs", devPath) + case "xfs": + msg, err = shared.TryRunCommand("xfs_growfs", devPath) + case "btrfs": + msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mntpoint) + default: + return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType) + } + + if err != nil { + errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg) + logger.Errorf(errorMsg) + return fmt.Errorf(errorMsg) + } + + logger.Debugf(`extended underlying %s filesystem for "%s"`, fsType, devPath) + return nil +} + +// ShrinkFileSystem shrinks a filesystem if it is supported. +func ShrinkFileSystem(fsType string, devPath string, mntpoint string, byteSize int64) error { + strSize := fmt.Sprintf("%dK", byteSize/1024) + + switch fsType { + case "": // if not specified, default to ext4 + fallthrough + case "ext4": + _, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath) + if err != nil { + return err + } + + _, err = shared.TryRunCommand("resize2fs", devPath, strSize) + if err != nil { + return err + } + case "btrfs": + _, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, mntpoint) + if err != nil { + return err + } + default: + return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType) + } + + return nil +} + +// GetStorageResource returns the available resources of a given path. +func GetStorageResource(path string) (*api.ResourcesStoragePool, error) { + st, err := shared.Statvfs(path) + if err != nil { + return nil, err + } + + 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 +} diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go index 66d541a9ab..5be6c321a4 100644 --- a/lxd/storage_btrfs.go +++ b/lxd/storage_btrfs.go @@ -157,7 +157,7 @@ func (s *storageBtrfs) StoragePoolCreate() error { return fmt.Errorf("Failed to create sparse file %s: %s", source, err) } - output, err := makeFSType(source, "btrfs", &mkfsOptions{label: s.pool.Name}) + output, err := driver.MakeFSType(source, "btrfs", &driver.MkfsOptions{Label: s.pool.Name}) if err != nil { return fmt.Errorf("Failed to create the BTRFS pool: %s", output) } @@ -168,7 +168,7 @@ func (s *storageBtrfs) StoragePoolCreate() error { if filepath.IsAbs(source) { isBlockDev = shared.IsBlockdevPath(source) if isBlockDev { - output, err := makeFSType(source, "btrfs", &mkfsOptions{label: s.pool.Name}) + output, err := driver.MakeFSType(source, "btrfs", &driver.MkfsOptions{Label: s.pool.Name}) if err != nil { return fmt.Errorf("Failed to create the BTRFS pool: %s", output) } @@ -208,7 +208,7 @@ func (s *storageBtrfs) StoragePoolCreate() error { poolMntPoint := getStoragePoolMountPoint(s.pool.Name) if !shared.PathExists(poolMntPoint) { - err := os.MkdirAll(poolMntPoint, storagePoolsDirMode) + err := os.MkdirAll(poolMntPoint, driver.StoragePoolsDirMode) if err != nil { return err } @@ -216,7 +216,7 @@ func (s *storageBtrfs) StoragePoolCreate() error { var err1 error var devUUID string - mountFlags, mountOptions := lxdResolveMountoptions(s.getBtrfsMountOptions()) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getBtrfsMountOptions()) mountFlags |= s.remount if isBlockDev && filepath.IsAbs(source) { devUUID, _ = shared.LookupUUIDByBlockDevPath(source) @@ -399,7 +399,7 @@ func (s *storageBtrfs) StoragePoolMount() (bool, error) { // Check whether the mount poolMntPoint exits. if !shared.PathExists(poolMntPoint) { - err := os.MkdirAll(poolMntPoint, storagePoolsDirMode) + err := os.MkdirAll(poolMntPoint, driver.StoragePoolsDirMode) if err != nil { return false, err } @@ -409,7 +409,7 @@ func (s *storageBtrfs) StoragePoolMount() (bool, error) { return false, nil } - mountFlags, mountOptions := lxdResolveMountoptions(s.getBtrfsMountOptions()) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getBtrfsMountOptions()) mountSource := source isBlockDev := shared.IsBlockdevPath(source) if filepath.IsAbs(source) { @@ -821,7 +821,7 @@ func (s *storageBtrfs) doContainerCreate(projectName, name string, privileged bo // doesn't already. containerSubvolumePath := s.getContainerSubvolumePath(s.pool.Name) if !shared.PathExists(containerSubvolumePath) { - err := os.MkdirAll(containerSubvolumePath, containersDirMode) + err := os.MkdirAll(containerSubvolumePath, driver.ContainersDirMode) if err != nil { return err } @@ -876,7 +876,7 @@ func (s *storageBtrfs) ContainerCreateFromImage(container container, fingerprint // doesn't already. containerSubvolumePath := s.getContainerSubvolumePath(s.pool.Name) if !shared.PathExists(containerSubvolumePath) { - err := os.MkdirAll(containerSubvolumePath, containersDirMode) + err := os.MkdirAll(containerSubvolumePath, driver.ContainersDirMode) if err != nil { return errors.Wrap(err, "Failed to create volume directory") } @@ -995,7 +995,7 @@ func (s *storageBtrfs) copyContainer(target container, source container) error { containersPath := getContainerMountPoint("default", s.pool.Name, "") // Ensure that the directories immediately preceding the subvolume directory exist. if !shared.PathExists(containersPath) { - err := os.MkdirAll(containersPath, containersDirMode) + err := os.MkdirAll(containersPath, driver.ContainersDirMode) if err != nil { return err } @@ -1036,7 +1036,7 @@ func (s *storageBtrfs) copySnapshot(target container, source container) error { // Ensure that the directories immediately preceding the subvolume directory exist. if !shared.PathExists(containersPath) { - err := os.MkdirAll(containersPath, containersDirMode) + err := os.MkdirAll(containersPath, driver.ContainersDirMode) if err != nil { return err } @@ -1379,7 +1379,7 @@ func (s *storageBtrfs) doContainerSnapshotCreate(projectName string, targetName // doesn't already. snapshotSubvolumePath := getSnapshotSubvolumePath(projectName, s.pool.Name, sourceName) if !shared.PathExists(snapshotSubvolumePath) { - err := os.MkdirAll(snapshotSubvolumePath, containersDirMode) + err := os.MkdirAll(snapshotSubvolumePath, driver.ContainersDirMode) if err != nil { return err } @@ -1389,7 +1389,7 @@ func (s *storageBtrfs) doContainerSnapshotCreate(projectName string, targetName snapshotMntPointSymlink := shared.VarPath("snapshots", project.Prefix(projectName, sourceName)) if !shared.PathExists(snapshotMntPointSymlink) { if !shared.PathExists(snapshotMntPointSymlinkTarget) { - err = os.MkdirAll(snapshotMntPointSymlinkTarget, snapshotsDirMode) + err = os.MkdirAll(snapshotMntPointSymlinkTarget, driver.SnapshotsDirMode) if err != nil { return err } @@ -1567,7 +1567,7 @@ func (s *storageBtrfs) ContainerSnapshotCreateEmpty(snapshotContainer container) snapshotSubvolumePath := getSnapshotSubvolumePath(snapshotContainer.Project(), s.pool.Name, sourceName) snapshotSubvolumeName := getSnapshotMountPoint(snapshotContainer.Project(), s.pool.Name, snapshotContainer.Name()) if !shared.PathExists(snapshotSubvolumePath) { - err := os.MkdirAll(snapshotSubvolumePath, containersDirMode) + err := os.MkdirAll(snapshotSubvolumePath, driver.ContainersDirMode) if err != nil { return err } @@ -2000,7 +2000,7 @@ func (s *storageBtrfs) ImageCreate(fingerprint string, tracker *ioprogress.Progr // doesn't already. imageSubvolumePath := s.getImageSubvolumePath(s.pool.Name) if !shared.PathExists(imageSubvolumePath) { - err := os.MkdirAll(imageSubvolumePath, imagesDirMode) + err := os.MkdirAll(imageSubvolumePath, driver.ImagesDirMode) if err != nil { return err } @@ -2723,7 +2723,7 @@ func (s *storageBtrfs) MigrationSink(conn *websocket.Conn, op *operation, args M _, containerPool, _ := args.Container.Storage().GetContainerPoolInfo() containersPath := getSnapshotMountPoint(args.Container.Project(), containerPool, containerName) if !args.ContainerOnly && len(args.Snapshots) > 0 { - err := os.MkdirAll(containersPath, containersDirMode) + err := os.MkdirAll(containersPath, driver.ContainersDirMode) if err != nil { return err } @@ -2961,7 +2961,7 @@ func (s *storageBtrfs) StoragePoolResources() (*api.ResourcesStoragePool, error) // Inode allocation is dynamic so no use in reporting them. - return storageResource(poolMntPoint) + return driver.GetStorageResource(poolMntPoint) } func (s *storageBtrfs) StoragePoolVolumeCopy(source *api.StorageVolumeSource) error { @@ -3037,7 +3037,7 @@ func (s *storageBtrfs) copyVolume(sourcePool string, sourceName string, targetNa } if !shared.PathExists(customDir) { - err := os.MkdirAll(customDir, customDirMode) + err := os.MkdirAll(customDir, driver.CustomDirMode) if err != nil { logger.Errorf("Failed to create directory \"%s\" for storage volume \"%s\" on storage pool \"%s\": %s", customDir, s.volume.Name, s.pool.Name, err) return err @@ -3163,7 +3163,7 @@ func (s *storageBtrfs) doVolumeSnapshotCreate(sourcePool string, sourceName stri customSnapshotSubvolumeName := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, s.volume.Name) - err = os.MkdirAll(customSnapshotSubvolumeName, snapshotsDirMode) + err = os.MkdirAll(customSnapshotSubvolumeName, driver.SnapshotsDirMode) if err != nil && !os.IsNotExist(err) { return err } diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go index e603190047..76fff33607 100644 --- a/lxd/storage_ceph.go +++ b/lxd/storage_ceph.go @@ -15,6 +15,7 @@ import ( "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/ioprogress" @@ -358,7 +359,7 @@ func (s *storageCeph) StoragePoolVolumeCreate() error { RBDFilesystem := s.getRBDFilesystem() logger.Debugf(`Retrieved filesystem type "%s" of RBD storage volume "%s" on storage pool "%s"`, RBDFilesystem, s.volume.Name, s.pool.Name) - msg, err := makeFSType(RBDDevPath, RBDFilesystem, nil) + msg, err := driver.MakeFSType(RBDDevPath, RBDFilesystem, nil) if err != nil { logger.Errorf(`Failed to create filesystem type "%s" on device path "%s" for RBD storage volume "%s" on storage pool "%s": %s`, RBDFilesystem, RBDDevPath, s.volume.Name, s.pool.Name, msg) return err @@ -424,7 +425,7 @@ func (s *storageCeph) StoragePoolVolumeDelete() error { volumeMntPoint := getStoragePoolVolumeMountPoint(s.pool.Name, s.volume.Name) if shared.IsMountPoint(volumeMntPoint) { - err := tryUnmount(volumeMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(volumeMntPoint, unix.MNT_DETACH) if err != nil { logger.Errorf(`Failed to unmount RBD storage volume "%s" on storage pool "%s": %s`, s.volume.Name, s.pool.Name, err) } @@ -508,8 +509,8 @@ func (s *storageCeph) StoragePoolVolumeMount() (bool, error) { RBDDevPath, ret = getRBDMappedDevPath(s.ClusterName, s.OSDPoolName, storagePoolVolumeTypeNameCustom, s.volume.Name, true, s.UserName) - mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions()) - customerr = tryMount( + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions()) + customerr = driver.TryMount( RBDDevPath, volumeMntPoint, RBDFilesystem, @@ -560,7 +561,7 @@ func (s *storageCeph) StoragePoolVolumeUmount() (bool, error) { var customerr error ourUmount := false if shared.IsMountPoint(volumeMntPoint) { - customerr = tryUnmount(volumeMntPoint, unix.MNT_DETACH) + customerr = driver.TryUnmount(volumeMntPoint, unix.MNT_DETACH) ourUmount = true } @@ -1366,7 +1367,7 @@ func (s *storageCeph) ContainerUmount(c container, path string) (bool, error) { var mounterr error ourUmount := false if shared.IsMountPoint(containerMntPoint) { - mounterr = tryUnmount(containerMntPoint, 0) + mounterr = driver.TryUnmount(containerMntPoint, 0) ourUmount = true } @@ -1804,7 +1805,7 @@ func (s *storageCeph) ContainerSnapshotStart(c container) (bool, error) { containerMntPoint := getSnapshotMountPoint(c.Project(), s.pool.Name, containerName) RBDFilesystem := s.getRBDFilesystem() - mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions()) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions()) if RBDFilesystem == "xfs" { idx := strings.Index(mountOptions, "nouuid") if idx < 0 { @@ -1812,7 +1813,7 @@ func (s *storageCeph) ContainerSnapshotStart(c container) (bool, error) { } } - err = tryMount( + err = driver.TryMount( RBDDevPath, containerMntPoint, RBDFilesystem, @@ -1845,7 +1846,7 @@ func (s *storageCeph) ContainerSnapshotStop(c container) (bool, error) { } // Unmount - err := tryUnmount(containerMntPoint, unix.MNT_DETACH) + err := driver.TryUnmount(containerMntPoint, unix.MNT_DETACH) if err != nil { logger.Errorf("Failed to unmount %s: %s", containerMntPoint, err) return false, err @@ -2103,7 +2104,7 @@ func (s *storageCeph) ImageCreate(fingerprint string, tracker *ioprogress.Progre // get filesystem RBDFilesystem := s.getRBDFilesystem() - msg, err := makeFSType(RBDDevPath, RBDFilesystem, nil) + msg, err := driver.MakeFSType(RBDDevPath, RBDFilesystem, nil) if err != nil { logger.Errorf(`Failed to create filesystem "%s" for RBD storage volume for image "%s" on storage pool "%s": %s`, RBDFilesystem, fingerprint, s.pool.Name, msg) @@ -2339,7 +2340,7 @@ func (s *storageCeph) ImageMount(fingerprint string) (bool, error) { RBDFilesystem := s.getRBDFilesystem() RBDMountOptions := s.getRBDMountOptions() - mountFlags, mountOptions := lxdResolveMountoptions(RBDMountOptions) + mountFlags, mountOptions := driver.LXDResolveMountoptions(RBDMountOptions) RBDDevPath, ret := getRBDMappedDevPath(s.ClusterName, s.OSDPoolName, storagePoolVolumeTypeNameImage, fingerprint, true, s.UserName) errMsg := fmt.Sprintf("Failed to mount RBD device %s onto %s", @@ -2349,7 +2350,7 @@ func (s *storageCeph) ImageMount(fingerprint string) (bool, error) { return false, fmt.Errorf(errMsg) } - err := tryMount(RBDDevPath, imageMntPoint, RBDFilesystem, mountFlags, mountOptions) + err := driver.TryMount(RBDDevPath, imageMntPoint, RBDFilesystem, mountFlags, mountOptions) if err != nil || ret < 0 { return false, err } @@ -2366,7 +2367,7 @@ func (s *storageCeph) ImageUmount(fingerprint string) (bool, error) { return false, nil } - err := tryUnmount(imageMntPoint, 0) + err := driver.TryUnmount(imageMntPoint, 0) if err != nil { return false, err } @@ -2633,7 +2634,7 @@ func (s *storageCeph) StoragePoolVolumeCopy(source *api.StorageVolumeSource) err // create snapshot mountpoint newTargetName := fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName) targetPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, newTargetName) - err = os.MkdirAll(targetPath, snapshotsDirMode) + err = os.MkdirAll(targetPath, driver.SnapshotsDirMode) if err != nil { logger.Errorf("Failed to create mountpoint \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s", targetPath, s.volume.Name, s.pool.Name, err) return err @@ -2718,7 +2719,7 @@ func (s *storageCeph) StoragePoolVolumeSnapshotCreate(target *api.StorageVolumeS } targetPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, target.Name) - err = os.MkdirAll(targetPath, snapshotsDirMode) + err = os.MkdirAll(targetPath, driver.SnapshotsDirMode) if err != nil { logger.Errorf("Failed to create mountpoint \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s", targetPath, s.volume.Name, s.pool.Name, err) return err diff --git a/lxd/storage_ceph_utils.go b/lxd/storage_ceph_utils.go index 9c458ad929..dacfec3b30 100644 --- a/lxd/storage_ceph_utils.go +++ b/lxd/storage_ceph_utils.go @@ -16,6 +16,7 @@ import ( "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/logger" @@ -705,7 +706,7 @@ func (s *storageCeph) getRBDFilesystem() string { // getRBDMountOptions returns the mount options the storage volume is supposed // to be mounted with // The option string that is returned needs to be passed to the approriate -// helper (currently named "lxdResolveMountoptions") which will take on the job +// helper (currently named "LXDResolveMountoptions") which will take on the job // of splitting it into appropriate flags and string options. func (s *storageCeph) getRBDMountOptions() string { if s.volume.Config["block.mount_options"] != "" { @@ -1556,7 +1557,7 @@ func (s *storageCeph) rbdGrow(path string, size int64, fsType string, } // Grow the filesystem - return growFileSystem(fsType, path, fsMntPoint) + return driver.GrowFileSystem(fsType, path, fsMntPoint) } // copyWithSnapshots creates a non-sparse copy of a container including its @@ -1647,7 +1648,7 @@ func (s *storageCeph) cephRBDVolumeBackupCreate(tmpPath string, backup backup, s // Generate a new UUID if needed RBDFilesystem := s.getRBDFilesystem() - msg, err := fsGenerateNewUUID(RBDFilesystem, RBDDevPath) + msg, err := driver.FSGenerateNewUUID(RBDFilesystem, RBDDevPath) if err != nil { logger.Errorf("Failed to create new UUID for filesystem \"%s\": %s: %s", RBDFilesystem, msg, err) return err @@ -1666,14 +1667,14 @@ func (s *storageCeph) cephRBDVolumeBackupCreate(tmpPath string, backup backup, s } // Mount the volume - mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions()) - err = tryMount(RBDDevPath, tmpContainerMntPoint, RBDFilesystem, mountFlags, mountOptions) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions()) + err = driver.TryMount(RBDDevPath, tmpContainerMntPoint, RBDFilesystem, mountFlags, mountOptions) if err != nil { logger.Errorf("Failed to mount RBD device %s onto %s: %s", RBDDevPath, tmpContainerMntPoint, err) return err } logger.Debugf("Mounted RBD device %s onto %s", RBDDevPath, tmpContainerMntPoint) - defer tryUnmount(tmpContainerMntPoint, unix.MNT_DETACH) + defer driver.TryUnmount(tmpContainerMntPoint, unix.MNT_DETACH) // Figure out the target name targetName := sourceContainerName @@ -1753,7 +1754,7 @@ func (s *storageCeph) doContainerCreate(projectName, name string, privileged boo // get filesystem RBDFilesystem := s.getRBDFilesystem() - msg, err := makeFSType(RBDDevPath, RBDFilesystem, nil) + msg, err := driver.MakeFSType(RBDDevPath, RBDFilesystem, nil) if err != nil { logger.Errorf(`Failed to create filesystem type "%s" on device path "%s" for RBD storage volume for container "%s" on storage pool "%s": %s`, RBDFilesystem, RBDDevPath, name, s.pool.Name, msg) return err @@ -1820,8 +1821,8 @@ func (s *storageCeph) doContainerMount(projectName string, name string) (bool, e s.OSDPoolName, storagePoolVolumeTypeNameContainer, volumeName, true, s.UserName) if ret >= 0 { - mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions()) - mounterr = tryMount(RBDDevPath, containerMntPoint, + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions()) + mounterr = driver.TryMount(RBDDevPath, containerMntPoint, RBDFilesystem, mountFlags, mountOptions) ourMount = true } @@ -2060,7 +2061,7 @@ func (s *storageCeph) cephRBDGenerateUUID(volumeName string, volumeType string) defer cephRBDVolumeUnmap(s.ClusterName, s.OSDPoolName, volumeName, volumeType, s.UserName, true) // Update the UUID - msg, err := fsGenerateNewUUID(s.getRBDFilesystem(), RBDDevPath) + msg, err := driver.FSGenerateNewUUID(s.getRBDFilesystem(), RBDDevPath) if err != nil { return fmt.Errorf("Failed to regenerate UUID for '%v': %v: %v", volumeName, err, msg) } diff --git a/lxd/storage_cephfs.go b/lxd/storage_cephfs.go index 7f4d5f86ec..58651befec 100644 --- a/lxd/storage_cephfs.go +++ b/lxd/storage_cephfs.go @@ -15,6 +15,7 @@ import ( "github.com/lxc/lxd/lxd/migration" "github.com/lxc/lxd/lxd/state" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/ioprogress" @@ -151,13 +152,13 @@ func (s *storageCephFs) StoragePoolCreate() error { connected := false for _, monAddress := range monAddresses { uri := fmt.Sprintf("%s:6789:/", monAddress) - err = tryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName)) + err = driver.TryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName)) if err != nil { continue } connected = true - defer tryUnmount(mountPoint, syscall.MNT_DETACH) + defer driver.TryUnmount(mountPoint, syscall.MNT_DETACH) break } @@ -227,13 +228,13 @@ func (s *storageCephFs) StoragePoolDelete() error { connected := false for _, monAddress := range monAddresses { uri := fmt.Sprintf("%s:6789:/", monAddress) - err = tryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName)) + err = driver.TryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName)) if err != nil { continue } connected = true - defer tryUnmount(mountPoint, syscall.MNT_DETACH) + defer driver.TryUnmount(mountPoint, syscall.MNT_DETACH) break } @@ -340,7 +341,7 @@ func (s *storageCephFs) StoragePoolMount() (bool, error) { connected := false for _, monAddress := range monAddresses { uri := fmt.Sprintf("%s:6789:/%s", monAddress, fsPath) - err = tryMount(uri, poolMntPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, secret, fsName)) + err = driver.TryMount(uri, poolMntPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, secret, fsName)) if err != nil { continue } @@ -395,7 +396,7 @@ func (s *storageCephFs) StoragePoolUmount() (bool, error) { } // Unmount - err := tryUnmount(poolMntPoint, syscall.MNT_DETACH) + err := driver.TryUnmount(poolMntPoint, syscall.MNT_DETACH) if err != nil { return false, err } @@ -756,7 +757,7 @@ func (s *storageCephFs) StoragePoolResources() (*api.ResourcesStoragePool, error } poolMntPoint := getStoragePoolMountPoint(s.pool.Name) - return storageResource(poolMntPoint) + return driver.GetStorageResource(poolMntPoint) } func (s *storageCephFs) StoragePoolVolumeCopy(source *api.StorageVolumeSource) error { diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go index 46482a01aa..edd26d8686 100644 --- a/lxd/storage_dir.go +++ b/lxd/storage_dir.go @@ -1386,7 +1386,7 @@ func (s *storageDir) StoragePoolResources() (*api.ResourcesStoragePool, error) { poolMntPoint := getStoragePoolMountPoint(s.pool.Name) - return storageResource(poolMntPoint) + return driver.GetStorageResource(poolMntPoint) } func (s *storageDir) StoragePoolVolumeCopy(source *api.StorageVolumeSource) error { diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go index 97ece383c6..c168697b33 100644 --- a/lxd/storage_lvm.go +++ b/lxd/storage_lvm.go @@ -302,7 +302,7 @@ func (s *storageLvm) StoragePoolCreate() error { } // Check that we don't already use this volume group. - inUse, user, err := lxdUsesPool(s.s.Cluster, poolName, s.pool.Driver, "lvm.vg_name") + inUse, user, err := driver.LXDUsesPool(s.s.Cluster, poolName, s.pool.Driver, "lvm.vg_name") if err != nil { return err } @@ -619,8 +619,8 @@ func (s *storageLvm) StoragePoolVolumeMount() (bool, error) { var customerr error ourMount := false if !shared.IsMountPoint(customPoolVolumeMntPoint) { - mountFlags, mountOptions := lxdResolveMountoptions(s.getLvmMountOptions()) - customerr = tryMount(lvmVolumePath, customPoolVolumeMntPoint, lvFsType, mountFlags, mountOptions) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getLvmMountOptions()) + customerr = driver.TryMount(lvmVolumePath, customPoolVolumeMntPoint, lvFsType, mountFlags, mountOptions) ourMount = true } @@ -662,7 +662,7 @@ func (s *storageLvm) StoragePoolVolumeUmount() (bool, error) { var customerr error ourUmount := false if shared.IsMountPoint(customPoolVolumeMntPoint) { - customerr = tryUnmount(customPoolVolumeMntPoint, 0) + customerr = driver.TryUnmount(customPoolVolumeMntPoint, 0) ourUmount = true } @@ -1028,7 +1028,7 @@ func (s *storageLvm) ContainerCreateFromImage(container container, fingerprint s containerLvDevPath := getLvmDevPath(container.Project(), poolName, storagePoolVolumeAPIEndpointContainers, containerLvmName) // Generate a new xfs's UUID lvFsType := s.getLvmFilesystem() - msg, err := fsGenerateNewUUID(lvFsType, containerLvDevPath) + msg, err := driver.FSGenerateNewUUID(lvFsType, containerLvDevPath) if err != nil { logger.Errorf("Failed to create new \"%s\" UUID for container \"%s\" on storage pool \"%s\": %s", lvFsType, containerName, s.pool.Name, msg) return err @@ -1073,7 +1073,7 @@ func lvmContainerDeleteInternal(projectName, poolName string, ctName string, isS } if shared.IsMountPoint(containerMntPoint) { - err := tryUnmount(containerMntPoint, 0) + err := driver.TryUnmount(containerMntPoint, 0) if err != nil { return fmt.Errorf(`Failed to unmount container path `+ `"%s": %s`, containerMntPoint, err) @@ -1265,7 +1265,7 @@ func (s *storageLvm) doContainerMount(project, name string, snap bool) (bool, er var mounterr error ourMount := false if !shared.IsMountPoint(containerMntPoint) { - mountFlags, mountOptions := lxdResolveMountoptions(s.getLvmMountOptions()) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getLvmMountOptions()) if snap && lvFsType == "xfs" { idx := strings.Index(mountOptions, "nouuid") if idx < 0 { @@ -1273,7 +1273,7 @@ func (s *storageLvm) doContainerMount(project, name string, snap bool) (bool, er } } - mounterr = tryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions) + mounterr = driver.TryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions) ourMount = true } @@ -1321,7 +1321,7 @@ func (s *storageLvm) umount(project, name string, path string) (bool, error) { var imgerr error ourUmount := false if shared.IsMountPoint(containerMntPoint) { - imgerr = tryUnmount(containerMntPoint, 0) + imgerr = driver.TryUnmount(containerMntPoint, 0) ourUmount = true } @@ -1585,7 +1585,7 @@ func (s *storageLvm) ContainerSnapshotStart(container container) (bool, error) { containerMntPoint := getSnapshotMountPoint(container.Project(), s.pool.Name, containerName) if !shared.IsMountPoint(containerMntPoint) { mntOptString := s.getLvmMountOptions() - mountFlags, mountOptions := lxdResolveMountoptions(mntOptString) + mountFlags, mountOptions := driver.LXDResolveMountoptions(mntOptString) if lvFsType == "xfs" { idx := strings.Index(mountOptions, "nouuid") @@ -1594,7 +1594,7 @@ func (s *storageLvm) ContainerSnapshotStart(container container) (bool, error) { } } - err = tryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions) + err = driver.TryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions) if err != nil { logger.Errorf(`Failed to mount LVM snapshot "%s" with filesystem "%s" options "%s" onto "%s": %s`, s.volume.Name, lvFsType, mntOptString, containerMntPoint, err) return false, err @@ -1619,7 +1619,7 @@ func (s *storageLvm) ContainerSnapshotStop(container container) (bool, error) { poolName := s.getOnDiskPoolName() if shared.IsMountPoint(snapshotMntPoint) { - err := tryUnmount(snapshotMntPoint, 0) + err := driver.TryUnmount(snapshotMntPoint, 0) if err != nil { return false, err } @@ -2028,8 +2028,8 @@ func (s *storageLvm) ImageMount(fingerprint string) (bool, error) { poolName := s.getOnDiskPoolName() lvmVolumePath := getLvmDevPath("default", poolName, storagePoolVolumeAPIEndpointImages, fingerprint) - mountFlags, mountOptions := lxdResolveMountoptions(s.getLvmMountOptions()) - err := tryMount(lvmVolumePath, imageMntPoint, lvmFstype, mountFlags, mountOptions) + mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getLvmMountOptions()) + err := driver.TryMount(lvmVolumePath, imageMntPoint, lvmFstype, mountFlags, mountOptions) if err != nil { logger.Errorf(fmt.Sprintf("Error mounting image LV for unpacking: %s", err)) return false, fmt.Errorf("Error mounting image LV: %v", err) @@ -2047,7 +2047,7 @@ func (s *storageLvm) ImageUmount(fingerprint string) (bool, error) { return false, nil } - err := tryUnmount(imageMntPoint, 0) + err := driver.TryUnmount(imageMntPoint, 0) if err != nil { return false, err } @@ -2287,7 +2287,7 @@ func (s *storageLvm) StoragePoolVolumeSnapshotCreate(target *api.StorageVolumeSn } targetPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, target.Name) - err = os.MkdirAll(targetPath, snapshotsDirMode) + err = os.MkdirAll(targetPath, driver.SnapshotsDirMode) if err != nil { logger.Errorf("Failed to create mountpoint \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s", targetPath, s.volume.Name, s.pool.Name, err) return err @@ -2303,7 +2303,7 @@ func (s *storageLvm) StoragePoolVolumeSnapshotDelete() error { snapshotLVName := containerNameToLVName(s.volume.Name) storageVolumeSnapshotPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, s.volume.Name) if shared.IsMountPoint(storageVolumeSnapshotPath) { - err := tryUnmount(storageVolumeSnapshotPath, 0) + err := driver.TryUnmount(storageVolumeSnapshotPath, 0) if err != nil { return fmt.Errorf("Failed to unmount snapshot path \"%s\": %s", storageVolumeSnapshotPath, err) } diff --git a/lxd/storage_lvm_utils.go b/lxd/storage_lvm_utils.go index c35e66bcca..297813efa2 100644 --- a/lxd/storage_lvm_utils.go +++ b/lxd/storage_lvm_utils.go @@ -13,6 +13,7 @@ import ( "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/project" "github.com/lxc/lxd/lxd/state" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/logger" @@ -58,7 +59,7 @@ func (s *storageLvm) lvExtend(lvPath string, lvSize int64, fsType string, fsMntP `volume type %d`, volumeType) } - return growFileSystem(fsType, lvPath, fsMntPoint) + return driver.GrowFileSystem(fsType, lvPath, fsMntPoint) } func (s *storageLvm) lvReduce(lvPath string, lvSize int64, fsType string, fsMntPoint string, volumeType int, data interface{}) error { @@ -331,7 +332,7 @@ func (s *storageLvm) copyContainerThinpool(target container, source container, r } } - msg, err := fsGenerateNewUUID(LVFilesystem, containerLvDevPath) + msg, err := driver.FSGenerateNewUUID(LVFilesystem, containerLvDevPath) if err != nil { logger.Errorf("Failed to create new \"%s\" UUID for container \"%s\" on storage pool \"%s\": %s", LVFilesystem, containerName, s.pool.Name, msg) return err @@ -853,7 +854,7 @@ func lvmCreateLv(projectName, vgName string, thinPoolName string, lvName string, fsPath := getLvmDevPath(projectName, vgName, volumeType, lvName) - output, err = makeFSType(fsPath, lvFsType, nil) + output, err = driver.MakeFSType(fsPath, lvFsType, nil) if err != nil { logger.Errorf("Filesystem creation failed: %s", output) return fmt.Errorf("Error making filesystem on image LV: %v", err) @@ -1078,7 +1079,7 @@ func (s *storageLvm) copyVolumeThinpool(source string, target string, readOnly b lvDevPath := getLvmDevPath("default", poolName, storagePoolVolumeAPIEndpointCustom, targetLvmName) - msg, err := fsGenerateNewUUID(lvFsType, lvDevPath) + msg, err := driver.FSGenerateNewUUID(lvFsType, lvDevPath) if err != nil { logger.Errorf("Failed to create new UUID for filesystem \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s: %s", lvFsType, s.volume.Name, s.pool.Name, msg, err) return err diff --git a/lxd/storage_pools_utils.go b/lxd/storage_pools_utils.go index 5b52dc5aa9..1831ea5970 100644 --- a/lxd/storage_pools_utils.go +++ b/lxd/storage_pools_utils.go @@ -7,6 +7,7 @@ import ( "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/state" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/version" ) @@ -41,7 +42,7 @@ func storagePoolUpdate(state *state.State, name, newDescription string, newConfi } }() - changedConfig, userOnly := storageConfigDiff(oldConfig, newConfig) + changedConfig, userOnly := driver.ConfigDiff(oldConfig, newConfig) // Apply config changes if there are any if len(changedConfig) != 0 { newWritable.Description = newDescription @@ -191,15 +192,15 @@ func storagePoolDBCreate(s *state.State, poolName, poolDescription string, drive return nil } -func storagePoolValidate(poolName string, driver string, config map[string]string) error { +func storagePoolValidate(poolName string, driverName string, config map[string]string) error { // Check if the storage pool name is valid. - err := storageValidName(poolName) + err := driver.ValidName(poolName) if err != nil { return err } // Validate the requested storage pool configuration. - err = storagePoolValidateConfig(poolName, driver, config, nil) + err = storagePoolValidateConfig(poolName, driverName, config, nil) if err != nil { return err } @@ -229,7 +230,7 @@ func storagePoolCreateInternal(state *state.State, poolName, poolDescription str } // This performs all non-db related work needed to create the pool. -func doStoragePoolCreateInternal(state *state.State, poolName, poolDescription string, driver string, config map[string]string, isNotification bool) error { +func doStoragePoolCreateInternal(state *state.State, poolName, poolDescription string, driverName string, config map[string]string, isNotification bool) error { tryUndo := true s, err := storagePoolInit(state, poolName) if err != nil { @@ -263,7 +264,7 @@ func doStoragePoolCreateInternal(state *state.State, poolName, poolDescription s // callback. So diff the config here to see if something like this has // happened. postCreateConfig := s.GetStoragePoolWritable().Config - configDiff, _ := storageConfigDiff(config, postCreateConfig) + configDiff, _ := driver.ConfigDiff(config, postCreateConfig) if len(configDiff) > 0 { // Create the database entry for the storage pool. err = state.Cluster.StoragePoolUpdate(poolName, poolDescription, postCreateConfig) diff --git a/lxd/storage_utils.go b/lxd/storage_utils.go index 76daed4da1..fd4ecf11bd 100644 --- a/lxd/storage_utils.go +++ b/lxd/storage_utils.go @@ -2,326 +2,11 @@ package main import ( "fmt" - "os" - "strings" - "time" - "golang.org/x/sys/unix" - - "github.com/lxc/lxd/lxd/db" - "github.com/lxc/lxd/shared" - "github.com/lxc/lxd/shared/api" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared/logger" ) -// Options for filesystem creation -type mkfsOptions struct { - label string -} - -// Export the mount options map since we might find it useful in other parts of -// LXD. -type mountOptions struct { - capture bool - flag uintptr -} - -var MountOptions = map[string]mountOptions{ - "async": {false, unix.MS_SYNCHRONOUS}, - "atime": {false, unix.MS_NOATIME}, - "bind": {true, unix.MS_BIND}, - "defaults": {true, 0}, - "dev": {false, unix.MS_NODEV}, - "diratime": {false, unix.MS_NODIRATIME}, - "dirsync": {true, unix.MS_DIRSYNC}, - "exec": {false, unix.MS_NOEXEC}, - "lazytime": {true, unix.MS_LAZYTIME}, - "mand": {true, unix.MS_MANDLOCK}, - "noatime": {true, unix.MS_NOATIME}, - "nodev": {true, unix.MS_NODEV}, - "nodiratime": {true, unix.MS_NODIRATIME}, - "noexec": {true, unix.MS_NOEXEC}, - "nomand": {false, unix.MS_MANDLOCK}, - "norelatime": {false, unix.MS_RELATIME}, - "nostrictatime": {false, unix.MS_STRICTATIME}, - "nosuid": {true, unix.MS_NOSUID}, - "rbind": {true, unix.MS_BIND | unix.MS_REC}, - "relatime": {true, unix.MS_RELATIME}, - "remount": {true, unix.MS_REMOUNT}, - "ro": {true, unix.MS_RDONLY}, - "rw": {false, unix.MS_RDONLY}, - "strictatime": {true, unix.MS_STRICTATIME}, - "suid": {false, unix.MS_NOSUID}, - "sync": {true, unix.MS_SYNCHRONOUS}, -} - -func lxdResolveMountoptions(options string) (uintptr, string) { - mountFlags := uintptr(0) - tmp := strings.SplitN(options, ",", -1) - for i := 0; i < len(tmp); i++ { - opt := tmp[i] - do, ok := MountOptions[opt] - if !ok { - continue - } - - if do.capture { - mountFlags |= do.flag - } else { - mountFlags &= ^do.flag - } - - copy(tmp[i:], tmp[i+1:]) - tmp[len(tmp)-1] = "" - tmp = tmp[:len(tmp)-1] - i-- - } - - return mountFlags, strings.Join(tmp, ",") -} - -// Useful functions for unreliable backends -func tryMount(src string, dst string, fs string, flags uintptr, options string) error { - var err error - - 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 tryUnmount(path string, flags int) error { - var err error - - for i := 0; i < 20; i++ { - err = unix.Unmount(path, flags) - if err == nil { - break - } - - time.Sleep(500 * time.Millisecond) - } - - if err != nil { - return err - } - - return nil -} - -func storageValidName(value string) error { - if strings.Contains(value, "/") { - return fmt.Errorf("Invalid storage volume name \"%s\". Storage volumes cannot contain \"/\" in their name", value) - } - - return nil -} - -func storageConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) { - changedConfig := []string{} - userOnly := true - for key := range oldConfig { - if oldConfig[key] != newConfig[key] { - if !strings.HasPrefix(key, "user.") { - userOnly = false - } - - if !shared.StringInSlice(key, changedConfig) { - changedConfig = append(changedConfig, key) - } - } - } - - for key := range newConfig { - if oldConfig[key] != newConfig[key] { - if !strings.HasPrefix(key, "user.") { - userOnly = false - } - - if !shared.StringInSlice(key, changedConfig) { - changedConfig = append(changedConfig, key) - } - } - } - - // Skip on no change - if len(changedConfig) == 0 { - return nil, false - } - - return changedConfig, userOnly -} - -// Default permissions for folders in ${LXD_DIR} -const storagePoolsDirMode os.FileMode = 0711 -const containersDirMode os.FileMode = 0711 -const customDirMode os.FileMode = 0711 -const imagesDirMode os.FileMode = 0700 -const snapshotsDirMode os.FileMode = 0700 - -// Detect whether LXD already uses the given storage pool. -func lxdUsesPool(dbObj *db.Cluster, onDiskPoolName string, driver string, onDiskProperty string) (bool, string, error) { - pools, err := dbObj.StoragePools() - if err != nil && err != db.ErrNoSuchObject { - return false, "", err - } - - for _, pool := range pools { - _, pl, err := dbObj.StoragePoolGet(pool) - if err != nil { - continue - } - - if pl.Driver != driver { - continue - } - - if pl.Config[onDiskProperty] == onDiskPoolName { - return true, pl.Name, nil - } - } - - return false, "", nil -} - -func makeFSType(path string, fsType string, options *mkfsOptions) (string, error) { - var err error - var msg string - - fsOptions := options - if fsOptions == nil { - fsOptions = &mkfsOptions{} - } - - cmd := []string{fmt.Sprintf("mkfs.%s", fsType), path} - if fsOptions.label != "" { - cmd = append(cmd, "-L", fsOptions.label) - } - - if fsType == "ext4" { - cmd = append(cmd, "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0") - } - - msg, err = shared.TryRunCommand(cmd[0], cmd[1:]...) - if err != nil { - return msg, err - } - - return "", nil -} - -func fsGenerateNewUUID(fstype string, lvpath string) (string, error) { - switch fstype { - case "btrfs": - return btrfsGenerateNewUUID(lvpath) - case "xfs": - return xfsGenerateNewUUID(lvpath) - } - - return "", nil -} - -func xfsGenerateNewUUID(devPath string) (string, error) { - // Attempt to generate a new UUID - msg, err := shared.RunCommand("xfs_admin", "-U", "generate", devPath) - if err != nil { - return msg, err - } - - if msg != "" { - // Exit 0 with a msg usually means some log entry getting in the way - msg, err = shared.RunCommand("xfs_repair", "-o", "force_geometry", "-L", devPath) - if err != nil { - return msg, err - } - - // Attempt to generate a new UUID again - msg, err = shared.RunCommand("xfs_admin", "-U", "generate", devPath) - if err != nil { - return msg, err - } - } - - return msg, nil -} - -func btrfsGenerateNewUUID(lvpath string) (string, error) { - msg, err := shared.RunCommand( - "btrfstune", - "-f", - "-u", - lvpath) - if err != nil { - return msg, err - } - - return msg, nil -} - -func growFileSystem(fsType string, devPath string, mntpoint string) error { - var msg string - var err error - switch fsType { - case "": // if not specified, default to ext4 - fallthrough - case "ext4": - msg, err = shared.TryRunCommand("resize2fs", devPath) - case "xfs": - msg, err = shared.TryRunCommand("xfs_growfs", devPath) - case "btrfs": - msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mntpoint) - default: - return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType) - } - - if err != nil { - errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg) - logger.Errorf(errorMsg) - return fmt.Errorf(errorMsg) - } - - logger.Debugf(`extended underlying %s filesystem for "%s"`, fsType, devPath) - return nil -} - -func shrinkFileSystem(fsType string, devPath string, mntpoint string, byteSize int64) error { - strSize := fmt.Sprintf("%dK", byteSize/1024) - - switch fsType { - case "": // if not specified, default to ext4 - fallthrough - case "ext4": - _, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath) - if err != nil { - return err - } - - _, err = shared.TryRunCommand("resize2fs", devPath, strSize) - if err != nil { - return err - } - case "btrfs": - _, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, mntpoint) - if err != nil { - return err - } - default: - return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType) - } - - return nil -} - func shrinkVolumeFilesystem(s storage, volumeType int, fsType string, devPath string, mntpoint string, byteSize int64, data interface{}) (func() (bool, error), error) { var cleanupFunc func() (bool, error) switch fsType { @@ -359,26 +44,6 @@ func shrinkVolumeFilesystem(s storage, volumeType int, fsType string, devPath st return nil, fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType) } - err := shrinkFileSystem(fsType, devPath, mntpoint, byteSize) + err := driver.ShrinkFileSystem(fsType, devPath, mntpoint, byteSize) return cleanupFunc, err } - -func storageResource(path string) (*api.ResourcesStoragePool, error) { - st, err := shared.Statvfs(path) - if err != nil { - return nil, err - } - - 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 -} diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go index 8e87a6e925..6de54b156b 100644 --- a/lxd/storage_volumes_snapshot.go +++ b/lxd/storage_volumes_snapshot.go @@ -9,6 +9,7 @@ import ( "github.com/gorilla/mux" "github.com/lxc/lxd/lxd/db" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/lxd/util" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" @@ -66,7 +67,7 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) Response { } // Validate the name - err = storageValidName(req.Name) + err = driver.ValidName(req.Name) if err != nil { return BadRequest(err) } diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go index d98f530956..cabdc2f993 100644 --- a/lxd/storage_zfs.go +++ b/lxd/storage_zfs.go @@ -273,14 +273,14 @@ func (s *storageZfs) zfsPoolCreate() error { } fixperms := shared.VarPath("storage-pools", s.pool.Name, "containers") - err = os.MkdirAll(fixperms, containersDirMode) + err = os.MkdirAll(fixperms, driver.ContainersDirMode) if err != nil && !os.IsNotExist(err) { return err } - err = os.Chmod(fixperms, containersDirMode) + err = os.Chmod(fixperms, driver.ContainersDirMode) if err != nil { - logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(containersDirMode), 8), err) + logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.ContainersDirMode), 8), err) } dataset = fmt.Sprintf("%s/images", poolName) @@ -291,13 +291,13 @@ func (s *storageZfs) zfsPoolCreate() error { } fixperms = shared.VarPath("storage-pools", s.pool.Name, "images") - err = os.MkdirAll(fixperms, imagesDirMode) + err = os.MkdirAll(fixperms, driver.ImagesDirMode) if err != nil && !os.IsNotExist(err) { return err } - err = os.Chmod(fixperms, imagesDirMode) + err = os.Chmod(fixperms, driver.ImagesDirMode) if err != nil { - logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(imagesDirMode), 8), err) + logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.ImagesDirMode), 8), err) } dataset = fmt.Sprintf("%s/custom", poolName) @@ -308,13 +308,13 @@ func (s *storageZfs) zfsPoolCreate() error { } fixperms = shared.VarPath("storage-pools", s.pool.Name, "custom") - err = os.MkdirAll(fixperms, customDirMode) + err = os.MkdirAll(fixperms, driver.CustomDirMode) if err != nil && !os.IsNotExist(err) { return err } - err = os.Chmod(fixperms, customDirMode) + err = os.Chmod(fixperms, driver.CustomDirMode) if err != nil { - logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(customDirMode), 8), err) + logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.CustomDirMode), 8), err) } dataset = fmt.Sprintf("%s/deleted", poolName) @@ -332,13 +332,13 @@ func (s *storageZfs) zfsPoolCreate() error { } fixperms = shared.VarPath("storage-pools", s.pool.Name, "containers-snapshots") - err = os.MkdirAll(fixperms, snapshotsDirMode) + err = os.MkdirAll(fixperms, driver.SnapshotsDirMode) if err != nil && !os.IsNotExist(err) { return err } - err = os.Chmod(fixperms, snapshotsDirMode) + err = os.Chmod(fixperms, driver.SnapshotsDirMode) if err != nil { - logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(snapshotsDirMode), 8), err) + logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.SnapshotsDirMode), 8), err) } dataset = fmt.Sprintf("%s/custom-snapshots", poolName) @@ -349,13 +349,13 @@ func (s *storageZfs) zfsPoolCreate() error { } fixperms = shared.VarPath("storage-pools", s.pool.Name, "custom-snapshots") - err = os.MkdirAll(fixperms, snapshotsDirMode) + err = os.MkdirAll(fixperms, driver.SnapshotsDirMode) if err != nil && !os.IsNotExist(err) { return err } - err = os.Chmod(fixperms, snapshotsDirMode) + err = os.Chmod(fixperms, driver.SnapshotsDirMode) if err != nil { - logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(snapshotsDirMode), 8), err) + logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.SnapshotsDirMode), 8), err) } return nil diff --git a/lxd/storage_zfs_utils.go b/lxd/storage_zfs_utils.go index 7e42cab87c..6bd0403d59 100644 --- a/lxd/storage_zfs_utils.go +++ b/lxd/storage_zfs_utils.go @@ -14,6 +14,7 @@ import ( "golang.org/x/sys/unix" "github.com/lxc/lxd/lxd/project" + driver "github.com/lxc/lxd/lxd/storage" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/logger" ) @@ -519,7 +520,7 @@ func zfsUmount(poolName string, path string, mountpoint string) error { fmt.Sprintf("%s/%s", poolName, path)) if err != nil { logger.Warnf("Failed to unmount ZFS filesystem via zfs unmount: %s. Trying lazy umount (MNT_DETACH)...", output) - err := tryUnmount(mountpoint, unix.MNT_DETACH) + err := driver.TryUnmount(mountpoint, unix.MNT_DETACH) if err != nil { logger.Warnf("Failed to unmount ZFS filesystem via lazy umount (MNT_DETACH)...") return err @@ -682,7 +683,7 @@ func (s *storageZfs) doContainerMount(projectName, name string, privileged bool) if !shared.IsMountPoint(containerPoolVolumeMntPoint) { source := fmt.Sprintf("%s/%s", s.getOnDiskPoolName(), fs) zfsMountOptions := fmt.Sprintf("rw,zfsutil,mntpoint=%s", containerPoolVolumeMntPoint) - mounterr := tryMount(source, containerPoolVolumeMntPoint, "zfs", 0, zfsMountOptions) + mounterr := driver.TryMount(source, containerPoolVolumeMntPoint, "zfs", 0, zfsMountOptions) if mounterr != nil { if mounterr != unix.EBUSY { logger.Errorf("Failed to mount ZFS dataset \"%s\" onto \"%s\": %v", source, containerPoolVolumeMntPoint, mounterr) From 30ac4707cec720a8f3b25b3558f37295a35b2061 Mon Sep 17 00:00:00 2001 From: Thomas Hipp <thomas.h...@canonical.com> Date: Thu, 15 Aug 2019 15:17:01 +0200 Subject: [PATCH 4/4] test: Lint storage package Signed-off-by: Thomas Hipp <thomas.h...@canonical.com> --- test/suites/static_analysis.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh index 1c601e9019..cd2da54018 100644 --- a/test/suites/static_analysis.sh +++ b/test/suites/static_analysis.sh @@ -82,6 +82,7 @@ test_static_analysis() { #golint -set_exit_status lxd/migration golint -set_exit_status lxd/node golint -set_exit_status lxd/state + golint -set_exit_status lxd/storage golint -set_exit_status lxd/sys golint -set_exit_status lxd/task golint -set_exit_status lxd/template
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel