|
| 1 | +package drivers |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "strings" |
| 6 | + |
| 7 | + "github.com/lxc/incus/v6/internal/linux" |
| 8 | + "github.com/lxc/incus/v6/internal/migration" |
| 9 | + deviceConfig "github.com/lxc/incus/v6/internal/server/device/config" |
| 10 | + localMigration "github.com/lxc/incus/v6/internal/server/migration" |
| 11 | + "github.com/lxc/incus/v6/shared/util" |
| 12 | +) |
| 13 | + |
| 14 | +type nfs struct { |
| 15 | + dir |
| 16 | +} |
| 17 | + |
| 18 | +// Info returns info about the driver and its environment. |
| 19 | +func (n *nfs) Info() Info { |
| 20 | + return Info{ |
| 21 | + Name: "nfs", |
| 22 | + Version: "1", |
| 23 | + DefaultVMBlockFilesystemSize: deviceConfig.DefaultVMBlockFilesystemSize, |
| 24 | + OptimizedImages: false, |
| 25 | + PreservesInodes: false, |
| 26 | + Remote: n.isRemote(), |
| 27 | + VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeContainer, VolumeTypeVM}, |
| 28 | + VolumeMultiNode: n.isRemote(), |
| 29 | + BlockBacking: false, |
| 30 | + RunningCopyFreeze: false, |
| 31 | + DirectIO: true, |
| 32 | + MountedRoot: true, |
| 33 | + Buckets: false, |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +// isRemote returns true indicating this driver uses remote storage. |
| 38 | +func (n *nfs) isRemote() bool { |
| 39 | + return true |
| 40 | +} |
| 41 | + |
| 42 | +func (n *nfs) getMountOptions() string { |
| 43 | + // Allow overriding the default options. |
| 44 | + if n.config["nfs.mount_options"] != "" { |
| 45 | + return n.config["nfs.mount_options"] |
| 46 | + } |
| 47 | + // We only really support vers=4.2 |
| 48 | + return "vers=4.2,addr=" + n.config["nfs.addr"] |
| 49 | +} |
| 50 | + |
| 51 | +// Create is called during pool creation and is effectively using an empty driver struct. |
| 52 | +// WARNING: The Create() function cannot rely on any of the struct attributes being set. |
| 53 | +func (n *nfs) Create() error { |
| 54 | + if n.config["source"] != "" { |
| 55 | + return fmt.Errorf(`The "source" property must be defined`) |
| 56 | + } |
| 57 | + |
| 58 | + // We are dealing with IPv4 or IPv6, assume the last occurrence of : splits between the network addr and the mount path |
| 59 | + s := strings.LastIndex(n.config["source"], ":") |
| 60 | + |
| 61 | + // URI should be first part of IP:PORT |
| 62 | + n.config["nfs.addr"] = n.config["source"][:s] |
| 63 | + |
| 64 | + // nfs.path is the NFS path we pass to mount |
| 65 | + n.config["nfs.path"] = n.config["source"] |
| 66 | + |
| 67 | + // Mount the nfs driver. |
| 68 | + mntFlags, mntOptions := linux.ResolveMountOptions(strings.Split(n.getMountOptions(), ",")) |
| 69 | + err := TryMount(n.config["nfs.path"], GetPoolMountPath(n.name), "nfs4", mntFlags, mntOptions) |
| 70 | + if err != nil { |
| 71 | + return err |
| 72 | + } |
| 73 | + |
| 74 | + defer func() { _, _ = forceUnmount(GetPoolMountPath(n.name)) }() |
| 75 | + |
| 76 | + return nil |
| 77 | +} |
| 78 | + |
| 79 | +// Mount mounts the storage pool. |
| 80 | +func (n *nfs) Mount() (bool, error) { |
| 81 | + path := GetPoolMountPath(n.name) |
| 82 | + |
| 83 | + // Check if already mounted. |
| 84 | + if linux.IsMountPoint(path) { |
| 85 | + return false, nil |
| 86 | + } |
| 87 | + |
| 88 | + sourcePath := n.config["nfs.path"] |
| 89 | + |
| 90 | + // Check if we're dealing with an external mount. |
| 91 | + if sourcePath == path { |
| 92 | + return false, nil |
| 93 | + } |
| 94 | + |
| 95 | + // Mount the nfs driver. |
| 96 | + mntFlags, mntOptions := linux.ResolveMountOptions(strings.Split(n.getMountOptions(), ",")) |
| 97 | + err := TryMount(sourcePath, GetPoolMountPath(n.name), "nfs4", mntFlags, mntOptions) |
| 98 | + if err != nil { |
| 99 | + return false, err |
| 100 | + } |
| 101 | + |
| 102 | + return true, nil |
| 103 | +} |
| 104 | + |
| 105 | +// MigrationTypes returns the type of transfer methods to be used when doing migrations between pools in preference order. |
| 106 | +func (n *nfs) MigrationTypes(contentType ContentType, refresh bool, copySnapshots bool, clusterMove bool, storageMove bool) []localMigration.Type { |
| 107 | + // NFS does not support xattr |
| 108 | + rsyncFeatures := []string{"delete", "bidirectional"} |
| 109 | + if util.IsTrue(n.Config()["rsync.compression"]) { |
| 110 | + rsyncFeatures = append(rsyncFeatures, "compress") |
| 111 | + } |
| 112 | + |
| 113 | + return []localMigration.Type{ |
| 114 | + { |
| 115 | + FSType: migration.MigrationFSType_BLOCK_AND_RSYNC, |
| 116 | + Features: rsyncFeatures, |
| 117 | + }, |
| 118 | + { |
| 119 | + FSType: migration.MigrationFSType_RSYNC, |
| 120 | + Features: rsyncFeatures, |
| 121 | + }, |
| 122 | + } |
| 123 | +} |
0 commit comments