|
|
|
@@ -24,6 +24,8 @@ niemand muss ihn pflegen, und er zeigt nie ins Leere.
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
import socket
|
|
|
|
|
import time
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from typing import Any, Mapping
|
|
|
|
|
|
|
|
|
@@ -64,16 +66,51 @@ class Zugang:
|
|
|
|
|
return f"vnc://{self.ziel}:{self.port}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#: Aufloesungen, die wir schon einmal versucht haben: ``name -> (Zeit, Ergebnis)``.
|
|
|
|
|
#:
|
|
|
|
|
#: Nicht wegen der Geschwindigkeit -- ein arbeitender Resolver antwortet in
|
|
|
|
|
#: Bruchteilen einer Millisekunde --, sondern wegen des Gegenteils: faellt der
|
|
|
|
|
#: Nameserver aus, laeuft jede Abfrage in sein eigenes Zeitlimit. Ohne diesen
|
|
|
|
|
#: Puffer haette das der Seitenaufbau ausgebadet, und zwar bei jedem Aufruf.
|
|
|
|
|
_AUFLOESUNG: dict[str, tuple[float, bool]] = {}
|
|
|
|
|
|
|
|
|
|
#: Fuenf Minuten. Lang genug, damit ein kaputter Resolver nicht jede Seite
|
|
|
|
|
#: aufhaelt; kurz genug, dass ein neuer DNS-Eintrag nicht bis zum Neustart
|
|
|
|
|
#: wartet.
|
|
|
|
|
AUFLOESUNG_GILT = 300.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def aufloesbar(name: str) -> bool:
|
|
|
|
|
"""Kennt der Resolver diesen Namen?"""
|
|
|
|
|
jetzt = time.monotonic()
|
|
|
|
|
gemerkt = _AUFLOESUNG.get(name)
|
|
|
|
|
if gemerkt is not None and jetzt - gemerkt[0] < AUFLOESUNG_GILT:
|
|
|
|
|
return gemerkt[1]
|
|
|
|
|
try:
|
|
|
|
|
socket.getaddrinfo(name, None)
|
|
|
|
|
ergebnis = True
|
|
|
|
|
except OSError:
|
|
|
|
|
ergebnis = False
|
|
|
|
|
_AUFLOESUNG[name] = (jetzt, ergebnis)
|
|
|
|
|
return ergebnis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ziel(device: Mapping[str, Any]) -> str:
|
|
|
|
|
"""Der Name des Clients, sonst seine IP-Adresse.
|
|
|
|
|
|
|
|
|
|
Der Name ist die bessere Wahl, wo er aufloest: er ueberlebt einen
|
|
|
|
|
Adresswechsel. Taugt er nicht als Hostname -- weil er ein Leerzeichen
|
|
|
|
|
enthaelt oder ein Umlaut darin steht --, waere ein Link darauf sicher
|
|
|
|
|
falsch; dann ist die Adresse richtig.
|
|
|
|
|
Der Name ist die bessere Wahl, wo er gilt: er ueberlebt einen
|
|
|
|
|
Adresswechsel, und genau so stehen die gewachsenen Verbindungen im Viewer
|
|
|
|
|
(``Host=hap02``). Zwei Dinge muessen dafuer stimmen -- er muss als Hostname
|
|
|
|
|
**taugen** ("Kamera Eingang" tut es nicht) und er muss sich **aufloesen**
|
|
|
|
|
lassen. Sonst zeigte die Datei auf einen Namen, den der Arbeitsplatz nicht
|
|
|
|
|
kennt, und niemand saehe, woran es liegt; dann ist die Adresse richtig, die
|
|
|
|
|
ohnehin im Datensatz steht.
|
|
|
|
|
|
|
|
|
|
Loest der Name auf eine **andere** Adresse auf als die gespeicherte, gewinnt
|
|
|
|
|
trotzdem der Name: dann ist der Eintrag im Bestand veraltet, nicht das DNS.
|
|
|
|
|
"""
|
|
|
|
|
name = str(device.get("name") or "").strip()
|
|
|
|
|
if name and HOSTNAME_RE.match(name):
|
|
|
|
|
if name and HOSTNAME_RE.match(name) and aufloesbar(name):
|
|
|
|
|
return name.lower()
|
|
|
|
|
return str(device.get("ip") or "").strip()
|
|
|
|
|
|
|
|
|
|