Skip to content
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
13 changes: 12 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ jobs:
- ceph
- linstor
- random
- nfs
os:
- ubuntu-24.04
include:
Expand Down Expand Up @@ -436,6 +437,16 @@ jobs:
# Update the runner env.
echo "INCUS_LINSTOR_CLUSTER=${runner_ip}" >> "$GITHUB_ENV"

- name: Setup NFS
if: ${{ matrix.backend == 'nfs' }}
run: |
set -x
sudo apt-get install --no-install-recommends -y nfs-kernel-server
echo "/media 10.0.0.0/8(rw,sync,no_subtree_check,no_root_squash,no_all_squash) 100.64.0.0/8(rw,sync,no_subtree_check,no_root_squash,no_all_squash)" | sudo tee /etc/exports
sudo exportfs -a
sudo systemctl restart nfs-server.service
echo "INCUS_NFS_SHARE=$(hostname -I | cut -d' ' -f1):/media" >> "$GITHUB_ENV"

- name: "Ensure offline mode (block image server)"
run: |
sudo nft add table inet filter
Expand All @@ -459,7 +470,7 @@ jobs:
chmod +x ~
echo "root:1000000:1000000000" | sudo tee /etc/subuid /etc/subgid
cd test
sudo --preserve-env=PATH,GOPATH,GITHUB_ACTIONS,INCUS_VERBOSE,INCUS_BACKEND,INCUS_CEPH_CLUSTER,INCUS_CEPH_CEPHFS,INCUS_CEPH_CEPHOBJECT_RADOSGW,INCUS_LINSTOR_LOCAL_SATELLITE,INCUS_LINSTOR_CLUSTER,INCUS_OFFLINE,INCUS_SKIP_TESTS,INCUS_REQUIRED_TESTS, INCUS_BACKEND=${{ matrix.backend }} ./main.sh ${{ matrix.suite }}
sudo --preserve-env=PATH,GOPATH,GITHUB_ACTIONS,INCUS_VERBOSE,INCUS_BACKEND,INCUS_CEPH_CLUSTER,INCUS_CEPH_CEPHFS,INCUS_CEPH_CEPHOBJECT_RADOSGW,INCUS_LINSTOR_LOCAL_SATELLITE,INCUS_LINSTOR_CLUSTER,INCUS_NFS_SHARE,INCUS_OFFLINE,INCUS_SKIP_TESTS,INCUS_REQUIRED_TESTS, INCUS_BACKEND=${{ matrix.backend }} ./main.sh ${{ matrix.suite }}

