From 8c53b59671adcf92f68963aa52b34cd09ee2e8af Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Fri, 7 Nov 2025 21:15:29 +0100 Subject: [PATCH] Cleanup and improvements --- .editorconfig | 12 +++ .gitignore | 3 +- Dockerfile | 40 -------- config.yaml | 2 + dhcp.yaml | 1 - generate.sh | 5 - nodes/_defaults.yaml | 1 - nodes/_schematic.yaml | 9 +- nodes/vm/_defaults.yaml | 1 - nodes/vm/{vm.yaml => talos-vm.yaml} | 2 +- templates/boot.ipxe | 21 +--- templates/dnsmasq.conf | 2 +- tools/{merge.py => merge} | 0 tools/render | 11 ++ tools/tftpd | 71 +++++++++++++ tools/vm | 154 ++++++++++++++++++++++++++++ vm/cluster-vm.xml | 13 --- vm/create.sh | 15 --- vm/destroy.sh | 8 -- vm/helper.sh | 10 -- vm/start.sh | 7 -- vm/tftp.sh | 26 ----- 22 files changed, 263 insertions(+), 151 deletions(-) create mode 100644 .editorconfig delete mode 100644 Dockerfile create mode 100644 config.yaml delete mode 100644 dhcp.yaml delete mode 100755 generate.sh rename nodes/vm/{vm.yaml => talos-vm.yaml} (73%) rename tools/{merge.py => merge} (100%) create mode 100755 tools/render create mode 100755 tools/tftpd create mode 100755 tools/vm delete mode 100644 vm/cluster-vm.xml delete mode 100755 vm/create.sh delete mode 100755 vm/destroy.sh delete mode 100644 vm/helper.sh delete mode 100755 vm/start.sh delete mode 100755 vm/tftp.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3715bc8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = tab + +[*.yaml] +indent_style = space +indent_size = 4 + +[{*.py,tools/merge}] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index ef4e642..19dd615 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -ipxe/ +.ipxe/ rendered/ -tftp/ diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 182021a..0000000 --- a/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -FROM docker.io/library/debian:stable AS builder-ipxe -RUN apt-get update \ - && apt-get install -y \ - build-essential \ - curl \ - liblzma-dev \ - genisoimage -ARG IPXE_VERSION=b41bda4413bf286d7b7a449bc05e1531da1eec2e -RUN curl -L https://github.com/ipxe/ipxe/archive/${IPXE_VERSION}.tar.gz | tar -xz -WORKDIR /ipxe-${IPXE_VERSION}/src - -# Enable HTTPS -RUN sed -i 's/^#undef[\t ]DOWNLOAD_PROTO_HTTPS.*$/#define DOWNLOAD_PROTO_HTTPS/g' config/general.h - -RUN mkdir /build -RUN make -j$(nproc) bin/ipxe.pxe && cp bin/ipxe.pxe /build -RUN make -j$(nproc) bin-x86_64-efi/ipxe.efi && cp bin-x86_64-efi/ipxe.efi /build - -FROM docker.io/library/python:3.13-slim AS config-renderer -COPY --from=docker.io/hairyhenderson/gomplate:v4.3 /gomplate /bin/gomplate -COPY ./requirements.txt /requirements.txt -RUN pip install -r /requirements.txt -COPY ./generate.sh /generate.sh -COPY ./tools /tools -COPY ./nodes /nodes -COPY ./templates /templates -RUN ./generate.sh - -FROM docker.io/library/alpine:3.22.2 AS runtime -RUN apk add dnsmasq - -COPY --from=builder-ipxe /build/ipxe.pxe /tftproot/ -COPY --from=builder-ipxe /build/ipxe.efi /tftproot/ -COPY --from=config-renderer /rendered/boot.ipxe /tftproot/ -COPY --from=config-renderer /rendered/dnsmasq.conf /dnsmasq.conf - -EXPOSE 67/udp -EXPOSE 69/udp - -CMD ["dnsmasq", "--conf-file=/dnsmasq.conf", "--keep-in-foreground", "--user=root", "--log-facility=-", "--port=0"] diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..e781eef --- /dev/null +++ b/config.yaml @@ -0,0 +1,2 @@ +dhcp: + tftpIp: 10.0.0.3 diff --git a/dhcp.yaml b/dhcp.yaml deleted file mode 100644 index 183bc1f..0000000 --- a/dhcp.yaml +++ /dev/null @@ -1 +0,0 @@ -tftpIp: 10.0.0.3 diff --git a/generate.sh b/generate.sh deleted file mode 100755 index 1c6d211..0000000 --- a/generate.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -euxo pipefail -SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") - -${SCRIPT_DIR}/tools/merge.py ./nodes | gomplate -d nodes=stdin://nodes.json -d dhcp=${SCRIPT_DIR}/dhcp.yaml --input-dir ${SCRIPT_DIR}/templates --output-dir ${SCRIPT_DIR}/rendered diff --git a/nodes/_defaults.yaml b/nodes/_defaults.yaml index 72530cc..725efc0 100644 --- a/nodes/_defaults.yaml +++ b/nodes/_defaults.yaml @@ -6,4 +6,3 @@ dns0: 1.1.1.1 dns1: 8.8.8.8 ntp: nl.pool.ntp.org install: false -upgradeIPXE: false diff --git a/nodes/_schematic.yaml b/nodes/_schematic.yaml index c766285..8cba538 100644 --- a/nodes/_schematic.yaml +++ b/nodes/_schematic.yaml @@ -1,5 +1,6 @@ customization: - systemExtensions: - officialExtensions: - - siderolabs/iscsi-tools - - siderolabs/util-linux-tools + systemExtensions: + officialExtensions: + - siderolabs/iscsi-tools + - siderolabs/util-linux-tools + - siderolabs/intel-ucode diff --git a/nodes/vm/_defaults.yaml b/nodes/vm/_defaults.yaml index 1066e5a..70bb7a5 100644 --- a/nodes/vm/_defaults.yaml +++ b/nodes/vm/_defaults.yaml @@ -1,3 +1,2 @@ netmask: 255.255.255.0 gateway: 192.168.1.1 -upgradeIPXE: ipxe.pxe diff --git a/nodes/vm/vm.yaml b/nodes/vm/talos-vm.yaml similarity index 73% rename from nodes/vm/vm.yaml rename to nodes/vm/talos-vm.yaml index a3e9b55..f360796 100644 --- a/nodes/vm/vm.yaml +++ b/nodes/vm/talos-vm.yaml @@ -1,4 +1,4 @@ -serial: vm +serial: talos-vm interface: enp1s0 ip: 192.168.1.2 install: true diff --git a/templates/boot.ipxe b/templates/boot.ipxe index d8841b2..bd3c208 100644 --- a/templates/boot.ipxe +++ b/templates/boot.ipxe @@ -2,35 +2,24 @@ dhcp +echo Starting ${serial} + :start # Is a known serial is set, execute that # If an unknown serial is set, exit # If no serial is set, ask the user -goto node_${serial} || goto manual +goto node_${serial} || shell # Default behavior (non install mode) is to exit iPXE script -{{ range (datasource "nodes" | jsonArray) }} +{{ range datasource "nodes" }} {{- if .install }} # {{ .filename }} :node_{{ .serial }} {{- $ipArg := printf "ip=%s::%s:%s:%s:%s::%s:%s:%s" .ip .gateway .netmask .hostname .interface .dns0 .dns1 .ntp }} {{- $kernelArgs := printf "%s %s" $ipArg .kernelArgs }} imgfree -kernel https://pxe.factory.talos.dev/image/{{ .schematicID }}/{{ .talosVersion }}/kernel-{{ .arch }} {{ $kernelArgs }} {{- if .upgradeIPXE }} || boot {{ .upgradeIPXE }} {{- end }} +kernel https://pxe.factory.talos.dev/image/{{ .schematicID }}/{{ .talosVersion }}/kernel-{{ .arch }} {{ $kernelArgs }} initrd https://pxe.factory.talos.dev/image/{{ .schematicID }}/{{ .talosVersion }}/initramfs-{{ .arch }}.xz boot {{- end }} {{ end }} - -:manual -menu Select node -{{ range (datasource "nodes" | jsonArray) }} -item {{ .serial }} {{ .hostname }} -{{ end }} -choose selected || goto cancel -goto node_${selected} - -:cancel -echo Type exit to restart script -shell -goto start diff --git a/templates/dnsmasq.conf b/templates/dnsmasq.conf index 8bd1d9c..54b6340 100644 --- a/templates/dnsmasq.conf +++ b/templates/dnsmasq.conf @@ -1,4 +1,4 @@ -{{ $tftpIp := (ds "dhcp").tftpIp -}} +{{ $tftpIp := (ds "config").dhcp.tftpIp -}} enable-tftp tftp-root=/tftproot diff --git a/tools/merge.py b/tools/merge similarity index 100% rename from tools/merge.py rename to tools/merge diff --git a/tools/render b/tools/render new file mode 100755 index 0000000..fccd78d --- /dev/null +++ b/tools/render @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT=$(git rev-parse --show-toplevel) +RENDERED=${ROOT}/rendered +TEMPLATES=${ROOT}/templates + +${ROOT}/tools/merge ./nodes > ${RENDERED}/nodes.json + +gomplate --input-dir ${TEMPLATES} --output-dir ${RENDERED} \ + -d nodes=file://${RENDERED}/nodes.json \ + -d config=${ROOT}/config.yaml \ diff --git a/tools/tftpd b/tools/tftpd new file mode 100755 index 0000000..4b092e5 --- /dev/null +++ b/tools/tftpd @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT=$(git rev-parse --show-toplevel) + +IPXE_VERSION=b41bda4413bf286d7b7a449bc05e1531da1eec2e +IPXE_BIN=(bin/ipxe.pxe bin-x86_64-efi/ipxe.efi) + +IPXE_DIR=${ROOT}/.ipxe/ipxe-${IPXE_VERSION} + +function download_ipxe() { + base_dir=$(dirname ${IPXE_DIR}) + # Download the iPXE source if needed + if [ ! -d "${IPXE_DIR}" ]; then + mkdir -p "${base_dir}" + curl -L https://github.com/ipxe/ipxe/archive/${IPXE_VERSION}.tar.gz | tar -xz -C "${base_dir}" + fi +} + +function patch_ipxe() { + # Apply patches to iPXE source + cd "${IPXE_DIR}/src" + sed -i 's/^#undef[\t ]DOWNLOAD_PROTO_HTTPS.*$/#define DOWNLOAD_PROTO_HTTPS/g' config/general.h + + cat > embed.ipxe << EOF +#!ipxe + +dhcp +chain boot.ipxe || shell +EOF + + cd - > /dev/null +} + +function build_ipxe() { + cd "${IPXE_DIR}/src" + for bin in "${IPXE_BIN[@]}"; do + path=${IPXE_DIR}/src/${bin} + if [ ! -f "${path}" ]; then + make -j$(nproc) ${bin} EMBED=embed.ipxe + fi + done + cd - > /dev/null +} + +function render() { + ${ROOT}/tools/render +} + +function host_tftp() { + TFTP_DIR=$(mktemp --tmpdir -d tftp.XXX) + chmod 755 ${TFTP_DIR} + function cleanup() { + rm -rf ${TFTP_DIR} + } + trap cleanup EXIT + + cp ${ROOT}/rendered/boot.ipxe ${TFTP_DIR} + for bin in "${IPXE_BIN[@]}"; do + path=${IPXE_DIR}/src/${bin} + cp ${path} ${TFTP_DIR} + done + + echo "Starting tftpd" + sudo in.tftpd --verbosity 100 --permissive -L --secure ${TFTP_DIR} +} + +download_ipxe +patch_ipxe +build_ipxe +render +host_tftp diff --git a/tools/vm b/tools/vm new file mode 100755 index 0000000..64b2124 --- /dev/null +++ b/tools/vm @@ -0,0 +1,154 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT=$(git rev-parse --show-toplevel) + +VM_NAME="talos-vm" +VCPUS="2" +RAM_MB="2048" +DISK_GB="10" +NETWORK=talos +CONNECTION="qemu:///system" + +function define_network() { + config_file=$(mktemp) + cat > ${config_file} << EOF + + ${NETWORK} + + + + + + + + + + + +EOF + + function cleanup() { + rm ${config_file} + } + trap cleanup EXIT + + if [[ $(virsh --connect="${CONNECTION}" net-list --all | grep -c "${NETWORK}") == "0" ]]; then + virsh --connect="${CONNECTION}" net-define "${config_file}" + virsh --connect="${CONNECTION}" net-start "${NETWORK}" + virsh --connect="${CONNECTION}" net-autostart "${NETWORK}" + fi + + trap - EXIT + cleanup +} + + +function create() { + define_network + + if [[ $(virsh --connect="${CONNECTION}" list --all | grep -c "${VM_NAME}") == "0" ]]; then + virt-install --connect="${CONNECTION}" --name="${VM_NAME}" --vcpus="${VCPUS}" --memory="${RAM_MB}" \ + --os-variant="linux2022" \ + --disk="size=${DISK_GB}" \ + --pxe \ + --sysinfo system.serial=${VM_NAME} \ + --network network="${NETWORK}" + else + echo -n "VM already exists, start it with: + ${0} start +" + exit -1 + fi +} + +function start() { + if [[ $(virsh --connect="${CONNECTION}" list --all | grep -c "${VM_NAME}") > "0" ]]; then + virsh --connect="${CONNECTION}" start ${VM_NAME} + virt-viewer --connect="${CONNECTION}" ${VM_NAME} + else + echo -n "VM doest not exists yet, create it with: + ${0} create +" + exit -1 + fi +} + +function connect() { + if [[ $(virsh --connect="${CONNECTION}" list | grep -c "${VM_NAME}") > "0" ]]; then + virt-viewer --connect="${CONNECTION}" ${VM_NAME} + else + echo "VM is not running" + exit -1 + fi +} + +function stop() { + if [[ $(virsh --connect="${CONNECTION}" list | grep -c "${VM_NAME}") > "0" ]]; then + virsh --connect="${CONNECTION}" shutdown ${VM_NAME} + WAIT=240 + for i in $(seq 0 1 ${WAIT}); do + echo -en "\rWaiting for VM to shutdown... (${i}/${WAIT})" + + if [[ $(virsh --connect="${CONNECTION}" list | grep -c "${VM_NAME}") == "0" ]]; then + echo -e "\nVM successfully shutdown" + exit + fi + + sleep 1 + done + + echo -e "\nDestroying VM" + virsh --connect="${CONNECTION}" destroy ${VM_NAME} + else + echo "VM is not running" + exit -1 + fi +} + +function delete() { + if [[ $(virsh --connect="${CONNECTION}" list --all | grep -c "${VM_NAME}") > "0" ]]; then + if [[ $(virsh --connect="${CONNECTION}" list | grep -c "${VM_NAME}") > "0" ]]; then + virsh --connect="${CONNECTION}" destroy "${VM_NAME}" + fi + virsh --connect="${CONNECTION}" undefine "${VM_NAME}" --remove-all-storage + else + echo "VM doest not exists" + exit -1 + fi + + if [[ $(virsh --connect="${CONNECTION}" net-list --all | grep -c "${NETWORK}") > "0" ]]; then + virsh --connect="${CONNECTION}" net-destroy "${NETWORK}" + virsh --connect="${CONNECTION}" net-undefine "${NETWORK}" + fi +} + +function help() { + echo -n "Available commands: + start + stop + remove + connect +" +} + +COMMAND=${1:-} +case ${COMMAND} in + create) + create Create the vm and perform first install + ;; + start) + start Start the vm + ;; + stop) + stop Stop the vm + ;; + delete) + delete Delete the vm + ;; + connect) + connect Connect to an already running vm + ;; + *) + help + ;; +esac diff --git a/vm/cluster-vm.xml b/vm/cluster-vm.xml deleted file mode 100644 index 5806cd6..0000000 --- a/vm/cluster-vm.xml +++ /dev/null @@ -1,13 +0,0 @@ - - cluster-vm - - - - - - - - - - - diff --git a/vm/create.sh b/vm/create.sh deleted file mode 100755 index ed31df2..0000000 --- a/vm/create.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") -source ${SCRIPT_DIR}/helper.sh - -if [[ $(virsh --connect="${CONNECTION}" net-list --all | grep -c "${NETWORK}") == "0" ]]; then - virsh --connect="${CONNECTION}" net-define "${SCRIPT_DIR}/${NETWORK}.xml" - virsh --connect="${CONNECTION}" net-start "${NETWORK}" - virsh --connect="${CONNECTION}" net-autostart "${NETWORK}" -fi - -virt-install --connect="${CONNECTION}" --name="${VM_NAME}" --vcpus="${VCPUS}" --memory="${RAM_MB}" \ - --os-variant="linux2022" \ - --disk="size=${DISK_GB}" \ - --pxe \ - --network network="${NETWORK}" diff --git a/vm/destroy.sh b/vm/destroy.sh deleted file mode 100755 index efac38b..0000000 --- a/vm/destroy.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") -source ${SCRIPT_DIR}/helper.sh - -virsh --connect="${CONNECTION}" destroy "${VM_NAME}" -virsh --connect="${CONNECTION}" undefine "${VM_NAME}" --remove-all-storage -virsh --connect="${CONNECTION}" net-destroy "${NETWORK}" -virsh --connect="${CONNECTION}" net-undefine "${NETWORK}" diff --git a/vm/helper.sh b/vm/helper.sh deleted file mode 100644 index 6f88d97..0000000 --- a/vm/helper.sh +++ /dev/null @@ -1,10 +0,0 @@ -set -euxo pipefail -VM_NAME="test" -VCPUS="2" -RAM_MB="2048" -DISK_GB="10" -NETWORK=cluster-vm -CONNECTION="qemu:///system" - -IPXE_VERSION=b41bda4413bf286d7b7a449bc05e1531da1eec2e -IPXE_BIN=bin/ipxe.pxe diff --git a/vm/start.sh b/vm/start.sh deleted file mode 100755 index 4023c85..0000000 --- a/vm/start.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") -source ${SCRIPT_DIR}/helper.sh - -virsh --connect="${CONNECTION}" start ${VM_NAME} -virt-viewer --connect="${CONNECTION}" ${VM_NAME} -virsh --connect="${CONNECTION}" shutdown ${VM_NAME} diff --git a/vm/tftp.sh b/vm/tftp.sh deleted file mode 100755 index c43358d..0000000 --- a/vm/tftp.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") -source ${SCRIPT_DIR}/helper.sh - -TFTP_DIR=${SCRIPT_DIR}/../tftp -rm -rf "${TFTP_DIR}" -mkdir -p "${TFTP_DIR}" - -IPXE_DIR=${SCRIPT_DIR}/../ipxe -IPXE_FILE=${IPXE_DIR}/ipxe-${IPXE_VERSION}/src/${IPXE_BIN} -if [ ! -f "${IPXE_FILE}" ]; then - mkdir -p "${IPXE_DIR}" - rm -rf "${IPXE_DIR}/ipxe-${IPXE_VERSION}" - curl -L https://github.com/ipxe/ipxe/archive/${IPXE_VERSION}.tar.gz | tar -xz -C "${IPXE_DIR}" - cd "${IPXE_DIR}/ipxe-${IPXE_VERSION}/src" - sed -i 's/^#undef[\t ]DOWNLOAD_PROTO_HTTPS.*$/#define DOWNLOAD_PROTO_HTTPS/g' config/general.h - make -j$(nproc) ${IPXE_BIN} - cd - -fi - -${SCRIPT_DIR}/../generate.sh - -cp ${SCRIPT_DIR}/../rendered/boot.ipxe ${TFTP_DIR} -cp ${IPXE_FILE} ${TFTP_DIR} - -sudo in.tftpd -L --secure ./tftp