Skip to content

Commit 90a09b1

Browse files
authored
Merge pull request #1621 from luissimas/linstor
Add `linstor` storage driver
2 parents 635a920 + 809c559 commit 90a09b1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3731
-330
lines changed

.github/workflows/tests.yml

+45-1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ jobs:
140140
- lvm
141141
- zfs
142142
- ceph
143+
- linstor
143144
- random
144145
os:
145146
- ubuntu-22.04
@@ -391,6 +392,48 @@ jobs:
391392
sudo microceph.ceph status
392393
sudo rm -f /snap/bin/rbd
393394
395+
- name: Setup LINSTOR
396+
if: ${{ matrix.backend == 'linstor' }}
397+
run: |
398+
set -x
399+
400+
# As with Ceph, we hope for a spare disk.
401+
if [ "$(stat -c '%d' /)" = "$(stat -c '%d' /mnt)" ]; then
402+
echo "FAIL: rootfs and ephemeral part on the same disk, aborting"
403+
exit 1
404+
fi
405+
406+
sudo add-apt-repository ppa:linbit/linbit-drbd9-stack -y
407+
408+
# Install everything required to compile DRBD and run LINSTOR tools.
409+
sudo apt-get install --no-install-recommends -y \
410+
drbd-dkms \
411+
linstor-client \
412+
linstor-controller \
413+
linstor-satellite \
414+
linux-headers-generic \
415+
python3-setuptools
416+
417+
# Enable DRBD.
418+
sudo modprobe -r drbd
419+
sudo modprobe drbd
420+
421+
# Get the runner IP.
422+
runner_ip="$(hostname -I | cut -d' ' -f1)"
423+
424+
# Create a single local node.
425+
sudo linstor node create local "${runner_ip}" --node-type combined
426+
427+
# Repurpose the ephemeral disk for LINSTOR physical storage.
428+
sudo swapoff /mnt/swapfile
429+
ephemeral_disk="$(findmnt --noheadings --output SOURCE --target /mnt | sed 's/[0-9]\+$//')"
430+
sudo umount /mnt
431+
sudo wipefs -a "${ephemeral_disk}"
432+
sudo linstor physical-storage create-device-pool --storage-pool incus --pool-name linstor-incus lvmthin local "${ephemeral_disk}"
433+
434+
# Update the runner env.
435+
echo "INCUS_LINSTOR_CLUSTER=${runner_ip}" >> "$GITHUB_ENV"
436+
394437
- name: "Ensure offline mode (block image server)"
395438
run: |
396439
sudo nft add table inet filter
@@ -404,6 +447,7 @@ jobs:
404447
INCUS_CEPH_CLUSTER: "ceph"
405448
INCUS_CEPH_CEPHFS: "cephfs"
406449
INCUS_CEPH_CEPHOBJECT_RADOSGW: "http://127.0.0.1"
450+
INCUS_LINSTOR_LOCAL_SATELLITE: "local"
407451
INCUS_CONCURRENT: "1"
408452
INCUS_VERBOSE: "1"
409453
INCUS_OFFLINE: "1"
@@ -413,7 +457,7 @@ jobs:
413457
chmod +x ~
414458
echo "root:1000000:1000000000" | sudo tee /etc/subuid /etc/subgid
415459
cd test
416-
sudo --preserve-env=PATH,GOPATH,GITHUB_ACTIONS,INCUS_VERBOSE,INCUS_BACKEND,INCUS_CEPH_CLUSTER,INCUS_CEPH_CEPHFS,INCUS_CEPH_CEPHOBJECT_RADOSGW,INCUS_OFFLINE,INCUS_SKIP_TESTS,INCUS_REQUIRED_TESTS, INCUS_BACKEND=${{ matrix.backend }} ./main.sh ${{ matrix.suite }}
460+
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 }}
417461
418462
client:
419463
name: Client

cmd/incusd/api_1.0.go

+11
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,7 @@ func doApi10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]str
792792
oidcChanged := false
793793
openFGAChanged := false
794794
ovnChanged := false
795+
linstorChanged := false
795796
ovsChanged := false
796797
syslogChanged := false
797798

@@ -835,6 +836,9 @@ func doApi10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]str
835836

836837
case "openfga.api.url", "openfga.api.token", "openfga.store.id":
837838
openFGAChanged = true
839+
840+
case "storage.linstor.controller_connection", "storage.linstor.ca_cert", "storage.linstor.client_cert", "storage.linstor.client_key":
841+
linstorChanged = true
838842
}
839843
}
840844

@@ -1002,6 +1006,13 @@ func doApi10UpdateTriggers(d *Daemon, nodeChanged, clusterChanged map[string]str
10021006
}
10031007
}
10041008