client:
name: Client
Expand Down
11 changes: 7 additions & 4 deletions cmd/incusd/instance_post.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,10 +1023,13 @@ func migrateInstance(ctx context.Context, s *state.State, inst instance.Instance

// Cleanup instance paths on source member if using remote shared storage
// and there was no storage pool change.
if sourcePool.Driver().Info().Remote && req.Pool == "" {
err = sourcePool.CleanupInstancePaths(inst, nil)
if err != nil {
return fmt.Errorf("Failed cleaning up instance paths on source member: %w", err)
driverInfo := sourcePool.Driver().Info()
if driverInfo.Remote && req.Pool == "" {
if !driverInfo.IgnoreCleanup {
err = sourcePool.CleanupInstancePaths(inst, nil)
if err != nil {
return fmt.Errorf("Failed cleaning up instance paths on source member: %w", err)
}
}
} else {
// Delete the instance on source member if pool isn't remote shared storage.
Expand Down
4 changes: 3 additions & 1 deletion cmd/incusd/storage_pools.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,9 @@ func storagePoolsGet(d *Daemon, r *http.Request) response.Response {
// If no member is specified and the daemon is clustered, we omit the node-specific fields.
if s.ServerClustered {
for _, key := range db.NodeSpecificStorageConfig {
delete(poolAPI.Config, key)
if key != "source" || !pool.Driver().Info().SameSource {
delete(poolAPI.Config, key)
}
}
} else {
// Use local status if not clustered. To allow seeing unavailable pools.
Expand Down
4 changes: 4 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2920,3 +2920,7 @@ A `used_by` field was added to the `GET /1.0/cluster/groups/{name}` endpoint.
## `bpf_token_delegation`

This adds support for [eBPF token delegation](https://docs.ebpf.io/linux/concepts/token/).

## `storage_driver_nfs`

This adds an NFS storage driver.
34 changes: 34 additions & 0 deletions doc/config_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5700,6 +5700,40 @@ This value is required by some providers.
```

<!-- config group storage_lvm-common end -->
<!-- config group storage_nfs-common start -->
```{config:option} nfs.host storage_nfs-common
:default: "-"
:scope: "global"
:shortdesc: "Hostname or IP address of the remote NFS server. Optional if included in `source`, or a configuration is used."
:type: "string"

```

```{config:option} nfs.mount_options storage_nfs-common
:default: "-"
:scope: "local"
:shortdesc: "Additional mount options for the NFS mount."
:type: "string"

```

```{config:option} nfs.path storage_nfs-common
:default: "-"
:scope: "local"
:shortdesc: "Remote NFS path. Typically inferred from `source`, but can be overridden."
:type: "string"

```

```{config:option} source storage_nfs-common
:default: "-"
:scope: "local"
:shortdesc: "NFS remote storage path. Format: `[<host>:]<remote path>`. If `host` is omitted here, it must be set via `nfs.host`."
:type: "string"

```

<!-- config group storage_nfs-common end -->
<!-- config group storage_truenas-common start -->
```{config:option} source storage_truenas-common
:default: "-"
Expand Down
31 changes: 16 additions & 15 deletions doc/reference/storage_drivers.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ storage_cephfs
storage_cephobject
storage_linstor
storage_truenas
storage_nfs
```

See the corresponding pages for driver-specific information and configuration options.
Expand All @@ -24,21 +25,21 @@ See the corresponding pages for driver-specific information and configuration op

Where possible, Incus uses the advanced features of each storage system to optimize operations.

| Feature | Directory | Btrfs | LVM | ZFS | Ceph RBD | CephFS | Ceph Object | LINSTOR | TRUENAS |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| {ref}`storage-optimized-image-storage` | no | yes | yes | yes | yes | n/a | n/a | yes | yes |
| Optimized instance creation | no | yes | yes | yes | yes | n/a | n/a | yes | yes |
| Optimized snapshot creation | no | yes | yes | yes | yes | yes | n/a | yes | yes |
| Optimized image transfer | no | yes | no | yes | yes | n/a | n/a | no | no |
| {ref}`storage-optimized-volume-transfer` | no | yes | no | yes | yes | n/a | n/a | no | no |
| Copy on write | no | yes | yes | yes | yes | yes | n/a | yes | yes |
| Block based | no | no | yes | no | yes | no | n/a | yes | yes |
| Instant cloning | no | yes | yes | yes | yes | yes | n/a | yes | yes |
| Storage driver usable inside a container | yes | yes | no | yes[^1] | no | n/a | n/a | no | no |
| Restore from older snapshots (not latest) | yes | yes | yes | no | yes | yes | n/a | no | no |
| Storage quotas | yes[^2] | yes | yes | yes | yes | yes | yes | yes | yes |
| Available on `incus admin init` | yes | yes | yes | yes | yes | no | no | no | no |
| Object storage | yes | yes | yes | yes | no | no | yes | no | no |
| Feature | Directory | Btrfs | LVM | ZFS | Ceph RBD | CephFS | Ceph Object | LINSTOR | TRUENAS | NFS |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| {ref}`storage-optimized-image-storage` | no | yes | yes | yes | yes | n/a | n/a | yes | yes | no |
| Optimized instance creation | no | yes | yes | yes | yes | n/a | n/a | yes | yes | no |
| Optimized snapshot creation | no | yes | yes | yes | yes | yes | n/a | yes | yes | no |
| Optimized image transfer | no | yes | no | yes | yes | n/a | n/a | no | no | no |
| {ref}`storage-optimized-volume-transfer` | no | yes | no | yes | yes | n/a | n/a | no | no | no |
| Copy on write | no | yes | yes | yes | yes | yes | n/a | yes | yes | no |
| Block based | no | no | yes | no | yes | no | n/a | yes | yes | n/a |
| Instant cloning | no | yes | yes | yes | yes | yes | n/a | yes | yes | no |
| Storage driver usable inside a container | yes | yes | no | yes[^1] | no | n/a | n/a | no | no | yes |
| Restore from older snapshots (not latest) | yes | yes | yes | no | yes | yes | n/a | no | no | yes |
| Storage quotas | yes[^2] | yes | yes | yes | yes | yes | yes | yes | yes | no |
| Available on `incus admin init` | yes | yes | yes | yes | yes | no | no | no | no | yes |
| Object storage | yes | yes | yes | yes | no | no | yes | no | no | no |

[^1]: Requires [`zfs.delegate`](storage-zfs-vol-config) to be enabled.
[^2]: % Include content from [storage_dir.md](storage_dir.md)
Expand Down
28 changes: 28 additions & 0 deletions doc/reference/storage_nfs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
(storage-nfs)=
# NFS - `nfs`

Network File System is a distributed file system protocol. It is used to serve and access files over a computer network.

To use NFS one need to setup a NFS file system following documentation from your Linux distribution of choice.

## `nfs` driver in Incus

The `nfs` driver in Incus only supports NFS version 4.2 and has a couple of limitations.

UID/GID squashing should be enabled. This can be done by explicitly setting `no_root_squash` and `no_all_squash` in `/etc/export`.

Note that it is not recommended to use `nfs` driver as container or virtual machine storage volumes as it is unclear how well it works.

## Configuration options

The following configuration options are available for storage pools that use the `nfs` driver and for storage volumes in these pools.

### Storage pool configuration

% Include content from [config_options.txt](../config_options.txt)
```{include} ../config_options.txt
:start-after: <!-- config group storage_nfs-common start -->
:end-before: <!-- config group storage_nfs-common end -->
```

{{volume_configuration}}
42 changes: 42 additions & 0 deletions internal/server/metadata/configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -6473,6 +6473,48 @@
]
}
},
"storage_nfs": {
"common": {
"keys": [
{
"nfs.host": {
"default": "-",
"longdesc": "",
"scope": "global",
"shortdesc": "Hostname or IP address of the remote NFS server. Optional if included in `source`, or a configuration is used.",
"type": "string"
}
},
{
"nfs.mount_options": {
"default": "-",
"longdesc": "",
"scope": "local",
"shortdesc": "Additional mount options for the NFS mount.",
"type": "string"
}
},
{
"nfs.path": {
"default": "-",
"longdesc": "",
"scope": "local",
"shortdesc": "Remote NFS path. Typically inferred from `source`, but can be overridden.",
"type": "string"
}
},
{
"source": {
"default": "-",
"longdesc": "",
"scope": "local",
"shortdesc": "NFS remote storage path. Format: `[\u003chost\u003e:]\u003cremote path\u003e`. If `host` is omitted here, it must be set via `nfs.host`.",
"type": "string"
}
}
]
}
},
"storage_truenas": {
"common": {
"keys": [
Expand Down
Loading
Loading