Skip to content

feat: support NFS storage pools #2025

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions internal/server/storage/drivers/driver_nfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package drivers

import (
"strings"

"github.com/lxc/incus/v6/internal/linux"
"github.com/lxc/incus/v6/internal/migration"
deviceConfig "github.com/lxc/incus/v6/internal/server/device/config"
localMigration "github.com/lxc/incus/v6/internal/server/migration"
"github.com/lxc/incus/v6/shared/util"
)

type nfs struct {
dir
}

// Info returns info about the driver and its environment.
func (n *nfs) Info() Info {
return Info{
Name: "nfs",
Version: "1",
DefaultVMBlockFilesystemSize: deviceConfig.DefaultVMBlockFilesystemSize,
OptimizedImages: false,
PreservesInodes: false,
Remote: n.isRemote(),
VolumeTypes: []VolumeType{VolumeTypeBucket, VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
VolumeMultiNode: n.isRemote(),
BlockBacking: false,
RunningCopyFreeze: false,
DirectIO: true,
MountedRoot: true,
Buckets: true,
}
}

// isRemote returns true indicating this driver uses remote storage.
func (n *nfs) isRemote() bool {
return true
}

// FillConfig populates the storage pool's configuration file with the default values.
func (n *nfs) FillConfig() error {
uri := strings.Split(n.config["source"], ":")

// URI should be first part of IP:PORT
n.config["nfs.addr"] = uri[0]

return nil
}

func (n *nfs) getMountOptions() string {
// Allow overriding the default options.
if n.config["nfs.mount_options"] != "" {
return n.config["nfs.mount_options"]
}
// We only really support vers=4.2
return "vers=4.2,addr=" + n.config["nfs.addr"]
}

// Create is called during pool creation and is effectively using an empty driver struct.
// WARNING: The Create() function cannot rely on any of the struct attributes being set.
func (n *nfs) Create() error {
err := n.FillConfig()
if err != nil {
return err
}

sourcePath := n.config["source"]

// Mount the nfs driver.
mntFlags, mntOptions := linux.ResolveMountOptions(strings.Split(n.getMountOptions(), ","))
err = TryMount(sourcePath, GetPoolMountPath(n.name), "nfs4", mntFlags, mntOptions)
if err != nil {
return err
}

defer func() { _, _ = forceUnmount(GetPoolMountPath(n.name)) }()

return nil
}

func (n *nfs) Mount() (bool, error) {

Check failure on line 82 in internal/server/storage/drivers/driver_nfs.go

View workflow job for this annotation

GitHub Actions / Code (oldstable)

exported: exported method nfs.Mount should have comment or be unexported (revive)

Check failure on line 82 in internal/server/storage/drivers/driver_nfs.go

View workflow job for this annotation

GitHub Actions / Code (stable)

exported: exported method nfs.Mount should have comment or be unexported (revive)

Check failure on line 82 in internal/server/storage/drivers/driver_nfs.go

View workflow job for this annotation

GitHub Actions / Code (tip)

exported: exported method nfs.Mount should have comment or be unexported (revive)
path := GetPoolMountPath(n.name)

// Check if already mounted.
if linux.IsMountPoint(path) {
return false, nil
}

sourcePath := n.config["source"]
n.config["nfs.path"] = sourcePath

// Check if we're dealing with an external mount.
if sourcePath == path {
return false, nil
}

// Mount the nfs driver.
mntFlags, mntOptions := linux.ResolveMountOptions(strings.Split(n.getMountOptions(), ","))
err := TryMount(sourcePath, GetPoolMountPath(n.name), "nfs4", mntFlags, mntOptions)
if err != nil {
return false, err
}

return true, nil
}

func (n *nfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type {

Check failure on line 108 in internal/server/storage/drivers/driver_nfs.go

View workflow job for this annotation

GitHub Actions / Code (oldstable)

exported: exported method nfs.MigrationTypes should have comment or be unexported (revive)

Check failure on line 108 in internal/server/storage/drivers/driver_nfs.go

View workflow job for this annotation

GitHub Actions / Code (stable)

exported: exported method nfs.MigrationTypes should have comment or be unexported (revive)

Check failure on line 108 in internal/server/storage/drivers/driver_nfs.go

View workflow job for this annotation

GitHub Actions / Code (tip)

exported: exported method nfs.MigrationTypes should have comment or be unexported (revive)
// NFS does not support xattr
rsyncFeatures := []string{"delete", "bidirectional"}
if util.IsTrue(n.Config()["rsync.compression"]) {
rsyncFeatures = append(rsyncFeatures, "compress")
}

return []localMigration.Type{
{
FSType: migration.MigrationFSType_BLOCK_AND_RSYNC,
Features: rsyncFeatures,
},
{
FSType: migration.MigrationFSType_RSYNC,
Features: rsyncFeatures,
},
}
}
1 change: 1 addition & 0 deletions internal/server/storage/drivers/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var drivers = map[string]func() driver{
"lvmcluster": func() driver { return &lvm{clustered: true} },
"zfs": func() driver { return &zfs{} },
"linstor": func() driver { return &linstor{} },
"nfs": func() driver { return &nfs{} },
}

// Validators contains functions used for validating a drivers's config.
Expand Down
Loading