Add multi-distro support for deckies
Introduces DistroProfile catalog (9 distros: Debian, Ubuntu 20/22, Rocky 9, CentOS 7, Alpine, Fedora, Kali, Arch) with distro-styled hostname generation. Adds --distro and --randomize-distros CLI flags, a `decnet distros` listing command, and fixes composer.py which was ignoring per-decky base_image in favour of a hardcoded Debian constant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
119
decnet/distros.py
Normal file
119
decnet/distros.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
Distro profiles for DECNET deckies.
|
||||
|
||||
Each profile maps a human-readable slug to a Docker image and OS metadata used
|
||||
to make deckies look like heterogeneous real machines on the LAN.
|
||||
"""
|
||||
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DistroProfile:
|
||||
slug: str # CLI-facing identifier, e.g. "debian", "rocky9"
|
||||
image: str # Docker image tag
|
||||
display_name: str # Human-readable label shown in tables
|
||||
hostname_style: str # "generic" | "rhel" | "minimal" | "rolling"
|
||||
|
||||
|
||||
DISTROS: dict[str, DistroProfile] = {
|
||||
"debian": DistroProfile(
|
||||
slug="debian",
|
||||
image="debian:bookworm-slim",
|
||||
display_name="Debian 12 (Bookworm)",
|
||||
hostname_style="generic",
|
||||
),
|
||||
"ubuntu22": DistroProfile(
|
||||
slug="ubuntu22",
|
||||
image="ubuntu:22.04",
|
||||
display_name="Ubuntu 22.04 LTS (Jammy)",
|
||||
hostname_style="generic",
|
||||
),
|
||||
"ubuntu20": DistroProfile(
|
||||
slug="ubuntu20",
|
||||
image="ubuntu:20.04",
|
||||
display_name="Ubuntu 20.04 LTS (Focal)",
|
||||
hostname_style="generic",
|
||||
),
|
||||
"rocky9": DistroProfile(
|
||||
slug="rocky9",
|
||||
image="rockylinux:9-minimal",
|
||||
display_name="Rocky Linux 9",
|
||||
hostname_style="rhel",
|
||||
),
|
||||
"centos7": DistroProfile(
|
||||
slug="centos7",
|
||||
image="centos:7",
|
||||
display_name="CentOS 7",
|
||||
hostname_style="rhel",
|
||||
),
|
||||
"alpine": DistroProfile(
|
||||
slug="alpine",
|
||||
image="alpine:3.19",
|
||||
display_name="Alpine Linux 3.19",
|
||||
hostname_style="minimal",
|
||||
),
|
||||
"fedora": DistroProfile(
|
||||
slug="fedora",
|
||||
image="fedora:39",
|
||||
display_name="Fedora 39",
|
||||
hostname_style="rhel",
|
||||
),
|
||||
"kali": DistroProfile(
|
||||
slug="kali",
|
||||
image="kalilinux/kali-rolling",
|
||||
display_name="Kali Linux (Rolling)",
|
||||
hostname_style="rolling",
|
||||
),
|
||||
"arch": DistroProfile(
|
||||
slug="arch",
|
||||
image="archlinux:latest",
|
||||
display_name="Arch Linux",
|
||||
hostname_style="rolling",
|
||||
),
|
||||
}
|
||||
|
||||
_NAME_WORDS = [
|
||||
"alpha", "bravo", "charlie", "delta", "echo",
|
||||
"foxtrot", "golf", "hotel", "india", "juliet",
|
||||
"kilo", "lima", "mike", "nova", "oscar",
|
||||
"prod", "web", "db", "mail", "proxy",
|
||||
"dev", "stage", "backup", "monitor", "files",
|
||||
]
|
||||
|
||||
|
||||
def random_hostname(distro_slug: str = "debian") -> str:
|
||||
"""Generate a plausible hostname for the given distro style."""
|
||||
profile = DISTROS.get(distro_slug)
|
||||
style = profile.hostname_style if profile else "generic"
|
||||
word = random.choice(_NAME_WORDS)
|
||||
num = random.randint(10, 99)
|
||||
|
||||
if style == "rhel":
|
||||
# RHEL/CentOS/Fedora convention: word+num.localdomain
|
||||
return f"{word}{num}.localdomain"
|
||||
elif style == "minimal":
|
||||
return f"{word}-{num}"
|
||||
elif style == "rolling":
|
||||
# Kali/Arch: just a word, no suffix
|
||||
return f"{word}-{random.choice(_NAME_WORDS)}"
|
||||
else:
|
||||
# Debian/Ubuntu: SRV-WORD-nn
|
||||
return f"SRV-{word.upper()}-{num}"
|
||||
|
||||
|
||||
def get_distro(slug: str) -> DistroProfile:
|
||||
if slug not in DISTROS:
|
||||
raise ValueError(
|
||||
f"Unknown distro '{slug}'. Available: {', '.join(sorted(DISTROS))}"
|
||||
)
|
||||
return DISTROS[slug]
|
||||
|
||||
|
||||
def random_distro() -> DistroProfile:
|
||||
return random.choice(list(DISTROS.values()))
|
||||
|
||||
|
||||
def all_distros() -> dict[str, DistroProfile]:
|
||||
return dict(DISTROS)
|
||||
Reference in New Issue
Block a user