From 0d041cbbbbe94188bccd343d5d2b0ac728e51e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Clerget?= Date: Fri, 5 Sep 2025 14:43:15 -0500 Subject: [PATCH] Vagrant/Libvirt basic test/dev environment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cédric Clerget --- .gitignore | 3 + .../contributing/development-environment.rst | 8 ++ vagrant/.vagrantplugins | 42 ++++++ vagrant/README.md | 55 ++++++++ vagrant/Vagrantfile | 55 ++++++++ vagrant/alpine-boot.sh | 47 +++++++ vagrant/alpine-bootable.def | 15 +++ vagrant/export-libvirt-sock.sh | 14 ++ vagrant/init-scripts/add-ww-nodes.sh | 4 + vagrant/init-scripts/init-wwctl.sh | 125 ++++++++++++++++++ vagrant/init-scripts/start-vbmcd.sh | 3 + 11 files changed, 371 insertions(+) create mode 100644 vagrant/.vagrantplugins create mode 100644 vagrant/README.md create mode 100644 vagrant/Vagrantfile create mode 100755 vagrant/alpine-boot.sh create mode 100644 vagrant/alpine-bootable.def create mode 100755 vagrant/export-libvirt-sock.sh create mode 100755 vagrant/init-scripts/add-ww-nodes.sh create mode 100755 vagrant/init-scripts/init-wwctl.sh create mode 100755 vagrant/init-scripts/start-vbmcd.sh diff --git a/.gitignore b/.gitignore index c3244d75..827fca88 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ userdocs/reference/* /warewulf-?.?.?/ *.coverprofile LICENSE_DEPENDENCIES.csv + +/vagrant/.vagrant +/vagrant/*.pid diff --git a/userdocs/contributing/development-environment.rst b/userdocs/contributing/development-environment.rst index 7d537959..e3e79468 100644 --- a/userdocs/contributing/development-environment.rst +++ b/userdocs/contributing/development-environment.rst @@ -95,3 +95,11 @@ required. To use the Dev Container, click the "Open a Remote Window" button on the bottom left of the editor (`><` icon) and select "Reopen in Container". This will build the container and open a new VSC window with the container as the development environment. + +Using Vagrant and Libvirt +========================= + +Vagrant can be used to quickly spin up a test/development environment for Warewulf. +A `Vagrantfile` is provided in `vagrant` directory of the Warewulf repository. +See the `README.md `_ +for more details. \ No newline at end of file diff --git a/vagrant/.vagrantplugins b/vagrant/.vagrantplugins new file mode 100644 index 00000000..d8a8c1ea --- /dev/null +++ b/vagrant/.vagrantplugins @@ -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 diff --git a/vagrant/README.md b/vagrant/README.md new file mode 100644 index 00000000..c12ad7d0 --- /dev/null +++ b/vagrant/README.md @@ -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" +``` \ No newline at end of file diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile new file mode 100644 index 00000000..46cc9716 --- /dev/null +++ b/vagrant/Vagrantfile @@ -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 diff --git a/vagrant/alpine-boot.sh b/vagrant/alpine-boot.sh new file mode 100755 index 00000000..234a3b06 --- /dev/null +++ b/vagrant/alpine-boot.sh @@ -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 </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 + diff --git a/vagrant/alpine-bootable.def b/vagrant/alpine-bootable.def new file mode 100644 index 00000000..a9b0baa5 --- /dev/null +++ b/vagrant/alpine-bootable.def @@ -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 </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 diff --git a/vagrant/init-scripts/add-ww-nodes.sh b/vagrant/init-scripts/add-ww-nodes.sh new file mode 100755 index 00000000..fe80ec91 --- /dev/null +++ b/vagrant/init-scripts/add-ww-nodes.sh @@ -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 diff --git a/vagrant/init-scripts/init-wwctl.sh b/vagrant/init-scripts/init-wwctl.sh new file mode 100755 index 00000000..3f5ceb46 --- /dev/null +++ b/vagrant/init-scripts/init-wwctl.sh @@ -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 < /etc/warewulf/warewulf.conf < /etc/systemd/system/vbmcd.service <