Schneller Neuversuch nach fehlgeschlagenem Lauf, Statusdateien + Erststart-Anzeige vor dem Anmeldebildschirm (Kunden-ISO-Test R6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-03 09:42:52 +02:00
1 parent 0075709fc2
commit 6e097ce8c1
3 files changed
+132 -1

No files matched your search

+38 -1
View File
@@ -20,6 +20,15 @@ FALLBACK_INTERVAL_SECONDS = 60
# schnellen Debug-Zyklus untergraben.
JITTER_THRESHOLD_SECONDS = 60 * 60
JITTER_MAX_SECONDS = 30 * 60
# Kunden-ISO-Test 02.10.2026 (R6): nach einem fehlgeschlagenen
# ansible-pull-Lauf galt bisher einfach das Server-Intervall weiter -
# ausserhalb des Debug-Modus 6h, ein fehlgeschlagener Erstlauf (z.B. kurzer
# Netzaussetzer) liess das Geraet also stundenlang ohne Benutzer/Software.
RETRY_AFTER_FAILURE_SECONDS = 5 * 60
# Statusdateien fuer die Erststart-Anzeige (tuxflotte-firstboot.sh): "laeuft"
# fluechtig unter /run, "erster erfolgreicher Lauf" dauerhaft unter /var/lib.
STATUS_PATH = "/run/tuxflotte/agent/status"
FIRST_SUCCESS_MARKER = "/var/lib/tuxflotte/agent/first-run-ok"
def log(message):
@@ -138,6 +147,26 @@ def report(device_id, agent_secret, results):
)
def write_status(status):
try:
os.makedirs(os.path.dirname(STATUS_PATH), exist_ok=True)
with open(STATUS_PATH, "w", encoding="utf-8") as f:
f.write(status + "\n")
except OSError as exc:
log(f"Status konnte nicht geschrieben werden: {exc}")
def mark_first_success():
if os.path.exists(FIRST_SUCCESS_MARKER):
return
try:
os.makedirs(os.path.dirname(FIRST_SUCCESS_MARKER), exist_ok=True)
with open(FIRST_SUCCESS_MARKER, "w", encoding="utf-8") as f:
f.write(time.strftime("%Y-%m-%dT%H:%M:%S%z") + "\n")
except OSError as exc:
log(f"Erstlauf-Marker konnte nicht geschrieben werden: {exc}")
def apply_roles(ansible_repo, blueprints, auftraege, webapps, hostname):
"""
Führt sowohl die additiv über den Workspace zugewiesenen Blueprints als
@@ -254,7 +283,11 @@ def run_once(device_id, agent_secret):
# der frueher hier stand: ein Geraet ohne Blueprints/Auftraege, aber
# mit soeben entfernten Webapps oder einer neu gesetzten
# Hostnamenkonvention, muesste sonst faelschlich uebersprungen werden.
write_status("running")
pull_succeeded = apply_roles(result["ansible_repo"], blueprints, auftraege, webapps, hostname)
write_status("ok" if pull_succeeded else "failed")
if pull_succeeded:
mark_first_success()
if auftraege:
try:
@@ -262,7 +295,11 @@ def run_once(device_id, agent_secret):
except (urllib.error.URLError, OSError) as exc:
log(f"Report-Fehler: {exc}")
return result.get("poll_interval_seconds", FALLBACK_INTERVAL_SECONDS), agent_secret
interval = result.get("poll_interval_seconds", FALLBACK_INTERVAL_SECONDS)
if not pull_succeeded:
interval = min(interval, RETRY_AFTER_FAILURE_SECONDS)
return interval, agent_secret
def main():
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Erststart-Anzeige (Kunden-ISO-Test 02.10.2026, R6): nach der Installation
# legt erst der erste Agent-Lauf per Ansible Benutzer und Software an. Bis
# dahin zeigte der Anmeldebildschirm ein Geraet ohne nutzbaren Login. Dieses
# Skript laeuft einmalig vor dem Display-Manager (tuxflotte-firstboot.service),
# zeigt den Fortschritt auf einer eigenen Konsole und gibt die Anmeldung
# frei, sobald der Agent seinen ersten erfolgreichen Lauf gemeldet hat
# (agent.py, mark_first_success()) - spaetestens nach TIMEOUT_SECONDS, damit
# ein dauerhaft scheiternder Lauf das Geraet nie blockiert. Der Agent
# arbeitet danach im Hintergrund weiter.
set -uo pipefail
FIRST_SUCCESS_MARKER="${TUXFLOTTE_FIRST_SUCCESS_MARKER:-/var/lib/tuxflotte/agent/first-run-ok}"
DONE_MARKER="${TUXFLOTTE_FIRSTBOOT_DONE_MARKER:-/var/lib/tuxflotte/firstboot-done}"
STATUS_PATH="/run/tuxflotte/agent/status"
TIMEOUT_SECONDS="${TUXFLOTTE_FIRSTBOOT_TIMEOUT:-1800}"
VT=12
finish() {
mkdir -p "$(dirname "${DONE_MARKER}")"
date --iso-8601=seconds > "${DONE_MARKER}"
exit 0
}
[[ -e "${FIRST_SUCCESS_MARKER}" ]] && finish
# Ohne das bliebe der Plymouth-Bootsplash stehen und verdeckte die Konsole.
if command -v plymouth >/dev/null 2>&1 && plymouth --ping 2>/dev/null; then
plymouth quit
fi
chvt "${VT}" 2>/dev/null || true
setterm --cursor off 2>/dev/null || true
current_step() {
journalctl -u tuxflotte-agent.service -b -o cat -n 300 --no-pager 2>/dev/null |
sed -n 's/^TASK \[\([^]]*\)\].*/\1/p' | tail -n 1 | cut -c1-70
}
start="$(date +%s)"
while true; do
[[ -e "${FIRST_SUCCESS_MARKER}" ]] && break
elapsed=$(( $(date +%s) - start ))
if (( elapsed >= TIMEOUT_SECONDS )); then
printf '\033[2J\033[H'
printf '\n \033[1mTuxflotte\033[0m\n\n'
printf ' Die Einrichtung ist noch nicht abgeschlossen.\n'
printf ' Die Anmeldung wird jetzt trotzdem freigegeben, die Einrichtung\n'
printf ' läuft im Hintergrund weiter.\n\n'
printf ' Fehlen Programme oder Benutzer, bitte die Administration informieren.\n'
sleep 15
break
fi
status="$(cat "${STATUS_PATH}" 2>/dev/null || echo "")"
step="$(current_step)"
printf '\033[2J\033[H'
printf '\n \033[1mTuxflotte richtet dieses Gerät ein\033[0m\n\n'
printf ' Software und Einstellungen werden installiert. Bitte das Gerät\n'
printf ' eingeschaltet und mit dem Netzwerk verbunden lassen.\n'
printf ' Die Anmeldung erscheint automatisch, sobald alles fertig ist.\n\n'
printf ' Vergangene Zeit: %02d:%02d\n' $((elapsed / 60)) $((elapsed % 60))
case "${status}" in
running) printf ' Status: Einrichtung läuft\n' ;;
failed) printf ' Status: \033[1;33mein Schritt ist fehlgeschlagen, neuer Versuch in wenigen Minuten\033[0m\n' ;;
*) printf ' Status: Verbindung zum Server wird hergestellt\n' ;;
esac
[[ -n "${step}" ]] && printf ' Aktueller Schritt: %s\n' "${step}"
sleep 2
done
finish
+20
View File
@@ -0,0 +1,20 @@
[Unit]
Description=Tuxflotte Ersteinrichtung abwarten (Hinweis am Bildschirm)
# Einmalig: nach dem ersten Durchlauf legt firstboot.sh DONE_MARKER an.
ConditionPathExists=!/var/lib/tuxflotte/firstboot-done
After=tuxflotte-agent.service systemd-user-sessions.service plymouth-start.service
Before=display-manager.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/bash /opt/tuxflotte/agent/firstboot.sh
StandardOutput=tty
StandardError=journal
TTYPath=/dev/tty12
TTYReset=yes
# firstboot.sh begrenzt die Wartezeit selbst (TIMEOUT_SECONDS).
TimeoutStartSec=infinity
[Install]
WantedBy=graphical.target