$ kode

← alle Dateien · als Text

kode/waechter.py

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106"""Hintergrunddienst: loescht die Karte, wenn ihre Zeit um ist. Laeuft als kode-waechter.service, unabhaengig von jeder CLI-Sitzung - das CLI darf abstuerzen, das Terminal darf zugehen, die Karte wird trotzdem geloescht. Drei Faelle: 1. Bereit, aber Frist abgelaufen (Leerlauf bzw. "halten bis"). 2. Start ohne Lebenszeichen: die startende Sitzung schreibt alle paar Sekunden einen Puls; bleibt er PULS_FRIST aus, ist sie weg. 3. Waisen: Karten mit unserem Label, die kein Zustand kennt (Absturz zwischen Mieten und Eintragen). Erst nach WAISEN_FRIST, damit ein laufender Start nicht getroffen wird. Ein unbestaetigtes Loeschen bleibt im Zustand stehen und wird bei jedem Durchgang wieder versucht. """ import logging import time from . import anbieter, karte, konfig, protokoll logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s", handlers=[logging.StreamHandler(), logging.FileHandler(konfig.WAECHTER_LOG)] if konfig.DATEN.exists() else [logging.StreamHandler()]) log = logging.getLogger("kode.waechter") logging.getLogger("httpx").setLevel(logging.WARNING) TAKT = 30 WAISEN_TAKT = 300 EXISTENZ_TAKT = 300 def _eigene_karten() -> list[tuple[str, str]] | None: rp = anbieter.runpod_eigene() va = anbieter.vast_eigene() if rp is None or va is None: return None return ([("runpod", p["id"]) for p in rp] + [("vast", str(i["id"])) for i in va]) def durchgang(waisen: dict[tuple[str, str], float], uhr: dict[str, float]) -> None: z = karte.lesen() jetzt = time.time() if z.get("kennung"): if z["phase"] != "bereit": if z.get("puls") and jetzt - z["puls"] > konfig.PULS_FRIST: ok, text = karte.herunterfahren("Start ohne Lebenszeichen") log.warning("%s", text) elif (z.get("fehler") or "").startswith("Löschen nicht bestätigt"): ok, text = karte.herunterfahren("erneuter Löschversuch") log.warning("%s", text) elif jetzt > (karte.frist(z) or jetzt + 1): ok, text = karte.herunterfahren("Leerlauf") log.info("%s", text) elif jetzt - uhr.get("existenz", 0) > EXISTENZ_TAKT: uhr["existenz"] = jetzt if not anbieter.existiert(z["anbieter"], z["kennung"]): log.warning("Karte %s gibt es nicht mehr (von Hand gelöscht?) - Zustand zurückgesetzt", z["kennung"]) karte._lauf_beenden(z, "von außen gelöscht", True) karte.aendern(**karte.LEER) elif z["phase"] != "aus" and z.get("puls") and jetzt - z["puls"] > konfig.PULS_FRIST: karte.aendern(**karte.LEER) # Start, der nie zu einer Karte kam if jetzt - uhr.get("waisen", 0) < WAISEN_TAKT: return uhr["waisen"] = jetzt liste = _eigene_karten() if liste is None: log.warning("Kartenliste nicht abrufbar - Waisensuche ausgesetzt") return bekannt = (z.get("anbieter"), z.get("kennung")) for k in list(waisen): if k not in liste: del waisen[k] for k in liste: if k == bekannt: continue erst = waisen.setdefault(k, jetzt) if jetzt - erst > konfig.WAISEN_FRIST: ok = anbieter.loeschen(*k) log.warning("Waise %s:%s %s", k[0], k[1], "gelöscht" if ok else "NICHT gelöscht") protokoll.ereignis("waechter_waise", anbieter=k[0], kennung=k[1], geloescht=ok) if ok: del waisen[k] def main() -> None: konfig.verzeichnisse() log.info("Wächter läuft (Label %s, Takt %d s)", konfig.LABEL, TAKT) waisen: dict[tuple[str, str], float] = {} uhr: dict[str, float] = {} while True: try: durchgang(waisen, uhr) except Exception as f: # noqa: BLE001 log.exception("Durchgang gescheitert") protokoll.fehler("waechter", f) time.sleep(TAKT) if __name__ == "__main__": main()