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:
1 parent
0075709fc2
commit
6e097ce8c1
3 files changed
+132
-1
No files matched your search
@@ -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():
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user