Vagrant/Libvirt basic test/dev environment

Signed-off-by: Cédric Clerget <cedric.clerget@gmail.com>
This commit is contained in:
Cédric Clerget
2025-09-05 14:43:15 -05:00
committed by Jonathon Anderson
parent f2869f4949
commit 0d041cbbbb
11 changed files with 371 additions and 0 deletions

42
vagrant/.vagrantplugins Normal file
View File

@@ -0,0 +1,42 @@
class InitWarewulf < Vagrant.plugin(2, :command)
def self.synopsis
"Initialize Warewulf Dev Environment"
end
def execute
ww_version = `git describe --abbrev=0 2>/dev/null`
ww_version = ww_version[1..-1]
system "WW_VERSION=#{ww_version} vagrant up"
system "vagrant provision --provision-with provision-vbmc wwctl"
system "vagrant provision --provision-with add-ww-nodes wwctl"
end
end
class CleanupWarewulf < Vagrant.plugin(2, :command)
def self.synopsis
"Cleanup Warewulf Dev Environment"
end
def execute
system "vagrant ssh wwctl -c 'sudo wwctl power off n1' || true"
system "vagrant ssh wwctl -c 'sudo wwctl power off n2' || true"
system "vagrant halt"
system "vagrant destroy -f"
end
end
class InitWarewulfPlugin < Vagrant.plugin(2)
name "InitWarewulf"
command "init-warewulf" do
InitWarewulf
end
end
class CleanupWarewulfPlugin < Vagrant.plugin(2)
name "CleanupWarewulf"
command "cleanup-warewulf" do
CleanupWarewulf
end
end

55
vagrant/README.md Normal file
View File

@@ -0,0 +1,55 @@
## Dev environment
For dev and testing purpose, a vagrant environment is provided.
For now, the only vagrant provider supported is libvirt and only usable on Linux machines.
### Initialize environment
To setup the initial environment:
```shell
vagrant init-warewulf
```
This command creates three VM nodes:
* wwctl
* wwnode1
* wwnode2
On `wwctl` node, the latest stable warewulf release available will be installed and
automatically adds `wwnode1`/`wwnode2` respectively as `n1` and `n2` in warewulf.
Once initialized, you can use `vagrant up` and `vagrant halt` to start/stop VM's as usual.
### Cleanup environment
To cleanup the environment:
```shell
vagrant cleanup-warewulf
```
This command stops VM nodes and deletes them
### Power control
`wwnode1` and `wwnode2` can be started/stopped from `wwctl` node via commands:
```shell
wwctl power on n1
wwctl power on n2
```
```shell
wwctl power off n1
wwctl power off n2
```
### Alpine boot test
For a quick test/demo, an alpine boot script is provided, you can run it with:
```shell
vagrant ssh wwctl -c "sudo ./alpine-boot.sh"
```

55
vagrant/Vagrantfile vendored Normal file
View File

@@ -0,0 +1,55 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
ENV['VAGRANT_NO_PARALLEL'] = 'yes'
ww_version = ENV.fetch('WW_VERSION', "4.6.3")
Vagrant.configure("2") do |config|
config.vm.define :wwctl do |wwctl|
wwctl.vm.box = "rockylinux/9"
wwctl.vm.box_version = "6.0.0"
wwctl.vm.network :private_network, :ip => "10.100.100.254", :libvirt__dhcp_enabled => false, :libvirt__network_name => 'netboot'
wwctl.vm.synced_folder '.', '/vagrant', disabled: true
wwctl.trigger.after :up, :reload do |trigger|
trigger.run = {path: "export-libvirt-sock.sh"}
end
wwctl.vm.provider :libvirt do |domain|
domain.memory = 1024
domain.cpus = 2
domain.machine_virtual_size = 12
end
wwctl.vm.provision "init-wwctl", type: "shell", path: "init-scripts/init-wwctl.sh", args: [ww_version]
wwctl.vm.provision "file", source: "alpine-bootable.def", destination: "alpine-bootable.def"
wwctl.vm.provision "file", source: "alpine-boot.sh", destination: "alpine-boot.sh"
wwctl.vm.provision "provision-vbmc", type: "shell", run: "never", path: "init-scripts/start-vbmcd.sh"
wwctl.vm.provision "add-ww-nodes", type: "shell", run: "never", path: "init-scripts/add-ww-nodes.sh"
end
config.vm.define :wwnode1 do |node1|
node1.vm.network :private_network, :libvirt__network_name => 'netboot', :libvirt__mac => '006e6f646531'
node1.vm.synced_folder '.', '/vagrant', disabled: true
node1.vm.provider :libvirt do |domain|
domain.memory = 768
domain.cpus = 1
domain.mgmt_attach = false
boot_network = {'network' => 'netboot'}
domain.boot boot_network
end
end
config.vm.define :wwnode2 do |node2|
node2.vm.network :private_network, :libvirt__network_name => 'netboot', :libvirt__mac => '006e6f646532'
node2.vm.synced_folder '.', '/vagrant', disabled: true
node2.vm.provider :libvirt do |domain|
domain.memory = 768
domain.cpus = 1
domain.mgmt_attach = false
boot_network = {'network' => 'netboot'}
domain.boot boot_network
end
end
end