1009+
if linstorChanged {
1010+
err := d.setupLinstor()
1011+
if err != nil {
1012+
return err
1013+
}
1014+
}
1015+
10051016
// Compile and load the instance placement scriptlet.
10061017
value, ok = clusterChanged["instances.placement.scriptlet"]
10071018
if ok {

cmd/incusd/daemon.go

+43
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import (
6363
"github.com/lxc/incus/v6/internal/server/state"
6464
storagePools "github.com/lxc/incus/v6/internal/server/storage"
6565
storageDrivers "github.com/lxc/incus/v6/internal/server/storage/drivers"
66+
"github.com/lxc/incus/v6/internal/server/storage/linstor"
6667
"github.com/lxc/incus/v6/internal/server/storage/s3/miniod"
6768
"github.com/lxc/incus/v6/internal/server/sys"
6869
"github.com/lxc/incus/v6/internal/server/syslog"
@@ -172,6 +173,10 @@ type Daemon struct {
172173

173174
// API info.
174175
apiExtensions int
176+
177+
// Linstor client.
178+
linstor *linstor.Client
179+
linstorMu sync.Mutex
175180
}
176181

177182
// DaemonConfig holds configuration values for Daemon.
@@ -604,6 +609,7 @@ func (d *Daemon) State() *state.State {
604609
OS: d.os,
605610
OVN: d.getOVN,
606611
OVS: d.getOVS,
612+
Linstor: d.getLinstor,
607613
Proxy: d.proxy,
608614
ServerCert: d.serverCert,
609615
ServerClustered: d.serverClustered,
@@ -2732,3 +2738,40 @@ func (d *Daemon) getOVS() (*ovs.VSwitch, error) {
27322738

27332739
return d.ovs, nil
27342740
}
2741+
2742+
func (d *Daemon) setupLinstor() error {
2743+
d.linstorMu.Lock()
2744+
defer d.linstorMu.Unlock()
2745+
2746+
// Clear any existing client.
2747+
d.linstor = nil
2748+
2749+
// Get the Linstor controller connection string.
2750+
controllerConnection := d.globalConfig.LinstorControllerConnection()
2751+
2752+
// Get the SSL certificates if needed.
2753+
sslCACert, sslClientCert, sslClientKey := d.globalConfig.LinstorSSL()
2754+
2755+
// Get Linstor client.
2756+
client, err := linstor.NewClient(controllerConnection, sslCACert, sslClientCert, sslClientKey)
2757+
if err != nil {
2758+
return fmt.Errorf("Failed to connect to Linstor: %w", err)
2759+
}
2760+
2761+
// Set the client.
2762+
d.linstor = client
2763+
2764+
return nil
2765+
}
2766+
2767+
func (d *Daemon) getLinstor() (*linstor.Client, error) {
2768+
// Setup the client if it does not exist.
2769+
if d.linstor == nil {
2770+
err := d.setupLinstor()
2771+
if err != nil {
2772+
return nil, err
2773+
}
2774+
}
2775+
2776+
return d.linstor, nil
2777+
}

doc/.wordlist.txt

+11
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,15 @@ DHCPv
6464
Diátaxis
6565
Diffie
6666
Distrobuilder
67+
diskful
68+
Diskless
69+
diskless
6770
DNS
6871
dnsmasq
6972
DNSSEC
7073
DoS
7174
DRM
75+
DRBD
7276
EB
7377
Ebit
7478
eBPF
@@ -104,8 +108,10 @@ GPUs
104108
Grafana
105109
HAProxy
106110
hardcoded
111+
HDDs
107112
Hellman
108113
Homebrew
114+
hostname
109115
hotplug
110116
hotplugged
111117
hotplugging
@@ -151,6 +157,9 @@ LXCFS
151157
LXC's
152158
LXD
153159
LXD's
160+
LINBIT
161+
LINSTOR
162+
LINSTOR's
154163
macOS
155164
macvlan
156165
Makefile
@@ -178,6 +187,7 @@ NIC
178187
NICs
179188
NixOS
180189
NUMA
190+
NVMe
181191
NVRAM
182192
OCI
183193
OData
@@ -212,6 +222,7 @@ PKI
212222
PNG
213223
Pongo
214224
POSIX
225+
PPA
215226
pre
216227
preseed
217228
proxied

doc/api-extensions.md

+4
Original file line numberDiff line numberDiff line change
@@ -2760,3 +2760,7 @@ This introduces a new `io.bus` property for compatible network devices allowing
27602760
## `disk_io_bus_usb`
27612761

27622762
Adds a new `usb` value for `io.bus` on `disk` devices.
2763+
2764+
## `storage_driver_linstor`
2765+
2766+
This adds a LINSTOR storage driver.

doc/config_options.txt

+35
Original file line numberDiff line numberDiff line change
@@ -2603,6 +2603,41 @@ Specify the volume using the syntax `POOL/VOLUME`.
26032603
Specify the volume using the syntax `POOL/VOLUME`.
26042604
```
26052605

2606+
```{config:option} storage.linstor.ca_cert server-miscellaneous
2607+
:scope: "global"
2608+
:shortdesc: "LINSTOR SSL certificate authority"
2609+
:type: "string"
2610+
2611+
```
2612+
2613+
```{config:option} storage.linstor.client_cert server-miscellaneous
2614+
:scope: "global"
2615+
:shortdesc: "LINSTOR SSL client certificate"
2616+
:type: "string"
2617+
2618+
```
2619+
2620+
```{config:option} storage.linstor.client_key server-miscellaneous
2621+
:scope: "global"
2622+
:shortdesc: "LINSTOR SSL client key"
2623+
:type: "string"
2624+
2625+
```
2626+
2627+
```{config:option} storage.linstor.controller_connection server-miscellaneous
2628+
:scope: "global"
2629+
:shortdesc: "LINSTOR controller connection string"
2630+
:type: "string"
2631+
2632+
```
2633+
2634+
```{config:option} storage.linstor.satellite.name server-miscellaneous
2635+
:scope: "global"
2636+
:shortdesc: "LINSTOR satellite node name override"
2637+
:type: "string"
2638+
Set this option to the name of the local LINSTOR satellite node, should it be different from the Incus server name.
2639+
```
2640+
26062641
<!-- config group server-miscellaneous end -->
26072642
<!-- config group server-oidc start -->
26082643
```{config:option} oidc.audience server-oidc

doc/explanation/storage.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The following storage drivers are supported:
2424
- [Ceph RBD - `ceph`](storage-ceph)
2525
- [CephFS - `cephfs`](storage-cephfs)
2626
- [Ceph Object - `cephobject`](storage-cephobject)
27+
- [LINSTOR - `linstor`](storage-linstor)
2728

2829
See the following how-to guides for additional information:
2930

@@ -36,12 +37,12 @@ See the following how-to guides for additional information:
3637
Where the Incus data is stored depends on the configuration and the selected storage driver.
3738
Depending on the storage driver that is used, Incus can either share the file system with its host or keep its data separate.
3839

39-
Storage location | Directory | Btrfs | LVM (all) | ZFS | Ceph (all) |
40-
:--- | :-: | :-: | :-: | :-: | :-: |
41-
Shared with the host | &#x2713; | &#x2713; | - | &#x2713; | - |
42-
Dedicated disk/partition | - | &#x2713; | &#x2713; | &#x2713; | - |
43-
Loop disk | - | &#x2713; | &#x2713; | &#x2713; | - |
44-
Remote storage | - | - | &#x2713; | - | &#x2713; |
40+
Storage location | Directory | Btrfs | LVM (all) | ZFS | Ceph (all) | LINSTOR |
41+
:--- | :-: | :-: | :-: | :-: | :-: | :-: |
42+
Shared with the host | &#x2713; | &#x2713; | - | &#x2713; | - | - |
43+
Dedicated disk/partition | - | &#x2713; | &#x2713; | &#x2713; | - | &#x2713; |
44+
Loop disk | - | &#x2713; | &#x2713; | &#x2713; | - | &#x2713; |
45+
Remote storage | - | - | &#x2713; | - | &#x2713; | &#x2713; |
4546

4647
#### Shared with the host
4748

@@ -54,7 +55,7 @@ This option is supported for the `dir` driver, the `btrfs` driver (if the host i
5455

5556
Having Incus use an empty partition on your main disk or a full dedicated disk keeps its storage completely independent from the host.
5657

57-
This option is supported for the `btrfs` driver, the `lvm` driver and the `zfs` driver.
58+
This option is supported for the `btrfs` driver, the `lvm` driver, the `zfs` driver and the `linstor` driver.
5859

5960
#### Loop disk
6061

@@ -72,6 +73,7 @@ You can increase their size though; see {ref}`storage-resize-pool`.
7273

7374
The `ceph`, `cephfs` and `cephobject` drivers store the data in a completely independent Ceph storage cluster that must be set up separately.
7475
The `lvmcluster` driver relies on a shared block device being available to all cluster members and on a pre-existing `lvmlockd` setup.
76+
The `linstor` driver stores the data in a LINSTOR storage cluster that must be setup separately.
7577

7678
(storage-default-pool)=
7779
### Default storage pool

0 commit comments

Comments
 (0)