This commit is contained in:
2025-09-19 09:58:36 +02:00
parent 7714cdb2d7
commit 55035ba890

View File

@@ -8,6 +8,7 @@ import argparse
import time import time
import json import json
def run_command(command_str, description, ignore_errors=False): def run_command(command_str, description, ignore_errors=False):
print(f"\n--- {description} ---") print(f"\n--- {description} ---")
try: try:
@@ -30,30 +31,36 @@ def run_command(command_str, description, ignore_errors=False):
sys.exit(1) sys.exit(1)
return None return None
def wait_for_network(container_name): def wait_for_network(container_name):
print(f"\n--- Warte auf Netzwerk für Container '{container_name}' ---") print(f"\n--- Warte auf Netzwerk für Container '{container_name}' ---")
for i in range(30): for i in range(30):
print(f"Versuch {i+1}/30...") print(f"Versuch {i+1}/30...")
try: try:
result = run_command(f"lxc list {container_name} --format json", "Frage Container-Status ab", ignore_errors=True) result = run_command(f"lxc list {
container_name} --format json", "Frage Container-Status ab", ignore_errors=True)
if result and result.returncode == 0: if result and result.returncode == 0:
data = json.loads(result.stdout) data = json.loads(result.stdout)
if data and data[0]["state"] and data[0]["state"]["network"] and "eth0" in data[0]["state"]["network"]: if data and data[0]["state"] and data[0]["state"]["network"] and "eth0" in data[0]["state"]["network"]:
addresses = data[0]["state"]["network"]["eth0"]["addresses"] addresses = data[0]["state"]["network"]["eth0"]["addresses"]
for addr in addresses: for addr in addresses:
if addr["family"] == "inet": if addr["family"] == "inet":
print(f"Container '{container_name}' hat IP-Adresse {addr['address']} erhalten.") print(f"Container '{
container_name}' hat IP-Adresse {addr['address']} erhalten.")
return return
except (json.JSONDecodeError, IndexError, KeyError): except (json.JSONDecodeError, IndexError, KeyError):
pass pass
time.sleep(2) time.sleep(2)
print(f"FEHLER: Container '{container_name}' hat nach 60 Sekunden keine IP-Adresse erhalten.", file=sys.stderr) print(f"FEHLER: Container '{
container_name}' hat nach 60 Sekunden keine IP-Adresse erhalten.", file=sys.stderr)
sys.exit(1) sys.exit(1)
def get_container_ip(container_name): def get_container_ip(container_name):
for _ in range(15): for _ in range(15):
try: try:
result = run_command(f"lxc list {container_name} --format json", "Frage Container-IP ab", ignore_errors=True) result = run_command(f"lxc list {
container_name} --format json", "Frage Container-IP ab", ignore_errors=True)
if result and result.returncode == 0: if result and result.returncode == 0:
data = json.loads(result.stdout) data = json.loads(result.stdout)
if data and data[0]["state"] and data[0]["state"]["network"] and "eth0" in data[0]["state"]["network"]: if data and data[0]["state"] and data[0]["state"]["network"] and "eth0" in data[0]["state"]["network"]:
@@ -66,19 +73,29 @@ def get_container_ip(container_name):
time.sleep(2) time.sleep(2)
return None return None
def main(): def main():
parser = argparse.ArgumentParser(description="Erstellt und konfiguriert mehrere LXD-Container.") parser = argparse.ArgumentParser(
parser.add_argument("-c", "--count", type=int, default=1, help="Anzahl der zu erstellenden Container.") description="Erstellt und konfiguriert mehrere LXD-Container.")
parser.add_argument("-b", "--basename", default="vm", help="Basisname für die Container.") parser.add_argument("-c", "--count", type=int, default=1,
parser.add_argument("--hostname-template", default="{basename}-{i:02d}", help="Vorlage für den Hostnamen, z.B. 'webserver-{i}'. Unterstützt {basename} und {i}.") help="Anzahl der zu erstellenden Container.")
parser.add_argument("-p", "--root-password", required=True, help="Root-Passwort für alle Container.") parser.add_argument("-b", "--basename", default="vm",
parser.add_argument("--port-start", type=int, default=2201, help="Start-Port für die SSH-Weiterleitung.") help="Basisname für die Container.")
parser.add_argument("--image", default="images:archlinux", help="Zu verwendendes Image (z.B. images:archlinux, ubuntu:22.04).") parser.add_argument("--hostname-template", default="{basename}-{i:02d}",
parser.add_argument("--no-port-forward", action="store_true", help="Überspringt die Einrichtung der SSH-Port-Weiterleitung.") help="Vorlage für den Hostnamen, z.B. 'webserver-{i}'. Unterstützt {basename} und {i}.")
parser.add_argument("-p", "--root-password", required=True,
help="Root-Passwort für alle Container.")
parser.add_argument("--port-start", type=int, default=2201,
help="Start-Port für die SSH-Weiterleitung.")
parser.add_argument("--image", default="images:archlinux",
help="Zu verwendendes Image (z.B. images:archlinux, ubuntu:22.04).")
parser.add_argument("--no-port-forward", action="store_true",
help="Überspringt die Einrichtung der SSH-Port-Weiterleitung.")
args = parser.parse_args() args = parser.parse_args()
if args.count > 1 and '{i}' not in args.hostname_template: if args.count > 1 and '{i}' not in args.hostname_template:
print("FEHLER: Bei --count > 1 muss die --hostname-template den Platzhalter '{i}' enthalten, um eindeutige Namen zu gewährleisten.", file=sys.stderr) print(
"FEHLER: Bei --count > 1 muss die --hostname-template den Platzhalter '{i}' enthalten, um eindeutige Namen zu gewährleisten.", file=sys.stderr)
sys.exit(1) sys.exit(1)
run_command("command -v lxc >/dev/null", "Prüfe, ob LXD installiert ist") run_command("command -v lxc >/dev/null", "Prüfe, ob LXD installiert ist")
@@ -86,8 +103,10 @@ def main():
if not os.path.exists("out"): if not os.path.exists("out"):
os.makedirs("out") os.makedirs("out")
with open(csv_path, "w") as f:
f.write("name,ip\n")
elif not os.path.isdir("out"): elif not os.path.isdir("out"):
os.remove("out") # os.remove("out")
os.makedirs("out") os.makedirs("out")
clients = [] clients = []
@@ -98,12 +117,15 @@ def main():
print(f"{'=' * 20} ERSTELLE CONTAINER: {vm_name} {'=' * 20}") print(f"{'=' * 20} ERSTELLE CONTAINER: {vm_name} {'=' * 20}")
run_command(f"lxc delete {vm_name} --force", f"{vm_name} vorsorglich löschen", ignore_errors=True) run_command(f"lxc delete {vm_name} --force",
run_command(f"lxc launch {args.image} {vm_name}", f"Starte {vm_name} von Image") f"{vm_name} vorsorglich löschen", ignore_errors=True)
run_command(f"lxc launch {args.image} {
vm_name}", f"Starte {vm_name} von Image")
wait_for_network(vm_name) wait_for_network(vm_name)
distro_id_command = "lxc exec {} -- cat /etc/os-release | grep '^ID=' | cut -d'=' -f2" distro_id_command = "lxc exec {} -- cat /etc/os-release | grep '^ID=' | cut -d'=' -f2"
distro_id_result = run_command(distro_id_command.format(vm_name), f"Ermittle Distributions-ID für {vm_name}") distro_id_result = run_command(distro_id_command.format(
vm_name), f"Ermittle Distributions-ID für {vm_name}")
distro_id = distro_id_result.stdout.strip().replace('"', '') distro_id = distro_id_result.stdout.strip().replace('"', '')
install_cmd = "" install_cmd = ""
@@ -114,17 +136,22 @@ def main():
elif distro_id == "fedora": elif distro_id == "fedora":
install_cmd = "dnf install -y python3" install_cmd = "dnf install -y python3"
else: else:
print(f"FEHLER: Nicht unterstützte Distribution: {distro_id}", file=sys.stderr) print(f"FEHLER: Nicht unterstützte Distribution: {
distro_id}", file=sys.stderr)
sys.exit(1) sys.exit(1)
run_command(f"lxc exec {vm_name} -- {install_cmd}", f"Installiere Python in {vm_name}") run_command(f"lxc exec {vm_name} -- {install_cmd}",
f"Installiere Python in {vm_name}")
run_command(f"lxc file push setup_container.py {vm_name}/root/", f"Kopiere Setup-Skript nach {vm_name}") run_command(f"lxc file push setup_container.py {
run_command(f"lxc exec {vm_name} -- python3 /root/setup_container.py '{args.root_password}' '{vm_name}' '{distro_id}'", f"Führe Konfiguration in {vm_name} aus") vm_name}/root/", f"Kopiere Setup-Skript nach {vm_name}")
run_command(f"lxc exec {vm_name} -- python3 /root/setup_container.py '{
args.root_password}' '{vm_name}' '{distro_id}'", f"Führe Konfiguration in {vm_name} aus")
ssh_info = "" ssh_info = ""
if not args.no_port_forward: if not args.no_port_forward:
run_command(f"lxc config device add {vm_name} ssh-proxy proxy listen=tcp:0.0.0.0:{ssh_port} connect=tcp:127.0.0.1:22", f"Richte Port-Weiterleitung für {vm_name} ein") run_command(f"lxc config device add {vm_name} ssh-proxy proxy listen=tcp:0.0.0.0:{
ssh_port} connect=tcp:127.0.0.1:22", f"Richte Port-Weiterleitung für {vm_name} ein")
ssh_info = f"SSH-Zugang: ssh root@localhost -p {ssh_port}" ssh_info = f"SSH-Zugang: ssh root@localhost -p {ssh_port}"
else: else:
print("\n--- Überspringe Port-Weiterleitung ---") print("\n--- Überspringe Port-Weiterleitung ---")
@@ -132,22 +159,26 @@ def main():
ip_address = get_container_ip(vm_name) ip_address = get_container_ip(vm_name)
if ip_address: if ip_address:
clients.append({"name": vm_name, "ip": ip_address}) clients.append({"name": vm_name, "ip": ip_address})
print(f"\n>>> Container {vm_name} erfolgreich erstellt und konfiguriert! <<<") print(f"\n>>> Container {
vm_name} erfolgreich erstellt und konfiguriert! <<<")
if ssh_info: if ssh_info:
print(ssh_info) print(ssh_info)
print(f"IP-Adresse: {ip_address}") print(f"IP-Adresse: {ip_address}")
else: else:
print(f"\nFEHLER: IP-Adresse für Container {vm_name} konnte nicht abgerufen werden.", file=sys.stderr) print(f"\nFEHLER: IP-Adresse für Container {
vm_name} konnte nicht abgerufen werden.", file=sys.stderr)
if clients: if clients:
csv_path = "out/clients.csv" csv_path = "out/clients.csv"
with open(csv_path, "w") as f: with open(csv_path, "a") as f:
f.write("name,ip\n") # f.write("name,ip\n")
for client in clients: for client in clients:
f.write(f"{client['name']},{client['ip']}\n") f.write(f"{client['name']},{client['ip']}\n")
print(f"\nAlle Container wurden erstellt und in {csv_path} gespeichert.") print(f"\nAlle Container wurden erstellt und in {
csv_path} gespeichert.")
else: else:
print("\nKeine Container wurden erstellt.") print("\nKeine Container wurden erstellt.")
if __name__ == "__main__": if __name__ == "__main__":
main() main()