47
vagrant/alpine-boot.sh Executable file
View File

@@ -0,0 +1,47 @@
#!/bin/sh
if [ -d /tmp/alpine-rootfs ]; then
rm -rf /tmp/alpine-rootfs
fi
apptainer build -s /tmp/alpine-rootfs alpine-bootable.def
wwctl image import --force /tmp/alpine-rootfs alpine
wwctl image build alpine
wwctl power off n1
wwctl power off n2
if ! wwctl overlay list | grep "alpine-net" >/dev/null; then
wwctl overlay create alpine-net
wwctl overlay mkdir alpine-net /etc/network
mkdir -p etc/network
cat > etc/network/interfaces.ww <<EOF
{{- range \$devname, \$netdev := .ThisNode.NetDevs }}
# This file is autogenerated by warewulf
{{- if \$netdev.OnBoot.BoolDefaultTrue }}
auto {{ \$netdev.Device }}
{{- end }}
iface {{ \$netdev.Device }} inet static
address {{ \$netdev.Ipaddr }}
netmask {{ \$netdev.Netmask }}
{{ if \$netdev.Gateway }}gateway {{ \$netdev.Gateway }}{{ end }}
{{ if \$netdev.MTU }}mtu {{ \$netdev.MTU }}{{ end }}
{{ end -}}
EOF
wwctl overlay import alpine-net etc/network/interfaces.ww
fi
if ! wwctl profile list -j | jq -e 'to_entries[] | select(.key=="alpine")' >/dev/null; then
wwctl profile add --profile=default alpine
wwctl profile set -y alpine --system-overlays=$(wwctl profile list -j | jq -r 'to_entries[] | select(.key=="default") | .value."system overlay" | join(",")' | sed 's/debian.interfaces/alpine-net/')
fi
wwctl node set -y --image=alpine --profile=alpine n1
wwctl node set -y --image=alpine --profile=alpine n2
wwctl overlay build n1
wwctl overlay build n2
wwctl power on n1
wwctl power on n2

View File

@@ -0,0 +1,15 @@
Bootstrap: docker
From: alpine:3.22
%post
apk add linux-lts linux-firmware-none openrc mdevd-openrc alpine-conf
setup-devd udev || true
passwd -d root
rc-update add networking boot
rc-update add modules
setup-sshd openssh
ln -s /bin/sh /usr/bin/sh
cat > /etc/ssh/sshd_config.d/empty-password.conf <<EOF
PermitRootLogin yes
PermitEmptyPasswords yes
EOF

14
vagrant/export-libvirt-sock.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
pidfile=".export-libvirt-sock.pid"
pid=$(cat $pidfile 2>/dev/null || true)
if [ "$pid" != "" ]; then
if grep "/var/tmp/libvirt.sock:" /proc/$pid/cmdline >/dev/null 2>&1; then
exit 0
fi
fi
nohup vagrant ssh wwctl -- -o ServerAliveInterval=5 -o ServerAliveCountMax=1 -f -R /var/tmp/libvirt.sock:/var/run/libvirt/libvirt-sock -N >/dev/null 2>&1 &
echo $! > .export-libvirt-sock.pid

View File

