From 40852f73885cb942776fffbdd21cfb1b220da1a0 Mon Sep 17 00:00:00 2001 From: Matthias Riegler Date: Mon, 21 Jul 2025 21:51:40 +0200 Subject: [PATCH] feat: add talos linux (very much a WIP!) Signed-off-by: Matthias Riegler --- talos/Makefile | 15 +++++ talos/README.md | 20 ++++++ talos/docker/Dockerfile | 41 +++++++++++++ talos/docker/launch.py | 133 ++++++++++++++++++++++++++++++++++++++++ talos/download.sh | 18 ++++++ 5 files changed, 227 insertions(+) create mode 100644 talos/Makefile create mode 100644 talos/README.md create mode 100644 talos/docker/Dockerfile create mode 100755 talos/docker/launch.py create mode 100755 talos/download.sh diff --git a/talos/Makefile b/talos/Makefile new file mode 100644 index 00000000..e60b4ad8 --- /dev/null +++ b/talos/Makefile @@ -0,0 +1,15 @@ +VENDOR=Siderolabs +NAME=Talos +IMAGE_FORMAT=qcow2 +IMAGE_GLOB=*.qcow2 + +VERSION=$(shell echo $(IMAGE) | sed -e 's/\(.\+\)-metal-amd64.*/\1/') + +-include ../makefile-sanity.include +-include ../makefile.include + +download: + /bin/bash download.sh + +build: download + $(MAKE) docker-image diff --git a/talos/README.md b/talos/README.md new file mode 100644 index 00000000..0af6f8af --- /dev/null +++ b/talos/README.md @@ -0,0 +1,20 @@ +# Talos Linux VM + +The [download.sh](download.sh) script will download Talos Linux with serial console enabled. The version is set in the script and can be changed manually. + + +The following command can be executed to download and build the Talos Linux VM container: + +```bash +make build +``` + +The resulting container will be tagged as `vrnetlab/siderolabs_talos:`. + +## Host requirements + +* 4 vCPU, 4 GB RAM + +## Configuration + +No initial configuration is provided (yet) diff --git a/talos/docker/Dockerfile b/talos/docker/Dockerfile new file mode 100644 index 00000000..415bd71f --- /dev/null +++ b/talos/docker/Dockerfile @@ -0,0 +1,41 @@ +FROM public.ecr.aws/docker/library/debian:bookworm-slim AS builder + +ARG DISK_SIZE=64G + +RUN apt-get update -qy && \ + apt-get install -y --no-install-recommends qemu-utils && \ + rm -rf /var/lib/apt/lists/* + +ARG IMAGE +COPY $IMAGE* / +RUN qemu-img resize /$IMAGE $DISK_SIZE + +FROM public.ecr.aws/docker/library/debian:bookworm-slim + +ARG DEBIAN_FRONTEND=noninteractive +ARG DISK_SIZE=64G + +RUN apt-get update -qy \ + && apt-get install -y --no-install-recommends\ + bridge-utils \ + iproute2 \ + socat \ + qemu-kvm \ + tcpdump \ + ssh \ + inetutils-ping \ + dnsutils \ + iptables \ + nftables \ + telnet \ + cloud-utils \ + sshpass \ + && rm -rf /var/lib/apt/lists/* + +ARG IMAGE +COPY --from=builder $IMAGE* / +COPY *.py / + +EXPOSE 22 5000 10000-10099 +HEALTHCHECK CMD ["/healthcheck.py"] +ENTRYPOINT ["/launch.py"] diff --git a/talos/docker/launch.py b/talos/docker/launch.py new file mode 100755 index 00000000..dd8424ca --- /dev/null +++ b/talos/docker/launch.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 + +import datetime +import logging +import os +import re +import signal +import subprocess +import sys + +import vrnetlab + + +def handle_SIGCHLD(signal, frame): + os.waitpid(-1, os.WNOHANG) + + +def handle_SIGTERM(signal, frame): + sys.exit(0) + + +signal.signal(signal.SIGINT, handle_SIGTERM) +signal.signal(signal.SIGTERM, handle_SIGTERM) +signal.signal(signal.SIGCHLD, handle_SIGCHLD) + +TRACE_LEVEL_NUM = 9 +logging.addLevelName(TRACE_LEVEL_NUM, "TRACE") + + +def trace(self, message, *args, **kws): + # Yes, logger takes its '*args' as 'args'. + if self.isEnabledFor(TRACE_LEVEL_NUM): + self._log(TRACE_LEVEL_NUM, message, args, **kws) + + +logging.Logger.trace = trace + + +class Talos_vm(vrnetlab.VM): + def __init__( + self, + nics, + conn_mode, + ): + for e in os.listdir("/"): + if re.search(".qcow2$", e): + disk_image = "/" + e + + super(Talos_vm, self).__init__( + "","", + disk_image=disk_image, ram=4192 + ) + + self.num_nics = nics + self.nic_type = "virtio-net-pci" + self.conn_mode = conn_mode + + if "ADD_DISK" in os.environ: + disk_size = os.getenv("ADD_DISK") + + self.add_disk(disk_size) + + + def bootstrap_spin(self): + """This function should be called periodically to do work.""" + return + + def gen_mgmt(self): + """ + Augment the parent class function to change the PCI bus + """ + # call parent function to generate the mgmt interface + res = super(Talos_vm, self).gen_mgmt() + + # This is a copy paste from ubuntu + if "bus=pci.1" not in res[-3]: + res[-3] = res[-3] + ",bus=pci.1" + return res + + def add_disk(self, disk_size, driveif="ide"): + additional_disk = f"disk_{disk_size}.qcow2" + + if not os.path.exists(additional_disk): + self.logger.debug(f"Creating additional disk image {additional_disk}") + vrnetlab.run_command( + ["qemu-img", "create", "-f", "qcow2", additional_disk, disk_size] + ) + + self.qemu_args.extend( + [ + "-drive", + f"if={driveif},file={additional_disk}", + ] + ) + + +class Talos(vrnetlab.VR): + def __init__(self, nics, conn_mode): + super(Talos, self).__init__("", "") + self.vms = [Talos_vm(nics, conn_mode)] + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="") + parser.add_argument( + "--trace", action="store_true", help="enable trace level logging" + ) + parser.add_argument("--nics", type=int, default=4, help="Number of NICS") + parser.add_argument("--username", default="not supported", help="NOOP") + parser.add_argument("--password", default="not supported", help="NOOP") + parser.add_argument("--hostname", default="not supported", help="NOOP") + parser.add_argument( + "--connection-mode", + default="tc", + help="Connection mode to use in the datapath", + ) + args = parser.parse_args() + + LOG_FORMAT = "%(asctime)s: %(module)-10s %(levelname)-8s %(message)s" + logging.basicConfig(format=LOG_FORMAT) + logger = logging.getLogger() + + logger.setLevel(logging.DEBUG) + if args.trace: + logger.setLevel(1) + + vr = Talos( + args.nics, + args.connection_mode, + ) + vr.start() diff --git a/talos/download.sh b/talos/download.sh new file mode 100755 index 00000000..6505bcb9 --- /dev/null +++ b/talos/download.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +version="v1.10.5" + +# Download latest jammy lts cloud image +download_url="https://factory.talos.dev/image/9ddb0c3e6bf64299a4013243fd14a209af1d0626ebf8e1eb4a151f897cd8f8f2/${version}/metal-amd64.qcow2" + +# Extract the filename from the URL +filename="$version-metal-amd64.qcow2" + +# Check if the file already exists in the current directory +if [ -e "$filename" ]; then + echo "File $filename already exists. Skipping download." +else + # Download the URL + curl -o $filename "$download_url" + echo "Download complete: $filename" +fi