Compare commits
5 Commits
fa54461cae
...
43a18074e0
| Author | SHA1 | Date | |
|---|---|---|---|
| 43a18074e0 | |||
| 8615d9fde1 | |||
| 06ba89697b | |||
| 3f17885d55 | |||
| 3739da37dc |
@ -21,6 +21,13 @@ BACKEND_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "${BACKEND_DIR}/../.." && pwd)"
|
||||
POSTINSTALL_SCRIPT="${BACKEND_DIR}/postinstall.sh"
|
||||
IMAGE_DEPLOY_LIB="${REPO_DIR}/scripts/lib/image_deploy.sh"
|
||||
BOOT_MEDIUM_LIB="${REPO_DIR}/scripts/lib/boot_medium.sh"
|
||||
|
||||
# Vom Netzwerkmodul geschuetzt abgelegtes WLAN-/Ethernet-Verbindungsprofil
|
||||
# (siehe export_connection_profile() in 05_network.sh) - existiert nur,
|
||||
# wenn das Provisionierungsinterface ueber ein aktives NetworkManager-
|
||||
# Profil lief (z.B. WLAN mit PSK).
|
||||
NETWORK_PROFILE_EXPORT="/run/tuxflotte/network/connection.nmconnection"
|
||||
|
||||
RUNTIME_BLUEPRINT_FILE="/run/tuxflotte/runtime/runtime_blueprint.json"
|
||||
SERVER_RESPONSE_FILE="/run/tuxflotte/server/response.json"
|
||||
@ -52,38 +59,15 @@ backend_fatal() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Live auf echter USB-Stick-Hardware gefunden (01.09.2026): auf einem
|
||||
# CD-ROM (wie in allen bisherigen QEMU-Tests) ist das Boot-Medium ein
|
||||
# eigener Geraetetyp ("rom"), der von der lsblk-Filterung unten schon
|
||||
# ausgeschlossen wird - auf einem echten USB-Stick ist das Boot-Medium
|
||||
# selbst aber ein ganz normales "disk"-Blockgeraet, genau wie die interne
|
||||
# Zielplatte. Ohne Ausschluss kann die Erkennung darunter den Stick selbst
|
||||
# waehlen (abhaengig von der Aufzaehlungsreihenfolge) - "parted mklabel"
|
||||
# darauf schlaegt dann IMMER fehl ("...have been unable to inform the
|
||||
# Kernel of the change, probably because it/they are in use"), weil man
|
||||
# nicht die Platte partitionieren kann, von der man gerade lebt. Der
|
||||
# vorherige Fix (Zielplatte freigeben + Neueinlesen wiederholen) half
|
||||
# deshalb nicht - das war die falsche Diagnose fuer dieses Problem.
|
||||
_mint_image_boot_medium_disk() {
|
||||
local medium_source parent
|
||||
|
||||
medium_source="$(findmnt --noheadings --output SOURCE /run/live/medium 2>/dev/null)" || return 1
|
||||
[[ -n "${medium_source}" ]] || return 1
|
||||
|
||||
parent="$(lsblk --noheadings --output PKNAME "${medium_source}" 2>/dev/null | head -n1)"
|
||||
[[ -n "${parent}" ]] || return 1
|
||||
|
||||
printf '/dev/%s\n' "${parent}"
|
||||
}
|
||||
|
||||
# Analog zu _tuxflotte_detect_target_disk() in backends/mint/backend.sh -
|
||||
# bewusst hier dupliziert statt geteilt, um dieses Backend unabhaengig vom
|
||||
# Mint-Referenzbackend zu halten (siehe Modul-Kommentar oben). Ein Umzug in
|
||||
# eine gemeinsame lib waere ein sinnvolles spaeteres Aufraeumen, sobald
|
||||
# mehr als zwei Backends dieselbe Logik brauchen.
|
||||
# Zieldatentraeger-Erkennung - schliesst das Boot-Medium selbst aus (siehe
|
||||
# tuxflotte_boot_medium_disk() in scripts/lib/boot_medium.sh: auf echter
|
||||
# USB-Stick-Hardware enumeriert das Medium als normales "disk"-Blockgeraet,
|
||||
# genau wie die Zielplatte - ohne Ausschluss schlaegt "parted mklabel"
|
||||
# darauf IMMER fehl, weil man nicht die Platte partitionieren kann, von der
|
||||
# man gerade lebt).
|
||||
_mint_image_detect_target_disk() {
|
||||
local boot_medium_disk
|
||||
boot_medium_disk="$(_mint_image_boot_medium_disk || true)"
|
||||
boot_medium_disk="$(tuxflotte_boot_medium_disk || true)"
|
||||
|
||||
lsblk --nodeps --noheadings --bytes --output NAME,TYPE,SIZE --paths |
|
||||
awk -v exclude="${boot_medium_disk}" '
|
||||
@ -119,6 +103,11 @@ backend_init() {
|
||||
# shellcheck source=../../scripts/lib/image_deploy.sh
|
||||
source "${IMAGE_DEPLOY_LIB}"
|
||||
|
||||
[[ -r "${BOOT_MEDIUM_LIB}" ]] ||
|
||||
{ backend_fatal "Boot-Medium-Bibliothek nicht gefunden: ${BOOT_MEDIUM_LIB}"; return 1; }
|
||||
# shellcheck source=../../scripts/lib/boot_medium.sh
|
||||
source "${BOOT_MEDIUM_LIB}"
|
||||
|
||||
[[ -r "${POSTINSTALL_SCRIPT}" ]] ||
|
||||
{ backend_fatal "Postinstall-Skript nicht gefunden: ${POSTINSTALL_SCRIPT}"; return 1; }
|
||||
|
||||
@ -241,6 +230,16 @@ backend_launch() {
|
||||
backend_log "Mounte unter ${TARGET_DIR}"
|
||||
image_deploy_mount "${TARGET_DIR}" "${boot_part}" "${root_part}" || return 1
|
||||
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): export_connection_profile()
|
||||
# in 05_network.sh legt das aktive WLAN-/Ethernet-Profil zwar geschuetzt unter
|
||||
# /run/tuxflotte/network/connection.nmconnection ab, aber bis hierhin holte es
|
||||
# nie jemand von dort ab - das frisch installierte System stand deshalb beim
|
||||
# ersten Boot ohne gespeichertes WLAN-Profil da und fragte den PSK erneut ab.
|
||||
if [[ -f "${NETWORK_PROFILE_EXPORT}" ]]; then
|
||||
backend_log "Übernehme WLAN-/Netzwerkverbindungsprofil ins Zielsystem"
|
||||
image_deploy_install_network_profile "${TARGET_DIR}" "${NETWORK_PROFILE_EXPORT}" || return 1
|
||||
fi
|
||||
|
||||
# Live gefunden (31.08.2026, erster echter End-to-End-Lauf mit dem vollen
|
||||
# Referenz-VM-Archiv): "erst nach /run/tuxflotte/... herunterladen, dann
|
||||
# entpacken" scheiterte an /run (RAM-Tmpfs, viel kleiner als das
|
||||
@ -335,6 +334,19 @@ backend_postinstall() {
|
||||
umount --recursive "${TARGET_DIR}" ||
|
||||
{ backend_fatal "${TARGET_DIR} konnte nicht ausgehängt werden."; return 1; }
|
||||
|
||||
# Nutzer-Feedback (01.09.2026, echter Hardware-Test): akustischer Hinweis
|
||||
# + klare Ansage, dass der USB-Stick jetzt gefahrlos gezogen werden kann
|
||||
# (ab hier wird nur noch von der Zielplatte gebootet) - Bestaetigung mit
|
||||
# ENTER, sonst automatischer Neustart nach 5 Minuten (kein Warten auf
|
||||
# eine Person, die vielleicht schon weitergegangen ist). "\a" (BEL) statt
|
||||
# eines externen Tonwerkzeugs - funktioniert auf jeder PC-Lautsprecher-
|
||||
# Hardware ohne zusaetzliches Paket.
|
||||
printf '\a\a\a'
|
||||
echo
|
||||
echo "Fertig! Sie können den USB-Stick jetzt entfernen."
|
||||
echo "Weiter mit ENTER, oder automatischer Neustart in 5 Minuten."
|
||||
read -r -t 300 _ || true
|
||||
|
||||
backend_log "Starte neu - kein Rücksprung erwartet, ab hier läuft das frisch installierte System."
|
||||
reboot
|
||||
}
|
||||
|
||||
@ -16,7 +16,12 @@ tuxflotte_agent_fatal() {
|
||||
ANODE_URL="https://anode.tuxflotte.de"
|
||||
AGENT_REPO_RAW="https://git.tuxflotte.de/admin/provisioning-agent/raw/branch/main"
|
||||
|
||||
install -d -m 0700 /etc/tuxflotte ||
|
||||
# 0755 statt 0700: /etc/tuxflotte muss fuer alle Nutzer durchsuchbar sein,
|
||||
# damit device_fingerprint (siehe unten) ohne sudo lesbar ist - einzelne
|
||||
# Dateien darin bleiben trotzdem individuell geschuetzt (agent.credentials
|
||||
# unten explizit chmod 0600, genau wie /etc/shadow trotz durchsuchbarem
|
||||
# /etc selbst geschuetzt bleibt).
|
||||
install -d -m 0755 /etc/tuxflotte ||
|
||||
tuxflotte_agent_fatal "Verzeichnis /etc/tuxflotte konnte nicht angelegt werden."
|
||||
|
||||
# Identifikation soll in beide Richtungen moeglich sein: die Geraeteliste
|
||||
@ -24,10 +29,15 @@ install -d -m 0700 /etc/tuxflotte ||
|
||||
# gab es auf dem installierten Geraet selbst keine Datei, um ihn mit einem
|
||||
# einfachen "cat" gegenzupruefen - build_device_fingerprint() (10_hardware.sh)
|
||||
# berechnet ihn nur einmalig waehrend des Live-Boots und haelt ihn sonst
|
||||
# nirgends fest. Absichtlich Klartext, kein Secret - reiner Hardware-Hash,
|
||||
# kein chmod 0600 noetig wie bei agent.credentials.
|
||||
# nirgends fest. Absichtlich Klartext, kein Secret - reiner Hardware-Hash.
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): trotz dieser Absicht
|
||||
# war die Datei vorher nicht ohne sudo lesbar, weil das umschliessende
|
||||
# Verzeichnis 0700 war (siehe oben) - explizites chmod 0644 hier, damit
|
||||
# sich das nicht auf einen zufaelligen umask verlaesst.
|
||||
echo "${TUXFLOTTE_DEVICE_FINGERPRINT}" > /etc/tuxflotte/device_fingerprint ||
|
||||
tuxflotte_agent_fatal "device_fingerprint konnte nicht abgelegt werden."
|
||||
chmod 0644 /etc/tuxflotte/device_fingerprint ||
|
||||
tuxflotte_agent_fatal "Rechte fuer device_fingerprint konnten nicht gesetzt werden."
|
||||
|
||||
cat > /etc/tuxflotte/runtime_blueprint.json <<'RUNTIME_BLUEPRINT_EOF'
|
||||
${TUXFLOTTE_BLUEPRINTS_JSON}
|
||||
|
||||
32
boot-medium/config/bootloaders/syslinux_common/splash.svg
Normal file
32
boot-medium/config/bootloaders/syslinux_common/splash.svg
Normal file
File diff suppressed because one or more lines are too long
@ -8,8 +8,8 @@ Wants=NetworkManager.service
|
||||
Type=idle
|
||||
ExecStart=/opt/tuxflotte/scripts/boot-autostart.sh
|
||||
StandardInput=tty-force
|
||||
StandardOutput=journal+console
|
||||
StandardError=journal+console
|
||||
StandardOutput=console
|
||||
StandardError=console
|
||||
TTYPath=/dev/tty1
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
@ -18,6 +18,22 @@ TTYVHangup=yes
|
||||
# TTY-Teardown). Geloest stattdessen in boot-autostart.sh selbst: die
|
||||
# Konsole faellt nach installer.sh deterministisch in eine interaktive
|
||||
# Shell, kein getty-Neustart noetig.
|
||||
#
|
||||
# StandardOutput/Error war zuerst "journal+console" (journalctl konnte die
|
||||
# Installationsausgabe so zusaetzlich zeigen) - Nutzer-Feedback (01.09.2026,
|
||||
# echter Hardware-Test): "Der Kernel Zeitstempel ist für den Kunden nicht
|
||||
# interessant, sondern nur verwirrend." journald spiegelt Konsolen-Ausgaben
|
||||
# im kmsg-Stil mit einem monotonen "[ 12.345678]"-Zeitstempel je Zeile -
|
||||
# genau das sah aus wie ein Kernel-Log, obwohl es nur unsere eigene
|
||||
# Installationsausgabe war. "console" (ohne "journal+") verbindet fd1/fd2
|
||||
# stattdessen direkt mit /dev/tty1, ohne journald-Vermittlung: kein
|
||||
# Zeitstempel-Praefix mehr, und "-t 1" ist ab hier wieder ein echter,
|
||||
# zuverlaessiger tty-Test (siehe TUXFLOTTE_TTY_LIVE in boot-autostart.sh).
|
||||
# journalctl -u tuxflotte-installer zeigt dadurch nur noch die eigenen
|
||||
# Lebenszyklus-Meldungen des Dienstes, nicht mehr die volle
|
||||
# Installationsausgabe - die bleibt weiterhin vollstaendig in
|
||||
# /var/log/tuxflotte-installer.log einsehbar (das war laut Kopfkommentar
|
||||
# oben ohnehin schon der dokumentierte Weg, "ohne journalctl zu brauchen").
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@ -21,6 +21,13 @@ BACKEND_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "${BACKEND_DIR}/../.." && pwd)"
|
||||
POSTINSTALL_SCRIPT="${BACKEND_DIR}/postinstall.sh"
|
||||
IMAGE_DEPLOY_LIB="${REPO_DIR}/scripts/lib/image_deploy.sh"
|
||||
BOOT_MEDIUM_LIB="${REPO_DIR}/scripts/lib/boot_medium.sh"
|
||||
|
||||
# Vom Netzwerkmodul geschuetzt abgelegtes WLAN-/Ethernet-Verbindungsprofil
|
||||
# (siehe export_connection_profile() in 05_network.sh) - existiert nur,
|
||||
# wenn das Provisionierungsinterface ueber ein aktives NetworkManager-
|
||||
# Profil lief (z.B. WLAN mit PSK).
|
||||
NETWORK_PROFILE_EXPORT="/run/tuxflotte/network/connection.nmconnection"
|
||||
|
||||
RUNTIME_BLUEPRINT_FILE="/run/tuxflotte/runtime/runtime_blueprint.json"
|
||||
SERVER_RESPONSE_FILE="/run/tuxflotte/server/response.json"
|
||||
@ -52,38 +59,15 @@ backend_fatal() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Live auf echter USB-Stick-Hardware gefunden (01.09.2026): auf einem
|
||||
# CD-ROM (wie in allen bisherigen QEMU-Tests) ist das Boot-Medium ein
|
||||
# eigener Geraetetyp ("rom"), der von der lsblk-Filterung unten schon
|
||||
# ausgeschlossen wird - auf einem echten USB-Stick ist das Boot-Medium
|
||||
# selbst aber ein ganz normales "disk"-Blockgeraet, genau wie die interne
|
||||
# Zielplatte. Ohne Ausschluss kann die Erkennung darunter den Stick selbst
|
||||
# waehlen (abhaengig von der Aufzaehlungsreihenfolge) - "parted mklabel"
|
||||
# darauf schlaegt dann IMMER fehl ("...have been unable to inform the
|
||||
# Kernel of the change, probably because it/they are in use"), weil man
|
||||
# nicht die Platte partitionieren kann, von der man gerade lebt. Der
|
||||
# vorherige Fix (Zielplatte freigeben + Neueinlesen wiederholen) half
|
||||
# deshalb nicht - das war die falsche Diagnose fuer dieses Problem.
|
||||
_mint_image_boot_medium_disk() {
|
||||
local medium_source parent
|
||||
|
||||
medium_source="$(findmnt --noheadings --output SOURCE /run/live/medium 2>/dev/null)" || return 1
|
||||
[[ -n "${medium_source}" ]] || return 1
|
||||
|
||||
parent="$(lsblk --noheadings --output PKNAME "${medium_source}" 2>/dev/null | head -n1)"
|
||||
[[ -n "${parent}" ]] || return 1
|
||||
|
||||
printf '/dev/%s\n' "${parent}"
|
||||
}
|
||||
|
||||
# Analog zu _tuxflotte_detect_target_disk() in backends/mint/backend.sh -
|
||||
# bewusst hier dupliziert statt geteilt, um dieses Backend unabhaengig vom
|
||||
# Mint-Referenzbackend zu halten (siehe Modul-Kommentar oben). Ein Umzug in
|
||||
# eine gemeinsame lib waere ein sinnvolles spaeteres Aufraeumen, sobald
|
||||
# mehr als zwei Backends dieselbe Logik brauchen.
|
||||
# Zieldatentraeger-Erkennung - schliesst das Boot-Medium selbst aus (siehe
|
||||
# tuxflotte_boot_medium_disk() in scripts/lib/boot_medium.sh: auf echter
|
||||
# USB-Stick-Hardware enumeriert das Medium als normales "disk"-Blockgeraet,
|
||||
# genau wie die Zielplatte - ohne Ausschluss schlaegt "parted mklabel"
|
||||
# darauf IMMER fehl, weil man nicht die Platte partitionieren kann, von der
|
||||
# man gerade lebt).
|
||||
_mint_image_detect_target_disk() {
|
||||
local boot_medium_disk
|
||||
boot_medium_disk="$(_mint_image_boot_medium_disk || true)"
|
||||
boot_medium_disk="$(tuxflotte_boot_medium_disk || true)"
|
||||
|
||||
lsblk --nodeps --noheadings --bytes --output NAME,TYPE,SIZE --paths |
|
||||
awk -v exclude="${boot_medium_disk}" '
|
||||
@ -119,6 +103,11 @@ backend_init() {
|
||||
# shellcheck source=../../scripts/lib/image_deploy.sh
|
||||
source "${IMAGE_DEPLOY_LIB}"
|
||||
|
||||
[[ -r "${BOOT_MEDIUM_LIB}" ]] ||
|
||||
{ backend_fatal "Boot-Medium-Bibliothek nicht gefunden: ${BOOT_MEDIUM_LIB}"; return 1; }
|
||||
# shellcheck source=../../scripts/lib/boot_medium.sh
|
||||
source "${BOOT_MEDIUM_LIB}"
|
||||
|
||||
[[ -r "${POSTINSTALL_SCRIPT}" ]] ||
|
||||
{ backend_fatal "Postinstall-Skript nicht gefunden: ${POSTINSTALL_SCRIPT}"; return 1; }
|
||||
|
||||
@ -241,6 +230,16 @@ backend_launch() {
|
||||
backend_log "Mounte unter ${TARGET_DIR}"
|
||||
image_deploy_mount "${TARGET_DIR}" "${boot_part}" "${root_part}" || return 1
|
||||
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): export_connection_profile()
|
||||
# in 05_network.sh legt das aktive WLAN-/Ethernet-Profil zwar geschuetzt unter
|
||||
# /run/tuxflotte/network/connection.nmconnection ab, aber bis hierhin holte es
|
||||
# nie jemand von dort ab - das frisch installierte System stand deshalb beim
|
||||
# ersten Boot ohne gespeichertes WLAN-Profil da und fragte den PSK erneut ab.
|
||||
if [[ -f "${NETWORK_PROFILE_EXPORT}" ]]; then
|
||||
backend_log "Übernehme WLAN-/Netzwerkverbindungsprofil ins Zielsystem"
|
||||
image_deploy_install_network_profile "${TARGET_DIR}" "${NETWORK_PROFILE_EXPORT}" || return 1
|
||||
fi
|
||||
|
||||
# Live gefunden (31.08.2026, erster echter End-to-End-Lauf mit dem vollen
|
||||
# Referenz-VM-Archiv): "erst nach /run/tuxflotte/... herunterladen, dann
|
||||
# entpacken" scheiterte an /run (RAM-Tmpfs, viel kleiner als das
|
||||
@ -335,6 +334,19 @@ backend_postinstall() {
|
||||
umount --recursive "${TARGET_DIR}" ||
|
||||
{ backend_fatal "${TARGET_DIR} konnte nicht ausgehängt werden."; return 1; }
|
||||
|
||||
# Nutzer-Feedback (01.09.2026, echter Hardware-Test): akustischer Hinweis
|
||||
# + klare Ansage, dass der USB-Stick jetzt gefahrlos gezogen werden kann
|
||||
# (ab hier wird nur noch von der Zielplatte gebootet) - Bestaetigung mit
|
||||
# ENTER, sonst automatischer Neustart nach 5 Minuten (kein Warten auf
|
||||
# eine Person, die vielleicht schon weitergegangen ist). "\a" (BEL) statt
|
||||
# eines externen Tonwerkzeugs - funktioniert auf jeder PC-Lautsprecher-
|
||||
# Hardware ohne zusaetzliches Paket.
|
||||
printf '\a\a\a'
|
||||
echo
|
||||
echo "Fertig! Sie können den USB-Stick jetzt entfernen."
|
||||
echo "Weiter mit ENTER, oder automatischer Neustart in 5 Minuten."
|
||||
read -r -t 300 _ || true
|
||||
|
||||
backend_log "Starte neu - kein Rücksprung erwartet, ab hier läuft das frisch installierte System."
|
||||
reboot
|
||||
}
|
||||
|
||||
@ -16,7 +16,12 @@ tuxflotte_agent_fatal() {
|
||||
ANODE_URL="https://anode.tuxflotte.de"
|
||||
AGENT_REPO_RAW="https://git.tuxflotte.de/admin/provisioning-agent/raw/branch/main"
|
||||
|
||||
install -d -m 0700 /etc/tuxflotte ||
|
||||
# 0755 statt 0700: /etc/tuxflotte muss fuer alle Nutzer durchsuchbar sein,
|
||||
# damit device_fingerprint (siehe unten) ohne sudo lesbar ist - einzelne
|
||||
# Dateien darin bleiben trotzdem individuell geschuetzt (agent.credentials
|
||||
# unten explizit chmod 0600, genau wie /etc/shadow trotz durchsuchbarem
|
||||
# /etc selbst geschuetzt bleibt).
|
||||
install -d -m 0755 /etc/tuxflotte ||
|
||||
tuxflotte_agent_fatal "Verzeichnis /etc/tuxflotte konnte nicht angelegt werden."
|
||||
|
||||
# Identifikation soll in beide Richtungen moeglich sein: die Geraeteliste
|
||||
@ -24,10 +29,15 @@ install -d -m 0700 /etc/tuxflotte ||
|
||||
# gab es auf dem installierten Geraet selbst keine Datei, um ihn mit einem
|
||||
# einfachen "cat" gegenzupruefen - build_device_fingerprint() (10_hardware.sh)
|
||||
# berechnet ihn nur einmalig waehrend des Live-Boots und haelt ihn sonst
|
||||
# nirgends fest. Absichtlich Klartext, kein Secret - reiner Hardware-Hash,
|
||||
# kein chmod 0600 noetig wie bei agent.credentials.
|
||||
# nirgends fest. Absichtlich Klartext, kein Secret - reiner Hardware-Hash.
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): trotz dieser Absicht
|
||||
# war die Datei vorher nicht ohne sudo lesbar, weil das umschliessende
|
||||
# Verzeichnis 0700 war (siehe oben) - explizites chmod 0644 hier, damit
|
||||
# sich das nicht auf einen zufaelligen umask verlaesst.
|
||||
echo "${TUXFLOTTE_DEVICE_FINGERPRINT}" > /etc/tuxflotte/device_fingerprint ||
|
||||
tuxflotte_agent_fatal "device_fingerprint konnte nicht abgelegt werden."
|
||||
chmod 0644 /etc/tuxflotte/device_fingerprint ||
|
||||
tuxflotte_agent_fatal "Rechte fuer device_fingerprint konnten nicht gesetzt werden."
|
||||
|
||||
cat > /etc/tuxflotte/runtime_blueprint.json <<'RUNTIME_BLUEPRINT_EOF'
|
||||
${TUXFLOTTE_BLUEPRINTS_JSON}
|
||||
|
||||
@ -23,6 +23,24 @@ if [[ -r "${BANNER_FILE}" ]]; then
|
||||
{ cat "${BANNER_FILE}"; echo; } | tee -a "${LOG_FILE}"
|
||||
fi
|
||||
|
||||
# TUXFLOTTE_TTY_LIVE steuert, ob log_module_ok()/log_module_failed()
|
||||
# (logging.sh) Kreisel/Haken mit ANSI-Fettschrift/Farbe zeigen oder einen
|
||||
# reinen Klartext-Fallback. "-t 1" selbst ist hier IMMER falsch: das
|
||||
# systemd-Unit setzt "StandardOutput=journal+console" (siehe
|
||||
# tuxflotte-installer.service) - fd1 dieses Prozesses ist deshalb ein
|
||||
# Journal-Socket, kein rohes tty-Zeichengeraet, ganz unabhaengig von der
|
||||
# "installer.sh | tee"-Pipe weiter unten. journald spiegelt die Bytes
|
||||
# trotzdem unveraendert (inkl. ANSI-Escapes) auf die echte Konsole - live
|
||||
# gefunden (01.09.2026, QEMU-Regressionstest): mit "-t 1" als Kriterium
|
||||
# blieb die geplante Fettschrift/Kreisel-Darstellung auf der echten Konsole
|
||||
# deshalb IMMER inaktiv (Klartext-Fallback), obwohl ein Mensch am Geraet
|
||||
# echte ANSI-Codes zu sehen bekommen haette. Da dieses Skript ausschliesslich
|
||||
# ueber genau dieses systemd-Unit gestartet wird, ist "immer aktiv" hier die
|
||||
# richtige, zuverlaessige Antwort - "-t 1" bleibt in logging.sh als Fallback
|
||||
# fuer andere Aufrufkontexte (z.B. installer.sh manuell per SSH-Pty).
|
||||
export TUXFLOTTE_TTY_LIVE=true
|
||||
export TUXFLOTTE_LOG_FILE="${LOG_FILE}"
|
||||
|
||||
/opt/tuxflotte/scripts/installer.sh 2>&1 | tee -a "${LOG_FILE}"
|
||||
STATUS="${PIPESTATUS[0]}"
|
||||
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Gemeinsame Hilfsfunktion rund um das Boot-Medium selbst (nicht das
|
||||
# Zielsystem). Wird an mehreren Stellen gebraucht:
|
||||
# - backends/mint-image/backend.sh (_mint_image_detect_target_disk):
|
||||
# verhindert, dass das Boot-Medium selbst als Zieldatentraeger gewaehlt
|
||||
# und dann partitioniert wird.
|
||||
# - scripts/lib/hardware_collectors.sh (build_storage_devices_json):
|
||||
# verhindert, dass das Boot-Medium in der an den Server gemeldeten
|
||||
# Datentraeger-Liste auftaucht (dort fuer den Kunden irrefuehrend, siehe
|
||||
# Hardware-Info in der Kundenplattform).
|
||||
#
|
||||
# Urspruenglich in backend.sh dupliziert (siehe Commit fa54461) - mit dem
|
||||
# zweiten Verwendungsort (Hardware-Erfassung) lohnt sich jetzt der Umzug in
|
||||
# eine gemeinsame lib, wie im urspruenglichen Kommentar dort schon vermerkt.
|
||||
|
||||
# Live auf echter USB-Stick-Hardware gefunden (01.09.2026): auf einem
|
||||
# CD-ROM (wie in allen bisherigen QEMU-Tests) ist das Boot-Medium ein
|
||||
# eigener lsblk-Geraetetyp ("rom"), der von reinen "disk"-Filtern schon
|
||||
# ausgeschlossen wird - auf einem echten USB-Stick ist das Boot-Medium
|
||||
# selbst aber ein ganz normales "disk"-Blockgeraet, genau wie die interne
|
||||
# Zielplatte. Ohne expliziten Ausschluss kann eine Datentraeger-Erkennung
|
||||
# darunter den Stick selbst waehlen (abhaengig von der Aufzaehlungs-
|
||||
# reihenfolge).
|
||||
tuxflotte_boot_medium_disk() {
|
||||
local medium_source parent
|
||||
|
||||
medium_source="$(findmnt --noheadings --output SOURCE /run/live/medium 2>/dev/null)" || return 1
|
||||
[[ -n "${medium_source}" ]] || return 1
|
||||
|
||||
parent="$(lsblk --noheadings --output PKNAME "${medium_source}" 2>/dev/null | head -n1)"
|
||||
[[ -n "${parent}" ]] || return 1
|
||||
|
||||
printf '/dev/%s\n' "${parent}"
|
||||
}
|
||||
@ -24,16 +24,28 @@ get_memory_bytes() {
|
||||
' /proc/meminfo
|
||||
}
|
||||
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): auf einem echten
|
||||
# USB-Stick enumeriert das Boot-Medium selbst als normales "disk"-
|
||||
# Blockgeraet (siehe tuxflotte_boot_medium_disk() in boot_medium.sh) - ohne
|
||||
# Ausschluss tauchte der Installations-Stick selbst in der an den Server
|
||||
# gemeldeten Datentraeger-Liste auf und war dort fuer den Kunden
|
||||
# irrefuehrend sichtbar (Hardware-Info in der Kundenplattform).
|
||||
build_storage_devices_json() {
|
||||
local boot_medium_disk boot_medium_name
|
||||
|
||||
boot_medium_disk="$(tuxflotte_boot_medium_disk 2>/dev/null || true)"
|
||||
boot_medium_name="${boot_medium_disk#/dev/}"
|
||||
|
||||
lsblk \
|
||||
--bytes \
|
||||
--json \
|
||||
--nodeps \
|
||||
--output NAME,TYPE,MODEL,SERIAL,SIZE,TRAN |
|
||||
jq '
|
||||
jq --arg exclude "${boot_medium_name}" '
|
||||
[
|
||||
.blockdevices[]
|
||||
| select(.type == "disk")
|
||||
| select($exclude == "" or .name != $exclude)
|
||||
| {
|
||||
name: .name,
|
||||
model: (
|
||||
|
||||
@ -217,10 +217,20 @@ image_deploy_extract_image_from_url() {
|
||||
local target="$2"
|
||||
local pipe_status
|
||||
|
||||
# "--progress-bar" statt "--silent": mit Abstand der laengste Schritt
|
||||
# der ganzen Installation (Minuten statt Sekunden) - Nutzer-Feedback
|
||||
# (01.09.2026, echter Hardware-Test) wollte hier ausdruecklich eine
|
||||
# sichtbare Fortschrittsanzeige statt einer stillen Wartezeit. Der
|
||||
# Balken geht an stderr, unabhaengig vom stdout/stderr-Umleiten des
|
||||
# restlichen Moduls (siehe TUXFLOTTE_VERBOSE_MODULES in utils.sh -
|
||||
# 40_backend.sh bleibt deshalb bewusst von der Kreisel-Kompaktdarstellung
|
||||
# ausgenommen, sonst waere dieser Balken fuer die Person vor dem Geraet
|
||||
# unsichtbar).
|
||||
#
|
||||
# "|| true" verhindert, dass "set -e" hier sofort abbricht - der
|
||||
# eigentliche Fehlerfall wird gleich anhand von PIPESTATUS gezielt
|
||||
# ausgewertet, statt die Pipeline roh durchschlagen zu lassen.
|
||||
curl --silent --show-error --fail --location "${url}" |
|
||||
curl --progress-bar --show-error --fail --location "${url}" |
|
||||
tar --zstd -xpf - -C "${target}" || true
|
||||
pipe_status=("${PIPESTATUS[@]}")
|
||||
|
||||
@ -235,6 +245,33 @@ image_deploy_extract_image_from_url() {
|
||||
# /etc/fstab aus den tatsaechlichen Partitions-UUIDs neu erzeugen - kein
|
||||
# genfstab auf Debian/Mint verfuegbar (das ist ein Arch-Linux-Werkzeug),
|
||||
# blkid reicht fuer unseren einfachen Fall (Root + optional ESP) locker.
|
||||
# Uebernimmt das vom Netzwerkmodul exportierte, aktive NetworkManager-
|
||||
# Verbindungsprofil (siehe export_connection_profile() in 05_network.sh)
|
||||
# ins Zielsystem - ohne diesen Schritt bleibt das Profil nur unter
|
||||
# /run/tuxflotte/network/... auf dem Boot-Medium liegen und geht beim
|
||||
# Reboot ins frisch installierte System verloren. Live gefunden
|
||||
# (01.09.2026, echter Hardware-Test): Kunde musste den WLAN-PSK nach dem
|
||||
# ersten Boot erneut eingeben, weil genau dieser Kopierschritt fehlte.
|
||||
# Keine Fatal-Bedingung, falls die Datei fehlt - das Provisionierungs-
|
||||
# interface kann auch unmanaged/kabelgebunden ohne Profil gelaufen sein.
|
||||
image_deploy_install_network_profile() {
|
||||
local target="$1"
|
||||
local profile_file="$2"
|
||||
local dest_dir="${target}/etc/NetworkManager/system-connections"
|
||||
local dest_name
|
||||
|
||||
[[ -n "${profile_file}" && -f "${profile_file}" ]] || return 0
|
||||
|
||||
dest_name="$(basename "${profile_file}")"
|
||||
|
||||
install -d --mode=0700 --owner=root --group=root "${dest_dir}" ||
|
||||
{ image_deploy_fatal "Verzeichnis ${dest_dir} konnte nicht angelegt werden"; return 1; }
|
||||
|
||||
install --mode=0600 --owner=root --group=root \
|
||||
"${profile_file}" "${dest_dir}/${dest_name}" ||
|
||||
{ image_deploy_fatal "Verbindungsprofil konnte nicht nach ${dest_dir} kopiert werden"; return 1; }
|
||||
}
|
||||
|
||||
image_deploy_write_fstab() {
|
||||
local target="$1"
|
||||
local boot_part="$2"
|
||||
|
||||
@ -16,6 +16,26 @@ log_success() {
|
||||
echo "[ OK ] $*"
|
||||
}
|
||||
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): "[[ -t 1 ]]" ist auf
|
||||
# der echten Konsole IMMER falsch, weil boot-autostart.sh installer.sh
|
||||
# grundsaetzlich durch "| tee -a logfile" schickt (fuer die persistente
|
||||
# Logdatei, siehe Phase 2) - installer.sh selbst schreibt deshalb nie
|
||||
# direkt auf ein tty, sondern immer in eine Pipe. Ergebnis: die geplante
|
||||
# Fettschrift/Farb-Hervorhebung war auf dem echten Geraet nie aktiv, ganz
|
||||
# unabhaengig vom Terminal, das der Kunde tatsaechlich vor sich hat.
|
||||
# boot-autostart.sh prueft "[[ -t 1 ]]" deshalb VOR dem Aufbau dieser Pipe
|
||||
# (an der Stelle noch zuverlaessig) und exportiert das Ergebnis als
|
||||
# TUXFLOTTE_TTY_LIVE - hier nur noch auswerten, mit "-t 1" als Fallback
|
||||
# fuer direkte, nicht ueber boot-autostart.sh gestartete Läufe (z.B.
|
||||
# manuelles Testen von installer.sh per SSH).
|
||||
_tuxflotte_console_is_live() {
|
||||
if [[ -n "${TUXFLOTTE_TTY_LIVE:-}" ]]; then
|
||||
[[ "${TUXFLOTTE_TTY_LIVE}" == true ]]
|
||||
else
|
||||
[[ -t 1 ]]
|
||||
fi
|
||||
}
|
||||
|
||||
# Klar abgesetzte Fortschrittsanzeige fuer die tty-Konsole des Boot-Mediums
|
||||
# (kein Desktop, kein Kiosk-Browser - die Terminal-Ausgabe selbst ist die
|
||||
# einzige UI, die eine Person vor dem Geraet sieht). Gilt fuer Dev- UND
|
||||
@ -58,7 +78,7 @@ log_step() {
|
||||
# umgeleitet wird) - kein tput-Aufruf, ncurses-bin ist bewusst nicht Teil
|
||||
# des schlanken Paketsatzes, rohe ANSI-Codes reichen und sind ueberall
|
||||
# ohne Zusatzpaket verfuegbar.
|
||||
if [[ -t 1 ]]; then
|
||||
if _tuxflotte_console_is_live; then
|
||||
bold=$'\033[1m'
|
||||
reset=$'\033[0m'
|
||||
fi
|
||||
@ -69,3 +89,57 @@ log_step() {
|
||||
printf '\n%s%s%s\n' "${bold}" "${title}" "${reset}"
|
||||
printf '%s\n\n' "$(printf '=%.0s' $(seq 1 ${#title}))"
|
||||
}
|
||||
|
||||
# Kompakte Alternative zu log_step() fuer den unbeaufsichtigten
|
||||
# Auto-Modus (echte Kunden-Installation, siehe run_module_quiet() in
|
||||
# utils.sh). Live-Rueckmeldung des Nutzers (01.09.2026) nach dem ersten
|
||||
# erfolgreichen Hardware-Test: die bisherige Fülle an [INFO]/[WARN]-Zeilen
|
||||
# je Modul ist "nicht schön für das menschliche Auge" - Vorschlag:
|
||||
# ein Kreisel waehrend ein Modul laeuft, ein gruener Haken wenn fertig,
|
||||
# Details nur bei einem Fehlschlag. Ein einziges Ergebnis pro Modul statt
|
||||
# einer ganzen Log-Wand ist genau das ("dann sind es nicht soviel
|
||||
# Zeichen").
|
||||
# Reines ASCII statt Unicode-Braille/Haken-Zeichen (⠋⠙.../✓/✗): live gefunden
|
||||
# (01.09.2026, QEMU-Regressionstest) - die vt-Konsolenschriftart des
|
||||
# Boot-Mediums deckt diese Codepoints nicht ab, gerendert wurde nur ein
|
||||
# generischer Ersatz-Kasten statt eines Haekchens. ASCII-Zeichen sind auf
|
||||
# jeder Konsolenschriftart garantiert vorhanden, unabhaengig von Hardware/
|
||||
# Zeichensatz.
|
||||
_TUXFLOTTE_SPINNER_FRAMES=(\| / - \\)
|
||||
|
||||
# Setzt TUXFLOTTE_CURRENT_TITLE statt den Titel per "echo"+Command-
|
||||
# Substitution zurueckzugeben - Command-Substitution laeuft in einer
|
||||
# Subshell, deren Variablenaenderungen (hier: der TUXFLOTTE_STEP_COUNT-
|
||||
# Zaehler) beim Verlassen verloren gehen. Live gefunden (01.09.2026, QEMU-
|
||||
# Regressionstest): mit "title=\"\$(log_module_title ...)\"" blieb der
|
||||
# Zaehler wegen genau dieser Subshell dauerhaft bei 1 stehen ("[1/12]" bei
|
||||
# jedem Modul statt hochzuzaehlen).
|
||||
log_module_title() {
|
||||
local module_name="$1"
|
||||
local label="${TUXFLOTTE_STEP_LABELS[$module_name]:-$module_name}"
|
||||
local total="${#TUXFLOTTE_STEP_ORDER[@]}"
|
||||
|
||||
TUXFLOTTE_STEP_COUNT=$((TUXFLOTTE_STEP_COUNT + 1))
|
||||
|
||||
TUXFLOTTE_CURRENT_TITLE="[${TUXFLOTTE_STEP_COUNT}/${total}] ${label}"
|
||||
}
|
||||
|
||||
log_module_ok() {
|
||||
local title="$1"
|
||||
|
||||
if _tuxflotte_console_is_live; then
|
||||
printf '\r%s \033[1;32m[OK]\033[0m\n' "${title}"
|
||||
else
|
||||
printf '%s [OK]\n' "${title}"
|
||||
fi
|
||||
}
|
||||
|
||||
log_module_failed() {
|
||||
local title="$1"
|
||||
|
||||
if _tuxflotte_console_is_live; then
|
||||
printf '\r%s \033[1;31m[FEHLER]\033[0m\n' "${title}"
|
||||
else
|
||||
printf '%s [FEHLER]\n' "${title}"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -12,24 +12,113 @@ load_config() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Module, die auch im Auto-Modus verbose bleiben (keine Kreisel/Haken-
|
||||
# Kompaktdarstellung, siehe run_module_quiet()): 40_backend.sh ist der mit
|
||||
# Abstand langlaeufigste Schritt (Minuten statt Sekunden, Golden-Image-
|
||||
# Download+Extraktion) - hier bleibt die eigene Schritt-fuer-Schritt-
|
||||
# Ausgabe (Partitionieren/Formatieren/... + der Download-Fortschrittsbalken,
|
||||
# siehe image_deploy_extract_image_from_url()) sichtbar, statt hinter einem
|
||||
# einzelnen Kreisel zu verschwinden.
|
||||
declare -a TUXFLOTTE_VERBOSE_MODULES=(40_backend.sh)
|
||||
|
||||
_tuxflotte_module_is_verbose() {
|
||||
local module_name="$1"
|
||||
local candidate
|
||||
|
||||
for candidate in "${TUXFLOTTE_VERBOSE_MODULES[@]}"; do
|
||||
[[ "${candidate}" == "${module_name}" ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Kompakte Modul-Ausfuehrung fuer den unbeaufsichtigten Auto-Modus (siehe
|
||||
# log_module_title()/log_module_ok()/log_module_failed() in logging.sh):
|
||||
# Modul laeuft im Hintergrund, waehrenddessen ein Kreisel auf derselben
|
||||
# Zeile, am Ende entweder ein gruener Haken (Erfolg, Detailausgabe nur ins
|
||||
# Logfile) oder ein rotes Kreuz + die volle mitgeschnittene Ausgabe auf der
|
||||
# Konsole (Fehlschlag - Fehlersuche darf nie schlechter werden als vorher).
|
||||
run_module_quiet() {
|
||||
local module="$1"
|
||||
local module_name
|
||||
module_name="$(basename "${module}")"
|
||||
|
||||
log_module_title "${module_name}"
|
||||
local title="${TUXFLOTTE_CURRENT_TITLE}"
|
||||
|
||||
local output_file
|
||||
output_file="$(mktemp)"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
"${module}" >"${output_file}" 2>&1 &
|
||||
local pid=$!
|
||||
|
||||
if _tuxflotte_console_is_live; then
|
||||
printf '%s ' "${title}"
|
||||
local frame_index=0
|
||||
local frame_count="${#_TUXFLOTTE_SPINNER_FRAMES[@]}"
|
||||
while kill -0 "${pid}" 2>/dev/null; do
|
||||
printf '\r%s %s' "${title}" "${_TUXFLOTTE_SPINNER_FRAMES[$((frame_index % frame_count))]}"
|
||||
frame_index=$((frame_index + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
fi
|
||||
|
||||
# "wait || status=$?" statt eines nackten "wait"-Aufrufs - genau das
|
||||
# bekannte &&/||-Gegenstueck zu "set -e" (siehe die mount-Bind-Lehre in
|
||||
# image_deploy.sh), hier bewusst genutzt, um den echten Exit-Code sauber
|
||||
# auszuwerten statt das ganze Skript sofort abzubrechen.
|
||||
local status=0
|
||||
wait "${pid}" || status=$?
|
||||
|
||||
if [[ "${status}" -eq 0 ]]; then
|
||||
log_module_ok "${title}"
|
||||
else
|
||||
log_module_failed "${title}"
|
||||
echo
|
||||
cat "${output_file}"
|
||||
echo
|
||||
fi
|
||||
|
||||
[[ -n "${TUXFLOTTE_LOG_FILE:-}" ]] && cat "${output_file}" >>"${TUXFLOTTE_LOG_FILE}" 2>/dev/null
|
||||
|
||||
rm -f "${output_file}"
|
||||
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
run_module() {
|
||||
local module="$1"
|
||||
local mode="${2:-normal}"
|
||||
local module_name
|
||||
module_name="$(basename "$module")"
|
||||
|
||||
[[ -f "$module" ]] || error_exit "Modul nicht gefunden: $module"
|
||||
|
||||
log_step "$(basename "$module")"
|
||||
log_info "Starte Modul: $(basename "$module")"
|
||||
|
||||
if [[ "${DRY_RUN:-false}" == true && "$mode" != "always" ]]; then
|
||||
log_step "${module_name}"
|
||||
log_warn "Dry-Run: Modul übersprungen: $module"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Kompakte Kreisel/Haken-Darstellung nur im echten Auto-Modus: nur dort
|
||||
# ist sichergestellt, dass kein Modul interaktiv nach Eingaben fragt
|
||||
# (12_enrollment_auth.sh/20_profile_selection.sh/25_installation_confirm.sh
|
||||
# pruefen TUXFLOTTE_AUTO_MODE selbst und ueberspringen ihre Prompts) -
|
||||
# ein Prompt-Text waere sonst zusammen mit der restlichen Ausgabe in
|
||||
# output_file gefangen und fuer die Person vor dem Geraet unsichtbar.
|
||||
if [[ "${TUXFLOTTE_AUTO_MODE:-false}" == "true" ]] && ! _tuxflotte_module_is_verbose "${module_name}"; then
|
||||
run_module_quiet "$module"
|
||||
return "$?"
|
||||
fi
|
||||
|
||||
log_step "${module_name}"
|
||||
log_info "Starte Modul: ${module_name}"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
"$module"
|
||||
|
||||
log_success "Modul abgeschlossen: $(basename "$module")"
|
||||
log_success "Modul abgeschlossen: ${module_name}"
|
||||
}
|
||||
|
||||
require_root() {
|
||||
|
||||
@ -23,6 +23,7 @@ SCRIPT_DIR="$(
|
||||
readonly SCRIPT_DIR
|
||||
|
||||
readonly COLLECTORS_FILE="${SCRIPT_DIR}/../lib/hardware_collectors.sh"
|
||||
readonly BOOT_MEDIUM_FILE="${SCRIPT_DIR}/../lib/boot_medium.sh"
|
||||
|
||||
if [[ ! -r "${COLLECTORS_FILE}" ]]; then
|
||||
printf '[%s] FEHLER: Collector-Library nicht gefunden: %s\n' \
|
||||
@ -31,8 +32,17 @@ if [[ ! -r "${COLLECTORS_FILE}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -r "${BOOT_MEDIUM_FILE}" ]]; then
|
||||
printf '[%s] FEHLER: Boot-Medium-Library nicht gefunden: %s\n' \
|
||||
"${0##*/}" \
|
||||
"${BOOT_MEDIUM_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=../lib/hardware_collectors.sh
|
||||
source "${COLLECTORS_FILE}"
|
||||
# shellcheck source=../lib/boot_medium.sh
|
||||
source "${BOOT_MEDIUM_FILE}"
|
||||
|
||||
readonly SCRIPT_NAME="${0##*/}"
|
||||
|
||||
|
||||
@ -23,6 +23,24 @@ if [[ -r "${BANNER_FILE}" ]]; then
|
||||
{ cat "${BANNER_FILE}"; echo; } | tee -a "${LOG_FILE}"
|
||||
fi
|
||||
|
||||
# TUXFLOTTE_TTY_LIVE steuert, ob log_module_ok()/log_module_failed()
|
||||
# (logging.sh) Kreisel/Haken mit ANSI-Fettschrift/Farbe zeigen oder einen
|
||||
# reinen Klartext-Fallback. "-t 1" selbst ist hier IMMER falsch: das
|
||||
# systemd-Unit setzt "StandardOutput=journal+console" (siehe
|
||||
# tuxflotte-installer.service) - fd1 dieses Prozesses ist deshalb ein
|
||||
# Journal-Socket, kein rohes tty-Zeichengeraet, ganz unabhaengig von der
|
||||
# "installer.sh | tee"-Pipe weiter unten. journald spiegelt die Bytes
|
||||
# trotzdem unveraendert (inkl. ANSI-Escapes) auf die echte Konsole - live
|
||||
# gefunden (01.09.2026, QEMU-Regressionstest): mit "-t 1" als Kriterium
|
||||
# blieb die geplante Fettschrift/Kreisel-Darstellung auf der echten Konsole
|
||||
# deshalb IMMER inaktiv (Klartext-Fallback), obwohl ein Mensch am Geraet
|
||||
# echte ANSI-Codes zu sehen bekommen haette. Da dieses Skript ausschliesslich
|
||||
# ueber genau dieses systemd-Unit gestartet wird, ist "immer aktiv" hier die
|
||||
# richtige, zuverlaessige Antwort - "-t 1" bleibt in logging.sh als Fallback
|
||||
# fuer andere Aufrufkontexte (z.B. installer.sh manuell per SSH-Pty).
|
||||
export TUXFLOTTE_TTY_LIVE=true
|
||||
export TUXFLOTTE_LOG_FILE="${LOG_FILE}"
|
||||
|
||||
/opt/tuxflotte/scripts/installer.sh 2>&1 | tee -a "${LOG_FILE}"
|
||||
STATUS="${PIPESTATUS[0]}"
|
||||
|
||||
|
||||
35
scripts/lib/boot_medium.sh
Normal file
35
scripts/lib/boot_medium.sh
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Gemeinsame Hilfsfunktion rund um das Boot-Medium selbst (nicht das
|
||||
# Zielsystem). Wird an mehreren Stellen gebraucht:
|
||||
# - backends/mint-image/backend.sh (_mint_image_detect_target_disk):
|
||||
# verhindert, dass das Boot-Medium selbst als Zieldatentraeger gewaehlt
|
||||
# und dann partitioniert wird.
|
||||
# - scripts/lib/hardware_collectors.sh (build_storage_devices_json):
|
||||
# verhindert, dass das Boot-Medium in der an den Server gemeldeten
|
||||
# Datentraeger-Liste auftaucht (dort fuer den Kunden irrefuehrend, siehe
|
||||
# Hardware-Info in der Kundenplattform).
|
||||
#
|
||||
# Urspruenglich in backend.sh dupliziert (siehe Commit fa54461) - mit dem
|
||||
# zweiten Verwendungsort (Hardware-Erfassung) lohnt sich jetzt der Umzug in
|
||||
# eine gemeinsame lib, wie im urspruenglichen Kommentar dort schon vermerkt.
|
||||
|
||||
# Live auf echter USB-Stick-Hardware gefunden (01.09.2026): auf einem
|
||||
# CD-ROM (wie in allen bisherigen QEMU-Tests) ist das Boot-Medium ein
|
||||
# eigener lsblk-Geraetetyp ("rom"), der von reinen "disk"-Filtern schon
|
||||
# ausgeschlossen wird - auf einem echten USB-Stick ist das Boot-Medium
|
||||
# selbst aber ein ganz normales "disk"-Blockgeraet, genau wie die interne
|
||||
# Zielplatte. Ohne expliziten Ausschluss kann eine Datentraeger-Erkennung
|
||||
# darunter den Stick selbst waehlen (abhaengig von der Aufzaehlungs-
|
||||
# reihenfolge).
|
||||
tuxflotte_boot_medium_disk() {
|
||||
local medium_source parent
|
||||
|
||||
medium_source="$(findmnt --noheadings --output SOURCE /run/live/medium 2>/dev/null)" || return 1
|
||||
[[ -n "${medium_source}" ]] || return 1
|
||||
|
||||
parent="$(lsblk --noheadings --output PKNAME "${medium_source}" 2>/dev/null | head -n1)"
|
||||
[[ -n "${parent}" ]] || return 1
|
||||
|
||||
printf '/dev/%s\n' "${parent}"
|
||||
}
|
||||
@ -24,16 +24,28 @@ get_memory_bytes() {
|
||||
' /proc/meminfo
|
||||
}
|
||||
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): auf einem echten
|
||||
# USB-Stick enumeriert das Boot-Medium selbst als normales "disk"-
|
||||
# Blockgeraet (siehe tuxflotte_boot_medium_disk() in boot_medium.sh) - ohne
|
||||
# Ausschluss tauchte der Installations-Stick selbst in der an den Server
|
||||
# gemeldeten Datentraeger-Liste auf und war dort fuer den Kunden
|
||||
# irrefuehrend sichtbar (Hardware-Info in der Kundenplattform).
|
||||
build_storage_devices_json() {
|
||||
local boot_medium_disk boot_medium_name
|
||||
|
||||
boot_medium_disk="$(tuxflotte_boot_medium_disk 2>/dev/null || true)"
|
||||
boot_medium_name="${boot_medium_disk#/dev/}"
|
||||
|
||||
lsblk \
|
||||
--bytes \
|
||||
--json \
|
||||
--nodeps \
|
||||
--output NAME,TYPE,MODEL,SERIAL,SIZE,TRAN |
|
||||
jq '
|
||||
jq --arg exclude "${boot_medium_name}" '
|
||||
[
|
||||
.blockdevices[]
|
||||
| select(.type == "disk")
|
||||
| select($exclude == "" or .name != $exclude)
|
||||
| {
|
||||
name: .name,
|
||||
model: (
|
||||
|
||||
@ -217,10 +217,20 @@ image_deploy_extract_image_from_url() {
|
||||
local target="$2"
|
||||
local pipe_status
|
||||
|
||||
# "--progress-bar" statt "--silent": mit Abstand der laengste Schritt
|
||||
# der ganzen Installation (Minuten statt Sekunden) - Nutzer-Feedback
|
||||
# (01.09.2026, echter Hardware-Test) wollte hier ausdruecklich eine
|
||||
# sichtbare Fortschrittsanzeige statt einer stillen Wartezeit. Der
|
||||
# Balken geht an stderr, unabhaengig vom stdout/stderr-Umleiten des
|
||||
# restlichen Moduls (siehe TUXFLOTTE_VERBOSE_MODULES in utils.sh -
|
||||
# 40_backend.sh bleibt deshalb bewusst von der Kreisel-Kompaktdarstellung
|
||||
# ausgenommen, sonst waere dieser Balken fuer die Person vor dem Geraet
|
||||
# unsichtbar).
|
||||
#
|
||||
# "|| true" verhindert, dass "set -e" hier sofort abbricht - der
|
||||
# eigentliche Fehlerfall wird gleich anhand von PIPESTATUS gezielt
|
||||
# ausgewertet, statt die Pipeline roh durchschlagen zu lassen.
|
||||
curl --silent --show-error --fail --location "${url}" |
|
||||
curl --progress-bar --show-error --fail --location "${url}" |
|
||||
tar --zstd -xpf - -C "${target}" || true
|
||||
pipe_status=("${PIPESTATUS[@]}")
|
||||
|
||||
@ -235,6 +245,33 @@ image_deploy_extract_image_from_url() {
|
||||
# /etc/fstab aus den tatsaechlichen Partitions-UUIDs neu erzeugen - kein
|
||||
# genfstab auf Debian/Mint verfuegbar (das ist ein Arch-Linux-Werkzeug),
|
||||
# blkid reicht fuer unseren einfachen Fall (Root + optional ESP) locker.
|
||||
# Uebernimmt das vom Netzwerkmodul exportierte, aktive NetworkManager-
|
||||
# Verbindungsprofil (siehe export_connection_profile() in 05_network.sh)
|
||||
# ins Zielsystem - ohne diesen Schritt bleibt das Profil nur unter
|
||||
# /run/tuxflotte/network/... auf dem Boot-Medium liegen und geht beim
|
||||
# Reboot ins frisch installierte System verloren. Live gefunden
|
||||
# (01.09.2026, echter Hardware-Test): Kunde musste den WLAN-PSK nach dem
|
||||
# ersten Boot erneut eingeben, weil genau dieser Kopierschritt fehlte.
|
||||
# Keine Fatal-Bedingung, falls die Datei fehlt - das Provisionierungs-
|
||||
# interface kann auch unmanaged/kabelgebunden ohne Profil gelaufen sein.
|
||||
image_deploy_install_network_profile() {
|
||||
local target="$1"
|
||||
local profile_file="$2"
|
||||
local dest_dir="${target}/etc/NetworkManager/system-connections"
|
||||
local dest_name
|
||||
|
||||
[[ -n "${profile_file}" && -f "${profile_file}" ]] || return 0
|
||||
|
||||
dest_name="$(basename "${profile_file}")"
|
||||
|
||||
install -d --mode=0700 --owner=root --group=root "${dest_dir}" ||
|
||||
{ image_deploy_fatal "Verzeichnis ${dest_dir} konnte nicht angelegt werden"; return 1; }
|
||||
|
||||
install --mode=0600 --owner=root --group=root \
|
||||
"${profile_file}" "${dest_dir}/${dest_name}" ||
|
||||
{ image_deploy_fatal "Verbindungsprofil konnte nicht nach ${dest_dir} kopiert werden"; return 1; }
|
||||
}
|
||||
|
||||
image_deploy_write_fstab() {
|
||||
local target="$1"
|
||||
local boot_part="$2"
|
||||
|
||||
@ -16,6 +16,26 @@ log_success() {
|
||||
echo "[ OK ] $*"
|
||||
}
|
||||
|
||||
# Live gefunden (01.09.2026, echter Hardware-Test): "[[ -t 1 ]]" ist auf
|
||||
# der echten Konsole IMMER falsch, weil boot-autostart.sh installer.sh
|
||||
# grundsaetzlich durch "| tee -a logfile" schickt (fuer die persistente
|
||||
# Logdatei, siehe Phase 2) - installer.sh selbst schreibt deshalb nie
|
||||
# direkt auf ein tty, sondern immer in eine Pipe. Ergebnis: die geplante
|
||||
# Fettschrift/Farb-Hervorhebung war auf dem echten Geraet nie aktiv, ganz
|
||||
# unabhaengig vom Terminal, das der Kunde tatsaechlich vor sich hat.
|
||||
# boot-autostart.sh prueft "[[ -t 1 ]]" deshalb VOR dem Aufbau dieser Pipe
|
||||
# (an der Stelle noch zuverlaessig) und exportiert das Ergebnis als
|
||||
# TUXFLOTTE_TTY_LIVE - hier nur noch auswerten, mit "-t 1" als Fallback
|
||||
# fuer direkte, nicht ueber boot-autostart.sh gestartete Läufe (z.B.
|
||||
# manuelles Testen von installer.sh per SSH).
|
||||
_tuxflotte_console_is_live() {
|
||||
if [[ -n "${TUXFLOTTE_TTY_LIVE:-}" ]]; then
|
||||
[[ "${TUXFLOTTE_TTY_LIVE}" == true ]]
|
||||
else
|
||||
[[ -t 1 ]]
|
||||
fi
|
||||
}
|
||||
|
||||
# Klar abgesetzte Fortschrittsanzeige fuer die tty-Konsole des Boot-Mediums
|
||||
# (kein Desktop, kein Kiosk-Browser - die Terminal-Ausgabe selbst ist die
|
||||
# einzige UI, die eine Person vor dem Geraet sieht). Gilt fuer Dev- UND
|
||||
@ -58,7 +78,7 @@ log_step() {
|
||||
# umgeleitet wird) - kein tput-Aufruf, ncurses-bin ist bewusst nicht Teil
|
||||
# des schlanken Paketsatzes, rohe ANSI-Codes reichen und sind ueberall
|
||||
# ohne Zusatzpaket verfuegbar.
|
||||
if [[ -t 1 ]]; then
|
||||
if _tuxflotte_console_is_live; then
|
||||
bold=$'\033[1m'
|
||||
reset=$'\033[0m'
|
||||
fi
|
||||
@ -69,3 +89,57 @@ log_step() {
|
||||
printf '\n%s%s%s\n' "${bold}" "${title}" "${reset}"
|
||||
printf '%s\n\n' "$(printf '=%.0s' $(seq 1 ${#title}))"
|
||||
}
|
||||
|
||||
# Kompakte Alternative zu log_step() fuer den unbeaufsichtigten
|
||||
# Auto-Modus (echte Kunden-Installation, siehe run_module_quiet() in
|
||||
# utils.sh). Live-Rueckmeldung des Nutzers (01.09.2026) nach dem ersten
|
||||
# erfolgreichen Hardware-Test: die bisherige Fülle an [INFO]/[WARN]-Zeilen
|
||||
# je Modul ist "nicht schön für das menschliche Auge" - Vorschlag:
|
||||
# ein Kreisel waehrend ein Modul laeuft, ein gruener Haken wenn fertig,
|
||||
# Details nur bei einem Fehlschlag. Ein einziges Ergebnis pro Modul statt
|
||||
# einer ganzen Log-Wand ist genau das ("dann sind es nicht soviel
|
||||
# Zeichen").
|
||||
# Reines ASCII statt Unicode-Braille/Haken-Zeichen (⠋⠙.../✓/✗): live gefunden
|
||||
# (01.09.2026, QEMU-Regressionstest) - die vt-Konsolenschriftart des
|
||||
# Boot-Mediums deckt diese Codepoints nicht ab, gerendert wurde nur ein
|
||||
# generischer Ersatz-Kasten statt eines Haekchens. ASCII-Zeichen sind auf
|
||||
# jeder Konsolenschriftart garantiert vorhanden, unabhaengig von Hardware/
|
||||
# Zeichensatz.
|
||||
_TUXFLOTTE_SPINNER_FRAMES=(\| / - \\)
|
||||
|
||||
# Setzt TUXFLOTTE_CURRENT_TITLE statt den Titel per "echo"+Command-
|
||||
# Substitution zurueckzugeben - Command-Substitution laeuft in einer
|
||||
# Subshell, deren Variablenaenderungen (hier: der TUXFLOTTE_STEP_COUNT-
|
||||
# Zaehler) beim Verlassen verloren gehen. Live gefunden (01.09.2026, QEMU-
|
||||
# Regressionstest): mit "title=\"\$(log_module_title ...)\"" blieb der
|
||||
# Zaehler wegen genau dieser Subshell dauerhaft bei 1 stehen ("[1/12]" bei
|
||||
# jedem Modul statt hochzuzaehlen).
|
||||
log_module_title() {
|
||||
local module_name="$1"
|
||||
local label="${TUXFLOTTE_STEP_LABELS[$module_name]:-$module_name}"
|
||||
local total="${#TUXFLOTTE_STEP_ORDER[@]}"
|
||||
|
||||
TUXFLOTTE_STEP_COUNT=$((TUXFLOTTE_STEP_COUNT + 1))
|
||||
|
||||
TUXFLOTTE_CURRENT_TITLE="[${TUXFLOTTE_STEP_COUNT}/${total}] ${label}"
|
||||
}
|
||||
|
||||
log_module_ok() {
|
||||
local title="$1"
|
||||
|
||||
if _tuxflotte_console_is_live; then
|
||||
printf '\r%s \033[1;32m[OK]\033[0m\n' "${title}"
|
||||
else
|
||||
printf '%s [OK]\n' "${title}"
|
||||
fi
|
||||
}
|
||||
|
||||
log_module_failed() {
|
||||
local title="$1"
|
||||
|
||||
if _tuxflotte_console_is_live; then
|
||||
printf '\r%s \033[1;31m[FEHLER]\033[0m\n' "${title}"
|
||||
else
|
||||
printf '%s [FEHLER]\n' "${title}"
|
||||
fi
|
||||
}
|
||||
|
||||
@ -12,24 +12,113 @@ load_config() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Module, die auch im Auto-Modus verbose bleiben (keine Kreisel/Haken-
|
||||
# Kompaktdarstellung, siehe run_module_quiet()): 40_backend.sh ist der mit
|
||||
# Abstand langlaeufigste Schritt (Minuten statt Sekunden, Golden-Image-
|
||||
# Download+Extraktion) - hier bleibt die eigene Schritt-fuer-Schritt-
|
||||
# Ausgabe (Partitionieren/Formatieren/... + der Download-Fortschrittsbalken,
|
||||
# siehe image_deploy_extract_image_from_url()) sichtbar, statt hinter einem
|
||||
# einzelnen Kreisel zu verschwinden.
|
||||
declare -a TUXFLOTTE_VERBOSE_MODULES=(40_backend.sh)
|
||||
|
||||
_tuxflotte_module_is_verbose() {
|
||||
local module_name="$1"
|
||||
local candidate
|
||||
|
||||
for candidate in "${TUXFLOTTE_VERBOSE_MODULES[@]}"; do
|
||||
[[ "${candidate}" == "${module_name}" ]] && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Kompakte Modul-Ausfuehrung fuer den unbeaufsichtigten Auto-Modus (siehe
|
||||
# log_module_title()/log_module_ok()/log_module_failed() in logging.sh):
|
||||
# Modul laeuft im Hintergrund, waehrenddessen ein Kreisel auf derselben
|
||||
# Zeile, am Ende entweder ein gruener Haken (Erfolg, Detailausgabe nur ins
|
||||
# Logfile) oder ein rotes Kreuz + die volle mitgeschnittene Ausgabe auf der
|
||||
# Konsole (Fehlschlag - Fehlersuche darf nie schlechter werden als vorher).
|
||||
run_module_quiet() {
|
||||
local module="$1"
|
||||
local module_name
|
||||
module_name="$(basename "${module}")"
|
||||
|
||||
log_module_title "${module_name}"
|
||||
local title="${TUXFLOTTE_CURRENT_TITLE}"
|
||||
|
||||
local output_file
|
||||
output_file="$(mktemp)"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
"${module}" >"${output_file}" 2>&1 &
|
||||
local pid=$!
|
||||
|
||||
if _tuxflotte_console_is_live; then
|
||||
printf '%s ' "${title}"
|
||||
local frame_index=0
|
||||
local frame_count="${#_TUXFLOTTE_SPINNER_FRAMES[@]}"
|
||||
while kill -0 "${pid}" 2>/dev/null; do
|
||||
printf '\r%s %s' "${title}" "${_TUXFLOTTE_SPINNER_FRAMES[$((frame_index % frame_count))]}"
|
||||
frame_index=$((frame_index + 1))
|
||||
sleep 0.1
|
||||
done
|
||||
fi
|
||||
|
||||
# "wait || status=$?" statt eines nackten "wait"-Aufrufs - genau das
|
||||
# bekannte &&/||-Gegenstueck zu "set -e" (siehe die mount-Bind-Lehre in
|
||||
# image_deploy.sh), hier bewusst genutzt, um den echten Exit-Code sauber
|
||||
# auszuwerten statt das ganze Skript sofort abzubrechen.
|
||||
local status=0
|
||||
wait "${pid}" || status=$?
|
||||
|
||||
if [[ "${status}" -eq 0 ]]; then
|
||||
log_module_ok "${title}"
|
||||
else
|
||||
log_module_failed "${title}"
|
||||
echo
|
||||
cat "${output_file}"
|
||||
echo
|
||||
fi
|
||||
|
||||
[[ -n "${TUXFLOTTE_LOG_FILE:-}" ]] && cat "${output_file}" >>"${TUXFLOTTE_LOG_FILE}" 2>/dev/null
|
||||
|
||||
rm -f "${output_file}"
|
||||
|
||||
return "${status}"
|
||||
}
|
||||
|
||||
run_module() {
|
||||
local module="$1"
|
||||
local mode="${2:-normal}"
|
||||
local module_name
|
||||
module_name="$(basename "$module")"
|
||||
|
||||
[[ -f "$module" ]] || error_exit "Modul nicht gefunden: $module"
|
||||
|
||||
log_step "$(basename "$module")"
|
||||
log_info "Starte Modul: $(basename "$module")"
|
||||
|
||||
if [[ "${DRY_RUN:-false}" == true && "$mode" != "always" ]]; then
|
||||
log_step "${module_name}"
|
||||
log_warn "Dry-Run: Modul übersprungen: $module"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Kompakte Kreisel/Haken-Darstellung nur im echten Auto-Modus: nur dort
|
||||
# ist sichergestellt, dass kein Modul interaktiv nach Eingaben fragt
|
||||
# (12_enrollment_auth.sh/20_profile_selection.sh/25_installation_confirm.sh
|
||||
# pruefen TUXFLOTTE_AUTO_MODE selbst und ueberspringen ihre Prompts) -
|
||||
# ein Prompt-Text waere sonst zusammen mit der restlichen Ausgabe in
|
||||
# output_file gefangen und fuer die Person vor dem Geraet unsichtbar.
|
||||
if [[ "${TUXFLOTTE_AUTO_MODE:-false}" == "true" ]] && ! _tuxflotte_module_is_verbose "${module_name}"; then
|
||||
run_module_quiet "$module"
|
||||
return "$?"
|
||||
fi
|
||||
|
||||
log_step "${module_name}"
|
||||
log_info "Starte Modul: ${module_name}"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
"$module"
|
||||
|
||||
log_success "Modul abgeschlossen: $(basename "$module")"
|
||||
log_success "Modul abgeschlossen: ${module_name}"
|
||||
}
|
||||
|
||||
require_root() {
|
||||
|
||||
@ -23,6 +23,7 @@ SCRIPT_DIR="$(
|
||||
readonly SCRIPT_DIR
|
||||
|
||||
readonly COLLECTORS_FILE="${SCRIPT_DIR}/../lib/hardware_collectors.sh"
|
||||
readonly BOOT_MEDIUM_FILE="${SCRIPT_DIR}/../lib/boot_medium.sh"
|
||||
|
||||
if [[ ! -r "${COLLECTORS_FILE}" ]]; then
|
||||
printf '[%s] FEHLER: Collector-Library nicht gefunden: %s\n' \
|
||||
@ -31,8 +32,17 @@ if [[ ! -r "${COLLECTORS_FILE}" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -r "${BOOT_MEDIUM_FILE}" ]]; then
|
||||
printf '[%s] FEHLER: Boot-Medium-Library nicht gefunden: %s\n' \
|
||||
"${0##*/}" \
|
||||
"${BOOT_MEDIUM_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck source=../lib/hardware_collectors.sh
|
||||
source "${COLLECTORS_FILE}"
|
||||
# shellcheck source=../lib/boot_medium.sh
|
||||
source "${BOOT_MEDIUM_FILE}"
|
||||
|
||||
readonly SCRIPT_NAME="${0##*/}"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user