@@ -0,0 +1,4 @@
#!/bin/sh
wwctl node add -G 10.100.100.1 -H 00:6e:6f:64:65:31 --ipaddr 10.100.100.10 --ipmiaddr 10.100.100.254 --ipmipass password --ipmiport 6231 --ipmiuser admin --ipmiinterface lanplus --netdev eth0 --netmask 255.255.255.0 --nettagadd="DNS1=10.100.100.1" n1
wwctl node add -G 10.100.100.1 -H 00:6e:6f:64:65:32 --ipaddr 10.100.100.11 --ipmiaddr 10.100.100.254 --ipmipass password --ipmiport 6232 --ipmiuser admin --ipmiinterface lanplus --netdev eth0 --netmask 255.255.255.0 --nettagadd="DNS1=10.100.100.1" n2

View File

@@ -0,0 +1,125 @@
#!/bin/sh
echo ", +" | sfdisk --no-reread -N 5 /dev/vda
partprobe
xfs_growfs /dev/vda5
echo "StreamLocalBindUnlink yes" > /etc/ssh/sshd_config.d/60-forward-cleanup.conf
systemctl reload sshd.service
dnf install -y libvirt-client python3-pip python3-libvirt ipmitool epel-release
pip3 install virtualbmc
dnf install -y apptainer
ww_version=$1
cat > /usr/local/bin/setup-wwnode <<EOF
#!/bin/sh
node=\$1
port=\$2
if [ ! -e /root/.vbmc/\$node ]; then
/usr/local/bin/vbmc add --username admin --password password --port \$port --address 10.100.100.254 --libvirt-uri qemu:///system?socket=/var/tmp/libvirt.sock \$node
fi
/usr/local/bin/vbmc start \$node
EOF
chmod 755 /usr/local/bin/setup-wwnode
dnf install -y https://github.com/warewulf/warewulf/releases/download/v${ww_version}/warewulf-${ww_version}-1.el9.$(arch).rpm
cat > /etc/warewulf/warewulf.conf <<EOF
ipaddr: 10.100.100.254
netmask: 255.255.255.0
network: 10.100.100.0
warewulf:
port: 9873
secure: false
update interval: 60
autobuild overlays: true
host overlay: true
grubboot: false
api:
enabled: false
allowed subnets:
- 127.0.0.0/8
- ::1/128
dhcp:
enabled: true
template: default
range start: 10.100.100.2
range end: 10.100.100.9
systemd name: dhcpd
tftp:
enabled: true
tftproot: /var/lib/tftpboot
systemd name: tftp
ipxe:
00:0B: arm64-efi/snponly.efi
"00:00": undionly.kpxe
"00:07": ipxe-snponly-x86_64.efi
"00:09": ipxe-snponly-x86_64.efi
nfs:
enabled: true
export paths:
- path: /home
export options: rw,sync
- path: /opt
export options: ro,sync,no_root_squash
systemd name: nfs-server
ssh:
key types:
- ed25519
- ecdsa
- rsa
- dsa
image mounts:
- source: /etc/resolv.conf
dest: /etc/resolv.conf
readonly: true
paths:
bindir: /usr/bin
sysconfdir: /etc
localstatedir: /var/lib
cachedir: /var/cache
ipxesource: /usr/share/ipxe
srvdir: /var/lib
firewallddir: /usr/lib/firewalld/services
systemddir: /usr/lib/systemd/system
datadir: /usr/share
wwoverlaydir: /var/lib/warewulf/overlays
wwchrootdir: /var/lib/warewulf/chroots
wwprovisiondir: /var/lib/warewulf/provision
wwclientdir: /warewulf
EOF
systemctl enable --now warewulfd
wwctl configure --all
systemctl restart warewulfd
cat > /etc/systemd/system/vbmcd.service <<EOF
[Install]
WantedBy = multi-user.target
[Service]
ExecReload = /bin/kill -HUP $MAINPID
ExecStart = /usr/local/bin/vbmcd --foreground
Group = root
Restart = on-failure
RestartSec = 2
TimeoutSec = 120
Type = simple
User = root
ExecStartPost = /usr/local/bin/setup-wwnode vagrant_wwnode1 6231
ExecStartPost = /usr/local/bin/setup-wwnode vagrant_wwnode2 6232
[Unit]
After = syslog.target
After = network.target
Description = vbmc service
EOF
systemctl daemon-reload

View File

@@ -0,0 +1,3 @@
#!/bin/sh
systemctl enable --now vbmcd.service